diff --git a/code/_onclick/autoclick.dm b/code/_onclick/autoclick.dm deleted file mode 100644 index bd978a921d..0000000000 --- a/code/_onclick/autoclick.dm +++ /dev/null @@ -1,98 +0,0 @@ -/client - var/list/atom/selected_target[2] - var/obj/item/active_mousedown_item = null - var/mouseParams = "" - var/mouseLocation = null - var/mouseObject = null - var/mouseControlObject = null - -/client/MouseDown(object, location, control, params) - var/delay = mob.CanMobAutoclick(object, location, params) - if(delay) - selected_target[1] = object - selected_target[2] = params - while(selected_target[1]) - Click(selected_target[1], location, control, selected_target[2]) - sleep(delay) - active_mousedown_item = mob.canMobMousedown(object, location, params) - if(active_mousedown_item) - active_mousedown_item.onMouseDown(object, location, params, mob) - -/client/MouseUp(object, location, control, params) - selected_target[1] = null - if(active_mousedown_item) - active_mousedown_item.onMouseUp(object, location, params, mob) - active_mousedown_item = null - -/mob/proc/CanMobAutoclick(object, location, params) - -/mob/living/carbon/CanMobAutoclick(atom/object, location, params) - if(!object.IsAutoclickable()) - return - var/obj/item/h = get_active_held_item() - if(h) - . = h.CanItemAutoclick(object, location, params) - -/mob/proc/canMobMousedown(object, location, params) - -/mob/living/carbon/canMobMousedown(atom/object, location, params) - var/obj/item/H = get_active_held_item() - if(H) - . = H.canItemMouseDown(object, location, params) - -/obj/item/proc/CanItemAutoclick(object, location, params) - -/obj/item/proc/canItemMouseDown(object, location, params) - if(canMouseDown) - return src - -/obj/item/proc/onMouseDown(object, location, params, mob) - return - -/obj/item/proc/onMouseUp(object, location, params, mob) - return - -/obj/item - var/canMouseDown = FALSE - -/obj/item/gun - var/automatic = 0 //can gun use it, 0 is no, anything above 0 is the delay between clicks in ds - -/obj/item/gun/CanItemAutoclick(object, location, params) - . = automatic - -/atom/proc/IsAutoclickable() - . = 1 - -/obj/screen/IsAutoclickable() - . = 0 - -/obj/screen/click_catcher/IsAutoclickable() - . = 1 - -//Please don't roast me too hard -/client/MouseMove(object,location,control,params) - mouseParams = params - mouseLocation = location - mouseObject = object - mouseControlObject = control - if(mob && LAZYLEN(mob.mousemove_intercept_objects)) - for(var/datum/D in mob.mousemove_intercept_objects) - D.onMouseMove(object, location, control, params) - -/datum/proc/onMouseMove(object, location, control, params) - return - -/client/MouseDrag(src_object,atom/over_object,src_location,over_location,src_control,over_control,params) - mouseParams = params - mouseLocation = over_location - mouseObject = over_object - mouseControlObject = over_control - if(selected_target[1] && over_object && over_object.IsAutoclickable()) - selected_target[1] = over_object - selected_target[2] = params - if(active_mousedown_item) - active_mousedown_item.onMouseDrag(src_object, over_object, src_location, over_location, params, mob) - -/obj/item/proc/onMouseDrag(src_object, over_object, src_location, over_location, params, mob) - return diff --git a/code/_onclick/drag_drop.dm b/code/_onclick/drag_drop.dm index 3d62cdd0ac..03bff92596 100644 --- a/code/_onclick/drag_drop.dm +++ b/code/_onclick/drag_drop.dm @@ -19,3 +19,120 @@ // recieve a mousedrop /atom/proc/MouseDrop_T(atom/dropping, mob/user) return + + +/client + var/list/atom/selected_target[2] + var/obj/item/active_mousedown_item = null + var/mouseParams = "" + var/mouseLocation = null + var/mouseObject = null + var/mouseControlObject = null + var/middragtime = 0 + var/atom/middragatom + +/client/MouseDown(object, location, control, params) + var/delay = mob.CanMobAutoclick(object, location, params) + if(delay) + selected_target[1] = object + selected_target[2] = params + while(selected_target[1]) + Click(selected_target[1], location, control, selected_target[2]) + sleep(delay) + active_mousedown_item = mob.canMobMousedown(object, location, params) + if(active_mousedown_item) + active_mousedown_item.onMouseDown(object, location, params, mob) + +/client/MouseUp(object, location, control, params) + selected_target[1] = null + if(active_mousedown_item) + active_mousedown_item.onMouseUp(object, location, params, mob) + active_mousedown_item = null + +/mob/proc/CanMobAutoclick(object, location, params) + +/mob/living/carbon/CanMobAutoclick(atom/object, location, params) + if(!object.IsAutoclickable()) + return + var/obj/item/h = get_active_held_item() + if(h) + . = h.CanItemAutoclick(object, location, params) + +/mob/proc/canMobMousedown(object, location, params) + +/mob/living/carbon/canMobMousedown(atom/object, location, params) + var/obj/item/H = get_active_held_item() + if(H) + . = H.canItemMouseDown(object, location, params) + +/obj/item/proc/CanItemAutoclick(object, location, params) + +/obj/item/proc/canItemMouseDown(object, location, params) + if(canMouseDown) + return src + +/obj/item/proc/onMouseDown(object, location, params, mob) + return + +/obj/item/proc/onMouseUp(object, location, params, mob) + return + +/obj/item + var/canMouseDown = FALSE + +/obj/item/gun + var/automatic = 0 //can gun use it, 0 is no, anything above 0 is the delay between clicks in ds + +/obj/item/gun/CanItemAutoclick(object, location, params) + . = automatic + +/atom/proc/IsAutoclickable() + . = 1 + +/obj/screen/IsAutoclickable() + . = 0 + +/obj/screen/click_catcher/IsAutoclickable() + . = 1 + +//Please don't roast me too hard +/client/MouseMove(object,location,control,params) + mouseParams = params + mouseLocation = location + mouseObject = object + mouseControlObject = control + if(mob && LAZYLEN(mob.mousemove_intercept_objects)) + for(var/datum/D in mob.mousemove_intercept_objects) + D.onMouseMove(object, location, control, params) + +/datum/proc/onMouseMove(object, location, control, params) + return + +/client/MouseDrag(src_object,atom/over_object,src_location,over_location,src_control,over_control,params) + var/list/L = params2list(params) + if (L["middle"]) + if (src_object && src_location != over_location) + middragtime = world.time + middragatom = src_object + else + middragtime = 0 + middragatom = null + mouseParams = params + mouseLocation = over_location + mouseObject = over_object + mouseControlObject = over_control + if(selected_target[1] && over_object && over_object.IsAutoclickable()) + selected_target[1] = over_object + selected_target[2] = params + if(active_mousedown_item) + active_mousedown_item.onMouseDrag(src_object, over_object, src_location, over_location, params, mob) + + +/obj/item/proc/onMouseDrag(src_object, over_object, src_location, over_location, params, mob) + return + +/client/MouseDrop(src_object, over_object, src_location, over_location, src_control, over_control, params) + if (middragatom == src_object) + middragtime = 0 + middragatom = null + ..() \ No newline at end of file diff --git a/code/controllers/configuration/entries/general.dm b/code/controllers/configuration/entries/general.dm index a0fb41ed12..258a695a25 100644 --- a/code/controllers/configuration/entries/general.dm +++ b/code/controllers/configuration/entries/general.dm @@ -344,6 +344,14 @@ config_entry_value = null min_val = 0 +/datum/config_entry/number/minute_click_limit + config_entry_value = 400 + min_val = 0 + +/datum/config_entry/number/second_click_limit + config_entry_value = 15 + min_val = 0 + /datum/config_entry/number/error_cooldown // The "cooldown" time for each occurrence of a unique error config_entry_value = 600 min_val = 0 diff --git a/code/modules/client/client_defines.dm b/code/modules/client/client_defines.dm index 193d23e7bf..30e61dba1c 100644 --- a/code/modules/client/client_defines.dm +++ b/code/modules/client/client_defines.dm @@ -62,6 +62,7 @@ var/inprefs = FALSE var/list/topiclimiter + var/list/clicklimiter var/datum/chatOutput/chatOutput diff --git a/code/modules/client/client_procs.dm b/code/modules/client/client_procs.dm index ff2333cdb4..b036e826c9 100644 --- a/code/modules/client/client_procs.dm +++ b/code/modules/client/client_procs.dm @@ -650,6 +650,49 @@ GLOBAL_LIST_EMPTY(external_rsc_urls) message_admins("Proxy Detection: [key_name_admin(src)] IP intel rated [res.intel*100]% likely to be a Proxy/VPN.") ip_intel = res.intel +/client/Click(atom/object, atom/location, control, params) + var/ab = FALSE + var/list/L = params2list(params) + if (object && object == middragatom && L["left"]) + ab = max(0, 5 SECONDS-(world.time-middragtime)*0.1) + var/mcl = CONFIG_GET(number/minute_click_limit) + if (!holder && mcl) + var/minute = round(world.time, 600) + if (!clicklimiter) + clicklimiter = new(LIMITER_SIZE) + if (minute != clicklimiter[CURRENT_MINUTE]) + clicklimiter[CURRENT_MINUTE] = minute + clicklimiter[MINUTE_COUNT] = 0 + clicklimiter[MINUTE_COUNT] += 1+(ab) + if (clicklimiter[MINUTE_COUNT] > mcl) + var/msg = "Your previous click was ignored because you've done too many in a minute." + if (minute != clicklimiter[ADMINSWARNED_AT]) //only one admin message per-minute. (if they spam the admins can just boot/ban them) + clicklimiter[ADMINSWARNED_AT] = minute + + msg += " Administrators have been informed." + if (ab) + log_game("[key_name(src)] is using the middle click aimbot exploit") + message_admins("[key_name_admin(src)] [ADMIN_FLW(usr)] [ADMIN_KICK(usr)] is using the middle click aimbot exploit") + add_system_note("aimbot", "Is using the middle click aimbot exploit") + + log_game("[key_name(src)] Has hit the per-minute click limit of [mcl] clicks in a given game minute") + message_admins("[key_name_admin(src)] [ADMIN_FLW(usr)] [ADMIN_KICK(usr)] Has hit the per-minute click limit of [mcl] clicks in a given game minute") + to_chat(src, "[msg]") + return + + var/scl = CONFIG_GET(number/second_click_limit) + if (!holder && scl) + var/second = round(world.time, 10) + if (!clicklimiter) + clicklimiter = new(LIMITER_SIZE) + if (second != clicklimiter[CURRENT_SECOND]) + clicklimiter[CURRENT_SECOND] = second + clicklimiter[SECOND_COUNT] = 0 + clicklimiter[SECOND_COUNT] += 1+(!!ab) + if (clicklimiter[SECOND_COUNT] > scl) + to_chat(src, "Your previous click was ignored because you've done too many in a second") + return + ..() /client/proc/add_verbs_from_config() if(CONFIG_GET(flag/see_own_notes)) diff --git a/config/config.txt b/config/config.txt index 290052308a..ac3cf9b3ab 100644 --- a/config/config.txt +++ b/config/config.txt @@ -386,6 +386,17 @@ SECOND_TOPIC_LIMIT 10 MINUTE_TOPIC_LIMIT 100 + +## CLICK RATE LIMITING +## Same as above, but applies to clicking on objects in the game window. +## This should be a higher then the interface limit to allow for the spam clickly nature of most battles. +## Admins are exempt from these limits. +## Hitting the minute limit notifies admins. +## Set to 0 to disable. +SECOND_CLICK_LIMIT 15 + +MINUTE_CLICK_LIMIT 400 + ##Error handling related options ## The "cooldown" time for each occurence of a unique error #ERROR_COOLDOWN 600 diff --git a/tgstation.dme b/tgstation.dme index f5a2ea4268..0e8718303b 100755 --- a/tgstation.dme +++ b/tgstation.dme @@ -149,7 +149,6 @@ #include "code\_js\menus.dm" #include "code\_onclick\adjacent.dm" #include "code\_onclick\ai.dm" -#include "code\_onclick\autoclick.dm" #include "code\_onclick\click.dm" #include "code\_onclick\cyborg.dm" #include "code\_onclick\drag_drop.dm"