Signed-off-by: Lixfel <git-5w3l@lixfel.de>
Dieser Commit ist enthalten in:
Ursprung
7deaed4961
Commit
b3a987f9a5
11
build.gradle
11
build.gradle
@ -76,11 +76,14 @@ allprojects {
|
||||
|
||||
shadowJar {
|
||||
exclude 'META-INF/*'
|
||||
exclude 'org/sqlite/native/FreeBSD/**', 'org/sqlite/native/Mac/**', 'org/sqlite/native/Windows/**', 'org/sqlite/native/Linux-Android/**', 'org/sqlite/native/Linux-Musl/**'
|
||||
exclude 'org/sqlite/native/Linux/aarch64/**', 'org/sqlite/native/Linux/arm/**', 'org/sqlite/native/Linux/armv6/**', 'org/sqlite/native/Linux/armv7/**', 'org/sqlite/native/Linux/ppc64/**', 'org/sqlite/native/Linux/x86/**'
|
||||
exclude 'org/slf4j/**'
|
||||
//https://imperceptiblethoughts.com/shadow/configuration/minimizing/
|
||||
/*minimize {
|
||||
minimize {
|
||||
exclude project(':')
|
||||
exclude 'mysql:mysql-connector-java:8.0.33'
|
||||
}*/
|
||||
exclude dependency('mysql:mysql-connector-java:.*')
|
||||
}
|
||||
duplicatesStrategy DuplicatesStrategy.INCLUDE
|
||||
}
|
||||
|
||||
@ -96,7 +99,7 @@ dependencies {
|
||||
compileOnly project(":Persistent")
|
||||
implementation project(":CommonCore")
|
||||
|
||||
runtimeOnly 'org.xerial:sqlite-jdbc:3.36.0' //TODO native only linux
|
||||
implementation 'org.xerial:sqlite-jdbc:3.46.0.0'
|
||||
implementation 'mysql:mysql-connector-java:8.0.33'
|
||||
|
||||
implementation("net.dv8tion:JDA:4.4.0_352") {
|
||||
|
@ -62,22 +62,6 @@ public class Config {
|
||||
|
||||
public static Config load() {
|
||||
return load(Config.class, new File(VelocityCore.get().getDataDirectory().toFile(), "config.yml"), description -> description.addPropertyParameters("servers", String.class, Config.Server.class));
|
||||
|
||||
/*
|
||||
|
||||
typeDescription.addPropertyParameters("servers", String.class, Config.Server.class);
|
||||
Constructor constructor = new Constructor(Config.class, new LoaderOptions());
|
||||
constructor.addTypeDescription(typeDescription);
|
||||
|
||||
Yaml yaml = new Yaml(constructor);
|
||||
yaml.setBeanAccess(BeanAccess.FIELD);
|
||||
|
||||
try{
|
||||
return yaml.load(new FileInputStream(new File(VelocityCore.get().getDataDirectory().toFile(), "config.yml")));
|
||||
}catch(IOException e){
|
||||
VelocityCore.getProxy().shutdown();
|
||||
throw new SecurityException("Could not load config.yml", e);
|
||||
}*/
|
||||
}
|
||||
|
||||
private String lobbyserver;
|
||||
|
@ -20,7 +20,6 @@
|
||||
package de.steamwar.velocitycore;
|
||||
|
||||
import com.google.inject.Inject;
|
||||
import com.mysql.cj.jdbc.Driver;
|
||||
import com.velocitypowered.api.event.Subscribe;
|
||||
import com.velocitypowered.api.event.proxy.ProxyInitializeEvent;
|
||||
import com.velocitypowered.api.event.proxy.ProxyShutdownEvent;
|
||||
@ -48,6 +47,7 @@ import de.steamwar.velocitycore.tablist.TablistManager;
|
||||
import lombok.Getter;
|
||||
|
||||
import java.nio.file.Path;
|
||||
import java.sql.Driver;
|
||||
import java.sql.DriverManager;
|
||||
import java.sql.SQLException;
|
||||
import java.util.Collection;
|
||||
@ -90,7 +90,7 @@ public class VelocityCore implements ReloadablePlugin {
|
||||
@Getter
|
||||
private final Path dataDirectory;
|
||||
|
||||
private Driver mysqlDriver;
|
||||
private Driver sqlDriver;
|
||||
@Getter
|
||||
private Config config;
|
||||
private ErrorLogger errorLogger;
|
||||
@ -111,20 +111,19 @@ public class VelocityCore implements ReloadablePlugin {
|
||||
@Override
|
||||
public void onProxyInitialization(ProxyInitializeEvent event) {
|
||||
instance = this;
|
||||
config = Config.load();
|
||||
MAIN_SERVER = proxyServer.getBoundAddress().getPort() == 25565;
|
||||
|
||||
try {
|
||||
mysqlDriver = new com.mysql.cj.jdbc.Driver();
|
||||
DriverManager.registerDriver(mysqlDriver);
|
||||
sqlDriver = Statement.mysqlMode() ? new com.mysql.cj.jdbc.Driver() : new org.sqlite.JDBC();
|
||||
DriverManager.registerDriver(sqlDriver);
|
||||
} catch (SQLException e) {
|
||||
throw new SecurityException(e);
|
||||
}
|
||||
|
||||
config = Config.load();
|
||||
MAIN_SERVER = proxyServer.getBoundAddress().getPort() == 25565;
|
||||
|
||||
errorLogger = new ErrorLogger();
|
||||
|
||||
SWCommandUtils.init((SWTypeMapperCreator<TypeMapper<Object>, Chatter, Object>) (mapper, tabCompleter) -> new TypeMapper<Object>() {
|
||||
SWCommandUtils.init((SWTypeMapperCreator<TypeMapper<Object>, Chatter, Object>) (mapper, tabCompleter) -> new TypeMapper<>() {
|
||||
@Override
|
||||
public Object map(Chatter sender, PreviousArguments previousArguments, String s) {
|
||||
return mapper.apply(s);
|
||||
@ -139,7 +138,6 @@ public class VelocityCore implements ReloadablePlugin {
|
||||
|
||||
initStaticServers();
|
||||
PollSystem.init();
|
||||
ServerListPing.init();
|
||||
|
||||
new Hostname();
|
||||
new PluginMessage();
|
||||
@ -263,7 +261,7 @@ public class VelocityCore implements ReloadablePlugin {
|
||||
Statement.closeAll();
|
||||
|
||||
try {
|
||||
DriverManager.deregisterDriver(mysqlDriver);
|
||||
DriverManager.deregisterDriver(sqlDriver);
|
||||
} catch (SQLException e) {
|
||||
throw new SecurityException(e);
|
||||
}
|
||||
|
@ -110,7 +110,7 @@ public class PluginMessage extends BasicListener {
|
||||
"forge:tier_sorting", "forge:split", "forge:login", "forge:handshake",
|
||||
|
||||
"labymod3:main", "labymod:neo",
|
||||
|
||||
"feather:client/frag",
|
||||
Alpine.HANDSHAKE, Alpine.PLAY,
|
||||
|
||||
"sw:hotkeys",
|
||||
@ -123,16 +123,12 @@ public class PluginMessage extends BasicListener {
|
||||
"servux:structures", //https://modrinth.com/mod/servux
|
||||
"architectury:spawn_entity_packet", //https://modrinth.com/mod/architectury-api
|
||||
"jei:channel", "jei:cheat_permission", //https://modrinth.com/mod/jei
|
||||
"shulkerboxtooltip:s2c_handshake", "shulkerboxtooltip:ec_update", //https://modrinth.com/mod/shulkerboxtooltip
|
||||
"owo:local_packet", "owo:sync_screen_handler_properties", //https://modrinth.com/mod/owo-lib
|
||||
"libgui:screen_message_s2c", //https://github.com/CottonMC/LibGui
|
||||
"minecraft:intave", //https://intave.ac seems to be a client side integration of intave with labymod 4
|
||||
"midnightcontrols:feature", "midnightcontrols:controls_mode", //https://modrinth.com/mod/midnightcontrols
|
||||
"controlify:vibrate_from_origin", "controlify:vibration", "controlify:vibrate_from_entity",
|
||||
"carpet:structures", //https://modrinth.com/mod/carpet
|
||||
"pickupnotifier:1/1", //https://modrinth.com/mod/pick-up-notifier
|
||||
"plasmo:voice/v2/installed", "plasmo:voice/v2", //https://modrinth.com/plugin/plasmo-voice (Voice chat)
|
||||
"whereisit:s2c_founditem", //https://modrinth.com/mod/where-is-it (needs server side component to work)
|
||||
"inventorysorter:sync_blacklist_packet", //https://github.com/cpw/inventorysorter (needs server side component to work)
|
||||
"emi:ping", "emi:command", "emi:chess", //https://github.com/emilyploszaj/emi/
|
||||
"fancymenu:execute_command", "fancymenu:packet_bridge", //https://github.com/Keksuccino/FancyMenu (Custom menus)
|
||||
@ -143,8 +139,112 @@ public class PluginMessage extends BasicListener {
|
||||
"essentialclient:chunkdebug", "essentialclient:clientscript", "essentialclient:gamerule", //https://github.com/senseiwells/EssentialClient (Carpet mod extension)
|
||||
"couplings:server_config", //https://github.com/ChloeDawn/Couplings (Opens/closes double doors/gates simultaneously)
|
||||
"yigd:grave_overview_s2c", "yigd:grave_selection_s2c", "yigd:player_selection_s2c", //https://github.com/B1n-ry/Youre-in-grave-danger (Adds new block - graves)
|
||||
"kiwi:sync_cosmetic", //github.com/Snownee/Kiwi (General purpose library)
|
||||
"bagofholding:1/0", //https://github.com/Fuzss/bagofholding (Adds new item - bag)
|
||||
"betterend:ritual_update", //https://github.com/quiqueck/BetterEnd (Dimension improvement)
|
||||
"calio:sync_data_object_registry", //https://github.com/apace100/calio (Data serialization library)
|
||||
"chunk_debug:batched_chunk_info", "chunk_debug:hello", //https://github.com/senseiwells/ChunkDebug (Serverside extension for EssentialClient)
|
||||
"chunky:border", //https://github.com/pop4959/Chunky (Serverside chunk generator)
|
||||
"commandblockide:edit_function", "commandblockide:update_function_command", //https://github.com/arm32x/command-block-ide (Command block interface)
|
||||
"configured:session_data", //https://github.com/MrCrayfish/Configured (Confiuration system)
|
||||
"corpse:default", //https://github.com/henkelmax/corpse/tree/master (Adds new entity - corpse)
|
||||
"craftingtweaks:hello", "craftingtweaks:sync_config", //https://github.com/TwelveIterationMods/CraftingTweaks (Additional Crafting UI)
|
||||
"create:main", //https://github.com/Creators-of-Create/Create (Additional redstone blocks mod)
|
||||
"dummmmmmy:0", "dummmmmmy:1", "dummmmmmy:2", //https://github.com/MehVahdJukaar/DuMmmMmmy (Adding dummy target item)
|
||||
"easyanvils:1/0", "easyanvils:1/2", //https://github.com/Fuzss/easyanvils (Anvil handling improvements)
|
||||
"easymagic:1/0", "easymagic.main:0", //https://github.com/Fuzss/easymagic (Enchantment table handling improvements)
|
||||
"enhancedvisuals:main0", "enhancedvisuals:main1", "enhancedvisuals:main2", //https://github.com/CreativeMD/EnhancedVisuals (Visual effects)
|
||||
"fallingtree:configuration-packet", //https://github.com/RakambdaOrg/FallingTree (Serverside tree cutting enhancements)
|
||||
"forgeconfigscreens:play/0", "forgeconfigscreens:play/1", //https://github.com/Fuzss/forgeconfigscreens (Config GUI)
|
||||
"fwaystones:void_totem_revive", "fwaystones:sync_player", "fwaystones:waystone_packet", //https://github.com/LordDeatHunter/FabricWaystones (Adds new block - waystone)
|
||||
"fzzy_config:sync_config_packet", //https://github.com/fzzyhmstrs/fconfig (Synchronizing configuation library)
|
||||
"graveyard:spawn_entity", //https://github.com/finallion/The-Graveyard-FORGE (Adding graveyard themed blocks, items and mobs)
|
||||
"immersive_weathering:0", //https://github.com/AstralOrdana/Immersive-Weathering (Adds additional random block transitions)
|
||||
"kiwi:sync_cosmetic", //github.com/Snownee/Kiwi (General purpose library)
|
||||
"libgui:screen_messag", "libgui:screen_message_s2c", //https://github.com/cottonmc/libgui (Ingame GUI library)
|
||||
"libjf-config-network", "libjf-config-network-v0:request", "libjf-config-network-v0:response", //https://git.frohnmeyer-wds.de/JfMods/LibJF (General purpose library)
|
||||
"libz:set_mouse_position", "libz:sync_config", //https://github.com/Globox1997/LibZ (General purpose library)
|
||||
"moonlight:0", "moonlight:1", "moonlight:2", "moonlight:3", "moonlight:4", "moonlight:5", //https://github.com/MehVahdJukaar/Moonlight (General purpose library)
|
||||
"nochatreports:sync", //https://github.com/Aizistral-Studios/No-Chat-Reports (Unsigns chat messages)
|
||||
"omegaconfig:sync", //https://github.com/Draylar/omega-config (Config library)
|
||||
"openpartiesandclaims:main", //https://github.com/thexaero/open-parties-and-claims (Chunk loading tool, needs server cooperation)
|
||||
"owo:config_sync", //https://github.com/wisp-forest/owo-lib (GUI and config library)
|
||||
"patchouli:open_book", "patchouli:reload_books", //https://github.com/VazkiiMods/Patchouli/ (Ingame user guide books)
|
||||
"pickupnotifier:1/0", "pickupnotifier:1/1", //https://modrinth.com/mod/pick-up-notifier
|
||||
"porting_lib:extra_entity_spawn_data", "porting_lib:open_screen", "port_lib:tier_sorting", //https://github.com/Fabricators-of-Create/Porting-Lib (Forge fabric porting library)
|
||||
"showmeyourskin:config_sync", //https://github.com/enjarai/show-me-your-skin (Configurable armor rendering)
|
||||
"simplerpc:serverconfig", //https://modrinth.com/mod/simple-discord-rpc (Customizable Discord rich presence)
|
||||
"skinshuffle:handshake", //https://github.com/IMB11/SkinShuffle (Ingame realtime skin editor)
|
||||
"toms_storage:data_s2c", //https://github.com/tom5454/Toms-Storage (Additional storage blocks)
|
||||
"travelersbackpack:sync_backpack", "travelersbackpack:update_config", //https://github.com/Tiviacz1337/Travelers-Backpack (Additional backpack items)
|
||||
"trinkets:break", "trinkets:sync_inventory", "trinkets:sync_slots", //https://github.com/emilyploszaj/trinkets (Additional armor slots)
|
||||
"tweed4:sync_config", //https://github.com/Siphalor/tweed-api (Config library)
|
||||
"umu_backpack:load", "umu_backpack:unload", "umu_config:sync_config", //https://github.com/Zemelua/UMU-Backpack (Additional backpack item)
|
||||
"visualoverhaul:brewingstand", "visualoverhaul:furnace", "visualoverhaul:record", //https://github.com/TeamMidnightDust/VisualOverhaul (Graphical overhaul for certain blocks)
|
||||
"walkietalkie:buttonpressedresponse", //https://github.com/Flaton1/walkie-talkie-mod (Simple voice chat walkietalkie addon)
|
||||
"whereisit:s2c_founditem", "whereisit:found_item_s2c", //https://modrinth.com/mod/where-is-it (needs server side component to work)
|
||||
"wildfire_gender:hurt", "wildfire_gender:sync", //https://github.com/WildfireRomeo/WildfireFemaleGenderMod (Female player model)
|
||||
|
||||
//https://github.com/ZsoltMolnarrr/SpellEngine (Magic library)
|
||||
"spell_engine:config_sync", "spell_engine:particle_effects", "spell_engine:spell_animation",
|
||||
"spell_engine:spell_cooldown", "spell_engine:spell_registry_sync",
|
||||
|
||||
//https://modrinth.com/mod/shulkerboxtooltip
|
||||
"shulkerboxtooltip:s2c_handshake", "shulkerboxtooltip:ec_update", "shulkerboxtooltip:ec",
|
||||
"shulkerboxtooltip:s2",
|
||||
|
||||
//https://github.com/QuiltMC/quilt-standard-libraries (Quilt general purpose library)
|
||||
"qsl:registry_sync/end", "qsl:registry_sync/error_style", "qsl:registry_sync/handshake",
|
||||
"qsl:registry_sync/mod_protocol", "qsl:registry_sync/registry_apply", "qsl:registry_sync/registry_data",
|
||||
"qsl:registry_sync/registry_restore", "qsl:registry_sync/registry_start",
|
||||
"qsl:registry_sync/validate/block_states", "qsl:registry_sync/validate/fluid_states",
|
||||
"quilt:extended_entity_spawn_packet", "quilt_registry_entry_attachments:sync",
|
||||
|
||||
//https://github.com/apace100/origins-fabric (Minecraft player special powers mod)
|
||||
"origins:badge_list", "origins:confirm_origin", "origins:layer_list", "origins:open_origin_screen",
|
||||
"origins:origin_list",
|
||||
|
||||
//https://github.com/skyecodes/IBE-Editor (Item data editor)
|
||||
"ibeeditor:network/block_editor_response", "ibeeditor:network/block_inventory_item_editor_response",
|
||||
"ibeeditor:network/editor_command", "ibeeditor:network/player_inventory_item_editor_response",
|
||||
"ibeeditor:network/entity_inventory_item_editor_response", "ibeeditor:network/server_notification",
|
||||
"ibeeditor:network/main_hand_item_editor_response", "ibeeditor:network/entity_editor_response",
|
||||
|
||||
//https://github.com/enjarai/do-a-barrel-roll (Elytra flight visuals changes)
|
||||
"do_a_barrel_roll:config_sync", "do_a_barrel_roll:handshake", "do_a_barrel_roll:player_roll",
|
||||
"do_a_barrel_roll:server_config_update",
|
||||
|
||||
//https://github.com/CreativeMD/CreativeCore (General purpose library)
|
||||
"creativecore:main0", "creativecore:main1", "creativecore:main2", "creativecore:main3",
|
||||
"creativecore:main4", "creativecore:main5", "creativecore:main6", "creativecore:main7",
|
||||
|
||||
//https://github.com/Ladysnake/Cardinal-Components-API (General purpose library)
|
||||
"cardinal-components:block_entity_sync", "cardinal-components:chunk_sync",
|
||||
"cardinal-components:entity_sync", "cardinal-components:level_sync", "cardinal-components:world_sync",
|
||||
"cardinal-components:scoreboard_sync", "cardinal-components:team_sync",
|
||||
|
||||
//https://github.com/RedLime/SpeedRunIGT (Speedrunning overlay)
|
||||
"speedrunigt:achieve_advancement", "speedrunigt:achieve_criteria", "speedrunigt:condition_custom",
|
||||
"speedrunigt:condition_data", "speedrunigt:timer_category", "speedrunigt:timer_complete",
|
||||
"speedrunigt:timer_init", "speedrunigt:timer_start", "speedrunigt:timer_timeline",
|
||||
"speedrunigt:timer_uncompleted",
|
||||
|
||||
//https://github.com/miyo6032/bosses-of-mass-destruction (Adds new entities)
|
||||
"bosses_of_mass_destruction:change_hitbox", "bosses_of_mass_destruction:charged_ender_pearl_impact",
|
||||
"bosses_of_mass_destruction:client_vec3d", "bosses_of_mass_destruction:gauntlet_blindness",
|
||||
"bosses_of_mass_destruction:obsidilith_revive", "bosses_of_mass_destruction:player_velocity",
|
||||
"bosses_of_mass_destruction:spawn_entity", "bosses_of_mass_destruction:void_blossom_head",
|
||||
"bosses_of_mass_destruction:void_blossom_place", "bosses_of_mass_destruction:void_blossom_revive",
|
||||
"bosses_of_mass_destruction:void_blossom_spikes", "bosses_of_mass_destruction:void_lily_pollen",
|
||||
|
||||
//https://github.com/AzureDoom/AzureLib (Animation library)
|
||||
"azurelib:anim_data_sync", "azurelib:anim_trigger_sync", "azurelibarmor:anim_data_sync",
|
||||
"azurelibarmor:anim_trigger_sync", "azurelib:block_entity_anim_data_sync",
|
||||
"azurelib:block_entity_anim_trigger_sync", "azurelib:entity_anim_data_sync",
|
||||
"azurelib:entity_anim_trigger_sync", "azurelib:s2c_send_config_data", "azurelib:spawn_entity",
|
||||
|
||||
//https://github.com/apace100/apoli (Entity power library)
|
||||
"apoli:player_dismount", "apoli:player_mount", "apoli:power_list", "apoli:set_attacker",
|
||||
"apoli:sync_power", "apoli:sync_status_effect",
|
||||
|
||||
//https://essential.gg/
|
||||
"essential:", "essential:game_rule_hello", "essential:game_rule_permissions",
|
||||
@ -213,6 +313,9 @@ public class PluginMessage extends BasicListener {
|
||||
))
|
||||
channelRegisterHandlers.put(channel, player -> Chatter.disconnect(player).prefixless("MOD_YELLOW_SING", "minimap"));
|
||||
|
||||
for(String channel : Arrays.asList("bedrockify:cauldron_particles", "bedrockify:eat-particles")) //https://github.com/juancarloscp52/BedrockIfy (Bedrock features on Java, banned for reach-around block placement)
|
||||
channelRegisterHandlers.put(channel, player -> Chatter.disconnect(player).prefixless("MOD_YELLOW_SING", "bedrockify"));
|
||||
|
||||
registerBiDirPassthrough("WECUI", "worldedit:cui", "worldedit:internal", "minecraft:wecui");
|
||||
|
||||
registerPassthroughToClient(
|
||||
|
@ -1,64 +0,0 @@
|
||||
/*
|
||||
* This file is a part of the SteamWar software.
|
||||
*
|
||||
* Copyright (C) 2024 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.velocitycore.mods;
|
||||
|
||||
import com.google.gson.*;
|
||||
import com.velocitypowered.api.proxy.server.ServerPing;
|
||||
import lombok.AllArgsConstructor;
|
||||
|
||||
import java.lang.reflect.Type;
|
||||
|
||||
@AllArgsConstructor
|
||||
public class ServerListPing implements JsonSerializer<ServerPing>, JsonDeserializer<ServerPing> {
|
||||
// https://github.com/Aizistral-Studios/No-Chat-Reports/discussions/206
|
||||
// https://github.com/Aizistral-Studios/No-Chat-Reports/wiki/How-to-Get-Safe-Server-Status
|
||||
|
||||
private static final String[] FIELDS_TO_OVERRIDE = new String[] {
|
||||
"PRE_1_16_PING_SERIALIZER",
|
||||
"PRE_1_20_3_PING_SERIALIZER",
|
||||
"MODERN_PING_SERIALIZER"
|
||||
};
|
||||
|
||||
public static void init() {
|
||||
/*for (String fieldName : FIELDS_TO_OVERRIDE) {
|
||||
Reflection.Field<VelocityServer, Gson> field = Reflection.getField(VelocityServer.class, fieldName);
|
||||
field.set(null, new GsonBuilder()
|
||||
.registerTypeAdapter(ServerPing.class, new ServerListPing(field.get(null)))
|
||||
.create());
|
||||
}*/
|
||||
}
|
||||
|
||||
private final Gson gson;
|
||||
|
||||
@Override
|
||||
public JsonElement serialize(ServerPing ping, Type type, JsonSerializationContext context) {
|
||||
JsonElement element = gson.toJsonTree(ping, type);
|
||||
|
||||
JsonObject object = element.getAsJsonObject();
|
||||
object.addProperty("preventsChatReports", true);
|
||||
|
||||
return element;
|
||||
}
|
||||
|
||||
@Override
|
||||
public ServerPing deserialize(JsonElement element, Type type, JsonDeserializationContext context) throws JsonParseException {
|
||||
return gson.fromJson(element, ServerPing.class);
|
||||
}
|
||||
}
|
In neuem Issue referenzieren
Einen Benutzer sperren