Add wrapper for MinecraftKey, more work on data watchers
Dieser Commit ist enthalten in:
Ursprung
5efc4ffee3
Commit
ec8fa0d1fe
@ -209,16 +209,18 @@ class EntityUtilities {
|
|||||||
|
|
||||||
// Read the entity hashmap
|
// Read the entity hashmap
|
||||||
Object trackedEntities = null;
|
Object trackedEntities = null;
|
||||||
|
|
||||||
try {
|
try {
|
||||||
trackedEntities = FieldUtils.readField(trackedEntitiesField, tracker, true);
|
trackedEntities = FieldUtils.readField(trackedEntitiesField, tracker, true);
|
||||||
} catch (IllegalAccessException e) {
|
} catch (IllegalAccessException e) {
|
||||||
throw new FieldAccessException("Cannot access 'trackedEntities' field due to security limitations.", e);
|
throw new FieldAccessException("Cannot access 'trackedEntities' field due to security limitations.", e);
|
||||||
}
|
}
|
||||||
|
|
||||||
return WrappedIntHashMap.fromHandle(trackedEntities).get(entityID);
|
Object trackerEntry = WrappedIntHashMap.fromHandle(trackedEntities).get(entityID);
|
||||||
|
Class<?> entryClass = MinecraftReflection.getEntityTrackerClass();
|
||||||
|
return entryClass.cast(trackerEntry);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Retrieve entity from a ID, even it it's newly created.
|
* Retrieve entity from a ID, even it it's newly created.
|
||||||
* @return The asssociated entity.
|
* @return The asssociated entity.
|
||||||
|
@ -31,6 +31,7 @@ import java.util.Map.Entry;
|
|||||||
import java.util.Set;
|
import java.util.Set;
|
||||||
|
|
||||||
import org.bukkit.Material;
|
import org.bukkit.Material;
|
||||||
|
import org.bukkit.Sound;
|
||||||
import org.bukkit.World;
|
import org.bukkit.World;
|
||||||
import org.bukkit.WorldType;
|
import org.bukkit.WorldType;
|
||||||
import org.bukkit.entity.Entity;
|
import org.bukkit.entity.Entity;
|
||||||
@ -962,7 +963,44 @@ public class BukkitConverters {
|
|||||||
|
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private static MethodAccessor soundGetter = null;
|
||||||
|
private static FieldAccessor soundKey = null;
|
||||||
|
|
||||||
|
public static EquivalentConverter<Sound> getSoundConverter() {
|
||||||
|
return new IgnoreNullConverter<Sound>() {
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Class<Sound> getSpecificType() {
|
||||||
|
return Sound.class;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected Object getGenericValue(Class<?> genericType, Sound specific) {
|
||||||
|
if (soundGetter == null) {
|
||||||
|
Class<?> soundEffects = MinecraftReflection.getMinecraftClass("SoundEffects");
|
||||||
|
FuzzyReflection fuzzy = FuzzyReflection.fromClass(soundEffects, true);
|
||||||
|
soundGetter = Accessors.getMethodAccessor(fuzzy.getMethodByParameters("getSound", MinecraftReflection.getMinecraftClass("SoundEffect"), String.class));
|
||||||
|
}
|
||||||
|
|
||||||
|
MinecraftKey key = MinecraftKey.fromEnum(specific);
|
||||||
|
return soundGetter.invoke(null, key.getFullKey());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected Sound getSpecificValue(Object generic) {
|
||||||
|
if (soundKey == null) {
|
||||||
|
Class<?> soundEffect = generic.getClass();
|
||||||
|
FuzzyReflection fuzzy = FuzzyReflection.fromClass(soundEffect, true);
|
||||||
|
soundKey = Accessors.getFieldAccessor(fuzzy.getFieldByType("key", MinecraftReflection.getMinecraftClass("MinecraftKey")));
|
||||||
|
}
|
||||||
|
|
||||||
|
MinecraftKey key = MinecraftKey.fromHandle(soundKey.get(generic));
|
||||||
|
return Sound.valueOf(key.getEnumFormat());
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Wraps a given equivalent converter in NULL checks, ensuring that such values are ignored.
|
* Wraps a given equivalent converter in NULL checks, ensuring that such values are ignored.
|
||||||
* @param <TType> Type
|
* @param <TType> Type
|
||||||
|
@ -0,0 +1,62 @@
|
|||||||
|
/**
|
||||||
|
* ProtocolLib - Bukkit server library that allows access to the Minecraft protocol.
|
||||||
|
* Copyright (C) 2016 dmulloy2
|
||||||
|
*
|
||||||
|
* 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 2 of
|
||||||
|
* the License, or (at your option) any later version.
|
||||||
|
*
|
||||||
|
* This program 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 this program;
|
||||||
|
* if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
|
||||||
|
* 02111-1307 USA
|
||||||
|
*/
|
||||||
|
package com.comphenix.protocol.wrappers;
|
||||||
|
|
||||||
|
import com.comphenix.protocol.reflect.StructureModifier;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @author dmulloy2
|
||||||
|
*/
|
||||||
|
|
||||||
|
public class MinecraftKey {
|
||||||
|
private final String prefix;
|
||||||
|
private final String key;
|
||||||
|
|
||||||
|
public MinecraftKey(String prefix, String key) {
|
||||||
|
this.prefix = prefix;
|
||||||
|
this.key = key;
|
||||||
|
}
|
||||||
|
|
||||||
|
public MinecraftKey(String key) {
|
||||||
|
this("minecraft", key);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static MinecraftKey fromHandle(Object handle) {
|
||||||
|
StructureModifier<String> modifier = new StructureModifier<String>(handle.getClass()).withTarget(handle).withType(String.class);
|
||||||
|
return new MinecraftKey(modifier.read(0), modifier.read(1));
|
||||||
|
}
|
||||||
|
|
||||||
|
public static MinecraftKey fromEnum(Enum<?> value) {
|
||||||
|
return new MinecraftKey(value.name().toLowerCase().replace("_", "."));
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getPrefix() {
|
||||||
|
return prefix;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getKey() {
|
||||||
|
return key;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getFullKey() {
|
||||||
|
return prefix + ":" + key;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getEnumFormat() {
|
||||||
|
return key.toUpperCase().replace(".", "_");
|
||||||
|
}
|
||||||
|
}
|
@ -52,9 +52,9 @@ public class WrappedDataWatcher extends AbstractWrapper implements Iterable<Wrap
|
|||||||
private static MethodAccessor GETTER = null;
|
private static MethodAccessor GETTER = null;
|
||||||
private static MethodAccessor SETTER = null;
|
private static MethodAccessor SETTER = null;
|
||||||
|
|
||||||
|
private static FieldAccessor ENTITY_FIELD = null;
|
||||||
private static FieldAccessor MAP_FIELD = null;
|
private static FieldAccessor MAP_FIELD = null;
|
||||||
private static Field ENTITY_DATA_FIELD = null;
|
private static Field ENTITY_DATA_FIELD = null;
|
||||||
private static Field ENTITY_FIELD = null;
|
|
||||||
|
|
||||||
private static ConstructorAccessor constructor = null;
|
private static ConstructorAccessor constructor = null;
|
||||||
private static ConstructorAccessor lightningConstructor = null;
|
private static ConstructorAccessor lightningConstructor = null;
|
||||||
@ -77,7 +77,14 @@ public class WrappedDataWatcher extends AbstractWrapper implements Iterable<Wrap
|
|||||||
*/
|
*/
|
||||||
public WrappedDataWatcher() {
|
public WrappedDataWatcher() {
|
||||||
this(newHandle(fakeEntity()));
|
this(newHandle(fakeEntity()));
|
||||||
clear();
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Constructs a new DataWatcher using a real entity.
|
||||||
|
* @param entity The entity
|
||||||
|
*/
|
||||||
|
public WrappedDataWatcher(Entity entity) {
|
||||||
|
this(newHandle(BukkitUnwrapper.getInstance().unwrapItem(entity)));
|
||||||
}
|
}
|
||||||
|
|
||||||
private static Object newHandle(Object entity) {
|
private static Object newHandle(Object entity) {
|
||||||
@ -160,10 +167,6 @@ public class WrappedDataWatcher extends AbstractWrapper implements Iterable<Wrap
|
|||||||
return getMap().size();
|
return getMap().size();
|
||||||
}
|
}
|
||||||
|
|
||||||
private void clear() {
|
|
||||||
getMap().clear();
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Gets the watchable object at a given index.
|
* Gets the watchable object at a given index.
|
||||||
* @param index Index
|
* @param index Index
|
||||||
@ -313,8 +316,13 @@ public class WrappedDataWatcher extends AbstractWrapper implements Iterable<Wrap
|
|||||||
* @return A cloned data watcher.
|
* @return A cloned data watcher.
|
||||||
*/
|
*/
|
||||||
public WrappedDataWatcher deepClone() {
|
public WrappedDataWatcher deepClone() {
|
||||||
// TODO This
|
WrappedDataWatcher clone = new WrappedDataWatcher(getEntity());
|
||||||
return null;
|
|
||||||
|
for (Entry<Integer, WrappedWatchableObject> entry : asMap().entrySet()) {
|
||||||
|
clone.setObject(entry.getKey(), entry.getValue());
|
||||||
|
}
|
||||||
|
|
||||||
|
return clone;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -354,6 +362,10 @@ public class WrappedDataWatcher extends AbstractWrapper implements Iterable<Wrap
|
|||||||
if (!MinecraftReflection.isUsingNetty())
|
if (!MinecraftReflection.isUsingNetty())
|
||||||
throw new IllegalStateException("This method is only supported on 1.7.2 and above.");
|
throw new IllegalStateException("This method is only supported on 1.7.2 and above.");
|
||||||
|
|
||||||
|
if (ENTITY_FIELD == null) {
|
||||||
|
ENTITY_FIELD = Accessors.getFieldAccessor(HANDLE_TYPE, MinecraftReflection.getEntityClass(), true);
|
||||||
|
}
|
||||||
|
|
||||||
try {
|
try {
|
||||||
return (Entity) MinecraftReflection.getBukkitEntity(ENTITY_FIELD.get(handle));
|
return (Entity) MinecraftReflection.getBukkitEntity(ENTITY_FIELD.get(handle));
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
@ -372,6 +384,10 @@ public class WrappedDataWatcher extends AbstractWrapper implements Iterable<Wrap
|
|||||||
if (!MinecraftReflection.isUsingNetty())
|
if (!MinecraftReflection.isUsingNetty())
|
||||||
throw new IllegalStateException("This method is only supported on 1.7.2 and above.");
|
throw new IllegalStateException("This method is only supported on 1.7.2 and above.");
|
||||||
|
|
||||||
|
if (ENTITY_FIELD == null) {
|
||||||
|
ENTITY_FIELD = Accessors.getFieldAccessor(HANDLE_TYPE, MinecraftReflection.getEntityClass(), true);
|
||||||
|
}
|
||||||
|
|
||||||
try {
|
try {
|
||||||
ENTITY_FIELD.set(handle, BukkitUnwrapper.getInstance().unwrapItem(entity));
|
ENTITY_FIELD.set(handle, BukkitUnwrapper.getInstance().unwrapItem(entity));
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
|
@ -19,6 +19,8 @@ package com.comphenix.protocol.wrappers;
|
|||||||
import org.bukkit.inventory.ItemStack;
|
import org.bukkit.inventory.ItemStack;
|
||||||
|
|
||||||
import com.comphenix.protocol.reflect.StructureModifier;
|
import com.comphenix.protocol.reflect.StructureModifier;
|
||||||
|
import com.comphenix.protocol.reflect.accessors.Accessors;
|
||||||
|
import com.comphenix.protocol.reflect.accessors.ConstructorAccessor;
|
||||||
import com.comphenix.protocol.utility.MinecraftReflection;
|
import com.comphenix.protocol.utility.MinecraftReflection;
|
||||||
import com.comphenix.protocol.wrappers.WrappedDataWatcher.WrappedDataWatcherObject;
|
import com.comphenix.protocol.wrappers.WrappedDataWatcher.WrappedDataWatcherObject;
|
||||||
|
|
||||||
@ -28,6 +30,9 @@ import com.comphenix.protocol.wrappers.WrappedDataWatcher.WrappedDataWatcherObje
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
public class WrappedWatchableObject extends AbstractWrapper {
|
public class WrappedWatchableObject extends AbstractWrapper {
|
||||||
|
private static final Class<?> HANDLE_TYPE = MinecraftReflection.getDataWatcherItemClass();
|
||||||
|
private static ConstructorAccessor constructor;
|
||||||
|
|
||||||
private final StructureModifier<Object> modifier;
|
private final StructureModifier<Object> modifier;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -35,12 +40,28 @@ public class WrappedWatchableObject extends AbstractWrapper {
|
|||||||
* @param handle Data watcher item
|
* @param handle Data watcher item
|
||||||
*/
|
*/
|
||||||
public WrappedWatchableObject(Object handle) {
|
public WrappedWatchableObject(Object handle) {
|
||||||
super(MinecraftReflection.getDataWatcherItemClass());
|
super(HANDLE_TYPE);
|
||||||
|
|
||||||
setHandle(handle);
|
setHandle(handle);
|
||||||
this.modifier = new StructureModifier<Object>(handleType).withTarget(handle);
|
this.modifier = new StructureModifier<Object>(handleType).withTarget(handle);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Constructs a wrapped watchable object with a given watcher object and initial value.
|
||||||
|
* @param watcherObject Watcher object
|
||||||
|
* @param value Initial value
|
||||||
|
*/
|
||||||
|
public WrappedWatchableObject(WrappedDataWatcherObject watcherObject, Object value) {
|
||||||
|
this(newHandle(watcherObject, value));
|
||||||
|
}
|
||||||
|
|
||||||
|
private static Object newHandle(WrappedDataWatcherObject watcherObject, Object value) {
|
||||||
|
if (constructor == null) {
|
||||||
|
constructor = Accessors.getConstructorAccessor(HANDLE_TYPE.getConstructors()[0]);
|
||||||
|
}
|
||||||
|
|
||||||
|
return constructor.invoke(watcherObject.getHandle(), value);
|
||||||
|
}
|
||||||
|
|
||||||
// ---- Getter methods
|
// ---- Getter methods
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -20,9 +20,12 @@ import static org.junit.Assert.assertArrayEquals;
|
|||||||
import static org.junit.Assert.assertEquals;
|
import static org.junit.Assert.assertEquals;
|
||||||
import static org.junit.Assert.assertNull;
|
import static org.junit.Assert.assertNull;
|
||||||
import static org.junit.Assert.assertTrue;
|
import static org.junit.Assert.assertTrue;
|
||||||
|
import static org.junit.Assert.fail;
|
||||||
|
|
||||||
|
import java.lang.reflect.Array;
|
||||||
import java.lang.reflect.Field;
|
import java.lang.reflect.Field;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
import java.util.Objects;
|
||||||
import java.util.UUID;
|
import java.util.UUID;
|
||||||
|
|
||||||
import net.minecraft.server.v1_9_R1.AttributeModifier;
|
import net.minecraft.server.v1_9_R1.AttributeModifier;
|
||||||
@ -47,10 +50,12 @@ import org.powermock.core.classloader.annotations.PowerMockIgnore;
|
|||||||
import com.comphenix.protocol.BukkitInitialization;
|
import com.comphenix.protocol.BukkitInitialization;
|
||||||
import com.comphenix.protocol.PacketType;
|
import com.comphenix.protocol.PacketType;
|
||||||
import com.comphenix.protocol.PacketType.Sender;
|
import com.comphenix.protocol.PacketType.Sender;
|
||||||
|
import com.comphenix.protocol.reflect.EquivalentConverter;
|
||||||
import com.comphenix.protocol.reflect.StructureModifier;
|
import com.comphenix.protocol.reflect.StructureModifier;
|
||||||
import com.comphenix.protocol.utility.MinecraftReflection;
|
import com.comphenix.protocol.utility.MinecraftReflection;
|
||||||
import com.comphenix.protocol.utility.Util;
|
import com.comphenix.protocol.utility.Util;
|
||||||
import com.comphenix.protocol.wrappers.BlockPosition;
|
import com.comphenix.protocol.wrappers.BlockPosition;
|
||||||
|
import com.comphenix.protocol.wrappers.BukkitConverters;
|
||||||
import com.comphenix.protocol.wrappers.WrappedBlockData;
|
import com.comphenix.protocol.wrappers.WrappedBlockData;
|
||||||
import com.comphenix.protocol.wrappers.WrappedChatComponent;
|
import com.comphenix.protocol.wrappers.WrappedChatComponent;
|
||||||
import com.comphenix.protocol.wrappers.WrappedDataWatcher;
|
import com.comphenix.protocol.wrappers.WrappedDataWatcher;
|
||||||
@ -66,8 +71,8 @@ import com.google.common.collect.Lists;
|
|||||||
//@PrepareForTest(CraftItemFactory.class)
|
//@PrepareForTest(CraftItemFactory.class)
|
||||||
public class PacketContainerTest {
|
public class PacketContainerTest {
|
||||||
// Helper converters
|
// Helper converters
|
||||||
// private EquivalentConverter<WrappedDataWatcher> watchConvert = BukkitConverters.getDataWatcherConverter();
|
private EquivalentConverter<WrappedDataWatcher> watchConvert = BukkitConverters.getDataWatcherConverter();
|
||||||
// private EquivalentConverter<ItemStack> itemConvert = BukkitConverters.getItemStackConverter();
|
private EquivalentConverter<ItemStack> itemConvert = BukkitConverters.getItemStackConverter();
|
||||||
|
|
||||||
@BeforeClass
|
@BeforeClass
|
||||||
public static void initializeBukkit() throws IllegalAccessException {
|
public static void initializeBukkit() throws IllegalAccessException {
|
||||||
@ -458,14 +463,11 @@ public class PacketContainerTest {
|
|||||||
assertEquals(effect.hasParticles(), packet.getBytes().read(2) == (effect.hasParticles() ? 1 : 0));
|
assertEquals(effect.hasParticles(), packet.getBytes().read(2) == (effect.hasParticles() ? 1 : 0));
|
||||||
}*/
|
}*/
|
||||||
|
|
||||||
// This is usually the last one, since it requires all the API stuff to be worked out
|
private static final List<PacketType> BLACKLISTED = Util.asList(
|
||||||
|
PacketType.Play.Client.CUSTOM_PAYLOAD, PacketType.Play.Server.CUSTOM_PAYLOAD,
|
||||||
/*private static final List<PacketType> BLACKLISTED = Util.asList(
|
PacketType.Play.Server.SET_COOLDOWN, PacketType.Play.Server.NAMED_SOUND_EFFECT
|
||||||
PacketType.Play.Client.CUSTOM_PAYLOAD, PacketType.Play.Server.CUSTOM_PAYLOAD, PacketType.Play.Server.MAP_CHUNK,
|
|
||||||
PacketType.Play.Server.UPDATE_ATTRIBUTES
|
|
||||||
);
|
);
|
||||||
|
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testDeepClone() {
|
public void testDeepClone() {
|
||||||
// Try constructing all the packets
|
// Try constructing all the packets
|
||||||
@ -505,7 +507,7 @@ public class PacketContainerTest {
|
|||||||
testEquality(firstMod.read(i), secondMod.read(i));
|
testEquality(firstMod.read(i), secondMod.read(i));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} catch (IllegalArgumentException e) {
|
} catch (IllegalArgumentException e) {
|
||||||
if (!registered) {
|
if (!registered) {
|
||||||
// Let the test pass
|
// Let the test pass
|
||||||
System.err.println("The packet ID " + type + " is not registered.");
|
System.err.println("The packet ID " + type + " is not registered.");
|
||||||
@ -518,7 +520,7 @@ public class PacketContainerTest {
|
|||||||
throw new RuntimeException("Failed to serialize packet " + type, e);
|
throw new RuntimeException("Failed to serialize packet " + type, e);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}*/
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testPacketType() {
|
public void testPacketType() {
|
||||||
@ -526,7 +528,7 @@ public class PacketContainerTest {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Convert to objects that support equals()
|
// Convert to objects that support equals()
|
||||||
/*private void testEquality(Object a, Object b) {
|
private void testEquality(Object a, Object b) {
|
||||||
if (a != null && b != null) {
|
if (a != null && b != null) {
|
||||||
if (MinecraftReflection.isDataWatcher(a)) {
|
if (MinecraftReflection.isDataWatcher(a)) {
|
||||||
a = watchConvert.getSpecific(a);
|
a = watchConvert.getSpecific(a);
|
||||||
@ -542,20 +544,20 @@ public class PacketContainerTest {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
if (a.equals(b) || Objects.equal(a, b) || a.toString().equals(b.toString())) {
|
if (a.equals(b) || Objects.equals(a, b) || a.toString().equals(b.toString())) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
assertEquals(a, b);
|
assertEquals(a, b);
|
||||||
}*/
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Get the underlying array as an object array.
|
* Get the underlying array as an object array.
|
||||||
* @param val - array wrapped as an Object.
|
* @param val - array wrapped as an Object.
|
||||||
* @return An object array.
|
* @return An object array.
|
||||||
*/
|
*/
|
||||||
/*private Object[] getArray(Object val) {
|
private Object[] getArray(Object val) {
|
||||||
if (val instanceof Object[])
|
if (val instanceof Object[])
|
||||||
return (Object[]) val;
|
return (Object[]) val;
|
||||||
if (val == null)
|
if (val == null)
|
||||||
@ -567,5 +569,5 @@ public class PacketContainerTest {
|
|||||||
for (int i = 0; i < arrlength; ++i)
|
for (int i = 0; i < arrlength; ++i)
|
||||||
outputArray[i] = Array.get(val, i);
|
outputArray[i] = Array.get(val, i);
|
||||||
return outputArray;
|
return outputArray;
|
||||||
}*/
|
}
|
||||||
}
|
}
|
||||||
|
In neuem Issue referenzieren
Einen Benutzer sperren