Skip to content

Commit

Permalink
Add /download, among other things
Browse files Browse the repository at this point in the history
Expand rename functionality
Expand schematic listing functionality
Refactor player messages
  • Loading branch information
Nickster258 committed Jul 18, 2020
1 parent 4a11520 commit a846760
Show file tree
Hide file tree
Showing 9 changed files with 162 additions and 31 deletions.
8 changes: 3 additions & 5 deletions build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ plugins {
}

group = ""
version = "1.0-SNAPSHOT"
version = "1.0"

var ktor_version = "1.3.2"

Expand Down Expand Up @@ -54,10 +54,8 @@ dependencies {
implementation(group = "mysql", name = "mysql-connector-java", version = "8.0.20")
implementation(group = "com.vladsch.kotlin-jdbc", name = "kotlin-jdbc", version = "0.5.0")

compileOnly(group = "org.spigotmc", name = "spigot-api", version = "1.15.1-R0.1-SNAPSHOT")
compileOnly(group = "com.sk89q.worldedit", name = "worldedit-bukkit", version = "7.1.0-SNAPSHOT")

kapt(group = "org.spigotmc", name = "plugin-annotations", version = "1.2.2-SNAPSHOT")
compileOnly(group = "org.spigotmc", name = "spigot-api", version = "1.16.1-R0.1-SNAPSHOT")
compileOnly(group = "com.sk89q.worldedit", name = "worldedit-bukkit", version = "7.2.0-SNAPSHOT")
}

