Merge pull request #14270 from LetterN/rustsql

Removes bsql 2
This commit is contained in:
silicons
2021-03-03 00:53:46 -07:00
committed by GitHub
61 changed files with 2419 additions and 1596 deletions

View File

@@ -1,6 +0,0 @@
#define BSQL_EXTERNAL_CONFIGURATION
#define BSQL_DEL_PROC(path) ##path/Destroy()
#define BSQL_DEL_CALL(obj) qdel(##obj)
#define BSQL_IS_DELETED(obj) (QDELETED(obj))
#define BSQL_PROTECT_DATUM(path) GENERAL_PROTECT_DATUM(##path)
#define BSQL_ERROR(message) SSdbcore.ReportError(message)

View File

@@ -1,135 +0,0 @@
//BSQL - DMAPI
#define BSQL_VERSION "v1.3.0.0"
//types of connections
#define BSQL_CONNECTION_TYPE_MARIADB "MySql"
#define BSQL_CONNECTION_TYPE_SQLSERVER "SqlServer"
#define BSQL_DEFAULT_TIMEOUT 5
#define BSQL_DEFAULT_THREAD_LIMIT 50
//Call this before rebooting or shutting down your world to clean up gracefully. This invalidates all active connection and operation datums
/world/proc/BSQL_Shutdown()
return
/*
Called whenever a library call is made with verbose information, override and do with as you please
message: English debug message
*/
/world/proc/BSQL_Debug(msg)
return
/*
Create a new database connection, does not perform the actual connect
connection_type: The BSQL connection_type to use
asyncTimeout: The timeout to use for normal operations, 0 for infinite, defaults to BSQL_DEFAULT_TIMEOUT
blockingTimeout: The timeout to use for blocking operations, must be less than or equal to asyncTimeout, 0 for infinite, defaults to asyncTimeout
threadLimit: The limit of additional threads BSQL will run simultaneously, defaults to BSQL_DEFAULT_THREAD_LIMIT
*/
/datum/BSQL_Connection/New(connection_type, asyncTimeout, blockingTimeout, threadLimit)
return ..()
/*
Starts an operation to connect to a database. Should only have 1 successful call
ipaddress: The ip/hostname of the target server
port: The port of the target server
username: The username to login to the target server
password: The password for the target server
database: Optional database to connect to. Must be used when trying to do database operations, `USE x` is not sufficient
Returns: A /datum/BSQL_Operation representing the connection or null if an error occurred
*/
/datum/BSQL_Connection/proc/BeginConnect(ipaddress, port, username, password, database)
return
/*
Properly quotes a string for use by the database. The connection must be open for this proc to succeed
str: The string to quote
Returns: The string quoted on success, null on error
*/
/datum/BSQL_Connection/proc/Quote(str)
return
/*
Starts an operation for a query
query: The text of the query. Only one query allowed per invocation, no semicolons
Returns: A /datum/BSQL_Operation/Query representing the running query and subsequent result set or null if an error occurred
Note for MariaDB: The underlying connection is pooled. In order to use connection state based properties (i.e. LAST_INSERT_ID()) you can guarantee multiple queries will use the same connection by running BSQL_DEL_CALL(query) on the finished /datum/BSQL_Operation/Query and then creating the next one with another call to BeginQuery() with no sleeps in between
*/
/datum/BSQL_Connection/proc/BeginQuery(query)
return
/*
Checks if the operation is complete. This, in some cases must be called multiple times with false return before a result is present regardless of timespan. For best performance check it once per tick
Returns: TRUE if the operation is complete, FALSE if it's not, null on error
*/
/datum/BSQL_Operation/proc/IsComplete()
return
/*
Blocks the entire game until the given operation completes. IsComplete should not be checked after calling this to avoid potential side effects.
Returns: TRUE on success, FALSE if the operation wait time exceeded the connection's blockingTimeout setting
*/
/datum/BSQL_Operation/proc/WaitForCompletion()
return
/*
Get the error message associated with an operation. Should not be used while IsComplete() returns FALSE
Returns: The error message, if any. null otherwise
*/
/datum/BSQL_Operation/proc/GetError()
return
/*
Get the error code associated with an operation. Should not be used while IsComplete() returns FALSE
Returns: The error code, if any. null otherwise
*/
/datum/BSQL_Operation/proc/GetErrorCode()
return
/*
Gets an associated list of column name -> value representation of the most recent row in the query. Only valid if IsComplete() returns TRUE. If this returns null and no errors are present there are no more results in the query. Important to note that once IsComplete() returns TRUE it must not be called again without checking this or the row values may be lost
Returns: An associated list of column name -> value for the row. Values will always be either strings or null
*/
/datum/BSQL_Operation/Query/proc/CurrentRow()
return
/*
Code configuration options below
Define this to avoid modifying this file but the following defines must be declared somewhere else before BSQL/includes.dm is included
*/
#ifndef BSQL_EXTERNAL_CONFIGURATION
//Modify this if you disagree with byond's GC schemes. Ensure this is called for all connections and operations when they are deleted or they will leak native resources until /world/proc/BSQL_Shutdown() is called
#define BSQL_DEL_PROC(path) ##path/Del()
//The equivalent of calling del() in your codebase
#define BSQL_DEL_CALL(obj) del(##obj)
//Returns TRUE if an object is delete
#define BSQL_IS_DELETED(obj) (obj == null)
//Modify this to add protections to the connection and query datums
#define BSQL_PROTECT_DATUM(path)
//Modify this to change up error handling for the library
#define BSQL_ERROR(message) CRASH("BSQL: [##message]")
#endif
/*
Copyright 2018 Jordan Brown
Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*/

View File

