So it turns out there's actually another version of this glitch where sometimes your character will fail to attack because they're at an extremely precise range where they are out of range to attack, but too close to move closer and then attack. This seemingly requires some degree of client desync and is rare, but it annoyed me enough that I spent many hours tracking down the exact problem.
Find this in unit.cpp:
if(src->type == BL_PC && ud->walktimer != INVALID_TIMER && (!battle_check_range(src, target, range-1) || ignore_range)) {
(it occurs twice in the file)
In both instances, replace it with this:
if(src->type == BL_PC && (!battle_check_range(src, target, range) || ignore_range)) {
if (ud->walktimer == INVALID_TIMER) {
unit_walktoxy(src, target->x, target->y, 8);
}
Do not touch anything below this code, leave everything about the stepskills untouched.
If your rathena is old, the ignore range part is not present so you are instead looking for this:
if(src->type == BL_PC && ud->walktimer != INVALID_TIMER && !battle_check_range(src, target, range)) {
Which you replace with this:
if(src->type == BL_PC && !battle_check_range(src, target, range)) {
if (ud->walktimer == INVALID_TIMER) {
unit_walktoxy(src, target->x, target->y, 8);
}
The problem is that if the user is too close for the client to execute a move to attack instruction, but not close enough to attack, the walktimer will not be initialized, but the range check will fail. It should execute a stepskill in this situation, but it does not because it requires a valid walktimer to execute a stepskill. This handles this boundary case by forcing the user to approach the target, as the client would do automatically under all other circumstances. This is maybe not the most graceful solution (maybe they should just move one tile closer to the target?) but it works and the problem can no longer occur.