mirror of
https://github.com/Rogiel/l2jserver2
synced 2025-12-10 09:22:49 +00:00
@@ -27,7 +27,11 @@ import com.l2jserver.game.net.codec.Lineage2Encrypter;
|
||||
import com.l2jserver.game.net.codec.Lineage2PacketReader;
|
||||
import com.l2jserver.game.net.codec.Lineage2PacketWriter;
|
||||
import com.l2jserver.game.net.packet.ServerPacket;
|
||||
import com.l2jserver.game.net.packet.server.ActionFailedPacket;
|
||||
import com.l2jserver.game.net.packet.server.SystemMessagePacket;
|
||||
import com.l2jserver.model.id.object.CharacterID;
|
||||
import com.l2jserver.model.template.ItemTemplate;
|
||||
import com.l2jserver.model.world.Item;
|
||||
import com.l2jserver.model.world.L2Character;
|
||||
import com.l2jserver.service.game.world.WorldService;
|
||||
import com.l2jserver.service.game.world.filter.impl.CharacterBroadcastFilter;
|
||||
@@ -242,6 +246,71 @@ public class Lineage2Connection {
|
||||
return channel.write(packet);
|
||||
}
|
||||
|
||||
/**
|
||||
* Sends a string message to this client
|
||||
*
|
||||
* @param message
|
||||
* the message
|
||||
* @return the {@link ChannelFuture} that will be notified once the packet
|
||||
* has been written.
|
||||
*/
|
||||
public ChannelFuture sendMessage(String message) {
|
||||
return write(new SystemMessagePacket(SystemMessage.S1)
|
||||
.addString(message));
|
||||
}
|
||||
|
||||
/**
|
||||
* Sends a {@link SystemMessage} to this client
|
||||
*
|
||||
* @param message
|
||||
* the {@link SystemMessage}
|
||||
* @return the {@link ChannelFuture} that will be notified once the packet
|
||||
* has been written.
|
||||
*/
|
||||
public ChannelFuture sendSystemMessage(SystemMessage message) {
|
||||
return write(message.packet);
|
||||
}
|
||||
|
||||
/**
|
||||
* Sends a {@link SystemMessage} to this client
|
||||
*
|
||||
* @param message
|
||||
* the {@link SystemMessage}
|
||||
* @param args
|
||||
* the arguments of the message, they will be automatically
|
||||
* detected and inserted. See {@link SystemMessagePacket} for
|
||||
* more about supported formats.
|
||||
* @return the {@link ChannelFuture} that will be notified once the packet
|
||||
* has been written.
|
||||
* @see SystemMessagePacket
|
||||
*/
|
||||
public ChannelFuture sendSystemMessage(SystemMessage message,
|
||||
Object... args) {
|
||||
final SystemMessagePacket packet = new SystemMessagePacket(message);
|
||||
for (final Object obj : args) {
|
||||
if (obj instanceof String)
|
||||
packet.addString((String) obj);
|
||||
else if (obj instanceof ItemTemplate)
|
||||
packet.addItem((ItemTemplate) obj);
|
||||
else if (obj instanceof Item)
|
||||
packet.addItem((Item) obj);
|
||||
}
|
||||
return write(message.packet);
|
||||
}
|
||||
|
||||
/**
|
||||
* Sends a {@link ActionFailedPacket} to the client.
|
||||
* <p>
|
||||
* This is an convenience method for <blockquote><code>
|
||||
* conn.write(ActionFailedPacket.SHARED_INSTANCE);</code></blockquote>
|
||||
*
|
||||
* @return the {@link ChannelFuture} that will be notified once the packet
|
||||
* has been written.
|
||||
*/
|
||||
public ChannelFuture sendActionFailed() {
|
||||
return write(ActionFailedPacket.SHARED_INSTANCE);
|
||||
}
|
||||
|
||||
/**
|
||||
* Broadcast a packet to all characters in this character knownlist.
|
||||
* <p>
|
||||
@@ -261,7 +330,7 @@ public class Lineage2Connection {
|
||||
.iterable(new CharacterBroadcastFilter(characterID.getObject()))) {
|
||||
final Lineage2Connection conn = networkService.discover(character
|
||||
.getID());
|
||||
if(conn == null)
|
||||
if (conn == null)
|
||||
continue;
|
||||
futures.add(conn.write(packet));
|
||||
}
|
||||
|
||||
15216
src/main/java/com/l2jserver/game/net/SystemMessage.java
Normal file
15216
src/main/java/com/l2jserver/game/net/SystemMessage.java
Normal file
File diff suppressed because it is too large
Load Diff
@@ -42,9 +42,6 @@ public class CharacterActionPacket extends AbstractClientPacket {
|
||||
*/
|
||||
public static final int OPCODE = 0x1f;
|
||||
|
||||
private final Logger log = LoggerFactory.getLogger(this.getClass());
|
||||
|
||||
private final WorldService worldService;
|
||||
private final ObjectIDResolver idResolver;
|
||||
|
||||
private int objectId;
|
||||
@@ -73,9 +70,7 @@ public class CharacterActionPacket extends AbstractClientPacket {
|
||||
}
|
||||
|
||||
@Inject
|
||||
public CharacterActionPacket(WorldService worldService,
|
||||
ObjectIDResolver idResolver) {
|
||||
this.worldService = worldService;
|
||||
public CharacterActionPacket(ObjectIDResolver idResolver) {
|
||||
this.idResolver = idResolver;
|
||||
}
|
||||
|
||||
@@ -90,9 +85,13 @@ public class CharacterActionPacket extends AbstractClientPacket {
|
||||
@Override
|
||||
public void process(final Lineage2Connection conn) {
|
||||
// since this is an erasure type, this is safe.
|
||||
System.out.println(objectId);
|
||||
final ObjectID<NPC> id = idResolver.resolve(objectId);
|
||||
if (!(id instanceof NPCID))
|
||||
if (!(id instanceof NPCID)) {
|
||||
System.out.println("Incorrect type: " + id);
|
||||
conn.sendActionFailed();
|
||||
return;
|
||||
}
|
||||
final NPC npc = id.getObject();
|
||||
npc.action(conn.getCharacter(), action);
|
||||
}
|
||||
|
||||
@@ -21,9 +21,14 @@ import org.jboss.netty.buffer.ChannelBuffer;
|
||||
import com.google.inject.Inject;
|
||||
import com.l2jserver.game.net.Lineage2Connection;
|
||||
import com.l2jserver.game.net.packet.AbstractClientPacket;
|
||||
import com.l2jserver.game.net.packet.server.ActionFailedPacket;
|
||||
import com.l2jserver.service.game.chat.ChatMessageDestination;
|
||||
import com.l2jserver.service.game.chat.ChatService;
|
||||
import com.l2jserver.service.game.chat.channel.PublicChatChannel;
|
||||
import com.l2jserver.service.game.chat.ChatService.CannotChatToSelfChatServiceException;
|
||||
import com.l2jserver.service.game.chat.ChatService.ChatBanActiveChatServiceException;
|
||||
import com.l2jserver.service.game.chat.ChatService.TargetNotFoundChatServiceException;
|
||||
import com.l2jserver.util.BufferUtils;
|
||||
import com.l2jserver.util.exception.L2ChatServiceException;
|
||||
|
||||
/**
|
||||
* Completes the creation of an character. Creates the object, inserts into the
|
||||
@@ -44,54 +49,7 @@ public class CharacterChatMessagePacket extends AbstractClientPacket {
|
||||
private final ChatService chatService;
|
||||
|
||||
private String message;
|
||||
private MessageDestination destination;
|
||||
|
||||
public enum MessageDestination {
|
||||
/**
|
||||
* Everyone
|
||||
*/
|
||||
ALL(0),
|
||||
/**
|
||||
* !
|
||||
*/
|
||||
SHOUT(1),
|
||||
/**
|
||||
* Private message
|
||||
*/
|
||||
TELL(2),
|
||||
/**
|
||||
* #
|
||||
*/
|
||||
PARTY(3),
|
||||
/**
|
||||
* @
|
||||
*/
|
||||
CLAN(4), GM(5), PETITION_PLAYER(6), PETITION_GM(7),
|
||||
/**
|
||||
* +
|
||||
*/
|
||||
TRADE(8),
|
||||
/**
|
||||
* $
|
||||
*/
|
||||
ALLIANCE(9), ANNOUNCEMENT(10), BOAT(11), L2FRIEND(12), MSNCHAT(13), PARTYMATCH_ROOM(
|
||||
14), PARTYROOM_COMMANDER(15), PARTYROOM_ALL(16), HERO_VOICE(17), CRITICAL_ANNOUNCE(
|
||||
18), SCREEN_ANNOUNCE(19), BATTLEFIELD(20), MPCC_ROOM(21);
|
||||
|
||||
public final int id;
|
||||
|
||||
MessageDestination(int id) {
|
||||
this.id = id;
|
||||
}
|
||||
|
||||
public static MessageDestination fromID(int id) {
|
||||
for (final MessageDestination dest : values()) {
|
||||
if (dest.id == id)
|
||||
return dest;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
}
|
||||
private ChatMessageDestination destination;
|
||||
|
||||
private String target;
|
||||
|
||||
@@ -103,16 +61,32 @@ public class CharacterChatMessagePacket extends AbstractClientPacket {
|
||||
@Override
|
||||
public void read(Lineage2Connection conn, ChannelBuffer buffer) {
|
||||
this.message = BufferUtils.readString(buffer);
|
||||
this.destination = MessageDestination.fromID(buffer.readInt());
|
||||
if (this.destination == MessageDestination.TELL) { // private message
|
||||
this.destination = ChatMessageDestination.fromID(buffer.readInt());
|
||||
if (this.destination == ChatMessageDestination.TELL) { // private
|
||||
// message
|
||||
this.target = BufferUtils.readString(buffer);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void process(final Lineage2Connection conn) {
|
||||
final PublicChatChannel channel = chatService.getGlobalChannel();
|
||||
// TODO handle chat destination!!!
|
||||
channel.send(conn.getCharacterID(), message);
|
||||
if (message.length() == 0 || destination == null) {
|
||||
conn.write(ActionFailedPacket.SHARED_INSTANCE);
|
||||
conn.close();
|
||||
}
|
||||
|
||||
try {
|
||||
chatService.send(conn.getCharacterID(), destination, message, target);
|
||||
} catch (TargetNotFoundChatServiceException e) {
|
||||
// TODO Auto-generated catch block
|
||||
e.printStackTrace();
|
||||
} catch (CannotChatToSelfChatServiceException e) {
|
||||
// TODO Auto-generated catch block
|
||||
e.printStackTrace();
|
||||
} catch (ChatBanActiveChatServiceException e) {
|
||||
// TODO Auto-generated catch block
|
||||
e.printStackTrace();
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
@@ -20,9 +20,9 @@ import org.jboss.netty.buffer.ChannelBuffer;
|
||||
|
||||
import com.l2jserver.game.net.Lineage2Connection;
|
||||
import com.l2jserver.game.net.packet.AbstractServerPacket;
|
||||
import com.l2jserver.game.net.packet.client.CharacterChatMessagePacket.MessageDestination;
|
||||
import com.l2jserver.game.net.packet.server.CharacterCreateFailPacket.Reason;
|
||||
import com.l2jserver.model.world.L2Character;
|
||||
import com.l2jserver.service.game.chat.ChatMessageDestination;
|
||||
import com.l2jserver.util.BufferUtils;
|
||||
|
||||
/**
|
||||
@@ -42,12 +42,12 @@ public class ActorChatMessagePacket extends AbstractServerPacket {
|
||||
* The selected character
|
||||
*/
|
||||
private final L2Character character;
|
||||
private MessageDestination destination;
|
||||
private ChatMessageDestination destination;
|
||||
private String message = null;
|
||||
private int messageID = 0;
|
||||
|
||||
public ActorChatMessagePacket(L2Character character,
|
||||
MessageDestination destination, String message) {
|
||||
ChatMessageDestination destination, String message) {
|
||||
super(OPCODE);
|
||||
this.character = character;
|
||||
this.destination = destination;
|
||||
|
||||
@@ -0,0 +1,53 @@
|
||||
/*
|
||||
* This file is part of l2jserver <l2jserver.com>.
|
||||
*
|
||||
* l2jserver 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.
|
||||
*
|
||||
* l2jserver is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with l2jserver. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
package com.l2jserver.game.net.packet.server;
|
||||
|
||||
import org.htmlparser.tags.Html;
|
||||
import org.jboss.netty.buffer.ChannelBuffer;
|
||||
|
||||
import com.l2jserver.game.net.Lineage2Connection;
|
||||
import com.l2jserver.game.net.packet.AbstractServerPacket;
|
||||
import com.l2jserver.model.world.NPC;
|
||||
import com.l2jserver.util.BufferUtils;
|
||||
|
||||
/**
|
||||
* This packet sends an HTML message to be displayed in the client.
|
||||
*
|
||||
* @author <a href="http://www.rogiel.com">Rogiel</a>
|
||||
*/
|
||||
public class NPCHtmlMessagePacket extends AbstractServerPacket {
|
||||
/**
|
||||
* The packet OPCODE
|
||||
*/
|
||||
public static final int OPCODE = 0x19;
|
||||
|
||||
private final NPC npc;
|
||||
private final Html html;
|
||||
|
||||
public NPCHtmlMessagePacket(NPC npc, Html html) {
|
||||
super(OPCODE);
|
||||
this.npc = npc;
|
||||
this.html = html;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void write(Lineage2Connection conn, ChannelBuffer buffer) {
|
||||
buffer.writeInt(npc.getID().getID());
|
||||
BufferUtils.writeString(buffer, html.toHtml());
|
||||
buffer.writeInt(0x00); // item id
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,256 @@
|
||||
/*
|
||||
* This file is part of l2jserver <l2jserver.com>.
|
||||
*
|
||||
* l2jserver 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.
|
||||
*
|
||||
* l2jserver is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with l2jserver. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
package com.l2jserver.game.net.packet.server;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
import org.jboss.netty.buffer.ChannelBuffer;
|
||||
|
||||
import com.l2jserver.game.net.Lineage2Connection;
|
||||
import com.l2jserver.game.net.SystemMessage;
|
||||
import com.l2jserver.game.net.packet.AbstractServerPacket;
|
||||
import com.l2jserver.model.game.Fort;
|
||||
import com.l2jserver.model.game.Skill;
|
||||
import com.l2jserver.model.template.ItemTemplate;
|
||||
import com.l2jserver.model.template.SkillTemplate;
|
||||
import com.l2jserver.model.world.Item;
|
||||
import com.l2jserver.model.world.capability.Actor;
|
||||
import com.l2jserver.util.BufferUtils;
|
||||
import com.l2jserver.util.factory.CollectionFactory;
|
||||
|
||||
/**
|
||||
* This packet sends an System Message to the client. Most messages appear in
|
||||
* the console.
|
||||
*
|
||||
* @author <a href="http://www.rogiel.com">Rogiel</a>
|
||||
*/
|
||||
public class SystemMessagePacket extends AbstractServerPacket {
|
||||
/**
|
||||
* The packet OPCODE
|
||||
*/
|
||||
public static final int OPCODE = 0x62;
|
||||
|
||||
/**
|
||||
* The System message id
|
||||
*/
|
||||
private int id;
|
||||
/**
|
||||
* The system message parameters
|
||||
*/
|
||||
private List<SystemMessagePacketParameter> params = CollectionFactory
|
||||
.newList();
|
||||
|
||||
public interface SystemMessagePacketParameter {
|
||||
public static final byte TYPE_SYSTEM_STRING = 13;
|
||||
public static final byte TYPE_PLAYER_NAME = 12;
|
||||
// id 11 - unknown
|
||||
public static final byte TYPE_INSTANCE_NAME = 10;
|
||||
public static final byte TYPE_ELEMENT_NAME = 9;
|
||||
// id 8 - same as 3
|
||||
public static final byte TYPE_ZONE_NAME = 7;
|
||||
public static final byte TYPE_ITEM_NUMBER = 6;
|
||||
public static final byte TYPE_CASTLE_NAME = 5;
|
||||
public static final byte TYPE_SKILL_NAME = 4;
|
||||
public static final byte TYPE_ITEM_NAME = 3;
|
||||
public static final byte TYPE_NPC_NAME = 2;
|
||||
public static final byte TYPE_NUMBER = 1;
|
||||
public static final byte TYPE_TEXT = 0;
|
||||
|
||||
void write(Lineage2Connection conn, ChannelBuffer buffer);
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a new instance
|
||||
*/
|
||||
public SystemMessagePacket(SystemMessage message) {
|
||||
super(OPCODE);
|
||||
this.id = message.id;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void write(Lineage2Connection conn, ChannelBuffer buffer) {
|
||||
buffer.writeInt(id);
|
||||
buffer.writeInt(params.size());
|
||||
for (final SystemMessagePacketParameter param : params) {
|
||||
param.write(conn, buffer);
|
||||
}
|
||||
}
|
||||
|
||||
public final SystemMessagePacket addString(final String text) {
|
||||
params.add(new SystemMessagePacketParameter() {
|
||||
@Override
|
||||
public void write(Lineage2Connection conn, ChannelBuffer buffer) {
|
||||
buffer.writeInt(TYPE_TEXT);
|
||||
BufferUtils.writeString(buffer, text);
|
||||
}
|
||||
});
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Castlename-e.dat<br>
|
||||
* 0-9 Castle names<br>
|
||||
* 21-64 CH names<br>
|
||||
* 81-89 Territory names<br>
|
||||
* 101-121 Fortress names<br>
|
||||
*
|
||||
* @param number
|
||||
* @return
|
||||
*/
|
||||
public final SystemMessagePacket addFort(final Fort fort) {
|
||||
params.add(new SystemMessagePacketParameter() {
|
||||
@Override
|
||||
public void write(Lineage2Connection conn, ChannelBuffer buffer) {
|
||||
buffer.writeInt(TYPE_CASTLE_NAME);
|
||||
buffer.writeInt(fort.getID().getID());
|
||||
}
|
||||
});
|
||||
return this;
|
||||
}
|
||||
|
||||
public final SystemMessagePacket addNumber(final int number) {
|
||||
params.add(new SystemMessagePacketParameter() {
|
||||
@Override
|
||||
public void write(Lineage2Connection conn, ChannelBuffer buffer) {
|
||||
buffer.writeInt(TYPE_NUMBER);
|
||||
buffer.writeInt(number);
|
||||
}
|
||||
});
|
||||
return this;
|
||||
}
|
||||
|
||||
public final SystemMessagePacket addItemCount(final long number) {
|
||||
params.add(new SystemMessagePacketParameter() {
|
||||
@Override
|
||||
public void write(Lineage2Connection conn, ChannelBuffer buffer) {
|
||||
buffer.writeInt(TYPE_ITEM_NUMBER);
|
||||
buffer.writeLong(number);
|
||||
}
|
||||
});
|
||||
return this;
|
||||
}
|
||||
|
||||
public final SystemMessagePacket addActorName(final Actor actor) {
|
||||
// params.add(new SystemMessagePacketParameter() {
|
||||
// @Override
|
||||
// public void write(Lineage2Connection conn, ChannelBuffer buffer) {
|
||||
// // buffer.writeInt(TYPE_TEXT);
|
||||
// // buffer.writeInt(number);
|
||||
// // TODO
|
||||
// }
|
||||
// });
|
||||
return this;
|
||||
}
|
||||
|
||||
public final SystemMessagePacket addItem(final ItemTemplate item) {
|
||||
params.add(new SystemMessagePacketParameter() {
|
||||
@Override
|
||||
public void write(Lineage2Connection conn, ChannelBuffer buffer) {
|
||||
buffer.writeInt(TYPE_ITEM_NAME);
|
||||
buffer.writeInt(item.getID().getID());
|
||||
}
|
||||
});
|
||||
return this;
|
||||
}
|
||||
|
||||
public final SystemMessagePacket addItem(final Item item) {
|
||||
return addItem(item.getTemplateID().getTemplate());
|
||||
}
|
||||
|
||||
public final SystemMessagePacket addZoneName(final int x, final int y,
|
||||
final int z) {
|
||||
params.add(new SystemMessagePacketParameter() {
|
||||
@Override
|
||||
public void write(Lineage2Connection conn, ChannelBuffer buffer) {
|
||||
buffer.writeInt(TYPE_ZONE_NAME);
|
||||
buffer.writeInt(x);
|
||||
buffer.writeInt(y);
|
||||
buffer.writeInt(z);
|
||||
}
|
||||
});
|
||||
return this;
|
||||
}
|
||||
|
||||
public final SystemMessagePacket addSkill(final SkillTemplate skill,
|
||||
final int level) {
|
||||
params.add(new SystemMessagePacketParameter() {
|
||||
@Override
|
||||
public void write(Lineage2Connection conn, ChannelBuffer buffer) {
|
||||
buffer.writeInt(TYPE_SKILL_NAME);
|
||||
buffer.writeInt(skill.getID().getID());
|
||||
buffer.writeInt(level);
|
||||
}
|
||||
});
|
||||
return this;
|
||||
}
|
||||
|
||||
public final SystemMessagePacket addSkill(final Skill skill) {
|
||||
return addSkill(skill.getSkillTemplate(), skill.getLevel());
|
||||
}
|
||||
|
||||
/**
|
||||
* Elemental name - 0(Fire) ...
|
||||
*
|
||||
* @param type
|
||||
* @return
|
||||
*/
|
||||
public final SystemMessagePacket addElemntal(final int type) {
|
||||
params.add(new SystemMessagePacketParameter() {
|
||||
@Override
|
||||
public void write(Lineage2Connection conn, ChannelBuffer buffer) {
|
||||
buffer.writeInt(TYPE_ELEMENT_NAME);
|
||||
buffer.writeInt(type);
|
||||
}
|
||||
});
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* ID from sysstring-e.dat
|
||||
*
|
||||
* @param type
|
||||
* @return
|
||||
*/
|
||||
public final SystemMessagePacket addSystemString(final int type) {
|
||||
params.add(new SystemMessagePacketParameter() {
|
||||
@Override
|
||||
public void write(Lineage2Connection conn, ChannelBuffer buffer) {
|
||||
buffer.writeInt(TYPE_SYSTEM_STRING);
|
||||
buffer.writeInt(type);
|
||||
}
|
||||
});
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Instance name from instantzonedata-e.dat
|
||||
*
|
||||
* @param type
|
||||
* id of instance
|
||||
* @return
|
||||
*/
|
||||
public final SystemMessagePacket addInstanceName(final int type) {
|
||||
params.add(new SystemMessagePacketParameter() {
|
||||
@Override
|
||||
public void write(Lineage2Connection conn, ChannelBuffer buffer) {
|
||||
buffer.writeInt(TYPE_INSTANCE_NAME);
|
||||
buffer.writeInt(type);
|
||||
}
|
||||
});
|
||||
return this;
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user