package co.sirblob.network import co.sirblob.HTTPException import co.sirblob.Request import com.cobblemon.mod.common.api.text.red import com.cobblemon.mod.common.pokemon.Pokemon import com.cobblemon.mod.common.util.pc import com.google.gson.JsonObject import com.google.gson.JsonParser import net.fabricmc.fabric.api.networking.v1.PayloadTypeRegistry import net.fabricmc.fabric.api.networking.v1.ServerPlayNetworking import net.minecraft.server.level.ServerPlayer import net.minecraft.world.item.ItemStack import net.minecraft.network.chat.Component import com.cobblemon.mod.common.api.text.red import org.json.JSONObject import org.slf4j.LoggerFactory /** Server-side packet handlers for CobbleSync operations */ object ServerPacketHandler { private val logger = LoggerFactory.getLogger("cobblesync") private val request = Request("https://authserver.sirblob.co") fun register() { // Register packet types PayloadTypeRegistry.playC2S() .register( CobbleSyncPackets.SYNC_REQUEST_ID, CobbleSyncPackets.SyncRequestPayload.STREAM_CODEC ) PayloadTypeRegistry.playC2S() .register( CobbleSyncPackets.LOAD_REQUEST_ID, CobbleSyncPackets.LoadRequestPayload.STREAM_CODEC ) PayloadTypeRegistry.playS2C() .register( CobbleSyncPackets.SYNC_RESPONSE_ID, CobbleSyncPackets.SyncResponsePayload.STREAM_CODEC ) // Register handlers ServerPlayNetworking.registerGlobalReceiver(CobbleSyncPackets.SYNC_REQUEST_ID) { _, context -> val player = context.player() context.server().execute { handleSyncRequest(player) } } ServerPlayNetworking.registerGlobalReceiver(CobbleSyncPackets.LOAD_REQUEST_ID) { _, context -> val player = context.player() context.server().execute { handleLoadRequest(player) } } } private fun handleSyncRequest(player: ServerPlayer) { try { val box30 = player.pc().boxes[29] var pokemonCount = 0 box30.filterNotNull().forEach { pokemon -> logger.info("Syncing Pokémon: ${pokemon.species.name} (Level ${pokemon.level})") pokemonCount++ } if (pokemonCount < 1) { sendResponse(player, false, "Box 30 is empty!") return } if (pokemonCount > 12) { sendResponse(player, false, "Box 30 has too many Pokémon! (Max 12)") return } val obj = box30.saveToJSON(JsonObject(), player.registryAccess()) // Remove held items from the JSON to prevent duping/transferring items obj.entrySet().forEach { entry -> if (entry.value.isJsonObject) { entry.value.asJsonObject.remove("HeldItem") } } val payload = JSONObject().put("pokemon", obj.toString()).put("count", pokemonCount) logger.info("/api/cobblesync/${player.uuid}") val response = request.POST("/api/cobblesync/${player.uuid}", payload) logger.info(response.toString()) sendResponse(player, true, "Successfully synced $pokemonCount Pokémon!") } catch (e: HTTPException) { logger.error("HTTP Exception: ${e.message}") sendResponse(player, false, "Server error: ${e.message}") } catch (e: Exception) { logger.error("Exception: ${e.message}") sendResponse(player, false, "An unexpected error occurred!") } } private fun handleLoadRequest(player: ServerPlayer) { try { val pc = player.pc() // Fetch synced Pokemon from the server val response = request.GET("/api/cobblesync/${player.uuid}") logger.info(response.toString()) if (response.getInt("status") == 403) { sendResponse(player, false, "Player already synced/loaded") return } if (response.getInt("status") != 200) { sendResponse(player, false, "No saved Pokémon found!") return } val obj = JsonParser.parseString(response.getString("pokemon")).asJsonObject val pokemonToLoad = mutableListOf() obj.entrySet().forEach { entry -> if (entry.key.startsWith("Slot")) { val pokemonJson = entry.value.asJsonObject val pokemon = Pokemon.loadFromJSON(player.registryAccess()!!, pokemonJson) if (pokemon != null) { pokemonToLoad.add(pokemon) } } } if (pokemonToLoad.isEmpty()) { sendResponse(player, false, "No Pokémon to load!") return } // Count available empty slots across all boxes var emptySlots = 0 for (box in pc.boxes) { for (slot in 0 until 30) { // Each box has 30 slots if (box[slot] == null) { emptySlots++ } } } if (emptySlots < pokemonToLoad.size) { sendResponse( player, false, "Not enough empty slots! Need ${pokemonToLoad.size}, have $emptySlots" ) return } // Add Pokemon to empty slots across all boxes var loadedCount = 0 for (pokemon in pokemonToLoad) { var placed = false for (box in pc.boxes) { if (placed) break for (slot in 0 until 30) { if (box[slot] == null) { box[slot] = pokemon logger.info( "Loaded Pokémon: ${pokemon.species.name} (Level ${pokemon.level}) to slot $slot" ) loadedCount++ placed = true break } } } } sendResponse(player, true, "Successfully loaded $loadedCount Pokémon to empty slots!") } catch (e: HTTPException) { logger.error("HTTP Exception: ${e.message}") sendResponse(player, false, "Server error: ${e.message}") } catch (e: Exception) { logger.error("Exception: ${e.message}") sendResponse(player, false, "An unexpected error occurred!") } } private fun sendResponse(player: ServerPlayer, success: Boolean, message: String) { ServerPlayNetworking.send(player, CobbleSyncPackets.SyncResponsePayload(success, message)) } }