Work on commands. Implemented helper functions, among others to handle asynchronous commands
This commit is contained in:
19
.editorconfig
Normal file
19
.editorconfig
Normal file
@@ -0,0 +1,19 @@
|
|||||||
|
[*]
|
||||||
|
charset=utf-8
|
||||||
|
end_of_line=lf
|
||||||
|
insert_final_newline=false
|
||||||
|
indent_style=space
|
||||||
|
indent_size=4
|
||||||
|
|
||||||
|
[{.babelrc,.stylelintrc,jest.config,.eslintrc,*.bowerrc,*.jsb3,*.jsb2,*.json}]
|
||||||
|
indent_style=space
|
||||||
|
indent_size=2
|
||||||
|
|
||||||
|
[{*.ddl,*.sql}]
|
||||||
|
indent_style=space
|
||||||
|
indent_size=2
|
||||||
|
|
||||||
|
[{*.yml,*.yaml}]
|
||||||
|
indent_style=space
|
||||||
|
indent_size=2
|
||||||
|
|
||||||
@@ -28,7 +28,7 @@ dependencies {
|
|||||||
compile("org.jetbrains.exposed:exposed:0.10.3")
|
compile("org.jetbrains.exposed:exposed:0.10.3")
|
||||||
compile("org.jetbrains.kotlinx:kotlinx-coroutines-core:0.23.4")
|
compile("org.jetbrains.kotlinx:kotlinx-coroutines-core:0.23.4")
|
||||||
compile("com.zaxxer:HikariCP:3.2.0")
|
compile("com.zaxxer:HikariCP:3.2.0")
|
||||||
compile(files("../h2/bin/h2-client-1.4.197.jar"))
|
compile(files("../h2/bin/h2-1.4.197.jar"))
|
||||||
|
|
||||||
val jacksonVersion = "2.9.6"
|
val jacksonVersion = "2.9.6"
|
||||||
compile("com.fasterxml.jackson.core:jackson-core:$jacksonVersion")
|
compile("com.fasterxml.jackson.core:jackson-core:$jacksonVersion")
|
||||||
@@ -44,9 +44,12 @@ dependencies {
|
|||||||
}
|
}
|
||||||
|
|
||||||
tasks {
|
tasks {
|
||||||
val jar by getting(Jar::class)
|
val jar by getting(Jar::class) {
|
||||||
|
group = "artifacts"
|
||||||
|
}
|
||||||
|
|
||||||
val fatJar by creating(Jar::class) {
|
val fatJar by creating(Jar::class) {
|
||||||
|
group = "artifacts"
|
||||||
destinationDir = file("$rootDir/debug/plugins")
|
destinationDir = file("$rootDir/debug/plugins")
|
||||||
baseName = "parcels2-all"
|
baseName = "parcels2-all"
|
||||||
from(*configurations.compile.map(::zipTree).toTypedArray())
|
from(*configurations.compile.map(::zipTree).toTypedArray())
|
||||||
@@ -54,6 +57,7 @@ tasks {
|
|||||||
}
|
}
|
||||||
|
|
||||||
val shadowJar by getting(ShadowJar::class) {
|
val shadowJar by getting(ShadowJar::class) {
|
||||||
|
group = "artifacts"
|
||||||
destinationDir = file("$rootDir/debug/plugins")
|
destinationDir = file("$rootDir/debug/plugins")
|
||||||
baseName = "parcels2-shaded"
|
baseName = "parcels2-shaded"
|
||||||
|
|
||||||
@@ -72,6 +76,7 @@ tasks {
|
|||||||
}
|
}
|
||||||
|
|
||||||
val relocateSnakeyamlJar by creating(ShadowJar::class) {
|
val relocateSnakeyamlJar by creating(ShadowJar::class) {
|
||||||
|
group = "artifacts"
|
||||||
destinationDir = file("$rootDir/debug/plugins")
|
destinationDir = file("$rootDir/debug/plugins")
|
||||||
baseName = "parcels2-shaded"
|
baseName = "parcels2-shaded"
|
||||||
relocate("org.yaml", "shadow.org.yaml")
|
relocate("org.yaml", "shadow.org.yaml")
|
||||||
|
|||||||
@@ -5,7 +5,6 @@ import com.fasterxml.jackson.annotation.JsonIgnore
|
|||||||
import io.dico.parcels2.storage.Storage
|
import io.dico.parcels2.storage.Storage
|
||||||
import io.dico.parcels2.storage.StorageFactory
|
import io.dico.parcels2.storage.StorageFactory
|
||||||
import io.dico.parcels2.storage.yamlObjectMapper
|
import io.dico.parcels2.storage.yamlObjectMapper
|
||||||
import org.bukkit.Bukkit
|
|
||||||
import org.bukkit.Bukkit.createBlockData
|
import org.bukkit.Bukkit.createBlockData
|
||||||
import org.bukkit.GameMode
|
import org.bukkit.GameMode
|
||||||
import org.bukkit.Material
|
import org.bukkit.Material
|
||||||
|
|||||||
@@ -19,6 +19,15 @@ interface ParcelData {
|
|||||||
|
|
||||||
var allowInteractInputs: Boolean
|
var allowInteractInputs: Boolean
|
||||||
var allowInteractInventory: Boolean
|
var allowInteractInventory: Boolean
|
||||||
|
|
||||||
|
fun isOwner(uuid: UUID): Boolean {
|
||||||
|
return owner?.uuid == uuid
|
||||||
|
}
|
||||||
|
|
||||||
|
val infoString: String
|
||||||
|
get() {
|
||||||
|
TODO()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -88,14 +97,14 @@ class ParcelDataHolder : ParcelData {
|
|||||||
|
|
||||||
override fun getAddedStatus(uuid: UUID): AddedStatus = added.getOrDefault(uuid, AddedStatus.DEFAULT)
|
override fun getAddedStatus(uuid: UUID): AddedStatus = added.getOrDefault(uuid, AddedStatus.DEFAULT)
|
||||||
override fun setAddedStatus(uuid: UUID, status: AddedStatus): Boolean = status.takeIf { it != AddedStatus.DEFAULT }
|
override fun setAddedStatus(uuid: UUID, status: AddedStatus): Boolean = status.takeIf { it != AddedStatus.DEFAULT }
|
||||||
?.let { added.put(uuid, it) != it }
|
?.let { added.put(uuid, it) != it }
|
||||||
?: added.remove(uuid) != null
|
?: added.remove(uuid) != null
|
||||||
|
|
||||||
override fun isBanned(uuid: UUID) = getAddedStatus(uuid) == AddedStatus.BANNED
|
override fun isBanned(uuid: UUID) = getAddedStatus(uuid) == AddedStatus.BANNED
|
||||||
override fun isAllowed(uuid: UUID) = getAddedStatus(uuid) == AddedStatus.ALLOWED
|
override fun isAllowed(uuid: UUID) = getAddedStatus(uuid) == AddedStatus.ALLOWED
|
||||||
override fun canBuild(player: Player) = isAllowed(player.uniqueId)
|
override fun canBuild(player: Player) = isAllowed(player.uniqueId)
|
||||||
|| owner?.matches(player, allowNameMatch = false) ?: false
|
|| owner?.matches(player, allowNameMatch = false) ?: false
|
||||||
|| player.hasBuildAnywhere
|
|| player.hasBuildAnywhere
|
||||||
|
|
||||||
override var allowInteractInputs = true
|
override var allowInteractInputs = true
|
||||||
override var allowInteractInventory = true
|
override var allowInteractInventory = true
|
||||||
@@ -121,7 +130,7 @@ class ParcelOwner(val uuid: UUID? = null,
|
|||||||
companion object {
|
companion object {
|
||||||
fun create(uuid: UUID?, name: String?): ParcelOwner? {
|
fun create(uuid: UUID?, name: String?): ParcelOwner? {
|
||||||
return uuid?.let { ParcelOwner(uuid, name) }
|
return uuid?.let { ParcelOwner(uuid, name) }
|
||||||
?: name?.let { ParcelOwner(uuid, name) }
|
?: name?.let { ParcelOwner(uuid, name) }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -141,7 +150,7 @@ class ParcelOwner(val uuid: UUID? = null,
|
|||||||
|
|
||||||
fun matches(player: Player, allowNameMatch: Boolean = false): Boolean {
|
fun matches(player: Player, allowNameMatch: Boolean = false): Boolean {
|
||||||
return uuid?.let { it == player.uniqueId } ?: false
|
return uuid?.let { it == player.uniqueId } ?: false
|
||||||
|| (allowNameMatch && name?.let { it == player.name } ?: false)
|
|| (allowNameMatch && name?.let { it == player.name } ?: false)
|
||||||
}
|
}
|
||||||
|
|
||||||
val onlinePlayer: Player? get() = uuid?.let { Bukkit.getPlayer(uuid) }
|
val onlinePlayer: Player? get() = uuid?.let { Bukkit.getPlayer(uuid) }
|
||||||
@@ -150,5 +159,5 @@ class ParcelOwner(val uuid: UUID? = null,
|
|||||||
@Suppress("DEPRECATION")
|
@Suppress("DEPRECATION")
|
||||||
val offlinePlayer
|
val offlinePlayer
|
||||||
get() = (uuid?.let { Bukkit.getOfflinePlayer(it) } ?: Bukkit.getOfflinePlayer(name))
|
get() = (uuid?.let { Bukkit.getOfflinePlayer(it) } ?: Bukkit.getOfflinePlayer(name))
|
||||||
?.takeIf { it.isOnline() || it.hasPlayedBefore() }
|
?.takeIf { it.isOnline() || it.hasPlayedBefore() }
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -53,10 +53,10 @@ class Worlds(private val plugin: ParcelsPlugin) {
|
|||||||
try {
|
try {
|
||||||
|
|
||||||
world = ParcelWorld(
|
world = ParcelWorld(
|
||||||
worldName,
|
worldName,
|
||||||
worldOptions,
|
worldOptions,
|
||||||
worldOptions.generator.getGenerator(this, worldName),
|
worldOptions.generator.getGenerator(this, worldName),
|
||||||
plugin.storage)
|
plugin.storage)
|
||||||
|
|
||||||
} catch (ex: Exception) {
|
} catch (ex: Exception) {
|
||||||
ex.printStackTrace()
|
ex.printStackTrace()
|
||||||
|
|||||||
@@ -1,8 +1,8 @@
|
|||||||
package io.dico.parcels2
|
package io.dico.parcels2
|
||||||
|
|
||||||
import io.dico.dicore.command.CommandBuilder
|
|
||||||
import io.dico.dicore.command.EOverridePolicy
|
import io.dico.dicore.command.EOverridePolicy
|
||||||
import io.dico.dicore.command.ICommandDispatcher
|
import io.dico.dicore.command.ICommandDispatcher
|
||||||
|
import io.dico.parcels2.command.getParcelCommands
|
||||||
import io.dico.parcels2.storage.Storage
|
import io.dico.parcels2.storage.Storage
|
||||||
import io.dico.parcels2.storage.yamlObjectMapper
|
import io.dico.parcels2.storage.yamlObjectMapper
|
||||||
import io.dico.parcels2.util.tryCreate
|
import io.dico.parcels2.util.tryCreate
|
||||||
@@ -14,6 +14,7 @@ import java.io.File
|
|||||||
val logger = LoggerFactory.getLogger("ParcelsPlugin")
|
val logger = LoggerFactory.getLogger("ParcelsPlugin")
|
||||||
|
|
||||||
private inline val plogger get() = logger
|
private inline val plogger get() = logger
|
||||||
|
const val debugging = true
|
||||||
|
|
||||||
class ParcelsPlugin : JavaPlugin() {
|
class ParcelsPlugin : JavaPlugin() {
|
||||||
lateinit var optionsFile: File
|
lateinit var optionsFile: File
|
||||||
@@ -73,15 +74,9 @@ class ParcelsPlugin : JavaPlugin() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private fun registerCommands() {
|
private fun registerCommands() {
|
||||||
//@formatting:off
|
cmdDispatcher = getParcelCommands(this).apply {
|
||||||
cmdDispatcher = CommandBuilder()
|
registerToCommandMap("parcels:", EOverridePolicy.FALLBACK_ONLY)
|
||||||
.group("parcel", "plot", "plots", "p")
|
}
|
||||||
.registerCommands(PlotCommands(this))
|
|
||||||
.parent()
|
|
||||||
.getDispatcher()
|
|
||||||
//@formatting:on
|
|
||||||
|
|
||||||
cmdDispatcher!!.registerToCommandMap("parcels:", EOverridePolicy.FALLBACK_ONLY)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
@@ -179,7 +179,7 @@ class DefaultParcelGenerator(val worlds: Worlds, val name: String, private val o
|
|||||||
}
|
}
|
||||||
|
|
||||||
override fun getBottomCoord(parcel: Parcel): Vec2i = Vec2i(sectionSize * parcel.pos.x + pathOffset + o.offsetX,
|
override fun getBottomCoord(parcel: Parcel): Vec2i = Vec2i(sectionSize * parcel.pos.x + pathOffset + o.offsetX,
|
||||||
sectionSize * parcel.pos.z + pathOffset + o.offsetZ)
|
sectionSize * parcel.pos.z + pathOffset + o.offsetZ)
|
||||||
|
|
||||||
override fun getHomeLocation(parcel: Parcel): Location {
|
override fun getHomeLocation(parcel: Parcel): Location {
|
||||||
val bottom = getBottomCoord(parcel)
|
val bottom = getBottomCoord(parcel)
|
||||||
|
|||||||
83
src/main/kotlin/io/dico/parcels2/command/CommandAsync.kt
Normal file
83
src/main/kotlin/io/dico/parcels2/command/CommandAsync.kt
Normal file
@@ -0,0 +1,83 @@
|
|||||||
|
package io.dico.parcels2.command
|
||||||
|
|
||||||
|
import io.dico.dicore.command.CommandException
|
||||||
|
import io.dico.dicore.command.CommandResult
|
||||||
|
import io.dico.dicore.command.EMessageType
|
||||||
|
import io.dico.dicore.command.ExecutionContext
|
||||||
|
import io.dico.dicore.command.chat.IChatController
|
||||||
|
import io.dico.parcels2.ParcelsPlugin
|
||||||
|
import io.dico.parcels2.logger
|
||||||
|
import kotlinx.coroutines.experimental.*
|
||||||
|
import org.bukkit.command.CommandSender
|
||||||
|
import kotlin.coroutines.experimental.Continuation
|
||||||
|
import kotlin.coroutines.experimental.suspendCoroutine
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Interface to implicitly access plugin by creating extension functions for it
|
||||||
|
*/
|
||||||
|
interface HasPlugin {
|
||||||
|
val plugin: ParcelsPlugin
|
||||||
|
}
|
||||||
|
|
||||||
|
class CommandAsyncScope {
|
||||||
|
|
||||||
|
suspend fun <T> HasPlugin.awaitSynchronousTask(delay: Int = 0, task: () -> T): T {
|
||||||
|
return suspendCoroutine { cont: Continuation<T> ->
|
||||||
|
plugin.server.scheduler.runTaskLater(plugin, l@ {
|
||||||
|
val result = try {
|
||||||
|
task()
|
||||||
|
} catch (ex: CommandException) {
|
||||||
|
cont.resumeWithException(ex)
|
||||||
|
return@l
|
||||||
|
} catch (ex: Throwable) {
|
||||||
|
cont.context.cancel(ex)
|
||||||
|
return@l
|
||||||
|
}
|
||||||
|
cont.resume(result)
|
||||||
|
}, delay.toLong())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fun <T> HasPlugin.synchronousTask(delay: Int = 0, task: () -> T): Deferred<T> {
|
||||||
|
return async { awaitSynchronousTask(delay, task) }
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
fun <T : Any?> HasPlugin.delegateCommandAsync(context: ExecutionContext,
|
||||||
|
block: suspend CommandAsyncScope.() -> T) {
|
||||||
|
|
||||||
|
val job: Deferred<Any?> = async(/*context = plugin.storage.asyncDispatcher, */start = CoroutineStart.ATOMIC) {
|
||||||
|
CommandAsyncScope().block()
|
||||||
|
}
|
||||||
|
|
||||||
|
fun Job.invokeOnCompletionSynchronously(block: (Throwable?) -> Unit) = invokeOnCompletion {
|
||||||
|
plugin.server.scheduler.runTask(plugin) { block(it) }
|
||||||
|
}
|
||||||
|
|
||||||
|
job.invokeOnCompletionSynchronously l@{ exception: Throwable? ->
|
||||||
|
exception?.let {
|
||||||
|
context.address.chatController.handleCoroutineException(context.sender, context, it)
|
||||||
|
return@l
|
||||||
|
}
|
||||||
|
|
||||||
|
val result = job.getCompleted()
|
||||||
|
val message = when (result) {
|
||||||
|
is String -> result
|
||||||
|
is CommandResult -> result.message
|
||||||
|
else -> null
|
||||||
|
}
|
||||||
|
|
||||||
|
context.address.chatController.sendMessage(context.sender, EMessageType.RESULT, message)
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
fun IChatController.handleCoroutineException(sender: CommandSender, context: ExecutionContext, exception: Throwable) {
|
||||||
|
if (exception is CancellationException) {
|
||||||
|
sendMessage(sender, EMessageType.EXCEPTION, "The command was cancelled unexpectedly (see console)")
|
||||||
|
logger.warn("An asynchronously dispatched command was cancelled unexpectedly", exception)
|
||||||
|
} else {
|
||||||
|
handleException(sender, context, exception)
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,63 @@
|
|||||||
|
@file:Suppress("NOTHING_TO_INLINE")
|
||||||
|
|
||||||
|
package io.dico.parcels2.command
|
||||||
|
|
||||||
|
import io.dico.dicore.command.CommandException
|
||||||
|
import io.dico.dicore.command.Validate
|
||||||
|
import io.dico.parcels2.Parcel
|
||||||
|
import io.dico.parcels2.ParcelWorld
|
||||||
|
import io.dico.parcels2.Worlds
|
||||||
|
import io.dico.parcels2.util.hasAdminManage
|
||||||
|
import io.dico.parcels2.util.uuid
|
||||||
|
import org.bukkit.entity.Player
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Scope types for extension lambdas
|
||||||
|
*/
|
||||||
|
sealed class BaseScope
|
||||||
|
|
||||||
|
class WorldOnlyScope(val world: ParcelWorld) : BaseScope()
|
||||||
|
class ParcelScope(val world: ParcelWorld, val parcel: Parcel) : BaseScope()
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Interface to implicitly access worlds object by creating extension functions for it
|
||||||
|
*/
|
||||||
|
interface HasWorlds {
|
||||||
|
val worlds: Worlds
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Functions to be used by command implementations
|
||||||
|
*/
|
||||||
|
inline fun <T> HasWorlds.requireInWorld(player: Player,
|
||||||
|
admin: Boolean = false,
|
||||||
|
block: WorldOnlyScope.() -> T): T {
|
||||||
|
return WorldOnlyScope(worlds.getWorldRequired(player, admin = admin)).block()
|
||||||
|
}
|
||||||
|
|
||||||
|
inline fun <T> HasWorlds.requireInParcel(player: Player,
|
||||||
|
admin: Boolean = false,
|
||||||
|
own: Boolean = false,
|
||||||
|
block: ParcelScope.() -> T): T {
|
||||||
|
val parcel = worlds.getParcelRequired(player, admin = admin, own = own)
|
||||||
|
return ParcelScope(parcel.world, parcel).block()
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Functions for checking
|
||||||
|
*/
|
||||||
|
fun Worlds.getWorldRequired(player: Player, admin: Boolean = false): ParcelWorld {
|
||||||
|
if (admin) Validate.isTrue(player.hasAdminManage, "You must have admin rights to use that command")
|
||||||
|
return getWorld(player.world)
|
||||||
|
?: throw CommandException("You must be in a parcel world to use that command")
|
||||||
|
}
|
||||||
|
|
||||||
|
fun Worlds.getParcelRequired(player: Player, admin: Boolean = false, own: Boolean = false): Parcel {
|
||||||
|
val parcel = getWorldRequired(player, admin = admin).parcelAt(player)
|
||||||
|
?: throw CommandException("You must be in a parcel to use that command")
|
||||||
|
if (own) Validate.isTrue(parcel.isOwner(player.uuid) || player.hasAdminManage,
|
||||||
|
"You must own this parcel to use that command")
|
||||||
|
return parcel
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
@@ -1,11 +1,12 @@
|
|||||||
package io.dico.parcels2
|
package io.dico.parcels2.command
|
||||||
|
|
||||||
import io.dico.dicore.command.CommandException
|
import io.dico.dicore.command.CommandException
|
||||||
import io.dico.dicore.command.annotation.Cmd
|
import io.dico.dicore.command.annotation.Cmd
|
||||||
|
import io.dico.parcels2.ParcelsPlugin
|
||||||
import org.bukkit.Bukkit
|
import org.bukkit.Bukkit
|
||||||
import org.bukkit.entity.Player
|
import org.bukkit.entity.Player
|
||||||
|
|
||||||
|
class DebugCommands(val plugin: ParcelsPlugin) {
|
||||||
class PlotCommands(val plugin: ParcelsPlugin) {
|
|
||||||
|
|
||||||
@Cmd("reloadoptions")
|
@Cmd("reloadoptions")
|
||||||
fun reloadOptions() {
|
fun reloadOptions() {
|
||||||
@@ -0,0 +1,68 @@
|
|||||||
|
package io.dico.parcels2.command
|
||||||
|
|
||||||
|
import io.dico.dicore.command.CommandBuilder
|
||||||
|
import io.dico.dicore.command.CommandException
|
||||||
|
import io.dico.dicore.command.ICommandDispatcher
|
||||||
|
import io.dico.dicore.command.parameter.ArgumentBuffer
|
||||||
|
import io.dico.dicore.command.parameter.IParameter
|
||||||
|
import io.dico.dicore.command.parameter.type.ParameterType
|
||||||
|
import io.dico.parcels2.Parcel
|
||||||
|
import io.dico.parcels2.ParcelsPlugin
|
||||||
|
import io.dico.parcels2.Worlds
|
||||||
|
import io.dico.parcels2.debugging
|
||||||
|
import org.bukkit.command.CommandSender
|
||||||
|
import org.bukkit.entity.Player
|
||||||
|
|
||||||
|
@Suppress("UsePropertyAccessSyntax")
|
||||||
|
fun getParcelCommands(plugin: ParcelsPlugin): ICommandDispatcher {
|
||||||
|
//@formatter:off
|
||||||
|
return CommandBuilder()
|
||||||
|
.addParameterType(false, ParcelParameterType(plugin.worlds))
|
||||||
|
.group("parcel", "plot", "plots", "p")
|
||||||
|
.registerCommands(ParcelCommands(plugin))
|
||||||
|
.putDebugCommands(plugin)
|
||||||
|
.parent()
|
||||||
|
.getDispatcher()
|
||||||
|
//@formatter:on
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun CommandBuilder.putDebugCommands(plugin: ParcelsPlugin): CommandBuilder {
|
||||||
|
if (!debugging) return this
|
||||||
|
//@formatter:off
|
||||||
|
return group("debug", "d")
|
||||||
|
.registerCommands(DebugCommands(plugin))
|
||||||
|
.parent()
|
||||||
|
//@formatter:on
|
||||||
|
}
|
||||||
|
|
||||||
|
private val regex = Regex.fromLiteral("((.+)->)?([0-9]+):([0-9]+)")
|
||||||
|
|
||||||
|
private class ParcelParameterType(val worlds: Worlds) : ParameterType<Parcel, Unit>(Parcel::class.java) {
|
||||||
|
|
||||||
|
private fun invalidInput(parameter: IParameter<*>, message: String): Nothing {
|
||||||
|
throw CommandException("invalid input for ${parameter.name}: $message")
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun parse(parameter: IParameter<Parcel>, sender: CommandSender, buffer: ArgumentBuffer): Parcel {
|
||||||
|
val matchResult = regex.matchEntire(buffer.next())
|
||||||
|
?: invalidInput(parameter, "must match (w->)?a:b (/${regex.pattern}/)")
|
||||||
|
|
||||||
|
val worldName = matchResult.groupValues[2]
|
||||||
|
.takeUnless { it.isEmpty() }
|
||||||
|
?: (sender as? Player)?.world?.name
|
||||||
|
?: invalidInput(parameter, "console cannot omit the world name")
|
||||||
|
|
||||||
|
val world = worlds.getWorld(worldName)
|
||||||
|
?: invalidInput(parameter, "$worldName is not a parcel world")
|
||||||
|
|
||||||
|
val x = matchResult.groupValues[3].toIntOrNull()
|
||||||
|
?: invalidInput(parameter, "couldn't parse int")
|
||||||
|
|
||||||
|
val z = matchResult.groupValues[4].toIntOrNull()
|
||||||
|
?: invalidInput(parameter, "couldn't parse int")
|
||||||
|
|
||||||
|
return world.parcelByID(x, z)
|
||||||
|
?: invalidInput(parameter, "parcel id is out of range")
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
51
src/main/kotlin/io/dico/parcels2/command/ParcelCommands.kt
Normal file
51
src/main/kotlin/io/dico/parcels2/command/ParcelCommands.kt
Normal file
@@ -0,0 +1,51 @@
|
|||||||
|
package io.dico.parcels2.command
|
||||||
|
|
||||||
|
import io.dico.dicore.command.CommandException
|
||||||
|
import io.dico.dicore.command.ExecutionContext
|
||||||
|
import io.dico.dicore.command.annotation.Cmd
|
||||||
|
import io.dico.dicore.command.annotation.Desc
|
||||||
|
import io.dico.parcels2.ParcelOwner
|
||||||
|
import io.dico.parcels2.ParcelsPlugin
|
||||||
|
import io.dico.parcels2.util.parcelLimit
|
||||||
|
import io.dico.parcels2.util.uuid
|
||||||
|
import org.bukkit.entity.Player
|
||||||
|
|
||||||
|
class ParcelCommands(override val plugin: ParcelsPlugin) : HasWorlds, HasPlugin {
|
||||||
|
override val worlds = plugin.worlds
|
||||||
|
|
||||||
|
private fun error(message: String): Nothing {
|
||||||
|
throw CommandException(message)
|
||||||
|
}
|
||||||
|
|
||||||
|
@Cmd("auto")
|
||||||
|
@Desc("Finds the unclaimed parcel nearest to origin,",
|
||||||
|
"and gives it to you",
|
||||||
|
shortVersion = "sets you up with a fresh, unclaimed parcel")
|
||||||
|
fun cmdAuto(player: Player, context: ExecutionContext) = requireInWorld(player) {
|
||||||
|
delegateCommandAsync(context) {
|
||||||
|
val numOwnedParcels = plugin.storage.getNumParcels(ParcelOwner(uuid = player.uuid)).await()
|
||||||
|
|
||||||
|
awaitSynchronousTask {
|
||||||
|
val limit = player.parcelLimit
|
||||||
|
|
||||||
|
if (numOwnedParcels >= limit) {
|
||||||
|
error("You have enough plots for now")
|
||||||
|
}
|
||||||
|
|
||||||
|
val parcel = world.nextEmptyParcel()
|
||||||
|
?: error("This world is full, please ask an admin to upsize it")
|
||||||
|
parcel.owner = ParcelOwner(uuid = player.uuid)
|
||||||
|
player.teleport(world.generator.getHomeLocation(parcel))
|
||||||
|
"Enjoy your new parcel!"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Cmd("info", aliases = ["i"])
|
||||||
|
@Desc("Displays general information",
|
||||||
|
"about the parcel you're on",
|
||||||
|
shortVersion = "displays information about this parcel")
|
||||||
|
fun cmdInfo(player: Player) = requireInParcel(player) { parcel.infoString }
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
@@ -3,7 +3,7 @@ package io.dico.parcels2.math
|
|||||||
fun Double.floor(): Int {
|
fun Double.floor(): Int {
|
||||||
val down = toInt()
|
val down = toInt()
|
||||||
if (down.toDouble() != this && (java.lang.Double.doubleToRawLongBits(this).ushr(63).toInt()) == 1) {
|
if (down.toDouble() != this && (java.lang.Double.doubleToRawLongBits(this).ushr(63).toInt()) == 1) {
|
||||||
return down-1
|
return down - 1
|
||||||
}
|
}
|
||||||
return down
|
return down
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
package io.dico.parcels2.math
|
package io.dico.parcels2.math
|
||||||
|
|
||||||
data class Vec2i(
|
data class Vec2i(
|
||||||
val x: Int,
|
val x: Int,
|
||||||
val z: Int
|
val z: Int
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|||||||
@@ -10,6 +10,8 @@ interface Backing {
|
|||||||
|
|
||||||
val name: String
|
val name: String
|
||||||
|
|
||||||
|
val isConnected: Boolean
|
||||||
|
|
||||||
suspend fun init()
|
suspend fun init()
|
||||||
|
|
||||||
suspend fun shutdown()
|
suspend fun shutdown()
|
||||||
@@ -25,6 +27,8 @@ interface Backing {
|
|||||||
|
|
||||||
suspend fun getOwnedParcels(user: ParcelOwner): List<SerializableParcel>
|
suspend fun getOwnedParcels(user: ParcelOwner): List<SerializableParcel>
|
||||||
|
|
||||||
|
suspend fun getNumParcels(user: ParcelOwner): Int = getOwnedParcels(user).size
|
||||||
|
|
||||||
|
|
||||||
suspend fun setParcelData(parcelFor: Parcel, data: ParcelData?)
|
suspend fun setParcelData(parcelFor: Parcel, data: ParcelData?)
|
||||||
|
|
||||||
|
|||||||
@@ -3,6 +3,7 @@ package io.dico.parcels2.storage
|
|||||||
import com.zaxxer.hikari.HikariDataSource
|
import com.zaxxer.hikari.HikariDataSource
|
||||||
import io.dico.parcels2.*
|
import io.dico.parcels2.*
|
||||||
import io.dico.parcels2.math.Vec2i
|
import io.dico.parcels2.math.Vec2i
|
||||||
|
import io.dico.parcels2.util.synchronized
|
||||||
import io.dico.parcels2.util.toByteArray
|
import io.dico.parcels2.util.toByteArray
|
||||||
import io.dico.parcels2.util.toUUID
|
import io.dico.parcels2.util.toUUID
|
||||||
import kotlinx.coroutines.experimental.channels.ProducerScope
|
import kotlinx.coroutines.experimental.channels.ProducerScope
|
||||||
@@ -16,7 +17,7 @@ object WorldsT : Table("worlds") {
|
|||||||
val id = integer("id").autoIncrement().primaryKey()
|
val id = integer("id").autoIncrement().primaryKey()
|
||||||
val name = varchar("name", 50)
|
val name = varchar("name", 50)
|
||||||
val uid = binary("uid", 16)
|
val uid = binary("uid", 16)
|
||||||
.also { uniqueIndex("index_uid", it) }
|
.also { uniqueIndex("index_uid", it) }
|
||||||
}
|
}
|
||||||
|
|
||||||
object ParcelsT : Table("parcels") {
|
object ParcelsT : Table("parcels") {
|
||||||
@@ -24,31 +25,31 @@ object ParcelsT : Table("parcels") {
|
|||||||
val px = integer("px")
|
val px = integer("px")
|
||||||
val pz = integer("pz")
|
val pz = integer("pz")
|
||||||
val world_id = integer("id")
|
val world_id = integer("id")
|
||||||
.also { uniqueIndex("index_location", it, px, pz) }
|
.also { uniqueIndex("index_location", it, px, pz) }
|
||||||
.references(WorldsT.id)
|
.references(WorldsT.id)
|
||||||
val owner_uuid = binary("owner_uuid", 16).nullable()
|
val owner_uuid = binary("owner_uuid", 16).nullable()
|
||||||
val owner_name = varchar("owner_name", 16).nullable()
|
val owner_name = varchar("owner_name", 16).nullable()
|
||||||
}
|
}
|
||||||
|
|
||||||
object AddedLocalT : Table("parcels_added_local") {
|
object AddedLocalT : Table("parcels_added_local") {
|
||||||
val parcel_id = integer("parcel_id")
|
val parcel_id = integer("parcel_id")
|
||||||
.references(ParcelsT.id, ReferenceOption.CASCADE)
|
.references(ParcelsT.id, ReferenceOption.CASCADE)
|
||||||
val player_uuid = binary("player_uuid", 16)
|
val player_uuid = binary("player_uuid", 16)
|
||||||
.also { uniqueIndex("index_pair", parcel_id, it) }
|
.also { uniqueIndex("index_pair", parcel_id, it) }
|
||||||
val allowed_flag = bool("allowed_flag")
|
val allowed_flag = bool("allowed_flag")
|
||||||
}
|
}
|
||||||
|
|
||||||
object AddedGlobalT : Table("parcels_added_global") {
|
object AddedGlobalT : Table("parcels_added_global") {
|
||||||
val owner_uuid = binary("owner_uuid", 16)
|
val owner_uuid = binary("owner_uuid", 16)
|
||||||
val player_uuid = binary("player_uuid", 16)
|
val player_uuid = binary("player_uuid", 16)
|
||||||
.also { uniqueIndex("index_pair", owner_uuid, it) }
|
.also { uniqueIndex("index_pair", owner_uuid, it) }
|
||||||
val allowed_flag = bool("allowed_flag")
|
val allowed_flag = bool("allowed_flag")
|
||||||
}
|
}
|
||||||
|
|
||||||
object ParcelOptionsT : Table("parcel_options") {
|
object ParcelOptionsT : Table("parcel_options") {
|
||||||
val parcel_id = integer("parcel_id")
|
val parcel_id = integer("parcel_id")
|
||||||
.also { uniqueIndex("index_parcel_id", it) }
|
.also { uniqueIndex("index_parcel_id", it) }
|
||||||
.references(ParcelsT.id, ReferenceOption.CASCADE)
|
.references(ParcelsT.id, ReferenceOption.CASCADE)
|
||||||
val interact_inventory = bool("interact_inventory").default(false)
|
val interact_inventory = bool("interact_inventory").default(false)
|
||||||
val interact_inputs = bool("interact_inputs").default(false)
|
val interact_inputs = bool("interact_inputs").default(false)
|
||||||
}
|
}
|
||||||
@@ -58,18 +59,29 @@ private class ExposedDatabaseException(message: String? = null) : Exception(mess
|
|||||||
@Suppress("NOTHING_TO_INLINE")
|
@Suppress("NOTHING_TO_INLINE")
|
||||||
class ExposedBacking(val dataSource: DataSource) : Backing {
|
class ExposedBacking(val dataSource: DataSource) : Backing {
|
||||||
override val name get() = "Exposed"
|
override val name get() = "Exposed"
|
||||||
lateinit var database: Database
|
private var database: Database? = null
|
||||||
|
private var isShutdown: Boolean = false
|
||||||
|
|
||||||
|
override val isConnected get() = database != null
|
||||||
|
|
||||||
override suspend fun init() {
|
override suspend fun init() {
|
||||||
database = Database.connect(dataSource)
|
synchronized {
|
||||||
transaction(database) {
|
if (isShutdown) throw IllegalStateException()
|
||||||
create(ParcelsT, AddedLocalT)
|
database = Database.connect(dataSource)
|
||||||
|
transaction(database) {
|
||||||
|
create(WorldsT, ParcelsT, AddedLocalT, ParcelOptionsT)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
override suspend fun shutdown() {
|
override suspend fun shutdown() {
|
||||||
if (dataSource is HikariDataSource) {
|
synchronized {
|
||||||
dataSource.close()
|
if (isShutdown) throw IllegalStateException()
|
||||||
|
if (dataSource is HikariDataSource) {
|
||||||
|
dataSource.close()
|
||||||
|
}
|
||||||
|
database = null
|
||||||
|
isShutdown = true
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -86,13 +98,13 @@ class ExposedBacking(val dataSource: DataSource) : Backing {
|
|||||||
private inline fun Transaction.getOrInitWorldId(worldUid: UUID, worldName: String): Int {
|
private inline fun Transaction.getOrInitWorldId(worldUid: UUID, worldName: String): Int {
|
||||||
val binaryUid = worldUid.toByteArray()!!
|
val binaryUid = worldUid.toByteArray()!!
|
||||||
return getWorldId(binaryUid)
|
return getWorldId(binaryUid)
|
||||||
?: WorldsT.insertIgnore { it[uid] = binaryUid; it[name] = worldName }.get(WorldsT.id)
|
?: WorldsT.insertIgnore { it[uid] = binaryUid; it[name] = worldName }.get(WorldsT.id)
|
||||||
?: throw ExposedDatabaseException("This should not happen - failed to insert world named $worldName and get its id")
|
?: throw ExposedDatabaseException("This should not happen - failed to insert world named $worldName and get its id")
|
||||||
}
|
}
|
||||||
|
|
||||||
private inline fun Transaction.getParcelId(worldId: Int, parcelX: Int, parcelZ: Int): Int? {
|
private inline fun Transaction.getParcelId(worldId: Int, parcelX: Int, parcelZ: Int): Int? {
|
||||||
return ParcelsT.select { (ParcelsT.world_id eq worldId) and (ParcelsT.px eq parcelX) and (ParcelsT.pz eq parcelZ) }
|
return ParcelsT.select { (ParcelsT.world_id eq worldId) and (ParcelsT.px eq parcelX) and (ParcelsT.pz eq parcelZ) }
|
||||||
.firstOrNull()?.let { it[ParcelsT.id] }
|
.firstOrNull()?.let { it[ParcelsT.id] }
|
||||||
}
|
}
|
||||||
|
|
||||||
private inline fun Transaction.getParcelId(worldUid: UUID, parcelX: Int, parcelZ: Int): Int? {
|
private inline fun Transaction.getParcelId(worldUid: UUID, parcelX: Int, parcelZ: Int): Int? {
|
||||||
@@ -102,8 +114,8 @@ class ExposedBacking(val dataSource: DataSource) : Backing {
|
|||||||
private inline fun Transaction.getOrInitParcelId(worldUid: UUID, worldName: String, parcelX: Int, parcelZ: Int): Int {
|
private inline fun Transaction.getOrInitParcelId(worldUid: UUID, worldName: String, parcelX: Int, parcelZ: Int): Int {
|
||||||
val worldId = getOrInitWorldId(worldUid, worldName)
|
val worldId = getOrInitWorldId(worldUid, worldName)
|
||||||
return getParcelId(worldId, parcelX, parcelZ)
|
return getParcelId(worldId, parcelX, parcelZ)
|
||||||
?: ParcelsT.insertIgnore { it[world_id] = worldId; it[px] = parcelX; it[pz] = parcelZ }.get(ParcelsT.id)
|
?: ParcelsT.insertIgnore { it[world_id] = worldId; it[px] = parcelX; it[pz] = parcelZ }.get(ParcelsT.id)
|
||||||
?: throw ExposedDatabaseException("This should not happen - failed to insert parcel at $worldName($parcelX, $parcelZ)")
|
?: throw ExposedDatabaseException("This should not happen - failed to insert parcel at $worldName($parcelX, $parcelZ)")
|
||||||
}
|
}
|
||||||
|
|
||||||
private inline fun Transaction.getParcelRow(id: Int): ResultRow? {
|
private inline fun Transaction.getParcelRow(id: Int): ResultRow? {
|
||||||
@@ -144,8 +156,8 @@ class ExposedBacking(val dataSource: DataSource) : Backing {
|
|||||||
ParcelDataHolder().apply {
|
ParcelDataHolder().apply {
|
||||||
|
|
||||||
owner = ParcelOwner.create(
|
owner = ParcelOwner.create(
|
||||||
uuid = row[ParcelsT.owner_uuid]?.toUUID(),
|
uuid = row[ParcelsT.owner_uuid]?.toUUID(),
|
||||||
name = row[ParcelsT.owner_name]
|
name = row[ParcelsT.owner_name]
|
||||||
)
|
)
|
||||||
|
|
||||||
val parcelId = row[ParcelsT.id]
|
val parcelId = row[ParcelsT.id]
|
||||||
@@ -176,16 +188,16 @@ class ExposedBacking(val dataSource: DataSource) : Backing {
|
|||||||
}
|
}
|
||||||
|
|
||||||
ParcelsT.select(where)
|
ParcelsT.select(where)
|
||||||
.map { parcelRow ->
|
.map { parcelRow ->
|
||||||
val worldId = parcelRow[ParcelsT.world_id]
|
val worldId = parcelRow[ParcelsT.world_id]
|
||||||
val worldRow = WorldsT.select({ WorldsT.id eq worldId }).firstOrNull()
|
val worldRow = WorldsT.select({ WorldsT.id eq worldId }).firstOrNull()
|
||||||
?: return@map null
|
?: return@map null
|
||||||
|
|
||||||
val world = SerializableWorld(worldRow[WorldsT.name], worldRow[WorldsT.uid].toUUID())
|
val world = SerializableWorld(worldRow[WorldsT.name], worldRow[WorldsT.uid].toUUID())
|
||||||
SerializableParcel(world, Vec2i(parcelRow[ParcelsT.px], parcelRow[ParcelsT.pz]))
|
SerializableParcel(world, Vec2i(parcelRow[ParcelsT.px], parcelRow[ParcelsT.pz]))
|
||||||
}
|
}
|
||||||
.filterNotNull()
|
.filterNotNull()
|
||||||
.toList()
|
.toList()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -10,15 +10,15 @@ import org.jetbrains.exposed.sql.transactions.TransactionManager
|
|||||||
* insertOrUpdate from https://github.com/JetBrains/Exposed/issues/167#issuecomment-403837917
|
* insertOrUpdate from https://github.com/JetBrains/Exposed/issues/167#issuecomment-403837917
|
||||||
*/
|
*/
|
||||||
inline fun <T : Table> T.insertOrUpdate(vararg onDuplicateUpdateKeys: Column<*>, body: T.(InsertStatement<Number>) -> Unit) =
|
inline fun <T : Table> T.insertOrUpdate(vararg onDuplicateUpdateKeys: Column<*>, body: T.(InsertStatement<Number>) -> Unit) =
|
||||||
InsertOrUpdate<Number>(onDuplicateUpdateKeys, this).apply {
|
InsertOrUpdate<Number>(onDuplicateUpdateKeys, this).apply {
|
||||||
body(this)
|
body(this)
|
||||||
execute(TransactionManager.current())
|
execute(TransactionManager.current())
|
||||||
}
|
}
|
||||||
|
|
||||||
class InsertOrUpdate<Key : Any>(
|
class InsertOrUpdate<Key : Any>(
|
||||||
private val onDuplicateUpdateKeys: Array<out Column<*>>,
|
private val onDuplicateUpdateKeys: Array<out Column<*>>,
|
||||||
table: Table,
|
table: Table,
|
||||||
isIgnore: Boolean = false
|
isIgnore: Boolean = false
|
||||||
) : InsertStatement<Key>(table, isIgnore) {
|
) : InsertStatement<Key>(table, isIgnore) {
|
||||||
override fun prepareSQL(transaction: Transaction): String {
|
override fun prepareSQL(transaction: Transaction): String {
|
||||||
val onUpdateSQL = if (onDuplicateUpdateKeys.isNotEmpty()) {
|
val onUpdateSQL = if (onDuplicateUpdateKeys.isNotEmpty()) {
|
||||||
|
|||||||
@@ -31,7 +31,7 @@ fun getHikariDataSource(dialectName: String,
|
|||||||
dataSourceProperties.remove("serverName")
|
dataSourceProperties.remove("serverName")
|
||||||
dataSourceProperties.remove("port")
|
dataSourceProperties.remove("port")
|
||||||
dataSourceProperties.remove("databaseName")
|
dataSourceProperties.remove("databaseName")
|
||||||
addDataSourceProperty("url", "jdbc:h2:tcp://$address/~/${dco.database}")
|
addDataSourceProperty("url", "jdbc:h2:${if (address.isBlank()) "" else "tcp://$address/"}~/${dco.database}")
|
||||||
} else {
|
} else {
|
||||||
// doesn't exist on the MariaDB driver
|
// doesn't exist on the MariaDB driver
|
||||||
addDataSourceProperty("cachePrepStmts", "true")
|
addDataSourceProperty("cachePrepStmts", "true")
|
||||||
|
|||||||
@@ -8,10 +8,11 @@ import com.fasterxml.jackson.databind.ser.BeanSerializerModifier
|
|||||||
import com.fasterxml.jackson.databind.ser.std.StdSerializer
|
import com.fasterxml.jackson.databind.ser.std.StdSerializer
|
||||||
import com.fasterxml.jackson.dataformat.yaml.YAMLFactory
|
import com.fasterxml.jackson.dataformat.yaml.YAMLFactory
|
||||||
import com.fasterxml.jackson.module.kotlin.KotlinModule
|
import com.fasterxml.jackson.module.kotlin.KotlinModule
|
||||||
import io.dico.parcels2.*
|
import io.dico.parcels2.GeneratorFactory
|
||||||
|
import io.dico.parcels2.GeneratorOptions
|
||||||
|
import io.dico.parcels2.StorageOptions
|
||||||
import org.bukkit.Bukkit
|
import org.bukkit.Bukkit
|
||||||
import org.bukkit.block.data.BlockData
|
import org.bukkit.block.data.BlockData
|
||||||
import kotlin.reflect.KClass
|
|
||||||
import kotlin.reflect.full.isSuperclassOf
|
import kotlin.reflect.full.isSuperclassOf
|
||||||
|
|
||||||
val yamlObjectMapper = ObjectMapper(YAMLFactory()).apply {
|
val yamlObjectMapper = ObjectMapper(YAMLFactory()).apply {
|
||||||
|
|||||||
@@ -14,10 +14,11 @@ interface Storage {
|
|||||||
val name: String
|
val name: String
|
||||||
val syncDispatcher: CoroutineDispatcher
|
val syncDispatcher: CoroutineDispatcher
|
||||||
val asyncDispatcher: CoroutineDispatcher
|
val asyncDispatcher: CoroutineDispatcher
|
||||||
|
val isConnected: Boolean
|
||||||
|
|
||||||
fun init(): Deferred<Unit>
|
fun init(): Job
|
||||||
|
|
||||||
fun shutdown(): Deferred<Unit>
|
fun shutdown(): Job
|
||||||
|
|
||||||
|
|
||||||
fun readParcelData(parcelFor: Parcel): Deferred<ParcelData?>
|
fun readParcelData(parcelFor: Parcel): Deferred<ParcelData?>
|
||||||
@@ -26,16 +27,18 @@ interface Storage {
|
|||||||
|
|
||||||
fun getOwnedParcels(user: ParcelOwner): Deferred<List<SerializableParcel>>
|
fun getOwnedParcels(user: ParcelOwner): Deferred<List<SerializableParcel>>
|
||||||
|
|
||||||
|
fun getNumParcels(user: ParcelOwner): Deferred<Int>
|
||||||
|
|
||||||
fun setParcelData(parcelFor: Parcel, data: ParcelData?): Deferred<Unit>
|
|
||||||
|
|
||||||
fun setParcelOwner(parcelFor: Parcel, owner: ParcelOwner?): Deferred<Unit>
|
fun setParcelData(parcelFor: Parcel, data: ParcelData?): Job
|
||||||
|
|
||||||
fun setParcelPlayerState(parcelFor: Parcel, player: UUID, state: Boolean?): Deferred<Unit>
|
fun setParcelOwner(parcelFor: Parcel, owner: ParcelOwner?): Job
|
||||||
|
|
||||||
fun setParcelAllowsInteractInventory(parcel: Parcel, value: Boolean): Deferred<Unit>
|
fun setParcelPlayerState(parcelFor: Parcel, player: UUID, state: Boolean?): Job
|
||||||
|
|
||||||
fun setParcelAllowsInteractInputs(parcel: Parcel, value: Boolean): Deferred<Unit>
|
fun setParcelAllowsInteractInventory(parcel: Parcel, value: Boolean): Job
|
||||||
|
|
||||||
|
fun setParcelAllowsInteractInputs(parcel: Parcel, value: Boolean): Job
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -44,15 +47,21 @@ class StorageWithCoroutineBacking internal constructor(val backing: Backing) : S
|
|||||||
override val syncDispatcher = Executor { it.run() }.asCoroutineDispatcher()
|
override val syncDispatcher = Executor { it.run() }.asCoroutineDispatcher()
|
||||||
val poolSize: Int get() = 4
|
val poolSize: Int get() = 4
|
||||||
override val asyncDispatcher = Executors.newFixedThreadPool(poolSize) { Thread(it, "Parcels2_StorageThread") }.asCoroutineDispatcher()
|
override val asyncDispatcher = Executors.newFixedThreadPool(poolSize) { Thread(it, "Parcels2_StorageThread") }.asCoroutineDispatcher()
|
||||||
|
override val isConnected get() = backing.isConnected
|
||||||
|
|
||||||
@Suppress("NOTHING_TO_INLINE")
|
@Suppress("NOTHING_TO_INLINE")
|
||||||
private inline fun <T> defer(noinline block: suspend CoroutineScope.() -> T): Deferred<T> {
|
private inline fun <T> defer(noinline block: suspend CoroutineScope.() -> T): Deferred<T> {
|
||||||
return async(context = asyncDispatcher, start = CoroutineStart.ATOMIC, block = block)
|
return async(context = asyncDispatcher, start = CoroutineStart.ATOMIC, block = block)
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun init() = defer { backing.init() }
|
@Suppress("NOTHING_TO_INLINE")
|
||||||
|
private inline fun job(noinline block: suspend CoroutineScope.() -> Unit): Job {
|
||||||
|
return launch(context = asyncDispatcher, start = CoroutineStart.ATOMIC, block = block)
|
||||||
|
}
|
||||||
|
|
||||||
override fun shutdown() = defer { backing.shutdown() }
|
override fun init() = job { backing.init() }
|
||||||
|
|
||||||
|
override fun shutdown() = job { backing.shutdown() }
|
||||||
|
|
||||||
|
|
||||||
override fun readParcelData(parcelFor: Parcel) = defer { backing.readParcelData(parcelFor) }
|
override fun readParcelData(parcelFor: Parcel) = defer { backing.readParcelData(parcelFor) }
|
||||||
@@ -61,16 +70,17 @@ class StorageWithCoroutineBacking internal constructor(val backing: Backing) : S
|
|||||||
with(backing) { produceParcelData(parcelsFor) }
|
with(backing) { produceParcelData(parcelsFor) }
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
override fun setParcelData(parcelFor: Parcel, data: ParcelData?) = defer { backing.setParcelData(parcelFor, data) }
|
|
||||||
|
|
||||||
override fun getOwnedParcels(user: ParcelOwner) = defer { backing.getOwnedParcels(user) }
|
override fun getOwnedParcels(user: ParcelOwner) = defer { backing.getOwnedParcels(user) }
|
||||||
|
|
||||||
override fun setParcelOwner(parcelFor: Parcel, owner: ParcelOwner?) = defer { backing.setParcelOwner(parcelFor, owner) }
|
override fun getNumParcels(user: ParcelOwner) = defer { backing.getNumParcels(user) }
|
||||||
|
|
||||||
override fun setParcelPlayerState(parcelFor: Parcel, player: UUID, state: Boolean?) = defer { backing.setParcelPlayerState(parcelFor, player, state) }
|
override fun setParcelData(parcelFor: Parcel, data: ParcelData?) = job { backing.setParcelData(parcelFor, data) }
|
||||||
|
|
||||||
override fun setParcelAllowsInteractInventory(parcel: Parcel, value: Boolean) = defer { backing.setParcelAllowsInteractInventory(parcel, value) }
|
override fun setParcelOwner(parcelFor: Parcel, owner: ParcelOwner?) = job { backing.setParcelOwner(parcelFor, owner) }
|
||||||
|
|
||||||
override fun setParcelAllowsInteractInputs(parcel: Parcel, value: Boolean) = defer { backing.setParcelAllowsInteractInputs(parcel, value) }
|
override fun setParcelPlayerState(parcelFor: Parcel, player: UUID, state: Boolean?) = job { backing.setParcelPlayerState(parcelFor, player, state) }
|
||||||
|
|
||||||
|
override fun setParcelAllowsInteractInventory(parcel: Parcel, value: Boolean) = job { backing.setParcelAllowsInteractInventory(parcel, value) }
|
||||||
|
|
||||||
|
override fun setParcelAllowsInteractInputs(parcel: Parcel, value: Boolean) = job { backing.setParcelAllowsInteractInputs(parcel, value) }
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,7 +1,6 @@
|
|||||||
package io.dico.parcels2.storage
|
package io.dico.parcels2.storage
|
||||||
|
|
||||||
import io.dico.parcels2.DataConnectionOptions
|
import io.dico.parcels2.DataConnectionOptions
|
||||||
import net.minecraft.server.v1_13_R1.WorldType.types
|
|
||||||
import kotlin.reflect.KClass
|
import kotlin.reflect.KClass
|
||||||
|
|
||||||
interface StorageFactory {
|
interface StorageFactory {
|
||||||
@@ -29,8 +28,8 @@ class ConnectionStorageFactory : StorageFactory {
|
|||||||
override val optionsClass = DataConnectionOptions::class
|
override val optionsClass = DataConnectionOptions::class
|
||||||
|
|
||||||
private val types: Map<String, String> = mutableMapOf(
|
private val types: Map<String, String> = mutableMapOf(
|
||||||
"mysql" to "com.mysql.jdbc.jdbc2.optional.MysqlDataSource",
|
"mysql" to "com.mysql.jdbc.jdbc2.optional.MysqlDataSource",
|
||||||
"h2" to "org.h2.jdbcx.JdbcDataSource"
|
"h2" to "org.h2.jdbcx.JdbcDataSource"
|
||||||
)
|
)
|
||||||
|
|
||||||
fun register(companion: StorageFactory.StorageFactories) {
|
fun register(companion: StorageFactory.StorageFactories) {
|
||||||
|
|||||||
@@ -1,9 +1,7 @@
|
|||||||
package io.dico.parcels2.util
|
package io.dico.parcels2.util
|
||||||
|
|
||||||
import io.dico.parcels2.logger
|
import io.dico.parcels2.logger
|
||||||
import org.slf4j.Logger
|
|
||||||
import java.io.File
|
import java.io.File
|
||||||
import java.io.PrintWriter
|
|
||||||
|
|
||||||
fun File.tryCreate(): Boolean {
|
fun File.tryCreate(): Boolean {
|
||||||
val parent = parentFile
|
val parent = parentFile
|
||||||
@@ -13,3 +11,7 @@ fun File.tryCreate(): Boolean {
|
|||||||
}
|
}
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
|
|
||||||
|
inline fun <R> Any.synchronized(block: () -> R): R {
|
||||||
|
return synchronized(this, block)
|
||||||
|
}
|
||||||
|
|||||||
@@ -6,18 +6,19 @@ import io.dico.parcels2.logger
|
|||||||
import org.bukkit.entity.Player
|
import org.bukkit.entity.Player
|
||||||
import org.bukkit.plugin.java.JavaPlugin
|
import org.bukkit.plugin.java.JavaPlugin
|
||||||
|
|
||||||
inline val Player.hasBanBypass get() = hasPermission("plots.admin.bypass.ban")
|
inline val Player.uuid get() = uniqueId
|
||||||
inline val Player.hasBuildAnywhere get() = hasPermission("plots.admin.bypass.build")
|
inline val Player.hasBanBypass get() = hasPermission("parcels.admin.bypass.ban")
|
||||||
inline val Player.hasGamemodeBypass get() = hasPermission("plots.admin.bypass.gamemode")
|
inline val Player.hasBuildAnywhere get() = hasPermission("parcels.admin.bypass.build")
|
||||||
inline val Player.hasAdminManage get() = hasPermission("plots.admin.manage")
|
inline val Player.hasGamemodeBypass get() = hasPermission("parcels.admin.bypass.gamemode")
|
||||||
inline val Player.hasPlotHomeOthers get() = hasPermission("plots.command.home.others")
|
inline val Player.hasAdminManage get() = hasPermission("parcels.admin.manage")
|
||||||
inline val Player.hasRandomSpecific get() = hasPermission("plots.command.random.specific")
|
inline val Player.hasParcelHomeOthers get() = hasPermission("parcels.command.home.others")
|
||||||
val Player.plotLimit: Int
|
inline val Player.hasRandomSpecific get() = hasPermission("parcels.command.random.specific")
|
||||||
|
val Player.parcelLimit: Int
|
||||||
get() {
|
get() {
|
||||||
for (info in effectivePermissions) {
|
for (info in effectivePermissions) {
|
||||||
val perm = info.permission
|
val perm = info.permission
|
||||||
if (perm.startsWith("plots.limit.")) {
|
if (perm.startsWith("parcels.limit.")) {
|
||||||
val limitString = perm.substring("plots.limit.".length)
|
val limitString = perm.substring("parcels.limit.".length)
|
||||||
if (limitString == "*") {
|
if (limitString == "*") {
|
||||||
return Int.MAX_VALUE
|
return Int.MAX_VALUE
|
||||||
}
|
}
|
||||||
@@ -32,7 +33,7 @@ val Player.plotLimit: Int
|
|||||||
private const val DEFAULT_LIMIT = 1
|
private const val DEFAULT_LIMIT = 1
|
||||||
private val prefix = Formatting.translateChars('&', "&4[&c${JavaPlugin.getPlugin(ParcelsPlugin::class.java).name}&4] &a")
|
private val prefix = Formatting.translateChars('&', "&4[&c${JavaPlugin.getPlugin(ParcelsPlugin::class.java).name}&4] &a")
|
||||||
|
|
||||||
fun Player.sendPlotMessage(except: Boolean = false, nopermit: Boolean = false, message: String) {
|
fun Player.sendParcelMessage(except: Boolean = false, nopermit: Boolean = false, message: String) {
|
||||||
if (except) {
|
if (except) {
|
||||||
sendMessage(prefix + Formatting.YELLOW + Formatting.translateChars('&', message))
|
sendMessage(prefix + Formatting.YELLOW + Formatting.translateChars('&', message))
|
||||||
} else if (nopermit) {
|
} else if (nopermit) {
|
||||||
|
|||||||
@@ -8,8 +8,8 @@ import java.util.*
|
|||||||
@Suppress("UsePropertyAccessSyntax")
|
@Suppress("UsePropertyAccessSyntax")
|
||||||
fun getPlayerName(uuid: UUID?, ifUnknown: String? = null): String {
|
fun getPlayerName(uuid: UUID?, ifUnknown: String? = null): String {
|
||||||
return uuid?.let { Bukkit.getOfflinePlayer(uuid)?.takeIf { it.isOnline() || it.hasPlayedBefore() }?.name }
|
return uuid?.let { Bukkit.getOfflinePlayer(uuid)?.takeIf { it.isOnline() || it.hasPlayedBefore() }?.name }
|
||||||
?: ifUnknown
|
?: ifUnknown
|
||||||
?: ":unknown_name:"
|
?: ":unknown_name:"
|
||||||
}
|
}
|
||||||
|
|
||||||
@Contract("null -> null; !null -> !null", pure = true)
|
@Contract("null -> null; !null -> !null", pure = true)
|
||||||
|
|||||||
Reference in New Issue
Block a user