tasks.shadowJar {
Expand Down
113 changes: 103 additions & 10 deletions src/main/kotlin/Commands.kt
Original file line number Diff line number Diff line change
Expand Up @@ -12,40 +12,71 @@ import com.sk89q.worldedit.extent.clipboard.io.BuiltInClipboardFormat
import com.sk89q.worldedit.extent.clipboard.io.ClipboardFormats
import com.sk89q.worldedit.extent.clipboard.io.ClipboardReader
import com.sk89q.worldedit.session.ClipboardHolder
import com.sk89q.worldedit.util.formatting.component.InvalidComponentException
import com.sk89q.worldedit.util.formatting.component.PaginationBox
import com.sk89q.worldedit.util.formatting.text.Component
import com.sk89q.worldedit.util.formatting.text.TextComponent
import com.sk89q.worldedit.util.formatting.text.event.ClickEvent
import com.sk89q.worldedit.util.formatting.text.event.HoverEvent
import com.sk89q.worldedit.util.formatting.text.format.TextColor
import org.bukkit.ChatColor
import org.bukkit.entity.Player
import java.io.FileInputStream
import java.io.FileOutputStream
import java.io.IOException
import kotlin.random.Random

@CommandAlias("/schematics")
@Description("Manage your schematics")
@CommandPermission("schemati.schematics")
class Commands(private val worldEdit: WorldEdit) : BaseCommand() {
class Commands(private val worldEdit: WorldEdit, private val url: String, private val serverSchems: PlayerSchematics) : BaseCommand() {
private val ioErrorMessage =
"An unexpected error happened against our expectations. Please consult your dictionary."

@Default
@Syntax("[number]")
@Subcommand("list")
fun list(player: Player, schems: PlayerSchematics) {
fun list(player: Player, schems: PlayerSchematics, @Default("1") page: Int) {
val files = schems.list()
player.sendMessage("Your schematics:")
player.sendMessage(files.toTypedArray())
val paginationBox = SchematicsPaginationBox(files, "Schematics", "//schematics list %page%")
val component = try {
paginationBox.create(page)
} catch (e: InvalidComponentException) {
TextComponent.of("Invalid page number.").color(TextColor.RED)
}
player.sendFormatted(component, prefix = false)
}

@Subcommand("rename")
@CommandAlias("/rename")
@CommandCompletion("@schematics")
fun rename(player: Player, schems: PlayerSchematics, filename: String, newName: String) {
schems.rename(filename, newName)
player.sendMessage("Renamed schematic $filename to $newName.")
player.sendBasic("Renamed schematic $filename to $newName.")
}

@Subcommand("delete")
@CommandAlias("/delete")
@CommandCompletion("@schematics")
fun delete(player: Player, schems: PlayerSchematics, filename: String) {
schems.delete(filename)
player.sendMessage("Deleted schematic $filename")
player.sendBasic("Deleted schematic $filename")
}

@Subcommand("download")
@CommandAlias("/download")
@CommandCompletion("@schematics")
fun download(player: Player, schems: PlayerSchematics, filename: String) {
val file = schems.file(filename)
val key = generateKey()
val copiedFile = serverSchems.file("$key.${filename}", mustExist = false)
file.copyTo(copiedFile)
player.sendFormatted(
TextComponent.of("Click to download schematic $filename.")
.clickEvent(ClickEvent.openUrl("${url}download/?file=$key"))
.hoverEvent(HoverEvent.showText(TextComponent.of("Download")))
, prefix = true
)
}

@Subcommand("save")
Expand All @@ -66,7 +97,7 @@ class Commands(private val worldEdit: WorldEdit) : BaseCommand() {
} catch (e: IOException) {
throw SchematicsException(ioErrorMessage, e)
}
player.sendMessage("Schematic $filename has been saved.")
player.sendBasic("Schematic $filename has been saved.")
}

@Subcommand("load")
Expand All @@ -84,16 +115,78 @@ class Commands(private val worldEdit: WorldEdit) : BaseCommand() {
throw SchematicsException(ioErrorMessage, e)
}
player.weSession.clipboard = ClipboardHolder(clipboard)
player.sendMessage("Loaded schematic $filename to clipboard.")
player.sendBasic("Loaded schematic $filename to clipboard.")
}

private fun generateKey(): String {
val characters = ('a'..'z') + ('A'..'Z') + ('0'..'9')
return (1..16)
.map { Random.nextInt(0, characters.size) }
.map(characters::get)
.joinToString("")
}

private fun Player.sendBasic(message: String) {
this.sendFormatted(TextComponent.of(message))
}

private fun Player.sendFormatted(component: Component, prefix: Boolean = true) {
if (prefix) {
BukkitAdapter.adapt(player).print(TextComponent.of("[Schemati] ").color(TextColor.AQUA).append(component))
} else {
BukkitAdapter.adapt(player).print(component)
}
}

private val Player.weSession: LocalSession
get() = worldEdit.sessionManager.get(BukkitAdapter.adapt(this))
}

class SchematicCompletionHandler(private val schems: Schematics) :
CommandCompletions.CommandCompletionHandler<BukkitCommandCompletionContext>
{
CommandCompletions.CommandCompletionHandler<BukkitCommandCompletionContext> {
override fun getCompletions(context: BukkitCommandCompletionContext): Collection<String> =
schems.forPlayer(context.player.uniqueId).list().toList()
}

class SchematicsPaginationBox(private val schematics: List<String>, title: String, command: String) :
PaginationBox("${ChatColor.LIGHT_PURPLE}$title", command) {

init {
setComponentsPerPage(7)
}

override fun getComponent(number: Int): Component {
if (number > schematics.size) throw IllegalArgumentException("Invalid location index.")
return TextComponent.of("").color(TextColor.GRAY)
.append(
TextComponent.of("")
.clickEvent(ClickEvent.suggestCommand("//schematics rename ${schematics[number]}"))
.hoverEvent(HoverEvent.showText(TextComponent.of("Rename")))
.color(TextColor.RED)
)
.append(TextComponent.of("|"))
.append(
TextComponent.of("")
.clickEvent(ClickEvent.suggestCommand("//schematics download ${schematics[number]}"))
.hoverEvent(HoverEvent.showText(TextComponent.of("Download")))
.color(TextColor.RED)
)
.append(TextComponent.of("|"))
.append(
TextComponent.of("")
.clickEvent(ClickEvent.suggestCommand("//schematics delete ${schematics[number]}"))
.hoverEvent(HoverEvent.showText(TextComponent.of("Delete")))
.color(TextColor.RED)
)
.append(TextComponent.of("|"))
.append(TextComponent.of(" ${schematics[number]}").color(TextColor.YELLOW))
}

override fun getComponentsSize(): Int = schematics.size

override fun create(page: Int): Component {
super.getContents().append(TextComponent.of("Total Schematics: ${schematics.size}").color(TextColor.GRAY))
.append(TextComponent.newline())
return super.create(page)
}
}
12 changes: 9 additions & 3 deletions src/main/kotlin/Schemati.kt
Original file line number Diff line number Diff line change
Expand Up @@ -12,8 +12,8 @@ import schemati.connector.NetworkDatabase
import schemati.web.AuthConfig
import schemati.web.startWeb
import java.io.File
import java.util.*
import java.util.logging.Level
import kotlin.concurrent.thread

class Schemati : JavaPlugin() {
private var web: ApplicationEngine? = null
Expand Down Expand Up @@ -44,7 +44,13 @@ class Schemati : JavaPlugin() {
schems.forPlayer(context.player.uniqueId)
}
commandCompletions.registerCompletion("schematics", SchematicCompletionHandler(schems))
registerCommand(Commands(wePlugin.worldEdit))
registerCommand(
Commands(
wePlugin.worldEdit,
config.getConfigurationSection("web")!!.getString("url")!!,
schems.forPlayer(UUID.fromString("00000000-0000-0000-0000-000000000000"))
)
)
setDefaultExceptionHandler(::handleCommandException, false)
}

Expand All @@ -68,7 +74,7 @@ class Schemati : JavaPlugin() {
)
}

if (config.contains("web.port")) {
if (config.getConfigurationSection("web")!!.getBoolean("enabled")) {
web = startWeb(
config.getConfigurationSection("web")!!.getInt("port"),
networkDatabase!!,
Expand Down
10 changes: 7 additions & 3 deletions src/main/kotlin/Schematics.kt
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,6 @@ package schemati
import com.sk89q.worldedit.extent.clipboard.io.ClipboardFormats
import java.io.File
import java.io.IOException
import java.nio.file.Files
import java.nio.file.Path
import java.util.*

// TODO: catch exception somewhere during instantiation
Expand Down Expand Up @@ -39,7 +37,13 @@ class PlayerSchematics(schematicsDir: File, uuid: UUID) {

fun rename(filename: String, newName: String) {
val file = file(filename)
val new = file(newName, mustExist = false)
val new = file(
if ("." !in newName) {
"$newName.${file.extension}"
} else {
newName
}, mustExist = false
)
if (file.extension != new.extension)
throw SchematicsException("You cannot change the file extension")
if (!file.renameTo(new))
Expand Down
26 changes: 24 additions & 2 deletions src/main/kotlin/web/Page.kt
Original file line number Diff line number Diff line change
Expand Up @@ -10,14 +10,18 @@ import io.ktor.http.content.PartData
import io.ktor.http.content.readAllParts
import io.ktor.http.content.streamProvider
import io.ktor.request.receiveMultipart
import io.ktor.response.*
import io.ktor.response.header
import io.ktor.response.respondFile
import io.ktor.response.respondRedirect
import io.ktor.sessions.clear
import io.ktor.sessions.get
import io.ktor.sessions.sessions
import io.ktor.sessions.set
import kotlinx.html.*
import kotlinx.html.p
import schemati.PlayerSchematics
import schemati.connector.Database
import java.time.Duration
import java.time.Instant

suspend fun pageLanding(call: ApplicationCall) {
val session = call.sessions.get<LoggedSession>()
Expand All @@ -44,6 +48,24 @@ suspend fun pageLogout(call: ApplicationCall) {
call.respondRedirect("/")
}

suspend fun pageDownload(call: ApplicationCall, schems: PlayerSchematics) {
val key = call.parameters["file"] ?: showLoggedInErrorPage("Did not receive parameter file")
schems.list().filter {
schems.file(it).lastModified() < Instant.now().minus(Duration.ofDays(7)).toEpochMilli()
}.forEach(schems::delete)
val filename = schems.list().firstOrNull { it.substringBefore(".") == key }
?: showErrorPage("Schematic download no longer available")
call.response.header(
HttpHeaders.ContentDisposition,
ContentDisposition.Attachment.withParameter(
ContentDisposition.Parameters.FileName,
filename.substringAfter(".")
).toString()
)
val file = schems.file(filename)
call.respondFile(file)
}

suspend fun pageLogin(call: ApplicationCall, networkDatabase: Database) {
val principal = call.authentication.principal<OAuthAccessTokenResponse.OAuth2>()
?: redirectTo("/")
Expand Down
12 changes: 8 additions & 4 deletions src/main/kotlin/web/Templates.kt
Original file line number Diff line number Diff line change
Expand Up @@ -25,8 +25,10 @@ class LoggedInErrorTemplate : Template<HTML> {
insert(base) {
content {
insert(errorContent)
// TODO: install design
a("/schems") { +"Back to start" }
p {
classes = setOf("center")
a("javascript:history.back()") { +"Back to start" }
}
}
}
}
Expand All @@ -39,8 +41,10 @@ class ErrorTemplate : Template<HTML> {
insert(base) {
mainContent {
insert(errorContent)
// TODO: install design
a("/schems") { +"Back to start" }
p {
classes = setOf("center")
a("/") { +"Back to start" }
}
}
}
}
Expand Down
4 changes: 4 additions & 0 deletions src/main/kotlin/web/Web.kt
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@ import kotlinx.html.p
import schemati.Schematics
import schemati.SchematicsException
import schemati.connector.Database
import java.util.*

const val discordApiBase = "https://discordapp.com/api/"

Expand Down Expand Up @@ -92,6 +93,9 @@ fun makeSchemsApp(networkDatabase: Database, authConfig: AuthConfig, schems: Sch
get("/") {
pageLanding(call)
}
get("/download") {
pageDownload(call, schems.forPlayer(UUID.fromString("00000000-0000-0000-0000-000000000000")))
}
get("/logout") {
pageLogout(call)
}
Expand Down
4 changes: 2 additions & 2 deletions src/main/resources/config.yml
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
web:
proxied: false
enabled: true
port: 8080
host: "localhost"
url: "http://schemati.openredstone.org:8080/"
oauth:
clientId: "***"
clientSecret: "***"
Expand Down
4 changes: 2 additions & 2 deletions src/main/resources/plugin.yml
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
name: Schemati
version: 1.0-SNAPSHOT
version: 1.0
main: schemati.Schemati
api-version: 1.14
api-version: 1.16
depend: [WorldEdit]

0 comments on commit a846760

Please sign in to comment.