From 9077451c088fcc476459b6df15140cf6131d61db Mon Sep 17 00:00:00 2001 From: Sen Date: Thu, 15 May 2025 21:25:28 +0200 Subject: [PATCH] add registration via command, password fields --- .../src/client/gui/element/PasswordBox.java | 27 +++++++ client/src/client/gui/ingame/GuiForm.java | 9 ++- server/src/server/Server.java | 30 +++++++- server/src/server/command/Command.java | 5 ++ .../server/command/CommandEnvironment.java | 6 ++ .../command/commands/CommandPasswd.java | 17 +++-- .../command/commands/CommandPlayers.java | 32 +++++++++ .../command/commands/CommandRegister.java | 72 +++++++++++++++++++ .../server/command/commands/CommandSave.java | 35 +++++++++ server/src/server/network/Player.java | 2 +- server/src/server/util/Form.java | 12 +++- 11 files changed, 232 insertions(+), 15 deletions(-) create mode 100644 client/src/client/gui/element/PasswordBox.java create mode 100644 server/src/server/command/commands/CommandPlayers.java create mode 100644 server/src/server/command/commands/CommandRegister.java create mode 100644 server/src/server/command/commands/CommandSave.java diff --git a/client/src/client/gui/element/PasswordBox.java b/client/src/client/gui/element/PasswordBox.java new file mode 100644 index 0000000..de104bb --- /dev/null +++ b/client/src/client/gui/element/PasswordBox.java @@ -0,0 +1,27 @@ +package client.gui.element; + +import client.gui.Font; +import client.renderer.Drawing; +import common.util.Util; + +public class PasswordBox extends Textbox { + public PasswordBox(int x, int y, int w, int h, int cap, Callback callback, String text) { + super(x, y, w, h, cap, true, callback, text); + } + + protected void drawForeground(int x1, int y1, int x2, int y2) { + Drawing.txt_draw(x1 + this.text_x, y1 + this.text_y, + x1 + this.text_x, y1 + this.text_y, + Integer.MAX_VALUE, Integer.MAX_VALUE, this.enabled ? this.gm.style.text_field : Util.mulColor(this.gm.style.text_field, 0.5f), this.text.isEmpty() ? "" : "****"); + } + + public void drawOverlay() { + if(Util.ftime() % 1.0f < 0.5f) { + int x1 = this.pos_x + this.margin_x1; + int y1 = this.pos_y + this.margin_y1; + int x2 = this.size_x - (this.margin_x1 + this.margin_x2); + int y2 = this.size_y - (this.margin_y1 + this.margin_y2); + Drawing.drawRect(x1, y1 + (y2 - Font.YGLYPH) / 2, 1, Font.YGLYPH, 0xff000000 | (~Util.mixColor(this.gm.style.field_top, this.gm.style.field_btm))); + } + } +} diff --git a/client/src/client/gui/ingame/GuiForm.java b/client/src/client/gui/ingame/GuiForm.java index 33dd965..dc5cce9 100644 --- a/client/src/client/gui/ingame/GuiForm.java +++ b/client/src/client/gui/ingame/GuiForm.java @@ -6,6 +6,7 @@ import client.gui.element.ActButton.Mode; import client.gui.element.Element; import client.gui.element.Label; import client.gui.element.NavButton; +import client.gui.element.PasswordBox; import client.gui.element.Switch; import client.gui.element.Textbox; import client.gui.element.Textbox.Action; @@ -56,12 +57,14 @@ public class GuiForm extends Gui implements ActButton.Callback { } else { this.labels[z] = this.add(new Label(0, 50 * z - 20, 300, 20, name, true)); - this.inputs[z] = this.add(new Textbox(0, 50 * z, 300, 24, Math.min(param & 0xffff, 256), true, new Textbox.Callback() { + Textbox.Callback callback = new Textbox.Callback() { public void use(Textbox elem, Action value) { if(value == Action.FOCUS) GuiForm.this.labels[index].setText(name); } - }, (String)obj)); + }; + this.inputs[z] = this.add((param & 0x80000000) != 0 ? new PasswordBox(0, 50 * z, 300, 24, Math.min(param & 0xffff, 256), callback, (String)obj) : + new Textbox(0, 50 * z, 300, 24, Math.min(param & 0xffff, 256), true, callback, (String)obj)); } } this.add(new NavButton(0, 50 * (this.inputs.length + 1), 148, 24, null, "Abbrechen")); @@ -98,7 +101,7 @@ public class GuiForm extends Gui implements ActButton.Callback { public void use(ActButton elem, Mode action) { for(int z = 0; z < this.inputs.length; z++) { if(this.inputs[z] instanceof Textbox) { - int min = this.inputData[z].third >> 16; + int min = (this.inputData[z].third & 0x7fffffff) >> 16; String text = this.inputs[z].getText(); if(text.length() < min) { if(!GuiForm.this.labels[z].getText().startsWith("" + TextColor.RED)) diff --git a/server/src/server/Server.java b/server/src/server/Server.java index ae4ec08..8b13220 100755 --- a/server/src/server/Server.java +++ b/server/src/server/Server.java @@ -267,7 +267,7 @@ public final class Server implements IThreadListener { } } - public Position getOfflinePosition(String user) { + public NBTTagCompound loadPlayerData(String user) { if(this.debug || !IPlayer.isValidUser(user)) return null; NBTTagCompound tag = null; @@ -281,6 +281,28 @@ public final class Server implements IThreadListener { catch(Exception e) { Log.JNI.error(e, "Konnte Spielerdaten für " + user + " (offline) nicht laden"); } + return tag; + } + + public void writePlayerData(String user, NBTTagCompound tag) { + if(this.debug || !IPlayer.isValidUser(user)) + return; + try { + File tmp = new File(new File("players"), user + ".nbt.tmp"); + File dat = new File(new File("players"), user + ".nbt"); + NBTLoader.writeGZip(tag, tmp); + if(dat.exists()) { + dat.delete(); + } + tmp.renameTo(dat); + } + catch(Exception e) { + Log.JNI.error(e, "Konnte Spielerdaten für " + user + " (offline) nicht speichern"); + } + } + + public Position getOfflinePosition(String user) { + NBTTagCompound tag = this.loadPlayerData(user); if(tag == null) return null; NBTTagList pos = tag.getTagList("Pos", 6); @@ -294,7 +316,7 @@ public final class Server implements IThreadListener { return new Position(posX, posY, posZ, rotYaw, rotPitch, dimension); } - private void saveAllWorlds(boolean message) { + public void saveAllWorlds(boolean message) { if(this.debug) return; if(message) @@ -672,6 +694,10 @@ public final class Server implements IThreadListener { return this.worlds; } + public void resetSaveTimer() { + this.saveTimer = 0; + } + public long[] getTickTimes() { return this.tickTimes; } diff --git a/server/src/server/command/Command.java b/server/src/server/command/Command.java index 637c07d..e58b0d7 100644 --- a/server/src/server/command/Command.java +++ b/server/src/server/command/Command.java @@ -7,6 +7,7 @@ import java.util.Map; import common.collect.Lists; import common.collect.Maps; +import common.util.CharValidator; import common.util.Vec3; import common.world.World; import server.command.DoubleParser.DefType; @@ -200,6 +201,10 @@ public abstract class Command implements Executable { protected Command addString(String name, String def, boolean allowEmpty, StringCompleter completer) { return this.addParameter(new StringParser(name, def, allowEmpty, null, null, null, completer)); } + + protected Command addString(String name, boolean allowEmpty, Integer min, Integer max, CharValidator validator) { + return this.addParameter(new StringParser(name, null, allowEmpty, min, max, validator)); + } public Map getParameters() { return this.parameters; diff --git a/server/src/server/command/CommandEnvironment.java b/server/src/server/command/CommandEnvironment.java index a3a6e6a..bb85367 100644 --- a/server/src/server/command/CommandEnvironment.java +++ b/server/src/server/command/CommandEnvironment.java @@ -20,10 +20,13 @@ import server.command.commands.CommandMessage; import server.command.commands.CommandMilk; import server.command.commands.CommandOfflinetp; import server.command.commands.CommandPasswd; +import server.command.commands.CommandPlayers; import server.command.commands.CommandPotion; import server.command.commands.CommandRebind; +import server.command.commands.CommandRegister; import server.command.commands.CommandRemove; import server.command.commands.CommandRevoke; +import server.command.commands.CommandSave; import server.command.commands.CommandShutdown; import server.command.commands.CommandSpawn; import server.command.commands.CommandTele; @@ -275,6 +278,9 @@ public class CommandEnvironment { this.registerExecutable(new CommandShutdown()); this.registerExecutable(new CommandRebind()); this.registerExecutable(new CommandPasswd()); + this.registerExecutable(new CommandPlayers()); + this.registerExecutable(new CommandSave()); + this.registerExecutable(new CommandRegister()); this.registerExecutable(new CommandHelp(this)); } diff --git a/server/src/server/command/commands/CommandPasswd.java b/server/src/server/command/commands/CommandPasswd.java index e67ee5b..7cf15ad 100644 --- a/server/src/server/command/commands/CommandPasswd.java +++ b/server/src/server/command/commands/CommandPasswd.java @@ -31,9 +31,9 @@ public class CommandPasswd extends Command { private Field confirmField; protected void init() { - this.checkField = player != exec ? null : this.addField("Aktuelles Passwort", 0, IPlayer.MAX_PASS_LENGTH, ""); - this.passwordField = this.addField("Neues Passwort", Config.minPassLength, IPlayer.MAX_PASS_LENGTH, ""); - this.confirmField = this.addField("Passwort bestätigen", Config.minPassLength, IPlayer.MAX_PASS_LENGTH, ""); + this.checkField = player != exec ? null : this.addPassword("Aktuelles Passwort", 0, IPlayer.MAX_PASS_LENGTH, ""); + this.passwordField = this.addPassword("Neues Passwort", Config.minPassLength, IPlayer.MAX_PASS_LENGTH, ""); + this.confirmField = this.addPassword("Passwort bestätigen", Config.minPassLength, IPlayer.MAX_PASS_LENGTH, ""); } public String getTitle() { @@ -41,7 +41,12 @@ public class CommandPasswd extends Command { } protected void accept() { - if(this.checkField != null && !this.checkField.get().equals(player.getPassword())) { + Player plr = env.getServer().getPlayer(player.getUser()); + if(!((Player)exec).isAdmin() || plr == null || (plr.isAdmin() && plr != exec)) { + exec.logConsole(TextColor.DRED + "Ein Fehler ist aufgetreten"); + return; + } + if(this.checkField != null && !this.checkField.get().equals(plr.getPassword())) { exec.logConsole(TextColor.RED + "Falsches Passwort eingegeben"); return; } @@ -49,8 +54,8 @@ public class CommandPasswd extends Command { exec.logConsole(TextColor.RED + "Passwörter stimmen nicht überein"); return; } - player.setPassword(this.passwordField.get()); - exec.logConsole(TextColor.GREEN + "Passwort" + (player != exec ? " für %s" : "") + " gesetzt", player.getUser()); + plr.setPassword(this.passwordField.get()); + exec.logConsole(TextColor.GREEN + "Passwort" + (plr != exec ? " für %s" : "") + " gesetzt", plr.getUser()); } }); } diff --git a/server/src/server/command/commands/CommandPlayers.java b/server/src/server/command/commands/CommandPlayers.java new file mode 100644 index 0000000..b665700 --- /dev/null +++ b/server/src/server/command/commands/CommandPlayers.java @@ -0,0 +1,32 @@ +package server.command.commands; + +import java.util.List; + +import common.color.TextColor; +import common.entity.npc.EntityNPC; +import common.util.ExtMath; +import server.command.Command; +import server.command.CommandEnvironment; +import server.command.Executor; +import server.network.Player; + +public class CommandPlayers extends Command { + public CommandPlayers() { + super("players"); + + this.addFlag("coords", 'c'); + } + + public void exec(CommandEnvironment env, Executor exec, boolean coords) { + List players = env.getServer().getPlayers(); + if(players.isEmpty()) { + exec.logConsole(TextColor.DGRAY + "Es sind keine Spieler online"); + return; + } + exec.logConsole(TextColor.GREEN + "Es " + (players.size() == 1 ? "ist" : "sind") + " " + TextColor.YELLOW + "%d" + TextColor.GREEN + " Spieler online", players.size()); + for(Player player : players) { + EntityNPC entity = player.getPresentEntity(); + exec.logConsole("%s%s" + TextColor.GRAY + ": '%s" + TextColor.GRAY + "'" + (coords ? " [" + TextColor.ORANGE + "%s @ %d, %d, %d" + TextColor.GRAY + "]" : ""), player.isAdmin() ? TextColor.RED : TextColor.NEON, player.getUser(), entity == null ? TextColor.DGRAY + "<->" : TextColor.ACID + entity.getCommandName(), entity == null ? null : entity.worldObj.dimension.getFormattedName(false), entity == null ? null : ExtMath.floord(entity.posX), entity == null ? null : ExtMath.floord(entity.posY), entity == null ? null : ExtMath.floord(entity.posZ)); + } + } +} diff --git a/server/src/server/command/commands/CommandRegister.java b/server/src/server/command/commands/CommandRegister.java new file mode 100644 index 0000000..c8a48e4 --- /dev/null +++ b/server/src/server/command/commands/CommandRegister.java @@ -0,0 +1,72 @@ +package server.command.commands; + +import common.color.TextColor; +import common.init.Config; +import common.nbt.NBTTagCompound; +import common.network.IPlayer; +import server.command.Command; +import server.command.CommandEnvironment; +import server.command.Executor; +import server.command.RunException; +import server.network.Player; +import server.util.Form; + +public class CommandRegister extends Command { + public CommandRegister() { + super("register"); + + this.addString("username", false, null, IPlayer.MAX_USER_LENGTH, IPlayer.VALID_USER); + this.setParamsOptional(); + this.addString("password", false); + this.addFlag("admin", 'a'); + } + + public void exec(CommandEnvironment env, Executor exec, String username, String password, boolean admin) { + Player player = env.getServer().getPlayer(username); + if(player != null) + throw new RunException("Ein Spieler mit diesem Nutzernamen ist bereits online"); + NBTTagCompound tag = env.getServer().loadPlayerData(username); + if(tag != null) + throw new RunException("Ein Spieler mit diesem Nutzernamen ist bereits registriert"); + if(exec.isPlayer()) { + if(password != null) + throw new RunException("Bei Verwendung als Spieler darf kein Passwort angegeben werden"); + ((Player)exec).displayForm(new Form() { + private Field passwordField; + private Field confirmField; + + protected void init() { + this.passwordField = this.addPassword("Passwort", Config.minPassLength, IPlayer.MAX_PASS_LENGTH, ""); + this.confirmField = this.addPassword("Passwort bestätigen", Config.minPassLength, IPlayer.MAX_PASS_LENGTH, ""); + } + + public String getTitle() { + return "Spieler " + username + " registrieren"; + } + + protected void accept() { + if(!((Player)exec).isAdmin() || env.getServer().getPlayer(username) != null || env.getServer().loadPlayerData(username) != null) { + exec.logConsole(TextColor.DRED + "Ein Fehler ist aufgetreten"); + return; + } + if(!this.passwordField.get().equals(this.confirmField.get())) { + exec.logConsole(TextColor.RED + "Passwörter stimmen nicht überein"); + return; + } + NBTTagCompound user = new NBTTagCompound(); + user.setString("password", this.passwordField.get()); + env.getServer().writePlayerData(username, user); + exec.logConsole(TextColor.GREEN + "Spieler %s registriert", username); + } + }); + } + else if(exec.isConsole()) { + if(password == null) + throw new RunException("Bei Verwendung in der Konsole muss ein Passwort angegeben werden"); + NBTTagCompound user = new NBTTagCompound(); + user.setString("password", password); + env.getServer().writePlayerData(username, user); + exec.logConsole(TextColor.GREEN + "Spieler %s registriert", username); + } + } +} diff --git a/server/src/server/command/commands/CommandSave.java b/server/src/server/command/commands/CommandSave.java new file mode 100644 index 0000000..4f80993 --- /dev/null +++ b/server/src/server/command/commands/CommandSave.java @@ -0,0 +1,35 @@ +package server.command.commands; + +import common.color.TextColor; +import common.packet.SPacketMessage; +import common.packet.SPacketMessage.Type; +import server.command.Command; +import server.command.CommandEnvironment; +import server.command.Executor; +import server.world.Region; + +public class CommandSave extends Command { + public CommandSave() { + super("save"); + + this.addFlag("message", 'm'); + this.addFlag("flush", 'f'); + } + + public void exec(CommandEnvironment env, Executor exec, boolean message, boolean flush) { + if(message) + env.getServer().sendPacket(new SPacketMessage(TextColor.RED + "Speichere Serverdaten, der Server könnte kurz einfrieren", Type.FEED)); + exec.logConsole(TextColor.ORANGE + "Speichere Spielerdaten ..."); + env.getServer().saveAllPlayerData(true); + exec.logConsole(TextColor.ORANGE + "Speichere Weltdaten ..."); + env.getServer().saveAllWorlds(true); + env.getServer().resetSaveTimer(); + if(flush) { + exec.logConsole(TextColor.ORANGE + "Beende E/A ..."); + Region.finishWrite(); + } + exec.logConsole(TextColor.DGREEN + "Alle Serverdaten wurden gespeichert"); + if(message) + env.getServer().sendPacket(new SPacketMessage(TextColor.GREEN + "Die Serverdaten wurden gespeichert", Type.FEED)); + } +} diff --git a/server/src/server/network/Player.java b/server/src/server/network/Player.java index 9db76b8..f42a452 100755 --- a/server/src/server/network/Player.java +++ b/server/src/server/network/Player.java @@ -339,7 +339,7 @@ public class Player extends NetHandler implements ICrafting, Executor, IPlayer } public boolean getAdmin() { - return this.admin; + return this.isAdmin(); } public void setPassword(String pass) { diff --git a/server/src/server/util/Form.java b/server/src/server/util/Form.java index cb6fbbf..e96bee5 100644 --- a/server/src/server/util/Form.java +++ b/server/src/server/util/Form.java @@ -94,14 +94,16 @@ public abstract class Form { protected class Field extends FormElement { private final int minLength; private final int maxLength; + private final boolean password; private String value; - protected Field(String name, String value, int min, int max) { + protected Field(String name, String value, int min, int max, boolean password) { super(name); this.value = value; this.minLength = min; this.maxLength = max; + this.password = password; } protected Object getInputData() { @@ -109,7 +111,7 @@ public abstract class Form { } protected int getInputParameter() { - return (this.minLength << 16) | this.maxLength; + return (this.password ? 0x80000000 : 0) | (this.minLength << 16) | this.maxLength; } protected boolean acceptValue(Object obj) { @@ -145,7 +147,11 @@ public abstract class Form { } protected Field addField(String name, int minLength, int maxLength, String def) { - return this.add(new Field(name, def, minLength, maxLength)); + return this.add(new Field(name, def, minLength, maxLength, false)); + } + + protected Field addPassword(String name, int minLength, int maxLength, String def) { + return this.add(new Field(name, def, minLength, maxLength, true)); } public abstract String getTitle();