Progress! Some mob disguises are working now
Added in dual hand animations Removed libraryaddict
This commit is contained in:
parent
cf1b15942c
commit
99daf8246d
@ -9,11 +9,11 @@ public class PigWatcher extends AgeableWatcher {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public boolean isSaddled() {
|
public boolean isSaddled() {
|
||||||
return (byte) getValue(16, (byte) 0) == 1;
|
return (boolean) getValue(12, false);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void setSaddled(boolean isSaddled) {
|
public void setSaddled(boolean isSaddled) {
|
||||||
setValue(16, (byte) (isSaddled ? 1 : 0));
|
setValue(12, isSaddled);
|
||||||
sendData(16);
|
sendData(12);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -6,9 +6,6 @@ import com.comphenix.protocol.ProtocolManager;
|
|||||||
import com.comphenix.protocol.events.PacketContainer;
|
import com.comphenix.protocol.events.PacketContainer;
|
||||||
import com.comphenix.protocol.reflect.StructureModifier;
|
import com.comphenix.protocol.reflect.StructureModifier;
|
||||||
import com.comphenix.protocol.wrappers.WrappedDataWatcher;
|
import com.comphenix.protocol.wrappers.WrappedDataWatcher;
|
||||||
import com.comphenix.protocol.wrappers.WrappedDataWatcher.Registry;
|
|
||||||
import com.comphenix.protocol.wrappers.WrappedDataWatcher.Serializer;
|
|
||||||
import com.comphenix.protocol.wrappers.WrappedDataWatcher.WrappedDataWatcherObject;
|
|
||||||
import com.comphenix.protocol.wrappers.WrappedGameProfile;
|
import com.comphenix.protocol.wrappers.WrappedGameProfile;
|
||||||
import me.libraryaddict.disguise.DisguiseAPI;
|
import me.libraryaddict.disguise.DisguiseAPI;
|
||||||
import me.libraryaddict.disguise.DisguiseConfig;
|
import me.libraryaddict.disguise.DisguiseConfig;
|
||||||
@ -28,7 +25,6 @@ import org.bukkit.World;
|
|||||||
import org.bukkit.block.BlockFace;
|
import org.bukkit.block.BlockFace;
|
||||||
import org.bukkit.entity.Ageable;
|
import org.bukkit.entity.Ageable;
|
||||||
import org.bukkit.entity.Entity;
|
import org.bukkit.entity.Entity;
|
||||||
import org.bukkit.entity.LivingEntity;
|
|
||||||
import org.bukkit.entity.Player;
|
import org.bukkit.entity.Player;
|
||||||
import org.bukkit.entity.Zombie;
|
import org.bukkit.entity.Zombie;
|
||||||
import org.bukkit.inventory.EquipmentSlot;
|
import org.bukkit.inventory.EquipmentSlot;
|
||||||
@ -42,7 +38,6 @@ import org.bukkit.scoreboard.Team.OptionStatus;
|
|||||||
import org.bukkit.util.Vector;
|
import org.bukkit.util.Vector;
|
||||||
|
|
||||||
import java.lang.reflect.Array;
|
import java.lang.reflect.Array;
|
||||||
import java.lang.reflect.Constructor;
|
|
||||||
import java.lang.reflect.Field;
|
import java.lang.reflect.Field;
|
||||||
import java.lang.reflect.InvocationTargetException;
|
import java.lang.reflect.InvocationTargetException;
|
||||||
import java.lang.reflect.Method;
|
import java.lang.reflect.Method;
|
||||||
@ -898,22 +893,55 @@ public class DisguiseUtilities {
|
|||||||
.createPacket(0, player.getPassenger(), player));
|
.createPacket(0, player.getPassenger(), player));
|
||||||
}
|
}
|
||||||
|
|
||||||
// Resend the armor
|
sendSelfPacket(player,
|
||||||
for (int i = 0; i < 5; i++) {
|
manager.createPacketConstructor(
|
||||||
ItemStack item;
|
Server.ENTITY_EQUIPMENT,
|
||||||
if (i == 0) {
|
0,
|
||||||
item = player.getItemInHand();
|
ReflectionManager.createEnumItemSlot(EquipmentSlot.HEAD),
|
||||||
} else {
|
ReflectionManager.getNmsItem(new ItemStack(Material.STONE)))
|
||||||
item = player.getInventory().getArmorContents()[i - 1];
|
.createPacket(player.getEntityId(), ReflectionManager.createEnumItemSlot(EquipmentSlot.HEAD),
|
||||||
}
|
ReflectionManager.getNmsItem(player.getInventory().getHelmet())));
|
||||||
|
sendSelfPacket(player,
|
||||||
|
manager.createPacketConstructor(
|
||||||
|
Server.ENTITY_EQUIPMENT,
|
||||||
|
0,
|
||||||
|
ReflectionManager.createEnumItemSlot(EquipmentSlot.HEAD),
|
||||||
|
ReflectionManager.getNmsItem(new ItemStack(Material.STONE)))
|
||||||
|
.createPacket(player.getEntityId(), ReflectionManager.createEnumItemSlot(EquipmentSlot.CHEST),
|
||||||
|
ReflectionManager.getNmsItem(player.getInventory().getChestplate())));
|
||||||
|
sendSelfPacket(player,
|
||||||
|
manager.createPacketConstructor(
|
||||||
|
Server.ENTITY_EQUIPMENT,
|
||||||
|
0,
|
||||||
|
ReflectionManager.createEnumItemSlot(EquipmentSlot.HEAD),
|
||||||
|
ReflectionManager.getNmsItem(new ItemStack(Material.STONE)))
|
||||||
|
.createPacket(player.getEntityId(), ReflectionManager.createEnumItemSlot(EquipmentSlot.LEGS),
|
||||||
|
ReflectionManager.getNmsItem(player.getInventory().getLeggings())));
|
||||||
|
sendSelfPacket(player,
|
||||||
|
manager.createPacketConstructor(
|
||||||
|
Server.ENTITY_EQUIPMENT,
|
||||||
|
0,
|
||||||
|
ReflectionManager.createEnumItemSlot(EquipmentSlot.HEAD),
|
||||||
|
ReflectionManager.getNmsItem(new ItemStack(Material.STONE)))
|
||||||
|
.createPacket(player.getEntityId(), ReflectionManager.createEnumItemSlot(EquipmentSlot.FEET),
|
||||||
|
ReflectionManager.getNmsItem(player.getInventory().getBoots())));
|
||||||
|
sendSelfPacket(player,
|
||||||
|
manager.createPacketConstructor(
|
||||||
|
Server.ENTITY_EQUIPMENT,
|
||||||
|
0,
|
||||||
|
ReflectionManager.createEnumItemSlot(EquipmentSlot.HEAD),
|
||||||
|
ReflectionManager.getNmsItem(new ItemStack(Material.STONE)))
|
||||||
|
.createPacket(player.getEntityId(), ReflectionManager.createEnumItemSlot(EquipmentSlot.HAND),
|
||||||
|
ReflectionManager.getNmsItem(player.getInventory().getItemInMainHand())));
|
||||||
|
sendSelfPacket(player,
|
||||||
|
manager.createPacketConstructor(
|
||||||
|
Server.ENTITY_EQUIPMENT,
|
||||||
|
0,
|
||||||
|
ReflectionManager.createEnumItemSlot(EquipmentSlot.HEAD),
|
||||||
|
ReflectionManager.getNmsItem(new ItemStack(Material.STONE)))
|
||||||
|
.createPacket(player.getEntityId(), ReflectionManager.createEnumItemSlot(EquipmentSlot.OFF_HAND),
|
||||||
|
ReflectionManager.getNmsItem(player.getInventory().getItemInOffHand())));
|
||||||
|
|
||||||
if (item != null && item.getType() != Material.AIR) {
|
|
||||||
sendSelfPacket(
|
|
||||||
player,
|
|
||||||
manager.createPacketConstructor(Server.ENTITY_EQUIPMENT, player.getEntityId(), i,
|
|
||||||
item).createPacket(player.getEntityId(), i, item));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
Location loc = player.getLocation();
|
Location loc = player.getLocation();
|
||||||
// If the disguised is sleeping for w/e reason
|
// If the disguised is sleeping for w/e reason
|
||||||
if (player.isSleeping()) {
|
if (player.isSleeping()) {
|
||||||
|
@ -46,7 +46,6 @@ import org.bukkit.entity.Zombie;
|
|||||||
import org.bukkit.inventory.EquipmentSlot;
|
import org.bukkit.inventory.EquipmentSlot;
|
||||||
import org.bukkit.inventory.ItemStack;
|
import org.bukkit.inventory.ItemStack;
|
||||||
import org.bukkit.metadata.FixedMetadataValue;
|
import org.bukkit.metadata.FixedMetadataValue;
|
||||||
import org.bukkit.util.Vector;
|
|
||||||
|
|
||||||
import java.lang.reflect.InvocationTargetException;
|
import java.lang.reflect.InvocationTargetException;
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
@ -176,15 +175,13 @@ public class PacketsManager {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (disguise.getType() == DisguiseType.EXPERIENCE_ORB) {
|
if (disguise.getType() == DisguiseType.EXPERIENCE_ORB) {
|
||||||
|
|
||||||
spawnPackets[0] = new PacketContainer(Server.SPAWN_ENTITY_EXPERIENCE_ORB);
|
spawnPackets[0] = new PacketContainer(Server.SPAWN_ENTITY_EXPERIENCE_ORB);
|
||||||
StructureModifier<Object> mods = spawnPackets[0].getModifier();
|
StructureModifier<Object> mods = spawnPackets[0].getModifier();
|
||||||
mods.write(0, disguisedEntity.getEntityId());
|
mods.write(0, disguisedEntity.getEntityId());
|
||||||
mods.write(1, (int) Math.floor(loc.getX() * 32));
|
mods.write(1, Math.floor(loc.getX() * 32));
|
||||||
mods.write(2, (int) Math.floor(loc.getY() * 32) + 2);
|
mods.write(2, Math.floor(loc.getY() * 32) + 2);
|
||||||
mods.write(3, (int) Math.floor(loc.getZ() * 32));
|
mods.write(3, Math.floor(loc.getZ() * 32));
|
||||||
mods.write(4, 1);
|
mods.write(4, 1);
|
||||||
|
|
||||||
} else if (disguise.getType() == DisguiseType.PAINTING) {
|
} else if (disguise.getType() == DisguiseType.PAINTING) {
|
||||||
spawnPackets[0] = new PacketContainer(Server.SPAWN_ENTITY_PAINTING);
|
spawnPackets[0] = new PacketContainer(Server.SPAWN_ENTITY_PAINTING);
|
||||||
StructureModifier<Object> mods = spawnPackets[0].getModifier();
|
StructureModifier<Object> mods = spawnPackets[0].getModifier();
|
||||||
@ -198,52 +195,44 @@ public class PacketsManager {
|
|||||||
spawnPackets[1] = new PacketContainer(Server.ENTITY_TELEPORT);
|
spawnPackets[1] = new PacketContainer(Server.ENTITY_TELEPORT);
|
||||||
mods = spawnPackets[1].getModifier();
|
mods = spawnPackets[1].getModifier();
|
||||||
mods.write(0, disguisedEntity.getEntityId());
|
mods.write(0, disguisedEntity.getEntityId());
|
||||||
mods.write(1, (int) Math.floor(loc.getX() * 32D));
|
mods.write(1, Math.floor(loc.getX() * 32D));
|
||||||
mods.write(2, (int) Math.floor(loc.getY() * 32D));
|
mods.write(2, Math.floor(loc.getY() * 32D));
|
||||||
mods.write(3, (int) Math.floor(loc.getZ() * 32D));
|
mods.write(3, Math.floor(loc.getZ() * 32D));
|
||||||
mods.write(4, yaw);
|
mods.write(4, yaw);
|
||||||
mods.write(5, pitch);
|
mods.write(5, pitch);
|
||||||
|
|
||||||
} else if (disguise.getType().isPlayer()) {
|
} else if (disguise.getType().isPlayer()) {
|
||||||
|
//TODO: Make player disguises visible again
|
||||||
spawnPackets[0] = new PacketContainer(Server.NAMED_ENTITY_SPAWN);
|
spawnPackets[0] = new PacketContainer(Server.NAMED_ENTITY_SPAWN);
|
||||||
StructureModifier<String> stringMods = spawnPackets[0].getStrings();
|
|
||||||
WrappedGameProfile gameProfile;
|
|
||||||
if (stringMods.size() > 0) {
|
|
||||||
for (int i = 0; i < stringMods.size(); i++) {
|
|
||||||
stringMods.write(i, ((PlayerDisguise) disguise).getName());
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
PlayerDisguise playerDisguise = (PlayerDisguise) disguise;
|
PlayerDisguise playerDisguise = (PlayerDisguise) disguise;
|
||||||
String name = playerDisguise.getSkin() != null ? playerDisguise.getSkin() : playerDisguise.getName();
|
String name = playerDisguise.getSkin() != null ? playerDisguise.getSkin() : playerDisguise.getName();
|
||||||
boolean removeName = false;
|
boolean removeName = false;
|
||||||
if (!DisguiseUtilities.hasGameProfile(name)) {
|
if (!DisguiseUtilities.hasGameProfile(name)) {
|
||||||
removeName = !DisguiseUtilities.getAddedByPlugins().contains(name);
|
removeName = !DisguiseUtilities.getAddedByPlugins().contains(name);
|
||||||
}
|
}
|
||||||
gameProfile = playerDisguise.getGameProfile();
|
WrappedGameProfile gameProfile = playerDisguise.getGameProfile();
|
||||||
if (removeName) {
|
if (removeName) {
|
||||||
DisguiseUtilities.getAddedByPlugins().remove(name);
|
DisguiseUtilities.getAddedByPlugins().remove(name);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
//Write spawn packet in order
|
||||||
|
//Id
|
||||||
|
//UUID
|
||||||
|
//x
|
||||||
|
//y
|
||||||
|
//z
|
||||||
|
//pitch
|
||||||
|
//yaw
|
||||||
|
spawnPackets[0].getIntegers().write(0, disguisedEntity.getEntityId());
|
||||||
spawnPackets[0].getSpecificModifier(UUID.class).write(0, gameProfile.getUUID());
|
spawnPackets[0].getSpecificModifier(UUID.class).write(0, gameProfile.getUUID());
|
||||||
}
|
|
||||||
StructureModifier<Integer> intMods = spawnPackets[0].getIntegers();
|
|
||||||
intMods.write(0, disguisedEntity.getEntityId());
|
|
||||||
StructureModifier<Double> doubleMods = spawnPackets[0].getDoubles();
|
StructureModifier<Double> doubleMods = spawnPackets[0].getDoubles();
|
||||||
doubleMods.write(0, Math.floor(loc.getX() * 32));
|
doubleMods.write(0, Math.floor(loc.getX() * 32));
|
||||||
doubleMods.write(1, Math.floor(loc.getY() * 32));
|
doubleMods.write(1, Math.floor(loc.getY() * 32));
|
||||||
doubleMods.write(2, Math.floor(loc.getZ() * 32));
|
doubleMods.write(2, Math.floor(loc.getZ() * 32));
|
||||||
ItemStack item = null;
|
|
||||||
if (disguisedEntity instanceof Player && ((Player) disguisedEntity).getInventory().getItemInMainHand() != null) {
|
|
||||||
item = ((Player) disguisedEntity).getInventory().getItemInMainHand();
|
|
||||||
} else if (disguisedEntity instanceof LivingEntity) {
|
|
||||||
item = ((LivingEntity) disguisedEntity).getEquipment().getItemInMainHand();
|
|
||||||
}
|
|
||||||
intMods.write(4, (item == null || item.getType() == Material.AIR ? 0 : item.getTypeId()));
|
|
||||||
StructureModifier<Byte> byteMods = spawnPackets[0].getBytes();
|
StructureModifier<Byte> byteMods = spawnPackets[0].getBytes();
|
||||||
byteMods.write(1, yaw);
|
|
||||||
byteMods.write(0, pitch);
|
byteMods.write(0, pitch);
|
||||||
spawnPackets[0].getDataWatcherModifier().write(0,
|
byteMods.write(1, yaw);
|
||||||
createDataWatcher(player, WrappedDataWatcher.getEntityWatcher(disguisedEntity), disguise.getWatcher()));
|
|
||||||
|
spawnPackets[0].getDataWatcherModifier().write(0, createDataWatcher(WrappedDataWatcher.getEntityWatcher(disguisedEntity), disguise.getWatcher()));
|
||||||
|
|
||||||
if (DisguiseConfig.isBedPacketsEnabled() && ((PlayerWatcher) disguise.getWatcher()).isSleeping()) {
|
if (DisguiseConfig.isBedPacketsEnabled() && ((PlayerWatcher) disguise.getWatcher()).isSleeping()) {
|
||||||
PacketContainer[] newPackets = new PacketContainer[spawnPackets.length + 1];
|
PacketContainer[] newPackets = new PacketContainer[spawnPackets.length + 1];
|
||||||
@ -263,53 +252,36 @@ public class PacketsManager {
|
|||||||
newPackets.add(spawnPacket);
|
newPackets.add(spawnPacket);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
//Send player info along with the disguise
|
||||||
spawnPackets = newPackets.toArray(new PacketContainer[newPackets.size()]);
|
spawnPackets = newPackets.toArray(new PacketContainer[newPackets.size()]);
|
||||||
spawnPackets[0] = new PacketContainer(Server.PLAYER_INFO);
|
spawnPackets[0] = new PacketContainer(Server.PLAYER_INFO);
|
||||||
|
|
||||||
|
//Add player to the list, necessary to spawn them
|
||||||
spawnPackets[0].getModifier().write(0, ReflectionManager.getEnumPlayerInfoAction(0));
|
spawnPackets[0].getModifier().write(0, ReflectionManager.getEnumPlayerInfoAction(0));
|
||||||
List playerList = new ArrayList();
|
List playerList = new ArrayList();
|
||||||
PlayerDisguise playerDisguise = (PlayerDisguise) disguise;
|
|
||||||
playerList.add(ReflectionManager.getPlayerInfoData(spawnPackets[0].getHandle(), playerDisguise.getGameProfile()));
|
playerList.add(ReflectionManager.getPlayerInfoData(spawnPackets[0].getHandle(), playerDisguise.getGameProfile()));
|
||||||
spawnPackets[0].getModifier().write(1, playerList);
|
spawnPackets[0].getModifier().write(1, playerList);
|
||||||
|
|
||||||
|
//Remove player from the list
|
||||||
PacketContainer delayedPacket = spawnPackets[0].shallowClone();
|
PacketContainer delayedPacket = spawnPackets[0].shallowClone();
|
||||||
delayedPacket.getModifier().write(0, ReflectionManager.getEnumPlayerInfoAction(4));
|
delayedPacket.getModifier().write(0, ReflectionManager.getEnumPlayerInfoAction(4));
|
||||||
delayedPackets = new PacketContainer[]{delayedPacket};
|
delayedPackets = new PacketContainer[]{delayedPacket};
|
||||||
|
|
||||||
} else if (disguise.getType().isMob() || disguise.getType() == DisguiseType.ARMOR_STAND) {
|
} else if (disguise.getType().isMob() || disguise.getType() == DisguiseType.ARMOR_STAND) {
|
||||||
DisguiseValues values = DisguiseValues.getDisguiseValues(disguise.getType());
|
Class<? extends Entity> entityClass = disguise.getType().getEntityClass();
|
||||||
Vector vec = disguisedEntity.getVelocity();
|
Entity entity = Bukkit.getWorlds().get(0).spawn(disguise.getEntity().getLocation(), entityClass);
|
||||||
spawnPackets[0] = new PacketContainer(Server.SPAWN_ENTITY_LIVING);
|
entity.setVelocity(disguisedEntity.getVelocity());
|
||||||
StructureModifier<Object> mods = spawnPackets[0].getModifier();
|
Object nms = ReflectionManager.getNmsEntity(entity);
|
||||||
mods.write(0, disguisedEntity.getEntityId());
|
PacketContainer packet = ProtocolLibrary.getProtocolManager().createPacketConstructor(Server.SPAWN_ENTITY_LIVING, nms)
|
||||||
mods.write(1, disguise.getType().getTypeId());
|
.createPacket(nms);
|
||||||
double d1 = 3.9D;
|
spawnPackets[0] = packet;
|
||||||
double d2 = vec.getX();
|
|
||||||
double d3 = vec.getY();
|
|
||||||
double d4 = vec.getZ();
|
|
||||||
if (d2 < -d1)
|
|
||||||
d2 = -d1;
|
|
||||||
if (d3 < -d1)
|
|
||||||
d3 = -d1;
|
|
||||||
if (d4 < -d1)
|
|
||||||
d4 = -d1;
|
|
||||||
if (d2 > d1)
|
|
||||||
d2 = d1;
|
|
||||||
if (d3 > d1)
|
|
||||||
d3 = d1;
|
|
||||||
if (d4 > d1)
|
|
||||||
d4 = d1;
|
|
||||||
mods.write(2, values.getEntitySize(loc.getX()));
|
|
||||||
mods.write(3, (int) Math.floor(loc.getY() * 32D));
|
|
||||||
mods.write(4, values.getEntitySize(loc.getZ()));
|
|
||||||
mods.write(5, (int) (d2 * 8000.0D));
|
|
||||||
mods.write(6, (int) (d3 * 8000.0D));
|
|
||||||
mods.write(7, (int) (d4 * 8000.0D));
|
|
||||||
mods.write(8, yaw);
|
|
||||||
mods.write(9, pitch);
|
|
||||||
spawnPackets[0].getDataWatcherModifier().write(0,
|
spawnPackets[0].getDataWatcherModifier().write(0,
|
||||||
createDataWatcher(player, WrappedDataWatcher.getEntityWatcher(disguisedEntity), disguise.getWatcher()));
|
createDataWatcher(WrappedDataWatcher.getEntityWatcher(disguisedEntity), disguise.getWatcher()));
|
||||||
|
entity.remove();
|
||||||
|
//You know, as cheap as this may seem, this is pretty damn effective
|
||||||
} else if (disguise.getType().isMisc()) {
|
} else if (disguise.getType().isMisc()) {
|
||||||
|
//TODO: Fix miscs
|
||||||
int id = disguise.getType().getEntityId();
|
int id = disguise.getType().getEntityId();
|
||||||
int data = ((MiscDisguise) disguise).getData();
|
int data = ((MiscDisguise) disguise).getData();
|
||||||
if (disguise.getType() == DisguiseType.FALLING_BLOCK) {
|
if (disguise.getType() == DisguiseType.FALLING_BLOCK) {
|
||||||
@ -352,13 +324,13 @@ public class PacketsManager {
|
|||||||
/**
|
/**
|
||||||
* Create a new datawatcher but with the 'correct' values
|
* Create a new datawatcher but with the 'correct' values
|
||||||
*/
|
*/
|
||||||
private static WrappedDataWatcher createDataWatcher(Player player, WrappedDataWatcher watcher, FlagWatcher flagWatcher) {
|
private static WrappedDataWatcher createDataWatcher(WrappedDataWatcher watcher, FlagWatcher flagWatcher) {
|
||||||
WrappedDataWatcher newWatcher = new WrappedDataWatcher();
|
WrappedDataWatcher newWatcher = new WrappedDataWatcher();
|
||||||
try {
|
try {
|
||||||
List<WrappedWatchableObject> list = DisguiseConfig.isMetadataPacketsEnabled() ? flagWatcher.convert(watcher
|
List<WrappedWatchableObject> list = DisguiseConfig.isMetadataPacketsEnabled() ?
|
||||||
.getWatchableObjects()) : flagWatcher.getWatchableObjects();
|
flagWatcher.convert(watcher.getWatchableObjects()) : flagWatcher.getWatchableObjects();
|
||||||
for (WrappedWatchableObject watchableObject : list) {
|
for (WrappedWatchableObject watchableObject : list) {
|
||||||
newWatcher.setObject(watchableObject.getIndex(), watchableObject.getValue());
|
newWatcher.setObject(watchableObject.getWatcherObject(), watchableObject.getValue());
|
||||||
}
|
}
|
||||||
} catch (Exception ex) {
|
} catch (Exception ex) {
|
||||||
ex.printStackTrace();
|
ex.printStackTrace();
|
||||||
|
Loading…
Reference in New Issue
Block a user