workign on local server
This commit is contained in:
7
.gitignore
vendored
7
.gitignore
vendored
@@ -1,4 +1,9 @@
|
|||||||
build/
|
build/
|
||||||
.gradle/
|
.gradle/
|
||||||
gradle/
|
gradle/
|
||||||
.gradle_cache/
|
.gradle_cache/
|
||||||
|
COBBLEVERSE-1.7.30-CF.zip
|
||||||
|
.env
|
||||||
|
modpacks/
|
||||||
|
minecraft-data/
|
||||||
|
out.log
|
||||||
3
cobble-api.http
Normal file
3
cobble-api.http
Normal file
@@ -0,0 +1,3 @@
|
|||||||
|
@host = http://localhost:8085
|
||||||
|
|
||||||
|
GET {{host}}/battles
|
||||||
26
docker-compose.yml
Normal file
26
docker-compose.yml
Normal file
@@ -0,0 +1,26 @@
|
|||||||
|
services:
|
||||||
|
minecraft:
|
||||||
|
image: itzg/minecraft-server:java21
|
||||||
|
stdin_open: true
|
||||||
|
tty: true
|
||||||
|
ports:
|
||||||
|
- "2222:2222" # Minecraft
|
||||||
|
- "25576:25576" # RCON
|
||||||
|
- "8085:8085" # Cobblemon Battle API
|
||||||
|
environment:
|
||||||
|
EULA: "true"
|
||||||
|
TYPE: "AUTO_CURSEFORGE"
|
||||||
|
CF_SLUG: "cobbleverse-cobblemon"
|
||||||
|
CF_MODPACK_ZIP: "/modpacks/COBBLEVERSE-1.7.30-CF.zip"
|
||||||
|
MEMORY: "4G"
|
||||||
|
SERVER_PORT: "2222"
|
||||||
|
RCON_PORT: "25576"
|
||||||
|
CF_OVERRIDES_EXCLUSIONS: |
|
||||||
|
shaderpacks/**
|
||||||
|
resourcepacks/**
|
||||||
|
env_file:
|
||||||
|
- .env
|
||||||
|
volumes:
|
||||||
|
- ./minecraft-data:/data
|
||||||
|
- ./COBBLEVERSE-1.7.30-CF.zip:/modpacks/COBBLEVERSE-1.7.30-CF.zip:ro
|
||||||
|
restart: unless-stopped
|
||||||
@@ -15,15 +15,13 @@ import com.cobblemon.mod.common.battles.BattleRegistry;
|
|||||||
import com.cobblemon.mod.common.battles.ActiveBattlePokemon;
|
import com.cobblemon.mod.common.battles.ActiveBattlePokemon;
|
||||||
import com.cobblemon.mod.common.battles.actor.PlayerBattleActor;
|
import com.cobblemon.mod.common.battles.actor.PlayerBattleActor;
|
||||||
import com.cobblemon.mod.common.api.storage.party.PlayerPartyStore;
|
import com.cobblemon.mod.common.api.storage.party.PlayerPartyStore;
|
||||||
import com.cobblemon.mod.common.api.storage.pc.PCStore;
|
|
||||||
import com.cobblemon.mod.common.Cobblemon;
|
import com.cobblemon.mod.common.Cobblemon;
|
||||||
import com.cobblemon.mod.common.pokemon.Pokemon;
|
import com.cobblemon.mod.common.pokemon.Pokemon;
|
||||||
|
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.io.OutputStream;
|
import java.io.OutputStream;
|
||||||
import java.net.InetSocketAddress;
|
import java.net.InetSocketAddress;
|
||||||
import java.util.ArrayList;
|
import java.util.LinkedHashMap;
|
||||||
import java.util.List;
|
|
||||||
import java.util.UUID;
|
import java.util.UUID;
|
||||||
import java.util.concurrent.CompletableFuture;
|
import java.util.concurrent.CompletableFuture;
|
||||||
|
|
||||||
@@ -31,15 +29,12 @@ import org.slf4j.Logger;
|
|||||||
import org.slf4j.LoggerFactory;
|
import org.slf4j.LoggerFactory;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Fabric server-side mod that exposes Cobblemon battle and Pokémon data
|
* Fabric mod exposing a single HTTP endpoint:
|
||||||
* over a lightweight HTTP API.
|
|
||||||
*
|
*
|
||||||
* Endpoints:
|
* GET /battles — all active battles; each player actor includes their
|
||||||
* GET /player/<uuid> — is the player in a battle?
|
* playerId, active Pokémon (no moves), and full party
|
||||||
* GET /player/<uuid>/battle — full battle state for that player
|
* with moves. Opponent move sets are only present under
|
||||||
* GET /player/<uuid>/party — player's party Pokémon
|
* that opponent's own actor block.
|
||||||
* GET /player/<uuid>/pc — player's PC Pokémon
|
|
||||||
* GET /battles — list all active battles
|
|
||||||
*/
|
*/
|
||||||
public class CobbleBattleApiMod implements ModInitializer {
|
public class CobbleBattleApiMod implements ModInitializer {
|
||||||
|
|
||||||
@@ -51,12 +46,10 @@ public class CobbleBattleApiMod implements ModInitializer {
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onInitialize() {
|
public void onInitialize() {
|
||||||
|
|
||||||
ServerLifecycleEvents.SERVER_STARTED.register(mc -> {
|
ServerLifecycleEvents.SERVER_STARTED.register(mc -> {
|
||||||
this.server = mc;
|
this.server = mc;
|
||||||
startHttpServer();
|
startHttpServer();
|
||||||
});
|
});
|
||||||
|
|
||||||
ServerLifecycleEvents.SERVER_STOPPING.register(mc -> {
|
ServerLifecycleEvents.SERVER_STOPPING.register(mc -> {
|
||||||
stopHttpServer();
|
stopHttpServer();
|
||||||
this.server = null;
|
this.server = null;
|
||||||
@@ -68,7 +61,6 @@ public class CobbleBattleApiMod implements ModInitializer {
|
|||||||
private void startHttpServer() {
|
private void startHttpServer() {
|
||||||
try {
|
try {
|
||||||
httpServer = HttpServer.create(new InetSocketAddress(PORT), 0);
|
httpServer = HttpServer.create(new InetSocketAddress(PORT), 0);
|
||||||
httpServer.createContext("/player", this::handlePlayer);
|
|
||||||
httpServer.createContext("/battles", this::handleBattles);
|
httpServer.createContext("/battles", this::handleBattles);
|
||||||
httpServer.start();
|
httpServer.start();
|
||||||
LOGGER.info("CobbleBattleAPI listening on port {}", PORT);
|
LOGGER.info("CobbleBattleAPI listening on port {}", PORT);
|
||||||
@@ -84,64 +76,13 @@ public class CobbleBattleApiMod implements ModInitializer {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// ─── /player/<uuid>[/battle|/party|/pc] ─────────────────────────────
|
// ─── GET /battles ────────────────────────────────────────────────────
|
||||||
|
|
||||||
private void handlePlayer(HttpExchange exchange) throws IOException {
|
|
||||||
if (!"GET".equalsIgnoreCase(exchange.getRequestMethod())) {
|
|
||||||
send(exchange, 405, json("error", "method not allowed"));
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
String path = exchange.getRequestURI().getPath(); // e.g. /player/<uuid>/battle
|
|
||||||
String[] parts = path.split("/"); // ["", "player", "<uuid>", ...]
|
|
||||||
|
|
||||||
if (parts.length < 3) {
|
|
||||||
send(exchange, 400, json("error", "missing uuid"));
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
UUID uuid;
|
|
||||||
try {
|
|
||||||
uuid = UUID.fromString(parts[2]);
|
|
||||||
} catch (IllegalArgumentException e) {
|
|
||||||
send(exchange, 400, json("error", "invalid uuid"));
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
// We need to touch the MC server on the server thread.
|
|
||||||
String subRoute = parts.length >= 4 ? parts[3] : "";
|
|
||||||
String response = runOnServerThread(() -> dispatch(uuid, subRoute));
|
|
||||||
|
|
||||||
if (response == null) {
|
|
||||||
send(exchange, 500, json("error", "server unavailable"));
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
send(exchange, 200, response);
|
|
||||||
}
|
|
||||||
|
|
||||||
private String dispatch(UUID uuid, String subRoute) {
|
|
||||||
ServerPlayerEntity player = server.getPlayerManager().getPlayer(uuid);
|
|
||||||
if (player == null) {
|
|
||||||
return json("error", "player not online");
|
|
||||||
}
|
|
||||||
|
|
||||||
return switch (subRoute) {
|
|
||||||
case "battle" -> buildBattleState(uuid, player);
|
|
||||||
case "party" -> buildParty(uuid, player);
|
|
||||||
case "pc" -> buildPc(uuid, player);
|
|
||||||
default -> buildBasicStatus(uuid, player);
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
// ─── /battles ───────────────────────────────────────────────────────
|
|
||||||
|
|
||||||
private void handleBattles(HttpExchange exchange) throws IOException {
|
private void handleBattles(HttpExchange exchange) throws IOException {
|
||||||
if (!"GET".equalsIgnoreCase(exchange.getRequestMethod())) {
|
if (!"GET".equalsIgnoreCase(exchange.getRequestMethod())) {
|
||||||
send(exchange, 405, json("error", "method not allowed"));
|
send(exchange, 405, json("error", "method not allowed"));
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
String response = runOnServerThread(this::buildAllBattles);
|
String response = runOnServerThread(this::buildAllBattles);
|
||||||
if (response == null) {
|
if (response == null) {
|
||||||
send(exchange, 500, json("error", "server unavailable"));
|
send(exchange, 500, json("error", "server unavailable"));
|
||||||
@@ -150,120 +91,15 @@ public class CobbleBattleApiMod implements ModInitializer {
|
|||||||
send(exchange, 200, response);
|
send(exchange, 200, response);
|
||||||
}
|
}
|
||||||
|
|
||||||
// ─── Response builders ──────────────────────────────────────────────
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* GET /player/<uuid> → { uuid, inBattle }
|
* Collects all active battles, deduplicating by battleId.
|
||||||
*/
|
* Each PlayerBattleActor includes:
|
||||||
private String buildBasicStatus(UUID uuid, ServerPlayerEntity player) {
|
* - playerId, name, type
|
||||||
boolean inBattle = findBattle(uuid) != null;
|
* - activePokemon: species/level/hp only (no moves — hides move info from opponent reads)
|
||||||
StringBuilder sb = new StringBuilder();
|
* - party: full Pokémon data including moves (keyed to this actor only)
|
||||||
sb.append('{');
|
|
||||||
jsonField(sb, "uuid", uuid.toString());
|
|
||||||
sb.append(',');
|
|
||||||
jsonFieldRaw(sb, "inBattle", String.valueOf(inBattle));
|
|
||||||
sb.append('}');
|
|
||||||
return sb.toString();
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* GET /player/<uuid>/battle → full battle snapshot
|
|
||||||
*/
|
|
||||||
private String buildBattleState(UUID uuid, ServerPlayerEntity player) {
|
|
||||||
PokemonBattle battle = findBattle(uuid);
|
|
||||||
if (battle == null) {
|
|
||||||
return json("error", "not in battle");
|
|
||||||
}
|
|
||||||
|
|
||||||
StringBuilder sb = new StringBuilder();
|
|
||||||
sb.append('{');
|
|
||||||
jsonField(sb, "battleId", battle.getBattleId().toString());
|
|
||||||
sb.append(',');
|
|
||||||
|
|
||||||
// actors
|
|
||||||
sb.append("\"actors\":[");
|
|
||||||
boolean first = true;
|
|
||||||
for (BattleActor actor : battle.getActors()) {
|
|
||||||
if (!first) sb.append(',');
|
|
||||||
first = false;
|
|
||||||
sb.append('{');
|
|
||||||
jsonField(sb, "name", actor.getName().getString());
|
|
||||||
sb.append(',');
|
|
||||||
|
|
||||||
String type = (actor instanceof PlayerBattleActor) ? "player" : "npc";
|
|
||||||
jsonField(sb, "type", type);
|
|
||||||
sb.append(',');
|
|
||||||
|
|
||||||
// active pokémon
|
|
||||||
sb.append("\"activePokemon\":[");
|
|
||||||
boolean afirst = true;
|
|
||||||
for (ActiveBattlePokemon abp : actor.getActivePokemon()) {
|
|
||||||
if (abp.getBattlePokemon() == null) continue;
|
|
||||||
if (!afirst) sb.append(',');
|
|
||||||
afirst = false;
|
|
||||||
var bp = abp.getBattlePokemon();
|
|
||||||
sb.append('{');
|
|
||||||
jsonField(sb, "species", bp.getOriginalPokemon().getSpecies().getName());
|
|
||||||
sb.append(',');
|
|
||||||
jsonFieldRaw(sb, "level", String.valueOf(bp.getOriginalPokemon().getLevel()));
|
|
||||||
sb.append(',');
|
|
||||||
jsonFieldRaw(sb, "hp", String.valueOf(bp.getHealth()));
|
|
||||||
sb.append(',');
|
|
||||||
jsonFieldRaw(sb, "maxHp", String.valueOf(bp.getMaxHealth()));
|
|
||||||
sb.append('}');
|
|
||||||
}
|
|
||||||
sb.append(']');
|
|
||||||
sb.append('}');
|
|
||||||
}
|
|
||||||
sb.append(']');
|
|
||||||
sb.append('}');
|
|
||||||
return sb.toString();
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* GET /player/<uuid>/party → party pokémon list
|
|
||||||
*/
|
|
||||||
private String buildParty(UUID uuid, ServerPlayerEntity player) {
|
|
||||||
PlayerPartyStore party = Cobblemon.INSTANCE.getStorage().getParty(player);
|
|
||||||
StringBuilder sb = new StringBuilder();
|
|
||||||
sb.append("{\"uuid\":\"").append(uuid).append("\",\"party\":[");
|
|
||||||
boolean first = true;
|
|
||||||
for (Pokemon p : party.toGappyList()) {
|
|
||||||
if (p == null) continue;
|
|
||||||
if (!first) sb.append(',');
|
|
||||||
first = false;
|
|
||||||
appendPokemonJson(sb, p);
|
|
||||||
}
|
|
||||||
sb.append("]}");
|
|
||||||
return sb.toString();
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* GET /player/<uuid>/pc → PC pokémon list
|
|
||||||
*/
|
|
||||||
private String buildPc(UUID uuid, ServerPlayerEntity player) {
|
|
||||||
PCStore pc = Cobblemon.INSTANCE.getStorage().getPC(player);
|
|
||||||
StringBuilder sb = new StringBuilder();
|
|
||||||
sb.append("{\"uuid\":\"").append(uuid).append("\",\"pc\":[");
|
|
||||||
boolean first = true;
|
|
||||||
for (com.cobblemon.mod.common.api.storage.pc.PCBox box : pc.getBoxes()) {
|
|
||||||
for (Pokemon p : box.getNonEmptySlots().values()) {
|
|
||||||
if (p == null) continue;
|
|
||||||
if (!first) sb.append(',');
|
|
||||||
first = false;
|
|
||||||
appendPokemonJson(sb, p);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
sb.append("]}");
|
|
||||||
return sb.toString();
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* GET /battles → all active battles
|
|
||||||
*/
|
*/
|
||||||
private String buildAllBattles() {
|
private String buildAllBattles() {
|
||||||
// No getBattles() in this Cobblemon version — collect battles from online players
|
LinkedHashMap<UUID, PokemonBattle> seen = new LinkedHashMap<>();
|
||||||
java.util.LinkedHashMap<UUID, PokemonBattle> seen = new java.util.LinkedHashMap<>();
|
|
||||||
for (ServerPlayerEntity p : server.getPlayerManager().getPlayerList()) {
|
for (ServerPlayerEntity p : server.getPlayerManager().getPlayerList()) {
|
||||||
PokemonBattle b = BattleRegistry.INSTANCE.getBattleByParticipatingPlayer(p);
|
PokemonBattle b = BattleRegistry.INSTANCE.getBattleByParticipatingPlayer(p);
|
||||||
if (b != null) {
|
if (b != null) {
|
||||||
@@ -273,38 +109,110 @@ public class CobbleBattleApiMod implements ModInitializer {
|
|||||||
|
|
||||||
StringBuilder sb = new StringBuilder();
|
StringBuilder sb = new StringBuilder();
|
||||||
sb.append("{\"battles\":[");
|
sb.append("{\"battles\":[");
|
||||||
boolean first = true;
|
boolean firstBattle = true;
|
||||||
|
|
||||||
for (PokemonBattle battle : seen.values()) {
|
for (PokemonBattle battle : seen.values()) {
|
||||||
if (!first) sb.append(',');
|
if (!firstBattle) sb.append(',');
|
||||||
first = false;
|
firstBattle = false;
|
||||||
|
|
||||||
sb.append('{');
|
sb.append('{');
|
||||||
jsonField(sb, "battleId", battle.getBattleId().toString());
|
jsonField(sb, "battleId", battle.getBattleId().toString());
|
||||||
sb.append(',');
|
sb.append(",\"actors\":[");
|
||||||
sb.append("\"actors\":[");
|
|
||||||
boolean afirst = true;
|
boolean firstActor = true;
|
||||||
for (BattleActor actor : battle.getActors()) {
|
for (BattleActor actor : battle.getActors()) {
|
||||||
if (!afirst) sb.append(',');
|
if (!firstActor) sb.append(',');
|
||||||
afirst = false;
|
firstActor = false;
|
||||||
|
|
||||||
sb.append('{');
|
sb.append('{');
|
||||||
jsonField(sb, "name", actor.getName().getString());
|
jsonField(sb, "name", actor.getName().getString());
|
||||||
sb.append(',');
|
sb.append(',');
|
||||||
String type = (actor instanceof PlayerBattleActor) ? "player" : "npc";
|
|
||||||
jsonField(sb, "type", type);
|
if (actor instanceof PlayerBattleActor playerActor) {
|
||||||
sb.append('}');
|
jsonField(sb, "type", "player");
|
||||||
|
sb.append(',');
|
||||||
|
|
||||||
|
UUID playerId = playerActor.getUuid();
|
||||||
|
jsonField(sb, "playerId", playerId.toString());
|
||||||
|
|
||||||
|
// Active Pokémon — species/level/hp, intentionally no moves
|
||||||
|
sb.append(",\"activePokemon\":[");
|
||||||
|
boolean firstActive = true;
|
||||||
|
for (ActiveBattlePokemon abp : actor.getActivePokemon()) {
|
||||||
|
if (abp.getBattlePokemon() == null) continue;
|
||||||
|
if (!firstActive) sb.append(',');
|
||||||
|
firstActive = false;
|
||||||
|
var bp = abp.getBattlePokemon();
|
||||||
|
var orig = bp.getOriginalPokemon();
|
||||||
|
sb.append('{');
|
||||||
|
jsonField(sb, "uuid", orig.getUuid().toString());
|
||||||
|
sb.append(',');
|
||||||
|
jsonField(sb, "species", orig.getSpecies().getName());
|
||||||
|
sb.append(',');
|
||||||
|
jsonFieldRaw(sb, "level", String.valueOf(orig.getLevel()));
|
||||||
|
sb.append(',');
|
||||||
|
jsonFieldRaw(sb, "hp", String.valueOf(bp.getHealth()));
|
||||||
|
sb.append(',');
|
||||||
|
jsonFieldRaw(sb, "maxHp", String.valueOf(bp.getMaxHealth()));
|
||||||
|
sb.append('}');
|
||||||
|
}
|
||||||
|
sb.append(']');
|
||||||
|
|
||||||
|
// Party — full data including moves, scoped to this player only
|
||||||
|
ServerPlayerEntity playerEntity = server.getPlayerManager().getPlayer(playerId);
|
||||||
|
sb.append(",\"party\":[");
|
||||||
|
if (playerEntity != null) {
|
||||||
|
PlayerPartyStore party = Cobblemon.INSTANCE.getStorage().getParty(playerEntity);
|
||||||
|
boolean firstParty = true;
|
||||||
|
for (Pokemon p : party.toGappyList()) {
|
||||||
|
if (p == null) continue;
|
||||||
|
if (!firstParty) sb.append(',');
|
||||||
|
firstParty = false;
|
||||||
|
appendPokemonJson(sb, p);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
sb.append(']');
|
||||||
|
|
||||||
|
} else {
|
||||||
|
// NPC actor — name and type only, no party or move data
|
||||||
|
jsonField(sb, "type", "npc");
|
||||||
|
|
||||||
|
sb.append(",\"activePokemon\":[");
|
||||||
|
boolean firstActive = true;
|
||||||
|
for (ActiveBattlePokemon abp : actor.getActivePokemon()) {
|
||||||
|
if (abp.getBattlePokemon() == null) continue;
|
||||||
|
if (!firstActive) sb.append(',');
|
||||||
|
firstActive = false;
|
||||||
|
var bp = abp.getBattlePokemon();
|
||||||
|
var orig = bp.getOriginalPokemon();
|
||||||
|
sb.append('{');
|
||||||
|
jsonField(sb, "uuid", orig.getUuid().toString());
|
||||||
|
sb.append(',');
|
||||||
|
jsonField(sb, "species", orig.getSpecies().getName());
|
||||||
|
sb.append(',');
|
||||||
|
jsonFieldRaw(sb, "level", String.valueOf(orig.getLevel()));
|
||||||
|
sb.append(',');
|
||||||
|
jsonFieldRaw(sb, "hp", String.valueOf(bp.getHealth()));
|
||||||
|
sb.append(',');
|
||||||
|
jsonFieldRaw(sb, "maxHp", String.valueOf(bp.getMaxHealth()));
|
||||||
|
sb.append('}');
|
||||||
|
}
|
||||||
|
sb.append(']');
|
||||||
|
}
|
||||||
|
|
||||||
|
sb.append('}'); // end actor
|
||||||
}
|
}
|
||||||
sb.append(']');
|
|
||||||
sb.append('}');
|
sb.append("]}"); // end actors + battle
|
||||||
}
|
}
|
||||||
sb.append("]}");
|
|
||||||
|
sb.append("]}"); // end battles
|
||||||
return sb.toString();
|
return sb.toString();
|
||||||
}
|
}
|
||||||
|
|
||||||
// ─── Helpers ────────────────────────────────────────────────────────
|
// ─── Helpers ────────────────────────────────────────────────────────
|
||||||
|
|
||||||
private PokemonBattle findBattle(UUID playerUuid) {
|
/** Full Pokémon snapshot including moves — only used for the owning player's party. */
|
||||||
return BattleRegistry.INSTANCE.getBattleByParticipatingPlayerId(playerUuid);
|
|
||||||
}
|
|
||||||
|
|
||||||
private void appendPokemonJson(StringBuilder sb, Pokemon p) {
|
private void appendPokemonJson(StringBuilder sb, Pokemon p) {
|
||||||
sb.append('{');
|
sb.append('{');
|
||||||
jsonField(sb, "uuid", p.getUuid().toString());
|
jsonField(sb, "uuid", p.getUuid().toString());
|
||||||
@@ -322,8 +230,6 @@ public class CobbleBattleApiMod implements ModInitializer {
|
|||||||
jsonField(sb, "nature", p.getNature().getName().toString());
|
jsonField(sb, "nature", p.getNature().getName().toString());
|
||||||
sb.append(',');
|
sb.append(',');
|
||||||
jsonFieldRaw(sb, "shiny", String.valueOf(p.getShiny()));
|
jsonFieldRaw(sb, "shiny", String.valueOf(p.getShiny()));
|
||||||
|
|
||||||
// moves
|
|
||||||
sb.append(",\"moves\":[");
|
sb.append(",\"moves\":[");
|
||||||
boolean mfirst = true;
|
boolean mfirst = true;
|
||||||
for (var move : p.getMoveSet()) {
|
for (var move : p.getMoveSet()) {
|
||||||
@@ -337,9 +243,7 @@ public class CobbleBattleApiMod implements ModInitializer {
|
|||||||
jsonFieldRaw(sb, "maxPp", String.valueOf(move.getMaxPp()));
|
jsonFieldRaw(sb, "maxPp", String.valueOf(move.getMaxPp()));
|
||||||
sb.append('}');
|
sb.append('}');
|
||||||
}
|
}
|
||||||
sb.append(']');
|
sb.append("]}");
|
||||||
|
|
||||||
sb.append('}');
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private static String json(String key, String value) {
|
private static String json(String key, String value) {
|
||||||
@@ -363,10 +267,6 @@ public class CobbleBattleApiMod implements ModInitializer {
|
|||||||
.replace("\t", "\\t");
|
.replace("\t", "\\t");
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Run a task on the MC server thread and wait for the result.
|
|
||||||
* Returns null if the server is unavailable.
|
|
||||||
*/
|
|
||||||
private String runOnServerThread(java.util.function.Supplier<String> task) {
|
private String runOnServerThread(java.util.function.Supplier<String> task) {
|
||||||
MinecraftServer mc = this.server;
|
MinecraftServer mc = this.server;
|
||||||
if (mc == null) return null;
|
if (mc == null) return null;
|
||||||
@@ -398,3 +298,4 @@ public class CobbleBattleApiMod implements ModInitializer {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user