Cobblesync GUI and Packet Handler Update
This commit is contained in:
10
build.gradle
10
build.gradle
@@ -47,14 +47,14 @@ dependencies {
|
||||
modImplementation "net.fabricmc:fabric-language-kotlin:${project.fabric_kotlin_version}"
|
||||
|
||||
// Other Mod Dependencies (e.g., Cobblemon)
|
||||
modImplementation "com.cobblemon:fabric:1.6.0+1.21.1-SNAPSHOT"
|
||||
modImplementation "com.cobblemon:fabric:1.7.2+1.21.1-SNAPSHOT"
|
||||
|
||||
// JSON and GSON dependencies - add implementation first, then include
|
||||
implementation "org.json:json:20231013"
|
||||
include "org.json:json:20231013"
|
||||
implementation "org.json:json:20250517"
|
||||
include "org.json:json:20250517"
|
||||
|
||||
implementation "com.google.code.gson:gson:2.10.1"
|
||||
include "com.google.code.gson:gson:2.10.1"
|
||||
implementation "com.google.code.gson:gson:2.13.2"
|
||||
include "com.google.code.gson:gson:2.13.2"
|
||||
}
|
||||
|
||||
processResources {
|
||||
|
||||
@@ -1,19 +1,19 @@
|
||||
# Done to increase the memory available to gradle.
|
||||
org.gradle.jvmargs=-Xmx1G
|
||||
org.gradle.jvmargs=-Xmx2G
|
||||
org.gradle.parallel=true
|
||||
|
||||
# Fabric Properties
|
||||
# check these on https://fabricmc.net/develop
|
||||
minecraft_version=1.21.1
|
||||
loader_version=0.16.14
|
||||
loom_version=1.10-SNAPSHOT
|
||||
fabric_kotlin_version=1.13.2+kotlin.2.1.20
|
||||
loader_version=0.17.2
|
||||
loom_version=1.11-SNAPSHOT
|
||||
fabric_kotlin_version=1.13.6+kotlin.2.2.20
|
||||
|
||||
# Mod Properties
|
||||
mod_version=1.0.0
|
||||
mod_version=1.5.0
|
||||
maven_group=co.sirblob
|
||||
archives_base_name=cobblesync
|
||||
|
||||
# Dependencies
|
||||
fabric_version=0.116.0+1.21.1
|
||||
fabric_version=0.116.6+1.21.1
|
||||
|
||||
|
||||
BIN
gradle/wrapper/gradle-wrapper.jar
vendored
BIN
gradle/wrapper/gradle-wrapper.jar
vendored
Binary file not shown.
2
gradle/wrapper/gradle-wrapper.properties
vendored
2
gradle/wrapper/gradle-wrapper.properties
vendored
@@ -1,6 +1,6 @@
|
||||
distributionBase=GRADLE_USER_HOME
|
||||
distributionPath=wrapper/dists
|
||||
distributionUrl=https\://services.gradle.org/distributions/gradle-8.12.1-bin.zip
|
||||
distributionUrl=https\://services.gradle.org/distributions/gradle-8.14-bin.zip
|
||||
networkTimeout=10000
|
||||
validateDistributionUrl=true
|
||||
zipStoreBase=GRADLE_USER_HOME
|
||||
|
||||
9
gradlew
vendored
9
gradlew
vendored
@@ -1,7 +1,7 @@
|
||||
#!/bin/sh
|
||||
|
||||
#
|
||||
# Copyright © 2015-2021 the original authors.
|
||||
# Copyright © 2015 the original authors.
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||
# you may not use this file except in compliance with the License.
|
||||
@@ -114,7 +114,6 @@ case "$( uname )" in #(
|
||||
NONSTOP* ) nonstop=true ;;
|
||||
esac
|
||||
|
||||
CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar
|
||||
|
||||
|
||||
# Determine the Java command to use to start the JVM.
|
||||
@@ -172,7 +171,6 @@ fi
|
||||
# For Cygwin or MSYS, switch paths to Windows format before running java
|
||||
if "$cygwin" || "$msys" ; then
|
||||
APP_HOME=$( cygpath --path --mixed "$APP_HOME" )
|
||||
CLASSPATH=$( cygpath --path --mixed "$CLASSPATH" )
|
||||
|
||||
JAVACMD=$( cygpath --unix "$JAVACMD" )
|
||||
|
||||
@@ -205,15 +203,14 @@ fi
|
||||
DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"'
|
||||
|
||||
# Collect all arguments for the java command:
|
||||
# * DEFAULT_JVM_OPTS, JAVA_OPTS, JAVA_OPTS, and optsEnvironmentVar are not allowed to contain shell fragments,
|
||||
# * DEFAULT_JVM_OPTS, JAVA_OPTS, and optsEnvironmentVar are not allowed to contain shell fragments,
|
||||
# and any embedded shellness will be escaped.
|
||||
# * For example: A user cannot expect ${Hostname} to be expanded, as it is an environment variable and will be
|
||||
# treated as '${Hostname}' itself on the command line.
|
||||
|
||||
set -- \
|
||||
"-Dorg.gradle.appname=$APP_BASE_NAME" \
|
||||
-classpath "$CLASSPATH" \
|
||||
org.gradle.wrapper.GradleWrapperMain \
|
||||
-jar "$APP_HOME/gradle/wrapper/gradle-wrapper.jar" \
|
||||
"$@"
|
||||
|
||||
# Stop when "xargs" is not available.
|
||||
|
||||
3
gradlew.bat
vendored
3
gradlew.bat
vendored
@@ -70,11 +70,10 @@ goto fail
|
||||
:execute
|
||||
@rem Setup the command line
|
||||
|
||||
set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar
|
||||
|
||||
|
||||
@rem Execute Gradle
|
||||
"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %*
|
||||
"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -jar "%APP_HOME%\gradle\wrapper\gradle-wrapper.jar" %*
|
||||
|
||||
:end
|
||||
@rem End local scope for the variables with windows NT shell
|
||||
|
||||
@@ -1,42 +1,49 @@
|
||||
package co.sirblob
|
||||
|
||||
import co.sirblob.gui.CobbleSyncScreen
|
||||
import co.sirblob.network.ClientPacketHandler
|
||||
import com.mojang.blaze3d.platform.InputConstants
|
||||
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 net.minecraft.client.KeyMapping
|
||||
import org.lwjgl.glfw.GLFW
|
||||
import org.slf4j.LoggerFactory
|
||||
|
||||
object CobbleSyncClient : ClientModInitializer {
|
||||
|
||||
override fun onInitializeClient() {
|
||||
private val logger = LoggerFactory.getLogger("cobblesync-client")
|
||||
|
||||
val binding1 = KeyBindingHelper.registerKeyBinding(
|
||||
KeyMapping(
|
||||
"key.fabric-key-binding-api-v1-testmod.test_keybinding_1",
|
||||
InputConstants.Type.KEYSYM,
|
||||
GLFW.GLFW_KEY_U,
|
||||
"key.category.first.test"
|
||||
)
|
||||
)
|
||||
override fun onInitializeClient() {
|
||||
logger.info("CobbleSync Client initializing...")
|
||||
|
||||
ClientTickEvents.END_CLIENT_TICK.register { client ->
|
||||
while (binding1.consumeClick()) {
|
||||
// Register client-side network handlers
|
||||
ClientPacketHandler.register()
|
||||
|
||||
var player = client.player
|
||||
|
||||
// client.player?.sendSystemMessage(
|
||||
// Component.literal("Key 1 was pressed!")
|
||||
// .withStyle { style ->
|
||||
// style.withColor(0x00FF00)
|
||||
// }
|
||||
// )
|
||||
// Register keybind for opening the CobbleSync GUI
|
||||
val openGuiKeybind =
|
||||
KeyBindingHelper.registerKeyBinding(
|
||||
KeyMapping(
|
||||
"key.cobblesync.open_gui",
|
||||
InputConstants.Type.KEYSYM,
|
||||
GLFW.GLFW_KEY_U,
|
||||
"key.categories.cobblesync"
|
||||
)
|
||||
)
|
||||
|
||||
if(player != null) {
|
||||
// Handle keybind press
|
||||
ClientTickEvents.END_CLIENT_TICK.register { client ->
|
||||
while (openGuiKeybind.consumeClick()) {
|
||||
val player = client.player
|
||||
|
||||
if (player != null && client.screen == null) {
|
||||
// Open the CobbleSync GUI
|
||||
client.setScreen(CobbleSyncScreen())
|
||||
logger.info("Opened CobbleSync GUI")
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
logger.info("CobbleSync Client initialized!")
|
||||
}
|
||||
}
|
||||
|
||||
151
src/client/kotlin/co/sirblob/gui/CobbleSyncScreen.kt
Normal file
151
src/client/kotlin/co/sirblob/gui/CobbleSyncScreen.kt
Normal file
@@ -0,0 +1,151 @@
|
||||
package co.sirblob.gui
|
||||
|
||||
import co.sirblob.network.CobbleSyncPackets
|
||||
import net.fabricmc.api.EnvType
|
||||
import net.fabricmc.api.Environment
|
||||
import net.fabricmc.fabric.api.client.networking.v1.ClientPlayNetworking
|
||||
import net.minecraft.client.gui.GuiGraphics
|
||||
import net.minecraft.client.gui.components.Button
|
||||
import net.minecraft.client.gui.screens.Screen
|
||||
import net.minecraft.network.chat.Component
|
||||
|
||||
@Environment(EnvType.CLIENT)
|
||||
class CobbleSyncScreen : Screen(Component.literal("CobbleSync")) {
|
||||
|
||||
// Status message to display feedback
|
||||
private var statusMessage: Component = Component.literal("")
|
||||
private var statusColor: Int = 0xFFFFFF
|
||||
|
||||
// Buttons
|
||||
private lateinit var syncButton: Button
|
||||
private lateinit var loadButton: Button
|
||||
private lateinit var closeButton: Button
|
||||
|
||||
override fun init() {
|
||||
super.init()
|
||||
|
||||
val buttonWidth = 200
|
||||
val buttonHeight = 20
|
||||
val centerX = width / 2
|
||||
val centerY = height / 2
|
||||
|
||||
// Sync Button - uploads Box 30 to the server
|
||||
syncButton = Button.builder(Component.literal("⬆ Sync Box 30")) { _ ->
|
||||
setStatus(Component.literal("Syncing..."), 0xFFFF55)
|
||||
ClientPlayNetworking.send(CobbleSyncPackets.SyncRequestPayload())
|
||||
}
|
||||
.bounds(centerX - buttonWidth / 2, centerY - 40, buttonWidth, buttonHeight)
|
||||
.build()
|
||||
|
||||
// Load Button - downloads saved Pokemon to Box 1
|
||||
loadButton = Button.builder(Component.literal("⬇ Load to Box 1")) { _ ->
|
||||
setStatus(Component.literal("Loading..."), 0xFFFF55)
|
||||
ClientPlayNetworking.send(CobbleSyncPackets.LoadRequestPayload())
|
||||
}
|
||||
.bounds(centerX - buttonWidth / 2, centerY - 10, buttonWidth, buttonHeight)
|
||||
.build()
|
||||
|
||||
// Close Button
|
||||
closeButton = Button.builder(Component.literal("Close")) { _ ->
|
||||
onClose()
|
||||
}
|
||||
.bounds(centerX - buttonWidth / 2, centerY + 30, buttonWidth, buttonHeight)
|
||||
.build()
|
||||
|
||||
addRenderableWidget(syncButton)
|
||||
addRenderableWidget(loadButton)
|
||||
addRenderableWidget(closeButton)
|
||||
}
|
||||
|
||||
override fun render(guiGraphics: GuiGraphics, mouseX: Int, mouseY: Int, partialTick: Float) {
|
||||
// Render background
|
||||
renderBackground(guiGraphics, mouseX, mouseY, partialTick)
|
||||
|
||||
// Draw title
|
||||
guiGraphics.drawCenteredString(
|
||||
font,
|
||||
Component.literal("§6§lCobbleSync"),
|
||||
width / 2,
|
||||
height / 2 - 80,
|
||||
0xFFFFFF
|
||||
)
|
||||
|
||||
// Draw subtitle/description
|
||||
guiGraphics.drawCenteredString(
|
||||
font,
|
||||
Component.literal("§7Sync your Pokémon across servers"),
|
||||
width / 2,
|
||||
height / 2 - 65,
|
||||
0xAAAAAA
|
||||
)
|
||||
|
||||
// Draw separator line
|
||||
guiGraphics.fill(
|
||||
width / 2 - 100,
|
||||
height / 2 - 55,
|
||||
width / 2 + 100,
|
||||
height / 2 - 54,
|
||||
0xFF444444.toInt()
|
||||
)
|
||||
|
||||
// Draw status message
|
||||
if (statusMessage.string.isNotEmpty()) {
|
||||
guiGraphics.drawCenteredString(
|
||||
font,
|
||||
statusMessage,
|
||||
width / 2,
|
||||
height / 2 + 60,
|
||||
statusColor
|
||||
)
|
||||
}
|
||||
|
||||
// Draw info text
|
||||
guiGraphics.drawCenteredString(
|
||||
font,
|
||||
Component.literal("§8Sync: Upload Box 30 (max 12 Pokémon)"),
|
||||
width / 2,
|
||||
height / 2 + 80,
|
||||
0x888888
|
||||
)
|
||||
guiGraphics.drawCenteredString(
|
||||
font,
|
||||
Component.literal("§8Load: Download to Box 1 (must be empty)"),
|
||||
width / 2,
|
||||
height / 2 + 92,
|
||||
0x888888
|
||||
)
|
||||
|
||||
super.render(guiGraphics, mouseX, mouseY, partialTick)
|
||||
}
|
||||
|
||||
override fun isPauseScreen(): Boolean = false
|
||||
|
||||
fun setStatus(message: Component, color: Int) {
|
||||
this.statusMessage = message
|
||||
this.statusColor = color
|
||||
}
|
||||
|
||||
companion object {
|
||||
// Singleton instance for updating status from network handlers
|
||||
var currentInstance: CobbleSyncScreen? = null
|
||||
private set
|
||||
|
||||
fun open(screen: CobbleSyncScreen) {
|
||||
currentInstance = screen
|
||||
}
|
||||
|
||||
fun close() {
|
||||
currentInstance = null
|
||||
}
|
||||
}
|
||||
|
||||
override fun onClose() {
|
||||
CobbleSyncScreen.close()
|
||||
super.onClose()
|
||||
}
|
||||
|
||||
override fun added() {
|
||||
super.added()
|
||||
CobbleSyncScreen.open(this)
|
||||
}
|
||||
}
|
||||
27
src/client/kotlin/co/sirblob/network/ClientPacketHandler.kt
Normal file
27
src/client/kotlin/co/sirblob/network/ClientPacketHandler.kt
Normal file
@@ -0,0 +1,27 @@
|
||||
package co.sirblob.network
|
||||
|
||||
import co.sirblob.gui.CobbleSyncScreen
|
||||
import net.fabricmc.api.EnvType
|
||||
import net.fabricmc.api.Environment
|
||||
import net.fabricmc.fabric.api.client.networking.v1.ClientPlayNetworking
|
||||
import net.minecraft.network.chat.Component
|
||||
|
||||
/**
|
||||
* Client-side packet handlers for CobbleSync
|
||||
*/
|
||||
@Environment(EnvType.CLIENT)
|
||||
object ClientPacketHandler {
|
||||
|
||||
fun register() {
|
||||
// Register handler for sync response
|
||||
ClientPlayNetworking.registerGlobalReceiver(CobbleSyncPackets.SYNC_RESPONSE_ID) { payload, context ->
|
||||
context.client().execute {
|
||||
val screen = CobbleSyncScreen.currentInstance
|
||||
if (screen != null) {
|
||||
val color = if (payload.success) 0x55FF55 else 0xFF5555
|
||||
screen.setStatus(Component.literal(payload.message), color)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,158 +1,283 @@
|
||||
package co.sirblob
|
||||
|
||||
import com.cobblemon.mod.common.api.text.red
|
||||
import com.cobblemon.mod.common.api.text.green
|
||||
import co.sirblob.network.ServerPacketHandler
|
||||
import com.cobblemon.mod.common.api.text.blue
|
||||
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.api.storage.pc.PCBox
|
||||
import com.cobblemon.mod.common.api.text.green
|
||||
import com.cobblemon.mod.common.api.text.red
|
||||
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 com.cobblemon.mod.relocations.oracle.truffle.regex.tregex.util.json.Json
|
||||
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 com.google.gson.JsonParser
|
||||
import com.mojang.brigadier.Command
|
||||
import com.mojang.brigadier.builder.LiteralArgumentBuilder
|
||||
import com.mojang.brigadier.context.CommandContext
|
||||
import net.fabricmc.api.ModInitializer
|
||||
import net.fabricmc.fabric.api.command.v2.CommandRegistrationCallback
|
||||
import net.minecraft.commands.CommandSourceStack
|
||||
import net.minecraft.network.chat.Component
|
||||
import net.minecraft.core.RegistryAccess
|
||||
import net.minecraft.resources.ResourceLocation
|
||||
import org.json.JSONObject
|
||||
import org.slf4j.LoggerFactory
|
||||
|
||||
|
||||
object CobbleSync : ModInitializer {
|
||||
|
||||
private val logger = LoggerFactory.getLogger("cobblesync")
|
||||
private val logger = LoggerFactory.getLogger("cobblesync")
|
||||
private val request = Request("https://authserver.sirblob.co")
|
||||
|
||||
override fun onInitialize() {
|
||||
logger.info("CobbleSync initializing...")
|
||||
|
||||
CommandRegistrationCallback.EVENT.register(CommandRegistrationCallback { dispatcher, _, _ ->
|
||||
dispatcher.register(
|
||||
LiteralArgumentBuilder.literal<CommandSourceStack>("cobblesync")
|
||||
.then(
|
||||
LiteralArgumentBuilder.literal<CommandSourceStack>("sync")
|
||||
.executes(Command<CommandSourceStack> { context: CommandContext<CommandSourceStack> ->
|
||||
|
||||
val player = context.source.playerOrException
|
||||
player.sendSystemMessage(Component.literal("Syncing box...").red())
|
||||
|
||||
val box30 = player.pc().boxes.get(29)
|
||||
var pokemonCount = 0
|
||||
// Register network packet handlers for GUI communication
|
||||
ServerPacketHandler.register()
|
||||
logger.info("Registered CobbleSync network handlers")
|
||||
|
||||
box30.filterNotNull().forEach { pokemon ->
|
||||
player.sendSystemMessage(
|
||||
Component.literal("Syncing Pokémon: ${pokemon.species.name} (Level ${pokemon.level})")
|
||||
.blue()
|
||||
CommandRegistrationCallback.EVENT.register(
|
||||
CommandRegistrationCallback { dispatcher, _, _ ->
|
||||
dispatcher.register(
|
||||
LiteralArgumentBuilder.literal<CommandSourceStack>("cobblesync")
|
||||
.then(
|
||||
LiteralArgumentBuilder.literal<CommandSourceStack>(
|
||||
"sync"
|
||||
)
|
||||
.executes(
|
||||
Command<CommandSourceStack> {
|
||||
context:
|
||||
CommandContext<
|
||||
CommandSourceStack>
|
||||
->
|
||||
val player =
|
||||
context.source
|
||||
.playerOrException
|
||||
player.sendSystemMessage(
|
||||
Component.literal(
|
||||
"Syncing box..."
|
||||
)
|
||||
.red()
|
||||
)
|
||||
|
||||
val box30 =
|
||||
player.pc().boxes.get(29)
|
||||
var pokemonCount = 0
|
||||
|
||||
box30.filterNotNull().forEach {
|
||||
pokemon ->
|
||||
player.sendSystemMessage(
|
||||
Component.literal(
|
||||
"Syncing Pokémon: ${pokemon.species.name} (Level ${pokemon.level})"
|
||||
)
|
||||
.blue()
|
||||
)
|
||||
pokemonCount++
|
||||
}
|
||||
|
||||
if (pokemonCount < 1) {
|
||||
player.sendSystemMessage(
|
||||
Component.literal(
|
||||
"[Sync Failed] Box 1 is empty!"
|
||||
)
|
||||
.red()
|
||||
)
|
||||
return@Command 1
|
||||
} else if (pokemonCount > 12) {
|
||||
player.sendSystemMessage(
|
||||
Component.literal(
|
||||
"[Sync Failed] Box 1 has too many pokemon!"
|
||||
)
|
||||
.red()
|
||||
)
|
||||
return@Command 1
|
||||
}
|
||||
|
||||
var obj =
|
||||
box30.saveToJSON(
|
||||
JsonObject(),
|
||||
player.registryAccess()
|
||||
)
|
||||
|
||||
val payload =
|
||||
JSONObject()
|
||||
.put(
|
||||
"pokemon",
|
||||
obj.toString()
|
||||
)
|
||||
.put(
|
||||
"count",
|
||||
pokemonCount
|
||||
)
|
||||
|
||||
logger.info(
|
||||
"/api/cobblesync/" +
|
||||
player.uuid
|
||||
.toString()
|
||||
)
|
||||
|
||||
try {
|
||||
var response =
|
||||
request.POST(
|
||||
"/api/cobblesync/" +
|
||||
player.uuid
|
||||
.toString(),
|
||||
payload
|
||||
)
|
||||
|
||||
logger.info(response.toString())
|
||||
|
||||
player.sendSystemMessage(
|
||||
Component.literal(
|
||||
"Box 30 synced successfully!"
|
||||
)
|
||||
.green()
|
||||
)
|
||||
} 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
|
||||
}
|
||||
|
||||
1
|
||||
}
|
||||
)
|
||||
)
|
||||
pokemonCount++
|
||||
}
|
||||
.then(
|
||||
LiteralArgumentBuilder.literal<CommandSourceStack>(
|
||||
"load"
|
||||
)
|
||||
.executes(
|
||||
Command<CommandSourceStack> {
|
||||
context:
|
||||
CommandContext<
|
||||
CommandSourceStack>
|
||||
->
|
||||
val player =
|
||||
context.source
|
||||
.playerOrException
|
||||
|
||||
if (pokemonCount < 1) {
|
||||
player.sendSystemMessage(Component.literal("[Sync Failed] Box 1 is empty!").red())
|
||||
return@Command 1
|
||||
} else if (pokemonCount > 12) {
|
||||
player.sendSystemMessage(Component.literal("[Sync Failed] Box 1 has too many pokemon!").red())
|
||||
return@Command 1
|
||||
}
|
||||
val pc = player.pc()
|
||||
player.sendSystemMessage(
|
||||
Component.literal(
|
||||
"Syncing box..."
|
||||
)
|
||||
.green()
|
||||
)
|
||||
|
||||
var obj = box30.saveToJSON(JsonObject(), player.registryAccess())
|
||||
var box1 = pc.boxes.get(0)
|
||||
|
||||
val payload = JSONObject()
|
||||
.put("pokemon", obj.toString())
|
||||
.put("count", pokemonCount)
|
||||
var pokemonCount = 0
|
||||
box1.pc.forEach({ _ ->
|
||||
pokemonCount++
|
||||
})
|
||||
|
||||
logger.info("/api/cobblesync/" + player.uuid.toString())
|
||||
if (pokemonCount > 0) {
|
||||
player.sendSystemMessage(
|
||||
Component.literal(
|
||||
"[Load Failed] Box 1 is not empty!"
|
||||
)
|
||||
.red()
|
||||
)
|
||||
return@Command 1
|
||||
}
|
||||
|
||||
try {
|
||||
var response = request.POST("/api/cobblesync/" + player.uuid.toString(), payload)
|
||||
try {
|
||||
|
||||
logger.info(response.toString())
|
||||
var response =
|
||||
request.GET(
|
||||
"/api/cobblesync/" +
|
||||
player.uuid
|
||||
.toString()
|
||||
)
|
||||
logger.info(response.toString())
|
||||
|
||||
player.sendSystemMessage(Component.literal("Box 30 synced successfully!").green())
|
||||
if (response.getInt("status") !=
|
||||
200
|
||||
) {
|
||||
player.sendSystemMessage(
|
||||
Component.literal(
|
||||
"Failed to load box 1!"
|
||||
)
|
||||
.red()
|
||||
)
|
||||
return@Command 1
|
||||
}
|
||||
|
||||
} 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
|
||||
}
|
||||
var obj =
|
||||
JsonParser.parseString(
|
||||
response.getString(
|
||||
"pokemon"
|
||||
)
|
||||
)
|
||||
.asJsonObject
|
||||
|
||||
1
|
||||
})
|
||||
var newBox =
|
||||
box1.loadFromJSON(
|
||||
obj,
|
||||
player.registryAccess()
|
||||
)
|
||||
|
||||
newBox.pc.filterNotNull()
|
||||
.forEach { pokemon ->
|
||||
player.sendSystemMessage(
|
||||
Component
|
||||
.literal(
|
||||
"Received Pokémon: ${pokemon.species.name} (Level ${pokemon.level})"
|
||||
)
|
||||
.blue()
|
||||
)
|
||||
|
||||
box1.pc.add(pokemon)
|
||||
}
|
||||
|
||||
player.sendSystemMessage(
|
||||
Component.literal(
|
||||
"Box 1 loaded successfully!"
|
||||
)
|
||||
.green()
|
||||
)
|
||||
} catch (e: HTTPException) {
|
||||
logger.error(
|
||||
"HTTP Exception: ${e.message}"
|
||||
)
|
||||
player.sendSystemMessage(
|
||||
Component.literal(
|
||||
"Error loading box 1!"
|
||||
)
|
||||
.red()
|
||||
)
|
||||
return@Command 1
|
||||
} catch (e: Exception) {
|
||||
logger.error(
|
||||
"Exception: ${e.message}"
|
||||
)
|
||||
player.sendSystemMessage(
|
||||
Component.literal(
|
||||
"An unexpected error occurred!"
|
||||
)
|
||||
.red()
|
||||
)
|
||||
return@Command 1
|
||||
}
|
||||
|
||||
1
|
||||
}
|
||||
)
|
||||
)
|
||||
)
|
||||
.then(
|
||||
LiteralArgumentBuilder.literal<CommandSourceStack>("load")
|
||||
.executes(Command<CommandSourceStack> { context: CommandContext<CommandSourceStack> ->
|
||||
val player = context.source.playerOrException
|
||||
|
||||
val pc = player.pc()
|
||||
player.sendSystemMessage(Component.literal("Syncing box...").green())
|
||||
|
||||
var box1 = pc.boxes.get(0)
|
||||
|
||||
var pokemonCount = 0
|
||||
box1.pc.forEach({ _ ->
|
||||
pokemonCount++
|
||||
})
|
||||
|
||||
if (pokemonCount > 0) {
|
||||
player.sendSystemMessage(Component.literal("[Load Failed] Box 1 is not empty!").red())
|
||||
return@Command 1
|
||||
}
|
||||
|
||||
try {
|
||||
|
||||
var response = request.GET("/api/cobblesync/" + player.uuid.toString())
|
||||
logger.info(response.toString())
|
||||
|
||||
if (response.getInt("status") != 200) {
|
||||
player.sendSystemMessage(Component.literal("Failed to load box 1!").red())
|
||||
return@Command 1
|
||||
}
|
||||
|
||||
var obj = JsonParser.parseString(response.getString("pokemon")).asJsonObject
|
||||
|
||||
var newBox = box1.loadFromJSON(obj, player.registryAccess())
|
||||
|
||||
newBox.pc.filterNotNull().forEach { pokemon ->
|
||||
player.sendSystemMessage(
|
||||
Component.literal("Received Pokémon: ${pokemon.species.name} (Level ${pokemon.level})")
|
||||
.blue()
|
||||
)
|
||||
|
||||
box1.pc.add(pokemon)
|
||||
}
|
||||
|
||||
player.sendSystemMessage(Component.literal("Box 1 loaded successfully!").green())
|
||||
|
||||
} catch (e: HTTPException) {
|
||||
logger.error("HTTP Exception: ${e.message}")
|
||||
player.sendSystemMessage(Component.literal("Error loading box 1!").red())
|
||||
return@Command 1
|
||||
} catch (e: Exception) {
|
||||
logger.error("Exception: ${e.message}")
|
||||
player.sendSystemMessage(Component.literal("An unexpected error occurred!").red())
|
||||
return@Command 1
|
||||
}
|
||||
|
||||
1
|
||||
}
|
||||
)
|
||||
|
||||
))
|
||||
})
|
||||
|
||||
}
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
75
src/main/kotlin/co/sirblob/network/CobbleSyncPackets.kt
Normal file
75
src/main/kotlin/co/sirblob/network/CobbleSyncPackets.kt
Normal file
@@ -0,0 +1,75 @@
|
||||
package co.sirblob.network
|
||||
|
||||
import net.minecraft.network.FriendlyByteBuf
|
||||
import net.minecraft.network.codec.StreamCodec
|
||||
import net.minecraft.network.protocol.common.custom.CustomPacketPayload
|
||||
import net.minecraft.resources.ResourceLocation
|
||||
|
||||
/**
|
||||
* Network packet definitions for CobbleSync client-server communication
|
||||
*/
|
||||
object CobbleSyncPackets {
|
||||
|
||||
// Packet IDs
|
||||
val SYNC_REQUEST_ID = CustomPacketPayload.Type<SyncRequestPayload>(
|
||||
ResourceLocation.fromNamespaceAndPath("cobblesync", "sync_request")
|
||||
)
|
||||
val LOAD_REQUEST_ID = CustomPacketPayload.Type<LoadRequestPayload>(
|
||||
ResourceLocation.fromNamespaceAndPath("cobblesync", "load_request")
|
||||
)
|
||||
val SYNC_RESPONSE_ID = CustomPacketPayload.Type<SyncResponsePayload>(
|
||||
ResourceLocation.fromNamespaceAndPath("cobblesync", "sync_response")
|
||||
)
|
||||
|
||||
/**
|
||||
* Client -> Server: Request to sync Box 30
|
||||
*/
|
||||
class SyncRequestPayload : CustomPacketPayload {
|
||||
override fun type(): CustomPacketPayload.Type<out CustomPacketPayload> = SYNC_REQUEST_ID
|
||||
|
||||
companion object {
|
||||
val STREAM_CODEC: StreamCodec<FriendlyByteBuf, SyncRequestPayload> = StreamCodec.of(
|
||||
{ _, _ -> }, // encode - nothing to write
|
||||
{ _ -> SyncRequestPayload() } // decode - just create instance
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Client -> Server: Request to load Pokemon to Box 1
|
||||
*/
|
||||
class LoadRequestPayload : CustomPacketPayload {
|
||||
override fun type(): CustomPacketPayload.Type<out CustomPacketPayload> = LOAD_REQUEST_ID
|
||||
|
||||
companion object {
|
||||
val STREAM_CODEC: StreamCodec<FriendlyByteBuf, LoadRequestPayload> = StreamCodec.of(
|
||||
{ _, _ -> },
|
||||
{ _ -> LoadRequestPayload() }
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Server -> Client: Response with status message
|
||||
*/
|
||||
class SyncResponsePayload(
|
||||
val success: Boolean,
|
||||
val message: String
|
||||
) : CustomPacketPayload {
|
||||
override fun type(): CustomPacketPayload.Type<out CustomPacketPayload> = SYNC_RESPONSE_ID
|
||||
|
||||
companion object {
|
||||
val STREAM_CODEC: StreamCodec<FriendlyByteBuf, SyncResponsePayload> = StreamCodec.of(
|
||||
{ buf, payload ->
|
||||
buf.writeBoolean(payload.success)
|
||||
buf.writeUtf(payload.message)
|
||||
},
|
||||
{ buf ->
|
||||
val success = buf.readBoolean()
|
||||
val message = buf.readUtf()
|
||||
SyncResponsePayload(success, message)
|
||||
}
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
135
src/main/kotlin/co/sirblob/network/ServerPacketHandler.kt
Normal file
135
src/main/kotlin/co/sirblob/network/ServerPacketHandler.kt
Normal file
@@ -0,0 +1,135 @@
|
||||
package co.sirblob.network
|
||||
|
||||
import co.sirblob.HTTPException
|
||||
import co.sirblob.Request
|
||||
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 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())
|
||||
|
||||
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()
|
||||
val box1 = pc.boxes[0]
|
||||
|
||||
var pokemonCount = 0
|
||||
box1.filterNotNull().forEach { _ -> pokemonCount++ }
|
||||
|
||||
if (pokemonCount > 0) {
|
||||
sendResponse(player, false, "Box 1 is not empty!")
|
||||
return
|
||||
}
|
||||
|
||||
val response = request.GET("/api/cobblesync/${player.uuid}")
|
||||
logger.info(response.toString())
|
||||
|
||||
if (response.getInt("status") != 200) {
|
||||
sendResponse(player, false, "No saved Pokémon found!")
|
||||
return
|
||||
}
|
||||
|
||||
val obj = JsonParser.parseString(response.getString("pokemon")).asJsonObject
|
||||
val newBox = box1.loadFromJSON(obj, player.registryAccess())
|
||||
|
||||
var loadedCount = 0
|
||||
newBox.pc.filterNotNull().forEach { pokemon ->
|
||||
logger.info("Loading Pokémon: ${pokemon.species.name} (Level ${pokemon.level})")
|
||||
box1.pc.add(pokemon)
|
||||
loadedCount++
|
||||
}
|
||||
|
||||
sendResponse(player, true, "Successfully loaded $loadedCount 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 sendResponse(player: ServerPlayer, success: Boolean, message: String) {
|
||||
ServerPlayNetworking.send(player, CobbleSyncPackets.SyncResponsePayload(success, message))
|
||||
}
|
||||
}
|
||||
4
src/main/resources/assets/cobblesync/lang/en_us.json
Normal file
4
src/main/resources/assets/cobblesync/lang/en_us.json
Normal file
@@ -0,0 +1,4 @@
|
||||
{
|
||||
"key.cobblesync.open_gui": "Open CobbleSync",
|
||||
"key.categories.cobblesync": "CobbleSync"
|
||||
}
|
||||
@@ -3,13 +3,13 @@
|
||||
"id": "cobblesync",
|
||||
"version": "${version}",
|
||||
"name": "CobbleSync",
|
||||
"description": "This is an example description! Tell everyone what your mod is about!",
|
||||
"description": "Sync your Pokémon between servers!",
|
||||
"authors": [
|
||||
"Sir_Blob_"
|
||||
],
|
||||
"contact": {
|
||||
"homepage": "https://fabricmc.net/",
|
||||
"sources": "https://github.com/FabricMC/fabric-example-mod"
|
||||
"sources": "https://git.sirblob.co/GMMC/Cobblesync"
|
||||
},
|
||||
"license": "CC0-1.0",
|
||||
"icon": "assets/cobblesync/icon.png",
|
||||
|
||||
Reference in New Issue
Block a user