TGS3 DM changes (#26534)

* TGS3

* Fix line endings

* Enable 16-bit long topics

* Cleans up topic socket usage

* Reduces and refines the IRC status throttle

* Increase the CP status delay to a reasonable amount

* Fixes the testmerge command not having a keyword

* Clean up rebooting a bit

* Get error codes from windows when symlinking fails

* Clean up world announces

* Aborting compilation will kill the DD process

* Add support for changing the project name

* Removes the log page

* Add support for compile cancellation

* Version bumps and docs

* Add merge-pr repo CL command

* Fixes DM cancel command's help message

* Refactor command line to show better formatted help text

* Corrects a typo

* Multi-key game options must be manually edited

* Moving of the server installation from the control panel

* Fix a bug with server moving

* Corrects webclient disposal syntax

* Service now handles the PR Json the game uses properly

* PR listing command for CL

* Fixes reversed testmerge and update help entries

* Windows scheduling to help avoid reboot crashes

* Generalization of chat infastructure

* Brings a file name in line with everything else

* Shutdown exceptions no longer keep the service online

* Enable provider switching on the backend. More thread safety

* Support for switching, password encryption and defaults.

* Removes boilerplate on log writing

* Discord integration

* Update the installer dependencies

* Version bump

* Adds support for getting the latest byond version

* Fixes issue with not being able to set discord channels

* Fix being able to reconnect if chat is disabled

* Extra validation for interface types

* Add the Chat page for the control panel

* Various cleanup

* Set read ACL on the data directory

* Remove redundant namespace usage

* Fixes some buttons not updating the server page

* Future proof against upcoming removal of repo data directory

* Normalize Main declaration

* Update the IRC library

* Enables CTCP

* Removes useless hack

* Logging + enable IRC private messages

* Jobs config

* And finally the maps config

* Save the last config panel visited

* Not gonna use these

* Minor formatting cleanup

* Fixes the chat page not refereshing after clicking reconnect

* Fixes server page not initializing correctly

* Repo now defaults to tgstation github when not found

* Revert "Set read ACL on the data directory"

This reverts commit 15b0021ec51532bca14690a884caa81e811fbc46.

* Design the admin config page

* Prep format the repo's admin_ranks.txt

* Add a negative permissions field

* IRC now RFC quits before disconnecting

* Turns out that fixed the disconnect lag

* Updates the admin ranks config api to work for us

* Done with this config shit

* @optimumtact

* Fix this

* Fix the .wxs

* Try to get md5/sha1 working.

* Add FCIV to appveyor

* Generalize the command class

* Revert "Generalize the command class"

This reverts commit 5c61f6df58d66f0fea4170c8aee0cd5beaa99b5d.

* ITS THE FUCKING SEX NUMBER!!!!

* Final touches

* No THESE are the final touches

* Do not advertise

* Revert "Do not advertise"

This reverts commit f64281d486f9ca27e39f19635ab4deacb2d7e1ac.

* Hopefully the last version bump for long time

* Fix line endings

* Fix default dbconfig.txt

* Fix Discord not checking the right admin channel

* Fix discord listening on ALL channels instead of configured ones

* Package the discord fixes for @JamieH

* Format the testmerge data a little better

* Apply 7 character clamping of commit strings

* Fold admin hard reboot into regular reboot list

* Backward ahelp compatibility with the adminbus bot

* Removes an unecessary semicolon

* Fix stray merge conflict in the config

* Fix Newtonsoft being included by the commandline

* Improve byond update logging

* Chat cleanup

* Fixes some setup non-errors from being displayed

* Repository no longer counts being busy as being valid

* Repository no longer valid while cloning

* Fixes a nudge socket change issue

* Frontend cleanup

* Fixes CanStart race condition

* Fixes compile cancel delays

* Various fixes

* More fixes

* Better readme

* More readme

* Fix a config command description

* Add missing repo status command

* Never delete the backups

* Log the compiles

* More logging

* Stuff

* A thing happened, but I'm not sure what

* Tiny

* INB4 second squash

* Version bump

* Shallow clones should speed things up

* Regular clones

* This is how it's set on travis

* Fix this dupe

* Add backup tag support to backend and command line

* Add some missing repo commands, fix GetHead. Fix reset on branches

* Remove the interfaces for commit and push

* Remove that generate changelog checkbox

* Yeah, that's a misunderstanding

* Make changelog pushing a config, with no way to enable for now

* Add Reset and Recompile option

* Update readme

* Repo page cleanup

* Fixed NudgePort message possible repeating
Fixed Reset and Recompile option always being visible

* Fixed compilation copy not overwriting files
Fixed compiler trying to unecessarily delete the whole A/B folder
Improved game folder initialization speed

* Selectively stage the html folder

* Make the restriction a config

* Switch to using LibGit2Sharp+SSH

* WIP SSH support

* Removes some success chat messages

* Make repo authentication purely file based

* Quick IRC fix

* Should all work in theory...

* More fine grained

* Remove the username thing

* Use the right default email for tgstation-server

* Update the readme

* That's worthy of a version bump

* Speling

* Makes it do as the readme says

* Fix testmerge list not having a scrollbar

* Trying out commit message based deployment [TGSDeploy]

* Whoops

* Testing

* Better

* Version Bump [TGSDeploy]

* Need to set the var at parent scope [TGSDeploy]

* Use the commit message

* Try this [TGSDeploy]

* Try just this

* This maybe? [TGSDeploy]

* >like [TGSDeploy]

* Wildcard, bitches [TGSDeploy]

* Saner title [TGSDeploy]

* Readme update

* This should loin ya

* Fix it [TGSDeploy]

* Readme, cleanup, and doc updates

* Improve DD crash handling

* Version bump [TGSDeploy]

* TGS3 Config Changes

* Line endings

* Map config code change

* Missed a few

* Security and Visibility selectors for the Server page

* Fixes OCD

* Fax it

* Fixes

* Version bump [TGSDeploy]

* eh

* The word comment has lost it's meaning to me

* This is a terrible name but whatever

* Support config changes

* This is part of the code so it belongs with the code

* ExportService now has a return value

* Copying of the logs dir during compile for #27674

* Version bump [TGSDeploy]

* Removes some uneedful

* Moves daemon config to BYOND folder, much safer

* Fix a config comment translation miss

* Fix project settings issue

* Fix config apply button not showing up after repo clone

* Fix anchoring for Backup Tags: label

* Version Bump [TGSDeploy]

* Nudge port only listens while server is running

* Fix some instances of the control panel crashing when the service stops

* Add start menu shortcuts

* Remove the actual server

* Remove appveyor

* Fix gitignore

* And this

* Readd HTTPS_Get for now

* Readd legacy support

* Fix

* Fix this stuff

* Last thing

* Line endings

* Final touches

* Dat newline

* More stuff

* Where'd that go?

* Real final touches
This commit is contained in:
Jordan Brown
2017-06-01 15:16:07 -04:00
committed by AnturK
parent 6af9400120
commit 916d1b4cd7
16 changed files with 208 additions and 56 deletions

View File

@@ -0,0 +1,27 @@
#define REBOOT_MODE_NORMAL 0
#define REBOOT_MODE_HARD 1
#define REBOOT_MODE_SHUTDOWN 2
#define IRC_STATUS_THROTTLE 50
//keep these in sync with TGS3
#define SERVICE_WORLD_PARAM "server_service"
#define SERVICE_PR_TEST_JSON "..\\..\\prtestjob.json"
#define SERVICE_CMD_HARD_REBOOT "hard_reboot"
#define SERVICE_CMD_GRACEFUL_SHUTDOWN "graceful_shutdown"
#define SERVICE_CMD_WORLD_ANNOUNCE "world_announce"
#define SERVICE_CMD_IRC_STATUS "irc_status"
#define SERVICE_CMD_ADMIN_MSG "adminmsg"
#define SERVICE_CMD_NAME_CHECK "namecheck"
#define SERVICE_CMD_ADMIN_WHO "adminwho"
//#define SERVICE_CMD_PARAM_KEY //defined in __compile_options.dm
#define SERVICE_CMD_PARAM_COMMAND "command"
#define SERVICE_CMD_PARAM_MESSAGE "message"
#define SERVICE_CMD_PARAM_TARGET "target"
#define SERVICE_CMD_PARAM_SENDER "sender"
#define SERVICE_REQUEST_KILL_PROCESS "killme"
#define SERVICE_REQUEST_IRC_BROADCAST "irc"
#define SERVICE_REQUEST_IRC_ADMIN_CHANNEL_MESSAGE "send2irc"

View File

@@ -1344,7 +1344,7 @@ GLOBAL_DATUM_INIT(dview_mob, /mob/dview, new)
//kevinz000 if you touch this I will hunt you down
GLOBAL_VAR_INIT(valid_HTTPSGet, FALSE)
GLOBAL_PROTECT(valid_HTTPSGet)
/proc/HTTPSGet(url)
/proc/HTTPSGet(url) //tgs2 support
if(findtext(url, "\""))
GLOB.valid_HTTPSGet = FALSE

View File

@@ -69,7 +69,5 @@
#error You need version 511 or higher
#endif
#define SERVICE_CMD_PARAM_KEY "serviceCommsKey"
#ifndef SERVERTOOLS
#define SERVERTOOLS 0
#endif

View File

@@ -263,6 +263,8 @@
var/mice_roundstart = 10 // how many wire chewing rodents spawn at roundstart.
var/irc_announce_new_game = FALSE
/datum/configuration/New()
gamemode_cache = typecacheof(/datum/game_mode,TRUE)
for(var/T in gamemode_cache)
@@ -427,7 +429,7 @@
popup_admin_pm = 1
if("allow_holidays")
allow_holidays = 1
if("useircbot")
if("useircbot") //tgs2 support
useircbot = 1
if("ticklag")
var/ticklag = text2num(value)
@@ -540,6 +542,8 @@
error_silence_time = text2num(value)
if("error_msg_delay")
error_msg_delay = text2num(value)
if("irc_announce_new_game")
irc_announce_new_game = TRUE
else
GLOB.config_error_log << "Unknown setting in configuration: '[name]'"

View File

@@ -2,16 +2,17 @@
var/originmastercommit
var/commit
var/list/testmerge = list()
var/has_pr_details = FALSE //example data in a testmerge entry when this is true: https://api.github.com/repositories/3234987/pulls/22586
var/has_pr_details = FALSE //tgs2 support
var/date
/datum/getrev/New()
if(SERVERTOOLS && fexists("..\\prtestjob.lk"))
if(fexists(SERVICE_PR_TEST_JSON))
testmerge = json_decode(file2text(SERVICE_PR_TEST_JSON))
else if(!world.RunningService() && fexists("../prtestjob.lk")) //tgs2 support
var/list/tmp = world.file2list("..\\prtestjob.lk")
for(var/I in tmp)
if(I)
testmerge |= I
log_world("Running /tg/ revision:")
var/list/logs = world.file2list(".git/logs/HEAD")
if(logs)
@@ -27,12 +28,16 @@
log_world(commit)
for(var/line in testmerge)
if(line)
if(world.RunningService())
var/tmcommit = testmerge[line]["commit"]
log_world("Test merge active of PR #[line] commit [tmcommit]")
SSblackbox.add_details("testmerged_prs","[line]|[tmcommit]")
else //tgs2 support
log_world("Test merge active of PR #[line]")
SSblackbox.add_details("testmerged_prs","[line]")
log_world("Based off origin/master commit [originmastercommit]")
else
log_world(originmastercommit)
/datum/getrev/proc/DownloadPRDetails()
if(!config.githubrepoid)
if(testmerge.len)
@@ -66,8 +71,11 @@
return ""
. = header ? "The following pull requests are currently test merged:<br>" : ""
for(var/line in testmerge)
var/details = ""
if(has_pr_details)
var/cm = testmerge[line]["commit"]
var/details
if(world.RunningService())
details = ": '" + html_encode(testmerge[line]["title"]) + "' by " + html_encode(testmerge[line]["author"]) + " at commit " + html_encode(copytext(cm, 1, min(length(cm), 7)))
else if(has_pr_details) //tgs2 support
details = ": '" + html_encode(testmerge[line]["title"]) + "' by " + html_encode(testmerge[line]["user"]["login"])
. += "<a href='[config.githuburl]/pull/[line]'>#[line][details]</a><br>"

View File

@@ -423,6 +423,8 @@
return
var/list/options = list("Regular Restart", "Hard Restart (No Delay/Feeback Reason)", "Hardest Restart (No actions, just reboot)")
if(world.RunningService())
options += "Service Restart (Force restart DD)";
var result = input(usr, "Select reboot method", "World Reboot", options[1]) as null|anything in options
if(result)
SSblackbox.add_details("admin_verb","Reboot World") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc!
@@ -433,6 +435,9 @@
world.Reboot()
if("Hardest Restart (No actions, just reboot)")
world.Reboot(fast_track = TRUE)
if("Service Restart (Force restart DD)")
GLOB.reboot_mode = REBOOT_MODE_HARD
world.ServiceReboot()
/datum/admins/proc/end_round()
set category = "Server"

View File

@@ -183,7 +183,7 @@ GLOBAL_DATUM_INIT(ahelp_tickets, /datum/admin_help_tickets, new)
to_chat(C, "<span class='adminnotice'>PM to-<b>Admins</b>: [name]</span>")
//send it to irc if nobody is on and tell us how many were on
var/admin_number_present = send2irc_adminless_only(initiator_ckey, name)
var/admin_number_present = send2irc_adminless_only("#[id] [initiator_ckey]", 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>")
@@ -567,9 +567,10 @@ GLOBAL_DATUM_INIT(ahelp_tickets, /datum/admin_help_tickets, new)
/proc/send2irc(msg,msg2)
if(config.useircbot)
if(world.RunningService())
world.ExportService("[SERVICE_REQUEST_IRC_ADMIN_CHANNEL_MESSAGE] [msg] | [msg2]")
else if(config.useircbot)
shell("python nudge.py [msg] [msg2]")
return
/proc/send2otherserver(source,msg,type = "Ahelp")
if(config.cross_allowed)

View File

@@ -59,11 +59,11 @@
if (!msg)
message_admins("[key_name_admin(src)] has cancelled their reply to [key_name(C, 0, 0)]'s admin help.")
return
cmd_admin_pm(whom, msg, AH)
cmd_admin_pm(whom, msg)
//takes input from cmd_admin_pm_context, cmd_admin_pm_panel or /client/Topic and sends them a PM.
//Fetching a message if needed. src is the sender and C is the target client
/client/proc/cmd_admin_pm(whom, msg, datum/admin_help/AH)
/client/proc/cmd_admin_pm(whom, msg)
if(prefs.muted & MUTE_ADMINHELP)
to_chat(src, "<font color='red'>Error: Admin-PM: You are unable to use admin PM-s (muted).</font>")
return
@@ -144,9 +144,9 @@
if(irc)
to_chat(src, "<font color='blue'>PM to-<b>Admins</b>: [rawmsg]</font>")
admin_ticket_log(src, "<font color='red'>Reply PM from-<b>[key_name(src, TRUE, TRUE)] to <i>IRC</i>: [keywordparsedmsg]</font>")
var/datum/admin_help/AH = admin_ticket_log(src, "<font color='red'>Reply PM from-<b>[key_name(src, TRUE, TRUE)] to <i>IRC</i>: [keywordparsedmsg]</font>")
ircreplyamount--
send2irc("Reply: [ckey]",rawmsg)
send2irc("[AH ? "#[AH.id] " : ""]Reply: [ckey]", rawmsg)
else
if(recipient.holder)
if(holder) //both are admins
@@ -221,42 +221,44 @@
var/datum/admin_help/ticket = C ? C.current_ticket : GLOB.ahelp_tickets.CKey2ActiveTicket(target)
var/compliant_msg = trim(lowertext(msg))
var/unhandled = FALSE
var/irc_tagged = "[sender](IRC)"
switch(compliant_msg)
if("ticket close")
var/list/splits = splittext(compliant_msg, " ")
if(splits.len && splits[1] == "ticket")
if(splits.len < 2)
return "Usage: ticket <close|resolve|icissue|reject>"
switch(splits[2])
if("close")
if(ticket)
ticket.Close(irc_tagged)
return "Ticket #[ticket.id] successfully closed"
if("ticket resolve")
if("resolve")
if(ticket)
ticket.Resolve(irc_tagged)
return "Ticket #[ticket.id] successfully resolved"
if("ticket ic")
if("icissue")
if(ticket)
ticket.ICIssue(irc_tagged)
return "Ticket #[ticket.id] successfully marked as IC issue"
if("ticket reject")
if("reject")
if(ticket)
ticket.Reject(irc_tagged)
return "Ticket #[ticket.id] successfully rejected"
else
unhandled = TRUE
if(!unhandled)
return "Ticket could not be found"
return "Usage: ticket <close|resolve|icissue|reject>"
return "Error: Ticket could not be found"
var/static/stealthkey
var/adminname = config.showircname ? irc_tagged : "Administrator"
if(!C)
return "No client"
return "Error: No client"
if(!stealthkey)
stealthkey = GenIrcStealthKey()
msg = sanitize(copytext(msg,1,MAX_MESSAGE_LEN))
if(!msg)
return "No message"
return "Error: No message"
message_admins("IRC message from [sender] to [key_name_admin(C)] : [msg]")
log_admin_private("IRC PM: [sender] -> [key_name(C)] : [msg]")
@@ -276,8 +278,6 @@
return "Message Successful"
/proc/GenIrcStealthKey()
var/num = (rand(0,1000))
var/i = 0

View File

@@ -0,0 +1,22 @@
#!/usr/bin/env python3
import sys
import pickle
import socket
def pack():
data = sys.argv[1]
nudge(str.encode(data))
def nudge(data):
s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
with open('config/server_to_tool_bridge_port.txt', 'r') as myfile:
portstr=myfile.read().replace('\n', '').strip()
s.connect(("localhost", int(portstr)))
s.send(data)
s.close()
if __name__ == "__main__" and len(sys.argv) > 1:
pack()

View File

@@ -0,0 +1,79 @@
GLOBAL_VAR_INIT(reboot_mode, REBOOT_MODE_NORMAL) //if the world should request the service to kill it at reboot
GLOBAL_PROTECT(reboot_mode)
/world/proc/RunningService()
return params[SERVICE_WORLD_PARAM]
/world/proc/ExportService(command)
return shell("python code/modules/server_tools/nudge.py \"[command]\"") == 0
/world/proc/IRCBroadcast(msg)
ExportService("[SERVICE_REQUEST_IRC_BROADCAST] [msg]")
//called at the exact moment the world is supposed to reboot
/world/proc/ServiceReboot()
switch(GLOB.reboot_mode)
if(REBOOT_MODE_HARD)
to_chat(src, "<span class='boldannounce'>Hard reboot triggered, you will automatically reconnect...</span>")
log_world("Sending shutdown request!");
sleep(1) //flush the buffers
ExportService(SERVICE_REQUEST_KILL_PROCESS)
if(REBOOT_MODE_SHUTDOWN)
to_chat(src, "<span class='boldannounce'>The server is shutting down...</span>")
log_world("Deleting world")
qdel(src)
/world/proc/ServiceCommand(list/params)
var/sCK = RunningService()
var/their_sCK = params[SERVICE_CMD_PARAM_KEY]
if(!their_sCK || their_sCK != sCK)
return "Invalid comms key!";
var/command = params[SERVICE_CMD_PARAM_COMMAND]
if(!command)
return "No command!"
var/static/last_irc_status = 0
switch(command)
if(SERVICE_CMD_HARD_REBOOT)
if(GLOB.reboot_mode != REBOOT_MODE_HARD)
GLOB.reboot_mode = REBOOT_MODE_HARD
log_world("Hard reboot requested by service")
message_admins("The world will hard reboot at the end of the game. Requested by service.")
SSblackbox.set_val("service_hard_restart", TRUE)
if(SERVICE_CMD_GRACEFUL_SHUTDOWN)
if(GLOB.reboot_mode != REBOOT_MODE_SHUTDOWN)
GLOB.reboot_mode = REBOOT_MODE_SHUTDOWN
log_world("Shutdown requested by service")
message_admins("The world will shutdown at the end of the game. Requested by service.")
SSblackbox.set_val("service_shutdown", TRUE)
if(SERVICE_CMD_WORLD_ANNOUNCE)
var/msg = params["message"]
if(!istext(msg) || !msg)
return "No message set!"
to_chat(src, "<span class='boldannounce'>[html_encode(msg)]</span>")
return "SUCCESS"
if(SERVICE_CMD_IRC_STATUS)
var/rtod = REALTIMEOFDAY
if(rtod - last_irc_status < IRC_STATUS_THROTTLE)
return
last_irc_status = rtod
var/list/adm = get_admin_counts()
var/list/allmins = adm["total"]
var/status = "Admins: [allmins.len] (Active: [english_list(adm["present"])] AFK: [english_list(adm["afk"])] Stealth: [english_list(adm["stealth"])] Skipped: [english_list(adm["noflags"])]). "
status += "Players: [GLOB.clients.len] (Active: [get_active_player_count(0,1,0)]). Mode: [SSticker.mode ? SSticker.mode.name : "Not started"]."
return status
if(SERVICE_CMD_ADMIN_MSG)
return IrcPm(params[SERVICE_CMD_PARAM_TARGET], params[SERVICE_CMD_PARAM_MESSAGE], params[SERVICE_CMD_PARAM_SENDER])
if(SERVICE_CMD_NAME_CHECK)
log_admin("IRC Name Check: [params[SERVICE_CMD_PARAM_SENDER]] on [params[SERVICE_CMD_PARAM_TARGET]]")
message_admins("IRC name checking on [params[SERVICE_CMD_PARAM_TARGET]] from [params[SERVICE_CMD_PARAM_SENDER]]")
return keywords_lookup(params[SERVICE_CMD_PARAM_TARGET], 1)
if(SERVICE_CMD_ADMIN_WHO)
return ircadminwho()
else
return "Unknown command: [command]"

View File

@@ -28,6 +28,7 @@
SetupLogs()
if(!RunningService()) //tgs2 support
GLOB.revdata.DownloadPRDetails()
load_motd()
@@ -41,6 +42,9 @@
Master.Initialize(10, FALSE)
if(config.irc_announce_new_game)
IRCBroadcast("New round starting on [SSmapping.config.map_name]!")
/world/proc/SetupExternalRSC()
#if (PRELOAD_RSC == 0)
external_rsc_urls = world.file2list("config/external_rsc_urls.txt","\n")
@@ -86,14 +90,14 @@
if(GLOB.round_id)
log_game("Round ID: [GLOB.round_id]")
#define IRC_STATUS_THROTTLE 50
/world/Topic(T, addr, master, key)
if(config && config.log_world_topic)
GLOB.world_game_log << "TOPIC: \"[T]\", from:[addr], master:[master], key:[key]"
var/list/input = params2list(T)
if(input[SERVICE_CMD_PARAM_KEY])
return ServiceCommand(input)
var/key_valid = (global.comms_allowed && input["key"] == global.comms_key)
var/static/last_irc_status = 0
if("ping" in input)
var/x = 1
@@ -108,8 +112,9 @@
n++
return n
else if("ircstatus" in input)
if(world.time - last_irc_status < IRC_STATUS_THROTTLE)
else if("ircstatus" in input) //tgs2 support
var/static/last_irc_status = 0
if(world.time - last_irc_status < 50)
return
var/list/adm = get_admin_counts()
var/list/allmins = adm["total"]
@@ -177,20 +182,20 @@
if(input["crossmessage"] == "News_Report")
minor_announce(input["message"], "Breaking Update From [input["message_sender"]]")
else if("adminmsg" in input)
else if("adminmsg" in input) //tgs2 support
if(!key_valid)
return "Bad Key"
else
return IrcPm(input["adminmsg"],input["msg"],input["sender"])
else if("namecheck" in input)
else if("namecheck" in input) //tgs2 support
if(!key_valid)
return "Bad Key"
else
log_admin("IRC Name Check: [input["sender"]] on [input["namecheck"]]")
message_admins("IRC name checking on [input["namecheck"]] from [input["sender"]]")
return keywords_lookup(input["namecheck"],1)
else if("adminwho" in input)
else if("adminwho" in input) //tgs2 support
if(!key_valid)
return "Bad Key"
else
@@ -216,6 +221,7 @@
C.AnnouncePR(final_composed)
/world/Reboot(reason = 0, fast_track = FALSE)
ServiceReboot() //handles alternative actions if necessary
if (reason || fast_track) //special reboot, do none of the normal stuff
if (usr)
log_admin("[key_name(usr)] Has requested an immediate world restart via client side debugging tools")

View File

@@ -59,6 +59,7 @@
#include "code\__DEFINES\robots.dm"
#include "code\__DEFINES\role_preferences.dm"
#include "code\__DEFINES\say.dm"
#include "code\__DEFINES\server_tools.dm"
#include "code\__DEFINES\shuttles.dm"
#include "code\__DEFINES\sight.dm"
#include "code\__DEFINES\sound.dm"
@@ -2003,6 +2004,7 @@
#include "code\modules\ruins\objects_and_mobs\sin_ruins.dm"
#include "code\modules\security_levels\keycard_authentication.dm"
#include "code\modules\security_levels\security_levels.dm"
#include "code\modules\server_tools\server_tools.dm"
#include "code\modules\shuttle\arrivals.dm"
#include "code\modules\shuttle\assault_pod.dm"
#include "code\modules\shuttle\computer.dm"

0
bot/minibot.py → tools/minibot/minibot.py Executable file → Normal file
View File

0
bot/nudge.py → tools/minibot/nudge.py Executable file → Normal file
View File