Refactor card management to support asynchronous operations and improve error handling
Signed-off-by: Dmitrii <computer@yawaflua.tech> Took 3 minutes
This commit is contained in:
@@ -20,11 +20,11 @@ jobs:
|
|||||||
dotnet-version: 10.0.x
|
dotnet-version: 10.0.x
|
||||||
|
|
||||||
- name: Restore dependencies
|
- name: Restore dependencies
|
||||||
run: dotnet restore
|
run: dotnet restore --project backend/SpMega.Backend/SpMega.Backend.csproj
|
||||||
- name: Build
|
- name: Build
|
||||||
run: dotnet build --no-restore
|
run: dotnet build --no-restore --project backend/SpMega.Backend/SpMega.Backend.csproj
|
||||||
- name: Test
|
- name: Test
|
||||||
run: dotnet test --no-build --verbosity normal
|
run: dotnet test --no-build --verbosity normal --project backend/SpMega.Backend/SpMega.Backend.csproj
|
||||||
|
|
||||||
compose:
|
compose:
|
||||||
name: Push Docker image to ghcr.io
|
name: Push Docker image to ghcr.io
|
||||||
|
|||||||
@@ -9,7 +9,6 @@ import net.minecraft.text.Text;
|
|||||||
|
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.concurrent.CompletableFuture;
|
|
||||||
import java.util.regex.Pattern;
|
import java.util.regex.Pattern;
|
||||||
|
|
||||||
public class ChatListener {
|
public class ChatListener {
|
||||||
@@ -57,8 +56,7 @@ public class ChatListener {
|
|||||||
if (client.player != null) {
|
if (client.player != null) {
|
||||||
String playerUuid = client.player.getUuidAsString();
|
String playerUuid = client.player.getUuidAsString();
|
||||||
|
|
||||||
CompletableFuture
|
BankUiService.instance().addCardAsync(cardId, tokenId, playerUuid)
|
||||||
.supplyAsync(() -> BankUiService.instance().addCard(cardId, tokenId, playerUuid))
|
|
||||||
.thenAccept(msg -> {
|
.thenAccept(msg -> {
|
||||||
if (client == null) {
|
if (client == null) {
|
||||||
return;
|
return;
|
||||||
|
|||||||
@@ -26,6 +26,7 @@ import net.minecraft.text.Text;
|
|||||||
import net.minecraft.util.ActionResult;
|
import net.minecraft.util.ActionResult;
|
||||||
import org.lwjgl.glfw.GLFW;
|
import org.lwjgl.glfw.GLFW;
|
||||||
|
|
||||||
|
import java.util.concurrent.CompletableFuture;
|
||||||
import java.util.regex.Matcher;
|
import java.util.regex.Matcher;
|
||||||
import java.util.regex.Pattern;
|
import java.util.regex.Pattern;
|
||||||
|
|
||||||
@@ -166,25 +167,13 @@ public class SPMegaClient implements ClientModInitializer {
|
|||||||
}
|
}
|
||||||
UiNotifications.instance().render(drawContext, client.textRenderer, scaledWidth, scaledHeight);
|
UiNotifications.instance().render(drawContext, client.textRenderer, scaledWidth, scaledHeight);
|
||||||
});
|
});
|
||||||
// CompletableFuture.supplyAsync(() -> BackendAuthenticator.authenticate(client))
|
|
||||||
// .thenAccept(authResult -> {
|
|
||||||
// if (authResult) {
|
|
||||||
// System.out.println("SPMega: Authenticated on backend successfully.");
|
|
||||||
// } else {
|
|
||||||
// System.err.println("SPMega: Authentication on backend failed");
|
|
||||||
// }
|
|
||||||
// })
|
|
||||||
// .exceptionally(ex -> {
|
|
||||||
// System.err.println("SPMega: Authentication error: " + ex.getMessage());
|
|
||||||
// return null;
|
|
||||||
// });
|
|
||||||
|
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
|
|
||||||
ClientPlayConnectionEvents.JOIN.register((handler, sender, client) -> {
|
ClientPlayConnectionEvents.JOIN.register((handler, sender, client) -> {
|
||||||
if (client.player != null) {
|
if (client.player != null) {
|
||||||
BankUiService.instance().refreshOnServerJoin(client.player.getUuidAsString());
|
BankUiService.instance().refreshOnServerJoinAsync(client.player.getUuidAsString());
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -238,8 +227,13 @@ public class SPMegaClient implements ClientModInitializer {
|
|||||||
});
|
});
|
||||||
|
|
||||||
new ChatListener().register();
|
new ChatListener().register();
|
||||||
BackendAuthenticator.authenticate(MinecraftClient.getInstance());
|
CompletableFuture.runAsync(() -> BackendAuthenticator.authenticate(MinecraftClient.getInstance()))
|
||||||
|
.exceptionally(throwable -> {
|
||||||
|
System.err.println("[SPMEGA] Error during async authenticate: " + throwable.getMessage());
|
||||||
|
return null;
|
||||||
|
});
|
||||||
System.out.println("Author of SPMega make it with 4 cans of monster");
|
System.out.println("Author of SPMega make it with 4 cans of monster");
|
||||||
|
System.out.println("If u want to see more updates - give me like 10 shekels for monster plzzz");
|
||||||
System.out.println("Initialized beshalom! Tieie tovim!");
|
System.out.println("Initialized beshalom! Tieie tovim!");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -7,7 +7,6 @@ import net.minecraft.client.gui.widget.ButtonWidget;
|
|||||||
import net.minecraft.client.gui.widget.TextFieldWidget;
|
import net.minecraft.client.gui.widget.TextFieldWidget;
|
||||||
import net.minecraft.text.Text;
|
import net.minecraft.text.Text;
|
||||||
|
|
||||||
import java.util.concurrent.CompletableFuture;
|
|
||||||
import java.util.function.Consumer;
|
import java.util.function.Consumer;
|
||||||
|
|
||||||
public class AddCardScreen extends Screen {
|
public class AddCardScreen extends Screen {
|
||||||
@@ -85,8 +84,7 @@ public class AddCardScreen extends Screen {
|
|||||||
addButton.active = false;
|
addButton.active = false;
|
||||||
cancelButton.active = false;
|
cancelButton.active = false;
|
||||||
|
|
||||||
CompletableFuture
|
bankUiService.addCardAsync(cardId, cardToken, playerUuid)
|
||||||
.supplyAsync(() -> bankUiService.addCard(cardId, cardToken, playerUuid))
|
|
||||||
.thenAccept(message -> {
|
.thenAccept(message -> {
|
||||||
if (this.client == null) {
|
if (this.client == null) {
|
||||||
return;
|
return;
|
||||||
|
|||||||
@@ -9,7 +9,6 @@ import net.minecraft.text.Text;
|
|||||||
|
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.concurrent.CompletableFuture;
|
|
||||||
|
|
||||||
public class CardScreen extends Screen {
|
public class CardScreen extends Screen {
|
||||||
private final Screen parent;
|
private final Screen parent;
|
||||||
@@ -46,25 +45,30 @@ public class CardScreen extends Screen {
|
|||||||
}
|
}
|
||||||
|
|
||||||
this.addDrawableChild(ButtonWidget.builder(Text.literal("Удалить"), button -> {
|
this.addDrawableChild(ButtonWidget.builder(Text.literal("Удалить"), button -> {
|
||||||
CompletableFuture.supplyAsync(() -> {
|
bankUiService.removeSelectedCardAsync()
|
||||||
bankUiService.removeSelectedCard();
|
.thenAccept(message -> {
|
||||||
return true;
|
if (this.client != null) {
|
||||||
});
|
this.client.execute(() -> {
|
||||||
notifications.showMessage(bankUiService.getLastMessage());
|
notifications.showMessage(message);
|
||||||
this.clearAndInit();
|
this.clearAndInit();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
});
|
||||||
}).dimensions(rightX, startY, columnWidth, 20).build());
|
}).dimensions(rightX, startY, columnWidth, 20).build());
|
||||||
|
|
||||||
this.addDrawableChild(ButtonWidget.builder(Text.literal("Обновить"), button -> {
|
this.addDrawableChild(ButtonWidget.builder(Text.literal("Обновить"), button -> {
|
||||||
String playerUuid = this.client != null && this.client.player != null
|
String playerUuid = this.client != null && this.client.player != null
|
||||||
? this.client.player.getUuidAsString()
|
? this.client.player.getUuidAsString()
|
||||||
: "";
|
: "";
|
||||||
CompletableFuture.supplyAsync(() -> {
|
bankUiService.refreshSelectedCardAsync(playerUuid)
|
||||||
bankUiService.refreshSelectedCard(playerUuid);
|
.thenAccept(message -> {
|
||||||
return true;
|
if (this.client != null) {
|
||||||
});
|
this.client.execute(() -> {
|
||||||
String message = bankUiService.getLastMessage().isBlank() ? "Карта обновлена" : bankUiService.getLastMessage();
|
|
||||||
notifications.showMessage(message);
|
notifications.showMessage(message);
|
||||||
this.clearAndInit();
|
this.clearAndInit();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
});
|
||||||
}).dimensions(rightX, startY + 24, columnWidth, 20).build());
|
}).dimensions(rightX, startY + 24, columnWidth, 20).build());
|
||||||
|
|
||||||
this.addDrawableChild(ButtonWidget.builder(Text.literal("Добавить новую"), button -> {
|
this.addDrawableChild(ButtonWidget.builder(Text.literal("Добавить новую"), button -> {
|
||||||
|
|||||||
@@ -12,7 +12,6 @@ import net.minecraft.text.Text;
|
|||||||
|
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.concurrent.CompletableFuture;
|
|
||||||
import java.util.regex.Matcher;
|
import java.util.regex.Matcher;
|
||||||
import java.util.regex.Pattern;
|
import java.util.regex.Pattern;
|
||||||
|
|
||||||
@@ -382,8 +381,7 @@ public class PaymentScreen extends Screen {
|
|||||||
}
|
}
|
||||||
lastRecipientLookup = username;
|
lastRecipientLookup = username;
|
||||||
|
|
||||||
CompletableFuture
|
bankUiService.loadRecipientCardsAsync(username)
|
||||||
.supplyAsync(() -> bankUiService.loadRecipientCards(username))
|
|
||||||
.thenAccept(cards -> {
|
.thenAccept(cards -> {
|
||||||
if (this.client == null) {
|
if (this.client == null) {
|
||||||
return;
|
return;
|
||||||
|
|||||||
+22
-31
@@ -84,12 +84,11 @@ public class BackendAuthenticator {
|
|||||||
}
|
}
|
||||||
|
|
||||||
JsonObject json = JsonParser.parseString(response.body()).getAsJsonObject();
|
JsonObject json = JsonParser.parseString(response.body()).getAsJsonObject();
|
||||||
if (json.has("sessionId")) {
|
if (!json.has("sessionId")) {
|
||||||
return json.get("sessionId").getAsString();
|
|
||||||
} else {
|
|
||||||
System.err.println("[SPMEGA] Invalid response from start endpoint: " + response.body());
|
System.err.println("[SPMEGA] Invalid response from start endpoint: " + response.body());
|
||||||
throw new IOException("Invalid response from start endpoint: " + response.body());
|
throw new IOException("Invalid response from start endpoint: " + response.body());
|
||||||
}
|
}
|
||||||
|
return json.get("sessionId").getAsString();
|
||||||
}
|
}
|
||||||
|
|
||||||
private static boolean sendAuthRequestToBackend(UUID uuid, String serverId) throws IOException, InterruptedException {
|
private static boolean sendAuthRequestToBackend(UUID uuid, String serverId) throws IOException, InterruptedException {
|
||||||
@@ -123,9 +122,16 @@ public class BackendAuthenticator {
|
|||||||
}
|
}
|
||||||
|
|
||||||
JsonObject json = JsonParser.parseString(response.body()).getAsJsonObject();
|
JsonObject json = JsonParser.parseString(response.body()).getAsJsonObject();
|
||||||
if (json.has("token")) {
|
if (!json.has("token")) {
|
||||||
|
System.err.println("[SPMEGA] Invalid response from validate endpoint: " + response.body());
|
||||||
|
throw new IOException("Invalid response from validate endpoint: " + response.body());
|
||||||
|
}
|
||||||
|
if (config == null) {
|
||||||
|
System.err.println("[SPMEGA] Config is null, cannot save token.");
|
||||||
|
throw new IOException("Config is null, cannot save token.");
|
||||||
|
}
|
||||||
|
|
||||||
String token = json.get("token").getAsString();
|
String token = json.get("token").getAsString();
|
||||||
if (config != null) {
|
|
||||||
ModConfig updated = new ModConfig(
|
ModConfig updated = new ModConfig(
|
||||||
config.apiDomain(),
|
config.apiDomain(),
|
||||||
token,
|
token,
|
||||||
@@ -137,14 +143,6 @@ public class BackendAuthenticator {
|
|||||||
SPMega.setConfig(updated);
|
SPMega.setConfig(updated);
|
||||||
System.out.println("[SPMEGA] Backend auth successful, saved token.");
|
System.out.println("[SPMEGA] Backend auth successful, saved token.");
|
||||||
return true;
|
return true;
|
||||||
} else {
|
|
||||||
System.err.println("[SPMEGA] Config is null, cannot save token.");
|
|
||||||
throw new IOException("Config is null, cannot save token.");
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
System.err.println("[SPMEGA] Invalid response from validate endpoint: " + response.body());
|
|
||||||
throw new IOException("Invalid response from validate endpoint: " + response.body());
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public static void sendCardToBackend(String cardId, String cardToken) {
|
public static void sendCardToBackend(String cardId, String cardToken) {
|
||||||
@@ -223,19 +221,13 @@ public class BackendAuthenticator {
|
|||||||
List<CardCredentials> cards = new ArrayList<>();
|
List<CardCredentials> cards = new ArrayList<>();
|
||||||
for (com.google.gson.JsonElement el : cardsArray) {
|
for (com.google.gson.JsonElement el : cardsArray) {
|
||||||
JsonObject cardJson = el.getAsJsonObject();
|
JsonObject cardJson = el.getAsJsonObject();
|
||||||
String cardId = "";
|
String cardId = cardJson.has("cardId") && !cardJson.get("cardId").isJsonNull()
|
||||||
if (cardJson.has("cardId") && !cardJson.get("cardId").isJsonNull()) {
|
? cardJson.get("cardId").getAsString()
|
||||||
cardId = cardJson.get("cardId").getAsString();
|
: (cardJson.has("id") && !cardJson.get("id").isJsonNull() ? cardJson.get("id").getAsString() : "");
|
||||||
} else if (cardJson.has("id") && !cardJson.get("id").isJsonNull()) {
|
|
||||||
cardId = cardJson.get("id").getAsString();
|
|
||||||
}
|
|
||||||
|
|
||||||
String cardToken = "";
|
String cardToken = cardJson.has("cardToken") && !cardJson.get("cardToken").isJsonNull()
|
||||||
if (cardJson.has("cardToken") && !cardJson.get("cardToken").isJsonNull()) {
|
? cardJson.get("cardToken").getAsString()
|
||||||
cardToken = cardJson.get("cardToken").getAsString();
|
: (cardJson.has("token") && !cardJson.get("token").isJsonNull() ? cardJson.get("token").getAsString() : "");
|
||||||
} else if (cardJson.has("token") && !cardJson.get("token").isJsonNull()) {
|
|
||||||
cardToken = cardJson.get("token").getAsString();
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!cardId.isEmpty() && !cardToken.isEmpty()) {
|
if (!cardId.isEmpty() && !cardToken.isEmpty()) {
|
||||||
cards.add(new CardCredentials(cardId, cardToken));
|
cards.add(new CardCredentials(cardId, cardToken));
|
||||||
@@ -299,10 +291,10 @@ public class BackendAuthenticator {
|
|||||||
String url = apiDomain + "/api/v1/transactions";
|
String url = apiDomain + "/api/v1/transactions";
|
||||||
|
|
||||||
JsonObject jsonPayload = new JsonObject();
|
JsonObject jsonPayload = new JsonObject();
|
||||||
jsonPayload.addProperty("senderCardId", senderCardId);
|
jsonPayload.addProperty("cardToUse", senderCardId);
|
||||||
jsonPayload.addProperty("cardId", senderCardId);
|
jsonPayload.addProperty("cardId", senderCardId);
|
||||||
jsonPayload.addProperty("receiver", receiver);
|
jsonPayload.addProperty("receiverCard", receiver);
|
||||||
jsonPayload.addProperty("recipient", receiver);
|
jsonPayload.addProperty("receiverName", receiver);
|
||||||
jsonPayload.addProperty("amount", amount);
|
jsonPayload.addProperty("amount", amount);
|
||||||
jsonPayload.addProperty("comment", comment);
|
jsonPayload.addProperty("comment", comment);
|
||||||
String requestBody = jsonPayload.toString();
|
String requestBody = jsonPayload.toString();
|
||||||
@@ -316,11 +308,10 @@ public class BackendAuthenticator {
|
|||||||
.build();
|
.build();
|
||||||
|
|
||||||
HttpResponse<String> response = httpClient.send(request, HttpResponse.BodyHandlers.ofString());
|
HttpResponse<String> response = httpClient.send(request, HttpResponse.BodyHandlers.ofString());
|
||||||
if (response.statusCode() == 200 || response.statusCode() == 201 || response.statusCode() == 204) {
|
if (response.statusCode() != 200 && response.statusCode() != 201 && response.statusCode() != 204) {
|
||||||
return true;
|
|
||||||
} else {
|
|
||||||
throw new IOException("Server returned status code " + response.statusCode() + ": " + response.body());
|
throw new IOException("Server returned status code " + response.statusCode() + ": " + response.body());
|
||||||
}
|
}
|
||||||
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
public static List<BankDatabase.LocalTransaction> fetchTransactionsFromBackend(String cardId) throws IOException, InterruptedException {
|
public static List<BankDatabase.LocalTransaction> fetchTransactionsFromBackend(String cardId) throws IOException, InterruptedException {
|
||||||
|
|||||||
@@ -5,6 +5,8 @@ import git.yawaflua.tech.spmega.SPMega;
|
|||||||
import git.yawaflua.tech.spmega.api.SPWorldsApiClient;
|
import git.yawaflua.tech.spmega.api.SPWorldsApiClient;
|
||||||
import net.fabricmc.loader.api.FabricLoader;
|
import net.fabricmc.loader.api.FabricLoader;
|
||||||
|
|
||||||
|
import net.minecraft.client.MinecraftClient;
|
||||||
|
|
||||||
import java.util.*;
|
import java.util.*;
|
||||||
import java.util.concurrent.CompletableFuture;
|
import java.util.concurrent.CompletableFuture;
|
||||||
|
|
||||||
@@ -17,7 +19,7 @@ public final class BankUiService {
|
|||||||
private final String apiDomain = "https://spworlds.ru";
|
private final String apiDomain = "https://spworlds.ru";
|
||||||
|
|
||||||
private int selectedCardIndex;
|
private int selectedCardIndex;
|
||||||
private String lastMessage = "";
|
private volatile String lastMessage = "";
|
||||||
|
|
||||||
private BankUiService() {
|
private BankUiService() {
|
||||||
this.database = new BankDatabase(FabricLoader.getInstance().getConfigDir().resolve("spmega.db"));
|
this.database = new BankDatabase(FabricLoader.getInstance().getConfigDir().resolve("spmega.db"));
|
||||||
@@ -54,11 +56,20 @@ public final class BankUiService {
|
|||||||
return normalized.substring(normalized.length() - 5);
|
return normalized.substring(normalized.length() - 5);
|
||||||
}
|
}
|
||||||
|
|
||||||
public synchronized List<CardViewModel> getCards() {
|
private void runOnMainThread(Runnable runnable) {
|
||||||
|
MinecraftClient client = MinecraftClient.getInstance();
|
||||||
|
if (client == null || client.isOnThread()) {
|
||||||
|
runnable.run();
|
||||||
|
} else {
|
||||||
|
client.execute(runnable);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public List<CardViewModel> getCards() {
|
||||||
return Collections.unmodifiableList(cards);
|
return Collections.unmodifiableList(cards);
|
||||||
}
|
}
|
||||||
|
|
||||||
public synchronized CardViewModel getSelectedCard() {
|
public CardViewModel getSelectedCard() {
|
||||||
if (cards.isEmpty()) {
|
if (cards.isEmpty()) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
@@ -66,7 +77,7 @@ public final class BankUiService {
|
|||||||
return cards.get(selectedCardIndex);
|
return cards.get(selectedCardIndex);
|
||||||
}
|
}
|
||||||
|
|
||||||
public synchronized int getSelectedCardIndex() {
|
public int getSelectedCardIndex() {
|
||||||
if (cards.isEmpty()) {
|
if (cards.isEmpty()) {
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
@@ -74,7 +85,7 @@ public final class BankUiService {
|
|||||||
return selectedCardIndex;
|
return selectedCardIndex;
|
||||||
}
|
}
|
||||||
|
|
||||||
public synchronized void setSelectedCardIndex(int index) {
|
public void setSelectedCardIndex(int index) {
|
||||||
if (cards.isEmpty()) {
|
if (cards.isEmpty()) {
|
||||||
selectedCardIndex = 0;
|
selectedCardIndex = 0;
|
||||||
return;
|
return;
|
||||||
@@ -82,7 +93,7 @@ public final class BankUiService {
|
|||||||
selectedCardIndex = Math.floorMod(index, cards.size());
|
selectedCardIndex = Math.floorMod(index, cards.size());
|
||||||
}
|
}
|
||||||
|
|
||||||
public synchronized CardViewModel cycleSelectedCard(int direction) {
|
public CardViewModel cycleSelectedCard(int direction) {
|
||||||
if (cards.isEmpty()) {
|
if (cards.isEmpty()) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
@@ -90,18 +101,19 @@ public final class BankUiService {
|
|||||||
return cards.get(selectedCardIndex);
|
return cards.get(selectedCardIndex);
|
||||||
}
|
}
|
||||||
|
|
||||||
public synchronized String getLastMessage() {
|
public String getLastMessage() {
|
||||||
return lastMessage;
|
return lastMessage;
|
||||||
}
|
}
|
||||||
|
|
||||||
public synchronized void syncCardsWithBackend(String playerUuid) {
|
public CompletableFuture<Void> syncCardsWithBackendAsync(String playerUuid) {
|
||||||
|
return CompletableFuture.runAsync(() -> {
|
||||||
try {
|
try {
|
||||||
List<CardCredentials> backendCards = BackendAuthenticator.fetchCardsFromBackend();
|
List<CardCredentials> backendCards = BackendAuthenticator.fetchCardsFromBackend();
|
||||||
boolean changed = false;
|
boolean changed = false;
|
||||||
for (CardCredentials creds : backendCards) {
|
for (CardCredentials creds : backendCards) {
|
||||||
if (database.getCredentials(creds.cardId()) == null) {
|
if (database.getCredentials(creds.cardId()) == null) {
|
||||||
database.upsertCardCredentials(creds.cardId(), creds.cardToken());
|
database.upsertCardCredentials(creds.cardId(), creds.cardToken());
|
||||||
refreshCard(creds.cardId(), creds.cardToken(), playerUuid, false, false);
|
refreshCardSync(creds.cardId(), creds.cardToken(), playerUuid, false, false);
|
||||||
changed = true;
|
changed = true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -111,24 +123,38 @@ public final class BankUiService {
|
|||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
System.err.println("[SPMEGA] Failed to sync cards with backend: " + e.getMessage());
|
System.err.println("[SPMEGA] Failed to sync cards with backend: " + e.getMessage());
|
||||||
}
|
}
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
public synchronized void refreshOnServerJoin(String playerUuid) {
|
public CompletableFuture<Void> refreshOnServerJoinAsync(String playerUuid) {
|
||||||
syncCardsWithBackend(playerUuid);
|
return CompletableFuture.runAsync(() -> {
|
||||||
|
try {
|
||||||
|
List<CardCredentials> backendCards = BackendAuthenticator.fetchCardsFromBackend();
|
||||||
|
for (CardCredentials creds : backendCards) {
|
||||||
|
if (database.getCredentials(creds.cardId()) == null) {
|
||||||
|
database.upsertCardCredentials(creds.cardId(), creds.cardToken());
|
||||||
|
refreshCardSync(creds.cardId(), creds.cardToken(), playerUuid, false, false);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} catch (Exception e) {
|
||||||
|
System.err.println("[SPMEGA] Failed to sync cards with backend: " + e.getMessage());
|
||||||
|
}
|
||||||
|
|
||||||
List<StoredCard> storedCards = database.loadCards();
|
List<StoredCard> storedCards = database.loadCards();
|
||||||
for (StoredCard card : storedCards) {
|
for (StoredCard card : storedCards) {
|
||||||
refreshCard(card.cardId(), card.cardToken(), playerUuid, false, false);
|
refreshCardSync(card.cardId(), card.cardToken(), playerUuid, false, false);
|
||||||
}
|
}
|
||||||
reloadCardsFromDb();
|
reloadCardsFromDb();
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
public synchronized List<String> loadRecipientCards(String username) {
|
public CompletableFuture<List<String>> loadRecipientCardsAsync(String username) {
|
||||||
CardCredentials credentials = getSelectedCredentials();
|
CardCredentials credentials = getSelectedCredentials();
|
||||||
if (credentials == null || username == null || username.isBlank()) {
|
if (credentials == null || username == null || username.isBlank()) {
|
||||||
return List.of();
|
return CompletableFuture.completedFuture(List.of());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
return CompletableFuture.supplyAsync(() -> {
|
||||||
try {
|
try {
|
||||||
List<SPWorldsApiClient.PlayerCard> apiCards = apiClient.getPlayerCards(username, toApiAuth(credentials));
|
List<SPWorldsApiClient.PlayerCard> apiCards = apiClient.getPlayerCards(username, toApiAuth(credentials));
|
||||||
List<String> numbers = new ArrayList<>();
|
List<String> numbers = new ArrayList<>();
|
||||||
@@ -143,75 +169,82 @@ public final class BankUiService {
|
|||||||
lastMessage = "Не удалось получить карты игрока: " + exception.getMessage();
|
lastMessage = "Не удалось получить карты игрока: " + exception.getMessage();
|
||||||
return List.of();
|
return List.of();
|
||||||
}
|
}
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
public synchronized String addCard(String cardIdRaw, String cardTokenRaw, String playerUuid) {
|
public CompletableFuture<String> addCardAsync(String cardIdRaw, String cardTokenRaw, String playerUuid) {
|
||||||
String cardId = cardIdRaw == null ? "" : cardIdRaw.trim();
|
String cardId = cardIdRaw == null ? "" : cardIdRaw.trim();
|
||||||
String cardToken = cardTokenRaw == null ? "" : cardTokenRaw.trim();
|
String cardToken = cardTokenRaw == null ? "" : cardTokenRaw.trim();
|
||||||
|
|
||||||
if (cardId.isEmpty() || cardToken.isEmpty()) {
|
if (cardId.isEmpty() || cardToken.isEmpty()) {
|
||||||
lastMessage = "Укажи cardId и cardToken";
|
return CompletableFuture.completedFuture("Укажи cardId и cardToken");
|
||||||
return lastMessage;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
try {
|
try {
|
||||||
UUID.fromString(cardId);
|
UUID.fromString(cardId);
|
||||||
} catch (IllegalArgumentException exception) {
|
} catch (IllegalArgumentException exception) {
|
||||||
lastMessage = "cardId должен быть UUID";
|
return CompletableFuture.completedFuture("cardId должен быть UUID");
|
||||||
return lastMessage;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
return CompletableFuture.supplyAsync(() -> {
|
||||||
database.upsertCardCredentials(cardId, cardToken);
|
database.upsertCardCredentials(cardId, cardToken);
|
||||||
boolean refreshed = refreshCard(cardId, cardToken, playerUuid, true, true);
|
boolean refreshed = refreshCardSync(cardId, cardToken, playerUuid, true, true);
|
||||||
if (!refreshed) {
|
if (!refreshed) {
|
||||||
database.deleteCard(cardId);
|
database.deleteCard(cardId);
|
||||||
}
|
}
|
||||||
reloadCardsFromDb();
|
reloadCardsFromDb();
|
||||||
|
|
||||||
if (refreshed && lastMessage.isBlank()) {
|
if (refreshed && lastMessage.isBlank()) {
|
||||||
lastMessage = "Карта добавлена";
|
return "Карта добавлена";
|
||||||
}
|
}
|
||||||
return lastMessage;
|
return lastMessage;
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
public synchronized void removeSelectedCard() {
|
public CompletableFuture<String> removeSelectedCardAsync() {
|
||||||
CardViewModel selected = getSelectedCard();
|
CardViewModel selected = getSelectedCard();
|
||||||
if (selected == null) {
|
if (selected == null) {
|
||||||
lastMessage = "Нет карты для удаления";
|
return CompletableFuture.completedFuture("Нет карты для удаления");
|
||||||
return;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
String cardId = selected.id();
|
String cardId = selected.id();
|
||||||
|
return CompletableFuture.supplyAsync(() -> {
|
||||||
database.deleteCard(cardId);
|
database.deleteCard(cardId);
|
||||||
ModConfig config = SPMega.getConfig();
|
ModConfig config = SPMega.getConfig();
|
||||||
if (config.allowBackend())
|
if (config != null && config.allowBackend()) {
|
||||||
BackendAuthenticator.deleteCardOnBackend(cardId);
|
BackendAuthenticator.deleteCardOnBackend(cardId);
|
||||||
|
}
|
||||||
|
|
||||||
reloadCardsFromDb();
|
reloadCardsFromDb();
|
||||||
|
runOnMainThread(() -> {
|
||||||
if (selectedCardIndex >= cards.size()) {
|
if (selectedCardIndex >= cards.size()) {
|
||||||
selectedCardIndex = Math.max(0, cards.size() - 1);
|
selectedCardIndex = Math.max(0, cards.size() - 1);
|
||||||
}
|
}
|
||||||
lastMessage = "Карта удалена";
|
});
|
||||||
|
return "Карта удалена";
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
public synchronized void refreshSelectedCard(String playerUuid) {
|
public CompletableFuture<String> refreshSelectedCardAsync(String playerUuid) {
|
||||||
CardViewModel selected = getSelectedCard();
|
CardViewModel selected = getSelectedCard();
|
||||||
if (selected == null) {
|
if (selected == null) {
|
||||||
lastMessage = "Нет карты для обновления";
|
return CompletableFuture.completedFuture("Нет карты для обновления");
|
||||||
return;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
return CompletableFuture.supplyAsync(() -> {
|
||||||
CardCredentials credentials = database.getCredentials(selected.id());
|
CardCredentials credentials = database.getCredentials(selected.id());
|
||||||
if (credentials == null) {
|
if (credentials == null) {
|
||||||
lastMessage = "Не найдены креды карты";
|
return "Не найдены креды карты";
|
||||||
return;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
refreshCard(credentials.cardId(), credentials.cardToken(), playerUuid, false, false);
|
refreshCardSync(credentials.cardId(), credentials.cardToken(), playerUuid, false, false);
|
||||||
reloadCardsFromDb();
|
reloadCardsFromDb();
|
||||||
|
return lastMessage.isBlank() ? "Карта обновлена" : lastMessage;
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
public synchronized boolean submitPayment(PaymentDraft draft) {
|
public CompletableFuture<Boolean> submitPaymentAsync(PaymentDraft draft) {
|
||||||
|
return CompletableFuture.supplyAsync(() -> {
|
||||||
CardCredentials credentials = database.getCredentials(draft.senderCardId());
|
CardCredentials credentials = database.getCredentials(draft.senderCardId());
|
||||||
if (credentials == null) {
|
if (credentials == null) {
|
||||||
lastMessage = "Не найдены креды карты отправителя";
|
lastMessage = "Не найдены креды карты отправителя";
|
||||||
@@ -231,7 +264,7 @@ public final class BankUiService {
|
|||||||
draft.comment()
|
draft.comment()
|
||||||
);
|
);
|
||||||
try {
|
try {
|
||||||
refreshCard(credentials.cardId(), credentials.cardToken(), "", false, false);
|
refreshCardSync(credentials.cardId(), credentials.cardToken(), "", false, false);
|
||||||
StoredCard updatedCard = database.loadCards().stream()
|
StoredCard updatedCard = database.loadCards().stream()
|
||||||
.filter(c -> c.cardId().equals(credentials.cardId()))
|
.filter(c -> c.cardId().equals(credentials.cardId()))
|
||||||
.findFirst()
|
.findFirst()
|
||||||
@@ -277,13 +310,10 @@ public final class BankUiService {
|
|||||||
lastMessage = "Ошибка перевода: " + exception.getMessage();
|
lastMessage = "Ошибка перевода: " + exception.getMessage();
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
public CompletableFuture<Boolean> submitPaymentAsync(PaymentDraft draft) {
|
private boolean refreshCardSync(
|
||||||
return CompletableFuture.supplyAsync(() -> submitPayment(draft));
|
|
||||||
}
|
|
||||||
|
|
||||||
private boolean refreshCard(
|
|
||||||
String cardId,
|
String cardId,
|
||||||
String cardToken,
|
String cardToken,
|
||||||
String playerUuid,
|
String playerUuid,
|
||||||
@@ -319,12 +349,9 @@ public final class BankUiService {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
git.yawaflua.tech.spmega.ModConfig config = git.yawaflua.tech.spmega.SPMega.getConfig();
|
git.yawaflua.tech.spmega.ModConfig config = git.yawaflua.tech.spmega.SPMega.getConfig();
|
||||||
if (config.allowBackend())
|
if (config != null && config.allowBackend()) {
|
||||||
CompletableFuture.supplyAsync(() -> {
|
|
||||||
BackendAuthenticator.sendCardToBackend(cardId, cardToken);
|
BackendAuthenticator.sendCardToBackend(cardId, cardToken);
|
||||||
return true;
|
|
||||||
}
|
}
|
||||||
);
|
|
||||||
|
|
||||||
lastMessage = "";
|
lastMessage = "";
|
||||||
return true;
|
return true;
|
||||||
@@ -336,8 +363,8 @@ public final class BankUiService {
|
|||||||
|
|
||||||
private void reloadCardsFromDb() {
|
private void reloadCardsFromDb() {
|
||||||
List<StoredCard> storedCards = database.loadCards();
|
List<StoredCard> storedCards = database.loadCards();
|
||||||
|
runOnMainThread(() -> {
|
||||||
cards.clear();
|
cards.clear();
|
||||||
|
|
||||||
for (StoredCard stored : storedCards) {
|
for (StoredCard stored : storedCards) {
|
||||||
String cardNumber = stored.cardNumber() == null || stored.cardNumber().isBlank()
|
String cardNumber = stored.cardNumber() == null || stored.cardNumber().isBlank()
|
||||||
? extractLastDigits(stored.cardId())
|
? extractLastDigits(stored.cardId())
|
||||||
@@ -352,6 +379,7 @@ public final class BankUiService {
|
|||||||
} else {
|
} else {
|
||||||
selectedCardIndex = Math.floorMod(selectedCardIndex, cards.size());
|
selectedCardIndex = Math.floorMod(selectedCardIndex, cards.size());
|
||||||
}
|
}
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
private CardCredentials getSelectedCredentials() {
|
private CardCredentials getSelectedCredentials() {
|
||||||
|
|||||||
@@ -3,12 +3,20 @@
|
|||||||
"id": "spmega",
|
"id": "spmega",
|
||||||
"version": "${version}",
|
"version": "${version}",
|
||||||
"name": "SPMega",
|
"name": "SPMega",
|
||||||
"description": "yawaflua`s SPRadar+SPMHelper mod, thats make a lot!",
|
"description": "yawaflua's SPRadar+SPMHelper mod, that's make a lot!",
|
||||||
"authors": [
|
"authors": [
|
||||||
"Dmitri 'yawaflua' Shimanski aka DOLBAYEB"
|
{
|
||||||
|
"name": "Dmitri 'yawaflua' Shimanski aka DOLBAYEB",
|
||||||
|
"contact": {
|
||||||
|
"homepage": "https://yawaflua.tech",
|
||||||
|
"email": "spmega@yawaflua.tech"
|
||||||
|
}
|
||||||
|
}
|
||||||
],
|
],
|
||||||
"contact": {
|
"contact": {
|
||||||
|
"homepage": "https://yawaflua.tech",
|
||||||
|
"issues": "https://git.yawaflua.tech/spmega",
|
||||||
|
"sources": "https://git.yawaflua.tech/spmega"
|
||||||
},
|
},
|
||||||
"license": "CC BY-NC-ND 4.0",
|
"license": "CC BY-NC-ND 4.0",
|
||||||
"icon": "icon.png",
|
"icon": "icon.png",
|
||||||
|
|||||||
Reference in New Issue
Block a user