catchup with master

This commit is contained in:
S34NW
2021-06-01 16:49:48 +01:00
parent 273d0347e9
commit bb972cb110
44 changed files with 425 additions and 145 deletions
+14
View File
@@ -277,6 +277,7 @@ CREATE TABLE `player` (
`fupdate` smallint(4) DEFAULT '0',
`parallax` tinyint(1) DEFAULT '8',
`byond_date` DATE DEFAULT NULL,
`2fa_status` ENUM('DISABLED','ENABLED_IP','ENABLED_ALWAYS') NOT NULL DEFAULT 'DISABLED' COLLATE 'utf8mb4_general_ci',
PRIMARY KEY (`id`),
UNIQUE KEY `ckey` (`ckey`),
KEY `lastseen` (`lastseen`),
@@ -585,3 +586,16 @@ CREATE TABLE `round` (
`station_name` VARCHAR(80) NULL,
PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
--
-- Table structure for table `2fa_secrets`
--
CREATE TABLE `2fa_secrets` (
`ckey` VARCHAR(50) NOT NULL COLLATE 'utf8mb4_general_ci',
`secret` VARCHAR(64) NOT NULL COLLATE 'utf8mb4_general_ci',
`date_setup` DATETIME NOT NULL DEFAULT current_timestamp(),
`last_time` DATETIME NULL DEFAULT NULL,
PRIMARY KEY (`ckey`) USING BTREE
)
COLLATE='utf8mb4_general_ci' ENGINE=InnoDB;
+13
View File
@@ -276,6 +276,7 @@ CREATE TABLE `SS13_player` (
`fupdate` smallint(4) DEFAULT '0',
`parallax` tinyint(1) DEFAULT '8',
`byond_date` DATE DEFAULT NULL,
`2fa_status` ENUM('DISABLED','ENABLED_IP','ENABLED_ALWAYS') NOT NULL DEFAULT 'DISABLED' COLLATE 'utf8mb4_general_ci',
PRIMARY KEY (`id`),
UNIQUE KEY `ckey` (`ckey`),
KEY `lastseen` (`lastseen`),
@@ -582,3 +583,15 @@ CREATE TABLE `SS13_round` (
`station_name` VARCHAR(80) NULL,
PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
--
-- Table structure for table `2fa_secrets`
--
CREATE TABLE `SS13_2fa_secrets` (
`ckey` VARCHAR(50) NOT NULL COLLATE 'utf8mb4_general_ci',
`secret` VARCHAR(64) NOT NULL COLLATE 'utf8mb4_general_ci',
`date_setup` DATETIME NOT NULL DEFAULT current_timestamp(),
`last_time` DATETIME NULL DEFAULT NULL,
PRIMARY KEY (`ckey`) USING BTREE
)
COLLATE='utf8mb4_general_ci' ENGINE=InnoDB;
+13
View File
@@ -0,0 +1,13 @@
# Updates DB from 23 to 24 -AffectedArc07
# Add new column to player table for 2FA status
ALTER TABLE `player` ADD COLUMN `2fa_status` ENUM('DISABLED','ENABLED_IP','ENABLED_ALWAYS') NOT NULL DEFAULT 'DISABLED' AFTER `byond_date`;
# Create new table for 2FA tokens
CREATE TABLE `2fa_secrets` (
`ckey` VARCHAR(50) NOT NULL COLLATE 'utf8mb4_general_ci',
`secret` VARCHAR(64) NOT NULL COLLATE 'utf8mb4_general_ci',
`date_setup` DATETIME NOT NULL DEFAULT current_timestamp(),
`last_time` DATETIME NULL DEFAULT NULL,
PRIMARY KEY (`ckey`) USING BTREE
)
COLLATE='utf8mb4_general_ci' ENGINE=InnoDB;
+3
View File
@@ -11168,6 +11168,9 @@
icon_state = "tube1";
tag = "icon-tube1 (WEST)"
},
/obj/structure/chair/comfy/shuttle{
dir = 4
},
/turf/simulated/floor/mineral/plastitanium/red,
/area/shuttle/specops)
"QL" = (
+1 -1
View File
@@ -363,7 +363,7 @@
#define INVESTIGATE_BOMB "bombs"
// The SQL version required by this version of the code
#define SQL_VERSION 23
#define SQL_VERSION 24
// Vending machine stuff
#define CAT_NORMAL 1
+9
View File
@@ -105,3 +105,12 @@
#define PARALLAX_MED 4
#define PARALLAX_HIGH 8
#define PARALLAX_INSANE 16
// 2FA Defines. These are the same as the schema DB enums //
/// Client has 2FA disabled
#define _2FA_DISABLED "DISABLED"
/// Client will be prompted for 2FA on IP changes
#define _2FA_ENABLED_IP "ENABLED_IP"
/// Client will be prompted for 2FA always
#define _2FA_ENABLED_ALWAYS "ENABLED_ALWAYS"
+1
View File
@@ -514,6 +514,7 @@
text = replacetext(text, "\[logo\]", "&ZeroWidthSpace;<img src = ntlogo.png>")
text = replacetext(text, "\[time\]", "[station_time_timestamp()]") // TO DO
text = replacetext(text, "\[date\]", "[GLOB.current_date_string]")
text = replacetext(text, "\[station\]", "[SSmapping.map_datum.fluff_name]")
if(!no_font)
if(P)
text = "<font face=\"[deffont]\" color=[P ? P.colour : "black"]>[text]</font>"
+5 -1
View File
@@ -99,7 +99,7 @@
Remember to update _globalvars/traits.dm if you're adding/removing/renaming traits.
*/
//mob traits
//***** MOB TRAITS *****//
#define TRAIT_BLIND "blind"
#define TRAIT_MUTE "mute"
#define TRAIT_DEAF "deaf"
@@ -155,6 +155,10 @@ Remember to update _globalvars/traits.dm if you're adding/removing/renaming trai
/// Blowing kisses actually does damage to the victim
#define TRAIT_KISS_OF_DEATH "kiss_of_death"
//***** ITEM TRAITS *****//
/// Show what machine/door wires do when held.
#define TRAIT_SHOW_WIRE_INFO "show_wire_info"
//
// common trait sources
#define TRAIT_GENERIC "generic"
+1 -1
View File
@@ -2072,7 +2072,7 @@ GLOBAL_DATUM_INIT(dview_mob, /mob/dview, new)
return _list
/// Waits at a line of code until X is true
#define UNTIL(X) while(!(X)) stoplag()
#define UNTIL(X) while(!(X)) sleep(world.tick_lag)
// Check if the source atom contains another atom
/atom/proc/contains(atom/location)
+6 -1
View File
@@ -57,7 +57,12 @@ GLOBAL_LIST_INIT(traits_by_type, list(
"TRAIT_NOGERMS" = TRAIT_NOGERMS,
"TRAIT_NODECAY" = TRAIT_NODECAY,
"TRAIT_NOEXAMINE" = TRAIT_NOEXAMINE,
"TRAIT_NOPAIN" = TRAIT_NOPAIN)))
"TRAIT_NOPAIN" = TRAIT_NOPAIN
),
/obj/item = list(
"TRAIT_SHOW_WIRE_INFO" = TRAIT_SHOW_WIRE_INFO
)
))
/// value -> trait name, generated on use from trait_by_type global
GLOBAL_LIST(trait_name_map)
+5
View File
@@ -278,6 +278,9 @@
// Enable map voting
var/map_voting_enabled = FALSE
// 2FA auth host
var/_2fa_auth_host = null
/datum/configuration/New()
for(var/T in subtypesof(/datum/game_mode))
var/datum/game_mode/M = T
@@ -775,6 +778,8 @@
auto_profile = TRUE
if("enable_map_voting")
map_voting_enabled = TRUE
if("2fa_host")
_2fa_auth_host = value
else
log_config("Unknown setting in configuration: '[name]'")
+4 -1
View File
@@ -12,8 +12,11 @@ SUBSYSTEM_DEF(http)
/// Total requests the SS has processed in a round
var/total_requests
/datum/controller/subsystem/http/Initialize(start_timeofday)
/datum/controller/subsystem/http/PreInit()
. = ..()
rustg_create_async_http_client() // Open the door
/datum/controller/subsystem/http/Initialize(start_timeofday)
active_async_requests = list()
return ..()
+8
View File
@@ -935,6 +935,14 @@ GLOBAL_LIST_INIT(all_supply_groups, list(SUPPLY_EMERGENCY,SUPPLY_SECURITY,SUPPLY
containername = "surgery crate"
access = ACCESS_MEDICAL
/datum/supply_packs/medical/gloves
name = "Nitrile Glove Crate"
contains = list(/obj/item/clothing/gloves/color/latex/nitrile,
/obj/item/clothing/gloves/color/latex/nitrile,
/obj/item/clothing/gloves/color/latex/nitrile,
/obj/item/clothing/gloves/color/latex/nitrile)
cost = 50
containername = "nitrile glove crate"
//////////////////////////////////////////////////////////////////////////////
//////////////////////////// Science /////////////////////////////////////////
+4 -5
View File
@@ -209,7 +209,7 @@
/**
* Proc called to determine if the user can see wire define information, such as "Contraband", "Door Bolts", etc.
*
* If the user is an admin, or has a multitool which reveals wire information in their active hand, the proc returns TRUE.
* If the user is an admin, or has an item which reveals wire information in their active hand, the proc returns TRUE.
*
* Arguments:
* * user - the mob who is interacting with the wires.
@@ -217,10 +217,9 @@
/datum/wires/proc/can_see_wire_info(mob/user)
if(user.can_admin_interact())
return TRUE
else if(istype(user.get_active_hand(), /obj/item/multitool))
var/obj/item/multitool/M = user.get_active_hand()
if(M.shows_wire_information)
return TRUE
var/obj/item/held_item = user.get_active_hand()
if(istype(held_item) && HAS_TRAIT(held_item, TRAIT_SHOW_WIRE_INFO))
return TRUE
return FALSE
/**
+52 -95
View File
@@ -607,53 +607,75 @@ NOTE: there are two lists of areas in the end of this file: centcom and station
icon_state = "fpmaint"
/area/maintenance/fpmaint
name = "EVA Maintenance"
name = "Fore-Port Maintenance"
icon_state = "fpmaint"
/area/maintenance/fpmaint2
name = "Arrivals North Maintenance"
name = "Fore-Port Secondary Maintenance"
icon_state = "fpmaint"
/area/maintenance/fsmaint
name = "Dormitory Maintenance"
name = "Fore-Starboard Maintenance"
icon_state = "fsmaint"
/area/maintenance/fsmaint2
name = "Bar Maintenance"
name = "Fore-Starboard Secondary Maintenance"
icon_state = "fsmaint"
/area/maintenance/asmaint
name = "Medbay Maintenance"
name = "Aft-Starboard Maintenance"
icon_state = "asmaint"
/area/maintenance/asmaint2
name = "Science Maintenance"
name = "Aft-Starboard Secondary Maintenance"
icon_state = "asmaint"
/area/maintenance/apmaint
name = "Cargo Maintenance"
name = "Aft-Port Maintenance"
icon_state = "apmaint"
/area/maintenance/apmaint2
name = "Aft-Port Secondary Maintenance"
icon_state = "apmaint"
/area/maintenance/maintcentral
name = "Bridge Maintenance"
name = "Central Maintenance"
icon_state = "maintcentral"
/area/maintenance/maintcentral2
name = "Central Secondary Maintenance"
icon_state = "maintcentral"
/area/maintenance/fore
name = "Fore Maintenance"
icon_state = "fmaint"
/area/maintenance/fore2
name = "Fore Secondary Maintenance"
icon_state = "fmaint"
/area/maintenance/aft
name = "Aft Maintenance"
icon_state = "amaint"
/area/maintenance/aft2
name = "Aft Secondary Maintenance"
icon_state = "amaint"
/area/maintenance/starboard
name = "Starboard Maintenance"
icon_state = "smaint"
/area/maintenance/starboard2
name = "Starboard Secondary Maintenance"
icon_state = "smaint"
/area/maintenance/port
name = "Locker Room Maintenance"
name = "Port Maintenance"
icon_state = "pmaint"
/area/maintenance/aft
name = "Engineering Maintenance"
icon_state = "amaint"
/area/maintenance/port2
name = "Port Secondary Maintenance"
icon_state = "pmaint"
/area/maintenance/storage
name = "Atmospherics Maintenance"
icon_state = "green"
@@ -677,18 +699,18 @@ NOTE: there are two lists of areas in the end of this file: centcom and station
/area/maintenance/electrical
name = "Electrical Maintenance"
icon_state = "yellow"
icon_state = "elec"
/area/maintenance/abandonedbar
name = "Maintenance Bar"
icon_state = "yellow"
icon_state = "oldbar"
power_equip = 0
power_light = 0
power_environ = 0
/area/maintenance/electrical_shop
name ="Electronics Den"
icon_state = "yellow"
icon_state = "elec"
/area/maintenance/gambling_den
name = "Gambling Den"
@@ -696,7 +718,7 @@ NOTE: there are two lists of areas in the end of this file: centcom and station
/area/maintenance/consarea
name = "Alternate Construction Area"
icon_state = "yellow"
icon_state = "construction"
//Hallway
@@ -836,7 +858,7 @@ NOTE: there are two lists of areas in the end of this file: centcom and station
/area/comms
name = "\improper Communications Relay"
icon_state = "tcomsatcham"
icon_state = "tcomms"
sound_environment = SOUND_AREA_STANDARD_STATION
/area/server
@@ -846,11 +868,11 @@ NOTE: there are two lists of areas in the end of this file: centcom and station
/area/ntrep
name = "\improper Nanotrasen Representative's Office"
icon_state = "bluenew"
icon_state = "ntrep"
/area/blueshield
name = "\improper Blueshield's Office"
icon_state = "blueold"
icon_state = "blueshield"
/area/centcomdocks
name = "\improper Central Command Docks"
@@ -919,7 +941,7 @@ NOTE: there are two lists of areas in the end of this file: centcom and station
/area/crew_quarters/bar
name = "\improper Bar"
icon_state = "bar"
icon_state = "barstation"
sound_environment = SOUND_AREA_WOODFLOOR
/area/crew_quarters/bar/atrium
@@ -1048,45 +1070,6 @@ NOTE: there are two lists of areas in the end of this file: centcom and station
/area/holodeck/source_knightarena
name = "\improper Holodeck - Knight Arena"
//Embassies
/area/embassy/
name = "\improper Embassy Hallway"
/area/embassy/tajaran
name = "\improper Tajaran Embassy"
icon_state = "tajaran"
/area/embassy/skrell
name = "\improper Skrell Embassy"
icon_state = "skrell"
/area/embassy/unathi
name = "\improper Unathi Embassy"
icon_state = "unathi"
/area/embassy/kidan
name = "\improper Kidan Embassy"
icon_state = "kidan"
/area/embassy/diona
name = "\improper Diona Embassy"
icon_state = "diona"
/area/embassy/slime
name = "\improper Slime Person Embassy"
icon_state = "slime"
/area/embassy/grey
name = "\improper Grey Embassy"
icon_state = "grey"
/area/embassy/vox
name = "\improper Vox Embassy"
icon_state = "vox"
//Engineering
/area/engine
ambientsounds = ENGINEERING_SOUNDS
@@ -1310,15 +1293,11 @@ NOTE: there are two lists of areas in the end of this file: centcom and station
/area/medical/robotics
name = "Robotics"
icon_state = "medresearch"
icon_state = "research"
/area/medical/research
name = "Medical Research"
icon_state = "medresearch"
/area/medical/research_shuttle_dock
name = "\improper Research Shuttle Dock"
icon_state = "medresearch"
name = "Research Division"
icon_state = "research"
/area/medical/virology
name = "Virology"
@@ -1459,10 +1438,6 @@ NOTE: there are two lists of areas in the end of this file: centcom and station
name = "Security Equipment Storage"
icon_state = "securityequipmentstorage"
/area/security/interrogationhallway
name = "\improper Interrogation Hallway"
icon_state = "interrogationhall"
/area/security/courtroomdandp
name = "\improper Courtroom Defense and Prosecution"
icon_state = "seccourt"
@@ -1505,7 +1480,7 @@ NOTE: there are two lists of areas in the end of this file: centcom and station
icon_state = "secarmory"
/area/security/securehallway
name = "\improper Security Secure Hallway"
name = "\improper Brig Secure Hallway"
icon_state = "securehall"
/area/security/hos
@@ -2061,45 +2036,27 @@ NOTE: there are two lists of areas in the end of this file: centcom and station
/area/tcommsat/chamber
name = "\improper Telecoms Central Compartment"
icon_state = "tcomsatcham"
icon_state = "tcomms"
// These areas are needed for MetaStation's AI sat
/area/turret_protected/tcomsat
name = "\improper Telecoms Satellite"
icon_state = "tcomsatlob"
ambientsounds = list('sound/ambience/ambisin2.ogg', 'sound/ambience/signal.ogg', 'sound/ambience/signal.ogg', 'sound/ambience/ambigen10.ogg')
/area/turret_protected/tcomfoyer
name = "\improper Telecoms Foyer"
icon_state = "tcomsatentrance"
ambientsounds = list('sound/ambience/ambisin2.ogg', 'sound/ambience/signal.ogg', 'sound/ambience/signal.ogg', 'sound/ambience/ambigen10.ogg')
/area/turret_protected/tcomwest
name = "\improper Telecoms West Wing"
icon_state = "tcomsatwest"
icon_state = "tcomms"
ambientsounds = list('sound/ambience/ambisin2.ogg', 'sound/ambience/signal.ogg', 'sound/ambience/signal.ogg', 'sound/ambience/ambigen10.ogg')
/area/turret_protected/tcomeast
name = "\improper Telecoms East Wing"
icon_state = "tcomsateast"
icon_state = "tcomms"
ambientsounds = list('sound/ambience/ambisin2.ogg', 'sound/ambience/signal.ogg', 'sound/ambience/signal.ogg', 'sound/ambience/ambigen10.ogg')
/area/tcommsat/computer
name = "\improper Telecoms Control Room"
icon_state = "tcomsatcomp"
icon_state = "tcomms"
sound_environment = SOUND_AREA_MEDIUM_SOFTFLOOR
/area/tcommsat/server
name = "\improper Telecoms Server Room"
icon_state = "tcomsatcham"
/area/tcommsat/lounge
name = "\improper Telecoms Lounge"
icon_state = "tcomsatlounge"
/area/tcommsat/powercontrol
name = "\improper Telecoms Power Control"
icon_state = "tcomsatwest"
icon_state = "tcomms"
// Away Missions
/area/awaymission
+6 -2
View File
@@ -366,6 +366,7 @@ structure_check() searches for nearby cultist structures required for the invoca
return
var/sacrifice_fulfilled
var/worthless = FALSE
var/datum/game_mode/gamemode = SSticker.mode
if(offering.mind)
GLOB.sacrificed += offering.mind
@@ -379,13 +380,16 @@ structure_check() searches for nearby cultist structures required for the invoca
if(sacrifice_fulfilled)
to_chat(M, "<span class='cultlarge'>\"Yes! This is the one I desire! You have done well.\"</span>")
else
if(ishuman(offering) || isrobot(offering))
if(ishuman(offering) && offering.mind.offstation_role && offering.mind.special_role != SPECIAL_ROLE_ERT) //If you try it on a ghost role, you get nothing
to_chat(M, "<span class='cultlarge'>\"This soul is of no use to either of us.\"</span>")
worthless = TRUE
else if(ishuman(offering) || isrobot(offering))
to_chat(M, "<span class='cultlarge'>\"I accept this sacrifice.\"</span>")
else
to_chat(M, "<span class='cultlarge'>\"I accept this meager sacrifice.\"</span>")
playsound(offering, 'sound/misc/demon_consume.ogg', 100, TRUE)
if((ishuman(offering) || isrobot(offering) || isbrain(offering)) && offering.mind)
if(((ishuman(offering) || isrobot(offering) || isbrain(offering)) && offering.mind) && !worthless)
var/obj/item/soulstone/stone = new /obj/item/soulstone(get_turf(src))
stone.invisibility = INVISIBILITY_MAXIMUM // So it's not picked up during transfer_soul()
stone.transfer_soul("FORCE", offering, user) // If it cannot be added
+1 -1
View File
@@ -382,7 +382,7 @@ GLOBAL_VAR(bomb_set)
else if(off_station == 2)
to_chat(world, "<b>A nuclear device was set off, but the device was not on the station!</b>")
else
to_chat(world, "<b>The station was destoyed by the nuclear blast!</b>")
to_chat(world, "<b>The station was destroyed by the nuclear blast!</b>")
SSticker.mode.station_was_nuked = (off_station < 2) //offstation==1 is a draw. the station becomes irradiated and needs to be evacuated.
//kinda shit but I couldn't get permission to do what I wanted to do.
+7 -3
View File
@@ -95,6 +95,10 @@
to_chat(user, "<span class='cultlarge'>\"Come now, do not capture your fellow's soul.\"</span>")
return ..()
if(M.mind.offstation_role && M.mind.special_role != SPECIAL_ROLE_ERT)
to_chat(user, "<span class='warning'>This being's soul seems worthless. Not even the stone will absorb it.</span>")
return ..()
if(optional)
if(!M.ckey)
to_chat(user, "<span class='warning'>They have no soul!</span>")
@@ -312,9 +316,9 @@
"Wraith" = /mob/living/simple_animal/hostile/construct/wraith,
"Artificer" = /mob/living/simple_animal/hostile/construct/builder)
/// Custom construct icons for different cults
var/list/construct_icons = list("Juggernaut" = image(icon = 'icons/mob/mob.dmi', icon_state = SSticker.cultdat.get_icon("juggernaut")),
"Wraith" = image(icon = 'icons/mob/mob.dmi', icon_state = SSticker.cultdat.get_icon("wraith")),
"Artificer" = image(icon = 'icons/mob/mob.dmi', icon_state = SSticker.cultdat.get_icon("builder")))
var/list/construct_icons = list("Juggernaut" = image(icon = 'icons/mob/cult.dmi', icon_state = SSticker.cultdat.get_icon("juggernaut")),
"Wraith" = image(icon = 'icons/mob/cult.dmi', icon_state = SSticker.cultdat.get_icon("wraith")),
"Artificer" = image(icon = 'icons/mob/cult.dmi', icon_state = SSticker.cultdat.get_icon("builder")))
if(shade)
var/construct_choice = show_radial_menu(user, shell, construct_icons, custom_check = CALLBACK(src, .proc/radial_check, user), require_near = TRUE)
+14 -3
View File
@@ -24,7 +24,6 @@
toolspeed = 1
tool_behaviour = TOOL_MULTITOOL
hitsound = 'sound/weapons/tap.ogg'
var/shows_wire_information = FALSE // shows what a wire does if set to TRUE
var/obj/machinery/buffer // simple machine buffer for device linkage
/obj/item/multitool/proc/IsBufferA(typepath)
@@ -95,7 +94,10 @@
/obj/item/multitool/ai_detect/admin
desc = "Used for pulsing wires to test which to cut. Not recommended by doctors. Has a strange tag that says 'Grief in Safety'" //What else should I say for a meme item?
track_delay = 5
shows_wire_information = TRUE
/obj/item/multitool/ai_detect/admin/Initialize(mapload)
. = ..()
ADD_TRAIT(src, TRAIT_SHOW_WIRE_INFO, ROUNDSTART_TRAIT)
/obj/item/multitool/ai_detect/admin/multitool_detect()
var/turf/our_turf = get_turf(src)
@@ -112,6 +114,12 @@
desc = "Optimised and stripped-down version of a regular multitool."
toolspeed = 0.5
/obj/item/multitool/cyborg/drone
/obj/item/multitool/cyborg/drone/Initialize(mapload)
. = ..()
ADD_TRAIT(src, TRAIT_SHOW_WIRE_INFO, ROUNDSTART_TRAIT) // Drones are linked to the station
/obj/item/multitool/abductor
name = "alien multitool"
desc = "An omni-technological interface."
@@ -119,4 +127,7 @@
icon_state = "multitool"
toolspeed = 0.1
origin_tech = "magnets=5;engineering=5;abductor=3"
shows_wire_information = TRUE
/obj/item/multitool/abductor/Initialize(mapload)
. = ..()
ADD_TRAIT(src, TRAIT_SHOW_WIRE_INFO, ROUNDSTART_TRAIT)
@@ -62,11 +62,21 @@
origin_tech = "materials=5;engineering=4;abductor=3"
random_color = FALSE
/obj/item/wirecutters/abductor/Initialize(mapload)
. = ..()
ADD_TRAIT(src, TRAIT_SHOW_WIRE_INFO, ROUNDSTART_TRAIT)
/obj/item/wirecutters/cyborg
name = "wirecutters"
desc = "This cuts wires."
toolspeed = 0.5
/obj/item/wirecutters/cyborg/drone
/obj/item/wirecutters/cyborg/drone/Initialize(mapload)
. = ..()
ADD_TRAIT(src, TRAIT_SHOW_WIRE_INFO, ROUNDSTART_TRAIT) // Drones are linked to the station
/obj/item/wirecutters/power
name = "jaws of life"
desc = "A set of jaws of life, the magic of science has managed to fit it down into a device small enough to fit in a tool belt. It's fitted with a cutting head."
+10 -1
View File
@@ -35,10 +35,19 @@
QDEL_NULL(showpiece)
return ..()
/obj/structure/displaycase/emag_act(mob/user)
if(!emagged)
to_chat(user, "<span class='warning'>You override the ID lock on [src].</span>")
trigger_alarm()
emagged = TRUE
/obj/structure/displaycase/examine(mob/user)
. = ..()
if(alert)
. += "<span class='notice'>Hooked up with an anti-theft system.</span>"
if(emagged)
. += "<span class='warning'>The ID lock has been shorted out.</span>"
if(showpiece)
. += "<span class='notice'>There's [showpiece] inside.</span>"
if(trophy_message)
@@ -100,7 +109,7 @@
/obj/structure/displaycase/attackby(obj/item/I, mob/user, params)
if(I.GetID() && !broken && openable)
if(allowed(user))
if(allowed(user) || emagged)
to_chat(user, "<span class='notice'>You [open ? "close":"open"] [src].</span>")
toggle_lock(user)
else
+44 -1
View File
@@ -1,5 +1,5 @@
//Blocks an attempt to connect before even creating our client datum thing.
/world/IsBanned(key, address, computer_id, type, check_ipintel = TRUE)
/world/IsBanned(key, address, computer_id, type, check_ipintel = TRUE, check_2fa = TRUE)
if(!key || !address || !computer_id)
log_adminwarn("Failed Login (invalid data): [key] [address]-[computer_id]")
@@ -44,6 +44,49 @@
return list("reason"="using proxy or vpn", "desc"="\nReason: Proxies/VPNs are not allowed here. [mistakemessage]")
// If 2FA is enabled, makes sure they were authed within the last minute
if(check_2fa && config._2fa_auth_host)
// First see if they exist at all
var/datum/db_query/check_query = SSdbcore.NewQuery("SELECT 2fa_status, ip FROM [format_table_name("player")] WHERE ckey=:ckey", list("ckey" = ckey(key)))
if(!check_query.warn_execute())
message_admins("Failed to do a DB 2FA check for [key]. You have been warned.")
qdel(check_query)
return
// If a row is returned, the player exists
var/_2fa_enabled = FALSE
var/always_check = FALSE
var/last_ip
if(check_query.NextRow())
if(check_query.item[1] != _2FA_DISABLED)
_2fa_enabled = TRUE
if(check_query.item[1] == _2FA_ENABLED_ALWAYS)
always_check = TRUE
last_ip = check_query.item[2]
qdel(check_query)
// If client has 2FA enabled, and they either:
// Have it set to always check, or their IP is different
if(_2fa_enabled && (always_check || (address != last_ip)))
// They have 2FA enabled, lets make sure they have authed within the last minute
var/datum/db_query/verify_query = SSdbcore.NewQuery("SELECT ckey FROM [format_table_name("2fa_secrets")] WHERE (last_time BETWEEN NOW() - INTERVAL 1 MINUTE AND NOW()) AND ckey=:ckey LIMIT 1", list(
"ckey" = ckey(key)
))
if(!verify_query.warn_execute())
message_admins("Failed to do a DB 2FA check for [key]. You have been warned.")
qdel(verify_query)
return
if(!verify_query.NextRow())
// If no row was returned, fail 2FA
qdel(verify_query)
return list("reason"="2fa check failed", "desc"="You have 2FA enabled but did not properly authenticate.")
qdel(verify_query)
if(config.ban_legacy_system)
//Ban Checking
. = CheckBan(ckey(key), computer_id, address)
@@ -133,7 +133,6 @@
area_whitelist = list(/area/medical/virology,
/area/toxins,
/area/medical/research,
/area/medical/research_shuttle_dock,
/area/crew_quarters/hor,
/area/maintenance/asmaint2)
@@ -164,7 +163,6 @@
TYPE_PSYCHIC)
area_blacklist = list(/area/toxins,
/area/medical/research,
/area/medical/research_shuttle_dock,
/area/crew_quarters/hor,
/area/maintenance/asmaint2,
/area/teleporter,
+11 -9
View File
@@ -358,28 +358,30 @@ GLOBAL_DATUM_INIT(_preloader, /datum/dmm_suite/preloader, new())
/datum/dmm_suite/proc/readlist(text, delimiter = ",")
var/list/to_return = list()
var/position
var/delimiter_position
var/old_position = 1
do
// find next delimiter that is not within "..."
position = find_next_delimiter_position(text, old_position, delimiter)
delimiter_position = find_next_delimiter_position(text, old_position, delimiter)
// check if this is a simple variable (as in list(var1, var2)) or an associative one (as in list(var1="foo", var2=7))
var/equal_position = findtext(text, "=", old_position, position)
var/equal_position = findtext(text, "=", old_position, delimiter_position)
var/trim_left = trim_text(copytext(text, old_position, (equal_position ? equal_position : position)), 1) // the name of the variable, must trim quotes to build a BYOND compliant associatives list
old_position = position + 1
// Take the left value of the association or just the value if it isn't an association
var/left_value = copytext(text, old_position, (equal_position ? equal_position : delimiter_position))
old_position = delimiter_position + 1
if(equal_position) // associative var, so do the association
var/trim_right = trim_text(copytext(text, equal_position + 1, position)) // the content of the variable
left_value = trim_text(left_value, TRUE) // the name of the variable, must trim quotes to build a BYOND compliant associatives list
var/trim_right = trim_text(copytext(text, equal_position + 1, delimiter_position)) // the content of the variable
to_return[trim_left] = parse_value(trim_right)
to_return[left_value] = parse_value(trim_right)
else// simple var
to_return += parse_value(trim_left)
to_return += parse_value(trim_text(left_value)) // Don't trim the quotes
while(position != 0)
while(delimiter_position != 0)
return to_return
/**
+131
View File
@@ -0,0 +1,131 @@
// This is in its own file as it has so much stuff to contend with
/client/proc/edit_2fa()
if(!config._2fa_auth_host)
alert(usr, "This server does not have 2FA enabled.")
return
// Client does not have 2FA enabled. Set it up.
if(prefs._2fa_status == _2FA_DISABLED)
// Get us an auth token
var/datum/http_response/qrcr = wrap_http_get("[config._2fa_auth_host]/generateQR?ckey=[ckey]")
// If this fails, shits gone bad
if(qrcr.errored)
alert(usr, "Something has gone VERY wrong ingame. Please inform the server host.\nError details: [qrcr.error]")
return
if(qrcr.status_code != 200)
alert(usr, "2FA QR code generation returned a non-200 code. Please inform the server host.\nError code: [qrcr.status_code]\nError details: [qrcr.body]")
return
var/list/data = json_decode(qrcr.body)
var/qr_img_src = data["qr_image"]
var/datum/browser/B = new(usr, "2fa_qrc", "2FA QR Code", 600, 400)
var/title_text = "<p>Below is a QR code to scan inside your authenticator app to generate 2FA codes. Please verify it before closing this window. (Behind this window is a text box)</p>"
var/img_data = "<div style=\"text-align:center;\"><img src=\"[qr_img_src]\"></div>"
B.set_content("[title_text][img_data]")
B.open(FALSE)
var/entered_code = input(usr, "Please enter a code from your auth app. Failure to enter the code correctly will abort 2FA setup.", "2FA Validation")
if(!entered_code)
// Cleanup so they can start again
var/datum/db_query/dbq = SSdbcore.NewQuery("DELETE FROM [format_table_name("2fa_secrets")] WHERE ckey=:ckey", list("ckey" = ckey))
dbq.warn_execute()
alert(usr, "2FA Setup aborted!")
B.close()
return
var/datum/http_response/vr = wrap_http_get("[config._2fa_auth_host]/validateCode?ckey=[ckey]&code=[entered_code]")
// If this fails, shits gone bad
if(vr.errored)
alert(usr, "Something has gone VERY wrong ingame. Please inform the server host.\nError details: [vr.error]")
B.close()
return
if(vr.status_code != 200)
// Cleanup so they can start again
var/datum/db_query/dbq = SSdbcore.NewQuery("DELETE FROM [format_table_name("2fa_secrets")] WHERE ckey=:ckey", list("ckey" = ckey))
dbq.warn_execute()
// See if its unauthorised. I used 400 for that dont at me
if(vr.status_code == 400)
alert(usr, "Invalid code entered. 2FA Setup aborted!")
B.close()
else
alert(usr, "2FA validation returned a non-200 code. Please inform the server host.\nError code: [vr.status_code]\nError details: [vr.body]")
B.close()
return
// If we are here, they authed successfully
alert(usr, "Congratulations. 2FA is now setup properly for your account. In preferences, you can change whether you want it to ask for a code on every connection or only when your IP changes")
B.close()
// Default to IP change only
prefs._2fa_status = _2FA_ENABLED_IP
prefs.save_preferences(src)
prefs.ShowChoices(usr)
return
// If we are here, they just want to change the mode
var/option = alert(usr, "Would you like to change 2FA mode or disable it entirely?", "2FA Mode", "Enable (Always)", "Enable (On IP Change)", "Deactivate")
switch(option)
if("Enable (Always)")
prefs._2fa_status = _2FA_ENABLED_ALWAYS
prefs.save_preferences(src)
prefs.ShowChoices(usr)
if("Enable (On IP Change)")
prefs._2fa_status = _2FA_ENABLED_IP
prefs.save_preferences(src)
prefs.ShowChoices(usr)
if("Deactivate")
var/confirm = alert(usr, "Are you SURE you want to deactivate 2FA?", "WARNING", "Yes", "No")
if(confirm != "Yes")
return
// Prompt them for a code to make sure they know what they are doing
var/entered_code = input(usr, "Please enter a code from your auth app", "2FA Validation")
if(!entered_code)
alert(usr, "2FA deactivation aborted!")
return
var/datum/http_response/vr = wrap_http_get("[config._2fa_auth_host]/validateCode?ckey=[ckey]&code=[entered_code]")
// If this fails, shits gone bad
if(vr.errored)
alert(usr, "Something has gone VERY wrong ingame. Please inform the server host.\nError details: [vr.error]")
return
if(vr.status_code != 200)
// See if its unauthorised. I used 400 for that dont at me
if(vr.status_code == 400)
alert(usr, "Invalid code entered. 2FA deactivation aborted!")
else
alert(usr, "2FA validation returned non-200 code. Please inform the server host.\nError code: [vr.status_code]\nError details: [vr.body]")
return
// If we are here, they authed properly
var/datum/db_query/dbq = SSdbcore.NewQuery("DELETE FROM [format_table_name("2fa_secrets")] WHERE ckey=:ckey", list("ckey" = ckey))
dbq.warn_execute()
prefs._2fa_status = _2FA_DISABLED
prefs.save_preferences(src)
prefs.ShowChoices(usr)
alert(usr, "2FA disabled successfully")
/datum/preferences/proc/_2fastatus_to_text()
switch(_2fa_status)
if(_2FA_DISABLED)
return "Disabled"
if(_2FA_ENABLED_IP)
return "Enabled (Will prompt on IP changes)"
if(_2FA_ENABLED_ALWAYS)
return "Enabled (Will prompt every time)"
// Proc to wrap HTTP requests properly, without needing SShttp firing
/proc/wrap_http_get(url)
var/datum/http_request/req = new()
req.prepare(RUSTG_HTTP_METHOD_GET, url)
req.begin_async()
// Check if we are complete
UNTIL(req.is_complete())
var/datum/http_response/res = req.into_response()
return res
@@ -205,6 +205,8 @@ GLOBAL_LIST_INIT(special_role_times, list( //minimum age (in days) for accounts
var/gear_tab = "General"
// Parallax
var/parallax = PARALLAX_HIGH
/// 2FA status
var/_2fa_status = _2FA_DISABLED
/// Do we want to force our runechat colour to be white?
var/force_white_runechat = FALSE
@@ -448,6 +450,7 @@ GLOBAL_LIST_INIT(special_role_times, list( //minimum age (in days) for accounts
// LEFT SIDE OF THE PAGE
dat += "<table><tr><td width='340px' height='300px' valign='top'>"
dat += "<h2>General Settings</h2>"
dat += "<b>2FA Setup:</b> <a href='?_src_=prefs;preference=edit_2fa'>[_2fastatus_to_text()]</a><br>"
if(user.client.holder)
dat += "<b>Adminhelp sound:</b> <a href='?_src_=prefs;preference=hear_adminhelps'><b>[(sound & SOUND_ADMINHELP)?"On":"Off"]</b></a><br>"
dat += "<b>AFK Cryoing:</b> <a href='?_src_=prefs;preference=afk_watch'>[(toggles2 & PREFTOGGLE_2_AFKWATCH) ? "Yes" : "No"]</a><br>"
@@ -2144,6 +2147,11 @@ GLOBAL_LIST_INIT(special_role_times, list( //minimum age (in days) for accounts
if(parent && parent.mob && parent.mob.hud_used)
parent.mob.hud_used.update_parallax_pref()
if("edit_2fa")
// Do this async so we arent holding up a topic() call
INVOKE_ASYNC(user.client, /client.proc/edit_2fa)
return // We return here to avoid focus being lost
ShowChoices(user)
return 1
@@ -16,7 +16,8 @@
clientfps,
atklog,
fuid,
parallax
parallax,
2fa_status
FROM [format_table_name("player")]
WHERE ckey=:ckey"}, list(
"ckey" = C.ckey
@@ -45,6 +46,7 @@
atklog = text2num(query.item[14])
fuid = text2num(query.item[15])
parallax = text2num(query.item[16])
_2fa_status = query.item[17]
qdel(query)
@@ -93,7 +95,8 @@
volume_mixer=:volume_mixer,
lastchangelog=:lastchangelog,
clientfps=:clientfps,
parallax=:parallax
parallax=:parallax,
2fa_status=:_2fa_status
WHERE ckey=:ckey"}, list(
// OH GOD THE PARAMETERS
"ooccolour" = ooccolor,
@@ -111,7 +114,8 @@
"lastchangelog" = lastchangelog,
"clientfps" = clientfps,
"parallax" = parallax,
"ckey" = C.ckey
"_2fa_status" = _2fa_status,
"ckey" = C.ckey,
)
)
@@ -141,7 +141,7 @@
user.visible_message("[user] places \the [src] against [M]'s chest and listens attentively.", "You place \the [src] against [M]'s chest...")
var/obj/item/organ/internal/H = M.get_int_organ(/obj/item/organ/internal/heart)
var/obj/item/organ/internal/L = M.get_int_organ(/obj/item/organ/internal/lungs)
if((H && M.pulse) || (L && !HAS_TRAIT(M, TRAIT_NOBREATH)))
if(M.pulse && (H || (L && !HAS_TRAIT(M, TRAIT_NOBREATH))))
var/color = "notice"
if(H)
var/heart_sound
+1 -1
View File
@@ -66,7 +66,7 @@
if(istype(M))
if(isnewplayer(M)) // People in the lobby screen; only have their ckey as a name.
continue
if(isobserver(M))
if(isobserver(M) && M.client)
ghosts += list(serialized)
else if(M.mind == null)
npcs += list(serialized)
@@ -692,8 +692,8 @@
/obj/item/screwdriver/cyborg,
/obj/item/wrench/cyborg,
/obj/item/crowbar/cyborg,
/obj/item/wirecutters/cyborg,
/obj/item/multitool/cyborg,
/obj/item/wirecutters/cyborg/drone,
/obj/item/multitool/cyborg/drone,
/obj/item/lightreplacer/cyborg,
/obj/item/gripper,
/obj/item/matter_decompiler,
@@ -449,6 +449,7 @@
var/dat = "<html><body><center>"
dat += "Round Duration: [round(hours)]h [round(mins)]m<br>"
dat += "<b>The station alert level is: [get_security_level_colors()]</b><br>"
if(SSshuttle.emergency.mode >= SHUTTLE_ESCAPE)
dat += "<font color='red'><b>The station has been evacuated.</b></font><br>"
+2
View File
@@ -249,6 +249,8 @@
/obj/machinery/power/port_gen/pacman/proc/overheat()
overheating++
if(overheating > 60)
message_admins("Pacman overheated at [ADMIN_JMP(loc)]. Last touched by: [fingerprintslast ? "[fingerprintslast]" : "*null*"].")
log_game("Pacman overheated at [COORD(loc)]. Last touched by: [fingerprintslast ? "[fingerprintslast]" : "*null*"].")
explode()
/obj/machinery/power/port_gen/pacman/explode()
@@ -188,7 +188,7 @@
return
if(being_used || !ismob(M))
return
if(!isanimal(M) || M.ckey) //only works on animals that aren't player controlled
if(!isanimal(M) || M.mind) //only works on animals that aren't player controlled
to_chat(user, "<span class='warning'>[M] is already too intelligent for this to work!</span>")
return ..()
if(M.stat)
@@ -173,3 +173,18 @@ GLOBAL_DATUM_INIT(security_announcement_down, /datum/announcement/priority/secur
return SEC_LEVEL_EPSILON
if("delta")
return SEC_LEVEL_DELTA
/proc/get_security_level_colors()
switch(GLOB.security_level)
if(SEC_LEVEL_GREEN)
return "<font color='limegreen'>Green</font>"
if(SEC_LEVEL_BLUE)
return "<font color='dodgerblue'>Blue</font>"
if(SEC_LEVEL_RED)
return "<font color='red'>Red</font>"
if(SEC_LEVEL_GAMMA)
return "<font color='gold'>Gamma</font>"
if(SEC_LEVEL_EPSILON)
return "<font color='blueviolet'>Epsilon</font>"
if(SEC_LEVEL_DELTA)
return "<font color='orangered'>Delta</font>"
+3
View File
@@ -474,3 +474,6 @@ ENABLE_AUTO_PROFILER
## Uncomment to enable map voting
#ENABLE_MAP_VOTING
## Host for a custom 2FA server. Comment to disable. Do not use a trailing slash or https.
#2FA_HOST http://127.0.0.1:8080
+1 -1
View File
@@ -18,7 +18,7 @@ FEEDBACK_DATABASE feedback
## This value must be set to the version of the paradise schema in use.
## If this value does not match, the SQL database will not be loaded and an error will be generated.
## Roundstart will be delayed.
DB_VERSION 23
DB_VERSION 24
## Prefix to be added to the name of every table, older databases will require this be set to erro_
## If left out defaults to erro_ for legacy reasons, if you want no table prefix, give a blank prefix rather then comment out
+1 -1
View File
@@ -185,7 +185,7 @@ var/list/chatResources = list(
var/list/row = connectionHistory[i]
if(!row || row.len < 3 || !(row["ckey"] && row["compid"] && row["ip"]))
return
if(world.IsBanned(key=row["ckey"], address=row["ip"], computer_id=row["compid"], type=null, check_ipintel=FALSE))
if(world.IsBanned(key=row["ckey"], address=row["ip"], computer_id=row["compid"], type=null, check_ipintel=FALSE, check_2fa=FALSE))
found = row
break
CHECK_TICK
Binary file not shown.

Before

Width:  |  Height:  |  Size: 34 KiB

After

Width:  |  Height:  |  Size: 30 KiB

+1
View File
@@ -1345,6 +1345,7 @@
#include "code\modules\buildmode\submodes\save.dm"
#include "code\modules\buildmode\submodes\throwing.dm"
#include "code\modules\buildmode\submodes\variable_edit.dm"
#include "code\modules\client\2fa.dm"
#include "code\modules\client\asset_cache.dm"
#include "code\modules\client\client_defines.dm"
#include "code\modules\client\client_procs.dm"
+1 -1
View File
@@ -7,6 +7,6 @@
"glob": "^7.1.4",
"source-map": "^0.7.3",
"stacktrace-parser": "^0.1.7",
"ws": "^7.1.2"
"ws": "^7.4.6"
}
}
@@ -125,7 +125,7 @@ const AtmosControlMapView = (_properties, context) => {
return (
<Box height="526px" mb="0.5rem" overflow="hidden">
<NanoMap onZoom={v => setZoom(v)}>
{alarms.filter(a => a.z === 1).map(aa => (
{alarms.filter(a => a.z === 2).map(aa => (
// The AA means air alarm, and nothing else
<NanoMap.Marker
key={aa.ref}
File diff suppressed because one or more lines are too long
+4 -4
View File
@@ -6263,10 +6263,10 @@ ws@^6.0.0:
dependencies:
async-limiter "~1.0.0"
ws@^7.1.2:
version "7.2.3"
resolved "https://registry.yarnpkg.com/ws/-/ws-7.2.3.tgz#a5411e1fb04d5ed0efee76d26d5c46d830c39b46"
integrity sha512-HTDl9G9hbkNDk98naoR/cHDws7+EyYMOdL1BmjsZXRUjf7d+MficC4B7HLUPlSiho0vg+CWKrGIt/VJBd1xunQ==
ws@^7.4.6:
version "7.4.6"
resolved "https://registry.yarnpkg.com/ws/-/ws-7.4.6.tgz#5654ca8ecdeee47c33a9a4bf6d28e2be2980377c"
integrity sha512-YmhHDO4MzaDLB+M9ym/mDA5z0naX8j7SIlT8f8z+I0VtzsRbekxEutHSme7NPS2qE8StCYQNUnfWdXta/Yu85A==
xregexp@^4.3.0:
version "4.3.0"
+1 -1
View File
@@ -2,7 +2,7 @@
# Dont use it ingame
# Remember to update this when you increase the SQL version! -aa
SQL_ENABLED
DB_VERSION 23
DB_VERSION 24
ADDRESS 127.0.0.1
PORT 3306
FEEDBACK_DATABASE feedback