diff --git a/SQL/bluemoon_schema.sql b/SQL/bluemoon_schema.sql index a00aac276bd5..e7433d5855d5 100644 --- a/SQL/bluemoon_schema.sql +++ b/SQL/bluemoon_schema.sql @@ -81,47 +81,33 @@ DELIMITER ; /*!40101 SET COLLATION_CONNECTION=@OLD_COLLATION_CONNECTION */; /*!40111 SET SQL_NOTES=@OLD_SQL_NOTES */; + -- -- Table structure for table `ipintel` -- + DROP TABLE IF EXISTS `ipintel`; /*!40101 SET @saved_cs_client = @@character_set_client */; /*!40101 SET character_set_client = utf8 */; -CREATE TABLE `ipintel` ( - `ip` int UNSIGNED NOT NULL, - `date` timestamp DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP NOT NULL, - `intel` real NOT NULL DEFAULT '0', - PRIMARY KEY (`ip`) -) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4; +CREATE TABLE `ipintel` ( + `ip` int(10) unsigned NOT NULL, + `date` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP, + `intel` double NOT NULL DEFAULT '0', + PRIMARY KEY (`ip`), + KEY `idx_ipintel` (`ip`,`intel`,`date`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci; /*!40101 SET character_set_client = @saved_cs_client */; -- --- Table structure for table `vpn_whitelist` +-- Table structure for table `ipintel_whitelist` -- -DROP TABLE IF EXISTS `vpn_whitelist`; + +DROP TABLE IF EXISTS `ipintel_whitelist`; /*!40101 SET @saved_cs_client = @@character_set_client */; /*!40101 SET character_set_client = utf8 */; -CREATE TABLE `vpn_whitelist` ( - `ckey` varchar(32) NOT NULL, - `reason` text, - PRIMARY KEY (`ckey`) -) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4; +CREATE TABLE `ipintel_whitelist` ( + `ckey` varchar(32) NOT NULL, + `admin_ckey` varchar(32) NOT NULL, + PRIMARY KEY (`ckey`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci; /*!40101 SET character_set_client = @saved_cs_client */; - --- --- Table structure for table `connection_ipintel_log` --- -DROP TABLE IF EXISTS `connection_ipintel_log`; -CREATE TABLE `connection_ipintel_log` ( - `id` int(11) NOT NULL AUTO_INCREMENT, - `datetime` datetime NOT NULL, - `ckey` varchar(32) NOT NULL, - `ip` INT UNSIGNED NOT NULL, - `computerid` varchar(32) NOT NULL, - `server_id` VARCHAR(50) NULL DEFAULT NULL, - `result` ENUM('ESTABLISHED','DROPPED - IPINTEL','DROPPED - BANNED','DROPPED - INVALID') NOT NULL DEFAULT 'ESTABLISHED' COLLATE 'utf8mb4_general_ci', - PRIMARY KEY (`id`), - KEY `ckey` (`ckey`), - KEY `ip` (`ip`), - KEY `computerid` (`computerid`) -) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4; diff --git a/code/_BLUEMOONCODE/_HELPERS/unsorted.dm b/code/_BLUEMOONCODE/_HELPERS/unsorted.dm deleted file mode 100644 index 438c32a968bb..000000000000 --- a/code/_BLUEMOONCODE/_HELPERS/unsorted.dm +++ /dev/null @@ -1,45 +0,0 @@ -/** - * HTTP Get (Powered by RUSTG) - * - * This proc should be used as a replacement for [/world/proc/Export] due to an underlying issue with it. - * See: https://www.byond.com/forum/post/2772166 - * The one thing you will need to be aware of is that this no longer wraps the response inside a "file", so anything that relies on a file2text() unwrap will need tweaking. - * RUST HTTP also has better support for HTTPS as well as weird quirks with modern webservers. - * Returns an assoc list that follows the standard [/world/proc/Export] format (https://secure.byond.com/docs/ref/index.html#/world/proc/Export), with the above exception - * - * Arguments: - * * url - URL to GET - */ -/proc/HTTPGet(url) - var/datum/http_request/req = new - req.prepare(RUSTG_HTTP_METHOD_GET, url) - req.begin_async() - - // Check if we are complete - UNTIL(req.is_complete()) - var/datum/http_response/res = req.into_response() - - if(res.errored) - . = list() // Return an empty list - CRASH("Internal error during HTTP get: [res.error]") - - var/list/output = list() - output["STATUS"] = res.status_code - - // Handle changes of line format. ASCII 13 = CR - var/content = replacetext(res.body, "[ascii2text(13)]\n", "\n") - output["CONTENT"] = content - - return output - -/proc/log_connection(ckey, ip, cid, connection_type) - ASSERT(connection_type in list(CONNECTION_TYPE_ESTABLISHED, CONNECTION_TYPE_DROPPED_IPINTEL, CONNECTION_TYPE_DROPPED_BANNED, CONNECTION_TYPE_DROPPED_INVALID)) - var/datum/db_query/query_accesslog = SSdbcore.NewQuery("INSERT INTO connection_ipintel_log (`datetime`, `ckey`, `ip`, `computerid`, `result`, `server_id`) VALUES(Now(), :ckey, INET_ATON(:ip), :cid, :result, :server_id)", list( - "ckey" = ckey, - "ip" = "[ip ? ip : "127.0.0.1"]", - "cid" = cid, - "result" = connection_type, - "server_id" = CONFIG_GET(string/servername) - )) - query_accesslog.warn_execute() - qdel(query_accesslog) diff --git a/code/__BLUEMOONCODE/_DEFINES/ipintel.dm b/code/__BLUEMOONCODE/_DEFINES/ipintel.dm index 781fd4bb277e..9fbc14ae40db 100644 --- a/code/__BLUEMOONCODE/_DEFINES/ipintel.dm +++ b/code/__BLUEMOONCODE/_DEFINES/ipintel.dm @@ -1,9 +1,15 @@ -// Connection types. These match enums in the SQL DB. Dont change them -/// Client was let into the server -#define CONNECTION_TYPE_ESTABLISHED "ESTABLISHED" -/// Client was disallowed due to IPIntel -#define CONNECTION_TYPE_DROPPED_IPINTEL "DROPPED - IPINTEL" -/// Client was disallowed due to being banned -#define CONNECTION_TYPE_DROPPED_BANNED "DROPPED - BANNED" -/// Client was disallowed due to invalid data -#define CONNECTION_TYPE_DROPPED_INVALID "DROPPED - INVALID" +#define IPINTEL_RATE_LIMIT_MINUTE "minute" +#define IPINTEL_RATE_LIMIT_DAY "day" + +/// An internal error occurred and the query cannot be processed +#define IPINTEL_UNKNOWN_INTERNAL_ERROR "unknown_internal_error" +/// An error occurred with the query and the result is unknown +#define IPINTEL_UNKNOWN_QUERY_ERROR "unknown_query_error" +/// Cannot query as we are rate limited for the rest of the day +#define IPINTEL_RATE_LIMITED_DAY "rate_limited_day" +/// Cannot query as we are rate limited for the rest of the minute +#define IPINTEL_RATE_LIMITED_MINUTE "rate_limited_minute" +/// The IP address is a VPN or bad IP +#define IPINTEL_BAD_IP "bad_ip" +/// The IP address is not a VPN or bad IP +#define IPINTEL_GOOD_IP "good_ip" diff --git a/code/controllers/configuration/bluemoon_entries/general.dm b/code/controllers/configuration/bluemoon_entries/general.dm deleted file mode 100644 index cf8b8566a57a..000000000000 --- a/code/controllers/configuration/bluemoon_entries/general.dm +++ /dev/null @@ -1,41 +0,0 @@ -/// Defines whether or not mentors can see ckeys alongside mobnames. -/datum/config_entry/flag/mentors_mobname_only - -/// Defines whether the server uses the legacy mentor system with mentors.txt or the SQL system. -/datum/config_entry/flag/mentor_legacy_system - protection = CONFIG_ENTRY_LOCKED - -/datum/config_entry/flag/ipintel_enabled - default = FALSE - -/datum/config_entry/string/ipintel_domain - default = "check.getipintel.net" - -/datum/config_entry/string/details_url - default = "https://iphub.info/?ip=" - -/datum/config_entry/string/contact_email - -/datum/config_entry/string/contact_email/ValidateAndSet(str_val) - return str_val != "ch@nge.me" && (!length(str_val) || findtext(str_val, "@")) && ..() - -/datum/config_entry/number/bad_rating - default = 0.9 - integer = FALSE - min_val = 0 - max_val = 1 - -/datum/config_entry/flag/whitelist_mode - default = TRUE - -/datum/config_entry/number/hours_save_good - default = 72 - min_val = 0 - -/datum/config_entry/number/hours_save_bad - default = 24 - min_val = 0 - -/datum/config_entry/number/playtime_ignore_threshold - default = 10 - min_val = 0 diff --git a/code/controllers/subsystem/ipintel.dm b/code/controllers/subsystem/ipintel.dm index a7f5da72de84..6aa4464fecc8 100644 --- a/code/controllers/subsystem/ipintel.dm +++ b/code/controllers/subsystem/ipintel.dm @@ -1,3 +1,4 @@ +/* // BLUEMOON EDIT:START IPINTEL FROM TG SUBSYSTEM_DEF(ipintel) name = "XKeyScore" init_order = INIT_ORDER_XKEYSCORE @@ -11,3 +12,4 @@ SUBSYSTEM_DEF(ipintel) /datum/controller/subsystem/ipintel/Initialize(timeofday, zlevel) enabled = TRUE . = ..() +*/ // BLUEMOON EDIT:END IPINTEL FROM TG diff --git a/code/modules/admin/IsBanned.dm b/code/modules/admin/IsBanned.dm index 32883d77500a..825e2d83c787 100644 --- a/code/modules/admin/IsBanned.dm +++ b/code/modules/admin/IsBanned.dm @@ -6,7 +6,7 @@ #define STICKYBAN_MAX_EXISTING_USER_MATCHES 5 //ie, users who were connected before the ban triggered #define STICKYBAN_MAX_ADMIN_MATCHES 2 -/world/IsBanned(key,address,computer_id,type,real_bans_only=FALSE, check_ipintel = TRUE, log_info = TRUE) // BLUEMOON EDIT:START IPINTEL FROM TG +/world/IsBanned(key,address,computer_id,type,real_bans_only=FALSE) var/static/key_cache = list() if(!real_bans_only) if(key_cache[key] >= REALTIMEOFDAY) @@ -17,17 +17,13 @@ if(real_bans_only) key_cache[key] = 0 return FALSE - log_access("Login (invalid data): [key] [address]-[computer_id]") + log_access("Failed Login (invalid data): [key] [address]-[computer_id]") key_cache[key] = 0 - if(log_info) // BLUEMOON EDIT:START IPINTEL FROM TG - INVOKE_ASYNC(GLOBAL_PROC, GLOBAL_PROC_REF(log_connection), (ckey(key) || ""), (address || ""), (computer_id || ""), CONNECTION_TYPE_DROPPED_INVALID) return list("reason"="invalid login data", "desc"="Error: Could not check ban status, Please try again. Error message: Your computer provided invalid or blank information to the server on connection (byond username, IP, and Computer ID.) Provided information for reference: Username:'[key]' IP:'[address]' Computer ID:'[computer_id]'. (If you continue to get this error, please restart byond or contact byond support.)") if (text2num(computer_id) == 2147483647) //this cid causes stickybans to go haywire - log_access("Login (invalid cid): [key] [address]-[computer_id]") + log_access("Failed Login (invalid cid): [key] [address]-[computer_id]") key_cache[key] = 0 - if(log_info) // BLUEMOON EDIT:START IPINTEL FROM TG - INVOKE_ASYNC(GLOBAL_PROC, GLOBAL_PROC_REF(log_connection), ckey(key), address, computer_id, CONNECTION_TYPE_DROPPED_INVALID) return list("reason"="invalid login data", "desc"="Error: Could not check ban status, Please try again. Error message: Your computer provided an invalid Computer ID.)") if (type == "world") @@ -52,10 +48,8 @@ message_admins("The admin [key] has been allowed to bypass the whitelist") addclientmessage(ckey,"You have been allowed to bypass the whitelist") else - log_access("Login: [key] - Not on whitelist") + log_access("Failed Login: [key] - Not on whitelist") key_cache[key] = 0 - if(log_info) // BLUEMOON EDIT:START IPINTEL FROM TG - INVOKE_ASYNC(GLOBAL_PROC, GLOBAL_PROC_REF(log_connection), ckey(key), address, computer_id, CONNECTION_TYPE_DROPPED_IPINTEL) return list("reason"="whitelist", "desc" = "\nReason: You are not on the white list for this server") //Guest Checking @@ -63,14 +57,10 @@ if (CONFIG_GET(flag/guest_ban)) log_access("Failed Login: [key] - Guests not allowed") key_cache[key] = 0 - if(log_info) // BLUEMOON EDIT:START IPINTEL FROM TG - INVOKE_ASYNC(GLOBAL_PROC, GLOBAL_PROC_REF(log_connection), ckey(key), address, computer_id, CONNECTION_TYPE_DROPPED_BANNED) return list("reason"="guest", "desc"="\nReason: Guests not allowed. Please sign in with a byond account.") if (CONFIG_GET(flag/panic_bunker) && SSdbcore.Connect()) log_access("Failed Login: [key] - Guests not allowed during panic bunker") key_cache[key] = 0 - if(log_info) // BLUEMOON EDIT:START IPINTEL FROM TG - INVOKE_ASYNC(GLOBAL_PROC, GLOBAL_PROC_REF(log_connection), ckey(key), address, computer_id, CONNECTION_TYPE_DROPPED_BANNED) return list("reason"="guest", "desc"="\nReason: Sorry but the server is currently not accepting connections from never before seen players or guests. If you have played on this server with a byond account before, please log in to the byond account you have played from.") //Population Cap Checking @@ -80,16 +70,6 @@ key_cache[key] = 0 return list("reason"="popcap", "desc"= "\nReason: [CONFIG_GET(string/extreme_popcap_message)]") - // BLUEMOON EDIT:START IPINTEL FROM TG - //check if the IP address is a known proxy/vpn, and the user is not whitelisted - if(check_ipintel && CONFIG_GET(string/contact_email) && CONFIG_GET(flag/whitelist_mode) && GLOB.ipintel_manager.ipintel_is_banned(key, address)) - log_admin("Failed Login: [key] [computer_id] [address] - Proxy/VPN") - var/mistakemessage = "" - if(CONFIG_GET(string/banappeals)) - mistakemessage = "\nIf you have to use one, request whitelisting at: [CONFIG_GET(string/banappeals)]" - return list("reason"="using proxy or vpn", "desc"="\nReason: Proxies/VPNs are not allowed here. [mistakemessage]") - // BLUEMOON EDIT:END IPINTEL FROM TG - if(CONFIG_GET(flag/ban_legacy_system)) //Ban Checking @@ -102,8 +82,6 @@ else log_access("Failed Login: [key] [computer_id] [address] - Banned [.["reason"]]") key_cache[key] = 0 - if(log_info) // BLUEMOON EDIT: IPINTEL FROM TG - INVOKE_ASYNC(GLOBAL_PROC, GLOBAL_PROC_REF(log_connection), ckey(key), address, computer_id, CONNECTION_TYPE_DROPPED_BANNED) return . else @@ -176,8 +154,6 @@ log_access("Failed Login: [key] [computer_id] [address] - Banned (#[banid]) [.["reason"]]") qdel(query_ban_check) key_cache[key] = 0 - if(log_info) // BLUEMOON EDIT IPINTEL FROM TG - INVOKE_ASYNC(GLOBAL_PROC, GLOBAL_PROC_REF(log_connection), ckey(key), address, computer_id, CONNECTION_TYPE_DROPPED_BANNED) return . qdel(query_ban_check) @@ -256,7 +232,7 @@ var/desc = "\nReason:(StickyBan) You, or another user of this computer or connection ([bannedckey]) is banned from playing here. The ban reason is:\n[ban["message"]]\nThis ban was applied by [ban["admin"]]\nThis is a BanEvasion Detection System ban, if you think this ban is a mistake, please wait EXACTLY 6 seconds, then try again before filing an appeal.\n" . = list("reason" = "Stickyban", "desc" = desc) - log_access("Login: [key] [computer_id] [address] - StickyBanned [ban["message"]] Target Username: [bannedckey] Placed by [ban["admin"]]") + log_access("Failed Login: [key] [computer_id] [address] - StickyBanned [ban["message"]] Target Username: [bannedckey] Placed by [ban["admin"]]") key_cache[key] = 0 return . diff --git a/code/modules/admin/admin_verbs.dm b/code/modules/admin/admin_verbs.dm index b9cee19d54d3..4254acce8536 100644 --- a/code/modules/admin/admin_verbs.dm +++ b/code/modules/admin/admin_verbs.dm @@ -58,7 +58,6 @@ GLOBAL_PROTECT(admin_verbs_admin) /client/proc/Getkey, /*teleports a mob with a certain ckey to our location*/ /client/proc/game_panel, /*game panel, allows to change game-mode etc*/ /client/proc/mail_panel, /*BLUEMOON ADD - панель управления почтой*/ - /datum/admins/proc/vpn_whitelist, /*BLUEMOON ADD - Допуск */ /client/proc/fax_panel, /*send a paper to fax*/ // /client/proc/sendmob, /*sends a mob somewhere*/ -Removed due to it needing two sorting procs to work, which were executed every time an admin right-clicked. ~Errorage /client/proc/jumptoarea, diff --git a/code/modules/admin/ipintel.dm b/code/modules/admin/ipintel.dm index 687ee92410db..ac2d39b98e2a 100644 --- a/code/modules/admin/ipintel.dm +++ b/code/modules/admin/ipintel.dm @@ -136,380 +136,3 @@ log_game("IPINTEL: [text]") debug_admins("IPINTEL: [text]") */ // BLUEMOON EDIT:END IPINTEL FROM TG - -// BLUEMOON EDIT:START IPINTEL FROM TG -GLOBAL_DATUM_INIT(ipintel_manager, /datum/ipintel_manager, new()) - -/datum/ipintel_manager - var/throttle = 0 - var/errors = 0 - - var/list/cache = list() - -// Represents an IP intel holder datum -/datum/ipintel - /// The IP being checked - var/ip - /// The current rating, 0-1 float. - var/intel = 0 - /// Whether this was loaded from the cache or not - var/cache = FALSE - /// How many minutes ago it was cached - var/cacheminutesago = 0 - /// The date it was cached - var/cachedate = "" - /// The real time it was cached - var/cacherealtime = 0 - -/datum/ipintel/New() - cachedate = SQLtime() - cacherealtime = world.realtime - -/datum/ipintel/proc/is_valid() - . = FALSE - if(intel < 0) - return - if(intel <= CONFIG_GET(number/bad_rating)) - if(world.realtime < cacherealtime + (CONFIG_GET(number/hours_save_good) HOURS)) - return TRUE - else - if(world.realtime < cacherealtime + (CONFIG_GET(number/hours_save_bad) HOURS)) - return TRUE - - - -/** - * Get IP intel - * - * Performs a lookup of the rating for an IP provided - * - * Arguments: - * * ip - The IP to lookup - * * bypasscache - Do we want to bypass the DB cache? - * * updatecache - Do we want to update the DB cache? - */ -/datum/ipintel_manager/proc/get_ip_intel(ip, bypasscache = FALSE, updatecache = TRUE) - var/datum/ipintel/res = new() - res.ip = ip - . = res - if(!ip || !CONFIG_GET(string/contact_email) || !CONFIG_GET(flag/ipintel_enabled)) - return - if(!bypasscache) - var/datum/ipintel/cachedintel = cache[ip] - if(cachedintel && cachedintel.is_valid()) - cachedintel.cache = TRUE - return cachedintel - - if(SSdbcore.IsConnected()) - var/datum/db_query/query_get_ip_intel = SSdbcore.NewQuery({" - SELECT date, intel, TIMESTAMPDIFF(MINUTE,date,NOW()) - FROM ipintel - WHERE - ip = INET_ATON(:ip) - AND (( - intel < :rating_bad - AND - date + INTERVAL :save_good HOUR > NOW() - ) OR ( - intel >= :rating_bad - AND - date + INTERVAL :save_bad HOUR > NOW() - )) - "}, list( - "ip" = ip, - "rating_bad" = CONFIG_GET(number/bad_rating), - "save_good" = CONFIG_GET(number/hours_save_good), - "save_bad" = CONFIG_GET(number/hours_save_bad), - )) - if(!query_get_ip_intel.warn_execute()) - qdel(query_get_ip_intel) - return - if(query_get_ip_intel.NextRow()) - res.cache = TRUE - res.cachedate = query_get_ip_intel.item[1] - res.intel = text2num(query_get_ip_intel.item[2]) - res.cacheminutesago = text2num(query_get_ip_intel.item[3]) - res.cacherealtime = world.realtime - (text2num(query_get_ip_intel.item[3])*10*60) - cache[ip] = res - qdel(query_get_ip_intel) - return - qdel(query_get_ip_intel) - res.intel = ip_intel_query(ip) - if(updatecache && res.intel >= 0) - cache[ip] = res - if(SSdbcore.IsConnected()) - var/datum/db_query/query_add_ip_intel = SSdbcore.NewQuery({" - INSERT INTO ipintel (ip, intel) VALUES (INET_ATON(:ip), :intel) - ON DUPLICATE KEY UPDATE intel = VALUES(intel), date = NOW()"}, - list( - "ip" = ip, - "intel" = res.intel - ) - ) - query_add_ip_intel.warn_execute() - qdel(query_add_ip_intel) - - - -/** - * Performs the remote IPintel lookup - * - * - * - * Arguments: - * * ip - The IP to lookup - * * retried - Was this attempt retried? - */ -/datum/ipintel_manager/proc/ip_intel_query(ip, retried = FALSE) - . = -1 //default - if(!ip) - return - if(throttle > world.timeofday) - return - - // Do not refactor this to use SShttp, because that requires the subsystem to be firing for requests to be made, and this will be triggered before the MC has finished loading - var/list/http[] = HTTPGet("http://[CONFIG_GET(string/ipintel_domain)]/check.php?ip=[ip]&contact=[CONFIG_GET(string/contact_email)]&format=json&flags=b") - - if(http) - var/status = text2num(http["STATUS"]) - - if(status == 200) - var/response = json_decode(http["CONTENT"]) - if(response) - if(response["status"] == "success") - var/intelnum = text2num(response["result"]) - if(isnum(intelnum)) - return text2num(response["result"]) - else - ipintel_handle_error("Bad intel from server: [response["result"]].", ip, retried) - if(!retried) - sleep(25) - return .(ip, 1) - else - ipintel_handle_error("Bad response from server: [response["status"]].", ip, retried) - if(!retried) - sleep(25) - return .(ip, 1) - - else if(status == 429) - ipintel_handle_error("Error #429: We have exceeded the rate limit.", ip, 1) - return - else - ipintel_handle_error("Unknown status code: [status].", ip, retried) - if(!retried) - sleep(25) - return .(ip, 1) - else - ipintel_handle_error("Unable to connect to API.", ip, retried) - if(!retried) - sleep(25) - return .(ip, 1) - - - -/** - * Error handler - * - * Handles an IP intel error, also throttling the susbystem if required - * - * Arguments: - * * error - The error description - * * ip - The IP that was tried - * * retried - Was this on a retried attempt - */ -/datum/ipintel_manager/proc/ipintel_handle_error(error, ip, retried) - if(retried) - errors++ - error += " Could not check [ip]. Disabling IPINTEL for [errors] minute[(errors == 1 ? "" : "s")]" - throttle = world.timeofday + (2 * errors MINUTES) - else - error += " Attempting retry on [ip]." - log_ipintel(error) - - - -/** - * Logs an IPintel error - * - * Pretty self explanatory. Logs errors regarding ipintel. - * - * Arguments: - * * text - Argument 1 - */ -/datum/ipintel_manager/proc/log_ipintel(text) - log_game("IPINTEL: [text]") - log_access("IPINTEL: [text]") - - - -/** - * IPIntel Ban Checker - * - * Checks if a user is banned due to IPintel. It will check configuration, DB, whitelist checks, and more - * - * Arguments: - * * t_ckey - The ckey to check - * * t_ip - The IP to check - */ -/datum/ipintel_manager/proc/ipintel_is_banned(t_ckey, t_ip) - if(!CONFIG_GET(string/contact_email)) - return FALSE - if(!CONFIG_GET(flag/ipintel_enabled)) - return FALSE - if(!CONFIG_GET(flag/whitelist_mode)) - return FALSE - if(!SSdbcore.IsConnected()) - return FALSE - if(!ipintel_badip_check(t_ip)) - return FALSE - if(vpn_whitelist_check(t_ckey)) - return FALSE - return TRUE - - - -/** - * IP Rating Checker - * - * Checks if a provided IP passes the config threshold for denial - * - * Arguments: - * * target_ip - The IP to check - */ -/datum/ipintel_manager/proc/ipintel_badip_check(target_ip) - var/rating_bad = CONFIG_GET(number/bad_rating) - if(!rating_bad) - log_access("ipintel_badip_check reports misconfigured rating_bad directive") - return FALSE - var/valid_hours = CONFIG_GET(number/hours_save_bad) - if(!valid_hours) - log_access("ipintel_badip_check reports misconfigured ipintel_save_bad directive") - return FALSE - var/datum/db_query/query_get_ip_intel = SSdbcore.NewQuery({" - SELECT * FROM ipintel WHERE ip = INET_ATON(:target_ip) - AND intel >= :rating_bad AND (date + INTERVAL :valid_hours HOUR) > NOW()"}, - list( - "target_ip" = target_ip, - "rating_bad" = rating_bad, - "valid_hours" = valid_hours - ) - ) - if(!query_get_ip_intel.warn_execute()) - log_access("ipintel_badip_check reports failed query execution") - qdel(query_get_ip_intel) - return FALSE - if(!query_get_ip_intel.NextRow()) - qdel(query_get_ip_intel) - return FALSE - qdel(query_get_ip_intel) - return TRUE - - - -/** - * VPN whitelist checker - * - * Checks if a ckey is whitelisted to be using a VPN against the DB - * - * Arguments: - * * target_ckey - The ckey to check - */ -/datum/ipintel_manager/proc/vpn_whitelist_check(target_ckey) - if(!CONFIG_GET(flag/whitelist_mode)) - return FALSE - var/datum/db_query/query_whitelist_check = SSdbcore.NewQuery("SELECT * FROM vpn_whitelist WHERE ckey=:ckey", list( - "ckey" = target_ckey - )) - if(!query_whitelist_check.warn_execute()) - qdel(query_whitelist_check) - return FALSE - if(query_whitelist_check.NextRow()) - qdel(query_whitelist_check) - return TRUE // At least one row in the whitelist names their ckey. That means they are whitelisted. - qdel(query_whitelist_check) - return FALSE - - - -/** - * VPN whitelist adder - * - * Adds a ckey to the VPN whitelist. Asks the admin to also provide a link to their request. - * - * Arguments: - * * target_ckey - The ckey to whitelist - */ -/datum/ipintel_manager/proc/vpn_whitelist_add(target_ckey) - var/reason_string = input(usr, "Enter link to the URL of their whitelist request on the forum.","Reason required") as message|null - if(!reason_string) - return FALSE - var/datum/db_query/query_whitelist_add = SSdbcore.NewQuery("INSERT INTO vpn_whitelist (ckey,reason) VALUES (:targetckey, :reason)", list( - "targetckey" = target_ckey, - "reason" = reason_string - )) - if(!query_whitelist_add.warn_execute()) - qdel(query_whitelist_add) - return FALSE - qdel(query_whitelist_add) - return TRUE - - - -/** - * VPN whitelist remover - * - * Removes a ckey from the VPN whitelist. Pretty simple. - * - * Arguments: - * * target_ckey - The ckey to remove - */ -/datum/ipintel_manager/proc/vpn_whitelist_remove(target_ckey) - var/datum/db_query/query_whitelist_remove = SSdbcore.NewQuery("DELETE FROM vpn_whitelist WHERE ckey=:targetckey", list( - "targetckey" = target_ckey - )) - if(!query_whitelist_remove.warn_execute()) - qdel(query_whitelist_remove) - return FALSE - qdel(query_whitelist_remove) - return TRUE - - - -/** - * VPN whitelist panel - * - * Doesnt actually open a panel, this is just a verb to handle the rest of the whitelist operations - * - * Arguments: - * * target_ckey - The ckey to add/remove - */ -/datum/ipintel_manager/proc/vpn_whitelist_panel(target_ckey as text) - if(!check_rights(R_ADMIN)) - return - if(!target_ckey) - return - var/is_already_whitelisted = vpn_whitelist_check(target_ckey) - if(is_already_whitelisted) - var/confirm = alert("[target_ckey] is already whitelisted. Remove them?", "Confirm Removal", "No", "Yes") - if(!confirm || confirm != "Yes") - to_chat(usr, "VPN whitelist alteration cancelled.") - return - else if(vpn_whitelist_remove(target_ckey)) - to_chat(usr, "[target_ckey] was removed from the VPN whitelist.") - else - to_chat(usr, "VPN whitelist unchanged.") - else - if(vpn_whitelist_add(target_ckey)) - to_chat(usr, "[target_ckey] was added to the VPN whitelist.") - else - to_chat(usr, "VPN whitelist unchanged.") - -/datum/admins/proc/vpn_whitelist() - set category = "Admin" - set name = "VPN Ckey Whitelist" - if(!check_rights(R_BAN)) - return - var/key = stripped_input(usr, "Enter ckey to add/remove, or leave blank to cancel:", "VPN Whitelist add/remove", max_length=32) - if(key) - GLOB.ipintel_manager.vpn_whitelist_panel(key) -// BLUEMOON EDIT:END IPINTEL FROM TG diff --git a/code/modules/client/client_procs.dm b/code/modules/client/client_procs.dm index 6c912111628f..ef0edc1a6323 100644 --- a/code/modules/client/client_procs.dm +++ b/code/modules/client/client_procs.dm @@ -458,10 +458,9 @@ GLOBAL_LIST_INIT(blacklisted_builds, list( if (CONFIG_GET(flag/irc_first_connection_alert)) send2tgs_adminless_only("new_byond_user", "[key_name(src)] (IP: [address], ID: [computer_id]) is a new BYOND account [account_age] day[(account_age==1?"":"s")] old, created on [account_join_date].") get_message_output("watchlist entry", ckey) - //check_ip_intel() // BLUEMOON EDIT: IPINTEL FROM TG + check_ip_intel() validate_key_in_db() - check_ip_intel() // BLUEMOON EDIT: IPINTEL FROM TG send_resources() update_clickcatcher() @@ -515,6 +514,7 @@ GLOBAL_LIST_INIT(blacklisted_builds, list( menuitem.Load_checked(src) SSambience.remove_ambience_client(src) + view_size = new(src, getScreenSize(prefs.widescreenpref)) view_size.resetFormat() view_size.setZoomMode() @@ -883,45 +883,6 @@ GLOBAL_LIST_INIT(blacklisted_builds, list( ip_intel = res.intel */ // BLUEMOON EDIT:END IPINTEL FROM TG -// BLUEMOON EDIT:START IPINTEL FROM TG -/client/proc/check_ip_intel() - set waitfor = 0 //we sleep when getting the intel, no need to hold up the client connection while we sleep - if(CONFIG_GET(flag/ipintel_enabled)) - if(CONFIG_GET(number/playtime_ignore_threshold) && CONFIG_GET(flag/use_exp_tracking)) - var/living_hours = text2num(prefs.exp[EXP_TYPE_LIVING]) / 60 - if(living_hours >= CONFIG_GET(number/playtime_ignore_threshold)) - return - - if(is_connecting_from_localhost()) - log_access("check_ip_intel: skip check for player [key_name_admin(src)] connecting from localhost.") - return - - if(GLOB.ipintel_manager.vpn_whitelist_check(ckey)) - log_access("check_ip_intel: skip check for player [key_name_admin(src)] [address] on whitelist.") - return - - var/datum/ipintel/res = GLOB.ipintel_manager.get_ip_intel(address) - ip_intel = res.intel - verify_ip_intel() - -/client/proc/verify_ip_intel() - if(ip_intel >= CONFIG_GET(number/bad_rating)) - var/detailsurl = CONFIG_GET(string/details_url) ? "(IP Info)" : "" - if(CONFIG_GET(flag/whitelist_mode)) - // Do not move this to isBanned(). This may sound weird, but: - // This needs to happen after their account is put into the DB - // This way, admins can then note people - spawn(40) // This is necessary because without it, they won't see the message, and addtimer cannot be used because the timer system may not have initialized yet - message_admins("IPIntel: [key_name_admin(src)] on IP [address] was rejected. [detailsurl]") - var/blockmsg = "Error: proxy/VPN detected. Proxy/VPN use is not allowed here. Deactivate it before you reconnect." - if(CONFIG_GET(string/banappeals)) - blockmsg += "\nIf you are not actually using a proxy/VPN, or have no choice but to use one, request whitelisting at: [CONFIG_GET(string/banappeals)]" - to_chat(src, blockmsg) - qdel(src) - else - message_admins("IPIntel: [key_name_admin(src)] on IP [address] is likely to be using a Proxy/VPN. [detailsurl]") -// BLUEMOON EDIT:END IPINTEL FROM TG - /client/Click(atom/object, atom/location, control, params, ignore_spam = FALSE, extra_info) if(last_click > world.time - world.tick_lag) return diff --git a/config/bluemoon/ipintel.txt b/config/bluemoon/ipintel.txt index 847274bbee4e..4bd48ad54235 100644 --- a/config/bluemoon/ipintel.txt +++ b/config/bluemoon/ipintel.txt @@ -1,21 +1,22 @@ ## IPINTEL: -## This section contains all the information for IPIntel (The Anti VPN system) ## This allows you to detect likely proxies by checking ips against getipintel.net -# Enable or disable IPIntel entirely -#IPINTEL_ENABLED TRUE -# Whitelist mode. If true, people on proxies/VPNs will need whitelisting if they arent past the threshold below. If false, admins are merely warned with no automatic action -#WHITELIST_MODE FALSE -# Threshold to kick people out (0-1 percentage float) -#BAD_RATING 0.98 -# Contact email (required, leaving blank disables this) -#CONTACT_EMAIL "ss13bluemoon@gmail.com" -# How many hours to save good matches for (IPIntel has rate limits) -#HOURS_SAVE_GOOD 72 -# How many hours to save bad matches for (IPIntel has rate limits) -#HOURS_SAVE_BAD 24 -# IPIntel Domain. Do not put http:// in front of it -#IPINTEL_DOMAIN "check.getipintel.net" -## Details URL for more info on an IP (such as ASN). IP is tacked on the end. -#DETAILS_URL "https://iphub.info/?ip=" -# Ignore checking IPs with more hours than the threshold below. Requires EXP tracking to be enabled -#PLAYTIME_IGNORE_THRESHOLD 90 +## Rating to warn at: (0.8 is good, 1 is 100% likely to be a spammer/proxy, 0.8 is 80%, etc) anything equal to or higher then this number triggers an admin warning +#IPINTEL_RATING_BAD 0.8 +## Contact email, (required to use the service, leaving blank or default disables IPINTEL) +#IPINTEL_EMAIL ch@nge.me +## Query base, if you pay for more queries this is what you want to change. +#IPINTEL_BASE check.getipintel.net +## Maximum number of queries in a minute +#IPINTEL_MAX_QUERY_MINUTE 15 +## Maximum number of queries in a day +#IPINTEL_MAX_QUERY_DAY 500 +## Whether clients which cannot be checked due to a rate limit will be denied +#IPINTEL_REJECT_RATE_LIMITED +## Whether clients which are flagged as a VPN will be denied +IPINTEL_REJECT_BAD +## Whether clients which cannot be checked due to an error of some form will be denied +#IPINTEL_REJECT_UNKNOWN +## How long to store results in the cache before they must be retrieved again. IN DAYS. +#IPINTEL_CACHE_LENGTH 7 +## How many minutes of living playtime to be automatically exempt from IPIntel. 0 for never. +#IPINTEL_EXEMPT_PLAYTIME_LIVING 0 diff --git a/modular_bluemoon/phoenix404/code/controllers/configuration/entries/bluemoon_config_entries.dm b/modular_bluemoon/phoenix404/code/controllers/configuration/entries/bluemoon_config_entries.dm new file mode 100644 index 000000000000..3894e3cdf788 --- /dev/null +++ b/modular_bluemoon/phoenix404/code/controllers/configuration/entries/bluemoon_config_entries.dm @@ -0,0 +1,41 @@ +/// Defines whether or not mentors can see ckeys alongside mobnames. +/datum/config_entry/flag/mentors_mobname_only + +/// Defines whether the server uses the legacy mentor system with mentors.txt or the SQL system. +/datum/config_entry/flag/mentor_legacy_system + protection = CONFIG_ENTRY_LOCKED + +/datum/config_entry/string/ipintel_base + default = "check.getipintel.net" + +/datum/config_entry/string/ipintel_email + +/datum/config_entry/string/ipintel_email/ValidateAndSet(str_val) + return str_val != "ch@nge.me" && (!length(str_val) || findtext(str_val, "@")) && ..() + +/datum/config_entry/number/ipintel_rating_bad + default = 1 + integer = FALSE + min_val = 0 + max_val = 1 + +/datum/config_entry/flag/ipintel_reject_rate_limited + default = FALSE + +/datum/config_entry/flag/ipintel_reject_bad + default = FALSE + +/datum/config_entry/flag/ipintel_reject_unknown + default = FALSE + +/datum/config_entry/number/ipintel_rate_minute + default = 15 + min_val = 0 + +/datum/config_entry/number/ipintel_cache_length + default = 7 + min_val = 0 + +/datum/config_entry/number/ipintel_exempt_playtime_living + default = 5 + min_val = 0 diff --git a/modular_bluemoon/phoenix404/code/controllers/subsystem/ipintel.dm b/modular_bluemoon/phoenix404/code/controllers/subsystem/ipintel.dm new file mode 100644 index 000000000000..236e781168d0 --- /dev/null +++ b/modular_bluemoon/phoenix404/code/controllers/subsystem/ipintel.dm @@ -0,0 +1,301 @@ +SUBSYSTEM_DEF(ipintel) + name = "XKeyScore" + init_order = INIT_ORDER_XKEYSCORE + flags = SS_NO_INIT|SS_NO_FIRE + /// The threshold for probability to be considered a VPN and/or bad IP + var/probability_threshold + + /// Cache for previously queried IP addresses and those stored in the database + var/list/datum/ip_intel/cached_queries = list() + /// The store for rate limiting + var/rate_limit_minute + +/// The ip intel for a given address +/datum/ip_intel + /// If this intel was just queried, the status of the query + var/query_status + var/result + var/address + var/date + +/datum/controller/subsystem/ipintel/OnConfigLoad() + var/list/fail_messages = list() + + var/contact_email = CONFIG_GET(string/ipintel_email) + + if(!length(contact_email)) + fail_messages += "No contact email" + + if(!findtext(contact_email, "@")) + fail_messages += "Invalid contact email" + + if(!length(CONFIG_GET(string/ipintel_base))) + fail_messages += "Invalid query base" + + if (!CONFIG_GET(flag/sql_enabled)) + fail_messages += "The database is not enabled" + + if(length(fail_messages)) + message_admins("IPIntel: Initialization failed check logs!") + WRITE_LOG(GLOB.world_suspicious_login_log, "IPIntel is not enabled because the configs are not valid. [fail_messages]") + +/datum/controller/subsystem/ipintel/stat_entry(msg) + return "[..()] | M: [CONFIG_GET(number/ipintel_rate_minute) - rate_limit_minute]" + + +/datum/controller/subsystem/ipintel/proc/is_enabled() + return length(CONFIG_GET(string/ipintel_email)) && length(CONFIG_GET(string/ipintel_base)) && CONFIG_GET(flag/sql_enabled) + +/datum/controller/subsystem/ipintel/proc/get_address_intel_state(address, probability_override) + if (!is_enabled()) + return IPINTEL_GOOD_IP + var/datum/ip_intel/intel = query_address(address) + if(isnull(intel)) + stack_trace("query_address did not return an ip intel response") + return IPINTEL_UNKNOWN_INTERNAL_ERROR + + if(istext(intel)) + return intel + + if(!(intel.query_status in list("success", "cached"))) + return IPINTEL_UNKNOWN_QUERY_ERROR + var/check_probability = probability_override || CONFIG_GET(number/ipintel_rating_bad) + if(intel.result >= check_probability) + return IPINTEL_BAD_IP + return IPINTEL_GOOD_IP + +/datum/controller/subsystem/ipintel/proc/is_rate_limited() + var/static/minute_key + var/expected_minute_key = floor(REALTIMEOFDAY / 1 MINUTES) + + if(minute_key != expected_minute_key) + minute_key = expected_minute_key + rate_limit_minute = 0 + + if(rate_limit_minute >= CONFIG_GET(number/ipintel_rate_minute)) + return IPINTEL_RATE_LIMITED_MINUTE + return FALSE + +/datum/controller/subsystem/ipintel/proc/query_address(address, allow_cached = TRUE) + if (!is_enabled()) + return + if(allow_cached && fetch_cached_ip_intel(address)) + return cached_queries[address] + var/is_rate_limited = is_rate_limited() + if(is_rate_limited) + return is_rate_limited + rate_limit_minute += 1 + + var/query_base = "https://[CONFIG_GET(string/ipintel_base)]/check.php?ip=" + var/query = "[query_base][address]&contact=[CONFIG_GET(string/ipintel_email)]&flags=b&format=json" + + var/datum/http_request/request = new + request.prepare(RUSTG_HTTP_METHOD_GET, query) + request.execute_blocking() + var/datum/http_response/response = request.into_response() + var/list/data = json_decode(response.body) + // Log the response + WRITE_LOG(GLOB.query_debug_log, "ip check response body [data]") + + var/datum/ip_intel/intel = new + intel.query_status = data["status"] + if(intel.query_status != "success") + return intel + intel.result = data["result"] + if(istext(intel.result)) + intel.result = text2num(intel.result) + intel.date = ISOtime() + intel.address = address + cached_queries[address] = intel + add_intel_to_database(intel) + return intel + +/datum/controller/subsystem/ipintel/proc/add_intel_to_database(datum/ip_intel/intel) + set waitfor = FALSE //no need to make the client connection wait for this step. + if (!SSdbcore.Connect()) + return + var/datum/db_query/query = SSdbcore.NewQuery( + "INSERT INTO [format_table_name("ipintel")] ( \ + ip, \ + intel \ + ) VALUES ( \ + INET_ATON(:address), \ + :result \ + )", list( + "address" = intel.address, + "result" = intel.result, + ) + ) + if(!query.warn_execute()) + qdel(query) + return + qdel(query) + +/datum/controller/subsystem/ipintel/proc/fetch_cached_ip_intel(address) + if (!SSdbcore.Connect()) + return + var/ipintel_cache_length = CONFIG_GET(number/ipintel_cache_length) + var/date_restrictor = "" + var/sql_args = list("address" = address) + if(ipintel_cache_length > 1) + date_restrictor = " AND date > DATE_SUB(NOW(), INTERVAL :ipintel_cache_length DAY)" + sql_args["ipintel_cache_length"] = ipintel_cache_length + var/datum/db_query/query = SSdbcore.NewQuery( + "SELECT * FROM [format_table_name("ipintel")] WHERE ip = INET_ATON(:address)[date_restrictor]", + sql_args + ) + if(!query.warn_execute()) + qdel(query) + return + + + query.NextRow() + var/list/data = query.item + qdel(query) + if(isnull(data)) + return null + + var/datum/ip_intel/intel = new + intel.query_status = "cached" + intel.result = data["intel"] + if(istext(intel.result)) + intel.result = text2num(intel.result) + intel.date = data["date"] + intel.address = address + return TRUE + +/datum/controller/subsystem/ipintel/proc/is_exempt(client/player) + if(player.holder || GLOB.deadmins[player.ckey]) + return TRUE + var/exempt_living_playtime = CONFIG_GET(number/ipintel_exempt_playtime_living) + if(exempt_living_playtime > 0) + var/list/play_records = player.prefs.exp + if (!play_records.len) + player.set_exp_from_db() + play_records = player.prefs.exp + if(length(play_records) && play_records[EXP_TYPE_LIVING] > exempt_living_playtime) + return TRUE + return FALSE + +/datum/controller/subsystem/ipintel/proc/is_whitelisted(ckey) + var/datum/db_query/query = SSdbcore.NewQuery( + "SELECT * FROM [format_table_name("ipintel_whitelist")] WHERE ckey = :ckey", list( + "ckey" = ckey + ) + ) + if(!query.warn_execute()) + qdel(query) + return + query.NextRow() + . = !!query.item // if they have a row, they are whitelisted + qdel(query) + +/client/proc/ipintel_allow(ckey as text) + set category = "Debug" + set name = "Whitelist Player VPN" + set desc = "Allow a player to connect even if they are using a VPN." + + if (!SSipintel.is_enabled()) + to_chat(usr, "The ipintel system is not currently enabled but you can still edit the whitelists") + if(SSipintel.is_whitelisted(ckey)) + to_chat(usr, "Player is already whitelisted.") + return + + var/datum/db_query/query = SSdbcore.NewQuery( + "INSERT INTO [format_table_name("ipintel_whitelist")] ( \ + ckey, \ + admin_ckey \ + ) VALUES ( \ + :ckey, \ + :admin_ckey \ + )", list( + "ckey" = ckey, + "admin_ckey" = usr.ckey, + ) + ) + if(!query.warn_execute()) + qdel(query) + return + + qdel(query) + message_admins("IPINTEL: [key_name_admin(usr)] has whitelisted '[ckey]'") + +/client/proc/ipintel_revoke(ckey as text) + set category = "Debug" + set name = "Revoke Player VPN Whitelist" + set desc = "Revoke a player's VPN whitelist." + + if (!SSipintel.is_enabled()) + to_chat(usr, "The ipintel system is not currently enabled but you can still edit the whitelists") + if(!SSipintel.is_whitelisted(ckey)) + to_chat(usr, "Player is not whitelisted.") + return + var/datum/db_query/query = SSdbcore.NewQuery( + "DELETE FROM [format_table_name("ipintel_whitelist")] WHERE ckey = :ckey", list( + "ckey" = ckey + ) + ) + if(!query.warn_execute()) + qdel(query) + return + + message_admins("IPINTEL: [key_name_admin(usr)] has revoked the VPN whitelist for '[ckey]'") + +/client/proc/check_ip_intel() + if (!SSipintel.is_enabled()) + return + if(SSipintel.is_exempt(src) || SSipintel.is_whitelisted(ckey)) + return + + var/intel_state = SSipintel.get_address_intel_state(address) + var/reject_bad_intel = CONFIG_GET(flag/ipintel_reject_bad) + var/reject_unknown_intel = CONFIG_GET(flag/ipintel_reject_unknown) + var/reject_rate_limited = CONFIG_GET(flag/ipintel_reject_rate_limited) + + var/connection_rejected = FALSE + var/datum/ip_intel/intel = SSipintel.cached_queries[address] + switch(intel_state) + if(IPINTEL_BAD_IP) + log_access("IPINTEL: [ckey] was flagged as a VPN with [intel.result * 100]% likelihood.") + if(reject_bad_intel) + to_chat_immediate(src, span_boldnotice("Your connection has been detected as a VPN.")) + connection_rejected = TRUE + else + message_admins("IPINTEL: [key_name_admin(src)] has been flagged as a VPN with [intel.result * 100]% likelihood.") + + if(IPINTEL_RATE_LIMITED_DAY, IPINTEL_RATE_LIMITED_MINUTE) + log_access("IPINTEL: [ckey] was unable to be checked due to the rate limit.") + if(reject_rate_limited) + to_chat_immediate(src, span_boldnotice("New connections are not being allowed at this time.")) + connection_rejected = TRUE + else + message_admins("IPINTEL: [key_name_admin(src)] was unable to be checked due to rate limiting.") + + if(IPINTEL_UNKNOWN_INTERNAL_ERROR, IPINTEL_UNKNOWN_QUERY_ERROR) + log_access("IPINTEL: [ckey] unable to be checked due to an error.") + if(reject_unknown_intel) + to_chat_immediate(src, span_boldnotice("Your connection cannot be processed at this time.")) + connection_rejected = TRUE + else + message_admins("IPINTEL: [key_name_admin(src)] was unable to be checked due to an error.") + + if(!connection_rejected) + return + + var/list/contact_where = list() + var/forum_url = CONFIG_GET(string/discordurl) + if(forum_url) + contact_where += list("Forums") + var/appeal_url = CONFIG_GET(string/banappeals) + if(appeal_url) + contact_where += list("Ban Appeals") + + var/message_string = "Your connection has been rejected at this time. If you believe this is in error or have any questions please contact an admin" + if(length(contact_where)) + message_string += " at [english_list(contact_where)]" + else + message_string += " somehow." + message_string += "." + + to_chat_immediate(src, span_userdanger(message_string)) + qdel(src) diff --git a/modular_bluemoon/phoenix404/modules/client/client_procs.dm b/modular_bluemoon/phoenix404/modules/client/client_procs.dm deleted file mode 100644 index 9469799fc647..000000000000 --- a/modular_bluemoon/phoenix404/modules/client/client_procs.dm +++ /dev/null @@ -1,5 +0,0 @@ -/client/proc/is_connecting_from_localhost() - var/static/list/localhost_addresses = list("127.0.0.1", "::1") - if((!address && !world.port) || (address in localhost_addresses)) - return TRUE - return FALSE diff --git a/tgstation.dme b/tgstation.dme index 21390adee8c5..bfe892538525 100644 --- a/tgstation.dme +++ b/tgstation.dme @@ -343,7 +343,6 @@ #include "code\__SPLURTCODE\DEFINES\arousal\genitals.dm" #include "code\__SPLURTCODE\DEFINES\dcs\signals.dm" #include "code\_BLUEMOONCODE\_HELPERS\time.dm" -#include "code\_BLUEMOONCODE\_HELPERS\unsorted.dm" #include "code\_globalvars\admin.dm" #include "code\_globalvars\bitfields.dm" #include "code\_globalvars\configuration.dm" @@ -443,7 +442,6 @@ #include "code\controllers\subsystem.dm" #include "code\controllers\configuration\config_entry.dm" #include "code\controllers\configuration\configuration.dm" -#include "code\controllers\configuration\bluemoon_entries\general.dm" #include "code\controllers\configuration\entries\admin.dm" #include "code\controllers\configuration\entries\alert.dm" #include "code\controllers\configuration\entries\antag_rep.dm" @@ -4548,8 +4546,9 @@ #include "modular_bluemoon\phoenix404\code\entries.dm" #include "modular_bluemoon\phoenix404\code\_HELPERS\admin.dm" #include "modular_bluemoon\phoenix404\code\_HELPERS\roundend.dm" +#include "modular_bluemoon\phoenix404\code\controllers\configuration\entries\bluemoon_config_entries.dm" +#include "modular_bluemoon\phoenix404\code\controllers\subsystem\ipintel.dm" #include "modular_bluemoon\phoenix404\modules\admin\code\player_ranks.dm" -#include "modular_bluemoon\phoenix404\modules\client\client_procs.dm" #include "modular_bluemoon\phoenix404\modules\mentor\code\mentor.dm" #include "modular_bluemoon\phoenix404\modules\player_ranks\code\player_rank_controller\_player_rank_controller.dm" #include "modular_bluemoon\phoenix404\modules\player_ranks\code\player_rank_controller\mentor_controller.dm" diff --git a/tgui/packages/tgui/interfaces/PlayerPanel2.js b/tgui/packages/tgui/interfaces/PlayerPanel2.js index 5a3824d68bfb..178452764f86 100644 --- a/tgui/packages/tgui/interfaces/PlayerPanel2.js +++ b/tgui/packages/tgui/interfaces/PlayerPanel2.js @@ -724,7 +724,6 @@ const PunishmentActions = (props, context) => { {data_player_join_date} {data_byond_version} {active_role_ban_count} - {active_role_ban_count}