[MIRROR] [MAJOR CHANGE] Admin rank datum (#10316)

Co-authored-by: Selis <12716288+ItsSelis@users.noreply.github.com>
This commit is contained in:
CHOMPStation2StaffMirrorBot
2025-03-11 14:36:14 -07:00
committed by GitHub
parent 86d5086018
commit a57d38624d
89 changed files with 1997 additions and 775 deletions

View File

@@ -1,22 +1,29 @@
-- Table structure for table `erro_admin`
CREATE TABLE `erro_admin` (
`id` int(11) NOT NULL AUTO_INCREMENT,
CREATE TABLE `admin` (
`ckey` varchar(32) NOT NULL,
`rank` varchar(32) NOT NULL DEFAULT 'Administrator',
`level` int(2) NOT NULL DEFAULT '0',
`flags` int(16) NOT NULL DEFAULT '0',
PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=latin1 ;
`rank` varchar(32) NOT NULL,
`feedback` varchar(255) DEFAULT NULL,
PRIMARY KEY (`ckey`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci;
-- Table structure for table `erro_admin_log`
CREATE TABLE `erro_admin_log` (
CREATE TABLE `admin_log` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`datetime` datetime NOT NULL,
`round_id` int(11) unsigned NULL,
`adminckey` varchar(32) NOT NULL,
`adminip` varchar(18) NOT NULL,
`log` text NOT NULL,
`adminip` int(10) unsigned NOT NULL,
`operation` enum('add admin','remove admin','change admin rank','add rank','remove rank','change rank flags') NOT NULL,
`target` varchar(32) NOT NULL,
`log` varchar(1000) NOT NULL,
PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=latin1 ;
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci;
CREATE TABLE `admin_ranks` (
`rank` varchar(32) NOT NULL,
`flags` mediumint(5) unsigned NOT NULL,
`exclude_flags` mediumint(5) unsigned NOT NULL,
`can_edit_flags` mediumint(5) unsigned NOT NULL,
PRIMARY KEY (`rank`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci;
-- Table structure for table `erro_ban`
CREATE TABLE `erro_ban` (

View File

@@ -0,0 +1,15 @@
INSERT INTO admin_ranks
(rank, flags, exclude_flags, can_edit_flags)
VALUES
('Host', 65535, 0, 65535),
('Head Admin', 65535, 0, 65535),
('Game Master', 65535, 0, 65535),
('Moderator', 8192, 0, 0),
('Game Admin', 8191, 0, 0),
('Dev Mod', 13937, 0, 0),
('Developer', 5745, 0, 0),
('Badmin', 5727, 0, 0),
('Trial Admin', 5638, 0, 0),
('Admin Candidate', 2, 0, 0),
('Retired Admin', 258, 0, 0),
('Admin Observer', 0, 0, 0);

View File

@@ -0,0 +1,4 @@
#define DEFINE_BITFIELD(_variable, _flags) /datum/bitfield/##_variable { \
flags = ##_flags; \
variable = #_variable; \
}

View File

@@ -22,25 +22,29 @@
#define ROUNDSTART_LOGOUT_REPORT_TIME 6000 // Amount of time (in deciseconds) after the rounds starts, that the player disconnect report is issued.
// Admin permissions.
#define R_BUILDMODE 0x1
#define R_ADMIN 0x2
#define R_BAN 0x4
#define R_FUN 0x8
#define R_SERVER 0x10
#define R_DEBUG 0x20
#define R_POSSESS 0x40
#define R_PERMISSIONS 0x80
#define R_STEALTH 0x100
#define R_REJUVINATE 0x200
#define R_VAREDIT 0x400
#define R_SOUNDS 0x800
#define R_SPAWN 0x1000
#define R_MOD 0x2000
#define R_EVENT 0x4000
#define R_HOST 0x8000 //higher than this will overflow
//Admin Permissions
/// Used for signifying that all admins can use this regardless of actual permissions
#define R_NONE NONE
#define R_BUILDMODE (1<<0)
#define R_ADMIN (1<<1)
#define R_BAN (1<<2)
#define R_FUN (1<<3)
#define R_SERVER (1<<4)
#define R_DEBUG (1<<5)
#define R_POSSESS (1<<6)
#define R_PERMISSIONS (1<<7)
#define R_STEALTH (1<<8)
#define R_REJUVINATE (1<<9)
#define R_VAREDIT (1<<10)
#define R_SOUNDS (1<<11)
#define R_SPAWN (1<<12)
#define R_MOD (1<<13)
#define R_EVENT (1<<14)
#define R_HOST (1<<15) //higher than this will overflow
#define R_MAXPERMISSION 0x8000 // This holds the maximum value for a permission. It is used in iteration, so keep it updated.
#define R_DEFAULT R_NONE
#define R_EVERYTHING (1<<16)-1 //the sum of all other rank permissions, used for +EVERYTHING
#define SMITE_BREAKLEGS "Break Legs"
#define SMITE_BLUESPACEARTILLERY "Bluespace Artillery"
@@ -72,3 +76,6 @@
#define AHELP_ACTIVE 1
#define AHELP_CLOSED 2
#define AHELP_RESOLVED 3
/// A value for /datum/admins/cached_feedback_link to indicate empty, rather than unobtained
#define NO_FEEDBACK_LINK "no_feedback_link"

View File

@@ -0,0 +1,94 @@
/client/CanProcCall(procname)
if(findtext(procname, "__avd_") == 1)
message_admins("[key_name_admin(usr)] attempted to directly call admin verb '[procname]'.")
log_admin("[key_name(usr)] attempted to directly call admin verb '[procname]'.")
return FALSE
return ..()
/**
* This is the only macro you should use to define admin verbs.
* It will define the verb and the verb holder for you.
* Using it is very simple:
* ADMIN_VERB(verb_path, R_PERM, "Name", "Description", "Admin.Category", args...)
* This sets up all of the above and also acts as syntatic sugar as a verb delcaration for the verb itself.
* Note that the verb args have an injected `client/user` argument that is the user that called the verb.
* Do not use usr in your verb; technically you can but I'll kill you.
*/
#define _ADMIN_VERB(verb_path_name, verb_permissions, verb_name, verb_desc, verb_category, show_in_context_menu, verb_args...) \
/datum/admin_verb/##verb_path_name \
{ \
name = ##verb_name; \
description = ##verb_desc; \
category = ##verb_category; \
permissions = ##verb_permissions; \
verb_path = /client/proc/__avd_##verb_path_name; \
}; \
/client/proc/__avd_##verb_path_name(##verb_args) \
{ \
set name = ##verb_name; \
set desc = ##verb_desc; \
set hidden = FALSE; /* this is explicitly needed as the proc begins with an underscore */ \
set popup_menu = ##show_in_context_menu; \
set category = ##verb_category; \
var/list/_verb_args = list(usr, /datum/admin_verb/##verb_path_name); \
_verb_args += args; \
SSadmin_verbs.dynamic_invoke_verb(arglist(_verb_args)); \
}; \
/datum/admin_verb/##verb_path_name/__avd_do_verb(client/user, ##verb_args)
#define ADMIN_VERB(verb_path_name, verb_permissions, verb_name, verb_desc, verb_category, verb_args...) \
_ADMIN_VERB(verb_path_name, verb_permissions, verb_name, verb_desc, verb_category, FALSE, ##verb_args)
#define ADMIN_VERB_ONLY_CONTEXT_MENU(verb_path_name, verb_permissions, verb_name, verb_args...) \
_ADMIN_VERB(verb_path_name, verb_permissions, verb_name, ADMIN_VERB_NO_DESCRIPTION, ADMIN_CATEGORY_HIDDEN, TRUE, ##verb_args)
#define ADMIN_VERB_AND_CONTEXT_MENU(verb_path_name, verb_permissions, verb_name, verb_desc, verb_category, verb_args...) \
_ADMIN_VERB(verb_path_name, verb_permissions, verb_name, verb_desc, verb_category, TRUE, ##verb_args)
/// Used to define a special check to determine if the admin verb should exist at all. Useful for verbs such as play sound which require configuration.
#define ADMIN_VERB_CUSTOM_EXIST_CHECK(verb_path_name) \
/datum/admin_verb/##verb_path_name/__avd_check_should_exist()
/// Used to define the visibility flag of the verb. If the admin does not have this flag enabled they will not see the verb.
#define ADMIN_VERB_VISIBILITY(verb_path_name, verb_visibility) /datum/admin_verb/##verb_path_name/visibility_flag = ##verb_visibility
// These are put here to prevent the "procedure override precedes definition" error.
/datum/admin_verb/proc/__avd_get_verb_path()
CRASH("__avd_get_verb_path not defined. use the macro")
/datum/admin_verb/proc/__avd_do_verb(...)
CRASH("__avd_do_verb not defined. use the macro")
/datum/admin_verb/proc/__avd_check_should_exist()
return TRUE
/*
* This is an example of how to use the above macro:
* ```
* ADMIN_VERB(name_of_verb, R_ADMIN, "Verb Name", "Verb Desc", "Verb Category", mob/target in world)
* to_chat(user, "Hello!")
* ```
* Note the implied `client/user` argument that is injected into the verb.
* Also note that byond is shit and you cannot multi-line the macro call.
*/
/// Use this to mark your verb as not having a description. Should ONLY be used if you are also hiding the verb!
#define ADMIN_VERB_NO_DESCRIPTION ""
/// Used to verbs you do not want to show up in the master verb panel.
#define ADMIN_CATEGORY_HIDDEN null
// Admin verb categories
#define ADMIN_CATEGORY_MAIN "Admin"
#define ADMIN_CATEGORY_EVENTS "Admin.Events"
#define ADMIN_CATEGORY_FUN "Admin.Fun"
#define ADMIN_CATEGORY_GAME "Admin.Game"
#define ADMIN_CATEGORY_SHUTTLE "Admin.Shuttle"
// Special categories that are separated
#define ADMIN_CATEGORY_DEBUG "Debug"
#define ADMIN_CATEGORY_SERVER "Server"
#define ADMIN_CATEGORY_OBJECT "Object"
#define ADMIN_CATEGORY_MAPPING "Mapping"
#define ADMIN_CATEGORY_PROFILE "Profile"
#define ADMIN_CATEGORY_IPINTEL "Admin.IPIntel"
// Visibility flags
#define ADMIN_VERB_VISIBLITY_FLAG_MAPPING_DEBUG "Map-Debug"

View File

@@ -0,0 +1,2 @@
/// Called after a client logs into a mob: (mob)
#define COMSIG_CLIENT_MOB_LOGIN "client_mob_changed"

View File

@@ -118,6 +118,7 @@ var/global/list/runlevel_flags = list(RUNLEVEL_LOBBY, RUNLEVEL_SETUP, RUNLEVEL_G
// The numbers just define the ordering, they are meaningless otherwise.
#define INIT_ORDER_TITLE 99 //CHOMPEdit
#define INIT_ORDER_SERVER_MAINT 93
#define INIT_ORDER_ADMIN_VERBS 84 // needs to be pretty high, admins can't do much without it
#define INIT_ORDER_WEBHOOKS 50
#define INIT_ORDER_SQLITE 41
#define INIT_ORDER_GARBAGE 40

View File

@@ -1,41 +1,77 @@
GLOBAL_LIST_INIT(bitfields, list(
"datum_flags" = list(
"DF_VAR_EDITED" = DF_VAR_EDITED,
"DF_ISPROCESSING" = DF_ISPROCESSING
),
"appearance_flags" = list(
"KEEP_APART" = KEEP_APART,
"KEEP_TOGETHER" = KEEP_TOGETHER,
"LONG_GLIDE" = LONG_GLIDE,
"NO_CLIENT_COLOR" = NO_CLIENT_COLOR,
"PIXEL_SCALE" = PIXEL_SCALE,
"PLANE_MASTER" = PLANE_MASTER,
"RESET_ALPHA" = RESET_ALPHA,
"RESET_COLOR" = RESET_COLOR,
"RESET_TRANSFORM" = RESET_TRANSFORM,
"TILE_BOUND" = TILE_BOUND,
"PASS_MOUSE" = PASS_MOUSE,
"TILE_MOVER" = TILE_MOVER
),
"vis_flags" = list(
"VIS_HIDE" = VIS_HIDE,
"VIS_INHERIT_DIR" = VIS_INHERIT_DIR,
"VIS_INHERIT_ICON" = VIS_INHERIT_ICON,
"VIS_INHERIT_ICON_STATE" = VIS_INHERIT_ICON_STATE,
"VIS_INHERIT_ID" = VIS_INHERIT_ID,
"VIS_INHERIT_LAYER" = VIS_INHERIT_LAYER,
"VIS_INHERIT_PLANE" = VIS_INHERIT_PLANE,
"VIS_UNDERLAY" = VIS_UNDERLAY,
),
"sight" = list(
"BLIND" = BLIND,
"SEE_BLACKNESS" = SEE_BLACKNESS,
"SEE_INFRA" = SEE_INFRA,
"SEE_MOBS" = SEE_MOBS,
"SEE_OBJS" = SEE_OBJS,
"SEE_PIXELS" = SEE_PIXELS,
"SEE_SELF" = SEE_SELF,
"SEE_THRU" = SEE_THRU,
"SEE_TURFS" = SEE_TURFS,
),
GLOBAL_LIST_INIT(bitfields, generate_bitfields())
/// Specifies a bitfield for smarter debugging
/datum/bitfield
/// The variable name that contains the bitfield
var/variable
/// An associative list of the readable flag and its true value
var/list/flags
/// Turns /datum/bitfield subtypes into a list for use in debugging
/proc/generate_bitfields()
var/list/bitfields = list()
for (var/_bitfield in subtypesof(/datum/bitfield))
var/datum/bitfield/bitfield = new _bitfield
bitfields[bitfield.variable] = bitfield.flags
return bitfields
DEFINE_BITFIELD(admin_flags, list(
"ADMIN" = R_ADMIN,
"REJUVINATE" = R_REJUVINATE,
"BAN" = R_BAN,
"BUILDMODE" = R_BUILDMODE,
"DEBUG" = R_DEBUG,
"FUN" = R_FUN,
"PERMISSIONS" = R_PERMISSIONS,
"MOD" = R_MOD,
"POSSESS" = R_POSSESS,
"SERVER" = R_SERVER,
"SOUNDS" = R_SOUNDS,
"SPAWN" = R_SPAWN,
"STEALTH" = R_STEALTH,
"VAREDIT" = R_VAREDIT,
))
DEFINE_BITFIELD(datum_flags, list(
"DF_VAR_EDITED" = DF_VAR_EDITED,
"DF_ISPROCESSING" = DF_ISPROCESSING
))
DEFINE_BITFIELD(appearance_flags, list(
"KEEP_APART" = KEEP_APART,
"KEEP_TOGETHER" = KEEP_TOGETHER,
"LONG_GLIDE" = LONG_GLIDE,
"NO_CLIENT_COLOR" = NO_CLIENT_COLOR,
"PIXEL_SCALE" = PIXEL_SCALE,
"PLANE_MASTER" = PLANE_MASTER,
"RESET_ALPHA" = RESET_ALPHA,
"RESET_COLOR" = RESET_COLOR,
"RESET_TRANSFORM" = RESET_TRANSFORM,
"TILE_BOUND" = TILE_BOUND,
"PASS_MOUSE" = PASS_MOUSE,
"TILE_MOVER" = TILE_MOVER
))
DEFINE_BITFIELD(vis_flags, list(
"VIS_HIDE" = VIS_HIDE,
"VIS_INHERIT_DIR" = VIS_INHERIT_DIR,
"VIS_INHERIT_ICON" = VIS_INHERIT_ICON,
"VIS_INHERIT_ICON_STATE" = VIS_INHERIT_ICON_STATE,
"VIS_INHERIT_ID" = VIS_INHERIT_ID,
"VIS_INHERIT_LAYER" = VIS_INHERIT_LAYER,
"VIS_INHERIT_PLANE" = VIS_INHERIT_PLANE,
"VIS_UNDERLAY" = VIS_UNDERLAY,
))
DEFINE_BITFIELD(sight, list(
"BLIND" = BLIND,
"SEE_BLACKNESS" = SEE_BLACKNESS,
"SEE_INFRA" = SEE_INFRA,
"SEE_MOBS" = SEE_MOBS,
"SEE_OBJS" = SEE_OBJS,
"SEE_PIXELS" = SEE_PIXELS,
"SEE_SELF" = SEE_SELF,
"SEE_THRU" = SEE_THRU,
"SEE_TURFS" = SEE_TURFS,
))

9
code/_helpers/admin.dm Normal file
View File

@@ -0,0 +1,9 @@
/// Returns if the given client is an admin, REGARDLESS of if they're deadminned or not.
///proc/is_admin(client/client)
// return !isnull(GLOB.admin_datums[client.ckey]) || !isnull(GLOB.deadmins[client.ckey])
/// Sends a message in the event that someone attempts to elevate their permissions through invoking a certain proc.
/proc/alert_to_permissions_elevation_attempt(mob/user)
var/message = " has tried to elevate permissions!"
message_admins(key_name_admin(user) + message)
log_admin(key_name(user) + message)

View File

@@ -352,7 +352,7 @@
. += "<a href='byond://?priv_msg=\ref[C]'>"
if(C && C.holder && C.holder.fakekey)
. += C.holder.rank // CHOMPEdit: Stealth mode displays staff rank in PM Messages
. += C.holder.rank_names() // CHOMPEdit: Stealth mode displays staff rank in PM Messages
else
. += key

View File

@@ -631,3 +631,14 @@ GLOBAL_LIST_EMPTY(text_tag_cache)
/proc/sanitize_css_class_name(name)
var/static/regex/regex = new(@"[^a-zA-Z0-9]","g")
return replacetext(name, regex, "")
//finds the first occurrence of one of the characters from needles argument inside haystack
//it may appear this can be optimised, but it really can't. findtext() is so much faster than anything you can do in byondcode.
//stupid byond :(
/proc/findchar(haystack, needles, start=1, end=0)
var/temp
var/len = length(needles)
for(var/i=1, i<=len, i++)
temp = findtextEx(haystack, ascii2text(text2ascii(needles,i)), start, end) //Note: ascii2text(text2ascii) is faster than copytext()
if(temp) end = temp
return end

View File

@@ -438,8 +438,17 @@
/datum/config_entry/flag/no_click_cooldown
/// Defines whether the server uses the legacy admin system with admins.txt or the SQL system. Config option in config.txt
/datum/config_entry/flag/admin_legacy_system
/datum/config_entry/flag/admin_legacy_system //Defines whether the server uses the legacy admin system with admins.txt or the SQL system
protection = CONFIG_ENTRY_LOCKED
/datum/config_entry/flag/protect_legacy_admins //Stops any admins loaded by the legacy system from having their rank edited by the permissions panel
protection = CONFIG_ENTRY_LOCKED
/datum/config_entry/flag/protect_legacy_ranks //Stops any ranks loaded by the legacy system from having their flags edited by the permissions panel
protection = CONFIG_ENTRY_LOCKED
/datum/config_entry/flag/load_legacy_ranks_only //Loads admin ranks only from legacy admin_ranks.txt, while enabled ranks are mirrored to the database
protection = CONFIG_ENTRY_LOCKED
/// Defines whether the server uses the legacy banning system with the files in /data or the SQL system. Config option in config.txt
/datum/config_entry/flag/ban_legacy_system
@@ -721,3 +730,5 @@
/// The endpoint for the chat to fetch the chatlogs from (for example, the last 2500 messages on init for the history)
/// REQUIRES chatlog_database_backend to be enabled
/datum/config_entry/string/chatlog_database_api_endpoint
/datum/config_entry/flag/forbid_admin_profiling

View File

@@ -53,7 +53,7 @@ SUBSYSTEM_DEF(persistence)
return
var/list/dat = list("<table width = '100%'>")
var/can_modify = check_rights(R_ADMIN, 0, user)
var/can_modify = check_rights_for(user.client, (R_ADMIN|R_DEBUG))
for(var/thing in persistence_datums)
var/datum/persistent/P = persistence_datums[thing]
if(P.has_admin_data)

View File

@@ -175,7 +175,9 @@ SUBSYSTEM_DEF(statpanels)
/datum/controller/subsystem/statpanels/proc/set_tickets_tab(client/target)
/* CHOMPRemove Start, our tickets are handled differently
var/list/tickets = list()
if(check_rights(R_ADMIN|R_SERVER|R_MOD,FALSE,target)) //Prevents non-staff from opening the list of ahelp tickets
if(check_rights_for(target, R_ADMIN|R_SERVER|R_MOD)) //Prevents non-staff from opening the list of ahelp tickets
tickets += GLOB.ahelp_tickets.stat_entry(target)
tickets += GLOB.mhelp_tickets.stat_entry(target)
*/// CHOMPRemove End
var/list/tickets = GLOB.tickets.stat_entry(target) // CHOMPEdit
target.stat_panel.send_message("update_tickets", tickets)

View File

@@ -87,7 +87,8 @@
<html>
<meta charset=ISO-8859-1">
<head>
<meta http-equiv="X-UA-Compatible" content="IE=edge" />
<meta http-equiv='Content-Type' content='text/html; charset=UTF-8'>
<meta http-equiv='X-UA-Compatible' content='IE=edge'>
[head_content]
</head>
<body scroll=auto>

View File

@@ -169,7 +169,7 @@
set name = "Release Virus"
set desc = "Release a pre-set virus."
if(!is_admin())
if(!check_rights(R_FUN|R_EVENT))
return FALSE
var/disease = tgui_input_list(usr, "Choose virus", "Viruses", subtypesof(/datum/disease), subtypesof(/datum/disease))

View File

@@ -23,7 +23,7 @@
var/database/query/last_query = null
/datum/managed_browser/feedback_viewer/New(client/new_client)
if(!check_rights(R_ADMIN|R_DEBUG|R_EVENT, new_client)) // Just in case someone figures out a way to spawn this as non-staff.
if(!check_rights_for(new_client, R_ADMIN|R_DEBUG|R_EVENT)) // Just in case someone figures out a way to spawn this as non-staff.
message_admins("[new_client] tried to view feedback with insufficent permissions.")
qdel(src)

View File

@@ -403,7 +403,7 @@
take_uplink()
memory = null//Remove any memory they may have had.
if("crystals")
if (usr.client.holder.rights & R_FUN)
if (check_rights_for(usr.client, R_FUN))
// var/obj/item/uplink/hidden/suplink = find_syndicate_uplink() No longer needed, uses stored in mind
var/crystals
crystals = tcrystals

View File

@@ -418,7 +418,7 @@ var/global/datum/controller/occupations/job_master
permitted = 1
// Check if they're whitelisted for this gear (in alien whitelist? seriously?)
if(G.whitelisted && !is_alien_whitelisted(H, GLOB.all_species[G.whitelisted]))
if(G.whitelisted && !is_alien_whitelisted(H.client, GLOB.all_species[G.whitelisted]))
permitted = 0
// If they aren't, tell them

View File

@@ -46,13 +46,13 @@ GLOBAL_LIST_EMPTY(alien_whitelist) // CHOMPEdit - Managed Globals
GLOB.alien_whitelist[key] = our_whitelists // CHOMPEdit - Managed Globals
our_whitelists += left_and_right[2]
/proc/is_alien_whitelisted(mob/M, var/datum/species/species)
/proc/is_alien_whitelisted(client/C, var/datum/species/species)
//They are admin or the whitelist isn't in use
if(whitelist_overrides(M))
if(whitelist_overrides(C))
return TRUE
//You did something wrong
if(!M || !species)
if(!C || !species)
return FALSE
//The species isn't even whitelisted
@@ -60,7 +60,7 @@ GLOBAL_LIST_EMPTY(alien_whitelist) // CHOMPEdit - Managed Globals
return TRUE
//Search the whitelist
var/list/our_whitelists = GLOB.alien_whitelist[M.ckey] // CHOMPEdit - Managed Globals
var/list/our_whitelists = GLOB.alien_whitelist[C.ckey] // CHOMPEdit - Managed Globals
if("All" in our_whitelists)
return TRUE
if(species.name in our_whitelists)
@@ -112,10 +112,14 @@ GLOBAL_LIST_EMPTY(alien_whitelist) // CHOMPEdit - Managed Globals
if(findtext(s,"[M.ckey] - All"))
return 1
/proc/whitelist_overrides(mob/M)
/proc/whitelist_overrides(client/C)
if(!CONFIG_GET(flag/usealienwhitelist))
return TRUE
if(check_rights(R_ADMIN|R_EVENT, 0, M))
if(ismob(C)) //Someone fed a mob into this by mistake. Bad, but we planned ahead for these mistakes.
var/mob/mob = C
C = mob.client
if(check_rights_for(C, R_ADMIN|R_EVENT|R_DEBUG))
return TRUE
return FALSE

View File

@@ -211,7 +211,7 @@
return
//No whitelist
if(!is_alien_whitelisted(user, GLOB.all_species[user.client.prefs.species]))
if(!is_alien_whitelisted(user.client, GLOB.all_species[user.client.prefs.species]))
to_chat(user, span_warning("You cannot use this spawnpoint to spawn as a species you are not whitelisted for!"))
return

View File

@@ -130,7 +130,7 @@
/obj/structure/mirror/raider/attack_hand(var/mob/living/carbon/human/user)
if(istype(get_area(src),/area/syndicate_mothership))
if(istype(user) && user.mind && user.mind.special_role == "Raider" && user.species.name != SPECIES_VOX && is_alien_whitelisted(user, SPECIES_VOX))
if(istype(user) && user.mind && user.mind.special_role == "Raider" && user.species.name != SPECIES_VOX && is_alien_whitelisted(user.client, SPECIES_VOX))
var/choice = tgui_alert(user, "Do you wish to become a true Vox of the Shoal? This is not reversible.", "Become Vox?", list("No","Yes"))
if(choice && choice == "Yes")
var/mob/living/carbon/human/vox/vox = new(get_turf(src),SPECIES_VOX)

View File

@@ -36,6 +36,8 @@
config.Load(params[OVERRIDE_CONFIG_DIRECTORY_PARAMETER])
load_admins()
ConfigLoaded()
makeDatumRefLists()
VgsNew()
@@ -165,7 +167,7 @@ var/world_topic_spam_protect_time = world.timeofday
if(C.holder)
if(C.holder.fakekey)
continue
admins[C.key] = C.holder.rank
admins[C.key] = C.holder.rank_names()
players += C.key
if(isliving(C.mob))
active++
@@ -536,7 +538,7 @@ var/world_topic_spam_protect_time = world.timeofday
continue
var/title = "Moderator"
var/rights = admin_ranks[title]
var/rights = GLOB.admin_ranks[title]
var/ckey = copytext(line, 1, length(line)+1)
var/datum/admins/D = new /datum/admins(title, rights, ckey)

View File

@@ -1,7 +1,7 @@
#ifndef OVERRIDE_BAN_SYSTEM
//Blocks an attempt to connect before even creating our client datum thing.
/world/IsBanned(key,address,computer_id)
if(ckey(key) in admin_datums)
if(ckey(key) in GLOB.admin_datums)
return ..()
//Guest Checking

View File

@@ -7,7 +7,7 @@ var/global/floorIsLava = 0
//log_adminwarn(msg) //log_and_message_admins is for this
for(var/client/C in GLOB.admins)
if((R_ADMIN|R_MOD) & C.holder.rights)
if(check_rights_for(C, (R_ADMIN|R_MOD|R_SERVER)))
to_chat(C,
type = MESSAGE_TYPE_ADMINLOG,
html = msg,
@@ -16,7 +16,7 @@ var/global/floorIsLava = 0
/proc/msg_admin_attack(var/text) //Toggleable Attack Messages
var/rendered = span_filter_attacklog(span_log_message(span_prefix("ATTACK:") + span_message("[text]")))
for(var/client/C in GLOB.admins)
if((R_ADMIN|R_MOD) & C.holder.rights)
if(check_rights_for(C, (R_ADMIN|R_MOD)))
if(C.prefs?.read_preference(/datum/preference/toggle/show_attack_logs))
var/msg = rendered
to_chat(C,
@@ -26,8 +26,16 @@ var/global/floorIsLava = 0
/proc/admin_notice(var/message, var/rights)
for(var/mob/M in mob_list)
if(check_rights(rights, 0, M))
to_chat(M,message)
var/C = M.client
if(!C)
return
if(!(istype(C, /client)))
return
if(check_rights_for(C, rights))
to_chat(C, message)
///////////////////////////////////////////////////////////////////////////////////////////////Panels
@@ -49,7 +57,7 @@ var/global/floorIsLava = 0
body += "<body>Options panel for" + span_bold("[M]")
if(M.client)
body += " played by " + span_bold("[M.client]")
body += "\[<A href='byond://?src=\ref[src];[HrefToken()];editrights=show'>[M.client.holder ? M.client.holder.rank : "Player"]</A>\]"
body += "\[<A href='byond://?src=\ref[src];[HrefToken()];editrights=show'>[M.client.holder ? M.client.holder.rank_names() : "Player"]</A>\]"
if(isnewplayer(M))
body += span_bold(" Hasn't Entered Game")
@@ -1353,7 +1361,7 @@ var/datum/announcement/minor/admin_min_announcer = new
if(istype(whom, /mob))
M = whom
C = M.client
if(R_HOST & C.holder.rights)
if(check_rights_for(C, R_HOST))
return 1
else
return 0
@@ -1562,12 +1570,12 @@ var/datum/announcement/minor/admin_min_announcer = new
if(P.sender) // sent as a reply
log_admin("[key_name(src.owner)] replied to a fax message from [key_name(P.sender)]")
for(var/client/C in GLOB.admins)
if((R_ADMIN | R_MOD | R_EVENT) & C.holder.rights)
if(check_rights_for(C, (R_ADMIN | R_MOD | R_EVENT)))
to_chat(C, span_log_message("[span_prefix("FAX LOG:")][key_name_admin(src.owner)] replied to a fax message from [key_name_admin(P.sender)] (<a href='byond://?_src_=holder;[HrefToken()];AdminFaxView=\ref[rcvdcopy]'>VIEW</a>)"))
else
log_admin("[key_name(src.owner)] has sent a fax message to [destination.department]")
for(var/client/C in GLOB.admins)
if((R_ADMIN | R_MOD | R_EVENT) & C.holder.rights)
if(check_rights_for(C, (R_ADMIN | R_MOD | R_EVENT)))
to_chat(C, span_log_message("[span_prefix("FAX LOG:")][key_name_admin(src.owner)] has sent a fax message to [destination.department] (<a href='byond://?_src_=holder;[HrefToken()];AdminFaxView=\ref[rcvdcopy]'>VIEW</a>)"))
var/plaintext_title = P.sender ? "replied to [key_name(P.sender)]'s fax" : "sent a fax message to [destination.department]"
@@ -1590,3 +1598,18 @@ var/datum/announcement/minor/admin_min_announcer = new
qdel(P)
faxreply = null
return
/datum/admins/proc/set_uplink(mob/living/carbon/human/H as mob)
set category = "Debug.Events"
set name = "Set Uplink"
set desc = "Allows admins to set up an uplink on a character. This will be required for a character to use telecrystals."
set popup_menu = FALSE
if(check_rights(R_ADMIN|R_DEBUG))
traitors.spawn_uplink(H)
H.mind.tcrystals = DEFAULT_TELECRYSTAL_AMOUNT
H.mind.accept_tcrystals = 1
var/msg = "[key_name(usr)] has given [H.ckey] an uplink."
message_admins(msg)
else
to_chat(usr, "You do not have access to this command.")

View File

@@ -1,175 +1,398 @@
var/list/admin_ranks = list() //list of all ranks with associated rights
GLOBAL_LIST_EMPTY(admin_ranks) //list of all admin_rank datums
GLOBAL_PROTECT(admin_ranks)
//load our rank - > rights associations
/proc/load_admin_ranks()
admin_ranks.Cut()
GLOBAL_LIST_EMPTY(protected_ranks) //admin ranks loaded from txt
GLOBAL_PROTECT(protected_ranks)
var/previous_rights = 0
/datum/admin_rank
var/name = "NoRank"
var/rights = R_DEFAULT
var/exclude_rights = NONE
var/include_rights = NONE
var/can_edit_rights = NONE
//Clear profile access
for(var/A in world.GetConfig("admin"))
world.SetConfig("APP/admin", A, null)
/datum/admin_rank/New(init_name, init_rights, init_exclude_rights, init_edit_rights)
if(IsAdminAdvancedProcCall())
alert_to_permissions_elevation_attempt(usr)
if (name == "NoRank") //only del if this is a true creation (and not just a New() proc call), other wise trialmins/coders could abuse this to deadmin other admins
QDEL_IN(src, 0)
CRASH("Admin proc call creation of admin datum")
return
name = init_name
if(!name)
qdel(src)
CRASH("Admin rank created without name.")
if(init_rights)
rights = init_rights
include_rights = rights
if(init_exclude_rights)
exclude_rights = init_exclude_rights
rights &= ~exclude_rights
if(init_edit_rights)
can_edit_rights = init_edit_rights
//load text from file
var/list/Lines = file2list("config/admin_ranks.txt")
/datum/admin_rank/Destroy()
if(IsAdminAdvancedProcCall())
alert_to_permissions_elevation_attempt(usr)
return QDEL_HINT_LETMELIVE
. = ..()
//process each line seperately
for(var/line in Lines)
if(!length(line)) continue
if(copytext(line,1,2) == "#") continue
/datum/admin_rank/vv_edit_var(var_name, var_value)
return FALSE
var/list/List = splittext(line,"+")
if(!List.len) continue
var/rank = ckeyEx(List[1])
switch(rank)
if(null,"") continue
if("Removed") continue //Reserved
var/rights = 0
for(var/i=2, i<=List.len, i++)
switch(ckey(List[i]))
if("@","prev") rights |= previous_rights
if("buildmode","build") rights |= R_BUILDMODE
if("admin") rights |= R_ADMIN
if("ban") rights |= R_BAN
if("fun") rights |= R_FUN
if("server") rights |= R_SERVER
if("debug") rights |= R_DEBUG
if("permissions","rights") rights |= R_PERMISSIONS
if("possess") rights |= R_POSSESS
if("stealth") rights |= R_STEALTH
if("rejuv","rejuvinate") rights |= R_REJUVINATE
if("varedit") rights |= R_VAREDIT
if("everything","host","all") rights |= (R_HOST | R_BUILDMODE | R_ADMIN | R_BAN | R_FUN | R_SERVER | R_DEBUG | R_PERMISSIONS | R_POSSESS | R_STEALTH | R_REJUVINATE | R_VAREDIT | R_SOUNDS | R_SPAWN | R_MOD| R_EVENT)
if("sound","sounds") rights |= R_SOUNDS
if("spawn","create") rights |= R_SPAWN
if("mod") rights |= R_MOD
if("event") rights |= R_EVENT
admin_ranks[rank] = rights
previous_rights = rights
// Adds/removes rights to this admin_rank
/datum/admin_rank/proc/process_keyword(group, group_count, datum/admin_rank/previous_rank)
if(IsAdminAdvancedProcCall())
alert_to_permissions_elevation_attempt(usr)
return
var/list/keywords = splittext(group, " ")
var/flag = 0
for(var/k in keywords)
switch(k)
if("BUILD")
flag = R_BUILDMODE
if("ADMIN")
flag = R_ADMIN
if("BAN")
flag = R_BAN
if("FUN")
flag = R_FUN
if("SERVER")
flag = R_SERVER
if("DEBUG")
flag = R_DEBUG
if("PERMISSIONS")
flag = R_PERMISSIONS
if("POSSESS")
flag = R_POSSESS
if("STEALTH")
flag = R_STEALTH
if("REJUVINATE")
flag = R_REJUVINATE
if("VAREDIT")
flag = R_VAREDIT
if("EVERYTHING")
flag = R_EVERYTHING
if("SOUND")
flag = R_SOUNDS
if("SPAWN")
flag = R_SPAWN
if("MOD")
flag = R_MOD
if("EVENT")
flag = R_EVENT
if("@")
if(previous_rank)
switch(group_count)
if(1)
flag = previous_rank.include_rights
if(2)
flag = previous_rank.exclude_rights
if(3)
flag = previous_rank.can_edit_rights
else
continue
switch(group_count)
if(1)
rights |= flag
include_rights |= flag
if(2)
rights &= ~flag
exclude_rights |= flag
if(3)
can_edit_rights |= flag
/// Loads admin ranks.
/// Return a list containing the backup data if they were loaded from the database backup json
/proc/load_admin_ranks(dbfail, no_update)
if(IsAdminAdvancedProcCall())
to_chat(usr, "<span class='admin prefix'>Admin Reload blocked: Advanced ProcCall detected.</span>", confidential = TRUE)
return
GLOB.admin_ranks.Cut()
GLOB.protected_ranks.Cut()
//load text from file and process each entry
var/ranks_text = file2text("[global.config.directory]/admin_ranks.txt")
var/datum/admin_rank/previous_rank
var/regex/admin_ranks_regex = new(@"^Name\s*=\s*(.+?)\s*\n+Include\s*=\s*([\l @]*?)\s*\n+Exclude\s*=\s*([\l @]*?)\s*\n+Edit\s*=\s*([\l @]*?)\s*\n*$", "gm")
while(admin_ranks_regex.Find(ranks_text))
var/datum/admin_rank/R = new(admin_ranks_regex.group[1])
if(!R)
continue
var/count = 1
for(var/i in admin_ranks_regex.group - admin_ranks_regex.group[1])
if(i)
R.process_keyword(i, count, previous_rank)
count++
GLOB.admin_ranks += R
GLOB.protected_ranks += R
previous_rank = R
if(!CONFIG_GET(flag/admin_legacy_system) && !dbfail)
if(CONFIG_GET(flag/load_legacy_ranks_only))
if(!no_update)
sync_ranks_with_db()
else
var/datum/db_query/query_load_admin_ranks = SSdbcore.NewQuery("SELECT `rank`, flags, exclude_flags, can_edit_flags FROM [format_table_name("admin_ranks")]")
if(!query_load_admin_ranks.Execute())
message_admins("Error loading admin ranks from database. Loading from backup.")
log_sql("Error loading admin ranks from database. Loading from backup.")
dbfail = TRUE
else
while(query_load_admin_ranks.NextRow())
var/skip
var/rank_name = query_load_admin_ranks.item[1]
for(var/datum/admin_rank/R in GLOB.admin_ranks)
if(R.name == rank_name) //this rank was already loaded from txt override
skip = 1
break
if(!skip)
var/rank_flags = text2num(query_load_admin_ranks.item[2])
var/rank_exclude_flags = text2num(query_load_admin_ranks.item[3])
var/rank_can_edit_flags = text2num(query_load_admin_ranks.item[4])
var/datum/admin_rank/R = new(rank_name, rank_flags, rank_exclude_flags, rank_can_edit_flags)
if(!R)
continue
GLOB.admin_ranks += R
qdel(query_load_admin_ranks)
//load ranks from backup file
if(dbfail)
var/backup_file = file2text("data/admins_backup.json")
if(backup_file == null)
log_world("Unable to locate admins backup file.")
return FALSE
var/list/json = json_decode(backup_file)
for(var/J in json["ranks"])
var/skip
for(var/datum/admin_rank/R in GLOB.admin_ranks)
if(R.name == "[J]") //this rank was already loaded from txt override
skip = TRUE
if(skip)
continue
var/datum/admin_rank/R = new("[J]", json["ranks"]["[J]"]["include rights"], json["ranks"]["[J]"]["exclude rights"], json["ranks"]["[J]"]["can edit rights"])
if(!R)
continue
GLOB.admin_ranks += R
return json
#ifdef TESTING
var/msg = "Permission Sets Built:\n"
for(var/rank in admin_ranks)
msg += "\t[rank] - [admin_ranks[rank]]\n"
for(var/datum/admin_rank/R in GLOB.admin_ranks)
msg += "\t[R.name]"
var/rights = rights2text(R.rights,"\n\t\t")
if(rights)
msg += "\t\t[rights]\n"
testing(msg)
#endif
/hook/startup/proc/loadAdmins()
load_admins()
return 1
/// Converts a rank name (such as "Coder+Moth") into a list of /datum/admin_rank
/proc/ranks_from_rank_name(rank_name)
var/list/rank_names = splittext(rank_name, "+")
var/list/ranks = list()
/proc/load_admins()
for (var/datum/admin_rank/rank as anything in GLOB.admin_ranks)
if (rank.name in rank_names)
rank_names -= rank.name
ranks += rank
if (rank_names.len == 0)
break
if (rank_names.len > 0)
log_config("Admin rank names were invalid: [jointext(ranks, ", ")]")
return ranks
/// Takes a list of rank names and joins them with +
/proc/join_admin_ranks(list/datum/admin_rank/ranks)
var/list/names = list()
for (var/datum/admin_rank/rank as anything in ranks)
names += rank.name
return jointext(names, "+")
/// (Re)Loads the admin list.
/// returns TRUE if database admins had to be loaded from the backup json
/proc/load_admins(no_update)
var/dbfail
if(!CONFIG_GET(flag/admin_legacy_system) && !SSdbcore.Connect())
message_admins("Failed to connect to database while loading admins. Loading from backup.")
log_sql("Failed to connect to database while loading admins. Loading from backup.")
dbfail = TRUE
//clear the datums references
admin_datums.Cut()
GLOB.admin_datums.Cut()
for(var/client/C in GLOB.admins)
C.remove_admin_verbs()
C.holder = null
GLOB.admins.Cut()
load_admin_ranks() //CHOMP Edit: moved this from "f(config.admin_legacy_system)" and put it here instead, literally just moved it 3 lines.
GLOB.protected_admins.Cut()
GLOB.deadmins.Cut()
var/list/backup_file_json = load_admin_ranks(dbfail, no_update)
dbfail = backup_file_json != null
//Clear profile access
for(var/A in world.GetConfig("admin"))
world.SetConfig("APP/admin", A, null)
var/list/rank_names = list()
for(var/datum/admin_rank/R in GLOB.admin_ranks)
rank_names[R.name] = R
//ckeys listed in admins.txt are always made admins before sql loading is attempted
var/admins_text = file2text("[global.config.directory]/admins.txt")
var/regex/admins_regex = new(@"^(?!#)(.+?)\s+=\s+(.+)", "gm")
if(CONFIG_GET(flag/admin_legacy_system)) // CHOMPEdit
//Clear profile access
for(var/A in world.GetConfig("admin"))
world.SetConfig("APP/admin", A, null)
while(admins_regex.Find(admins_text))
var/admin_key = admins_regex.group[1]
var/admin_rank = admins_regex.group[2]
new /datum/admins(ranks_from_rank_name(admin_rank), ckey(admin_key), force_active = FALSE, protected = TRUE)
//load text from file
var/list/Lines = file2list("config/admins.txt")
if(!CONFIG_GET(flag/admin_legacy_system) && !dbfail)
var/datum/db_query/query_load_admins = SSdbcore.NewQuery("SELECT ckey, `rank`, feedback FROM [format_table_name("admin")] ORDER BY `rank`")
if(!query_load_admins.Execute())
message_admins("Error loading admins from database. Loading from backup.")
log_sql("Error loading admins from database. Loading from backup.")
dbfail = 1
else
while(query_load_admins.NextRow())
var/admin_ckey = ckey(query_load_admins.item[1])
var/admin_rank = query_load_admins.item[2]
var/admin_feedback = query_load_admins.item[3]
var/skip
//process each line seperately
for(var/line in Lines)
if(!length(line)) continue
if(copytext(line,1,2) == "#") continue
//Split the line at every "-"
var/list/List = splittext(line, "-")
if(!List.len) continue
//ckey is before the first "-"
var/ckey = ckey(List[1])
if(!ckey) continue
//rank follows the first "-"
var/rank = ""
if(List.len >= 2)
rank = ckeyEx(List[2])
//load permissions associated with this rank
var/rights = admin_ranks[rank]
//create the admin datum and store it for later use
var/datum/admins/D = new /datum/admins(rank, rights, ckey)
if(D.rights & R_DEBUG) //grant profile access
world.SetConfig("APP/admin", ckey, "role=admin")
//find the client for a ckey if they are connected and associate them with the new admin datum
D.associate(GLOB.directory[ckey])
else
//The current admin system uses SQL
establish_db_connection()
if(!SSdbcore.IsConnected())
error("Failed to connect to database in load_admins(). Reverting to legacy system.")
log_misc("Failed to connect to database in load_admins(). Reverting to legacy system.")
CONFIG_SET(flag/admin_legacy_system, TRUE)
load_admins()
return
var/datum/db_query/query = SSdbcore.NewQuery("SELECT ckey, rank, level, flags FROM erro_admin")
query.Execute()
while(query.NextRow())
var/ckey = query.item[1]
var/rank = query.item[2]
if(rank == "Removed") continue //This person was de-adminned. They are only in the admin list for archive purposes.
var/rights = query.item[4]
if(istext(rights)) rights = text2num(rights)
var/datum/admins/D = new /datum/admins(rank, rights, ckey)
if(D.rights & R_DEBUG) //grant profile access
world.SetConfig("APP/admin", ckey, "role=admin")
//find the client for a ckey if they are connected and associate them with the new admin datum
D.associate(GLOB.directory[ckey])
qdel(query)
if(!admin_datums)
error("The database query in load_admins() resulted in no admins being added to the list. Reverting to legacy system.")
log_misc("The database query in load_admins() resulted in no admins being added to the list. Reverting to legacy system.")
CONFIG_SET(flag/admin_legacy_system, TRUE)
load_admins()
return
var/list/admin_ranks = ranks_from_rank_name(admin_rank)
if(admin_ranks.len == 0)
message_admins("[admin_ckey] loaded with invalid admin rank [admin_rank].")
skip = 1
if(GLOB.admin_datums[admin_ckey] || GLOB.deadmins[admin_ckey])
skip = 1
if(!skip)
var/datum/admins/admin_holder = new(admin_ranks, admin_ckey)
admin_holder.cached_feedback_link = admin_feedback || NO_FEEDBACK_LINK
qdel(query_load_admins)
if (!no_update)
save_admin_backup()
sync_admins_with_db()
//load admins from backup file
if(dbfail)
if(!backup_file_json)
if(backup_file_json != null)
//already tried
return
var/backup_file = file2text("data/admins_backup.json")
if(backup_file == null)
log_world("Unable to locate admins backup file.")
return
backup_file_json = json_decode(backup_file)
for(var/backup_admin_ckey in backup_file_json["admins"])
var/skip
for(var/admin_ckey in GLOB.admin_datums + GLOB.deadmins)
if(ckey(admin_ckey) == ckey("[backup_admin_ckey]")) //this admin was already loaded from txt override
skip = TRUE
break
if(skip)
continue
new /datum/admins(ranks_from_rank_name(backup_file_json["admins"]["[backup_admin_ckey]"]), ckey("[backup_admin_ckey]"))
#ifdef TESTING
var/msg = "Admins Built:\n"
for(var/ckey in admin_datums)
var/rank
var/datum/admins/D = admin_datums[ckey]
if(D) rank = D.rank
msg += "\t[ckey] - [rank]\n"
for(var/ckey in GLOB.admin_datums)
var/datum/admins/D = GLOB.admin_datums[ckey]
msg += "\t[ckey] - [D.rank_names()]\n"
testing(msg)
#endif
return dbfail
#ifdef TESTING
/client/verb/changerank(newrank in admin_ranks)
if(holder)
holder.rank = newrank
holder.rights = admin_ranks[newrank]
else
holder = new /datum/admins(newrank,admin_ranks[newrank],ckey)
remove_admin_verbs()
holder.associate(src)
/proc/sync_ranks_with_db()
set waitfor = FALSE
/client/verb/changerights(newrights as num)
if(holder)
holder.rights = newrights
else
holder = new /datum/admins("testing",newrights,ckey)
remove_admin_verbs()
holder.associate(src)
if(IsAdminAdvancedProcCall())
to_chat(usr, "<span class='admin prefix'>Admin rank DB Sync blocked: Advanced ProcCall detected.</span>", confidential = TRUE)
return
#endif
var/list/sql_ranks = list()
for(var/datum/admin_rank/R as anything in GLOB.protected_ranks)
sql_ranks += list(list("rank" = R.name, "flags" = R.include_rights, "exclude_flags" = R.exclude_rights, "can_edit_flags" = R.can_edit_rights))
SSdbcore.MassInsert(format_table_name("admin_ranks"), sql_ranks, duplicate_key = TRUE)
update_everything_flag_in_db()
/proc/update_everything_flag_in_db()
for(var/datum/admin_rank/R as anything in GLOB.admin_ranks)
var/list/flags = list()
if(R.include_rights == R_EVERYTHING)
flags += "flags"
if(R.exclude_rights == R_EVERYTHING)
flags += "exclude_flags"
if(R.can_edit_rights == R_EVERYTHING)
flags += "can_edit_flags"
if(!flags.len)
continue
var/flags_to_check = flags.Join(" != [R_EVERYTHING] AND ") + " != [R_EVERYTHING]"
var/datum/db_query/query_check_everything_ranks = SSdbcore.NewQuery(
"SELECT flags, exclude_flags, can_edit_flags FROM [format_table_name("admin_ranks")] WHERE rank = :rank AND ([flags_to_check])",
list("rank" = R.name)
)
if(!query_check_everything_ranks.Execute())
qdel(query_check_everything_ranks)
return
if(query_check_everything_ranks.NextRow()) //no row is returned if the rank already has the correct flag value
var/flags_to_update = flags.Join(" = [R_EVERYTHING], ") + " = [R_EVERYTHING]"
var/datum/db_query/query_update_everything_ranks = SSdbcore.NewQuery(
"UPDATE [format_table_name("admin_ranks")] SET [flags_to_update] WHERE rank = :rank",
list("rank" = R.name)
)
if(!query_update_everything_ranks.Execute())
qdel(query_update_everything_ranks)
return
qdel(query_update_everything_ranks)
qdel(query_check_everything_ranks)
/proc/sync_admins_with_db()
if(IsAdminAdvancedProcCall())
to_chat(usr, "<span class='admin prefix'>Admin rank DB Sync blocked: Advanced ProcCall detected.</span>")
return
if(CONFIG_GET(flag/admin_legacy_system) || !SSdbcore.IsConnected()) //we're already using legacy system so there's nothing to save
return
sync_ranks_with_db()
var/list/sql_admins = list()
for(var/holder_ckey in GLOB.protected_admins)
var/datum/admins/holder = GLOB.protected_admins[holder_ckey]
sql_admins += list(list("ckey" = holder.target, "rank" = holder.rank_names()))
SSdbcore.MassInsert(format_table_name("admin"), sql_admins, duplicate_key = TRUE)
var/datum/db_query/query_admin_rank_update = SSdbcore.NewQuery("UPDATE [format_table_name("player")] AS p INNER JOIN [format_table_name("admin")] AS a ON p.ckey = a.ckey SET p.lastadminrank = a.rank")
query_admin_rank_update.Execute()
qdel(query_admin_rank_update)
/proc/save_admin_backup()
if(IsAdminAdvancedProcCall())
to_chat(usr, "<span class='admin prefix'>Admin rank DB Sync blocked: Advanced ProcCall detected.</span>")
return
if(CONFIG_GET(flag/admin_legacy_system)) //we're already using legacy system so there's nothing to save
return
//json format backup file generation stored per server
var/json_file = file("data/admins_backup.json")
var/list/file_data = list(
"ranks" = list(),
"admins" = list()
)
for(var/datum/admin_rank/R as anything in GLOB.admin_ranks)
file_data["ranks"]["[R.name]"] = list()
file_data["ranks"]["[R.name]"]["include rights"] = R.include_rights
file_data["ranks"]["[R.name]"]["exclude rights"] = R.exclude_rights
file_data["ranks"]["[R.name]"]["can edit rights"] = R.can_edit_rights
for(var/admin_ckey in GLOB.admin_datums + GLOB.deadmins)
var/datum/admins/admin = GLOB.admin_datums[admin_ckey]
if(!admin)
admin = GLOB.deadmins[admin_ckey]
if (!admin)
continue
file_data["admins"][admin_ckey] = admin.rank_names()
//admin.backup_connections()
fdel(json_file)
WRITE_FILE(json_file, json_encode(file_data, JSON_PRETTY_PRINT))

View File

@@ -55,7 +55,7 @@ var/datum/admin_secrets/admin_secrets = new()
return name
/datum/admin_secret_item/proc/can_view(var/mob/user)
return check_rights(permissions, 0, user)
return check_rights_for(user.client, permissions)
/datum/admin_secret_item/proc/can_execute(var/mob/user)
if(can_view(user))

View File

@@ -3,7 +3,7 @@ var/list/admin_verbs_default = list(
/datum/admins/proc/show_player_panel, //shows an interface for individual players, with various links (links require additional flags,
/client/proc/player_panel_new, //shows an interface for all players, with links to various panels,
/client/proc/player_panel,
/client/proc/deadmin_self, //destroys our own admin datum so we can play as a regular player,
/client/proc/deadmin, //destroys our own admin datum so we can play as a regular player,
/client/proc/hide_verbs, //hides all our adminverbs,
/client/proc/hide_most_verbs, //hides all our hideable adminverbs,
/client/proc/debug_variables, //allows us to -see- the variables of any instance in the game. +VAREDIT needed to modify,
@@ -273,7 +273,7 @@ var/list/admin_verbs_rejuv = list(
//verbs which can be hidden - needs work
var/list/admin_verbs_hideable = list(
/client/proc/deadmin_self,
/client/proc/deadmin,
// /client/proc/deadchat,
/datum/admins/proc/show_traitor_panel,
/datum/admins/proc/toggleenter,

View File

@@ -3,7 +3,7 @@ var/list/admin_verbs_default = list(
// /datum/admins/proc/show_player_panel, //shows an interface for individual players, with various links (links require additional flags, //VOREStation Remove,
// /client/proc/player_panel_new, //shows an interface for all players, with links to various panels, //VOREStation Remove,
// /client/proc/player_panel, //VOREStation Remove,
/client/proc/deadmin_self, //destroys our own admin datum so we can play as a regular player,
/client/proc/deadmin, //destroys our own admin datum so we can play as a regular player,
/client/proc/cmd_admin_say, //VOREStation Add,
/client/proc/cmd_mod_say, //VOREStation Add,
/client/proc/cmd_event_say, //VOREStation Add,
@@ -320,7 +320,7 @@ var/list/admin_verbs_rejuv = list(
//verbs which can be hidden - needs work
var/list/admin_verbs_hideable = list(
/client/proc/deadmin_self,
/client/proc/deadmin,
// /client/proc/deadchat,
/datum/admins/proc/show_traitor_panel,
/datum/admins/proc/toggleenter,
@@ -596,24 +596,25 @@ var/list/admin_verbs_event_manager = list(
/client/proc/add_admin_verbs()
if(holder)
var/rights = holder.rank_flags()
add_verb(src, admin_verbs_default)
if(holder.rights & R_BUILDMODE) add_verb(src, /client/proc/togglebuildmodeself)
if(holder.rights & R_ADMIN) add_verb(src, admin_verbs_admin)
if(holder.rights & R_BAN) add_verb(src, admin_verbs_ban)
if(holder.rights & R_FUN) add_verb(src, admin_verbs_fun)
if(holder.rights & R_SERVER) add_verb(src, admin_verbs_server)
if(holder.rights & R_DEBUG)
if(rights & R_BUILDMODE) add_verb(src, /client/proc/togglebuildmodeself)
if(rights & R_ADMIN) add_verb(src, admin_verbs_admin)
if(rights & R_BAN) add_verb(src, admin_verbs_ban)
if(rights & R_FUN) add_verb(src, admin_verbs_fun)
if(rights & R_SERVER) add_verb(src, admin_verbs_server)
if(rights & R_DEBUG)
add_verb(src, admin_verbs_debug)
if(CONFIG_GET(flag/debugparanoid) && !(holder.rights & R_ADMIN))
if(CONFIG_GET(flag/debugparanoid) && !(rights & R_ADMIN))
remove_verb(src, admin_verbs_paranoid_debug) //Right now it's just callproc but we can easily add others later on.
if(holder.rights & R_POSSESS) add_verb(src, admin_verbs_possess)
if(holder.rights & R_PERMISSIONS) add_verb(src, admin_verbs_permissions)
if(holder.rights & R_STEALTH) add_verb(src, /client/proc/stealth)
if(holder.rights & R_REJUVINATE) add_verb(src, admin_verbs_rejuv)
if(holder.rights & R_SOUNDS) add_verb(src, admin_verbs_sounds)
if(holder.rights & R_SPAWN) add_verb(src, admin_verbs_spawn)
if(holder.rights & R_MOD) add_verb(src, admin_verbs_mod)
if(holder.rights & R_EVENT) add_verb(src, admin_verbs_event_manager)
if(rights & R_POSSESS) add_verb(src, admin_verbs_possess)
if(rights & R_PERMISSIONS) add_verb(src, admin_verbs_permissions)
if(rights & R_STEALTH) add_verb(src, /client/proc/stealth)
if(rights & R_REJUVINATE) add_verb(src, admin_verbs_rejuv)
if(rights & R_SOUNDS) add_verb(src, admin_verbs_sounds)
if(rights & R_SPAWN) add_verb(src, admin_verbs_spawn)
if(rights & R_MOD) add_verb(src, admin_verbs_mod)
if(rights & R_EVENT) add_verb(src, admin_verbs_event_manager)
//CHOMPEdit Begin
/client/proc/remove_admin_verbs()

View File

@@ -206,7 +206,7 @@
if(!check_rights(R_ADMIN)) return
if(!warned_ckey || !istext(warned_ckey)) return
if(warned_ckey in admin_datums)
if(warned_ckey in GLOB.admin_datums)
to_chat(usr, span_warning("Error: warn(): You can't warn admins."))
return
@@ -335,36 +335,22 @@
log_admin("[key_name(usr)] used 'kill air'.")
message_admins(span_blue("[key_name_admin(usr)] used 'kill air'."), 1)
/client/proc/readmin_self()
set name = "Re-Admin self"
/client/proc/deadmin()
set name = "DeAdmin"
set category = "Admin.Misc"
set desc = "Shed your admin powers."
if(deadmin_holder)
deadmin_holder.reassociate()
log_admin("[src] re-admined themself.")
message_admins("[src] re-admined themself.", 1)
to_chat(src, span_filter_system(span_interface("You now have the keys to control the planet, or at least a small space station")))
remove_verb(src, /client/proc/readmin_self)
if(isobserver(mob))
var/mob/observer/dead/our_mob = mob
our_mob.visualnet?.addVisibility(our_mob, src)
/client/proc/deadmin_self()
set name = "De-admin self"
set category = "Admin.Misc"
if(holder)
if(tgui_alert(usr, "Confirm self-deadmin for the round? You can't re-admin yourself without someone promoting you.","Deadmin",list("Yes","No")) == "Yes")
log_admin("[src] deadmined themself.")
message_admins("[src] deadmined themself.", 1)
deadmin()
to_chat(src, span_filter_system(span_interface("You are now a normal player.")))
add_verb(src, /client/proc/readmin_self)
if(isobserver(mob))
var/mob/observer/dead/our_mob = mob
our_mob.visualnet?.removeVisibility(our_mob, src)
src.holder.deactivate()
to_chat(src, span_interface("You are now a normal player."))
log_admin("[key_name(src)] deadminned themselves.")
message_admins("[key_name_admin(src)] deadminned themselves.")
//BLACKBOX_LOG_ADMIN_VERB("Deadmin")
feedback_add_details("admin_verb","DAS") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc!
if(isobserver(mob))
var/mob/observer/dead/our_mob = mob
our_mob.visualnet?.removeVisibility(our_mob, src)
/client/proc/toggle_log_hrefs()
set name = "Toggle href logging"
set category = "Server.Config"
@@ -584,3 +570,144 @@
return
SSmotiontracker.hide_all = !SSmotiontracker.hide_all
log_admin("[key_name(usr)] changed the motion echo visibility to [SSmotiontracker.hide_all ? "hidden" : "visible"].")
/client/proc/adminorbit()
set category = "Fun.Event Kit"
set name = "Orbit Things"
set desc = "Makes something orbit around something else."
set popup_menu = FALSE
if(!check_rights(R_FUN))
return
var/center
var/atom/movable/orbiter
var/input
if(holder.marked_datum)
input = tgui_alert(usr, "You have \n[holder.marked_datum] marked, should this be the center of the orbit, or the orbiter?", "Orbit", list("Center", "Orbiter", "Neither"))
switch(input)
if("Center")
center = holder.marked_datum
if("Orbiter")
orbiter = holder.marked_datum
var/list/possible_things = list()
for(var/T as mob in view(view)) //Let's do mobs before objects
if(ismob(T))
possible_things |= T
for(var/T as obj in view(view))
if(isobj(T))
possible_things |= T
if(!center)
center = tgui_input_list(src, "What should act as the center of the orbit?", "Center", possible_things)
possible_things -= center
if(!orbiter)
orbiter = tgui_input_list(src, "What should act as the orbiter of the orbit?", "Orbiter", possible_things)
if(!center || !orbiter)
to_chat(usr, span_warning("A center of orbit and an orbiter must be configured. You can also do this by marking a target."))
return
if(center == orbiter)
to_chat(usr, span_warning("The center of the orbit cannot also be the orbiter."))
return
if(isturf(orbiter))
to_chat(usr, span_warning("The orbiter cannot be a turf. It can only be used as a center."))
return
var/distance = tgui_input_number(usr, "How large will their orbit radius be? (In pixels. 32 is 'near around a character)", "Orbit Radius", 32)
var/speed = tgui_input_number(usr, "How fast will they orbit (negative numbers spin clockwise)", "Orbit Speed", 20)
var/segments = tgui_input_number(usr, "How many segments will they have in their orbit? (3 is a triangle, 36 is a circle, etc)", "Orbit Segments", 36)
var/clock = FALSE
if(!distance)
distance = 32
if(!speed)
speed = 20
else if (speed < 0)
clock = TRUE
speed *= -1
if(!segments)
segments = 36
if(tgui_alert(usr, "\The [orbiter] will orbit around [center]. Is this okay?", "Confirm Orbit", list("Yes", "No")) == "Yes")
orbiter.orbit(center, distance, clock, speed, segments)
/client/proc/removetickets()
set name = "Security Tickets"
set category = "Admin.Investigate"
set desc = "Allows one to remove tickets from the global list."
if(!check_rights(R_ADMIN))
return
if(security_printer_tickets.len >= 1)
var/input = tgui_input_list(usr, "Which message?", "Security Tickets", security_printer_tickets)
if(!input)
return
if(tgui_alert(usr, "Do you want to remove the following message from the global list? \"[input]\"", "Remove Ticket", list("Yes", "No")) == "Yes")
security_printer_tickets -= input
log_and_message_admins("removed a security ticket from the global list: \"[input]\"", usr)
else
tgui_alert_async(usr, "The ticket list is empty.","Empty")
/client/proc/delbook()
set name = "Delete Book"
set desc = "Permamently deletes a book from the database."
set category = "Admin.Game"
if(!src.holder)
to_chat(src, "Only administrators may use this command.")
return
var/obj/machinery/librarycomp/our_comp
for(var/obj/machinery/librarycomp/l in world)
if(istype(l, /obj/machinery/librarycomp))
our_comp = l
break
if(!our_comp)
to_chat(usr, span_warning("Unable to locate a library computer to use for book deleting."))
return
var/dat = "<HEAD><TITLE>Book Inventory Management</TITLE></HEAD><BODY>\n"
dat += "<h3>ADMINISTRATIVE MANAGEMENT</h3>"
establish_db_connection()
if(!SSdbcore.IsConnected())
dat += span_red(span_bold("ERROR") + ": Unable to contact External Archive. Please contact your system administrator for assistance.")
else
dat += {"<A href='byond://?our_comp=\ref[our_comp];[HrefToken()];orderbyid=1'>(Order book by SS<sup>13</sup>BN)</A><BR><BR>
<table>
<tr><td><A href='byond://?our_comp=\ref[our_comp];[HrefToken()];sort=author>AUTHOR</A></td><td><A href='byond://?our_comp=\ref[our_comp];[HrefToken()];sort=title>TITLE</A></td><td><A href='byond://?our_comp=\ref[our_comp];[HrefToken()];sort=category>CATEGORY</A></td><td></td></tr>"}
var/datum/db_query/query = SSdbcore.NewQuery("SELECT id, author, title, category FROM library ORDER BY [our_comp.sortby]")
query.Execute()
var/show_admin_options = check_rights(R_ADMIN, show_msg = FALSE)
while(query.NextRow())
var/id = query.item[1]
var/author = query.item[2]
var/title = query.item[3]
var/category = query.item[4]
dat += "<tr><td>[author]</td><td>[title]</td><td>[category]</td><td>"
if(show_admin_options) // This isn't the only check, since you can just href-spoof press this button. Just to tidy things up.
dat += "<A href='byond://?our_comp=\ref[our_comp];[HrefToken()];delid=[id]'>\[Del\]</A>"
dat += "</td></tr>"
dat += "</table>"
qdel(query)
usr << browse("<html>[dat]</html>", "window=library")
onclose(usr, "library")
/client/proc/toggle_spawning_with_recolour()
set name = "Toggle Simple/Robot recolour verb"
set desc = "Makes it so new robots/simple_mobs spawn with a verb to recolour themselves for this round. You must set them separately."
set category = "Server.Game"
if(!check_rights(R_ADMIN|R_EVENT|R_FUN))
return
var/which = tgui_alert(usr, "Which do you want to toggle?", "Choose Recolour Toggle", list("Robot", "Simple Mob"))
switch(which)
if("Robot")
CONFIG_SET(flag/allow_robot_recolor, !CONFIG_GET(flag/allow_robot_recolor))
to_chat(usr, "You have [CONFIG_GET(flag/allow_robot_recolor) ? "enabled" : "disabled"] newly spawned cyborgs to spawn with the recolour verb")
if("Simple Mob")
CONFIG_SET(flag/allow_simple_mob_recolor, !CONFIG_GET(flag/allow_simple_mob_recolor))
to_chat(usr, "You have [CONFIG_GET(flag/allow_simple_mob_recolor) ? "enabled" : "disabled"] newly spawned simple mobs to spawn with the recolour verb")

View File

@@ -1,14 +0,0 @@
/datum/admins/proc/set_uplink(mob/living/carbon/human/H as mob)
set category = "Debug.Events"
set name = "Set Uplink"
set desc = "Allows admins to set up an uplink on a character. This will be required for a character to use telecrystals."
set popup_menu = FALSE
if(check_rights(R_ADMIN|R_DEBUG))
traitors.spawn_uplink(H)
H.mind.tcrystals = DEFAULT_TELECRYSTAL_AMOUNT
H.mind.accept_tcrystals = 1
var/msg = "[key_name(usr)] has given [H.ckey] an uplink."
message_admins(msg)
else
to_chat(usr, "You do not have access to this command.")

View File

@@ -1,13 +1,18 @@
GLOBAL_LIST_EMPTY(admin_datums)
GLOBAL_PROTECT(admin_datums)
GLOBAL_LIST_EMPTY(protected_admins)
GLOBAL_PROTECT(protected_admins)
GLOBAL_VAR_INIT(href_token, GenerateToken())
GLOBAL_PROTECT(href_token)
var/list/admin_datums = list()
/datum/admins
var/rank = "Temporary Admin"
var/client/owner = null
var/rights = 0
var/fakekey = null
var/list/datum/admin_rank/ranks
var/target
var/name = "nobody's admin datum (no rank)" //Makes for better runtimes
var/client/owner = null
var/fakekey = null
var/datum/marked_datum
@@ -18,49 +23,194 @@ var/list/admin_datums = list()
var/href_token
/// Link from the database pointing to the admin's feedback forum
var/cached_feedback_link
/datum/admins/New(initial_rank = "Temporary Admin", initial_rights = 0, ckey)
if(!ckey)
error("Admin datum created without a ckey argument. Datum has been deleted")
qdel(src)
var/deadmined
var/given_profiling = FALSE
/datum/admins/New(list/datum/admin_rank/ranks, ckey, force_active = FALSE, protected)
if(IsAdminAdvancedProcCall())
alert_to_permissions_elevation_attempt(usr)
if (!target) //only del if this is a true creation (and not just a New() proc call), other wise trialmins/coders could abuse this to deadmin other admins
QDEL_IN(src, 0)
CRASH("Admin proc call creation of admin datum")
return
if(!ckey)
QDEL_IN(src, 0)
CRASH("Admin datum created without a ckey")
if(!istype(ranks))
QDEL_IN(src, 0)
CRASH("Admin datum created with invalid ranks: [ranks] ([json_encode(ranks)])")
target = ckey
name = "[ckey]'s admin datum ([join_admin_ranks(ranks)])"
src.ranks = ranks
admincaster_signature = "[using_map.company_name] Officer #[rand(0,9)][rand(0,9)][rand(0,9)]"
href_token = GenerateToken()
rank = initial_rank
rights = initial_rights
admin_datums[ckey] = src
if(rights & R_DEBUG) //grant profile access
world.SetConfig("APP/admin", ckey, "role=admin")
if(protected)
GLOB.protected_admins[target] = src
activate()
/datum/admins/proc/associate(client/C)
if(istype(C))
owner = C
owner.holder = src
owner.add_admin_verbs() //TODO
owner.init_verbs() //re-initialize the verb list
GLOB.admins |= C
/datum/admins/Destroy()
if(IsAdminAdvancedProcCall())
alert_to_permissions_elevation_attempt(usr)
return QDEL_HINT_LETMELIVE
. = ..()
/datum/admins/proc/activate()
if(IsAdminAdvancedProcCall())
alert_to_permissions_elevation_attempt(usr)
return
GLOB.deadmins -= target
GLOB.admin_datums[target] = src
deadmined = FALSE
//plane_debug = new(src)
if (GLOB.directory[target])
associate(GLOB.directory[target]) //find the client for a ckey if they are connected and associate them with us
/datum/admins/proc/deactivate()
if(IsAdminAdvancedProcCall())
alert_to_permissions_elevation_attempt(usr)
return
GLOB.deadmins[target] = src
GLOB.admin_datums -= target
//QDEL_NULL(plane_debug)
deadmined = TRUE
var/client/client = owner || GLOB.directory[target]
if (!isnull(client))
disassociate()
add_verb(client, /client/proc/readmin)
//client.disable_combo_hud()
//client.update_special_keybinds()
/datum/admins/proc/associate(client/client)
if(IsAdminAdvancedProcCall())
alert_to_permissions_elevation_attempt(usr)
return
if(!istype(client))
return
if(client?.ckey != target)
var/msg = " has attempted to associate with [target]'s admin datum"
message_admins("[key_name_admin(client)][msg]")
log_admin("[key_name(client)][msg]")
return
if (deadmined)
activate()
owner = client
owner.holder = src
owner.add_admin_verbs()
remove_verb(owner, /client/proc/readmin)
owner.init_verbs() //re-initialize the verb list
//owner.update_special_keybinds()
GLOB.admins |= client
try_give_profiling()
/datum/admins/proc/disassociate()
if(IsAdminAdvancedProcCall())
alert_to_permissions_elevation_attempt(usr)
return
if(owner)
GLOB.admins -= owner
owner.remove_admin_verbs()
owner.init_verbs() //re-initialize the verb list
owner.deadmin_holder = owner.holder
// owner.init_verbs() //re-initialize the verb list
owner.holder = null
owner = null
/datum/admins/proc/reassociate()
if(owner)
GLOB.admins += owner
owner.holder = src
owner.deadmin_holder = null
owner.add_admin_verbs()
/// Returns the feedback forum thread for the admin holder's owner, as according to DB.
/datum/admins/proc/feedback_link()
// This intentionally does not follow the 10-second maximum TTL rule,
// as this can be reloaded through the Reload-Admins verb.
if (cached_feedback_link == NO_FEEDBACK_LINK)
return null
if (!isnull(cached_feedback_link))
return cached_feedback_link
if (!SSdbcore.IsConnected())
return FALSE
var/datum/db_query/feedback_query = SSdbcore.NewQuery("SELECT feedback FROM [format_table_name("admin")] WHERE ckey = '[owner.ckey]'")
if(!feedback_query.Execute())
log_sql("Error retrieving feedback link for [src]")
qdel(feedback_query)
return FALSE
if(!feedback_query.NextRow())
qdel(feedback_query)
return FALSE // no feedback link exists
cached_feedback_link = feedback_query.item[1] || NO_FEEDBACK_LINK
qdel(feedback_query)
if (cached_feedback_link == NO_FEEDBACK_LINK) // Because we don't want to send fake clickable links.
return null
return cached_feedback_link
/datum/admins/proc/check_for_rights(rights_required)
if(rights_required && !(rights_required & rank_flags()))
return FALSE
return TRUE
/datum/admins/proc/check_if_greater_rights_than_holder(datum/admins/other)
if(!other)
return TRUE //they have no rights
if(rank_flags() == R_EVERYTHING)
return TRUE //we have all the rights
if(src == other)
return TRUE //you always have more rights than yourself
if(rank_flags() != other.rank_flags())
if( (rank_flags() & other.rank_flags()) == other.rank_flags() )
return TRUE //we have all the rights they have and more
return FALSE
/// Get the rank name of the admin
/datum/admins/proc/rank_names()
return join_admin_ranks(ranks)
/// Get the rank flags of the admin
/datum/admins/proc/rank_flags()
var/combined_flags = NONE
for (var/datum/admin_rank/rank as anything in ranks)
combined_flags |= rank.rights
return combined_flags
/// Get the permissions this admin is allowed to edit on other ranks
/datum/admins/proc/can_edit_rights_flags()
var/combined_flags = NONE
for (var/datum/admin_rank/rank as anything in ranks)
combined_flags |= rank.can_edit_rights
return combined_flags
/datum/admins/proc/try_give_profiling()
if (CONFIG_GET(flag/forbid_admin_profiling))
return
if (given_profiling)
return
if (!(rank_flags() & R_DEBUG))
return
given_profiling = TRUE
world.SetConfig("APP/admin", owner.ckey, "role=admin")
/datum/admins/vv_edit_var(var_name, var_value)
if(var_name == NAMEOF(src, rights) || var_name == NAMEOF(src, owner) || var_name == NAMEOF(src, rank))
return FALSE
return ..()
//TODO: Proccall guard, when all try/catch are removed and WrapAdminProccall is ported.
return FALSE //nice try trialmin
/*
checks if usr is an admin with at least ONE of the flags in rights_required. (Note, they don't need all the flags)
@@ -69,45 +219,36 @@ if it doesn't return 1 and show_msg=1 it will prints a message explaining why th
generally it would be used like so:
/proc/admin_proc()
if(!check_rights(R_ADMIN)) return
to_world("you have enough rights!")
if(!check_rights(R_ADMIN))
return
to_chat(world, "you have enough rights!", confidential = TRUE)
NOTE: It checks usr by default. Supply the "user" argument if you wish to check for a specific mob.
NOTE: it checks usr! not src! So if you're checking somebody's rank in a proc which they did not call
you will have to do something like if(client.rights & R_ADMIN) yourself.
*/
/proc/check_rights(rights_required, show_msg=1, var/client/C = usr)
if(ismob(C))
var/mob/M = C
C = M.client
if(!C)
return FALSE
if(!(istype(C, /client))) // If we still didn't find a client, something is wrong.
return FALSE
if(!C.holder)
if(show_msg)
to_chat(C, span_filter_adminlog(span_warning("Error: You are not an admin.")))
return FALSE
if(rights_required)
if(rights_required & C.holder.rights)
/proc/check_rights(rights_required, show_msg=1)
if(usr?.client)
if (check_rights_for(usr.client, rights_required))
return TRUE
else
if(show_msg)
to_chat(C, span_filter_adminlog(span_warning("Error: You do not have sufficient rights to do that. You require one of the following flags:[rights2text(rights_required," ")].")))
return FALSE
else
return TRUE
to_chat(usr, "<font color='red'>Error: You do not have sufficient rights to do that. You require one of the following flags:[rights2text(rights_required," ")].</font>", confidential = TRUE)
return FALSE
//probably a bit iffy - will hopefully figure out a better solution
/proc/check_if_greater_rights_than(client/other)
if(usr && usr.client)
if(usr?.client)
if(usr.client.holder)
if(!other || !other.holder)
return 1
if(usr.client.holder.rights != other.holder.rights)
if( (usr.client.holder.rights & other.holder.rights) == other.holder.rights )
return 1 //we have all the rights they have and more
to_chat(usr, span_filter_adminlog(span_warning("Error: Cannot proceed. They have more or equal rights to us.")))
return 0
return TRUE
return usr.client.holder.check_if_greater_rights_than_holder(other.holder)
return FALSE
//This proc checks whether subject has at least ONE of the rights specified in rights_required.
/proc/check_rights_for(client/subject, rights_required)
if(subject?.holder)
return subject.holder.check_for_rights(rights_required)
return FALSE
/client/proc/mark_datum(datum/D)
if(!holder)
@@ -122,20 +263,6 @@ NOTE: It checks usr by default. Supply the "user" argument if you wish to check
set name = "Mark Object"
mark_datum(D)
/client/proc/deadmin()
if(holder)
holder.disassociate()
//qdel(holder)
return 1
//This proc checks whether subject has at least ONE of the rights specified in rights_required.
/proc/check_rights_for(client/subject, rights_required)
if(subject && subject.holder)
if(rights_required && !(rights_required & subject.holder.rights))
return 0
return 1
return 0
/proc/GenerateToken()
. = ""
for(var/I in 1 to 32)

View File

@@ -0,0 +1,561 @@
/client/proc/edit_admin_permissions()
set category = "Admin.Secrets"
set name = "Permissions Panel"
set desc = "Edit admin permissions"
if(!check_rights(R_PERMISSIONS))
return
usr.client.holder.edit_admin_permissions()
/datum/admins/proc/edit_admin_permissions(action, target, operation, page)
if(!check_rights(R_PERMISSIONS))
return
var/datum/asset/asset_cache_datum = get_asset_datum(/datum/asset/group/permissions)
asset_cache_datum.send(usr)
var/list/output = list("<link rel='stylesheet' type='text/css' href='[SSassets.transport.get_asset_url("panels.css")]'><a href='byond://?_src_=holder;[HrefToken()];editrightsbrowser=1'>\[Permissions\]</a>")
if(action)
output += " | <a href='byond://?_src_=holder;[HrefToken()];editrightsbrowserlog=1;editrightspage=0'>\[Log\]</a> | <a href='byond://?_src_=holder;[HrefToken()];editrightsbrowsermanage=1'>\[Management\]</a><hr style='background:#000000; border:0; height:3px'>"
else
output += "<br><a href='byond://?_src_=holder;[HrefToken()];editrightsbrowserlog=1;editrightspage=0'>\[Log\]</a><br><a href='byond://?_src_=holder;[HrefToken()];editrightsbrowsermanage=1'>\[Management\]</a>"
if(action == 1)
var/logcount = 0
var/logssperpage = 20
var/pagecount = 0
page = text2num(page)
var/datum/db_query/query_count_admin_logs = SSdbcore.NewQuery(
"SELECT COUNT(id) FROM [format_table_name("admin_log")] WHERE (:target IS NULL OR adminckey = :target) AND (:operation IS NULL OR operation = :operation)",
list("target" = target, "operation" = operation)
)
if(!query_count_admin_logs.warn_execute())
qdel(query_count_admin_logs)
return
if(query_count_admin_logs.NextRow())
logcount = text2num(query_count_admin_logs.item[1])
qdel(query_count_admin_logs)
if(logcount > logssperpage)
output += "<br><b>Page: </b>"
while(logcount > 0)
output += "|<a href='byond://?_src_=holder;[HrefToken()];editrightsbrowserlog=1;editrightstarget=[target];editrightsoperation=[operation];editrightspage=[pagecount]'>[pagecount == page ? "<b>\[[pagecount]\]</b>" : "\[[pagecount]\]"]</a>"
logcount -= logssperpage
pagecount++
output += "|"
var/datum/db_query/query_search_admin_logs = SSdbcore.NewQuery({"
SELECT
datetime,
round_id,
IFNULL((SELECT ckey FROM [format_table_name("erro_player")] WHERE ckey = adminckey), adminckey),
operation,
IF(ckey IS NULL, target, ckey),
log
FROM [format_table_name("admin_log")]
LEFT JOIN [format_table_name("erro_player")] ON target = ckey
WHERE (:target IS NULL OR ckey = :target) AND (:operation IS NULL OR operation = :operation)
ORDER BY datetime DESC
LIMIT :skip, :take
"}, list("target" = target, "operation" = operation, "skip" = logssperpage * page, "take" = logssperpage))
if(!query_search_admin_logs.warn_execute())
qdel(query_search_admin_logs)
return
while(query_search_admin_logs.NextRow())
var/datetime = query_search_admin_logs.item[1]
var/round_id = query_search_admin_logs.item[2]
var/admin_key = query_search_admin_logs.item[3]
operation = query_search_admin_logs.item[4]
target = query_search_admin_logs.item[5]
var/log = query_search_admin_logs.item[6]
output += "<p style='margin:0px'><b>[datetime] | Round ID [round_id] | Admin [admin_key] | Operation [operation] on [target]</b><br>[log]</p><hr style='background:#000000; border:0; height:3px'>"
qdel(query_search_admin_logs)
if(action == 2)
output += "<h3>Admin ckeys with invalid ranks</h3>"
var/datum/db_query/query_check_admin_errors = SSdbcore.NewQuery("SELECT IFNULL((SELECT ckey FROM [format_table_name("erro_player")] WHERE [format_table_name("erro_player")].ckey = [format_table_name("admin")].ckey), ckey), [format_table_name("admin")].`rank` FROM [format_table_name("admin")] LEFT JOIN [format_table_name("admin_ranks")] ON [format_table_name("admin_ranks")].`rank` = [format_table_name("admin")].`rank` WHERE [format_table_name("admin_ranks")].`rank` IS NULL")
if(!query_check_admin_errors.warn_execute())
qdel(query_check_admin_errors)
return
while(query_check_admin_errors.NextRow())
var/admin_key = query_check_admin_errors.item[1]
var/admin_rank = query_check_admin_errors.item[2]
output += "[admin_key] has non-existent rank [admin_rank] | <a href='byond://?_src_=holder;[HrefToken()];editrightsbrowsermanage=1;editrightschange=[admin_key]'>\[Change Rank\]</a> | <a href='byond://?_src_=holder;[HrefToken()];editrightsbrowsermanage=1;editrightsremove=[admin_key]'>\[Remove\]</a>"
output += "<hr style='background:#000000; border:0; height:1px'>"
qdel(query_check_admin_errors)
output += "<h3>Unused ranks</h3>"
var/datum/db_query/query_check_unused_rank = SSdbcore.NewQuery("SELECT [format_table_name("admin_ranks")].`rank`, flags, exclude_flags, can_edit_flags FROM [format_table_name("admin_ranks")] LEFT JOIN [format_table_name("admin")] ON [format_table_name("admin")].`rank` = [format_table_name("admin_ranks")].`rank` WHERE [format_table_name("admin")].`rank` IS NULL")
if(!query_check_unused_rank.warn_execute())
qdel(query_check_unused_rank)
return
while(query_check_unused_rank.NextRow())
var/admin_rank = query_check_unused_rank.item[1]
output += {"Rank [admin_rank] is not held by any admin | <a href='byond://?_src_=holder;[HrefToken()];editrightsbrowsermanage=1;editrightsremoverank=[admin_rank]'>\[Remove\]</a>
<br>Permissions: [rights2text(text2num(query_check_unused_rank.item[2])," ")]
<br>Denied: [rights2text(text2num(query_check_unused_rank.item[3])," ", "-")]
<br>Allowed to edit: [rights2text(text2num(query_check_unused_rank.item[4])," ", "*")]
<hr style='background:#000000; border:0; height:1px'>"}
qdel(query_check_unused_rank)
else if(!action)
output += {"
<head>
<meta http-equiv='Content-Type' content='text/html; charset=UTF-8'>
<title>Permissions Panel</title>
<script type='text/javascript' src='[SSassets.transport.get_asset_url("search.js")]'></script>
</head>
<body onload='selectTextField();updateSearch();'>
<div id='main'><table id='searchable' cellspacing='0'>
<tr class='title'>
<th style='width:150px;'>CKEY <a class='small' href='byond://?src=[REF(src)];[HrefToken()];editrights=add'>\[+\]</a></th>
<th style='width:125px;'>RANK</th>
<th>PERMISSIONS</th>
</tr>
"}
for(var/adm_ckey in GLOB.admin_datums+GLOB.deadmins)
var/datum/admins/D = GLOB.admin_datums[adm_ckey]
if(!D)
D = GLOB.deadmins[adm_ckey]
if (!D)
continue
var/deadminlink = ""
if(D.owner)
adm_ckey = D.owner.key
if (D.deadmined)
deadminlink = " <a class='small' href='byond://?src=[REF(src)];[HrefToken()];editrights=activate;key=[adm_ckey]'>\[RA\]</a>"
else
deadminlink = " <a class='small' href='byond://?src=[REF(src)];[HrefToken()];editrights=deactivate;key=[adm_ckey]'>\[DA\]</a>"
var/verify_link = ""
//if (D.blocked_by_2fa)
// verify_link += " | <a class='small' href='byond://?src=[REF(src)];[HrefToken()];editrights=verify;key=[adm_ckey]'>\[2FA VERIFY\]</a>"
output += "<tr>"
output += "<td style='text-align:center;'>[adm_ckey]<br>[deadminlink]<a class='small' href='byond://?src=[REF(src)];[HrefToken()];editrights=remove;key=[adm_ckey]'>\[-\]</a><a class='small' href='byond://?src=[REF(src)];[HrefToken()];editrights=sync;key=[adm_ckey]'>\[SYNC TGDB\]</a>[verify_link]</td>"
output += "<td><a href='byond://?src=[REF(src)];[HrefToken()];editrights=rank;key=[adm_ckey]'>[D.rank_names()]</a></td>"
output += "<td><a class='small' href='byond://?src=[REF(src)];[HrefToken()];editrights=permissions;key=[adm_ckey]'>[rights2text(D.rank_flags(), " ")]</a></td>"
output += "</tr>"
output += "</table></div><div id='top'><b>Search:</b> <input type='text' id='filter' value='' style='width:70%;' onkeyup='updateSearch();'></div></body>"
if(QDELETED(usr))
return
usr << browse("<!DOCTYPE html><html>[jointext(output, "")]</html>","window=editrights;size=1000x650")
/datum/admins/proc/edit_rights_topic(list/href_list)
if(!check_rights(R_PERMISSIONS))
message_admins("[key_name_admin(usr)] attempted to edit admin permissions without sufficient rights.")
log_admin("[key_name(usr)] attempted to edit admin permissions without sufficient rights.")
return
if(IsAdminAdvancedProcCall())
to_chat(usr, "<span class='admin prefix'>Admin Edit blocked: Advanced ProcCall detected.</span>", confidential = TRUE)
return
var/datum/asset/permissions_assets = get_asset_datum(/datum/asset/simple/namespaced/common)
permissions_assets.send(usr.client)
var/admin_key = href_list["key"]
var/admin_ckey = ckey(admin_key)
var/task = href_list["editrights"]
var/datum/admins/target_admin_datum = GLOB.admin_datums[admin_ckey]
if(!target_admin_datum)
target_admin_datum = GLOB.deadmins[admin_ckey]
if (!target_admin_datum && task != "add")
return
var/use_db
var/skip
var/legacy_only
if(task == "activate" || task == "deactivate" || task == "sync" || task == "verify")
skip = TRUE
if(!CONFIG_GET(flag/admin_legacy_system) && CONFIG_GET(flag/protect_legacy_admins) && task == "rank")
if(admin_ckey in GLOB.protected_admins)
to_chat(usr, "<span class='admin prefix'>Editing the rank of this admin is blocked by server configuration.</span>", confidential = TRUE)
return
if(!CONFIG_GET(flag/admin_legacy_system) && CONFIG_GET(flag/protect_legacy_ranks) && task == "permissions")
if((target_admin_datum.ranks & GLOB.protected_ranks).len > 0)
to_chat(usr, "<span class='admin prefix'>Editing the flags of this rank is blocked by server configuration.</span>", confidential = TRUE)
return
if(CONFIG_GET(flag/load_legacy_ranks_only) && (task == "add" || task == "rank" || task == "permissions"))
to_chat(usr, "<span class='admin prefix'>Database rank loading is disabled, only temporary changes can be made to a rank's permissions and permanently creating a new rank is blocked.</span>", confidential = TRUE)
legacy_only = TRUE
//if(check_rights(R_DBRANKS, FALSE))
if(!skip)
if(!SSdbcore.Connect())
to_chat(usr, span_danger("Unable to connect to database, changes are temporary only."), confidential = TRUE)
use_db = FALSE
else
use_db = tgui_alert(usr,"Permanent changes are saved to the database for future rounds, temporary changes will affect only the current round", "Permanent or Temporary?", list("Permanent", "Temporary", "Cancel"))
if(use_db == "Cancel")
return
if(use_db == "Permanent")
use_db = TRUE
else
use_db = FALSE
if(QDELETED(usr))
return
if(target_admin_datum && (task != "sync" && task != "verify") && !check_if_greater_rights_than_holder(target_admin_datum))
message_admins("[key_name_admin(usr)] attempted to change the rank of [admin_key] without sufficient rights.")
log_admin("[key_name(usr)] attempted to change the rank of [admin_key] without sufficient rights.")
return
switch(task)
if("add")
admin_ckey = add_admin(admin_ckey, admin_key, use_db)
if(!admin_ckey)
return
if(!admin_key) // Prevents failures in logging admin rank changes.
admin_key = admin_ckey
change_admin_rank(admin_ckey, admin_key, use_db, null, legacy_only)
if("remove")
remove_admin(admin_ckey, admin_key, use_db, target_admin_datum)
if("rank")
change_admin_rank(admin_ckey, admin_key, use_db, target_admin_datum, legacy_only)
if("permissions")
change_admin_flags(admin_ckey, admin_key, target_admin_datum)
if("activate")
force_readmin(admin_key, target_admin_datum)
if("deactivate")
force_deadmin(admin_key, target_admin_datum)
if("sync")
sync_lastadminrank(admin_ckey, admin_key, target_admin_datum)
/*
if("verify")
var/msg = "has authenticated [admin_ckey]"
message_admins("[key_name_admin(usr)] [msg]")
log_admin("[key_name(usr)] [msg]")
target_admin_datum.bypass_2fa = TRUE
target_admin_datum.associate(GLOB.directory[admin_ckey])
*/
edit_admin_permissions()
/datum/admins/proc/add_admin(admin_ckey, admin_key, use_db)
if(admin_ckey)
. = admin_ckey
else
admin_key = input("New admin's key","Admin key") as text|null
. = ckey(admin_key)
if(!.)
return FALSE
if(!admin_ckey && (. in (GLOB.admin_datums+GLOB.deadmins)))
to_chat(usr, span_danger("[admin_key] is already an admin."), confidential = TRUE)
return FALSE
if(use_db)
//if an admin exists without a datum they won't be caught by the above
var/datum/db_query/query_admin_in_db = SSdbcore.NewQuery(
"SELECT 1 FROM [format_table_name("admin")] WHERE ckey = :ckey",
list("ckey" = .)
)
if(!query_admin_in_db.warn_execute())
qdel(query_admin_in_db)
return FALSE
if(query_admin_in_db.NextRow())
qdel(query_admin_in_db)
to_chat(usr, span_danger("[admin_key] already listed in admin database. Check the Management tab if they don't appear in the list of admins."), confidential = TRUE)
return FALSE
qdel(query_admin_in_db)
var/datum/db_query/query_add_admin = SSdbcore.NewQuery(
"INSERT INTO [format_table_name("admin")] (ckey, `rank`) VALUES (:ckey, 'NEW ADMIN')",
list("ckey" = .)
)
if(!query_add_admin.warn_execute())
qdel(query_add_admin)
return FALSE
qdel(query_add_admin)
var/datum/db_query/query_add_admin_log = SSdbcore.NewQuery({"
INSERT INTO [format_table_name("admin_log")] (datetime, round_id, adminckey, adminip, operation, target, log)
VALUES (NOW(), :round_id, :adminckey, INET_ATON(:adminip), 'add admin', :target, CONCAT('New admin added: ', :target))
"}, list("round_id" = "[GLOB.round_id]", "adminckey" = usr.ckey, "adminip" = usr.client.address, "target" = .))
if(!query_add_admin_log.warn_execute())
qdel(query_add_admin_log)
return FALSE
qdel(query_add_admin_log)
/datum/admins/proc/remove_admin(admin_ckey, admin_key, use_db, datum/admins/D)
if(tgui_alert(usr,"Are you sure you want to remove [admin_ckey]?","Confirm Removal",list("Do it","Cancel")) == "Do it")
GLOB.admin_datums -= admin_ckey
GLOB.deadmins -= admin_ckey
if(D)
D.disassociate()
var/m1 = "[key_name_admin(usr)] removed [admin_key] from the admins list [use_db ? "permanently" : "temporarily"]"
var/m2 = "[key_name(usr)] removed [admin_key] from the admins list [use_db ? "permanently" : "temporarily"]"
if(use_db)
var/datum/db_query/query_add_rank = SSdbcore.NewQuery(
"DELETE FROM [format_table_name("admin")] WHERE ckey = :ckey",
list("ckey" = admin_ckey)
)
if(!query_add_rank.warn_execute())
qdel(query_add_rank)
return
qdel(query_add_rank)
var/datum/db_query/query_add_rank_log = SSdbcore.NewQuery({"
INSERT INTO [format_table_name("admin_log")] (datetime, round_id, adminckey, adminip, operation, target, log)
VALUES (NOW(), :round_id, :adminckey, INET_ATON(:adminip), 'remove admin', :admin_ckey, CONCAT('Admin removed: ', :admin_ckey))
"}, list("round_id" = "[GLOB.round_id]", "adminckey" = usr.ckey, "adminip" = usr.client.address, "admin_ckey" = admin_ckey))
if(!query_add_rank_log.warn_execute())
qdel(query_add_rank_log)
return
qdel(query_add_rank_log)
sync_lastadminrank(admin_ckey, admin_key)
message_admins(m1)
log_admin(m2)
/datum/admins/proc/force_readmin(admin_key, datum/admins/D)
if(!D || !D.deadmined)
return
D.activate()
message_admins("[key_name_admin(usr)] forcefully readmined [admin_key]")
log_admin("[key_name(usr)] forcefully readmined [admin_key]")
/datum/admins/proc/force_deadmin(admin_key, datum/admins/D)
if(!D || D.deadmined)
return
message_admins("[key_name_admin(usr)] forcefully deadmined [admin_key]")
log_admin("[key_name(usr)] forcefully deadmined [admin_key]")
D.deactivate() //after logs so the deadmined admin can see the message.
#define RANK_DONE ":) I'm Done"
/datum/admins/proc/change_admin_rank(admin_ckey, admin_key, use_db, datum/admins/D, legacy_only)
if(!check_rights(R_PERMISSIONS))
return
var/list/rank_names = list()
if(!use_db || (use_db && !legacy_only))
rank_names += "*New Rank*"
for(var/datum/admin_rank/admin_rank as anything in GLOB.admin_ranks)
if((admin_rank.rights & usr.client.holder.can_edit_rights_flags()) == admin_rank.rights)
rank_names[admin_rank.name] = admin_rank
var/list/new_rank_names = list()
var/list/custom_ranks = list()
while (TRUE)
var/list/display_rank_names = list(RANK_DONE)
if (new_rank_names.len > 0)
display_rank_names += "** SELECTED **"
for (var/rank_name in new_rank_names)
display_rank_names += rank_name
display_rank_names += "---------"
for (var/rank_name in rank_names)
if (!(rank_name in display_rank_names))
display_rank_names += rank_name
var/next_rank = input("Please select a rank, or select [RANK_DONE] if you are finished.") as null|anything in display_rank_names
if (isnull(next_rank))
return
if (next_rank == RANK_DONE)
break
// They clicked "** SELECTED **" or something silly.
if (!(next_rank in rank_names))
continue
if (next_rank in new_rank_names)
new_rank_names -= next_rank
continue
if (next_rank == "*New Rank*")
var/new_rank_name = input("Please input a new rank", "New custom rank") as text|null
if (!new_rank_name)
return
var/datum/admin_rank/custom_rank = rank_names[new_rank_name]
if (isnull(custom_rank))
if (D)
custom_rank = new(new_rank_name, D.rank_flags())
else
custom_rank = new(new_rank_name)
GLOB.admin_ranks += custom_rank
custom_ranks += custom_rank
new_rank_names += new_rank_name
new_rank_names += next_rank
var/list/new_ranks = list()
for (var/datum/admin_rank/admin_rank as anything in GLOB.admin_ranks)
if (admin_rank.name in new_rank_names)
new_ranks += admin_rank
new_rank_names -= admin_rank.name
if (new_rank_names.len == 0)
break
var/joined_rank = join_admin_ranks(new_ranks)
var/m1 = "[key_name_admin(usr)] edited the admin rank of [admin_key] to [joined_rank] [use_db ? "permanently" : "temporarily"]"
var/m2 = "[key_name(usr)] edited the admin rank of [admin_key] to [joined_rank] [use_db ? "permanently" : "temporarily"]"
if(use_db)
//if a player was tempminned before having a permanent change made to their rank they won't yet be in the db
var/old_rank
var/datum/db_query/query_admin_in_db = SSdbcore.NewQuery(
"SELECT `rank` FROM [format_table_name("admin")] WHERE ckey = :admin_ckey",
list("admin_ckey" = admin_ckey)
)
if(!query_admin_in_db.warn_execute())
qdel(query_admin_in_db)
return
if(!query_admin_in_db.NextRow())
add_admin(admin_ckey, admin_key, TRUE)
old_rank = "NEW ADMIN"
else
old_rank = query_admin_in_db.item[1]
qdel(query_admin_in_db)
for (var/datum/admin_rank/custom_rank in custom_ranks)
//similarly if a temp rank is created it won't be in the db if someone is permanently changed to it
var/datum/db_query/query_rank_in_db = SSdbcore.NewQuery(
"SELECT 1 FROM [format_table_name("admin_ranks")] WHERE `rank` = :new_rank",
list("new_rank" = custom_rank.name)
)
if(!query_rank_in_db.warn_execute())
qdel(query_rank_in_db)
return
if(!query_rank_in_db.NextRow())
QDEL_NULL(query_rank_in_db)
var/datum/db_query/query_add_rank = SSdbcore.NewQuery({"
INSERT INTO [format_table_name("admin_ranks")] (`rank`, flags, exclude_flags, can_edit_flags)
VALUES (:new_rank, '0', '0', '0')
"}, list("new_rank" = custom_rank.name))
if(!query_add_rank.warn_execute())
qdel(query_add_rank)
return
qdel(query_add_rank)
var/datum/db_query/query_add_rank_log = SSdbcore.NewQuery({"
INSERT INTO [format_table_name("admin_log")] (datetime, round_id, adminckey, adminip, operation, target, log)
VALUES (NOW(), :round_id, :adminckey, INET_ATON(:adminip), 'add rank', :new_rank, CONCAT('New rank added: ', :new_rank))
"}, list("round_id" = "[GLOB.round_id]", "adminckey" = usr.ckey, "adminip" = usr.client.address, "new_rank" = custom_rank.name))
if(!query_add_rank_log.warn_execute())
qdel(query_add_rank_log)
return
qdel(query_add_rank_log)
qdel(query_rank_in_db)
var/datum/db_query/query_change_rank = SSdbcore.NewQuery(
"UPDATE [format_table_name("admin")] SET `rank` = :new_rank WHERE ckey = :admin_ckey",
list("new_rank" = joined_rank, "admin_ckey" = admin_ckey)
)
if(!query_change_rank.warn_execute())
qdel(query_change_rank)
return
qdel(query_change_rank)
var/datum/db_query/query_change_rank_log = SSdbcore.NewQuery({"
INSERT INTO [format_table_name("admin_log")] (datetime, round_id, adminckey, adminip, operation, target, log)
VALUES (NOW(), :round_id, :adminckey, INET_ATON(:adminip), 'change admin rank', :target, CONCAT('Rank of ', :target, ' changed from ', :old_rank, ' to ', :new_rank))
"}, list("round_id" = "[GLOB.round_id]", "adminckey" = usr.ckey, "adminip" = usr.client.address, "target" = admin_ckey, "old_rank" = old_rank, "new_rank" = joined_rank))
if(!query_change_rank_log.warn_execute())
qdel(query_change_rank_log)
return
qdel(query_change_rank_log)
if(D) //they were previously an admin
D.disassociate() //existing admin needs to be disassociated
D.ranks = new_ranks //set the admin_rank as our rank
//D.bypass_2fa = TRUE // Another admin has cleared us
var/client/C = GLOB.directory[admin_ckey]
D.associate(C)
else
D = new(new_ranks, admin_ckey) //new admin
//D.bypass_2fa = TRUE // Another admin has cleared us
D.activate()
message_admins(m1)
log_admin(m2)
#undef RANK_DONE
/datum/admins/proc/change_admin_flags(admin_ckey, admin_key, datum/admins/admin_holder)
var/new_flags = input_bitfield(
usr,
"Admin rights<br>This will affect only the current admin [admin_key]",
"admin_flags",
admin_holder.rank_flags(),
350,
590,
allowed_edit_list = usr.client.holder.can_edit_rights_flags(),
)
admin_holder.disassociate()
if (findtext(admin_holder.rank_names(), "([admin_ckey])"))
var/datum/admin_rank/rank = admin_holder.ranks[1]
rank.rights = new_flags
rank.include_rights = new_flags
rank.exclude_rights = NONE
rank.can_edit_rights = rank.can_edit_rights
else
// Not a modified subrank, need to duplicate the admin_rank datum to prevent modifying others too.
var/datum/admin_rank/new_admin_rank = new(
/* init_name = */ "[admin_holder.rank_names()]([admin_ckey])",
/* init_rights = */ new_flags,
// rank_flags() includes the exclude rights, so we no longer need to handle them separately.
/* init_exclude_rights = */ NONE,
/* init_edit_rights = */ admin_holder.can_edit_rights_flags(),
)
admin_holder.ranks = list(new_admin_rank)
var/log = "[key_name(usr)] has updated the admin rights of [admin_ckey] into [rights2text(new_flags)]"
message_admins(log)
log_admin(log)
var/client/admin_client = GLOB.directory[admin_ckey]
admin_holder.associate(admin_client)
/datum/admins/proc/remove_rank(admin_rank)
if(!admin_rank)
return
for(var/datum/admin_rank/R in GLOB.admin_ranks)
if(R.name == admin_rank && (!(R.rights & usr.client.holder.can_edit_rights_flags()) == R.rights))
to_chat(usr, "<span class='admin prefix'>You don't have edit rights to all the rights this rank has, rank deletion not permitted.</span>", confidential = TRUE)
return
if(!CONFIG_GET(flag/admin_legacy_system) && CONFIG_GET(flag/protect_legacy_ranks) && (admin_rank in GLOB.protected_ranks))
to_chat(usr, "<span class='admin prefix'>Deletion of protected ranks is not permitted, it must be removed from admin_ranks.txt.</span>", confidential = TRUE)
return
if(CONFIG_GET(flag/load_legacy_ranks_only))
to_chat(usr, "<span class='admin prefix'>Rank deletion not permitted while database rank loading is disabled.</span>", confidential = TRUE)
return
var/datum/db_query/query_admins_with_rank = SSdbcore.NewQuery(
"SELECT 1 FROM [format_table_name("admin")] WHERE `rank` = :admin_rank",
list("admin_rank" = admin_rank)
)
if(!query_admins_with_rank.warn_execute())
qdel(query_admins_with_rank)
return
if(query_admins_with_rank.NextRow())
qdel(query_admins_with_rank)
to_chat(usr, span_danger("Error: Rank deletion attempted while rank still used; Tell a coder, this shouldn't happen."), confidential = TRUE)
return
qdel(query_admins_with_rank)
if(tgui_alert(usr,"Are you sure you want to remove [admin_rank]?","Confirm Removal",list("Do it","Cancel")) == "Do it")
var/m1 = "[key_name_admin(usr)] removed rank [admin_rank] permanently"
var/m2 = "[key_name(usr)] removed rank [admin_rank] permanently"
var/datum/db_query/query_add_rank = SSdbcore.NewQuery(
"DELETE FROM [format_table_name("admin_ranks")] WHERE `rank` = :admin_rank",
list("admin_rank" = admin_rank)
)
if(!query_add_rank.warn_execute())
qdel(query_add_rank)
return
qdel(query_add_rank)
var/datum/db_query/query_add_rank_log = SSdbcore.NewQuery({"
INSERT INTO [format_table_name("admin_log")] (datetime, round_id, adminckey, adminip, operation, target, log)
VALUES (NOW(), :round_id, :adminckey, INET_ATON(:adminip), 'remove rank', :admin_rank, CONCAT('Rank removed: ', :admin_rank))
"}, list("round_id" = "[GLOB.round_id]", "adminckey" = usr.ckey, "adminip" = usr.client.address, "admin_rank" = admin_rank))
if(!query_add_rank_log.warn_execute())
qdel(query_add_rank_log)
return
qdel(query_add_rank_log)
message_admins(m1)
log_admin(m2)
/datum/admins/proc/sync_lastadminrank(admin_ckey, admin_key, datum/admins/D)
var/sqlrank = "Player"
if (D)
sqlrank = D.rank_names()
var/datum/db_query/query_sync_lastadminrank = SSdbcore.NewQuery(
"UPDATE [format_table_name("erro_player")] SET lastadminrank = :rank WHERE ckey = :ckey",
list("rank" = sqlrank, "ckey" = admin_ckey)
)
if(!query_sync_lastadminrank.warn_execute())
qdel(query_sync_lastadminrank)
return
qdel(query_sync_lastadminrank)
to_chat(usr, span_admin("Sync of [admin_key] successful."), confidential = TRUE)

View File

@@ -1,158 +0,0 @@
/client/proc/edit_admin_permissions()
set category = "Admin.Secrets"
set name = "Permissions Panel"
set desc = "Edit admin permissions"
if(!check_rights(R_PERMISSIONS)) return
usr.client.holder.edit_admin_permissions()
/datum/admins/proc/edit_admin_permissions()
if(!check_rights(R_PERMISSIONS)) return
var/output = {"<!DOCTYPE html>
<html>
<head>
<title>Permissions Panel</title>
<script type='text/javascript' src='search.js'></script>
<link rel='stylesheet' type='text/css' href='panels.css'>
</head>
<body onload='selectTextField();updateSearch();'>
<div id='main'><table id='searchable' cellspacing='0'>
<tr class='title'>
<th style='width:125px;text-align:right;'>CKEY <a class='small' href='byond://?src=\ref[src];[HrefToken()];editrights=add'>\[+\]</a></th>
<th style='width:125px;'>RANK</th><th style='width:100%;'>PERMISSIONS</th>
</tr>
"}
for(var/adm_ckey in admin_datums)
var/datum/admins/D = admin_datums[adm_ckey]
if(!D) continue
var/rank = D.rank ? D.rank : "*none*"
var/rights = rights2text(D.rights," ")
if(!rights) rights = "*none*"
output += "<tr>"
output += "<td style='text-align:right;'>[adm_ckey] <a class='small' href='byond://?src=\ref[src];[HrefToken()];editrights=remove;ckey=[adm_ckey]'>\[-\]</a></td>"
output += "<td><a href='byond://?src=\ref[src];[HrefToken()];editrights=rank;ckey=[adm_ckey]'>[rank]</a></td>"
output += "<td><a class='small' href='byond://?src=\ref[src];[HrefToken()];editrights=permissions;ckey=[adm_ckey]'>[rights]</a></td>"
output += "</tr>"
output += {"
</table></div>
<div id='top'><b>Search:</b> <input type='text' id='filter' value='' style='width:70%;' onkeyup='updateSearch();'></div>
</body>
</html>"}
usr << browse(output,"window=editrights;size=600x500")
/datum/admins/proc/log_admin_rank_modification(var/adm_ckey, var/new_rank)
if(CONFIG_GET(flag/admin_legacy_system)) return
if(!usr.client)
return
if(!usr.client.holder || !(usr.client.holder.rights & R_PERMISSIONS))
to_chat(usr, span_filter_adminlog("[span_red("You do not have permission to do this!")]"))
return
establish_db_connection()
if(!SSdbcore.IsConnected())
to_chat(usr, span_filter_adminlog("[span_red("Failed to establish database connection")]"))
return
if(!adm_ckey || !new_rank)
return
adm_ckey = ckey(adm_ckey)
if(!adm_ckey)
return
if(!istext(adm_ckey) || !istext(new_rank))
return
var/datum/db_query/select_query = SSdbcore.NewQuery("SELECT id FROM erro_admin WHERE ckey = '[adm_ckey]'")
select_query.Execute()
var/new_admin = 1
var/admin_id
while(select_query.NextRow())
new_admin = 0
admin_id = text2num(select_query.item[1])
qdel(select_query)
if(new_admin)
var/datum/db_query/insert_query = SSdbcore.NewQuery("INSERT INTO `erro_admin` (`id`, `ckey`, `rank`, `level`, `flags`) VALUES (null, '[adm_ckey]', '[new_rank]', -1, 0)")
insert_query.Execute()
qdel(insert_query)
var/datum/db_query/log_query = SSdbcore.NewQuery("INSERT INTO `test`.`erro_admin_log` (`id` ,`datetime` ,`adminckey` ,`adminip` ,`log` ) VALUES (NULL , NOW( ) , '[usr.ckey]', '[usr.client.address]', 'Added new admin [adm_ckey] to rank [new_rank]');")
log_query.Execute()
qdel(log_query)
to_chat(usr, span_filter_adminlog("[span_blue("New admin added.")]"))
else
if(!isnull(admin_id) && isnum(admin_id))
var/datum/db_query/insert_query = SSdbcore.NewQuery("UPDATE `erro_admin` SET rank = '[new_rank]' WHERE id = [admin_id]")
insert_query.Execute()
qdel(insert_query)
var/datum/db_query/log_query = SSdbcore.NewQuery("INSERT INTO `test`.`erro_admin_log` (`id` ,`datetime` ,`adminckey` ,`adminip` ,`log` ) VALUES (NULL , NOW( ) , '[usr.ckey]', '[usr.client.address]', 'Edited the rank of [adm_ckey] to [new_rank]');")
log_query.Execute()
qdel(log_query)
to_chat(usr, span_filter_adminlog("[span_blue("Admin rank changed.")]"))
/datum/admins/proc/log_admin_permission_modification(var/adm_ckey, var/new_permission)
if(CONFIG_GET(flag/admin_legacy_system)) return
if(!usr.client)
return
if(!usr.client.holder || !(usr.client.holder.rights & R_PERMISSIONS))
to_chat(usr, span_filter_adminlog("[span_red(">You do not have permission to do this!")]"))
return
establish_db_connection()
if(!SSdbcore.IsConnected())
to_chat(usr, span_filter_adminlog("[span_red("Failed to establish database connection!")]"))
return
if(!adm_ckey || !new_permission)
return
adm_ckey = ckey(adm_ckey)
if(!adm_ckey)
return
if(istext(new_permission))
new_permission = text2num(new_permission)
if(!istext(adm_ckey) || !isnum(new_permission))
return
var/datum/db_query/select_query = SSdbcore.NewQuery("SELECT id, flags FROM erro_admin WHERE ckey = '[adm_ckey]'")
select_query.Execute()
var/admin_id
var/admin_rights
while(select_query.NextRow())
admin_id = text2num(select_query.item[1])
admin_rights = text2num(select_query.item[2])
qdel(select_query)
if(!admin_id)
return
if(admin_rights & new_permission) //This admin already has this permission, so we are removing it.
var/datum/db_query/insert_query = SSdbcore.NewQuery("UPDATE `erro_admin` SET flags = [admin_rights & ~new_permission] WHERE id = [admin_id]")
insert_query.Execute()
qdel(insert_query)
var/datum/db_query/log_query = SSdbcore.NewQuery("INSERT INTO `test`.`erro_admin_log` (`id` ,`datetime` ,`adminckey` ,`adminip` ,`log` ) VALUES (NULL , NOW( ) , '[usr.ckey]', '[usr.client.address]', 'Removed permission [rights2text(new_permission)] (flag = [new_permission]) to admin [adm_ckey]');")
log_query.Execute()
qdel(log_query)
to_chat(usr, span_filter_adminlog("[span_blue("Permission removed.")]"))
else //This admin doesn't have this permission, so we are adding it.
var/datum/db_query/insert_query = SSdbcore.NewQuery("UPDATE `erro_admin` SET flags = '[admin_rights | new_permission]' WHERE id = [admin_id]")
insert_query.Execute()
qdel(insert_query)
var/datum/db_query/log_query = SSdbcore.NewQuery("INSERT INTO `test`.`erro_admin_log` (`id` ,`datetime` ,`adminckey` ,`adminip` ,`log` ) VALUES (NULL , NOW( ) , '[usr.ckey]', '[usr.client.address]', 'Added permission [rights2text(new_permission)] (flag = [new_permission]) to admin [adm_ckey]')")
log_query.Execute()
qdel(log_query)
to_chat(usr, span_filter_adminlog("[span_blue("Permission added.")]"))

View File

@@ -27,7 +27,7 @@
var/datum/player_info/P = new
if (user)
P.author = user.key
P.rank = user.client.holder.rank
P.rank = user.client.holder.rank_names()
else
P.author = "Adminbot"
P.rank = "Friendly Robot"

View File

@@ -129,99 +129,23 @@
if((bantype == BANTYPE_PERMA || bantype == BANTYPE_TEMP) && playermob.client)
qdel(playermob.client)
else if(href_list["editrightsbrowser"])
edit_admin_permissions(0)
else if(href_list["editrightsbrowserlog"])
edit_admin_permissions(1, href_list["editrightstarget"], href_list["editrightsoperation"], href_list["editrightspage"])
if(href_list["editrightsbrowsermanage"])
if(href_list["editrightschange"])
change_admin_rank(ckey(href_list["editrightschange"]), href_list["editrightschange"], TRUE)
else if(href_list["editrightsremove"])
remove_admin(ckey(href_list["editrightsremove"]), href_list["editrightsremove"], TRUE)
else if(href_list["editrightsremoverank"])
remove_rank(href_list["editrightsremoverank"])
edit_admin_permissions(2)
else if(href_list["editrights"])
if(!check_rights(R_PERMISSIONS))
message_admins("[key_name_admin(usr)] attempted to edit the admin permissions without sufficient rights.")
log_admin("[key_name(usr)] attempted to edit the admin permissions without sufficient rights.")
return
var/adm_ckey
var/task = href_list["editrights"]
if(task == "add")
var/new_ckey = ckey(tgui_input_text(usr,"New admin's ckey","Admin ckey", null))
if(!new_ckey) return
if(new_ckey in admin_datums)
to_chat(usr, span_filter_adminlog(span_warning("Error: Topic 'editrights': [new_ckey] is already an admin")))
return
adm_ckey = new_ckey
task = "rank"
else if(task != "show")
adm_ckey = ckey(href_list["ckey"])
if(!adm_ckey)
to_chat(usr, span_filter_adminlog(span_warning("Error: Topic 'editrights': No valid ckey")))
return
var/datum/admins/D = admin_datums[adm_ckey]
if(task == "remove")
if(tgui_alert(usr, "Are you sure you want to remove [adm_ckey]?","Message",list("Yes","Cancel")) == "Yes")
if(!D) return
admin_datums -= adm_ckey
D.disassociate()
message_admins("[key_name_admin(usr)] removed [adm_ckey] from the admins list")
log_admin("[key_name(usr)] removed [adm_ckey] from the admins list")
log_admin_rank_modification(adm_ckey, "Removed")
else if(task == "rank")
var/new_rank
if(admin_ranks.len)
new_rank = tgui_input_list(usr, "Please select a rank", "New rank", (admin_ranks|"*New Rank*"))
else
new_rank = tgui_input_list(usr, "Please select a rank", "New rank", list("Game Master","Head Admin","Game Admin", "Trial Admin", "Admin Observer","Moderator","Mentor","Badmin","Retired Admin","Event Manager","Developer","DevMod","*New Rank*")) //CHOMP Edit bandaid fix to assigning titles because we're having some funky database issues, I think. Other option is to manually edit database entry for someone's title.
var/rights = 0
if(D)
rights = D.rights
switch(new_rank)
if(null,"") return
if("*New Rank*")
new_rank = tgui_input_text(usr, "Please input a new rank", "New custom rank")
if(CONFIG_GET(flag/admin_legacy_system))
new_rank = ckeyEx(new_rank)
if(!new_rank)
to_chat(usr, span_filter_adminlog(span_warning("Error: Topic 'editrights': Invalid rank")))
return
if(CONFIG_GET(flag/admin_legacy_system))
if(admin_ranks.len)
if(new_rank in admin_ranks)
rights = admin_ranks[new_rank] //we typed a rank which already exists, use its rights
else
admin_ranks[new_rank] = 0 //add the new rank to admin_ranks
else
if(CONFIG_GET(flag/admin_legacy_system))
new_rank = ckeyEx(new_rank)
rights = admin_ranks[new_rank] //we input an existing rank, use its rights
if(D)
D.disassociate() //remove adminverbs and unlink from client
D.rank = new_rank //update the rank
D.rights = rights //update the rights based on admin_ranks (default: 0)
else
D = new /datum/admins(new_rank, rights, adm_ckey)
var/client/C = GLOB.directory[adm_ckey] //find the client with the specified ckey (if they are logged in)
D.associate(C) //link up with the client and add verbs
message_admins("[key_name_admin(usr)] edited the admin rank of [adm_ckey] to [new_rank]")
log_admin("[key_name(usr)] edited the admin rank of [adm_ckey] to [new_rank]")
log_admin_rank_modification(adm_ckey, new_rank)
else if(task == "permissions")
if(!D) return
var/list/permissionlist = list()
for(var/i=1, i<=R_MAXPERMISSION, i<<=1) //that <<= is shorthand for i = i << 1. Which is a left bitshift
permissionlist[rights2text(i)] = i
var/new_permission = tgui_input_list(usr, "Select a permission to turn on/off", "Permission toggle", permissionlist)
if(!new_permission) return
D.rights ^= permissionlist[new_permission]
message_admins("[key_name_admin(usr)] toggled the [new_permission] permission of [adm_ckey]")
log_admin("[key_name(usr)] toggled the [new_permission] permission of [adm_ckey]")
log_admin_permission_modification(adm_ckey, permissionlist[new_permission])
edit_admin_permissions()
edit_rights_topic(href_list)
else if(href_list["call_shuttle"])
if(!check_rights(R_ADMIN|R_EVENT)) return
@@ -692,7 +616,7 @@
return
if(M != usr) //we can jobban ourselves
if(M.client && M.client.holder && (M.client.holder.rights & R_BAN)) //they can ban too. So we can't ban them
if(M.client && M.client.holder && (check_rights_for(M.client, R_BAN))) //they can ban too. So we can't ban them
tgui_alert_async(usr, "You cannot perform this action. You must be of a higher administrative rank!")
return
@@ -1364,7 +1288,7 @@
if(ismob(M))
var/take_msg = span_notice("<b>ADMINHELP</b>: <b>[key_name(usr.client)]</b> is attending to <b>[key_name(M)]'s</b> adminhelp, please don't dogpile them.")
for(var/client/X in GLOB.admins)
if((R_ADMIN|R_MOD|R_SERVER) & X.holder.rights) //VOREStation Edit
if(check_rights_for(X, (R_ADMIN|R_MOD|R_SERVER)))
to_chat(X, take_msg)
to_chat(M, span_filter_pm(span_boldnotice("Your adminhelp is being attended to by [usr.client]. Thanks for your patience!")))
// VoreStation Edit Start

View File

@@ -0,0 +1,27 @@
GENERAL_PROTECT_DATUM(/datum/admin_verb)
/**
* This is the admin verb datum. It is used to store the verb's information and handle the verb's functionality.
* All of this is setup for you, and you should not be defining this manually.
* That means you reader.
*/
/datum/admin_verb
var/name //! The name of the verb.
var/description //! The description of the verb.
var/category //! The category of the verb.
var/permissions //! The permissions required to use the verb.
var/visibility_flag //! The flag that determines if the verb is visible.
VAR_PROTECTED/verb_path //! The path to the verb proc.
/datum/admin_verb/Destroy(force)
if(!force)
return QDEL_HINT_LETMELIVE
return ..()
/// Assigns the verb to the admin.
/datum/admin_verb/proc/assign_to_client(client/admin)
add_verb(admin, verb_path)
/// Unassigns the verb from the admin.
/datum/admin_verb/proc/unassign_from_client(client/admin)
remove_verb(admin, verb_path)

View File

@@ -296,7 +296,7 @@ GLOBAL_DATUM_INIT(ahelp_tickets, /datum/admin_help_tickets, new)
//send this msg to all admins
for(var/client/X in GLOB.admins)
// if(!check_rights(R_ADMIN, 0, X)) //CHOMP Remove let everyone hear the ahelp
// if(!check_rights_for(X, R_ADMIN)) //CHOMP Remove let everyone hear the ahelp
// continue //CHOMP Remove let everyone hear the ahelp
if(X.prefs?.read_preference(/datum/preference/toggle/holder/play_adminhelp_ping))
X << 'sound/effects/adminhelp.ogg'
@@ -723,7 +723,7 @@ GLOBAL_DATUM_INIT(ahelp_tickets, /datum/admin_help_tickets, new)
. = list("total" = list(), "noflags" = list(), "afk" = list(), "stealth" = list(), "present" = list())
for(var/client/X in GLOB.admins)
.["total"] += X
if(requiredflags != 0 && !check_rights(rights_required = requiredflags, show_msg = FALSE, C = X))
if(requiredflags != 0 && !check_rights_for(X, requiredflags))
.["noflags"] += X
else if(X.is_afk())
.["afk"] += X

View File

@@ -202,14 +202,14 @@
if(irc)
log_admin("PM: [key_name(src)]->IRC: [rawmsg]")
for(var/client/X in GLOB.admins)
if(!check_rights(R_ADMIN|R_SERVER, 0, X)) //CHOMPEdit
if(!check_rights_for(X, R_ADMIN|R_SERVER)) //CHOMPEdit
continue
to_chat(X, span_admin_pm_notice(span_bold("PM: [key_name(src, X, 0)]-&gt;IRC:") + " [keywordparsedmsg]"))
else
log_admin("PM: [key_name(src)]->[key_name(recipient)]: [rawmsg]")
//we don't use message_admins here because the sender/receiver might get it too
for(var/client/X in GLOB.admins)
if(!check_rights(R_ADMIN|R_SERVER, 0, X)) //CHOMPEdit
if(!check_rights_for(X, R_ADMIN|R_SERVER)) //CHOMPEdit
continue
if(X.key!=key && X.key!=recipient.key) //check client/X is an admin and isn't the sender or recipient
to_chat(X, span_admin_pm_notice(span_bold("PM: [key_name(src, X, 0)]-&gt;[key_name(recipient, X, 0)]:") + " [keywordparsedmsg]"))

View File

@@ -12,7 +12,7 @@
log_adminsay(msg,src)
for(var/client/C in GLOB.admins)
if(check_rights(R_ADMIN, 0, C))
if(check_rights_for(C, R_ADMIN))
to_chat(C, span_admin_channel(create_text_tag("admin", "ADMIN:", C) + " " + span_name("[key_name(usr, 1)]") + "([admin_jump_link(mob, src)]): " + span_name("[msg]") ))
feedback_add_details("admin_verb","M") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc!

View File

@@ -27,10 +27,10 @@
display_name = usr.client.holder.fakekey
// Name shown to other players. Admins whom are not also antags have their rank displayed.
var/player_display = (is_admin && !is_antag) ? "[display_name]([usr.client.holder.rank])" : display_name
var/player_display = (is_admin && !is_antag) ? "[display_name]([usr.client.holder.rank_names()])" : display_name
for(var/mob/M in mob_list)
if(check_rights(R_ADMIN|R_MOD|R_EVENT, 0, M)) // Staff can see AOOC unconditionally, and with more details.
if(check_rights_for(M.client, R_ADMIN|R_MOD|R_EVENT)) // Staff can see AOOC unconditionally, and with more details.
to_chat(M, span_ooc(span_aooc("[create_text_tag("aooc", "Antag-OOC:", M.client)] <EM>[get_options_bar(src, 0, 1, 1)]([admin_jump_link(usr, M.client.holder)]):</EM> " + span_message("[msg]"))))
else if(M.client) // Players can only see AOOC if observing, or if they are an antag type allowed to use AOOC.
var/datum/antagonist/A = null

View File

@@ -18,7 +18,7 @@
if (src.handle_spam_prevention(msg,MUTE_DEADCHAT))
return
var/stafftype = uppertext(holder.rank)
var/stafftype = uppertext(holder.rank_names())
msg = sanitize(msg)
log_admin("DSAY: [key_name(src)] : [msg]")

View File

@@ -16,7 +16,7 @@
var/msg = span_filter_pray(span_blue("[icon2html(cross, GLOB.admins)] <b>" + span_purple("PRAY: ") + "[key_name(src, 1)] [ADMIN_QUE(src)] [ADMIN_PP(src)] [ADMIN_VV(src)] [ADMIN_SM(src)] ([admin_jump_link(src, src)]) [ADMIN_CA(src)] [ADMIN_SC(src)] [ADMIN_SMITE(src)]:</b> [raw_msg]"))
for(var/client/C in GLOB.admins)
if(!check_rights(R_ADMIN|R_EVENT, 0, C)) //CHOMPEdit
if(!check_rights_for(C, R_ADMIN|R_EVENT)) //CHOMPEdit
continue
if(C.prefs?.read_preference(/datum/preference/toggle/show_chat_prayers))
to_chat(C, msg, type = MESSAGE_TYPE_PRAYER, confidential = TRUE)
@@ -29,7 +29,7 @@
/proc/CentCom_announce(var/msg, var/mob/Sender, var/iamessage)
msg = span_blue(span_bold(span_orange("[uppertext(using_map.boss_short)]M[iamessage ? " IA" : ""]:") + "[key_name(Sender, 1)] [ADMIN_PP(Sender)] [ADMIN_VV(Sender)] [ADMIN_SM(Sender)] ([admin_jump_link(Sender)]) [ADMIN_CA(Sender)] [ADMIN_BSA(Sender)] [ADMIN_CENTCOM_REPLY(Sender)]:") + " [msg]")
for(var/client/C in GLOB.admins) //VOREStation Edit - GLOB admins
if(!check_rights(R_ADMIN|R_EVENT, 0, C)) //CHOMPEdit
if(!check_rights_for(C, R_ADMIN|R_EVENT)) //CHOMPEdit
continue
to_chat(C,msg)
C << 'sound/machines/signal.ogg'
@@ -37,7 +37,7 @@
/proc/Syndicate_announce(var/msg, var/mob/Sender)
msg = span_blue(span_bold(span_crimson("ILLEGAL:") + "[key_name(Sender, 1)] [ADMIN_PP(Sender)] [ADMIN_VV(Sender)] [ADMIN_SM(Sender)] ([admin_jump_link(Sender)]) [ADMIN_CA(Sender)] [ADMIN_BSA(Sender)] [ADMIN_SYNDICATE_REPLY(Sender)]:") + " [msg]")
for(var/client/C in GLOB.admins) //VOREStation Edit - GLOB admins
if(!check_rights(R_ADMIN|R_EVENT, 0, C)) //CHOMPEdit
if(!check_rights_for(C, R_ADMIN|R_EVENT)) //CHOMPEdit
continue
to_chat(C,msg)
C << 'sound/machines/signal.ogg'

View File

@@ -0,0 +1,30 @@
// Admin Verbs in this file are special and cannot use the AVD system for some reason or another.
/client/proc/readmin()
set name = "Readmin"
set category = "Admin.Misc"
set desc = "Regain your admin powers."
var/datum/admins/A = GLOB.deadmins[ckey]
if(!A)
A = GLOB.admin_datums[ckey]
if (!A)
var/msg = " is trying to readmin but they have no deadmin entry"
message_admins("[key_name_admin(src)][msg]")
log_admin_private("[key_name(src)][msg]")
return
A.associate(src)
if(!holder)
return //This can happen if an admin attempts to vv themself into somebody elses's deadmin datum by getting ref via brute force
to_chat(src, span_interface("You are now an admin."), confidential = TRUE)
message_admins("[src] re-adminned themselves.")
log_admin("[src] re-adminned themselves.")
//BLACKBOX_LOG_ADMIN_VERB("Readmin")
if(isobserver(mob))
var/mob/observer/dead/our_mob = mob
our_mob.visualnet?.addVisibility(our_mob, src)

View File

@@ -172,21 +172,24 @@
/client/VV_ckey_edit()
return list("key", "ckey")
/datum/proc/may_edit_var(var/user, var/var_to_edit)
/datum/proc/may_edit_var(var/user, var/var_to_edit) //User must be a CLIENT that is passed to this.
if(!user)
return FALSE
if(ismob(user)) //Failsafe catch in case someone feeds a mob into us.
var/mob/living = user
user = living.client
if(!(var_to_edit in vars))
to_chat(user, span_warning("\The [src] does not have a var '[var_to_edit]'"))
return FALSE
if(var_to_edit in VV_static())
return FALSE
if((var_to_edit in VV_secluded()) && !check_rights(R_ADMIN|R_DEBUG, FALSE, C = user))
if((var_to_edit in VV_secluded()) && !check_rights_for(user, R_ADMIN|R_DEBUG))
return FALSE
if((var_to_edit in VV_locked()) && !check_rights(R_DEBUG, C = user))
if((var_to_edit in VV_locked()) && !check_rights_for(user, R_DEBUG))
return FALSE
if((var_to_edit in VV_ckey_edit()) && !check_rights(R_SPAWN|R_DEBUG, C = user))
if((var_to_edit in VV_ckey_edit()) && !check_rights_for(user, R_SPAWN|R_DEBUG))
return FALSE
if((var_to_edit in VV_icon_edit_lock()) && !check_rights(R_FUN|R_DEBUG, C = user))
if((var_to_edit in VV_icon_edit_lock()) && !check_rights_for(user, R_FUN|R_DEBUG))
return FALSE
return TRUE

View File

@@ -1,7 +1,5 @@
/datum/asset/simple/generic
assets = list(
"search.js" = 'html/search.js',
"panels.css" = 'html/panels.css',
"loading.gif" = 'html/images/loading.gif',
"ntlogo.png" = 'html/images/ntlogo.png',
"sglogo.png" = 'html/images/sglogo.png',

View File

@@ -0,0 +1,3 @@
/datum/asset/simple/namespaced/common
assets = list("padlock.png" = 'icons/ui/common/padlock.png')
parents = list("common.css" = 'html/browser/common.css')

View File

@@ -0,0 +1,11 @@
/datum/asset/simple/permissions
assets = list(
"search.js" = 'html/admin/search.js',
"panels.css" = 'html/admin/panels.css'
)
/datum/asset/group/permissions
children = list(
/datum/asset/simple/permissions,
/datum/asset/simple/namespaced/common
)

View File

@@ -40,7 +40,6 @@
show_verb_panel = FALSE
///Contains admin info. Null if client is not an admin.
var/datum/admins/holder = null
var/datum/admins/deadmin_holder = null
var/buildmode = 0
///Contains the last message sent by this client - used to protect against copy-paste spamming.

View File

@@ -262,7 +262,7 @@
GLOB.tickets.ClientLogin(src) // CHOMPedit - Tickets System
//Admin Authorisation
holder = admin_datums[ckey]
holder = GLOB.admin_datums[ckey]
if(holder)
GLOB.admins += src
holder.owner = src
@@ -465,7 +465,7 @@
var/admin_rank = "Player"
if(src.holder)
admin_rank = src.holder.rank
admin_rank = src.holder.rank_names()
var/sql_ip = sql_sanitize_text(src.address)
var/sql_computerid = sql_sanitize_text(src.computer_id)
@@ -475,7 +475,7 @@
//Panic bunker code
if (isnum(player_age) && player_age == 0) //first connection
if (CONFIG_GET(flag/panic_bunker) && !holder && !deadmin_holder)
if (CONFIG_GET(flag/panic_bunker) && !holder && !GLOB.deadmins[key])
log_adminwarn("Failed Login: [key] - New account attempting to connect during panic bunker")
message_admins(span_adminnotice("Failed Login: [key] - New account attempting to connect during panic bunker"))
disconnect_with_message("Sorry but the server is currently not accepting connections from never before seen players.")

View File

@@ -60,7 +60,7 @@ var/global/list/valid_bloodtypes = list("A+", "A-", "B+", "B-", "AB+", "AB-", "O
continue
if(instance.ckeys_allowed && (!client || !(client.ckey in instance.ckeys_allowed)))
continue
if(instance.species_allowed && (!species || !(species in instance.species_allowed)) && (!client || !check_rights(R_ADMIN | R_EVENT | R_FUN, 0, client)) && (!custom_base || !(custom_base in instance.species_allowed)))
if(instance.species_allowed && (!species || !(species in instance.species_allowed)) && (!client || !check_rights_for(client, R_ADMIN | R_EVENT | R_FUN)) && (!custom_base || !(custom_base in instance.species_allowed)))
continue
.[instance.name] = instance
@@ -539,7 +539,7 @@ var/global/list/valid_bloodtypes = list("A+", "A-", "B+", "B-", "AB+", "AB-", "O
else
return TOPIC_NOACTION
if(((!(setting_species.spawn_flags & SPECIES_CAN_JOIN)) || (!is_alien_whitelisted(preference_mob(),setting_species))) && !check_rights(R_ADMIN|R_EVENT, 0) && !(setting_species.spawn_flags & SPECIES_WHITELIST_SELECTABLE))
if(((!(setting_species.spawn_flags & SPECIES_CAN_JOIN)) || (!is_alien_whitelisted(user.client,setting_species))) && !check_rights(R_ADMIN|R_EVENT, 0) && !(setting_species.spawn_flags & SPECIES_WHITELIST_SELECTABLE))
return TOPIC_NOACTION
var/prev_species = pref.species
@@ -1237,7 +1237,7 @@ var/global/list/valid_bloodtypes = list("A+", "A-", "B+", "B-", "AB+", "AB-", "O
if(!(current_species.spawn_flags & SPECIES_CAN_JOIN))
restricted = 2
else if(!is_alien_whitelisted(preference_mob(),current_species))
else if(!is_alien_whitelisted(user.client,current_species))
restricted = 1
if(restricted)

View File

@@ -83,7 +83,7 @@ var/list/gear_datums = list()
if(G.whitelisted && CONFIG_GET(flag/loadout_whitelist) != LOADOUT_WHITELIST_OFF && pref.client) //VOREStation Edit.
if(CONFIG_GET(flag/loadout_whitelist) == LOADOUT_WHITELIST_STRICT && G.whitelisted != pref.species)
continue
if(CONFIG_GET(flag/loadout_whitelist) == LOADOUT_WHITELIST_LAX && !is_alien_whitelisted(preference_mob(), GLOB.all_species[G.whitelisted]))
if(CONFIG_GET(flag/loadout_whitelist) == LOADOUT_WHITELIST_LAX && !is_alien_whitelisted(preference_mob.client, GLOB.all_species[G.whitelisted]))
continue
if(max_cost && G.cost > max_cost)

View File

@@ -9,7 +9,7 @@
if(!.)
return
return check_rights(R_MOD|R_ADMIN, FALSE, preferences.client)
return check_rights_for(preferences.client, R_MOD|R_ADMIN)
/datum/preference/toggle/show_debug_logs
category = PREFERENCE_CATEGORY_GAME_PREFERENCES
@@ -22,7 +22,7 @@
if(!.)
return
return check_rights(R_DEBUG|R_ADMIN, FALSE, preferences.client)
return check_rights_for(preferences.client, R_DEBUG|R_ADMIN)
/datum/preference/toggle/show_chat_prayers
category = PREFERENCE_CATEGORY_GAME_PREFERENCES
@@ -35,7 +35,7 @@
if(!.)
return
return check_rights(R_EVENT|R_ADMIN, FALSE, preferences.client)
return check_rights_for(preferences.client, R_EVENT|R_ADMIN)
// General holder prefs
/datum/preference/toggle/holder
@@ -85,4 +85,4 @@
if(!..(preferences))
return FALSE
return CONFIG_GET(flag/allow_admin_ooccolor) && check_rights(R_ADMIN|R_EVENT|R_FUN, 0, preferences.client)
return CONFIG_GET(flag/allow_admin_ooccolor) && check_rights_for(preferences.client, R_ADMIN|R_EVENT|R_FUN)

View File

@@ -8,7 +8,7 @@
var/list/Lines = list()
if(check_rights(R_ADMIN|R_SERVER|R_MOD,FALSE,src))
if(check_rights_for(src, R_ADMIN|R_SERVER|R_MOD))
for(var/client/C in GLOB.clients)
var/entry = "<tr><td>[C.key]"
if(C.holder && C.holder.fakekey)

View File

@@ -51,13 +51,13 @@
if(holder && !holder.fakekey)
ooc_style = "elevated"
if(holder.rights & R_EVENT) //Retired Admins
if(check_rights(R_EVENT)) //Retired Admins
ooc_style = "event_manager"
if(holder.rights & R_ADMIN && !(holder.rights & R_BAN)) //Game Masters
if(check_rights(R_ADMIN) && !(check_rights(R_BAN))) //Game Masters
ooc_style = "moderator"
if(holder.rights & R_DEBUG && !(holder.rights & R_BAN)) //Developers
if(check_rights(R_SERVER) && !(check_rights(R_BAN))) //Developers
ooc_style = "developer"
if(holder.rights & R_ADMIN && holder.rights & R_BAN) //Admins
if(check_rights(R_ADMIN) && check_rights(R_BAN)) //Admins
ooc_style = "admin"
msg = GLOB.is_valid_url.Replace(msg,span_linkify("$1"))
@@ -74,7 +74,7 @@
else
display_name = holder.fakekey
var/pref_color = prefs.read_preference(/datum/preference/color/ooc_color)
if(holder && !holder.fakekey && (holder.rights & R_ADMIN|R_FUN|R_EVENT) && CONFIG_GET(flag/allow_admin_ooccolor) && pref_color != "#010000") // keeping this for the badmins
if(holder && !holder.fakekey && (check_rights(R_ADMIN|R_FUN|R_EVENT)) && CONFIG_GET(flag/allow_admin_ooccolor) && pref_color != "#010000") // keeping this for the badmins
to_chat(target, span_ooc("<font color='[pref_color]'>" + create_text_tag("ooc", "OOC:", target) + " <EM>[display_name]:</EM> [span_message(msg)]</font>"))
else
to_chat(target, span_ooc("<span class='[ooc_style]'>" + create_text_tag("ooc", "OOC:", target) + " <EM>[display_name]:</EM> " + span_message(msg)) + "</span>")
@@ -163,7 +163,7 @@
// Admins with RLOOC displayed who weren't already in
for(var/client/admin in GLOB.admins)
if(!(admin in receivers) && admin.prefs?.read_preference(/datum/preference/toggle/holder/show_rlooc))
if(check_rights(R_ADMIN|R_SERVER, FALSE, admin)) //Stop rLOOC showing for retired staff //CHOMPEdit, admins should see LOOC
if(check_rights_for(admin, R_ADMIN|R_SERVER)) //Stop rLOOC showing for retired staff //CHOMPEdit, admins should see LOOC
r_receivers |= admin
msg = GLOB.is_valid_url.Replace(msg,span_linkify("$1"))

View File

@@ -73,19 +73,19 @@
if(C.holder.fakekey && !check_rights_for(src, R_ADMIN|R_MOD)) // Only admins and mods can see stealthmins
continue
// VOREStation Edit End
if(check_rights(R_BAN, FALSE, C)) // admins //VOREStation Edit
if(check_rights_for(C, R_BAN)) // admins //VOREStation Edit
num_admins_online++
else if(check_rights(R_ADMIN, FALSE, C) && !check_rights(R_SERVER, FALSE, C)) // mods //VOREStation Edit: Game masters
else if(check_rights_for(C, R_ADMIN) && !check_rights_for(C, R_SERVER)) // mods //VOREStation Edit: Game masters
category = R_MOD
num_mods_online++
else if(check_rights(R_SERVER, FALSE, C)) // developers
else if(check_rights_for(C, R_SERVER)) // developers
category = R_SERVER
num_devs_online++
else if(check_rights(R_STEALTH, FALSE, C)) // event managers //VOREStation Edit: Retired Staff
else if(check_rights_for(C, R_STEALTH)) // event managers //VOREStation Edit: Retired Staff
category = R_EVENT
num_event_managers_online++
temp += "\t[C] is a [C.holder.rank]"
temp += "\t[C] is a [C.holder.rank_names()]"
if(holder)
if(C.holder.fakekey)
temp += " " + span_italics("(as [C.holder.fakekey])")

View File

@@ -55,7 +55,7 @@ var/list/mentor_verbs_default = list(
if(!target)
return
var/client/C = targets[target]
if(has_mentor_powers(C) || C.deadmin_holder) // If an admin is deadminned you could mentor them and that will cause fuckery if they readmin
if(has_mentor_powers(C) || GLOB.deadmins[C.ckey]) // If an admin is deadminned you could mentor them and that will cause fuckery if they readmin
to_chat(src, span_admin_pm_warning("Error: They already have mentor powers."))
return
var/datum/mentor/M = new /datum/mentor(C.ckey)

View File

@@ -148,7 +148,7 @@
visualnet = ghostnet
/mob/observer/dead/proc/checkStatic()
return !(check_rights(R_ADMIN|R_FUN|R_EVENT|R_SERVER, 0, src) || (client && client.buildmode) || isbelly(loc))
return !(check_rights_for(src.client, R_ADMIN|R_FUN|R_EVENT|R_SERVER) || (client && client.buildmode) || isbelly(loc))
/mob/observer/dead/Moved(atom/old_loc, direction, forced)
. = ..()
@@ -260,7 +260,7 @@ This is the proc mobs get to turn into a ghost. Forked from ghostize due to comp
announce_ghost_joinleave(ghostize(1))
else
var/response
if(check_rights(R_ADMIN|R_SERVER|R_MOD,FALSE,src)) //No need to sanity check for client and holder here as that is part of check_rights
if(check_rights_for(src.client, R_ADMIN|R_SERVER|R_MOD)) //No need to sanity check for client and holder here as that is part of check_rights
response = tgui_alert(src, "You have the ability to Admin-Ghost. The regular Ghost verb will announce your presence to dead chat. Both variants will allow you to return to your body using 'aghost'.\n\nWhat do you wish to do?", "Are you sure you want to ghost?", list("Admin Ghost", "Ghost", "Stay in body"))
if(response == "Admin Ghost")
if(!src.client)
@@ -613,7 +613,7 @@ This is the proc mobs get to turn into a ghost. Forked from ghostize due to comp
return 0
/mob/observer/dead/check_holy(var/turf/T)
if(check_rights(R_ADMIN|R_FUN|R_EVENT, 0, src))
if(check_rights_for(src.client, R_ADMIN|R_FUN|R_EVENT))
return 0
return (T && T.holy) && (is_manifest || (mind in cult.current_antagonists))
@@ -923,7 +923,7 @@ This is the proc mobs get to turn into a ghost. Forked from ghostize due to comp
return 1
/mob/observer/dead/proc/can_admin_interact()
return check_rights(R_ADMIN|R_EVENT, 0, src)
return check_rights_for(src.client, R_ADMIN|R_EVENT|R_DEBUG)
/mob/observer/dead/verb/toggle_ghostsee()
set name = "Toggle Ghost Vision"

View File

@@ -6,7 +6,7 @@
/mob/living/carbon/alien/diona/confirm_evolution()
if(!is_alien_whitelisted(src, GLOB.all_species[SPECIES_DIONA]))
if(!is_alien_whitelisted(src.client, GLOB.all_species[SPECIES_DIONA]))
tgui_alert(src, "You are currently not whitelisted to play as a full diona.")
return null

View File

@@ -180,14 +180,14 @@
for(var/current_species_name in GLOB.all_species)
var/datum/species/current_species = GLOB.all_species[current_species_name]
if(check_whitelist && CONFIG_GET(flag/usealienwhitelist) && !check_rights(R_ADMIN|R_EVENT, 0, src)) //If we're using the whitelist, make sure to check it!
if(check_whitelist && CONFIG_GET(flag/usealienwhitelist) && !check_rights_for(src.client, R_ADMIN|R_EVENT)) //If we're using the whitelist, make sure to check it!
if(!(current_species.spawn_flags & SPECIES_CAN_JOIN))
continue
if(whitelist.len && !(current_species_name in whitelist))
continue
if(blacklist.len && (current_species_name in blacklist))
continue
if((current_species.spawn_flags & SPECIES_IS_WHITELISTED) && !is_alien_whitelisted(src, current_species))
if((current_species.spawn_flags & SPECIES_IS_WHITELISTED) && !is_alien_whitelisted(src.client, current_species))
continue
valid_species += current_species_name

View File

@@ -87,3 +87,4 @@
client.images += cloaked_selfimage
client.init_verbs()
SEND_SIGNAL(src, COMSIG_MOB_CLIENT_LOGIN, client)
SEND_SIGNAL(client, COMSIG_CLIENT_MOB_LOGIN, src)

View File

@@ -5,9 +5,9 @@
update_client_z(null)
log_access_out(src)
unset_machine()
if(admin_datums[src.ckey])
if(GLOB.admin_datums[src.ckey])
message_admins("Staff logout: [key_name(src)]") // CHOMPEdit: Admin logout notice displays no matter what//Edit2: STAFF
if(ticker && ticker.current_state == GAME_STATE_PLAYING) //Only report this stuff if we are currently playing.
if (ticker && ticker.current_state == GAME_STATE_PLAYING) //Only report this stuff if we are currently playing.
var/admins_number = GLOB.admins.len
if(admins_number == 0) //Apparently the admin logging out is no longer an admin at this point, so we have to check this towards 0 and not towards 1. Awell.

View File

@@ -467,7 +467,7 @@
set category = "OOC.Game"
var/is_admin = 0
if(client.holder && (client.holder.rights & R_ADMIN|R_EVENT))
if(check_rights_for(client, R_ADMIN|R_EVENT))
is_admin = 1
else if(stat != DEAD || isnewplayer(src))
to_chat(src, span_filter_notice("[span_blue("You must be observing to use this!")]"))

View File

@@ -83,7 +83,7 @@
/proc/is_admin(var/mob/user)
return check_rights(R_ADMIN|R_EVENT, 0, user) != 0
return check_rights_for(user.client, R_ADMIN|R_EVENT) != 0
/**
* Moved into its own file as part of port from CHOMP.
@@ -406,7 +406,7 @@ var/list/intents = list(I_HELP,I_DISARM,I_GRAB,I_HURT)
return // Can't talk in deadchat if you can't see it.
for(var/mob/M in player_list)
if(M.client && ((!isnewplayer(M) && M.stat == DEAD) || (M.client.holder && M.client.holder.rights && M.client?.prefs?.read_preference(/datum/preference/toggle/holder/show_staff_dsay))) && M.client?.prefs?.read_preference(/datum/preference/toggle/show_dsay))
if(M.client && ((!isnewplayer(M) && M.stat == DEAD) || (M.client.holder && check_rights_for(M.client, R_NONE) && M.client?.prefs?.read_preference(/datum/preference/toggle/holder/show_staff_dsay))) && M.client?.prefs?.read_preference(/datum/preference/toggle/show_dsay))
var/follow
var/lname
if(M.forbid_seeing_deadchat && !M.client.holder)
@@ -436,7 +436,7 @@ var/list/intents = list(I_HELP,I_DISARM,I_GRAB,I_HURT)
/proc/say_dead_object(var/message, var/obj/subject = null)
for(var/mob/M in player_list)
if(M.client && ((!isnewplayer(M) && M.stat == DEAD) || (M.client.holder && M.client.holder.rights && M.client?.prefs?.read_preference(/datum/preference/toggle/holder/show_staff_dsay))) && M.client?.prefs?.read_preference(/datum/preference/toggle/show_dsay))
if(M.client && ((!isnewplayer(M) && M.stat == DEAD) || (M.client.holder && check_rights_for(M.client, R_NONE) && M.client?.prefs?.read_preference(/datum/preference/toggle/holder/show_staff_dsay))) && M.client?.prefs?.read_preference(/datum/preference/toggle/show_dsay))
var/follow
var/lname = "Game Master"
if(M.forbid_seeing_deadchat && !M.client.holder)

View File

@@ -621,7 +621,7 @@
if(chosen_species && use_species_name)
// Have to recheck admin due to no usr at roundstart. Latejoins are fine though.
if(is_alien_whitelisted(chosen_species))
if(is_alien_whitelisted(src.client, chosen_species))
new_character = new(T, use_species_name)
if(!new_character)
@@ -697,9 +697,6 @@
src << browse(null, "window=News") //closes news window
panel.close()
/mob/new_player/proc/has_admin_rights()
return check_rights(R_ADMIN, 0, src)
/mob/new_player/get_species()
var/datum/species/chosen_species
if(client.prefs.species)
@@ -708,7 +705,7 @@
if(!chosen_species)
return SPECIES_HUMAN
if(is_alien_whitelisted(chosen_species))
if(is_alien_whitelisted(src.client, chosen_species))
return chosen_species.name
return SPECIES_HUMAN

View File

@@ -27,7 +27,7 @@
to_chat(src,span_warning("You have not set your scale yet. Do this on the VORE tab in character setup."))
//Can they play?
if(!is_alien_whitelisted(src,GLOB.all_species[client?.prefs?.species]) && !check_rights(R_ADMIN, 0))
if(!is_alien_whitelisted(src.client,GLOB.all_species[client?.prefs?.species]) && !check_rights(R_ADMIN, 0))
pass = FALSE
to_chat(src,span_warning("You are not allowed to spawn in as this species."))

View File

@@ -398,7 +398,7 @@
var/adminrank = "Player"
if(usr && usr.client && usr.client.holder)
adminrank = usr.client.holder.rank
adminrank = usr.client.holder.rank_names()
var/datum/db_query/insert_query = SSdbcore.NewQuery("INSERT INTO erro_poll_vote (id ,datetime ,pollid ,optionid ,ckey ,ip ,adminrank) VALUES (null, Now(), [pollid], [optionid], '[usr.ckey]', '[usr.client.address]', '[adminrank]')")
@@ -448,7 +448,7 @@
var/adminrank = "Player"
if(usr && usr.client && usr.client.holder)
adminrank = usr.client.holder.rank
adminrank = usr.client.holder.rank_names()
replytext = replacetext(replytext, "%BR%", "")
@@ -520,7 +520,7 @@
var/adminrank = "Player"
if(usr && usr.client && usr.client.holder)
adminrank = usr.client.holder.rank
adminrank = usr.client.holder.rank_names()
var/datum/db_query/insert_query = SSdbcore.NewQuery("INSERT INTO erro_poll_vote (id ,datetime ,pollid ,optionid ,ckey ,ip ,adminrank, rating) VALUES (null, Now(), [pollid], [optionid], '[usr.ckey]', '[usr.client.address]', '[adminrank]', [(isnull(rating)) ? "null" : rating])")

View File

@@ -100,7 +100,7 @@
/obj/item/modular_computer/attack_ghost(var/mob/observer/dead/user)
if(enabled)
tgui_interact(user)
else if(check_rights(R_ADMIN|R_EVENT, 0, user))
else if(check_rights_for(user.client, R_ADMIN|R_EVENT|R_DEBUG))
var/response = tgui_alert(user, "This computer is turned off. Would you like to turn it on?", "Admin Override", list("Yes", "No"))
if(response == "Yes")
turn_on(user)

View File

@@ -109,7 +109,7 @@
return 1
// Admin override - allows operation of any computer as aghosted admin, as if you had any required access.
if(isobserver(user) && check_rights(R_ADMIN|R_EVENT, 0, user))
if(isobserver(user) && check_rights_for(user.client, R_ADMIN|R_EVENT|R_DEBUG))
return 1
if(!istype(user))

View File

@@ -425,7 +425,7 @@ Extracted to its own procedure for easier logic handling with paper bundles.
msg = span_notice(msg)
for(var/client/C in GLOB.admins)
if(check_rights((R_ADMIN|R_MOD|R_EVENT),0,C))
if(check_rights_for(C, (R_ADMIN|R_MOD|R_EVENT)))
to_chat(C,msg)
C << 'sound/machines/printer.ogg'
sender.client << 'sound/machines/printer.ogg' //CHOMPEdit - The pain must be felt

View File

@@ -75,7 +75,7 @@
var/client/ghost_client = ghost.client
if(!is_alien_whitelisted(ghost, GLOB.all_species[ghost_client?.prefs?.species]) && !check_rights(R_ADMIN, 0)) // Prevents a ghost ghosting in on a slot and spawning via a resleever with race they're not whitelisted for, getting around normal join restrictions.
if(!is_alien_whitelisted(ghost.client, GLOB.all_species[ghost_client?.prefs?.species]) && !check_rights(R_ADMIN, 0)) // Prevents a ghost ghosting in on a slot and spawning via a resleever with race they're not whitelisted for, getting around normal join restrictions.
to_chat(ghost, span_warning("You are not whitelisted to spawn as this species!"))
return

View File

@@ -63,13 +63,13 @@
else
keymsg += " (Ingame)"
if(R_ADMIN & C.holder.rights && R_BAN & C.holder.rights) // R_ADMIN and R_BAN apparently an admin makes
if(check_rights_for(C, R_ADMIN) && check_rights_for(C, R_BAN)) // R_ADMIN and R_BAN apparently an admin makes
admin_keys += keymsg
else if(R_ADMIN & C.holder.rights && !(R_SERVER & C.holder.rights)) // R_ADMIN but not R_SERVER makes a moderator
else if(check_rights_for(C, R_ADMIN) && !(check_rights_for(C, R_SERVER))) // R_ADMIN but not R_SERVER makes a moderator
mod_keys += keymsg
else if(R_SERVER & C.holder.rights) // R_SERVER makes a dev
else if(check_rights_for(C, R_SERVER)) // R_SERVER makes a dev
dev_keys += keymsg
else // No R_ADMIN&&R_BAN, R_ADMIN!R_BAN, R_SERVER, must be a GM or something

View File

@@ -48,7 +48,7 @@
current_filter = filter
/datum/tgui_module/player_notes/proc/open_legacy()
var/datum/admins/A = admin_datums[usr.ckey]
var/datum/admins/A = GLOB.admin_datums[usr.ckey]
A.PlayerNotesLegacy()
/datum/tgui_module/player_notes/tgui_state(mob/user)
@@ -113,7 +113,7 @@
if(..())
return TRUE
var/datum/admins/A = admin_datums[usr.ckey]
var/datum/admins/A = GLOB.admin_datums[usr.ckey]
A.show_player_info_legacy(key)
/datum/tgui_module/player_notes_info/tgui_act(action, params, datum/tgui/ui)

View File

@@ -131,7 +131,7 @@
return
var/datum/species/S = GLOB.all_species[new_user.client.prefs.species]
if(!is_alien_whitelisted(new_user, S))
if(!is_alien_whitelisted(new_user.client, S))
tgui_alert(new_user, "You are currently not whitelisted to play [new_user.client.prefs.species].")
return 0

View File

@@ -24,7 +24,7 @@
if(isobserver(user))
// Admins can always interact.
if(check_rights(R_ADMIN|R_EVENT, 0, src))
if(check_rights_for(user.client, R_ADMIN|R_EVENT|R_DEBUG))
. = max(., STATUS_INTERACTIVE)
// Regular ghosts can always at least view if in range.

View File

@@ -12,6 +12,6 @@
GLOBAL_DATUM_INIT(tgui_admin_state, /datum/tgui_state/admin_state, new)
/datum/tgui_state/admin_state/can_use_topic(src_object, mob/user)
if(check_rights_for(user.client, R_ADMIN|R_EVENT))
if(check_rights_for(user.client, R_ADMIN|R_EVENT|R_DEBUG))
return STATUS_INTERACTIVE
return STATUS_CLOSE

View File

@@ -79,6 +79,6 @@ GLOBAL_DATUM_INIT(tgui_default_state, /datum/tgui_state/default, new)
return ..()
/mob/observer/dead/default_can_use_tgui_topic()
if(check_rights(R_ADMIN|R_EVENT, 0, src))
if(check_rights_for(src.client, R_ADMIN|R_EVENT|R_DEBUG))
return STATUS_INTERACTIVE // Admins are more equal
return STATUS_UPDATE // Ghosts can view updates

View File

@@ -14,6 +14,6 @@ GLOBAL_DATUM_INIT(tgui_observer_state, /datum/tgui_state/observer_state, new)
/datum/tgui_state/observer_state/can_use_topic(src_object, mob/user)
if(isobserver(user))
return STATUS_INTERACTIVE
if(check_rights(R_ADMIN|R_EVENT, 0, src))
if(check_rights_for(user.client, R_ADMIN|R_EVENT))
return STATUS_INTERACTIVE
return STATUS_CLOSE

View File

@@ -169,7 +169,7 @@
data["question"] = question
data["choices"] = choices
if(show_counts || check_rights(R_ADMIN, FALSE, user))
if(show_counts || check_rights_for(user.client, R_ADMIN))
data["show_counts"] = TRUE
var/list/counts = list()

View File

@@ -97,4 +97,4 @@
/proc/whitelist_overrides(mob/M)
return !CONFIG_GET(flag/usealienwhitelist) || check_rights(R_ADMIN|R_EVENT, 0, M) // CHOMPEdit
return !CONFIG_GET(flag/usealienwhitelist) || check_rights_for(M.client, R_ADMIN|R_EVENT) // CHOMPEdit

View File

@@ -1,46 +1,89 @@
########################################################################################
# ADMIN RANK DEFINES #
# The format of this is very simple. Rank name goes first. #
# Rank is CASE-SENSITIVE, all punctuation will be stripped so spaces don't matter. #
# Each rank is then followed by keywords with the prefix "+". #
# These keywords represent groups of verbs and abilities which are given to that rank. #
# +@ (or +prev) is a special shorthand which adds all the rights of the rank above it. #
# Ranks with no keywords will just be given the most basic verbs and abilities ~Carn #
########################################################################################
# PLEASE NOTE: depending on config options, some abilities will be unavailable regardless if you have permission to use them!
# ALSO NOTE: this is a WorkInProgress at the moment. Most of this is just arbitrarily thrown in whatever group because LoadsaWork2Do+LittleTime.
# I'll be doing more moving around as feedback comes in. So be sure to check the notes after updates.
#Admin Rank format is as follows:
#
#Name = Game Admin
#Include = @ ADMIN BAN SOUND
#Exclude = FUN
#Edit =
#
#Name will match anything after '=' and must be identical to an admin's rank in admins.txt to be linked but otherwise has no formatting restrictions.
#A rank's permissions are defined with keywords that control access to groups of verbs and abilities, they are case-sensitive and separated by a space with no prefix.
#To define no permissions for a type, leave it empty.
#There are three types of permissions:
#Include will give a keyword to a rank.
#Exclude removes a keyword and takes precedence over Include.
#Edit will allow an admin to edit these permissions on other ranks or change an admin's rank to another if they can edit all the permissions it has.
#Edit is only used when SQL-based admin loading is enabled.
#If SQL-based admin loading is enabled, ranks and their keywords listed here will be loaded first and override any with the same name loaded from the database.
#
#The following are valid permission keywords:
#ADMIN = general admin tools, verbs etc.
#FUN = events, other event-orientated actions. Access to the fun secrets in the secrets panel.
#BAN = the ability to ban and unban.
#STEALTH = the ability to stealthmin (make yourself appear with a fake name to everyone but other admins).
#POSSESS = the ability to possess objects.
#REJUVINATE = the ability to heal, respawn, modify damage and use godmode
#BUILDMODE = the ability to use buildmode.
#SERVER = the ability to restart the server, change the game mode or force a round to start/end.
#DEBUG = debug tools used for diagnosing and fixing problems. It's useful to give this to coders so they can investigate problems on a live server.
#VAREDIT = everyone may view viewvars/debugvars/whatever you call it. This keyword allows you to actually EDIT those variables.
#PERMISSIONS = allows you to promote and/or demote people.
#SOUNDS = allows you to upload and play SOUND.
#SPAWN = mob transformations, spawning of most atoms including mobs (high-risk atoms, e.g. blackholes, will require the +FUN flag too).
#EVENT = a group of verbs that make it possible to run an event, or other badminnery.
#EVERYTHING = Simply gives you everything without having to type every flag.
#@ = special keyword for the current permission type that adds all the keywords that the preceding rank has of the same type.
# KEYWORDS:
# +ADMIN = general admin tools, verbs etc
# +FUN = events, other event-orientated actions. Access to the fun secrets in the secrets panel.
# +BAN = the ability to ban, jobban and fullban
# +STEALTH = the ability to stealthmin (make yourself appear with a fake name to everyone but other admins
# +POSSESS = the ability to possess objects
# +REJUV (or +REJUVINATE) = the ability to heal, respawn, modify damage and use godmode
# +BUILD (or +BUILDMODE) = the ability to use buildmode
# +SERVER = higher-risk admin verbs and abilities, such as those which affect the server configuration.
# +DEBUG = debug tools used for diagnosing and fixing problems. It's useful to give this to coders so they can investigate problems on a live server.
# +VAREDIT = everyone may view viewvars/debugvars/whatever you call it. This keyword allows you to actually EDIT those variables.
# +RIGHTS (or +PERMISSIONS) = allows you to promote and/or demote people.
# +SOUND (or +SOUNDS) = allows you to upload and play sounds
# +SPAWN (or +CREATE) = mob transformations, spawning of most atoms including mobs (high-risk atoms, e.g. blackholes, will require the +FUN flag too)
# +EVENT = a group of verbs that make it possible to run an event, or other badminnery.
# +EVERYTHING (or +HOST or +ALL) = Simply gives you everything without having to type every flag
Name = Admin Observer
Include =
Exclude =
Edit =
Admin Observer
Moderator +MOD
Mentor +MENTOR
Name = Moderator
Include = MOD
Exclude =
Edit =
Admin Candidate +ADMIN
Trial Admin +@ +STEALTH +SPAWN +REJUV +VAREDIT +BAN +FUN
Admin +@ +POSSESS +BUILDMODE +SERVER
Admin Supervisor +@ +SOUNDS +DEBUG +PERMISSIONS
Game Master +ADMIN +FUN +POSSESS +REJUV +BUILD +SERVER +DEBUG +VAREDIT +SOUND +SPAWN
Head Admin +EVERYTHING
Retired Admin +ADMIN +STEALTH
Name = Admin Candidate
Include = ADMIN
Exclude =
Edit =
Host +EVERYTHING
Name = Trial Admin
Include = @ SPAWN REJUVINATE VAREDIT BAN
Exclude =
Edit =
Developer +DEBUG +VAREDIT +SERVER +SPAWN +REJUV +POSSESS +BUILDMODE
Dev Mod +@ +MOD
Name = Badmin
Include = @ POSSESS BUILDMODE SERVER FUN
Exclude =
Edit =
Name = Game Admin
Include = @ STEALTH SOUNDS DEBUG PERMISSIONS
Exclude =
Edit =
Name = Game Master
Include = EVERYTHING
Exclude =
Edit = EVERYTHING
Name = Head Admin
Include = EVERYTHING
Exclude =
Edit = EVERYTHING
Name = Retired Admin
Include = ADMIN STEALTH
Exclude =
Edit =
Name = Host
Include = EVERYTHING
Exclude =
Edit = EVERYTHING
Name = Developer
Include = DEBUG VAREDIT SERVER SPAWN REJUVINATE POSSESS BUILDMODE
Exclude =
Edit =

View File

@@ -1,8 +1,11 @@
######################################################################
# Basically, ckey goes first. Rank goes after the "-" #
# Case is not important for ckey. #
# Case IS important for the rank. However punctuation/spaces are not #
# Ranks can be anything defined in admin_ranks.txt ~Carn #
######################################################################
###############################################################################################
# Basically, ckey goes first. Rank goes after the "=" #
# Case is not important for ckey. #
# Case IS important for the rank. #
# All punctuation (spaces etc) EXCEPT '-', '_' and '@' will be stripped from rank names. #
# Ranks can be anything defined in admin_ranks.txt #
# NOTE: if the rank-name cannot be found in admin_ranks.txt, they will not be adminned! ~Carn #
# NOTE: syntax was changed to allow hyphenation of ranknames, since spaces are stripped. #
###############################################################################################
# not_a_user - Admin
# not_a_user = Admin

View File

@@ -17,9 +17,21 @@ $include sqlite.txt
## Server name: This appears at the top of the screen in-game. In this case it will read "tgstation: station_name" where station_name is the randomly generated name of the station for the round. Remove the # infront of SERVERNAME and replace 'tgstation' with the name of your choice
# SERVERNAME spacestation13
## Add a # infront of this if you want to use the SQL based admin system, the legacy system uses admins.txt. You need to set up your database to use the SQL based system.
## Comment this out if you want to use the SQL based admin system, the legacy system uses admins.txt.
## You need to set up your database to use the SQL based system.
## This flag is automatically enabled if SQL_ENABLED isn't
ADMIN_LEGACY_SYSTEM
##Uncomment this to stop any admins loaded by the legacy system from having their rank edited by the permissions panel
#PROTECT_LEGACY_ADMINS
##Uncomment this to stop any ranks loaded by the legacy system from having their flags edited by the permissions panel
#PROTECT_LEGACY_RANKS
##Uncomment this to have admin ranks only loaded from the legacy admin_ranks.txt
##If enabled, each time admins are loaded ranks the database will be updated with the current ranks and their flags
#LOAD_LEGACY_RANKS_ONLY
## Add a # infront of this if you want to use the SQL based banning system. The legacy systems use the files in the data folder. You need to set up your database to use the SQL based system.
BAN_LEGACY_SYSTEM
@@ -596,3 +608,6 @@ JUKEBOX_TRACK_FILES config/jukebox.json
# The endpoint for the chat to fetch the chatlogs from (for example, the last 2500 messages on init for the history)
# REQUIRES chatlog_database_backend to be enabled
#CHATLOG_DATABASE_API_ENDPOINT https://example.com
## Uncomment to block granting profiling privileges to users with R_DEBUG, for performance purposes
#FORBID_ADMIN_PROFILING

View File

@@ -1,10 +1,10 @@
body {padding:0px;margin:0px;}
#top {position:fixed;top:5px;left:10%;width:80%;text-align:center;background-color:#fff;border:2px solid #ccc;}
#main {position:relative;top:50px;left:3%;width:96%;text-align:center;z-index:0;}
#main {position:relative;top:10px;left:3%;width:96%;text-align:center;z-index:0;}
#searchable {table-layout:fixed;width:100%;text-align:center;"#f4f4f4";}
tr.norm {background-color:#f4f4f4;}
tr.title {background-color:#ccc;}
tr.alt {background-color:#e7e7e7;}
.small {font-size:80%;}
a {text-decoration:none;color:#a0a;}
a {text-decoration:none;}
a:hover {color:#d3d;}

View File

@@ -23,11 +23,11 @@ function updateSearch(){
}
if(found == 0) row.style.display='none';
else{
row.style.display='block';
row.style.display='table-row'; /* DON'T make tables with block property */
row.className = alt_style;
if(alt_style == 'alt') alt_style = 'norm';
else alt_style = 'alt';
}
}catch(err) { }
}
}
}

BIN
icons/ui/common/padlock.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 173 B

View File

@@ -701,7 +701,7 @@ GLOBAL_DATUM_INIT(tickets, /datum/tickets, new)
. = list("total" = list(), "noflags" = list(), "afk" = list(), "stealth" = list(), "present" = list())
for(var/client/X in GLOB.admins)
.["total"] += X
if(requiredflags != 0 && !check_rights(rights_required = requiredflags, show_msg = FALSE, C = X))
if(requiredflags != 0 && !check_rights(X, requiredflags))
.["noflags"] += X
else if(X.is_afk())
.["afk"] += X

View File

@@ -0,0 +1,66 @@
import tkinter as tk
from tkinter import ttk
bitflags = {
"R_BUILDMODE": 1<<0,
"R_ADMIN": 1<<1,
"R_BAN": 1<<2,
"R_FUN": 1<<3,
"R_SERVER": 1<<4,
"R_DEBUG": 1<<5,
"R_POSSESS": 1<<6,
"R_PERMISSIONS": 1<<7,
"R_STEALTH": 1<<8,
"R_REJUVINATE": 1<<9,
"R_VAREDIT": 1<<10,
"R_SOUNDS": 1<<11,
"R_SPAWN": 1<<12,
"R_MOD": 1<<13,
"R_EVENT": 1<<14,
"R_HOST": 1<<15,
"-------------": 0,
"EVERYTHING": (1<<16)-1
}
class BitflagCalculator:
def __init__(self, master):
self.master = master
master.title("Bitflag Calculator")
master.geometry("300x625")
self.checkboxes = {}
self.vars = {}
for i, (flag, value) in enumerate(bitflags.items()):
var = tk.IntVar()
cb = ttk.Checkbutton(master, text=flag, variable=var, command=self.update_result)
cb.grid(row=i, column=0, sticky="w", padx=10, pady=2)
self.checkboxes[flag] = cb
self.vars[flag] = var
self.result_label = ttk.Label(master, text="Result: 0")
self.result_label.grid(row=len(bitflags), column=0, pady=10)
self.copy_button = ttk.Button(master, text="Copy Result", command=self.copy_result)
self.copy_button.grid(row=len(bitflags)+1, column=0, pady=5)
self.clear_button = ttk.Button(master, text="Clear All", command=self.clear_all)
self.clear_button.grid(row=len(bitflags)+2, column=0, pady=5)
def update_result(self):
result = sum(value for flag, value in bitflags.items() if self.vars[flag].get())
self.result_label.config(text=f"Result: {result}")
def copy_result(self):
result = sum(value for flag, value in bitflags.items() if self.vars[flag].get())
self.master.clipboard_clear()
self.master.clipboard_append(str(result))
def clear_all(self):
for var in self.vars.values():
var.set(0)
self.update_result()
root = tk.Tk()
calculator = BitflagCalculator(root)
root.mainloop()

View File

@@ -21,6 +21,7 @@
#include "code\names.dm"
#include "code\world.dm"
#include "code\__defines\__globals.dm"
#include "code\__defines\_bitfields.dm"
#include "code\__defines\_click.dm"
#include "code\__defines\_compile_options.dm"
#include "code\__defines\_fruits.dm"
@@ -35,6 +36,7 @@
#include "code\__defines\action.dm"
#include "code\__defines\admin.dm"
#include "code\__defines\admin_ch.dm"
#include "code\__defines\admin_verb.dm"
#include "code\__defines\admin_vr.dm"
#include "code\__defines\airlock_control.dm"
#include "code\__defines\ammunition.dm"
@@ -185,6 +187,7 @@
#include "code\__defines\dcs\helpers.dm"
#include "code\__defines\dcs\signals.dm"
#include "code\__defines\dcs\signals_ch.dm"
#include "code\__defines\dcs\signals\signals_client.dm"
#include "code\__defines\dcs\signals\signals_subsystem.dm"
#include "code\__defines\dcs\signals\signals_mobs\signals_mob_main.dm"
#include "code\__defines\dcs\signals\signals_turf.dm"
@@ -210,6 +213,7 @@
#include "code\_helpers\_global_objects.dm"
#include "code\_helpers\_global_objects_vr.dm"
#include "code\_helpers\_lists.dm"
#include "code\_helpers\admin.dm"
#include "code\_helpers\announcements.dm"
#include "code\_helpers\atmospherics.dm"
#include "code\_helpers\atom_movables.dm"
@@ -1966,8 +1970,6 @@
#include "code\modules\admin\admin_tools.dm"
#include "code\modules\admin\admin_verb_lists_vr.dm"
#include "code\modules\admin\admin_verbs.dm"
#include "code\modules\admin\admin_verbs_vr.dm"
#include "code\modules\admin\admin_vr.dm"
#include "code\modules\admin\banjob.dm"
#include "code\modules\admin\ckey_vr.dm"
#include "code\modules\admin\create_mob.dm"
@@ -1980,6 +1982,7 @@
#include "code\modules\admin\modify_robot.dm"
#include "code\modules\admin\NewBan.dm"
#include "code\modules\admin\news.dm"
#include "code\modules\admin\permissionedit.dm"
#include "code\modules\admin\persistence.dm"
#include "code\modules\admin\player_effects.dm"
#include "code\modules\admin\player_notes.dm"
@@ -1988,7 +1991,6 @@
#include "code\modules\admin\ToRban.dm"
#include "code\modules\admin\callproc\callproc.dm"
#include "code\modules\admin\DB ban\functions.dm"
#include "code\modules\admin\permissionverbs\permissionedit.dm"
#include "code\modules\admin\secrets\admin_secrets\admin_logs.dm"
#include "code\modules\admin\secrets\admin_secrets\alter_narsie.dm"
#include "code\modules\admin\secrets\admin_secrets\bombing_list.dm"
@@ -2026,6 +2028,7 @@
#include "code\modules\admin\secrets\random_events\gravity.dm"
#include "code\modules\admin\secrets\random_events\trigger_cordical_borer_infestation.dm"
#include "code\modules\admin\secrets\random_events\trigger_xenomorph_infestation.dm"
#include "code\modules\admin\verb_datums\_admin_verb_datum.dm"
#include "code\modules\admin\verbs\admin_ch.dm"
#include "code\modules\admin\verbs\adminjump.dm"
#include "code\modules\admin\verbs\adminpm.dm"
@@ -2061,6 +2064,7 @@
#include "code\modules\admin\verbs\randomverbs_vr.dm"
#include "code\modules\admin\verbs\resize.dm"
#include "code\modules\admin\verbs\smite.dm"
#include "code\modules\admin\verbs\special_verbs.dm"
#include "code\modules\admin\verbs\striketeam.dm"
#include "code\modules\admin\verbs\tripAI.dm"
#include "code\modules\admin\verbs\SDQL2\SDQL_2.dm"
@@ -2124,10 +2128,12 @@
#include "code\modules\asset_cache\assets\browser.dm"
#include "code\modules\asset_cache\assets\circuits.dm"
#include "code\modules\asset_cache\assets\cloning.dm"
#include "code\modules\asset_cache\assets\common.dm"
#include "code\modules\asset_cache\assets\fontawesome.dm"
#include "code\modules\asset_cache\assets\icon_ref_map.dm"
#include "code\modules\asset_cache\assets\jquery.dm"
#include "code\modules\asset_cache\assets\ntos.dm"
#include "code\modules\asset_cache\assets\permissions.dm"
#include "code\modules\asset_cache\assets\preferences.dm"
#include "code\modules\asset_cache\assets\tgfont.dm"
#include "code\modules\asset_cache\assets\tgui.dm"