Open-source multiplayer game server compatible with the RuneScape client https://www.openrs2.org/
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 
openrs2/archive/src/main/kotlin/org/openrs2/archive/web/WebServer.kt

117 lines
5.3 KiB

package org.openrs2.archive.web
import com.fasterxml.jackson.databind.ObjectMapper
import io.ktor.http.ContentType
import io.ktor.serialization.jackson.JacksonConverter
import io.ktor.server.application.ApplicationCall
import io.ktor.server.application.call
import io.ktor.server.application.install
import io.ktor.server.cio.CIO
import io.ktor.server.engine.embeddedServer
import io.ktor.server.http.content.resources
import io.ktor.server.http.content.static
import io.ktor.server.plugins.autohead.AutoHeadResponse
import io.ktor.server.plugins.cachingheaders.CachingHeaders
import io.ktor.server.plugins.conditionalheaders.ConditionalHeaders
import io.ktor.server.plugins.contentnegotiation.ContentNegotiation
import io.ktor.server.plugins.cors.routing.CORS
import io.ktor.server.plugins.defaultheaders.DefaultHeaders
import io.ktor.server.plugins.forwardedheaders.XForwardedHeaders
import io.ktor.server.response.respond
import io.ktor.server.response.respondRedirect
import io.ktor.server.routing.get
import io.ktor.server.routing.post
import io.ktor.server.routing.routing
import io.ktor.server.thymeleaf.Thymeleaf
import io.ktor.server.thymeleaf.ThymeleafContent
import io.ktor.server.webjars.Webjars
import org.openrs2.json.Json
import org.thymeleaf.extras.java8time.dialect.Java8TimeDialect
import org.thymeleaf.templatemode.TemplateMode
import org.thymeleaf.templateresolver.ClassLoaderTemplateResolver
import javax.inject.Inject
import javax.inject.Singleton
@Singleton
public class WebServer @Inject constructor(
private val cachesController: CachesController,
private val keysController: KeysController,
@Json private val mapper: ObjectMapper
) {
public fun start(address: String, port: Int) {
embeddedServer(CIO, host = address, port = port) {
install(AutoHeadResponse)
install(CachingHeaders)
install(ConditionalHeaders)
install(CORS) {
anyHost()
}
install(ContentNegotiation) {
ignoreType<ThymeleafContent>()
register(ContentType.Application.Json, JacksonConverter(mapper))
}
install(DefaultHeaders)
install(Thymeleaf) {
addDialect(ByteUnitsDialect)
addDialect(Java8TimeDialect())
setTemplateResolver(ClassLoaderTemplateResolver().apply {
prefix = "/org/openrs2/archive/templates/"
templateMode = TemplateMode.HTML
})
}
install(XForwardedHeaders)
install(Webjars)
routing {
get("/") { call.respond(ThymeleafContent("index.html", emptyMap())) }
get("/api") { call.respond(ThymeleafContent("api/index.html", mapOf("active" to "api"))) }
get("/caches") { cachesController.index(call) }
get("/caches.json") { cachesController.indexJson(call) }
get("/caches/{scope}/{id}") { cachesController.show(call) }
get("/caches/{scope}/{id}/archives/{archive}/groups/{group}.dat") { cachesController.exportGroup(call) }
get("/caches/{scope}/{id}/disk.zip") { cachesController.exportDisk(call) }
get("/caches/{scope}/{id}/flat-file.tar.gz") { cachesController.exportFlatFile(call) }
get("/caches/{scope}/{id}/keys.json") { cachesController.exportKeysJson(call) }
get("/caches/{scope}/{id}/keys.zip") { cachesController.exportKeysZip(call) }
get("/caches/{scope}/{id}/map.png") { cachesController.renderMap(call) }
get("/keys") { keysController.index(call) }
post("/keys") { keysController.import(call) }
get("/keys/all.json") { keysController.exportAll(call) }
get("/keys/valid.json") { keysController.exportValid(call) }
static("/static") { resources("/org/openrs2/archive/static") }
// compatibility redirects
get("/caches/{id}") { redirect(call, permanent = true, "/caches/runescape/{id}") }
get("/caches/{id}.json") { redirect(call, permanent = true, "/caches/runescape/{id}/keys.json") }
get("/caches/{id}.zip") { redirect(call, permanent = true, "/caches/runescape/{id}/disk.zip") }
get("/caches/{id}/disk.zip") { redirect(call, permanent = true, "/caches/runescape/{id}/disk.zip") }
get("/caches/{id}/flat-file.tar.gz") {
redirect(call, permanent = true, "/caches/runescape/{id}/flat-file.tar.gz")
}
get("/caches/{id}/keys.json") { redirect(call, permanent = true, "/caches/runescape/{id}/keys.json") }
get("/caches/{id}/keys.zip") { redirect(call, permanent = true, "/caches/runescape/{id}/keys.zip") }
get("/caches/{id}/map.png") { redirect(call, permanent = true, "/caches/runescape/{id}/map.png") }
}
}.start(wait = true)
}
private suspend fun redirect(call: ApplicationCall, permanent: Boolean, path: String) {
val destination = path.replace(PARAMETER) { match ->
val (name) = match.destructured
call.parameters[name] ?: throw IllegalArgumentException()
}
call.respondRedirect(destination, permanent)
}
private companion object {
private val PARAMETER = Regex("\\{([^}]*)}")
}
}