This commit is contained in:
parent
25c4d6e490
commit
79cea96292
@ -11,6 +11,12 @@ jobs:
|
|||||||
RUNNER_TOOL_CACHE: /toolcache
|
RUNNER_TOOL_CACHE: /toolcache
|
||||||
REGISTRY: tuxpa.in
|
REGISTRY: tuxpa.in
|
||||||
IMAGE_ROOT: a/wynn
|
IMAGE_ROOT: a/wynn
|
||||||
|
strategy:
|
||||||
|
matrix:
|
||||||
|
options:
|
||||||
|
name: ts
|
||||||
|
context: ./ts
|
||||||
|
file: ./ts/Dockerfile
|
||||||
steps:
|
steps:
|
||||||
- name: Checkout
|
- name: Checkout
|
||||||
uses: actions/checkout@v4
|
uses: actions/checkout@v4
|
||||||
@ -25,13 +31,13 @@ jobs:
|
|||||||
- name: build and push typescript
|
- name: build and push typescript
|
||||||
uses: docker/build-push-action@v6
|
uses: docker/build-push-action@v6
|
||||||
with:
|
with:
|
||||||
context: ./ts
|
context: ${{ matrix.options.context }}
|
||||||
file: ./ts/Dockerfile
|
file: ${{ matrix.options.file }}
|
||||||
platforms: linux/amd64
|
platforms: linux/amd64
|
||||||
push: true
|
push: true
|
||||||
cache-from: type=registry,ref=${{ env.REGISTRY }}/${{ env.IMAGE_ROOT }}/ts:cache
|
cache-from: type=registry,ref=${{ env.REGISTRY }}/${{ env.IMAGE_ROOT }}/${{matrix.options.name}}:cache
|
||||||
cache-to: type=registry,ref=${{ env.REGISTRY }}/${{ env.IMAGE_ROOT }}/ts:cache,mode=max,image-manifest=true,oci-mediatypes=true,type=registry
|
cache-to: type=registry,ref=${{ env.REGISTRY }}/${{ env.IMAGE_ROOT }}/${{matrix.options.name}}:cache,mode=max,image-manifest=true,oci-mediatypes=true,type=registry
|
||||||
tags: |
|
tags: |
|
||||||
${{env.REGISTRY}}/${{env.IMAGE_ROOT}}/ts:${{ gitea.ref_name }},
|
${{env.REGISTRY}}/${{env.IMAGE_ROOT}}/${{matrix.options.name}}:${{ gitea.ref_name }},
|
||||||
${{env.REGISTRY}}/${{env.IMAGE_ROOT}}/ts:${{ gitea.head_ref || gitea.ref_name }}
|
${{env.REGISTRY}}/${{env.IMAGE_ROOT}}/${{matrix.options.name}}:${{ gitea.head_ref || gitea.ref_name }}
|
||||||
|
|
||||||
|
119
java/clientmod/.gitignore
vendored
Normal file
119
java/clientmod/.gitignore
vendored
Normal file
@ -0,0 +1,119 @@
|
|||||||
|
# User-specific stuff
|
||||||
|
.idea/
|
||||||
|
|
||||||
|
*.iml
|
||||||
|
*.ipr
|
||||||
|
*.iws
|
||||||
|
|
||||||
|
# IntelliJ
|
||||||
|
out/
|
||||||
|
# mpeltonen/sbt-idea plugin
|
||||||
|
.idea_modules/
|
||||||
|
|
||||||
|
# JIRA plugin
|
||||||
|
atlassian-ide-plugin.xml
|
||||||
|
|
||||||
|
# Compiled class file
|
||||||
|
*.class
|
||||||
|
|
||||||
|
# Log file
|
||||||
|
*.log
|
||||||
|
|
||||||
|
# BlueJ files
|
||||||
|
*.ctxt
|
||||||
|
|
||||||
|
# Package Files #
|
||||||
|
*.jar
|
||||||
|
*.war
|
||||||
|
*.nar
|
||||||
|
*.ear
|
||||||
|
*.zip
|
||||||
|
*.tar.gz
|
||||||
|
*.rar
|
||||||
|
|
||||||
|
# virtual machine crash logs, see http://www.java.com/en/download/help/error_hotspot.xml
|
||||||
|
hs_err_pid*
|
||||||
|
|
||||||
|
*~
|
||||||
|
|
||||||
|
# temporary files which can be created if a process still has a handle open of a deleted file
|
||||||
|
.fuse_hidden*
|
||||||
|
|
||||||
|
# KDE directory preferences
|
||||||
|
.directory
|
||||||
|
|
||||||
|
# Linux trash folder which might appear on any partition or disk
|
||||||
|
.Trash-*
|
||||||
|
|
||||||
|
# .nfs files are created when an open file is removed but is still being accessed
|
||||||
|
.nfs*
|
||||||
|
|
||||||
|
# General
|
||||||
|
.DS_Store
|
||||||
|
.AppleDouble
|
||||||
|
.LSOverride
|
||||||
|
|
||||||
|
# Icon must end with two \r
|
||||||
|
Icon
|
||||||
|
|
||||||
|
# Thumbnails
|
||||||
|
._*
|
||||||
|
|
||||||
|
# Files that might appear in the root of a volume
|
||||||
|
.DocumentRevisions-V100
|
||||||
|
.fseventsd
|
||||||
|
.Spotlight-V100
|
||||||
|
.TemporaryItems
|
||||||
|
.Trashes
|
||||||
|
.VolumeIcon.icns
|
||||||
|
.com.apple.timemachine.donotpresent
|
||||||
|
|
||||||
|
# Directories potentially created on remote AFP share
|
||||||
|
.AppleDB
|
||||||
|
.AppleDesktop
|
||||||
|
Network Trash Folder
|
||||||
|
Temporary Items
|
||||||
|
.apdisk
|
||||||
|
|
||||||
|
# Windows thumbnail cache files
|
||||||
|
Thumbs.db
|
||||||
|
Thumbs.db:encryptable
|
||||||
|
ehthumbs.db
|
||||||
|
ehthumbs_vista.db
|
||||||
|
|
||||||
|
# Dump file
|
||||||
|
*.stackdump
|
||||||
|
|
||||||
|
# Folder config file
|
||||||
|
[Dd]esktop.ini
|
||||||
|
|
||||||
|
# Recycle Bin used on file shares
|
||||||
|
$RECYCLE.BIN/
|
||||||
|
|
||||||
|
# Windows Installer files
|
||||||
|
*.cab
|
||||||
|
*.msi
|
||||||
|
*.msix
|
||||||
|
*.msm
|
||||||
|
*.msp
|
||||||
|
|
||||||
|
# Windows shortcuts
|
||||||
|
*.lnk
|
||||||
|
|
||||||
|
.gradle
|
||||||
|
build/
|
||||||
|
|
||||||
|
# Ignore Gradle GUI config
|
||||||
|
gradle-app.setting
|
||||||
|
|
||||||
|
# Cache of project
|
||||||
|
.gradletasknamecache
|
||||||
|
|
||||||
|
**/build/
|
||||||
|
|
||||||
|
# Common working directory
|
||||||
|
run/
|
||||||
|
runs/
|
||||||
|
|
||||||
|
# Avoid ignoring Gradle wrapper jar file (.jar files are usually ignored)
|
||||||
|
!gradle-wrapper.jar
|
2
java/clientmod/LICENSE.txt
Normal file
2
java/clientmod/LICENSE.txt
Normal file
@ -0,0 +1,2 @@
|
|||||||
|
Copyright (c) 2025
|
||||||
|
All rights reserved.
|
116
java/clientmod/build.gradle.kts
Normal file
116
java/clientmod/build.gradle.kts
Normal file
@ -0,0 +1,116 @@
|
|||||||
|
import org.jetbrains.kotlin.gradle.dsl.JvmTarget
|
||||||
|
import org.jetbrains.kotlin.gradle.tasks.KotlinCompile
|
||||||
|
|
||||||
|
plugins {
|
||||||
|
kotlin("jvm") version "2.1.10"
|
||||||
|
kotlin("plugin.serialization") version "2.1.10"
|
||||||
|
id("fabric-loom") version "1.10.1"
|
||||||
|
id("maven-publish")
|
||||||
|
}
|
||||||
|
|
||||||
|
version = project.property("mod_version") as String
|
||||||
|
group = project.property("maven_group") as String
|
||||||
|
|
||||||
|
val ktor_version = "3.1.1"
|
||||||
|
|
||||||
|
base {
|
||||||
|
archivesName.set(project.property("archives_base_name") as String)
|
||||||
|
}
|
||||||
|
|
||||||
|
val targetJavaVersion = 21
|
||||||
|
java {
|
||||||
|
toolchain.languageVersion = JavaLanguageVersion.of(targetJavaVersion)
|
||||||
|
// Loom will automatically attach sourcesJar to a RemapSourcesJar task and to the "build" task
|
||||||
|
// if it is present.
|
||||||
|
// If you remove this line, sources will not be generated.
|
||||||
|
withSourcesJar()
|
||||||
|
}
|
||||||
|
|
||||||
|
loom {
|
||||||
|
splitEnvironmentSourceSets()
|
||||||
|
|
||||||
|
mods {
|
||||||
|
register("wynnreporter") {
|
||||||
|
sourceSet("main")
|
||||||
|
sourceSet("client")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
repositories {
|
||||||
|
// Add repositories to retrieve artifacts from in here.
|
||||||
|
// You should only use this when depending on other mods because
|
||||||
|
// Loom adds the essential maven repositories to download Minecraft and libraries from automatically.
|
||||||
|
// See https://docs.gradle.org/current/userguide/declaring_repositories.html
|
||||||
|
// for more information about repositories.
|
||||||
|
}
|
||||||
|
|
||||||
|
dependencies {
|
||||||
|
// To change the versions see the gradle.properties file
|
||||||
|
minecraft("com.mojang:minecraft:${project.property("minecraft_version")}")
|
||||||
|
mappings("net.fabricmc:yarn:${project.property("yarn_mappings")}:v2")
|
||||||
|
modImplementation("net.fabricmc:fabric-loader:${project.property("loader_version")}")
|
||||||
|
modImplementation("net.fabricmc:fabric-language-kotlin:${project.property("kotlin_loader_version")}")
|
||||||
|
|
||||||
|
modImplementation("net.fabricmc.fabric-api:fabric-api:${project.property("fabric_version")}")
|
||||||
|
|
||||||
|
|
||||||
|
// non minecraft-related dependencies
|
||||||
|
implementation("io.ktor:ktor-client-core:$ktor_version")
|
||||||
|
implementation("io.ktor:ktor-client-cio:$ktor_version")
|
||||||
|
implementation("io.ktor:ktor-client-resources:$ktor_version")
|
||||||
|
implementation("org.jetbrains.kotlinx:kotlinx-serialization-json:1.8.0")
|
||||||
|
}
|
||||||
|
|
||||||
|
tasks.processResources {
|
||||||
|
inputs.property("version", project.version)
|
||||||
|
inputs.property("minecraft_version", project.property("minecraft_version"))
|
||||||
|
inputs.property("loader_version", project.property("loader_version"))
|
||||||
|
filteringCharset = "UTF-8"
|
||||||
|
|
||||||
|
filesMatching("fabric.mod.json") {
|
||||||
|
expand(
|
||||||
|
"version" to project.version,
|
||||||
|
"minecraft_version" to project.property("minecraft_version"),
|
||||||
|
"loader_version" to project.property("loader_version"),
|
||||||
|
"kotlin_loader_version" to project.property("kotlin_loader_version")
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
tasks.withType<JavaCompile>().configureEach {
|
||||||
|
// ensure that the encoding is set to UTF-8, no matter what the system default is
|
||||||
|
// this fixes some edge cases with special characters not displaying correctly
|
||||||
|
// see http://yodaconditions.net/blog/fix-for-java-file-encoding-problems-with-gradle.html
|
||||||
|
// If Javadoc is generated, this must be specified in that task too.
|
||||||
|
options.encoding = "UTF-8"
|
||||||
|
options.release.set(targetJavaVersion)
|
||||||
|
}
|
||||||
|
|
||||||
|
tasks.withType<KotlinCompile>().configureEach {
|
||||||
|
compilerOptions.jvmTarget.set(JvmTarget.fromTarget(targetJavaVersion.toString()))
|
||||||
|
}
|
||||||
|
|
||||||
|
tasks.jar {
|
||||||
|
from("LICENSE") {
|
||||||
|
rename { "${it}_${project.base.archivesName}" }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// configure the maven publication
|
||||||
|
publishing {
|
||||||
|
publications {
|
||||||
|
create<MavenPublication>("mavenJava") {
|
||||||
|
artifactId = project.property("archives_base_name") as String
|
||||||
|
from(components["java"])
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// See https://docs.gradle.org/current/userguide/publishing_maven.html for information on how to set up publishing.
|
||||||
|
repositories {
|
||||||
|
// Add repositories to publish to here.
|
||||||
|
// Notice: This block does NOT have the same function as the block in the top level.
|
||||||
|
// The repositories here will be used for publishing your artifact, not for
|
||||||
|
// retrieving dependencies.
|
||||||
|
}
|
||||||
|
}
|
15
java/clientmod/gradle.properties
Normal file
15
java/clientmod/gradle.properties
Normal file
@ -0,0 +1,15 @@
|
|||||||
|
# Done to increase the memory available to gradle.
|
||||||
|
org.gradle.jvmargs=-Xmx1G
|
||||||
|
# Fabric Properties
|
||||||
|
# check these on https://modmuss50.me/fabric.html
|
||||||
|
minecraft_version=1.21.4
|
||||||
|
yarn_mappings=1.21.4+build.8
|
||||||
|
loader_version=0.16.10
|
||||||
|
kotlin_loader_version=1.13.1+kotlin.2.1.10
|
||||||
|
# Mod Properties
|
||||||
|
mod_version=1.0-SNAPSHOT
|
||||||
|
maven_group=org.tux
|
||||||
|
archives_base_name=clientmod
|
||||||
|
# Dependencies
|
||||||
|
# check this on https://modmuss50.me/fabric.html
|
||||||
|
fabric_version=0.118.0+1.21.4
|
1
java/clientmod/gradle/wrapper/gradle-wrapper.properties
vendored
Normal file
1
java/clientmod/gradle/wrapper/gradle-wrapper.properties
vendored
Normal file
@ -0,0 +1 @@
|
|||||||
|
distributionUrl=https\://services.gradle.org/distributions/gradle-8.12.1-bin.zip
|
8
java/clientmod/settings.gradle.kts
Normal file
8
java/clientmod/settings.gradle.kts
Normal file
@ -0,0 +1,8 @@
|
|||||||
|
pluginManagement {
|
||||||
|
repositories {
|
||||||
|
maven("https://maven.fabricmc.net/") {
|
||||||
|
name = "Fabric"
|
||||||
|
}
|
||||||
|
gradlePluginPortal()
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,49 @@
|
|||||||
|
package org.tux.wynnreporter.client
|
||||||
|
|
||||||
|
import client.kotlin.org.tux.wynnreporter.client.singleton.InventoryReader
|
||||||
|
import kotlinx.coroutines.DelicateCoroutinesApi
|
||||||
|
import kotlinx.coroutines.GlobalScope
|
||||||
|
import kotlinx.coroutines.channels.Channel
|
||||||
|
import kotlinx.coroutines.launch
|
||||||
|
import kotlinx.coroutines.runBlocking
|
||||||
|
import net.fabricmc.api.ClientModInitializer
|
||||||
|
import net.fabricmc.fabric.api.client.event.lifecycle.v1.ClientLifecycleEvents
|
||||||
|
import net.fabricmc.fabric.api.client.event.lifecycle.v1.ClientTickEvents
|
||||||
|
import net.minecraft.client.MinecraftClient
|
||||||
|
import org.tux.wynnreporter.Wynnreporter
|
||||||
|
import org.tux.wynnreporter.client.processors.InventoryProcessor
|
||||||
|
import org.tux.wynnreporter.client.structs.InventoryPayload
|
||||||
|
|
||||||
|
class WynnreporterClient : ClientModInitializer {
|
||||||
|
|
||||||
|
private val inventoryPayloadChannel = Channel<InventoryPayload>()
|
||||||
|
private val inventoryReader: InventoryReader = InventoryReader(inventoryPayloadChannel)
|
||||||
|
|
||||||
|
|
||||||
|
@OptIn(DelicateCoroutinesApi::class)
|
||||||
|
override fun onInitializeClient() {
|
||||||
|
Wynnreporter.LOGGER.info("initializing client")
|
||||||
|
ClientTickEvents.END_CLIENT_TICK.register(this::onEndClientTick)
|
||||||
|
val inventoryProcessor = InventoryProcessor(inventoryPayloadChannel)
|
||||||
|
// can you tell i write golang lol
|
||||||
|
runBlocking {
|
||||||
|
GlobalScope.launch{
|
||||||
|
inventoryProcessor.start()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
ClientLifecycleEvents.CLIENT_STARTED.register { client ->
|
||||||
|
Wynnreporter.LOGGER.info("client starting")
|
||||||
|
Wynnreporter.LOGGER.info("client started")
|
||||||
|
}
|
||||||
|
ClientLifecycleEvents.CLIENT_STOPPING.register { client ->
|
||||||
|
Wynnreporter.LOGGER.info("client stopping")
|
||||||
|
inventoryPayloadChannel.close()
|
||||||
|
Wynnreporter.LOGGER.info("client stopped")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fun onEndClientTick(client: MinecraftClient?) {
|
||||||
|
inventoryReader.handle(client)
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@ -0,0 +1,16 @@
|
|||||||
|
package org.tux.wynnreporter.client.processors
|
||||||
|
|
||||||
|
import kotlinx.coroutines.channels.Channel
|
||||||
|
import org.tux.wynnreporter.Wynnreporter
|
||||||
|
import org.tux.wynnreporter.client.structs.InventoryPayload
|
||||||
|
|
||||||
|
class InventoryProcessor (
|
||||||
|
val invChannel: Channel<InventoryPayload>
|
||||||
|
){
|
||||||
|
suspend fun start() {
|
||||||
|
for(item in invChannel) {
|
||||||
|
Wynnreporter.LOGGER.info("got item {}", item)
|
||||||
|
}
|
||||||
|
Wynnreporter.LOGGER.info("inventory payload channel closed. exiting")
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,69 @@
|
|||||||
|
package client.kotlin.org.tux.wynnreporter.client.singleton
|
||||||
|
|
||||||
|
import kotlinx.coroutines.channels.Channel
|
||||||
|
import kotlinx.coroutines.channels.onFailure
|
||||||
|
import net.minecraft.client.MinecraftClient
|
||||||
|
import net.minecraft.client.gui.screen.Screen
|
||||||
|
import net.minecraft.client.gui.screen.ingame.GenericContainerScreen
|
||||||
|
import org.tux.wynnreporter.Wynnreporter
|
||||||
|
import org.tux.wynnreporter.client.structs.InventoryPayload
|
||||||
|
import org.tux.wynnreporter.client.structs.InventoryPayloadItem
|
||||||
|
import java.util.*
|
||||||
|
|
||||||
|
|
||||||
|
class InventoryReader(
|
||||||
|
val invChannel: Channel<InventoryPayload>
|
||||||
|
){
|
||||||
|
private var previousScreen: Screen? = null
|
||||||
|
|
||||||
|
private fun tick(client: MinecraftClient, screen: Screen) {
|
||||||
|
val containerScreen = screen as? GenericContainerScreen ?: return
|
||||||
|
val inventory = containerScreen.screenHandler.inventory
|
||||||
|
if(inventory.isEmpty) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
val itemList = mutableListOf<InventoryPayloadItem>()
|
||||||
|
// iterate over size
|
||||||
|
for(i in 0 until inventory.size()) {
|
||||||
|
val stack= inventory.getStack(i)
|
||||||
|
if(stack.isEmpty) {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
val item = stack.item;
|
||||||
|
val components = stack.components
|
||||||
|
val componentMap = mutableMapOf<String, String>()
|
||||||
|
components.forEach { component ->
|
||||||
|
componentMap[component.type.toString()] = component.value.toString()
|
||||||
|
}
|
||||||
|
// create a copy to send to the channel so that we dont block main thread :)
|
||||||
|
itemList.add(InventoryPayloadItem(
|
||||||
|
item.name.toString(),
|
||||||
|
stack.count,
|
||||||
|
componentMap,
|
||||||
|
))
|
||||||
|
}
|
||||||
|
val payload = InventoryPayload(
|
||||||
|
client.player?.uuid ?: UUID(0,0),
|
||||||
|
containerScreen.title.toString(),
|
||||||
|
itemList,
|
||||||
|
)
|
||||||
|
invChannel.trySend(payload).onFailure { cause ->
|
||||||
|
Wynnreporter.LOGGER.warn("failed to send inventory payload", cause)
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
fun handle(client: MinecraftClient?) {
|
||||||
|
val screen: Screen? = client?.currentScreen
|
||||||
|
// see if the screen changed
|
||||||
|
if (screen == null || screen == previousScreen) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
this.tick(client, screen)
|
||||||
|
// screen didnt change, run callback
|
||||||
|
this.previousScreen = screen
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
}
|
@ -0,0 +1,31 @@
|
|||||||
|
package org.tux.wynnreporter.client.services
|
||||||
|
import io.ktor.client.*
|
||||||
|
import io.ktor.client.engine.cio.*
|
||||||
|
import io.ktor.client.request.*
|
||||||
|
import io.ktor.client.plugins.*
|
||||||
|
import io.ktor.client.plugins.resources.*
|
||||||
|
import io.ktor.client.plugins.resources.Resources
|
||||||
|
import io.ktor.resources.*
|
||||||
|
import java.util.UUID
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
class ApiClient(val rootUrl: String) {
|
||||||
|
|
||||||
|
val client = HttpClient(CIO) {
|
||||||
|
expectSuccess = true
|
||||||
|
defaultRequest {
|
||||||
|
url(rootUrl)
|
||||||
|
}
|
||||||
|
install(Resources)
|
||||||
|
}
|
||||||
|
|
||||||
|
suspend fun RequestChallenge(uuid: UUID) {
|
||||||
|
val response = client.get {
|
||||||
|
url("/api/v1/ingame/challenge")
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,12 @@
|
|||||||
|
package org.tux.wynnreporter.client.services.ApiClientSpec
|
||||||
|
|
||||||
|
import io.ktor.resources.*
|
||||||
|
|
||||||
|
@Resource("/api/v1")
|
||||||
|
class ApiV1 {
|
||||||
|
@Resource("ingame")
|
||||||
|
class Ingame {
|
||||||
|
@Resource("challenge")
|
||||||
|
class Challenge(val parent:)
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,6 @@
|
|||||||
|
package org.tux.wynnreporter.client.services.ApiClientSpec
|
||||||
|
|
||||||
|
import kotlinx.serialization.Serializable
|
||||||
|
|
||||||
|
@Serializable
|
||||||
|
data class GetChallengeResponse()
|
@ -0,0 +1,8 @@
|
|||||||
|
package org.tux.wynnreporter.client.structs
|
||||||
|
import java.util.UUID
|
||||||
|
|
||||||
|
data class InventoryPayload(
|
||||||
|
val opener: UUID,
|
||||||
|
val name: String,
|
||||||
|
val items: List<InventoryPayloadItem>,
|
||||||
|
)
|
@ -0,0 +1,7 @@
|
|||||||
|
package org.tux.wynnreporter.client.structs
|
||||||
|
|
||||||
|
data class InventoryPayloadItem(
|
||||||
|
val itemName: String,
|
||||||
|
val stackSize: Int,
|
||||||
|
val components: Map<String, String>,
|
||||||
|
)
|
@ -0,0 +1,11 @@
|
|||||||
|
{
|
||||||
|
"required": true,
|
||||||
|
"minVersion": "0.8",
|
||||||
|
"package": "org.tux.wynnreporter.mixin.client",
|
||||||
|
"compatibilityLevel": "JAVA_21",
|
||||||
|
"client": [
|
||||||
|
],
|
||||||
|
"injectors": {
|
||||||
|
"defaultRequire": 1
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,18 @@
|
|||||||
|
package org.tux.wynnreporter
|
||||||
|
|
||||||
|
import net.fabricmc.api.ModInitializer
|
||||||
|
|
||||||
|
import org.slf4j.Logger;
|
||||||
|
import org.slf4j.LoggerFactory;
|
||||||
|
|
||||||
|
class Wynnreporter : ModInitializer {
|
||||||
|
|
||||||
|
companion object {
|
||||||
|
private const val MOD_ID: String = "wynnreporter"
|
||||||
|
val LOGGER: Logger = LoggerFactory.getLogger(MOD_ID)
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onInitialize() {
|
||||||
|
LOGGER.info("initializing wynnreporter")
|
||||||
|
}
|
||||||
|
}
|
33
java/clientmod/src/main/resources/fabric.mod.json
Normal file
33
java/clientmod/src/main/resources/fabric.mod.json
Normal file
@ -0,0 +1,33 @@
|
|||||||
|
{
|
||||||
|
"schemaVersion": 1,
|
||||||
|
"id": "wynnreporter",
|
||||||
|
"version": "${version}",
|
||||||
|
"name": "wynnreporter",
|
||||||
|
"description": "",
|
||||||
|
"authors": [],
|
||||||
|
"contact": {},
|
||||||
|
"license": "All-Rights-Reserved",
|
||||||
|
"icon": "assets/wynnreporter/icon.png",
|
||||||
|
"environment": "client",
|
||||||
|
"entrypoints": {
|
||||||
|
"client": [
|
||||||
|
"org.tux.wynnreporter.client.WynnreporterClient"
|
||||||
|
],
|
||||||
|
"main": [
|
||||||
|
"org.tux.wynnreporter.Wynnreporter"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"mixins": [
|
||||||
|
"wynnreporter.mixins.json",
|
||||||
|
{
|
||||||
|
"config": "wynnreporter.client.mixins.json",
|
||||||
|
"environment": "client"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"depends": {
|
||||||
|
"fabricloader": ">=${loader_version}",
|
||||||
|
"fabric-language-kotlin": ">=${kotlin_loader_version}",
|
||||||
|
"fabric": "*",
|
||||||
|
"minecraft": "${minecraft_version}"
|
||||||
|
}
|
||||||
|
}
|
11
java/clientmod/src/main/resources/wynnreporter.mixins.json
Normal file
11
java/clientmod/src/main/resources/wynnreporter.mixins.json
Normal file
@ -0,0 +1,11 @@
|
|||||||
|
{
|
||||||
|
"required": true,
|
||||||
|
"minVersion": "0.8",
|
||||||
|
"package": "org.tux.wynnreporter.mixin",
|
||||||
|
"compatibilityLevel": "JAVA_21",
|
||||||
|
"mixins": [
|
||||||
|
],
|
||||||
|
"injectors": {
|
||||||
|
"defaultRequire": 1
|
||||||
|
}
|
||||||
|
}
|
Binary file not shown.
@ -18,9 +18,11 @@
|
|||||||
"@temporalio/common": "^1.11.7",
|
"@temporalio/common": "^1.11.7",
|
||||||
"@temporalio/worker": "^1.11.7",
|
"@temporalio/worker": "^1.11.7",
|
||||||
"@temporalio/workflow": "^1.11.7",
|
"@temporalio/workflow": "^1.11.7",
|
||||||
|
"@ts-rest/core": "https://gitpkg.vercel.app/aidant/ts-rest/libs/ts-rest/core?feat-standard-schema",
|
||||||
|
"@ts-rest/fastify": "https://gitpkg.vercel.app/aidant/ts-rest/libs/ts-rest/fastify?feat-standard-schema",
|
||||||
"@types/node": "^22.13.4",
|
"@types/node": "^22.13.4",
|
||||||
"any-date-parser": "^2.0.3",
|
"any-date-parser": "^2.0.3",
|
||||||
"arktype": "2.0.4",
|
"arktype": "2.1.1",
|
||||||
"axios": "^1.7.9",
|
"axios": "^1.7.9",
|
||||||
"axios-cache-interceptor": "^1.6.2",
|
"axios-cache-interceptor": "^1.6.2",
|
||||||
"chrono-node": "^2.7.8",
|
"chrono-node": "^2.7.8",
|
||||||
|
@ -60,7 +60,6 @@ export async function update_guild({
|
|||||||
created = EXCLUDED.created
|
created = EXCLUDED.created
|
||||||
`
|
`
|
||||||
const {total, ...rest} = parsed.members
|
const {total, ...rest} = parsed.members
|
||||||
|
|
||||||
for(const [rank_name, rank] of Object.entries(rest)){
|
for(const [rank_name, rank] of Object.entries(rest)){
|
||||||
for(const [userName, member] of Object.entries(rank)) {
|
for(const [userName, member] of Object.entries(rank)) {
|
||||||
await sql`insert into wynn_guild_members
|
await sql`insert into wynn_guild_members
|
||||||
@ -71,7 +70,6 @@ export async function update_guild({
|
|||||||
joined_at = EXCLUDED.joined_at,
|
joined_at = EXCLUDED.joined_at,
|
||||||
contributed = EXCLUDED.contributed
|
contributed = EXCLUDED.contributed
|
||||||
`
|
`
|
||||||
|
|
||||||
await sql`insert into minecraft_user
|
await sql`insert into minecraft_user
|
||||||
(uid, name, server) values (${member.uuid}, ${userName}, ${member.server})
|
(uid, name, server) values (${member.uuid}, ${userName}, ${member.server})
|
||||||
on conflict (uid) do update set
|
on conflict (uid) do update set
|
||||||
|
50
ts/src/apiserver/contract.ts
Normal file
50
ts/src/apiserver/contract.ts
Normal file
@ -0,0 +1,50 @@
|
|||||||
|
import { initContract } from "@ts-rest/core/src";
|
||||||
|
import { type } from "arktype";
|
||||||
|
|
||||||
|
const con = initContract();
|
||||||
|
|
||||||
|
|
||||||
|
const ingameauth = con.router({
|
||||||
|
challenge: {
|
||||||
|
description: "generate a challenge for the client to solve",
|
||||||
|
method: "GET",
|
||||||
|
path: "/challenge",
|
||||||
|
responses: {
|
||||||
|
200: type({
|
||||||
|
challenge: "string.uuid",
|
||||||
|
}),
|
||||||
|
},
|
||||||
|
query: type({
|
||||||
|
uuid: "string.uuid",
|
||||||
|
}),
|
||||||
|
},
|
||||||
|
solve: {
|
||||||
|
description: "attempt to solve the challenge and get the token for the challenge",
|
||||||
|
method: "POST",
|
||||||
|
path: "/solve",
|
||||||
|
body: type({
|
||||||
|
challenge: "string.uuid",
|
||||||
|
uuid: "string.uuid",
|
||||||
|
}),
|
||||||
|
responses: {
|
||||||
|
200: type({
|
||||||
|
success: "true",
|
||||||
|
challenge: "string.uuid",
|
||||||
|
uuid: "string.uuid",
|
||||||
|
}),
|
||||||
|
401: type({
|
||||||
|
success: "false",
|
||||||
|
reason: "string",
|
||||||
|
}),
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}, {pathPrefix: "/ingame"})
|
||||||
|
|
||||||
|
export const api = con.router({
|
||||||
|
"ingameauth": ingameauth,
|
||||||
|
}, {pathPrefix: "/api/v1"})
|
||||||
|
|
||||||
|
export const contract = con.router({
|
||||||
|
api: api
|
||||||
|
})
|
||||||
|
|
@ -1,7 +1,8 @@
|
|||||||
import {bot} from "#/bot"
|
import {Bot, BotType} from "#/bot"
|
||||||
import { ActivityTypes, ApplicationCommandOptionTypes, InteractionTypes } from "discordeno"
|
import { ActivityTypes, ApplicationCommandOptionTypes, InteractionTypes } from "discordeno"
|
||||||
import { InteractionType, MuxHandler, SlashHandler } from "./types"
|
import { InteractionType, MuxHandler, SlashHandler } from "./types"
|
||||||
import { uuid4 } from "@temporalio/workflow"
|
import { uuid4 } from "@temporalio/workflow"
|
||||||
|
import { c } from "#/di"
|
||||||
|
|
||||||
|
|
||||||
export const slashHandler = async (interaction: InteractionType, rootHandler: SlashHandler) => {
|
export const slashHandler = async (interaction: InteractionType, rootHandler: SlashHandler) => {
|
||||||
@ -58,6 +59,7 @@ export const events = (rootHandler: SlashHandler) => {return {
|
|||||||
return
|
return
|
||||||
},
|
},
|
||||||
ready: async ({shardId}) => {
|
ready: async ({shardId}) => {
|
||||||
|
const bot = await c.getAsync(Bot)
|
||||||
await bot.gateway.editShardStatus(shardId, {
|
await bot.gateway.editShardStatus(shardId, {
|
||||||
status: 'online',
|
status: 'online',
|
||||||
activities: [
|
activities: [
|
||||||
@ -71,4 +73,4 @@ export const events = (rootHandler: SlashHandler) => {return {
|
|||||||
],
|
],
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
} as typeof bot.events}
|
} as BotType['events']}
|
||||||
|
@ -1,7 +1,8 @@
|
|||||||
import {bot} from "#/bot"
|
import {BotType} from "#/bot"
|
||||||
|
|
||||||
export type BotEventsType = typeof bot.events
|
|
||||||
export type InteractionHandler = NonNullable<typeof bot.events.interactionCreate>
|
export type BotEventsType = BotType['events']
|
||||||
|
export type InteractionHandler = NonNullable<BotType['events']['interactionCreate']>
|
||||||
export type InteractionType = Parameters<InteractionHandler>[0]
|
export type InteractionType = Parameters<InteractionHandler>[0]
|
||||||
|
|
||||||
export type MuxHandler<T> = (interaction: InteractionType, params?: T) => Promise<any>
|
export type MuxHandler<T> = (interaction: InteractionType, params?: T) => Promise<any>
|
||||||
|
@ -1,4 +1,6 @@
|
|||||||
import { config } from "#/config";
|
import { config } from "#/config";
|
||||||
|
import { c } from "#/di";
|
||||||
|
import { InjectionToken } from "@needle-di/core";
|
||||||
|
|
||||||
import {createBot, Intents} from "discordeno"
|
import {createBot, Intents} from "discordeno"
|
||||||
|
|
||||||
@ -45,28 +47,16 @@ export const createBotWithToken = (token: string) => createBot({
|
|||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
export const bot = createBot({
|
export type BotType = ReturnType<typeof createBotWithToken>
|
||||||
intents: intents.reduce((acc, curr) => acc | curr, Intents.Guilds),
|
export const Bot = new InjectionToken<BotType>("DISCORD_BOT")
|
||||||
token: config.DISCORD_TOKEN || "",
|
c.bind({
|
||||||
desiredProperties: {
|
provide: Bot,
|
||||||
interaction: {
|
async: true,
|
||||||
id: true,
|
useFactory: async () => {
|
||||||
data: true,
|
let token = config.DISCORD_TOKEN
|
||||||
type: true,
|
if(!token) {
|
||||||
token: true,
|
throw new Error('no discord token found. bot cant start');
|
||||||
message: true,
|
}
|
||||||
channelId: true,
|
return createBotWithToken(token)
|
||||||
channel: true,
|
},
|
||||||
guildId: true,
|
|
||||||
guild: true,
|
|
||||||
user: true,
|
|
||||||
member: true,
|
|
||||||
},
|
|
||||||
message: {
|
|
||||||
id: true,
|
|
||||||
member: true,
|
|
||||||
guildId: true,
|
|
||||||
},
|
|
||||||
}
|
|
||||||
})
|
})
|
||||||
|
|
||||||
|
@ -4,7 +4,7 @@ import { Command } from 'clipanion';
|
|||||||
// di
|
// di
|
||||||
import "#/services/pg"
|
import "#/services/pg"
|
||||||
import { DISCORD_GUILD_ID } from '#/constants';
|
import { DISCORD_GUILD_ID } from '#/constants';
|
||||||
import { bot } from '#/bot';
|
import { Bot } from '#/bot';
|
||||||
import { events } from '#/bot/botevent/handler';
|
import { events } from '#/bot/botevent/handler';
|
||||||
import { SlashCommandHandler } from '#/bot/botevent/slash_commands';
|
import { SlashCommandHandler } from '#/bot/botevent/slash_commands';
|
||||||
import { c } from '#/di';
|
import { c } from '#/di';
|
||||||
@ -18,6 +18,7 @@ export class BotCommand extends Command {
|
|||||||
throw new Error('no discord token found. bot cant start');
|
throw new Error('no discord token found. bot cant start');
|
||||||
}
|
}
|
||||||
const rootHandler = await c.getAsync(SlashCommandHandler)
|
const rootHandler = await c.getAsync(SlashCommandHandler)
|
||||||
|
const bot = await c.getAsync(Bot)
|
||||||
bot.events = events(rootHandler.root())
|
bot.events = events(rootHandler.root())
|
||||||
console.log('registring slash commands');
|
console.log('registring slash commands');
|
||||||
await bot.rest.upsertGuildApplicationCommands(DISCORD_GUILD_ID, rootHandler.commands()).catch(console.error)
|
await bot.rest.upsertGuildApplicationCommands(DISCORD_GUILD_ID, rootHandler.commands()).catch(console.error)
|
||||||
|
@ -4,11 +4,8 @@ import { c } from '#/di';
|
|||||||
import { runMigrations } from '#/services/pg/migrations';
|
import { runMigrations } from '#/services/pg/migrations';
|
||||||
|
|
||||||
// di
|
// di
|
||||||
import "#/services/pg"
|
|
||||||
import "#/services/temporal"
|
import "#/services/temporal"
|
||||||
|
|
||||||
import { NativeConnection, Worker } from '@temporalio/worker';
|
import { NativeConnection, Worker } from '@temporalio/worker';
|
||||||
import { config } from '#/config';
|
|
||||||
import * as activities from '../activities';
|
import * as activities from '../activities';
|
||||||
import path from 'path';
|
import path from 'path';
|
||||||
import { Client, ScheduleNotFoundError, ScheduleOptions, ScheduleOverlapPolicy } from '@temporalio/client';
|
import { Client, ScheduleNotFoundError, ScheduleOptions, ScheduleOverlapPolicy } from '@temporalio/client';
|
||||||
@ -16,6 +13,9 @@ import { workflowSyncAllGuilds, workflowSyncGuilds, workflowSyncOnline, workflow
|
|||||||
import { PG } from '#/services/pg';
|
import { PG } from '#/services/pg';
|
||||||
|
|
||||||
|
|
||||||
|
import { config } from '#/config';
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
const schedules: ScheduleOptions[] = [
|
const schedules: ScheduleOptions[] = [
|
||||||
{
|
{
|
||||||
|
58
ts/yarn.lock
58
ts/yarn.lock
@ -5,19 +5,19 @@ __metadata:
|
|||||||
version: 8
|
version: 8
|
||||||
cacheKey: 10c0
|
cacheKey: 10c0
|
||||||
|
|
||||||
"@ark/schema@npm:0.39.0":
|
"@ark/schema@npm:0.42.0":
|
||||||
version: 0.39.0
|
version: 0.42.0
|
||||||
resolution: "@ark/schema@npm:0.39.0"
|
resolution: "@ark/schema@npm:0.42.0"
|
||||||
dependencies:
|
dependencies:
|
||||||
"@ark/util": "npm:0.39.0"
|
"@ark/util": "npm:0.42.0"
|
||||||
checksum: 10c0/8a279ac688218937ca5585670e6eb8f3bac9c3ccf5f1868e3223ff9f1edabe713e76b30715510606f154e8f0208cd2e1b6551fd947f6cfae309bd7a65cf6c315
|
checksum: 10c0/d0e93fc456d0ee835ad6fa70317202992d7ede0f847506ad61d7317ce9677f96731f2e2ab1380f8e372b1b3f6e0a95c76305a81eb0abf02184ee90fea719ac75
|
||||||
languageName: node
|
languageName: node
|
||||||
linkType: hard
|
linkType: hard
|
||||||
|
|
||||||
"@ark/util@npm:0.39.0":
|
"@ark/util@npm:0.42.0":
|
||||||
version: 0.39.0
|
version: 0.42.0
|
||||||
resolution: "@ark/util@npm:0.39.0"
|
resolution: "@ark/util@npm:0.42.0"
|
||||||
checksum: 10c0/0e5a580e0e238ef457c07aa68f9382ae34fae8ced93dddd5c3402bce9898b8372e2a2022327b9d234eab8adc7d9a347e731721d329049dad17be37c9257b05f8
|
checksum: 10c0/7639c66aa876c475d66f41c8767e4977958293c5516e36cbd6541ce98421b091d6ec97455522e7a0361cfca5383991e4fbc40ccc3fd9bf701a0ac227561cccbc
|
||||||
languageName: node
|
languageName: node
|
||||||
linkType: hard
|
linkType: hard
|
||||||
|
|
||||||
@ -746,6 +746,30 @@ __metadata:
|
|||||||
languageName: node
|
languageName: node
|
||||||
linkType: hard
|
linkType: hard
|
||||||
|
|
||||||
|
"@ts-rest/core@https://gitpkg.vercel.app/aidant/ts-rest/libs/ts-rest/core?feat-standard-schema":
|
||||||
|
version: 3.52.0
|
||||||
|
resolution: "@ts-rest/core@https://gitpkg.vercel.app/aidant/ts-rest/libs/ts-rest/core?feat-standard-schema"
|
||||||
|
peerDependencies:
|
||||||
|
"@types/node": ^18.18.7 || >=20.8.4
|
||||||
|
zod: ^3.24.0
|
||||||
|
peerDependenciesMeta:
|
||||||
|
"@types/node":
|
||||||
|
optional: true
|
||||||
|
zod:
|
||||||
|
optional: true
|
||||||
|
checksum: 10c0/fba55ca7d1a5161d3ec1af850c9c98d34efd1b4373ad8ca4108737c2d75fa11ef033da6e9cb657cd5078465c09ff6761c029053f9c59022075b8d3e128f298a5
|
||||||
|
languageName: node
|
||||||
|
linkType: hard
|
||||||
|
|
||||||
|
"@ts-rest/fastify@https://gitpkg.vercel.app/aidant/ts-rest/libs/ts-rest/fastify?feat-standard-schema":
|
||||||
|
version: 3.52.0
|
||||||
|
resolution: "@ts-rest/fastify@https://gitpkg.vercel.app/aidant/ts-rest/libs/ts-rest/fastify?feat-standard-schema"
|
||||||
|
peerDependencies:
|
||||||
|
fastify: ^4.0.0
|
||||||
|
checksum: 10c0/8e8a31fda5a49c4fc976962df29129a24ad3c9c4896af38a6deb95bf60e36943c29bef56ff601bb63cab883d845095c7793ae266e92876cf74a7a05d238ba00f
|
||||||
|
languageName: node
|
||||||
|
linkType: hard
|
||||||
|
|
||||||
"@types/eslint-scope@npm:^3.7.7":
|
"@types/eslint-scope@npm:^3.7.7":
|
||||||
version: 3.7.7
|
version: 3.7.7
|
||||||
resolution: "@types/eslint-scope@npm:3.7.7"
|
resolution: "@types/eslint-scope@npm:3.7.7"
|
||||||
@ -1116,13 +1140,13 @@ __metadata:
|
|||||||
languageName: node
|
languageName: node
|
||||||
linkType: hard
|
linkType: hard
|
||||||
|
|
||||||
"arktype@npm:2.0.4":
|
"arktype@npm:2.1.1":
|
||||||
version: 2.0.4
|
version: 2.1.1
|
||||||
resolution: "arktype@npm:2.0.4"
|
resolution: "arktype@npm:2.1.1"
|
||||||
dependencies:
|
dependencies:
|
||||||
"@ark/schema": "npm:0.39.0"
|
"@ark/schema": "npm:0.42.0"
|
||||||
"@ark/util": "npm:0.39.0"
|
"@ark/util": "npm:0.42.0"
|
||||||
checksum: 10c0/2bff7326ffebedee74ada3456147a3adb833df939db0c48cb9a78e6aa433592cc1f1877999a2ed0559f1021b70ab1dfbdfa3a6eee2b52d0a5dd68e0249471acf
|
checksum: 10c0/6e416a61bbd6890fb589be222bf69b86b79145cda1a025fb87b6a78818629669a7cc8aadd6261b8d99e74d43e8858d959fcacdebd222341f97e73eefa3a39498
|
||||||
languageName: node
|
languageName: node
|
||||||
linkType: hard
|
linkType: hard
|
||||||
|
|
||||||
@ -1176,10 +1200,12 @@ __metadata:
|
|||||||
"@temporalio/common": "npm:^1.11.7"
|
"@temporalio/common": "npm:^1.11.7"
|
||||||
"@temporalio/worker": "npm:^1.11.7"
|
"@temporalio/worker": "npm:^1.11.7"
|
||||||
"@temporalio/workflow": "npm:^1.11.7"
|
"@temporalio/workflow": "npm:^1.11.7"
|
||||||
|
"@ts-rest/core": "https://gitpkg.vercel.app/aidant/ts-rest/libs/ts-rest/core?feat-standard-schema"
|
||||||
|
"@ts-rest/fastify": "https://gitpkg.vercel.app/aidant/ts-rest/libs/ts-rest/fastify?feat-standard-schema"
|
||||||
"@types/node": "npm:^22.13.4"
|
"@types/node": "npm:^22.13.4"
|
||||||
"@types/object-hash": "npm:^3"
|
"@types/object-hash": "npm:^3"
|
||||||
any-date-parser: "npm:^2.0.3"
|
any-date-parser: "npm:^2.0.3"
|
||||||
arktype: "npm:2.0.4"
|
arktype: "npm:2.1.1"
|
||||||
axios: "npm:^1.7.9"
|
axios: "npm:^1.7.9"
|
||||||
axios-cache-interceptor: "npm:^1.6.2"
|
axios-cache-interceptor: "npm:^1.6.2"
|
||||||
barrelsby: "npm:^2.8.1"
|
barrelsby: "npm:^2.8.1"
|
||||||
|
Loading…
Reference in New Issue
Block a user