mirror of
https://github.com/ParadiseSS13/Paradise.git
synced 2025-12-28 11:11:52 +00:00
Based on their length, they will now start playing early enough to finish playing right as the server reboots, so long as there's time to do so.
478 lines
14 KiB
Plaintext
478 lines
14 KiB
Plaintext
var/global/datum/global_init/init = new ()
|
|
|
|
/*
|
|
Pre-map initialization stuff should go here.
|
|
*/
|
|
/datum/global_init/New()
|
|
|
|
makeDatumRefLists()
|
|
load_configuration()
|
|
|
|
del(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 510
|
|
|
|
var/global/list/map_transition_config = MAP_TRANSITION_CONFIG
|
|
|
|
/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")
|
|
diaryofmeanpeople = file("data/logs/[date_string] Attack.log")
|
|
diary << "\n\nStarting up. [time2text(world.timeofday, "hh:mm.ss")]\n---------------------"
|
|
diaryofmeanpeople << "\n\nStarting up. [time2text(world.timeofday, "hh:mm.ss")]\n---------------------"
|
|
|
|
if(byond_version < RECOMMENDED_VERSION)
|
|
log_to_dd("Your server's byond version does not meet the recommended requirements for this code. Please update BYOND")
|
|
|
|
if(config && config.log_runtimes)
|
|
log = file("data/logs/runtime/[time2text(world.realtime,"YYYY-MM-DD-(hh-mm-ss)")]-runtime.log")
|
|
|
|
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]"
|
|
|
|
timezoneOffset = text2num(time2text(0,"hh")) * 36000
|
|
|
|
callHook("startup")
|
|
|
|
src.update_status()
|
|
|
|
. = ..()
|
|
|
|
// Create robolimbs for chargen.
|
|
populate_robolimb_list()
|
|
|
|
space_manager.initialize() //Before the MC starts up
|
|
|
|
processScheduler = new
|
|
master_controller = new /datum/controller/game_controller()
|
|
spawn(1)
|
|
processScheduler.deferSetupFor(/datum/controller/process/ticker)
|
|
processScheduler.setup()
|
|
|
|
master_controller.setup()
|
|
sleep_offline = 1
|
|
|
|
#ifdef MAP_NAME
|
|
map_name = "[MAP_NAME]"
|
|
#else
|
|
map_name = "Unknown"
|
|
#endif
|
|
|
|
|
|
|
|
|
|
#undef RECOMMENDED_VERSION
|
|
|
|
return
|
|
|
|
//world/Topic(href, href_list[])
|
|
// to_chat(world, "Received a Topic() call!")
|
|
// to_chat(world, "[href]")
|
|
// for(var/a in href_list)
|
|
// to_chat(world, "[a]")
|
|
// if(href_list["hello"])
|
|
// to_chat(world, "Hello world!")
|
|
// return "Hello world!"
|
|
// to_chat(world, "End of Topic() call.")
|
|
// ..()
|
|
|
|
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]"
|
|
|
|
var/list/input = params2list(T)
|
|
var/key_valid = (config.comms_password && input["key"] == config.comms_password) //no password means no comms, not any password
|
|
|
|
if("ping" in input)
|
|
var/x = 1
|
|
for(var/client/C)
|
|
x++
|
|
return x
|
|
|
|
else if("players" in input)
|
|
var/n = 0
|
|
for(var/mob/M in player_list)
|
|
if(M.client)
|
|
n++
|
|
return n
|
|
|
|
else if("status" in input)
|
|
var/list/s = list()
|
|
var/list/admins = list()
|
|
s["version"] = game_version
|
|
s["mode"] = master_mode
|
|
s["respawn"] = config ? abandon_allowed : 0
|
|
s["enter"] = enter_allowed
|
|
s["vote"] = config.allow_vote_mode
|
|
s["ai"] = config.allow_ai
|
|
s["host"] = host ? host : null
|
|
s["players"] = list()
|
|
s["stationtime"] = worldtime2text()
|
|
var/player_count = 0
|
|
var/admin_count = 0
|
|
|
|
for(var/client/C in clients)
|
|
if(C.holder)
|
|
if(C.holder.fakekey)
|
|
continue //so stealthmins aren't revealed by the hub
|
|
admin_count++
|
|
admins += list(list(C.key, C.holder.rank))
|
|
s["player[player_count]"] = C.key
|
|
player_count++
|
|
s["players"] = player_count
|
|
s["admins"] = admin_count
|
|
s["map_name"] = map_name ? map_name : "Unknown"
|
|
|
|
if(key_valid)
|
|
if(ticker && ticker.mode)
|
|
s["real_mode"] = ticker.mode.name
|
|
|
|
s["security_level"] = get_security_level()
|
|
|
|
if(shuttle_master && shuttle_master.emergency)
|
|
// Shuttle status, see /__DEFINES/stat.dm
|
|
s["shuttle_mode"] = shuttle_master.emergency.mode
|
|
// Shuttle timer, in seconds
|
|
s["shuttle_timer"] = shuttle_master.emergency.timeLeft()
|
|
|
|
for(var/i in 1 to admins.len)
|
|
var/list/A = admins[i]
|
|
s["admin[i - 1]"] = A[1]
|
|
s["adminrank[i - 1]"] = A[2]
|
|
|
|
return list2params(s)
|
|
|
|
else if("manifest" in input)
|
|
var/list/positions = list()
|
|
var/list/set_names = list(
|
|
"heads" = command_positions,
|
|
"sec" = security_positions,
|
|
"eng" = engineering_positions,
|
|
"med" = medical_positions,
|
|
"sci" = science_positions,
|
|
"car" = supply_positions,
|
|
"srv" = service_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 = 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
|
|
|
|
return json_encode(positions)
|
|
|
|
else if("adminmsg" in input)
|
|
/*
|
|
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.
|
|
*/
|
|
if(!key_valid)
|
|
return keySpamProtect(addr)
|
|
|
|
var/client/C
|
|
|
|
for(var/client/K in clients)
|
|
if(K.ckey == input["adminmsg"])
|
|
C = K
|
|
break
|
|
if(!C)
|
|
return "No client with that name on server"
|
|
|
|
var/message = "<font color='red'>IRC-Admin PM from <b><a href='?irc_msg=1'>[C.holder ? "IRC-" + input["sender"] : "Administrator"]</a></b>: [input["msg"]]</font>"
|
|
var/amessage = "<font color='blue'>IRC-Admin PM from <a href='?irc_msg=1'>IRC-[input["sender"]]</a> to <b>[key_name(C)]</b> : [input["msg"]]</font>"
|
|
|
|
C.received_irc_pm = world.time
|
|
C.irc_admin = input["sender"]
|
|
|
|
C << 'sound/effects/adminhelp.ogg'
|
|
to_chat(C, message)
|
|
|
|
for(var/client/A in admins)
|
|
if(A != C)
|
|
to_chat(A, amessage)
|
|
|
|
return "Message Successful"
|
|
|
|
else if("notes" in input)
|
|
/*
|
|
We got a request for notes from the IRC Bot
|
|
expected output:
|
|
1. notes = ckey of person the notes lookup is for
|
|
2. validationkey = the key the bot has, it should match the gameservers commspassword in it's configuration.
|
|
*/
|
|
if(!key_valid)
|
|
return keySpamProtect(addr)
|
|
|
|
return show_player_info_irc(input["notes"])
|
|
|
|
else if("announce" in input)
|
|
if(config.comms_password)
|
|
if(input["key"] != config.comms_password)
|
|
return "Bad Key"
|
|
else
|
|
for(var/client/C in clients)
|
|
to_chat(C, "<span class='announce'>PR: [input["announce"]]</span>")
|
|
|
|
/proc/keySpamProtect(var/addr)
|
|
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"
|
|
|
|
/world/Reboot(var/reason, var/feedback_c, var/feedback_r, var/time)
|
|
if(reason == 1) //special reboot, do none of the normal stuff
|
|
if(usr)
|
|
message_admins("[key_name_admin(usr)] has requested an immediate world restart via client side debugging tools")
|
|
log_admin("[key_name(usr)] has requested an immediate world restart via client side debugging tools")
|
|
spawn(0)
|
|
to_chat(world, "<span class='boldannounce'>Rebooting world immediately due to host request</span>")
|
|
if(config && config.shutdown_on_reboot)
|
|
sleep(0)
|
|
if(shutdown_shell_command)
|
|
shell(shutdown_shell_command)
|
|
del(world)
|
|
return
|
|
else
|
|
return ..(1)
|
|
|
|
var/delay
|
|
if(!isnull(time))
|
|
delay = max(0,time)
|
|
else
|
|
delay = ticker.restart_timeout
|
|
if(ticker.delay_end)
|
|
to_chat(world, "<span class='boldannounce'>An admin has delayed the round end.</span>")
|
|
return
|
|
to_chat(world, "<span class='boldannounce'>Rebooting world in [delay/10] [delay > 10 ? "seconds" : "second"]. [reason]</span>")
|
|
|
|
var/round_end_sound = pick(round_end_sounds)
|
|
var/sound_length = round_end_sounds[round_end_sound]
|
|
if(delay > sound_length) // If there's time, play the round-end sound before rebooting
|
|
spawn(delay - sound_length)
|
|
if(!ticker.delay_end)
|
|
world << round_end_sound
|
|
sleep(delay)
|
|
if(blackbox)
|
|
blackbox.save_all_data_to_sql()
|
|
if(ticker.delay_end)
|
|
to_chat(world, "<span class='boldannounce'>Reboot was cancelled by an admin.</span>")
|
|
return
|
|
feedback_set_details("[feedback_c]","[feedback_r]")
|
|
log_game("<span class='boldannounce'>Rebooting world. [reason]</span>")
|
|
//kick_clients_in_lobby("<span class='boldannounce'>The round came to an end with you in the lobby.</span>", 1)
|
|
|
|
processScheduler.stop()
|
|
|
|
if(config && config.shutdown_on_reboot)
|
|
sleep(0)
|
|
if(shutdown_shell_command)
|
|
shell(shutdown_shell_command)
|
|
del(world)
|
|
return
|
|
else
|
|
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]")
|
|
..(0)
|
|
|
|
#define INACTIVITY_KICK 6000 //10 minutes in ticks (approx.)
|
|
/world/proc/KickInactiveClients()
|
|
var/tmp/sleep_check = 0 // buffer for checking elapsed ticks
|
|
var/tmp/work_length = 2 // number of ticks to run before yielding cpu
|
|
var/tmp/sleep_length = 5 // number of ticks to yield
|
|
var/waiting=1
|
|
|
|
sleep_check = world.timeofday
|
|
|
|
while(waiting)
|
|
waiting = 0
|
|
sleep(INACTIVITY_KICK)
|
|
for(var/client/C in clients)
|
|
if(C.holder) return
|
|
if(C.is_afk(INACTIVITY_KICK))
|
|
if(!istype(C.mob, /mob/dead))
|
|
log_access("AFK: [key_name(C)]")
|
|
to_chat(C, "\red You have been inactive for more than 10 minutes and have been disconnected.")
|
|
del(C)
|
|
if( ((world.timeofday - sleep_check) > work_length) || ((world.timeofday - sleep_check) < 0) )
|
|
sleep(sleep_length)
|
|
sleep_check = world.timeofday
|
|
waiting++
|
|
//#undef INACTIVITY_KICK
|
|
|
|
/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]
|
|
diary << "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")
|
|
config.loadsql("config/dbconfig.txt")
|
|
config.loadoverflowwhitelist("config/ofwhitelist.txt")
|
|
// apply some settings from config..
|
|
|
|
/world/proc/update_status()
|
|
var/s = ""
|
|
|
|
if(config && config.server_name)
|
|
s += "<b>[config.server_name]</b> — "
|
|
|
|
s += "<b>[station_name()]</b>";
|
|
s += " ("
|
|
s += "<a href=\"http://nanotrasen.se/phpBB3/index.php\">" //Change this to wherever you want the hub to link to.
|
|
s += "[game_version]"
|
|
s += "</a>"
|
|
s += ")"
|
|
s += "<br>The Perfect Mix of RP & Action<br>"
|
|
|
|
|
|
|
|
|
|
var/list/features = list()
|
|
|
|
if(ticker)
|
|
if(master_mode)
|
|
features += master_mode
|
|
else
|
|
features += "<b>STARTING</b>"
|
|
|
|
if(!enter_allowed)
|
|
features += "closed"
|
|
|
|
features += 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 += ": [jointext(features, ", ")]"
|
|
|
|
/* does this help? I do not know */
|
|
if(src.status != s)
|
|
src.status = s
|
|
|
|
#define FAILED_DB_CONNECTION_CUTOFF 5
|
|
var/failed_db_connections = 0
|
|
var/failed_old_db_connections = 0
|
|
|
|
/hook/startup/proc/connectDB()
|
|
if(!setup_database_connection())
|
|
log_to_dd("Your server failed to establish a connection with the feedback database.")
|
|
else
|
|
log_to_dd("Feedback database connection established.")
|
|
return 1
|
|
|
|
proc/setup_database_connection()
|
|
|
|
if(failed_db_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
|
|
|
|
if(!dbcon)
|
|
dbcon = new()
|
|
|
|
var/user = sqlfdbklogin
|
|
var/pass = sqlfdbkpass
|
|
var/db = sqlfdbkdb
|
|
var/address = sqladdress
|
|
var/port = sqlport
|
|
|
|
dbcon.Connect("dbi:mysql:[db]:[address]:[port]","[user]","[pass]")
|
|
. = dbcon.IsConnected()
|
|
if( . )
|
|
failed_db_connections = 0 //If this connection succeeded, reset the failed connections counter.
|
|
else
|
|
failed_db_connections++ //If it failed, increase the failed connections counter.
|
|
log_to_dd(dbcon.ErrorMsg())
|
|
|
|
return .
|
|
|
|
//This proc ensures that the connection to the feedback database (global variable dbcon) is established
|
|
proc/establish_db_connection()
|
|
if(failed_db_connections > FAILED_DB_CONNECTION_CUTOFF)
|
|
return 0
|
|
|
|
if(!dbcon || !dbcon.IsConnected())
|
|
return setup_database_connection()
|
|
else
|
|
return 1
|
|
|
|
#undef FAILED_DB_CONNECTION_CUTOFF
|