mirror of
https://github.com/GTNewHorizons/NotEnoughItems.git
synced 2026-10-27 06:44:00 +00:00
Compare commits
8
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
195b984493 | ||
|
|
8d3a0056ab | ||
|
|
a34df30231 | ||
|
|
61f427f0d6 | ||
|
|
82e58c4eb5 | ||
|
|
13e9f3c547 | ||
|
|
405bb35495 | ||
|
|
368018c2b1 |
No files matched your search
@@ -9,9 +9,6 @@ import java.io.InputStreamReader;
|
||||
import java.net.MalformedURLException;
|
||||
import java.net.URL;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.Collection;
|
||||
import java.util.Collections;
|
||||
import java.util.HashSet;
|
||||
import java.util.Iterator;
|
||||
import java.util.List;
|
||||
@@ -48,20 +45,12 @@ import codechicken.nei.recipe.GuiRecipeTab;
|
||||
import codechicken.nei.recipe.StackInfo;
|
||||
import cpw.mods.fml.client.CustomModLoadingErrorDisplayException;
|
||||
import cpw.mods.fml.common.FMLCommonHandler;
|
||||
import cpw.mods.fml.common.Loader;
|
||||
import cpw.mods.fml.common.eventhandler.SubscribeEvent;
|
||||
import cpw.mods.fml.common.gameevent.TickEvent;
|
||||
import cpw.mods.fml.common.gameevent.TickEvent.Phase;
|
||||
|
||||
public class ClientHandler {
|
||||
|
||||
private static String[] defaultSerialHandlers = {
|
||||
"WayofTime.alchemicalWizardry.client.nei.NEIAlchemyRecipeHandler" };
|
||||
private static String[] defaultHeightHackHandlerRegex = { "buildcraft.compat.nei.*",
|
||||
"cofh.thermalexpansion.plugins.nei.handlers.*", "crazypants.enderio.nei.*",
|
||||
"forestry.factory.recipes.nei.*", "ic2.neiIntegration.core.recipehandler.*", "mariculture.plugins.nei.*",
|
||||
"redgear.brewcraft.plugins.nei.*", "tconstruct.plugins.nei.*",
|
||||
"WayofTime.alchemicalWizardry.client.nei.*", };
|
||||
private static String[] defaultHandlerOrdering = {
|
||||
"# Each line in this file should either be a comment (starts with '#') or an ordering.",
|
||||
"# Ordering lines are <handler ID>,<ordering number>.",
|
||||
@@ -165,16 +154,19 @@ public class ClientHandler {
|
||||
if (!file.exists()) {
|
||||
try (FileWriter writer = new FileWriter(file)) {
|
||||
NEIClientConfig.logger.info("Creating default serial handlers list {}", file);
|
||||
Collection<String> toSave = Loader.isModLoaded("dreamcraft") ? Collections.singletonList("")
|
||||
: Arrays.asList(defaultSerialHandlers);
|
||||
IOUtils.writeLines(toSave, "\n", writer);
|
||||
URL defaultSerialHandlersResource = ClientHandler.class
|
||||
.getResource("/assets/nei/cfg/serialhandlers.cfg");
|
||||
if (defaultSerialHandlersResource != null) {
|
||||
IOUtils.copy(defaultSerialHandlersResource.openStream(), writer);
|
||||
}
|
||||
} catch (IOException e) {
|
||||
NEIClientConfig.logger.error("Failed to save default serial handlers list to file {}", file, e);
|
||||
}
|
||||
}
|
||||
try (FileReader reader = new FileReader(file)) {
|
||||
NEIClientConfig.logger.info("Loading serial handlers from file {}", file);
|
||||
NEIClientConfig.serialHandlers = new HashSet<>(IOUtils.readLines(reader));
|
||||
NEIClientConfig.serialHandlers = IOUtils.readLines(reader).stream().filter((line) -> !line.startsWith("#"))
|
||||
.collect(Collectors.toCollection(HashSet::new));
|
||||
} catch (IOException e) {
|
||||
NEIClientConfig.logger.error("Failed to load serial handlers from file {}", file, e);
|
||||
}
|
||||
@@ -185,8 +177,11 @@ public class ClientHandler {
|
||||
if (!file.exists()) {
|
||||
try (FileWriter writer = new FileWriter(file)) {
|
||||
NEIClientConfig.logger.info("Creating default height hack handlers list {}", file);
|
||||
Collection<String> toSave = Arrays.asList(defaultHeightHackHandlerRegex);
|
||||
IOUtils.writeLines(toSave, "\n", writer);
|
||||
URL defaultHeightHackHandlersResource = ClientHandler.class
|
||||
.getResource("/assets/nei/cfg/heighthackhandlers.cfg");
|
||||
if (defaultHeightHackHandlersResource != null) {
|
||||
IOUtils.copy(defaultHeightHackHandlersResource.openStream(), writer);
|
||||
}
|
||||
} catch (IOException e) {
|
||||
NEIClientConfig.logger.error("Failed to save default height hack handlers list to file {}", file, e);
|
||||
}
|
||||
@@ -194,7 +189,8 @@ public class ClientHandler {
|
||||
|
||||
try (FileReader reader = new FileReader(file)) {
|
||||
NEIClientConfig.logger.info("Loading height hack handlers from file {}", file);
|
||||
NEIClientConfig.heightHackHandlerRegex = IOUtils.readLines(reader).stream().map(Pattern::compile)
|
||||
NEIClientConfig.heightHackHandlerRegex = IOUtils.readLines(reader).stream()
|
||||
.filter((line) -> !line.startsWith("#")).map(Pattern::compile)
|
||||
.collect(Collectors.toCollection(HashSet::new));
|
||||
} catch (IOException e) {
|
||||
NEIClientConfig.logger.error("Failed to load height hack handlers from file {}", file, e);
|
||||
|
||||
@@ -135,6 +135,13 @@ public class IMCHandler {
|
||||
NEIClientConfig.logger.warn("Missing handlerID for registerCatalystInfo!");
|
||||
return;
|
||||
}
|
||||
final String modId = tag.hasKey("modId") ? tag.getString("modId") : null;
|
||||
final boolean requiresMod = tag.getBoolean("modRequired");
|
||||
final String excludedModId = tag.hasKey("excludedModId") ? tag.getString("excludedModId") : null;
|
||||
|
||||
if (requiresMod && modId != null && !Loader.isModLoaded(modId)) return;
|
||||
if (excludedModId != null && Loader.isModLoaded(excludedModId)) return;
|
||||
|
||||
final String itemName = tag.getString("itemName");
|
||||
final String nbtInfo = tag.hasKey("nbtInfo") ? tag.getString("nbtInfo") : null;
|
||||
if (itemName.isEmpty()) {
|
||||
@@ -170,7 +177,7 @@ public class IMCHandler {
|
||||
final String itemName = tag.getString("itemName");
|
||||
final String nbtInfo = tag.hasKey("nbtInfo") ? tag.getString("nbtInfo") : null;
|
||||
if (itemName.isEmpty()) {
|
||||
NEIClientConfig.logger.warn(String.format("Missing itemName for registerCatalystInfo in `%s`!", handlerID));
|
||||
NEIClientConfig.logger.warn(String.format("Missing itemName for removeCatalystInfo in `%s`!", handlerID));
|
||||
return;
|
||||
}
|
||||
final ItemStack itemStack = NEIServerUtils.getModdedItem(itemName, nbtInfo);
|
||||
|
||||
@@ -100,6 +100,8 @@ public abstract class GuiRecipe<H extends IRecipeHandler> extends GuiContainer i
|
||||
|
||||
private int yShift = 0;
|
||||
|
||||
private boolean isHeightHackApplied = false;
|
||||
|
||||
protected GuiRecipe(GuiScreen prevgui) {
|
||||
super(new ContainerRecipe());
|
||||
recipeTabs = new GuiRecipeTabs(this);
|
||||
@@ -118,7 +120,7 @@ public abstract class GuiRecipe<H extends IRecipeHandler> extends GuiContainer i
|
||||
|
||||
/**
|
||||
* Many old mods assumed a fixed NEI window height of {@code 166} pixels. Now that this is no longer the case, their
|
||||
* fluid mouseover handling is broken. This helper class fixes these old mods by hacking the {@link #height}
|
||||
* tooltip and click zone handling is broken. This helper class fixes these old mods by hacking the {@link #height}
|
||||
* property's value so that these old mods' calculations return the correct value for the new height.
|
||||
*
|
||||
* <p>
|
||||
@@ -128,31 +130,43 @@ public abstract class GuiRecipe<H extends IRecipeHandler> extends GuiContainer i
|
||||
private class HeightHack implements AutoCloseable {
|
||||
|
||||
private final int trueHeight;
|
||||
private final int trueGuiTop;
|
||||
|
||||
private HeightHack() {
|
||||
trueHeight = height;
|
||||
trueGuiTop = guiTop;
|
||||
|
||||
boolean applyHeightHack = NEIClientConfig.heightHackHandlerRegex.stream()
|
||||
isHeightHackApplied = NEIClientConfig.heightHackHandlerRegex.stream()
|
||||
.map(pattern -> pattern.matcher(handler.getHandlerId())).anyMatch(Matcher::matches);
|
||||
if (applyHeightHack) {
|
||||
// The old mods use the calculation ((height - 166) / 2) to compute the y-value of
|
||||
// the top edge of the NEI window.
|
||||
// guiTop is exactly that: the y-value of the top edge of the NEI window.
|
||||
// So we set height to be equal to the inverse of this calculation, applied to
|
||||
// guiTop, so that old mods get the right result back when they use this calculation.
|
||||
if (isHeightHackApplied) {
|
||||
// guiTop is the top edge of the recipe screen on the y-axis. Old recipe handlers expect a single paging
|
||||
// widget at the top and at the bottom of the recipe screen with a height of 16px, but GTNH NEI move the
|
||||
// bottom paging widget to the top following JEI's UI increasing the forbidden zone at the top to 32px.
|
||||
// To fix these old recipe handlers we move guiTop down 16px so that they can keep working with the old
|
||||
// 16px gap.
|
||||
guiTop += 16;
|
||||
|
||||
// The old NEI recipe screen had a fixed width and height and was always centered on the screen. Legacy
|
||||
// recipe handlers use the calculation ((height - 166) / 2) to compute the y-value of the top edge of
|
||||
// the NEI recipe screen. GTNH NEI now has a flexible recipe screen length and is no longer centered
|
||||
// vertically, so in order for the top-of-screen calculation to return the correct result, we have to
|
||||
// hack the height field with an inverse of the calculation using the actual top of the recipe screen
|
||||
// stored in guiTop.
|
||||
height = (2 * guiTop) + 166;
|
||||
|
||||
// For reference, in case we ever need to modify width as well:
|
||||
// the legacy calculation used for width is ((width - 176) / 2), which should
|
||||
// evaluate to be equal to guiWidth (the x-value of the left edge of the NEI window).
|
||||
// So if we wanted to override width as well, we'd do this:
|
||||
// the legacy calculation used for width is ((width - 176) / 2), which should evaluate to be equal to
|
||||
// guiWidth (the x-value of the left edge of the NEI recipe screen). So if we wanted to override width
|
||||
// as well, we'd do this:
|
||||
// width = (2 * guiWidth) + 176;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void close() {
|
||||
guiTop = trueGuiTop;
|
||||
height = trueHeight;
|
||||
isHeightHackApplied = false;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -454,9 +468,9 @@ public abstract class GuiRecipe<H extends IRecipeHandler> extends GuiContainer i
|
||||
@Override
|
||||
public List<String> handleItemTooltip(GuiContainer gui, ItemStack stack, int mousex, int mousey,
|
||||
List<String> currenttip) {
|
||||
// Height hacking is probably not needed as it doesn't look like any mods check mouse
|
||||
// position for this method.
|
||||
for (int i : getRecipeIndices()) currenttip = handler.handleItemTooltip(this, stack, currenttip, i);
|
||||
try (HeightHack heightHack = new HeightHack()) {
|
||||
for (int i : getRecipeIndices()) currenttip = handler.handleItemTooltip(this, stack, currenttip, i);
|
||||
}
|
||||
|
||||
return currenttip;
|
||||
}
|
||||
@@ -542,6 +556,12 @@ public abstract class GuiRecipe<H extends IRecipeHandler> extends GuiContainer i
|
||||
final int recipesPerPage = getRecipesPerPage();
|
||||
for (int i = page * recipesPerPage; i < handler.numRecipes() && i < (page + 1) * recipesPerPage; i++) {
|
||||
Point p = getRecipePosition(i);
|
||||
// Legacy recipe handlers only expect a single paging widget at the top of the recipe screen, in contrast,
|
||||
// GTNH NEI moves the recipe paging widget from the bottom to the top, which means said legacy handlers will
|
||||
// position item slots 16px too high in the screen.
|
||||
if (isHeightHackApplied) {
|
||||
p.translate(0, 16);
|
||||
}
|
||||
|
||||
List<PositionedStack> stacks = handler.getIngredientStacks(i);
|
||||
for (PositionedStack stack : stacks) slotcontainer.addSlot(stack, p.x, p.y);
|
||||
@@ -735,7 +755,12 @@ public abstract class GuiRecipe<H extends IRecipeHandler> extends GuiContainer i
|
||||
}
|
||||
|
||||
public Point getRecipePosition(int recipe) {
|
||||
return new Point(5, 32 + yShift + ((recipe % getRecipesPerPage()) * handlerInfo.getHeight()));
|
||||
// Legacy recipe handlers using the height hack might use getRecipePosition in combination with guiTop/height to
|
||||
// position certain elements like tooltips. Since guiTop is moved down by 16px during height hacking, we need to
|
||||
// reduce the vertical shift here to 16px instead of 32px.
|
||||
return new Point(
|
||||
5,
|
||||
(isHeightHackApplied ? 16 : 32) + yShift + ((recipe % getRecipesPerPage()) * handlerInfo.getHeight()));
|
||||
}
|
||||
|
||||
public abstract ArrayList<H> getCurrentRecipeHandlers();
|
||||
|
||||
@@ -0,0 +1,13 @@
|
||||
# Each line in this file should either be a comment (starts with '#') or a fully qualified class name (that includes the
|
||||
# package name) for a recipe handler. Wildcard patterns ('*') can be used to catch multiple classes with a single entry.
|
||||
# If you delete this file, it will be regenerated with the default height hacked handlers list.
|
||||
buildcraft.compat.nei.*
|
||||
cofh.thermalexpansion.plugins.nei.handlers.*
|
||||
crazypants.enderio.nei.*
|
||||
forestry.factory.recipes.nei.*
|
||||
ic2.neiIntegration.core.recipehandler.*
|
||||
mariculture.plugins.nei.*
|
||||
redgear.brewcraft.plugins.nei.*
|
||||
tconstruct.plugins.nei.*
|
||||
WayofTime.alchemicalWizardry.client.nei.*
|
||||
exnihilo.compatibility.nei.*
|
||||
@@ -0,0 +1,6 @@
|
||||
# This version of NEI loads recipes in parallel. Some recipe handlers have not been designed with that in mind and might
|
||||
# crash. List these handlers in this file to force them to be loaded a single thread. Each line in this file should
|
||||
# either be a comment (starts with '#') or a fully qualified class name (that includes the package name) for a recipe
|
||||
# handler.
|
||||
# If you delete this file, it will be regenerated with the default height hacked handlers list.
|
||||
WayofTime.alchemicalWizardry.client.nei.NEIAlchemyRecipeHandler
|
||||
@@ -373,11 +373,11 @@ NEIArcFurnaceHandlerCarrot Ingot,Immersive Engineering,minecraft:carrot,,Immersi
|
||||
NEIArcFurnaceHandlerExtraction,Immersive Engineering,minecraft:glowstone_dust,,ImmersiveEngineering,TRUE,,,65,166,5,,,,,,
|
||||
blusunrize.immersiveengineering.client.nei.NEIShaderBagHandler,Immersive Engineering,ImmersiveEngineering:shaderBag,,ImmersiveEngineering,TRUE,,,65,166,5,,,,,,
|
||||
blusunrize.immersiveengineering.client.nei.NEIBlueprintHandler,Immersive Engineering,ImmersiveEngineering:woodenDevice:5,,ImmersiveEngineering,TRUE,,,65,166,5,,,,,,
|
||||
blusunrize.immersiveengineering.client.nei.NEICokeOvenHandler,Immersive Engineering,ImmersiveEngineering:stoneDecoration:1,,ImmersiveEngineering,TRUE,,,71,166,5,,,,,,
|
||||
blusunrize.immersiveengineering.client.nei.NEIBlastFurnaceHandler,Immersive Engineering,ImmersiveEngineering:stoneDecoration:2,,ImmersiveEngineering,TRUE,,,71,166,5,,,,,,
|
||||
blusunrize.immersiveengineering.client.nei.NEISqueezerHandler,Immersive Engineering,ImmersiveEngineering:metalMultiblock:2,,ImmersiveEngineering,TRUE,,,71,166,5,,,,,,
|
||||
blusunrize.immersiveengineering.client.nei.NEIFermenterHandler,Immersive Engineering,ImmersiveEngineering:metalMultiblock:3,,ImmersiveEngineering,TRUE,,,71,166,5,,,,,,
|
||||
blusunrize.immersiveengineering.client.nei.NEIRefineryHandler,Immersive Engineering,ImmersiveEngineering:metalMultiblock:4,,ImmersiveEngineering,TRUE,,,65,166,5,,,,,,
|
||||
blusunrize.immersiveengineering.client.nei.NEICokeOvenHandler,Immersive Engineering,ImmersiveEngineering:stoneDecoration:1,,ImmersiveEngineering,TRUE,,,71,166,2,,,,,
|
||||
blusunrize.immersiveengineering.client.nei.NEIBlastFurnaceHandler,Immersive Engineering,ImmersiveEngineering:stoneDecoration:2,,ImmersiveEngineering,TRUE,,,71,166,2,,,,,,
|
||||
blusunrize.immersiveengineering.client.nei.NEISqueezerHandler,Immersive Engineering,ImmersiveEngineering:metalMultiblock:2,,ImmersiveEngineering,TRUE,,1,64,166,2,,,,,,
|
||||
blusunrize.immersiveengineering.client.nei.NEIFermenterHandler,Immersive Engineering,ImmersiveEngineering:metalMultiblock:3,,ImmersiveEngineering,TRUE,,,71,166,2,,,,,,
|
||||
blusunrize.immersiveengineering.client.nei.NEIRefineryHandler,Immersive Engineering,ImmersiveEngineering:metalMultiblock:4,,ImmersiveEngineering,TRUE,,,65,166,2,,,,,,
|
||||
blusunrize.immersiveengineering.client.nei.NEIBottlingMachineHandler,Immersive Engineering,minecraft:glass_bottle,,ImmersiveEngineering,TRUE,,,65,166,5,,,,,,
|
||||
blusunrize.immersiveengineering.client.nei.NEIMetalPressHandler,Immersive Engineering,ImmersiveEngineering:metalMultiblock:13,,ImmersiveEngineering,TRUE,,,65,166,5,,,,,,
|
||||
blusunrize.immersiveengineering.client.nei.NEICrusherHandler,Immersive Engineering,ImmersiveEngineering:metalMultiblock:5,,ImmersiveEngineering,TRUE,,,71,166,5,,,,,,
|
||||
@@ -410,13 +410,13 @@ cofh.thermalexpansion.plugins.nei.handlers.RecipeHandlerCharger,Thermal Expansio
|
||||
cofh.thermalexpansion.plugins.nei.handlers.RecipeHandlerCraftingMachine,Thermal Expansion,ThermalExpansion:Frame,,ThermalExpansion,TRUE,,,65,166,5,,,,,,
|
||||
cofh.thermalexpansion.plugins.nei.handlers.RecipeHandlerCraftingSecure,Thermal Expansion,ThermalExpansion:material:16,,ThermalExpansion,TRUE,,,65,166,5,,,,,,
|
||||
cofh.thermalexpansion.plugins.nei.handlers.RecipeHandlerCraftingUpgrade,Thermal Expansion,ThermalExpansion:Frame:3,,ThermalExpansion,TRUE,,,65,166,5,,,,,,
|
||||
cofh.thermalexpansion.plugins.nei.handlers.RecipeHandlerCrucible,Thermal Expansion,ThermalExpansion:Machine:4,,ThermalExpansion,TRUE,,,65,166,5,,,,,,
|
||||
cofh.thermalexpansion.plugins.nei.handlers.RecipeHandlerCrucible,Thermal Expansion,ThermalExpansion:Machine:4,,ThermalExpansion,TRUE,,,65,166,2,,,,,,
|
||||
cofh.thermalexpansion.plugins.nei.handlers.RecipeHandlerFurnace,Thermal Expansion,ThermalExpansion:Machine:0,,ThermalExpansion,TRUE,,,65,166,5,,,,,,
|
||||
cofh.thermalexpansion.plugins.nei.handlers.RecipeHandlerInsolator,Thermal Expansion,ThermalExpansion:Machine:11,,ThermalExpansion,TRUE,,,65,166,5,,,,,,
|
||||
cofh.thermalexpansion.plugins.nei.handlers.RecipeHandlerInsolator,Thermal Expansion,ThermalExpansion:Machine:11,,ThermalExpansion,TRUE,,,65,166,2,,,,,,
|
||||
cofh.thermalexpansion.plugins.nei.handlers.RecipeHandlerPulverizer,Thermal Expansion,ThermalExpansion:Machine:1,,ThermalExpansion,TRUE,,,65,166,5,,,,,,
|
||||
cofh.thermalexpansion.plugins.nei.handlers.RecipeHandlerSawmill,Thermal Expansion,ThermalExpansion:Machine:2,,ThermalExpansion,TRUE,,,65,166,5,,,,,,
|
||||
cofh.thermalexpansion.plugins.nei.handlers.RecipeHandlerSmelter,Thermal Expansion,ThermalExpansion:Machine:3,,ThermalExpansion,TRUE,,,65,166,5,,,,,,
|
||||
cofh.thermalexpansion.plugins.nei.handlers.RecipeHandlerTransposer,Thermal Expansion,ThermalExpansion:Machine:5,,ThermalExpansion,TRUE,,,65,166,5,,,,,,
|
||||
cofh.thermalexpansion.plugins.nei.handlers.RecipeHandlerTransposer,Thermal Expansion,ThermalExpansion:Machine:5,,ThermalExpansion,TRUE,,,65,166,2,,,,,,
|
||||
mekanism.client.nei.ChemicalCrystallizerRecipeHandler,Mekanism,Mekanism:MachineBlock2:8,,Mekanism,TRUE,,,79,,4,,,,,,
|
||||
mekanism.client.nei.ChemicalDissolutionChamberRecipeHandler,Mekanism,Mekanism:MachineBlock2:6,,Mekanism,TRUE,,,79,,4,,,,,,
|
||||
mekanism.client.nei.ChemicalInfuserRecipeHandler,Mekanism,Mekanism:MachineBlock2:2,,Mekanism,TRUE,,,80,,4,,,,,,
|
||||
@@ -437,7 +437,7 @@ mekanism.client.nei.ShapelessMekanismRecipeHandler,Mekanism,minecraft:crafting_t
|
||||
mekanism.client.nei.SolarNeutronRecipeHandler,Mekanism,Mekanism:MachineBlock3:1,,Mekanism,TRUE,,,70,,5,,,,,,
|
||||
mekanism.client.nei.ThermalEvaporationRecipeHandler,Mekanism,Mekanism:BasicBlock:14,,Mekanism,TRUE,,,62,,5,,,,,,
|
||||
exnihilo.compatibility.nei.RecipeHandlerHammer,Ex Nihilo,exnihilo:hammer_iron,,exnihilo,TRUE,,,58,,5,,,,,,
|
||||
exnihilo.compatibility.nei.RecipeHandlerSieve,Ex Nihilo,exnihilo:sifting_table,,exnihilo,TRUE,,,130,,2,,,,,,
|
||||
exnihilo.compatibility.nei.RecipeHandlerSieve,Ex Nihilo,exnihilo:sifting_table,,exnihilo,TRUE,,,130,,1,,,,,,
|
||||
gregtech.Alloy,GregTech-Addon,gregtech_addon:machine:56,,gregtech_addon,TRUE,,15,145,166,2,,,,,,
|
||||
gregtech.Assembler,GregTech-Addon,gregtech_addon:machine:60,,gregtech_addon,TRUE,,15,145,166,2,,,,,,
|
||||
gregtech.Bender,GregTech-Addon,gregtech_addon:machine:59,,gregtech_addon,TRUE,,15,145,166,2,,,,,,
|
||||
|
||||
|
Reference in new issue
Block a user