SteamWar/BungeeCore
Archiviert
13
2

Ban System Rework #150

Manuell gemergt
Lixfel hat 38 Commits von ban-rework nach master 2021-01-30 08:42:35 +01:00 zusammengeführt
7 geänderte Dateien mit 78 neuen und 48 gelöschten Zeilen
Nur Änderungen aus Commit e0222982cb werden angezeigt - Alle Commits anzeigen

Datei anzeigen

@ -28,36 +28,26 @@ import net.md_5.bungee.api.chat.HoverEvent;
import net.md_5.bungee.api.chat.TextComponent;
import net.md_5.bungee.api.connection.ProxiedPlayer;
import java.text.ChoiceFormat;
import java.text.MessageFormat;
import java.util.Locale;
import java.util.ResourceBundle;
public class Message {
private Message(){}
public static TextComponent parseToComponent(String message, boolean prefixed, CommandSender sender, int[] useFormatter, Object... params){
return new TextComponent(TextComponent.fromLegacyText(parse(message, prefixed, sender, useFormatter, params)));
}
public static String parsePrefixed(String message, CommandSender sender, int[] useFormatter, Object... params){
return parse(message, true, sender, useFormatter, params);
public static TextComponent parseToComponent(String message, boolean prefixed, CommandSender sender, Object... params){
return new TextComponent(TextComponent.fromLegacyText(parse(message, prefixed, sender, params)));
}
Veraltet
Review

Ich würde mir da wenn wünschen (weil das eine API-Funktion ist), dass es die alte Funktion weiterhin gibt und dazu eine neue mit den neuen Aufrufparametern (damit das System nicht umständlich für Sonderfälle ist). Und dass dann entsprechend die alte Funktion verwendet wird, soweit der Formatter nicht benötigt wird.

Ich würde mir da wenn wünschen (weil das eine API-Funktion ist), dass es die alte Funktion weiterhin gibt und dazu eine neue mit den neuen Aufrufparametern (damit das System nicht umständlich für Sonderfälle ist). Und dass dann entsprechend die alte Funktion verwendet wird, soweit der Formatter nicht benötigt wird.
public static String parsePrefixed(String message, CommandSender sender, Object... params){
return parse(message, true, sender, new int[0], params);
return parse(message, true, sender, params);
}
public static String parse(String message, CommandSender sender, int[] useFormatter, Object... params){
return parse(message, false, sender, useFormatter, params);
}
public static String parse(String message, CommandSender sender, Object... params){
return parse(message, false, sender, new int[0], params);
return parse(message, false, sender, params);
}
private static String parse(String message, boolean prefixed, CommandSender sender, int[] useFormatter, Object... params){
private static String parse(String message, boolean prefixed, CommandSender sender, Object... params){
Locale locale = null;
if(sender instanceof ProxiedPlayer)
locale = ((ProxiedPlayer)sender).getLocale();
@ -71,8 +61,11 @@ public class Message {
pattern += (String)resourceBundle.getObject(message);
MessageFormat format = new MessageFormat(pattern, locale);
for (int i : useFormatter) {
params[i] = new MessageFormat((String) resourceBundle.getObject((String) params[i]), locale).format(params);
for (int i = 0; i < params.length; i++) {
if(params[i] instanceof Message) {
Message msg = (Message) params[i];
params[i] = parse(msg.getMessage(), sender, msg.getParams());
}
}
return format.format(params);
}
@ -105,10 +98,10 @@ public class Message {
send(message, false, sender, ChatMessageType.SYSTEM, onHover, onClick, params);
}
public static void send(String message, boolean prefixed, CommandSender sender, ChatMessageType type, String onHover, ClickEvent onClick, int[] useFormatter, Object... params){
public static void send(String message, boolean prefixed, CommandSender sender, ChatMessageType type, String onHover, ClickEvent onClick, Object... params){
if(type == ChatMessageType.CHAT && sender instanceof ProxiedPlayer && ((ProxiedPlayer)sender).getChatMode() != ProxiedPlayer.ChatMode.SHOWN)
return;
TextComponent msg = parseToComponent(message, prefixed, sender, useFormatter, params);
TextComponent msg = parseToComponent(message, prefixed, sender, params);
if(onHover != null)
msg.setHoverEvent(new HoverEvent(HoverEvent.Action.SHOW_TEXT, TextComponent.fromLegacyText(onHover)));
if(onClick != null)
@ -143,8 +136,25 @@ public class Message {
public static void team(String message, ChatMessageType type, Object... params){
for(ProxiedPlayer player : ProxyServer.getInstance().getPlayers()){
if(player.getGroups().contains(ConnectionListener.TEAM_GROUP))
if(player.getGroups().contains(ConnectionListener.TEAM_GROUP)) {
sendPrefixless(message, player, type, params);
}
}
}
private final String message;
private final Object[] params;
public Message(String message, Object... params) {
this.message = message;
this.params = params;
}
Veraltet
Review

Arrrg.

Arrrg.
public String getMessage() {
return message;
}
public Object[] getParams() {
return params;
}
}

Datei anzeigen

@ -54,9 +54,10 @@ public class BanCommand extends BasicCommand {
for (int i = 2; i < args.length; i++){
banReason.append(args[i]).append(" ");
}
boolean isPerma = args[1].equalsIgnoreCase("perma");
String msg = banReason.toString();
Veraltet
Review

Wenn du das ganze Team benachrichtigst, brauchst du nicht mehr den Spieler persönlich zu benachrichtigen.

Wenn du das ganze Team benachrichtigst, brauchst du nicht mehr den Spieler persönlich zu benachrichtigen.
target.ban(banTime, msg, SteamwarUser.get(sender.getName()).getId(), args[1].equalsIgnoreCase("perma"));
Message.team("BAN_TEAM_BANNED", new int[]{2}, target.getUserName(), sender.getName(), (args[1].equalsIgnoreCase("perma")?"BAN_PERMA":"BAN_UNTIL"), msg, banTime);
target.ban(banTime, msg, SteamwarUser.get(sender.getName()).getId(), isPerma);
Veraltet
Review

In internen Messages musst du nicht die Person colorcoden.

In internen Messages musst du nicht die Person colorcoden.
Veraltet
Review

Ok, das gefällt mir doch nicht so ganz mit dem IntArray etc. Evtl. wäre es eleganter, man macht da etwas a la new Message(...equals() ? "BAN_PERMA": "BAN_UNTIL") und wenn das object eine Message ist, (welche sich einfach nur zu merken Braucht, welchen String sie hat, ggf. auch noch parameter) und dann diese Message parst und den String entsprechend einfügt.

Ok, das gefällt mir doch nicht so ganz mit dem IntArray etc. Evtl. wäre es eleganter, man macht da etwas a la new Message(...equals() ? "BAN_PERMA": "BAN_UNTIL") und wenn das object eine Message ist, (welche sich einfach nur zu merken Braucht, welchen String sie hat, ggf. auch noch parameter) und dann diese Message parst und den String entsprechend einfügt.
Message.team("BAN_TEAM_BANNED", target.getUserName(), sender.getName(), new Message((isPerma?"BAN_PERMA":"BAN_UNTIL"), banTime), msg);
Veraltet
Review

Bitte erst die Aktion ausführen, dann die Aktion verkünden (im Fehlerfall kein Fehlannouncement).

Bitte erst die Aktion ausführen, dann die Aktion verkünden (im Fehlerfall kein Fehlannouncement).
}
public static Timestamp parseTime(CommandSender sender, String arg){

Datei anzeigen

@ -52,6 +52,6 @@ public class MuteCommand extends BasicCommand {
}
String msg = muteReason.toString();
target.mute(muteTime, msg, SteamwarUser.get(sender.getName()).getId(), args[1].equalsIgnoreCase("perma"));
Message.team("MUTE_TEAM_MUTED", target.getUserName(), new int[]{2}, sender.getName(), (args[1].equalsIgnoreCase("perma")?"BAN_PERMA":"BAN_UNTIL"), msg, muteTime);
Message.team("MUTE_TEAM_MUTED", target.getUserName(), sender.getName(), new Message((args[1].equalsIgnoreCase("perma")?"BAN_PERMA":"BAN_UNTIL"), muteTime), msg);
Veraltet
Review

Hier genauso.

Hier genauso.
}
Veraltet
Review

Hier genauso.

Hier genauso.
}

Datei anzeigen

@ -29,7 +29,6 @@ import net.md_5.bungee.api.chat.ClickEvent;
import net.md_5.bungee.api.connection.ProxiedPlayer;
import java.text.DecimalFormat;
import java.time.format.DateTimeFormatter;
public class WhoisCommand extends BasicCommand {
public WhoisCommand(){
@ -64,19 +63,18 @@ public class WhoisCommand extends BasicCommand {
}
private static void sendUserinfo(ProxiedPlayer player, SteamwarUser user) {
BungeeCore.send(player, "§7Username§8: §e" + user.getUserName());
BungeeCore.send(player, "§7UUID§8: §e" + user.getUuid().toString(), "", new ClickEvent(ClickEvent.Action.SUGGEST_COMMAND, user.getUuid().toString()));
BungeeCore.send(player, "§7ID§8: §e" + user.getId());
BungeeCore.send(player, "§7Beigetreten am§8: §e" + user.getFirstjoin().toString());
BungeeCore.send(player, "§7Online Time§8: §e" + new DecimalFormat("###.##").format(user.getOnlinetime() / (double) 3600) + "h");
Message.send("WHOIS_USERNAME", player, user.getUserName());
Message.send("WHOIS_UUID", player, Message.parse("WHOIS_UUID_HOVER", player), new ClickEvent(ClickEvent.Action.COPY_TO_CLIPBOARD, user.getUuid().toString()), user.getUuid().toString());
Message.send("WHOIS_ID", player, user.getId());
Message.send("WHOIS_JOINED_FIRST", player, user.getFirstjoin().toString());
Message.send("WHOIS_HOURS_PLAYED", player, new DecimalFormat("###.##").format(user.getOnlinetime() / (double) 3600));
Team team = Team.get(user.getTeam());
BungeeCore.send(player, "§7Team§8: §e" + team.getTeamName());
Message.send("WHOIS_TEAM", player, team.getTeamColor(), team.getTeamKuerzel(), team.getTeamName());
Veraltet
Review

Strafen

Strafen
BungeeCore.send(player, "§7Strafen: ");
Message.send("WHOIS_PUNISHMENTS", player);
for (Punishment punishment : Punishment.getAllPunishmentsOfPlayer(user.getId())) {
BungeeCore.send(player, "§7" + SteamwarUser.get(punishment.getPunisher()).getUserName() + "§8» §f§l" + punishment.getType().name() + ": §e"
+ punishment.getStartTime().toLocalDateTime().format(DateTimeFormatter.ofPattern(Message.parse("TIMEFORMAT", player))) + " - " + (punishment.isPerma()?"Perma":punishment.getEndTime().toLocalDateTime().format(DateTimeFormatter.ofPattern(Message.parse("TIMEFORMAT", player)))) + " §c" + punishment.getReason());
Message.send("WHOIS_PUNISHMENT", player, SteamwarUser.get(punishment.getPunisher()).getUserName(), punishment.getType().name(), punishment.getBantime(punishment.getStartTime(), false), punishment.getBantime(punishment.getEndTime(), punishment.isPerma()), punishment.getReason());
Veraltet
Review

Wenn wir die Mute & Bannsachen schon Multilingual haben, sollten wir auch das Multilingual machen. Um das Timestamp-Formatting müsste sich dann glaube ich auch das ML-Framework kümmern.

Wenn wir die Mute & Bannsachen schon Multilingual haben, sollten wir auch das Multilingual machen. Um das Timestamp-Formatting müsste sich dann glaube ich auch das ML-Framework kümmern.
Veraltet
Review

Dafür wurden wir dann ein Cache wo wir die Sprache speichern könnten, weil sonst müsste man dies von dem gemacht, der den Spieler bannt

Dafür wurden wir dann ein Cache wo wir die Sprache speichern könnten, weil sonst müsste man dies von dem gemacht, der den Spieler bannt
Veraltet
Review

Hier in dem Fall ist das Ziel klar player. Und player hat eine Sprache. Den Cache bräuchten wir wenn für den BanListener, aber nicht hier. Daher kann das schon mit umgezogen werden.

Hier in dem Fall ist das Ziel klar player. Und player hat eine Sprache. Den Cache bräuchten wir wenn für den BanListener, aber nicht hier. Daher kann das schon mit umgezogen werden.
}
Veraltet
Review

Da kannste aber auch mal aufs Multilinguale-System zurückgreifen

Da kannste aber auch mal aufs Multilinguale-System zurückgreifen
}
}

Datei anzeigen

@ -44,12 +44,8 @@ public class Punishment {
public static Map<PunishmentType, Punishment> getPunishmentsOfPlayer(int user) {
Map<PunishmentType, Punishment> punishmentMap = new HashMap<>();
Veraltet
Review

Da das immer aufgerufen wird, wenn wir einen SW-User brauchen (brauchen wir häufig): Bitte auf einmal beide möglichen Punishments holen und nicht nacheinander die einzelnen PunishmentTypes. Die Datenbank braucht da nämlich immer ein paar ms.

Da das immer aufgerufen wird, wenn wir einen SW-User brauchen (brauchen wir häufig): Bitte auf einmal beide möglichen Punishments holen und nicht nacheinander die einzelnen PunishmentTypes. Die Datenbank braucht da nämlich immer ein paar ms.
Veraltet
Review

Immer noch zu fixen.

Immer noch zu fixen.
for (PunishmentType type : PunishmentType.values()) {
Punishment punishment = getPunishmentOfPlayer(user, type);
if(punishment == null)
continue;
punishmentMap.put(type, punishment);
}
punishmentMap.put(PunishmentType.Ban, getPunishmentOfPlayer(user, PunishmentType.Ban));
punishmentMap.put(PunishmentType.Mute, getPunishmentOfPlayer(user, PunishmentType.Mute));
return punishmentMap;
Veraltet
Review

Da diese Funktion beim Laden eines jeden SWUsers aufgerufen wird: Bitte beide Punishments auf einmal laden.

Da diese Funktion beim Laden eines jeden SWUsers aufgerufen wird: Bitte beide Punishments auf einmal laden.
}
@ -126,7 +122,6 @@ public class Punishment {
getBantime(endTime, this.perma),
getBantime(newUpdate, perma),
newreason);
//TODO Add User
SQL.update("UPDATE Punishments SET EndTime = ?, Reason = ?, Perma = ? WHERE PunishmentId = ?", newUpdate, newReason, perma, id);
this.reason = newReason;

Datei anzeigen

@ -33,6 +33,7 @@ import java.net.UnknownHostException;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Timestamp;
import java.time.Instant;
import java.time.format.DateTimeFormatter;
import java.util.Date;
import java.util.HashMap;
@ -70,7 +71,7 @@ public class SteamwarUser {
usersById.put(id, this);
usersByName.put(userName.toLowerCase(), this);
usersByUUID.put(uuid, this);
punishments = Punishment.getPunishmentsOfPlayer(id);
punishments = null;
}
public static SteamwarUser getOrCreate(PendingConnection connection){
@ -150,7 +151,14 @@ public class SteamwarUser {
return team;
}
Veraltet
Review

Nicht multiple get() auf die Map machen, wenn dann einmal .get(), dann auf null überprüfen (z.B. kein Bann vorliegend), dann Zeiten prüfen.

Nicht multiple get() auf die Map machen, wenn dann einmal .get(), dann auf null überprüfen (z.B. kein Bann vorliegend), dann Zeiten prüfen.
Veraltet
Review

Das macht das ganze dann auch übersichtlicher.

Das macht das ganze dann auch übersichtlicher.
Veraltet
Review

Und ggf. eine Methode isCurrent() in Punishment implementieren, wo angezeigt wird, ob die Strafe derzeit aktiv ist, das macht es einfacher und weniger Code duplication.

Und ggf. eine Methode isCurrent() in Punishment implementieren, wo angezeigt wird, ob die Strafe derzeit aktiv ist, das macht es einfacher und weniger Code duplication.
Veraltet
Review

Wenn schon, dann über ProxyServer.getInstance().getPlayer(), Problem: Diese Funktion kann den Anschein erwecken, immer einen ProxiedPlayer zurückgeben zu können, was nicht der Fall ist, daher würde ich diese Funktion entfernen.

Wenn schon, dann über ProxyServer.getInstance().getPlayer(), Problem: Diese Funktion kann den Anschein erwecken, immer einen ProxiedPlayer zurückgeben zu können, was nicht der Fall ist, daher würde ich diese Funktion entfernen.
Veraltet
Review

Dann wurden wir mal ein Cache von den Sprachen gebrauchen, weil wenn der Spieler nicht Online ist weiß das Programm nicht auf welcher Sprache es bannen soll.

Dann wurden wir mal ein Cache von den Sprachen gebrauchen, weil wenn der Spieler nicht Online ist weiß das Programm nicht auf welcher Sprache es bannen soll.
Veraltet
Review

Wenn der Spieler nicht online ist, kommt hier null zurück. Und ja, das brauchen wir auch.

Wenn der Spieler nicht online ist, kommt hier null zurück. Und ja, das brauchen wir auch.
Veraltet
Review

Hier besteht weiterhin das Problem: Wenn der User nicht online ist, kommt hier null zurück. Bitte diese Methode hier entfernen, das ist wenn überall woanders im Code. Und nutze eher ProxyServer.getInstance() als BungeeCore.get().getProxy()

Hier besteht weiterhin das Problem: Wenn der User nicht online ist, kommt hier null zurück. Bitte diese Methode hier entfernen, das ist wenn überall woanders im Code. Und nutze eher ProxyServer.getInstance() als BungeeCore.get().getProxy()
private void loadPunishments() {
Veraltet
Review

Das funktioniert nicht so ganz, da der SteamwarUser-Cache einmal pro Stunde geleert wird. Daher kann es sein, dass der Key rausfällt, ohne dass je die BannedUserIPs geleert werden. Damit aber auch nicht ständig bei allen die BannedUserIPs gelöscht werden, brauchst du irgendwo ein Indikator, ob die Strafe abgesessen wurde.

Das funktioniert nicht so ganz, da der SteamwarUser-Cache einmal pro Stunde geleert wird. Daher kann es sein, dass der Key rausfällt, ohne dass je die BannedUserIPs geleert werden. Damit aber auch nicht ständig bei allen die BannedUserIPs gelöscht werden, brauchst du irgendwo ein Indikator, ob die Strafe abgesessen wurde.
Veraltet
Review

Das hier zu softloaden bringt kaum etwas, da bei jedem Join als allererstes der Gebannt-Status abgefragt wird.

Das hier zu softloaden bringt kaum etwas, da bei jedem Join als allererstes der Gebannt-Status abgefragt wird.
if(punishments == null) {
punishments = Punishment.getPunishmentsOfPlayer(id);
}
}
public boolean isBanned() {
loadPunishments();
if(!punishments.containsKey(Punishment.PunishmentType.Ban))
return false;
if(!isCurrent(punishments.get(Punishment.PunishmentType.Ban))) {
@ -162,6 +170,7 @@ public class SteamwarUser {
}
public boolean isMuted(){
loadPunishments();
Veraltet
Review

Die Methode scheint mir eher in Punishment zu passen.

Die Methode scheint mir eher in Punishment zu passen.
if(!punishments.containsKey(Punishment.PunishmentType.Mute))
return false;
return isCurrent(punishments.get(Punishment.PunishmentType.Mute));
@ -172,6 +181,7 @@ public class SteamwarUser {
}
Veraltet
Review

Ich meine, das ML-Framework müsste selbst (mit Optionen, die in den {} angegeben werden können) Dates oder sogar Timestamps parsen können. Schließlich ist die Datumsnotation in verschiedenen Teilen der Welt unterschiedlich.

Ich meine, das ML-Framework müsste selbst (mit Optionen, die in den {} angegeben werden können) Dates oder sogar Timestamps parsen können. Schließlich ist die Datumsnotation in verschiedenen Teilen der Welt unterschiedlich.
public TextComponent banMessage(ProxiedPlayer player){
loadPunishments();
Punishment punishment = punishments.get(Punishment.PunishmentType.Ban);
if (punishment.isPerma()) {
return BungeeCore.stringToText(Message.parsePrefixed("BANNED_MESSAGE_PERMA", player, punishment.getReason()));
@ -182,6 +192,7 @@ public class SteamwarUser {
}
Veraltet
Review

Siehe banMessage()

Siehe banMessage()
public TextComponent muteMessage(ProxiedPlayer player){
loadPunishments();
Punishment punishment = punishments.get(Punishment.PunishmentType.Mute);
if (punishment.isPerma()) {
return BungeeCore.stringToText(Message.parsePrefixed("MUTED_MESSAGE_PERMA", player, punishment.getReason()));
Veraltet
Review

Das fixt das Problem nur temporär, aber nicht in der Datenbank. Du musst entweder das alte Punishment aufräumen (Zeit entsprechend verkürzen) oder beim Selecten einer Strafe automatisch immer die am längesten noch andauernde Strafe SELECTen (dann würdest du mehrere Punishments zeitgleich handeln können).

Das fixt das Problem nur temporär, aber nicht in der Datenbank. Du musst entweder das alte Punishment aufräumen (Zeit entsprechend verkürzen) oder beim Selecten einer Strafe automatisch immer die am längesten noch andauernde Strafe SELECTen (dann würdest du mehrere Punishments zeitgleich handeln können).
@ -195,6 +206,7 @@ public class SteamwarUser {
}
public void ban(Timestamp time, String banReason, int from, boolean perma){
loadPunishments();
if(isBanned()) {
punishments.get(Punishment.PunishmentType.Ban).updateEndTime(from, banReason, time, perma);
return;
@ -217,6 +229,7 @@ public class SteamwarUser {
}
public void mute(Timestamp time, String muteReason, int from, boolean perma){
loadPunishments();
if(isMuted()) {
punishments.get(Punishment.PunishmentType.Mute).updateEndTime(from, muteReason, time, perma);
return;
@ -249,7 +262,8 @@ public class SteamwarUser {
public Timestamp getFirstjoin() {
ResultSet set = SQL.select("SELECT MIN(StartTime) AS FirstJoin FROM Session WHERE UserID = ?", id);
try {
set.next();
if(!set.next())
return Timestamp.from(Instant.MIN);
return set.getTimestamp("FirstJoin");
} catch (SQLException throwables) {
throw new SecurityException("Could not load First Join");

Datei anzeigen

@ -100,16 +100,17 @@ USAGE_IGNORE=§8/§7ignore §8[§eSpieler§8]
#Various commands
ALERT=§f{0}
#Ban&Mute-Command
BAN_TEAM_BANNED=§c{0} wurde von {1} {2} gebannt. §f§lGrund: §f{3}
Veraltet
Review

Können wir bitte im Farbschema bleiben?

Können wir bitte im Farbschema bleiben?
BANNED_MESSAGE_PERMA=§cDu bist permanent gebannt. §r§lGrund§r: §c{0}
BANNED_MESSAGE_UNTIL=Du bist bis zum {0} gebannt. §r§lGrund§r: §c{1}
MUTE_TEAM_MUTED=§c {0} wurde von {1} {2} gemuted. §f§lGrund: §f{3}
BANNED_MESSAGE_UNTIL=§cDu bist bis zum {0} gebannt. §r§lGrund§r: §c{1}
MUTE_TEAM_MUTED=§c{0} wurde von {1} {2} gemuted. §f§lGrund: §f{3}
MUTED_MESSAGE_PERMA=§cDu bist permanent gemuted. §r§lGrund§r: §c{0}
MUTED_MESSAGE_UNTIL=Du bist bis zum {0} gemuted. §r§lGrund§r: §c{1}
BAN_CHANGED={0} verändert von {1} von {2} auf {3} wegen {4}
MUTED_MESSAGE_UNTIL=§cDu bist bis zum {0} gemuted. §r§lGrund§r: §c{1}
BAN_CHANGED=§c{0} verändert von {1} von {2} auf {3} wegen {4}
Veraltet
Review

Da brauchen wir kein spezielles Präfix. Und so schön du dir das gedacht hast: Die eckigen Klammern machens irgendwie wieder hässlich. ;) (Persönliche Meinung)

Da brauchen wir kein spezielles Präfix. Und so schön du dir das gedacht hast: Die eckigen Klammern machens irgendwie wieder hässlich. ;) (Persönliche Meinung)
BAN_INVALID_TIME=§cUngültige Zeitangabe.
BAN_PERMA=Permanent
BAN_UNTIL=bis zum {4}
BAN_UNTIL=bis zum {0}
Veraltet
Review

same

same
BAN_AVOIDING_ALERT=§cMögliche Bannumgehung durch §r{0}§c: §c
BAN_AVOIDING_LIST={0} §e{1} §c
BAN_AVOIDING_BAN_HOVER=§cBanne Spieler wegen Bannumgehung
@ -157,4 +158,15 @@ CHECK_RANK_HOVER=§aMit diesem Rang freigeben
CHECK_ACCEPTED=§aDein §e{0} {1} §ewurde freigegeben§8!
CHECK_ACCEPTED_TEAM=§7Die Schematic §e{0} §7von §e{1} §7ist nun freigegeben!
CHECK_DECLINED=§cDein §e{0} {1} §cwurde abgelehnt§8: §c{2}
CHECK_DECLINED_TEAM=§7Die Schematic §e{0} §7von §e{1} §awurde aufgrund von §e{2} §7abgelehnt!
CHECK_DECLINED_TEAM=§7Die Schematic §e{0} §7von §e{1} §awurde aufgrund von §e{2} §7abgelehnt!
#WhoisCommand
WHOIS_USERNAME=§7Username§8: §e{0}
WHOIS_UUID=§7UUID§8: §e{0}
WHOIS_UUID_HOVER=§eUUID Kopieren
WHOIS_ID=§7ID§8: §e{0}
Veraltet
Review

Ich glaube, das haben wir generell USAGE_WHOIS genannt. Auch wenn SYNTAX ne gute idee ist, sollte das doch im Stil der USAGE messages oben und auch dort oben sein.

Ich glaube, das haben wir generell USAGE_WHOIS genannt. Auch wenn SYNTAX ne gute idee ist, sollte das doch im Stil der USAGE messages oben und auch dort oben sein.
WHOIS_JOINED_FIRST=§7Beigetreten am§8: §e{0}
WHOIS_HOURS_PLAYED=§7Online Time§8: §e{0}h
WHOIS_TEAM=§7Team§8: §e[§{0}{1}§e] {2}
WHOIS_PUNISHMENTS=§7Strafen:
WHOIS_PUNISHMENT=§7{0}§8» §f§l{1}: §e{2} - {3} §f{4}