mirror of
https://github.com/Bubberstation/Bubberstation.git
synced 2025-12-11 18:22:14 +00:00
[MIRROR] Closets now initialize their contents on demand (more than 1.6 seconds of init time saved) [MDB IGNORE] (#16037)
* Closets now initialize their contents on demand (more than 1.6 seconds of init time saved) * conflicts Co-authored-by: Mothblocks <35135081+Mothblocks@users.noreply.github.com> Co-authored-by: Tom <8881105+tf-4@users.noreply.github.com>
This commit is contained in:
2
code/__DEFINES/dcs/signals/signals_closet.dm
Normal file
2
code/__DEFINES/dcs/signals/signals_closet.dm
Normal file
@@ -0,0 +1,2 @@
|
||||
/// Called in /obj/structure/closet/PopulateContents()
|
||||
#define COMSIG_CLOSET_POPULATE_CONTENTS "closet_populate_contents"
|
||||
13
code/__DEFINES/loot.dm
Normal file
13
code/__DEFINES/loot.dm
Normal file
@@ -0,0 +1,13 @@
|
||||
/// The loot table for spawning a random crate
|
||||
#define RANDOM_CRATE_LOOT list( \
|
||||
/obj/structure/closet/crate = 20, \
|
||||
/obj/structure/closet/crate/wooden = 1, \
|
||||
/obj/structure/closet/crate/internals = 1, \
|
||||
/obj/structure/closet/crate/medical = 1, \
|
||||
/obj/structure/closet/crate/freezer = 1, \
|
||||
/obj/structure/closet/crate/radiation = 1, \
|
||||
/obj/structure/closet/crate/hydroponics = 1, \
|
||||
/obj/structure/closet/crate/engineering = 1, \
|
||||
/obj/structure/closet/crate/engineering/electrical = 1, \
|
||||
/obj/structure/closet/crate/science = 1, \
|
||||
)
|
||||
@@ -22,6 +22,3 @@
|
||||
#define MAINT_ODD_WEIGHT 1 //1 out of 10,000 would give metastation (180 spawns) a 2 in 111 chance of spawning an oddity per round, similar to xeno egg.
|
||||
#define MAINT_HOLIDAY_WEIGHT 3500 // When holiday loot is enabled, it'll give every loot item a 25% chance of being a holiday item.
|
||||
#define maint_holiday_weight MAINT_HOLIDAY_WEIGHT
|
||||
|
||||
// List of all maintenance loot spawners, for easy finding at roundstart.
|
||||
GLOBAL_LIST_EMPTY(maintenance_loot_spawners)
|
||||
|
||||
@@ -394,7 +394,4 @@ GLOBAL_LIST_INIT(ratking_coins, list(//Coins: Used by the regal rat mob when spa
|
||||
/obj/item/coin/silver,
|
||||
/obj/item/coin/titanium,
|
||||
))
|
||||
|
||||
// List of all maintenance loot spawners, for easy finding at roundstart.
|
||||
GLOBAL_LIST_EMPTY(maintenance_loot_spawners)
|
||||
*/
|
||||
|
||||
@@ -110,7 +110,6 @@ SUBSYSTEM_DEF(mapping)
|
||||
setup_map_transitions()
|
||||
generate_station_area_list()
|
||||
initialize_reserved_level(transit.z_value)
|
||||
SSticker.OnRoundstart(CALLBACK(src, .proc/spawn_maintenance_loot))
|
||||
generate_z_level_linkages()
|
||||
calculate_default_z_level_gravities()
|
||||
|
||||
@@ -666,10 +665,3 @@ GLOBAL_LIST_EMPTY(the_station_areas)
|
||||
isolated_ruins_z = add_new_zlevel("Isolated Ruins/Reserved", list(ZTRAIT_RESERVED = TRUE, ZTRAIT_ISOLATED_RUINS = TRUE))
|
||||
initialize_reserved_level(isolated_ruins_z.z_value)
|
||||
return isolated_ruins_z.z_value
|
||||
|
||||
/datum/controller/subsystem/mapping/proc/spawn_maintenance_loot()
|
||||
for(var/obj/effect/spawner/random/maintenance/spawner as anything in GLOB.maintenance_loot_spawners)
|
||||
CHECK_TICK
|
||||
|
||||
spawner.spawn_loot()
|
||||
qdel(spawner)
|
||||
|
||||
@@ -35,6 +35,7 @@
|
||||
icon = 'icons/turf/decals.dmi'
|
||||
icon_state = "warningline"
|
||||
layer = TURF_DECAL_LAYER
|
||||
anchored = TRUE
|
||||
|
||||
/obj/effect/turf_decal/Initialize(mapload)
|
||||
. = ..()
|
||||
|
||||
@@ -2,7 +2,6 @@
|
||||
name = "maintenance loot spawner"
|
||||
desc = "Come on Lady Luck, spawn me a pair of sunglasses."
|
||||
icon_state = "loot"
|
||||
spawn_on_init = FALSE
|
||||
// see code/_globalvars/lists/maintenance_loot.dm for loot table
|
||||
|
||||
/obj/effect/spawner/random/maintenance/examine(mob/user)
|
||||
@@ -14,22 +13,6 @@
|
||||
|
||||
. = ..()
|
||||
|
||||
GLOB.maintenance_loot_spawners += src
|
||||
|
||||
/obj/effect/spawner/random/maintenance/should_spawn_on_init()
|
||||
. = ..()
|
||||
|
||||
if(.)
|
||||
return
|
||||
|
||||
// Late loaded templates like shuttles can have maintenance loot.
|
||||
// Once the game state progresses to roundstart, new maint loot spawners should just instantly pop.
|
||||
return (SSticker.current_state >= GAME_STATE_SETTING_UP)
|
||||
|
||||
/obj/effect/spawner/random/maintenance/Destroy()
|
||||
GLOB.maintenance_loot_spawners -= src
|
||||
return ..()
|
||||
|
||||
/obj/effect/spawner/random/maintenance/proc/hide()
|
||||
invisibility = INVISIBILITY_OBSERVER
|
||||
alpha = 100
|
||||
|
||||
@@ -85,21 +85,11 @@
|
||||
/obj/structure/tank_holder/extinguisher/advanced = 1,
|
||||
)
|
||||
|
||||
|
||||
/obj/effect/spawner/random/structure/crate_empty
|
||||
name = "empty crate spawner"
|
||||
icon_state = "crate"
|
||||
loot = list(
|
||||
/obj/structure/closet/crate = 20,
|
||||
/obj/structure/closet/crate/wooden = 1,
|
||||
/obj/structure/closet/crate/internals = 1,
|
||||
/obj/structure/closet/crate/medical = 1,
|
||||
/obj/structure/closet/crate/freezer = 1,
|
||||
/obj/structure/closet/crate/radiation = 1,
|
||||
/obj/structure/closet/crate/hydroponics = 1,
|
||||
/obj/structure/closet/crate/engineering = 1,
|
||||
/obj/structure/closet/crate/engineering/electrical = 1,
|
||||
/obj/structure/closet/crate/science = 1,
|
||||
)
|
||||
loot = RANDOM_CRATE_LOOT
|
||||
|
||||
/obj/effect/spawner/random/structure/crate_empty/make_item(spawn_loc, type_path_to_make)
|
||||
var/obj/structure/closet/crate/peek_a_boo = ..()
|
||||
|
||||
@@ -68,14 +68,14 @@
|
||||
var/obj/item/electronics/airlock/electronics
|
||||
var/can_install_electronics = TRUE
|
||||
|
||||
var/contents_initialized = FALSE
|
||||
|
||||
/obj/structure/closet/Initialize(mapload)
|
||||
if(mapload && !opened) // if closed, any item at the crate's loc is put in the contents
|
||||
addtimer(CALLBACK(src, .proc/take_contents, TRUE), 0)
|
||||
. = ..()
|
||||
update_appearance()
|
||||
PopulateContents()
|
||||
if(QDELETED(src)) //It turns out populate contents has a 1 in 100 chance of qdeling src on /obj/structure/closet/emcloset
|
||||
return //Why
|
||||
populate_contents_immediate()
|
||||
var/static/list/loc_connections = list(
|
||||
COMSIG_CARBON_DISARM_COLLIDE = .proc/locker_carbon,
|
||||
COMSIG_ATOM_MAGICALLY_UNLOCKED = .proc/on_magic_unlock,
|
||||
@@ -84,6 +84,11 @@
|
||||
|
||||
//USE THIS TO FILL IT, NOT INITIALIZE OR NEW
|
||||
/obj/structure/closet/proc/PopulateContents()
|
||||
SEND_SIGNAL(src, COMSIG_CLOSET_POPULATE_CONTENTS)
|
||||
|
||||
/// Populate the closet with stuff that needs to be added before it is opened.
|
||||
/// This is useful for things like traitor objectives.
|
||||
/obj/structure/closet/proc/populate_contents_immediate()
|
||||
return
|
||||
|
||||
/obj/structure/closet/Destroy()
|
||||
@@ -231,6 +236,10 @@
|
||||
return TRUE
|
||||
|
||||
/obj/structure/closet/dump_contents()
|
||||
if (!contents_initialized)
|
||||
contents_initialized = TRUE
|
||||
PopulateContents()
|
||||
|
||||
var/atom/L = drop_location()
|
||||
for(var/atom/movable/AM in src)
|
||||
AM.forceMove(L)
|
||||
|
||||
@@ -26,8 +26,13 @@
|
||||
new /obj/item/circuitboard/machine/techfab/department/cargo(src)
|
||||
new /obj/item/storage/photo_album/qm(src)
|
||||
new /obj/item/circuitboard/machine/ore_silo(src)
|
||||
new /obj/item/card/id/departmental_budget/car(src)
|
||||
new /obj/item/clothing/suit/hooded/wintercoat/cargo/qm(src)
|
||||
new /obj/item/gun/ballistic/rifle/boltaction/brand_new/quartermaster(src) // SKYRAT EDIT - The QM's 'special' head item. It spawns loaded, but you have to find more ammo if you run out and get ready to manually load rounds in!
|
||||
new /obj/item/cargo_teleporter(src) // SKYRAT EDIT - Adds a cargo teleporter to QM locker, so they can intice others to research it
|
||||
new /obj/item/clothing/glasses/hud/gun_permit/sunglasses(src) //SKYRAT EDIT - GUN CARGO
|
||||
|
||||
/obj/structure/closet/secure_closet/quartermaster/populate_contents_immediate()
|
||||
. = ..()
|
||||
|
||||
// Traitor steal objective
|
||||
new /obj/item/card/id/departmental_budget/car(src)
|
||||
|
||||
@@ -10,7 +10,6 @@
|
||||
new /obj/item/computer_hardware/hard_drive/portable/command/ce(src)
|
||||
new /obj/item/radio/headset/heads/ce(src)
|
||||
new /obj/item/megaphone/command(src)
|
||||
new /obj/item/areaeditor/blueprints(src)
|
||||
new /obj/item/holosign_creator/atmos(src)
|
||||
new /obj/item/assembly/flash/handheld(src)
|
||||
new /obj/item/door_remote/chief_engineer(src)
|
||||
@@ -23,6 +22,12 @@
|
||||
new /obj/item/construction/plumbing/engineering(src) //SKYRAT EDIT ADDITION
|
||||
new /obj/item/circuitboard/machine/rodstopper(src) //SKYRAT EDIT ADDITION
|
||||
|
||||
/obj/structure/closet/secure_closet/engineering_chief/populate_contents_immediate()
|
||||
. = ..()
|
||||
|
||||
// Traitor steal objective
|
||||
new /obj/item/areaeditor/blueprints(src)
|
||||
|
||||
/obj/structure/closet/secure_closet/engineering_electrical
|
||||
name = "electrical supplies locker"
|
||||
req_access = list(ACCESS_ENGINE_EQUIP)
|
||||
|
||||
@@ -84,9 +84,7 @@
|
||||
new /obj/item/defibrillator/compact/loaded(src)
|
||||
new /obj/item/healthanalyzer/advanced(src)
|
||||
new /obj/item/assembly/flash/handheld(src)
|
||||
new /obj/item/storage/briefcase/medicalgunset/cmo(src) //SKYRAT ADDITON MEDIGUNS//
|
||||
// new /obj/item/reagent_containers/hypospray/cmo(src) - SKYRAT REMOVAL - New Hyposprays
|
||||
new /obj/item/storage/hypospraykit/cmo(src) //SKYRAT ADDITION - New Hyposprays
|
||||
new /obj/item/storage/briefcase/medicalgunset/cmo(src) //SKYRAT EDIT ADDITION MEDIGUNS
|
||||
new /obj/item/autosurgeon/medical_hud(src)
|
||||
new /obj/item/door_remote/chief_medical_officer(src)
|
||||
new /obj/item/clothing/neck/petcollar(src)
|
||||
@@ -96,6 +94,12 @@
|
||||
new /obj/item/storage/photo_album/cmo(src)
|
||||
new /obj/item/storage/lockbox/medal/med(src)
|
||||
|
||||
/obj/structure/closet/secure_closet/chief_medical/populate_contents_immediate()
|
||||
. = ..()
|
||||
|
||||
// Traitor steal objective
|
||||
//new /obj/item/reagent_containers/hypospray/cmo(src) - ORIGINAL
|
||||
new /obj/item/storage/hypospraykit/cmo(src) //SKYRAT EDIT ADDITION - New Hyposprays
|
||||
|
||||
/obj/structure/closet/secure_closet/animal
|
||||
name = "animal control"
|
||||
|
||||
@@ -8,7 +8,6 @@
|
||||
..()
|
||||
new /obj/item/storage/medkit/regular(src)
|
||||
new /obj/item/storage/box/handcuffs(src)
|
||||
new /obj/item/aicard(src)
|
||||
new /obj/item/assembly/flash/handheld(src)
|
||||
if(prob(50))
|
||||
new /obj/item/ammo_box/magazine/m50(src)
|
||||
@@ -19,6 +18,12 @@
|
||||
new /obj/item/ammo_box/a357(src)
|
||||
new /obj/item/gun/ballistic/revolver/mateba(src)
|
||||
|
||||
/obj/structure/closet/secure_closet/ert_com/populate_contents_immediate()
|
||||
. = ..()
|
||||
|
||||
// Traitor steal objective
|
||||
new /obj/item/aicard(src)
|
||||
|
||||
/obj/structure/closet/secure_closet/ert_sec
|
||||
name = "emergency response team security locker"
|
||||
desc = "A storage unit containing equipment for an Emergency Response Team Security Officer."
|
||||
|
||||
@@ -13,15 +13,18 @@
|
||||
new /obj/item/radio/headset/heads/rd(src)
|
||||
new /obj/item/megaphone/command(src)
|
||||
new /obj/item/storage/lockbox/medal/sci(src)
|
||||
new /obj/item/clothing/suit/armor/reactive/teleport(src)
|
||||
new /obj/item/assembly/flash/handheld(src)
|
||||
new /obj/item/laser_pointer(src)
|
||||
new /obj/item/door_remote/research_director(src)
|
||||
new /obj/item/circuitboard/machine/techfab/department/science(src)
|
||||
new /obj/item/storage/photo_album/rd(src)
|
||||
new /obj/item/storage/box/skillchips/science(src)
|
||||
|
||||
/obj/structure/closet/secure_closet/research_director/populate_contents_immediate()
|
||||
. = ..()
|
||||
|
||||
// Traitor steal objectives
|
||||
new /obj/item/clothing/suit/armor/reactive/teleport(src)
|
||||
new /obj/item/laser_pointer(src)
|
||||
|
||||
/obj/structure/closet/secure_closet/cytology
|
||||
name = "cytology equipment locker"
|
||||
|
||||
@@ -63,11 +63,16 @@
|
||||
new /obj/item/storage/box/flashbangs(src)
|
||||
new /obj/item/shield/riot/tele(src)
|
||||
new /obj/item/storage/belt/security/full(src)
|
||||
new /obj/item/gun/energy/e_gun/hos(src)
|
||||
new /obj/item/pinpointer/nuke(src)
|
||||
new /obj/item/circuitboard/machine/techfab/department/security(src)
|
||||
new /obj/item/storage/photo_album/hos(src)
|
||||
|
||||
/obj/structure/closet/secure_closet/hos/populate_contents_immediate()
|
||||
. = ..()
|
||||
|
||||
// Traitor steal objectives
|
||||
new /obj/item/gun/energy/e_gun/hos(src)
|
||||
new /obj/item/pinpointer/nuke(src)
|
||||
|
||||
/obj/structure/closet/secure_closet/warden
|
||||
name = "\proper warden's locker"
|
||||
req_access = list(ACCESS_ARMORY)
|
||||
@@ -321,7 +326,6 @@
|
||||
|
||||
/obj/structure/closet/secure_closet/armory1/PopulateContents()
|
||||
..()
|
||||
new /obj/item/clothing/suit/hooded/ablative(src)
|
||||
for(var/i in 1 to 3)
|
||||
new /obj/item/clothing/suit/armor/riot(src)
|
||||
for(var/i in 1 to 3)
|
||||
@@ -329,6 +333,12 @@
|
||||
for(var/i in 1 to 3)
|
||||
new /obj/item/shield/riot(src)
|
||||
|
||||
/obj/structure/closet/secure_closet/armory1/populate_contents_immediate()
|
||||
. = ..()
|
||||
|
||||
// Traitor steal objective
|
||||
new /obj/item/clothing/suit/hooded/ablative(src)
|
||||
|
||||
/obj/structure/closet/secure_closet/armory2
|
||||
name = "armory ballistics locker"
|
||||
req_access = list(ACCESS_ARMORY)
|
||||
|
||||
@@ -32,8 +32,9 @@
|
||||
/obj/structure/closet/syndicate/resources
|
||||
desc = "An old, dusty locker."
|
||||
|
||||
/obj/structure/closet/syndicate/resources/PopulateContents()
|
||||
..()
|
||||
// A lot of this stuff is objective items, and it's also only used for debugging, so init times don't matter here.
|
||||
/obj/structure/closet/syndicate/resources/populate_contents_immediate()
|
||||
. = ..()
|
||||
var/common_min = 30 //Minimum amount of minerals in the stack for common minerals
|
||||
var/common_max = 50 //Maximum amount of HONK in the stack for HONK common minerals
|
||||
var/rare_min = 5 //Minimum HONK of HONK in the stack HONK HONK rare minerals
|
||||
@@ -97,7 +98,8 @@
|
||||
desc = "It's an emergency storage closet for repairs."
|
||||
storage_capacity = 60 // This is gonna be used for debug.
|
||||
|
||||
/obj/structure/closet/syndicate/resources/everything/PopulateContents()
|
||||
// A lot of this stuff is objective items, and it's also only used for debugging, so init times don't matter here.
|
||||
/obj/structure/closet/syndicate/resources/everything/populate_contents_immediate()
|
||||
var/list/resources = list(
|
||||
/obj/item/stack/sheet/iron,
|
||||
/obj/item/stack/sheet/glass,
|
||||
|
||||
@@ -20,13 +20,19 @@
|
||||
/obj/structure/closet/emcloset/anchored
|
||||
anchored = TRUE
|
||||
|
||||
/obj/structure/closet/emcloset/Initialize(mapload)
|
||||
. = ..()
|
||||
|
||||
if (prob(1))
|
||||
return INITIALIZE_HINT_QDEL
|
||||
|
||||
/obj/structure/closet/emcloset/PopulateContents() // SKYRAT EDIT OVERRIDE - emergency_spacesuit.dm
|
||||
..()
|
||||
|
||||
if (prob(40))
|
||||
new /obj/item/storage/toolbox/emergency(src)
|
||||
|
||||
switch (pick_weight(list("small" = 35, "aid" = 30, "tank" = 20, "both" = 10, "nothing" = 4, "delete" = 1)))
|
||||
switch (pick_weight(list("small" = 35, "aid" = 30, "tank" = 20, "both" = 10, "nothing" = 4)))
|
||||
if ("small")
|
||||
new /obj/item/tank/internals/emergency_oxygen(src)
|
||||
new /obj/item/tank/internals/emergency_oxygen(src)
|
||||
@@ -48,10 +54,7 @@
|
||||
|
||||
if ("nothing")
|
||||
// doot
|
||||
|
||||
// teehee //Fuck you
|
||||
if ("delete")
|
||||
qdel(src)
|
||||
pass()
|
||||
|
||||
/*
|
||||
* Fire Closet
|
||||
|
||||
@@ -113,11 +113,22 @@
|
||||
|
||||
/obj/structure/closet/crate/maint/Initialize(mapload)
|
||||
..()
|
||||
|
||||
var/static/list/possible_crates = RANDOM_CRATE_LOOT
|
||||
|
||||
var/crate_path = pick_weight(possible_crates)
|
||||
|
||||
var/obj/structure/closet/crate = new crate_path(loc)
|
||||
crate.RegisterSignal(crate, COMSIG_CLOSET_POPULATE_CONTENTS, /obj/structure/closet/.proc/populate_with_random_maint_loot)
|
||||
if (prob(50))
|
||||
crate.opened = TRUE
|
||||
crate.update_appearance()
|
||||
|
||||
return INITIALIZE_HINT_QDEL
|
||||
|
||||
/obj/structure/closet/crate/maint/PopulateContents()
|
||||
. = ..()
|
||||
new /obj/effect/spawner/random/structure/crate_empty(loc)
|
||||
/obj/structure/closet/proc/populate_with_random_maint_loot()
|
||||
SIGNAL_HANDLER
|
||||
|
||||
for (var/i in 1 to rand(2,6))
|
||||
new /obj/effect/spawner/random/maintenance(src)
|
||||
|
||||
@@ -270,9 +281,14 @@
|
||||
|
||||
/obj/structure/closet/crate/goldcrate/PopulateContents()
|
||||
..()
|
||||
new /obj/item/storage/belt/champion(src)
|
||||
|
||||
/obj/structure/closet/crate/goldcrate/populate_contents_immediate()
|
||||
. = ..()
|
||||
|
||||
// /datum/objective_item/stack/gold
|
||||
for(var/i in 1 to 3)
|
||||
new /obj/item/stack/sheet/mineral/gold(src, 1, FALSE)
|
||||
new /obj/item/storage/belt/champion(src)
|
||||
|
||||
/obj/structure/closet/crate/silvercrate
|
||||
name = "silver crate"
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
/// Checks that the length of the initial contents of a closet doesn't exceed its storage capacity
|
||||
/// Checks that the length of the initial contents of a closet doesn't exceed its storage capacity.
|
||||
/// Also checks that nothing inside that isn't immediate is a steal objective.
|
||||
/datum/unit_test/closets
|
||||
|
||||
/datum/unit_test/closets/Run()
|
||||
@@ -8,6 +9,16 @@
|
||||
|
||||
for(var/closet_type in all_closets)
|
||||
var/obj/structure/closet/closet = allocate(closet_type)
|
||||
|
||||
// Copy is necessary otherwise closet.contents - immediate_contents returns an empty list
|
||||
var/list/immediate_contents = closet.contents.Copy()
|
||||
|
||||
closet.PopulateContents()
|
||||
var/contents_len = length(closet.contents)
|
||||
|
||||
if(contents_len > closet.storage_capacity)
|
||||
Fail("Initial Contents of [closet.type] ([contents_len]) exceed its storage capacity ([closet.storage_capacity]).")
|
||||
TEST_FAIL("Initial Contents of [closet.type] ([contents_len]) exceed its storage capacity ([closet.storage_capacity]).")
|
||||
|
||||
for (var/obj/item/item in closet.contents - immediate_contents)
|
||||
if (item.type in GLOB.steal_item_handler.objectives_by_path)
|
||||
TEST_FAIL("[closet_type] contains a steal objective [item.type] in PopulateContents(). Move it to populate_contents_immediate().")
|
||||
|
||||
@@ -105,6 +105,7 @@
|
||||
#include "code\__DEFINES\lighting.dm"
|
||||
#include "code\__DEFINES\living.dm"
|
||||
#include "code\__DEFINES\logging.dm"
|
||||
#include "code\__DEFINES\loot.dm"
|
||||
#include "code\__DEFINES\machines.dm"
|
||||
#include "code\__DEFINES\magic.dm"
|
||||
#include "code\__DEFINES\maps.dm"
|
||||
@@ -217,6 +218,7 @@
|
||||
#include "code\__DEFINES\dcs\signals\signals_changeling.dm"
|
||||
#include "code\__DEFINES\dcs\signals\signals_circuit.dm"
|
||||
#include "code\__DEFINES\dcs\signals\signals_client.dm"
|
||||
#include "code\__DEFINES\dcs\signals\signals_closet.dm"
|
||||
#include "code\__DEFINES\dcs\signals\signals_clothing.dm"
|
||||
#include "code\__DEFINES\dcs\signals\signals_container.dm"
|
||||
#include "code\__DEFINES\dcs\signals\signals_customizable.dm"
|
||||
|
||||
Reference in New Issue
Block a user