This commit is contained in:
jannis committed 2026-10-29 21:16:51 +01:00
1 parent 3f672d1819
commit 15d1c70b8c
5 files changed
+101 -97

No files matched your search

@@ -1,18 +1,17 @@
package eu.mhsl.craftattack.loginServer;
import com.google.gson.JsonArray;
import com.google.gson.JsonElement;
import com.google.gson.JsonObject;
import com.google.gson.JsonParser;
import java.io.IOException;
import java.net.URI;
import java.net.URLEncoder;
import java.net.http.HttpClient;
import java.net.http.HttpRequest;
import java.net.http.HttpResponse;
import java.nio.charset.StandardCharsets;
import java.time.Duration;
import java.util.Optional;
import java.util.Base64;
import java.util.UUID;
public final class Api {
@@ -20,67 +19,78 @@ public final class Api {
private Api() {}
public static void setProfile(UUID uuid, String username, String name, String secondName, String birthday, String phoneNumber) {
JsonObject body = new JsonObject();
body.addProperty("uuid", uuid.toString());
body.addProperty("username", username);
body.addProperty("name", name);
body.addProperty("secondName", secondName);
body.addProperty("birthday", birthday);
body.addProperty("phoneNumber", phoneNumber);
send("POST", "players", "", body);
}
public static boolean isRegistered(UUID uuid) {
return find("players", "uuid", uuid.toString()).isPresent();
HttpResponse<String> response = call("GET", "/api/users/" + uuid, null, true);
int status = response.statusCode();
if (status == 200) return true;
if (status == 404) return false;
throw new IllegalStateException("API-Fehler " + status + ": " + response.body());
}
public static boolean isSecretValid(String username, String secret) {
return find("secrets", "player", username, "secret", secret).isPresent();
}
private static Optional<JsonObject> find(String collection, String... fieldValuePairs) {
StringBuilder filter = new StringBuilder();
for (int i = 0; i < fieldValuePairs.length; i += 2) {
if (i > 0) filter.append(" && ");
String escaped = fieldValuePairs[i + 1].replace("\\", "\\\\").replace("'", "\\'");
filter.append(fieldValuePairs[i]).append("='").append(escaped).append("'");
public static final class SignupError extends RuntimeException {
public final String code;
public SignupError(String code, String message) {
super(message);
this.code = code;
}
String query = "?perPage=1&filter=" + URLEncoder.encode(filter.toString(), StandardCharsets.UTF_8);
JsonObject response = send("GET", collection, query, null);
if (!response.has("items")) return Optional.empty();
JsonArray items = response.getAsJsonArray("items");
return items.isEmpty() ? Optional.empty() : Optional.of(items.get(0).getAsJsonObject());
}
private static JsonObject send(String method, String collection, String path, JsonObject body) {
JsonObject login = new JsonObject();
login.addProperty("identity", config("email"));
login.addProperty("password", config("password"));
public static void signup(
String firstname,
String lastname,
String birthdayIso,
String phone,
String username,
UUID uuid,
String edition
) {
JsonObject body = new JsonObject();
body.addProperty("firstname", firstname);
body.addProperty("lastname", lastname);
body.addProperty("birthday", birthdayIso);
body.addProperty("phone", phone);
body.addProperty("username", username);
body.addProperty("uuid", uuid.toString());
body.addProperty("edition", edition);
String token = call("POST", "/api/collections/_superusers/auth-with-password", login, null)
.get("token").getAsString();
return call(method, "/api/collections/" + collection + "/records" + path, body, token);
HttpResponse<String> response = call("POST", "/api/users", body.toString(), true);
int status = response.statusCode();
if (status >= 200 && status < 300) return;
String code = switch (status) {
case 400 -> "BAD_REQUEST";
case 401 -> "UNAUTHORIZED";
case 409 -> "CONFLICT";
default -> "UNKNOWN";
};
String message = response.body();
try {
JsonObject json = JsonParser.parseString(response.body()).getAsJsonObject();
JsonElement error = json.get("error");
if (error != null && error.isJsonPrimitive()) message = error.getAsString();
} catch (RuntimeException ignored) {}
throw new SignupError(code, "Signup fehlgeschlagen (" + status + "/" + code + "): " + message);
}
private static JsonObject call(String method, String path, JsonObject body, String token) {
private static HttpResponse<String> call(String method, String path, String body, boolean withAuth) {
try {
HttpRequest.Builder builder = HttpRequest.newBuilder(URI.create(config("url") + path))
.timeout(Duration.ofSeconds(5))
.header("Content-Type", "application/json")
.method(method, body == null
? HttpRequest.BodyPublishers.noBody()
: HttpRequest.BodyPublishers.ofString(body.toString()));
if (token != null) builder.header("Authorization", token);
: HttpRequest.BodyPublishers.ofString(body));
HttpResponse<String> response = client.send(builder.build(), HttpResponse.BodyHandlers.ofString());
if (response.statusCode() >= 300) {
throw new IllegalStateException("API-Fehler " + response.statusCode() + ": " + response.body());
if (withAuth) {
String secret = config("secret");
if (!secret.isBlank()) {
String credentials = Base64.getEncoder()
.encodeToString(("api:" + secret).getBytes(StandardCharsets.UTF_8));
builder.header("Authorization", "Basic " + credentials);
}
}
if (response.body().isBlank()) return new JsonObject();
return JsonParser.parseString(response.body()).getAsJsonObject();
return client.send(builder.build(), HttpResponse.BodyHandlers.ofString());
} catch (IOException | InterruptedException e) {
throw new IllegalStateException("API nicht erreichbar", e);
}
@@ -89,12 +99,4 @@ public final class Api {
private static String config(String key) {
return Main.globalConfig.node("api", key).getString("");
}
public static boolean removeSecret(String username, String secret) {
Optional<JsonObject> entry = find("secrets", "player", username, "secret", secret);
if (entry.isEmpty()) return false;
send("DELETE", "secrets", "/" + entry.get().get("id").getAsString(), null);
return true;
}
}
@@ -83,11 +83,6 @@ public final class Dialog {
String title;
switch (state.currentPage()) {
case secret -> {
title = "Secret";
body.add(plain("Bitte gib das Secret ein, das du erhalten hast."));
inputs.add(text("secret", "Secret", state.secret, 50));
}
case data -> {
title = "Profildaten";
body.add(plain("Bitte fülle deine Profildaten aus."));
@@ -158,7 +153,6 @@ public final class Dialog {
private static void saveCurrentPage(DialogState state, CompoundBinaryTag payload) {
switch (state.currentPage()) {
case secret -> state.secret = payload.getString("secret", "").trim();
case data -> {
state.firstName = payload.getString("name", "").trim();
state.secondName = payload.getString("secondName", "").trim();
@@ -175,15 +169,6 @@ public final class Dialog {
private static Component validateCurrentPage(Player player, DialogState state) {
switch (state.currentPage()) {
case secret -> {
try {
if (!Api.isSecretValid(player.getUsername(), state.secret))
return error("Das Secret ist ungültig.");
} catch (IllegalStateException e) {
e.printStackTrace();
return error("Die Überprüfung ist gerade nicht möglich. Bitte versuche es später erneut.");
}
}
case data -> {
if (state.firstName.isBlank() || state.secondName.isBlank() || state.birthday.isBlank())
return error("Bitte fülle alle Pflichtfelder aus.");
@@ -3,12 +3,11 @@ package eu.mhsl.craftattack.loginServer;
import java.util.List;
public final class DialogState {
public enum Page { secret, data, consent }
public enum Page { data, consent }
private final List<Page> pages;
private static final List<Page> pages = List.of(Page.data, Page.consent);
private int pageIndex = 0;
String secret = "";
String firstName = "";
String secondName = "";
String birthday = "";
@@ -17,18 +16,10 @@ public final class DialogState {
boolean logsAccepted = false;
boolean rulesAccepted = false;
private DialogState(List<Page> pages) {
this.pages = pages;
}
private DialogState() {}
public static DialogState create() {
boolean isAfterStart = Main.globalConfig
.node("server", "is-after-start")
.getBoolean(false);
return new DialogState(isAfterStart
? List.of(Page.secret, Page.data, Page.consent)
: List.of(Page.data, Page.consent));
return new DialogState();
}
public Page currentPage() {
@@ -1,6 +1,7 @@
package eu.mhsl.craftattack.loginServer;
import net.kyori.adventure.nbt.CompoundBinaryTag;
import net.kyori.adventure.text.Component;
import net.minestom.server.MinecraftServer;
import net.minestom.server.coordinate.Pos;
import net.minestom.server.entity.Player;
@@ -9,6 +10,9 @@ import net.minestom.server.event.player.PlayerCustomClickEvent;
import net.minestom.server.instance.InstanceManager;
import java.time.Duration;
import java.time.LocalDate;
import java.time.format.DateTimeFormatter;
import java.util.Locale;
import java.util.Map;
import java.util.UUID;
import java.util.concurrent.ConcurrentHashMap;
@@ -53,27 +57,51 @@ public final class PlayerHandler {
private static void complete(Player player, DialogState state) {
states.remove(player.getUuid());
if (Api.isRegistered(player.getUuid())) {
player.kick("Du bist bereits auf Craftattack registriert, wir freuen uns auf dich!");
String birthdayIso;
try {
birthdayIso = LocalDate
.parse(state.birthday, DateTimeFormatter.ofPattern("dd.MM.uuuu", Locale.GERMANY))
.format(DateTimeFormatter.ISO_LOCAL_DATE);
} catch (RuntimeException e) {
player.kick(Component.text("Geburtsdatum ungültig."));
return;
}
try {
Api.signup(
state.firstName,
state.secondName,
birthdayIso,
state.phoneNumber,
player.getUsername(),
player.getUuid(),
"java"
);
} catch (Api.SignupError e) {
e.printStackTrace();
player.kick(Component.text(mapSignupError(e)));
return;
} catch (RuntimeException e) {
e.printStackTrace();
player.kick(Component.text("Anmeldung gerade nicht möglich. Bitte später erneut versuchen."));
return;
}
player.sendMessage("Danke für deine Anmeldung.");
player.sendMessage("Du bist für das Projekt Craftattack 9 registriert!");
Api.setProfile(player.getUuid(), player.getUsername(), state.firstName, state.secondName, state.birthday, state.phoneNumber);
Api.removeSecret(player.getUsername(), state.secret);
MinecraftServer.getSchedulerManager()
.buildTask(() -> player.kick("Anmeldung erfolgreich, wir freuen uns auf dich!"))
.delay(Duration.ofMillis(1000))
.schedule();
}
System.out.println("Vorname: " + state.firstName);
System.out.println("Nachname: " + state.secondName);
System.out.println("Geburtstag: " + state.birthday);
System.out.println("Telefonnummer: " + state.phoneNumber);
System.out.println("Spielername: " + player.getUsername());
System.out.println("Spieler UUID: " + player.getUuid());
private static String mapSignupError(Api.SignupError e) {
return switch (e.code) {
case "CONFLICT" -> "Du bist bereits registriert.";
case "UNAUTHORIZED" -> "Server nicht autorisiert. Bitte Admin kontaktieren.";
case "BAD_REQUEST" -> "Deine Angaben sind nicht gültig.";
default -> "Anmeldung fehlgeschlagen. Bitte später erneut versuchen.";
};
}
}
+1 -3
View File
@@ -3,7 +3,6 @@ server:
view-distance: 12
compression-threshold: 0
open-to-lan: false
is-after-start: false
bungeecord:
enabled: false
@@ -15,5 +14,4 @@ velocity:
api:
url: ""
email: ""
password: ""
secret: ""