diff --git a/code/__HELPERS/roundend.dm b/code/__HELPERS/roundend.dm index 6006e718227..87801cf418a 100644 --- a/code/__HELPERS/roundend.dm +++ b/code/__HELPERS/roundend.dm @@ -716,7 +716,7 @@ var/list/sql_admins = list() for(var/i in GLOB.protected_admins) var/datum/admins/A = GLOB.protected_admins[i] - sql_admins += list(list("ckey" = A.target, "rank" = A.rank.name)) + sql_admins += list(list("ckey" = A.target, "rank" = A.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")] p INNER JOIN [format_table_name("admin")] a ON p.ckey = a.ckey SET p.lastadminrank = a.rank") query_admin_rank_update.Execute() @@ -743,7 +743,7 @@ if (!admin) continue - file_data["admins"][admin_ckey] = admin.rank.name + file_data["admins"][admin_ckey] = admin.rank_names() if (admin.owner) file_data["connections"][admin_ckey] = list( diff --git a/code/modules/admin/admin_ranks.dm b/code/modules/admin/admin_ranks.dm index fd531c64065..1f21960eaf9 100644 --- a/code/modules/admin/admin_ranks.dm +++ b/code/modules/admin/admin_ranks.dm @@ -7,9 +7,9 @@ GLOBAL_PROTECT(protected_ranks) /datum/admin_rank var/name = "NoRank" var/rights = R_DEFAULT - var/exclude_rights = 0 - var/include_rights = 0 - var/can_edit_rights = 0 + var/exclude_rights = NONE + var/include_rights = NONE + var/can_edit_rights = NONE /datum/admin_rank/New(init_name, init_rights, init_exclude_rights, init_edit_rights) if(IsAdminAdvancedProcCall()) @@ -199,6 +199,33 @@ GLOBAL_PROTECT(protected_ranks) testing(msg) #endif +/// 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() + + 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, "+") + /proc/load_admins(no_update) var/dbfail if(!CONFIG_GET(flag/admin_legacy_system) && !SSdbcore.Connect()) @@ -224,8 +251,12 @@ GLOBAL_PROTECT(protected_ranks) //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") + while(admins_regex.Find(admins_text)) - new /datum/admins(rank_names[admins_regex.group[2]], ckey(admins_regex.group[1]), FALSE, TRUE) + 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) + if(!CONFIG_GET(flag/admin_legacy_system) || dbfail) var/datum/db_query/query_load_admins = SSdbcore.NewQuery("SELECT ckey, `rank` FROM [format_table_name("admin")] ORDER BY `rank`") if(!query_load_admins.Execute()) @@ -243,7 +274,7 @@ GLOBAL_PROTECT(protected_ranks) if(GLOB.admin_datums[admin_ckey] || GLOB.deadmins[admin_ckey]) skip = 1 if(!skip) - new /datum/admins(rank_names[admin_rank], admin_ckey) + new /datum/admins(ranks_from_rank_name(admin_rank), admin_ckey) qdel(query_load_admins) //load admins from backup file if(dbfail) @@ -263,30 +294,12 @@ GLOBAL_PROTECT(protected_ranks) skip = TRUE if(skip) continue - new /datum/admins(rank_names[backup_file_json["admins"]["[J]"]], ckey("[J]")) + new /datum/admins(ranks_from_rank_name(backup_file_json["admins"]["[J]"]), ckey("[J]")) #ifdef TESTING var/msg = "Admins Built:\n" for(var/ckey in GLOB.admin_datums) var/datum/admins/D = GLOB.admin_datums[ckey] - msg += "\t[ckey] - [D.rank.name]\n" + msg += "\t[ckey] - [D.rank_names()]\n" testing(msg) #endif return dbfail - -#ifdef TESTING -/client/verb/changerank(newrank in GLOB.admin_ranks) - if(holder) - holder.rank = newrank - else - holder = new /datum/admins(newrank, ckey) - remove_admin_verbs() - holder.associate(src) - -/client/verb/changerights(newrights as num) - if(holder) - holder.rank.rights = newrights - else - holder = new /datum/admins("testing", newrights, ckey) - remove_admin_verbs() - holder.associate(src) -#endif diff --git a/code/modules/admin/admin_verbs.dm b/code/modules/admin/admin_verbs.dm index 3c7d27283ac..5e03c964032 100644 --- a/code/modules/admin/admin_verbs.dm +++ b/code/modules/admin/admin_verbs.dm @@ -275,7 +275,7 @@ GLOBAL_PROTECT(admin_verbs_hideable) if(holder) control_freak = CONTROL_FREAK_SKIN | CONTROL_FREAK_MACROS - var/rights = holder.rank.rights + var/rights = holder.rank_flags() add_verb(src, GLOB.admin_verbs_default) if(rights & R_BUILD) add_verb(src, /client/proc/togglebuildmodeself) @@ -729,7 +729,7 @@ GLOBAL_PROTECT(admin_verbs_hideable) /client/proc/togglebuildmodeself() set name = "Toggle Build Mode Self" set category = "Admin.Events" - if (!(holder.rank.rights & R_BUILD)) + if (!(holder.rank_flags() & R_BUILD)) return if(src.mob) togglebuildmode(src.mob) diff --git a/code/modules/admin/holder2.dm b/code/modules/admin/holder2.dm index 3390477f976..850452716a3 100644 --- a/code/modules/admin/holder2.dm +++ b/code/modules/admin/holder2.dm @@ -10,7 +10,7 @@ GLOBAL_PROTECT(href_token) #define RESULT_2FA_ID 2 /datum/admins - var/datum/admin_rank/rank + var/list/datum/admin_rank/ranks var/target var/name = "nobody's admin datum (no rank)" //Makes for better runtimes @@ -43,7 +43,7 @@ GLOBAL_PROTECT(href_token) /// A lazylist of tagged datums, for quick reference with the View Tags verb var/list/tagged_datums -/datum/admins/New(datum/admin_rank/R, ckey, force_active = FALSE, protected) +/datum/admins/New(list/datum/admin_rank/ranks, ckey, force_active = FALSE, protected) if(IsAdminAdvancedProcCall()) var/msg = " has tried to elevate permissions!" message_admins("[key_name_admin(usr)][msg]") @@ -55,21 +55,21 @@ GLOBAL_PROTECT(href_token) if(!ckey) QDEL_IN(src, 0) CRASH("Admin datum created without a ckey") - if(!istype(R)) + if(!istype(ranks)) QDEL_IN(src, 0) - CRASH("Admin datum created without a rank") + CRASH("Admin datum created with invalid ranks: [ranks] ([json_encode(ranks)])") target = ckey - name = "[ckey]'s admin datum ([R])" - rank = R + name = "[ckey]'s admin datum ([join_admin_ranks(ranks)])" + src.ranks = ranks admin_signature = "Nanotrasen Officer #[rand(0,9)][rand(0,9)][rand(0,9)]" href_token = GenerateToken() if(!CONFIG_GET(flag/forbid_admin_profiling)) - if(R.rights & R_DEBUG) //grant profile access, assuming admin profile access is enabled + if(rank_flags() & R_DEBUG) //grant profile access, assuming admin profile access is enabled world.SetConfig("APP/admin", ckey, "role=admin") //only admins with +ADMIN start admined if(protected) GLOB.protected_admins[target] = src - if (force_active || (R.rights & R_AUTOADMIN)) + if (force_active || (rank_flags() & R_AUTOADMIN)) activate() else deactivate() @@ -166,7 +166,7 @@ GLOBAL_PROTECT(href_token) owner = null /datum/admins/proc/check_for_rights(rights_required) - if(rights_required && !(rights_required & rank.rights)) + if(rights_required && !(rights_required & rank_flags())) return FALSE return TRUE @@ -174,12 +174,12 @@ GLOBAL_PROTECT(href_token) /datum/admins/proc/check_if_greater_rights_than_holder(datum/admins/other) if(!other) return TRUE //they have no rights - if(rank.rights == R_EVERYTHING) + 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.rights != other.rank.rights) - if( (rank.rights & other.rank.rights) == other.rank.rights ) + 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 @@ -331,6 +331,28 @@ GLOBAL_PROTECT(href_token) confidential = TRUE, ) +/// 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/vv_edit_var(var_name, var_value) return FALSE //nice try trialmin diff --git a/code/modules/admin/permissionedit.dm b/code/modules/admin/permissionedit.dm index d9ffcb3eaf3..5544f90fb74 100644 --- a/code/modules/admin/permissionedit.dm +++ b/code/modules/admin/permissionedit.dm @@ -101,9 +101,7 @@ CKEY \[+\] RANK - PERMISSIONS - DENIED - ALLOWED TO EDIT + PERMISSIONS "} for(var/adm_ckey in GLOB.admin_datums+GLOB.deadmins) @@ -126,10 +124,8 @@ output += "" output += "[adm_ckey]
[deadminlink]\[-\]\[SYNC TGDB\][verify_link]" - output += "[D.rank.name]" - output += "[rights2text(D.rank.include_rights," ")]" - output += "[rights2text(D.rank.exclude_rights," ", "-")]" - output += "[rights2text(D.rank.can_edit_rights," ", "*")]" + output += "[D.rank_names()]" + output += "[rights2text(D.rank_flags(), " ")]" output += "" output += "
Search:
" if(QDELETED(usr)) @@ -160,7 +156,7 @@ to_chat(usr, "Editing the rank of this admin is blocked by server configuration.", confidential = TRUE) return if(!CONFIG_GET(flag/admin_legacy_system) && CONFIG_GET(flag/protect_legacy_ranks) && task == "permissions") - if(D.rank in GLOB.protected_ranks) + if((D.ranks & GLOB.protected_ranks).len > 0) to_chat(usr, "Editing the flags of this rank is blocked by server configuration.", confidential = TRUE) return if(CONFIG_GET(flag/load_legacy_ranks_only) && (task == "add" || task == "rank" || task == "permissions")) @@ -202,7 +198,7 @@ if("rank") change_admin_rank(admin_ckey, admin_key, use_db, D, legacy_only) if("permissions") - change_admin_flags(admin_ckey, admin_key, use_db, D, legacy_only) + change_admin_flags(admin_ckey, admin_key, D) if("activate") force_readmin(admin_key, D) if("deactivate") @@ -314,30 +310,81 @@ log_admin("[old_owner] deadmined via auto-deadmin config.") return TRUE +#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/datum/admin_rank/R + var/list/rank_names = list() if(!use_db || (use_db && !legacy_only)) rank_names += "*New Rank*" - for(R in GLOB.admin_ranks) - if((R.rights & usr.client.holder.rank.can_edit_rights) == R.rights) - rank_names[R.name] = R - var/new_rank = input("Please select a rank", "New rank") as null|anything in rank_names - if(new_rank == "*New Rank*") - new_rank = input("Please input a new rank", "New custom rank") as text|null - if(!new_rank) - return - R = rank_names[new_rank] - if(!R) //rank with that name doesn't exist yet - make it - if(D) - R = new(new_rank, D.rank.rights) //duplicate our previous admin_rank but with a new name - else - R = new(new_rank) //blank new admin_rank - GLOB.admin_ranks += R - var/m1 = "[key_name_admin(usr)] edited the admin rank of [admin_key] to [new_rank] [use_db ? "permanently" : "temporarily"]" - var/m2 = "[key_name(usr)] edited the admin rank of [admin_key] to [new_rank] [use_db ? "permanently" : "temporarily"]" + 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 @@ -354,36 +401,38 @@ else old_rank = query_admin_in_db.item[1] qdel(query_admin_in_db) - //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" = new_rank) - ) - 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" = new_rank)) - if(!query_add_rank.warn_execute()) + + 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) - 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 (:time, :round_id, :adminckey, INET_ATON(:adminip), 'add rank', :new_rank, CONCAT('New rank added: ', :new_rank)) - "}, list("time" = SQLtime(), "round_id" = "[GLOB.round_id]", "adminckey" = usr.ckey, "adminip" = usr.client.address, "new_rank" = new_rank)) - if(!query_add_rank_log.warn_execute()) + 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 (:time, :round_id, :adminckey, INET_ATON(:adminip), 'add rank', :new_rank, CONCAT('New rank added: ', :new_rank)) + "}, list("time" = SQLtime(), "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) - return - qdel(query_add_rank_log) - qdel(query_rank_in_db) + 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" = new_rank, "admin_ckey" = admin_ckey) + list("new_rank" = joined_rank, "admin_ckey" = admin_ckey) ) if(!query_change_rank.warn_execute()) qdel(query_change_rank) @@ -392,108 +441,71 @@ 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 (:time, :round_id, :adminckey, INET_ATON(:adminip), 'change admin rank', :target, CONCAT('Rank of ', :target, ' changed from ', :old_rank, ' to ', :new_rank)) - "}, list("time" = SQLtime(), "round_id" = "[GLOB.round_id]", "adminckey" = usr.ckey, "adminip" = usr.client.address, "target" = admin_ckey, "old_rank" = old_rank, "new_rank" = new_rank)) + "}, list("time" = SQLtime(), "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.rank = R //set the admin_rank as our rank + 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(R, admin_ckey) //new admin + 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) -/datum/admins/proc/change_admin_flags(admin_ckey, admin_key, use_db, datum/admins/D, legacy_only) - var/new_flags = input_bitfield(usr, "Include permission flags
[use_db ? "This will affect ALL admins with this rank." : "This will affect only the current admin [admin_key]"]", "admin_flags", D.rank.include_rights, 350, 590, allowed_edit_list = usr.client.holder.rank.can_edit_rights) - if(isnull(new_flags)) - return - var/new_exclude_flags = input_bitfield(usr, "Exclude permission flags
Flags enabled here will be removed from a rank.
Note these take precedence over included flags.
[use_db ? "This will affect ALL admins with this rank." : "This will affect only the current admin [admin_key]"]", "admin_flags", D.rank.exclude_rights, 350, 670, "red", usr.client.holder.rank.can_edit_rights) - if(isnull(new_exclude_flags)) - return - var/new_can_edit_flags = input_bitfield(usr, "Editable permission flags
These are the flags this rank is allowed to edit if they have access to the permissions panel.
They will be unable to modify admins to a rank that has a flag not included here.
[use_db ? "This will affect ALL admins with this rank." : "This will affect only the current admin [admin_key]"]", "admin_flags", D.rank.can_edit_rights, 350, 710, allowed_edit_list = usr.client.holder.rank.can_edit_rights) - if(isnull(new_can_edit_flags)) - return - var/m1 = "[key_name_admin(usr)] edited the permissions of [use_db ? " rank [D.rank.name] permanently" : "[admin_key] temporarily"]" - var/m2 = "[key_name(usr)] edited the permissions of [use_db ? " rank [D.rank.name] permanently" : "[admin_key] temporarily"]" - if(use_db && !legacy_only) - var/rank_name = D.rank.name - var/old_flags - var/old_exclude_flags - var/old_can_edit_flags - var/datum/db_query/query_get_rank_flags = SSdbcore.NewQuery( - "SELECT flags, exclude_flags, can_edit_flags FROM [format_table_name("admin_ranks")] WHERE `rank` = :rank_name", - list("rank_name" = rank_name) - ) - if(!query_get_rank_flags.warn_execute()) - qdel(query_get_rank_flags) - return - if(query_get_rank_flags.NextRow()) - old_flags = text2num(query_get_rank_flags.item[1]) - old_exclude_flags = text2num(query_get_rank_flags.item[2]) - old_can_edit_flags = text2num(query_get_rank_flags.item[3]) - qdel(query_get_rank_flags) - var/datum/db_query/query_change_rank_flags = SSdbcore.NewQuery( - "UPDATE [format_table_name("admin_ranks")] SET flags = :new_flags, exclude_flags = :new_exclude_flags, can_edit_flags = :new_can_edit_flags WHERE `rank` = :rank_name", - list("new_flags" = new_flags, "new_exclude_flags" = new_exclude_flags, "new_can_edit_flags" = new_can_edit_flags, "rank_name" = rank_name) - ) - if(!query_change_rank_flags.warn_execute()) - qdel(query_change_rank_flags) - return - qdel(query_change_rank_flags) - var/log_message = "Permissions of [rank_name] changed from[rights2text(old_flags," ")][rights2text(old_exclude_flags," ", "-")][rights2text(old_can_edit_flags," ", "*")] to[rights2text(new_flags," ")][rights2text(new_exclude_flags," ", "-")][rights2text(new_can_edit_flags," ", "*")]" - var/datum/db_query/query_change_rank_flags_log = SSdbcore.NewQuery({" - INSERT INTO [format_table_name("admin_log")] (datetime, round_id, adminckey, adminip, operation, target, log) - VALUES (:time, :round_id, :adminckey, INET_ATON(:adminip), 'change rank flags', :rank_name, :log) - "}, list("time" = SQLtime(), "round_id" = "[GLOB.round_id]", "adminckey" = usr.ckey, "adminip" = usr.client.address, "rank_name" = rank_name, "log" = log_message)) - if(!query_change_rank_flags_log.warn_execute()) - qdel(query_change_rank_flags_log) - return - qdel(query_change_rank_flags_log) - for(var/datum/admin_rank/R in GLOB.admin_ranks) - if(R.name != D.rank.name) - continue - R.rights = new_flags &= ~new_exclude_flags - R.exclude_rights = new_exclude_flags - R.include_rights = new_flags - R.can_edit_rights = new_can_edit_flags - for(var/i in GLOB.admin_datums+GLOB.deadmins) - var/datum/admins/A = GLOB.admin_datums[i] - if(!A) - A = GLOB.deadmins[i] - if (!A) - continue - if(A.rank.name != D.rank.name) - continue - var/client/C = GLOB.directory[A.target] - A.disassociate() - A.associate(C) +#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
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 - D.disassociate() - if(!findtext(D.rank.name, "([admin_ckey])")) //not a modified subrank, need to duplicate the admin_rank datum to prevent modifying others too - D.rank = new("[D.rank.name]([admin_ckey])", new_flags, new_exclude_flags, new_can_edit_flags) //duplicate our previous admin_rank but with a new name - //we don't add this clone to the admin_ranks list, as it is unique to that ckey - else - D.rank.rights = new_flags &= ~new_exclude_flags - D.rank.include_rights = new_flags - D.rank.exclude_rights = new_exclude_flags - D.rank.can_edit_rights = new_can_edit_flags - var/client/C = GLOB.directory[admin_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(m1) - log_admin(m2) + // 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.rank.can_edit_rights) == R.rights)) + if(R.name == admin_rank && (!(R.rights & usr.client.holder.can_edit_rights_flags()) == R.rights)) to_chat(usr, "You don't have edit rights to all the rights this rank has, rank deletion not permitted.", confidential = TRUE) return if(!CONFIG_GET(flag/admin_legacy_system) && CONFIG_GET(flag/protect_legacy_ranks) && (admin_rank in GLOB.protected_ranks)) @@ -539,7 +551,7 @@ /datum/admins/proc/sync_lastadminrank(admin_ckey, admin_key, datum/admins/D) var/sqlrank = "Player" if (D) - sqlrank = D.rank.name + sqlrank = D.rank_names() var/datum/db_query/query_sync_lastadminrank = SSdbcore.NewQuery( "UPDATE [format_table_name("player")] SET lastadminrank = :rank WHERE ckey = :ckey", list("rank" = sqlrank, "ckey" = admin_ckey) diff --git a/code/modules/admin/sql_ban_system.dm b/code/modules/admin/sql_ban_system.dm index 64a76ca31b5..e8781222670 100644 --- a/code/modules/admin/sql_ban_system.dm +++ b/code/modules/admin/sql_ban_system.dm @@ -972,7 +972,7 @@ if(query_check_adminban_count.NextRow()) var/adminban_count = text2num(query_check_adminban_count.item[1]) var/max_adminbans = MAX_ADMINBANS_PER_ADMIN - if(check_rights(R_PERMISSIONS, show_msg = FALSE) && (rank.can_edit_rights & R_EVERYTHING) == R_EVERYTHING) //edit rights are a more effective way to check hierarchical rank since many non-headmins have R_PERMISSIONS now + if(check_rights(R_PERMISSIONS, show_msg = FALSE) && (can_edit_rights_flags() & R_EVERYTHING) == R_EVERYTHING) //edit rights are a more effective way to check hierarchical rank since many non-headmins have R_PERMISSIONS now max_adminbans = MAX_ADMINBANS_PER_HEADMIN if(adminban_count >= max_adminbans) to_chat(usr, span_danger("You've already logged [max_adminbans] admin ban(s) or more. Do not abuse this function!"), confidential = TRUE) diff --git a/code/modules/admin/verbs/admingame.dm b/code/modules/admin/verbs/admingame.dm index 2b7b4fe9080..df3aacbee49 100644 --- a/code/modules/admin/verbs/admingame.dm +++ b/code/modules/admin/verbs/admingame.dm @@ -18,7 +18,7 @@ body += "Options panel for [M]" if(M.client) body += " played by [M.client] " - body += "\[[M.client.holder ? M.client.holder.rank : "Player"]\]" + body += "\[[M.client.holder ? M.client.holder.rank_names() : "Player"]\]" if(CONFIG_GET(flag/use_exp_tracking)) body += "\[" + M.client.get_exp_living(FALSE) + "\]" diff --git a/code/modules/admin/verbs/deadsay.dm b/code/modules/admin/verbs/deadsay.dm index 75e2749ed39..a88652e0d21 100644 --- a/code/modules/admin/verbs/deadsay.dm +++ b/code/modules/admin/verbs/deadsay.dm @@ -19,7 +19,7 @@ if (!msg) return - var/rank_name = holder.rank + var/rank_name = holder.rank_names() var/admin_name = key if(holder.fakekey) rank_name = pick(strings("admin_nicknames.json", "ranks", "config")) diff --git a/code/modules/admin/verbs/secrets.dm b/code/modules/admin/verbs/secrets.dm index 9ae887952e6..8345f1f340e 100644 --- a/code/modules/admin/verbs/secrets.dm +++ b/code/modules/admin/verbs/secrets.dm @@ -64,7 +64,7 @@ GLOBAL_DATUM(everyone_a_traitor, /datum/everyone_is_a_traitor_controller) if(GLOB.admin_datums) for(var/ckey in GLOB.admin_datums) var/datum/admins/D = GLOB.admin_datums[ckey] - dat += "[ckey] - [D.rank.name]
" + dat += "[ckey] - [D.rank_names()]
" holder << browse(dat, "window=showadmins;size=600x500") //Buttons for debug. if("maint_access_engiebrig") diff --git a/code/modules/cargo/centcom_podlauncher.dm b/code/modules/cargo/centcom_podlauncher.dm index 52eb1611bae..18d165baea4 100644 --- a/code/modules/cargo/centcom_podlauncher.dm +++ b/code/modules/cargo/centcom_podlauncher.dm @@ -192,7 +192,7 @@ . = TRUE if("buildMode") var/mob/holder_mob = holder.mob - if (holder_mob && (holder.holder?.rank?.rights & R_BUILD)) + if (holder_mob && (holder.holder?.rank_flags() & R_BUILD)) togglebuildmode(holder_mob) SSblackbox.record_feedback("tally", "admin_verb", 1, "Toggle Build Mode") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc! . = TRUE diff --git a/code/modules/client/client_procs.dm b/code/modules/client/client_procs.dm index 1d02f71b03d..7f607f7da9a 100644 --- a/code/modules/client/client_procs.dm +++ b/code/modules/client/client_procs.dm @@ -228,20 +228,16 @@ GLOBAL_LIST_INIT(blacklisted_builds, list( connecting_admin = TRUE if(CONFIG_GET(flag/autoadmin)) if(!GLOB.admin_datums[ckey]) - var/datum/admin_rank/autorank - for(var/datum/admin_rank/R in GLOB.admin_ranks) - if(R.name == CONFIG_GET(string/autoadmin_rank)) - autorank = R - break - if(!autorank) + var/list/autoadmin_ranks = ranks_from_rank_name(CONFIG_GET(string/autoadmin_rank)) + if (autoadmin_ranks.len == 0) to_chat(world, "Autoadmin rank not found") else - new /datum/admins(autorank, ckey) + new /datum/admins(autoadmin_ranks, ckey) if(CONFIG_GET(flag/enable_localhost_rank) && !connecting_admin) var/localhost_addresses = list("127.0.0.1", "::1") if(isnull(address) || (address in localhost_addresses)) var/datum/admin_rank/localhost_rank = new("!localhost!", R_EVERYTHING, R_DBRANKS, R_EVERYTHING) //+EVERYTHING -DBRANKS *EVERYTHING - new /datum/admins(localhost_rank, ckey, 1, 1) + new /datum/admins(list(localhost_rank), ckey, 1, 1) //preferences datum - also holds some persistent data for the client (because we may as well keep these datums to a minimum) prefs = GLOB.preferences_datums[ckey] if(prefs) @@ -581,12 +577,9 @@ GLOBAL_LIST_INIT(blacklisted_builds, list( while (query_get_related_cid.NextRow()) related_accounts_cid += "[query_get_related_cid.item[1]], " qdel(query_get_related_cid) - var/admin_rank = "Player" - if (src.holder && src.holder.rank) - admin_rank = src.holder.rank.name - else - if (!GLOB.deadmins[ckey] && check_randomizer(connectiontopic)) - return + var/admin_rank = holder?.rank_names() || "Player" + if (!holder && !GLOB.deadmins[ckey] && check_randomizer(connectiontopic)) + return var/new_player var/datum/db_query/query_client_in_db = SSdbcore.NewQuery( "SELECT 1 FROM [format_table_name("player")] WHERE ckey = :ckey", diff --git a/code/modules/client/verbs/who.dm b/code/modules/client/verbs/who.dm index c0f14f6c01d..0371a3d70ab 100644 --- a/code/modules/client/verbs/who.dm +++ b/code/modules/client/verbs/who.dm @@ -75,7 +75,7 @@ var/msg = "Current Admins:\n" if(holder) for(var/client/C in GLOB.admins) - msg += "\t[C] is a [C.holder.rank]" + msg += "\t[C] is a [C.holder.rank_names()]" if(C.holder.fakekey) msg += " (as [C.holder.fakekey])" @@ -102,7 +102,7 @@ if(C.is_afk()) continue //Don't show afk admins to adminwho if(!C.holder.fakekey) - msg += "\t[C] is a [C.holder.rank]\n" + msg += "\t[C] is a [C.holder.rank_names()]\n" msg += span_info("Adminhelps are also sent through TGS to services like IRC and Discord. If no admins are available in game, sending an adminhelp might still be noticed and responded to.") to_chat(src, msg) diff --git a/code/modules/mob/dead/new_player/poll.dm b/code/modules/mob/dead/new_player/poll.dm index 55257d4ac8f..c1cc72da248 100644 --- a/code/modules/mob/dead/new_player/poll.dm +++ b/code/modules/mob/dead/new_player/poll.dm @@ -332,7 +332,7 @@ GLOBAL_PROTECT(poll_options) return var/admin_rank if(client.holder) - admin_rank = client.holder.rank.name + admin_rank = client.holder.rank_names() else if(poll.admin_only) return diff --git a/config/admins.txt b/config/admins.txt index 394efa7b350..9956a197b9b 100644 --- a/config/admins.txt +++ b/config/admins.txt @@ -143,7 +143,7 @@ Time-Green = Game Master StyleMistake = Game Master actioninja = Game Master bobbahbrown = Game Master -Jaredfogle = Game Master +Jaredfogle = Game Master+Coder WaylandSmithy = Game Master NamelessFairy = Game Master WalterMeldron = Game Master