Moves various things to the new DBCore (#21544)

```
- server: "Changed the synthsprites to use the new DBCore"
- server: "Changed the MalfAI to use the new DBCore"
- server: "Changed the Alien Whitelists to use the new DBCore"
- server: "Changed the Requests Console to use the new DBCore"
- server: "Changed the Contracts Uplink to use the new DBCore"
- server: "Changed the Admin Ranks to use the new DBCore"
- server: "Changed the Job Bans to use the new DBCore"
- server: "Changed the Tickets to use the new DBCore"
- server: "Changed the Create Command Report to use the new DBCore"
- server: "Changed the WebInterface interconnect to use the new DBCore"
- server: "Changed the CCIA Recorder to use the new DBCore"
- server: "Changed the IPCTags to use the new DBCore"
- server: "Changed the Main Menu Poll-Check to use the new DBCore"
- server: "Changed the Client-Procs to use the new DBCore"
```

---------

Co-authored-by: Werner <Arrow768@users.noreply.github.com>
This commit is contained in:
Arrow768
2026-04-13 22:45:29 +02:00
committed by GitHub
co-authored by Werner
parent 17721ddae4
commit 03f313a19d
33 changed files with 717 additions and 384 deletions
+4 -3
View File
@@ -83,9 +83,10 @@
#define SS_PAUSING 5 /// in the middle of pausing
// Subsystem init stages
#define INITSTAGE_EARLY 1 //! Early init stuff that doesn't need to wait for mapload
#define INITSTAGE_MAIN 2 //! Main init stage
#define INITSTAGE_MAX 2 //! Highest initstage.
#define INITSTAGE_FIRST 1 //! Stuff that needs to start firing before everything else (DBCore)
#define INITSTAGE_EARLY 2 //! Early init stuff that doesn't need to wait for mapload
#define INITSTAGE_MAIN 3 //! Main init stage
#define INITSTAGE_MAX 3 //! Highest initstage.
#define SUBSYSTEM_DEF(X) GLOBAL_REAL(SS##X, /datum/controller/subsystem/##X);\
/datum/controller/subsystem/##X/New(){\
@@ -0,0 +1,4 @@
/// Logging for the IPIntel (XKeyScore) subsystem
/proc/log_subsystem_ipintel(text)
if (GLOB.config?.logsettings["log_subsystems_ipintel"])
WRITE_LOG(GLOB.config.logfiles["world_subsystems_ipintel"], "IPINTEL: [text]")
+5 -2
View File
@@ -5,6 +5,7 @@ SUBSYSTEM_DEF(dbcore)
wait = 10 // Not seconds because we're running on SS_TICKER
runlevels = RUNLEVEL_LOBBY|RUNLEVELS_DEFAULT
init_order = INIT_ORDER_DBCORE
init_stage = INITSTAGE_FIRST
priority = FIRE_PRIORITY_DATABASE
@@ -308,6 +309,8 @@ SUBSYSTEM_DEF(dbcore)
/// Check if we have established a DB Connection
/datum/controller/subsystem/dbcore/proc/IsConnected()
PRIVATE_PROC(TRUE)
if(!GLOB.config.sql_enabled)
return FALSE
if (!connection)
@@ -489,7 +492,7 @@ SUBSYSTEM_DEF(dbcore)
if(status == DB_QUERY_STARTED)
CRASH("Attempted to start a new query while waiting on the old one")
if(!SSdbcore.IsConnected())
if(!SSdbcore.Connect())
last_error = "No connection!"
return FALSE
@@ -528,7 +531,7 @@ SUBSYSTEM_DEF(dbcore)
if(status == DB_QUERY_STARTED)
CRASH("Attempted to start a new query while waiting on the old one")
if(!SSdbcore.IsConnected())
if(!SSdbcore.Connect())
last_error = "No connection!"
return FALSE
+1 -1
View File
@@ -28,7 +28,7 @@ SUBSYSTEM_DEF(garbage)
flags = SS_POST_FIRE_TIMING|SS_BACKGROUND|SS_NO_INIT
runlevels = RUNLEVELS_DEFAULT | RUNLEVEL_LOBBY
init_order = INIT_ORDER_GARBAGE
init_stage = INITSTAGE_EARLY
init_stage = INITSTAGE_FIRST
var/list/collection_timeout = list(GC_FILTER_QUEUE, GC_CHECK_QUEUE, GC_DEL_QUEUE) // deciseconds to wait before moving something up in the queue to the next level
+232 -3
View File
@@ -2,13 +2,242 @@ SUBSYSTEM_DEF(ipintel)
name = "XKeyScore"
init_order = INIT_ORDER_MISC_FIRST
flags = SS_NO_FIRE
var/enabled = FALSE //disable at round start to avoid checking reconnects
/// Whether IPIntel lookups are currently enabled. Disabled until Initialize() to avoid checking reconnects at roundstart.
var/enabled = FALSE
/// world.timeofday value before which all API requests are suppressed due to prior rate-limit or connection errors
var/throttle = 0
/// Number of consecutive API failures; used to scale the throttle window
var/errors = 0
/// In-memory cache of recent [/datum/ipintel] results, keyed by IP address string
var/list/cache = list()
/datum/controller/subsystem/ipintel/Initialize()
enabled = TRUE
return SS_INIT_SUCCESS
/**
* Looks up the IPIntel score for the given IP address.
*
* Called from [/client/proc/InitClient] during client connection to check
* whether the connecting IP is likely a proxy or VPN.
*
* Checks sources in priority order:
* 1. In-memory cache ([/datum/controller/subsystem/ipintel/var/cache])
* 2. Database cache (`ss13_ipintel` table)
* 3. Live IPIntel API request via [/datum/controller/subsystem/ipintel/proc/query]
*
* The result is always delivered via `on_complete`, which is called with a [/datum/ipintel]
* as its sole argument. For cache hits the callback is invoked before this proc returns;
* for live requests it is invoked asynchronously once the HTTP response arrives.
*
* The return value is set to the [/datum/ipintel] datum immediately, but its `intel` field
* is only guaranteed to be populated by the time `on_complete` fires.
*
* Arguments:
* * ip - The IP address string to look up
* * bypasscache - If TRUE, skips both memory and database cache and always queries the API
* * updatecache - If TRUE, writes a successful live result back to memory and database cache
* * on_complete - Callback invoked with the final [/datum/ipintel] datum when the result is ready
*/
/datum/controller/subsystem/ipintel/proc/get_ip_intel(ip, bypasscache = FALSE, updatecache = TRUE, datum/callback/on_complete)
var/datum/ipintel/res = new()
res.ip = ip
. = res
if (!ip || !GLOB.config.ipintel_email || !enabled)
if (on_complete)
on_complete.Invoke(res)
return
if (!bypasscache)
var/datum/ipintel/cachedintel = cache[ip]
if (cachedintel && cachedintel.is_valid())
cachedintel.cache = TRUE
if (on_complete)
on_complete.Invoke(cachedintel)
return cachedintel
var/datum/db_query/query_get_ip_intel = SSdbcore.NewQuery(
{"SELECT date, intel, TIMESTAMPDIFF(MINUTE, date, NOW())
FROM ss13_ipintel
WHERE
ip = INET6_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" = GLOB.config.ipintel_rating_bad,
"save_good" = GLOB.config.ipintel_save_good,
"save_bad" = GLOB.config.ipintel_save_bad,
)
)
if (!query_get_ip_intel.Execute())
qdel(query_get_ip_intel)
if (on_complete)
on_complete.Invoke(res)
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)
if (on_complete)
on_complete.Invoke(res)
return
qdel(query_get_ip_intel)
query(ip, CALLBACK(src, PROC_REF(on_query_complete), ip, res, updatecache, on_complete))
/**
* Internal callback for [/datum/controller/subsystem/ipintel/proc/get_ip_intel] — invoked by
* [/datum/controller/subsystem/ipintel/proc/query] once the live API result is available.
*
* Writes the intel score into `res`, updates memory and database caches when appropriate,
* then invokes the outer `on_complete` callback.
*
* Arguments:
* * ip - The IP address that was looked up
* * res - The [/datum/ipintel] datum being populated
* * updatecache - Whether to persist the result to memory and database cache
* * on_complete - The callback originally passed to [/datum/controller/subsystem/ipintel/proc/get_ip_intel]
* * intel - The raw intel score returned by [/datum/controller/subsystem/ipintel/proc/query], or -1 on error
*/
/datum/controller/subsystem/ipintel/proc/on_query_complete(ip, datum/ipintel/res, updatecache, datum/callback/on_complete, intel)
PRIVATE_PROC(TRUE)
res.intel = intel
if (updatecache && res.intel >= 0)
cache[ip] = res
var/datum/db_query/query_add_ip_intel = SSdbcore.NewQuery(
"INSERT INTO ss13_ipintel (ip, intel) VALUES (INET6_ATON(:ip), :intel) ON DUPLICATE KEY UPDATE intel = VALUES(intel), date = NOW()",
list("ip" = ip, "intel" = res.intel)
)
query_add_ip_intel.SetSuccessCallback(CALLBACK(GLOBAL_PROC, /proc/qdel))
query_add_ip_intel.SetFailCallback(CALLBACK(GLOBAL_PROC, /proc/qdel))
query_add_ip_intel.ExecuteNoSleep(TRUE) // Allow to run while we still load the game
if (on_complete)
on_complete.Invoke(res)
/**
* Submits an async IPIntel API request for the given IP address via [/datum/controller/subsystem/http].
*
* The result is delivered to `on_complete` as a raw intel score (0.01.0), or -1 on any
* failure. Does nothing and invokes `on_complete(-1)` if IPIntel is disabled or currently
* throttled due to prior errors.
*
* Use [/datum/controller/subsystem/ipintel/proc/get_ip_intel] instead of calling this directly;
* it handles cache lookup and result storage around this proc.
*
* Arguments:
* * ip - The IP address to query
* * on_complete - Callback invoked with the intel score (num) when the response arrives
* * retry - TRUE if this is an automatic retry after a transient failure
*/
/datum/controller/subsystem/ipintel/proc/query(ip, datum/callback/on_complete, retry = FALSE)
PRIVATE_PROC(TRUE)
if (!ip || !enabled)
if (on_complete)
on_complete.Invoke(-1)
return
if (throttle > world.timeofday)
if (on_complete)
on_complete.Invoke(-1)
return
var/url = "https://[GLOB.config.ipintel_domain]/check.php?ip=[ip]&contact=[GLOB.config.ipintel_email]&format=json&flags=f"
SShttp.create_async_request(
RUSTG_HTTP_METHOD_GET,
url,
"",
null,
CALLBACK(src, PROC_REF(process_response), ip, on_complete, retry)
)
/**
* SShttp callback for [/datum/controller/subsystem/ipintel/proc/query] — processes the HTTP
* response from the IPIntel API.
*
* On success, parses the JSON body and invokes `on_complete` with the numeric result.
* On transient errors (connection failure, unexpected status, bad JSON) the request is
* retried once after a brief delay. On a second failure, or on a hard 429 rate-limit
* response, `on_complete` is invoked with -1.
*
* Arguments:
* * ip - The IP address that was queried
* * on_complete - Callback to invoke with the final intel score or -1
* * retry - TRUE if this invocation is already a retry
* * http - The [/datum/http_response] returned by SShttp
*/
/datum/controller/subsystem/ipintel/proc/process_response(ip, datum/callback/on_complete, retry, datum/http_response/http)
PRIVATE_PROC(TRUE)
if (http.errored)
handle_error("Unable to connect to API.", ip, retry)
if (!retry)
sleep(25)
query(ip, on_complete, TRUE)
else
if (on_complete)
on_complete.Invoke(-1)
return
if (http.status_code == 200)
var/response = json_decode(http.body)
if (response)
if (response["status"] == "success")
var/intelnum = text2num(response["result"])
if (isnum(intelnum))
if (on_complete)
on_complete.Invoke(intelnum)
return
else
handle_error("Bad intel from server: [response["result"]].", ip, retry)
else
handle_error("Bad response from server: [response["status"]].", ip, retry)
if (!retry)
sleep(25)
query(ip, on_complete, TRUE)
else
if (on_complete)
on_complete.Invoke(-1)
else if (http.status_code == 429)
handle_error("Status Code 429: We have exceeded the rate limit.", ip, TRUE)
if (on_complete)
on_complete.Invoke(-1)
else
handle_error("Unknown status code: [http.status_code].", ip, retry)
if (!retry)
sleep(25)
query(ip, on_complete, TRUE)
else
if (on_complete)
on_complete.Invoke(-1)
/**
* Logs an IPIntel error and, on a final retry failure, increments [/datum/controller/subsystem/ipintel/var/errors]
* and sets [/datum/controller/subsystem/ipintel/var/throttle] to temporarily suppress further API calls.
*
* The throttle duration grows linearly with the error count:
* each additional failure adds another 12-second window (10 ticks × 120 × error count).
* All errors are written via [/proc/log_subsystem_ipintel].
*
* Arguments:
* * error - Base error message to log
* * ip - The IP address that triggered the error
* * retry - TRUE if this is a final failure (no further retry will be attempted)
*/
/datum/controller/subsystem/ipintel/proc/handle_error(error, ip, retry)
PRIVATE_PROC(TRUE)
if (retry)
errors++
error += " Could not check [ip]. Disabling IPINTEL for [errors] minute\s"
throttle = world.timeofday + (10 * 120 * errors)
else
error += " Attempting retry on [ip]."
log_subsystem_ipintel(error)
+3 -21
View File
@@ -15,7 +15,6 @@ GLOBAL_DATUM_INIT(revdata, /datum/getrev, new())
var/date
var/showinfo
var/list/datum/tgs_revision_information/test_merge/test_merges
var/greeting_info
/datum/getrev/New()
var/list/head_branch = file2list(".git/HEAD", "\n")
@@ -56,6 +55,9 @@ GLOBAL_DATUM_INIT(revdata, /datum/getrev, new())
to_chat(src, "<b>Current Map:</b> [SSatlas.current_map.full_name]")
if(GLOB.revdata.test_merges.len)
to_chat(src, GLOB.revdata.testmerge_overview())
/datum/getrev/proc/testmerge_overview()
if (!test_merges.len)
return
@@ -69,24 +71,6 @@ GLOBAL_DATUM_INIT(revdata, /datum/getrev, new())
return out.Join()
/datum/getrev/proc/generate_greeting_info()
if (!test_merges.len)
greeting_info = {"<div class="alert alert-info">
There are currently no test merges loaded onto the server.
</div>"}
return
var/list/out = list("<p>There are currently [test_merges.len] PRs being tested live.</p>",
{"<table class="table table-hover">"}
)
for (var/TM in test_merges)
out += testmerge_long_oveview(TM)
out += "</table>"
greeting_info = out.Join()
/datum/getrev/proc/testmerge_initialize()
var/datum/tgs_api/api = TGS_READ_GLOBAL(tgs)
@@ -98,8 +82,6 @@ GLOBAL_DATUM_INIT(revdata, /datum/getrev, new())
LOG_DEBUG("GETREV: No TGS API found.")
test_merges = list()
generate_greeting_info()
/datum/getrev/proc/testmerge_short_overview(datum/tgs_revision_information/test_merge/tm)
. = list()
+3 -2
View File
@@ -53,12 +53,12 @@ paiicon is the pai icon sprite name
LOG_DEBUG("Synthsprites: SQL Disabled - Falling back to JSON")
loadsynths_from_json()
return
if(!establish_db_connection(GLOB.dbcon))
if(!SSdbcore.Connect())
LOG_DEBUG("Synthsprites: SQL ERROR - Failed to connect. - Falling back to JSON")
loadsynths_from_json()
return
var/DBQuery/customsynthsprites = GLOB.dbcon.NewQuery("SELECT synthname, synthckey, synthicon, aichassisicon, aiholoicon, paiicon FROM ss13_customsynths ORDER BY synthckey ASC")
var/datum/db_query/customsynthsprites = SSdbcore.NewQuery("SELECT synthname, synthckey, synthicon, aichassisicon, aiholoicon, paiicon FROM ss13_customsynths ORDER BY synthckey ASC")
customsynthsprites.Execute()
while(customsynthsprites.NextRow())
CHECK_TICK
@@ -71,3 +71,4 @@ paiicon is the pai icon sprite name
synth.aiholoicon = customsynthsprites.item[5]
synth.paiicon = customsynthsprites.item[6]
robot_custom_icons[synth.synthname] = synth
qdel(customsynthsprites)
+2 -2
View File
@@ -112,7 +112,7 @@
new_log.SetSuccessCallback(CALLBACK(src, .proc/set_db_log_id))
new_log.SetFailCallback(CALLBACK(GLOBAL_PROC, /proc/qdel))
new_log.ExecuteNoSleep()
new_log.ExecuteNoSleep(TRUE) //Allow to execute before roundstarts for roundstart antags
return
/datum/antagonist/proc/set_db_log_id(var/datum/db_query/new_log)
@@ -137,4 +137,4 @@
)
update_query.SetSuccessCallback(CALLBACK(src, .proc/set_db_log_id))
update_query.SetFailCallback(CALLBACK(GLOBAL_PROC, /proc/qdel))
update_query.ExecuteNoSleep()
update_query.ExecuteNoSleep(TRUE) //Shouldnt happen but still, in case it happens we want it to run before roundstart
@@ -111,7 +111,7 @@
if (!establish_db_connection(GLOB.dbcon))
to_chat(src, SPAN_NOTICE("Unable to connect to the database."))
return
var/DBQuery/query = GLOB.dbcon.NewQuery("SELECT title, message FROM ss13_ccia_general_notice_list WHERE deleted_at IS NULL")
var/datum/db_query/query = SSdbcore.NewQuery("SELECT title, message FROM ss13_ccia_general_notice_list WHERE deleted_at IS NULL")
query.Execute()
var/list/template_names = list()
@@ -121,6 +121,8 @@
template_names += query.item[1]
templates[query.item[1]] = query.item[2]
qdel(query)
// Catch empty list
if (!templates.len)
to_chat(src, SPAN_NOTICE("There are no templates in the database."))
+3 -1
View File
@@ -75,13 +75,15 @@ var/list/whitelist_jobconfig = list()
log_world("ERROR: Database connection failed while loading alien whitelists. Reverting to legacy system.")
GLOB.config.sql_whitelists = 0
else
var/DBQuery/query = GLOB.dbcon.NewQuery("SELECT status_name, flag FROM ss13_whitelist_statuses")
var/datum/db_query/query = SSdbcore.NewQuery("SELECT status_name, flag FROM ss13_whitelist_statuses")
query.Execute()
while (query.NextRow())
if (query.item[1] in GLOB.whitelisted_species)
GLOB.whitelisted_species[query.item[1]] = text2num(query.item[2])
qdel(query)
return
var/text = file2text("config/alienwhitelist.txt")
+8 -3
View File
@@ -266,13 +266,15 @@ GLOBAL_LIST_INIT_TYPED(allConsoles, /obj/machinery/requests_console, list())
if (!SQLquery)
SQLquery = "SELECT id, name, department FROM ss13_forms ORDER BY id"
var/DBQuery/query = GLOB.dbcon.NewQuery(SQLquery)
var/datum/db_query/query = SSdbcore.NewQuery(SQLquery)
query.Execute()
var/list/forms = list()
while (query.NextRow())
forms += list(list("id" = query.item[1], "name" = query.item[2], "department" = query.item[3]))
qdel(query)
if (!forms.len)
data["sql_error"] = 1
@@ -397,7 +399,7 @@ GLOBAL_LIST_INIT_TYPED(allConsoles, /obj/machinery/requests_console, list())
alert("Connection to the database lost. Aborting.")
if(!printid)
alert("Invalid query. Try again.")
var/DBQuery/query = GLOB.dbcon.NewQuery("SELECT id, name, data FROM ss13_forms WHERE id=[printid]")
var/datum/db_query/query = SSdbcore.NewQuery("SELECT id, name, data FROM ss13_forms WHERE id=[printid]")
query.Execute()
while(query.NextRow())
@@ -415,6 +417,8 @@ GLOBAL_LIST_INIT_TYPED(allConsoles, /obj/machinery/requests_console, list())
paperstock--
qdel(query)
// Get extra information about the form.
if(href_list["whatis"])
var/whatisid = sanitizeSQL(href_list["whatis"])
@@ -423,7 +427,7 @@ GLOBAL_LIST_INIT_TYPED(allConsoles, /obj/machinery/requests_console, list())
alert("Connection to the database lost. Aborting.")
if(!whatisid)
alert("Invalid query. Try again.")
var/DBQuery/query = GLOB.dbcon.NewQuery("SELECT id, name, department, info FROM ss13_forms WHERE id=[whatisid]")
var/datum/db_query/query = SSdbcore.NewQuery("SELECT id, name, department, info FROM ss13_forms WHERE id=:id",list("id"=whatisid))
query.Execute()
var/dat = "<center><b>Stellar Corporate Conglomerate Form</b><br>"
while(query.NextRow())
@@ -438,6 +442,7 @@ GLOBAL_LIST_INIT_TYPED(allConsoles, /obj/machinery/requests_console, list())
dat += "[info]"
dat += "</center>"
usr << browse(HTML_SKELETON(dat), "window=Information;size=560x240")
qdel(query)
// Toggle the paper bin lid.
if(href_list["setLid"])
+9 -6
View File
@@ -244,8 +244,8 @@ Then check if it's true, if true return. This will stop the normal menu appearin
.["contracts_view"] = 1
query_details["status"] = "open"
var/DBQuery/index_query = GLOB.dbcon.NewQuery("SELECT count(*) as Total_Contracts FROM ss13_syndie_contracts WHERE deleted_at IS NULL AND status = :status:")
index_query.Execute(query_details)
var/datum/db_query/index_query = SSdbcore.NewQuery("SELECT count(*) as Total_Contracts FROM ss13_syndie_contracts WHERE deleted_at IS NULL AND status = :status", query_details)
index_query.Execute()
var/pages = 0
@@ -271,8 +271,8 @@ Then check if it's true, if true return. This will stop the normal menu appearin
query_details["offset"] = (.["contracts_current_page"] - 1) * 10
var/DBQuery/list_query = GLOB.dbcon.NewQuery("SELECT contract_id, contractee_name, title FROM ss13_syndie_contracts WHERE deleted_at IS NULL AND status = :status: LIMIT 10 OFFSET :offset:")
list_query.Execute(query_details)
var/datum/db_query/list_query = SSdbcore.NewQuery("SELECT contract_id, contractee_name, title FROM ss13_syndie_contracts WHERE deleted_at IS NULL AND status = :status LIMIT 10 OFFSET :offset",query_details)
list_query.Execute()
var/list/contracts = list()
while (list_query.NextRow())
@@ -283,6 +283,8 @@ Then check if it's true, if true return. This will stop the normal menu appearin
.["contracts"] = contracts
.["contracts_found"] = 1
qdel(list_query)
qdel(index_query)
if(tgui_menu == 31)
.["contracts_found"] = 0
@@ -299,8 +301,8 @@ Then check if it's true, if true return. This will stop the normal menu appearin
var/query_details[0]
query_details["contract_id"] = exploit_id
var/DBQuery/select_query = GLOB.dbcon.NewQuery("SELECT contract_id, contractee_name, status, title, description, reward_other FROM ss13_syndie_contracts WHERE contract_id = :contract_id:")
select_query.Execute(query_details)
var/datum/db_query/select_query = SSdbcore.NewQuery("SELECT contract_id, contractee_name, status, title, description, reward_other FROM ss13_syndie_contracts WHERE contract_id = :contract_id",query_details)
select_query.Execute()
if (select_query.NextRow())
.["contracts_found"] = 1
@@ -324,6 +326,7 @@ Then check if it's true, if true return. This will stop the normal menu appearin
contract["reward_other"] = select_query.item[6]
.["contract"] = contract
qdel(select_query)
// I placed this here because of how relevant it is.
// You place this in your uplinkable item to check if an uplink is active or not.
+3 -2
View File
@@ -180,7 +180,7 @@ GLOBAL_PROTECT(jobban_keylist)
*/
/proc/jobban_loaddatabase()
// No database. Weee.
if (!establish_db_connection(GLOB.dbcon))
if (!SSdbcore.Connect())
log_world("ERROR: Database connection failed. Reverting to the legacy ban system.")
log_misc("Database connection failed. Reverting to the legacy ban system.")
GLOB.config.ban_legacy_system = 1
@@ -188,7 +188,7 @@ GLOBAL_PROTECT(jobban_keylist)
return
// All jobbans in one query. Because we don't actually care.
var/DBQuery/query = GLOB.dbcon.NewQuery("SELECT id, ckey, job, reason FROM ss13_ban WHERE isnull(unbanned) AND ((bantype = 'JOB_PERMABAN') OR (bantype = 'JOB_TEMPBAN' AND expiration_time > Now()))")
var/datum/db_query/query = SSdbcore.NewQuery("SELECT id, ckey, job, reason FROM ss13_ban WHERE isnull(unbanned) AND ((bantype = 'JOB_PERMABAN') OR (bantype = 'JOB_TEMPBAN' AND expiration_time > Now()))")
query.Execute()
while (query.NextRow())
@@ -207,6 +207,7 @@ GLOBAL_PROTECT(jobban_keylist)
else
// Woups. What happened here...?
log_and_message_admins("JOBBANS: Duplicate jobban entry in MySQL for [ckey]. Ban ID: #[query.item[1]]")
qdel(query)
/**
* Saves the current bans into the data/job_full.ban file.
+19 -107
View File
@@ -1,15 +1,34 @@
/**
* # IPIntel datum
*
* Holds the result of an IPIntel lookup for a given IP address.
* Created by [/datum/controller/subsystem/ipintel/proc/get_ip_intel] and delivered to its `on_complete` callback.
*/
/datum/ipintel
/// The IP address that was looked up
var/ip
/// The raw intel score returned by the API (0.01.0), or -1 on error
var/intel = 0
/// Whether this result came from cache (memory or database) rather than a live API call
var/cache = FALSE
/// How many minutes ago the cached entry was written
var/cacheminutesago = 0
/// SQL timestamp of when the cache entry was written
var/cachedate = ""
/// world.realtime equivalent of the cache write time, used for in-memory cache expiry
var/cacherealtime = 0
/datum/ipintel/New()
cachedate = SQLtime()
cacherealtime = world.realtime
/**
* Returns TRUE if this cached result is still within its configured validity window.
*
* Good results (below [/datum/configuration/var/ipintel_rating_bad]) are kept for
* [/datum/configuration/var/ipintel_save_good] hours; bad results are kept for
* [/datum/configuration/var/ipintel_save_bad] hours. Returns FALSE for error results (intel < 0).
*/
/datum/ipintel/proc/is_valid()
. = FALSE
if (intel < 0)
@@ -20,110 +39,3 @@
else
if (world.realtime < cacherealtime+(GLOB.config.ipintel_save_bad*60*60*10))
return TRUE
/proc/get_ip_intel(ip, bypasscache = FALSE, updatecache = TRUE)
var/datum/ipintel/res = new()
res.ip = ip
. = res
if (!ip || !GLOB.config.ipintel_email || !SSipintel.enabled)
return
if (!bypasscache)
var/datum/ipintel/cachedintel = SSipintel.cache[ip]
if (cachedintel && cachedintel.is_valid())
cachedintel.cache = TRUE
return cachedintel
if(establish_db_connection(GLOB.dbcon))
var/DBQuery/query_get_ip_intel = GLOB.dbcon.NewQuery({"
SELECT date, intel, TIMESTAMPDIFF(MINUTE,date,NOW())
FROM ss13_ipintel
WHERE
ip = INET6_ATON('[ip]')
AND ((
intel < [GLOB.config.ipintel_rating_bad]
AND
date + INTERVAL [GLOB.config.ipintel_save_good] HOUR > NOW()
) OR (
intel >= [GLOB.config.ipintel_rating_bad]
AND
date + INTERVAL [GLOB.config.ipintel_save_bad] HOUR > NOW()
))
"})
if(!query_get_ip_intel.Execute())
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)
SSipintel.cache[ip] = res
return
res.intel = ip_intel_query(ip)
if (updatecache && res.intel >= 0)
SSipintel.cache[ip] = res
if(establish_db_connection(GLOB.dbcon))
var/DBQuery/query_add_ip_intel = GLOB.dbcon.NewQuery("INSERT INTO ss13_ipintel (ip, intel) VALUES (INET6_ATON('[ip]'), [res.intel]) ON DUPLICATE KEY UPDATE intel = VALUES(intel), date = NOW()")
query_add_ip_intel.Execute()
/proc/ip_intel_query(ip, var/retryed=0)
. = -1 //default
if (!ip)
return
if (SSipintel.throttle > world.timeofday)
return
if (!SSipintel.enabled)
return
var/list/http[] = world.Export("http://[GLOB.config.ipintel_domain]/check.php?ip=[ip]&contact=[GLOB.config.ipintel_email]&format=json&flags=f")
if (http)
var/status = text2num(http["STATUS"])
if (status == 200)
var/response = json_decode(file2text(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, retryed)
if (!retryed)
sleep(25)
return .(ip, 1)
else
ipintel_handle_error("Bad response from server: [response["status"]].", ip, retryed)
if (!retryed)
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, retryed)
if (!retryed)
sleep(25)
return .(ip, 1)
else
ipintel_handle_error("Unable to connect to API.", ip, retryed)
if (!retryed)
sleep(25)
return .(ip, 1)
/proc/ipintel_handle_error(error, ip, retryed)
if (retryed)
SSipintel.errors++
error += " Could not check [ip]. Disabling IPINTEL for [SSipintel.errors] minute\s"
SSipintel.throttle = world.timeofday + (10 * 120 * SSipintel.errors)
else
error += " Attempting retry on [ip]."
log_ipintel(error)
/proc/log_ipintel(text)
log_game("IPINTEL: [text]")
LOG_DEBUG("IPINTEL: [text]")
+19 -16
View File
@@ -162,25 +162,28 @@ GLOBAL_LIST_EMPTY(ticket_panels)
if (status != TICKET_CLOSED)
return
if (!establish_db_connection(GLOB.dbcon))
if (!SSdbcore.Connect())
return
var/DBQuery/Q = GLOB.dbcon.NewQuery({"INSERT INTO ss13_tickets
var/datum/db_query/query = SSdbcore.NewQuery({"INSERT INTO ss13_tickets
(game_id, message_count, admin_count, admin_list, opened_by, taken_by, closed_by, response_delay, opened_at, closed_at)
VALUES
(:g_id:, :m_count:, :a_count:, :a_list:, :opened_by:, :taken_by:, :closed_by:, :delay:, :opened_at:, :closed_at:)"})
Q.Execute(list(
"g_id" = GLOB.round_id,
"m_count" = length(msgs),
"a_count" = length(assigned_admins),
"a_list" = json_encode(assigned_admins),
"opened_by" = owner,
"taken_by" = length(assigned_admins) ? assigned_admins[1] : null,
"closed_by" = closed_by,
"delay" = response_time || -1,
"opened_at" = SQLtime(opened_rt),
"closed_at" = SQLtime(closed_rt)
))
VALUES (:g_id, :m_count, :a_count, :a_list, :opened_by, :taken_by, :closed_by, :delay, :opened_at, :closed_at)"},
list(
"g_id" = GLOB.round_id,
"m_count" = length(msgs),
"a_count" = length(assigned_admins),
"a_list" = json_encode(assigned_admins),
"opened_by" = owner,
"taken_by" = length(assigned_admins) ? assigned_admins[1] : null,
"closed_by" = closed_by,
"delay" = response_time || -1,
"opened_at" = SQLtime(opened_rt),
"closed_at" = SQLtime(closed_rt)
),
TRUE
)
query.SetSuccessCallback(CALLBACK(GLOBAL_PROC, GLOBAL_PROC_REF(qdel)))
query.Execute()
/datum/ticket/proc/append_message(m_from, m_to, msg)
msgs += new /datum/ticket_msg(m_from, m_to, msg)
+3 -1
View File
@@ -620,7 +620,7 @@ Traitors and the like can also be revived with the previous role mostly intact.
if (!establish_db_connection(GLOB.dbcon))
to_chat(src, SPAN_NOTICE("Unable to connect to the database."))
return
var/DBQuery/query = GLOB.dbcon.NewQuery("SELECT title, message FROM ss13_ccia_general_notice_list WHERE deleted_at IS NULL")
var/datum/db_query/query = SSdbcore.NewQuery("SELECT title, message FROM ss13_ccia_general_notice_list WHERE deleted_at IS NULL")
query.Execute()
var/list/template_names = list()
@@ -630,6 +630,8 @@ Traitors and the like can also be revived with the previous role mostly intact.
template_names += query.item[1]
templates[query.item[1]] = query.item[2]
qdel(query)
// Catch empty list
if (!templates.len)
to_chat(src, SPAN_NOTICE("There are no templates in the database."))
+33 -20
View File
@@ -243,34 +243,47 @@
* Called by /datum/preferences/proc/gather_notifications() in preferences.dm
*/
/client/proc/warnings_gather()
var/count = 0
var/count_expire = 0
if (!establish_db_connection(GLOB.dbcon))
if (!SSdbcore.Connect())
return
var/list/client_details = list("ckey" = ckey, "computer_id" = computer_id, "address" = address)
var/DBQuery/expire_query = GLOB.dbcon.NewQuery("SELECT id FROM ss13_warnings WHERE (acknowledged = 1 AND expired = 0 AND DATE_SUB(CURDATE(),INTERVAL 3 MONTH) > time) AND (ckey = :ckey: OR computerid = :computer_id: OR ip = :address:)")
expire_query.Execute(client_details)
while (expire_query.NextRow())
var/warning_id = text2num(expire_query.item[1])
var/DBQuery/update_query = GLOB.dbcon.NewQuery("UPDATE ss13_warnings SET expired = 1 WHERE id = :warning_id:")
update_query.Execute(list("warning_id" = warning_id))
count_expire++
var/datum/db_query/expire_query = SSdbcore.NewQuery(
"SELECT id FROM ss13_warnings WHERE (acknowledged = 1 AND expired = 0 AND DATE_SUB(CURDATE(),INTERVAL 3 MONTH) > time) AND (ckey = :ckey OR computerid = :computer_id OR ip = :address)",
client_details)
expire_query.SetSuccessCallback(CALLBACK(src, PROC_REF(_warnings_expire_cb)))
expire_query.SetFailCallback(CALLBACK(GLOBAL_PROC, GLOBAL_PROC_REF(qdel)))
expire_query.ExecuteNoSleep(TRUE)
var/DBQuery/query = GLOB.dbcon.NewQuery("SELECT id FROM ss13_warnings WHERE (visible = 1 AND acknowledged = 0 AND expired = 0) AND (ckey = :ckey: OR computerid = :computer_id: OR ip = :address:)")
query.Execute(client_details)
var/datum/db_query/count_query = SSdbcore.NewQuery(
"SELECT id FROM ss13_warnings WHERE (visible = 1 AND acknowledged = 0 AND expired = 0) AND (ckey = :ckey OR computerid = :computer_id OR ip = :address)",
client_details)
count_query.SetSuccessCallback(CALLBACK(src, PROC_REF(_warnings_count_cb)))
count_query.SetFailCallback(CALLBACK(GLOBAL_PROC, GLOBAL_PROC_REF(qdel)))
count_query.ExecuteNoSleep(TRUE)
/client/proc/_warnings_expire_cb(datum/db_query/query)
var/count_expire = 0
while (query.NextRow())
var/warning_id = text2num(query.item[1])
var/datum/db_query/update_query = SSdbcore.NewQuery(
"UPDATE ss13_warnings SET expired = 1 WHERE id = :warning_id",
list("warning_id" = warning_id))
update_query.SetSuccessCallback(CALLBACK(GLOBAL_PROC, GLOBAL_PROC_REF(qdel)))
update_query.SetFailCallback(CALLBACK(GLOBAL_PROC, GLOBAL_PROC_REF(qdel)))
update_query.ExecuteNoSleep(TRUE)
count_expire++
qdel(query)
if (count_expire)
prefs?.new_notification("info", "[count_expire] of your warnings have expired.")
/client/proc/_warnings_count_cb(datum/db_query/query)
var/count = 0
while (query.NextRow())
count++
var/list/data = list("unread" = "", "expired" = "")
qdel(query)
if (count)
data["unread"] = "You have <b>[count] unread warning\s!</b> Click <a href='byond://?JSlink=warnings;notification=:src_ref'>here</a> to review and acknowledge them!"
if (count_expire)
data["expired"] = "[count_expire] of your warnings have expired."
return data
prefs?.new_notification("danger", "You have <b>[count] unread warning\s!</b> Click <a href='byond://?JSlink=warnings;notification=:src_ref'>here</a> to review and acknowledge them!", 1)
/**
* A proc used to gather if someone has Unacknowledged Warnings
+19 -17
View File
@@ -59,15 +59,16 @@
//If nothing has been done with the device yet
if(!selected_report && !interviewee_id)
if(GLOB.config.sql_ccia_logs)
if(GLOB.config.sql_ccia_logs && SSdbcore.Connect())
//Get the active cases from the database and display them
var/list/reports = list()
var/DBQuery/report_query = GLOB.dbcon.NewQuery("SELECT id, report_date, title, public_topic, internal_topic, game_id, status FROM ss13_ccia_reports WHERE status IN ('in progress', 'approved') AND deleted_at IS NULL")
var/datum/db_query/report_query = SSdbcore.NewQuery("SELECT id, report_date, title, public_topic, internal_topic, game_id, status FROM ss13_ccia_reports WHERE status IN ('in progress', 'approved') AND deleted_at IS NULL")
report_query.Execute()
while(report_query.NextRow())
CHECK_TICK
var/datum/ccia_report/R = new(report_query.item[1], report_query.item[2], report_query.item[3], report_query.item[4], report_query.item[5], report_query.item[6], report_query.item[7])
reports["[report_query.item[1]] - [report_query.item[2]] - [report_query.item[3]]"] = R
qdel(report_query)
var/selection = input(usr, "Select Report","Report Name") as null|anything in reports
if(!selection)
@@ -152,32 +153,33 @@
P.forceMove(get_turf(src.loc))
//If we have sql ccia logs enabled, then persist it here
if(GLOB.config.sql_ccia_logs && establish_db_connection(GLOB.dbcon))
if(GLOB.config.sql_ccia_logs && SSdbcore.Connect())
//This query is split up into multiple parts due to the length limitations of byond.
//To avoid this the text and the antag_involvement_text are saved separately
var/DBQuery/save_log = GLOB.dbcon.NewQuery("INSERT INTO ss13_ccia_reports_transcripts (id, report_id, character_id, interviewer, antag_involvement, text) VALUES (NULL, :report_id:, :character_id:, :interviewer:, :antag_involvement:, :text:)")
save_log.Execute(list("report_id" = selected_report.id, "character_id" = interviewee_id, "interviewer" = usr.name, "antag_involvement" = antag_involvement, "text" = P.info))
var/datum/db_query/save_log = SSdbcore.NewQuery("INSERT INTO ss13_ccia_reports_transcripts (id, report_id, character_id, interviewer, antag_involvement, text) VALUES (NULL, :report_id, :character_id, :interviewer, :antag_involvement, :text)",list("report_id" = selected_report.id, "character_id" = interviewee_id, "interviewer" = usr.name, "antag_involvement" = antag_involvement, "text" = P.info),TRUE)
save_log.warn_execute()
//Run the query to get the inserted id
var/transcript_id = null
var/DBQuery/tid = GLOB.dbcon.NewQuery("SELECT LAST_INSERT_ID() AS log_id")
tid.Execute()
if (tid.NextRow())
transcript_id = text2num(tid.item[1])
var/transcript_id = save_log.last_insert_id
if(tid)
var/DBQuery/add_text = GLOB.dbcon.NewQuery("UPDATE ss13_ccia_reports_transcripts SET text = :text: WHERE id = :id:")
add_text.Execute(list("id" = transcript_id, "text" = P.info))
var/DBQuery/add_antag_involvement_text = GLOB.dbcon.NewQuery("UPDATE ss13_ccia_reports_transcripts SET antag_involvement_text = :antag_involvement_text: WHERE id = :id:")
add_antag_involvement_text.Execute(list("id" = transcript_id, "antag_involvement_text" = antag_involvement_text))
if(transcript_id)
var/datum/db_query/add_text = SSdbcore.NewQuery("UPDATE ss13_ccia_reports_transcripts SET text = :text WHERE id = :id",list("id" = transcript_id, "text" = P.info),TRUE)
add_text.warn_execute()
qdel(add_text)
var/datum/db_query/add_antag_involvement_text = SSdbcore.NewQuery("UPDATE ss13_ccia_reports_transcripts SET antag_involvement_text = :antag_involvement_text WHERE id = :id",list("id" = transcript_id, "antag_involvement_text" = antag_involvement_text),TRUE)
add_antag_involvement_text.warn_execute()
qdel(add_antag_involvement_text)
else
message_cciaa("Transcript could not be saved correctly. TiD Missing")
//Check if we need to update the status to review required
if(antag_involvement && selected_report.status == "in progress")
to_chat(usr, SPAN_NOTICE("The device beeps and flashes \"Liaison Review Required. Interviewee claimed antag involvement.\"."))
var/DBQuery/update_db = GLOB.dbcon.NewQuery("UPDATE ss13_ccia_reports SET status = 'review required' WHERE id = :id:")
update_db.Execute(list("id" = selected_report.id))
var/datum/db_query/update_db = SSdbcore.NewQuery("UPDATE ss13_ccia_reports SET status = 'review required' WHERE id = :id",list("id" = selected_report.id),TRUE)
update_db.warn_execute()
qdel(update_db)
qdel(save_log)
sLogFile = null
selected_report = null
+1
View File
@@ -36,6 +36,7 @@
var/related_accounts_ip = "Requires database" //So admins know why it isn't working - Used to determine what other accounts previously logged in from this ip
var/related_accounts_cid = "Requires database" //So admins know why it isn't working - Used to determine what other accounts previously logged in from this computer id
var/whitelist_status = 0 //Used to determine what whitelists the player has access to. Uses bitflag values!
var/whitelist_status_loaded = FALSE //Set to TRUE once log_client_to_db() has written whitelist_status from the DB. Prevents false whitelist failures during login before the async query completes.
var/need_saves_migrated = "Requires database" //Used to determine whether or not the ckey needs their saves migrated over to the database. Default is 0 upon successful connection.
var/account_age = -1 // Age on the BYOND account in days.
var/account_join_date = null // Date of the BYOND account creation in ISO 8601 format.
+150 -76
View File
@@ -145,13 +145,13 @@ GLOBAL_LIST_INIT(localhost_addresses, list(
var/request_id = text2num(href_list["linkingrequest"])
if (!establish_db_connection(GLOB.dbcon))
if (!SSdbcore.Connect())
to_chat(src, SPAN_WARNING("Action failed! Database link could not be established!"))
return
var/DBQuery/check_query = GLOB.dbcon.NewQuery("SELECT player_ckey, status FROM ss13_player_linking WHERE id = :id:")
check_query.Execute(list("id" = request_id))
var/datum/db_query/check_query = SSdbcore.NewQuery("SELECT player_ckey, status FROM ss13_player_linking WHERE id = :id",list("id" = request_id))
check_query.Execute()
if (!check_query.NextRow())
to_chat(src, SPAN_WARNING("No request found!"))
@@ -161,18 +161,20 @@ GLOBAL_LIST_INIT(localhost_addresses, list(
to_chat(src, SPAN_WARNING("Request authentication failed!"))
return
qdel(check_query)
var/query_contents = ""
var/list/query_details = list("new_status", "id")
var/feedback_message = ""
switch (href_list["linkingaction"])
if ("accept")
query_contents = "UPDATE ss13_player_linking SET status = :new_status:, updated_at = NOW() WHERE id = :id:"
query_contents = "UPDATE ss13_player_linking SET status = :new_status, updated_at = NOW() WHERE id = :id"
query_details["new_status"] = "confirmed"
query_details["id"] = request_id
feedback_message = SPAN_DANGER("<b>Account successfully linked!</b>")
if ("deny")
query_contents = "UPDATE ss13_player_linking SET status = :new_status:, deleted_at = NOW() WHERE id = :id:"
query_contents = "UPDATE ss13_player_linking SET status = :new_status, deleted_at = NOW() WHERE id = :id"
query_details["new_status"] = "rejected"
query_details["id"] = request_id
@@ -181,8 +183,9 @@ GLOBAL_LIST_INIT(localhost_addresses, list(
to_chat(src, SPAN_WARNING("Invalid command sent."))
return
var/DBQuery/update_query = GLOB.dbcon.NewQuery(query_contents)
update_query.Execute(query_details)
var/datum/db_query/update_query = SSdbcore.NewQuery(query_contents,query_details)
update_query.Execute()
qdel(update_query)
if (href_list["linkingaction"] == "accept" && alert("To complete the process, you have to visit the website. Do you want to do so now?",,"Yes","No") == "Yes")
process_webint_link("interface/user/link")
@@ -542,21 +545,35 @@ GLOBAL_LIST_INIT(localhost_addresses, list(
..() //Even though we're going to be hard deleted there are still some things that want to know the destroy is happening
return QDEL_HINT_HARDDEL_NOW
// here because it's similar to below
// Returns null if no DB connection can be established, or -1 if the requested key was not found in the database
/**
* Ensures whitelist_status is populated from the database.
*
* Idempotent: returns immediately if already loaded.
* Called by log_client_to_db() as part of the full player record fetch,
* and by the loadout sanitization to block until the status is available.
*/
/client/proc/load_whitelist_status()
if (whitelist_status_loaded)
return
/proc/get_player_age(key)
if(!establish_db_connection(GLOB.dbcon))
return null
if (IsGuestKey(key) || !GLOB.config.sql_whitelists)
whitelist_status_loaded = TRUE
return
var/DBQuery/query = GLOB.dbcon.NewQuery("SELECT datediff(Now(),firstseen) as age FROM ss13_player WHERE ckey = :ckey:")
query.Execute(list("ckey"=ckey(key)))
var/datum/db_query/query = SSdbcore.NewQuery(
"SELECT whitelist_status FROM ss13_player WHERE ckey = :ckey",
list("ckey" = ckey(key))
)
if (!query.Execute())
qdel(query)
whitelist_status_loaded = TRUE
return
if(query.NextRow())
return text2num(query.item[1])
else
return -1
if (query.NextRow())
whitelist_status = text2num(query.item[1])
qdel(query)
whitelist_status_loaded = TRUE
/client/proc/log_client_to_db()
set waitfor = FALSE
@@ -564,16 +581,17 @@ GLOBAL_LIST_INIT(localhost_addresses, list(
if (IsGuestKey(src.key))
return
if(!establish_db_connection(GLOB.dbcon))
return
var/DBQuery/query = GLOB.dbcon.NewQuery("SELECT datediff(Now(),firstseen) as age, whitelist_status, account_join_date, DATEDIFF(NOW(), account_join_date), ckey_is_external FROM ss13_player WHERE ckey = :ckey:")
if(!query.Execute(list("ckey"=ckey(key))))
// --- Main player record lookup ---
var/datum/db_query/query = SSdbcore.NewQuery(
"SELECT datediff(Now(),firstseen), whitelist_status, account_join_date, DATEDIFF(NOW(), account_join_date), ckey_is_external FROM ss13_player WHERE ckey = :ckey",
list("ckey" = ckey(key))
)
if (!query.Execute())
qdel(query)
return
var/found = 0
player_age = 0 // New players won't have an entry so knowing we have a connection we set this to zero to be updated if their is a record.
player_age = 0 // New players won't have an entry; zero signals we have a connection but no record yet.
if (query.NextRow())
found = 1
@@ -583,44 +601,85 @@ GLOBAL_LIST_INIT(localhost_addresses, list(
account_age = text2num(query.item[4])
ckey_is_external = !!text2num(query.item[5])
if (!account_age)
account_join_date = sanitizeSQL(findJoinDate())
account_join_date = findJoinDate()
if (!account_join_date)
account_age = -1
else
var/DBQuery/query_datediff = GLOB.dbcon.NewQuery("SELECT DATEDIFF(NOW(), [account_join_date])")
var/datum/db_query/query_datediff = SSdbcore.NewQuery(
"SELECT DATEDIFF(NOW(), :join_date)",
list("join_date" = account_join_date)
)
if (!query_datediff.Execute())
qdel(query)
qdel(query_datediff)
return
if (query_datediff.NextRow())
account_age = text2num(query_datediff.item[1])
qdel(query_datediff)
qdel(query)
whitelist_status_loaded = TRUE
var/DBQuery/query_ip = GLOB.dbcon.NewQuery("SELECT ckey FROM ss13_player WHERE ip = '[address]'")
// --- Related account lookups ---
var/datum/db_query/query_ip = SSdbcore.NewQuery(
"SELECT ckey FROM ss13_player WHERE ip = :ip",
list("ip" = address)
)
query_ip.Execute()
related_accounts_ip = ""
while(query_ip.NextRow())
related_accounts_ip += "[query_ip.item[1]], "
break
if (query_ip.NextRow())
related_accounts_ip = "[query_ip.item[1]], "
qdel(query_ip)
var/DBQuery/query_cid = GLOB.dbcon.NewQuery("SELECT ckey FROM ss13_player WHERE computerid = '[computer_id]'")
var/datum/db_query/query_cid = SSdbcore.NewQuery(
"SELECT ckey FROM ss13_player WHERE computerid = :computerid",
list("computerid" = computer_id)
)
query_cid.Execute()
related_accounts_cid = ""
while(query_cid.NextRow())
related_accounts_cid += "[query_cid.item[1]], "
break
if (query_cid.NextRow())
related_accounts_cid = "[query_cid.item[1]], "
qdel(query_cid)
// --- Write ---
var/admin_rank = "Player"
if(src.holder)
if (src.holder)
admin_rank = src.holder.rank
if(found)
//Player already identified previously, we need to just update the 'lastseen', 'ip', 'computer_id', 'byond_version' and 'byond_build' variables
var/DBQuery/query_update = GLOB.dbcon.NewQuery("UPDATE ss13_player SET lastseen = Now(), ip = :ip:, computerid = :computerid:, lastadminrank = :lastadminrank:, account_join_date = :account_join_date:, byond_version = :byond_version:, byond_build = :byond_build: WHERE ckey = :ckey:")
query_update.Execute(list("ckey"=ckey(key),"ip"=src.address,"computerid"=src.computer_id,"lastadminrank"=admin_rank,"account_join_date"=account_join_date,"byond_version"=byond_version,"byond_build"=byond_build))
if (found)
var/datum/db_query/query_update = SSdbcore.NewQuery(
"UPDATE ss13_player SET lastseen = Now(), ip = :ip, computerid = :computerid, lastadminrank = :lastadminrank, account_join_date = :account_join_date, byond_version = :byond_version, byond_build = :byond_build WHERE ckey = :ckey",
list(
"ckey" = ckey(key),
"ip" = src.address,
"computerid" = src.computer_id,
"lastadminrank" = admin_rank,
"account_join_date" = account_join_date,
"byond_version" = byond_version,
"byond_build" = byond_build,
)
)
query_update.SetSuccessCallback(CALLBACK(GLOBAL_PROC, /proc/qdel))
query_update.SetFailCallback(CALLBACK(GLOBAL_PROC, /proc/qdel))
query_update.Execute()
else if (!GLOB.config.access_deny_new_players)
//New player!! Need to insert all the stuff
var/DBQuery/query_insert = GLOB.dbcon.NewQuery("INSERT INTO ss13_player (ckey, ckey_is_external, firstseen, lastseen, ip, computerid, lastadminrank, account_join_date, byond_version, byond_build) VALUES (:ckey:, :ckey_is_external:, Now(), Now(), :ip:, :computerid:, :lastadminrank:, :account_join_date:, :byond_version:, :byond_build:)")
query_insert.Execute(list("ckey"=ckey(key), "ckey_is_external"=src.ckey_is_external,"ip"=src.address,"computerid"=src.computer_id,"lastadminrank"=admin_rank,"account_join_date"=account_join_date,"byond_version"=byond_version,"byond_build"=byond_build))
var/datum/db_query/query_insert = SSdbcore.NewQuery(
"INSERT INTO ss13_player (ckey, ckey_is_external, firstseen, lastseen, ip, computerid, lastadminrank, account_join_date, byond_version, byond_build) VALUES (:ckey, :ckey_is_external, Now(), Now(), :ip, :computerid, :lastadminrank, :account_join_date, :byond_version, :byond_build)",
list(
"ckey" = ckey(key),
"ckey_is_external" = src.ckey_is_external,
"ip" = src.address,
"computerid" = src.computer_id,
"lastadminrank" = admin_rank,
"account_join_date" = account_join_date,
"byond_version" = byond_version,
"byond_build" = byond_build,
)
)
query_insert.SetSuccessCallback(CALLBACK(GLOBAL_PROC, /proc/qdel))
query_insert.SetFailCallback(CALLBACK(GLOBAL_PROC, /proc/qdel))
query_insert.Execute()
else
// Flag as -1 to know we have to kiiick them.
// Flag as -1 to signal this player must be kicked (new players denied).
player_age = -1
if (!account_join_date)
@@ -746,18 +805,20 @@ GLOBAL_LIST_INIT(localhost_addresses, list(
if (!GLOB.config.webint_url || !GLOB.config.sql_enabled)
return
if (!establish_db_connection(GLOB.dbcon))
if (!SSdbcore.Connect())
return
var/list/requests = list()
var/list/query_details = list("ckey" = ckey)
var/DBQuery/select_query = GLOB.dbcon.NewQuery("SELECT id, forum_id, forum_username, datediff(Now(), created_at) as request_age FROM ss13_player_linking WHERE status = 'new' AND player_ckey = :ckey: AND deleted_at IS NULL")
select_query.Execute(query_details)
var/datum/db_query/select_query = SSdbcore.NewQuery("SELECT id, forum_id, forum_username, datediff(Now(), created_at) as request_age FROM ss13_player_linking WHERE status = 'new' AND player_ckey = :ckey AND deleted_at IS NULL",query_details)
select_query.Execute()
while (select_query.NextRow())
requests.Add(list(list("id" = text2num(select_query.item[1]), "forum_id" = text2num(select_query.item[2]), "forum_username" = select_query.item[3], "request_age" = select_query.item[4])))
qdel(select_query)
if (!requests.len)
return
@@ -783,17 +844,18 @@ GLOBAL_LIST_INIT(localhost_addresses, list(
if (!GLOB.config.webint_url || !GLOB.config.sql_enabled)
return
if (!establish_db_connection(GLOB.dbcon))
if (!SSdbcore.Connect())
return
var/DBQuery/select_query = GLOB.dbcon.NewQuery("SELECT COUNT(*) AS request_count FROM ss13_player_linking WHERE status = 'new' AND player_ckey = :ckey: AND deleted_at IS NULL")
select_query.Execute(list("ckey" = ckey))
var/datum/db_query/select_query = SSdbcore.NewQuery("SELECT COUNT(*) AS request_count FROM ss13_player_linking WHERE status = 'new' AND player_ckey = :ckey AND deleted_at IS NULL", list("ckey" = ckey))
select_query.SetSuccessCallback(CALLBACK(src, PROC_REF(_gather_linking_requests_cb)))
select_query.SetFailCallback(CALLBACK(GLOBAL_PROC, GLOBAL_PROC_REF(qdel)))
select_query.ExecuteNoSleep(TRUE)
if (select_query.NextRow())
if (text2num(select_query.item[1]) > 0)
return "You have [select_query.item[1]] account linking requests pending review. Click <a href='byond://?JSlink=linking;notification=:src_ref'>here</a> to see them!"
return null
/client/proc/_gather_linking_requests_cb(datum/db_query/query)
if (query.NextRow() && text2num(query.item[1]) > 0)
prefs?.new_notification("info", "You have [query.item[1]] account linking requests pending review. Click <a href='byond://?JSlink=linking;notification=:src_ref'>here</a> to see them!", callback_src = src, callback_proc = "check_linking_requests")
qdel(query)
/client/proc/process_webint_link(var/route, var/attributes)
if (!route)
@@ -838,34 +900,46 @@ GLOBAL_LIST_INIT(localhost_addresses, list(
send_link(src, linkURL)
return
/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
set waitfor = 0
if (GLOB.config.ipintel_email)
var/datum/ipintel/res = get_ip_intel(address)
if (GLOB.config.ipintel_rating_kick && res.intel >= GLOB.config.ipintel_rating_kick)
if (!holder)
message_admins("Proxy Detection: [key_name_admin(src)] IP intel rated [res.intel*100]% likely to be a proxy/VPN. They are being kicked because of this.")
log_admin("Proxy Detection: [key_name_admin(src)] IP intel rated [res.intel*100]% likely to be a proxy/VPN. They are being kicked because of this.")
to_chat(src, SPAN_DANGER("Usage of proxies is not permitted by the rules. You are being kicked because of this."))
del(src)
else
message_admins("Proxy Detection: [key_name_admin(src)] IP intel rated [res.intel*100]% likely to be a Proxy/VPN.")
else if (res.intel >= GLOB.config.ipintel_rating_bad)
SSipintel.get_ip_intel(address, on_complete = CALLBACK(src, /client/proc/on_ip_intel_result))
/client/proc/on_ip_intel_result(datum/ipintel/res)
if (GLOB.config.ipintel_rating_kick && res.intel >= GLOB.config.ipintel_rating_kick)
if (!holder)
message_admins("Proxy Detection: [key_name_admin(src)] IP intel rated [res.intel*100]% likely to be a proxy/VPN. They are being kicked because of this.")
log_admin("Proxy Detection: [key_name_admin(src)] IP intel rated [res.intel*100]% likely to be a proxy/VPN. They are being kicked because of this.")
to_chat(src, SPAN_DANGER("Usage of proxies is not permitted by the rules. You are being kicked because of this."))
del(src)
else
message_admins("Proxy Detection: [key_name_admin(src)] IP intel rated [res.intel*100]% likely to be a Proxy/VPN.")
ip_intel = res.intel
else if (res.intel >= GLOB.config.ipintel_rating_bad)
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/proc/findJoinDate()
var/list/http = world.Export("http://byond.com/members/[ckey]?format=text")
if(!http)
var/datum/http_request/req = new()
req.prepare(RUSTG_HTTP_METHOD_GET, "https://byond.com/members/[ckey]?format=text")
req.begin_async()
// Poll RUSTG directly rather than routing through SShttp — this proc can be called
// before SShttp is firing (e.g. during MC init), so we cannot rely on SShttp's fire
// loop to dispatch the callback.
while (!req.is_complete())
sleep(world.tick_lag)
var/datum/http_response/http = req.into_response()
if (http.errored || http.status_code != 200)
LOG_DEBUG("ACCESS CONTROL: Failed to connect to byond age check for [ckey]")
return
var/F = file2text(http["CONTENT"])
if(F)
var/regex/R = regex("joined = \"(\\d{4}-\\d{2}-\\d{2})\"")
if(R.Find(F))
. = R.group[1]
else
CRASH("Age check regex failed for [src.ckey]")
var/regex/R = regex("joined = \"(\\d{4}-\\d{2}-\\d{2})\"")
if (R.Find(http.body))
return R.group[1]
else
CRASH("Age check regex failed for [src.ckey]")
/client/Click(atom/object, atom/location, control, params)
SEND_SIGNAL(src, COMSIG_CLIENT_CLICK, object, location, control, params, usr)
@@ -104,6 +104,8 @@ GLOBAL_LIST_INIT(gear_datums, list())
var/list/whitelist_cache = list()
if(preference_mob)
if(preference_mob.client)
preference_mob.client.load_whitelist_status()
for(var/species in GLOB.all_species)
var/datum/species/S = GLOB.all_species[species]
if(is_alien_whitelisted(preference_mob, S))
+56 -82
View File
@@ -139,113 +139,87 @@
new_notification("info", "You have unread updates in the changelog.")
if (GLOB.config.sql_enabled)
var/list/warnings = user.warnings_gather()
if (warnings["unread"])
new_notification("danger", warnings["unread"], 1)
if (warnings["expired"])
new_notification("info", warnings["expired"])
var/linking = user.gather_linking_requests()
if (linking)
new_notification("info", linking, callback_src = user, callback_proc = "check_linking_requests")
var/cciaa_actions = count_ccia_actions(user)
if (cciaa_actions)
new_notification("info", cciaa_actions)
user.warnings_gather()
user.gather_linking_requests()
count_ccia_actions(user)
add_active_notifications(user)
/datum/preferences/proc/add_active_notifications(var/client/user)
if(!user)
return null
if (!user)
return
if (!establish_db_connection(GLOB.dbcon))
log_world("ERROR: Error initiatlizing database connection while getting notifications.")
return null
if (!SSdbcore.Connect())
return
var/DBQuery/query = GLOB.dbcon.NewQuery({"SELECT
message, type, id
FROM ss13_player_notifications
WHERE acked_at IS NULL AND ckey = :ckey:
"})
query.Execute(list("ckey" = user.ckey))
var/datum/db_query/query = SSdbcore.NewQuery(
"SELECT message, type, id FROM ss13_player_notifications WHERE acked_at IS NULL AND ckey = :ckey",
list("ckey" = user.ckey))
query.SetSuccessCallback(CALLBACK(src, PROC_REF(_add_active_notifications_cb), user))
query.SetFailCallback(CALLBACK(GLOBAL_PROC, GLOBAL_PROC_REF(qdel)))
query.ExecuteNoSleep(TRUE)
var/chat_notification=0
var/panel_notification=0
var/notification_count=0
while(query.NextRow())
var/autoack=0
//Lets loop through the results
/datum/preferences/proc/_add_active_notifications_cb(var/client/user, datum/db_query/query)
var/chat_notification = 0
var/panel_notification = 0
var/notification_count = 0
while (query.NextRow())
var/autoack = 0
switch(query.item[2])
if("player_greeting")
panel_notification=1
panel_notification = 1
notification_count++
if("player_greeting_chat")
chat_notification=1
panel_notification=1
chat_notification = 1
panel_notification = 1
notification_count++
if("admin")
SSdiscord.send_to_admins("Server Notification for [user.ckey]: [query.item[1]]")
SSdiscord.post_webhook_event(WEBHOOK_ADMIN, list("title"="Server Notification for: [user.ckey]", "message"="Server Notification Triggered for [user.ckey]: [query.item[1]]"))
//Immediately ack the notification
autoack=1
autoack = 1
if("ccia")
SSdiscord.send_to_cciaa("Server Notification for [user.ckey]: [query.item[1]]")
SSdiscord.post_webhook_event(WEBHOOK_CCIAA_EMERGENCY_MESSAGE, list("title"="Server Notification for: [user.ckey]", "message"="Server Notification Triggered for [user.ckey]: [query.item[1]]"))
//Immeidately ack the notification
autoack=1
if(autoack)
var/DBQuery/ackquery = GLOB.dbcon.NewQuery({"UPDATE ss13_player_notifications
SET acked_by = 'autoack-server', acked_at = NOW()
WHERE id = :id:
"})
ackquery.Execute(list("id" = query.item[3]))
if(panel_notification)
new_notification("warning","You have <b>[notification_count] unread notifications!</b> Click <a href='byond://?JSlink=warnings;notification=:src_ref'>here</a> to review and acknowledge them!")
if(chat_notification)
to_chat(user,SPAN_WARNING("<BIG><B>You have unacknowledged notifications.</B></BIG><br>Click <a href='byond://?JSlink=warnings;notification=:src_ref'>here</a> to review and acknowledge them!"))
autoack = 1
if (autoack)
var/datum/db_query/ackquery = SSdbcore.NewQuery(
"UPDATE ss13_player_notifications SET acked_by = 'autoack-server', acked_at = NOW() WHERE id = :id",
list("id" = query.item[3]))
ackquery.SetSuccessCallback(CALLBACK(GLOBAL_PROC, GLOBAL_PROC_REF(qdel)))
ackquery.SetFailCallback(CALLBACK(GLOBAL_PROC, GLOBAL_PROC_REF(qdel)))
ackquery.ExecuteNoSleep(TRUE)
qdel(query)
if (panel_notification)
new_notification("warning", "You have <b>[notification_count] unread notifications!</b> Click <a href='byond://?JSlink=warnings;notification=:src_ref'>here</a> to review and acknowledge them!")
if (chat_notification)
to_chat(user, SPAN_WARNING("<BIG><B>You have unacknowledged notifications.</B></BIG><br>Click <a href='byond://?JSlink=warnings;notification=:src_ref'>here</a> to review and acknowledge them!"))
/*
* Helper proc for getting a count of active CCIA actions against the player's characters.
*/
/datum/preferences/proc/count_ccia_actions(var/client/user)
if (!user)
return null
return
if (!establish_db_connection(GLOB.dbcon))
log_world("ERROR: Error initiatlizing database connection while counting CCIA actions.")
return null
if (!SSdbcore.Connect())
return
var/DBQuery/prep_query = GLOB.dbcon.NewQuery("SELECT id FROM ss13_characters WHERE ckey = :ckey:")
prep_query.Execute(list("ckey" = user.ckey))
var/list/chars = list()
while (prep_query.NextRow())
chars += text2num(prep_query.item[1])
if (!chars.len)
return null
var/DBQuery/query = GLOB.dbcon.NewQuery({"SELECT
COUNT(act_chr.action_id) AS action_count
FROM ss13_ccia_action_char act_chr
JOIN ss13_characters chr ON act_chr.char_id = chr.id
JOIN ss13_ccia_actions act ON act_chr.action_id = act.id
WHERE
act_chr.char_id IN :char_id: AND
(act.expires_at IS NULL OR act.expires_at >= CURRENT_DATE()) AND
act.deleted_at IS NULL;"})
query.Execute(list("char_id" = chars))
var/datum/db_query/actions_query = SSdbcore.NewQuery(
{"SELECT COUNT(act_chr.action_id) AS action_count
FROM ss13_ccia_action_char act_chr
JOIN ss13_characters chr ON act_chr.char_id = chr.id
JOIN ss13_ccia_actions act ON act_chr.action_id = act.id
WHERE
chr.ckey = :ckey AND
(act.expires_at IS NULL OR act.expires_at >= CURRENT_DATE()) AND
act.deleted_at IS NULL;"},
list("ckey" = user.ckey))
actions_query.SetSuccessCallback(CALLBACK(src, PROC_REF(_count_ccia_actions_cb)))
actions_query.SetFailCallback(CALLBACK(GLOBAL_PROC, GLOBAL_PROC_REF(qdel)))
actions_query.ExecuteNoSleep(TRUE)
/datum/preferences/proc/_count_ccia_actions_cb(datum/db_query/query)
if (query.NextRow())
var/action_count = text2num(query.item[1])
if (action_count == 0)
return null
var/string = "There are [action_count] active CCIA actions currently active against your character(s)."
return string
return null
if (action_count > 0)
new_notification("info", "There are [action_count] active CCIA actions currently active against your character(s).")
qdel(query)
+4 -3
View File
@@ -7,16 +7,17 @@
/datum/event/ccia_general_notice/start()
..()
if (!establish_db_connection(GLOB.dbcon))
LOG_DEBUG("CCIA Autoamtic General Notice - Could not establish database connection")
if (!SSdbcore.Connect())
LOG_DEBUG("CCIA Automatic General Notice - Could not establish database connection")
return
var/DBQuery/query = GLOB.dbcon.NewQuery("SELECT SQL_NO_CACHE title, message FROM ss13_ccia_general_notice_list WHERE deleted_at IS NULL AND automatic = 1 ORDER BY RAND() LIMIT 1;")
var/datum/db_query/query = SSdbcore.NewQuery("SELECT SQL_NO_CACHE title, message FROM ss13_ccia_general_notice_list WHERE deleted_at IS NULL AND automatic = 1 ORDER BY RAND() LIMIT 1;")
query.Execute()
if (query.NextRow())
reporttitle = query.item[1]
reportbody = query.item[2]
reportbody += "\n\n- CCIAAMS, [commstation_name()]"
announce = 1
qdel(query)
/datum/event/ccia_general_notice/announce()
if(announce)
+9 -6
View File
@@ -407,15 +407,18 @@ ABSTRACT_TYPE(/atom/movable/screen/new_player/selection)
/atom/movable/screen/new_player/selection/polls/Initialize()
. = ..()
if(establish_db_connection(GLOB.dbcon))
if(SSdbcore.Connect())
var/mob/M = hud.mymob
var/isadmin = M && M.client && M.client.holder
var/DBQuery/query = GLOB.dbcon.NewQuery("SELECT id FROM ss13_poll_question WHERE [(isadmin ? "" : "adminonly = false AND")] Now() BETWEEN starttime AND endtime AND id NOT IN (SELECT pollid FROM ss13_poll_vote WHERE ckey = \"[M.ckey]\") AND id NOT IN (SELECT pollid FROM ss13_poll_textreply WHERE ckey = \"[M.ckey]\")")
query.Execute()
var/newpoll = query.NextRow()
var/datum/db_query/query = SSdbcore.NewQuery("SELECT id FROM ss13_poll_question WHERE [(isadmin ? "" : "adminonly = false AND")] Now() BETWEEN starttime AND endtime AND id NOT IN (SELECT pollid FROM ss13_poll_vote WHERE ckey = :ckey) AND id NOT IN (SELECT pollid FROM ss13_poll_textreply WHERE ckey = :ckey) LIMIT 0,1", list("ckey" = M.ckey))
query.SetSuccessCallback(CALLBACK(src, PROC_REF(_polls_check_cb)))
query.SetFailCallback(CALLBACK(GLOBAL_PROC, GLOBAL_PROC_REF(qdel)))
query.ExecuteNoSleep(TRUE)
if(newpoll)
new_polls = TRUE
/atom/movable/screen/new_player/selection/polls/proc/_polls_check_cb(datum/db_query/query)
if (query.NextRow())
new_polls = TRUE
qdel(query)
/atom/movable/screen/new_player/selection/polls/Click()
var/mob/abstract/new_player/player = usr
@@ -303,15 +303,15 @@
if (!target || !player)
return
if (establish_db_connection(GLOB.dbcon) && target.character_id)
if (SSdbcore.Connect() && target.character_id)
var/status = FALSE
var/sql_status = FALSE
if (target.internal_organs_by_name[BP_IPCTAG])
status = TRUE
var/list/query_details = list("char_id" = target.character_id)
var/DBQuery/query = GLOB.dbcon.NewQuery("SELECT tag_status FROM ss13_characters_ipc_tags WHERE char_id = :char_id:")
query.Execute(query_details)
var/datum/db_query/query = SSdbcore.NewQuery("SELECT tag_status FROM ss13_characters_ipc_tags WHERE char_id = :char_id",query_details)
query.Execute()
if (query.NextRow())
sql_status = text2num(query.item[1])
@@ -319,8 +319,10 @@
return
query_details["status"] = status
var/DBQuery/update_query = GLOB.dbcon.NewQuery("UPDATE ss13_characters_ipc_tags SET tag_status = :status: WHERE char_id = :char_id:")
update_query.Execute(query_details)
var/datum/db_query/update_query = SSdbcore.NewQuery("UPDATE ss13_characters_ipc_tags SET tag_status = :status: WHERE char_id = :char_id",query_details)
update_query.Execute()
qdel(update_query)
qdel(query)
/datum/species/machine/get_light_color(mob/living/carbon/human/H)
if (!istype(H))
+22
View File
@@ -65,5 +65,27 @@
if (!ckey)
return
// Convert assoc-list connections to positional format for apply_ban_mirror,
// which reads extra_info[i][1] to extract ckeys for DB storage.
var/list/extra_info = list()
for(var/list/conn in telemetry_connections)
extra_info += list(list(conn["ckey"], conn["address"], conn["computer_id"]))
var/ban_id = 0
for(var/list/conn in telemetry_connections)
var/list/bdata = world.IsBanned(conn["ckey"], conn["address"], conn["computer_id"], 1, real_bans_only = TRUE, log_connection = FALSE)
if(bdata && bdata.len && !isnull(bdata["id"]))
ban_id = bdata["id"]
break
if(ban_id)
if(!client.holder)
log_and_message_admins("[ckey] from [client.address]-[client.computer_id] was caught bandodging. Mirror applied for ban #[ban_id], kicking shortly.")
apply_ban_mirror(ckey, client.address, client.computer_id, ban_id, 2, extra_info)
spawn(20)
del(client)
else
log_and_message_admins("[ckey] is a staff but was caught bandodging! Ban ID: #[ban_id].")
#undef TGUI_TELEMETRY_MAX_CONNECTIONS
#undef TGUI_TELEMETRY_RESPONSE_WINDOW
+3
View File
@@ -89,6 +89,9 @@
if(type == "audio/setAdminMusicVolume")
client.admin_music_volume = payload["volume"]
return TRUE
if(type == "telemetry")
analyze_telemetry(payload)
return TRUE
/**
* public
*
+5 -3
View File
@@ -82,13 +82,15 @@
alert("An error occured while attempting to connect to the database!")
return 0
var/DBQuery/insert_query = GLOB.dbcon.NewQuery("INSERT INTO ss13_web_sso (ckey, token, ip, created_at) VALUES (:ckey:, :token:, :ip:, NOW())")
insert_query.Execute(list("ckey" = user.ckey, "token" = token, "ip" = user.address))
var/datum/db_query/insert_query = SSdbcore.NewQuery("INSERT INTO ss13_web_sso (ckey, token, ip, created_at) VALUES (:ckey, :token, :ip, NOW())",list("ckey" = user.ckey, "token" = token, "ip" = user.address))
if (insert_query.ErrorMsg())
if (!insert_query.Execute())
alert("An error occured while trying to upload the session data!")
qdel(insert_query)
return 0
qdel(insert_query)
if (alert("This will take you to the webpage and log you in. Do you wish to proceed?",,"Yes","No") == "No")
return 0