From a63b0319584d934b82ac332e05a3e39d8c473b6e Mon Sep 17 00:00:00 2001 From: Playtester Date: Sun, 21 Feb 2016 17:06:39 +0100 Subject: [PATCH] aMotion delay is now set at cast begin (fixes #944) * When casting a spell with cast time shorter than aMotion, the delay will now be set to aMotion or min_skill_delay_limit * aMotion and min_skill_delay_limit are no longer applied at cast end nor by auto spells * Aftercast delay can only increase the delay and never decrease it * When there is no aftercast delay, the client will no longer show a non-existent 10 second delay * Removed "double cast" code of Jupitel and Waterball, it's no longer needed as it fits into the official delay system * Added a constant SECURITY_CASTTIME, which is added at cast begin and subtracted at cast end to prevent hackers to send in skill packets before the timer triggers cast end * Set skill_amotion_leniency to 0 by default as it applies delay at cast end and would consequently block the official system from working; you can set it back to 90 or even 140 for increased security --- conf/battle/skill.conf | 19 +++++++++---------- src/map/battle.c | 8 ++++---- src/map/clif.c | 3 +++ src/map/skill.c | 38 ++++++++++---------------------------- src/map/skill.h | 4 ++++ src/map/unit.c | 8 ++++---- 6 files changed, 34 insertions(+), 46 deletions(-) diff --git a/conf/battle/skill.conf b/conf/battle/skill.conf index 71998453f3..1fbacfb68c 100644 --- a/conf/battle/skill.conf +++ b/conf/battle/skill.conf @@ -20,7 +20,7 @@ delay_rate: 100 delay_dependon_dex: no delay_dependon_agi: no -// Minimum allowed delay for ANY skills after casting (in milliseconds) (Note 1) +// Minimum allowed delay for ANY skills after castbegin (in milliseconds) (Note 1) // Note: Setting this to anything above 0 can stop speedhacks. min_skill_delay_limit: 100 @@ -30,8 +30,8 @@ min_skill_delay_limit: 100 // appear to "teleport" afterwards. default_walk_delay: 300 -//Completely disable skill delay of the following types (Note 3) -//NOTE: By default mobs don't have the skill delay as specified in the skill +// Completely disable skill delay of the following types (Note 3) +// NOTE: By default mobs don't have the skill delay as specified in the skill // database, but follow their own 'reuse' skill delay which is specified on // the mob skill db. When set, the delay for all skills become // min_skill_delay_limit. @@ -43,13 +43,12 @@ castrate_dex_scale: 150 // How much (dex*2+int) does variable cast turns zero? vcast_stat_scale: 530 -// What level of leniency should the skill system give for skills when -// accounting attack motion (ASPD) for casting skills (Note 2, between 0 and 300) -// -// NOTE: Setting this to 100% may cause some issues with valid skills not being cast. -// The time difference between client and server varies so allowing 90% leniency -// should be enough to forgive very small margins of error. -skill_amotion_leniency: 90 +// On official servers, amotion delay is applied at castbegin. There is no amotion delay applied +// at castend. Set this to anything above 0 to also apply amotion delay at castend. (Note 2) +// NOTE: Setting this will break chaining of skills with cast time but no aftercast delay. +// The client-sided delays are different from skill to skill and usually range from 140 to 180. +// If you want to be secure, a value between 90 and 140 is recommended. +skill_amotion_leniency: 0 // Will normal attacks be able to ignore the delay after skills? (Note 1) skill_delay_attack_enable: yes diff --git a/src/map/battle.c b/src/map/battle.c index 75a4747692..b7283a8c54 100644 --- a/src/map/battle.c +++ b/src/map/battle.c @@ -6991,7 +6991,7 @@ enum damage_lv battle_weapon_attack(struct block_list* src, struct block_list* t } if (rnd()%100 < triple_rate) { //Need to apply canact_tick here because it doesn't go through skill_castend_id - sd->ud.canact_tick = tick + skill_delayfix(src, MO_TRIPLEATTACK, skillv); + sd->ud.canact_tick = max(tick + skill_delayfix(src, MO_TRIPLEATTACK, skillv), sd->ud.canact_tick); if( skill_attack(BF_WEAPON,src,src,target,MO_TRIPLEATTACK,skillv,tick,0) ) return ATK_DEF; return ATK_MISS; @@ -7189,7 +7189,7 @@ enum damage_lv battle_weapon_attack(struct block_list* src, struct block_list* t int autospell_tick = skill_delayfix(src, skill_id, skill_lv); if (DIFF_TICK(ud->canact_tick, tick + autospell_tick) < 0) { - ud->canact_tick = tick + autospell_tick; + ud->canact_tick = max(tick + autospell_tick, ud->canact_tick); if (battle_config.display_status_timers && sd) clif_status_change(src, SI_ACTIONDELAY, 1, autospell_tick, 0, 0, 0); } @@ -7253,7 +7253,7 @@ enum damage_lv battle_weapon_attack(struct block_list* src, struct block_list* t } sd->state.autocast = 0; - sd->ud.canact_tick = tick + skill_delayfix(src, r_skill, r_lv); + sd->ud.canact_tick = max(tick + skill_delayfix(src, r_skill, r_lv), sd->ud.canact_tick); clif_status_change(src, SI_ACTIONDELAY, 1, skill_delayfix(src, r_skill, r_lv), 0, 0, 1); } } @@ -8098,7 +8098,7 @@ static const struct _battle_data { { "max_trans_parameter", &battle_config.max_trans_parameter, 99, 10, SHRT_MAX, }, { "max_third_trans_parameter", &battle_config.max_third_trans_parameter, 135, 10, SHRT_MAX, }, { "max_extended_parameter", &battle_config.max_extended_parameter, 125, 10, SHRT_MAX, }, - { "skill_amotion_leniency", &battle_config.skill_amotion_leniency, 90, 0, 300 }, + { "skill_amotion_leniency", &battle_config.skill_amotion_leniency, 0, 0, 300 }, { "mvp_tomb_enabled", &battle_config.mvp_tomb_enabled, 1, 0, 1 }, { "feature.atcommand_suggestions", &battle_config.atcommand_suggestions_enabled, 0, 0, 1 }, { "min_npc_vendchat_distance", &battle_config.min_npc_vendchat_distance, 3, 0, 100 }, diff --git a/src/map/clif.c b/src/map/clif.c index 989762b97a..01c174c770 100644 --- a/src/map/clif.c +++ b/src/map/clif.c @@ -5868,6 +5868,9 @@ void clif_status_change(struct block_list *bl,int type,int flag,int tick,int val if (type == SI_BLANK) //It shows nothing on the client... return; + if (type == SI_ACTIONDELAY && tick == 0) + return; + nullpo_retv(bl); sd = BL_CAST(BL_PC, bl); diff --git a/src/map/skill.c b/src/map/skill.c index 3a49ebe991..f942230559 100755 --- a/src/map/skill.c +++ b/src/map/skill.c @@ -1920,7 +1920,7 @@ int skill_additional_effect(struct block_list* src, struct block_list *bl, uint1 if (ud) { rate = skill_delayfix(src, skill, skill_lv); if (DIFF_TICK(ud->canact_tick, tick + rate) < 0){ - ud->canact_tick = tick+rate; + ud->canact_tick = max(tick + rate, ud->canact_tick); if ( battle_config.display_status_timers ) clif_status_change(src, SI_ACTIONDELAY, 1, rate, 0, 0, 0); } @@ -2014,7 +2014,7 @@ int skill_additional_effect(struct block_list* src, struct block_list *bl, uint1 if (ud) { rate = skill_delayfix(src, skill, autospl_skill_lv); if (DIFF_TICK(ud->canact_tick, tick + rate) < 0){ - ud->canact_tick = tick+rate; + ud->canact_tick = max(tick + rate, ud->canact_tick); if ( battle_config.display_status_timers && sd ) clif_status_change(src, SI_ACTIONDELAY, 1, rate, 0, 0, 0); } @@ -2348,7 +2348,7 @@ int skill_counter_additional_effect (struct block_list* src, struct block_list * if (ud) { autospl_rate = skill_delayfix(bl, autospl_skill_id, autospl_skill_lv); if (DIFF_TICK(ud->canact_tick, tick + autospl_rate) < 0){ - ud->canact_tick = tick+autospl_rate; + ud->canact_tick = max(tick + autospl_rate, ud->canact_tick); if ( battle_config.display_status_timers && dstsd ) clif_status_change(bl, SI_ACTIONDELAY, 1, autospl_rate, 0, 0, 0); } @@ -3930,7 +3930,7 @@ static int skill_timerskill(int tid, unsigned int tick, int id, intptr_t data) // Official behaviour is to hit as long as there is a line of sight, regardless of distance if (skl->type > 0 && !status_isdead(target) && path_search_long(NULL,src->m,src->x,src->y,target->x,target->y,CELL_CHKWALL)) { // Apply canact delay here to prevent hacks (unlimited casting) - ud->canact_tick = tick + skill_delayfix(src, skl->skill_id, skl->skill_lv); + ud->canact_tick = max(tick + status_get_amotion(src), ud->canact_tick); skill_attack(BF_MAGIC, src, src, target, skl->skill_id, skl->skill_lv, tick, skl->flag); } if (unit && !status_isdead(target) && !status_isdead(src)) { @@ -5231,7 +5231,7 @@ int skill_castend_damage_id (struct block_list* src, struct block_list *bl, uint break; } - sd->ud.canact_tick = tick + skill_delayfix(src, pres_skill_id, pres_skill_lv); + sd->ud.canact_tick = max(tick + skill_delayfix(src, pres_skill_id, pres_skill_lv), sd->ud.canact_tick); clif_status_change(src, SI_ACTIONDELAY, 1, skill_delayfix(src, pres_skill_id, pres_skill_lv), 0, 0, 0); cooldown = pc_get_skillcooldown(sd,pres_skill_id, pres_skill_lv); @@ -5721,22 +5721,7 @@ int skill_castend_damage_id (struct block_list* src, struct block_list *bl, uint if( sd && !(flag&1) ) {// ensure that the skill last-cast tick is recorded - tick = gettick(); - switch (skill_id) { - //These skill don't call skill_attack right away and allow to cast a second spell before the first skill deals damage - case WZ_JUPITEL: - case WZ_WATERBALL: - //Only allow the double-cast trick every 2000ms to prevent hacks - if (DIFF_TICK(tick, sd->canskill_tick) > 2000) { - sd->ud.canact_tick = tick; - sd->canskill_tick = tick-2000+TIMERSKILL_INTERVAL; - break; - } - //Fall through - default: - sd->canskill_tick = tick; - break; - } + sd->canskill_tick = gettick(); if( sd->state.arrow_atk ) {// consume arrow on last invocation to this skill. @@ -10999,8 +10984,8 @@ int skill_castend_id(int tid, unsigned int tick, int id, intptr_t data) if (ud->walktimer != INVALID_TIMER && ud->skill_id != TK_RUN && ud->skill_id != RA_WUGDASH) unit_stop_walking(src,1); - if( !sd || sd->skillitem != ud->skill_id || skill_get_delay(ud->skill_id,ud->skill_lv) ) - ud->canact_tick = tick + skill_delayfix(src, ud->skill_id, ud->skill_lv); //Tests show wings don't overwrite the delay but skill scrolls do. [Inkfish] + if (!sd || sd->skillitem != ud->skill_id || skill_get_delay(ud->skill_id, ud->skill_lv)) + ud->canact_tick = max(tick + skill_delayfix(src, ud->skill_id, ud->skill_lv), ud->canact_tick - SECURITY_CASTTIME); if (sd) { //Cooldown application int cooldown = pc_get_skillcooldown(sd,ud->skill_id, ud->skill_lv); // Increases/Decreases cooldown of a skill by item/card bonuses. if(cooldown) skill_blockpc_start(sd, ud->skill_id, cooldown); @@ -11228,8 +11213,8 @@ int skill_castend_pos(int tid, unsigned int tick, int id, intptr_t data) if (ud->walktimer != INVALID_TIMER) unit_stop_walking(src,1); - if( !sd || sd->skillitem != ud->skill_id || skill_get_delay(ud->skill_id,ud->skill_lv) ) - ud->canact_tick = tick + skill_delayfix(src, ud->skill_id, ud->skill_lv); + if (!sd || sd->skillitem != ud->skill_id || skill_get_delay(ud->skill_id, ud->skill_lv)) + ud->canact_tick = max(tick + skill_delayfix(src, ud->skill_id, ud->skill_lv), ud->canact_tick - SECURITY_CASTTIME); if (sd) { //Cooldown application int cooldown = pc_get_skillcooldown(sd,ud->skill_id, ud->skill_lv); if(cooldown) skill_blockpc_start(sd, ud->skill_id, cooldown); @@ -16233,9 +16218,6 @@ int skill_delayfix(struct block_list *bl, uint16 skill_id, uint16 skill_lv) if (battle_config.delay_rate != 100) time = time * battle_config.delay_rate / 100; - //min delay - time = max(time, status_get_amotion(bl)); // Delay can never be below amotion [Playtester] - time = max(time, battle_config.min_skill_delay_limit); //ShowInfo("Delay delayfix = %d\n",time); return time; diff --git a/src/map/skill.h b/src/map/skill.h index 0ac3216893..c93cbc5349 100644 --- a/src/map/skill.h +++ b/src/map/skill.h @@ -104,6 +104,10 @@ enum e_skill_inf3 { /// If you change this, make sure it's an odd value (for icewall block behavior). #define WALK_SKILL_INTERVAL 5 +/// Time that's added to canact delay on castbegin and substracted on castend +/// This is to prevent hackers from sending a skill packet after cast but before a timer triggers castend +#define SECURITY_CASTTIME 100 + /// Flags passed to skill_attack/skill_area_sub enum e_skill_display { SD_LEVEL = 0x1000, // skill_attack will send -1 instead of skill level (affects display of some skills) diff --git a/src/map/unit.c b/src/map/unit.c index 6e1ed15a73..9b25129211 100644 --- a/src/map/unit.c +++ b/src/map/unit.c @@ -1858,8 +1858,8 @@ int unit_skilluse_id2(struct block_list *src, int target_id, uint16 skill_id, ui if( casttime <= 0 ) ud->state.skillcastcancel = 0; - if( !sd || sd->skillitem != skill_id || skill_get_cast(skill_id,skill_lv) ) - ud->canact_tick = tick + casttime + 100; + if (!sd || sd->skillitem != skill_id || skill_get_cast(skill_id, skill_lv)) + ud->canact_tick = tick + max(casttime, max(status_get_amotion(src), battle_config.min_skill_delay_limit)) + SECURITY_CASTTIME; if( sd ) { switch( skill_id ) { @@ -2037,8 +2037,8 @@ int unit_skilluse_pos2( struct block_list *src, short skill_x, short skill_y, ui casttime = 0; ud->state.skillcastcancel = castcancel&&casttime>0?1:0; - if( !sd || sd->skillitem != skill_id || skill_get_cast(skill_id,skill_lv) ) - ud->canact_tick = tick + casttime + 100; + if (!sd || sd->skillitem != skill_id || skill_get_cast(skill_id, skill_lv)) + ud->canact_tick = tick + max(casttime, max(status_get_amotion(src), battle_config.min_skill_delay_limit)) + SECURITY_CASTTIME; // if( sd ) // {