Files
Aurora.3/code/world.dm
skull132 7d21c400cf Fix World Status
Now displays fun things.
2016-04-02 23:38:18 +03:00

745 lines
20 KiB
Plaintext

var/global/datum/global_init/init = new ()
/*
Pre-map initialization stuff should go here.
*/
/datum/global_init/New()
makeDatumRefLists()
load_configuration()
qdel(src)
/world
mob = /mob/new_player
turf = /turf/space
area = /area/space
view = "15x15"
cache_lifespan = 0 //stops player uploaded stuff from being kept in the rsc past the current session
#define RECOMMENDED_VERSION 501
/world/New()
//logs
var/date_string = time2text(world.realtime, "YYYY/MM-Month/DD-Day")
href_logfile = file("data/logs/[date_string] hrefs.htm")
diary = file("data/logs/[date_string].log")
diary << "[log_end]\n[log_end]\nStarting up. [time2text(world.timeofday, "hh:mm.ss")][log_end]\n---------------------[log_end]"
changelog_hash = md5('html/changelog.html') //used for telling if the changelog has changed recently
if(byond_version < RECOMMENDED_VERSION)
world.log << "Your server's byond version does not meet the recommended requirements for this server. Please update BYOND"
config.post_load()
if(config && config.server_name != null && config.server_suffix && world.port > 0)
// dumb and hardcoded but I don't care~
config.server_name += " #[(world.port % 1000) / 100]"
if(config && config.log_runtime)
log = file("data/logs/runtime/[time2text(world.realtime,"YYYY-MM-DD-(hh-mm-ss)")]-runtime.log")
callHook("startup")
//Emergency Fix
load_mods()
//end-emergency fix
src.update_status()
. = ..()
sleep_offline = 1
// Set up roundstart seed list.
plant_controller = new()
// This is kinda important. Set up details of what the hell things are made of.
populate_material_list()
//Create the asteroid Z-level.
if(config.generate_asteroid)
new /datum/random_map(null,13,32,5,217,223)
// Create autolathe recipes, as above.
populate_lathe_recipes()
// Create robolimbs for chargen.
populate_robolimb_list()
processScheduler = new
master_controller = new /datum/controller/game_controller()
spawn(1)
processScheduler.deferSetupFor(/datum/controller/process/ticker)
processScheduler.setup()
master_controller.setup()
spawn(3000) //so we aren't adding to the round-start lag
if(config.ToRban)
ToRban_autoupdate()
#undef RECOMMENDED_VERSION
return
var/world_topic_spam_protect_ip = "0.0.0.0"
var/world_topic_spam_protect_time = world.timeofday
/world/Topic(T, addr, master, key)
diary << "TOPIC: \"[T]\", from:[addr], master:[master], key:[key][log_end]"
if (T == "ping")
var/x = 1
for (var/client/C)
x++
return x
else if(T == "players")
var/n = 0
for(var/mob/M in player_list)
if(M.client)
n++
return n
else if (T == "admins")
var/n = 0
for (var/client/client in clients)
if (client.holder && client.holder.rights & (R_MOD|R_ADMIN))
n++
return n
else if (T == "cciaa")
var/n = 0
for (var/client/client in clients)
if (client.holder && (client.holder.rights & R_CCIAA) && !(client.holder.rights & R_ADMIN))
n++
return n
else if (T == "gamemode")
return master_mode
else if (T == "who")
var/list/players = list()
for (var/client/C in clients)
players += C.key
return list2params(players)
else if (copytext(T,1,7) == "status")
var/input[] = params2list(T)
var/list/s = list()
s["version"] = game_version
s["mode"] = master_mode
s["respawn"] = config.abandon_allowed
s["enter"] = config.enter_allowed
s["vote"] = config.allow_vote_mode
s["ai"] = config.allow_ai
s["host"] = host ? host : null
// This is dumb, but spacestation13.com's banners break if player count isn't the 8th field of the reply, so... this has to go here.
s["players"] = 0
s["stationtime"] = worldtime2text()
s["roundduration"] = round_duration()
if(input["status"] == "2")
var/list/players = list()
var/list/admins = list()
for(var/client/C in clients)
if(C.holder)
if(C.holder.fakekey)
continue
admins[C.key] = C.holder.rank
players += C.key
s["players"] = players.len
s["playerlist"] = list2params(players)
s["admins"] = admins.len
s["adminlist"] = list2params(admins)
else
var/n = 0
var/admins = 0
for(var/client/C in clients)
if(C.holder)
if(C.holder.fakekey)
continue //so stealthmins aren't revealed by the hub
admins++
s["player[n]"] = C.key
n++
s["players"] = n
s["admins"] = admins
return list2params(s)
else if(T == "manifest")
if (!ticker)
return "Game not started yet!"
var/list/positions = list()
var/list/set_names = list(
"heads" = command_positions,
"sec" = security_positions,
"eng" = engineering_positions,
"med" = medical_positions,
"sci" = science_positions,
"civ" = civilian_positions,
"bot" = nonhuman_positions
)
for(var/datum/data/record/t in data_core.general)
var/name = t.fields["name"]
var/rank = t.fields["rank"]
var/real_rank = make_list_rank(t.fields["real_rank"])
var/department = 0
for(var/k in set_names)
if(real_rank in set_names[k])
if(!positions[k])
positions[k] = list()
positions[k][name] = rank
department = 1
if(!department)
if(!positions["misc"])
positions["misc"] = list()
positions["misc"][name] = rank
for(var/k in positions)
positions[k] = list2params(positions[k]) // converts positions["heads"] = list("Bob"="Captain", "Bill"="CMO") into positions["heads"] = "Bob=Captain&Bill=CMO"
return list2params(positions)
else if(copytext(T,1,5) == "mute")
var/input[] = params2list(T)
var/bad_key = do_topic_spam_protection(addr, input["key"])
if (bad_key)
return bad_key
for (var/client/C in clients)
if (C.ckey == ckey(input["mute"]))
C.mute_discord = !C.mute_discord
switch (C.mute_discord)
if (1)
C << "<b><font color='red'>You have been muted from replying to Discord PMs by [input["admin"]]!</font></b>"
log_and_message_admins("[C] has been muted from Discord PMs by [input["admin"]].")
return "[C.key] is now muted from replying to Discord PMs."
if (0)
C << "<b><font color='red'>You have been unmuted from replying to Discord PMs by [input["admin"]]!</font></b>"
log_and_message_admins("[C] has been unmuted from Discord PMs by [input["admin"]].")
return "[C.key] is now unmuted from replying to Discord PMs."
return "I couldn't find that ckey!"
else if(copytext(T,1,5) == "info")
var/input[] = params2list(T)
var/bad_key = do_topic_spam_protection(addr, input["key"])
if (bad_key)
return bad_key
var/list/search = params2list(input["info"])
var/list/ckeysearch = list()
for(var/text in search)
ckeysearch += ckey(text)
var/list/match = list()
for(var/mob/M in mob_list)
var/strings = list(M.name, M.ckey)
if(M.mind)
strings += M.mind.assigned_role
strings += M.mind.special_role
for(var/text in strings)
if(ckey(text) in ckeysearch)
match[M] += 10 // an exact match is far better than a partial one
else
for(var/searchstr in search)
if(findtext(text, searchstr))
match[M] += 1
var/maxstrength = 0
for(var/mob/M in match)
maxstrength = max(match[M], maxstrength)
for(var/mob/M in match)
if(match[M] < maxstrength)
match -= M
if(!match.len)
return "No matches"
else if(match.len == 1)
var/mob/M = match[1]
var/info = list()
info["key"] = M.key
if (M.client)
var/client/C = M.client
info["discordmuted"] = C.mute_discord ? "Yes" : "No"
info["name"] = M.name == M.real_name ? M.name : "[M.name] ([M.real_name])"
info["role"] = M.mind ? (M.mind.assigned_role ? M.mind.assigned_role : "No role") : "No mind"
var/turf/MT = get_turf(M)
info["loc"] = M.loc ? "[M.loc]" : "null"
info["turf"] = MT ? "[MT] @ [MT.x], [MT.y], [MT.z]" : "null"
info["area"] = MT ? "[MT.loc]" : "null"
info["antag"] = M.mind ? (M.mind.special_role ? M.mind.special_role : "Not antag") : "No mind"
info["hasbeenrev"] = M.mind ? M.mind.has_been_rev : "No mind"
info["stat"] = M.stat
info["type"] = M.type
if(isliving(M))
var/mob/living/L = M
info["damage"] = list2params(list(
oxy = L.getOxyLoss(),
tox = L.getToxLoss(),
fire = L.getFireLoss(),
brute = L.getBruteLoss(),
clone = L.getCloneLoss(),
brain = L.getBrainLoss()
))
else
info["damage"] = "non-living"
info["gender"] = M.gender
return list2params(info)
else
return "Multiple matches found!"
else if(copytext(T,1,9) == "adminmsg")
/*
We got an adminmsg from IRC bot lets split the input then validate the input.
expected output:
1. adminmsg = ckey of person the message is to
2. msg = contents of message, parems2list requires
3. validatationkey = the key the bot has, it should match the gameservers commspassword in it's configuration.
4. sender = the ircnick that send the message.
*/
var/input[] = params2list(T)
var/bad_key = do_topic_spam_protection(addr, input["key"])
if (bad_key)
return bad_key
var/client/C
var/req_ckey = ckey(input["adminmsg"])
for(var/client/K in clients)
if(K.ckey == req_ckey)
C = K
break
if(!C)
return "No client with that name on server"
var/rank = input["rank"]
if(!rank)
rank = "Admin"
var/message = "<font color='red'>Discord-[rank] PM from <b><a href='?discord_msg=[input["sender"]]'>Discord-[input["sender"]]</a></b>: [input["msg"]]</font>"
var/amessage = "<font color='blue'>Discord-[rank] PM from <a href='?discord_msg=[input["sender"]]'>Discord-[input["sender"]]</a> to <b>[key_name(C)]</b> : [input["msg"]]</font>"
C.received_discord_pm = world.time
C.discord_admin = input["sender"]
C << 'sound/effects/adminhelp.ogg'
C << message
for(var/client/A in admins)
if(A != C)
A << amessage
return "Message Successful"
else if(copytext(T,1,6) == "notes")
var/input[] = params2list(T)
var/bad_key = do_topic_spam_protection(addr, input["key"])
if (bad_key)
return bad_key
return show_player_info_discord(ckey(input["notes"]))
else if(copytext(T,1,4) == "age")
var/input[] = params2list(T)
var/bad_key = do_topic_spam_protection(addr, input["key"])
if (bad_key)
return bad_key
var/age = get_player_age(input["age"])
if(isnum(age))
if(age >= 0)
return "[age]"
else
return "Ckey not found"
else
return "Database connection failed or not set up"
else if (copytext(T, 1, 8) == "restart")
var/input[] = params2list(T)
var/bad_key = do_topic_spam_protection(addr, input["key"])
if (bad_key)
log_and_message_admins("Remote restart attempted and stopped. Dumping topic call data.")
log_and_message_admins("TOPIC: \"[T]\", from: [addr], master: [master], key: [key].")
return bad_key
world << "<font size=4 color='#ff2222'>Server restarting by remote command.</font>"
log_and_message_admins("World restart initiated remotely by [input["restart"]].")
feedback_set_details("end_error","remote restart")
if (blackbox)
blackbox.save_all_data_to_sql()
sleep(50)
log_game("Rebooting due to remote command.")
world.Reboot(2)
return "Server successfully restarted."
else if (copytext(T, 1, 9) == "announce")
var/input[] = params2list(T)
var/bad_key = do_topic_spam_protection(addr, input["key"])
if (bad_key)
return bad_key
var/message = replacetext(input["msg"], "\n", "<br>")
world << "<span class=notice><b>[input["announce"] ? input["announce"] : "Administrator"] Announces via Discord:</b><p style='text-indent: 50px'>[message]</p></span>"
log_and_message_admins("[input["announce"]] announced remotely: [input["msg"]].")
return "Announcement successfully sent."
else if (copytext(T, 1, 8) == "faxlist")
var/input[] = params2list(T)
var/bad_key = do_topic_spam_protection(addr, input["key"])
if (bad_key)
return bad_key
if (!ticker)
return "Round hasn't started yet! No faxes to display!"
var/list/faxes = list()
switch (input["faxlist"])
if ("received")
faxes = arrived_faxes
if ("sent")
faxes = sent_faxes
if (!faxes || !faxes.len)
return "No faxes found!"
var/list/output = list()
for (var/i = 1, i <= faxes.len, i++)
var/obj/item/a = faxes[i]
output += "ID [i]"
output["ID [i]"] = a.name ? a.name : "Untitled Fax"
return list2params(output)
else if (copytext(T, 1, 7) == "getfax")
var/input[] = params2list(T)
var/bad_key = do_topic_spam_protection(addr, input["key"])
if (bad_key)
return bad_key
var/list/faxes = list()
switch (input["received"])
if ("received")
faxes = arrived_faxes
if ("sent")
faxes = sent_faxes
if (!faxes || !faxes.len)
return "No faxes found!"
var/fax_id = text2num(input["getfax"])
if (fax_id > faxes.len || fax_id < 1)
return "Invalid fax ID!"
var/output = list()
if (istype(faxes[fax_id], /obj/item/weapon/paper))
var/obj/item/weapon/paper/a = faxes[fax_id]
output["title"] = a.name ? a.name : "Untitled Fax"
var/content = replacetext(a.info, "<br>", "\n")
content = strip_html_properly(content, 0)
output["content"] = content
return list2params(output)
else if (istype(faxes[fax_id], /obj/item/weapon/photo))
return "The fax is a photo. I cannot send images, unfortunately..."
else if (istype(faxes[fax_id], /obj/item/weapon/paper_bundle))
var/obj/item/weapon/paper_bundle/b = faxes[fax_id]
output["title"] = b.name ? b.name : "Untitled Paper Bundle"
if (!b.pages || !b.pages.len)
return "The bundle was empty! How is that even possible?"
var/i = 0
for (var/obj/item/weapon/paper/c in b.pages)
i++
var/content = replacetext(c.info, "<br>", "\n")
content = strip_html_properly(content, 0)
output["content"] += "Page [i]:\n[content]\n\n"
return list2params(output)
return "Unable to recognize the fax type. Cannot send contents!"
/world/Reboot(var/reason)
/*spawn(0)
world << sound(pick('sound/AI/newroundsexy.ogg','sound/misc/apcdestroyed.ogg','sound/misc/bangindonk.ogg')) // random end sounds!! - LastyBatsy
*/
processScheduler.stop()
for(var/client/C in clients)
if(config.server) //if you set a server location in config.txt, it sends you there instead of trying to reconnect to the same world address. -- NeoFite
C << link("byond://[config.server]")
..(reason)
/hook/startup/proc/loadMode()
world.load_mode()
return 1
/world/proc/load_mode()
var/list/Lines = file2list("data/mode.txt")
if(Lines.len)
if(Lines[1])
master_mode = Lines[1]
log_misc("Saved mode is '[master_mode]'")
/world/proc/save_mode(var/the_mode)
var/F = file("data/mode.txt")
fdel(F)
F << the_mode
/hook/startup/proc/loadMOTD()
world.load_motd()
return 1
/world/proc/load_motd()
join_motd = file2text("config/motd.txt")
/proc/load_configuration()
config = new /datum/configuration()
config.load("config/config.txt")
config.load("config/game_options.txt","game_options")
/hook/startup/proc/loadMods()
world.load_mods()
world.load_mentors() // no need to write another hook.
return 1
/world/proc/load_mods()
if(config.admin_legacy_system)
var/text = file2text("config/moderators.txt")
if (!text)
error("Failed to load config/mods.txt")
else
var/list/lines = text2list(text, "\n")
for(var/line in lines)
if (!line)
continue
if (copytext(line, 1, 2) == ";")
continue
var/title = "Moderator"
var/rights = admin_ranks[title]
var/ckey = copytext(line, 1, length(line)+1)
var/datum/admins/D = new /datum/admins(title, rights, ckey)
D.associate(directory[ckey])
/world/proc/load_mentors()
if(config.admin_legacy_system)
var/text = file2text("config/mentors.txt")
if (!text)
error("Failed to load config/mentors.txt")
else
var/list/lines = text2list(text, "\n")
for(var/line in lines)
if (!line)
continue
if (copytext(line, 1, 2) == ";")
continue
var/title = "Mentor"
var/rights = admin_ranks[title]
var/ckey = copytext(line, 1, length(line)+1)
var/datum/admins/D = new /datum/admins(title, rights, ckey)
D.associate(directory[ckey])
/world/proc/update_status()
var/s = ""
if (config && config.server_name)
s += "<b>[config.server_name]</b> &#8212; "
s += "<b>[station_name()]</b>";
s += " ("
s += "<a href=\"[config.forumurl]\">" //Change this to wherever you want the hub to link to.
// s += "[game_version]"
s += "Forums" //Replace this with something else. Or ever better, delete it and uncomment the game version.
s += "</a>"
s += ")"
var/list/features = list()
if(ticker)
if(master_mode)
features += master_mode
else
features += "<b>STARTING</b>"
if (!config.enter_allowed)
features += "closed"
features += config.abandon_allowed ? "respawn" : "no respawn"
if (config && config.allow_vote_mode)
features += "vote"
if (config && config.allow_ai)
features += "AI allowed"
var/n = 0
for (var/mob/M in player_list)
if (M.client)
n++
if (n > 1)
features += "~[n] players"
else if (n > 0)
features += "~[n] player"
/*
is there a reason for this? the byond site shows 'hosted by X' when there is a proper host already.
if (host)
features += "hosted by <b>[host]</b>"
*/
if (!host && config && config.hostedby)
features += "hosted by <b>[config.hostedby]</b>"
if (features)
s += ": [list2text(features, ", ")]"
/* does this help? I do not know */
if (src.status != s)
src.status = s
#define FAILED_DB_CONNECTION_CUTOFF 5
/hook/startup/proc/load_databases()
//Construct the database object from an init file.
dbcon = initialize_database_object("config/dbconfig.txt")
if (!setup_database_connection(dbcon))
world.log << "Your server failed to establish a connection with the feedback database."
else
world.log << "Feedback database connection established."
return 1
/proc/initialize_database_object(var/filename)
if (!filename)
return 0
var/list/data = list("address", "port", "database", "login", "password")
var/list/Lines = file2list(filename)
for (var/t in Lines)
if (!t)
continue
t = trim(t)
if (length(t) == 0)
continue
else if (copytext(t, 1, 2) == "#")
continue
var/pos = findtext(t, " ")
var/name = null
var/value = null
name = lowertext(copytext(t, 1, pos))
value = copytext(t, pos + 1)
if (!name)
continue
if (name in data)
data[name] = value
else
log_misc("Unknown setting while setting up database connection. Filename: '[filename]', value: '[value]'.")
//Validate the data before proceeding.
for (var/d in data)
if (!data[d] || data[d] == null)
return 0
return new/DBConnection(data["address"], data["port"], data["database"], data["login"], data["password"])
/proc/setup_database_connection(var/DBConnection/con)
if (!con)
error("No DBConnection object passed to setup_database_connection().")
return 0
if (con.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 conenct anymore.
return 0
con.Connect()
. = con.IsConnected()
if ( . )
con.failed_connections = 0 //If this connection succeeded, reset the failed connections counter.
else
con.failed_connections++ //If it failed, increase the failed connections counter.
world.log << con.ErrorMsg()
return .
//This proc ensures that the connection to the feedback database (global variable dbcon) is established
/proc/establish_db_connection(var/DBConnection/con)
if (!con)
error("No DBConnection object passed to establish_db_connection() proc.")
return 0
if (con.failed_connections > FAILED_DB_CONNECTION_CUTOFF)
return 0
if (!con.IsConnected())
return setup_database_connection(con)
else
return 1
#undef FAILED_DB_CONNECTION_CUTOFF
/world/proc/do_topic_spam_protection(var/addr, var/key)
if (!config.comms_password || config.comms_password == "")
return "No comms password configured, aborting."
if (key == config.comms_password)
return 0
else
if (world_topic_spam_protect_ip == addr && abs(world_topic_spam_protect_time - world.time) < 50)
spawn(50)
world_topic_spam_protect_time = world.time
return "Bad Key (Throttled)"
world_topic_spam_protect_time = world.time
world_topic_spam_protect_ip = addr
return "Bad Key"