Explorar el Código

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

Playtester hace 9 años
padre
commit
a63b031958
Se han modificado 6 ficheros con 34 adiciones y 46 borrados
  1. 9 10
      conf/battle/skill.conf
  2. 4 4
      src/map/battle.c
  3. 3 0
      src/map/clif.c
  4. 10 28
      src/map/skill.c
  5. 4 0
      src/map/skill.h
  6. 4 4
      src/map/unit.c

+ 9 - 10
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

+ 4 - 4
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             },

+ 3 - 0
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);

+ 10 - 28
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;

+ 4 - 0
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)

+ 4 - 4
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 )
 // 	{