mirror of
https://github.com/ParadiseSS13/Paradise.git
synced 2025-12-27 02:32:20 +00:00
A lot has changed.
Major points:
- All mobs now use the -tg- thrown alerts system. This is a system where
a maximum of 5 "alerts" (which take the apperance of HUD icons like the
inventory and modular action buttons) on their HUD.
- Alerts are defined as a subtype of /obj/screen/alert.
- Alerts are "thrown", or activated on the mob with the proc
`mob.throw_alert("alert_id", /obj/screen/alert/path, severity
(optional), new_master (optional, overlays the referenced "obj" on the
button))`
- Alerts are cleared by calling `mob.clear_alert("alert_id")` or by the
/obj/screen/alert containing a `timeout` setting, which will
automatically clear itself after that period of deciseconds.
- Alerts may have functionality beyond appearance: Mousing over them
will use the WireWraith tooltip system to generate a themed tooltip,
and clicking them may have a function defined on the
/obj/screen/alert object.
- Shift-clicking an alert will give you it's name and status, in case
the tooltips fail to work.
- Hunger/Oxygen/Toxin/Nitrogen/Pressure/Etc warnings are now handled via
the -tg- thrown alerts system.
- Failing to breathe anything will display as an "No O2" warning.
- Species breathing has been refactored to accomodate this. Vox are
able to breathe the station air without instantly dying, but will be
poisoned every time they breathe by the oxygen in the air. Any
species with a unique gas breathing type will also display the
correct "Choking (no X gas)" instead of just "oxygen". (Unless they
have not breathed anything in, it's a quirk.)
- Robot cell/temperature alerts also use this.
- Aliens, though not harmed by toxins, will have an alert if they
breathe in toxins, notifying them that "You'll be toast if this
lights up."
- More alerts have been added
- Buckling
- Activated on: Being buckled to an object.
- Cleared on: Being unbuckled.
- Click functionality: Calls resist(); Will unbuckle from chair
instantly if unrestrained, otherwise, there is a delay period.
- Hand/Leg cuffs
- Activated on: Being handcuffed.
- Cleared on: Being unhandcuffed.
- Click functionality: Calls resist(); Will attempt to either get out
of the handcuffs, or, if you are a xenomorph or hulk, break the
handcuffs in 5 seconds.
- Blind/High
- Activated on: Becoming blinded/becoming high on LSD.
- Cleared on: Becoming unblinded/the high wearing off.
- Click functionality: None.
- Asleep
- Activated on: Life() tick detecting sleeping.
- Deactivated on: Sleeping being 0 or less.
- Click functionality: None.
- Weightless
- Activated on: Losing gravity.
- Deactivated on: Moving back into an area with gravity.
- Click functionality: None.
- On Fire
- Activated on: Catching fire.
- Deactivated on: Being extinguished.
- Click functionality: Calls resist(); Causes you to stop, drop, and
roll, which will reduce firestacks and possibly extinguish you.
- Law update (BORG ONLY)
- Activated on: Laws being changed.
- Deactivated on: Timer, 300 deciseconds.
- Click functionality: None.
- Hacked (BORG ONLY)
- Activated on: Being emagged/given malf tools by the malf AI.
- Deactivated on: Emagged status being removed.
- Click functionality: None.
- Locked (BORG ONLY)
- Activated on: Lockdown being set (by emag law rewriting/robotics
console/wire being destroyed)
- Deactivated on: Movement being unlocked.
- Click functionality: None.
- Notify Cloning (GHOST ONLY)
- Activated on: A human's body being placed in a DNA scanner.
- Deactivated on: Timer, 300 deciseconds.
- Click functionality: Reenters body.
- Notify Jump (MULTIPURPOSE) (GHOST ONLY)
- Activated on: Any sort of chance to become a mob (ie, golem rune).
- Deactivated on: Timer, 300 deciseconds.
- Click functionality: Varies.
440 lines
15 KiB
Plaintext
440 lines
15 KiB
Plaintext
////////////
|
|
//SECURITY//
|
|
////////////
|
|
//debugging, uncomment for viewing topic calls
|
|
//#define TOPIC_DEBUGGING 1
|
|
|
|
#define TOPIC_SPAM_DELAY 2 //2 ticks is about 2/10ths of a second; it was 4 ticks, but that caused too many clicks to be lost due to lag
|
|
#define UPLOAD_LIMIT 10485760 //Restricts client uploads to the server to 10MB //Boosted this thing. What's the worst that can happen?
|
|
#define MIN_CLIENT_VERSION 0 //Just an ambiguously low version for now, I don't want to suddenly stop people playing.
|
|
//I would just like the code ready should it ever need to be used.
|
|
/*
|
|
When somebody clicks a link in game, this Topic is called first.
|
|
It does the stuff in this proc and then is redirected to the Topic() proc for the src=[0xWhatever]
|
|
(if specified in the link). ie locate(hsrc).Topic()
|
|
|
|
Such links can be spoofed.
|
|
|
|
Because of this certain things MUST be considered whenever adding a Topic() for something:
|
|
- Can it be fed harmful values which could cause runtimes?
|
|
- Is the Topic call an admin-only thing?
|
|
- If so, does it have checks to see if the person who called it (usr.client) is an admin?
|
|
- Are the processes being called by Topic() particularly laggy?
|
|
- If so, is there any protection against somebody spam-clicking a link?
|
|
If you have any questions about this stuff feel free to ask. ~Carn
|
|
*/
|
|
/client/Topic(href, href_list, hsrc)
|
|
if(!usr || usr != mob) //stops us calling Topic for somebody else's client. Also helps prevent usr=null
|
|
return
|
|
|
|
#if defined(TOPIC_DEBUGGING)
|
|
to_chat(world, "[src]'s Topic: [href] destined for [hsrc].")
|
|
#endif
|
|
|
|
if(href_list["nano_err"]) //nano throwing errors
|
|
if(topic_debugging)
|
|
to_chat(src, "## NanoUI: " + html_decode(href_list["nano_err"]))//NANO DEBUG HOOK
|
|
|
|
|
|
|
|
if(href_list["asset_cache_confirm_arrival"])
|
|
// to_chat(src, "ASSET JOB [href_list["asset_cache_confirm_arrival"]] ARRIVED.")
|
|
var/job = text2num(href_list["asset_cache_confirm_arrival"])
|
|
completed_asset_jobs += job
|
|
return
|
|
|
|
//Reduces spamming of links by dropping calls that happen during the delay period
|
|
if(next_allowed_topic_time > world.time)
|
|
return
|
|
next_allowed_topic_time = world.time + TOPIC_SPAM_DELAY
|
|
|
|
//search the href for script injection
|
|
if( findtext(href,"<script",1,0) )
|
|
log_to_dd("Attempted use of scripts within a topic call, by [src]")
|
|
message_admins("Attempted use of scripts within a topic call, by [src]")
|
|
//del(usr)
|
|
return
|
|
|
|
//Admin PM
|
|
if(href_list["priv_msg"])
|
|
var/client/C = locate(href_list["priv_msg"])
|
|
if(ismob(C)) //Old stuff can feed-in mobs instead of clients
|
|
var/mob/M = C
|
|
C = M.client
|
|
cmd_admin_pm(C,null, href_list["type"])
|
|
return
|
|
|
|
if(href_list["irc_msg"])
|
|
if(!holder && received_irc_pm < world.time - 6000) //Worse they can do is spam IRC for 10 minutes
|
|
to_chat(usr, "<span class='warning'>You are no longer able to use this, it's been more then 10 minutes since an admin on IRC has responded to you</span>")
|
|
return
|
|
if(mute_irc)
|
|
to_chat(usr, "<span class='warning'You cannot use this as your client has been muted from sending messages to the admins on IRC</span>")
|
|
return
|
|
cmd_admin_irc_pm()
|
|
return
|
|
|
|
|
|
|
|
//Logs all hrefs
|
|
if(config && config.log_hrefs && href_logfile)
|
|
to_chat(href_logfile, "<small>[time2text(world.timeofday,"hh:mm")] [src] (usr:[usr])</small> || [hsrc ? "[hsrc] " : ""][href]<br>")
|
|
|
|
switch(href_list["karmashop"])
|
|
if("tab")
|
|
karma_tab = text2num(href_list["tab"])
|
|
karmashopmenu()
|
|
return
|
|
if("shop")
|
|
if(href_list["KarmaBuy"])
|
|
var/karma=verify_karma()
|
|
switch(href_list["KarmaBuy"])
|
|
if("1")
|
|
if(karma <5)
|
|
to_chat(usr, "You do not have enough karma!")
|
|
return
|
|
else
|
|
src.DB_job_unlock("Barber",5)
|
|
return
|
|
if("2")
|
|
if(karma <5)
|
|
to_chat(usr, "You do not have enough karma!")
|
|
return
|
|
else
|
|
src.DB_job_unlock("Brig Physician",5)
|
|
return
|
|
if("3")
|
|
if(karma <30)
|
|
to_chat(usr, "You do not have enough karma!")
|
|
return
|
|
else
|
|
src.DB_job_unlock("Nanotrasen Representative",30)
|
|
return
|
|
if("5")
|
|
if(karma <30)
|
|
to_chat(usr, "You do not have enough karma!")
|
|
return
|
|
else
|
|
src.DB_job_unlock("Blueshield",30)
|
|
return
|
|
if("6")
|
|
if(karma <30)
|
|
to_chat(usr, "You do not have enough karma!")
|
|
return
|
|
else
|
|
src.DB_job_unlock("Mechanic",30)
|
|
return
|
|
if("7")
|
|
if(karma <45)
|
|
to_chat(usr, "You do not have enough karma!")
|
|
return
|
|
else
|
|
src.DB_job_unlock("Magistrate",45)
|
|
return
|
|
if("9")
|
|
if(karma <30)
|
|
to_chat(usr, "You do not have enough karma!")
|
|
return
|
|
else
|
|
src.DB_job_unlock("Security Pod Pilot",30)
|
|
return
|
|
if(href_list["KarmaBuy2"])
|
|
var/karma=verify_karma()
|
|
switch(href_list["KarmaBuy2"])
|
|
if("1")
|
|
if(karma <15)
|
|
to_chat(usr, "You do not have enough karma!")
|
|
return
|
|
else
|
|
src.DB_species_unlock("Machine",15)
|
|
return
|
|
if("2")
|
|
if(karma <30)
|
|
to_chat(usr, "You do not have enough karma!")
|
|
return
|
|
else
|
|
src.DB_species_unlock("Kidan",30)
|
|
return
|
|
if("3")
|
|
if(karma <30)
|
|
to_chat(usr, "You do not have enough karma!")
|
|
return
|
|
else
|
|
src.DB_species_unlock("Grey",30)
|
|
return
|
|
if("4")
|
|
if(karma <45)
|
|
to_chat(usr, "You do not have enough karma!")
|
|
return
|
|
else
|
|
src.DB_species_unlock("Vox",45)
|
|
return
|
|
if("5")
|
|
if(karma <45)
|
|
to_chat(usr, "You do not have enough karma!")
|
|
return
|
|
else
|
|
src.DB_species_unlock("Slime People",45)
|
|
return
|
|
if("6")
|
|
if(karma <100)
|
|
to_chat(usr, "You do not have enough karma!")
|
|
return
|
|
else
|
|
src.DB_species_unlock("Plasmaman",100)
|
|
return
|
|
if(href_list["KarmaRefund"])
|
|
var/type = href_list["KarmaRefundType"]
|
|
var/job = href_list["KarmaRefund"]
|
|
var/cost = href_list["KarmaRefundCost"]
|
|
src.karmarefund(type,job,cost)
|
|
return
|
|
|
|
switch(href_list["_src_"])
|
|
if("holder") hsrc = holder
|
|
if("usr") hsrc = mob
|
|
if("prefs") return prefs.process_link(usr,href_list)
|
|
if("vars") return view_var_Topic(href,href_list,hsrc)
|
|
|
|
..() //redirect to hsrc.Topic()
|
|
|
|
/client/proc/is_content_unlocked()
|
|
if(!prefs.unlock_content)
|
|
to_chat(src, "Become a BYOND member to access member-perks and features, as well as support the engine that makes this game possible. <a href='http://www.byond.com/membership'>Click here to find out more</a>.")
|
|
return 0
|
|
return 1
|
|
|
|
/client/proc/handle_spam_prevention(var/message, var/mute_type)
|
|
if(config.automute_on && !holder && src.last_message == message)
|
|
src.last_message_count++
|
|
if(src.last_message_count >= SPAM_TRIGGER_AUTOMUTE)
|
|
to_chat(src, "\red You have exceeded the spam filter limit for identical messages. An auto-mute was applied.")
|
|
cmd_admin_mute(src.mob, mute_type, 1)
|
|
return 1
|
|
if(src.last_message_count >= SPAM_TRIGGER_WARNING)
|
|
to_chat(src, "\red You are nearing the spam filter limit for identical messages.")
|
|
return 0
|
|
else
|
|
last_message = message
|
|
src.last_message_count = 0
|
|
return 0
|
|
|
|
//This stops files larger than UPLOAD_LIMIT being sent from client to server via input(), client.Import() etc.
|
|
/client/AllowUpload(filename, filelength)
|
|
if(filelength > UPLOAD_LIMIT)
|
|
to_chat(src, "<font color='red'>Error: AllowUpload(): File Upload too large. Upload Limit: [UPLOAD_LIMIT/1024]KiB.</font>")
|
|
return 0
|
|
/* //Don't need this at the moment. But it's here if it's needed later.
|
|
//Helps prevent multiple files being uploaded at once. Or right after eachother.
|
|
var/time_to_wait = fileaccess_timer - world.time
|
|
if(time_to_wait > 0)
|
|
to_chat(src, "<font color='red'>Error: AllowUpload(): Spam prevention. Please wait [round(time_to_wait/10)] seconds.</font>")
|
|
return 0
|
|
fileaccess_timer = world.time + FTPDELAY */
|
|
return 1
|
|
|
|
|
|
///////////
|
|
//CONNECT//
|
|
///////////
|
|
/client/New(TopicData)
|
|
TopicData = null //Prevent calls to client.Topic from connect
|
|
|
|
if(connection != "seeker") //Invalid connection type.
|
|
return null
|
|
if(byond_version < MIN_CLIENT_VERSION) //Out of date client.
|
|
return null
|
|
|
|
if(IsGuestKey(key))
|
|
alert(src,"This server doesn't allow guest accounts to play. Please go to http://www.byond.com/ and register for a key.","Guest","OK")
|
|
del(src)
|
|
return
|
|
|
|
// Change the way they should download resources.
|
|
if(config.resource_urls)
|
|
src.preload_rsc = pick(config.resource_urls)
|
|
else src.preload_rsc = 1 // If config.resource_urls is not set, preload like normal.
|
|
|
|
to_chat(src, "\red If the title screen is black, resources are still downloading. Please be patient until the title screen appears.")
|
|
|
|
|
|
clients += src
|
|
directory[ckey] = src
|
|
|
|
//Admin Authorisation
|
|
holder = admin_datums[ckey]
|
|
if(holder)
|
|
admins += src
|
|
holder.owner = src
|
|
|
|
//preferences datum - also holds some persistant data for the client (because we may as well keep these datums to a minimum)
|
|
prefs = preferences_datums[ckey]
|
|
if(!prefs)
|
|
prefs = new /datum/preferences(src)
|
|
preferences_datums[ckey] = prefs
|
|
prefs.last_ip = address //these are gonna be used for banning
|
|
prefs.last_id = computer_id //these are gonna be used for banning
|
|
|
|
. = ..() //calls mob.Login()
|
|
|
|
if(custom_event_msg && custom_event_msg != "")
|
|
to_chat(src, "<h1 class='alert'>Custom Event</h1>")
|
|
to_chat(src, "<h2 class='alert'>A custom event is taking place. OOC Info:</h2>")
|
|
to_chat(src, "<span class='alert'>[html_encode(custom_event_msg)]</span>")
|
|
to_chat(src, "<br>")
|
|
|
|
if( (world.address == address || !address) && !host )
|
|
host = key
|
|
world.update_status()
|
|
|
|
if(holder)
|
|
add_admin_verbs()
|
|
admin_memo_output("Show", 0, 1)
|
|
|
|
// Forcibly enable hardware-accelerated graphics, as we need them for the lighting overlays.
|
|
// (but turn them off first, since sometimes BYOND doesn't turn them on properly otherwise)
|
|
spawn(5) // And wait a half-second, since it sounds like you can do this too fast.
|
|
if(src)
|
|
winset(src, null, "command=\".configure graphics-hwmode off\"")
|
|
winset(src, null, "command=\".configure graphics-hwmode on\"")
|
|
|
|
log_client_to_db()
|
|
|
|
if (ckey in clientmessages)
|
|
for (var/message in clientmessages[ckey])
|
|
to_chat(src, message)
|
|
clientmessages.Remove(ckey)
|
|
|
|
|
|
send_resources()
|
|
|
|
if(!void)
|
|
void = new()
|
|
|
|
screen += void
|
|
|
|
if(!winexists(src, "asset_cache_browser")) // The client is using a custom skin, tell them.
|
|
to_chat(src, "<span class='warning'>Unable to access asset cache browser, if you are using a custom skin file, please allow DS to download the updated version, if you are not, then make a bug report. This is not a critical issue but can cause issues with resource downloading, as it is impossible to know when extra resources arrived to you.</span>")
|
|
|
|
|
|
//This is down here because of the browse() calls in tooltip/New()
|
|
if(!tooltips)
|
|
tooltips = new /datum/tooltip(src)
|
|
|
|
//////////////
|
|
//DISCONNECT//
|
|
//////////////
|
|
/client/Del()
|
|
if(holder)
|
|
holder.owner = null
|
|
admins -= src
|
|
directory -= ckey
|
|
clients -= src
|
|
return ..()
|
|
|
|
|
|
|
|
/client/proc/log_client_to_db()
|
|
|
|
if ( IsGuestKey(src.key) )
|
|
return
|
|
|
|
establish_db_connection()
|
|
if(!dbcon.IsConnected())
|
|
return
|
|
|
|
var/sql_ckey = sql_sanitize_text(src.ckey)
|
|
|
|
var/DBQuery/query = dbcon.NewQuery("SELECT id, datediff(Now(),firstseen) as age FROM [format_table_name("player")] WHERE ckey = '[sql_ckey]'")
|
|
query.Execute()
|
|
var/sql_id = 0
|
|
player_age = 0 // New players won't have an entry so knowing we have a connection we set this to zero to be updated if their is a record.
|
|
while(query.NextRow())
|
|
sql_id = query.item[1]
|
|
player_age = text2num(query.item[2])
|
|
break
|
|
|
|
var/DBQuery/query_ip = dbcon.NewQuery("SELECT ckey FROM [format_table_name("player")] WHERE ip = '[address]'")
|
|
query_ip.Execute()
|
|
related_accounts_ip = list()
|
|
while(query_ip.NextRow())
|
|
if(ckey != query_ip.item[1])
|
|
related_accounts_ip.Add("[query_ip.item[1]]")
|
|
|
|
var/DBQuery/query_cid = dbcon.NewQuery("SELECT ckey FROM [format_table_name("player")] WHERE computerid = '[computer_id]'")
|
|
query_cid.Execute()
|
|
related_accounts_cid = list()
|
|
while(query_cid.NextRow())
|
|
if(ckey != query_cid.item[1])
|
|
related_accounts_cid.Add("[query_cid.item[1]]")
|
|
|
|
//Log all the alts
|
|
if(related_accounts_cid.len)
|
|
log_access("Alts: [key_name(src)]:[jointext(related_accounts_cid, " - ")]")
|
|
|
|
var/watchreason = check_watchlist(sql_ckey)
|
|
if(watchreason)
|
|
message_admins("<font color='red'><B>Notice: </B></font><font color='blue'>[key_name_admin(src)] is on the watchlist and has just connected - Reason: [watchreason]</font>")
|
|
send2adminirc("Watchlist - [key_name(src)] is on the watchlist and has just connected - Reason: [watchreason]")
|
|
|
|
//Just the standard check to see if it's actually a number
|
|
if(sql_id)
|
|
if(istext(sql_id))
|
|
sql_id = text2num(sql_id)
|
|
if(!isnum(sql_id))
|
|
return
|
|
|
|
var/admin_rank = "Player"
|
|
if(src.holder)
|
|
admin_rank = src.holder.rank
|
|
|
|
var/sql_ip = sql_sanitize_text(src.address)
|
|
var/sql_computerid = sql_sanitize_text(src.computer_id)
|
|
var/sql_admin_rank = sql_sanitize_text(admin_rank)
|
|
|
|
|
|
if(sql_id)
|
|
//Player already identified previously, we need to just update the 'lastseen', 'ip' and 'computer_id' variables
|
|
var/DBQuery/query_update = dbcon.NewQuery("UPDATE [format_table_name("player")] SET lastseen = Now(), ip = '[sql_ip]', computerid = '[sql_computerid]', lastadminrank = '[sql_admin_rank]' WHERE id = [sql_id]")
|
|
query_update.Execute()
|
|
else
|
|
//New player!! Need to insert all the stuff
|
|
var/DBQuery/query_insert = dbcon.NewQuery("INSERT INTO [format_table_name("player")] (id, ckey, firstseen, lastseen, ip, computerid, lastadminrank) VALUES (null, '[sql_ckey]', Now(), Now(), '[sql_ip]', '[sql_computerid]', '[sql_admin_rank]')")
|
|
query_insert.Execute()
|
|
|
|
//Logging player access
|
|
var/serverip = "[world.internet_address]:[world.port]"
|
|
var/DBQuery/query_accesslog = dbcon.NewQuery("INSERT INTO `[format_table_name("connection_log")]`(`id`,`datetime`,`serverip`,`ckey`,`ip`,`computerid`) VALUES(null,Now(),'[serverip]','[sql_ckey]','[sql_ip]','[sql_computerid]');")
|
|
query_accesslog.Execute()
|
|
|
|
|
|
#undef TOPIC_SPAM_DELAY
|
|
#undef UPLOAD_LIMIT
|
|
#undef MIN_CLIENT_VERSION
|
|
|
|
//checks if a client is afk
|
|
//3000 frames = 5 minutes
|
|
/client/proc/is_afk(duration=3000)
|
|
if(inactivity > duration) return inactivity
|
|
return 0
|
|
|
|
//Send resources to the client.
|
|
/client/proc/send_resources()
|
|
// Most assets are now handled through global_cache.dm
|
|
getFiles(
|
|
'html/search.js', // Used in various non-NanoUI HTML windows for search functionality
|
|
'html/panels.css' // Used for styling certain panels, such as in the new player panel
|
|
)
|
|
spawn (10)
|
|
//Precache the client with all other assets slowly, so as to not block other browse() calls
|
|
getFilesSlow(src, asset_cache, register_asset = FALSE)
|
|
|
|
//For debugging purposes
|
|
/client/proc/list_all_languages()
|
|
for(var/L in all_languages)
|
|
var/datum/language/lang = all_languages[L]
|
|
var/message = "[lang.name] : [lang.type]"
|
|
if(lang.flags & RESTRICTED)
|
|
message += " (RESTRICTED)"
|
|
to_chat(world, "[message]")
|