mirror of
https://gitlab.bixilon.de/bixilon/minosoft.git
synced 2025-09-18 03:44:54 -04:00
bump kutil
This commit is contained in:
parent
3cb35bdb3a
commit
165b875e39
8
pom.xml
8
pom.xml
@ -1,7 +1,7 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<!--
|
||||
~ Minosoft
|
||||
~ Copyright (C) 2020 Moritz Zwerger
|
||||
~ Copyright (C) 2020-2022 Moritz Zwerger
|
||||
~
|
||||
~ This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version.
|
||||
~
|
||||
@ -417,12 +417,12 @@
|
||||
<dependency>
|
||||
<groupId>org.kordamp.ikonli</groupId>
|
||||
<artifactId>ikonli-fontawesome5-pack</artifactId>
|
||||
<version>12.2.0</version>
|
||||
<version>12.3.0</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.kordamp.ikonli</groupId>
|
||||
<artifactId>ikonli-javafx</artifactId>
|
||||
<version>12.2.0</version>
|
||||
<version>12.3.0</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.apache.commons</groupId>
|
||||
@ -457,7 +457,7 @@
|
||||
<dependency>
|
||||
<groupId>de.bixilon</groupId>
|
||||
<artifactId>kutil</artifactId>
|
||||
<version>1.5.2</version>
|
||||
<version>1.6</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>io.netty</groupId>
|
||||
|
@ -1,6 +1,6 @@
|
||||
/*
|
||||
* Minosoft
|
||||
* Copyright (C) 2021 Moritz Zwerger
|
||||
* Copyright (C) 2020-2022 Moritz Zwerger
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version.
|
||||
*
|
||||
@ -14,6 +14,7 @@
|
||||
package de.bixilon.minosoft.gui.eros.main.account.add
|
||||
|
||||
import de.bixilon.kutil.concurrent.pool.DefaultThreadPool
|
||||
import de.bixilon.kutil.http.QueryUtil.fromQuery
|
||||
import de.bixilon.minosoft.config.profile.profiles.eros.ErosProfileManager
|
||||
import de.bixilon.minosoft.gui.eros.controller.JavaFXWindowController
|
||||
import de.bixilon.minosoft.gui.eros.dialog.ErosErrorReport.Companion.report
|
||||
@ -21,7 +22,6 @@ import de.bixilon.minosoft.gui.eros.main.account.AccountController
|
||||
import de.bixilon.minosoft.gui.eros.util.JavaFXUtil
|
||||
import de.bixilon.minosoft.protocol.protocol.ProtocolDefinition
|
||||
import de.bixilon.minosoft.util.KUtil.toResourceLocation
|
||||
import de.bixilon.minosoft.util.Util
|
||||
import de.bixilon.minosoft.util.account.microsoft.MicrosoftOAuthUtils
|
||||
import javafx.concurrent.Worker
|
||||
import javafx.fxml.FXML
|
||||
@ -62,7 +62,7 @@ class MicrosoftAddController(
|
||||
DefaultThreadPool += {
|
||||
try {
|
||||
// ms-xal-00000000402b5328://auth/?code=M.R3_BL2.9c86df10-b29b-480d-9094-d8accb31e4a5
|
||||
val account = MicrosoftOAuthUtils.loginToMicrosoftAccount(Util.urlQueryToMap(URL(location).query)["code"]!!)
|
||||
val account = MicrosoftOAuthUtils.loginToMicrosoftAccount(URL(location).query.fromQuery()["code"]!!)
|
||||
profile.entries[account.id] = account
|
||||
profile.selected = account
|
||||
JavaFXUtil.runLater { accountController.refreshList() }
|
||||
|
@ -13,10 +13,10 @@
|
||||
|
||||
package de.bixilon.minosoft.protocol.network.network.client.exceptions.ciritical
|
||||
|
||||
import de.bixilon.kutil.primitive.IntUtil.toHex
|
||||
import de.bixilon.minosoft.data.registries.versions.Version
|
||||
import de.bixilon.minosoft.protocol.network.network.client.exceptions.NetworkException
|
||||
import de.bixilon.minosoft.protocol.protocol.ProtocolStates
|
||||
import de.bixilon.minosoft.util.KUtil.toHex
|
||||
|
||||
class UnknownPacketIdException(
|
||||
val packetId: Int,
|
||||
|
@ -13,10 +13,10 @@
|
||||
|
||||
package de.bixilon.minosoft.protocol.network.network.client.exceptions.implementation
|
||||
|
||||
import de.bixilon.kutil.primitive.IntUtil.toHex
|
||||
import de.bixilon.minosoft.data.registries.versions.Version
|
||||
import de.bixilon.minosoft.protocol.network.network.client.exceptions.NetworkException
|
||||
import de.bixilon.minosoft.protocol.protocol.ProtocolStates
|
||||
import de.bixilon.minosoft.util.KUtil.toHex
|
||||
|
||||
class S2CPacketNotImplementedException(
|
||||
val packetId: Int,
|
||||
|
@ -13,8 +13,8 @@
|
||||
|
||||
package de.bixilon.minosoft.protocol.network.network.client.pipeline.compression
|
||||
|
||||
import de.bixilon.kutil.compression.zlib.ZlibUtil.decompress
|
||||
import de.bixilon.minosoft.protocol.protocol.InByteBuffer
|
||||
import de.bixilon.minosoft.util.KUtil.decompressZlib
|
||||
import io.netty.channel.ChannelHandlerContext
|
||||
import io.netty.handler.codec.MessageToMessageDecoder
|
||||
|
||||
@ -31,7 +31,7 @@ class PacketDeflater : MessageToMessageDecoder<ByteArray>() {
|
||||
return
|
||||
}
|
||||
|
||||
out += rest.decompressZlib()
|
||||
out += rest.decompress()
|
||||
}
|
||||
|
||||
companion object {
|
||||
|
@ -13,8 +13,8 @@
|
||||
|
||||
package de.bixilon.minosoft.protocol.network.network.client.pipeline.compression
|
||||
|
||||
import de.bixilon.kutil.compression.zlib.ZlibUtil.compress
|
||||
import de.bixilon.minosoft.protocol.protocol.OutByteBuffer
|
||||
import de.bixilon.minosoft.util.KUtil.compressZlib
|
||||
import de.bixilon.minosoft.util.KUtil.withLengthPrefix
|
||||
import io.netty.channel.ChannelHandlerContext
|
||||
import io.netty.handler.codec.MessageToMessageEncoder
|
||||
@ -35,7 +35,7 @@ class PacketInflater(
|
||||
return
|
||||
}
|
||||
|
||||
val compressed = data.compressZlib()
|
||||
val compressed = data.compress()
|
||||
|
||||
out += compressed.withLengthPrefix()
|
||||
}
|
||||
|
@ -20,6 +20,7 @@ import de.bixilon.kutil.collections.CollectionUtil.synchronizedMapOf
|
||||
import de.bixilon.kutil.collections.map.SynchronizedMap
|
||||
import de.bixilon.kutil.concurrent.pool.DefaultThreadPool
|
||||
import de.bixilon.kutil.latch.CountUpAndDownLatch
|
||||
import de.bixilon.kutil.reflection.KotlinReflection.kClass
|
||||
import de.bixilon.kutil.reflection.ReflectionUtil.realName
|
||||
import de.bixilon.kutil.string.StringUtil.toSnakeCase
|
||||
import de.bixilon.minosoft.protocol.PacketErrorHandler
|
||||
@ -30,7 +31,6 @@ import de.bixilon.minosoft.protocol.packets.factory.factories.PacketFactory
|
||||
import de.bixilon.minosoft.protocol.packets.factory.factories.ReflectionFactory
|
||||
import de.bixilon.minosoft.protocol.packets.s2c.S2CPacket
|
||||
import de.bixilon.minosoft.protocol.protocol.ProtocolStates
|
||||
import de.bixilon.minosoft.util.KUtil.kClass
|
||||
import kotlin.reflect.full.companionObjectInstance
|
||||
|
||||
object PacketTypeRegistry {
|
||||
|
@ -12,6 +12,7 @@
|
||||
*/
|
||||
package de.bixilon.minosoft.protocol.packets.s2c.play.chunk
|
||||
|
||||
import de.bixilon.kutil.compression.zlib.ZlibUtil.decompress
|
||||
import de.bixilon.kutil.json.JsonUtil.asJsonObject
|
||||
import de.bixilon.kutil.json.JsonUtil.toJsonObject
|
||||
import de.bixilon.kutil.primitive.IntUtil.toInt
|
||||
@ -37,7 +38,6 @@ import de.bixilon.minosoft.protocol.protocol.ProtocolVersions.V_20W45A
|
||||
import de.bixilon.minosoft.protocol.protocol.ProtocolVersions.V_21W03A
|
||||
import de.bixilon.minosoft.protocol.protocol.ProtocolVersions.V_21W37A
|
||||
import de.bixilon.minosoft.util.KUtil.toResourceLocation
|
||||
import de.bixilon.minosoft.util.Util
|
||||
import de.bixilon.minosoft.util.chunk.ChunkUtil
|
||||
import de.bixilon.minosoft.util.logging.Log
|
||||
import de.bixilon.minosoft.util.logging.LogLevels
|
||||
@ -68,7 +68,7 @@ class ChunkS2CP(buffer: PlayInByteBuffer) : PlayS2CPacket {
|
||||
|
||||
// decompress chunk data
|
||||
val decompressed: PlayInByteBuffer = if (buffer.versionId < V_14W28A) {
|
||||
Util.decompress(buffer.readByteArray(buffer.readInt()), buffer.connection)
|
||||
PlayInByteBuffer(buffer.readByteArray(buffer.readInt()).decompress(), buffer.connection)
|
||||
} else {
|
||||
buffer
|
||||
}
|
||||
|
@ -12,13 +12,13 @@
|
||||
*/
|
||||
package de.bixilon.minosoft.protocol.packets.s2c.play.chunk
|
||||
|
||||
import de.bixilon.kutil.compression.zlib.ZlibUtil.decompress
|
||||
import de.bixilon.minosoft.data.world.ChunkData
|
||||
import de.bixilon.minosoft.protocol.network.connection.play.PlayConnection
|
||||
import de.bixilon.minosoft.protocol.packets.factory.LoadPacket
|
||||
import de.bixilon.minosoft.protocol.packets.s2c.PlayS2CPacket
|
||||
import de.bixilon.minosoft.protocol.protocol.PlayInByteBuffer
|
||||
import de.bixilon.minosoft.protocol.protocol.ProtocolVersions
|
||||
import de.bixilon.minosoft.util.Util
|
||||
import de.bixilon.minosoft.util.chunk.ChunkUtil
|
||||
import de.bixilon.minosoft.util.logging.Log
|
||||
import de.bixilon.minosoft.util.logging.LogLevels
|
||||
@ -39,7 +39,7 @@ class ChunksS2CP : PlayS2CPacket {
|
||||
|
||||
// decompress chunk data
|
||||
val decompressed: PlayInByteBuffer = if (buffer.versionId < ProtocolVersions.V_14W28A) {
|
||||
Util.decompress(buffer.readByteArray(dataLength), buffer.connection)
|
||||
PlayInByteBuffer(buffer.readByteArray(dataLength).decompress(), buffer.connection)
|
||||
} else {
|
||||
buffer
|
||||
}
|
||||
|
@ -12,13 +12,13 @@
|
||||
*/
|
||||
package de.bixilon.minosoft.protocol.protocol
|
||||
|
||||
import de.bixilon.kutil.compression.zlib.GzipUtil.decompress
|
||||
import de.bixilon.kutil.uuid.UUIDUtil.toUUID
|
||||
import de.bixilon.minosoft.data.direction.Directions
|
||||
import de.bixilon.minosoft.data.entities.Poses
|
||||
import de.bixilon.minosoft.data.registries.ResourceLocation
|
||||
import de.bixilon.minosoft.data.tags.Tag
|
||||
import de.bixilon.minosoft.data.text.ChatComponent
|
||||
import de.bixilon.minosoft.util.Util
|
||||
import de.bixilon.minosoft.util.json.Jackson
|
||||
import de.bixilon.minosoft.util.nbt.tag.NBTTagTypes
|
||||
import glm_.vec2.Vec2
|
||||
@ -383,7 +383,7 @@ open class InByteBuffer {
|
||||
// no nbt data here...
|
||||
null
|
||||
} else {
|
||||
InByteBuffer(Util.decompressGzip(readByteArray(length))).readNBTTag(false)
|
||||
InByteBuffer(readByteArray(length).decompress()).readNBTTag(false)
|
||||
}
|
||||
}
|
||||
val type = NBTTagTypes[readUnsignedByte()]
|
||||
|
@ -40,12 +40,7 @@ import glm_.vec2.Vec2t
|
||||
import glm_.vec3.Vec3t
|
||||
import glm_.vec4.Vec4t
|
||||
import org.kamranzafar.jtar.TarHeader
|
||||
import java.io.ByteArrayOutputStream
|
||||
import java.util.*
|
||||
import java.util.zip.Deflater
|
||||
import java.util.zip.Inflater
|
||||
import kotlin.jvm.internal.Reflection
|
||||
import kotlin.reflect.KClass
|
||||
|
||||
|
||||
object KUtil {
|
||||
@ -251,51 +246,9 @@ object KUtil {
|
||||
ShutdownManager::class.java.forceInit()
|
||||
}
|
||||
|
||||
fun <T> Array<T>.index(value: T): Int? {
|
||||
val index = indexOf(value)
|
||||
if (index < 0) {
|
||||
return null
|
||||
}
|
||||
return index
|
||||
}
|
||||
|
||||
fun ByteArray.decompressZlib(): ByteArray {
|
||||
val inflater = Inflater()
|
||||
inflater.setInput(this, 0, this.size)
|
||||
val buffer = ByteArray(ProtocolDefinition.DEFAULT_BUFFER_SIZE)
|
||||
val stream = ByteArrayOutputStream(this.size)
|
||||
while (!inflater.finished()) {
|
||||
stream.write(buffer, 0, inflater.inflate(buffer))
|
||||
}
|
||||
stream.close()
|
||||
return stream.toByteArray()
|
||||
}
|
||||
|
||||
|
||||
fun ByteArray.compressZlib(): ByteArray {
|
||||
val deflater = Deflater()
|
||||
deflater.setInput(this)
|
||||
deflater.finish()
|
||||
val buffer = ByteArray(ProtocolDefinition.DEFAULT_BUFFER_SIZE)
|
||||
val stream = ByteArrayOutputStream(this.size)
|
||||
while (!deflater.finished()) {
|
||||
stream.write(buffer, 0, deflater.deflate(buffer))
|
||||
}
|
||||
stream.close()
|
||||
return stream.toByteArray()
|
||||
}
|
||||
|
||||
|
||||
fun ByteArray.withLengthPrefix(): ByteArray {
|
||||
val prefixed = OutByteBuffer()
|
||||
prefixed.writeByteArray(this)
|
||||
return prefixed.toArray()
|
||||
}
|
||||
|
||||
fun Int.toHex(): String {
|
||||
return Integer.toHexString(this)
|
||||
}
|
||||
|
||||
val <T : Any>Class<T>.kClass: KClass<T>
|
||||
get() = Reflection.createKotlinClass(this).unsafeCast()
|
||||
}
|
||||
|
@ -1,6 +1,6 @@
|
||||
/*
|
||||
* Minosoft
|
||||
* Copyright (C) 2020 Moritz Zwerger
|
||||
* Copyright (C) 2020-2022 Moritz Zwerger
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version.
|
||||
*
|
||||
@ -14,32 +14,14 @@
|
||||
package de.bixilon.minosoft.util;
|
||||
|
||||
import com.google.gson.Gson;
|
||||
import com.google.gson.JsonObject;
|
||||
import com.google.gson.JsonParser;
|
||||
import com.google.gson.stream.JsonReader;
|
||||
import de.bixilon.minosoft.protocol.network.connection.play.PlayConnection;
|
||||
import de.bixilon.minosoft.protocol.protocol.PlayInByteBuffer;
|
||||
import de.bixilon.minosoft.protocol.protocol.ProtocolDefinition;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
import java.io.*;
|
||||
import java.io.StringReader;
|
||||
import java.lang.reflect.Field;
|
||||
import java.net.URLEncoder;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
import java.util.Random;
|
||||
import java.util.zip.DataFormatException;
|
||||
import java.util.zip.Deflater;
|
||||
import java.util.zip.GZIPInputStream;
|
||||
import java.util.zip.Inflater;
|
||||
|
||||
@Deprecated(forRemoval = true)
|
||||
public final class Util {
|
||||
public static final char[] RANDOM_STRING_CHARS = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz".toCharArray();
|
||||
public static final String LINE_SEPARATOR = System.getProperty("line.separator");
|
||||
public static final Gson GSON = new Gson();
|
||||
private static final Random RANDOM = new Random();
|
||||
private static final Field JSON_READER_POS_FIELD;
|
||||
private static final Field JSON_READER_LINE_START_FIELD;
|
||||
|
||||
@ -57,77 +39,6 @@ public final class Util {
|
||||
}
|
||||
}
|
||||
|
||||
public static PlayInByteBuffer decompress(byte[] bytes, PlayConnection connection) {
|
||||
return new PlayInByteBuffer(decompress(bytes), connection);
|
||||
}
|
||||
|
||||
public static byte[] decompress(byte[] bytes) {
|
||||
Inflater inflater = new Inflater();
|
||||
inflater.setInput(bytes, 0, bytes.length);
|
||||
byte[] buffer = new byte[ProtocolDefinition.DEFAULT_BUFFER_SIZE];
|
||||
ByteArrayOutputStream stream = new ByteArrayOutputStream(bytes.length);
|
||||
try {
|
||||
while (!inflater.finished()) {
|
||||
stream.write(buffer, 0, inflater.inflate(buffer));
|
||||
}
|
||||
stream.close();
|
||||
} catch (IOException | DataFormatException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
return stream.toByteArray();
|
||||
}
|
||||
|
||||
public static byte[] compress(byte[] bytes) {
|
||||
Deflater deflater = new Deflater();
|
||||
deflater.setInput(bytes);
|
||||
deflater.finish();
|
||||
byte[] buffer = new byte[ProtocolDefinition.DEFAULT_BUFFER_SIZE];
|
||||
ByteArrayOutputStream stream = new ByteArrayOutputStream(bytes.length);
|
||||
while (!deflater.finished()) {
|
||||
stream.write(buffer, 0, deflater.deflate(buffer));
|
||||
}
|
||||
try {
|
||||
stream.close();
|
||||
} catch (IOException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
return stream.toByteArray();
|
||||
}
|
||||
|
||||
public static byte[] decompressGzip(byte[] raw) throws IOException {
|
||||
GZIPInputStream gzipInputStream = new GZIPInputStream(new ByteArrayInputStream(raw));
|
||||
ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
|
||||
|
||||
int res = 0;
|
||||
byte[] buffer = new byte[ProtocolDefinition.DEFAULT_BUFFER_SIZE];
|
||||
while (res >= 0) {
|
||||
res = gzipInputStream.read(buffer, 0, buffer.length);
|
||||
if (res > 0) {
|
||||
outputStream.write(buffer, 0, res);
|
||||
}
|
||||
}
|
||||
gzipInputStream.close();
|
||||
byte[] ret = outputStream.toByteArray();
|
||||
outputStream.close();
|
||||
return ret;
|
||||
}
|
||||
|
||||
public static String readReader(BufferedReader reader, boolean closeStream) throws IOException {
|
||||
StringBuilder stringBuilder = new StringBuilder();
|
||||
String line;
|
||||
while ((line = reader.readLine()) != null) {
|
||||
stringBuilder.append(line);
|
||||
stringBuilder.append(LINE_SEPARATOR);
|
||||
}
|
||||
if (stringBuilder.length() > 0) {
|
||||
stringBuilder.deleteCharAt(stringBuilder.length() - 1);
|
||||
}
|
||||
if (closeStream) {
|
||||
reader.close();
|
||||
}
|
||||
return stringBuilder.toString();
|
||||
}
|
||||
|
||||
public static int getJsonReaderPosition(JsonReader jsonReader) {
|
||||
try {
|
||||
return JSON_READER_POS_FIELD.getInt(jsonReader) - JSON_READER_LINE_START_FIELD.getInt(jsonReader) + 1;
|
||||
@ -135,41 +46,4 @@ public final class Util {
|
||||
throw new IllegalStateException();
|
||||
}
|
||||
}
|
||||
|
||||
public static Map<String, String> urlQueryToMap(String query) {
|
||||
Map<String, String> map = new HashMap<>();
|
||||
for (String parameter : query.split("&")) {
|
||||
String[] split = parameter.split("=");
|
||||
map.put(split[0], split[1]);
|
||||
}
|
||||
return map;
|
||||
}
|
||||
|
||||
public static String mapToUrlQuery(Map<String, Object> data) {
|
||||
StringBuilder builder = new StringBuilder();
|
||||
for (Map.Entry<String, Object> entry : data.entrySet()) {
|
||||
if (builder.length() != 0) {
|
||||
builder.append("&");
|
||||
}
|
||||
try {
|
||||
builder.append(URLEncoder.encode(entry.getKey(), StandardCharsets.UTF_8.name()));
|
||||
builder.append("=");
|
||||
builder.append(URLEncoder.encode(entry.getValue().toString(), StandardCharsets.UTF_8.name()));
|
||||
} catch (UnsupportedEncodingException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
return builder.toString();
|
||||
}
|
||||
|
||||
@NotNull
|
||||
@Deprecated
|
||||
public static JsonObject readJsonFromStream(@NotNull InputStream stream, boolean close) throws IOException {
|
||||
InputStreamReader reader = new InputStreamReader(stream);
|
||||
JsonObject json = JsonParser.parseReader(reader).getAsJsonObject();
|
||||
if (close) {
|
||||
reader.close();
|
||||
}
|
||||
return json;
|
||||
}
|
||||
}
|
||||
|
@ -1,6 +1,6 @@
|
||||
/*
|
||||
* Minosoft
|
||||
* Copyright (C) 2021 Moritz Zwerger
|
||||
* Copyright (C) 2020-2022 Moritz Zwerger
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version.
|
||||
*
|
||||
@ -14,8 +14,8 @@
|
||||
package de.bixilon.minosoft.util.http
|
||||
|
||||
import de.bixilon.kutil.collections.CollectionUtil.extend
|
||||
import de.bixilon.kutil.http.QueryUtil.toQuery
|
||||
import de.bixilon.kutil.primitive.BooleanUtil.decide
|
||||
import de.bixilon.minosoft.util.Util
|
||||
import de.bixilon.minosoft.util.json.Jackson
|
||||
import java.net.URI
|
||||
import java.net.http.HttpClient
|
||||
@ -64,7 +64,7 @@ object HTTP2 {
|
||||
return post(
|
||||
url = url,
|
||||
data = this,
|
||||
bodyPublisher = { Util.mapToUrlQuery(this) },
|
||||
bodyPublisher = { this.toQuery() },
|
||||
bodyBuilder = { it.isBlank().decide(null) { Jackson.MAPPER.readValue(it, Jackson.JSON_MAP_TYPE) as Map<String, Any> } },
|
||||
headers = headers.extend(
|
||||
"Content-Type" to "application/x-www-form-urlencoded",
|
||||
|
Loading…
x
Reference in New Issue
Block a user