SyncMon Structure
This commit is contained in:
18
build.gradle
18
build.gradle
@@ -46,16 +46,18 @@ fabricApi {
|
|||||||
}
|
}
|
||||||
|
|
||||||
dependencies {
|
dependencies {
|
||||||
// To change the versions see the gradle.properties file
|
// To change the versions see the gradle.properties file
|
||||||
minecraft "com.mojang:minecraft:${project.minecraft_version}"
|
minecraft "com.mojang:minecraft:${project.minecraft_version}"
|
||||||
mappings loom.officialMojangMappings()
|
mappings loom.officialMojangMappings()
|
||||||
modImplementation "net.fabricmc:fabric-loader:${project.loader_version}"
|
modImplementation "net.fabricmc:fabric-loader:${project.loader_version}"
|
||||||
|
|
||||||
// Fabric API. This is technically optional, but you probably want it anyway.
|
// Fabric API. This is technically optional, but you probably want it anyway.
|
||||||
modImplementation "net.fabricmc.fabric-api:fabric-api:${project.fabric_version}"
|
modImplementation "net.fabricmc.fabric-api:fabric-api:${project.fabric_version}"
|
||||||
modImplementation "net.fabricmc:fabric-language-kotlin:${project.fabric_kotlin_version}"
|
modImplementation "net.fabricmc:fabric-language-kotlin:${project.fabric_kotlin_version}"
|
||||||
|
|
||||||
modImplementation "com.cobblemon:fabric:1.6.0+1.21.1-SNAPSHOT"
|
modImplementation "com.cobblemon:fabric:1.6.0+1.21.1-SNAPSHOT"
|
||||||
|
implementation "com.google.code.gson:gson:2.10.1"
|
||||||
|
implementation "org.json:json:20231013" // Or the latest version
|
||||||
}
|
}
|
||||||
|
|
||||||
processResources {
|
processResources {
|
||||||
|
|||||||
@@ -1,18 +1,11 @@
|
|||||||
package co.sirblob
|
package co.sirblob
|
||||||
|
|
||||||
import org.lwjgl.glfw.GLFW
|
|
||||||
import org.lwjgl.system.windows.KEYBDINPUT
|
|
||||||
|
|
||||||
import net.minecraft.client.KeyMapping
|
|
||||||
import net.minecraft.network.chat.Component
|
|
||||||
|
|
||||||
import net.fabricmc.api.ClientModInitializer;
|
|
||||||
import net.fabricmc.fabric.api.client.event.lifecycle.v1.ClientTickEvents;
|
|
||||||
import net.fabricmc.fabric.api.client.keybinding.v1.KeyBindingHelper
|
|
||||||
import com.mojang.blaze3d.platform.InputConstants
|
import com.mojang.blaze3d.platform.InputConstants
|
||||||
|
import net.fabricmc.api.ClientModInitializer
|
||||||
import com.cobblemon.mod.common.api.*
|
import net.fabricmc.fabric.api.client.event.lifecycle.v1.ClientTickEvents
|
||||||
import com.cobblemon.mod.common.api.storage.PokemonStoreManager
|
import net.fabricmc.fabric.api.client.keybinding.v1.KeyBindingHelper
|
||||||
|
import net.minecraft.client.KeyMapping
|
||||||
|
import org.lwjgl.glfw.GLFW
|
||||||
|
|
||||||
object CobbleSyncClient : ClientModInitializer {
|
object CobbleSyncClient : ClientModInitializer {
|
||||||
|
|
||||||
@@ -41,8 +34,6 @@ object CobbleSyncClient : ClientModInitializer {
|
|||||||
|
|
||||||
if(player != null) {
|
if(player != null) {
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
0
src/client/kotlin/co/sirblob/KeybindImports.kt
Normal file
0
src/client/kotlin/co/sirblob/KeybindImports.kt
Normal file
@@ -1,22 +1,60 @@
|
|||||||
package co.sirblob
|
package co.sirblob
|
||||||
|
|
||||||
|
import com.cobblemon.mod.common.api.text.red
|
||||||
|
import com.cobblemon.mod.common.api.pokemon.PokemonSpecies
|
||||||
|
import com.cobblemon.mod.common.api.pokemon.stats.Stat
|
||||||
|
import com.cobblemon.mod.common.api.pokemon.stats.Stats
|
||||||
|
import com.cobblemon.mod.common.util.pc
|
||||||
|
import com.cobblemon.mod.common.pokemon.Pokemon
|
||||||
|
import com.cobblemon.mod.common.pokemon.Species
|
||||||
|
import com.cobblemon.mod.common.Cobblemon
|
||||||
|
import org.json.JSONObject
|
||||||
|
import com.mojang.brigadier.Command
|
||||||
|
import com.mojang.brigadier.arguments.IntegerArgumentType
|
||||||
|
import com.mojang.brigadier.builder.LiteralArgumentBuilder
|
||||||
|
import com.mojang.brigadier.context.CommandContext
|
||||||
|
import com.google.gson.JsonObject
|
||||||
import net.fabricmc.api.ModInitializer
|
import net.fabricmc.api.ModInitializer
|
||||||
import net.fabricmc.fabric.api.command.v2.CommandRegistrationCallback
|
import net.fabricmc.fabric.api.command.v2.CommandRegistrationCallback
|
||||||
import net.minecraft.commands.CommandSourceStack
|
import net.minecraft.commands.CommandSourceStack
|
||||||
import net.minecraft.network.chat.Component
|
import net.minecraft.network.chat.Component
|
||||||
|
import net.minecraft.core.RegistryAccess
|
||||||
|
import net.minecraft.resources.ResourceLocation
|
||||||
import org.slf4j.LoggerFactory
|
import org.slf4j.LoggerFactory
|
||||||
import com.mojang.brigadier.builder.LiteralArgumentBuilder
|
import SyncMon
|
||||||
import com.mojang.brigadier.context.CommandContext
|
|
||||||
import com.mojang.brigadier.Command
|
|
||||||
import com.cobblemon.mod.common.api.text.Text
|
|
||||||
|
|
||||||
|
|
||||||
object CobbleSync : ModInitializer {
|
object CobbleSync : ModInitializer {
|
||||||
private val logger = LoggerFactory.getLogger("cobblesync")
|
|
||||||
|
private val logger = LoggerFactory.getLogger("cobblesync")
|
||||||
|
private val request = Request("http://localhost:5173")
|
||||||
|
|
||||||
|
|
||||||
|
private fun Pokemon.toSyncJSONObject(): JSONObject {
|
||||||
|
val statsJson = JSONObject()
|
||||||
|
.put("hp", this.maxHealth)
|
||||||
|
.put("attack", this.attack)
|
||||||
|
.put("defense", this.defence)
|
||||||
|
.put("specialAttack", this.specialAttack)
|
||||||
|
.put("specialDefense", this.specialDefence)
|
||||||
|
.put("speed", this.speed)
|
||||||
|
|
||||||
|
return JSONObject()
|
||||||
|
.put("species", this.species.name)
|
||||||
|
.put("level", this.level)
|
||||||
|
.put("nickname", this.nickname ?: "")
|
||||||
|
.put("shiny", this.shiny)
|
||||||
|
.put("stats", statsJson)
|
||||||
|
.put("ivs", this.ivs.map { (stat, value) ->
|
||||||
|
JSONObject().put(stat.toString(), value)
|
||||||
|
})
|
||||||
|
.put("friendship", this.friendship)
|
||||||
|
.put("nature", this.nature.toString())
|
||||||
|
.put("ability", this.ability)
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
override fun onInitialize() {
|
override fun onInitialize() {
|
||||||
|
|
||||||
|
|
||||||
CommandRegistrationCallback.EVENT.register(CommandRegistrationCallback { dispatcher, _, _ ->
|
CommandRegistrationCallback.EVENT.register(CommandRegistrationCallback { dispatcher, _, _ ->
|
||||||
dispatcher.register(
|
dispatcher.register(
|
||||||
LiteralArgumentBuilder.literal<CommandSourceStack>("cobblesync")
|
LiteralArgumentBuilder.literal<CommandSourceStack>("cobblesync")
|
||||||
@@ -24,18 +62,95 @@ object CobbleSync : ModInitializer {
|
|||||||
LiteralArgumentBuilder.literal<CommandSourceStack>("sync")
|
LiteralArgumentBuilder.literal<CommandSourceStack>("sync")
|
||||||
.executes(Command<CommandSourceStack> { context: CommandContext<CommandSourceStack> ->
|
.executes(Command<CommandSourceStack> { context: CommandContext<CommandSourceStack> ->
|
||||||
|
|
||||||
var player = context.source.player
|
val player = context.source.playerOrException
|
||||||
|
|
||||||
player?.sendSystemMessage(
|
val pc = player.pc()
|
||||||
Component.literal("CobbleSync is not yet implemented. Please check back later.")
|
|
||||||
.withStyle { style ->
|
|
||||||
style.withColor(0xFF0000)
|
|
||||||
}
|
|
||||||
)
|
|
||||||
|
|
||||||
1
|
player.sendSystemMessage(Component.literal("Syncing box...").red())
|
||||||
})
|
|
||||||
)
|
val box30 = pc.boxes.get(29)
|
||||||
|
|
||||||
|
var pokemonArry = ArrayList<Pokemon>()
|
||||||
|
var pokemonCount = 0
|
||||||
|
|
||||||
|
box30.filterNotNull().forEach { pokemon ->
|
||||||
|
pokemonArry.add(pokemon)
|
||||||
|
logger.info("Found pokemon: ${pokemon.species.name}")
|
||||||
|
pokemonCount++
|
||||||
|
}
|
||||||
|
|
||||||
|
if (pokemonCount < 1) {
|
||||||
|
player.sendSystemMessage(Component.literal("Box 30 is empty!").red())
|
||||||
|
return@Command 1
|
||||||
|
} else if (pokemonCount > 12) {
|
||||||
|
player.sendSystemMessage(Component.literal("Box 30 has too many pokemon!").red())
|
||||||
|
return@Command 1
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
|
||||||
|
val pokemonJsonList = pokemonArry.map { pokemon ->
|
||||||
|
pokemon.toSyncJSONObject()
|
||||||
|
}
|
||||||
|
|
||||||
|
val payload = JSONObject()
|
||||||
|
.put("pokemon", pokemonJsonList)
|
||||||
|
.put("count", pokemonCount)
|
||||||
|
|
||||||
|
var response = request.POST("/api/cobblesync/" + player.uuid.toString(), payload)
|
||||||
|
|
||||||
|
logger.info(response.toString())
|
||||||
|
|
||||||
|
if (response.getInt("status") != 200) {
|
||||||
|
player.sendSystemMessage(Component.literal("Failed to sync box 30!").red())
|
||||||
|
return@Command 1
|
||||||
|
}
|
||||||
|
|
||||||
|
// Clear Box 30 before adding new Pokémon
|
||||||
|
// for (i in 0 until box30.count()) {
|
||||||
|
// box30.set(i, null)
|
||||||
|
// }
|
||||||
|
|
||||||
|
val newPokemonsArray = response.optJSONArray("pokemon")
|
||||||
|
if (newPokemonsArray == null) {
|
||||||
|
player.sendSystemMessage(Component.literal("No Pokémon data received in server response.").red())
|
||||||
|
// Box is now empty, which might be the intended state if server sends no pokemon
|
||||||
|
return@Command 1
|
||||||
|
}
|
||||||
|
|
||||||
|
// Iterate through the received Pokémon and add them to Box 30 in an open slot
|
||||||
|
|
||||||
|
for (i in 0 until newPokemonsArray.length()) {
|
||||||
|
val pokemonJson = newPokemonsArray.getJSONObject(i)
|
||||||
|
val newPokemon = SyncMon().createFromJSON(pokemonJson)
|
||||||
|
|
||||||
|
// Find the first empty slot in Box 30
|
||||||
|
|
||||||
|
logger.info(newPokemon.toSyncJSONObject().toString())
|
||||||
|
|
||||||
|
for (j in 0 until box30.count()) {
|
||||||
|
if (box30.get(j) == null) {
|
||||||
|
box30.pc.add(newPokemon)
|
||||||
|
logger.info("Added ${newPokemon.species.name} to Box 30 at slot $j")
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
} catch (e: HTTPException) {
|
||||||
|
logger.error("HTTP Exception: ${e.message}")
|
||||||
|
player.sendSystemMessage(Component.literal("Error syncing box 30!").red())
|
||||||
|
return@Command 1
|
||||||
|
} catch (e: Exception) {
|
||||||
|
logger.error("Exception: ${e.message}")
|
||||||
|
player.sendSystemMessage(Component.literal("An unexpected error occurred!").red())
|
||||||
|
return@Command 1
|
||||||
|
}
|
||||||
|
|
||||||
|
player.sendSystemMessage(Component.literal("Box 30 synced successfully!").red())
|
||||||
|
1
|
||||||
|
})
|
||||||
|
)
|
||||||
)
|
)
|
||||||
})
|
})
|
||||||
|
|
||||||
|
|||||||
4
src/main/kotlin/co/sirblob/HTTPException.kt
Normal file
4
src/main/kotlin/co/sirblob/HTTPException.kt
Normal file
@@ -0,0 +1,4 @@
|
|||||||
|
package co.sirblob
|
||||||
|
|
||||||
|
|
||||||
|
class HTTPException(val responseCode: Int, message: String) : Exception(message)
|
||||||
137
src/main/kotlin/co/sirblob/Request.kt
Normal file
137
src/main/kotlin/co/sirblob/Request.kt
Normal file
@@ -0,0 +1,137 @@
|
|||||||
|
package co.sirblob
|
||||||
|
|
||||||
|
|
||||||
|
import java.io.BufferedReader
|
||||||
|
import java.io.DataOutputStream
|
||||||
|
import java.io.IOException
|
||||||
|
import java.io.InputStreamReader
|
||||||
|
import java.net.HttpURLConnection
|
||||||
|
import java.net.URI
|
||||||
|
import java.net.URL
|
||||||
|
import java.nio.charset.StandardCharsets
|
||||||
|
import org.json.JSONObject
|
||||||
|
|
||||||
|
class Request(baseUrl: String) {
|
||||||
|
|
||||||
|
var baseUrl: String = baseUrl
|
||||||
|
private set
|
||||||
|
|
||||||
|
private val headers: MutableMap<String, String> = mutableMapOf()
|
||||||
|
|
||||||
|
init {
|
||||||
|
this.addHeader("Accept", "*/*")
|
||||||
|
this.addHeader("Content-Type", "application/json; charset=utf-8")
|
||||||
|
}
|
||||||
|
|
||||||
|
fun setBaseUrl(url: String) {
|
||||||
|
this.baseUrl = url
|
||||||
|
}
|
||||||
|
|
||||||
|
fun addHeader(key: String, value: String) {
|
||||||
|
this.headers[key] = value
|
||||||
|
}
|
||||||
|
|
||||||
|
fun setHeader(key: String, value: String) {
|
||||||
|
this.headers[key] = value
|
||||||
|
}
|
||||||
|
|
||||||
|
fun removeHeader(key: String) {
|
||||||
|
this.headers.remove(key)
|
||||||
|
}
|
||||||
|
|
||||||
|
@Throws(HTTPException::class)
|
||||||
|
private fun generateResponse(responseCode: Int, responseBody: String): JSONObject {
|
||||||
|
return when (responseCode) {
|
||||||
|
HttpURLConnection.HTTP_OK -> {
|
||||||
|
if (responseBody.startsWith("{") || responseBody.startsWith("[")) {
|
||||||
|
JSONObject(responseBody).put("status", responseCode)
|
||||||
|
} else if (responseBody.isBlank()) {
|
||||||
|
JSONObject().put("status", responseCode)
|
||||||
|
} else {
|
||||||
|
JSONObject().put("data", responseBody).put("status", responseCode)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
HttpURLConnection.HTTP_NO_CONTENT -> JSONObject().put("status", responseCode)
|
||||||
|
HttpURLConnection.HTTP_BAD_REQUEST -> throw HTTPException(responseCode, "Bad request")
|
||||||
|
HttpURLConnection.HTTP_UNAUTHORIZED -> throw HTTPException(responseCode, "Unauthorized")
|
||||||
|
HttpURLConnection.HTTP_FORBIDDEN -> throw HTTPException(responseCode, "Forbidden")
|
||||||
|
HttpURLConnection.HTTP_NOT_FOUND -> throw HTTPException(responseCode, "Not found")
|
||||||
|
HttpURLConnection.HTTP_INTERNAL_ERROR -> throw HTTPException(responseCode, "Internal server error")
|
||||||
|
else -> {
|
||||||
|
if (!responseBody.startsWith("{") && !responseBody.startsWith("[") && responseBody.isNotBlank()) {
|
||||||
|
JSONObject().put("data", responseBody).put("status", responseCode)
|
||||||
|
} else if (responseBody.startsWith("{") || responseBody.startsWith("[")) {
|
||||||
|
JSONObject(responseBody).put("status", responseCode)
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
JSONObject().put("status", responseCode)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Throws(IOException::class, HTTPException::class)
|
||||||
|
private fun performRequest(urlPath: String, method: String, requestBodyJson: JSONObject? = null): JSONObject {
|
||||||
|
val fullUrl = URI.create(baseUrl).resolve(urlPath).toURL()
|
||||||
|
val con = fullUrl.openConnection() as HttpURLConnection
|
||||||
|
try {
|
||||||
|
con.requestMethod = method
|
||||||
|
headers.forEach { (key, value) -> con.setRequestProperty(key, value) }
|
||||||
|
|
||||||
|
con.connectTimeout = 5000
|
||||||
|
con.readTimeout = 5000
|
||||||
|
|
||||||
|
if (requestBodyJson != null && (method == "POST" || method == "PUT" || method == "PATCH")) {
|
||||||
|
con.doOutput = true
|
||||||
|
val bodyBytes = requestBodyJson.toString().toByteArray(StandardCharsets.UTF_8)
|
||||||
|
con.setRequestProperty("Content-Length", bodyBytes.size.toString())
|
||||||
|
DataOutputStream(con.outputStream).use { outputStream ->
|
||||||
|
outputStream.write(bodyBytes)
|
||||||
|
outputStream.flush()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
val responseCode = con.responseCode
|
||||||
|
|
||||||
|
val responseBody: String = try {
|
||||||
|
val streamToRead = if (responseCode < HttpURLConnection.HTTP_BAD_REQUEST) {
|
||||||
|
con.inputStream
|
||||||
|
} else {
|
||||||
|
con.errorStream
|
||||||
|
}
|
||||||
|
streamToRead?.bufferedReader(StandardCharsets.UTF_8)?.use { it.readText() } ?: ""
|
||||||
|
} catch (e: IOException) {
|
||||||
|
""
|
||||||
|
}
|
||||||
|
|
||||||
|
return generateResponse(responseCode, responseBody)
|
||||||
|
} finally {
|
||||||
|
con.disconnect()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Throws(IOException::class, HTTPException::class)
|
||||||
|
fun GET(urlPath: String): JSONObject {
|
||||||
|
return performRequest(urlPath, "GET")
|
||||||
|
}
|
||||||
|
|
||||||
|
@Throws(IOException::class, HTTPException::class)
|
||||||
|
fun POST(urlPath: String, body: JSONObject): JSONObject {
|
||||||
|
return performRequest(urlPath, "POST", body)
|
||||||
|
}
|
||||||
|
|
||||||
|
@Throws(IOException::class, HTTPException::class)
|
||||||
|
fun PUT(urlPath: String, body: JSONObject): JSONObject {
|
||||||
|
return performRequest(urlPath, "PUT", body)
|
||||||
|
}
|
||||||
|
|
||||||
|
@Throws(IOException::class, HTTPException::class)
|
||||||
|
fun PATCH(urlPath: String, body: JSONObject): JSONObject {
|
||||||
|
return performRequest(urlPath, "PATCH", body)
|
||||||
|
}
|
||||||
|
|
||||||
|
@Throws(IOException::class, HTTPException::class)
|
||||||
|
fun DELETE(urlPath: String): JSONObject {
|
||||||
|
return performRequest(urlPath, "DELETE")
|
||||||
|
}
|
||||||
|
}
|
||||||
64
src/main/kotlin/co/sirblob/SyncMon.kt
Normal file
64
src/main/kotlin/co/sirblob/SyncMon.kt
Normal file
@@ -0,0 +1,64 @@
|
|||||||
|
|
||||||
|
import com.cobblemon.mod.common.api.pokemon.PokemonSpecies
|
||||||
|
import com.cobblemon.mod.common.api.pokemon.stats.Stats
|
||||||
|
import com.cobblemon.mod.common.api.pokemon.Natures
|
||||||
|
import com.cobblemon.mod.common.pokemon.Pokemon
|
||||||
|
import com.cobblemon.mod.common.pokemon.Nature
|
||||||
|
import org.json.JSONObject
|
||||||
|
import net.minecraft.network.chat.Component
|
||||||
|
|
||||||
|
|
||||||
|
class SyncMon: Pokemon {
|
||||||
|
|
||||||
|
constructor() : super()
|
||||||
|
|
||||||
|
public fun toSyncJSONObject(): JSONObject {
|
||||||
|
val statsJson = JSONObject()
|
||||||
|
.put("hp", this.maxHealth)
|
||||||
|
.put("attack", this.attack)
|
||||||
|
.put("defense", this.defence)
|
||||||
|
.put("specialAttack", this.specialAttack)
|
||||||
|
.put("specialDefense", this.specialDefence)
|
||||||
|
.put("speed", this.speed)
|
||||||
|
|
||||||
|
return JSONObject()
|
||||||
|
.put("species", this.species.name)
|
||||||
|
.put("level", this.level)
|
||||||
|
.put("nickname", this.nickname ?: "")
|
||||||
|
.put("shiny", this.shiny)
|
||||||
|
.put("stats", statsJson)
|
||||||
|
.put("ivs", this.ivs.map { (stat, value) ->
|
||||||
|
JSONObject().put(stat.toString(), value)
|
||||||
|
})
|
||||||
|
.put("friendship", this.friendship)
|
||||||
|
.put("nature", this.nature.toString())
|
||||||
|
.put("ability", this.ability)
|
||||||
|
}
|
||||||
|
|
||||||
|
public fun createFromJSON(json: JSONObject): Pokemon {
|
||||||
|
val speciesName = json.getString("species")
|
||||||
|
val species = PokemonSpecies.getByName(speciesName.lowercase()) ?: throw IllegalArgumentException("Unknown species: $speciesName")
|
||||||
|
|
||||||
|
val pokemon = Pokemon()
|
||||||
|
pokemon.species = species
|
||||||
|
pokemon.level = json.getInt("level")
|
||||||
|
pokemon.shiny = json.getBoolean("shiny")
|
||||||
|
pokemon.setFriendship(json.getInt("friendship"))
|
||||||
|
super.nickname = Component.literal(json.getString("nickname"))
|
||||||
|
pokemon.nature = Natures.getNature(json.getString("nature")) ?: Natures.GENTLE
|
||||||
|
|
||||||
|
//val statsJson = json.getJSONObject("stats")
|
||||||
|
|
||||||
|
val ivsJson = json.getJSONArray("ivs")
|
||||||
|
for (i in 0 until ivsJson.length()) {
|
||||||
|
val ivJson = ivsJson.getJSONObject(i)
|
||||||
|
for (key in ivJson.keys()) {
|
||||||
|
val stat = Stats.getStat(key)
|
||||||
|
pokemon.setIV(stat, ivJson.getInt(key))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return pokemon
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user