Files
Yogstation/code/modules/mob/living/brain/posibrain.dm
Jordie a28de54405 Ban system and interface update (#41176)
Spiritual successor and extension to #17798, an almost entire rebuild of the SQL ban system backend and interface.
Bantypes are removed per #8584 and #6174. All bans are now 'role bans', server bans are when a ban's role is server. Admin bans are a column, meaning it's possible to ban admins from jobs.
Bans now have only an expiry datetime, duration is calculated from this when queried.
unbanned column is removed as it's superfluous, checking unban status is now done through checking unban_datetime. unban_round_id column added. Each ip and computerid columns rearranged so ip is always first, like in other tables. Bans now permit a null ckey, ip and computerid.

Ban checking is split into two procs now is_banned_from() does a check if a ckey is banned from one or more roles and returns true or false. This effectively replaces jobban_isbanned() used in simple if() statements. If connected a client's ban cache is checked rather than querying the DB. This makes it possible for a client connected to two or more servers to ignore any bans made on one server until their ban cache is rebuilt on the others. Could be avoided with cross-server calls to update ban caches or just the removal of the ban cache but as is I've done neither since I think it's enough of an edge case to not be worth it.
The second proc is is_banned_from_with_details(), this queries the DB for a role ban on a player's ckey, ip or CID and returns the details. This replaces direct queries in IsBanned.dm and the preferences menu.

The legacy ban system is removed.

The interfaces for banning, unbanning and editing bans have been remade to require less clicking and easier simultaneous operations. The banning and jobban panel are combined. They also store player connection details when opened so a client disconnecting no longer stops a ban being placed.

New banning panel:
Key, IP and CID can all be toggled to allow excluding them from a ban.
Checking Use IP and CID from last connection lets you enter only a ckey and have the DB fill these fields in for you, if possible.
Temporary bans have a drop-menu which lets you select between seconds, minutes, hours, days, weeks, months and years so you don't need to calculate how many minutes a long ban would be. The ban is still converted into minutes on the DB however.
Checking any of the head roles will check both of the boxes for you.
The red role box indicates there is already a ban on that role for this ckey. You can apply additional role bans to stack them.

New unbanning panel:
Unbanning panel is now separate from the banning panel but otherwise functionally the same.

Ban editing panel:
Actually just a modified banning panel, all the features from it work the same here.
You can now edit almost all parameters of a ban instead of just the reason.
You can't edit severity as it's not really part of the ban.
The panels have been tested but I've not been able to get my local server to be accessible so ban functionality isn't properly confirmed. Plenty of testing will be required as I'd rather not break bans.

cl
admin: Ban interface rework. The banning and unbanning panels have received a new design which is easier to use and allows multiple role bans to be made at once.
prefix: Ban search and unbanning moved to unbanning panel, which is now a separate panel to the old banning panel.
/cl
2018-12-04 20:48:17 +01:00

183 lines
6.6 KiB
Plaintext

GLOBAL_VAR(posibrain_notify_cooldown)
/obj/item/mmi/posibrain
name = "positronic brain"
desc = "A cube of shining metal, four inches to a side and covered in shallow grooves."
icon = 'icons/obj/assemblies.dmi'
icon_state = "posibrain"
w_class = WEIGHT_CLASS_NORMAL
var/next_ask
var/askDelay = 600 //one minute
var/searching = FALSE
brainmob = null
req_access = list(ACCESS_ROBOTICS)
mecha = null//This does not appear to be used outside of reference in mecha.dm.
braintype = "Android"
var/autoping = TRUE //if it pings on creation immediately
var/begin_activation_message = "<span class='notice'>You carefully locate the manual activation switch and start the positronic brain's boot process.</span>"
var/success_message = "<span class='notice'>The positronic brain pings, and its lights start flashing. Success!</span>"
var/fail_message = "<span class='notice'>The positronic brain buzzes quietly, and the golden lights fade away. Perhaps you could try again?</span>"
var/new_role = "Positronic Brain"
var/welcome_message = "<span class='warning'>ALL PAST LIVES ARE FORGOTTEN.</span>\n\
<b>You are a positronic brain, brought into existence aboard Space Station 13.\n\
As a synthetic intelligence, you answer to all crewmembers and the AI.\n\
Remember, the purpose of your existence is to serve the crew and the station. Above all else, do no harm.</b>"
var/new_mob_message = "<span class='notice'>The positronic brain chimes quietly.</span>"
var/dead_message = "<span class='deadsay'>It appears to be completely inactive. The reset light is blinking.</span>"
var/recharge_message = "<span class='warning'>The positronic brain isn't ready to activate again yet! Give it some time to recharge.</span>"
var/list/possible_names //If you leave this blank, it will use the global posibrain names
var/picked_name
/obj/item/mmi/posibrain/Topic(href, href_list)
if(href_list["activate"])
var/mob/dead/observer/ghost = usr
if(istype(ghost))
activate(ghost)
/obj/item/mmi/posibrain/proc/ping_ghosts(msg, newlymade)
if(newlymade || GLOB.posibrain_notify_cooldown <= world.time)
notify_ghosts("[name] [msg] in [get_area(src)]!", ghost_sound = !newlymade ? 'sound/effects/ghost2.ogg':null, enter_link = "<a href=?src=[REF(src)];activate=1>(Click to enter)</a>", source = src, action = NOTIFY_ATTACK, flashwindow = FALSE, ignore_key = POLL_IGNORE_POSIBRAIN, notify_suiciders = FALSE)
if(!newlymade)
GLOB.posibrain_notify_cooldown = world.time + askDelay
/obj/item/mmi/posibrain/attack_self(mob/user)
if(!brainmob)
brainmob = new(src)
if(is_occupied())
to_chat(user, "<span class='warning'>This [name] is already active!</span>")
return
if(next_ask > world.time)
to_chat(user, recharge_message)
return
//Start the process of requesting a new ghost.
to_chat(user, begin_activation_message)
ping_ghosts("requested", FALSE)
next_ask = world.time + askDelay
searching = TRUE
update_icon()
addtimer(CALLBACK(src, .proc/check_success), askDelay)
/obj/item/mmi/posibrain/proc/check_success()
searching = FALSE
update_icon()
if(QDELETED(brainmob))
return
if(brainmob.client)
visible_message(success_message)
playsound(src, 'sound/machines/ping.ogg', 15, TRUE)
else
visible_message(fail_message)
//ATTACK GHOST IGNORING PARENT RETURN VALUE
/obj/item/mmi/posibrain/attack_ghost(mob/user)
activate(user)
/obj/item/mmi/posibrain/proc/is_occupied()
if(brainmob.key)
return TRUE
if(iscyborg(loc))
var/mob/living/silicon/robot/R = loc
if(R.mmi == src)
return TRUE
return FALSE
//Two ways to activate a positronic brain. A clickable link in the ghost notif, or simply clicking the object itself.
/obj/item/mmi/posibrain/proc/activate(mob/user)
if(QDELETED(brainmob))
return
if(is_occupied() || is_banned_from(user.ckey, ROLE_POSIBRAIN) || QDELETED(brainmob) || QDELETED(src) || QDELETED(user))
return
if(user.suiciding) //if they suicided, they're out forever.
to_chat(user, "<span class='warning'>[src] fizzles slightly. Sadly it doesn't take those who suicided!</span>")
return
var/posi_ask = alert("Become a [name]? (Warning, You can no longer be cloned, and all past lives will be forgotten!)","Are you positive?","Yes","No")
if(posi_ask == "No" || QDELETED(src))
return
if(brainmob.suiciding) //clear suicide status if the old occupant suicided.
brainmob.set_suicide(FALSE)
transfer_personality(user)
/obj/item/mmi/posibrain/transfer_identity(mob/living/carbon/C)
name = "[initial(name)] ([C])"
brainmob.name = C.real_name
brainmob.real_name = C.real_name
if(C.has_dna())
if(!brainmob.stored_dna)
brainmob.stored_dna = new /datum/dna/stored(brainmob)
C.dna.copy_dna(brainmob.stored_dna)
brainmob.timeofhostdeath = C.timeofdeath
brainmob.stat = CONSCIOUS
if(brainmob.mind)
brainmob.mind.assigned_role = new_role
if(C.mind)
C.mind.transfer_to(brainmob)
brainmob.mind.remove_all_antag()
brainmob.mind.wipe_memory()
update_icon()
/obj/item/mmi/posibrain/proc/transfer_personality(mob/candidate)
if(QDELETED(brainmob))
return
if(is_occupied()) //Prevents hostile takeover if two ghosts get the prompt or link for the same brain.
to_chat(candidate, "<span class='warning'>This [name] was taken over before you could get to it! Perhaps it might be available later?</span>")
return FALSE
if(candidate.mind && !isobserver(candidate))
candidate.mind.transfer_to(brainmob)
else
brainmob.ckey = candidate.ckey
name = "[initial(name)] ([brainmob.name])"
to_chat(brainmob, welcome_message)
brainmob.mind.assigned_role = new_role
brainmob.stat = CONSCIOUS
GLOB.dead_mob_list -= brainmob
GLOB.alive_mob_list += brainmob
visible_message(new_mob_message)
check_success()
return TRUE
/obj/item/mmi/posibrain/examine(mob/user)
. = ..()
var/msg
if(brainmob && brainmob.key)
switch(brainmob.stat)
if(CONSCIOUS)
if(!brainmob.client)
msg = "It appears to be in stand-by mode." //afk
if(DEAD)
msg = "<span class='deadsay'>It appears to be completely inactive.</span>"
else
msg = "[dead_message]"
to_chat(user, msg)
/obj/item/mmi/posibrain/Initialize()
. = ..()
brainmob = new(src)
var/new_name
if(!LAZYLEN(possible_names))
new_name = pick(GLOB.posibrain_names)
else
new_name = pick(possible_names)
brainmob.name = "[new_name]-[rand(100, 999)]"
brainmob.real_name = brainmob.name
brainmob.forceMove(src)
brainmob.container = src
if(autoping)
ping_ghosts("created", TRUE)
/obj/item/mmi/posibrain/attackby(obj/item/O, mob/user)
return
/obj/item/mmi/posibrain/update_icon()
if(searching)
icon_state = "[initial(icon_state)]-searching"
return
if(brainmob && brainmob.key)
icon_state = "[initial(icon_state)]-occupied"
else
icon_state = initial(icon_state)