API changes for getting traces and upgrades
This commit is contained in:
@@ -10,7 +10,6 @@ import io.anuke.mindustry.entities.Player;
|
||||
import io.anuke.mindustry.game.EventType.*;
|
||||
import io.anuke.mindustry.game.Team;
|
||||
import io.anuke.mindustry.game.Tutorial;
|
||||
import io.anuke.mindustry.game.UpgradeInventory;
|
||||
import io.anuke.mindustry.input.AndroidInput;
|
||||
import io.anuke.mindustry.input.DefaultKeybinds;
|
||||
import io.anuke.mindustry.input.DesktopInput;
|
||||
@@ -37,7 +36,6 @@ import static io.anuke.mindustry.Vars.*;
|
||||
* Should <i>not</i> handle any game-critical state.
|
||||
* This class is not created in the headless server.*/
|
||||
public class Control extends Module{
|
||||
private UpgradeInventory upgrades = new UpgradeInventory();
|
||||
private Tutorial tutorial = new Tutorial();
|
||||
private boolean hiscore = false;
|
||||
|
||||
@@ -144,10 +142,10 @@ public class Control extends Module{
|
||||
});
|
||||
|
||||
Events.on(ResetEvent.class, () -> {
|
||||
upgrades.reset();
|
||||
player.weapon = Weapons.blaster;
|
||||
player.team = Team.blue;
|
||||
player.inventory.clear();
|
||||
player.upgrades.clear();
|
||||
|
||||
player.add();
|
||||
player.heal();
|
||||
@@ -194,10 +192,6 @@ public class Control extends Module{
|
||||
this.error = error;
|
||||
}
|
||||
|
||||
public UpgradeInventory upgrades() {
|
||||
return upgrades;
|
||||
}
|
||||
|
||||
public Saves getSaves(){
|
||||
return saves;
|
||||
}
|
||||
|
||||
@@ -268,7 +268,7 @@ public class NetClient extends Module {
|
||||
Weapon weapon = Upgrade.getByID(packet.id);
|
||||
|
||||
state.inventory.removeItems(UpgradeRecipes.get(weapon));
|
||||
control.upgrades().addWeapon(weapon);
|
||||
player.upgrades.add(weapon);
|
||||
Effects.sound("purchase");
|
||||
});
|
||||
}
|
||||
|
||||
@@ -1,6 +1,8 @@
|
||||
package io.anuke.mindustry.core;
|
||||
|
||||
import com.badlogic.gdx.utils.*;
|
||||
import com.badlogic.gdx.utils.Base64Coder;
|
||||
import com.badlogic.gdx.utils.IntMap;
|
||||
import com.badlogic.gdx.utils.TimeUtils;
|
||||
import io.anuke.mindustry.content.Mechs;
|
||||
import io.anuke.mindustry.content.Recipes;
|
||||
import io.anuke.mindustry.content.UpgradeRecipes;
|
||||
@@ -8,7 +10,6 @@ import io.anuke.mindustry.core.GameState.State;
|
||||
import io.anuke.mindustry.entities.BulletType;
|
||||
import io.anuke.mindustry.entities.Player;
|
||||
import io.anuke.mindustry.entities.SyncEntity;
|
||||
import io.anuke.mindustry.game.EventType.GameOverEvent;
|
||||
import io.anuke.mindustry.io.Platform;
|
||||
import io.anuke.mindustry.io.Version;
|
||||
import io.anuke.mindustry.net.*;
|
||||
@@ -20,7 +21,6 @@ import io.anuke.mindustry.resource.Upgrade;
|
||||
import io.anuke.mindustry.resource.Weapon;
|
||||
import io.anuke.mindustry.world.Block;
|
||||
import io.anuke.mindustry.world.Placement;
|
||||
import io.anuke.ucore.core.Events;
|
||||
import io.anuke.ucore.core.Timers;
|
||||
import io.anuke.ucore.entities.BaseBulletType;
|
||||
import io.anuke.ucore.entities.Entities;
|
||||
@@ -45,15 +45,12 @@ public class NetServer extends Module{
|
||||
|
||||
/**Maps connection IDs to players.*/
|
||||
private IntMap<Player> connections = new IntMap<>();
|
||||
private ObjectMap<String, ByteArray> weapons = new ObjectMap<>();
|
||||
private boolean closing = false;
|
||||
private Timer timer = new Timer(5);
|
||||
private ByteBuffer writeBuffer = ByteBuffer.allocate(32);
|
||||
|
||||
public NetServer(){
|
||||
|
||||
Events.on(GameOverEvent.class, () -> weapons.clear());
|
||||
|
||||
Net.handleServer(Connect.class, (id, connect) -> {
|
||||
if(admins.isIPBanned(connect.addressTCP)){
|
||||
kick(id, KickReason.banned);
|
||||
@@ -66,7 +63,7 @@ public class NetServer extends Module{
|
||||
if(Net.getConnection(id) == null ||
|
||||
admins.isIPBanned(Net.getConnection(id).address)) return;
|
||||
|
||||
TraceInfo trace = admins.getTrace(Net.getConnection(id).address);
|
||||
TraceInfo trace = admins.getTraceByID(uuid);
|
||||
PlayerInfo info = admins.getInfo(uuid);
|
||||
trace.uuid = uuid;
|
||||
trace.android = packet.android;
|
||||
@@ -81,6 +78,13 @@ public class NetServer extends Module{
|
||||
return;
|
||||
}
|
||||
|
||||
for(Player player : playerGroup.all()){
|
||||
if(player.name.equalsIgnoreCase(packet.name)){
|
||||
kick(id, KickReason.nameInUse);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
Log.info("Recieved connect packet for player '{0}' / UUID {1} / IP {2}", packet.name, uuid, trace.ip);
|
||||
|
||||
String ip = Net.getConnection(id).address;
|
||||
@@ -100,6 +104,7 @@ public class NetServer extends Module{
|
||||
player.isAdmin = admins.isAdmin(uuid, ip);
|
||||
player.clientid = id;
|
||||
player.name = packet.name;
|
||||
player.uuid = uuid;
|
||||
player.mech = packet.android ? Mechs.standardShip : Mechs.standard;
|
||||
player.set(world.getSpawnX(), world.getSpawnY());
|
||||
player.setNet(player.x, player.y);
|
||||
@@ -111,7 +116,7 @@ public class NetServer extends Module{
|
||||
|
||||
//TODO try DeflaterOutputStream
|
||||
ByteArrayOutputStream stream = new ByteArrayOutputStream();
|
||||
NetworkIO.writeWorld(player, weapons.get(admins.getTrace(Net.getConnection(id).address).uuid, new ByteArray()), stream);
|
||||
NetworkIO.writeWorld(player, stream);
|
||||
WorldData data = new WorldData();
|
||||
data.stream = new ByteArrayInputStream(stream.toByteArray());
|
||||
Net.sendStream(id, data);
|
||||
@@ -160,10 +165,14 @@ public class NetServer extends Module{
|
||||
Player player = connections.get(id);
|
||||
|
||||
BulletType type = BaseBulletType.getByID(packet.bulletid);
|
||||
Weapon weapon = Upgrade.getByID((byte)packet.data);
|
||||
|
||||
if(!player.upgrades.contains(weapon, true)){
|
||||
return;
|
||||
}
|
||||
|
||||
player.onRemoteShoot(type, packet.x, packet.y, packet.rotation, packet.data);
|
||||
TraceInfo info = admins.getTrace(Net.getConnection(id).address);
|
||||
Weapon weapon = Upgrade.getByID((byte)packet.data);
|
||||
TraceInfo info = admins.getTraceByID(getUUID(id));
|
||||
|
||||
float wtrc = 80;
|
||||
|
||||
@@ -197,9 +206,11 @@ public class NetServer extends Module{
|
||||
|
||||
Placement.placeBlock(placer.team, packet.x, packet.y, block, packet.rotation, true, false);
|
||||
|
||||
admins.getTrace(Net.getConnection(id).address).lastBlockPlaced = block;
|
||||
admins.getTrace(Net.getConnection(id).address).totalBlocksPlaced ++;
|
||||
admins.getInfo(admins.getTrace(Net.getConnection(id).address).uuid).totalBlockPlaced ++;
|
||||
TraceInfo trace = admins.getTraceByID(getUUID(id));
|
||||
|
||||
trace.lastBlockPlaced = block;
|
||||
trace.totalBlocksPlaced ++;
|
||||
admins.getInfo(getUUID(id)).totalBlockPlaced ++;
|
||||
|
||||
Net.send(packet, SendMode.tcp);
|
||||
});
|
||||
@@ -213,11 +224,13 @@ public class NetServer extends Module{
|
||||
Block block = Placement.breakBlock(placer.team, packet.x, packet.y, true, false);
|
||||
|
||||
if(block != null) {
|
||||
admins.getTrace(Net.getConnection(id).address).lastBlockBroken = block;
|
||||
admins.getTrace(Net.getConnection(id).address).totalBlocksBroken++;
|
||||
admins.getInfo(admins.getTrace(Net.getConnection(id).address).uuid).totalBlocksBroken ++;
|
||||
TraceInfo trace = admins.getTraceByID(getUUID(id));
|
||||
|
||||
trace.lastBlockBroken = block;
|
||||
trace.totalBlocksBroken++;
|
||||
admins.getInfo(getUUID(id)).totalBlocksBroken ++;
|
||||
if (block.update || block.destructible)
|
||||
admins.getTrace(Net.getConnection(id).address).structureBlocksBroken++;
|
||||
trace.structureBlocksBroken++;
|
||||
}
|
||||
|
||||
Net.send(packet, SendMode.tcp);
|
||||
@@ -240,16 +253,13 @@ public class NetServer extends Module{
|
||||
Player player = connections.get(id);
|
||||
|
||||
Weapon weapon = Upgrade.getByID(packet.id);
|
||||
String uuid = admins.getTrace(Net.getConnection(id).address).uuid;
|
||||
|
||||
if(!state.inventory.hasItems(UpgradeRecipes.get(weapon))){
|
||||
return;
|
||||
}
|
||||
|
||||
if (!weapons.containsKey(uuid)) weapons.put(uuid, new ByteArray());
|
||||
|
||||
if (!weapons.get(uuid).contains(weapon.id)){
|
||||
weapons.get(uuid).add(weapon.id);
|
||||
if (!player.upgrades.contains(weapon, true)){
|
||||
player.upgrades.add(weapon);
|
||||
}else{
|
||||
return;
|
||||
}
|
||||
@@ -259,8 +269,6 @@ public class NetServer extends Module{
|
||||
});
|
||||
|
||||
Net.handleServer(WeaponSwitchPacket.class, (id, packet) -> {
|
||||
TraceInfo info = admins.getTrace(Net.getConnection(id).address);
|
||||
|
||||
packet.playerid = connections.get(id).id;
|
||||
Net.sendExcept(id, packet, SendMode.tcp);
|
||||
});
|
||||
@@ -318,7 +326,7 @@ public class NetServer extends Module{
|
||||
Log.info("&lc{0} has kicked {1}.", player.name, other.name);
|
||||
}else if(packet.action == AdminAction.trace){
|
||||
TracePacket trace = new TracePacket();
|
||||
trace.info = admins.getTrace(ip);
|
||||
trace.info = admins.getTraceByID(getUUID(id));
|
||||
Net.sendTo(id, trace, SendMode.tcp);
|
||||
Log.info("&lc{0} has requested trace info of {1}.", player.name, other.name);
|
||||
}
|
||||
@@ -343,7 +351,6 @@ public class NetServer extends Module{
|
||||
}
|
||||
|
||||
public void reset(){
|
||||
weapons.clear();
|
||||
admins.clearTraces();
|
||||
}
|
||||
|
||||
@@ -356,8 +363,8 @@ public class NetServer extends Module{
|
||||
Log.info("Kicking connection #{0} / IP: {1}. Reason: {2}", connection, con.address, reason);
|
||||
}
|
||||
|
||||
if((reason == KickReason.kick || reason == KickReason.banned) && admins.getTrace(con.address).uuid != null){
|
||||
PlayerInfo info = admins.getInfo(admins.getTrace(con.address).uuid);
|
||||
if((reason == KickReason.kick || reason == KickReason.banned) && admins.getTraceByID(getUUID(con.id)).uuid != null){
|
||||
PlayerInfo info = admins.getInfo(admins.getTraceByID(getUUID(con.id)).uuid);
|
||||
info.timesKicked ++;
|
||||
info.lastKicked = TimeUtils.millis();
|
||||
}
|
||||
@@ -371,6 +378,10 @@ public class NetServer extends Module{
|
||||
admins.save();
|
||||
}
|
||||
|
||||
String getUUID(int connectionID){
|
||||
return connections.get(connectionID).uuid;
|
||||
}
|
||||
|
||||
void sync(){
|
||||
|
||||
if(timer.get(timerEntitySync, serverSyncTime)){
|
||||
|
||||
@@ -2,6 +2,7 @@ package io.anuke.mindustry.entities;
|
||||
|
||||
import com.badlogic.gdx.graphics.Color;
|
||||
import com.badlogic.gdx.math.Vector2;
|
||||
import com.badlogic.gdx.utils.Array;
|
||||
import io.anuke.mindustry.content.Mechs;
|
||||
import io.anuke.mindustry.content.Weapons;
|
||||
import io.anuke.mindustry.content.fx.ExplosionFx;
|
||||
@@ -37,15 +38,16 @@ public class Player extends Unit{
|
||||
static final int timerRegen = 3;
|
||||
|
||||
public String name = "name";
|
||||
public String uuid;
|
||||
public boolean isAdmin;
|
||||
public Color color = new Color();
|
||||
|
||||
public Array<Upgrade> upgrades = new Array<>();
|
||||
public Weapon weapon = Weapons.blaster;
|
||||
public Mech mech = Mechs.standard;
|
||||
|
||||
public float targetAngle = 0f;
|
||||
public boolean dashing = false;
|
||||
public boolean selectingItem;
|
||||
|
||||
public int clientid = -1;
|
||||
public boolean isLocal = false;
|
||||
@@ -53,7 +55,6 @@ public class Player extends Unit{
|
||||
public float walktime;
|
||||
|
||||
private Vector2 movement = new Vector2();
|
||||
private Translator tr = new Translator();
|
||||
|
||||
public Player(){
|
||||
hitbox.setSize(5);
|
||||
@@ -161,8 +162,10 @@ public class Player extends Unit{
|
||||
}
|
||||
|
||||
for (int i : Mathf.signs) {
|
||||
tr.trns(baseRotation, ft * i);
|
||||
Draw.rect(mname + "-leg", x + tr.x, y + tr.y, 12f * i, 12f - Mathf.clamp(ft * i, 0, 2), baseRotation - 90);
|
||||
Draw.rect(mname + "-leg",
|
||||
x + Angles.trnsx(baseRotation, ft * i),
|
||||
y + Angles.trnsy(baseRotation, ft * i),
|
||||
12f * i, 12f - Mathf.clamp(ft * i, 0, 2), baseRotation - 90);
|
||||
}
|
||||
|
||||
Draw.rect(mname + "-base", x, y,baseRotation- 90);
|
||||
@@ -177,9 +180,12 @@ public class Player extends Unit{
|
||||
Draw.rect(mname, x, y, rotation -90);
|
||||
|
||||
for (int i : Mathf.signs) {
|
||||
tr.trns(rotation - 90, 4*i, 3 - weapon.getRecoil(this, i > 0)*1.5f);
|
||||
float tra = rotation - 90,
|
||||
trX = 4*i, trY = 3 - weapon.getRecoil(this, i > 0)*1.5f;
|
||||
float w = i > 0 ? -8 : 8;
|
||||
Draw.rect(weapon.name + "-equip", x + tr.x, y + tr.y, w, 8, rotation - 90);
|
||||
Draw.rect(weapon.name + "-equip",
|
||||
x + Angles.trnsx(tra, trX, trY),
|
||||
y + Angles.trnsy(tra, trX, trY), w, 8, rotation - 90);
|
||||
}
|
||||
|
||||
float backTrns = 4f, itemSize = 5f;
|
||||
|
||||
@@ -5,6 +5,7 @@ import com.badlogic.gdx.math.Vector2;
|
||||
import io.anuke.mindustry.core.GameState.State;
|
||||
import io.anuke.mindustry.net.Net;
|
||||
import io.anuke.mindustry.net.NetEvents;
|
||||
import io.anuke.mindustry.resource.Weapon;
|
||||
import io.anuke.mindustry.world.Tile;
|
||||
import io.anuke.ucore.core.Graphics;
|
||||
import io.anuke.ucore.core.Inputs;
|
||||
@@ -103,12 +104,20 @@ public class DesktopInput extends InputHandler{
|
||||
}else{
|
||||
breakMode = PlaceMode.hold;
|
||||
}
|
||||
|
||||
int keyIndex = 1;
|
||||
|
||||
for(int i = 1; i <= 6 && i <= control.upgrades().getWeapons().size; i ++){
|
||||
if(Inputs.keyTap("weapon_" + i)){
|
||||
player.weapon = control.upgrades().getWeapons().get(i - 1);
|
||||
for(int i = 0; i < 6 && i < player.upgrades.size; i ++){
|
||||
if(!(player.upgrades.get(i) instanceof Weapon)){
|
||||
continue;
|
||||
}
|
||||
|
||||
if(Inputs.keyTap("weapon_" + keyIndex)){
|
||||
player.weapon = (Weapon) player.upgrades.get(i);
|
||||
if(Net.active()) NetEvents.handleWeaponSwitch();
|
||||
}
|
||||
|
||||
keyIndex ++;
|
||||
}
|
||||
|
||||
Tile cursor = world.tile(tilex(), tiley());
|
||||
|
||||
@@ -31,10 +31,10 @@ public class Administration {
|
||||
}
|
||||
|
||||
/**Returns trace info by IP.*/
|
||||
public TraceInfo getTrace(String ip){
|
||||
if(!traceInfo.containsKey(ip)) traceInfo.put(ip, new TraceInfo(ip));
|
||||
public TraceInfo getTraceByID(String uuid){
|
||||
if(!traceInfo.containsKey(uuid)) traceInfo.put(uuid, new TraceInfo(uuid));
|
||||
|
||||
return traceInfo.get(ip);
|
||||
return traceInfo.get(uuid);
|
||||
}
|
||||
|
||||
public void clearTraces(){
|
||||
|
||||
@@ -133,7 +133,7 @@ public class NetEvents {
|
||||
if(Net.client()) {
|
||||
handleAdministerRequest(target, AdminAction.trace);
|
||||
}else{
|
||||
ui.traces.show(target, netServer.admins.getTrace(Net.getConnection(target.clientid).address));
|
||||
ui.traces.show(target, netServer.admins.getTraceByID(target.uuid));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,15 +1,14 @@
|
||||
package io.anuke.mindustry.net;
|
||||
|
||||
import com.badlogic.gdx.utils.ByteArray;
|
||||
import com.badlogic.gdx.utils.TimeUtils;
|
||||
import io.anuke.mindustry.content.Weapons;
|
||||
import io.anuke.mindustry.content.blocks.Blocks;
|
||||
import io.anuke.mindustry.entities.Player;
|
||||
import io.anuke.mindustry.game.GameMode;
|
||||
import io.anuke.mindustry.game.Team;
|
||||
import io.anuke.mindustry.io.Version;
|
||||
import io.anuke.mindustry.resource.Upgrade;
|
||||
import io.anuke.mindustry.world.Tile;
|
||||
import io.anuke.mindustry.content.blocks.Blocks;
|
||||
import io.anuke.mindustry.world.blocks.types.BlockPart;
|
||||
import io.anuke.ucore.core.Timers;
|
||||
import io.anuke.ucore.entities.Entities;
|
||||
@@ -22,7 +21,7 @@ import static io.anuke.mindustry.Vars.*;
|
||||
|
||||
public class NetworkIO {
|
||||
|
||||
public static void writeWorld(Player player, ByteArray upgrades, OutputStream os){
|
||||
public static void writeWorld(Player player, OutputStream os){
|
||||
|
||||
try(DataOutputStream stream = new DataOutputStream(os)){
|
||||
|
||||
@@ -41,10 +40,9 @@ public class NetworkIO {
|
||||
stream.writeInt(player.id); //player remap ID
|
||||
stream.writeBoolean(player.isAdmin);
|
||||
|
||||
stream.writeByte(upgrades.size); //upgrade data
|
||||
|
||||
for(int i = 0; i < upgrades.size; i ++){
|
||||
stream.writeByte(upgrades.get(i));
|
||||
stream.writeByte(player.upgrades.size);
|
||||
for(Upgrade u : player.upgrades){
|
||||
stream.writeByte(u.id);
|
||||
}
|
||||
|
||||
//--MAP DATA--
|
||||
@@ -114,16 +112,13 @@ public class NetworkIO {
|
||||
int pid = stream.readInt();
|
||||
boolean admin = stream.readBoolean();
|
||||
|
||||
control.upgrades().getWeapons().clear();
|
||||
control.upgrades().getWeapons().add(Weapons.blaster);
|
||||
|
||||
byte weapons = stream.readByte();
|
||||
|
||||
for(int i = 0; i < weapons; i ++){
|
||||
control.upgrades().getWeapons().add(Upgrade.getByID(stream.readByte()));
|
||||
player.upgrades.add(Upgrade.getByID(stream.readByte()));
|
||||
}
|
||||
|
||||
player.weapon = control.upgrades().getWeapons().peek();
|
||||
player.weapon = Weapons.blaster;
|
||||
|
||||
Entities.clear();
|
||||
player.id = pid;
|
||||
|
||||
@@ -350,7 +350,7 @@ public class Packets {
|
||||
}
|
||||
|
||||
public enum KickReason{
|
||||
kick, invalidPassword, clientOutdated, serverOutdated, banned, gameover(true), recentKick;
|
||||
kick, invalidPassword, clientOutdated, serverOutdated, banned, gameover(true), recentKick, nameInUse, idInUse;
|
||||
public final boolean quiet;
|
||||
|
||||
KickReason(){ quiet = false; }
|
||||
|
||||
@@ -148,7 +148,7 @@ public class PlayerListFragment implements Fragment{
|
||||
t.addImageButton("icon-admin", "toggle", 14*2, () -> {
|
||||
if(Net.client()) return;
|
||||
|
||||
String id = netServer.admins.getTrace(connection.address).uuid;
|
||||
String id = netServer.admins.getTraceByID(player.uuid).uuid;
|
||||
|
||||
if(netServer.admins.isAdmin(id, connection.address)){
|
||||
ui.showConfirm("$text.confirm", "$text.confirmunadmin", () -> {
|
||||
|
||||
@@ -2,26 +2,13 @@ package io.anuke.mindustry.world.blocks.types.production;
|
||||
|
||||
import com.badlogic.gdx.graphics.Color;
|
||||
import io.anuke.mindustry.Vars;
|
||||
import io.anuke.mindustry.net.Net;
|
||||
import io.anuke.mindustry.net.NetEvents;
|
||||
import io.anuke.mindustry.resource.ItemStack;
|
||||
import io.anuke.mindustry.resource.Upgrade;
|
||||
import io.anuke.mindustry.content.UpgradeRecipes;
|
||||
import io.anuke.mindustry.resource.Weapon;
|
||||
import io.anuke.mindustry.world.Block;
|
||||
import io.anuke.mindustry.world.Tile;
|
||||
import io.anuke.ucore.core.Effects;
|
||||
import io.anuke.ucore.core.Timers;
|
||||
import io.anuke.ucore.function.Listenable;
|
||||
import io.anuke.ucore.graphics.Draw;
|
||||
import io.anuke.ucore.scene.style.TextureRegionDrawable;
|
||||
import io.anuke.ucore.scene.ui.ImageButton;
|
||||
import io.anuke.ucore.scene.ui.Tooltip;
|
||||
import io.anuke.ucore.scene.ui.layout.Table;
|
||||
import io.anuke.ucore.util.Mathf;
|
||||
|
||||
import static io.anuke.mindustry.Vars.*;
|
||||
|
||||
public class WeaponFactory extends Block{
|
||||
|
||||
public WeaponFactory(String name){
|
||||
@@ -57,6 +44,7 @@ public class WeaponFactory extends Block{
|
||||
|
||||
Table content = new Table();
|
||||
|
||||
/*
|
||||
for(Upgrade upgrade : Upgrade.getAllUpgrades()){
|
||||
if(!(upgrade instanceof Weapon)) continue;
|
||||
Weapon weapon = (Weapon)upgrade;
|
||||
@@ -126,7 +114,7 @@ public class WeaponFactory extends Block{
|
||||
if(++i % 3 == 0){
|
||||
content.row();
|
||||
}
|
||||
}
|
||||
}*/
|
||||
|
||||
table.add(content).padTop(140f);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user