Added the ability to read NBT ItemStack tags.
Dieser Commit ist enthalten in:
Ursprung
38137cea2c
Commit
70589a6263
@ -72,6 +72,15 @@ public class NbtCompound implements NbtWrapper<Map<String, NbtBase<?>>>, Iterabl
|
|||||||
container.setName(name);
|
container.setName(name);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Determine if an entry with the given key exists or not.
|
||||||
|
* @param key - the key to lookup.
|
||||||
|
* @return TRUE if an entry with the given key exists, FALSE otherwise.
|
||||||
|
*/
|
||||||
|
public boolean containsKey(String key) {
|
||||||
|
return getValue().containsKey(key);
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Retrieve a Set view of the keys of each entry in this compound.
|
* Retrieve a Set view of the keys of each entry in this compound.
|
||||||
* @return The keys of each entry.
|
* @return The keys of each entry.
|
||||||
@ -118,17 +127,36 @@ public class NbtCompound implements NbtWrapper<Map<String, NbtBase<?>>>, Iterabl
|
|||||||
/**
|
/**
|
||||||
* Retrieve the value of a given entry.
|
* Retrieve the value of a given entry.
|
||||||
* @param key - key of the entry to retrieve.
|
* @param key - key of the entry to retrieve.
|
||||||
* @return The value of this entry.
|
* @return The value of this entry, or NULL if not found.
|
||||||
*/
|
*/
|
||||||
@SuppressWarnings("unchecked")
|
@SuppressWarnings("unchecked")
|
||||||
public <T> NbtBase<T> getValue(String key) {
|
public <T> NbtBase<T> getValue(String key) {
|
||||||
return (NbtBase<T>) getValue().get(key);
|
return (NbtBase<T>) getValue().get(key);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Retrieve a value by its key, or assign and return a new NBT element if it doesn't exist.
|
||||||
|
* @param key - the key of the entry to find or create.
|
||||||
|
* @param type - the NBT element we will create if not found.
|
||||||
|
* @return The value that was retrieved or just created.
|
||||||
|
*/
|
||||||
|
public NbtBase<?> getValueOrDefault(String key, NbtType type) {
|
||||||
|
NbtBase<?> nbt = getValue(key);
|
||||||
|
|
||||||
|
// Create or get a compound
|
||||||
|
if (nbt == null)
|
||||||
|
put(nbt = NbtFactory.ofType(type, key));
|
||||||
|
else if (nbt.getType() != type)
|
||||||
|
throw new IllegalArgumentException("Cannot get tag " + nbt + ": Not a " + type);
|
||||||
|
|
||||||
|
return nbt;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Retrieve a value, or throw an exception.
|
* Retrieve a value, or throw an exception.
|
||||||
* @param key - the key to retrieve.
|
* @param key - the key to retrieve.
|
||||||
* @return The value of the entry.
|
* @return The value of the entry.
|
||||||
|
* @throws IllegalArgumentException If the key doesn't exist.
|
||||||
*/
|
*/
|
||||||
private <T> NbtBase<T> getValueExact(String key) {
|
private <T> NbtBase<T> getValueExact(String key) {
|
||||||
NbtBase<T> value = getValue(key);
|
NbtBase<T> value = getValue(key);
|
||||||
@ -159,11 +187,21 @@ public class NbtCompound implements NbtWrapper<Map<String, NbtBase<?>>>, Iterabl
|
|||||||
* Retrieve the string value of an entry identified by a given key.
|
* Retrieve the string value of an entry identified by a given key.
|
||||||
* @param key - the key of the entry.
|
* @param key - the key of the entry.
|
||||||
* @return The string value of the entry.
|
* @return The string value of the entry.
|
||||||
|
* @throws IllegalArgumentException If the key doesn't exist.
|
||||||
*/
|
*/
|
||||||
public String getString(String key) {
|
public String getString(String key) {
|
||||||
return (String) getValueExact(key).getValue();
|
return (String) getValueExact(key).getValue();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Retrieve the string value of an existing entry, or from a new default entry if it doesn't exist.
|
||||||
|
* @param key - the key of the entry.
|
||||||
|
* @return The value that was retrieved or just created.
|
||||||
|
*/
|
||||||
|
public String getStringOrDefault(String key) {
|
||||||
|
return (String) getValueOrDefault(key, NbtType.TAG_STRING).getValue();
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Associate a NBT string value with the given key.
|
* Associate a NBT string value with the given key.
|
||||||
* @param key - the key and NBT name.
|
* @param key - the key and NBT name.
|
||||||
@ -179,11 +217,21 @@ public class NbtCompound implements NbtWrapper<Map<String, NbtBase<?>>>, Iterabl
|
|||||||
* Retrieve the byte value of an entry identified by a given key.
|
* Retrieve the byte value of an entry identified by a given key.
|
||||||
* @param key - the key of the entry.
|
* @param key - the key of the entry.
|
||||||
* @return The byte value of the entry.
|
* @return The byte value of the entry.
|
||||||
|
* @throws IllegalArgumentException If the key doesn't exist.
|
||||||
*/
|
*/
|
||||||
public Byte getByte(String key) {
|
public byte getByte(String key) {
|
||||||
return (Byte) getValueExact(key).getValue();
|
return (Byte) getValueExact(key).getValue();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Retrieve the byte value of an existing entry, or from a new default entry if it doesn't exist.
|
||||||
|
* @param key - the key of the entry.
|
||||||
|
* @return The value that was retrieved or just created.
|
||||||
|
*/
|
||||||
|
public byte getByteOrDefault(String key) {
|
||||||
|
return (Byte) getValueOrDefault(key, NbtType.TAG_BYTE).getValue();
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Associate a NBT byte value with the given key.
|
* Associate a NBT byte value with the given key.
|
||||||
* @param key - the key and NBT name.
|
* @param key - the key and NBT name.
|
||||||
@ -199,11 +247,21 @@ public class NbtCompound implements NbtWrapper<Map<String, NbtBase<?>>>, Iterabl
|
|||||||
* Retrieve the short value of an entry identified by a given key.
|
* Retrieve the short value of an entry identified by a given key.
|
||||||
* @param key - the key of the entry.
|
* @param key - the key of the entry.
|
||||||
* @return The short value of the entry.
|
* @return The short value of the entry.
|
||||||
|
* @throws IllegalArgumentException If the key doesn't exist.
|
||||||
*/
|
*/
|
||||||
public Short getShort(String key) {
|
public Short getShort(String key) {
|
||||||
return (Short) getValueExact(key).getValue();
|
return (Short) getValueExact(key).getValue();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Retrieve the short value of an existing entry, or from a new default entry if it doesn't exist.
|
||||||
|
* @param key - the key of the entry.
|
||||||
|
* @return The value that was retrieved or just created.
|
||||||
|
*/
|
||||||
|
public short getShortOrDefault(String key) {
|
||||||
|
return (Short) getValueOrDefault(key, NbtType.TAG_SHORT).getValue();
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Associate a NBT short value with the given key.
|
* Associate a NBT short value with the given key.
|
||||||
* @param key - the key and NBT name.
|
* @param key - the key and NBT name.
|
||||||
@ -219,11 +277,21 @@ public class NbtCompound implements NbtWrapper<Map<String, NbtBase<?>>>, Iterabl
|
|||||||
* Retrieve the integer value of an entry identified by a given key.
|
* Retrieve the integer value of an entry identified by a given key.
|
||||||
* @param key - the key of the entry.
|
* @param key - the key of the entry.
|
||||||
* @return The integer value of the entry.
|
* @return The integer value of the entry.
|
||||||
|
* @throws IllegalArgumentException If the key doesn't exist.
|
||||||
*/
|
*/
|
||||||
public Integer getInteger(String key) {
|
public int getInteger(String key) {
|
||||||
return (Integer) getValueExact(key).getValue();
|
return (Integer) getValueExact(key).getValue();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Retrieve the integer value of an existing entry, or from a new default entry if it doesn't exist.
|
||||||
|
* @param key - the key of the entry.
|
||||||
|
* @return The value that was retrieved or just created.
|
||||||
|
*/
|
||||||
|
public int getIntegerOrDefault(String key) {
|
||||||
|
return (Integer) getValueOrDefault(key, NbtType.TAG_INT).getValue();
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Associate a NBT integer value with the given key.
|
* Associate a NBT integer value with the given key.
|
||||||
* @param key - the key and NBT name.
|
* @param key - the key and NBT name.
|
||||||
@ -239,11 +307,21 @@ public class NbtCompound implements NbtWrapper<Map<String, NbtBase<?>>>, Iterabl
|
|||||||
* Retrieve the long value of an entry identified by a given key.
|
* Retrieve the long value of an entry identified by a given key.
|
||||||
* @param key - the key of the entry.
|
* @param key - the key of the entry.
|
||||||
* @return The long value of the entry.
|
* @return The long value of the entry.
|
||||||
|
* @throws IllegalArgumentException If the key doesn't exist.
|
||||||
*/
|
*/
|
||||||
public Long getLong(String key) {
|
public long getLong(String key) {
|
||||||
return (Long) getValueExact(key).getValue();
|
return (Long) getValueExact(key).getValue();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Retrieve the long value of an existing entry, or from a new default entry if it doesn't exist.
|
||||||
|
* @param key - the key of the entry.
|
||||||
|
* @return The value that was retrieved or just created.
|
||||||
|
*/
|
||||||
|
public long getLongOrDefault(String key) {
|
||||||
|
return (Long) getValueOrDefault(key, NbtType.TAG_LONG).getValue();
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Associate a NBT long value with the given key.
|
* Associate a NBT long value with the given key.
|
||||||
* @param key - the key and NBT name.
|
* @param key - the key and NBT name.
|
||||||
@ -259,11 +337,21 @@ public class NbtCompound implements NbtWrapper<Map<String, NbtBase<?>>>, Iterabl
|
|||||||
* Retrieve the float value of an entry identified by a given key.
|
* Retrieve the float value of an entry identified by a given key.
|
||||||
* @param key - the key of the entry.
|
* @param key - the key of the entry.
|
||||||
* @return The float value of the entry.
|
* @return The float value of the entry.
|
||||||
|
* @throws IllegalArgumentException If the key doesn't exist.
|
||||||
*/
|
*/
|
||||||
public Float getFloat(String key) {
|
public float getFloat(String key) {
|
||||||
return (Float) getValueExact(key).getValue();
|
return (Float) getValueExact(key).getValue();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Retrieve the float value of an existing entry, or from a new default entry if it doesn't exist.
|
||||||
|
* @param key - the key of the entry.
|
||||||
|
* @return The value that was retrieved or just created.
|
||||||
|
*/
|
||||||
|
public float getFloatOrDefault(String key) {
|
||||||
|
return (Float) getValueOrDefault(key, NbtType.TAG_FLOAT).getValue();
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Associate a NBT float value with the given key.
|
* Associate a NBT float value with the given key.
|
||||||
* @param key - the key and NBT name.
|
* @param key - the key and NBT name.
|
||||||
@ -279,11 +367,21 @@ public class NbtCompound implements NbtWrapper<Map<String, NbtBase<?>>>, Iterabl
|
|||||||
* Retrieve the double value of an entry identified by a given key.
|
* Retrieve the double value of an entry identified by a given key.
|
||||||
* @param key - the key of the entry.
|
* @param key - the key of the entry.
|
||||||
* @return The double value of the entry.
|
* @return The double value of the entry.
|
||||||
|
* @throws IllegalArgumentException If the key doesn't exist.
|
||||||
*/
|
*/
|
||||||
public Double getDouble(String key) {
|
public double getDouble(String key) {
|
||||||
return (Double) getValueExact(key).getValue();
|
return (Double) getValueExact(key).getValue();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Retrieve the double value of an existing entry, or from a new default entry if it doesn't exist.
|
||||||
|
* @param key - the key of the entry.
|
||||||
|
* @return The value that was retrieved or just created.
|
||||||
|
*/
|
||||||
|
public double getDoubleOrDefault(String key) {
|
||||||
|
return (Double) getValueOrDefault(key, NbtType.TAG_DOUBlE).getValue();
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Associate a NBT double value with the given key.
|
* Associate a NBT double value with the given key.
|
||||||
* @param key - the key and NBT name.
|
* @param key - the key and NBT name.
|
||||||
@ -299,6 +397,7 @@ public class NbtCompound implements NbtWrapper<Map<String, NbtBase<?>>>, Iterabl
|
|||||||
* Retrieve the byte array value of an entry identified by a given key.
|
* Retrieve the byte array value of an entry identified by a given key.
|
||||||
* @param key - the key of the entry.
|
* @param key - the key of the entry.
|
||||||
* @return The byte array value of the entry.
|
* @return The byte array value of the entry.
|
||||||
|
* @throws IllegalArgumentException If the key doesn't exist.
|
||||||
*/
|
*/
|
||||||
public byte[] getByteArray(String key) {
|
public byte[] getByteArray(String key) {
|
||||||
return (byte[]) getValueExact(key).getValue();
|
return (byte[]) getValueExact(key).getValue();
|
||||||
@ -319,6 +418,7 @@ public class NbtCompound implements NbtWrapper<Map<String, NbtBase<?>>>, Iterabl
|
|||||||
* Retrieve the integer array value of an entry identified by a given key.
|
* Retrieve the integer array value of an entry identified by a given key.
|
||||||
* @param key - the key of the entry.
|
* @param key - the key of the entry.
|
||||||
* @return The integer array value of the entry.
|
* @return The integer array value of the entry.
|
||||||
|
* @throws IllegalArgumentException If the key doesn't exist.
|
||||||
*/
|
*/
|
||||||
public int[] getIntegerArray(String key) {
|
public int[] getIntegerArray(String key) {
|
||||||
return (int[]) getValueExact(key).getValue();
|
return (int[]) getValueExact(key).getValue();
|
||||||
@ -339,12 +439,22 @@ public class NbtCompound implements NbtWrapper<Map<String, NbtBase<?>>>, Iterabl
|
|||||||
* Retrieve the compound (map) value of an entry identified by a given key.
|
* Retrieve the compound (map) value of an entry identified by a given key.
|
||||||
* @param key - the key of the entry.
|
* @param key - the key of the entry.
|
||||||
* @return The compound value of the entry.
|
* @return The compound value of the entry.
|
||||||
|
* @throws IllegalArgumentException If the key doesn't exist.
|
||||||
*/
|
*/
|
||||||
@SuppressWarnings("rawtypes")
|
@SuppressWarnings("rawtypes")
|
||||||
public NbtCompound getCompound(String key) {
|
public NbtCompound getCompound(String key) {
|
||||||
return (NbtCompound) ((NbtBase) getValueExact(key));
|
return (NbtCompound) ((NbtBase) getValueExact(key));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Retrieve a compound (map) value by its key, or create a new compound if it doesn't exist.
|
||||||
|
* @param key - the key of the entry to find or create.
|
||||||
|
* @return The compound value that was retrieved or just created.
|
||||||
|
*/
|
||||||
|
public NbtCompound getCompoundOrDefault(String key) {
|
||||||
|
return (NbtCompound) getValueOrDefault(key, NbtType.TAG_COMPOUND);
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Associate a NBT compound with its name as key.
|
* Associate a NBT compound with its name as key.
|
||||||
* @param compound - the compound value.
|
* @param compound - the compound value.
|
||||||
@ -359,12 +469,23 @@ public class NbtCompound implements NbtWrapper<Map<String, NbtBase<?>>>, Iterabl
|
|||||||
* Retrieve the NBT list value of an entry identified by a given key.
|
* Retrieve the NBT list value of an entry identified by a given key.
|
||||||
* @param key - the key of the entry.
|
* @param key - the key of the entry.
|
||||||
* @return The NBT list value of the entry.
|
* @return The NBT list value of the entry.
|
||||||
|
* @throws IllegalArgumentException If the key doesn't exist.
|
||||||
*/
|
*/
|
||||||
@SuppressWarnings({"unchecked", "rawtypes"})
|
@SuppressWarnings({"unchecked", "rawtypes"})
|
||||||
public <T> NbtList<T> getList(String key) {
|
public <T> NbtList<T> getList(String key) {
|
||||||
return (NbtList) getValueExact(key);
|
return (NbtList) getValueExact(key);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Retrieve a NBT list value by its key, or create a new list if it doesn't exist.
|
||||||
|
* @param key - the key of the entry to find or create.
|
||||||
|
* @return The compound value that was retrieved or just created.
|
||||||
|
*/
|
||||||
|
@SuppressWarnings("unchecked")
|
||||||
|
public <T> NbtList<T> getListOrDefault(String key) {
|
||||||
|
return (NbtList<T>) getValueOrDefault(key, NbtType.TAG_LIST);
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Associate a NBT list with the given key.
|
* Associate a NBT list with the given key.
|
||||||
* @param list - the list value.
|
* @param list - the list value.
|
||||||
|
@ -7,9 +7,13 @@ import java.util.Collection;
|
|||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
|
|
||||||
|
import org.bukkit.inventory.ItemStack;
|
||||||
|
|
||||||
import com.comphenix.protocol.reflect.FieldAccessException;
|
import com.comphenix.protocol.reflect.FieldAccessException;
|
||||||
import com.comphenix.protocol.reflect.FuzzyReflection;
|
import com.comphenix.protocol.reflect.FuzzyReflection;
|
||||||
|
import com.comphenix.protocol.reflect.StructureModifier;
|
||||||
import com.comphenix.protocol.utility.MinecraftReflection;
|
import com.comphenix.protocol.utility.MinecraftReflection;
|
||||||
|
import com.comphenix.protocol.wrappers.BukkitConverters;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Factory methods for creating NBT elements, lists and compounds.
|
* Factory methods for creating NBT elements, lists and compounds.
|
||||||
@ -24,6 +28,39 @@ public class NbtFactory {
|
|||||||
private static Method methodWrite;
|
private static Method methodWrite;
|
||||||
private static Method methodLoad;
|
private static Method methodLoad;
|
||||||
|
|
||||||
|
// Item stack trickery
|
||||||
|
private static StructureModifier<Object> itemStackModifier;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Attempt to cast this wrapper as a compund.
|
||||||
|
* @return This instance as a compound.
|
||||||
|
* @throws UnsupportedOperationException If this is not a compound.
|
||||||
|
*/
|
||||||
|
public static NbtCompound asCompound(NbtWrapper<?> wrapper) {
|
||||||
|
if (wrapper instanceof NbtCompound)
|
||||||
|
return (NbtCompound) wrapper;
|
||||||
|
else if (wrapper != null)
|
||||||
|
throw new UnsupportedOperationException(
|
||||||
|
"Cannot cast a " + wrapper.getClass() + "( " + wrapper.getType() + ") to TAG_COMPUND.");
|
||||||
|
else
|
||||||
|
throw new IllegalArgumentException("Wrapper cannot be NULL.");
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Attempt to cast this wrapper as a list.
|
||||||
|
* @return This instance as a list.
|
||||||
|
* @throws UnsupportedOperationException If this is not a list.
|
||||||
|
*/
|
||||||
|
public static NbtList<?> asList(NbtWrapper<?> wrapper) {
|
||||||
|
if (wrapper instanceof NbtList)
|
||||||
|
return (NbtList<?>) wrapper;
|
||||||
|
else if (wrapper != null)
|
||||||
|
throw new UnsupportedOperationException(
|
||||||
|
"Cannot cast a " + wrapper.getClass() + "( " + wrapper.getType() + ") to TAG_LIST.");
|
||||||
|
else
|
||||||
|
throw new IllegalArgumentException("Wrapper cannot be NULL.");
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Get a NBT wrapper from a NBT base.
|
* Get a NBT wrapper from a NBT base.
|
||||||
* @param base - the base class.
|
* @param base - the base class.
|
||||||
@ -63,6 +100,38 @@ public class NbtFactory {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Construct a wrapper for an NBT tag stored (in memory) in an item stack.
|
||||||
|
* <p>
|
||||||
|
* The item stack must be a wrapper for a CraftItemStack. Use
|
||||||
|
* {@link MinecraftReflection#getBukkitItemStack(ItemStack)} if not.
|
||||||
|
* @param stack - the item stack.
|
||||||
|
* @return A wrapper for its NBT tag.
|
||||||
|
*/
|
||||||
|
public static NbtWrapper<?> fromItemStack(ItemStack stack) {
|
||||||
|
if (!MinecraftReflection.isCraftItemStack(stack))
|
||||||
|
throw new IllegalArgumentException("Stack must be a CraftItemStack.");
|
||||||
|
|
||||||
|
Object nmsStack = MinecraftReflection.getMinecraftItemStack(stack);
|
||||||
|
|
||||||
|
if (itemStackModifier == null) {
|
||||||
|
itemStackModifier = new StructureModifier<Object>(nmsStack.getClass(), Object.class, false);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Use the first and best NBT tag
|
||||||
|
StructureModifier<NbtWrapper<?>> modifier = itemStackModifier.
|
||||||
|
withTarget(nmsStack).
|
||||||
|
withType(MinecraftReflection.getNBTBaseClass(), BukkitConverters.getNbtConverter());
|
||||||
|
NbtWrapper<?> result = modifier.read(0);
|
||||||
|
|
||||||
|
// Create the tag if it doesn't exist
|
||||||
|
if (result == null) {
|
||||||
|
result = NbtFactory.ofCompound("tag");
|
||||||
|
modifier.write(0, result);
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Initialize a NBT wrapper.
|
* Initialize a NBT wrapper.
|
||||||
* @param handle - the underlying net.minecraft.server object to wrap.
|
* @param handle - the underlying net.minecraft.server object to wrap.
|
||||||
|
@ -300,6 +300,7 @@ public class NbtList<TType> implements NbtWrapper<List<NbtBase<TType>>>, Iterabl
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public String toString() {
|
public String toString() {
|
||||||
|
// Essentially JSON
|
||||||
StringBuilder builder = new StringBuilder();
|
StringBuilder builder = new StringBuilder();
|
||||||
|
|
||||||
builder.append("{\"name\": \"" + getName() + "\", \"value\": [");
|
builder.append("{\"name\": \"" + getName() + "\", \"value\": [");
|
||||||
|
In neuem Issue referenzieren
Einen Benutzer sperren