Archived
0

Work on a couple of the todos

This commit is contained in:
Dico
2018-08-12 18:07:43 +01:00
parent 957d6f2434
commit 5bd0970c54
32 changed files with 503 additions and 148 deletions

View File

@@ -1,5 +1,6 @@
package io.dico.dicore.command;
import io.dico.dicore.command.predef.DefaultGroupCommand;
import io.dico.dicore.command.predef.HelpCommand;
import java.util.*;
@@ -23,7 +24,7 @@ public class ChildCommandAddress extends ModifiableCommandAddress {
}
public static ChildCommandAddress newPlaceHolderCommand(String name, String... aliases) {
ChildCommandAddress rv = new ChildCommandAddress(null, name, aliases);
ChildCommandAddress rv = new ChildCommandAddress(DefaultGroupCommand.getInstance(), name, aliases);
HelpCommand.registerAsChild(rv);
return rv;
}

View File

@@ -210,15 +210,39 @@ public final class CommandBuilder {
* @param shortDescription a short description
* @param description the lines of a full description.
* @return this
* @throws IllegalStateException if the current group has no command
*/
public CommandBuilder setGroupDescription(String shortDescription, String... description) {
Command command = cur.getCommand();
if (command == null) throw new IllegalStateException();
cur.setCommand(command
.setShortDescription(shortDescription)
.setDescription(description));
return this;
}
/**
* Add a context filter to the command of the current group
* @return this
* @throws IllegalStateException if the current group has no command
*/
public CommandBuilder addContextFilter(IContextFilter contextFilter) {
Command command = cur.getCommand();
if (command == null) throw new IllegalStateException();
cur.setCommand(command
.addContextFilter(contextFilter));
return this;
}
/**
* Add a required permission to the command of the current group
* @return this
* @throws IllegalStateException if the current group has no command
*/
public CommandBuilder addRequiredPermission(String permission) {
return addContextFilter(IContextFilter.permission(permission));
}
/**
* Jump up a level in the address
*

View File

@@ -2,6 +2,7 @@ package io.dico.dicore.command;
import io.dico.dicore.command.chat.ChatControllers;
import io.dico.dicore.command.chat.IChatController;
import io.dico.dicore.command.predef.DefaultGroupCommand;
import io.dico.dicore.command.predef.HelpCommand;
import io.dico.dicore.command.predef.PredefinedCommand;
@@ -32,7 +33,7 @@ public abstract class ModifiableCommandAddress implements ICommandAddress {
@Override
public boolean hasUserDeclaredCommand() {
Command command = getCommand();
return command != null && !(command instanceof PredefinedCommand);
return command != null && !(command instanceof PredefinedCommand) && !(command instanceof DefaultGroupCommand);
}
@Override

View File

@@ -1,6 +1,7 @@
package io.dico.dicore.command;
import io.dico.dicore.command.parameter.ArgumentBuffer;
import io.dico.dicore.command.predef.DefaultGroupCommand;
import io.dico.dicore.command.registration.BukkitCommand;
import org.bukkit.Location;
import org.bukkit.command.CommandSender;
@@ -167,10 +168,10 @@ public class RootCommandAddress extends ModifiableCommandAddress implements ICom
ModifiableCommandAddress targetAddress = getCommandTarget(sender, buffer);
Command target = targetAddress.getCommand();
if (target == null) {
if (target == null || target instanceof DefaultGroupCommand) {
if (targetAddress.hasHelpCommand()) {
target = targetAddress.getHelpCommand().getCommand();
} else {
} else if (target == null){
return false;
}
}

View File

@@ -7,6 +7,9 @@ import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
/**
* If this annotation is not present, inheriting permissions is default.
*/
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.METHOD)
public @interface RequirePermissions {

View File

@@ -0,0 +1,24 @@
package io.dico.dicore.command.predef;
import io.dico.dicore.command.Command;
import io.dico.dicore.command.CommandException;
import io.dico.dicore.command.ExecutionContext;
import org.bukkit.command.CommandSender;
public class DefaultGroupCommand extends Command {
private static final DefaultGroupCommand instance = new DefaultGroupCommand();
public static DefaultGroupCommand getInstance() {
return instance;
}
private DefaultGroupCommand() {
}
@Override public String execute(CommandSender sender, ExecutionContext context) throws CommandException {
context.getAddress().getChatController().sendHelpMessage(sender, context, context.getAddress(), 1);
return null;
}
}

View File

@@ -2,6 +2,7 @@ package io.dico.parcels2
import io.dico.parcels2.util.Vec2i
import io.dico.parcels2.util.hasBuildAnywhere
import org.bukkit.Location
import org.bukkit.OfflinePlayer
import org.bukkit.entity.Player
import org.joda.time.DateTime
@@ -30,11 +31,14 @@ interface Parcel : ParcelData {
fun copyData(data: ParcelData)
fun dispose()
val homeLocation: Location get() = world.blockManager.getHomeLocation(id)
}
interface ParcelData : AddedData {
var owner: PlayerProfile?
val since: DateTime?
val lastClaimTime: DateTime?
var ownerSignOutdated: Boolean
fun canBuild(player: OfflinePlayer, checkAdmin: Boolean = true, checkGlobal: Boolean = true): Boolean
@@ -53,7 +57,8 @@ interface ParcelData : AddedData {
class ParcelDataHolder(addedMap: MutableAddedDataMap = mutableMapOf()) : AddedDataHolder(addedMap), ParcelData {
override var owner: PlayerProfile? = null
override var since: DateTime? = null
override var lastClaimTime: DateTime? = null
override var ownerSignOutdated = false
override fun canBuild(player: OfflinePlayer, checkAdmin: Boolean, checkGlobal: Boolean) = isAllowed(player.statusKey)
|| owner.let { it != null && it.matches(player, allowNameMatch = false) }
|| (checkAdmin && player is Player && player.hasBuildAnywhere)

View File

@@ -1,9 +1,12 @@
package io.dico.parcels2
import io.dico.parcels2.blockvisitor.RegionTraversal
import io.dico.parcels2.blockvisitor.RegionTraverser
import io.dico.parcels2.blockvisitor.Worker
import io.dico.parcels2.blockvisitor.WorkerScope
import io.dico.parcels2.blockvisitor.WorktimeLimiter
import io.dico.parcels2.util.Region
import io.dico.parcels2.util.Vec2i
import io.dico.parcels2.util.get
import org.bukkit.Chunk
import org.bukkit.Location
import org.bukkit.World
@@ -15,6 +18,8 @@ import org.bukkit.generator.ChunkGenerator
import java.util.Random
abstract class ParcelGenerator : ChunkGenerator() {
abstract val worldName: String
abstract val world: World
abstract override fun generateChunkData(world: World?, random: Random?, chunkX: Int, chunkZ: Int, biome: BiomeGrid?): ChunkData
@@ -31,31 +36,57 @@ abstract class ParcelGenerator : ChunkGenerator() {
})
}
abstract fun makeParcelBlockManager(worktimeLimiter: WorktimeLimiter): ParcelBlockManager
abstract fun makeParcelLocator(container: ParcelContainer): ParcelLocator
abstract fun makeParcelLocatorAndBlockManager(worldId: ParcelWorldId,
container: ParcelContainer,
worktimeLimiter: WorktimeLimiter): Pair<ParcelLocator, ParcelBlockManager>
}
@Suppress("DeprecatedCallableAddReplaceWith")
interface ParcelBlockManager {
val world: World
val worktimeLimiter: WorktimeLimiter
val parcelTraverser: RegionTraverser
fun getBottomBlock(parcel: ParcelId): Vec2i
// fun getBottomBlock(parcel: ParcelId): Vec2i
fun getHomeLocation(parcel: ParcelId): Location
fun getRegion(parcel: ParcelId): Region
fun getEntities(parcel: ParcelId): Collection<Entity>
fun setOwnerBlock(parcel: ParcelId, owner: PlayerProfile?)
@Deprecated("")
fun getEntities(parcel: ParcelId): Collection<Entity> = TODO()
@Deprecated("")
fun getBlocks(parcel: ParcelId, yRange: IntRange = 0..255): Iterator<Block> = TODO()
fun setBiome(parcel: ParcelId, biome: Biome): Worker
fun clearParcel(parcel: ParcelId): Worker
fun doBlockOperation(parcel: ParcelId, direction: RegionTraversal = RegionTraversal.DOWNWARD, operation: (Block) -> Unit): Worker
/**
* Used to update owner blocks in the corner of the parcel
*/
fun getParcelsWithOwnerBlockIn(chunk: Chunk): Collection<Vec2i>
}
inline fun ParcelBlockManager.doBlockOperation(parcel: ParcelId,
traverser: RegionTraverser,
crossinline operation: suspend WorkerScope.(Block) -> Unit) = worktimeLimiter.submit {
val region = getRegion(parcel)
val blockCount = region.blockCount.toDouble()
val blocks = traverser.traverseRegion(region)
for ((index, vec) in blocks.withIndex()) {
markSuspensionPoint()
operation(world[vec])
setProgress((index + 1) / blockCount)
}
}
abstract class ParcelBlockManagerBase : ParcelBlockManager {
override fun getEntities(parcel: ParcelId): Collection<Entity> {
val region = getRegion(parcel)
val center = region.center
val centerLoc = Location(world, center.x, center.y, center.z)
val centerDist = (center - region.origin).add(0.2, 0.2, 0.2)
return world.getNearbyEntities(centerLoc, centerDist.x, centerDist.y, centerDist.z)
}
}

View File

@@ -4,10 +4,12 @@ import io.dico.parcels2.options.RuntimeWorldOptions
import io.dico.parcels2.storage.Storage
import io.dico.parcels2.util.Vec2i
import io.dico.parcels2.util.floor
import org.bukkit.Chunk
import org.bukkit.Location
import org.bukkit.World
import org.bukkit.block.Block
import org.bukkit.entity.Entity
import org.joda.time.DateTime
import java.util.UUID
interface ParcelProvider {
@@ -58,7 +60,6 @@ interface ParcelLocator {
fun getParcelAt(entity: Entity): Parcel? = getParcelAt(entity.location).takeIf { entity.world == world }
fun getParcelAt(block: Block): Parcel? = getParcelAt(block.x, block.z).takeIf { block.world == world }
}
typealias ParcelContainerFactory = (ParcelWorld) -> ParcelContainer
@@ -73,7 +74,7 @@ interface ParcelContainer {
}
interface ParcelWorld : ParcelLocator, ParcelContainer, ParcelBlockManager {
interface ParcelWorld : ParcelLocator, ParcelContainer {
val id: ParcelWorldId
val name: String
val uid: UUID?
@@ -84,4 +85,7 @@ interface ParcelWorld : ParcelLocator, ParcelContainer, ParcelBlockManager {
val locator: ParcelLocator
val blockManager: ParcelBlockManager
val globalAddedData: GlobalAddedDataManager
val creationTime: DateTime?
}

View File

@@ -21,6 +21,7 @@ import org.bukkit.plugin.java.JavaPlugin
import org.slf4j.Logger
import org.slf4j.LoggerFactory
import java.io.File
import java.util.Random
val logger: Logger = LoggerFactory.getLogger("ParcelsPlugin")
private inline val plogger get() = logger
@@ -49,7 +50,15 @@ class ParcelsPlugin : JavaPlugin() {
}
override fun onDisable() {
val hasWorkers = worktimeLimiter.workers.isNotEmpty()
if (hasWorkers) {
plogger.warn("Parcels is attempting to complete all ${worktimeLimiter.workers.size} remaining jobs before shutdown...")
}
worktimeLimiter.completeAllTasks()
if (hasWorkers) {
plogger.info("Parcels has completed the remaining jobs.")
}
cmdDispatcher?.unregisterFromCommandMap()
}
@@ -124,7 +133,7 @@ class ParcelsPlugin : JavaPlugin() {
private fun registerListeners() {
if (listeners == null) {
listeners = ParcelListeners(parcelProvider, entityTracker)
listeners = ParcelListeners(parcelProvider, entityTracker, storage)
registrator.registerListeners(listeners!!)
}

View File

@@ -77,7 +77,8 @@ interface PlayerProfile {
interface Real : PlayerProfile {
override val uuid: UUID
override val nameOrBukkitName: String?
get() = name ?: Bukkit.getOfflinePlayer(uuid).takeIf { it.isValid }?.name
// If a player is online, their name is prioritized to get name changes right immediately
get() = Bukkit.getPlayer(uuid)?.name ?: name ?: Bukkit.getOfflinePlayer(uuid).takeIf { it.isValid }?.name
override val notNullName: String
get() = name ?: getPlayerNameOrDefault(uuid)

View File

@@ -1,43 +0,0 @@
package io.dico.parcels2.blockvisitor
import io.dico.parcels2.util.Region
import io.dico.parcels2.util.Vec3i
import kotlin.coroutines.experimental.SequenceBuilder
import kotlin.coroutines.experimental.buildIterator
enum class RegionTraversal(private val builder: suspend SequenceBuilder<Vec3i>.(Region) -> Unit) {
DOWNWARD({ region ->
val origin = region.origin
val size = region.size
repeat(size.y) { y ->
repeat(size.z) { z ->
repeat(size.x) { x ->
yield(origin.add(x, size.y - y - 1, z))
}
}
}
}),
UPWARD({ region ->
val origin = region.origin
val size = region.size
repeat(size.y) { y ->
repeat(size.z) { z ->
repeat(size.x) { x ->
yield(origin.add(x, y, z))
}
}
}
}),
;
fun regionTraverser(region: Region) = Iterable { buildIterator { builder(region) } }
}

View File

@@ -0,0 +1,67 @@
package io.dico.parcels2.blockvisitor
import io.dico.parcels2.util.Region
import io.dico.parcels2.util.Vec3i
import kotlin.coroutines.experimental.SequenceBuilder
import kotlin.coroutines.experimental.buildIterator
abstract class RegionTraverser {
fun traverseRegion(region: Region): Iterable<Vec3i> = Iterable { buildIterator { build(region) } }
protected abstract suspend fun SequenceBuilder<Vec3i>.build(region: Region)
companion object {
val upward = create { traverseUpward(it) }
val downward = create { traverseDownward(it) }
val forClearing get() = downward
val forFilling get() = upward
inline fun create(crossinline builder: suspend SequenceBuilder<Vec3i>.(Region) -> Unit) = object : RegionTraverser() {
override suspend fun SequenceBuilder<Vec3i>.build(region: Region) {
builder(region)
}
}
private suspend fun SequenceBuilder<Vec3i>.traverseDownward(region: Region) {
val origin = region.origin
val size = region.size
repeat(size.y) { y ->
repeat(size.z) { z ->
repeat(size.x) { x ->
yield(origin.add(x, size.y - y - 1, z))
}
}
}
}
private suspend fun SequenceBuilder<Vec3i>.traverseUpward(region: Region) {
val origin = region.origin
val size = region.size
repeat(size.y) { y ->
repeat(size.z) { z ->
repeat(size.x) { x ->
yield(origin.add(x, size.y - y - 1, z))
}
}
}
}
private fun slice(region: Region, atY: Int): Pair<Region, Region?> {
if (atY < region.size.y + 1) {
val first = Region(region.origin, region.size.withY(atY + 1))
val second = Region(region.origin.withY(atY), region.size.addY(-atY-1))
return first to second
}
return region to null
}
fun upToAndDownUntil(y: Int) = create { region ->
val (bottom, top) = slice(region, y)
traverseUpward(bottom)
top?.let { traverseDownward(it) }
}
}
}

View File

@@ -23,7 +23,7 @@ class Schematic {
val size = region.size.also { _size = it }
val data = arrayOfNulls<BlockData>(region.blockCount).also { _data = it }
//val extra = mutableMapOf<Vec3i, (Block) -> Unit>().also { extra = it }
val blocks = RegionTraversal.DOWNWARD.regionTraverser(region)
val blocks = RegionTraverser.downward.traverseRegion(region)
for ((index, vec) in blocks.withIndex()) {
markSuspensionPoint()
@@ -39,7 +39,7 @@ class Schematic {
fun getPasteTask(world: World, position: Vec3i): TimeLimitedTask = {
if (!isLoaded) throw IllegalStateException()
val region = Region(position, _size!!)
val blocks = RegionTraversal.DOWNWARD.regionTraverser(region)
val blocks = RegionTraverser.downward.traverseRegion(region)
val data = _data!!
for ((index, vec) in blocks.withIndex()) {

View File

@@ -122,8 +122,14 @@ class TickWorktimeLimiter(private val plugin: ParcelsPlugin, var options: TickWo
override fun submit(task: TimeLimitedTask): Worker {
val worker: WorkerContinuation = WorkerImpl(plugin.functionHelper, task)
if (bukkitTask == null) {
val completed = worker.resume(options.workTime.toLong())
if (completed) return worker
bukkitTask = plugin.functionHelper.scheduleRepeating(0, options.tickInterval) { tickJobs() }
}
_workers.addFirst(worker)
if (bukkitTask == null) bukkitTask = plugin.functionHelper.scheduleRepeating(0, options.tickInterval) { tickJobs() }
return worker
}

View File

@@ -5,7 +5,8 @@ import io.dico.dicore.command.EMessageType
import io.dico.dicore.command.ExecutionContext
import io.dico.dicore.command.annotation.Cmd
import io.dico.parcels2.ParcelsPlugin
import io.dico.parcels2.blockvisitor.RegionTraversal
import io.dico.parcels2.blockvisitor.RegionTraverser
import io.dico.parcels2.doBlockOperation
import org.bukkit.Bukkit
import org.bukkit.Material
import org.bukkit.entity.Player
@@ -43,7 +44,7 @@ class CommandsDebug(plugin: ParcelsPlugin) : AbstractParcelCommands(plugin) {
)
val random = Random()
world.doBlockOperation(parcel.id, direction = RegionTraversal.UPWARD) { block ->
world.blockManager.doBlockOperation(parcel.id, traverser = RegionTraverser.upward) { block ->
block.blockData = blockDatas[random.nextInt(7)]
}.onProgressUpdate(1000, 1000) { progress, elapsedTime ->
context.sendMessage(EMessageType.INFORMATIVE, "Mess progress: %.02f%%, %.2fs elapsed"

View File

@@ -26,7 +26,7 @@ class CommandsGeneral(plugin: ParcelsPlugin) : AbstractParcelCommands(plugin) {
val parcel = world.nextEmptyParcel()
?: error("This world is full, please ask an admin to upsize it")
parcel.owner = PlayerProfile(uuid = player.uuid)
player.teleport(parcel.world.getHomeLocation(parcel.id))
player.teleport(parcel.homeLocation)
return "Enjoy your new parcel!"
}
@@ -63,7 +63,7 @@ class CommandsGeneral(plugin: ParcelsPlugin) : AbstractParcelCommands(plugin) {
val match = target.getParcelSuspend(plugin.storage)
?: error("The specified parcel could not be matched")
player.teleport(match.world.getHomeLocation(match.id))
player.teleport(match.homeLocation)
return ""
}
@@ -100,7 +100,7 @@ class CommandsGeneral(plugin: ParcelsPlugin) : AbstractParcelCommands(plugin) {
if (!sure) return "Are you sure? You cannot undo this action!\n" +
"Run \"/${context.rawInput} -sure\" if you want to go through with this."
world.clearParcel(parcel.id)
world.blockManager.clearParcel(parcel.id)
.onProgressUpdate(1000, 1000) { progress, elapsedTime ->
val alt = context.getFormat(EMessageType.NUMBER)
val main = context.getFormat(EMessageType.INFORMATIVE)

View File

@@ -17,11 +17,12 @@ fun getParcelCommands(plugin: ParcelsPlugin): ICommandDispatcher {
.addParameterType(true, ParcelTarget.PType(plugin.parcelProvider))
.group("parcel", "plot", "plots", "p")
.addRequiredPermission("parcels.command")
.registerCommands(CommandsGeneral(plugin))
.registerCommands(CommandsAddedStatusLocal(plugin))
.group("option", "opt", "o")
//.apply { CommandsParcelOptions.setGroupDescription(this) }
.apply { CommandsParcelOptions.setGroupDescription(this) }
.registerCommands(CommandsParcelOptions(plugin))
.parent()

View File

@@ -1,14 +1,13 @@
package io.dico.parcels2.defaultimpl
import io.dico.parcels2.*
import io.dico.parcels2.blockvisitor.RegionTraversal
import io.dico.parcels2.blockvisitor.RegionTraverser
import io.dico.parcels2.blockvisitor.Worker
import io.dico.parcels2.blockvisitor.WorktimeLimiter
import io.dico.parcels2.options.DefaultGeneratorOptions
import io.dico.parcels2.util.*
import org.bukkit.*
import org.bukkit.block.Biome
import org.bukkit.block.Block
import org.bukkit.block.BlockFace
import org.bukkit.block.Skull
import org.bukkit.block.data.BlockData
@@ -18,11 +17,13 @@ import java.util.Random
private val airType = Bukkit.createBlockData(Material.AIR)
class DefaultParcelGenerator(val name: String, private val o: DefaultGeneratorOptions) : ParcelGenerator() {
private const val chunkSize = 16
class DefaultParcelGenerator(override val worldName: String, private val o: DefaultGeneratorOptions) : ParcelGenerator() {
private var _world: World? = null
override val world: World
get() {
if (_world == null) _world = Bukkit.getWorld(name)!!.also {
if (_world == null) _world = Bukkit.getWorld(worldName)!!.also {
maxHeight = it.maxHeight
return it
}
@@ -103,12 +104,10 @@ class DefaultParcelGenerator(val name: String, private val o: DefaultGeneratorOp
return Location(world, o.offsetX + fix, o.floorHeight + 1.0, o.offsetZ + fix)
}
override fun makeParcelBlockManager(worktimeLimiter: WorktimeLimiter): ParcelBlockManager {
return ParcelBlockManagerImpl(worktimeLimiter)
}
override fun makeParcelLocator(container: ParcelContainer): ParcelLocator {
return ParcelLocatorImpl(container)
override fun makeParcelLocatorAndBlockManager(worldId: ParcelWorldId,
container: ParcelContainer,
worktimeLimiter: WorktimeLimiter): Pair<ParcelLocator, ParcelBlockManager> {
return ParcelLocatorImpl(worldId, container) to ParcelBlockManagerImpl(worldId, worktimeLimiter)
}
private inline fun <T> convertBlockLocationToId(x: Int, z: Int, mapper: (Int, Int) -> T): T? {
@@ -124,22 +123,26 @@ class DefaultParcelGenerator(val name: String, private val o: DefaultGeneratorOp
return null
}
private inner class ParcelLocatorImpl(val container: ParcelContainer) : ParcelLocator {
private inner class ParcelLocatorImpl(val worldId: ParcelWorldId,
val container: ParcelContainer) : ParcelLocator {
override val world: World = this@DefaultParcelGenerator.world
override fun getParcelAt(x: Int, z: Int): Parcel? {
return convertBlockLocationToId(x, z, container::getParcelById)
}
override fun getParcelIdAt(x: Int, z: Int): ParcelId? {
return convertBlockLocationToId(x, z) { idx, idz -> ParcelId(world.name, world.uid, idx, idz) }
return convertBlockLocationToId(x, z) { idx, idz -> ParcelId(worldId, idx, idz) }
}
}
@Suppress("DEPRECATION")
private inner class ParcelBlockManagerImpl(override val worktimeLimiter: WorktimeLimiter) : ParcelBlockManager {
private inner class ParcelBlockManagerImpl(val worldId: ParcelWorldId,
override val worktimeLimiter: WorktimeLimiter) : ParcelBlockManagerBase() {
override val world: World = this@DefaultParcelGenerator.world
override val parcelTraverser: RegionTraverser = RegionTraverser.upToAndDownUntil(o.floorHeight)
override fun getBottomBlock(parcel: ParcelId): Vec2i = Vec2i(
/*override*/ fun getBottomBlock(parcel: ParcelId): Vec2i = Vec2i(
sectionSize * (parcel.x - 1) + pathOffset + o.offsetX,
sectionSize * (parcel.z - 1) + pathOffset + o.offsetZ
)
@@ -151,6 +154,11 @@ class DefaultParcelGenerator(val name: String, private val o: DefaultGeneratorOp
return Location(world, x + 0.5, o.floorHeight + 1.0, z + 0.5, 0F, 0F)
}
override fun getRegion(parcel: ParcelId): Region {
val bottom = getBottomBlock(parcel)
return Region(Vec3i(bottom.x, 0, bottom.z), Vec3i(o.parcelSize, maxHeight + 1, o.parcelSize))
}
override fun setOwnerBlock(parcel: ParcelId, owner: PlayerProfile?) {
val b = getBottomBlock(parcel)
@@ -203,9 +211,8 @@ class DefaultParcelGenerator(val name: String, private val o: DefaultGeneratorOp
}
override fun clearParcel(parcel: ParcelId): Worker = worktimeLimiter.submit {
val bottom = getBottomBlock(parcel)
val region = Region(Vec3i(bottom.x, 0, bottom.z), Vec3i(o.parcelSize, maxHeight + 1, o.parcelSize))
val blocks = RegionTraversal.DOWNWARD.regionTraverser(region)
val region = getRegion(parcel)
val blocks = parcelTraverser.traverseRegion(region)
val blockCount = region.blockCount.toDouble()
val world = world
@@ -227,17 +234,78 @@ class DefaultParcelGenerator(val name: String, private val o: DefaultGeneratorOp
}
}
override fun doBlockOperation(parcel: ParcelId, direction: RegionTraversal, operation: (Block) -> Unit): Worker = worktimeLimiter.submit {
val bottom = getBottomBlock(parcel)
val region = Region(Vec3i(bottom.x, 0, bottom.z), Vec3i(o.parcelSize, maxHeight + 1, o.parcelSize))
val blocks = direction.regionTraverser(region)
val blockCount = region.blockCount.toDouble()
val world = world
override fun getParcelsWithOwnerBlockIn(chunk: Chunk): Collection<Vec2i> {
/*
* Get the offsets for the world out of the way
* to simplify the calculation that follows.
*/
for ((index, vec) in blocks.withIndex()) {
markSuspensionPoint()
operation(world[vec])
setProgress((index + 1) / blockCount)
val x = chunk.x.shl(4) - (o.offsetX + pathOffset)
val z = chunk.z.shl(4) - (o.offsetZ + pathOffset)
/* Locations of wall corners (where owner blocks are placed) are defined as:
*
* x umod sectionSize == sectionSize-1
*
* This check needs to be made for all 16 slices of the chunk in 2 dimensions
* How to optimize this?
* Let's take the expression
*
* x umod sectionSize
*
* And call it modX
* x can be shifted (chunkSize -1) times to attempt to get a modX of 0.
* This means that if the modX is 1, and sectionSize == (chunkSize-1), there would be a match at the last shift.
* To check that there are any matches, we can see if the following holds:
*
* modX >= ((sectionSize-1) - (chunkSize-1))
*
* Which can be simplified to:
* modX >= sectionSize - chunkSize
*
* if sectionSize == chunkSize, this expression can be simplified to
* modX >= 0
* which is always true. This is expected.
* To get the total number of matches on a dimension, we can evaluate the following:
*
* (modX - (sectionSize - chunkSize) + sectionSize) / sectionSize
*
* We add sectionSize to the lhs because, if the other part of the lhs is 0, we need at least 1.
* This can be simplified to:
*
* (modX + chunkSize) / sectionSize
*/
val sectionSize = sectionSize
val modX = x umod sectionSize
val matchesOnDimensionX = (modX + chunkSize) / sectionSize
if (matchesOnDimensionX <= 0) return emptyList()
val modZ = z umod sectionSize
val matchesOnDimensionZ = (modZ + chunkSize) / sectionSize
if (matchesOnDimensionZ <= 0) return emptyList()
/*
* Now we need to find the first id within the matches,
* and then return the subsequent matches in a rectangle following it.
*
* On each dimension, get the distance to the first match, which is equal to (sectionSize-1 - modX)
* and add it to the coordinate value
*/
val firstX = x + (sectionSize - 1 - modX)
val firstZ = z + (sectionSize - 1 - modZ)
val firstIdX = (firstX + 1) / sectionSize + 1
val firstIdZ = (firstZ + 1) / sectionSize + 1
if (matchesOnDimensionX == 1 && matchesOnDimensionZ == 1) {
// fast-path optimization
return listOf(Vec2i(firstIdX, firstIdZ))
}
return (0 until matchesOnDimensionX).flatMap { idOffsetX ->
(0 until matchesOnDimensionZ).map { idOffsetZ -> Vec2i(firstIdX + idOffsetX, firstIdZ + idOffsetZ) }
}
}

View File

@@ -47,13 +47,23 @@ class ParcelImpl(override val world: ParcelWorld,
val globalAddedMap: AddedDataMap? get() = owner?.let { world.globalAddedData[it].addedMap }
override val since: DateTime? get() = data.since
override val lastClaimTime: DateTime? get() = data.lastClaimTime
override var ownerSignOutdated: Boolean
get() = data.ownerSignOutdated
set(value) {
if (data.ownerSignOutdated != value) {
world.storage.setParcelOwnerSignOutdated(this, value)
data.ownerSignOutdated = value
}
}
override var owner: PlayerProfile?
get() = data.owner
set(value) {
if (data.owner != value) {
world.storage.setParcelOwner(this, value)
world.blockManager.setOwnerBlock(this, value)
data.owner = value
}
}

View File

@@ -1,8 +1,11 @@
package io.dico.parcels2.defaultimpl
import io.dico.parcels2.*
import kotlinx.coroutines.experimental.Unconfined
import kotlinx.coroutines.experimental.launch
import org.bukkit.Bukkit
import org.bukkit.WorldCreator
import org.joda.time.DateTime
class ParcelProviderImpl(val plugin: ParcelsPlugin) : ParcelProvider {
inline val options get() = plugin.options
@@ -49,9 +52,24 @@ class ParcelProviderImpl(val plugin: ParcelsPlugin) : ParcelProvider {
if (parcelWorld != null) continue
val generator: ParcelGenerator = getWorldGenerator(worldName)!!
val bukkitWorld = Bukkit.getWorld(worldName) ?: WorldCreator(worldName).generator(generator).createWorld()
val worldExists = Bukkit.getWorld(worldName) == null
val bukkitWorld =
if (worldExists) Bukkit.getWorld(worldName)!!
else WorldCreator(worldName).generator(generator).createWorld().also { logger.info("Creating world $worldName") }
parcelWorld = ParcelWorldImpl(bukkitWorld, generator, worldOptions.runtime, plugin.storage,
plugin.globalAddedData, ::DefaultParcelContainer, plugin.worktimeLimiter)
if (!worldExists) {
val time = DateTime.now()
plugin.storage.setWorldCreationTime(parcelWorld.id, time)
parcelWorld.creationTime = time
} else {
launch(context = Unconfined) {
parcelWorld.creationTime = plugin.storage.getWorldCreationTime(parcelWorld.id).await() ?: DateTime.now()
}
}
_worlds[worldName] = parcelWorld
}

View File

@@ -7,21 +7,21 @@ import io.dico.parcels2.blockvisitor.WorktimeLimiter
import io.dico.parcels2.options.RuntimeWorldOptions
import io.dico.parcels2.storage.Storage
import org.bukkit.World
import org.joda.time.DateTime
import java.util.UUID
class ParcelWorldImpl private
constructor(override val world: World,
override val generator: ParcelGenerator,
override var options: RuntimeWorldOptions,
override val storage: Storage,
override val globalAddedData: GlobalAddedDataManager,
containerFactory: ParcelContainerFactory,
blockManager: ParcelBlockManager)
class ParcelWorldImpl(override val world: World,
override val generator: ParcelGenerator,
override var options: RuntimeWorldOptions,
override val storage: Storage,
override val globalAddedData: GlobalAddedDataManager,
containerFactory: ParcelContainerFactory,
worktimeLimiter: WorktimeLimiter)
: ParcelWorld,
ParcelWorldId,
ParcelContainer, // missing delegation
ParcelLocator, // missing delegation
ParcelBlockManager by blockManager {
ParcelContainer, /* missing delegation */
ParcelLocator /* missing delegation */ {
override val id: ParcelWorldId get() = this
override val uid: UUID? get() = world.uid
@@ -33,10 +33,14 @@ constructor(override val world: World,
override val name: String = world.name!!
override val container: ParcelContainer = containerFactory(this)
override val locator: ParcelLocator = generator.makeParcelLocator(container)
override val blockManager: ParcelBlockManager = blockManager
override val locator: ParcelLocator
override val blockManager: ParcelBlockManager
init {
val pair = generator.makeParcelLocatorAndBlockManager(id, container, worktimeLimiter)
locator = pair.first
blockManager = pair.second
enforceOptions()
}
@@ -55,24 +59,13 @@ constructor(override val world: World,
world.setGameRuleValue("doTileDrops", "${options.doTileDrops}")
}
// Updated by ParcelProviderImpl
override var creationTime: DateTime? = null
/*
Interface delegation needs to be implemented manually because JetBrains has yet to fix it.
*/
companion object {
// Use this to be able to delegate blockManager and assign it to a property too, at least.
operator fun invoke(world: World,
generator: ParcelGenerator,
options: RuntimeWorldOptions,
storage: Storage,
globalAddedData: GlobalAddedDataManager,
containerFactory: ParcelContainerFactory,
worktimeLimiter: WorktimeLimiter): ParcelWorldImpl {
val blockManager = generator.makeParcelBlockManager(worktimeLimiter)
return ParcelWorldImpl(world, generator, options, storage, globalAddedData, containerFactory, blockManager)
}
}
// ParcelLocator interface
override fun getParcelAt(x: Int, z: Int): Parcel? {
return locator.getParcelAt(x, z)

View File

@@ -7,6 +7,7 @@ import io.dico.parcels2.Parcel
import io.dico.parcels2.ParcelProvider
import io.dico.parcels2.ParcelWorld
import io.dico.parcels2.statusKey
import io.dico.parcels2.storage.Storage
import io.dico.parcels2.util.*
import org.bukkit.Material.*
import org.bukkit.World
@@ -16,6 +17,7 @@ import org.bukkit.block.data.Directional
import org.bukkit.block.data.type.Bed
import org.bukkit.entity.*
import org.bukkit.entity.minecart.ExplosiveMinecart
import org.bukkit.event.EventPriority
import org.bukkit.event.EventPriority.NORMAL
import org.bukkit.event.block.*
import org.bukkit.event.entity.*
@@ -26,11 +28,14 @@ import org.bukkit.event.inventory.InventoryInteractEvent
import org.bukkit.event.player.*
import org.bukkit.event.vehicle.VehicleMoveEvent
import org.bukkit.event.weather.WeatherChangeEvent
import org.bukkit.event.world.ChunkLoadEvent
import org.bukkit.event.world.StructureGrowEvent
import org.bukkit.inventory.InventoryHolder
@Suppress("NOTHING_TO_INLINE")
class ParcelListeners(val parcelProvider: ParcelProvider, val entityTracker: ParcelEntityTracker) {
class ParcelListeners(val parcelProvider: ParcelProvider,
val entityTracker: ParcelEntityTracker,
val storage: Storage) {
private inline fun Parcel?.canBuildN(user: Player) = isPresentAnd { canBuild(user) } || user.hasBuildAnywhere
/**
@@ -54,7 +59,7 @@ class ParcelListeners(val parcelProvider: ParcelProvider, val entityTracker: Par
val parcel = parcelProvider.getParcelAt(event.to) ?: return@l
if (parcel.isBanned(user.statusKey)) {
parcelProvider.getParcelAt(event.from)?.also {
user.teleport(it.world.getHomeLocation(it.id))
user.teleport(it.homeLocation)
user.sendParcelMessage(nopermit = true, message = "You are banned from this parcel")
} ?: run { event.to = event.from }
}
@@ -575,4 +580,26 @@ class ParcelListeners(val parcelProvider: ParcelProvider, val entityTracker: Par
}
}
/**
* Updates owner signs of parcels that get loaded if it is marked outdated
*/
@ListenerMarker(priority = EventPriority.NORMAL)
val onChunkLoadEvent = RegistratorListener<ChunkLoadEvent> l@{ event ->
val world = parcelProvider.getWorld(event.chunk.world) ?: return@l
val parcels = world.blockManager.getParcelsWithOwnerBlockIn(event.chunk)
if (parcels.isEmpty()) return@l
parcels.forEach { id ->
val parcel = world.getParcelById(id)?.takeIf { it.ownerSignOutdated } ?: return@forEach
world.blockManager.setOwnerBlock(parcel.id, parcel.owner)
parcel.ownerSignOutdated = false
}
}
@ListenerMarker
val onPlayerJoinEvent = RegistratorListener<PlayerJoinEvent> l@{ event ->
storage.updatePlayerName(event.player.uuid, event.player.name)
}
}

View File

@@ -6,6 +6,7 @@ import kotlinx.coroutines.experimental.Deferred
import kotlinx.coroutines.experimental.Job
import kotlinx.coroutines.experimental.channels.ReceiveChannel
import kotlinx.coroutines.experimental.channels.SendChannel
import org.joda.time.DateTime
import java.util.UUID
interface Backing {
@@ -30,8 +31,14 @@ interface Backing {
fun shutdown()
fun getWorldCreationTime(worldId: ParcelWorldId): DateTime?
fun setWorldCreationTime(worldId: ParcelWorldId, time: DateTime)
fun getPlayerUuidForName(name: String): UUID?
fun updatePlayerName(uuid: UUID, name: String)
fun transmitParcelData(channel: SendChannel<DataPair>, parcels: Sequence<ParcelId>)
fun transmitAllParcelData(channel: SendChannel<DataPair>)
@@ -47,6 +54,8 @@ interface Backing {
fun setParcelOwner(parcel: ParcelId, owner: PlayerProfile?)
fun setParcelOwnerSignOutdated(parcel: ParcelId, outdated: Boolean)
fun setLocalPlayerStatus(parcel: ParcelId, player: PlayerProfile, status: AddedStatus)
fun setParcelAllowsInteractInventory(parcel: ParcelId, value: Boolean)

View File

@@ -8,6 +8,7 @@ import kotlinx.coroutines.experimental.Job
import kotlinx.coroutines.experimental.channels.ReceiveChannel
import kotlinx.coroutines.experimental.channels.SendChannel
import kotlinx.coroutines.experimental.launch
import org.joda.time.DateTime
import java.util.UUID
typealias DataPair = Pair<ParcelId, ParcelData?>
@@ -22,8 +23,14 @@ interface Storage {
fun shutdown(): Job
fun getWorldCreationTime(worldId: ParcelWorldId): Deferred<DateTime?>
fun setWorldCreationTime(worldId: ParcelWorldId, time: DateTime): Job
fun getPlayerUuidForName(name: String): Deferred<UUID?>
fun updatePlayerName(uuid: UUID, name: String): Job
fun readParcelData(parcel: ParcelId): Deferred<ParcelData?>
fun transmitParcelData(parcels: Sequence<ParcelId>): ReceiveChannel<DataPair>
@@ -39,6 +46,8 @@ interface Storage {
fun setParcelOwner(parcel: ParcelId, owner: PlayerProfile?): Job
fun setParcelOwnerSignOutdated(parcel: ParcelId, outdated: Boolean): Job
fun setParcelPlayerStatus(parcel: ParcelId, player: PlayerProfile, status: AddedStatus): Job
fun setParcelAllowsInteractInventory(parcel: ParcelId, value: Boolean): Job
@@ -65,8 +74,14 @@ class BackedStorage internal constructor(val b: Backing) : Storage {
override fun shutdown() = launch(b.dispatcher) { b.shutdown() }
override fun getWorldCreationTime(worldId: ParcelWorldId): Deferred<DateTime?> = b.launchFuture { b.getWorldCreationTime(worldId) }
override fun setWorldCreationTime(worldId: ParcelWorldId, time: DateTime): Job = b.launchJob { b.setWorldCreationTime(worldId, time) }
override fun getPlayerUuidForName(name: String): Deferred<UUID?> = b.launchFuture { b.getPlayerUuidForName(name) }
override fun updatePlayerName(uuid: UUID, name: String): Job = b.launchJob { b.updatePlayerName(uuid, name) }
override fun readParcelData(parcel: ParcelId) = b.launchFuture { b.readParcelData(parcel) }
override fun transmitParcelData(parcels: Sequence<ParcelId>) = b.openChannel<DataPair> { b.transmitParcelData(it, parcels) }
@@ -81,6 +96,8 @@ class BackedStorage internal constructor(val b: Backing) : Storage {
override fun setParcelOwner(parcel: ParcelId, owner: PlayerProfile?) = b.launchJob { b.setParcelOwner(parcel, owner) }
override fun setParcelOwnerSignOutdated(parcel: ParcelId, outdated: Boolean): Job = b.launchJob { b.setParcelOwnerSignOutdated(parcel, outdated) }
override fun setParcelPlayerStatus(parcel: ParcelId, player: PlayerProfile, status: AddedStatus) = b.launchJob { b.setLocalPlayerStatus(parcel, player, status) }
override fun setParcelAllowsInteractInventory(parcel: ParcelId, value: Boolean) = b.launchJob { b.setParcelAllowsInteractInventory(parcel, value) }

View File

@@ -4,10 +4,12 @@ package io.dico.parcels2.storage.exposed
import com.zaxxer.hikari.HikariDataSource
import io.dico.parcels2.*
import io.dico.parcels2.PlayerProfile.Star.name
import io.dico.parcels2.storage.AddedDataPair
import io.dico.parcels2.storage.Backing
import io.dico.parcels2.storage.DataPair
import io.dico.parcels2.util.synchronized
import io.dico.parcels2.util.toByteArray
import io.dico.parcels2.util.toUUID
import kotlinx.coroutines.experimental.*
import kotlinx.coroutines.experimental.channels.ArrayChannel
@@ -114,11 +116,28 @@ class ExposedBacking(private val dataSourceFactory: () -> DataSource, val poolSi
else -> throw InternalError("Case should not be reached")
}
override fun getWorldCreationTime(worldId: ParcelWorldId): DateTime? {
return WorldsT.getWorldCreationTime(worldId)
}
override fun setWorldCreationTime(worldId: ParcelWorldId, time: DateTime) {
WorldsT.setWorldCreationTime(worldId, time)
}
override fun getPlayerUuidForName(name: String): UUID? {
return ProfilesT.slice(ProfilesT.uuid).select { ProfilesT.name.upperCase() eq name.toUpperCase() }
.firstOrNull()?.let { it[ProfilesT.uuid]?.toUUID() }
}
override fun updatePlayerName(uuid: UUID, name: String) {
val binaryUuid = uuid.toByteArray()
ProfilesT.upsert(ProfilesT.uuid) {
it[ProfilesT.uuid] = binaryUuid
it[ProfilesT.name] = name
}
}
override fun transmitParcelData(channel: SendChannel<DataPair>, parcels: Sequence<ParcelId>) {
for (parcel in parcels) {
val data = readParcelData(parcel)
@@ -193,6 +212,14 @@ class ExposedBacking(private val dataSourceFactory: () -> DataSource, val poolSi
ParcelsT.update({ ParcelsT.id eq id }) {
it[ParcelsT.owner_id] = owner_id
it[claim_time] = time
it[sign_oudated] = false
}
}
override fun setParcelOwnerSignOutdated(parcel: ParcelId, outdated: Boolean) {
val id = ParcelsT.getId(parcel) ?: return
ParcelsT.update({ ParcelsT.id eq id }) {
it[sign_oudated] = outdated
}
}
@@ -203,16 +230,16 @@ class ExposedBacking(private val dataSourceFactory: () -> DataSource, val poolSi
override fun setParcelAllowsInteractInventory(parcel: ParcelId, value: Boolean) {
val id = ParcelsT.getOrInitId(parcel)
ParcelOptionsT.upsert(ParcelOptionsT.parcel_id) {
it[ParcelOptionsT.parcel_id] = id
it[ParcelOptionsT.interact_inventory] = value
it[parcel_id] = id
it[interact_inventory] = value
}
}
override fun setParcelAllowsInteractInputs(parcel: ParcelId, value: Boolean) {
val id = ParcelsT.getOrInitId(parcel)
ParcelOptionsT.upsert(ParcelOptionsT.parcel_id) {
it[ParcelOptionsT.parcel_id] = id
it[ParcelOptionsT.interact_inputs] = value
it[parcel_id] = id
it[interact_inputs] = value
}
}
@@ -231,7 +258,8 @@ class ExposedBacking(private val dataSourceFactory: () -> DataSource, val poolSi
private fun rowToParcelData(row: ResultRow) = ParcelDataHolder().apply {
owner = row[ParcelsT.owner_id]?.let { ProfilesT.getItem(it) }
since = row[ParcelsT.claim_time]
lastClaimTime = row[ParcelsT.claim_time]
ownerSignOutdated = row[ParcelsT.sign_oudated]
val id = row[ParcelsT.id]
ParcelOptionsT.select { ParcelOptionsT.parcel_id eq id }.firstOrNull()?.let { optrow ->

View File

@@ -9,6 +9,7 @@ import io.dico.parcels2.util.toByteArray
import io.dico.parcels2.util.toUUID
import org.jetbrains.exposed.sql.*
import org.jetbrains.exposed.sql.statements.UpdateBuilder
import org.joda.time.DateTime
import java.util.UUID
sealed class IdTransactionsTable<TableT : IdTransactionsTable<TableT, QueryObj>, QueryObj>(tableName: String, columnName: String)
@@ -24,7 +25,8 @@ sealed class IdTransactionsTable<TableT : IdTransactionsTable<TableT, QueryObj>,
}
internal inline fun getOrInitId(getId: () -> Int?, noinline body: TableT.(UpdateBuilder<*>) -> Unit, objName: () -> String): Int {
return getId() ?: table.insertIgnore(body)[id] ?: getId() ?: throw ExposedDatabaseException("This should not happen - failed to insert ${objName()} and get its id")
return getId() ?: table.insertIgnore(body)[id] ?: getId()
?: throw ExposedDatabaseException("This should not happen - failed to insert ${objName()} and get its id")
}
abstract fun getId(obj: QueryObj): Int?
@@ -35,9 +37,10 @@ sealed class IdTransactionsTable<TableT : IdTransactionsTable<TableT, QueryObj>,
fun getId(obj: QueryObj, init: Boolean): Int? = if (init) getOrInitId(obj) else getId(obj)
}
object WorldsT : IdTransactionsTable<WorldsT, ParcelWorldId>("parcel_worlds", "world_id") {
object WorldsT : IdTransactionsTable<WorldsT, ParcelWorldId>("parcels_worlds", "world_id") {
val name = varchar("name", 50)
val uid = binary("uid", 16).nullable()
val creation_time = datetime("creation_time").nullable()
val index_name = uniqueIndexR("index_name", name)
val index_uid = uniqueIndexR("index_uid", uid)
@@ -56,6 +59,18 @@ object WorldsT : IdTransactionsTable<WorldsT, ParcelWorldId>("parcel_worlds", "w
override fun getItem(row: ResultRow): ParcelWorldId {
return ParcelWorldId(row[name], row[uid]?.toUUID())
}
fun getWorldCreationTime(worldId: ParcelWorldId): DateTime? {
val id = getId(worldId) ?: return null
return select { WorldsT.id eq id }.firstOrNull()?.let { it[WorldsT.creation_time] }
}
fun setWorldCreationTime(worldId: ParcelWorldId, time: DateTime) {
val id = getOrInitId(worldId)
update({ WorldsT.id eq id }) {
it[WorldsT.creation_time] = time
}
}
}
object ParcelsT : IdTransactionsTable<ParcelsT, ParcelId>("parcels", "parcel_id") {
@@ -63,6 +78,7 @@ object ParcelsT : IdTransactionsTable<ParcelsT, ParcelId>("parcels", "parcel_id"
val px = integer("px")
val pz = integer("pz")
val owner_id = integer("owner_id").references(ProfilesT.id).nullable()
val sign_oudated = bool("sign_outdated").default(false)
val claim_time = datetime("claim_time").nullable()
val index_location = uniqueIndexR("index_location", world_id, px, pz)
@@ -89,7 +105,7 @@ object ParcelsT : IdTransactionsTable<ParcelsT, ParcelId>("parcels", "parcel_id"
}
}
object ProfilesT : IdTransactionsTable<ProfilesT, PlayerProfile>("parcel_profiles", "owner_id") {
object ProfilesT : IdTransactionsTable<ProfilesT, PlayerProfile>("parcels_profiles", "owner_id") {
val uuid = binary("uuid", 16).nullable()
val name = varchar("name", 32).nullable()
@@ -103,7 +119,8 @@ object ProfilesT : IdTransactionsTable<ProfilesT, PlayerProfile>("parcel_profile
private inline fun getId(nameIn: String) = getId { uuid.isNull() and (name.lowerCase() eq nameIn.toLowerCase()) }
private inline fun getRealId(nameIn: String) = getId { uuid.isNotNull() and (name.lowerCase() eq nameIn.toLowerCase()) }
private inline fun getOrInitId(uuid: UUID, name: String?) = uuid.toByteArray().let { binaryUuid -> getOrInitId(
private inline fun getOrInitId(uuid: UUID, name: String?) = uuid.toByteArray().let { binaryUuid ->
getOrInitId(
{ getId(binaryUuid) },
{ it[this@ProfilesT.uuid] = binaryUuid; it[this@ProfilesT.name] = name },
{ "profile(uuid = $uuid, name = $name)" })

View File

@@ -83,6 +83,7 @@ class PlotmeMigration(val options: PlotmeMigrationOptions) : Migration {
val parcel = getParcelId(PlotmePlotsT, row) ?: return@forEach
val owner = PlayerProfile.safe(row[PlotmePlotsT.owner_uuid]?.toUUID(), row[PlotmePlotsT.owner_name])
target.setParcelOwner(parcel, owner)
target.setParcelOwnerSignOutdated(parcel, true)
}
}

View File

@@ -2,4 +2,12 @@ package io.dico.parcels2.util
data class Region(val origin: Vec3i, val size: Vec3i) {
val blockCount: Int get() = size.x * size.y * size.z
val center: Vec3d
get() {
val x = (origin.x + size.x) / 2.0
val y = (origin.y + size.y) / 2.0
val z = (origin.z + size.z) / 2.0
return Vec3d(x, y, z)
}
}

View File

@@ -5,3 +5,7 @@ data class Vec2i(
val z: Int
)
data class Region2i(
val bottom: Vec2i,
val top: Vec2i
)

View File

@@ -3,6 +3,22 @@ package io.dico.parcels2.util
import org.bukkit.World
import org.bukkit.block.Block
data class Vec3d(
val x: Double,
val y: Double,
val z: Double
) {
operator fun plus(o: Vec3d) = Vec3d(x + o.x, y + o.y, z + o.z)
operator fun minus(o: Vec3i) = Vec3d(x - o.x, y - o.y, z - o.z)
infix fun addX(o: Double) = Vec3d(x + o, y, z)
infix fun addY(o: Double) = Vec3d(x, y + o, z)
infix fun addZ(o: Double) = Vec3d(x, y, z + o)
infix fun withX(o: Double) = Vec3d(o, y, z)
infix fun withY(o: Double) = Vec3d(x, o, z)
infix fun withZ(o: Double) = Vec3d(x, y, o)
fun add(ox: Double, oy: Double, oz: Double) = Vec3d(x + ox, y + oy, z + oz)
}
data class Vec3i(
val x: Int,
val y: Int,
@@ -12,6 +28,9 @@ data class Vec3i(
infix fun addX(o: Int) = Vec3i(x + o, y, z)
infix fun addY(o: Int) = Vec3i(x, y + o, z)
infix fun addZ(o: Int) = Vec3i(x, y, z + o)
infix fun withX(o: Int) = Vec3i(o, y, z)
infix fun withY(o: Int) = Vec3i(x, o, z)
infix fun withZ(o: Int) = Vec3i(x, y, o)
fun add(ox: Int, oy: Int, oz: Int) = Vec3i(x + ox, y + oy, z + oz)
}

12
todo.md
View File

@@ -17,8 +17,8 @@ Modify home command:
* Make `:` not be required if prior component cannot be parsed to an int
* Listen for command events that use plotme-style argument, and transform the command
Add permissions to commands (replace or fix `IContextFilter` from command lib
to allow inheriting permissions properly).
~~Add permissions to commands (replace or fix `IContextFilter` from command lib
to allow inheriting permissions properly).~~
Parcel Options
-
@@ -39,8 +39,8 @@ This could become optional.
Block Management
-
Update the parcel corner with owner info when a player flies into the parcel (after migrations).
Parcels has a player head in that corner in addition to the sign that PlotMe uses.
~~Update the parcel corner with owner info when a player flies into the parcel (after migrations).
Parcels has a player head in that corner in addition to the sign that PlotMe uses.~~
Commands that modify parcel blocks must be kept track of to prevent multiple
from running simultaneously in the same parcel. `hasBlockVisitors` field must be updated.
@@ -50,9 +50,9 @@ Swap - schematic is in place, but proper placement order must be enforced to mak
blocks are placed properly. Alternatively, if a block change method can be found that doesn't
cause block updates, that would be preferred subject to having good performance.
Change `RegionTraversal` to allow traversing different parts of a region in a different order.
~~Change `RegionTraversal` to allow traversing different parts of a region in a different order.
This could apply to clearing of plots, for example. It would be better if the bottom 64 (floor height)
layers are done upwards, and the rest downwards.
layers are done upwards, and the rest downwards.~~
Events
-