Skip to content

Commit

Permalink
Restore web server
Browse files Browse the repository at this point in the history
  • Loading branch information
andyksaw committed Oct 19, 2024
1 parent 1f83b19 commit 2ed54ff
Show file tree
Hide file tree
Showing 11 changed files with 201 additions and 4 deletions.
Original file line number Diff line number Diff line change
@@ -1,9 +1,6 @@
package com.projectcitybuild.pcbridge.http

import com.projectcitybuild.pcbridge.http.requests.MojangRequest
import com.projectcitybuild.pcbridge.http.requests.PCBRequest
import retrofit2.Retrofit

internal fun Retrofit.pcb() = create(PCBRequest::class.java)

internal fun Retrofit.mojang() = create(MojangRequest::class.java)
1 change: 1 addition & 0 deletions pcbridge-spigot/build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ repositories {

dependencies {
implementation(project(":pcbridge-http"))
implementation(project(":pcbridge-web-server"))

// Paper
compileOnly("io.papermc.paper:paper-api:1.20.2-R0.1-SNAPSHOT")
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,8 @@ import com.projectcitybuild.pcbridge.support.spigot.SpigotCommandRegistry
import com.projectcitybuild.pcbridge.support.spigot.SpigotListenerRegistry
import com.projectcitybuild.pcbridge.support.spigot.SpigotNamespace
import com.projectcitybuild.pcbridge.support.spigot.SpigotTimer
import com.projectcitybuild.pcbridge.webserver.HttpServer
import com.projectcitybuild.pcbridge.webserver.HttpServerConfig
import io.github.reactivecircus.cache4k.Cache
import net.kyori.adventure.platform.bukkit.BukkitAudiences
import net.kyori.adventure.text.Component
Expand All @@ -77,6 +79,7 @@ fun pluginModule(_plugin: JavaPlugin) =
spigot(_plugin)
core()
http()
webServer()
integrations()

// Features
Expand Down Expand Up @@ -201,6 +204,20 @@ private fun Module.core() {
single { Store() }
}

private fun Module.webServer() {
single {
val config = get<Config>().get()

HttpServer(
config = HttpServerConfig(
authToken = config.webServer.token,
port = config.webServer.port,
),
delegate = WebServerDelegate(),
)
}
}

private fun Module.http() {
single {
val config = get<Config>().get()
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@ import com.projectcitybuild.pcbridge.integrations.LuckPermsIntegration
import com.projectcitybuild.pcbridge.support.spigot.SpigotCommandRegistry
import com.projectcitybuild.pcbridge.support.spigot.SpigotListenerRegistry
import com.projectcitybuild.pcbridge.support.spigot.SpigotTimer
import com.projectcitybuild.pcbridge.webserver.HttpServer
import net.kyori.adventure.platform.bukkit.BukkitAudiences
import org.koin.core.KoinApplication
import org.koin.core.component.KoinComponent
Expand Down Expand Up @@ -77,9 +78,12 @@ private class Lifecycle : KoinComponent {
private val sentry: SentryReporter by inject()
private val commandRegistry: SpigotCommandRegistry by inject()
private val listenerRegistry: SpigotListenerRegistry by inject()
private val httpServer: HttpServer by inject()

suspend fun boot() =
sentry.trace {
httpServer.start()

commandRegistry.apply {
register(
handler = get<WarpCommand>(),
Expand Down Expand Up @@ -134,6 +138,8 @@ private class Lifecycle : KoinComponent {

suspend fun shutdown() =
sentry.trace {
httpServer.stop()

get<SpigotTimer>().cancelAll()

get<DynmapIntegration>().disable()
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
package com.projectcitybuild.pcbridge

import com.projectcitybuild.pcbridge.webserver.HttpServerDelegate
import java.util.UUID

class WebServerDelegate() : HttpServerDelegate {

override fun syncPlayer(uuid: UUID) {
// val player = minecraftServer.onlinePlayers.firstOrNull {
// it.uniqueId.toString().unformatted() == uuid.unformatted()
// }
// if (player == null) {
// logger.info("No matching player found for sync request UUID: $uuid")
// return
// }
//
// log.info { "Syncing player: $uuid" }
//
// scheduler.async<Result<Unit, UpdatePlayerGroups.FailureReason>> { resolver ->
// CoroutineScope(Dispatchers.IO).launch {
// val result = updatePlayerGroups.execute(playerUUID = player.uniqueId)
// resolver(result)
// }
// }.startAndSubscribe { result ->
// when (result) {
// is Failure -> when (result.reason) {
// UpdatePlayerGroups.FailureReason.ACCOUNT_NOT_LINKED
// -> player.send().error("Your rank failed to be updated. Please contact a staff member")
// }
//
// is Success -> {
// player.send().success("Your rank has been updated")
// }
// }
// }
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,10 @@ class CodeCommand(
code = args.code,
playerUUID = sender.uniqueId,
)
sender.sendMessage(
Component.text("Registration complete! Your account will be synced momentarily...")
.color(NamedTextColor.GREEN),
)
} catch (e: ResponseParser.NotFoundError) {
sender.sendMessage(
Component.text("Error: Code is invalid or expired")
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,7 @@ class RegisterCommand(
sender.sendMessage(
MiniMessage.miniMessage().deserialize(
"<color:gray>A code has been emailed to ${args.email}.<newline>" +
"Please type it in with </color><color:aqua><bold><click:suggest_command:/code>/code [code]</click></bold></color>"
"Please type it in with </color><color:aqua><bold><hover:show_text:'/code'><click:suggest_command:/code >/code [code]</click></hover></bold></color>"
)
)
} catch (e: ResponseParser.ValidationError) {
Expand Down
20 changes: 20 additions & 0 deletions pcbridge-web-server/build.gradle.kts
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
plugins {
application
}

application {
mainClass.set("com.projectcitybuild.pcbridge.ApplicationKt")
}

repositories {
mavenCentral()
}

dependencies {
implementation(project(":pcbridge-http"))

implementation("io.ktor:ktor-server-core:2.2.4")
implementation("io.ktor:ktor-server-netty:2.2.4")
implementation("io.ktor:ktor-server-call-logging:2.2.4")
implementation("io.ktor:ktor-server-auth:2.2.4")
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,108 @@
package com.projectcitybuild.pcbridge.webserver

import io.ktor.http.HttpStatusCode
import io.ktor.server.application.call
import io.ktor.server.application.install
import io.ktor.server.auth.Authentication
import io.ktor.server.auth.UserIdPrincipal
import io.ktor.server.auth.authenticate
import io.ktor.server.auth.bearer
import io.ktor.server.engine.embeddedServer
import io.ktor.server.netty.Netty
import io.ktor.server.netty.NettyApplicationEngine
import io.ktor.server.plugins.callloging.CallLogging
import io.ktor.server.request.receiveParameters
import io.ktor.server.response.respond
import io.ktor.server.routing.post
import io.ktor.server.routing.routing
import org.slf4j.LoggerFactory
import org.slf4j.event.Level
import java.math.BigInteger
import java.util.UUID

interface HttpServerDelegate {
fun syncPlayer(uuid: UUID)
}

class HttpServer(
private val config: HttpServerConfig,
private val delegate: HttpServerDelegate,
) {
private var server: NettyApplicationEngine? = null

private val log
get() = LoggerFactory.getLogger("PCBridge.WebServer")

fun start() {
if (config.authToken.isEmpty()) {
log.warn("Internal web server token not set in config. Web server will not be started")
return
}

log.info("Starting web server on port ${config.port}")

server?.stop(gracePeriodMillis = 0, timeoutMillis = 0)
server = embeddedServer(Netty, port = config.port) {
install(CallLogging) {
level = Level.INFO
}
install(Authentication) {
bearer("token") {
authenticate { token ->
if (token.token == config.authToken) {
UserIdPrincipal("pcb-web")
} else {
null
}
}
}
}
routing {
authenticate("token") {
post("events/player/sync") {
val body = call.receiveParameters()

val rawUUID = body["uuid"]
if (rawUUID.isNullOrEmpty()) {
call.application.environment.log.info("Bad Request: No UUID provided")
call.respond(HttpStatusCode.BadRequest, "UUID cannot be empty")
return@post
}

val uuid = try {
uuidFromAnyString(rawUUID)
} catch (e: Exception) {
call.application.environment.log.info("Bad Request: Invalid UUID")
call.respond(HttpStatusCode.BadRequest, "Invalid UUID")
return@post
}

call.application.environment.log.info("Syncing player: $uuid")
delegate.syncPlayer(uuid)
call.respond(HttpStatusCode.OK)
}
}
}
}.start(wait = false)
}

fun stop() {
log.info("Stopping web server")

server?.stop(gracePeriodMillis = 0, timeoutMillis = 0)
server = null
}
}

/**
* Same as UUID.fromString(), except that it allows a non-hyphen string
*/
private fun uuidFromAnyString(string: String) = string.run {
if (string.contains("-")) {
UUID.fromString(string)
} else {
val bi1 = BigInteger(substring(0, 16), 16)
val bi2 = BigInteger(substring(16, 32), 16)
UUID(bi1.toLong(), bi2.toLong())
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
package com.projectcitybuild.pcbridge.webserver

data class HttpServerConfig(
val authToken: String,
val port: Int,
)
1 change: 1 addition & 0 deletions settings.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -2,3 +2,4 @@ rootProject.name = "pcbridge"

include("pcbridge-http")
include("pcbridge-spigot")
include("pcbridge-web-server")

0 comments on commit 2ed54ff

Please sign in to comment.