Merge pull request '1.19' (#353) from 1.19 into master
Alle Prüfungen waren erfolgreich
SteamWarCI Build successful
Alle Prüfungen waren erfolgreich
SteamWarCI Build successful
Reviewed-on: #353 Reviewed-by: YoyoNow <jwsteam@nidido.de>
Dieser Commit ist enthalten in:
Commit
58b8d1eb69
@ -55,7 +55,7 @@ public class TechHider18 implements TechHider.ChunkHider {
|
||||
private static final Reflection.FieldAccessor<byte[]> dataField = Reflection.getField(ClientboundLevelChunkPacketData.class, byte[].class, 0);
|
||||
private static final Reflection.FieldAccessor<List> tileEntities = Reflection.getField(ClientboundLevelChunkPacketData.class, List.class, 0);
|
||||
public static final Class<?> tileEntity = Reflection.getClass("net.minecraft.network.protocol.game.ClientboundLevelChunkPacketData$a");
|
||||
private static final Reflection.FieldAccessor<TileEntityTypes> entityType = Reflection.getField(tileEntity, TileEntityTypes.class, 0);
|
||||
protected static final Reflection.FieldAccessor<TileEntityTypes> entityType = Reflection.getField(tileEntity, TileEntityTypes.class, 0);
|
||||
|
||||
private final Set<Integer> hiddenBlockIds = BlockIdWrapper.impl.getHiddenBlockIds();
|
||||
private final int obfuscateWith = BlockIdWrapper.impl.getObfuscateWith();
|
||||
@ -68,9 +68,7 @@ public class TechHider18 implements TechHider.ChunkHider {
|
||||
packet = chunkPacketCloner.apply(packet);
|
||||
Object data = chunkDataCloner.apply(chunkData.get(packet));
|
||||
|
||||
tileEntities.set(data, ((List<?>)tileEntities.get(data)).stream().filter(
|
||||
tile -> Config.HiddenBlockEntities.contains(IRegistry.aa.b(entityType.get(tile)).a())
|
||||
).collect(Collectors.toList()));
|
||||
tileEntities.set(data, ((List<?>)tileEntities.get(data)).stream().filter(this::tileEntityVisible).collect(Collectors.toList()));
|
||||
|
||||
World world = p.getWorld();
|
||||
int sections = (world.getMaxHeight() - world.getMinHeight()) / 16;
|
||||
@ -80,6 +78,10 @@ public class TechHider18 implements TechHider.ChunkHider {
|
||||
return packet;
|
||||
}
|
||||
|
||||
protected boolean tileEntityVisible(Object tile) {
|
||||
return !Config.HiddenBlockEntities.contains(IRegistry.aa.b(entityType.get(tile)).a());
|
||||
}
|
||||
|
||||
private byte[] dataHider(byte[] data, int sections) {
|
||||
ByteBuf buffer = UnpooledByteBufAllocator.DEFAULT.directBuffer(data.length + 100);
|
||||
int i = 0;
|
||||
|
89
FightSystem_19/pom.xml
Normale Datei
89
FightSystem_19/pom.xml
Normale Datei
@ -0,0 +1,89 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<!--
|
||||
This file is a part of the SteamWar software.
|
||||
|
||||
Copyright (C) 2021 SteamWar.de-Serverteam
|
||||
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU Affero General Public License as published by
|
||||
the Free Software Foundation, either version 3 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 Affero General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU Affero General Public License
|
||||
along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
-->
|
||||
|
||||
<project xmlns="http://maven.apache.org/POM/4.0.0"
|
||||
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
|
||||
<parent>
|
||||
<groupId>steamwar</groupId>
|
||||
<artifactId>FightSystem</artifactId>
|
||||
<version>1.0</version>
|
||||
</parent>
|
||||
|
||||
<properties>
|
||||
<main.basedir>${project.basedir}/..</main.basedir>
|
||||
</properties>
|
||||
|
||||
<artifactId>FightSystem_19</artifactId>
|
||||
<version>1.0</version>
|
||||
|
||||
<dependencies>
|
||||
<dependency>
|
||||
<groupId>org.spigotmc</groupId>
|
||||
<artifactId>spigot-api</artifactId>
|
||||
<version>1.19-R0.1-SNAPSHOT</version>
|
||||
<scope>provided</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>com.mojang</groupId>
|
||||
<artifactId>datafixerupper</artifactId>
|
||||
<version>4.0.26</version>
|
||||
<scope>provided</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>io.netty</groupId>
|
||||
<artifactId>netty-all</artifactId>
|
||||
<version>4.1.68.Final</version>
|
||||
<scope>provided</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>com.mojang</groupId>
|
||||
<artifactId>authlib</artifactId>
|
||||
<version>1.5.25</version>
|
||||
<scope>provided</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>steamwar</groupId>
|
||||
<artifactId>Spigot</artifactId>
|
||||
<version>1.19</version>
|
||||
<scope>system</scope>
|
||||
<systemPath>${main.basedir}/lib/Spigot-1.19.jar</systemPath>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>steamwar</groupId>
|
||||
<artifactId>WorldEdit</artifactId>
|
||||
<version>1.0</version>
|
||||
<scope>system</scope>
|
||||
<systemPath>${main.basedir}/lib/WorldEdit-1.15.jar</systemPath>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>steamwar</groupId>
|
||||
<artifactId>FightSystem_Core</artifactId>
|
||||
<version>1.0</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>steamwar</groupId>
|
||||
<artifactId>FightSystem_18</artifactId>
|
||||
<version>1.0</version>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
</project>
|
@ -0,0 +1,87 @@
|
||||
/*
|
||||
This file is a part of the SteamWar software.
|
||||
|
||||
Copyright (C) 2021 SteamWar.de-Serverteam
|
||||
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU Affero General Public License as published by
|
||||
the Free Software Foundation, either version 3 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 Affero General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU Affero General Public License
|
||||
along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package de.steamwar.fightsystem.utils;
|
||||
|
||||
import de.steamwar.fightsystem.Config;
|
||||
import net.minecraft.core.BlockPosition;
|
||||
import net.minecraft.core.IRegistry;
|
||||
import net.minecraft.resources.MinecraftKey;
|
||||
import net.minecraft.server.level.WorldServer;
|
||||
import net.minecraft.world.entity.EntityPose;
|
||||
import net.minecraft.world.level.block.state.IBlockData;
|
||||
import net.minecraft.world.level.material.Fluid;
|
||||
import net.minecraft.world.level.material.FluidTypes;
|
||||
import org.bukkit.World;
|
||||
import org.bukkit.block.Block;
|
||||
import org.bukkit.craftbukkit.v1_19_R1.CraftWorld;
|
||||
import org.bukkit.craftbukkit.v1_19_R1.block.CraftBlock;
|
||||
|
||||
import java.util.HashSet;
|
||||
import java.util.Objects;
|
||||
import java.util.Set;
|
||||
|
||||
public class BlockIdWrapper19 implements BlockIdWrapper {
|
||||
|
||||
@Override
|
||||
public int blockToId(Block block) {
|
||||
return net.minecraft.world.level.block.Block.i(((CraftBlock)block).getNMS());
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setBlock(World world, int x, int y, int z, int blockState) {
|
||||
IBlockData blockData = Objects.requireNonNull(net.minecraft.world.level.block.Block.a(blockState));
|
||||
WorldServer cworld = ((CraftWorld)world).getHandle();
|
||||
BlockPosition pos = new BlockPosition(x, y, z);
|
||||
cworld.m(pos);
|
||||
cworld.a(pos, blockData, 1042);
|
||||
cworld.k().a(pos);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Set<Integer> getHiddenBlockIds() {
|
||||
Set<Integer> hiddenBlockIds = new HashSet<>();
|
||||
for(String tag : Config.HiddenBlocks){
|
||||
for(IBlockData data : IRegistry.V.a(new MinecraftKey(tag)).k().a()){
|
||||
hiddenBlockIds.add(net.minecraft.world.level.block.Block.i(data));
|
||||
}
|
||||
}
|
||||
|
||||
if(Config.HiddenBlocks.contains("water")){
|
||||
Fluid water = FluidTypes.c.h();
|
||||
for(IBlockData data : net.minecraft.world.level.block.Block.o) {
|
||||
if(data.p() == water) {
|
||||
hiddenBlockIds.add(net.minecraft.world.level.block.Block.i(data));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return hiddenBlockIds;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getObfuscateWith() { //ResourceLocation, DefaultedRegistry
|
||||
return net.minecraft.world.level.block.Block.i(IRegistry.V.a(new MinecraftKey(Config.ObfuscateWith)).m());
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object getPose(boolean sneaking) {
|
||||
return sneaking ? EntityPose.f : EntityPose.a;
|
||||
}
|
||||
}
|
@ -0,0 +1,71 @@
|
||||
/*
|
||||
This file is a part of the SteamWar software.
|
||||
|
||||
Copyright (C) 2021 SteamWar.de-Serverteam
|
||||
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU Affero General Public License as published by
|
||||
the Free Software Foundation, either version 3 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 Affero General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU Affero General Public License
|
||||
along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package de.steamwar.fightsystem.utils;
|
||||
|
||||
import de.steamwar.fightsystem.Config;
|
||||
import net.minecraft.world.level.chunk.Chunk;
|
||||
import org.bukkit.World;
|
||||
import org.bukkit.craftbukkit.v1_19_R1.CraftWorld;
|
||||
import org.bukkit.craftbukkit.v1_19_R1.entity.CraftEntity;
|
||||
import org.bukkit.craftbukkit.v1_19_R1.entity.CraftPlayer;
|
||||
import org.bukkit.craftbukkit.v1_19_R1.inventory.CraftItemStack;
|
||||
import org.bukkit.entity.Entity;
|
||||
import org.bukkit.entity.Player;
|
||||
import org.bukkit.inventory.ItemStack;
|
||||
|
||||
import java.util.HashSet;
|
||||
import java.util.Set;
|
||||
import java.util.stream.Stream;
|
||||
import java.util.stream.StreamSupport;
|
||||
|
||||
public class CraftbukkitWrapper19 implements CraftbukkitWrapper {
|
||||
|
||||
@Override
|
||||
public void resetChunk(World world, World backup, int x, int z) {
|
||||
net.minecraft.world.level.World w = ((CraftWorld) world).getHandle();
|
||||
Chunk chunk = w.d(x, z);
|
||||
Chunk backupChunk = ((CraftWorld) backup).getHandle().d(x, z);
|
||||
|
||||
System.arraycopy(backupChunk.d(), 0, chunk.d(), 0, chunk.d().length);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void sendResourcePack(Player player, String pack, String sha1) {
|
||||
((CraftPlayer)player).getHandle().a(pack, sha1, true, null);
|
||||
}
|
||||
|
||||
@Override
|
||||
public float headRotation(Entity e) {
|
||||
return ((CraftEntity)e).getHandle().ch();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean hasItems(ItemStack stack) {
|
||||
Set<String> keys = new HashSet<>(CraftItemStack.asNMSCopy(stack).u().d());
|
||||
keys.remove("Enchantments");
|
||||
keys.remove("Damage");
|
||||
return !keys.isEmpty();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Stream<?> entityIterator() {
|
||||
return StreamSupport.stream(((CraftWorld) Config.world).getHandle().F().a().spliterator(), false);
|
||||
}
|
||||
}
|
120
FightSystem_19/src/de/steamwar/fightsystem/utils/ProtocolWrapper19.java
Normale Datei
120
FightSystem_19/src/de/steamwar/fightsystem/utils/ProtocolWrapper19.java
Normale Datei
@ -0,0 +1,120 @@
|
||||
/*
|
||||
This file is a part of the SteamWar software.
|
||||
|
||||
Copyright (C) 2021 SteamWar.de-Serverteam
|
||||
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU Affero General Public License as published by
|
||||
the Free Software Foundation, either version 3 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 Affero General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU Affero General Public License
|
||||
along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package de.steamwar.fightsystem.utils;
|
||||
|
||||
import com.comphenix.tinyprotocol.Reflection;
|
||||
import com.mojang.authlib.GameProfile;
|
||||
import com.mojang.datafixers.util.Pair;
|
||||
import de.steamwar.fightsystem.Config;
|
||||
import de.steamwar.fightsystem.fight.Fight;
|
||||
import de.steamwar.fightsystem.record.REntity;
|
||||
import net.minecraft.core.IRegistry;
|
||||
import net.minecraft.core.SectionPosition;
|
||||
import net.minecraft.world.entity.EntityTypes;
|
||||
import net.minecraft.world.level.block.entity.TileEntityTypes;
|
||||
import net.minecraft.world.level.block.state.IBlockData;
|
||||
import org.bukkit.entity.EntityType;
|
||||
import org.bukkit.entity.Player;
|
||||
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
import java.util.function.BiFunction;
|
||||
import java.util.function.UnaryOperator;
|
||||
|
||||
public class ProtocolWrapper19 implements ProtocolWrapper {
|
||||
|
||||
private static final Reflection.FieldAccessor<List> equipmentStack = Reflection.getField(REntity.equipmentPacket, List.class, 0);
|
||||
@Override
|
||||
public void setEquipmentPacketStack(Object packet, String slot, Object stack) {
|
||||
equipmentStack.set(packet, Collections.singletonList(new Pair<>(getSlot(slot), stack)));
|
||||
}
|
||||
|
||||
private static final Reflection.FieldAccessor<?> spawnType = Reflection.getField(REntity.spawnPacket, EntityTypes.class, 0);
|
||||
@Override
|
||||
public void setSpawnPacketType(Object packet, EntityType type) {
|
||||
switch(type) {
|
||||
case PRIMED_TNT:
|
||||
spawnType.set(packet, EntityTypes.as);
|
||||
break;
|
||||
case ARROW:
|
||||
spawnType.set(packet, EntityTypes.d);
|
||||
break;
|
||||
case FIREBALL:
|
||||
spawnType.set(packet, EntityTypes.S);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
private static final Class<?> enumItemSlot = Reflection.getClass("{nms.world.entity}.EnumItemSlot");
|
||||
private static final Object[] itemSlots = enumItemSlot.getEnumConstants();
|
||||
private static Object getSlot(String slot) {
|
||||
switch(slot){
|
||||
case "HEAD":
|
||||
return itemSlots[5];
|
||||
case "CHEST":
|
||||
return itemSlots[4];
|
||||
case "LEGS":
|
||||
return itemSlots[3];
|
||||
case "FEET":
|
||||
return itemSlots[2];
|
||||
case "OFFHAND":
|
||||
return itemSlots[1];
|
||||
case "MAINHAND":
|
||||
default:
|
||||
return itemSlots[0];
|
||||
}
|
||||
}
|
||||
|
||||
private static final Reflection.FieldAccessor<SectionPosition> multiBlockChangeChunk = Reflection.getField(TechHider.multiBlockChangePacket, SectionPosition.class, 0);
|
||||
private static final Reflection.FieldAccessor<IBlockData[]> multiBlockChangeBlocks = Reflection.getField(TechHider.multiBlockChangePacket, IBlockData[].class, 0);
|
||||
private static final BiFunction<Object, UnaryOperator<Object>, Object> iBlockDataArrayCloner = ProtocolAPI.arrayCloneGenerator(TechHider.iBlockData);
|
||||
@Override
|
||||
public Object multiBlockChangeHider(Player p, Object packet) {
|
||||
Object chunkCoords = multiBlockChangeChunk.get(packet);
|
||||
if(TechHider.bypass(p, TechHider.blockPositionX.get(chunkCoords), TechHider.blockPositionZ.get(chunkCoords)))
|
||||
return packet;
|
||||
|
||||
packet = TechHider.multiBlockChangeCloner.apply(packet);
|
||||
multiBlockChangeBlocks.set(packet, iBlockDataArrayCloner.apply(multiBlockChangeBlocks.get(packet), block -> ProtocolWrapper.impl.iBlockDataHidden(block) ? TechHider.obfuscateIBlockData : block));
|
||||
return packet;
|
||||
}
|
||||
|
||||
private static final Reflection.FieldAccessor<TileEntityTypes> tileEntityType = Reflection.getField(TechHider.tileEntityDataPacket, TileEntityTypes.class, 0);
|
||||
@Override
|
||||
public boolean unfilteredTileEntityDataAction(Object packet) {
|
||||
return tileEntityType.get(packet) != TileEntityTypes.h;
|
||||
}
|
||||
|
||||
@Override
|
||||
public BiFunction<Player, Object, Object> blockBreakHiderGenerator(Class<?> blockBreakPacket) {
|
||||
return null;
|
||||
}
|
||||
|
||||
private static final Reflection.ConstructorInvoker playerInfoDataConstructor = Reflection.getConstructor(Fight.playerInfoDataClass, GameProfile.class, int.class, Fight.enumGamemode, Fight.iChatBaseComponent, Reflection.getClass("net.minecraft.world.entity.player.ProfilePublicKey$a"));
|
||||
@Override
|
||||
public Object playerInfoDataConstructor(Object packet, GameProfile profile, Object mode) {
|
||||
return playerInfoDataConstructor.invoke(profile, 0, mode, null, null);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean iBlockDataHidden(Object iBlockData) {
|
||||
return Config.HiddenBlocks.contains(IRegistry.V.b(((IBlockData) iBlockData).b()).a());
|
||||
}
|
||||
}
|
31
FightSystem_19/src/de/steamwar/fightsystem/utils/TechHider19.java
Normale Datei
31
FightSystem_19/src/de/steamwar/fightsystem/utils/TechHider19.java
Normale Datei
@ -0,0 +1,31 @@
|
||||
/*
|
||||
This file is a part of the SteamWar software.
|
||||
|
||||
Copyright (C) 2021 SteamWar.de-Serverteam
|
||||
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU Affero General Public License as published by
|
||||
the Free Software Foundation, either version 3 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 Affero General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU Affero General Public License
|
||||
along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package de.steamwar.fightsystem.utils;
|
||||
|
||||
import de.steamwar.fightsystem.Config;
|
||||
import net.minecraft.core.IRegistry;
|
||||
|
||||
public class TechHider19 extends TechHider18 {
|
||||
|
||||
@Override
|
||||
protected boolean tileEntityVisible(Object tile) {
|
||||
return !Config.HiddenBlockEntities.contains(IRegistry.ab.b(entityType.get(tile)).a());
|
||||
}
|
||||
}
|
@ -21,7 +21,7 @@
|
||||
<dependency>
|
||||
<groupId>org.spigotmc</groupId>
|
||||
<artifactId>spigot-api</artifactId>
|
||||
<version>1.18-R0.1-SNAPSHOT</version>
|
||||
<version>1.19-R0.1-SNAPSHOT</version>
|
||||
<scope>provided</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
|
@ -75,7 +75,7 @@ public class TechHider extends StateDependent {
|
||||
ChunkHider chunkHider = VersionDependent.getVersionImpl(FightSystem.getPlugin());
|
||||
techhiders.put(chunkHider.mapChunkPacket(), chunkHider::mapChunkHider);
|
||||
|
||||
if(Core.getVersion() > 12) {
|
||||
if(Core.getVersion() > 12 && Core.getVersion() < 19) {
|
||||
Class<?> blockBreakClass = Reflection.getClass("{nms.network.protocol.game}.PacketPlayOutBlockBreak");
|
||||
techhiders.put(blockBreakClass, ProtocolWrapper.impl.blockBreakHiderGenerator(blockBreakClass));
|
||||
}
|
||||
|
@ -98,6 +98,12 @@
|
||||
<version>1.0</version>
|
||||
<scope>compile</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>steamwar</groupId>
|
||||
<artifactId>FightSystem_19</artifactId>
|
||||
<version>1.0</version>
|
||||
<scope>compile</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>steamwar</groupId>
|
||||
<artifactId>FightSystem_Core</artifactId>
|
||||
|
1
pom.xml
1
pom.xml
@ -58,6 +58,7 @@
|
||||
<module>FightSystem_14</module>
|
||||
<module>FightSystem_15</module>
|
||||
<module>FightSystem_18</module>
|
||||
<module>FightSystem_19</module>
|
||||
<module>FightSystem_Core</module>
|
||||
<module>FightSystem_Main</module>
|
||||
<module>FightSystem_Standalone</module>
|
||||
|
In neuem Issue referenzieren
Einen Benutzer sperren