@@ -240,3 +240,9 @@ GLOBAL_LIST_INIT(glass_sheet_types, typecacheof(list(
#define isshuttleturf(T) (length(T.baseturfs) && (/turf/baseturf_skipover/shuttle in T.baseturfs))
#define isProbablyWallMounted(O) (O.pixel_x > 20 || O.pixel_x < -20 || O.pixel_y > 20 || O.pixel_y < -20)
#define isbook(O) (is_type_in_typecache(O, GLOB.book_types))
GLOBAL_LIST_INIT(book_types, typecacheof(list(
/obj/item/book,
/obj/item/spellbook,
/obj/item/storage/book)))

View File

@@ -1,6 +1,6 @@
// tgstation-server DMAPI
#define TGS_DMAPI_VERSION "5.2.10"
#define TGS_DMAPI_VERSION "5.2.9"
// All functions and datums outside this document are subject to change with any version and should not be relied on.
@@ -67,7 +67,7 @@
#define TGS_EVENT_REPO_CHECKOUT 1
/// When the repository performs a fetch operation. No parameters
#define TGS_EVENT_REPO_FETCH 2
/// When the repository test merges. Parameters: PR Number, PR Sha, (Nullable) Comment made by TGS user
/// When the repository merges a pull request. Parameters: PR Number, PR Sha, (Nullable) Comment made by TGS user
#define TGS_EVENT_REPO_MERGE_PULL_REQUEST 3
/// Before the repository makes a sychronize operation. Parameters: Absolute repostiory path
#define TGS_EVENT_REPO_PRE_SYNCHRONIZE 4
@@ -190,21 +190,21 @@
/// Represents a merge of a GitHub pull request.
/datum/tgs_revision_information/test_merge
/// The test merge number.
/// The pull request number.
var/number
/// The test merge source's title when it was merged.
/// The pull request title when it was merged.
var/title
/// The test merge source's body when it was merged.
/// The pull request body when it was merged.
var/body
/// The Username of the test merge source's author.
/// The GitHub username of the pull request's author.
var/author
/// An http URL to the test merge source.
/// An http URL to the pull request.
var/url
/// The SHA of the test merge when that was merged.
/// The SHA of the pull request when that was merged.
var/pull_request_commit
/// ISO 8601 timestamp of when the test merge was created on TGS.
/// ISO 8601 timestamp of when the pull request was merged.
var/time_merged
/// Optional comment left by the TGS user who initiated the merge.
/// (Nullable) Comment left by the TGS user who initiated the merge..
var/comment
/// Represents a connected chat channel.

74
code/__HELPERS/chat.dm Normal file
View File

@@ -0,0 +1,74 @@
/*
Here's how to use the chat system with configs
send2adminchat is a simple function that broadcasts to admin channels
send2chat is a bit verbose but can be very specific
The second parameter is a string, this string should be read from a config.
What this does is dictacte which TGS4 channels can be sent to.
For example if you have the following channels in tgs4 set up
- Channel 1, Tag: asdf
- Channel 2, Tag: bombay,asdf
- Channel 3, Tag: Hello my name is asdf
- Channel 4, No Tag
- Channel 5, Tag: butts
and you make the call:
send2chat("I sniff butts", CONFIG_GET(string/where_to_send_sniff_butts))
and the config option is set like:
WHERE_TO_SEND_SNIFF_BUTTS asdf
It will be sent to channels 1 and 2
Alternatively if you set the config option to just:
WHERE_TO_SEND_SNIFF_BUTTS
it will be sent to all connected chats.
In TGS3 it will always be sent to all connected designated game chats.
*/
/**
* Sends a message to TGS chat channels.
*
* message - The message to send.
* channel_tag - Required. If "", the message with be sent to all connected (Game-type for TGS3) channels. Otherwise, it will be sent to TGS4 channels with that tag (Delimited by ','s).
*/
/proc/send2chat(message, channel_tag)
if(channel_tag == null || !world.TgsAvailable())
return
var/datum/tgs_version/version = world.TgsVersion()
if(channel_tag == "" || version.suite == 3)
world.TgsTargetedChatBroadcast(message, FALSE)
return
var/list/channels_to_use = list()
for(var/I in world.TgsChatChannelInfo())
var/datum/tgs_chat_channel/channel = I
var/list/applicable_tags = splittext(channel.custom_tag, ",")
if(channel_tag in applicable_tags)
channels_to_use += channel
if(channels_to_use.len)
world.TgsChatBroadcast(message, channels_to_use)
/**
* Sends a message to TGS admin chat channels.
*
* category - The category of the mssage.
* message - The message to send.
*/
/proc/send2adminchat(category, message, embed_links = FALSE)
category = replacetext(replacetext(category, "\proper", ""), "\improper", "")
message = replacetext(replacetext(message, "\proper", ""), "\improper", "")
// if(!embed_links)
// message = GLOB.has_discord_embeddable_links.Replace(replacetext(message, "`", ""), " ```$1``` ")
world.TgsTargetedChatBroadcast("[category] | [message]", TRUE)

View File

@@ -204,7 +204,7 @@
var/survival_rate = GLOB.joined_player_list.len ? "[PERCENT(popcount[POPCOUNT_SURVIVORS]/GLOB.joined_player_list.len)]%" : "there's literally no player"
send2irc("Server", "A round of [mode.name] just ended[mode_result == "undefined" ? "." : " with a [mode_result]."] Survival rate: [survival_rate]")
send2adminchat("Server", "A round of [mode.name] just ended[mode_result == "undefined" ? "." : " with a [mode_result]."] Survival rate: [survival_rate]")
if(length(CONFIG_GET(keyed_list/cross_server)))
send_news_report()
@@ -591,11 +591,9 @@
var/list/sql_admins = list()
for(var/i in GLOB.protected_admins)
var/datum/admins/A = GLOB.protected_admins[i]
var/sql_ckey = sanitizeSQL(A.target)
var/sql_rank = sanitizeSQL(A.rank.name)
sql_admins += list(list("ckey" = "'[sql_ckey]'", "rank" = "'[sql_rank]'"))
sql_admins += list(list("ckey" = A.target, "rank" = A.rank.name))
SSdbcore.MassInsert(format_table_name("admin"), sql_admins, duplicate_key = TRUE)
var/datum/DBQuery/query_admin_rank_update = SSdbcore.NewQuery("UPDATE [format_table_name("player")] p INNER JOIN [format_table_name("admin")] a ON p.ckey = a.ckey SET p.lastadminrank = a.rank")
var/datum/db_query/query_admin_rank_update = SSdbcore.NewQuery("UPDATE [format_table_name("player")] p INNER JOIN [format_table_name("admin")] a ON p.ckey = a.ckey SET p.lastadminrank = a.rank")
query_admin_rank_update.Execute()
qdel(query_admin_rank_update)
@@ -628,15 +626,20 @@
flags += "can_edit_flags"
if(!flags.len)
continue
var/sql_rank = sanitizeSQL(R.name)
var/flags_to_check = flags.Join(" != [R_EVERYTHING] AND ") + " != [R_EVERYTHING]"
var/datum/DBQuery/query_check_everything_ranks = SSdbcore.NewQuery("SELECT flags, exclude_flags, can_edit_flags FROM [format_table_name("admin_ranks")] WHERE rank = '[sql_rank]' AND ([flags_to_check])")
var/datum/db_query/query_check_everything_ranks = SSdbcore.NewQuery(
"SELECT flags, exclude_flags, can_edit_flags FROM [format_table_name("admin_ranks")] WHERE rank = :rank AND ([flags_to_check])",
list("rank" = R.name)
)
if(!query_check_everything_ranks.Execute())
qdel(query_check_everything_ranks)
return
if(query_check_everything_ranks.NextRow()) //no row is returned if the rank already has the correct flag value
var/flags_to_update = flags.Join(" = [R_EVERYTHING], ") + " = [R_EVERYTHING]"
var/datum/DBQuery/query_update_everything_ranks = SSdbcore.NewQuery("UPDATE [format_table_name("admin_ranks")] SET [flags_to_update] WHERE rank = '[sql_rank]'")
var/datum/db_query/query_update_everything_ranks = SSdbcore.NewQuery(
"UPDATE [format_table_name("admin_ranks")] SET [flags_to_update] WHERE rank = :rank",
list("rank" = R.name)
)
if(!query_update_everything_ranks.Execute())
qdel(query_update_everything_ranks)
return

View File

@@ -13,10 +13,6 @@
* SQL sanitization
*/
// Run all strings to be used in an SQL query through this proc first to properly escape out injection attempts.
/proc/sanitizeSQL(t)
return SSdbcore.Quote("[t]")
/proc/format_table_name(table as text)
return CONFIG_GET(string/feedback_tableprefix) + table

View File

@@ -1579,33 +1579,6 @@ GLOBAL_DATUM_INIT(dview_mob, /mob/dview, new)
for(var/i in 1 to items_list[each_item])
new each_item(where_to)
//sends a message to chat
//config_setting should be one of the following
//null - noop
//empty string - use TgsTargetBroadcast with admin_only = FALSE
//other string - use TgsChatBroadcast with the tag that matches config_setting, only works with TGS4, if using TGS3 the above method is used
/proc/send2chat(message, config_setting)
if(config_setting == null)
return
UNTIL(GLOB.tgs_initialized)
if(!world.TgsAvailable())
return
var/datum/tgs_version/version = world.TgsVersion()
if(config_setting == "" || version.suite == 3)
world.TgsTargetedChatBroadcast(message, FALSE)
return
var/list/channels_to_use = list()
for(var/I in world.TgsChatChannelInfo())
var/datum/tgs_chat_channel/channel = I
if(channel.tag == config_setting)
channels_to_use += channel
if(channels_to_use.len)
world.TgsChatBroadcast()
//Checks to see if either the victim has a garlic necklace or garlic in their blood
/proc/blood_sucking_checks(var/mob/living/carbon/target, check_neck, check_blood)
//Bypass this if the target isnt carbon.

View File

@@ -48,7 +48,7 @@
to_chat(src, "<span class='warning'>You're experiencing a bug. Reconnect immediately to fix it. Admins have been notified.</span>")
if(REALTIMEOFDAY >= chnotify + 9000)
chnotify = REALTIMEOFDAY
send2irc_adminless_only("NOCHEAT", message)
send2tgs_adminless_only("NOCHEAT", message)
return
var/list/modifiers = params2list(params)
@@ -113,7 +113,7 @@
A.AICtrlClick(src)
/mob/living/silicon/ai/AltClickOn(var/atom/A)
A.AIAltClick(src)
/*
The following criminally helpful code is just the previous code cleaned up;

View File

@@ -275,6 +275,11 @@
/datum/config_entry/flag/panic_bunker // prevents people the server hasn't seen before from connecting
/datum/config_entry/number/panic_bunker_living // living time in minutes that a player needs to pass the panic bunker
/datum/config_entry/string/panic_bunker_message
config_entry_value = "Sorry but the server is currently not accepting connections from never before seen players."
/datum/config_entry/number/notify_new_player_age // how long do we notify admins of a new player
min_val = -1

View File

@@ -5,10 +5,10 @@ SUBSYSTEM_DEF(blackbox)
runlevels = RUNLEVEL_GAME | RUNLEVEL_POSTGAME
init_order = INIT_ORDER_BLACKBOX
var/list/feedback = list() //list of datum/feedback_variable
var/list/feedback = list() //list of datum/feedback_variable
var/list/first_death = list() //the first death of this round, assoc. vars keep track of different things
var/triggertime = 0
var/sealed = FALSE //time to stop tracking stats?
var/sealed = FALSE //time to stop tracking stats?
var/list/versions = list("antagonists" = 3,
"admin_secrets_fun_used" = 2,
"explosion" = 2,
@@ -28,12 +28,12 @@ SUBSYSTEM_DEF(blackbox)
//poll population
/datum/controller/subsystem/blackbox/fire()
set waitfor = FALSE //for population query
set waitfor = FALSE //for population query
CheckPlayerCount()
if(CONFIG_GET(flag/use_exp_tracking))
if((triggertime < 0) || (world.time > (triggertime +3000))) //subsystem fires once at roundstart then once every 10 minutes. a 5 min check skips the first fire. The <0 is midnight rollover check
if((triggertime < 0) || (world.time > (triggertime +3000))) //subsystem fires once at roundstart then once every 10 minutes. a 5 min check skips the first fire. The <0 is midnight rollover check
update_exp(10,FALSE)
/datum/controller/subsystem/blackbox/proc/CheckPlayerCount()
@@ -43,7 +43,17 @@ SUBSYSTEM_DEF(blackbox)
return
var/playercount = LAZYLEN(GLOB.player_list)
var/admincount = GLOB.admins.len
var/datum/DBQuery/query_record_playercount = SSdbcore.NewQuery("INSERT INTO [format_table_name("legacy_population")] (playercount, admincount, time, server_ip, server_port, round_id) VALUES ([playercount], [admincount], '[SQLtime()]', INET_ATON(IF('[world.internet_address]' LIKE '', '0', '[world.internet_address]')), '[world.port]', '[GLOB.round_id]')")
var/datum/db_query/query_record_playercount = SSdbcore.NewQuery({"
INSERT INTO [format_table_name("legacy_population")] (playercount, admincount, time, server_ip, server_port, round_id)
VALUES (:playercount, :admincount, :time, INET_ATON(:server_ip), :server_port, :round_id)
"}, list(
"playercount" = playercount,
"admincount" = admincount,
"time" = SQLtime(),
"server_ip" = world.internet_address || "0",
"server_port" = "[world.port]",
"round_id" = GLOB.round_id,
))
query_record_playercount.Execute()
qdel(query_record_playercount)
@@ -87,24 +97,23 @@ SUBSYSTEM_DEF(blackbox)
if (!SSdbcore.Connect())
return
// var/list/special_columns = list(
// "datetime" = "NOW()"
// )
var/list/special_columns = list(
"datetime" = "NOW()"
)
var/list/sqlrowlist = list()
for (var/datum/feedback_variable/FV in feedback)
sqlrowlist += list(list(
"datetime" = "Now()", //legacy
"round_id" = GLOB.round_id,
"key_name" = sanitizeSQL(FV.key),
"key_name" = FV.key,
"key_type" = FV.key_type,
"version" = versions[FV.key] || 1,
"json" = sanitizeSQL(json_encode(FV.json))
"json" = json_encode(FV.json)
))
if (!length(sqlrowlist))
return
SSdbcore.MassInsert(format_table_name("feedback"), sqlrowlist, ignore_errors = TRUE, delayed = TRUE)//, special_columns = special_columns)
SSdbcore.MassInsert(format_table_name("feedback"), sqlrowlist, ignore_errors = TRUE, delayed = TRUE, special_columns = special_columns)
/datum/controller/subsystem/blackbox/proc/Seal()
if(sealed)
@@ -162,13 +171,13 @@ feedback data can be recorded in 5 formats:
used for simple single-string records i.e. the current map
further calls to the same key will append saved data unless the overwrite argument is true or it already exists
when encoded calls made with overwrite will lack square brackets
calls: SSblackbox.record_feedback("text", "example", 1, "sample text")
calls: SSblackbox.record_feedback("text", "example", 1, "sample text")
SSblackbox.record_feedback("text", "example", 1, "other text")
json: {"data":["sample text","other text"]}
"amount"
used to record simple counts of data i.e. the number of ahelps received
further calls to the same key will add or subtract (if increment argument is a negative) from the saved amount
calls: SSblackbox.record_feedback("amount", "example", 8)
calls: SSblackbox.record_feedback("amount", "example", 8)
SSblackbox.record_feedback("amount", "example", 2)
json: {"data":10}
"tally"
@@ -176,7 +185,7 @@ feedback data can be recorded in 5 formats:
further calls to the same key will:
add or subtract from the saved value of the data key if it already exists
append the key and it's value if it doesn't exist
calls: SSblackbox.record_feedback("tally", "example", 1, "sample data")
calls: SSblackbox.record_feedback("tally", "example", 1, "sample data")
SSblackbox.record_feedback("tally", "example", 4, "sample data")
SSblackbox.record_feedback("tally", "example", 2, "other data")
json: {"data":{"sample data":5,"other data":2}}
@@ -188,19 +197,19 @@ feedback data can be recorded in 5 formats:
further calls to the same key will:
add or subtract from the saved value of the data key if it already exists in the same multi-dimensional position
append the key and it's value if it doesn't exist
calls: SSblackbox.record_feedback("nested tally", "example", 1, list("fruit", "orange", "apricot"))
calls: SSblackbox.record_feedback("nested tally", "example", 1, list("fruit", "orange", "apricot"))
SSblackbox.record_feedback("nested tally", "example", 2, list("fruit", "orange", "orange"))
SSblackbox.record_feedback("nested tally", "example", 3, list("fruit", "orange", "apricot"))
SSblackbox.record_feedback("nested tally", "example", 10, list("fruit", "red", "apple"))
SSblackbox.record_feedback("nested tally", "example", 1, list("vegetable", "orange", "carrot"))
json: {"data":{"fruit":{"orange":{"apricot":4,"orange":2},"red":{"apple":10}},"vegetable":{"orange":{"carrot":1}}}}
tracking values associated with a number can't merge with a nesting value, trying to do so will append the list
call: SSblackbox.record_feedback("nested tally", "example", 3, list("fruit", "orange"))
call: SSblackbox.record_feedback("nested tally", "example", 3, list("fruit", "orange"))
json: {"data":{"fruit":{"orange":{"apricot":4,"orange":2},"red":{"apple":10},"orange":3},"vegetable":{"orange":{"carrot":1}}}}
"associative"
used to record text that's associated with a value i.e. coordinates
further calls to the same key will append a new list to existing data
calls: SSblackbox.record_feedback("associative", "example", 1, list("text" = "example", "path" = /obj/item, "number" = 4))
calls: SSblackbox.record_feedback("associative", "example", 1, list("text" = "example", "path" = /obj/item, "number" = 4))
SSblackbox.record_feedback("associative", "example", 1, list("number" = 7, "text" = "example", "other text" = "sample"))
json: {"data":{"1":{"text":"example","path":"/obj/item","number":"4"},"2":{"number":"7","text":"example","other text":"sample"}}}
@@ -275,7 +284,7 @@ Versioning
/datum/feedback_variable/New(new_key, new_key_type)
key = new_key
key_type = new_key_type
/*
/datum/controller/subsystem/blackbox/proc/LogAhelp(ticket, action, message, recipient, sender)
if(!SSdbcore.Connect())
return
@@ -286,7 +295,7 @@ Versioning
"}, list("ticket" = ticket, "action" = action, "message" = message, "recipient" = recipient, "sender" = sender, "server_ip" = world.internet_address || "0", "server_port" = world.port, "round_id" = GLOB.round_id, "time" = SQLtime()))
query_log_ahelp.Execute()
qdel(query_log_ahelp)
*/
/datum/controller/subsystem/blackbox/proc/ReportDeath(mob/living/L)
set waitfor = FALSE
@@ -302,51 +311,39 @@ Versioning
first_death["area"] = "[AREACOORD(L)]"
first_death["damage"] = "<font color='#FF5555'>[L.getBruteLoss()]</font>/<font color='orange'>[L.getFireLoss()]</font>/<font color='lightgreen'>[L.getToxLoss()]</font>/<font color='lightblue'>[L.getOxyLoss()]</font>/<font color='pink'>[L.getCloneLoss()]</font>"
first_death["last_words"] = L.last_words
var/sqlname = L.real_name
var/sqlkey = L.ckey
var/sqljob = L.mind.assigned_role
var/sqlspecial = L.mind.special_role
var/sqlpod = get_area_name(L, TRUE)
var/laname = L.lastattacker
var/lakey = L.lastattackerckey
var/sqlbrute = L.getBruteLoss()
var/sqlfire = L.getFireLoss()
var/sqlbrain = L.getOrganLoss(ORGAN_SLOT_BRAIN)
var/sqloxy = L.getOxyLoss()
var/sqltox = L.getToxLoss()
var/sqlclone = L.getCloneLoss()
var/sqlstamina = L.getStaminaLoss()
var/x_coord = L.x
var/y_coord = L.y
var/z_coord = L.z
var/last_words = L.last_words
var/suicide = L.suiciding
var/map = SSmapping.config.map_name
if(!SSdbcore.Connect())
return
sqlname = sanitizeSQL(sqlname)
sqlkey = sanitizeSQL(sqlkey)
sqljob = sanitizeSQL(sqljob)
sqlspecial = sanitizeSQL(sqlspecial)
sqlpod = sanitizeSQL(sqlpod)
laname = sanitizeSQL(laname)
lakey = sanitizeSQL(lakey)
sqlbrute = sanitizeSQL(sqlbrute)
sqlfire = sanitizeSQL(sqlfire)
sqlbrain = sanitizeSQL(sqlbrain)
sqloxy = sanitizeSQL(sqloxy)
sqltox = sanitizeSQL(sqltox)
sqlclone = sanitizeSQL(sqlclone)
sqlstamina = sanitizeSQL(sqlstamina)
x_coord = sanitizeSQL(x_coord)
y_coord = sanitizeSQL(y_coord)
z_coord = sanitizeSQL(z_coord)
last_words = sanitizeSQL(last_words)
suicide = sanitizeSQL(suicide)
map = sanitizeSQL(map)
var/datum/DBQuery/query_report_death = SSdbcore.NewQuery("INSERT INTO [format_table_name("death")] (pod, x_coord, y_coord, z_coord, mapname, server_ip, server_port, round_id, tod, job, special, name, byondkey, laname, lakey, bruteloss, fireloss, brainloss, oxyloss, toxloss, cloneloss, staminaloss, last_words, suicide) VALUES ('[sqlpod]', '[x_coord]', '[y_coord]', '[z_coord]', '[map]', INET_ATON(IF('[world.internet_address]' LIKE '', '0', '[world.internet_address]')), '[world.port]', [GLOB.round_id], '[SQLtime()]', '[sqljob]', '[sqlspecial]', '[sqlname]', '[sqlkey]', '[laname]', '[lakey]', [sqlbrute], [sqlfire], [sqlbrain], [sqloxy], [sqltox], [sqlclone], [sqlstamina], '[last_words]', [suicide])")
var/datum/db_query/query_report_death = SSdbcore.NewQuery({"
INSERT INTO [format_table_name("death")] (pod, x_coord, y_coord, z_coord, mapname, server_ip, server_port, round_id, tod, job, special, name, byondkey, laname, lakey, bruteloss, fireloss, brainloss, oxyloss, toxloss, cloneloss, staminaloss, last_words, suicide)
VALUES (:pod, :x_coord, :y_coord, :z_coord, :map, INET_ATON(:internet_address), :port, :round_id, :time, :job, :special, :name, :key, :laname, :lakey, :brute, :fire, :brain, :oxy, :tox, :clone, :stamina, :last_words, :suicide)
"}, list(
"name" = L.real_name,
"key" = L.ckey,
"job" = L.mind.assigned_role,
"special" = L.mind.special_role,
"pod" = get_area_name(L, TRUE),
"laname" = L.lastattacker,
"lakey" = L.lastattackerckey,
"brute" = L.getBruteLoss(),
"fire" = L.getFireLoss(),
"brain" = L.getOrganLoss(ORGAN_SLOT_BRAIN) || BRAIN_DAMAGE_DEATH, //getOrganLoss returns null without a brain but a value is required for this column
"oxy" = L.getOxyLoss(),
"tox" = L.getToxLoss(),
"clone" = L.getCloneLoss(),
"stamina" = L.getStaminaLoss(),
"x_coord" = L.x,
"y_coord" = L.y,
"z_coord" = L.z,
"last_words" = L.last_words,
"suicide" = L.suiciding,
"map" = SSmapping.config.map_name,
"internet_address" = world.internet_address || "0",
"port" = "[world.port]",
"round_id" = GLOB.round_id,
"time" = SQLtime(),
))
if(query_report_death)
query_report_death.Execute(async = TRUE)
qdel(query_report_death)

View File

@@ -3,7 +3,7 @@ SUBSYSTEM_DEF(dbcore)
flags = SS_BACKGROUND
wait = 1 MINUTES
init_order = INIT_ORDER_DBCORE
var/const/FAILED_DB_CONNECTION_CUTOFF = 5
var/failed_connection_timeout = 0
var/schema_mismatch = 0
var/db_minor = 0
@@ -13,8 +13,7 @@ SUBSYSTEM_DEF(dbcore)
var/last_error
var/list/active_queries = list()
var/datum/BSQL_Connection/connection
var/datum/BSQL_Operation/connectOperation
var/connection // Arbitrary handle returned from rust_g.
/datum/controller/subsystem/dbcore/Initialize()
//We send warnings to the admins during subsystem init, as the clients will be New'd and messages
@@ -29,7 +28,7 @@ SUBSYSTEM_DEF(dbcore)
/datum/controller/subsystem/dbcore/fire()
for(var/I in active_queries)
var/datum/DBQuery/Q = I
var/datum/db_query/Q = I
if(world.time - Q.last_activity_time > (5 MINUTES))
message_admins("Found undeleted query, please check the server logs and notify coders.")
log_sql("Undeleted query: \"[Q.sql]\" LA: [Q.last_activity] LAT: [Q.last_activity_time]")
@@ -39,24 +38,25 @@ SUBSYSTEM_DEF(dbcore)
/datum/controller/subsystem/dbcore/Recover()
connection = SSdbcore.connection
connectOperation = SSdbcore.connectOperation
/datum/controller/subsystem/dbcore/Shutdown()
//This is as close as we can get to the true round end before Disconnect() without changing where it's called, defeating the reason this is a subsystem
if(SSdbcore.Connect())
var/datum/DBQuery/query_round_shutdown = SSdbcore.NewQuery("UPDATE [format_table_name("round")] SET shutdown_datetime = Now(), end_state = '[sanitizeSQL(SSticker.end_state)]' WHERE id = [GLOB.round_id]")
var/datum/db_query/query_round_shutdown = SSdbcore.NewQuery(
"UPDATE [format_table_name("round")] SET shutdown_datetime = Now(), end_state = :end_state WHERE id = :round_id",
list("end_state" = SSticker.end_state, "round_id" = GLOB.round_id)
)
query_round_shutdown.Execute()
qdel(query_round_shutdown)
if(IsConnected())
Disconnect()
world.BSQL_Shutdown()
//nu
/datum/controller/subsystem/dbcore/can_vv_get(var_name)
return var_name != NAMEOF(src, connection) && var_name != NAMEOF(src, active_queries) && var_name != NAMEOF(src, connectOperation) && ..()
return var_name != NAMEOF(src, connection) && var_name != NAMEOF(src, active_queries) && ..()
/datum/controller/subsystem/dbcore/vv_edit_var(var_name, var_value)
if(var_name == NAMEOF(src, connection) || var_name == NAMEOF(src, connectOperation))
if(var_name == NAMEOF(src, connection))
return FALSE
return ..()
@@ -64,7 +64,11 @@ SUBSYSTEM_DEF(dbcore)
if(IsConnected())
return TRUE
if(failed_connections > FAILED_DB_CONNECTION_CUTOFF) //If it failed to establish a connection more than 5 times in a row, don't bother attempting to connect anymore.
if(failed_connection_timeout <= world.time) //it's been more than 5 seconds since we failed to connect, reset the counter
failed_connections = 0
if(failed_connections > 5) //If it failed to establish a connection more than 5 times in a row, don't bother attempting to connect for 5 seconds.
failed_connection_timeout = world.time + 50
return FALSE
if(!CONFIG_GET(flag/sql_enabled))
@@ -75,32 +79,33 @@ SUBSYSTEM_DEF(dbcore)
var/db = CONFIG_GET(string/feedback_database)
var/address = CONFIG_GET(string/address)
var/port = CONFIG_GET(number/port)
var/timeout = max(CONFIG_GET(number/async_query_timeout), CONFIG_GET(number/blocking_query_timeout))
var/thread_limit = CONFIG_GET(number/bsql_thread_limit)
connection = new /datum/BSQL_Connection(BSQL_CONNECTION_TYPE_MARIADB, CONFIG_GET(number/async_query_timeout), CONFIG_GET(number/blocking_query_timeout), CONFIG_GET(number/bsql_thread_limit))
var/error
if(QDELETED(connection))
connection = null
error = last_error
var/result = json_decode(rustg_sql_connect_pool(json_encode(list(
"host" = address,
"port" = port,
"user" = user,
"pass" = pass,
"db_name" = db,
"read_timeout" = timeout,
"write_timeout" = timeout,
"max_threads" = thread_limit,
))))
. = (result["status"] == "ok")
if (.)
connection = result["handle"]
else
SSdbcore.last_error = null
connectOperation = connection.BeginConnect(address, port, user, pass, db)
if(SSdbcore.last_error)
CRASH(SSdbcore.last_error)
UNTIL(connectOperation.IsComplete())
error = connectOperation.GetError()
. = !error
if (!.)
last_error = error
log_sql("Connect() failed | [error]")
connection = null
last_error = result["data"]
log_sql("Connect() failed | [last_error]")
++failed_connections
QDEL_NULL(connection)
QDEL_NULL(connectOperation)
/datum/controller/subsystem/dbcore/proc/CheckSchemaVersion()
if(CONFIG_GET(flag/sql_enabled))
if(Connect())
log_world("Database connection established.")
var/datum/DBQuery/query_db_version = NewQuery("SELECT major, minor FROM [format_table_name("schema_revision")] ORDER BY date DESC LIMIT 1")
var/datum/db_query/query_db_version = NewQuery("SELECT major, minor FROM [format_table_name("schema_revision")] ORDER BY date DESC LIMIT 1")
query_db_version.Execute()
if(query_db_version.NextRow())
db_major = text2num(query_db_version.item[1])
@@ -120,47 +125,46 @@ SUBSYSTEM_DEF(dbcore)
/datum/controller/subsystem/dbcore/proc/SetRoundID()
if(!Connect())
return
var/datum/DBQuery/query_round_initialize = SSdbcore.NewQuery("INSERT INTO [format_table_name("round")] (initialize_datetime, server_ip, server_port) VALUES (Now(), INET_ATON(IF('[world.internet_address]' LIKE '', '0', '[world.internet_address]')), '[world.port]')")
query_round_initialize.Execute()
var/datum/db_query/query_round_initialize = SSdbcore.NewQuery(
"INSERT INTO [format_table_name("round")] (initialize_datetime, server_ip, server_port) VALUES (Now(), INET_ATON(:internet_address), :port)",
list("internet_address" = world.internet_address || "0", "port" = "[world.port]")
)
query_round_initialize.Execute(async = FALSE)
GLOB.round_id = "[query_round_initialize.last_insert_id]"
qdel(query_round_initialize)
var/datum/DBQuery/query_round_last_id = SSdbcore.NewQuery("SELECT LAST_INSERT_ID()")
query_round_last_id.Execute()
if(query_round_last_id.NextRow())
GLOB.round_id = query_round_last_id.item[1]
qdel(query_round_last_id)
/datum/controller/subsystem/dbcore/proc/SetRoundStart()
if(!Connect())
return
var/datum/DBQuery/query_round_start = SSdbcore.NewQuery("UPDATE [format_table_name("round")] SET start_datetime = Now() WHERE id = [GLOB.round_id]")
var/datum/db_query/query_round_start = SSdbcore.NewQuery(
"UPDATE [format_table_name("round")] SET start_datetime = Now() WHERE id = :round_id",
list("round_id" = GLOB.round_id)
)
query_round_start.Execute()
qdel(query_round_start)
/datum/controller/subsystem/dbcore/proc/SetRoundEnd()
if(!Connect())
return
var/sql_station_name = sanitizeSQL(station_name())
var/datum/DBQuery/query_round_end = SSdbcore.NewQuery("UPDATE [format_table_name("round")] SET end_datetime = Now(), game_mode_result = '[sanitizeSQL(SSticker.mode_result)]', station_name = '[sql_station_name]' WHERE id = [GLOB.round_id]")
var/datum/db_query/query_round_end = SSdbcore.NewQuery(
"UPDATE [format_table_name("round")] SET end_datetime = Now(), game_mode_result = :game_mode_result, station_name = :station_name WHERE id = :round_id",
list("game_mode_result" = SSticker.mode_result, "station_name" = station_name(), "round_id" = GLOB.round_id)
)
query_round_end.Execute()
qdel(query_round_end)
/datum/controller/subsystem/dbcore/proc/Disconnect()
failed_connections = 0
QDEL_NULL(connectOperation)
QDEL_NULL(connection)
if (connection)
rustg_sql_disconnect_pool(connection)
connection = null
/datum/controller/subsystem/dbcore/proc/IsConnected()
if(!CONFIG_GET(flag/sql_enabled))
if (!CONFIG_GET(flag/sql_enabled))
return FALSE
//block until any connect operations finish
var/datum/BSQL_Connection/_connection = connection
var/datum/BSQL_Operation/op = connectOperation
UNTIL(QDELETED(_connection) || op.IsComplete())
return !QDELETED(connection) && !op.GetError()
/datum/controller/subsystem/dbcore/proc/Quote(str)
if(connection)
return connection.Quote(str)
if (!connection)
return FALSE
return json_decode(rustg_sql_connected(connection))["status"] == "online"
/datum/controller/subsystem/dbcore/proc/ErrorMsg()
if(!CONFIG_GET(flag/sql_enabled))
@@ -170,32 +174,34 @@ SUBSYSTEM_DEF(dbcore)
/datum/controller/subsystem/dbcore/proc/ReportError(error)
last_error = error
/datum/controller/subsystem/dbcore/proc/NewQuery(sql_query)
/datum/controller/subsystem/dbcore/proc/NewQuery(sql_query, arguments)
if(IsAdminAdvancedProcCall())
log_admin_private("ERROR: Advanced admin proc call led to sql query: [sql_query]. Query has been blocked")
message_admins("ERROR: Advanced admin proc call led to sql query. Query has been blocked")
return FALSE
return new /datum/DBQuery(sql_query, connection)
return new /datum/db_query(connection, sql_query, arguments)
/datum/controller/subsystem/dbcore/proc/QuerySelect(list/querys, warn = FALSE, qdel = FALSE)
if (!islist(querys))
if (!istype(querys, /datum/DBQuery))
if (!istype(querys, /datum/db_query))
CRASH("Invalid query passed to QuerySelect: [querys]")
querys = list(querys)
for (var/thing in querys)
var/datum/DBQuery/query = thing
var/datum/db_query/query = thing
if (warn)
INVOKE_ASYNC(query, /datum/DBQuery.proc/warn_execute)
INVOKE_ASYNC(query, /datum/db_query.proc/warn_execute)
else
INVOKE_ASYNC(query, /datum/DBQuery.proc/Execute)
INVOKE_ASYNC(query, /datum/db_query.proc/Execute)
for (var/thing in querys)
var/datum/DBQuery/query = thing
var/datum/db_query/query = thing
UNTIL(!query.in_progress)
if (qdel)
qdel(query)
/*
Takes a list of rows (each row being an associated list of column => value) and inserts them via a single mass query.
Rows missing columns present in other rows will resolve to SQL NULL
@@ -203,137 +209,135 @@ You are expected to do your own escaping of the data, and expected to provide yo
The duplicate_key arg can be true to automatically generate this part of the query
or set to a string that is appended to the end of the query
Ignore_errors instructes mysql to continue inserting rows if some of them have errors.
the erroneous row(s) aren't inserted and there isn't really any way to know why or why errored
the erroneous row(s) aren't inserted and there isn't really any way to know why or why errored
Delayed insert mode was removed in mysql 7 and only works with MyISAM type tables,
It was included because it is still supported in mariadb.
It does not work with duplicate_key and the mysql server ignores it in those cases
*/
/datum/controller/subsystem/dbcore/proc/MassInsert(table, list/rows, duplicate_key = FALSE, ignore_errors = FALSE, delayed = FALSE, warn = FALSE, async = TRUE)
/datum/controller/subsystem/dbcore/proc/MassInsert(table, list/rows, duplicate_key = FALSE, ignore_errors = FALSE, delayed = FALSE, warn = FALSE, async = TRUE, special_columns = null)
if (!table || !rows || !istype(rows))
return
// Prepare column list
var/list/columns = list()
var/list/sorted_rows = list()
var/list/has_question_mark = list()
for (var/list/row in rows)
var/list/sorted_row = list()
sorted_row.len = columns.len
for (var/column in row)
var/idx = columns[column]
if (!idx)
idx = columns.len + 1
columns[column] = idx
sorted_row.len = columns.len
columns[column] = "?"
has_question_mark[column] = TRUE
for (var/column in special_columns)
columns[column] = special_columns[column]
has_question_mark[column] = findtext(special_columns[column], "?")
sorted_row[idx] = row[column]
sorted_rows[++sorted_rows.len] = sorted_row
// Prepare SQL query full of placeholders
var/list/query_parts = list("INSERT")
if (delayed)
query_parts += " DELAYED"
if (ignore_errors)
query_parts += " IGNORE"
query_parts += " INTO "
query_parts += table
query_parts += "\n([columns.Join(", ")])\nVALUES"
var/list/arguments = list()
var/has_row = FALSE
for (var/list/row in rows)
if (has_row)
query_parts += ","
query_parts += "\n ("
var/has_col = FALSE
for (var/column in columns)
if (has_col)
query_parts += ", "
if (has_question_mark[column])
var/name = "p[arguments.len]"
query_parts += replacetext(columns[column], "?", ":[name]")
arguments[name] = row[column]
else
query_parts += columns[column]
has_col = TRUE
query_parts += ")"
has_row = TRUE
if (duplicate_key == TRUE)
var/list/column_list = list()
for (var/column in columns)
column_list += "[column] = VALUES([column])"
duplicate_key = "ON DUPLICATE KEY UPDATE [column_list.Join(", ")]\n"
else if (duplicate_key == FALSE)
duplicate_key = null
query_parts += "\nON DUPLICATE KEY UPDATE [column_list.Join(", ")]"
else if (duplicate_key != FALSE)
query_parts += duplicate_key
if (ignore_errors)
ignore_errors = " IGNORE"
else
ignore_errors = null
if (delayed)
delayed = " DELAYED"
else
delayed = null
var/list/sqlrowlist = list()
var/len = columns.len
for (var/list/row in sorted_rows)
if (length(row) != len)
row.len = len
for (var/value in row)
if (value == null)
value = "NULL"
sqlrowlist += "([row.Join(", ")])"
sqlrowlist = " [sqlrowlist.Join(",\n ")]"
var/datum/DBQuery/Query = NewQuery("INSERT[delayed][ignore_errors] INTO [table]\n([columns.Join(", ")])\nVALUES\n[sqlrowlist]\n[duplicate_key]")
var/datum/db_query/Query = NewQuery(query_parts.Join(), arguments)
if (warn)
. = Query.warn_execute(async)
else
. = Query.Execute(async)
qdel(Query)
/datum/DBQuery
var/sql // The sql query being executed.
var/list/item //list of data values populated by NextRow()
/datum/db_query
// Inputs
var/connection
var/sql
var/arguments
// Status information
var/in_progress
var/last_error
var/last_activity
var/last_activity_time
var/last_error
var/skip_next_is_complete
var/in_progress
var/datum/BSQL_Connection/connection
var/datum/BSQL_Operation/Query/query
// Output
var/list/list/rows
var/next_row_to_take = 1
var/affected
var/last_insert_id
/datum/DBQuery/New(sql_query, datum/BSQL_Connection/connection)
var/list/item //list of data values populated by NextRow()
/datum/db_query/New(connection, sql, arguments)
SSdbcore.active_queries[src] = TRUE
Activity("Created")
item = list()
src.connection = connection
sql = sql_query
/datum/DBQuery/Destroy()
src.connection = connection
src.sql = sql
src.arguments = arguments
/datum/db_query/Destroy()
Close()
SSdbcore.active_queries -= src
return ..()
/datum/DBQuery/CanProcCall(proc_name)
/datum/db_query/CanProcCall(proc_name)
//fuck off kevinz
return FALSE
/datum/DBQuery/proc/SetQuery(new_sql)
if(in_progress)
CRASH("Attempted to set new sql while waiting on active query")
Close()
sql = new_sql
/datum/DBQuery/proc/Activity(activity)
/datum/db_query/proc/Activity(activity)
last_activity = activity
last_activity_time = world.time
/datum/DBQuery/proc/warn_execute(async = FALSE)
/datum/db_query/proc/warn_execute(async = TRUE)
. = Execute(async)
if(!.)
to_chat(usr, "<span class='danger'>A SQL error occurred during this operation, check the server logs.</span>")
/datum/DBQuery/proc/Execute(async = FALSE, log_error = TRUE)
/datum/db_query/proc/Execute(async = TRUE, log_error = TRUE)
Activity("Execute")
if(in_progress)
CRASH("Attempted to start a new query while waiting on the old one")
if(QDELETED(connection))
if(!SSdbcore.IsConnected())
last_error = "No connection!"
return FALSE
var/start_time
var/timed_out
if(!async)
start_time = REALTIMEOFDAY
Close()
query = connection.BeginQuery(sql)
if(!async)
timed_out = !query.WaitForCompletion()
else
in_progress = TRUE
UNTIL(query.IsComplete())
in_progress = FALSE
skip_next_is_complete = TRUE
var/error = QDELETED(query) ? "Query object deleted!" : query.GetError()
last_error = error
. = !error
. = run_query(async)
var/timed_out = !. && findtext(last_error, "Operation timed out")
if(!. && log_error)
log_sql("[error] | Query used: [sql]")
log_sql("[last_error] | Query used: [sql] | Arguments: [json_encode(arguments)]")
if(!async && timed_out)
log_query_debug("Query execution started at [start_time]")
log_query_debug("Query execution ended at [REALTIMEOFDAY]")
@@ -341,44 +345,51 @@ Delayed insert mode was removed in mysql 7 and only works with MyISAM type table
log_query_debug("Query used: [sql]")
slow_query_check()
/datum/DBQuery/proc/slow_query_check()
/datum/db_query/proc/run_query(async)
var/job_result_str
if (async)
var/job_id = rustg_sql_query_async(connection, sql, json_encode(arguments))
in_progress = TRUE
UNTIL((job_result_str = rustg_sql_check_query(job_id)) != RUSTG_JOB_NO_RESULTS_YET)
in_progress = FALSE
if (job_result_str == RUSTG_JOB_ERROR)
last_error = job_result_str
return FALSE
else
job_result_str = rustg_sql_query_blocking(connection, sql, json_encode(arguments))
var/result = json_decode(job_result_str)
switch (result["status"])
if ("ok")
rows = result["rows"]
affected = result["affected"]
last_insert_id = result["last_insert_id"]
return TRUE
if ("err")
last_error = result["data"]
return FALSE
if ("offline")
last_error = "offline"
return FALSE
/datum/db_query/proc/slow_query_check()
message_admins("HEY! A database query timed out. Did the server just hang? <a href='?_src_=holder;[HrefToken()];slowquery=yes'>\[YES\]</a>|<a href='?_src_=holder;[HrefToken()];slowquery=no'>\[NO\]</a>")
/datum/DBQuery/proc/NextRow(async)
/datum/db_query/proc/NextRow(async = TRUE)
Activity("NextRow")
UNTIL(!in_progress)
if(!skip_next_is_complete)
if(!async)
query.WaitForCompletion()
else
in_progress = TRUE
UNTIL(query.IsComplete())
in_progress = FALSE
if (rows && next_row_to_take <= rows.len)
item = rows[next_row_to_take]
next_row_to_take++
return !!item
else
skip_next_is_complete = FALSE
return FALSE
last_error = query.GetError()
var/list/results = query.CurrentRow()
. = results != null
item.Cut()
//populate item array
for(var/I in results)
item += results[I]
/datum/DBQuery/proc/ErrorMsg()
/datum/db_query/proc/ErrorMsg()
return last_error
/datum/DBQuery/proc/Close()
item.Cut()
QDEL_NULL(query)
/world/BSQL_Debug(message)
if(!CONFIG_GET(flag/bsql_debug))
return
//strip sensitive stuff
if(findtext(message, ": CreateConnection("))
message = "CreateConnection CENSORED"
log_sql("BSQL_DEBUG: [message]")
/datum/db_query/proc/Close()
rows = null
item = null

View File

@@ -286,7 +286,9 @@ SUBSYSTEM_DEF(mapping)
setup_station_z_index()
if(SSdbcore.Connect())
var/datum/DBQuery/query_round_map_name = SSdbcore.NewQuery("UPDATE [format_table_name("round")] SET map_name = '[config.map_name]' WHERE id = [GLOB.round_id]")
var/datum/db_query/query_round_map_name = SSdbcore.NewQuery({"
UPDATE [format_table_name("round")] SET map_name = :map_name WHERE id = :round_id
"}, list("map_name" = config.map_name, "round_id" = GLOB.round_id))
query_round_map_name.Execute()
qdel(query_round_map_name)

View File

@@ -60,15 +60,10 @@ SUBSYSTEM_DEF(stickyban)
/datum/controller/subsystem/stickyban/proc/Populatedbcache()
var/newdbcache = list() //so if we runtime or the db connection dies we don't kill the existing cache
// var/datum/db_query/query_stickybans = SSdbcore.NewQuery("SELECT ckey, reason, banning_admin, datetime FROM [format_table_name("stickyban")] ORDER BY ckey")
// var/datum/db_query/query_ckey_matches = SSdbcore.NewQuery("SELECT stickyban, matched_ckey, first_matched, last_matched, exempt FROM [format_table_name("stickyban_matched_ckey")] ORDER BY first_matched")
// var/datum/db_query/query_cid_matches = SSdbcore.NewQuery("SELECT stickyban, matched_cid, first_matched, last_matched FROM [format_table_name("stickyban_matched_cid")] ORDER BY first_matched")
// var/datum/db_query/query_ip_matches = SSdbcore.NewQuery("SELECT stickyban, INET_NTOA(matched_ip), first_matched, last_matched FROM [format_table_name("stickyban_matched_ip")] ORDER BY first_matched")
var/datum/DBQuery/query_stickybans = SSdbcore.NewQuery("SELECT ckey, reason, banning_admin, datetime FROM [format_table_name("stickyban")] ORDER BY ckey")
var/datum/DBQuery/query_ckey_matches = SSdbcore.NewQuery("SELECT stickyban, matched_ckey, first_matched, last_matched, exempt FROM [format_table_name("stickyban_matched_ckey")] ORDER BY first_matched")
var/datum/DBQuery/query_cid_matches = SSdbcore.NewQuery("SELECT stickyban, matched_cid, first_matched, last_matched FROM [format_table_name("stickyban_matched_cid")] ORDER BY first_matched")
var/datum/DBQuery/query_ip_matches = SSdbcore.NewQuery("SELECT stickyban, INET_NTOA(matched_ip), first_matched, last_matched FROM [format_table_name("stickyban_matched_ip")] ORDER BY first_matched")
var/datum/db_query/query_stickybans = SSdbcore.NewQuery("SELECT ckey, reason, banning_admin, datetime FROM [format_table_name("stickyban")] ORDER BY ckey")
var/datum/db_query/query_ckey_matches = SSdbcore.NewQuery("SELECT stickyban, matched_ckey, first_matched, last_matched, exempt FROM [format_table_name("stickyban_matched_ckey")] ORDER BY first_matched")
var/datum/db_query/query_cid_matches = SSdbcore.NewQuery("SELECT stickyban, matched_cid, first_matched, last_matched FROM [format_table_name("stickyban_matched_cid")] ORDER BY first_matched")
var/datum/db_query/query_ip_matches = SSdbcore.NewQuery("SELECT stickyban, INET_NTOA(matched_ip), first_matched, last_matched FROM [format_table_name("stickyban_matched_ip")] ORDER BY first_matched")
SSdbcore.QuerySelect(list(query_stickybans, query_ckey_matches, query_cid_matches, query_ip_matches))
@@ -161,25 +156,15 @@ SUBSYSTEM_DEF(stickyban)
if (!ban["message"])
ban["message"] = "Evasion"
// TODO: USE NEW DB IMPLEMENTATION
var/datum/DBQuery/query_create_stickyban = SSdbcore.NewQuery(
"INSERT IGNORE INTO [format_table_name("stickyban")] (ckey, reason, banning_admin) VALUES ([ckey], [ban["message"]], ban["admin"]))"
var/datum/db_query/query_create_stickyban = SSdbcore.NewQuery(
"INSERT IGNORE INTO [format_table_name("stickyban")] (ckey, reason, banning_admin) VALUES (:ckey, :message, :admin)",
list("ckey" = ckey, "message" = ban["message"], "admin" = ban["admin"])
)
if (query_create_stickyban.warn_execute())
if (!query_create_stickyban.warn_execute())
qdel(query_create_stickyban)
return
qdel(query_create_stickyban)
// var/datum/db_query/query_create_stickyban = SSdbcore.NewQuery(
// "INSERT IGNORE INTO [format_table_name("stickyban")] (ckey, reason, banning_admin) VALUES (:ckey, :message, :admin)",
// list("ckey" = ckey, "message" = ban["message"], "admin" = ban["admin"])
// )
// if (!query_create_stickyban.warn_execute())
// qdel(query_create_stickyban)
// return
// qdel(query_create_stickyban)
var/list/sqlckeys = list()
var/list/sqlcids = list()
var/list/sqlips = list()

View File

@@ -268,7 +268,7 @@ SUBSYSTEM_DEF(ticker)
if(!GLOB.Debug2)
if(!can_continue)
log_game("[mode.name] failed pre_setup, cause: [mode.setup_error]")
send2irc("SSticker", "[mode.name] failed pre_setup, cause: [mode.setup_error]")
send2adminchat("SSticker", "[mode.name] failed pre_setup, cause: [mode.setup_error]")
message_admins("<span class='notice'>[mode.name] failed pre_setup, cause: [mode.setup_error]</span>")
QDEL_NULL(mode)
to_chat(world, "<B>Error setting up [GLOB.master_mode].</B> Reverting to pre-game lobby.")
@@ -334,7 +334,7 @@ SUBSYSTEM_DEF(ticker)
var/list/adm = get_admin_counts()
var/list/allmins = adm["present"]
send2irc("Server", "Round [GLOB.round_id ? "#[GLOB.round_id]:" : "of"] [hide_mode ? "secret":"[mode.name]"] has started[allmins.len ? ".":" with no active admins online!"]")
send2adminchat("Server", "Round [GLOB.round_id ? "#[GLOB.round_id]:" : "of"] [hide_mode ? "secret":"[mode.name]"] has started[allmins.len ? ".":" with no active admins online!"]")
setup_done = TRUE
for(var/i in GLOB.start_landmarks_list)

View File

@@ -5,8 +5,8 @@
switch(event_code)
if(TGS_EVENT_REBOOT_MODE_CHANGE)
var/list/reboot_mode_lookup = list ("[TGS_REBOOT_MODE_NORMAL]" = "be normal", "[TGS_REBOOT_MODE_SHUTDOWN]" = "shutdown the server", "[TGS_REBOOT_MODE_RESTART]" = "hard restart the server")
var old_reboot_mode = args[2]
var new_reboot_mode = args[3]
var/old_reboot_mode = args[2]
var/new_reboot_mode = args[3]
message_admins("TGS: Reboot will no longer [reboot_mode_lookup["[old_reboot_mode]"]], it will instead [reboot_mode_lookup["[new_reboot_mode]"]]")
if(TGS_EVENT_PORT_SWAP)
message_admins("TGS: Changing port from [world.port] to [args[2]]")
@@ -28,12 +28,14 @@
var/datum/tgs_version/old_version = world.TgsVersion()
var/datum/tgs_version/new_version = args[2]
if(!old_version.Equals(new_version))
to_chat(world, "<span class='boldannounce'>TGS updated to v[old_version.deprefixed_parameter]</span>")
to_chat(world, "<span class='boldannounce'>TGS updated to v[new_version.deprefixed_parameter]</span>")
else
message_admins("TGS: Back online")
if(reattach_timer)
deltimer(reattach_timer)
reattach_timer = null
if(TGS_EVENT_WATCHDOG_SHUTDOWN)
to_chat_immediate(world, "<span class='boldannounce'>Server is shutting down!</span>")
/datum/tgs_event_handler/impl/proc/LateOnReattach()
message_admins("Warning: TGS hasn't notified us of it coming back for a full minute! Is there a problem?")

View File

@@ -90,7 +90,7 @@
if(!is_new_ckey)
log_admin("AUTO BUNKER: [ckeytobypass] given access (incoming comms from [sender]).")
message_admins("AUTO BUNKER: [ckeytobypass] given access (incoming comms from [sender]).")
send2irc("Panic Bunker", "AUTO BUNKER: [ckeytobypass] given access (incoming comms from [sender]).")
send2adminchat("Panic Bunker", "AUTO BUNKER: [ckeytobypass] given access (incoming comms from [sender]).")
return "Success"
/datum/world_topic/ahelp_relay

View File

@@ -81,30 +81,43 @@
///Everyone should now be on the station and have their normal gear. This is the place to give the special roles extra things
/datum/game_mode/proc/post_setup(report) //Gamemodes can override the intercept report. Passing TRUE as the argument will force a report.
//finalize_monster_hunters() Disabled for now
if(!report)
report = !CONFIG_GET(flag/no_intercept_report)
addtimer(CALLBACK(GLOBAL_PROC, .proc/display_roundstart_logout_report), ROUNDSTART_LOGOUT_REPORT_TIME)
if(prob(20)) //CIT CHANGE - adds a 20% chance for the security level to be the opposite of what it normally is
if(prob(20)) //cit-change
flipseclevel = TRUE
// if(CONFIG_GET(flag/reopen_roundstart_suicide_roles))
// var/delay = CONFIG_GET(number/reopen_roundstart_suicide_roles_delay)
// if(delay)
// delay = (delay SECONDS)
// else
// delay = (4 MINUTES) //default to 4 minutes if the delay isn't defined.
// addtimer(CALLBACK(GLOBAL_PROC, .proc/reopen_roundstart_suicide_roles), delay)
if(SSdbcore.Connect())
var/sql
var/list/to_set = list()
var/arguments = list()
if(SSticker.mode)
sql += "game_mode = '[SSticker.mode]'"
to_set += "game_mode = :game_mode"
arguments["game_mode"] = SSticker.mode
if(GLOB.revdata.originmastercommit)
if(sql)
sql += ", "
sql += "commit_hash = '[GLOB.revdata.originmastercommit]'"
if(sql)
var/datum/DBQuery/query_round_game_mode = SSdbcore.NewQuery("UPDATE [format_table_name("round")] SET [sql] WHERE id = [GLOB.round_id]")
to_set += "commit_hash = :commit_hash"
arguments["commit_hash"] = GLOB.revdata.originmastercommit
if(to_set.len)
arguments["round_id"] = GLOB.round_id
var/datum/db_query/query_round_game_mode = SSdbcore.NewQuery(
"UPDATE [format_table_name("round")] SET [to_set.Join(", ")] WHERE id = :round_id",
arguments
)
query_round_game_mode.Execute()
qdel(query_round_game_mode)
if(report)
addtimer(CALLBACK(src, .proc/send_intercept, 0), rand(waittime_l, waittime_h))
generate_station_goals()
gamemode_ready = TRUE
return 1
return TRUE
///Handles late-join antag assignments

View File

@@ -80,8 +80,11 @@
var/client/banned_client = banned_mob?.client
var/banned_mob_guest_key = had_banned_mob && IsGuestKey(banned_mob.key)
banned_mob = null
var/sql_ckey = sanitizeSQL(ckey)
var/datum/DBQuery/query_add_ban_get_ckey = SSdbcore.NewQuery("SELECT 1 FROM [format_table_name("player")] WHERE ckey = '[sql_ckey]'")
var/datum/db_query/query_add_ban_get_ckey = SSdbcore.NewQuery({"
SELECT 1
FROM [format_table_name("player")]
WHERE ckey = :ckey"},
list("ckey" = ckey))
if(!query_add_ban_get_ckey.warn_execute())
qdel(query_add_ban_get_ckey)
return
@@ -122,10 +125,11 @@
else
adminwho += ", [C]"
reason = sanitizeSQL(reason)
var/sql_a_ckey = sanitizeSQL(a_ckey)
if(maxadminbancheck)
var/datum/DBQuery/query_check_adminban_amt = SSdbcore.NewQuery("SELECT count(id) AS num FROM [format_table_name("ban")] WHERE (a_ckey = '[sql_a_ckey]') AND (bantype = 'ADMIN_PERMABAN' OR (bantype = 'ADMIN_TEMPBAN' AND expiration_time > Now())) AND isnull(unbanned)")
var/datum/db_query/query_check_adminban_amt = SSdbcore.NewQuery({"
SELECT count(id) AS num FROM [format_table_name("ban")]
WHERE (a_ckey = :a_ckey) AND (bantype = 'ADMIN_PERMABAN' OR (bantype = 'ADMIN_TEMPBAN' AND expiration_time > Now())) AND isnull(unbanned)
"}, list("a_ckey" = a_ckey))
if(!query_check_adminban_amt.warn_execute())
qdel(query_check_adminban_amt)
return
@@ -143,13 +147,15 @@
computerid = "0"
if(!ip)
ip = "0.0.0.0"
var/sql_job = sanitizeSQL(job)
var/sql_computerid = sanitizeSQL(computerid)
var/sql_ip = sanitizeSQL(ip)
var/sql_a_computerid = sanitizeSQL(a_computerid)
var/sql_a_ip = sanitizeSQL(a_ip)
var/sql = "INSERT INTO [format_table_name("ban")] (`bantime`,`server_ip`,`server_port`,`round_id`,`bantype`,`reason`,`job`,`duration`,`expiration_time`,`ckey`,`computerid`,`ip`,`a_ckey`,`a_computerid`,`a_ip`,`who`,`adminwho`) VALUES (Now(), INET_ATON(IF('[world.internet_address]' LIKE '', '0', '[world.internet_address]')), '[world.port]', '[GLOB.round_id]', '[bantype_str]', '[reason]', '[sql_job]', [(duration)?"[duration]":"0"], Now() + INTERVAL [(duration>0) ? duration : 0] MINUTE, '[sql_ckey]', '[sql_computerid]', INET_ATON('[sql_ip]'), '[sql_a_ckey]', '[sql_a_computerid]', INET_ATON('[sql_a_ip]'), '[who]', '[adminwho]')"
var/datum/DBQuery/query_add_ban = SSdbcore.NewQuery(sql)
var/datum/db_query/query_add_ban = SSdbcore.NewQuery({"
INSERT INTO [format_table_name("ban")] (`bantime`,`server_ip`,`server_port`,`round_id`,`bantype`,`reason`,`job`,`duration`,`expiration_time`,`ckey`,`computerid`,`ip`,`a_ckey`,`a_computerid`,`a_ip`,`who`,`adminwho`)
VALUES (Now(), INET_ATON(:internet_address), :port, :round_id, :bantype_str, :reason, :job, :duration, Now() + INTERVAL :expiration_time MINUTE, :ckey, :computerid, INET_ATON(:ip), :a_ckey, :a_computerid, INET_ATON(:a_ip), :who, :adminwho)
"}, list("internet_address" = world.internet_address ? world.internet_address : 0,
"port" = world.port, "round_id" = GLOB.round_id, "bantype_str" = bantype_str, "reason" = reason,
"job" = job, "duration" = duration ? "[duration]":"0", "expiration_time" = (duration > 0) ? duration : 0,
"ckey" = ckey, "computerid" = computerid, "ip" = ip,
"a_ckey" = a_ckey, "a_computerid" = a_computerid, "a_ip" = a_ip, "who" = who, "adminwho" = adminwho
))
if(!query_add_ban.warn_execute())
qdel(query_add_ban)
return
@@ -160,7 +166,7 @@
var/datum/admin_help/AH = admin_ticket_log(ckey, msg)
if(announceinirc)
send2irc("BAN ALERT","[a_key] applied a [bantype_str] on [bankey]")
send2adminchat("BAN ALERT","[a_key] applied a [bantype_str] on [bankey]")
if(kickbannedckey)
if(AH)
@@ -212,11 +218,11 @@
bantype_sql = "(bantype = 'JOB_PERMABAN' OR (bantype = 'JOB_TEMPBAN' AND expiration_time > Now() ) )"
else
bantype_sql = "bantype = '[bantype_str]'"
var/sql_ckey = sanitizeSQL(ckey)
var/sql = "SELECT id FROM [format_table_name("ban")] WHERE ckey = '[sql_ckey]' AND [bantype_sql] AND (unbanned is null OR unbanned = false)"
var/sql = "SELECT id FROM [format_table_name("ban")] WHERE ckey = :ckey AND [bantype_sql] AND (unbanned is null OR unbanned = false)"
var/list/sql_args = list("ckey" = ckey)
if(job)
var/sql_job = sanitizeSQL(job)
sql += " AND job = '[sql_job]'"
sql += " AND job = :job"
sql_args["job"] = job
if(!SSdbcore.Connect())
return
@@ -224,7 +230,7 @@
var/ban_id
var/ban_number = 0 //failsafe
var/datum/DBQuery/query_unban_get_id = SSdbcore.NewQuery(sql)
var/datum/db_query/query_unban_get_id = SSdbcore.NewQuery(sql, sql_args)
if(!query_unban_get_id.warn_execute())
qdel(query_unban_get_id)
return
@@ -258,7 +264,7 @@
to_chat(usr, "Cancelled")
return
var/datum/DBQuery/query_edit_ban_get_details = SSdbcore.NewQuery("SELECT IFNULL((SELECT byond_key FROM [format_table_name("player")] WHERE [format_table_name("player")].ckey = [format_table_name("ban")].ckey), ckey), duration, reason FROM [format_table_name("ban")] WHERE id = [banid]")
var/datum/db_query/query_edit_ban_get_details = SSdbcore.NewQuery("SELECT IFNULL((SELECT byond_key FROM [format_table_name("player")] WHERE [format_table_name("player")].ckey = [format_table_name("ban")].ckey), ckey), duration, reason FROM [format_table_name("ban")] WHERE id = [banid]")
if(!query_edit_ban_get_details.warn_execute())
qdel(query_edit_ban_get_details)
return
@@ -278,19 +284,17 @@
return
qdel(query_edit_ban_get_details)
reason = sanitizeSQL(reason)
var/value
switch(param)
if("reason")
if(!value)
value = input("Insert the new reason for [p_key]'s ban", "New Reason", "[reason]", null) as null|text
value = sanitizeSQL(value)
if(!value)
to_chat(usr, "Cancelled")
return
var/datum/DBQuery/query_edit_ban_reason = SSdbcore.NewQuery("UPDATE [format_table_name("ban")] SET reason = '[value]', edits = CONCAT(edits,'- [e_key] changed ban reason from <cite><b>\\\"[reason]\\\"</b></cite> to <cite><b>\\\"[value]\\\"</b></cite><BR>') WHERE id = [banid]")
var/datum/db_query/query_edit_ban_reason = SSdbcore.NewQuery("UPDATE [format_table_name("ban")] SET reason = '[value]', edits = CONCAT(edits,'- [e_key] changed ban reason from <cite><b>\\\"[reason]\\\"</b></cite> to <cite><b>\\\"[value]\\\"</b></cite><BR>') WHERE id = [banid]")
if(!query_edit_ban_reason.warn_execute())
qdel(query_edit_ban_reason)
return
@@ -303,7 +307,7 @@
to_chat(usr, "Cancelled")
return
var/datum/DBQuery/query_edit_ban_duration = SSdbcore.NewQuery("UPDATE [format_table_name("ban")] SET duration = [value], edits = CONCAT(edits,'- [e_key] changed ban duration from [duration] to [value]<br>'), expiration_time = DATE_ADD(bantime, INTERVAL [value] MINUTE) WHERE id = [banid]")
var/datum/db_query/query_edit_ban_duration = SSdbcore.NewQuery("UPDATE [format_table_name("ban")] SET duration = [value], edits = CONCAT(edits,'- [e_key] changed ban duration from [duration] to [value]<br>'), expiration_time = DATE_ADD(bantime, INTERVAL [value] MINUTE) WHERE id = [banid]")
if(!query_edit_ban_duration.warn_execute())
qdel(query_edit_ban_duration)
return
@@ -333,7 +337,7 @@
var/ban_number = 0 //failsafe
var/p_key
var/datum/DBQuery/query_unban_get_ckey = SSdbcore.NewQuery(sql)
var/datum/db_query/query_unban_get_ckey = SSdbcore.NewQuery(sql)
if(!query_unban_get_ckey.warn_execute())
qdel(query_unban_get_ckey)
return
@@ -358,7 +362,7 @@
var/unban_ip = owner.address
var/sql_update = "UPDATE [format_table_name("ban")] SET unbanned = 1, unbanned_datetime = Now(), unbanned_ckey = '[unban_ckey]', unbanned_computerid = '[unban_computerid]', unbanned_ip = INET_ATON('[unban_ip]') WHERE id = [id]"
var/datum/DBQuery/query_unban = SSdbcore.NewQuery(sql_update)
var/datum/db_query/query_unban = SSdbcore.NewQuery(sql_update)
if(!query_unban.warn_execute())
qdel(query_unban)
return
@@ -448,20 +452,28 @@
if(adminckey || playerckey || ip || cid)
var/list/searchlist = list()
var/list/searchlist_args = list()
if(playerckey)
searchlist += "ckey = '[sanitizeSQL(ckey(playerckey))]'"
searchlist += "ckey = :playerckey"
searchlist_args["playerckey"] = playerckey
if(adminckey)
searchlist += "a_ckey = '[sanitizeSQL(ckey(adminckey))]'"
searchlist += "a_ckey = :adminckey"
searchlist_args["adminckey"] = adminckey
if(ip)
searchlist += "ip = INET_ATON('[sanitizeSQL(ip)]')"
searchlist += "ip = INET_ATON(:ip)"
searchlist_args["ip"] = ip
if(cid)
searchlist += "computerid = '[sanitizeSQL(cid)]'"
var/search = searchlist.Join(" AND ")
searchlist += "computerid = :cid"
searchlist_args["cid"] = cid
var/search = searchlist.Join(" AND ") // x = x AND y = z
var/bancount = 0
var/bansperpage = 15
var/pagecount = 0
page = text2num(page)
var/datum/DBQuery/query_count_bans = SSdbcore.NewQuery("SELECT COUNT(id) FROM [format_table_name("ban")] WHERE [search]")
var/datum/db_query/query_count_bans = SSdbcore.NewQuery({"
SELECT COUNT(id) FROM [format_table_name("ban")]
WHERE [search]
"}, searchlist_args)
if(!query_count_bans.warn_execute())
qdel(query_count_bans)
return
@@ -489,7 +501,11 @@
output += "<th width='15%'><b>OPTIONS</b></th>"
output += "</tr>"
var/limit = " LIMIT [bansperpage * page], [bansperpage]"
var/datum/DBQuery/query_search_bans = SSdbcore.NewQuery("SELECT id, bantime, bantype, reason, job, duration, expiration_time, IFNULL((SELECT byond_key FROM [format_table_name("player")] WHERE [format_table_name("player")].ckey = [format_table_name("ban")].ckey), ckey), IFNULL((SELECT byond_key FROM [format_table_name("player")] WHERE [format_table_name("player")].ckey = [format_table_name("ban")].a_ckey), a_ckey), unbanned, IFNULL((SELECT byond_key FROM [format_table_name("player")] WHERE [format_table_name("player")].ckey = [format_table_name("ban")].unbanned_ckey), unbanned_ckey), unbanned_datetime, edits, round_id FROM [format_table_name("ban")] WHERE [search] ORDER BY bantime DESC[limit]")
var/datum/db_query/query_search_bans = SSdbcore.NewQuery({"
SELECT id, bantime, bantype, reason, job, duration, expiration_time, IFNULL((SELECT byond_key FROM [format_table_name("player")] WHERE [format_table_name("player")].ckey = [format_table_name("ban")].ckey), ckey), IFNULL((SELECT byond_key FROM [format_table_name("player")] WHERE [format_table_name("player")].ckey = [format_table_name("ban")].a_ckey), a_ckey), unbanned, IFNULL((SELECT byond_key FROM [format_table_name("player")] WHERE [format_table_name("player")].ckey = [format_table_name("ban")].unbanned_ckey), unbanned_ckey), unbanned_datetime, edits, round_id
FROM [format_table_name("ban")]
WHERE [search] ORDER BY bantime DESC[limit]"}, searchlist_args)
if(!query_search_bans.warn_execute())
qdel(query_search_bans)
return

View File

@@ -100,7 +100,18 @@
if(computer_id)
cidquery = " OR computerid = '[computer_id]' "
var/datum/DBQuery/query_ban_check = SSdbcore.NewQuery("SELECT IFNULL((SELECT byond_key FROM [format_table_name("player")] WHERE [format_table_name("player")].ckey = [format_table_name("ban")].ckey), ckey), IFNULL((SELECT byond_key FROM [format_table_name("player")] WHERE [format_table_name("player")].ckey = [format_table_name("ban")].a_ckey), a_ckey), reason, expiration_time, duration, bantime, bantype, id, round_id FROM [format_table_name("ban")] WHERE (ckey = '[ckey]' [ipquery] [cidquery]) AND (bantype = 'PERMABAN' OR bantype = 'ADMIN_PERMABAN' OR ((bantype = 'TEMPBAN' OR bantype = 'ADMIN_TEMPBAN') AND expiration_time > Now())) AND isnull(unbanned)")
var/datum/db_query/query_ban_check = SSdbcore.NewQuery({"
SELECT IFNULL((SELECT byond_key
FROM [format_table_name("player")]
WHERE [format_table_name("player")].ckey = [format_table_name("ban")].ckey), ckey),
IFNULL((SELECT byond_key FROM [format_table_name("player")]
WHERE [format_table_name("player")].ckey = [format_table_name("ban")].a_ckey), a_ckey), reason, expiration_time, duration, bantime, bantype, id, round_id FROM [format_table_name("ban")]
WHERE (ckey = :ckey [ipquery] [cidquery])
AND (bantype = 'PERMABAN' OR bantype = 'ADMIN_PERMABAN' OR ((bantype = 'TEMPBAN' OR bantype = 'ADMIN_TEMPBAN')
AND expiration_time > Now())) AND isnull(unbanned)
"}, list(
"ckey" = ckey
))
if(!query_ban_check.Execute(async = TRUE))
qdel(query_ban_check)
key_cache[key] = 0

View File

@@ -119,16 +119,12 @@ GLOBAL_PROTECT(protected_ranks)
set waitfor = FALSE
if(IsAdminAdvancedProcCall())
to_chat(usr, "<span class='admin prefix'>Admin rank DB Sync blocked: Advanced ProcCall detected.</span>")
to_chat(usr, "<span class='admin prefix'>Admin rank DB Sync blocked: Advanced ProcCall detected.</span>", confidential = TRUE)
return
var/list/sql_ranks = list()
for(var/datum/admin_rank/R in GLOB.protected_ranks)
var/sql_rank = sanitizeSQL(R.name)
var/sql_flags = sanitizeSQL(R.include_rights)
var/sql_exclude_flags = sanitizeSQL(R.exclude_rights)
var/sql_can_edit_flags = sanitizeSQL(R.can_edit_rights)
sql_ranks += list(list("rank" = "'[sql_rank]'", "flags" = "[sql_flags]", "exclude_flags" = "[sql_exclude_flags]", "can_edit_flags" = "[sql_can_edit_flags]"))
sql_ranks += list(list("rank" = R.name, "flags" = R.include_rights, "exclude_flags" = R.exclude_rights, "can_edit_flags" = R.can_edit_rights))
SSdbcore.MassInsert(format_table_name("admin_ranks"), sql_ranks, duplicate_key = TRUE)
//load our rank - > rights associations
@@ -160,7 +156,7 @@ GLOBAL_PROTECT(protected_ranks)
if(!no_update)
sync_ranks_with_db()
else
var/datum/DBQuery/query_load_admin_ranks = SSdbcore.NewQuery("SELECT rank, flags, exclude_flags, can_edit_flags FROM [format_table_name("admin_ranks")]")
var/datum/db_query/query_load_admin_ranks = SSdbcore.NewQuery("SELECT `rank`, flags, exclude_flags, can_edit_flags FROM [format_table_name("admin_ranks")]")
if(!query_load_admin_ranks.Execute())
message_admins("Error loading admin ranks from database. Loading from backup.")
log_sql("Error loading admin ranks from database. Loading from backup.")
@@ -168,7 +164,7 @@ GLOBAL_PROTECT(protected_ranks)
else
while(query_load_admin_ranks.NextRow())
var/skip
var/rank_name = ckeyEx(query_load_admin_ranks.item[1])
var/rank_name = query_load_admin_ranks.item[1]
for(var/datum/admin_rank/R in GLOB.admin_ranks)
if(R.name == rank_name) //this rank was already loaded from txt override
skip = 1
@@ -234,20 +230,12 @@ GLOBAL_PROTECT(protected_ranks)
for(var/datum/admin_rank/R in GLOB.admin_ranks)
rank_names[R.name] = R
//ckeys listed in admins.txt are always made admins before sql loading is attempted
var/list/lines = world.file2list("[global.config.directory]/admins.txt")
for(var/line in lines)
if(!length(line) || findtextEx(line, "#", 1, 2))
continue
var/list/entry = splittext(line, "=")
if(entry.len < 2)
continue
var/ckey = ckey(entry[1])
var/rank = ckeyEx(entry[2])
if(!ckey || !rank)
continue
new /datum/admins(rank_names[rank], ckey, 0, 1)
var/admins_text = file2text("[global.config.directory]/admins.txt")
var/regex/admins_regex = new(@"^(?!#)(.+?)\s+=\s+(.+)", "gm")
while(admins_regex.Find(admins_text))
new /datum/admins(rank_names[admins_regex.group[2]], ckey(admins_regex.group[1]), FALSE, TRUE)
if(!CONFIG_GET(flag/admin_legacy_system) || dbfail)
var/datum/DBQuery/query_load_admins = SSdbcore.NewQuery("SELECT ckey, rank FROM [format_table_name("admin")] ORDER BY rank")
var/datum/db_query/query_load_admins = SSdbcore.NewQuery("SELECT ckey, `rank` FROM [format_table_name("admin")] ORDER BY `rank`")
if(!query_load_admins.Execute())
message_admins("Error loading admins from database. Loading from backup.")
log_sql("Error loading admins from database. Loading from backup.")
@@ -255,7 +243,7 @@ GLOBAL_PROTECT(protected_ranks)
else
while(query_load_admins.NextRow())
var/admin_ckey = ckey(query_load_admins.item[1])
var/admin_rank = ckeyEx(query_load_admins.item[2])
var/admin_rank = query_load_admins.item[2]
var/skip
if(rank_names[admin_rank] == null)
message_admins("[admin_ckey] loaded with invalid admin rank [admin_rank].")

View File

@@ -3,8 +3,11 @@
if(!M || !istype(M) || !M.ckey)
return FALSE
if(!M.client) //no cache. fallback to a datum/DBQuery
var/datum/DBQuery/query_jobban_check_ban = SSdbcore.NewQuery("SELECT reason FROM [format_table_name("ban")] WHERE ckey = '[sanitizeSQL(M.ckey)]' AND (bantype = 'JOB_PERMABAN' OR (bantype = 'JOB_TEMPBAN' AND expiration_time > Now())) AND isnull(unbanned) AND job = '[sanitizeSQL(rank)]'")
if(!M.client) //no cache. fallback to a datum/db_query
var/datum/db_query/query_jobban_check_ban = SSdbcore.NewQuery({"
SELECT reason FROM [format_table_name("ban")]
WHERE ckey = :ckey AND (bantype = 'JOB_PERMABAN' OR (bantype = 'JOB_TEMPBAN' AND expiration_time > Now())) AND isnull(unbanned) AND job = :rank
"}, list("ckey" = M.ckey, "rank" = rank))
if(!query_jobban_check_ban.warn_execute())
qdel(query_jobban_check_ban)
return
@@ -28,7 +31,10 @@
return
if(C && istype(C))
C.jobbancache = list()
var/datum/DBQuery/query_jobban_build_cache = SSdbcore.NewQuery("SELECT job, reason FROM [format_table_name("ban")] WHERE ckey = '[sanitizeSQL(C.ckey)]' AND (bantype = 'JOB_PERMABAN' OR (bantype = 'JOB_TEMPBAN' AND expiration_time > Now())) AND isnull(unbanned)")
var/datum/db_query/query_jobban_build_cache = SSdbcore.NewQuery({"
SELECT job, reason FROM [format_table_name("ban")]
WHERE ckey = :ckey AND (bantype = 'JOB_PERMABAN' OR (bantype = 'JOB_TEMPBAN' AND expiration_time > Now())) AND isnull(unbanned)
"}, list("ckey" = C.ckey))
if(!query_jobban_build_cache.warn_execute())
qdel(query_jobban_build_cache)
return

View File

@@ -34,8 +34,9 @@
var/endtime = input("Set end time for poll as format YYYY-MM-DD HH:MM:SS. All times in server time. HH:MM:SS is optional and 24-hour. Must be later than starting time for obvious reasons.", "Set end time", SQLtime()) as text
if(!endtime)
return
endtime = sanitizeSQL(endtime)
var/datum/DBQuery/query_validate_time = SSdbcore.NewQuery("SELECT IF(STR_TO_DATE('[endtime]','%Y-%c-%d %T') > NOW(), STR_TO_DATE('[endtime]','%Y-%c-%d %T'), 0)")
var/datum/db_query/query_validate_time = SSdbcore.NewQuery({"
SELECT IF(STR_TO_DATE(:endtime,'%Y-%c-%d %T') > NOW(), STR_TO_DATE(:endtime,'%Y-%c-%d %T'), 0)
"}, list("endtime" = endtime))
if(!query_validate_time.warn_execute() || QDELETED(usr) || !src)
qdel(query_validate_time)
return
@@ -63,11 +64,9 @@
dontshow = 0
else
return
var/sql_ckey = sanitizeSQL(ckey)
var/question = input("Write your question","Question") as message|null
if(!question)
return
question = sanitizeSQL(question)
var/list/sql_option_list = list()
if(polltype != POLLTYPE_TEXT)
var/add_option = 1
@@ -75,7 +74,6 @@
var/option = input("Write your option","Option") as message|null
if(!option)
return
option = sanitizeSQL(option)
var/default_percentage_calc = 0
if(polltype != POLLTYPE_IRV)
switch(alert("Should this option be included by default when poll result percentages are generated?",,"Yes","No","Cancel"))
@@ -92,34 +90,27 @@
var/descmax = ""
if(polltype == POLLTYPE_RATING)
minval = input("Set minimum rating value.","Minimum rating") as num|null
if(minval)
minval = sanitizeSQL(minval)
else if(minval == null)
if(minval == null)
return
maxval = input("Set maximum rating value.","Maximum rating") as num|null
if(maxval)
maxval = sanitizeSQL(maxval)
if(minval >= maxval)
to_chat(src, "Maximum rating value can't be less than or equal to minimum rating value")
continue
else if(maxval == null)
if(maxval == null)
return
descmin = input("Optional: Set description for minimum rating","Minimum rating description") as message|null
if(descmin)
descmin = sanitizeSQL(descmin)
else if(descmin == null)
if(descmin == null)
return
descmid = input("Optional: Set description for median rating","Median rating description") as message|null
if(descmid)
descmid = sanitizeSQL(descmid)
else if(descmid == null)
if(descmid == null)
return
descmax = input("Optional: Set description for maximum rating","Maximum rating description") as message|null
if(descmax)
descmax = sanitizeSQL(descmax)
else if(descmax == null)
if(descmax == null)
return
sql_option_list += list(list("text" = "'[option]'", "minval" = "'[minval]'", "maxval" = "'[maxval]'", "descmin" = "'[descmin]'", "descmid" = "'[descmid]'", "descmax" = "'[descmax]'", "default_percentage_calc" = "'[default_percentage_calc]'"))
sql_option_list += list(list(
"text" = option, "minval" = minval, "maxval" = maxval,
"descmin" = descmin, "descmid" = descmid, "descmax" = descmax,
"default_percentage_calc" = default_percentage_calc))
switch(alert(" ",,"Add option","Finish", "Cancel"))
if("Add option")
add_option = 1
@@ -129,14 +120,21 @@
return 0
var/m1 = "[key_name(usr)] has created a new server poll. Poll type: [polltype] - Admin Only: [adminonly ? "Yes" : "No"] - Question: [question]"
var/m2 = "[key_name_admin(usr)] has created a new server poll. Poll type: [polltype] - Admin Only: [adminonly ? "Yes" : "No"]<br>Question: [question]"
var/datum/DBQuery/query_polladd_question = SSdbcore.NewQuery("INSERT INTO [format_table_name("poll_question")] (polltype, starttime, endtime, question, adminonly, multiplechoiceoptions, createdby_ckey, createdby_ip, dontshow) VALUES ('[polltype]', '[starttime]', '[endtime]', '[question]', '[adminonly]', '[choice_amount]', '[sql_ckey]', INET_ATON('[address]'), '[dontshow]')")
var/datum/db_query/query_polladd_question = SSdbcore.NewQuery({"
INSERT INTO [format_table_name("poll_question")] (polltype, starttime, endtime, question, adminonly, multiplechoiceoptions, createdby_ckey, createdby_ip, dontshow)
VALUES (:polltype, :starttime, :endtime, :question, :adminonly, :choice_amount, :ckey, INET_ATON(:address), :dontshow)
"}, list(
"polltype" = polltype, "starttime" = starttime, "endtime" = endtime,
"question" = question, "adminonly" = adminonly, "choice_amount" = choice_amount,
"ckey" = ckey, "address" = address, "dontshow" = dontshow
))
if(!query_polladd_question.warn_execute())
qdel(query_polladd_question)
return
qdel(query_polladd_question)
if(polltype != POLLTYPE_TEXT)
var/pollid = 0
var/datum/DBQuery/query_get_id = SSdbcore.NewQuery("SELECT LAST_INSERT_ID()")
var/datum/db_query/query_get_id = SSdbcore.NewQuery("SELECT LAST_INSERT_ID()")
if(!query_get_id.warn_execute())
qdel(query_get_id)
return
@@ -145,6 +143,6 @@
qdel(query_get_id)
for(var/list/i in sql_option_list)
i |= list("pollid" = "'[pollid]'")
SSdbcore.MassInsert(format_table_name("poll_option"), sql_option_list, warn = 1)
SSdbcore.MassInsert(format_table_name("poll_option"), sql_option_list, warn = TRUE)
log_admin(m1)
message_admins(m2)

View File

@@ -29,27 +29,27 @@
return
if (!bypasscache)
var/datum/ipintel/cachedintel = SSipintel.cache[ip]
if (cachedintel && cachedintel.is_valid())
if (cachedintel?.is_valid())
cachedintel.cache = TRUE
return cachedintel
if(SSdbcore.Connect())
var/rating_bad = CONFIG_GET(number/ipintel_rating_bad)
var/datum/DBQuery/query_get_ip_intel = SSdbcore.NewQuery({"
var/datum/db_query/query_get_ip_intel = SSdbcore.NewQuery({"
SELECT date, intel, TIMESTAMPDIFF(MINUTE,date,NOW())
FROM [format_table_name("ipintel")]
WHERE
ip = INET_ATON('[ip]')
ip = INET_ATON(':ip')
AND ((
intel < [rating_bad]
intel < :rating_bad
AND
date + INTERVAL [CONFIG_GET(number/ipintel_save_good)] HOUR > NOW()
date + INTERVAL :save_good HOUR > NOW()
) OR (
intel >= [rating_bad]
intel >= :rating_bad
AND
date + INTERVAL [CONFIG_GET(number/ipintel_save_bad)] HOUR > NOW()
date + INTERVAL :save_bad HOUR > NOW()
))
"})
"}, list("ip" = ip, "rating_bad" = rating_bad, "save_good" = CONFIG_GET(number/ipintel_save_good), "save_bad" = CONFIG_GET(number/ipintel_save_bad)))
if(!query_get_ip_intel.Execute())
qdel(query_get_ip_intel)
return
@@ -67,12 +67,15 @@
if (updatecache && res.intel >= 0)
SSipintel.cache[ip] = res
if(SSdbcore.Connect())
var/datum/DBQuery/query_add_ip_intel = SSdbcore.NewQuery("INSERT INTO [format_table_name("ipintel")] (ip, intel) VALUES (INET_ATON('[ip]'), [res.intel]) ON DUPLICATE KEY UPDATE intel = VALUES(intel), date = NOW()")
var/datum/db_query/query_add_ip_intel = SSdbcore.NewQuery(
"INSERT INTO [format_table_name("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.Execute()
qdel(query_add_ip_intel)
/proc/ip_intel_query(ip, var/retryed=0)
/proc/ip_intel_query(ip, retryed=0)
. = -1 //default
if (!ip)
return
@@ -131,8 +134,3 @@
/proc/log_ipintel(text)
log_game("IPINTEL: [text]")
debug_admins("IPINTEL: [text]")

View File

@@ -15,21 +15,14 @@
else
output += "<br><a href='?_src_=holder;[HrefToken()];editrightsbrowserlog=1;editrightspage=0'>\[Log\]</a><br><a href='?_src_=holder;[HrefToken()];editrightsbrowsermanage=1'>\[Management\]</a>"
if(action == 1)
var/list/searchlist = list(" WHERE ")
if(target)
searchlist += "ckey = '[sanitizeSQL(target)]'"
if(operation)
if(target)
searchlist += " AND "
searchlist += "operation = '[sanitizeSQL(operation)]'"
var/search
if(searchlist.len > 1)
search = searchlist.Join("")
var/logcount = 0
var/logssperpage = 20
var/pagecount = 0
page = text2num(page)
var/datum/DBQuery/query_count_admin_logs = SSdbcore.NewQuery("SELECT COUNT(id) FROM [format_table_name("admin_log")][search]")
var/datum/db_query/query_count_admin_logs = SSdbcore.NewQuery(
"SELECT COUNT(id) FROM [format_table_name("admin_log")] WHERE (:target IS NULL OR adminckey = :target) AND (:operation IS NULL OR operation = :operation)",
list("target" = target, "operation" = operation)
)
if(!query_count_admin_logs.warn_execute())
qdel(query_count_admin_logs)
return
@@ -43,8 +36,20 @@
logcount -= logssperpage
pagecount++
output += "|"
var/limit = " LIMIT [logssperpage * page], [logssperpage]"
var/datum/DBQuery/query_search_admin_logs = SSdbcore.NewQuery("SELECT datetime, round_id, IFNULL((SELECT byond_key FROM [format_table_name("player")] WHERE ckey = adminckey), adminckey), operation, IF(ckey IS NULL, target, byond_key), log FROM [format_table_name("admin_log")] LEFT JOIN [format_table_name("player")] ON target = ckey[search] ORDER BY datetime DESC[limit]")
var/datum/db_query/query_search_admin_logs = SSdbcore.NewQuery({"
SELECT
datetime,
round_id,
IFNULL((SELECT byond_key FROM [format_table_name("player")] WHERE ckey = adminckey), adminckey),
operation,
IF(ckey IS NULL, target, byond_key),
log
FROM [format_table_name("admin_log")]
LEFT JOIN [format_table_name("player")] ON target = ckey
WHERE (:target IS NULL OR ckey = :target) AND (:operation IS NULL OR operation = :operation)
ORDER BY datetime DESC
LIMIT :skip, :take
"}, list("target" = target, "operation" = operation, "skip" = logssperpage * page, "take" = logssperpage))
if(!query_search_admin_logs.warn_execute())
qdel(query_search_admin_logs)
return
@@ -59,7 +64,7 @@
qdel(query_search_admin_logs)
if(action == 2)
output += "<h3>Admin ckeys with invalid ranks</h3>"
var/datum/DBQuery/query_check_admin_errors = SSdbcore.NewQuery("SELECT IFNULL((SELECT byond_key FROM [format_table_name("player")] WHERE [format_table_name("player")].ckey = [format_table_name("admin")].ckey), ckey), [format_table_name("admin")].rank FROM [format_table_name("admin")] LEFT JOIN [format_table_name("admin_ranks")] ON [format_table_name("admin_ranks")].rank = [format_table_name("admin")].rank WHERE [format_table_name("admin_ranks")].rank IS NULL")
var/datum/db_query/query_check_admin_errors = SSdbcore.NewQuery("SELECT IFNULL((SELECT byond_key FROM [format_table_name("player")] WHERE [format_table_name("player")].ckey = [format_table_name("admin")].ckey), ckey), [format_table_name("admin")].`rank` FROM [format_table_name("admin")] LEFT JOIN [format_table_name("admin_ranks")] ON [format_table_name("admin_ranks")].`rank` = [format_table_name("admin")].`rank` WHERE [format_table_name("admin_ranks")].`rank` IS NULL")
if(!query_check_admin_errors.warn_execute())
qdel(query_check_admin_errors)
return
@@ -70,7 +75,7 @@
output += "<hr style='background:#000000; border:0; height:1px'>"
qdel(query_check_admin_errors)
output += "<h3>Unused ranks</h3>"
var/datum/DBQuery/query_check_unused_rank = SSdbcore.NewQuery("SELECT [format_table_name("admin_ranks")].rank, flags, exclude_flags, can_edit_flags FROM [format_table_name("admin_ranks")] LEFT JOIN [format_table_name("admin")] ON [format_table_name("admin")].rank = [format_table_name("admin_ranks")].rank WHERE [format_table_name("admin")].rank IS NULL")
var/datum/db_query/query_check_unused_rank = SSdbcore.NewQuery("SELECT [format_table_name("admin_ranks")].`rank`, flags, exclude_flags, can_edit_flags FROM [format_table_name("admin_ranks")] LEFT JOIN [format_table_name("admin")] ON [format_table_name("admin")].`rank` = [format_table_name("admin_ranks")].`rank` WHERE [format_table_name("admin")].`rank` IS NULL")
if(!query_check_unused_rank.warn_execute())
qdel(query_check_unused_rank)
return
@@ -130,7 +135,7 @@
log_admin("[key_name(usr)] attempted to edit admin permissions without sufficient rights.")
return
if(IsAdminAdvancedProcCall())
to_chat(usr, "<span class='admin prefix'>Admin Edit blocked: Advanced ProcCall detected.</span>")
to_chat(usr, "<span class='admin prefix'>Admin Edit blocked: Advanced ProcCall detected.</span>", confidential = TRUE)
return
var/datum/asset/permissions_assets = get_asset_datum(/datum/asset/simple/permissions)
permissions_assets.send(src)
@@ -145,19 +150,19 @@
skip = TRUE
if(!CONFIG_GET(flag/admin_legacy_system) && CONFIG_GET(flag/protect_legacy_admins) && task == "rank")
if(admin_ckey in GLOB.protected_admins)
to_chat(usr, "<span class='admin prefix'>Editing the rank of this admin is blocked by server configuration.</span>")
to_chat(usr, "<span class='admin prefix'>Editing the rank of this admin is blocked by server configuration.</span>", confidential = TRUE)
return
if(!CONFIG_GET(flag/admin_legacy_system) && CONFIG_GET(flag/protect_legacy_ranks) && task == "permissions")
if(D.rank in GLOB.protected_ranks)
to_chat(usr, "<span class='admin prefix'>Editing the flags of this rank is blocked by server configuration.</span>")
to_chat(usr, "<span class='admin prefix'>Editing the flags of this rank is blocked by server configuration.</span>", confidential = TRUE)
return
if(CONFIG_GET(flag/load_legacy_ranks_only) && (task == "add" || task == "rank" || task == "permissions"))
to_chat(usr, "<span class='admin prefix'>Database rank loading is disabled, only temporary changes can be made to a rank's permissions and permanently creating a new rank is blocked.</span>")
to_chat(usr, "<span class='admin prefix'>Database rank loading is disabled, only temporary changes can be made to a rank's permissions and permanently creating a new rank is blocked.</span>", confidential = TRUE)
legacy_only = TRUE
if(check_rights(R_DBRANKS, FALSE))
if(!skip)
if(!SSdbcore.Connect())
to_chat(usr, "<span class='danger'>Unable to connect to database, changes are temporary only.</span>")
to_chat(usr, "<span class='danger'>Unable to connect to database, changes are temporary only.</span>", confidential = TRUE)
use_db = FALSE
else
use_db = alert("Permanent changes are saved to the database for future rounds, temporary changes will affect only the current round", "Permanent or Temporary?", "Permanent", "Temporary", "Cancel")
@@ -165,7 +170,6 @@
return
if(use_db == "Permanent")
use_db = TRUE
admin_ckey = sanitizeSQL(admin_ckey)
else
use_db = FALSE
if(QDELETED(usr))
@@ -209,26 +213,34 @@
if(!.)
return FALSE
if(!admin_ckey && (. in GLOB.admin_datums+GLOB.deadmins))
to_chat(usr, "<span class='danger'>[admin_key] is already an admin.</span>")
to_chat(usr, "<span class='danger'>[admin_key] is already an admin.</span>", confidential = TRUE)
return FALSE
if(use_db)
. = sanitizeSQL(.)
//if an admin exists without a datum they won't be caught by the above
var/datum/DBQuery/query_admin_in_db = SSdbcore.NewQuery("SELECT 1 FROM [format_table_name("admin")] WHERE ckey = '[.]'")
var/datum/db_query/query_admin_in_db = SSdbcore.NewQuery(
"SELECT 1 FROM [format_table_name("admin")] WHERE ckey = :ckey",
list("ckey" = .)
)
if(!query_admin_in_db.warn_execute())
qdel(query_admin_in_db)
return FALSE
if(query_admin_in_db.NextRow())
qdel(query_admin_in_db)
to_chat(usr, "<span class='danger'>[admin_key] already listed in admin database. Check the Management tab if they don't appear in the list of admins.</span>")
to_chat(usr, "<span class='danger'>[admin_key] already listed in admin database. Check the Management tab if they don't appear in the list of admins.</span>", confidential = TRUE)
return FALSE
qdel(query_admin_in_db)
var/datum/DBQuery/query_add_admin = SSdbcore.NewQuery("INSERT INTO [format_table_name("admin")] (ckey, rank) VALUES ('[.]', 'NEW ADMIN')")
var/datum/db_query/query_add_admin = SSdbcore.NewQuery(
"INSERT INTO [format_table_name("admin")] (ckey, `rank`) VALUES (:ckey, 'NEW ADMIN')",
list("ckey" = .)
)
if(!query_add_admin.warn_execute())
qdel(query_add_admin)
return FALSE
qdel(query_add_admin)
var/datum/DBQuery/query_add_admin_log = SSdbcore.NewQuery("INSERT INTO [format_table_name("admin_log")] (datetime, round_id, adminckey, adminip, operation, target, log) VALUES ('[SQLtime()]', '[GLOB.round_id]', '[sanitizeSQL(usr.ckey)]', INET_ATON('[sanitizeSQL(usr.client.address)]'), 'add admin', '[.]', 'New admin added: [.]')")
var/datum/db_query/query_add_admin_log = SSdbcore.NewQuery({"
INSERT INTO [format_table_name("admin_log")] (datetime, round_id, adminckey, adminip, operation, target, log)
VALUES (:time, :round_id, :adminckey, INET_ATON(:adminip), 'add admin', :target, CONCAT('New admin added: ', :target))
"}, list("time" = SQLtime(), "round_id" = "[GLOB.round_id]", "adminckey" = usr.ckey, "adminip" = usr.client.address, "target" = .))
if(!query_add_admin_log.warn_execute())
qdel(query_add_admin_log)
return FALSE
@@ -243,12 +255,18 @@
var/m1 = "[key_name_admin(usr)] removed [admin_key] from the admins list [use_db ? "permanently" : "temporarily"]"
var/m2 = "[key_name(usr)] removed [admin_key] from the admins list [use_db ? "permanently" : "temporarily"]"
if(use_db)
var/datum/DBQuery/query_add_rank = SSdbcore.NewQuery("DELETE FROM [format_table_name("admin")] WHERE ckey = '[admin_ckey]'")
var/datum/db_query/query_add_rank = SSdbcore.NewQuery(
"DELETE FROM [format_table_name("admin")] WHERE ckey = :ckey",
list("ckey" = admin_ckey)
)
if(!query_add_rank.warn_execute())
qdel(query_add_rank)
return
qdel(query_add_rank)
var/datum/DBQuery/query_add_rank_log = SSdbcore.NewQuery("INSERT INTO [format_table_name("admin_log")] (datetime, round_id, adminckey, adminip, operation, target, log) VALUES ('[SQLtime()]', '[GLOB.round_id]', '[sanitizeSQL(usr.ckey)]', INET_ATON('[sanitizeSQL(usr.client.address)]'), 'remove admin', '[admin_ckey]', 'Admin removed: [admin_ckey]')")
var/datum/db_query/query_add_rank_log = SSdbcore.NewQuery({"
INSERT INTO [format_table_name("admin_log")] (datetime, round_id, adminckey, adminip, operation, target, log)
VALUES (:time, :round_id, :adminckey, INET_ATON(:adminip), 'remove admin', :admin_ckey, CONCAT('Admin removed: ', :admin_ckey))
"}, list("time" = SQLtime(), "round_id" = "[GLOB.round_id]", "adminckey" = usr.ckey, "adminip" = usr.client.address, "admin_ckey" = admin_ckey))
if(!query_add_rank_log.warn_execute())
qdel(query_add_rank_log)
return
@@ -271,6 +289,14 @@
log_admin("[key_name(usr)] forcefully deadmined [admin_key]")
D.deactivate() //after logs so the deadmined admin can see the message.
/datum/admins/proc/auto_deadmin()
to_chat(owner, "<span class='interface'>You are now a normal player.</span>", confidential = TRUE)
var/old_owner = owner
deactivate()
message_admins("[old_owner] deadmined via auto-deadmin config.")
log_admin("[old_owner] deadmined via auto-deadmin config.")
return TRUE
/datum/admins/proc/change_admin_rank(admin_ckey, admin_key, use_db, datum/admins/D, legacy_only)
var/datum/admin_rank/R
var/list/rank_names = list()
@@ -281,7 +307,7 @@
rank_names[R.name] = R
var/new_rank = input("Please select a rank", "New rank") as null|anything in rank_names
if(new_rank == "*New Rank*")
new_rank = ckeyEx(input("Please input a new rank", "New custom rank") as text|null)
new_rank = input("Please input a new rank", "New custom rank") as text|null
if(!new_rank)
return
R = rank_names[new_rank]
@@ -294,10 +320,12 @@
var/m1 = "[key_name_admin(usr)] edited the admin rank of [admin_key] to [new_rank] [use_db ? "permanently" : "temporarily"]"
var/m2 = "[key_name(usr)] edited the admin rank of [admin_key] to [new_rank] [use_db ? "permanently" : "temporarily"]"
if(use_db)
new_rank = sanitizeSQL(new_rank)
//if a player was tempminned before having a permanent change made to their rank they won't yet be in the db
var/old_rank
var/datum/DBQuery/query_admin_in_db = SSdbcore.NewQuery("SELECT rank FROM [format_table_name("admin")] WHERE ckey = '[admin_ckey]'")
var/datum/db_query/query_admin_in_db = SSdbcore.NewQuery(
"SELECT `rank` FROM [format_table_name("admin")] WHERE ckey = :admin_ckey",
list("admin_ckey" = admin_ckey)
)
if(!query_admin_in_db.warn_execute())
qdel(query_admin_in_db)
return
@@ -308,29 +336,44 @@
old_rank = query_admin_in_db.item[1]
qdel(query_admin_in_db)
//similarly if a temp rank is created it won't be in the db if someone is permanently changed to it
var/datum/DBQuery/query_rank_in_db = SSdbcore.NewQuery("SELECT 1 FROM [format_table_name("admin_ranks")] WHERE rank = '[new_rank]'")
var/datum/db_query/query_rank_in_db = SSdbcore.NewQuery(
"SELECT 1 FROM [format_table_name("admin_ranks")] WHERE `rank` = :new_rank",
list("new_rank" = new_rank)
)
if(!query_rank_in_db.warn_execute())
qdel(query_rank_in_db)
return
if(!query_rank_in_db.NextRow())
QDEL_NULL(query_rank_in_db)
var/datum/DBQuery/query_add_rank = SSdbcore.NewQuery("INSERT INTO [format_table_name("admin_ranks")] (rank, flags, exclude_flags, can_edit_flags) VALUES ('[new_rank]', '0', '0', '0')")
var/datum/db_query/query_add_rank = SSdbcore.NewQuery({"
INSERT INTO [format_table_name("admin_ranks")] (`rank`, flags, exclude_flags, can_edit_flags)
VALUES (:new_rank, '0', '0', '0')
"}, list("new_rank" = new_rank))
if(!query_add_rank.warn_execute())
qdel(query_add_rank)
return
qdel(query_add_rank)
var/datum/DBQuery/query_add_rank_log = SSdbcore.NewQuery("INSERT INTO [format_table_name("admin_log")] (datetime, round_id, adminckey, adminip, operation, target, log) VALUES ('[SQLtime()]', '[GLOB.round_id]', '[sanitizeSQL(usr.ckey)]', INET_ATON('[sanitizeSQL(usr.client.address)]'), 'add rank', '[new_rank]', 'New rank added: [new_rank]')")
var/datum/db_query/query_add_rank_log = SSdbcore.NewQuery({"
INSERT INTO [format_table_name("admin_log")] (datetime, round_id, adminckey, adminip, operation, target, log)
VALUES (:time, :round_id, :adminckey, INET_ATON(:adminip), 'add rank', :new_rank, CONCAT('New rank added: ', :new_rank))
"}, list("time" = SQLtime(), "round_id" = "[GLOB.round_id]", "adminckey" = usr.ckey, "adminip" = usr.client.address, "new_rank" = new_rank))
if(!query_add_rank_log.warn_execute())
qdel(query_add_rank_log)
return
qdel(query_add_rank_log)
qdel(query_rank_in_db)
var/datum/DBQuery/query_change_rank = SSdbcore.NewQuery("UPDATE [format_table_name("admin")] SET rank = '[new_rank]' WHERE ckey = '[admin_ckey]'")
var/datum/db_query/query_change_rank = SSdbcore.NewQuery(
"UPDATE [format_table_name("admin")] SET `rank` = :new_rank WHERE ckey = :admin_ckey",
list("new_rank" = new_rank, "admin_ckey" = admin_ckey)
)
if(!query_change_rank.warn_execute())
qdel(query_change_rank)
return
qdel(query_change_rank)
var/datum/DBQuery/query_change_rank_log = SSdbcore.NewQuery("INSERT INTO [format_table_name("admin_log")] (datetime, round_id, adminckey, adminip, operation, target, log) VALUES ('[SQLtime()]', '[GLOB.round_id]', '[sanitizeSQL(usr.ckey)]', INET_ATON('[sanitizeSQL(usr.client.address)]'), 'change admin rank', '[admin_ckey]', 'Rank of [admin_ckey] changed from [old_rank] to [new_rank]')")
var/datum/db_query/query_change_rank_log = SSdbcore.NewQuery({"
INSERT INTO [format_table_name("admin_log")] (datetime, round_id, adminckey, adminip, operation, target, log)
VALUES (:time, :round_id, :adminckey, INET_ATON(:adminip), 'change admin rank', :target, CONCAT('Rank of ', :target, ' changed from ', :old_rank, ' to ', :new_rank))
"}, list("time" = SQLtime(), "round_id" = "[GLOB.round_id]", "adminckey" = usr.ckey, "adminip" = usr.client.address, "target" = admin_ckey, "old_rank" = old_rank, "new_rank" = new_rank))
if(!query_change_rank_log.warn_execute())
qdel(query_change_rank_log)
return
@@ -357,11 +400,15 @@
return
var/m1 = "[key_name_admin(usr)] edited the permissions of [use_db ? " rank [D.rank.name] permanently" : "[admin_key] temporarily"]"
var/m2 = "[key_name(usr)] edited the permissions of [use_db ? " rank [D.rank.name] permanently" : "[admin_key] temporarily"]"
if(use_db || legacy_only)
if(use_db && !legacy_only)
var/rank_name = D.rank.name
var/old_flags
var/old_exclude_flags
var/old_can_edit_flags
var/datum/DBQuery/query_get_rank_flags = SSdbcore.NewQuery("SELECT flags, exclude_flags, can_edit_flags FROM [format_table_name("admin_ranks")] WHERE rank = '[D.rank.name]'")
var/datum/db_query/query_get_rank_flags = SSdbcore.NewQuery(
"SELECT flags, exclude_flags, can_edit_flags FROM [format_table_name("admin_ranks")] WHERE `rank` = :rank_name",
list("rank_name" = rank_name)
)
if(!query_get_rank_flags.warn_execute())
qdel(query_get_rank_flags)
return
@@ -370,12 +417,19 @@
old_exclude_flags = text2num(query_get_rank_flags.item[2])
old_can_edit_flags = text2num(query_get_rank_flags.item[3])
qdel(query_get_rank_flags)
var/datum/DBQuery/query_change_rank_flags = SSdbcore.NewQuery("UPDATE [format_table_name("admin_ranks")] SET flags = '[new_flags]', exclude_flags = '[new_exclude_flags]', can_edit_flags = '[new_can_edit_flags]' WHERE rank = '[D.rank.name]'")
var/datum/db_query/query_change_rank_flags = SSdbcore.NewQuery(
"UPDATE [format_table_name("admin_ranks")] SET flags = :new_flags, exclude_flags = :new_exclude_flags, can_edit_flags = :new_can_edit_flags WHERE `rank` = :rank_name",
list("new_flags" = new_flags, "new_exclude_flags" = new_exclude_flags, "new_can_edit_flags" = new_can_edit_flags, "rank_name" = rank_name)
)
if(!query_change_rank_flags.warn_execute())
qdel(query_change_rank_flags)
return
qdel(query_change_rank_flags)
var/datum/DBQuery/query_change_rank_flags_log = SSdbcore.NewQuery("INSERT INTO [format_table_name("admin_log")] (datetime, round_id, adminckey, adminip, operation, target, log) VALUES ('[SQLtime()]', '[GLOB.round_id]', '[sanitizeSQL(usr.ckey)]', INET_ATON('[sanitizeSQL(usr.client.address)]'), 'change rank flags', '[D.rank.name]', 'Permissions of [D.rank.name] changed from[rights2text(old_flags," ")][rights2text(old_exclude_flags," ", "-")][rights2text(old_can_edit_flags," ", "*")] to[rights2text(new_flags," ")][rights2text(new_exclude_flags," ", "-")][rights2text(new_can_edit_flags," ", "*")]')")
var/log_message = "Permissions of [rank_name] changed from[rights2text(old_flags," ")][rights2text(old_exclude_flags," ", "-")][rights2text(old_can_edit_flags," ", "*")] to[rights2text(new_flags," ")][rights2text(new_exclude_flags," ", "-")][rights2text(new_can_edit_flags," ", "*")]"
var/datum/db_query/query_change_rank_flags_log = SSdbcore.NewQuery({"
INSERT INTO [format_table_name("admin_log")] (datetime, round_id, adminckey, adminip, operation, target, log)
VALUES (:time, :round_id, :adminckey, INET_ATON(:adminip), 'change rank flags', :rank_name, :log)
"}, list("time" = SQLtime(), "round_id" = "[GLOB.round_id]", "adminckey" = usr.ckey, "adminip" = usr.client.address, "rank_name" = rank_name, "log" = log_message))
if(!query_change_rank_flags_log.warn_execute())
qdel(query_change_rank_flags_log)
return
@@ -418,33 +472,41 @@
return
for(var/datum/admin_rank/R in GLOB.admin_ranks)
if(R.name == admin_rank && (!(R.rights & usr.client.holder.rank.can_edit_rights) == R.rights))
to_chat(usr, "<span class='admin prefix'>You don't have edit rights to all the rights this rank has, rank deletion not permitted.</span>")
to_chat(usr, "<span class='admin prefix'>You don't have edit rights to all the rights this rank has, rank deletion not permitted.</span>", confidential = TRUE)
return
if(!CONFIG_GET(flag/admin_legacy_system) && CONFIG_GET(flag/protect_legacy_ranks) && (admin_rank in GLOB.protected_ranks))
to_chat(usr, "<span class='admin prefix'>Deletion of protected ranks is not permitted, it must be removed from admin_ranks.txt.</span>")
to_chat(usr, "<span class='admin prefix'>Deletion of protected ranks is not permitted, it must be removed from admin_ranks.txt.</span>", confidential = TRUE)
return
if(CONFIG_GET(flag/load_legacy_ranks_only))
to_chat(usr, "<span class='admin prefix'>Rank deletion not permitted while database rank loading is disabled.</span>")
to_chat(usr, "<span class='admin prefix'>Rank deletion not permitted while database rank loading is disabled.</span>", confidential = TRUE)
return
admin_rank = sanitizeSQL(admin_rank)
var/datum/DBQuery/query_admins_with_rank = SSdbcore.NewQuery("SELECT 1 FROM [format_table_name("admin")] WHERE rank = '[admin_rank]'")
var/datum/db_query/query_admins_with_rank = SSdbcore.NewQuery(
"SELECT 1 FROM [format_table_name("admin")] WHERE `rank` = :admin_rank",
list("admin_rank" = admin_rank)
)
if(!query_admins_with_rank.warn_execute())
qdel(query_admins_with_rank)
return
if(query_admins_with_rank.NextRow())
qdel(query_admins_with_rank)
to_chat(usr, "<span class='danger'>Error: Rank deletion attempted while rank still used; Tell a coder, this shouldn't happen.</span>")
to_chat(usr, "<span class='danger'>Error: Rank deletion attempted while rank still used; Tell a coder, this shouldn't happen.</span>", confidential = TRUE)
return
qdel(query_admins_with_rank)
if(alert("Are you sure you want to remove [admin_rank]?","Confirm Removal","Do it","Cancel") == "Do it")
var/m1 = "[key_name_admin(usr)] removed rank [admin_rank] permanently"
var/m2 = "[key_name(usr)] removed rank [admin_rank] permanently"
var/datum/DBQuery/query_add_rank = SSdbcore.NewQuery("DELETE FROM [format_table_name("admin_ranks")] WHERE rank = '[admin_rank]'")
var/datum/db_query/query_add_rank = SSdbcore.NewQuery(
"DELETE FROM [format_table_name("admin_ranks")] WHERE `rank` = :admin_rank",
list("admin_rank" = admin_rank)
)
if(!query_add_rank.warn_execute())
qdel(query_add_rank)
return
qdel(query_add_rank)
var/datum/DBQuery/query_add_rank_log = SSdbcore.NewQuery("INSERT INTO [format_table_name("admin_log")] (datetime, round_id, adminckey, adminip, operation, target, log) VALUES ('[SQLtime()]', '[GLOB.round_id]', '[sanitizeSQL(usr.ckey)]', INET_ATON('[sanitizeSQL(usr.client.address)]'), 'remove rank', '[admin_rank]', 'Rank removed: [admin_rank]')")
var/datum/db_query/query_add_rank_log = SSdbcore.NewQuery({"
INSERT INTO [format_table_name("admin_log")] (datetime, round_id, adminckey, adminip, operation, target, log)
VALUES (:time, :round_id, :adminckey, INET_ATON(:adminip), 'remove rank', :admin_rank, CONCAT('Rank removed: ', :admin_rank))
"}, list("time" = SQLtime(), "round_id" = "[GLOB.round_id]", "adminckey" = usr.ckey, "adminip" = usr.client.address, "admin_rank" = admin_rank))
if(!query_add_rank_log.warn_execute())
qdel(query_add_rank_log)
return
@@ -455,11 +517,13 @@
/datum/admins/proc/sync_lastadminrank(admin_ckey, admin_key, datum/admins/D)
var/sqlrank = "Player"
if (D)
sqlrank = sanitizeSQL(D.rank.name)
admin_ckey = sanitizeSQL(admin_ckey)
var/datum/DBQuery/query_sync_lastadminrank = SSdbcore.NewQuery("UPDATE [format_table_name("player")] SET lastadminrank = '[sqlrank]' WHERE ckey = '[admin_ckey]'")
sqlrank = D.rank.name
var/datum/db_query/query_sync_lastadminrank = SSdbcore.NewQuery(
"UPDATE [format_table_name("player")] SET lastadminrank = :rank WHERE ckey = :ckey",
list("rank" = sqlrank, "ckey" = admin_ckey)
)
if(!query_sync_lastadminrank.warn_execute())
qdel(query_sync_lastadminrank)
return
qdel(query_sync_lastadminrank)
to_chat(usr, "<span class='admin'>Sync of [admin_key] successful.</span>")
to_chat(usr, "<span class='admin'>Sync of [admin_key] successful.</span>", confidential = TRUE)

View File

@@ -1,6 +1,6 @@
/proc/create_message(type, target_key, admin_ckey, text, timestamp, server, secret, logged = 1, browse, expiry, note_severity)
if(!SSdbcore.Connect())
to_chat(usr, "<span class='danger'>Failed to establish database connection.</span>")
to_chat(usr, "<span class='danger'>Failed to establish database connection.</span>", confidential = TRUE)
return
if(!type)
return
@@ -9,8 +9,11 @@
var/new_key = input(usr,"Who would you like to create a [type] for?","Enter a key or ckey",null) as null|text
if(!new_key)
return
var/new_ckey = sanitizeSQL(ckey(new_key))
var/datum/DBQuery/query_find_ckey = SSdbcore.NewQuery("SELECT ckey FROM [format_table_name("player")] WHERE ckey = '[new_ckey]'")
var/new_ckey = ckey(new_key)
var/datum/db_query/query_find_ckey = SSdbcore.NewQuery(
"SELECT ckey FROM [format_table_name("player")] WHERE ckey = :ckey",
list("ckey" = new_ckey)
)
if(!query_find_ckey.warn_execute())
qdel(query_find_ckey)
return
@@ -23,29 +26,24 @@
target_key = new_key
if(QDELETED(usr))
return
if(target_ckey)
target_ckey = sanitizeSQL(target_ckey)
if(!target_key)
target_key = target_ckey
if(!admin_ckey)
admin_ckey = usr.ckey
if(!admin_ckey)
return
admin_ckey = sanitizeSQL(admin_ckey)
if(!target_ckey)
target_ckey = admin_ckey
if(!text)
text = input(usr,"Write your [type]","Create [type]") as null|message
if(!text)
return
text = sanitizeSQL(text)
if(!timestamp)
timestamp = SQLtime()
if(!server)
var/ssqlname = CONFIG_GET(string/serversqlname)
if (ssqlname)
server = ssqlname
server = sanitizeSQL(server)
if(isnull(secret))
switch(alert("Hide note from being viewed by players?", "Secret note?","Yes","No","Cancel"))
if("Yes")
@@ -59,15 +57,17 @@
var/expire_time = input("Set expiry time for [type] as format YYYY-MM-DD HH:MM:SS. All times in server time. HH:MM:SS is optional and 24-hour. Must be later than current time for obvious reasons.", "Set expiry time", SQLtime()) as null|text
if(!expire_time)
return
expire_time = sanitizeSQL(expire_time)
var/datum/DBQuery/query_validate_expire_time = SSdbcore.NewQuery("SELECT IF(STR_TO_DATE('[expire_time]','%Y-%c-%d %T') > NOW(), STR_TO_DATE('[expire_time]','%Y-%c-%d %T'), 0)")
var/datum/db_query/query_validate_expire_time = SSdbcore.NewQuery(
"SELECT IF(STR_TO_DATE(:expire_time,'%Y-%c-%d %T') > NOW(), STR_TO_DATE(:expire_time,'%Y-%c-%d %T'), 0)",
list("expire_time" = expire_time)
)
if(!query_validate_expire_time.warn_execute())
qdel(query_validate_expire_time)
return
if(query_validate_expire_time.NextRow())
var/checktime = text2num(query_validate_expire_time.item[1])
if(!checktime)
to_chat(usr, "Datetime entered is improperly formatted or not later than current server time.")
to_chat(usr, "Datetime entered is improperly formatted or not later than current server time.", confidential = TRUE)
qdel(query_validate_expire_time)
return
expiry = query_validate_expire_time.item[1]
@@ -76,8 +76,23 @@
note_severity = input("Set the severity of the note.", "Severity", null, null) as null|anything in list("High", "Medium", "Minor", "None")
if(!note_severity)
return
note_severity = sanitizeSQL(note_severity)
var/datum/DBQuery/query_create_message = SSdbcore.NewQuery("INSERT INTO [format_table_name("messages")] (type, targetckey, adminckey, text, timestamp, server, server_ip, server_port, round_id, secret, expire_timestamp, severity) VALUES ('[type]', '[target_ckey]', '[admin_ckey]', '[text]', '[timestamp]', '[server]', INET_ATON(IF('[world.internet_address]' LIKE '', '0', '[world.internet_address]')), '[world.port]', '[GLOB.round_id]','[secret]', [expiry ? "'[expiry]'" : "NULL"], [note_severity ? "'[note_severity]'" : "NULL"])")
var/datum/db_query/query_create_message = SSdbcore.NewQuery({"
INSERT INTO [format_table_name("messages")] (type, targetckey, adminckey, text, timestamp, server, server_ip, server_port, round_id, secret, expire_timestamp, severity)
VALUES (:type, :target_ckey, :admin_ckey, :text, :timestamp, :server, INET_ATON(:internet_address), :port, :round_id, :secret, :expiry, :note_severity)
"}, list(
"type" = type,
"target_ckey" = target_ckey,
"admin_ckey" = admin_ckey,
"text" = text,
"timestamp" = timestamp,
"server" = server,
"internet_address" = world.internet_address || "0",
"port" = "[world.port]",
"round_id" = GLOB.round_id,
"secret" = secret,
"expiry" = expiry || null,
"note_severity" = note_severity,
))
var/pm = "[key_name(usr)] has created a [type][(type == "note" || type == "message" || type == "watchlist entry") ? " for [target_key]" : ""]: [text]"
var/header = "[key_name_admin(usr)] has created a [type][(type == "note" || type == "message" || type == "watchlist entry") ? " for [target_key]" : ""]"
if(!query_create_message.warn_execute())
@@ -96,7 +111,7 @@
/proc/delete_message(message_id, logged = 1, browse)
if(!SSdbcore.Connect())
to_chat(usr, "<span class='danger'>Failed to establish database connection.</span>")
to_chat(usr, "<span class='danger'>Failed to establish database connection.</span>", confidential = TRUE)
return
message_id = text2num(message_id)
if(!message_id)
@@ -106,7 +121,11 @@
var/text
var/user_key_name = key_name(usr)
var/user_name_admin = key_name_admin(usr)
var/datum/DBQuery/query_find_del_message = SSdbcore.NewQuery("SELECT type, IFNULL((SELECT byond_key FROM [format_table_name("player")] WHERE ckey = targetckey), targetckey), text FROM [format_table_name("messages")] WHERE id = [message_id] AND deleted = 0")
var/deleted_by_ckey = usr.ckey
var/datum/db_query/query_find_del_message = SSdbcore.NewQuery(
"SELECT type, IFNULL((SELECT byond_key FROM [format_table_name("player")] WHERE ckey = targetckey), targetckey), text FROM [format_table_name("messages")] WHERE id = :id AND deleted = 0",
list("id" = message_id)
)
if(!query_find_del_message.warn_execute())
qdel(query_find_del_message)
return
@@ -115,7 +134,10 @@
target_key = query_find_del_message.item[2]
text = query_find_del_message.item[3]
qdel(query_find_del_message)
var/datum/DBQuery/query_del_message = SSdbcore.NewQuery("UPDATE [format_table_name("messages")] SET deleted = 1 WHERE id = [message_id]")
var/datum/db_query/query_del_message = SSdbcore.NewQuery(
"UPDATE [format_table_name("messages")] SET deleted = 1, deleted_ckey = :deleted_ckey WHERE id = :id",
list("deleted_ckey" = deleted_by_ckey, "id" = message_id)
)
if(!query_del_message.warn_execute())
qdel(query_del_message)
return
@@ -132,16 +154,24 @@
/proc/edit_message(message_id, browse)
if(!SSdbcore.Connect())
to_chat(usr, "<span class='danger'>Failed to establish database connection.</span>")
to_chat(usr, "<span class='danger'>Failed to establish database connection.</span>", confidential = TRUE)
return
message_id = text2num(message_id)
if(!message_id)
return
var/editor_ckey = sanitizeSQL(usr.ckey)
var/editor_key = sanitizeSQL(usr.key)
var/editor_ckey = usr.ckey
var/editor_key = usr.key
var/kn = key_name(usr)
var/kna = key_name_admin(usr)
var/datum/DBQuery/query_find_edit_message = SSdbcore.NewQuery("SELECT type, IFNULL((SELECT byond_key FROM [format_table_name("player")] WHERE ckey = targetckey), targetckey), IFNULL((SELECT byond_key FROM [format_table_name("player")] WHERE ckey = adminckey), targetckey), text FROM [format_table_name("messages")] WHERE id = [message_id] AND deleted = 0")
var/datum/db_query/query_find_edit_message = SSdbcore.NewQuery({"
SELECT
type,
IFNULL((SELECT byond_key FROM [format_table_name("player")] WHERE ckey = targetckey), targetckey),
IFNULL((SELECT byond_key FROM [format_table_name("player")] WHERE ckey = adminckey), targetckey),
text
FROM [format_table_name("messages")]
WHERE id = :id AND deleted = 0
"}, list("id" = message_id))
if(!query_find_edit_message.warn_execute())
qdel(query_find_edit_message)
return
@@ -154,9 +184,12 @@
if(!new_text)
qdel(query_find_edit_message)
return
new_text = sanitizeSQL(new_text)
var/edit_text = sanitizeSQL("Edited by [editor_key] on [SQLtime()] from<br>[old_text]<br>to<br>[new_text]<hr>")
var/datum/DBQuery/query_edit_message = SSdbcore.NewQuery("UPDATE [format_table_name("messages")] SET text = '[new_text]', lasteditor = '[editor_ckey]', edits = CONCAT(IFNULL(edits,''),'[edit_text]') WHERE id = [message_id] AND deleted = 0")
var/edit_text = "Edited by [editor_key] on [SQLtime()] from<br>[old_text]<br>to<br>[new_text]<hr>"
var/datum/db_query/query_edit_message = SSdbcore.NewQuery({"
UPDATE [format_table_name("messages")]
SET text = :text, lasteditor = :lasteditor, edits = CONCAT(IFNULL(edits,''),:edit_text)
WHERE id = :id AND deleted = 0
"}, list("text" = new_text, "lasteditor" = editor_ckey, "edit_text" = edit_text, "id" = message_id))
if(!query_edit_message.warn_execute())
qdel(query_edit_message)
return
@@ -171,16 +204,24 @@
/proc/edit_message_expiry(message_id, browse)
if(!SSdbcore.Connect())
to_chat(usr, "<span class='danger'>Failed to establish database connection.</span>")
to_chat(usr, "<span class='danger'>Failed to establish database connection.</span>", confidential = TRUE)
return
message_id = text2num(message_id)
if(!message_id)
return
var/editor_ckey = sanitizeSQL(usr.ckey)
var/editor_key = sanitizeSQL(usr.key)
var/editor_ckey = usr.ckey
var/editor_key = usr.key
var/kn = key_name(usr)
var/kna = key_name_admin(usr)
var/datum/DBQuery/query_find_edit_expiry_message = SSdbcore.NewQuery("SELECT type, IFNULL((SELECT byond_key FROM [format_table_name("player")] WHERE ckey = targetckey), targetckey), IFNULL((SELECT byond_key FROM [format_table_name("player")] WHERE ckey = adminckey), adminckey), expire_timestamp FROM [format_table_name("messages")] WHERE id = [message_id] AND deleted = 0")
var/datum/db_query/query_find_edit_expiry_message = SSdbcore.NewQuery({"
SELECT
type,
IFNULL((SELECT byond_key FROM [format_table_name("player")] WHERE ckey = targetckey), targetckey),
IFNULL((SELECT byond_key FROM [format_table_name("player")] WHERE ckey = adminckey), adminckey),
expire_timestamp
FROM [format_table_name("messages")]
WHERE id = :id AND deleted = 0
"}, list("id" = message_id))
if(!query_find_edit_expiry_message.warn_execute())
qdel(query_find_edit_expiry_message)
return
@@ -197,8 +238,9 @@
if(expire_time == "-1")
new_expiry = "non-expiring"
else
expire_time = sanitizeSQL(expire_time)
var/datum/DBQuery/query_validate_expire_time_edit = SSdbcore.NewQuery("SELECT IF(STR_TO_DATE('[expire_time]','%Y-%c-%d %T') > NOW(), STR_TO_DATE('[expire_time]','%Y-%c-%d %T'), 0)")
var/datum/db_query/query_validate_expire_time_edit = SSdbcore.NewQuery({"
SELECT IF(STR_TO_DATE(:expire_time,'%Y-%c-%d %T') > NOW(), STR_TO_DATE(:expire_time,'%Y-%c-%d %T'), 0)
"}, list("expire_time" = expire_time))
if(!query_validate_expire_time_edit.warn_execute())
qdel(query_validate_expire_time_edit)
qdel(query_find_edit_expiry_message)
@@ -206,14 +248,18 @@
if(query_validate_expire_time_edit.NextRow())
var/checktime = text2num(query_validate_expire_time_edit.item[1])
if(!checktime)
to_chat(usr, "Datetime entered is improperly formatted or not later than current server time.")
to_chat(usr, "Datetime entered is improperly formatted or not later than current server time.", confidential = TRUE)
qdel(query_validate_expire_time_edit)
qdel(query_find_edit_expiry_message)
return
new_expiry = query_validate_expire_time_edit.item[1]
qdel(query_validate_expire_time_edit)
var/edit_text = sanitizeSQL("Expiration time edited by [editor_key] on [SQLtime()] from [old_expiry] to [new_expiry]<hr>")
var/datum/DBQuery/query_edit_message_expiry = SSdbcore.NewQuery("UPDATE [format_table_name("messages")] SET expire_timestamp = [expire_time == "-1" ? "NULL" : "'[new_expiry]'"], lasteditor = '[editor_ckey]', edits = CONCAT(IFNULL(edits,''),'[edit_text]') WHERE id = [message_id] AND deleted = 0")
var/edit_text = "Expiration time edited by [editor_key] on [SQLtime()] from [old_expiry] to [new_expiry]<hr>"
var/datum/db_query/query_edit_message_expiry = SSdbcore.NewQuery({"
UPDATE [format_table_name("messages")]
SET expire_timestamp = :expire_time, lasteditor = :lasteditor, edits = CONCAT(IFNULL(edits,''),:edit_text)
WHERE id = :id AND deleted = 0
"}, list("expire_time" = (expire_time == "-1" ? null : new_expiry), "lasteditor" = editor_ckey, "edit_text" = edit_text, "id" = message_id))
if(!query_edit_message_expiry.warn_execute())
qdel(query_edit_message_expiry)
qdel(query_find_edit_expiry_message)
@@ -229,14 +275,22 @@
/proc/edit_message_severity(message_id)
if(!SSdbcore.Connect())
to_chat(usr, "<span class='danger'>Failed to establish database connection.</span>")
to_chat(usr, "<span class='danger'>Failed to establish database connection.</span>", confidential = TRUE)
return
message_id = text2num(message_id)
if(!message_id)
return
var/kn = key_name(usr)
var/kna = key_name_admin(usr)
var/datum/DBQuery/query_find_edit_note_severity = SSdbcore.NewQuery("SELECT type, IFNULL((SELECT byond_key FROM [format_table_name("player")] WHERE ckey = targetckey), targetckey), IFNULL((SELECT byond_key FROM [format_table_name("player")] WHERE ckey = adminckey), adminckey), severity FROM [format_table_name("messages")] WHERE id = [message_id] AND deleted = 0")
var/datum/db_query/query_find_edit_note_severity = SSdbcore.NewQuery({"
SELECT
type,
IFNULL((SELECT byond_key FROM [format_table_name("player")] WHERE ckey = targetckey), targetckey),
IFNULL((SELECT byond_key FROM [format_table_name("player")] WHERE ckey = adminckey), adminckey),
severity
FROM [format_table_name("messages")]
WHERE id = :id AND deleted = 0
"}, list("id" = message_id))
if(!query_find_edit_note_severity.warn_execute())
qdel(query_find_edit_note_severity)
return
@@ -247,15 +301,19 @@
var/old_severity = query_find_edit_note_severity.item[4]
if(!old_severity)
old_severity = "NA"
var/editor_key = sanitizeSQL(usr.key)
var/editor_ckey = sanitizeSQL(usr.ckey)
var/editor_key = usr.key
var/editor_ckey = usr.ckey
var/new_severity = input("Set the severity of the note.", "Severity", null, null) as null|anything in list("high", "medium", "minor", "none") //lowercase for edit log consistency
if(!new_severity)
qdel(query_find_edit_note_severity)
return
new_severity = sanitizeSQL(new_severity)
var/edit_text = sanitizeSQL("Note severity edited by [editor_key] on [SQLtime()] from [old_severity] to [new_severity]<hr>")
var/datum/DBQuery/query_edit_note_severity = SSdbcore.NewQuery("UPDATE [format_table_name("messages")] SET severity = '[new_severity]', lasteditor = '[editor_ckey]', edits = CONCAT(IFNULL(edits,''),'[edit_text]') WHERE id = [message_id] AND deleted = 0")
new_severity = new_severity
var/edit_text = "Note severity edited by [editor_key] on [SQLtime()] from [old_severity] to [new_severity]<hr>"
var/datum/db_query/query_edit_note_severity = SSdbcore.NewQuery({"
UPDATE [format_table_name("messages")]
SET severity = :severity, lasteditor = :lasteditor, edits = CONCAT(IFNULL(edits,''),:edit_text)
WHERE id = :id AND deleted = 0
"}, list("severity" = new_severity, "lasteditor" = editor_ckey, "edit_text" = edit_text, "id" = message_id))
if(!query_edit_note_severity.warn_execute(async = TRUE))
qdel(query_edit_note_severity)
qdel(qdel(query_find_edit_note_severity))
@@ -268,16 +326,24 @@
/proc/toggle_message_secrecy(message_id)
if(!SSdbcore.Connect())
to_chat(usr, "<span class='danger'>Failed to establish database connection.</span>")
to_chat(usr, "<span class='danger'>Failed to establish database connection.</span>", confidential = TRUE)
return
message_id = text2num(message_id)
if(!message_id)
return
var/editor_ckey = sanitizeSQL(usr.ckey)
var/editor_key = sanitizeSQL(usr.key)
var/editor_ckey = usr.ckey
var/editor_key = usr.key
var/kn = key_name(usr)
var/kna = key_name_admin(usr)
var/datum/DBQuery/query_find_message_secret = SSdbcore.NewQuery("SELECT type, IFNULL((SELECT byond_key FROM [format_table_name("player")] WHERE ckey = targetckey), targetckey), IFNULL((SELECT byond_key FROM [format_table_name("player")] WHERE ckey = adminckey), targetckey), secret FROM [format_table_name("messages")] WHERE id = [message_id] AND deleted = 0")
var/datum/db_query/query_find_message_secret = SSdbcore.NewQuery({"
SELECT
type,
IFNULL((SELECT byond_key FROM [format_table_name("player")] WHERE ckey = targetckey), targetckey),
IFNULL((SELECT byond_key FROM [format_table_name("player")] WHERE ckey = adminckey), targetckey),
secret
FROM [format_table_name("messages")]
WHERE id = :id AND deleted = 0
"}, list("id" = message_id))
if(!query_find_message_secret.warn_execute())
qdel(query_find_message_secret)
return
@@ -287,7 +353,11 @@
var/admin_key = query_find_message_secret.item[3]
var/secret = text2num(query_find_message_secret.item[4])
var/edit_text = "Made [secret ? "not secret" : "secret"] by [editor_key] on [SQLtime()]<hr>"
var/datum/DBQuery/query_message_secret = SSdbcore.NewQuery("UPDATE [format_table_name("messages")] SET secret = NOT secret, lasteditor = '[editor_ckey]', edits = CONCAT(IFNULL(edits,''),'[edit_text]') WHERE id = [message_id]")
var/datum/db_query/query_message_secret = SSdbcore.NewQuery({"
UPDATE [format_table_name("messages")]
SET secret = NOT secret, lasteditor = :lasteditor, edits = CONCAT(IFNULL(edits,''),:edit_text)
WHERE id = :id
"}, list("lasteditor" = editor_ckey, "edit_text" = edit_text, "id" = message_id))
if(!query_message_secret.warn_execute())
qdel(query_find_message_secret)
qdel(query_message_secret)
@@ -298,11 +368,9 @@
browse_messages(target_ckey = ckey(target_key), agegate = TRUE)
qdel(query_find_message_secret)
/proc/browse_messages(type, target_ckey, index, linkless = FALSE, filter, agegate = FALSE, override = FALSE)
if((!override || IsAdminAdvancedProcCall()) && !check_rights(R_SENSITIVE))
return
/proc/browse_messages(type, target_ckey, index, linkless = FALSE, filter, agegate = FALSE)
if(!SSdbcore.Connect())
to_chat(usr, "<span class='danger'>Failed to establish database connection.</span>")
to_chat(usr, "<span class='danger'>Failed to establish database connection.</span>", confidential = TRUE)
return
var/list/output = list()
var/ruler = "<hr style='background:#000000; border:0; height:3px'>"
@@ -329,7 +397,20 @@
else
output += "<a href='?_src_=holder;[HrefToken()];showwatchfilter=1'>Filter offline clients</a></center>"
output += ruler
var/datum/DBQuery/query_get_type_messages = SSdbcore.NewQuery("SELECT id, IFNULL((SELECT byond_key FROM [format_table_name("player")] WHERE ckey = targetckey), targetckey), targetckey, IFNULL((SELECT byond_key FROM [format_table_name("player")] WHERE ckey = adminckey), adminckey), text, timestamp, server, IFNULL((SELECT byond_key FROM [format_table_name("player")] WHERE ckey = lasteditor), lasteditor), expire_timestamp FROM [format_table_name("messages")] WHERE type = '[type]' AND deleted = 0 AND (expire_timestamp > NOW() OR expire_timestamp IS NULL)")
var/datum/db_query/query_get_type_messages = SSdbcore.NewQuery({"
SELECT
id,
IFNULL((SELECT byond_key FROM [format_table_name("player")] WHERE ckey = targetckey), targetckey),
targetckey,
IFNULL((SELECT byond_key FROM [format_table_name("player")] WHERE ckey = adminckey), adminckey),
text,
timestamp,
server,
IFNULL((SELECT byond_key FROM [format_table_name("player")] WHERE ckey = lasteditor), lasteditor),
expire_timestamp
FROM [format_table_name("messages")]
WHERE type = :type AND deleted = 0 AND (expire_timestamp > NOW() OR expire_timestamp IS NULL)
"}, list("type" = type))
if(!query_get_type_messages.warn_execute())
qdel(query_get_type_messages)
return
@@ -362,9 +443,24 @@
output += "<br>[text]<hr style='background:#000000; border:0; height:1px'>"
qdel(query_get_type_messages)
if(target_ckey)
target_ckey = sanitizeSQL(target_ckey)
var/target_key
var/datum/DBQuery/query_get_messages = SSdbcore.NewQuery("SELECT type, secret, id, IFNULL((SELECT byond_key FROM [format_table_name("player")] WHERE ckey = adminckey), adminckey), text, timestamp, server, IFNULL((SELECT byond_key FROM [format_table_name("player")] WHERE ckey = lasteditor), lasteditor), DATEDIFF(NOW(), timestamp), IFNULL((SELECT byond_key FROM [format_table_name("player")] WHERE ckey = targetckey), targetckey), expire_timestamp, severity FROM [format_table_name("messages")] WHERE type <> 'memo' AND targetckey = '[target_ckey]' AND deleted = 0 AND (expire_timestamp > NOW() OR expire_timestamp IS NULL) ORDER BY timestamp DESC")
var/datum/db_query/query_get_messages = SSdbcore.NewQuery({"
SELECT
type,
secret,
id,
IFNULL((SELECT byond_key FROM [format_table_name("player")] WHERE ckey = adminckey), adminckey),
text,
timestamp,
server,
IFNULL((SELECT byond_key FROM [format_table_name("player")] WHERE ckey = lasteditor), lasteditor),
DATEDIFF(NOW(), timestamp),
IFNULL((SELECT byond_key FROM [format_table_name("player")] WHERE ckey = targetckey), targetckey),
expire_timestamp, severity
FROM [format_table_name("messages")]
WHERE type <> 'memo' AND targetckey = :targetckey AND deleted = 0 AND (expire_timestamp > NOW() OR expire_timestamp IS NULL)
ORDER BY timestamp DESC
"}, list("targetckey" = target_ckey))
if(!query_get_messages.warn_execute())
qdel(query_get_messages)
return
@@ -442,7 +538,9 @@
notedata += data
qdel(query_get_messages)
if(!target_key)
var/datum/DBQuery/query_get_message_key = SSdbcore.NewQuery("SELECT byond_key FROM [format_table_name("player")] WHERE ckey = '[target_ckey]'")
var/datum/db_query/query_get_message_key = SSdbcore.NewQuery({"
SELECT byond_key FROM [format_table_name("player")] WHERE ckey = :ckey
"}, list("ckey" = target_ckey))
if(!query_get_message_key.warn_execute())
qdel(query_get_message_key)
return
@@ -479,8 +577,6 @@
var/search
output += "<center><a href='?_src_=holder;[HrefToken()];addmessageempty=1'>Add message</a><a href='?_src_=holder;[HrefToken()];addwatchempty=1'>Add watchlist entry</a><a href='?_src_=holder;[HrefToken()];addnoteempty=1'>Add note</a></center>"
output += ruler
if(!isnum(index))
index = sanitizeSQL(index)
switch(index)
if(1)
search = "^."
@@ -488,7 +584,17 @@
search = "^\[^\[:alpha:\]\]"
else
search = "^[index]"
var/datum/DBQuery/query_list_messages = SSdbcore.NewQuery("SELECT DISTINCT targetckey, (SELECT byond_key FROM [format_table_name("player")] WHERE ckey = targetckey) FROM [format_table_name("messages")] WHERE type <> 'memo' AND targetckey REGEXP '[search]' AND deleted = 0 AND (expire_timestamp > NOW() OR expire_timestamp IS NULL) ORDER BY targetckey")
var/datum/db_query/query_list_messages = SSdbcore.NewQuery({"
SELECT DISTINCT
targetckey,
(SELECT byond_key FROM [format_table_name("player")] WHERE ckey = targetckey)
FROM [format_table_name("messages")]
WHERE type <> 'memo'
AND targetckey REGEXP :search
AND deleted = 0
AND (expire_timestamp > NOW() OR expire_timestamp IS NULL)
ORDER BY targetckey
"}, list("search" = search))
if(!query_list_messages.warn_execute())
qdel(query_list_messages)
return
@@ -512,17 +618,24 @@
/proc/get_message_output(type, target_ckey)
if(!SSdbcore.Connect())
to_chat(usr, "<span class='danger'>Failed to establish database connection.</span>")
to_chat(usr, "<span class='danger'>Failed to establish database connection.</span>", confidential = TRUE)
return
if(!type)
return
var/output
if(target_ckey)
target_ckey = sanitizeSQL(target_ckey)
var/query = "SELECT id, IFNULL((SELECT byond_key FROM [format_table_name("player")] WHERE ckey = adminckey), adminckey), text, timestamp, IFNULL((SELECT byond_key FROM [format_table_name("player")] WHERE ckey = lasteditor), lasteditor) FROM [format_table_name("messages")] WHERE type = '[type]' AND deleted = 0 AND (expire_timestamp > NOW() OR expire_timestamp IS NULL)"
if(type == "message" || type == "watchlist entry")
query += " AND targetckey = '[target_ckey]'"
var/datum/DBQuery/query_get_message_output = SSdbcore.NewQuery(query)
var/datum/db_query/query_get_message_output = SSdbcore.NewQuery({"
SELECT
id,
IFNULL((SELECT byond_key FROM [format_table_name("player")] WHERE ckey = adminckey), adminckey),
text,
timestamp,
IFNULL((SELECT byond_key FROM [format_table_name("player")] WHERE ckey = lasteditor), lasteditor)
FROM [format_table_name("messages")]
WHERE type = :type
AND deleted = 0
AND (expire_timestamp > NOW() OR expire_timestamp IS NULL)
AND ((type != 'message' AND type != 'watchlist entry') OR targetckey = :targetckey)
"}, list("targetckey" = target_ckey, "type" = type))
if(!query_get_message_output.warn_execute())
qdel(query_get_message_output)
return
@@ -536,7 +649,10 @@
if("message")
output += "<font color='red' size='3'><b>Admin message left by <span class='prefix'>[admin_key]</span> on [timestamp]</b></font>"
output += "<br><font color='red'>[text]</font><br>"
var/datum/DBQuery/query_message_read = SSdbcore.NewQuery("UPDATE [format_table_name("messages")] SET type = 'message sent' WHERE id = [message_id]")
var/datum/db_query/query_message_read = SSdbcore.NewQuery(
"UPDATE [format_table_name("messages")] SET type = 'message sent' WHERE id = :id",
list("id" = message_id)
)
if(!query_message_read.warn_execute())
qdel(query_get_message_output)
qdel(query_message_read)
@@ -544,7 +660,7 @@
qdel(query_message_read)
if("watchlist entry")
message_admins("<font color='red'><B>Notice: </B></font><font color='blue'>[key_name_admin(target_ckey)] has been on the watchlist since [timestamp] and has just connected - Reason: [text]</font>")
send2irc_adminless_only("Watchlist", "[key_name(target_ckey)] is on the watchlist and has just connected - Reason: [text]")
send2tgs_adminless_only("Watchlist", "[key_name(target_ckey)] is on the watchlist and has just connected - Reason: [text]")
if("memo")
output += "<span class='memo'>Memo by <span class='prefix'>[admin_key]</span> on [timestamp]"
if(editor_key)
@@ -576,7 +692,7 @@
var/timestamp = note.group[1]
notetext = note.group[2]
var/admin_ckey = note.group[3]
var/datum/DBQuery/query_convert_time = SSdbcore.NewQuery("SELECT ADDTIME(STR_TO_DATE('[timestamp]','%d-%b-%Y'), '0')")
var/datum/db_query/query_convert_time = SSdbcore.NewQuery("SELECT ADDTIME(STR_TO_DATE(:timestamp,'%d-%b-%Y'), '0')", list("timestamp" = timestamp))
if(!query_convert_time.Execute())
qdel(query_convert_time)
return
@@ -591,7 +707,7 @@
/*alternatively this proc can be run once to pass through every note and attempt to convert it before deleting the file, if done then AUTOCONVERT_NOTES should be turned off
this proc can take several minutes to execute fully if converting and cause DD to hang if converting a lot of notes; it's not advised to do so while a server is live
/proc/mass_convert_notes()
to_chat(world, "Beginning mass note conversion")
to_chat(world, "Beginning mass note conversion", confidential = TRUE)
var/savefile/notesfile = new(NOTESFILE)
if(!notesfile)
log_game("Error: Cannot access [NOTESFILE]")
@@ -599,7 +715,7 @@ this proc can take several minutes to execute fully if converting and cause DD t
notesfile.cd = "/"
for(var/ckey in notesfile.dir)
convert_notes_sql(ckey)
to_chat(world, "Deleting NOTESFILE")
to_chat(world, "Deleting NOTESFILE", confidential = TRUE)
fdel(NOTESFILE)
to_chat(world, "Finished mass note conversion, remember to turn off AUTOCONVERT_NOTES")*/
to_chat(world, "Finished mass note conversion, remember to turn off AUTOCONVERT_NOTES", confidential = TRUE)*/
#undef NOTESFILE

View File

@@ -32,15 +32,11 @@
return
ban["message"] = "[reason]"
if(SSdbcore.Connect()) // todo: second wave
// var/datum/db_query/query_create_stickyban = SSdbcore.NewQuery({"
// INSERT INTO [format_table_name("stickyban")] (ckey, reason, banning_admin)
// VALUES (:ckey, :message, :banning_admin)
// "}, list("ckey" = ckey, "message" = ban["message"], "banning_admin" = usr.ckey))
var/datum/DBQuery/query_create_stickyban = SSdbcore.NewQuery({"
if(SSdbcore.Connect())
var/datum/db_query/query_create_stickyban = SSdbcore.NewQuery({"
INSERT INTO [format_table_name("stickyban")] (ckey, reason, banning_admin)
VALUES ([ckey], [ban["message"]], [usr.ckey])
"})
VALUES (:ckey, :message, :banning_admin)
"}, list("ckey" = ckey, "message" = ban["message"], "banning_admin" = usr.ckey))
if (query_create_stickyban.warn_execute())
ban["fromdb"] = TRUE
qdel(query_create_stickyban)
@@ -74,19 +70,14 @@
SSstickyban.cache -= ckey
if (SSdbcore.Connect())
// SSdbcore.QuerySelect(list(
// SSdbcore.NewQuery("DELETE FROM [format_table_name("stickyban")] WHERE ckey = :ckey", list("ckey" = ckey)),
// SSdbcore.NewQuery("DELETE FROM [format_table_name("stickyban_matched_ckey")] WHERE stickyban = :ckey", list("ckey" = ckey)),
// SSdbcore.NewQuery("DELETE FROM [format_table_name("stickyban_matched_cid")] WHERE stickyban = :ckey", list("ckey" = ckey)),
// SSdbcore.NewQuery("DELETE FROM [format_table_name("stickyban_matched_ip")] WHERE stickyban = :ckey", list("ckey" = ckey))
// ), warn = TRUE, qdel = TRUE)
SSdbcore.QuerySelect(list(
SSdbcore.NewQuery("DELETE FROM [format_table_name("stickyban")] WHERE ckey = [ckey]"),
SSdbcore.NewQuery("DELETE FROM [format_table_name("stickyban_matched_ckey")] WHERE stickyban = [ckey]"),
SSdbcore.NewQuery("DELETE FROM [format_table_name("stickyban_matched_cid")] WHERE stickyban = [ckey]"),
SSdbcore.NewQuery("DELETE FROM [format_table_name("stickyban_matched_ip")] WHERE stickyban = [ckey]")
SSdbcore.NewQuery("DELETE FROM [format_table_name("stickyban")] WHERE ckey = :ckey", list("ckey" = ckey)),
SSdbcore.NewQuery("DELETE FROM [format_table_name("stickyban_matched_ckey")] WHERE stickyban = :ckey", list("ckey" = ckey)),
SSdbcore.NewQuery("DELETE FROM [format_table_name("stickyban_matched_cid")] WHERE stickyban = :ckey", list("ckey" = ckey)),
SSdbcore.NewQuery("DELETE FROM [format_table_name("stickyban_matched_ip")] WHERE stickyban = :ckey", list("ckey" = ckey))
), warn = TRUE, qdel = TRUE)
log_admin_private("[key_name(usr)] removed [ckey]'s stickyban")
message_admins("<span class='adminnotice'>[key_name_admin(usr)] removed [ckey]'s stickyban</span>")
@@ -128,12 +119,9 @@
SSstickyban.cache[ckey] = ban
if (SSdbcore.Connect())
// var/datum/db_query/query_remove_stickyban_alt = SSdbcore.NewQuery(
// "DELETE FROM [format_table_name("stickyban_matched_ckey")] WHERE stickyban = :ckey AND matched_ckey = :alt",
// list("ckey" = ckey, "alt" = alt)
// )
var/datum/DBQuery/query_remove_stickyban_alt = SSdbcore.NewQuery(
"DELETE FROM [format_table_name("stickyban_matched_ckey")] WHERE stickyban = [ckey] AND matched_ckey = [alt]"
var/datum/db_query/query_remove_stickyban_alt = SSdbcore.NewQuery(
"DELETE FROM [format_table_name("stickyban_matched_ckey")] WHERE stickyban = :ckey AND matched_ckey = :alt",
list("ckey" = ckey, "alt" = alt)
)
query_remove_stickyban_alt.warn_execute()
qdel(query_remove_stickyban_alt)
@@ -165,12 +153,9 @@
SSstickyban.cache[ckey] = ban
if (SSdbcore.Connect())
// var/datum/db_query/query_edit_stickyban = SSdbcore.NewQuery(
// "UPDATE [format_table_name("stickyban")] SET reason = :reason WHERE ckey = :ckey",
// list("reason" = reason, "ckey" = ckey)
// )
var/datum/DBQuery/query_edit_stickyban = SSdbcore.NewQuery(
"UPDATE [format_table_name("stickyban")] SET reason = [reason] WHERE ckey = [ckey]"
var/datum/db_query/query_edit_stickyban = SSdbcore.NewQuery(
"UPDATE [format_table_name("stickyban")] SET reason = :reason WHERE ckey = :ckey",
list("reason" = reason, "ckey" = ckey)
)
query_edit_stickyban.warn_execute()
qdel(query_edit_stickyban)
@@ -218,12 +203,9 @@
SSstickyban.cache[ckey] = ban
if (SSdbcore.Connect())
// var/datum/db_query/query_exempt_stickyban_alt = SSdbcore.NewQuery(
// "UPDATE [format_table_name("stickyban_matched_ckey")] SET exempt = 1 WHERE stickyban = :ckey AND matched_ckey = :alt",
// list("ckey" = ckey, "alt" = alt)
// )
var/datum/DBQuery/query_exempt_stickyban_alt = SSdbcore.NewQuery(
"UPDATE [format_table_name("stickyban_matched_ckey")] SET exempt = 1 WHERE stickyban = [ckey] AND matched_ckey = [alt]"
var/datum/db_query/query_exempt_stickyban_alt = SSdbcore.NewQuery(
"UPDATE [format_table_name("stickyban_matched_ckey")] SET exempt = 1 WHERE stickyban = :ckey AND matched_ckey = :alt",
list("ckey" = ckey, "alt" = alt)
)
query_exempt_stickyban_alt.warn_execute()
qdel(query_exempt_stickyban_alt)
@@ -271,12 +253,9 @@
SSstickyban.cache[ckey] = ban
if (SSdbcore.Connect())
// var/datum/db_query/query_unexempt_stickyban_alt = SSdbcore.NewQuery(
// "UPDATE [format_table_name("stickyban_matched_ckey")] SET exempt = 0 WHERE stickyban = :ckey AND matched_ckey = :alt",
// list("ckey" = ckey, "alt" = alt)
// )
var/datum/DBQuery/query_unexempt_stickyban_alt = SSdbcore.NewQuery(
"UPDATE [format_table_name("stickyban_matched_ckey")] SET exempt = 0 WHERE stickyban = [ckey] AND matched_ckey = [alt]"
var/datum/db_query/query_unexempt_stickyban_alt = SSdbcore.NewQuery(
"UPDATE [format_table_name("stickyban_matched_ckey")] SET exempt = 0 WHERE stickyban = :ckey AND matched_ckey = :alt",
list("ckey" = ckey, "alt" = alt)
)
query_unexempt_stickyban_alt.warn_execute()
qdel(query_unexempt_stickyban_alt)

View File

@@ -1269,8 +1269,10 @@
else if(href_list["messageedits"])
if(!check_rights(R_ADMIN))
return
var/message_id = sanitizeSQL("[href_list["messageedits"]]")
var/datum/DBQuery/query_get_message_edits = SSdbcore.NewQuery("SELECT edits FROM [format_table_name("messages")] WHERE id = '[message_id]'")
var/datum/db_query/query_get_message_edits = SSdbcore.NewQuery(
"SELECT edits FROM [format_table_name("messages")] WHERE id = :message_id",
list("message_id" = href_list["messageedits"])
)
if(!query_get_message_edits.warn_execute())
qdel(query_get_message_edits)
return
@@ -2970,16 +2972,19 @@
to_chat(usr, "<span class='danger'>The client chosen is an admin! Cannot mentorize.</span>")
return
if(SSdbcore.Connect())
var/datum/DBQuery/query_get_mentor = SSdbcore.NewQuery("SELECT id FROM [format_table_name("mentor")] WHERE ckey = '[ckey]'")
var/datum/db_query/query_get_mentor = SSdbcore.NewQuery(
"SELECT id FROM [format_table_name("mentor")] WHERE ckey = :ckey",
list("ckey" = ckey)
)
if(!query_get_mentor.warn_execute())
return
if(query_get_mentor.NextRow())
to_chat(usr, "<span class='danger'>[ckey] is already a mentor.</span>")
return
var/datum/DBQuery/query_add_mentor = SSdbcore.NewQuery("INSERT INTO `[format_table_name("mentor")]` (`id`, `ckey`) VALUES (null, '[ckey]')")
var/datum/db_query/query_add_mentor = SSdbcore.NewQuery("INSERT INTO `[format_table_name("mentor")]` (`id`, `ckey`) VALUES (null, '[ckey]')")
if(!query_add_mentor.warn_execute())
return
var/datum/DBQuery/query_add_admin_log = SSdbcore.NewQuery("INSERT INTO `[format_table_name("admin_log")]` (`id` ,`datetime` ,`adminckey` ,`adminip` ,`log` ) VALUES (NULL , NOW( ) , '[usr.ckey]', '[usr.client.address]', 'Added new mentor [ckey]');")
var/datum/db_query/query_add_admin_log = SSdbcore.NewQuery("INSERT INTO `[format_table_name("admin_log")]` (`id` ,`datetime` ,`adminckey` ,`adminip` ,`log` ) VALUES (NULL , NOW( ) , '[usr.ckey]', '[usr.client.address]', 'Added new mentor [ckey]');")
if(!query_add_admin_log.warn_execute())
return
else
@@ -3003,10 +3008,10 @@
C.mentor_datum = null
GLOB.mentors -= C
if(SSdbcore.Connect())
var/datum/DBQuery/query_remove_mentor = SSdbcore.NewQuery("DELETE FROM [format_table_name("mentor")] WHERE ckey = '[ckey]'")
var/datum/db_query/query_remove_mentor = SSdbcore.NewQuery("DELETE FROM [format_table_name("mentor")] WHERE ckey = '[ckey]'")
if(!query_remove_mentor.warn_execute())
return
var/datum/DBQuery/query_add_admin_log = SSdbcore.NewQuery("INSERT INTO `[format_table_name("admin_log")]` (`id` ,`datetime` ,`adminckey` ,`adminip` ,`log` ) VALUES (NULL , NOW( ) , '[usr.ckey]', '[usr.client.address]', 'Removed mentor [ckey]');")
var/datum/db_query/query_add_admin_log = SSdbcore.NewQuery("INSERT INTO `[format_table_name("admin_log")]` (`id` ,`datetime` ,`adminckey` ,`adminip` ,`log` ) VALUES (NULL , NOW( ) , '[usr.ckey]', '[usr.client.address]', 'Removed mentor [ckey]');")
if(!query_add_admin_log.warn_execute())
return
else

View File

@@ -205,7 +205,7 @@ GLOBAL_DATUM_INIT(ahelp_tickets, /datum/admin_help_tickets, new)
MessageNoRecipient(msg)
//send it to irc if nobody is on and tell us how many were on
var/admin_number_present = send2irc_adminless_only(initiator_ckey, "Ticket #[id]: [name]")
var/admin_number_present = send2tgs_adminless_only(initiator_ckey, "Ticket #[id]: [name]")
log_admin_private("Ticket #[id]: [key_name(initiator)]: [name] - heard by [admin_number_present] non-AFK admins who have +BAN.")
if(admin_number_present <= 0)
to_chat(C, "<span class='notice'>No active admins are online, your adminhelp was sent to the admin irc.</span>")
@@ -222,7 +222,7 @@ GLOBAL_DATUM_INIT(ahelp_tickets, /datum/admin_help_tickets, new)
/datum/admin_help/proc/AddInteraction(formatted_message)
if(heard_by_no_admins && usr && usr.ckey != initiator_ckey)
heard_by_no_admins = FALSE
send2irc(initiator_ckey, "Ticket #[id]: Answered by [key_name(usr)]")
send2adminchat(initiator_ckey, "Ticket #[id]: Answered by [key_name(usr)]")
_interactions += "[TIME_STAMP("hh:mm:ss", FALSE)]: [formatted_message]"
//Removes the ahelp verb and returns it after 2 minutes
@@ -573,7 +573,7 @@ GLOBAL_DATUM_INIT(ahelp_tickets, /datum/admin_help_tickets, new)
. = list("total" = list(), "noflags" = list(), "afk" = list(), "stealth" = list(), "present" = list())
for(var/client/X in GLOB.admins)
.["total"] += X
if(requiredflags != 0 && !check_rights_for(X, requiredflags))
if(requiredflags != NONE && !check_rights_for(X, requiredflags))
.["noflags"] += X
else if(X.is_afk())
.["afk"] += X
@@ -582,7 +582,7 @@ GLOBAL_DATUM_INIT(ahelp_tickets, /datum/admin_help_tickets, new)
else
.["present"] += X
/proc/send2irc_adminless_only(source, msg, requiredflags = R_BAN)
/proc/send2tgs_adminless_only(source, msg, requiredflags = R_BAN)
var/list/adm = get_admin_counts(requiredflags)
var/list/activemins = adm["present"]
. = activemins.len
@@ -596,29 +596,52 @@ GLOBAL_DATUM_INIT(ahelp_tickets, /datum/admin_help_tickets, new)
final = "[msg] - No admins online"
else
final = "[msg] - All admins stealthed\[[english_list(stealthmins)]\], AFK\[[english_list(afkmins)]\], or lacks +BAN\[[english_list(powerlessmins)]\]! Total: [allmins.len] "
send2irc(source,final)
send2adminchat(source,final)
send2otherserver(source,final)
/proc/send2irc(msg,msg2)
msg = replacetext(replacetext(msg, "\proper", ""), "\improper", "")
msg2 = replacetext(replacetext(msg2, "\proper", ""), "\improper", "")
world.TgsTargetedChatBroadcast("[msg] | [msg2]", TRUE)
/proc/send2otherserver(source,msg,type = "Ahelp")
var/comms_key = CONFIG_GET(string/comms_key)
if(!comms_key)
/**
* Sends a message to a set of cross-communications-enabled servers using world topic calls
*
* Arguments:
* * source - Who sent this message
* * msg - The message body
* * type - The type of message, becomes the topic command under the hood
* * target_servers - A collection of servers to send the message to, defined in config
* * additional_data - An (optional) associated list of extra parameters and data to send with this world topic call
*/
/proc/send2otherserver(source, msg, type = "Ahelp", target_servers, list/additional_data = list())
if(!CONFIG_GET(string/comms_key))
debug_world_log("Server cross-comms message not sent for lack of configured key")
return
var/list/message = list()
message["message_sender"] = source
message["message"] = msg
message["source"] = "([CONFIG_GET(string/cross_comms_name)])"
message["key"] = comms_key
message += type
var/our_id = CONFIG_GET(string/cross_comms_name)
additional_data["message_sender"] = source
additional_data["message"] = msg
additional_data["source"] = "([our_id])"
additional_data += type
var/list/servers = CONFIG_GET(keyed_list/cross_server)
for(var/I in servers)
world.Export("[servers[I]]?[list2params(message)]")
if(I == our_id) //No sending to ourselves
continue
if(target_servers && !(I in target_servers))
continue
world.send_cross_comms(I, additional_data)
/// Sends a message to a given cross comms server by name (by name for security).
/world/proc/send_cross_comms(server_name, list/message, auth = TRUE)
set waitfor = FALSE
if (auth)
var/comms_key = CONFIG_GET(string/comms_key)
if(!comms_key)
debug_world_log("Server cross-comms message not sent for lack of configured key")
return
message["key"] = comms_key
var/list/servers = CONFIG_GET(keyed_list/cross_server)
var/server_url = servers[server_name]
if (!server_url)
CRASH("Invalid cross comms config: [server_name]")
world.Export("[server_url]?[list2params(message)]")
/proc/ircadminwho()

View File

@@ -165,7 +165,7 @@
to_chat(src, "<span class='notice'>PM to-<b>Admins</b>: <span class='linkify'>[rawmsg]</span></span>", confidential = TRUE)
var/datum/admin_help/AH = admin_ticket_log(src, "<font color='red'>Reply PM from-<b>[key_name(src, TRUE, TRUE)]</b> to <i>External</i>: [keywordparsedmsg]</font>")
ircreplyamount--
send2irc("[AH ? "#[AH.id] " : ""]Reply: [ckey]", rawmsg)
send2adminchat("[AH ? "#[AH.id] " : ""]Reply: [ckey]", rawmsg)
else
var/badmin = FALSE //Lets figure out if an admin is getting bwoinked.

View File

@@ -13,7 +13,7 @@
if (new_pb && !SSdbcore.Connect())
message_admins("The Database is not connected! Panic bunker will not work until the connection is reestablished.")
SSblackbox.record_feedback("nested tally", "admin_toggle", 1, list("Toggle Panic Bunker", "[new_pb ? "Enabled" : "Disabled"]")) //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc!
send2irc("Panic Bunker", "[key_name(usr)] has toggled the Panic Bunker, it is now [new_pb ? "enabled" : "disabled"].")
send2adminchat("Panic Bunker", "[key_name(usr)] has toggled the Panic Bunker, it is now [new_pb ? "enabled" : "disabled"].")
/client/proc/addbunkerbypass(ckeytobypass as text)
set category = "Special Verbs"
@@ -28,7 +28,7 @@
SSpersistence.SavePanicBunker() //we can do this every time, it's okay
log_admin("[key_name(usr)] has added [ckeytobypass] to the current round's bunker bypass list.")
message_admins("[key_name_admin(usr)] has added [ckeytobypass] to the current round's bunker bypass list.")
send2irc("Panic Bunker", "[key_name(usr)] has added [ckeytobypass] to the current round's bunker bypass list.")
send2adminchat("Panic Bunker", "[key_name(usr)] has added [ckeytobypass] to the current round's bunker bypass list.")
/client/proc/revokebunkerbypass(ckeytobypass as text)
set category = "Special Verbs"
@@ -42,4 +42,4 @@
SSpersistence.SavePanicBunker()
log_admin("[key_name(usr)] has removed [ckeytobypass] from the current round's bunker bypass list.")
message_admins("[key_name_admin(usr)] has removed [ckeytobypass] from the current round's bunker bypass list.")
send2irc("Panic Bunker", "[key_name(usr)] has removed [ckeytobypass] from the current round's bunker bypass list.")
send2adminchat("Panic Bunker", "[key_name(usr)] has removed [ckeytobypass] from the current round's bunker bypass list.")

View File

@@ -1,7 +0,0 @@
Copyright 2018 Jordan Brown
Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.

View File

@@ -1,68 +0,0 @@
/datum/BSQL_Connection
var/id
var/connection_type
BSQL_PROTECT_DATUM(/datum/BSQL_Connection)
/datum/BSQL_Connection/New(connection_type, asyncTimeout, blockingTimeout, threadLimit)
if(asyncTimeout == null)
asyncTimeout = BSQL_DEFAULT_TIMEOUT
if(blockingTimeout == null)
blockingTimeout = asyncTimeout
if(threadLimit == null)
threadLimit = BSQL_DEFAULT_THREAD_LIMIT
src.connection_type = connection_type
world._BSQL_InitCheck(src)
var/error = world._BSQL_Internal_Call("CreateConnection", connection_type, "[asyncTimeout]", "[blockingTimeout]", "[threadLimit]")
if(error)
BSQL_ERROR(error)
return
id = world._BSQL_Internal_Call("GetConnection")
if(!id)
BSQL_ERROR("BSQL library failed to provide connect operation for connection id [id]([connection_type])!")
BSQL_DEL_PROC(/datum/BSQL_Connection)
var/error
if(id)
error = world._BSQL_Internal_Call("ReleaseConnection", id)
. = ..()
if(error)
BSQL_ERROR(error)
/datum/BSQL_Connection/BeginConnect(ipaddress, port, username, password, database)
var/error = world._BSQL_Internal_Call("OpenConnection", id, ipaddress, "[port]", username, password, database)
if(error)
BSQL_ERROR(error)
return
var/op_id = world._BSQL_Internal_Call("GetOperation")
if(!op_id)
BSQL_ERROR("Library failed to provide connect operation for connection id [id]([connection_type])!")
return
return new /datum/BSQL_Operation(src, op_id)
/datum/BSQL_Connection/BeginQuery(query)
var/error = world._BSQL_Internal_Call("NewQuery", id, query)
if(error)
BSQL_ERROR(error)
return
var/op_id = world._BSQL_Internal_Call("GetOperation")
if(!op_id)
BSQL_ERROR("Library failed to provide query operation for connection id [id]([connection_type])!")
return
return new /datum/BSQL_Operation/Query(src, op_id)
/datum/BSQL_Connection/Quote(str)
if(!str)
return null;
. = world._BSQL_Internal_Call("QuoteString", id, "[str]")
if(!.)
BSQL_ERROR("Library failed to provide quote for [str]!")

View File

@@ -1,43 +0,0 @@
/world/proc/_BSQL_Internal_Call(func, ...)
var/list/call_args = args.Copy(2)
BSQL_Debug("_BSQL_Internal_Call(): [args[1]]([call_args.Join(", ")])")
. = call(_BSQL_Library_Path(), func)(arglist(call_args))
BSQL_Debug("Result: [. == null ? "NULL" : "\"[.]\""]")
/world/proc/_BSQL_Library_Path()
return system_type == MS_WINDOWS ? "BSQL.dll" : "libBSQL.so"
/world/proc/_BSQL_InitCheck(datum/BSQL_Connection/caller)
var/static/library_initialized = FALSE
if(_BSQL_Initialized())
return
var/libPath = _BSQL_Library_Path()
if(!fexists(libPath))
BSQL_DEL_CALL(caller)
BSQL_ERROR("Could not find [libPath]!")
return
var/version = _BSQL_Internal_Call("Version")
if(version != BSQL_VERSION)
BSQL_DEL_CALL(caller)
BSQL_ERROR("BSQL DMAPI version mismatch! Expected [BSQL_VERSION], got [version == null ? "NULL" : version]!")
return
var/result = _BSQL_Internal_Call("Initialize")
if(result)
BSQL_DEL_CALL(caller)
BSQL_ERROR(result)
return
_BSQL_Initialized(TRUE)
/world/proc/_BSQL_Initialized(new_val)
var/static/bsql_library_initialized = FALSE
if(new_val != null)
bsql_library_initialized = new_val
return bsql_library_initialized
/world/BSQL_Shutdown()
if(!_BSQL_Initialized())
return
_BSQL_Internal_Call("Shutdown")
_BSQL_Initialized(FALSE)

View File

@@ -1,47 +0,0 @@
/datum/BSQL_Operation
var/datum/BSQL_Connection/connection
var/id
BSQL_PROTECT_DATUM(/datum/BSQL_Operation)
/datum/BSQL_Operation/New(datum/BSQL_Connection/connection, id)
src.connection = connection
src.id = id
BSQL_DEL_PROC(/datum/BSQL_Operation)
var/error
if(!BSQL_IS_DELETED(connection))
error = world._BSQL_Internal_Call("ReleaseOperation", connection.id, id)
. = ..()
if(error)
BSQL_ERROR(error)
/datum/BSQL_Operation/IsComplete()
if(BSQL_IS_DELETED(connection))
return TRUE
var/result = world._BSQL_Internal_Call("OpComplete", connection.id, id)
if(!result)
BSQL_ERROR("Error fetching operation [id] for connection [connection.id]!")
return
return result == "DONE"
/datum/BSQL_Operation/GetError()
if(BSQL_IS_DELETED(connection))
return "Connection deleted!"
return world._BSQL_Internal_Call("GetError", connection.id, id)
/datum/BSQL_Operation/GetErrorCode()
if(BSQL_IS_DELETED(connection))
return -2
return text2num(world._BSQL_Internal_Call("GetErrorCode", connection.id, id))
/datum/BSQL_Operation/WaitForCompletion()
if(BSQL_IS_DELETED(connection))
return
var/error = world._BSQL_Internal_Call("BlockOnOperation", connection.id, id)
if(error)
if(error == "Operation timed out!") //match this with the implementation
return FALSE
BSQL_ERROR("Error waiting for operation [id] for connection [connection.id]! [error]")
return
return TRUE

View File

@@ -1,35 +0,0 @@
/datum/BSQL_Operation/Query
var/last_result_json
var/list/last_result
BSQL_PROTECT_DATUM(/datum/BSQL_Operation/Query)
/datum/BSQL_Operation/Query/CurrentRow()
return last_result
/datum/BSQL_Operation/Query/IsComplete()
//whole different ballgame here
if(BSQL_IS_DELETED(connection))
return TRUE
var/result = world._BSQL_Internal_Call("ReadyRow", connection.id, id)
switch(result)
if("DONE")
//load the data
LoadQueryResult()
return TRUE
if("NOTDONE")
return FALSE
else
BSQL_ERROR(result)
/datum/BSQL_Operation/Query/WaitForCompletion()
. = ..()
if(.)
LoadQueryResult()
/datum/BSQL_Operation/Query/proc/LoadQueryResult()
last_result_json = world._BSQL_Internal_Call("GetRow", connection.id, id)
if(last_result_json)
last_result = json_decode(last_result_json)
else
last_result = null

View File

@@ -1,4 +0,0 @@
#include "core\connection.dm"
#include "core\library.dm"
#include "core\operation.dm"
#include "core\query.dm"

View File

@@ -42,18 +42,21 @@
/datum/supply_pack/misc/book_crate
name = "Book Crate"
desc = "Surplus from the Nanotrasen Archives, these five books are sure to be good reads."
desc = "Surplus from the Nanotrasen Archives, these seven books are sure to be good reads."
// cost = CARGO_CRATE_VALUE * 3
cost = 1500
contains = list(/obj/item/book/codex_gigas,
/obj/item/book/manual/random/,
/obj/item/book/manual/random/,
/obj/item/book/manual/random/,
/obj/item/book/random/triple)
/obj/item/book/random,
/obj/item/book/random,
/obj/item/book/random)
crate_type = /obj/structure/closet/crate/wooden
/datum/supply_pack/misc/paper
name = "Bureaucracy Crate"
desc = "High stacks of papers on your desk Are a big problem - make it Pea-sized with these bureaucratic supplies! Contains five pens, some camera film, hand labeler supplies, a paper bin, three folders, two clipboards and two stamps as well as a briefcase."//that was too forced
desc = "High stacks of papers on your desk Are a big problem - make it Pea-sized with these bureaucratic supplies! Contains six pens, some camera film, hand labeler supplies, a paper bin, a carbon paper bin, three folders, a laser pointer, two clipboards and two stamps."//that was too forced
cost = 1500
contains = list(/obj/structure/filingcabinet/chestdrawer/wheeled,
/obj/item/camera_film,
@@ -61,9 +64,11 @@
/obj/item/hand_labeler_refill,
/obj/item/hand_labeler_refill,
/obj/item/paper_bin,
/obj/item/paper_bin/carbon,
/obj/item/pen/fourcolor,
/obj/item/pen/fourcolor,
/obj/item/pen,
/obj/item/pen/fountain,
/obj/item/pen/blue,
/obj/item/pen/red,
/obj/item/folder/blue,
@@ -73,7 +78,7 @@
/obj/item/clipboard,
/obj/item/stamp,
/obj/item/stamp/denied,
/obj/item/storage/briefcase)
/obj/item/laser_pointer/purple)
crate_name = "bureaucracy crate"
/datum/supply_pack/misc/captain_pen
@@ -94,6 +99,30 @@
crate_type = /obj/structure/closet/crate/wooden
crate_name = "calligraphy crate"
/datum/supply_pack/misc/toner
name = "Toner Crate"
desc = "Spent too much ink printing butt pictures? Fret not, with these six toner refills, you'll be printing butts 'till the cows come home!'"
cost = 200 * 4
contains = list(/obj/item/toner,
/obj/item/toner,
/obj/item/toner,
/obj/item/toner,
/obj/item/toner,
/obj/item/toner)
crate_name = "toner crate"
/datum/supply_pack/misc/toner_large
name = "Toner Crate (Large)"
desc = "Tired of changing toner cartridges? These six extra heavy duty refills contain roughly five times as much toner as the base model!"
cost = 200 * 6
contains = list(/obj/item/toner/large,
/obj/item/toner/large,
/obj/item/toner/large,
/obj/item/toner/large,
/obj/item/toner/large,
/obj/item/toner/large)
crate_name = "large toner crate"
//////////////////////////////////////////////////////////////////////////////
//////////////////////////////// Entertainment ///////////////////////////////
//////////////////////////////////////////////////////////////////////////////

View File

@@ -419,7 +419,7 @@ GLOBAL_LIST_INIT(blacklisted_builds, list(
if (nnpa >= 0)
message_admins("New user: [key_name_admin(src)] is connecting here for the first time.")
if (CONFIG_GET(flag/irc_first_connection_alert))
send2irc_adminless_only("New-user", "[key_name(src)] is connecting for the first time!")
send2tgs_adminless_only("New-user", "[key_name(src)] is connecting for the first time!")
else if (isnum(cached_player_age) && cached_player_age < nnpa)
message_admins("New user: [key_name_admin(src)] just connected with an age of [cached_player_age] day[(player_age==1?"":"s")]")
if(CONFIG_GET(flag/use_account_age_for_jobs) && account_age >= 0)
@@ -427,7 +427,7 @@ GLOBAL_LIST_INIT(blacklisted_builds, list(
if(account_age >= 0 && account_age < nnpa)
message_admins("[key_name_admin(src)] (IP: [address], ID: [computer_id]) is a new BYOND account [account_age] day[(account_age==1?"":"s")] old, created on [account_join_date].")
if (CONFIG_GET(flag/irc_first_connection_alert))
send2irc_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].")
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()
validate_key_in_db()
@@ -523,7 +523,7 @@ GLOBAL_LIST_INIT(blacklisted_builds, list(
"Forever alone :("\
)
send2irc("Server", "[cheesy_message] (No admins online)")
send2adminchat("Server", "[cheesy_message] (No admins online)")
QDEL_LIST_ASSOC_VAL(char_render_holders)
if(movingmob != null)
movingmob.client_mobs_in_contents -= mob
@@ -538,14 +538,21 @@ GLOBAL_LIST_INIT(blacklisted_builds, list(
return
if(!SSdbcore.Connect())
return
var/sql_ckey = sanitizeSQL(src.ckey)
var/datum/DBQuery/query_get_related_ip = SSdbcore.NewQuery("SELECT ckey FROM [format_table_name("player")] WHERE ip = INET_ATON('[address]') AND ckey != '[sql_ckey]'")
query_get_related_ip.Execute()
var/datum/db_query/query_get_related_ip = SSdbcore.NewQuery(
"SELECT ckey FROM [format_table_name("player")] WHERE ip = INET_ATON(:address) AND ckey != :ckey",
list("address" = address, "ckey" = ckey)
)
if(!query_get_related_ip.Execute())
qdel(query_get_related_ip)
return
related_accounts_ip = ""
while(query_get_related_ip.NextRow())
related_accounts_ip += "[query_get_related_ip.item[1]], "
qdel(query_get_related_ip)
var/datum/DBQuery/query_get_related_cid = SSdbcore.NewQuery("SELECT ckey FROM [format_table_name("player")] WHERE computerid = '[computer_id]' AND ckey != '[sql_ckey]'")
var/datum/db_query/query_get_related_cid = SSdbcore.NewQuery(
"SELECT ckey FROM [format_table_name("player")] WHERE computerid = :computerid AND ckey != :ckey",
list("computerid" = computer_id, "ckey" = ckey)
)
if(!query_get_related_cid.Execute())
qdel(query_get_related_cid)
return
@@ -559,45 +566,40 @@ GLOBAL_LIST_INIT(blacklisted_builds, list(
else
if (!GLOB.deadmins[ckey] && check_randomizer(connectiontopic))
return
var/sql_ip = sanitizeSQL(address)
var/sql_computerid = sanitizeSQL(computer_id)
var/sql_admin_rank = sanitizeSQL(admin_rank)
var/new_player
var/datum/DBQuery/query_client_in_db = SSdbcore.NewQuery("SELECT 1 FROM [format_table_name("player")] WHERE ckey = '[sql_ckey]'")
var/datum/db_query/query_client_in_db = SSdbcore.NewQuery(
"SELECT 1 FROM [format_table_name("player")] WHERE ckey = :ckey",
list("ckey" = ckey)
)
if(!query_client_in_db.Execute())
qdel(query_client_in_db)
return
if(!query_client_in_db.NextRow()) //new user detected
if(!holder && !GLOB.deadmins[ckey])
if(CONFIG_GET(flag/panic_bunker) && !(ckey in GLOB.bunker_passthrough))
log_access("Failed Login: [key] - New account attempting to connect during panic bunker")
message_admins("<span class='adminnotice'>Failed Login: [key] - New account attempting to connect during panic bunker</span>")
to_chat(src, "<span class='notice'>You must first join the Discord to verify your account before joining this server.<br>To do so, read the rules and post a request in the #station-access-requests channel under the \"Main server\" category in the Discord server linked here: <a href='https://discord.gg/E6SQuhz'>https://discord.gg/E6SQuhz</a><br>If you have already done so, wait a few minutes then try again; sometimes the server needs to fully load before you can join.</span>") //CIT CHANGE - makes the panic bunker disconnect message point to the discord
var/list/connectiontopic_a = params2list(connectiontopic)
var/list/panic_addr = CONFIG_GET(string/panic_server_address)
if(panic_addr && !connectiontopic_a["redirect"])
var/panic_name = CONFIG_GET(string/panic_server_name)
to_chat(src, "<span class='notice'>Sending you to [panic_name ? panic_name : panic_addr].</span>")
winset(src, null, "command=.options")
src << link("[panic_addr]?redirect=1")
qdel(query_client_in_db)
qdel(src)
return
new_player = 1
account_join_date = sanitizeSQL(findJoinDate())
var/sql_key = sanitizeSQL(key)
var/datum/DBQuery/query_add_player = SSdbcore.NewQuery("INSERT INTO [format_table_name("player")] (`ckey`, `byond_key`, `firstseen`, `firstseen_round_id`, `lastseen`, `lastseen_round_id`, `ip`, `computerid`, `lastadminrank`, `accountjoindate`) VALUES ('[sql_ckey]', '[sql_key]', Now(), '[GLOB.round_id]', Now(), '[GLOB.round_id]', INET_ATON('[sql_ip]'), '[sql_computerid]', '[sql_admin_rank]', [account_join_date ? "'[account_join_date]'" : "NULL"])")
if(!query_add_player.Execute())
qdel(query_client_in_db)
qdel(query_add_player)
return
qdel(query_add_player)
if(!account_join_date)
account_join_date = "Error"
account_age = -1
else if(ckey in GLOB.bunker_passthrough)
GLOB.bunker_passthrough -= ckey
//If we aren't an admin, and the flag is set
if(CONFIG_GET(flag/panic_bunker) && !holder && !GLOB.deadmins[ckey] && !(ckey in GLOB.bunker_passthrough))
var/living_recs = CONFIG_GET(number/panic_bunker_living)
//Relies on pref existing, but this proc is only called after that occurs, so we're fine.
var/minutes = get_exp_living(pure_numeric = TRUE)
if(minutes <= living_recs) // && !CONFIG_GET(flag/panic_bunker_interview)
var/reject_message = "Failed Login: [key] - Account attempting to connect during panic bunker, but they do not have the required living time [minutes]/[living_recs]"
log_access(reject_message)
message_admins("<span class='adminnotice'>[reject_message]</span>")
var/message = CONFIG_GET(string/panic_bunker_message)
message = replacetext(message, "%minutes%", living_recs)
to_chat(src, message)
var/list/connectiontopic_a = params2list(connectiontopic)
var/list/panic_addr = CONFIG_GET(string/panic_server_address)
if(panic_addr && !connectiontopic_a["redirect"])
var/panic_name = CONFIG_GET(string/panic_server_name)
to_chat(src, "<span class='notice'>Sending you to [panic_name ? panic_name : panic_addr].</span>")
winset(src, null, "command=.options")
src << link("[panic_addr]?redirect=1")
qdel(query_client_in_db)
qdel(src)
return
if(!query_client_in_db.NextRow())
new_player = 1
if(CONFIG_GET(flag/age_verification)) //setup age verification
if(!set_db_player_flags())
message_admins(usr, "<span class='danger'>ERROR: Unable to read player flags from database. Please check logs.</span>")
@@ -609,9 +611,24 @@ GLOBAL_LIST_INIT(blacklisted_builds, list(
update_flag_db(DB_FLAG_AGE_CONFIRMATION_COMPLETE, TRUE)
else
update_flag_db(DB_FLAG_AGE_CONFIRMATION_INCOMPLETE, TRUE)
account_join_date = findJoinDate()
var/datum/db_query/query_add_player = SSdbcore.NewQuery({"
INSERT INTO [format_table_name("player")] (`ckey`, `byond_key`, `firstseen`, `firstseen_round_id`, `lastseen`, `lastseen_round_id`, `ip`, `computerid`, `lastadminrank`, `accountjoindate`)
VALUES (:ckey, :key, Now(), :round_id, Now(), :round_id, INET_ATON(:ip), :computerid, :adminrank, :account_join_date)
"}, list("ckey" = ckey, "key" = key, "round_id" = GLOB.round_id, "ip" = address, "computerid" = computer_id, "adminrank" = admin_rank, "account_join_date" = account_join_date || null))
if(!query_add_player.Execute())
qdel(query_client_in_db)
qdel(query_add_player)
return
qdel(query_add_player)
if(!account_join_date)
account_join_date = "Error"
account_age = -1
qdel(query_client_in_db)
var/datum/DBQuery/query_get_client_age = SSdbcore.NewQuery("SELECT firstseen, DATEDIFF(Now(),firstseen), accountjoindate, DATEDIFF(Now(),accountjoindate) FROM [format_table_name("player")] WHERE ckey = '[sql_ckey]'")
var/datum/db_query/query_get_client_age = SSdbcore.NewQuery(
"SELECT firstseen, DATEDIFF(Now(),firstseen), accountjoindate, DATEDIFF(Now(),accountjoindate) FROM [format_table_name("player")] WHERE ckey = :ckey",
list("ckey" = ckey)
)
if(!query_get_client_age.Execute())
qdel(query_get_client_age)
return
@@ -622,11 +639,14 @@ GLOBAL_LIST_INIT(blacklisted_builds, list(
account_join_date = query_get_client_age.item[3]
account_age = text2num(query_get_client_age.item[4])
if(!account_age)
account_join_date = sanitizeSQL(findJoinDate())
account_join_date = findJoinDate()
if(!account_join_date)
account_age = -1
else
var/datum/DBQuery/query_datediff = SSdbcore.NewQuery("SELECT DATEDIFF(Now(),'[account_join_date]')")
var/datum/db_query/query_datediff = SSdbcore.NewQuery(
"SELECT DATEDIFF(Now(), :account_join_date)",
list("account_join_date" = account_join_date)
)
if(!query_datediff.Execute())
qdel(query_datediff)
return
@@ -635,14 +655,20 @@ GLOBAL_LIST_INIT(blacklisted_builds, list(
qdel(query_datediff)
qdel(query_get_client_age)
if(!new_player)
var/datum/DBQuery/query_log_player = SSdbcore.NewQuery("UPDATE [format_table_name("player")] SET lastseen = Now(), lastseen_round_id = '[GLOB.round_id]', ip = INET_ATON('[sql_ip]'), computerid = '[sql_computerid]', lastadminrank = '[sql_admin_rank]', accountjoindate = [account_join_date ? "'[account_join_date]'" : "NULL"] WHERE ckey = '[sql_ckey]'")
var/datum/db_query/query_log_player = SSdbcore.NewQuery(
"UPDATE [format_table_name("player")] SET lastseen = Now(), lastseen_round_id = :round_id, ip = INET_ATON(:ip), computerid = :computerid, lastadminrank = :admin_rank, accountjoindate = :account_join_date WHERE ckey = :ckey",
list("round_id" = GLOB.round_id, "ip" = address, "computerid" = computer_id, "admin_rank" = admin_rank, "account_join_date" = account_join_date || null, "ckey" = ckey)
)
if(!query_log_player.Execute())
qdel(query_log_player)
return
qdel(query_log_player)
if(!account_join_date)
account_join_date = "Error"
var/datum/DBQuery/query_log_connection = SSdbcore.NewQuery("INSERT INTO `[format_table_name("connection_log")]` (`id`,`datetime`,`server_ip`,`server_port`,`round_id`,`ckey`,`ip`,`computerid`) VALUES(null,Now(),INET_ATON(IF('[world.internet_address]' LIKE '', '0', '[world.internet_address]')),'[world.port]','[GLOB.round_id]','[sql_ckey]',INET_ATON('[sql_ip]'),'[sql_computerid]')")
var/datum/db_query/query_log_connection = SSdbcore.NewQuery({"
INSERT INTO `[format_table_name("connection_log")]` (`id`,`datetime`,`server_ip`,`server_port`,`round_id`,`ckey`,`ip`,`computerid`)
VALUES(null,Now(),INET_ATON(:internet_address),:port,:round_id,:ckey,INET_ATON(:ip),:computerid)
"}, list("internet_address" = world.internet_address || "0", "port" = world.port, "round_id" = GLOB.round_id, "ckey" = ckey, "ip" = address, "computerid" = computer_id))
query_log_connection.Execute()
qdel(query_log_connection)
@@ -666,9 +692,11 @@ GLOBAL_LIST_INIT(blacklisted_builds, list(
CRASH("Age check regex failed for [src.ckey]")
/client/proc/validate_key_in_db()
var/sql_ckey = sanitizeSQL(ckey)
var/sql_key
var/datum/DBQuery/query_check_byond_key = SSdbcore.NewQuery("SELECT byond_key FROM [format_table_name("player")] WHERE ckey = '[sql_ckey]'")
var/datum/db_query/query_check_byond_key = SSdbcore.NewQuery(
"SELECT byond_key FROM [format_table_name("player")] WHERE ckey = :ckey",
list("ckey" = ckey)
)
if(!query_check_byond_key.Execute())
qdel(query_check_byond_key)
return
@@ -684,8 +712,11 @@ GLOBAL_LIST_INIT(blacklisted_builds, list(
if(F)
var/regex/R = regex("\\tkey = \"(.+)\"")
if(R.Find(F))
var/web_key = sanitizeSQL(R.group[1])
var/datum/DBQuery/query_update_byond_key = SSdbcore.NewQuery("UPDATE [format_table_name("player")] SET byond_key = '[web_key]' WHERE ckey = '[sql_ckey]'")
var/web_key = R.group[1]
var/datum/db_query/query_update_byond_key = SSdbcore.NewQuery(
"UPDATE [format_table_name("player")] SET byond_key = :byond_key WHERE ckey = :ckey",
list("byond_key" = web_key, "ckey" = ckey)
)
query_update_byond_key.Execute()
qdel(query_update_byond_key)
else
@@ -702,8 +733,10 @@ GLOBAL_LIST_INIT(blacklisted_builds, list(
var/static/tokens = list()
var/static/cidcheck_failedckeys = list() //to avoid spamming the admins if the same guy keeps trying.
var/static/cidcheck_spoofckeys = list()
var/sql_ckey = sanitizeSQL(ckey)
var/datum/DBQuery/query_cidcheck = SSdbcore.NewQuery("SELECT computerid FROM [format_table_name("player")] WHERE ckey = '[sql_ckey]'")
var/datum/db_query/query_cidcheck = SSdbcore.NewQuery(
"SELECT computerid FROM [format_table_name("player")] WHERE ckey = :ckey",
list("ckey" = ckey)
)
query_cidcheck.Execute()
var/lastcid
@@ -722,7 +755,7 @@ GLOBAL_LIST_INIT(blacklisted_builds, list(
sleep(15 SECONDS) //Longer sleep here since this would trigger if a client tries to reconnect manually because the inital reconnect failed
//we sleep after telling the client to reconnect, so if we still exist something is up
//we sleep after telling the client to reconnect, so if we still exist something is up
log_access("Forced disconnect: [key] [computer_id] [address] - CID randomizer check")
qdel(src)
@@ -736,7 +769,7 @@ GLOBAL_LIST_INIT(blacklisted_builds, list(
if (!cidcheck_failedckeys[ckey])
message_admins("<span class='adminnotice'>[key_name(src)] has been detected as using a cid randomizer. Connection rejected.</span>")
send2irc_adminless_only("CidRandomizer", "[key_name(src)] has been detected as using a cid randomizer. Connection rejected.")
send2tgs_adminless_only("CidRandomizer", "[key_name(src)] has been detected as using a cid randomizer. Connection rejected.")
cidcheck_failedckeys[ckey] = TRUE
note_randomizer_user()
@@ -747,7 +780,7 @@ GLOBAL_LIST_INIT(blacklisted_builds, list(
else
if (cidcheck_failedckeys[ckey])
message_admins("<span class='adminnotice'>[key_name_admin(src)] has been allowed to connect after showing they removed their cid randomizer</span>")
send2irc_adminless_only("CidRandomizer", "[key_name(src)] has been allowed to connect after showing they removed their cid randomizer.")
send2tgs_adminless_only("CidRandomizer", "[key_name(src)] has been allowed to connect after showing they removed their cid randomizer.")
cidcheck_failedckeys -= ckey
if (cidcheck_spoofckeys[ckey])
message_admins("<span class='adminnotice'>[key_name_admin(src)] has been allowed to connect after appearing to have attempted to spoof a cid randomizer check because it <i>appears</i> they aren't spoofing one this time</span>")
@@ -778,10 +811,11 @@ GLOBAL_LIST_INIT(blacklisted_builds, list(
add_system_note("CID-Error", "Detected as using a cid randomizer.")
/client/proc/add_system_note(system_ckey, message)
var/sql_system_ckey = sanitizeSQL(system_ckey)
var/sql_ckey = sanitizeSQL(ckey)
//check to see if we noted them in the last day.
var/datum/DBQuery/query_get_notes = SSdbcore.NewQuery("SELECT id FROM [format_table_name("messages")] WHERE type = 'note' AND targetckey = '[sql_ckey]' AND adminckey = '[sql_system_ckey]' AND timestamp + INTERVAL 1 DAY < NOW() AND deleted = 0 AND expire_timestamp > NOW()")
var/datum/db_query/query_get_notes = SSdbcore.NewQuery(
"SELECT id FROM [format_table_name("messages")] WHERE type = 'note' AND targetckey = :targetckey AND adminckey = :adminckey AND timestamp + INTERVAL 1 DAY < NOW() AND deleted = 0 AND (expire_timestamp > NOW() OR expire_timestamp IS NULL)",
list("targetckey" = ckey, "adminckey" = system_ckey)
)
if(!query_get_notes.Execute())
qdel(query_get_notes)
return
@@ -790,7 +824,10 @@ GLOBAL_LIST_INIT(blacklisted_builds, list(
return
qdel(query_get_notes)
//regardless of above, make sure their last note is not from us, as no point in repeating the same note over and over.
query_get_notes = SSdbcore.NewQuery("SELECT adminckey FROM [format_table_name("messages")] WHERE targetckey = '[sql_ckey]' AND deleted = 0 AND expire_timestamp > NOW() ORDER BY timestamp DESC LIMIT 1")
query_get_notes = SSdbcore.NewQuery(
"SELECT adminckey FROM [format_table_name("messages")] WHERE targetckey = :targetckey AND deleted = 0 AND (expire_timestamp > NOW() OR expire_timestamp IS NULL) ORDER BY timestamp DESC LIMIT 1",
list("targetckey" = ckey)
)
if(!query_get_notes.Execute())
qdel(query_get_notes)
return

View File

@@ -1355,9 +1355,11 @@ GLOBAL_LIST_EMPTY(preferences_datums)
/datum/preferences/proc/process_link(mob/user, list/href_list)
if(href_list["jobbancheck"])
var/job = sanitizeSQL(href_list["jobbancheck"])
var/sql_ckey = sanitizeSQL(user.ckey)
var/datum/DBQuery/query_get_jobban = SSdbcore.NewQuery("SELECT reason, bantime, duration, expiration_time, IFNULL((SELECT byond_key FROM [format_table_name("player")] WHERE [format_table_name("player")].ckey = [format_table_name("ban")].a_ckey), a_ckey) FROM [format_table_name("ban")] WHERE ckey = '[sql_ckey]' AND (bantype = 'JOB_PERMABAN' OR (bantype = 'JOB_TEMPBAN' AND expiration_time > Now())) AND isnull(unbanned) AND job = '[job]'")
var/job = href_list["jobbancheck"]
var/datum/db_query/query_get_jobban = SSdbcore.NewQuery({"
SELECT reason, bantime, duration, expiration_time, IFNULL((SELECT byond_key FROM [format_table_name("player")] WHERE [format_table_name("player")].ckey = [format_table_name("ban")].a_ckey), a_ckey)
FROM [format_table_name("ban")] WHERE ckey = :ckey AND (bantype = 'JOB_PERMABAN' OR (bantype = 'JOB_TEMPBAN' AND expiration_time > Now())) AND isnull(unbanned) AND job = :job
"}, list("ckey" = user.ckey, "job" = job))
if(!query_get_jobban.warn_execute())
qdel(query_get_jobban)
return
@@ -1372,7 +1374,7 @@ GLOBAL_LIST_EMPTY(preferences_datums)
if(text2num(duration) > 0)
text += ". The ban is for [duration] minutes and expires on [expiration_time] (server time)"
text += ".</span>"
to_chat(user, text)
to_chat(user, text, confidential = TRUE)
qdel(query_get_jobban)
return

View File

@@ -179,7 +179,7 @@ GLOBAL_VAR_INIT(normal_ooc_colour, "#002eb8")
to_chat(usr, "<span class='notice'>Sorry, that function is not enabled on this server.</span>")
return
browse_messages(null, usr.ckey, null, TRUE, override = TRUE)
browse_messages(null, usr.ckey, null, TRUE)
/client/proc/self_playtime()
set name = "View tracked playtime"

View File

@@ -1,7 +1,6 @@
GLOBAL_LIST_EMPTY(exp_to_update)
GLOBAL_PROTECT(exp_to_update)
// Procs
/datum/job/proc/required_playtime_remaining(client/C)
if(!C)
@@ -57,6 +56,7 @@ GLOBAL_PROTECT(exp_to_update)
amount += explist[job]
return amount
// todo: port tgui exp
/client/proc/get_exp_report()
if(!CONFIG_GET(flag/use_exp_tracking))
return "Tracking is disabled in the server configuration file."
@@ -121,12 +121,11 @@ GLOBAL_PROTECT(exp_to_update)
return_text += "</LI></UL>"
return return_text
/client/proc/get_exp_living()
if(!prefs.exp)
return "No data"
/client/proc/get_exp_living(pure_numeric = FALSE)
if(!prefs.exp || !prefs.exp[EXP_TYPE_LIVING])
return pure_numeric ? 0 : "No data"
var/exp_living = text2num(prefs.exp[EXP_TYPE_LIVING])
return get_exp_format(exp_living)
return pure_numeric ? exp_living : get_exp_format(exp_living)
/proc/get_exp_format(expnum)
if(expnum > 60)
@@ -148,7 +147,7 @@ GLOBAL_PROTECT(exp_to_update)
set waitfor = FALSE
var/list/old_minutes = GLOB.exp_to_update
GLOB.exp_to_update = null
SSdbcore.MassInsert(format_table_name("role_time"), old_minutes, "ON DUPLICATE KEY UPDATE minutes = minutes + VALUES(minutes)")
SSdbcore.MassInsert(format_table_name("role_time"), old_minutes, duplicate_key = "ON DUPLICATE KEY UPDATE minutes = minutes + VALUES(minutes)")
//resets a client's exp to what was in the db.
/client/proc/set_exp_from_db()
@@ -156,7 +155,10 @@ GLOBAL_PROTECT(exp_to_update)
return -1
if(!SSdbcore.Connect())
return -1
var/datum/DBQuery/exp_read = SSdbcore.NewQuery("SELECT job, minutes FROM [format_table_name("role_time")] WHERE ckey = '[sanitizeSQL(ckey)]'")
var/datum/db_query/exp_read = SSdbcore.NewQuery(
"SELECT job, minutes FROM [format_table_name("role_time")] WHERE ckey = :ckey",
list("ckey" = ckey)
)
if(!exp_read.Execute(async = TRUE))
qdel(exp_read)
return -1
@@ -188,7 +190,10 @@ GLOBAL_PROTECT(exp_to_update)
else
prefs.db_flags |= newflag
var/datum/DBQuery/flag_update = SSdbcore.NewQuery("UPDATE [format_table_name("player")] SET flags = '[prefs.db_flags]' WHERE ckey='[sanitizeSQL(ckey)]'")
var/datum/db_query/flag_update = SSdbcore.NewQuery(
"UPDATE [format_table_name("player")] SET flags=:flags WHERE ckey=:ckey",
list("flags" = "[prefs.db_flags]", "ckey" = ckey)
)
if(!flag_update.Execute())
qdel(flag_update)
@@ -256,8 +261,8 @@ GLOBAL_PROTECT(exp_to_update)
CRASH("invalid job value [jtype]:[jvalue]")
LAZYINITLIST(GLOB.exp_to_update)
GLOB.exp_to_update.Add(list(list(
"job" = "'[sanitizeSQL(jtype)]'",
"ckey" = "'[sanitizeSQL(ckey)]'",
"job" = jtype,
"ckey" = ckey,
"minutes" = jvalue)))
prefs.exp[jtype] += jvalue
addtimer(CALLBACK(SSblackbox,/datum/controller/subsystem/blackbox/proc/update_exp_db),20,TIMER_OVERRIDE|TIMER_UNIQUE)
@@ -268,7 +273,10 @@ GLOBAL_PROTECT(exp_to_update)
if(!SSdbcore.Connect())
return FALSE
var/datum/DBQuery/flags_read = SSdbcore.NewQuery("SELECT flags FROM [format_table_name("player")] WHERE ckey='[ckey]'")
var/datum/db_query/flags_read = SSdbcore.NewQuery(
"SELECT flags FROM [format_table_name("player")] WHERE ckey=:ckey",
list("ckey" = ckey)
)
if(!flags_read.Execute(async = TRUE))
qdel(flags_read)

View File

@@ -1,3 +1,7 @@
#define BOOKCASE_UNANCHORED 0
#define BOOKCASE_ANCHORED 1
#define BOOKCASE_FINISHED 2
/* Library Items
*
* Contains:
@@ -17,69 +21,85 @@
desc = "A great place for storing knowledge."
anchored = FALSE
density = TRUE
opacity = 0
opacity = FALSE
resistance_flags = FLAMMABLE
max_integrity = 200
armor = list("melee" = 0, "bullet" = 0, "laser" = 0, "energy" = 0, "bomb" = 0, "bio" = 0, "rad" = 0, "fire" = 50, "acid" = 0)
var/state = 0
var/list/allowed_books = list(/obj/item/book, /obj/item/spellbook, /obj/item/storage/book, /obj/item/gun/magic/wand/book) //Things allowed in the bookcase
var/state = BOOKCASE_UNANCHORED
/// When enabled, books_to_load number of random books will be generated for this bookcase when first interacted with.
var/load_random_books = FALSE
/// The category of books to pick from when populating random books.
var/random_category = null
/// How many random books to generate.
var/books_to_load = 0
/obj/structure/bookcase/examine(mob/user)
. = ..()
if(!anchored)
. += "<span class='notice'>The <i>bolts</i> on the bottom are unsecured.</span>"
if(anchored)
else
. += "<span class='notice'>It's secured in place with <b>bolts</b>.</span>"
switch(state)
if(0)
if(BOOKCASE_UNANCHORED)
. += "<span class='notice'>There's a <b>small crack</b> visible on the back panel.</span>"
if(1)
if(BOOKCASE_ANCHORED)
. += "<span class='notice'>There's space inside for a <i>wooden</i> shelf.</span>"
if(2)
if(BOOKCASE_FINISHED)
. += "<span class='notice'>There's a <b>small crack</b> visible on the shelf.</span>"
/obj/structure/bookcase/Initialize(mapload)
. = ..()
if(!mapload)
return
state = 2
icon_state = "book-0"
anchored = TRUE
set_anchored(TRUE)
state = BOOKCASE_FINISHED
for(var/obj/item/I in loc)
if(istype(I, /obj/item/book))
I.forceMove(src)
if(!isbook(I))
continue
I.forceMove(src)
update_icon()
/obj/structure/bookcase/set_anchored(anchorvalue)
. = ..()
if(isnull(.))
return
state = anchorvalue
if(!anchorvalue) //in case we were vareditted or uprooted by a hostile mob, ensure we drop all our books instead of having them disappear till we're rebuild.
var/atom/Tsec = drop_location()
for(var/obj/I in contents)
if(!isbook(I))
continue
I.forceMove(Tsec)
update_icon()
/obj/structure/bookcase/attackby(obj/item/I, mob/user, params)
switch(state)
if(0)
if(BOOKCASE_UNANCHORED)
if(I.tool_behaviour == TOOL_WRENCH)
if(I.use_tool(src, user, 20, volume=50))
to_chat(user, "<span class='notice'>You wrench the frame into place.</span>")
anchored = TRUE
state = 1
if(I.tool_behaviour == TOOL_CROWBAR)
set_anchored(TRUE)
else if(I.tool_behaviour == TOOL_CROWBAR)
if(I.use_tool(src, user, 20, volume=50))
to_chat(user, "<span class='notice'>You pry the frame apart.</span>")
deconstruct(TRUE)
if(1)
if(BOOKCASE_ANCHORED)
if(istype(I, /obj/item/stack/sheet/mineral/wood))
var/obj/item/stack/sheet/mineral/wood/W = I
if(W.get_amount() >= 2)
W.use(2)
to_chat(user, "<span class='notice'>You add a shelf.</span>")
state = 2
icon_state = "book-0"
if(I.tool_behaviour == TOOL_WRENCH)
state = BOOKCASE_FINISHED
update_icon()
else if(I.tool_behaviour == TOOL_WRENCH)
I.play_tool_sound(src, 100)
to_chat(user, "<span class='notice'>You unwrench the frame.</span>")
anchored = FALSE
state = 0
set_anchored(FALSE)
if(2)
if(BOOKCASE_FINISHED)
var/datum/component/storage/STR = I.GetComponent(/datum/component/storage)
if(is_type_in_list(I, allowed_books))
if(isbook(I))
if(!user.transferItemToLoc(I, src))
return
update_icon()
@@ -107,19 +127,22 @@
I.play_tool_sound(src, 100)
to_chat(user, "<span class='notice'>You pry the shelf out.</span>")
new /obj/item/stack/sheet/mineral/wood(drop_location(), 2)
state = 1
icon_state = "bookempty"
state = BOOKCASE_ANCHORED
update_icon()
else
return ..()
/obj/structure/bookcase/on_attack_hand(mob/living/user, act_intent = user.a_intent, unarmed_attack_flags)
. = ..()
if(. || !istype(user))
/obj/structure/bookcase/on_attack_hand(mob/living/user)
if(!istype(user))
return
if(load_random_books)
create_random_books(books_to_load, src, FALSE, random_category)
load_random_books = FALSE
if(contents.len)
var/obj/item/book/choice = input("Which book would you like to remove from the shelf?") as null|obj in contents
var/obj/item/book/choice = input(user, "Which book would you like to remove from the shelf?") as null|obj in sortNames(contents.Copy())
if(choice)
if(!CHECK_MOBILITY(user, MOBILITY_USE) || !in_range(loc, user))
if(!(user.mobility_flags & MOBILITY_USE) || user.stat != CONSCIOUS || !in_range(loc, user))
return
if(ishuman(user))
if(!user.get_active_held_item())
@@ -128,36 +151,25 @@
choice.forceMove(drop_location())
update_icon()
/obj/structure/bookcase/attack_ghost(mob/dead/observer/user)
. = ..()
if(!length(contents))
to_chat(user, "<span class='warning'>It's empty!</span>")
return
var/obj/item/book/choice = input("Which book would you like to read?") as null|obj in contents
if(choice)
if(!istype(choice)) //spellbook, cult tome, or the one weird bible storage
to_chat(user,"A mysterious force is keeping you from reading that.")
return
choice.attack_ghost(user)
/obj/structure/bookcase/deconstruct(disassembled = TRUE)
new /obj/item/stack/sheet/mineral/wood(loc, 4)
for(var/obj/item/book/B in contents)
B.forceMove(get_turf(src))
qdel(src)
var/atom/Tsec = drop_location()
new /obj/item/stack/sheet/mineral/wood(Tsec, 4)
for(var/obj/item/I in contents)
if(!isbook(I))
continue
I.forceMove(Tsec)
return ..()
/obj/structure/bookcase/update_icon_state()
icon_state = "book-[min(length(contents), 5)]"
/obj/structure/bookcase/manuals/medical
name = "medical manuals bookcase"
/obj/structure/bookcase/manuals/medical/Initialize()
. = ..()
new /obj/item/book/manual/wiki/medical_cloning(src)
update_icon()
if(state == BOOKCASE_UNANCHORED || state == BOOKCASE_ANCHORED)
icon_state = "bookempty"
return
var/amount = contents.len
if(load_random_books)
amount += books_to_load
icon_state = "book-[clamp(amount, 0, 5)]"
/obj/structure/bookcase/manuals/engineering
@@ -198,34 +210,27 @@
var/dat //Actual page content
var/due_date = 0 //Game time in 1/10th seconds
var/author //Who wrote the thing, can be changed by pen or PC. It is not automatically assigned
var/unique = 0 //0 - Normal book, 1 - Should not be treated as normal book, unable to be copied, unable to be modified
var/unique = FALSE //false - Normal book, true - Should not be treated as normal book, unable to be copied, unable to be modified
var/title //The real name of the book.
var/window_size = null // Specific window size for the book, i.e: "1920x1080", Size x Width
/obj/item/book/attack_self(mob/user)
if(is_blind(user))
to_chat(user, "<span class='warning'>As you are trying to read, you suddenly feel very stupid!</span>")
return
if(ismonkey(user))
to_chat(user, "<span class='notice'>You skim through the book but can't comprehend any of it.</span>")
if(!user.can_read(src))
return
if(dat)
show_to(user)
user.visible_message("[user] opens a book titled \"[title]\" and begins reading intently.")
user << browse("<TT><I>Penned by [author].</I></TT> <BR>" + "[dat]", "window=book[window_size != null ? ";size=[window_size]" : ""]")
user.visible_message("<span class='notice'>[user] opens a book titled \"[title]\" and begins reading intently.</span>")
// SEND_SIGNAL(user, COMSIG_ADD_MOOD_EVENT, "book_nerd", /datum/mood_event/book_nerd)
onclose(user, "book")
else
to_chat(user, "<span class='notice'>This book is completely blank!</span>")
/obj/item/book/attack_ghost(mob/dead/observer/O)
. = ..()
show_to(O)
/obj/item/book/proc/show_to(mob/user)
user << browse("<TT><I>Penned by [author].</I></TT> <BR>" + "[dat]", "window=book[window_size != null ? ";size=[window_size]" : ""]")
/obj/item/book/attackby(obj/item/I, mob/user, params)
if(istype(I, /obj/item/pen))
if(is_blind(user))
to_chat(user, "<span class='warning'> As you are trying to write on the book, you suddenly feel very stupid!</span>")
if(user.is_blind())
to_chat(user, "<span class='warning'>As you are trying to write on the book, you suddenly feel very stupid!</span>")
return
if(unique)
to_chat(user, "<span class='warning'>These pages don't seem to take the ink well! Looks like you can't modify it.</span>")
@@ -243,10 +248,10 @@
if(!user.canUseTopic(src, BE_CLOSE, literate))
return
if (length(newtitle) > 20)
to_chat(user, "That title won't fit on the cover!")
to_chat(user, "<span class='warning'>That title won't fit on the cover!</span>")
return
if(!newtitle)
to_chat(user, "That title is invalid.")
to_chat(user, "<span class='warning'>That title is invalid.</span>")
return
else
name = newtitle
@@ -256,7 +261,7 @@
if(!user.canUseTopic(src, BE_CLOSE, literate))
return
if(!content)
to_chat(user, "The content is invalid.")
to_chat(user, "<span class='warning'>The content is invalid.</span>")
return
else
dat += content
@@ -265,7 +270,7 @@
if(!user.canUseTopic(src, BE_CLOSE, literate))
return
if(!newauthor)
to_chat(user, "The name is invalid.")
to_chat(user, "<span class='warning'>The name is invalid.</span>")
return
else
author = newauthor
@@ -275,32 +280,32 @@
else if(istype(I, /obj/item/barcodescanner))
var/obj/item/barcodescanner/scanner = I
if(!scanner.computer)
to_chat(user, "[I]'s screen flashes: 'No associated computer found!'")
to_chat(user, "<span class='alert'>[I]'s screen flashes: 'No associated computer found!'</span>")
else
switch(scanner.mode)
if(0)
scanner.book = src
to_chat(user, "[I]'s screen flashes: 'Book stored in buffer.'")
to_chat(user, "<span class='notice'>[I]'s screen flashes: 'Book stored in buffer.'</span>")
if(1)
scanner.book = src
scanner.computer.buffer_book = name
to_chat(user, "[I]'s screen flashes: 'Book stored in buffer. Book title stored in associated computer buffer.'")
to_chat(user, "<span class='notice'>[I]'s screen flashes: 'Book stored in buffer. Book title stored in associated computer buffer.'</span>")
if(2)
scanner.book = src
for(var/datum/borrowbook/b in scanner.computer.checkouts)
if(b.bookname == name)
scanner.computer.checkouts.Remove(b)
to_chat(user, "[I]'s screen flashes: 'Book stored in buffer. Book has been checked in.'")
to_chat(user, "<span class='notice'>[I]'s screen flashes: 'Book stored in buffer. Book has been checked in.'</span>")
return
to_chat(user, "[I]'s screen flashes: 'Book stored in buffer. No active check-out record found for current title.'")
to_chat(user, "<span class='notice'>[I]'s screen flashes: 'Book stored in buffer. No active check-out record found for current title.'</span>")
if(3)
scanner.book = src
for(var/obj/item/book in scanner.computer.inventory)
if(book == src)
to_chat(user, "[I]'s screen flashes: 'Book stored in buffer. Title already present in inventory, aborting to avoid duplicate entry.'")
to_chat(user, "<span class='alert'>[I]'s screen flashes: 'Book stored in buffer. Title already present in inventory, aborting to avoid duplicate entry.'</span>")
return
scanner.computer.inventory.Add(src)
to_chat(user, "[I]'s screen flashes: 'Book stored in buffer. Title added to general inventory.'")
to_chat(user, "<span class='notice'>[I]'s screen flashes: 'Book stored in buffer. Title added to general inventory.'</span>")
else if(istype(I, /obj/item/kitchen/knife) || I.tool_behaviour == TOOL_WIRECUTTER)
to_chat(user, "<span class='notice'>You begin to carve out [title]...</span>")
@@ -361,3 +366,8 @@
else
to_chat(user, "<font color=red>No associated computer found. Only local scans will function properly.</font>")
to_chat(user, "\n")
#undef BOOKCASE_UNANCHORED
#undef BOOKCASE_ANCHORED
#undef BOOKCASE_FINISHED

View File

@@ -25,12 +25,13 @@
var/title
var/category = "Any"
var/author
var/SQLquery
clockwork = TRUE //it'd look weird
var/search_page = 0
COOLDOWN_DECLARE(library_visitor_topic_cooldown)
clockwork = TRUE
/obj/machinery/computer/libraryconsole/ui_interact(mob/user)
. = ..()
var/dat = "" // <META HTTP-EQUIV='Refresh' CONTENT='10'>
var/list/dat = list() // <META HTTP-EQUIV='Refresh' CONTENT='10'>
switch(screenstate)
if(0)
dat += "<h2>Search Settings</h2><br>"
@@ -43,13 +44,43 @@
dat += "<font color=red><b>ERROR</b>: Unable to contact External Archive. Please contact your system administrator for assistance.</font><BR>"
else if(QDELETED(user))
return
else if(!SQLquery)
dat += "<font color=red><b>ERROR</b>: Malformed search request. Please contact your system administrator for assistance.</font><BR>"
else
dat += "<table>"
dat += "<tr><td>AUTHOR</td><td>TITLE</td><td>CATEGORY</td><td>SS<sup>13</sup>BN</td></tr>"
var/datum/DBQuery/query_library_list_books = SSdbcore.NewQuery(SQLquery)
var/bookcount = 0
var/booksperpage = 20
var/datum/db_query/query_library_count_books = SSdbcore.NewQuery({"
SELECT COUNT(id) FROM [format_table_name("library")]
WHERE isnull(deleted)
AND author LIKE CONCAT('%',:author,'%')
AND title LIKE CONCAT('%',:title,'%')
AND (:category = 'Any' OR category = :category)
"}, list("author" = author, "title" = title, "category" = category))
if(!query_library_count_books.warn_execute())
qdel(query_library_count_books)
return
if(query_library_count_books.NextRow())
bookcount = text2num(query_library_count_books.item[1])
qdel(query_library_count_books)
if(bookcount > booksperpage)
dat += "<b>Page: </b>"
var/pagecount = 1
var/list/pagelist = list()
while(bookcount > 0)
pagelist += "<a href='?src=[REF(src)];bookpagecount=[pagecount - 1]'>[pagecount == search_page + 1 ? "<b>\[[pagecount]\]</b>" : "\[[pagecount]\]"]</a>"
bookcount -= booksperpage
pagecount++
dat += pagelist.Join(" | ")
search_page = text2num(search_page)
var/datum/db_query/query_library_list_books = SSdbcore.NewQuery({"
SELECT author, title, category, id
FROM [format_table_name("library")]
WHERE isnull(deleted)
AND author LIKE CONCAT('%',:author,'%')
AND title LIKE CONCAT('%',:title,'%')
AND (:category = 'Any' OR category = :category)
LIMIT :skip, :take
"}, list("author" = author, "title" = title, "category" = category, "skip" = booksperpage * search_page, "take" = booksperpage))
if(!query_library_list_books.Execute())
dat += "<font color=red><b>ERROR</b>: Unable to retrieve book listings. Please contact your system administrator for assistance.</font><BR>"
else
@@ -65,12 +96,15 @@
dat += "</table><BR>"
dat += "<A href='?src=[REF(src)];back=1'>\[Go Back\]</A><BR>"
var/datum/browser/popup = new(user, "publiclibrary", name, 600, 400)
popup.set_content(dat)
popup.set_content(jointext(dat, ""))
popup.open()
/obj/machinery/computer/libraryconsole/Topic(href, href_list)
if(!COOLDOWN_FINISHED(src, library_visitor_topic_cooldown))
return
COOLDOWN_START(src, library_visitor_topic_cooldown, 1 SECONDS)
. = ..()
if(..())
if(.)
usr << browse(null, "window=publiclibrary")
onclose(usr, "publiclibrary")
return
@@ -81,29 +115,24 @@
title = sanitize(newtitle)
else
title = null
title = sanitizeSQL(title)
if(href_list["setcategory"])
var/newcategory = input("Choose a category to search for:") in list("Any", "Fiction", "Non-Fiction", "Adult", "Reference", "Religion")
if(newcategory)
category = sanitize(newcategory)
else
category = "Any"
category = sanitizeSQL(category)
if(href_list["setauthor"])
var/newauthor = input("Enter an author to search for:") as text|null
if(newauthor)
author = sanitize(newauthor)
else
author = null
author = sanitizeSQL(author)
if(href_list["search"])
SQLquery = "SELECT author, title, category, id FROM [format_table_name("library")] WHERE isnull(deleted) AND "
if(category == "Any")
SQLquery += "author LIKE '%[author]%' AND title LIKE '%[title]%'"
else
SQLquery += "author LIKE '%[author]%' AND title LIKE '%[title]%' AND category='[category]'"
screenstate = 1
if(href_list["bookpagecount"])
search_page = text2num(href_list["bookpagecount"])
if(href_list["back"])
screenstate = 0
@@ -120,44 +149,12 @@
var/getdate
var/duedate
/*
* Cachedbook datum
*/
/datum/cachedbook // Datum used to cache the SQL DB books locally in order to achieve a performance gain.
var/id
var/title
var/author
var/category
GLOBAL_LIST(cachedbooks) // List of our cached book datums
/proc/load_library_db_to_cache()
if(GLOB.cachedbooks)
return
if(!SSdbcore.Connect())
return
GLOB.cachedbooks = list()
var/datum/DBQuery/query_library_cache = SSdbcore.NewQuery("SELECT id, author, title, category FROM [format_table_name("library")] WHERE isnull(deleted)")
if(!query_library_cache.Execute())
qdel(query_library_cache)
return
while(query_library_cache.NextRow())
var/datum/cachedbook/newbook = new()
newbook.id = query_library_cache.item[1]
newbook.author = query_library_cache.item[2]
newbook.title = query_library_cache.item[3]
newbook.category = query_library_cache.item[4]
GLOB.cachedbooks += newbook
qdel(query_library_cache)
#define PRINTER_COOLDOWN 60
/*
* Library Computer
* After 860 days, it's finally a buildable computer.
* After 860 days, it's finally a buildable computer.*
* * i cannot change maps because you are a buch of fucks who ignore map changes
*/
// TODO: Make this an actual /obj/machinery/computer that can be crafted from circuit boards and such
// It is August 22nd, 2012... This TODO has already been here for months.. I wonder how long it'll last before someone does something about it.
@@ -165,11 +162,15 @@ GLOBAL_LIST(cachedbooks) // List of our cached book datums
/obj/machinery/computer/libraryconsole/bookmanagement
name = "book inventory management console"
desc = "Librarian's command station."
screenstate = 0 // 0 - Main Menu, 1 - Inventory, 2 - Checked Out, 3 - Check Out a Book
verb_say = "beeps"
verb_ask = "beeps"
verb_exclaim = "beeps"
pass_flags = PASSTABLE
circuit = /obj/item/circuitboard/computer/libraryconsole
// var/screenstate = 0 // 0 - Main Menu, 1 - Inventory, 2 - Checked Out, 3 - Check Out a Book
var/arcanecheckout = 0
var/buffer_book
var/buffer_mob
@@ -178,25 +179,9 @@ GLOBAL_LIST(cachedbooks) // List of our cached book datums
var/list/inventory = list()
var/checkoutperiod = 5 // In minutes
var/obj/machinery/libraryscanner/scanner // Book scanner that will be used when uploading books to the Archive
var/list/libcomp_menu
var/page = 1 //current page of the external archives
var/cooldown = 0
/obj/machinery/computer/libraryconsole/bookmanagement/proc/build_library_menu()
if(libcomp_menu)
return
load_library_db_to_cache()
if(!GLOB.cachedbooks)
return
libcomp_menu = list("")
for(var/i in 1 to GLOB.cachedbooks.len)
var/datum/cachedbook/C = GLOB.cachedbooks[i]
var/page = round(i/250)+1
if (libcomp_menu.len < page)
libcomp_menu.len = page
libcomp_menu[page] = ""
libcomp_menu[page] += "<tr><td>[C.author]</td><td>[C.title]</td><td>[C.category]</td><td><A href='?src=[REF(src)];targetid=[C.id]'>\[Order\]</A></td></tr>\n"
var/printer_cooldown = 0
COOLDOWN_DECLARE(library_console_topic_cooldown)
/obj/machinery/computer/libraryconsole/bookmanagement/Initialize()
. = ..()
@@ -258,17 +243,37 @@ GLOBAL_LIST(cachedbooks) // List of our cached book datums
dat += "<A href='?src=[REF(src)];switchscreen=0'>(Return to main menu)</A><BR>"
if(4)
dat += "<h3>External Archive</h3>"
build_library_menu()
if(!GLOB.cachedbooks)
if(!SSdbcore.Connect())
dat += "<font color=red><b>ERROR</b>: Unable to contact External Archive. Please contact your system administrator for assistance.</font>"
else
var/booksperpage = 50
var/pagecount
var/datum/db_query/query_library_count_books = SSdbcore.NewQuery("SELECT COUNT(id) FROM [format_table_name("library")] WHERE isnull(deleted)")
if(!query_library_count_books.Execute())
qdel(query_library_count_books)
return
if(query_library_count_books.NextRow())
pagecount = CEILING(text2num(query_library_count_books.item[1]) / booksperpage, 1)
qdel(query_library_count_books)
var/list/booklist = list()
var/datum/db_query/query_library_get_books = SSdbcore.NewQuery({"
SELECT id, author, title, category
FROM [format_table_name("library")]
WHERE isnull(deleted)
LIMIT :skip, :take
"}, list("skip" = booksperpage * (page - 1), "take" = booksperpage))
if(!query_library_get_books.Execute())
qdel(query_library_get_books)
return
while(query_library_get_books.NextRow())
booklist += "<tr><td>[query_library_get_books.item[2]]</td><td>[query_library_get_books.item[3]]</td><td>[query_library_get_books.item[4]]</td><td><A href='?src=[REF(src)];targetid=[query_library_get_books.item[1]]'>\[Order\]</A></td></tr>\n"
dat += "<A href='?src=[REF(src)];orderbyid=1'>(Order book by SS<sup>13</sup>BN)</A><BR><BR>"
dat += "<table>"
dat += "<tr><td>AUTHOR</td><td>TITLE</td><td>CATEGORY</td><td></td></tr>"
dat += libcomp_menu[clamp(page,1,libcomp_menu.len)]
dat += "<tr><td><A href='?src=[REF(src)];page=[(max(1,page-1))]'>&lt;&lt;&lt;&lt;</A></td> <td></td> <td></td> <td><span style='text-align:right'><A href='?src=[REF(src)];page=[(min(libcomp_menu.len,page+1))]'>&gt;&gt;&gt;&gt;</A></span></td></tr>"
dat += jointext(booklist, "")
dat += "<tr><td><A href='?src=[REF(src)];page=[max(1,page-1)]'>&lt;&lt;&lt;&lt;</A></td> <td></td> <td></td> <td><span style='text-align:right'><A href='?src=[REF(src)];page=[min(pagecount,page+1)]'>&gt;&gt;&gt;&gt;</A></span></td></tr>"
dat += "</table>"
qdel(query_library_get_books)
dat += "<BR><A href='?src=[REF(src)];switchscreen=0'>(Return to main menu)</A><BR>"
if(5)
dat += "<H3>Upload a New Title</H3>"
@@ -321,33 +326,27 @@ GLOBAL_LIST(cachedbooks) // List of our cached book datums
return null
/obj/machinery/computer/libraryconsole/bookmanagement/proc/print_forbidden_lore(mob/user)
var/spook = pick("blood", "brass")
var/turf/T = get_turf(src)
if(spook == "blood")
new /obj/item/melee/cultblade/dagger(T)
else
new /obj/item/clockwork/slab(T)
to_chat(user, "<span class='warning'>Your sanity barely endures the seconds spent in the vault's browsing window. The only thing to remind you of this when you stop browsing is a [spook == "blood" ? "sinister dagger" : "strange metal tablet"] sitting on the desk. You don't even remember where it came from...</span>")
user.visible_message("[user] stares at the blank screen for a few moments, [user.p_their()] expression frozen in fear. When [user.p_they()] finally awaken[user.p_s()] from it, [user.p_they()] look[user.p_s()] a lot older.", 2)
new /obj/item/melee/cultblade/dagger(get_turf(src))
to_chat(user, "<span class='warning'>Your sanity barely endures the seconds spent in the vault's browsing window. The only thing to remind you of this when you stop browsing is a sinister dagger sitting on the desk. You don't even remember where it came from...</span>")
user.visible_message("<span class='warning'>[user] stares at the blank screen for a few moments, [user.p_their()] expression frozen in fear. When [user.p_they()] finally awaken[user.p_s()] from it, [user.p_they()] look[user.p_s()] a lot older.</span>", 2)
/obj/machinery/computer/libraryconsole/bookmanagement/attackby(obj/item/W, mob/user, params)
if(istype(W, /obj/item/barcodescanner))
var/obj/item/barcodescanner/scanner = W
scanner.computer = src
to_chat(user, "[scanner]'s associated machine has been set to [src].")
audible_message("[src] lets out a low, short blip.")
to_chat(user, "<span class='notice'>[scanner]'s associated machine has been set to [src].</span>")
audible_message("<span class='hear'>[src] lets out a low, short blip.</span>")
else
return ..()
/obj/machinery/computer/libraryconsole/bookmanagement/emag_act(mob/user)
. = ..()
if(!density || obj_flags & EMAGGED)
return
obj_flags |= EMAGGED
return TRUE
if(density && !(obj_flags & EMAGGED))
obj_flags |= EMAGGED
/obj/machinery/computer/libraryconsole/bookmanagement/Topic(href, href_list)
if(!COOLDOWN_FINISHED(src, library_console_topic_cooldown))
return
COOLDOWN_START(src, library_console_topic_cooldown, 1 SECONDS)
if(..())
usr << browse(null, "window=library")
onclose(usr, "library")
@@ -385,7 +384,7 @@ GLOBAL_LIST(cachedbooks) // List of our cached book datums
if(checkoutperiod < 1)
checkoutperiod = 1
if(href_list["editbook"])
buffer_book = stripped_input(usr, "Enter the book's title:")
buffer_book = stripped_input(usr, "Enter the book's title:", max_length = 45)
if(href_list["editmob"])
buffer_mob = stripped_input(usr, "Enter the recipient's name:", max_length = MAX_NAME_LEN)
if(href_list["checkout"])
@@ -404,7 +403,7 @@ GLOBAL_LIST(cachedbooks) // List of our cached book datums
if(b && istype(b))
inventory.Remove(b)
if(href_list["setauthor"])
var/newauthor = stripped_input(usr, "Enter the author's name: ")
var/newauthor = stripped_input(usr, "Enter the author's name: ", max_length = 45)
if(newauthor)
scanner.cache.author = newauthor
if(href_list["setcategory"])
@@ -419,14 +418,11 @@ GLOBAL_LIST(cachedbooks) // List of our cached book datums
if (!SSdbcore.Connect())
alert("Connection to Archive has been severed. Aborting.")
else
var/sqltitle = sanitizeSQL(scanner.cache.name)
var/sqlauthor = sanitizeSQL(scanner.cache.author)
var/sqlcontent = sanitizeSQL(scanner.cache.dat)
var/sqlcategory = sanitizeSQL(upload_category)
var/sqlckey = sanitizeSQL(usr.ckey)
var/msg = "[key_name(usr)] has uploaded the book titled [scanner.cache.name], [length(scanner.cache.dat)] signs"
var/datum/DBQuery/query_library_upload = SSdbcore.NewQuery("INSERT INTO [format_table_name("library")] (author, title, content, category, ckey, datetime, round_id_created) VALUES ('[sqlauthor]', '[sqltitle]', '[sqlcontent]', '[sqlcategory]', '[sqlckey]', Now(), '[GLOB.round_id]')")
var/datum/db_query/query_library_upload = SSdbcore.NewQuery({"
INSERT INTO [format_table_name("library")] (author, title, content, category, ckey, datetime, round_id_created)
VALUES (:author, :title, :content, :category, :ckey, Now(), :round_id)
"}, list("title" = scanner.cache.name, "author" = scanner.cache.author, "content" = scanner.cache.dat, "category" = upload_category, "ckey" = usr.ckey, "round_id" = GLOB.round_id))
if(!query_library_upload.Execute())
qdel(query_library_upload)
alert("Database error encountered uploading to Archive")
@@ -448,7 +444,7 @@ GLOBAL_LIST(cachedbooks) // List of our cached book datums
GLOB.news_network.SubmitArticle(scanner.cache.dat, "[scanner.cache.name]", "Nanotrasen Book Club", null)
alert("Upload complete. Your uploaded title is now available on station newscasters.")
if(href_list["orderbyid"])
if(cooldown > world.time)
if(printer_cooldown > world.time)
say("Printer unavailable. Please allow a short time before attempting to print.")
else
var/orderid = input("Enter your order:") as num|null
@@ -457,14 +453,17 @@ GLOBAL_LIST(cachedbooks) // List of our cached book datums
href_list["targetid"] = num2text(orderid)
if(href_list["targetid"])
var/sqlid = sanitizeSQL(href_list["targetid"])
var/id = href_list["targetid"]
if (!SSdbcore.Connect())
alert("Connection to Archive has been severed. Aborting.")
if(cooldown > world.time)
if(printer_cooldown > world.time)
say("Printer unavailable. Please allow a short time before attempting to print.")
else
cooldown = world.time + PRINTER_COOLDOWN
var/datum/DBQuery/query_library_print = SSdbcore.NewQuery("SELECT * FROM [format_table_name("library")] WHERE id=[sqlid] AND isnull(deleted)")
printer_cooldown = world.time + PRINTER_COOLDOWN
var/datum/db_query/query_library_print = SSdbcore.NewQuery(
"SELECT * FROM [format_table_name("library")] WHERE id=:id AND isnull(deleted)",
list("id" = id)
)
if(!query_library_print.Execute())
qdel(query_library_print)
say("PRINTER ERROR! Failed to print document (0x0000000F)")
@@ -480,24 +479,24 @@ GLOBAL_LIST(cachedbooks) // List of our cached book datums
B.author = author
B.dat = content
B.icon_state = "book[rand(1,8)]"
visible_message("[src]'s printer hums as it produces a completely bound book. How did it do that?")
visible_message("<span class='notice'>[src]'s printer hums as it produces a completely bound book. How did it do that?</span>")
break
qdel(query_library_print)
if(href_list["printbible"])
if(cooldown < world.time)
if(printer_cooldown < world.time)
var/obj/item/storage/book/bible/B = new /obj/item/storage/book/bible(src.loc)
if(GLOB.bible_icon_state && GLOB.bible_item_state)
B.icon_state = GLOB.bible_icon_state
B.item_state = GLOB.bible_item_state
B.name = GLOB.bible_name
B.deity_name = GLOB.deity
cooldown = world.time + PRINTER_COOLDOWN
printer_cooldown = world.time + PRINTER_COOLDOWN
else
say("Printer currently unavailable, please wait a moment.")
if(href_list["printposter"])
if(cooldown < world.time)
if(printer_cooldown < world.time)
new /obj/item/poster/random_official(src.loc)
cooldown = world.time + PRINTER_COOLDOWN
printer_cooldown = world.time + PRINTER_COOLDOWN
else
say("Printer currently unavailable, please wait a moment.")
add_fingerprint(usr)
@@ -521,7 +520,10 @@ GLOBAL_LIST(cachedbooks) // List of our cached book datums
else
return ..()
/obj/machinery/libraryscanner/on_attack_hand(mob/user, act_intent = user.a_intent, unarmed_attack_flags)
/obj/machinery/libraryscanner/attack_hand(mob/user)
. = ..()
if(.)
return
usr.set_machine(src)
var/dat = "" // <META HTTP-EQUIV='Refresh' CONTENT='10'>
if(cache)
@@ -584,14 +586,14 @@ GLOBAL_LIST(cachedbooks) // List of our cached book datums
return
if(!user.transferItemToLoc(P, src))
return
user.visible_message("[user] loads some paper into [src].", "You load some paper into [src].")
audible_message("[src] begins to hum as it warms up its printing drums.")
user.visible_message("<span class='notice'>[user] loads some paper into [src].</span>", "<span class='notice'>You load some paper into [src].</span>")
audible_message("<span class='hear'>[src] begins to hum as it warms up its printing drums.</span>")
busy = TRUE
sleep(rand(200,400))
busy = FALSE
if(P)
if(!stat)
visible_message("[src] whirs as it prints and binds a new book.")
visible_message("<span class='notice'>[src] whirs as it prints and binds a new book.</span>")
var/obj/item/book/B = new(src.loc)
B.dat = P.info
B.name = "Print Job #" + "[rand(100, 999)]"

View File

@@ -10,78 +10,83 @@
/obj/item/book/random
icon_state = "random_book"
var/amount = 1
var/category = null
/// The category of books to pick from when creating this book.
var/random_category = null
/// If this book has already been 'generated' yet.
var/random_loaded = FALSE
/obj/item/book/random/Initialize()
..()
create_random_books(amount, src.loc, TRUE, category)
return INITIALIZE_HINT_QDEL
/obj/item/book/random/Initialize(mapload)
. = ..()
icon_state = "book[rand(1,8)]"
/obj/item/book/random/triple
amount = 3
/obj/item/book/random/attack_self()
if(!random_loaded)
create_random_books(1, loc, TRUE, random_category, src)
random_loaded = TRUE
return ..()
/obj/structure/bookcase/random
var/category = null
var/book_count = 2
load_random_books = TRUE
books_to_load = 2
icon_state = "random_bookcase"
anchored = TRUE
state = 2
/obj/structure/bookcase/random/Initialize(mapload)
. = ..()
if(!book_count || !isnum(book_count))
update_icon()
return
book_count += pick(-1,-1,0,1,1)
create_random_books(book_count, src, FALSE, category)
if(books_to_load && isnum(books_to_load))
books_to_load += pick(-1,-1,0,1,1)
update_icon()
/proc/create_random_books(amount = 2, location, fail_loud = FALSE, category = null)
/proc/create_random_books(amount, location, fail_loud = FALSE, category = null, obj/item/book/existing_book)
. = list()
if(!isnum(amount) || amount<1)
return
if (!SSdbcore.Connect())
if(fail_loud || prob(5))
var/obj/item/paper/P = new(location)
P.info = "There once was a book from Nantucket<br>But the database failed us, so f*$! it.<br>I tried to be good to you<br>Now this is an I.O.U<br>If you're feeling entitled, well, stuff it!<br><br><font color='gray'>~</font>"
P.update_icon()
if(existing_book && (fail_loud || prob(5)))
existing_book.author = "???"
existing_book.title = "Strange book"
existing_book.name = "Strange book"
existing_book.dat = "There once was a book from Nantucket<br>But the database failed us, so f*$! it.<br>I tried to be good to you<br>Now this is an I.O.U<br>If you're feeling entitled, well, stuff it!<br><br><font color='gray'>~</font>"
return
if(prob(25))
category = null
var/c = category? " AND category='[sanitizeSQL(category)]'" :""
var/datum/DBQuery/query_get_random_books = SSdbcore.NewQuery("SELECT * FROM [format_table_name("library")] WHERE isnull(deleted)[c] GROUP BY title ORDER BY rand() LIMIT [amount];") // isdeleted copyright (c) not me
var/datum/db_query/query_get_random_books = SSdbcore.NewQuery({"
SELECT author, title, content
FROM [format_table_name("library")]
WHERE isnull(deleted) AND (:category IS NULL OR category = :category)
ORDER BY rand() LIMIT :limit
"}, list("category" = category, "limit" = amount))
if(query_get_random_books.Execute())
while(query_get_random_books.NextRow())
var/obj/item/book/B = new(location)
. += B
B.author = query_get_random_books.item[2]
B.title = query_get_random_books.item[3]
B.dat = query_get_random_books.item[4]
var/obj/item/book/B
B = existing_book ? existing_book : new(location)
B.author = query_get_random_books.item[1]
B.title = query_get_random_books.item[2]
B.dat = query_get_random_books.item[3]
B.name = "Book: [B.title]"
B.icon_state= "book[rand(1,8)]"
if(!existing_book)
B.icon_state= "book[rand(1,8)]"
qdel(query_get_random_books)
/obj/structure/bookcase/random/fiction
name = "bookcase (Fiction)"
category = "Fiction"
random_category = "Fiction"
/obj/structure/bookcase/random/nonfiction
name = "bookcase (Non-Fiction)"
category = "Non-fiction"
random_category = "Non-fiction"
/obj/structure/bookcase/random/religion
name = "bookcase (Religion)"
category = "Religion"
random_category = "Religion"
/obj/structure/bookcase/random/adult
name = "bookcase (Adult)"
category = "Adult"
random_category = "Adult"
/obj/structure/bookcase/random/reference
name = "bookcase (Reference)"
category = "Reference"
random_category = "Reference"
var/ref_book_prob = 20
/obj/structure/bookcase/random/reference/Initialize(mapload)
. = ..()
while(book_count > 0 && prob(ref_book_prob))
book_count--
while(books_to_load > 0 && prob(ref_book_prob))
books_to_load--
new /obj/item/book/manual/random(src)

View File

@@ -54,24 +54,7 @@
output += "<p>[LINKIFY_READY("Observe", PLAYER_READY_TO_OBSERVE)]</p>"
if(!IsGuestKey(src.key))
if (SSdbcore.Connect())
var/isadmin = 0
if(src.client && src.client.holder)
isadmin = 1
var/datum/DBQuery/query_get_new_polls = SSdbcore.NewQuery("SELECT id FROM [format_table_name("poll_question")] WHERE [(isadmin ? "" : "adminonly = false AND")] Now() BETWEEN starttime AND endtime AND id NOT IN (SELECT pollid FROM [format_table_name("poll_vote")] WHERE ckey = \"[sanitizeSQL(ckey)]\") AND id NOT IN (SELECT pollid FROM [format_table_name("poll_textreply")] WHERE ckey = \"[sanitizeSQL(ckey)]\")")
var/rs = REF(src)
if(query_get_new_polls.Execute())
var/newpoll = 0
if(query_get_new_polls.NextRow())
newpoll = 1
if(newpoll)
output += "<p><b><a href='byond://?src=[rs];showpoll=1'>Show Player Polls</A> (NEW!)</b></p>"
else
output += "<p><a href='byond://?src=[rs];showpoll=1'>Show Player Polls</A></p>"
qdel(query_get_new_polls)
if(QDELETED(src))
return
output += playerpolls()
output += "</center>"
@@ -81,6 +64,41 @@
popup.set_content(output)
popup.open(FALSE)
/mob/dead/new_player/proc/playerpolls()
var/output = "" //hey tg why is this a list?
if (SSdbcore.Connect())
var/isadmin = FALSE
if(client?.holder)
isadmin = TRUE
var/datum/db_query/query_get_new_polls = SSdbcore.NewQuery({"
SELECT id FROM [format_table_name("poll_question")]
WHERE (adminonly = 0 OR :isadmin = 1)
AND Now() BETWEEN starttime AND endtime
AND deleted = 0
AND id NOT IN (
SELECT pollid FROM [format_table_name("poll_vote")]
WHERE ckey = :ckey
AND deleted = 0
)
AND id NOT IN (
SELECT pollid FROM [format_table_name("poll_textreply")]
WHERE ckey = :ckey
AND deleted = 0
)
"}, list("isadmin" = isadmin, "ckey" = ckey))
var/rs = REF(src)
if(!query_get_new_polls.Execute())
qdel(query_get_new_polls)
return
if(query_get_new_polls.NextRow())
output += "<p><b><a href='byond://?src=[rs];showpoll=1'>Show Player Polls</A> (NEW!)</b></p>"
else
output += "<p><a href='byond://?src=[rs];showpoll=1'>Show Player Polls</A></p>"
qdel(query_get_new_polls)
if(QDELETED(src))
return
return output
/mob/dead/new_player/proc/age_gate()
var/list/dat = list("<center>")
dat += "Enter your date of birth here, to confirm that you are over 18.<BR>"
@@ -123,71 +141,76 @@
if(!client.set_db_player_flags())
message_admins("Blocked [src] from new player panel because age gate could not access player database flags.")
return FALSE
else
var/dbflags = client.prefs.db_flags
if(dbflags & DB_FLAG_AGE_CONFIRMATION_INCOMPLETE) //they have not completed age gate
var/age_verification = age_gate()
if(age_verification != 1)
client.add_system_note("Automated-Age-Gate", "Failed automatic age gate process.")
//ban them and kick them
//parameters used by sql line, easier to read:
var/bantype_str = "ADMIN_PERMABAN"
var/reason = "SYSTEM BAN - Inputted date during join verification was under 18 years of age. Contact administration on discord for verification."
var/duration = -1
var/sql_ckey = sanitizeSQL(client.ckey)
var/computerid = client.computer_id
if(!computerid)
computerid = "0"
var/sql_computerid = sanitizeSQL(computerid)
var/ip = client.address
if(!ip)
ip = "0.0.0.0"
var/sql_ip = sanitizeSQL(ip)
if(!(client.prefs.db_flags & DB_FLAG_AGE_CONFIRMATION_INCOMPLETE)) //completed? Skip
return TRUE
//parameter not used as there's no job but i want to fill out all parameters for the insert line
var/sql_job
var/age_verification = age_gate()
//ban them and kick them
if(age_verification != 1)
// this isn't code, this is paragraphs.
var/player_ckey = ckey(client.ckey)
// record all admins and non-admins online at the time
var/list/clients_online = GLOB.clients.Copy()
var/list/admins_online = GLOB.admins.Copy() //list() // remove the GLOB.admins.Copy() and the comments if you want the pure admins_online check
// for(var/client/C in clients_online)
// if(C.holder) //deadmins aren't included since they wouldn't show up on adminwho
// admins_online += C
var/who = clients_online.Join(", ")
var/adminwho = admins_online.Join(", ")
// these are typically the banning admin's, but it's the system so we leave them null, but they're still here for the sake of a full set of values
var/sql_a_ckey
var/sql_a_computerid
var/sql_a_ip
var/datum/db_query/query_add_ban = SSdbcore.NewQuery({"
INSERT INTO [format_table_name("ban")]
(bantime, server_ip, server_port , round_id, bantype, reason, job, duration, expiration_time, ckey, computerid, ip, a_ckey, a_computerid, a_ip, who, adminwho)
VALUES (Now(), INET_ATON(:server_ip), :server_port, :round_id, :bantype_str, :reason, :role, :duration, Now() + INTERVAL :duration MINUTE, :ckey, :computerid, INET_ATON(:ip), :a_ckey, :a_computerid, INET_ATON(:a_ip), :who, :adminwho)"},
list(
// Server info
"server_ip" = world.internet_address || 0,
"server_port" = world.port,
"round_id" = GLOB.round_id,
// Client ban info
"bantype_str" = "ADMIN_PERMABAN",
"reason" = "SYSTEM BAN - Inputted date during join verification was under 18 years of age. Contact administration on discord for verification.",
"role" = null,
"duration" = -1,
"ckey" = player_ckey,
"ip" = client.address || null,
"computerid" = client.computer_id || null,
// Admin banning info
"a_ckey" = "SYSTEM (Automated-Age-Gate)", // the server
"a_ip" = null, //key_name
"a_computerid" = "0",
"who" = who,
"adminwho" = adminwho
))
// record all admins and non-admins online at the time
var/who
for(var/client/C in GLOB.clients)
if(!who)
who = "[C]"
else
who += ", [C]"
client.add_system_note("Automated-Age-Gate", "Failed automatic age gate process.")
if(!query_add_ban.Execute())
// this is the part where you should panic.
qdel(query_add_ban)
message_admins("WARNING! Failed to ban [ckey] for failing the automatic age gate.")
send2tgs_adminless_only("WARNING! Failed to ban [ckey] for failing the automatic age gate.")
qdel(client)
return FALSE
qdel(query_add_ban)
var/adminwho
for(var/client/C in GLOB.admins)
if(!adminwho)
adminwho = "[C]"
else
adminwho += ", [C]"
create_message("note", player_ckey, "SYSTEM (Automated-Age-Gate)", "SYSTEM BAN - Inputted date during join verification was under 18 years of age. Contact administration on discord for verification.", null, null, 0, 0, null, 0, "high")
var/sql = "INSERT INTO [format_table_name("ban")] (`bantime`,`server_ip`,`server_port`,`round_id`,`bantype`,`reason`,`job`,`duration`,`expiration_time`,`ckey`,`computerid`,`ip`,`a_ckey`,`a_computerid`,`a_ip`,`who`,`adminwho`) VALUES (Now(), INET_ATON(IF('[world.internet_address]' LIKE '', '0', '[world.internet_address]')), '[world.port]', '[GLOB.round_id]', '[bantype_str]', '[reason]', '[sql_job]', [(duration)?"[duration]":"0"], Now() + INTERVAL [(duration>0) ? duration : 0] MINUTE, '[sql_ckey]', '[sql_computerid]', INET_ATON('[sql_ip]'), '[sql_a_ckey]', '[sql_a_computerid]', INET_ATON('[sql_a_ip]'), '[who]', '[adminwho]')"
var/datum/DBQuery/query_add_ban = SSdbcore.NewQuery(sql)
qdel(query_add_ban)
// announce this
message_admins("[ckey] has been banned for failing the automatic age gate.")
send2tgs_adminless_only("[ckey] has been banned for failing the automatic age gate.")
// announce this
message_admins("[html_encode(client.ckey)] has been banned for failing the automatic age gate.")
send2irc("[html_encode(client.ckey)] has been banned for failing the automatic age gate.")
// removing the client disconnects them
qdel(client)
// removing the client disconnects them
qdel(client)
return FALSE
//they claim to be of age, so allow them to continue and update their flags
client.update_flag_db(DB_FLAG_AGE_CONFIRMATION_COMPLETE, TRUE)
client.update_flag_db(DB_FLAG_AGE_CONFIRMATION_INCOMPLETE, FALSE)
//log this
message_admins("[ckey] has joined through the automated age gate process.")
return FALSE
else
//they claim to be of age, so allow them to continue and update their flags
client.update_flag_db(DB_FLAG_AGE_CONFIRMATION_COMPLETE, TRUE)
client.update_flag_db(DB_FLAG_AGE_CONFIRMATION_INCOMPLETE, FALSE)
//log this
message_admins("[ckey] has joined through the automated age gate process.")
return TRUE
return TRUE
/mob/dead/new_player/Topic(href, href_list[])

View File

@@ -4,9 +4,13 @@
/mob/dead/new_player/proc/handle_player_polling()
if(!SSdbcore.IsConnected())
to_chat(usr, "<span class='danger'>Failed to establish database connection.</span>")
to_chat(usr, "<span class='danger'>Failed to establish database connection.</span>", confidential = TRUE)
return
var/datum/DBQuery/query_poll_get = SSdbcore.NewQuery("SELECT id, question FROM [format_table_name("poll_question")] WHERE Now() BETWEEN starttime AND endtime [(client.holder ? "" : "AND adminonly = false")]")
var/datum/db_query/query_poll_get = SSdbcore.NewQuery({"
SELECT id, question
FROM [format_table_name("poll_question")]
WHERE Now() BETWEEN starttime AND endtime [(client.holder ? "" : "AND adminonly = false")]
"})
if(!query_poll_get.warn_execute())
qdel(query_poll_get)
return
@@ -27,9 +31,15 @@
if(!pollid)
return
if (!SSdbcore.Connect())
to_chat(usr, "<span class='danger'>Failed to establish database connection.</span>")
to_chat(usr, "<span class='danger'>Failed to establish database connection.</span>", confidential = TRUE)
return
var/datum/DBQuery/query_poll_get_details = SSdbcore.NewQuery("SELECT starttime, endtime, question, polltype, multiplechoiceoptions FROM [format_table_name("poll_question")] WHERE id = [pollid]")
var/datum/db_query/query_poll_get_details = SSdbcore.NewQuery({"
SELECT starttime, endtime, question, polltype, multiplechoiceoptions
FROM [format_table_name("poll_question")]
WHERE id = :id
"}, list(
"id" = pollid
))
if(!query_poll_get_details.warn_execute())
qdel(query_poll_get_details)
return
@@ -47,7 +57,14 @@
qdel(query_poll_get_details)
switch(polltype)
if(POLLTYPE_OPTION)
var/datum/DBQuery/query_option_get_votes = SSdbcore.NewQuery("SELECT optionid FROM [format_table_name("poll_vote")] WHERE pollid = [pollid] AND ckey = '[ckey]'")
var/datum/db_query/query_option_get_votes = SSdbcore.NewQuery({"
SELECT optionid
FROM [format_table_name("poll_vote")]
WHERE pollid = :id AND ckey = :ckey
"}, list(
"id" = pollid,
"ckey" = ckey
))
if(!query_option_get_votes.warn_execute())
qdel(query_option_get_votes)
return
@@ -56,7 +73,13 @@
votedoptionid = text2num(query_option_get_votes.item[1])
qdel(query_option_get_votes)
var/list/datum/polloption/options = list()
var/datum/DBQuery/query_option_options = SSdbcore.NewQuery("SELECT id, text FROM [format_table_name("poll_option")] WHERE pollid = [pollid]")
var/datum/db_query/query_option_options = SSdbcore.NewQuery({"
SELECT id, text
FROM [format_table_name("poll_option")]
WHERE pollid = :id
"}, list(
"id" = pollid
))
if(!query_option_options.warn_execute())
qdel(query_option_options)
return
@@ -92,7 +115,14 @@
src << browse(null ,"window=playerpolllist")
src << browse(output,"window=playerpoll;size=500x250")
if(POLLTYPE_TEXT)
var/datum/DBQuery/query_text_get_votes = SSdbcore.NewQuery("SELECT replytext FROM [format_table_name("poll_textreply")] WHERE pollid = [pollid] AND ckey = '[ckey]'")
var/datum/db_query/query_text_get_votes = SSdbcore.NewQuery({"
SELECT replytext
FROM [format_table_name("poll_textreply")]
WHERE pollid = :id AND ckey = :ckey
"}, list(
"id" = pollid,
"ckey" = ckey
))
if(!query_text_get_votes.warn_execute())
qdel(query_text_get_votes)
return
@@ -120,7 +150,13 @@
src << browse(null ,"window=playerpolllist")
src << browse(output,"window=playerpoll;size=500x500")
if(POLLTYPE_RATING)
var/datum/DBQuery/query_rating_get_votes = SSdbcore.NewQuery("SELECT o.text, v.rating FROM [format_table_name("poll_option")] o, [format_table_name("poll_vote")] v WHERE o.pollid = [pollid] AND v.ckey = '[ckey]' AND o.id = v.optionid")
var/datum/db_query/query_rating_get_votes = SSdbcore.NewQuery({"
SELECT o.text, v.rating FROM [format_table_name("poll_option")] o, [format_table_name("poll_vote")] v
WHERE o.pollid = :id AND v.ckey = :ckey AND o.id = v.optionid
"}, list(
"id" = pollid,
"ckey" = ckey
))
if(!query_rating_get_votes.warn_execute())
qdel(query_rating_get_votes)
return
@@ -140,7 +176,13 @@
output += "<input type='hidden' name='votetype' value=[POLLTYPE_RATING]>"
var/minid = 999999
var/maxid = 0
var/datum/DBQuery/query_rating_options = SSdbcore.NewQuery("SELECT id, text, minval, maxval, descmin, descmid, descmax FROM [format_table_name("poll_option")] WHERE pollid = [pollid]")
var/datum/db_query/query_rating_options = SSdbcore.NewQuery({"
SELECT id, text, minval, maxval, descmin, descmid, descmax
FROM [format_table_name("poll_option")]
WHERE pollid = :id
"}, list(
"id" = pollid
))
if(!query_rating_options.warn_execute())
qdel(query_rating_options)
return
@@ -177,7 +219,14 @@
src << browse(null ,"window=playerpolllist")
src << browse(output,"window=playerpoll;size=500x500")
if(POLLTYPE_MULTI)
var/datum/DBQuery/query_multi_get_votes = SSdbcore.NewQuery("SELECT optionid FROM [format_table_name("poll_vote")] WHERE pollid = [pollid] AND ckey = '[ckey]'")
var/datum/db_query/query_multi_get_votes = SSdbcore.NewQuery({"
SELECT optionid
FROM [format_table_name("poll_vote")]
WHERE pollid = :id AND ckey = :ckey
"}, list(
"id" = pollid,
"ckey" = ckey
))
if(!query_multi_get_votes.warn_execute())
qdel(query_multi_get_votes)
return
@@ -188,7 +237,13 @@
var/list/datum/polloption/options = list()
var/maxoptionid = 0
var/minoptionid = 0
var/datum/DBQuery/query_multi_options = SSdbcore.NewQuery("SELECT id, text FROM [format_table_name("poll_option")] WHERE pollid = [pollid]")
var/datum/db_query/query_multi_options = SSdbcore.NewQuery({"
SELECT id, text
FROM [format_table_name("poll_option")]
WHERE pollid = :id
"}, list(
"id" = pollid
))
if(!query_multi_options.warn_execute())
qdel(query_multi_options)
return
@@ -231,8 +286,10 @@
if(POLLTYPE_IRV)
var/datum/asset/irv_assets = get_asset_datum(/datum/asset/group/irv)
irv_assets.send(src)
var/datum/DBQuery/query_irv_get_votes = SSdbcore.NewQuery("SELECT optionid FROM [format_table_name("poll_vote")] WHERE pollid = [pollid] AND ckey = '[ckey]'")
var/datum/db_query/query_irv_get_votes = SSdbcore.NewQuery({"
SELECT optionid FROM [format_table_name("poll_vote")]
WHERE pollid = :pollid AND ckey = :ckey AND deleted = 0
"}, list("pollid" = pollid, "ckey" = ckey))
if(!query_irv_get_votes.warn_execute())
qdel(query_irv_get_votes)
return
@@ -244,7 +301,13 @@
var/list/datum/polloption/options = list()
var/datum/DBQuery/query_irv_options = SSdbcore.NewQuery("SELECT id, text FROM [format_table_name("poll_option")] WHERE pollid = [pollid]")
var/datum/db_query/query_irv_options = SSdbcore.NewQuery({"
SELECT id, text
FROM [format_table_name("poll_option")]
WHERE pollid = :id
"}, list(
"id" = pollid
))
if(!query_irv_options.warn_execute())
qdel(query_irv_options)
return
@@ -352,16 +415,23 @@
if (text)
table = "poll_textreply"
if (!SSdbcore.Connect())
to_chat(usr, "<span class='danger'>Failed to establish database connection.</span>")
to_chat(usr, "<span class='danger'>Failed to establish database connection.</span>", confidential = TRUE)
return
var/datum/DBQuery/query_hasvoted = SSdbcore.NewQuery("SELECT id FROM `[format_table_name(table)]` WHERE pollid = [pollid] AND ckey = '[ckey]'")
var/datum/db_query/query_hasvoted = SSdbcore.NewQuery({"
SELECT id
FROM `[format_table_name(table)]`
WHERE pollid = :id AND ckey = :ckey
"}, list(
"id" = pollid,
"ckey" = ckey
))
if(!query_hasvoted.warn_execute())
qdel(query_hasvoted)
return
if(query_hasvoted.NextRow())
qdel(query_hasvoted)
if(!silent)
to_chat(usr, "<span class='danger'>You've already replied to this poll.</span>")
to_chat(usr, "<span class='danger'>You've already replied to this poll.</span>", confidential = TRUE)
return TRUE
qdel(query_hasvoted)
return FALSE
@@ -376,24 +446,31 @@
/mob/dead/new_player/proc/vote_rig_check()
if (usr != src)
if (!usr || !src)
return 0
return FALSE
//we gots ourselfs a dirty cheater on our hands!
log_game("[key_name(usr)] attempted to rig the vote by voting as [key]")
message_admins("[key_name_admin(usr)] attempted to rig the vote by voting as [key]")
to_chat(usr, "<span class='danger'>You don't seem to be [key].</span>")
to_chat(src, "<span class='danger'>Something went horribly wrong processing your vote. Please contact an administrator, they should have gotten a message about this</span>")
return 0
return 1
return FALSE
return TRUE
/mob/dead/new_player/proc/vote_valid_check(pollid, holder, type)
if (!SSdbcore.Connect())
if(!SSdbcore.Connect())
to_chat(src, "<span class='danger'>Failed to establish database connection.</span>")
return 0
return
pollid = text2num(pollid)
if (!pollid || pollid < 0)
return 0
//validate the poll is actually the right type of poll and its still active
var/datum/DBQuery/query_validate_poll = SSdbcore.NewQuery("SELECT id FROM [format_table_name("poll_question")] WHERE id = [pollid] AND Now() BETWEEN starttime AND endtime AND polltype = '[type]' [(holder ? "" : "AND adminonly = false")]")
var/datum/db_query/query_validate_poll = SSdbcore.NewQuery({"
SELECT id
FROM [format_table_name("poll_question")]
WHERE id = :id AND Now() BETWEEN starttime AND endtime AND polltype = :type [(holder ? "" : "AND adminonly = false")]
"}, list(
"id" = pollid,
"type" = type
))
if(!query_validate_poll.warn_execute())
qdel(query_validate_poll)
return 0
@@ -403,88 +480,66 @@
qdel(query_validate_poll)
return 1
/**
* Processes vote form data and saves results to the database for an IRV type poll.
*
*/
/mob/dead/new_player/proc/vote_on_irv_poll(pollid, list/votelist)
if (!SSdbcore.Connect())
if(!SSdbcore.Connect())
to_chat(src, "<span class='danger'>Failed to establish database connection.</span>")
return 0
if (!vote_rig_check())
return 0
pollid = text2num(pollid)
if (!pollid || pollid < 0)
return 0
if (!votelist || !istype(votelist) || !votelist.len)
return 0
if (!client)
return 0
//save these now so we can still process the vote if the client goes away while we process.
var/datum/admins/holder = client.holder
var/rank = "Player"
if (holder)
rank = holder.rank.name
var/ckey = client.ckey
var/address = client.address
//validate the poll
if (!vote_valid_check(pollid, holder, POLLTYPE_IRV))
return
if(IsAdminAdvancedProcCall())
return
if(!vote_rig_check())
return
if(!pollid)
return
// var/list/votelist = splittext(href_list["IRVdata"], ",")
if(!length(votelist))
to_chat(src, "<span class='danger'>No ordering data found. Please try again or contact an administrator.</span>")
var/admin_rank = "Player"
if(!QDELETED(client) && client.holder)
admin_rank = client.holder.rank.name
if (!vote_valid_check(pollid, client?.holder, POLLTYPE_IRV))
return 0
//lets collect the options
var/datum/DBQuery/query_irv_id = SSdbcore.NewQuery("SELECT id FROM [format_table_name("poll_option")] WHERE pollid = [pollid]")
if(!query_irv_id.warn_execute())
qdel(query_irv_id)
return 0
var/list/optionlist = list()
while (query_irv_id.NextRow())
optionlist += text2num(query_irv_id.item[1])
qdel(query_irv_id)
var/list/special_columns = list(
"datetime" = "NOW()",
"ip" = "INET_ATON(?)",
)
//validate their votes are actually in the list of options and actually numbers
var/list/numberedvotelist = list()
for (var/vote in votelist)
vote = text2num(vote)
numberedvotelist += vote
if (!vote) //this is fine because voteid starts at 1, so it will never be 0
to_chat(src, "<span class='danger'>Error: Invalid (non-numeric) votes in the vote data.</span>")
return 0
if (!(vote in optionlist))
to_chat(src, "<span class='danger'>Votes for choices that do not appear to be in the poll detected.</span>")
return 0
if (!numberedvotelist.len)
to_chat(src, "<span class='danger'>Invalid vote data</span>")
return 0
//lets add the vote, first we generate an insert statement.
var/sqlrowlist = ""
for (var/vote in numberedvotelist)
if (sqlrowlist != "")
sqlrowlist += ", " //a comma (,) at the start of the first row to insert will trigger a SQL error
sqlrowlist += "(Now(), [pollid], [vote], '[sanitizeSQL(ckey)]', INET_ATON('[sanitizeSQL(address)]'), '[sanitizeSQL(rank)]')"
//now lets delete their old votes (if any)
var/datum/DBQuery/query_irv_del_old = SSdbcore.NewQuery("DELETE FROM [format_table_name("poll_vote")] WHERE pollid = [pollid] AND ckey = '[ckey]'")
if(!query_irv_del_old.warn_execute())
qdel(query_irv_del_old)
return 0
qdel(query_irv_del_old)
//now to add the new ones.
var/datum/DBQuery/query_irv_vote = SSdbcore.NewQuery("INSERT INTO [format_table_name("poll_vote")] (datetime, pollid, optionid, ckey, ip, adminrank) VALUES [sqlrowlist]")
if(!query_irv_vote.warn_execute())
qdel(query_irv_vote)
return 0
qdel(query_irv_vote)
if(!QDELETED(src))
src << browse(null,"window=playerpoll")
return 1
var/sql_votes = list()
for(var/o in votelist)
var/voteid = text2num(o)
if(!voteid)
continue
sql_votes += list(list(
"pollid" = pollid,
"optionid" = voteid,
"ckey" = ckey,
"ip" = client.address,
"adminrank" = admin_rank
))
//IRV results are calculated based on id order, we delete all of a user's votes to avoid potential errors caused by revoting and option editing
var/datum/db_query/query_delete_irv_votes = SSdbcore.NewQuery({"
UPDATE [format_table_name("poll_vote")] SET deleted = 1 WHERE pollid = :pollid AND ckey = :ckey
"}, list("pollid" = pollid, "ckey" = ckey))
if(!query_delete_irv_votes.warn_execute())
qdel(query_delete_irv_votes)
return
qdel(query_delete_irv_votes)
SSdbcore.MassInsert(format_table_name("poll_vote"), sql_votes, special_columns = special_columns)
return TRUE
/mob/dead/new_player/proc/vote_on_poll(pollid, optionid)
if (!SSdbcore.Connect())
if(!SSdbcore.Connect())
to_chat(src, "<span class='danger'>Failed to establish database connection.</span>")
return 0
if (!vote_rig_check())
return 0
return
if(!vote_rig_check())
return
if(IsAdminAdvancedProcCall())
return
if(!pollid || !optionid)
return
//validate the poll
@@ -493,10 +548,19 @@
var/voted = poll_check_voted(pollid)
if(isnull(voted) || voted) //Failed or already voted.
return
var/adminrank = sanitizeSQL(poll_rank())
var/adminrank = poll_rank()
if(!adminrank)
return
var/datum/DBQuery/query_option_vote = SSdbcore.NewQuery("INSERT INTO [format_table_name("poll_vote")] (datetime, pollid, optionid, ckey, ip, adminrank) VALUES (Now(), [pollid], [optionid], '[ckey]', INET_ATON('[client.address]'), '[adminrank]')")
var/datum/db_query/query_option_vote = SSdbcore.NewQuery({"
INSERT INTO [format_table_name("poll_vote")] (datetime, pollid, optionid, ckey, ip, adminrank)
VALUES (Now(), :pollid, :optionid, :ckey, INET_ATON(:address), :adminrank)
"}, list(
"pollid" = pollid,
"optionid" = optionid,
"ckey" = ckey,
"address" = client.address,
"adminrank" = adminrank
))
if(!query_option_vote.warn_execute())
qdel(query_option_vote)
return
@@ -506,11 +570,13 @@
return 1
/mob/dead/new_player/proc/log_text_poll_reply(pollid, replytext)
if (!SSdbcore.Connect())
if(!SSdbcore.Connect())
to_chat(src, "<span class='danger'>Failed to establish database connection.</span>")
return 0
if (!vote_rig_check())
return 0
return
if(!vote_rig_check())
return
if(IsAdminAdvancedProcCall())
return
if(!pollid)
return
//validate the poll
@@ -522,18 +588,23 @@
var/voted = poll_check_voted(pollid, text = TRUE, silent = TRUE)
if(isnull(voted))
return
var/adminrank = sanitizeSQL(poll_rank())
var/adminrank = poll_rank()
if(!adminrank)
return
replytext = sanitizeSQL(replytext)
if(!(length(replytext) > 0) || !(length(replytext) <= 8000))
to_chat(usr, "The text you entered was invalid or too long. Please correct the text and submit again.")
return
var/datum/DBQuery/query_text_vote
var/datum/db_query/query_text_vote
if(!voted)
query_text_vote = SSdbcore.NewQuery("INSERT INTO [format_table_name("poll_textreply")] (datetime ,pollid ,ckey ,ip ,replytext ,adminrank) VALUES (Now(), [pollid], '[ckey]', INET_ATON('[client.address]'), '[replytext]', '[adminrank]')")
query_text_vote = SSdbcore.NewQuery({"
INSERT INTO [format_table_name("poll_textreply")] (datetime, pollid, ckey, ip, replytext, adminrank)
VALUES (Now(), :pollid, :ckey, INET_ATON(:address), :replytext, :adminrank)
"}, list("pollid" = pollid, "ckey" = ckey, "address" = client.address, "replytext" = replytext, "adminrank" = adminrank))
else
query_text_vote = SSdbcore.NewQuery("UPDATE [format_table_name("poll_textreply")] SET datetime = Now(), ip = INET_ATON('[client.address]'), replytext = '[replytext]' WHERE pollid = '[pollid]' AND ckey = '[ckey]'")
query_text_vote = SSdbcore.NewQuery({"
UPDATE [format_table_name("poll_textreply")]
SET datetime = Now(), ip = INET_ATON(:address), replytext = :replytext WHERE pollid = :pollid AND ckey = :ckey
"}, list("address" = client.address, "replytext" = replytext, "pollid" = pollid, "ckey" = ckey))
if(!query_text_vote.warn_execute())
qdel(query_text_vote)
return
@@ -543,17 +614,26 @@
return 1
/mob/dead/new_player/proc/vote_on_numval_poll(pollid, optionid, rating)
if (!SSdbcore.Connect())
if(!SSdbcore.Connect())
to_chat(src, "<span class='danger'>Failed to establish database connection.</span>")
return 0
if (!vote_rig_check())
return 0
return
if(!vote_rig_check())
return
if(IsAdminAdvancedProcCall())
return
if(!pollid || !optionid || !rating)
return
//validate the poll
if (!vote_valid_check(pollid, client.holder, POLLTYPE_RATING))
return 0
var/datum/DBQuery/query_numval_hasvoted = SSdbcore.NewQuery("SELECT id FROM [format_table_name("poll_vote")] WHERE optionid = [optionid] AND ckey = '[ckey]'")
var/datum/db_query/query_numval_hasvoted = SSdbcore.NewQuery({"
SELECT id
FROM [format_table_name("poll_vote")]
WHERE optionid = :id AND ckey = :ckey
"}, list(
"id" = optionid,
"ckey" = ckey
))
if(!query_numval_hasvoted.warn_execute())
qdel(query_numval_hasvoted)
return
@@ -565,8 +645,10 @@
var/adminrank = "Player"
if(client.holder)
adminrank = client.holder.rank.name
adminrank = sanitizeSQL(adminrank)
var/datum/DBQuery/query_numval_vote = SSdbcore.NewQuery("INSERT INTO [format_table_name("poll_vote")] (datetime ,pollid ,optionid ,ckey ,ip ,adminrank, rating) VALUES (Now(), [pollid], [optionid], '[ckey]', INET_ATON('[client.address]'), '[adminrank]', [(isnull(rating)) ? "null" : rating])")
var/datum/db_query/query_numval_vote = SSdbcore.NewQuery({"
INSERT INTO [format_table_name("poll_vote")] (datetime ,pollid ,optionid ,ckey ,ip ,adminrank, rating)
VALUES (Now(), :pollid, :optionid, :ckey, INET_ATON(:address), :adminrank, :rating)
"}, list("pollid" = pollid, "optionid" = optionid, "ckey" = ckey, "address" = client.address, "adminrank" = adminrank, "rating" = isnull(rating) ? "null" : rating))
if(!query_numval_vote.warn_execute())
qdel(query_numval_vote)
return
@@ -575,46 +657,59 @@
usr << browse(null,"window=playerpoll")
return 1
/**
* Processes vote form data and saves results to the database for a multiple choice type poll.
*
*/
/mob/dead/new_player/proc/vote_on_multi_poll(pollid, optionid)
if (!SSdbcore.Connect())
if(!SSdbcore.Connect())
to_chat(src, "<span class='danger'>Failed to establish database connection.</span>")
return 0
if (!vote_rig_check())
return 0
if(!pollid || !optionid)
return 1
return
if(!vote_rig_check())
return
if(IsAdminAdvancedProcCall())
return
//validate the poll
if (!vote_valid_check(pollid, client.holder, POLLTYPE_MULTI))
return 0
var/datum/DBQuery/query_multi_choicelen = SSdbcore.NewQuery("SELECT multiplechoiceoptions FROM [format_table_name("poll_question")] WHERE id = [pollid]")
if(!query_multi_choicelen.warn_execute())
qdel(query_multi_choicelen)
return 1
var/i
if(query_multi_choicelen.NextRow())
i = text2num(query_multi_choicelen.item[1])
qdel(query_multi_choicelen)
var/datum/DBQuery/query_multi_hasvoted = SSdbcore.NewQuery("SELECT id FROM [format_table_name("poll_vote")] WHERE pollid = [pollid] AND ckey = '[ckey]'")
if(!query_multi_hasvoted.warn_execute())
qdel(query_multi_hasvoted)
return 1
while(i)
if(query_multi_hasvoted.NextRow())
i--
else
break
qdel(query_multi_hasvoted)
if(!i)
return 2
var/adminrank = "Player"
if(!QDELETED(client) && client.holder)
adminrank = client.holder.rank.name
adminrank = sanitizeSQL(adminrank)
var/datum/DBQuery/query_multi_vote = SSdbcore.NewQuery("INSERT INTO [format_table_name("poll_vote")] (datetime, pollid, optionid, ckey, ip, adminrank) VALUES (Now(), [pollid], [optionid], '[ckey]', INET_ATON('[client.address]'), '[adminrank]')")
if(!query_multi_vote.warn_execute())
qdel(query_multi_vote)
return 1
qdel(query_multi_vote)
if(!QDELETED(usr))
usr << browse(null,"window=playerpoll")
return 0
if(!vote_valid_check(pollid, client.holder, POLLTYPE_MULTI))
return
if(!pollid || !optionid)
return
// if(length(href_list) > 2)
// href_list.Cut(1,3) //first two values aren't options
// else
// to_chat(src, "<span class='danger'>No options were selected.</span>")
var/special_columns = list(
"datetime" = "NOW()",
"ip" = "INET_ATON(?)",
)
var/sql_votes = list()
// var/vote_count = 0
// for(var/h in href_list)
// if(vote_count == poll.options_allowed)
// to_chat(src, "<span class='danger'>Allowed option count exceeded, only the first [poll.options_allowed] selected options have been saved.</span>")
// break
// vote_count++
// var/datum/poll_option/option = locate(h) in poll.options
var/admin_rank = "Player"
if(!QDELETED(client) && client?.holder)
admin_rank = client.holder.rank.name
sql_votes += list(list(
"pollid" = pollid,
"optionid" = optionid,
"ckey" = ckey,
"ip" = client.address,
"adminrank" = admin_rank
))
/*with revoting and poll editing possible there can be an edge case where a poll is changed to allow less multiple choice options than a user has already voted on
rather than trying to calculate which options should be updated and which deleted, we just delete all of a user's votes and re-insert as needed*/
var/datum/db_query/query_delete_multi_votes = SSdbcore.NewQuery({"
UPDATE [format_table_name("poll_vote")] SET deleted = 1 WHERE pollid = :pollid AND ckey = :ckey
"}, list("pollid" = pollid, "ckey" = ckey))
if(!query_delete_multi_votes.warn_execute())
qdel(query_delete_multi_votes)
return
qdel(query_delete_multi_votes)
SSdbcore.MassInsert(format_table_name("poll_vote"), sql_votes, special_columns = special_columns)
return TRUE

View File

@@ -24,7 +24,7 @@
break
var/msg = "[key_name_admin(src)] [ADMIN_JMP(src)] was found to have no .loc with an attached client, if the cause is unknown it would be wise to ask how this was accomplished."
message_admins(msg)
send2irc_adminless_only("Mob", msg, R_ADMIN)
send2tgs_adminless_only("Mob", msg, R_ADMIN)
log_game("[key_name(src)] was found to have no .loc with an attached client.")
// This is a temporary error tracker to make sure we've caught everything

View File

@@ -364,7 +364,9 @@
set waitfor = FALSE
if(!SSdbcore.Connect())
return
var/datum/DBQuery/query_round_shuttle_name = SSdbcore.NewQuery("UPDATE [format_table_name("round")] SET shuttle_name = '[name]' WHERE id = [GLOB.round_id]")
var/datum/db_query/query_round_shuttle_name = SSdbcore.NewQuery({"
UPDATE [format_table_name("round")] SET shuttle_name = :name WHERE id = :round_id
"}, list("name" = name, "round_id" = GLOB.round_id))
query_round_shuttle_name.Execute()
qdel(query_round_shuttle_name)
@@ -396,7 +398,7 @@
return
mode = SHUTTLE_DOCKED
setTimer(SSshuttle.emergencyDockTime)
send2irc("Server", "The Emergency Shuttle has docked with the station.")
send2adminchat("Server", "The Emergency Shuttle has docked with the station.")
priority_announce("The Emergency Shuttle has docked with the station. You have [timeLeft(600)] minutes to board the Emergency Shuttle.", null, "shuttledock", "Priority")
ShuttleDBStuff()

View File

@@ -98,19 +98,18 @@
return json_encode(response)
/datum/tgs_api/v5/OnTopic(T)
if(!initialized)
return FALSE //continue world/Topic
var/list/params = params2list(T)
var/json = params[DMAPI5_TOPIC_DATA]
if(!json)
return FALSE // continue to /world/Topic
return FALSE
var/list/topic_parameters = json_decode(json)
if(!topic_parameters)
return TopicResponse("Invalid topic parameters json!");
if(!initialized)
TGS_WARNING_LOG("Missed topic due to not being initialized: [T]")
return TRUE // too early to handle, but it's still our responsibility
var/their_sCK = topic_parameters[DMAPI5_PARAMETER_ACCESS_IDENTIFIER]
if(their_sCK != access_identifier)
return TopicResponse("Failed to decode [DMAPI5_PARAMETER_ACCESS_IDENTIFIER] from: [json]!");