Work on a couple of the todos
This commit is contained in:
@@ -1,5 +1,6 @@
|
|||||||
package io.dico.dicore.command;
|
package io.dico.dicore.command;
|
||||||
|
|
||||||
|
import io.dico.dicore.command.predef.DefaultGroupCommand;
|
||||||
import io.dico.dicore.command.predef.HelpCommand;
|
import io.dico.dicore.command.predef.HelpCommand;
|
||||||
|
|
||||||
import java.util.*;
|
import java.util.*;
|
||||||
@@ -23,7 +24,7 @@ public class ChildCommandAddress extends ModifiableCommandAddress {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public static ChildCommandAddress newPlaceHolderCommand(String name, String... aliases) {
|
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);
|
HelpCommand.registerAsChild(rv);
|
||||||
return rv;
|
return rv;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -210,15 +210,39 @@ public final class CommandBuilder {
|
|||||||
* @param shortDescription a short description
|
* @param shortDescription a short description
|
||||||
* @param description the lines of a full description.
|
* @param description the lines of a full description.
|
||||||
* @return this
|
* @return this
|
||||||
|
* @throws IllegalStateException if the current group has no command
|
||||||
*/
|
*/
|
||||||
public CommandBuilder setGroupDescription(String shortDescription, String... description) {
|
public CommandBuilder setGroupDescription(String shortDescription, String... description) {
|
||||||
Command command = cur.getCommand();
|
Command command = cur.getCommand();
|
||||||
|
if (command == null) throw new IllegalStateException();
|
||||||
cur.setCommand(command
|
cur.setCommand(command
|
||||||
.setShortDescription(shortDescription)
|
.setShortDescription(shortDescription)
|
||||||
.setDescription(description));
|
.setDescription(description));
|
||||||
return this;
|
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
|
* Jump up a level in the address
|
||||||
*
|
*
|
||||||
|
|||||||
@@ -2,6 +2,7 @@ package io.dico.dicore.command;
|
|||||||
|
|
||||||
import io.dico.dicore.command.chat.ChatControllers;
|
import io.dico.dicore.command.chat.ChatControllers;
|
||||||
import io.dico.dicore.command.chat.IChatController;
|
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.HelpCommand;
|
||||||
import io.dico.dicore.command.predef.PredefinedCommand;
|
import io.dico.dicore.command.predef.PredefinedCommand;
|
||||||
|
|
||||||
@@ -32,7 +33,7 @@ public abstract class ModifiableCommandAddress implements ICommandAddress {
|
|||||||
@Override
|
@Override
|
||||||
public boolean hasUserDeclaredCommand() {
|
public boolean hasUserDeclaredCommand() {
|
||||||
Command command = getCommand();
|
Command command = getCommand();
|
||||||
return command != null && !(command instanceof PredefinedCommand);
|
return command != null && !(command instanceof PredefinedCommand) && !(command instanceof DefaultGroupCommand);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
package io.dico.dicore.command;
|
package io.dico.dicore.command;
|
||||||
|
|
||||||
import io.dico.dicore.command.parameter.ArgumentBuffer;
|
import io.dico.dicore.command.parameter.ArgumentBuffer;
|
||||||
|
import io.dico.dicore.command.predef.DefaultGroupCommand;
|
||||||
import io.dico.dicore.command.registration.BukkitCommand;
|
import io.dico.dicore.command.registration.BukkitCommand;
|
||||||
import org.bukkit.Location;
|
import org.bukkit.Location;
|
||||||
import org.bukkit.command.CommandSender;
|
import org.bukkit.command.CommandSender;
|
||||||
@@ -167,10 +168,10 @@ public class RootCommandAddress extends ModifiableCommandAddress implements ICom
|
|||||||
ModifiableCommandAddress targetAddress = getCommandTarget(sender, buffer);
|
ModifiableCommandAddress targetAddress = getCommandTarget(sender, buffer);
|
||||||
Command target = targetAddress.getCommand();
|
Command target = targetAddress.getCommand();
|
||||||
|
|
||||||
if (target == null) {
|
if (target == null || target instanceof DefaultGroupCommand) {
|
||||||
if (targetAddress.hasHelpCommand()) {
|
if (targetAddress.hasHelpCommand()) {
|
||||||
target = targetAddress.getHelpCommand().getCommand();
|
target = targetAddress.getHelpCommand().getCommand();
|
||||||
} else {
|
} else if (target == null){
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
+3
@@ -7,6 +7,9 @@ import java.lang.annotation.Retention;
|
|||||||
import java.lang.annotation.RetentionPolicy;
|
import java.lang.annotation.RetentionPolicy;
|
||||||
import java.lang.annotation.Target;
|
import java.lang.annotation.Target;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* If this annotation is not present, inheriting permissions is default.
|
||||||
|
*/
|
||||||
@Retention(RetentionPolicy.RUNTIME)
|
@Retention(RetentionPolicy.RUNTIME)
|
||||||
@Target(ElementType.METHOD)
|
@Target(ElementType.METHOD)
|
||||||
public @interface RequirePermissions {
|
public @interface RequirePermissions {
|
||||||
|
|||||||
@@ -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;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@@ -2,6 +2,7 @@ package io.dico.parcels2
|
|||||||
|
|
||||||
import io.dico.parcels2.util.Vec2i
|
import io.dico.parcels2.util.Vec2i
|
||||||
import io.dico.parcels2.util.hasBuildAnywhere
|
import io.dico.parcels2.util.hasBuildAnywhere
|
||||||
|
import org.bukkit.Location
|
||||||
import org.bukkit.OfflinePlayer
|
import org.bukkit.OfflinePlayer
|
||||||
import org.bukkit.entity.Player
|
import org.bukkit.entity.Player
|
||||||
import org.joda.time.DateTime
|
import org.joda.time.DateTime
|
||||||
@@ -30,11 +31,14 @@ interface Parcel : ParcelData {
|
|||||||
fun copyData(data: ParcelData)
|
fun copyData(data: ParcelData)
|
||||||
|
|
||||||
fun dispose()
|
fun dispose()
|
||||||
|
|
||||||
|
val homeLocation: Location get() = world.blockManager.getHomeLocation(id)
|
||||||
}
|
}
|
||||||
|
|
||||||
interface ParcelData : AddedData {
|
interface ParcelData : AddedData {
|
||||||
var owner: PlayerProfile?
|
var owner: PlayerProfile?
|
||||||
val since: DateTime?
|
val lastClaimTime: DateTime?
|
||||||
|
var ownerSignOutdated: Boolean
|
||||||
|
|
||||||
fun canBuild(player: OfflinePlayer, checkAdmin: Boolean = true, checkGlobal: Boolean = true): 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 {
|
class ParcelDataHolder(addedMap: MutableAddedDataMap = mutableMapOf()) : AddedDataHolder(addedMap), ParcelData {
|
||||||
|
|
||||||
override var owner: PlayerProfile? = null
|
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)
|
override fun canBuild(player: OfflinePlayer, checkAdmin: Boolean, checkGlobal: Boolean) = isAllowed(player.statusKey)
|
||||||
|| owner.let { it != null && it.matches(player, allowNameMatch = false) }
|
|| owner.let { it != null && it.matches(player, allowNameMatch = false) }
|
||||||
|| (checkAdmin && player is Player && player.hasBuildAnywhere)
|
|| (checkAdmin && player is Player && player.hasBuildAnywhere)
|
||||||
|
|||||||
@@ -1,9 +1,12 @@
|
|||||||
package io.dico.parcels2
|
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.Worker
|
||||||
|
import io.dico.parcels2.blockvisitor.WorkerScope
|
||||||
import io.dico.parcels2.blockvisitor.WorktimeLimiter
|
import io.dico.parcels2.blockvisitor.WorktimeLimiter
|
||||||
|
import io.dico.parcels2.util.Region
|
||||||
import io.dico.parcels2.util.Vec2i
|
import io.dico.parcels2.util.Vec2i
|
||||||
|
import io.dico.parcels2.util.get
|
||||||
import org.bukkit.Chunk
|
import org.bukkit.Chunk
|
||||||
import org.bukkit.Location
|
import org.bukkit.Location
|
||||||
import org.bukkit.World
|
import org.bukkit.World
|
||||||
@@ -15,6 +18,8 @@ import org.bukkit.generator.ChunkGenerator
|
|||||||
import java.util.Random
|
import java.util.Random
|
||||||
|
|
||||||
abstract class ParcelGenerator : ChunkGenerator() {
|
abstract class ParcelGenerator : ChunkGenerator() {
|
||||||
|
abstract val worldName: String
|
||||||
|
|
||||||
abstract val world: World
|
abstract val world: World
|
||||||
|
|
||||||
abstract override fun generateChunkData(world: World?, random: Random?, chunkX: Int, chunkZ: Int, biome: BiomeGrid?): ChunkData
|
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 makeParcelLocatorAndBlockManager(worldId: ParcelWorldId,
|
||||||
|
container: ParcelContainer,
|
||||||
abstract fun makeParcelLocator(container: ParcelContainer): ParcelLocator
|
worktimeLimiter: WorktimeLimiter): Pair<ParcelLocator, ParcelBlockManager>
|
||||||
}
|
}
|
||||||
|
|
||||||
@Suppress("DeprecatedCallableAddReplaceWith")
|
|
||||||
interface ParcelBlockManager {
|
interface ParcelBlockManager {
|
||||||
val world: World
|
val world: World
|
||||||
val worktimeLimiter: WorktimeLimiter
|
val worktimeLimiter: WorktimeLimiter
|
||||||
|
val parcelTraverser: RegionTraverser
|
||||||
|
|
||||||
fun getBottomBlock(parcel: ParcelId): Vec2i
|
// fun getBottomBlock(parcel: ParcelId): Vec2i
|
||||||
|
|
||||||
fun getHomeLocation(parcel: ParcelId): Location
|
fun getHomeLocation(parcel: ParcelId): Location
|
||||||
|
|
||||||
|
fun getRegion(parcel: ParcelId): Region
|
||||||
|
|
||||||
|
fun getEntities(parcel: ParcelId): Collection<Entity>
|
||||||
|
|
||||||
fun setOwnerBlock(parcel: ParcelId, owner: PlayerProfile?)
|
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 setBiome(parcel: ParcelId, biome: Biome): Worker
|
||||||
|
|
||||||
fun clearParcel(parcel: ParcelId): 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)
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -4,10 +4,12 @@ import io.dico.parcels2.options.RuntimeWorldOptions
|
|||||||
import io.dico.parcels2.storage.Storage
|
import io.dico.parcels2.storage.Storage
|
||||||
import io.dico.parcels2.util.Vec2i
|
import io.dico.parcels2.util.Vec2i
|
||||||
import io.dico.parcels2.util.floor
|
import io.dico.parcels2.util.floor
|
||||||
|
import org.bukkit.Chunk
|
||||||
import org.bukkit.Location
|
import org.bukkit.Location
|
||||||
import org.bukkit.World
|
import org.bukkit.World
|
||||||
import org.bukkit.block.Block
|
import org.bukkit.block.Block
|
||||||
import org.bukkit.entity.Entity
|
import org.bukkit.entity.Entity
|
||||||
|
import org.joda.time.DateTime
|
||||||
import java.util.UUID
|
import java.util.UUID
|
||||||
|
|
||||||
interface ParcelProvider {
|
interface ParcelProvider {
|
||||||
@@ -58,7 +60,6 @@ interface ParcelLocator {
|
|||||||
fun getParcelAt(entity: Entity): Parcel? = getParcelAt(entity.location).takeIf { entity.world == world }
|
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 }
|
fun getParcelAt(block: Block): Parcel? = getParcelAt(block.x, block.z).takeIf { block.world == world }
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
typealias ParcelContainerFactory = (ParcelWorld) -> ParcelContainer
|
typealias ParcelContainerFactory = (ParcelWorld) -> ParcelContainer
|
||||||
@@ -73,7 +74,7 @@ interface ParcelContainer {
|
|||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
interface ParcelWorld : ParcelLocator, ParcelContainer, ParcelBlockManager {
|
interface ParcelWorld : ParcelLocator, ParcelContainer {
|
||||||
val id: ParcelWorldId
|
val id: ParcelWorldId
|
||||||
val name: String
|
val name: String
|
||||||
val uid: UUID?
|
val uid: UUID?
|
||||||
@@ -84,4 +85,7 @@ interface ParcelWorld : ParcelLocator, ParcelContainer, ParcelBlockManager {
|
|||||||
val locator: ParcelLocator
|
val locator: ParcelLocator
|
||||||
val blockManager: ParcelBlockManager
|
val blockManager: ParcelBlockManager
|
||||||
val globalAddedData: GlobalAddedDataManager
|
val globalAddedData: GlobalAddedDataManager
|
||||||
|
|
||||||
|
val creationTime: DateTime?
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -21,6 +21,7 @@ import org.bukkit.plugin.java.JavaPlugin
|
|||||||
import org.slf4j.Logger
|
import org.slf4j.Logger
|
||||||
import org.slf4j.LoggerFactory
|
import org.slf4j.LoggerFactory
|
||||||
import java.io.File
|
import java.io.File
|
||||||
|
import java.util.Random
|
||||||
|
|
||||||
val logger: Logger = LoggerFactory.getLogger("ParcelsPlugin")
|
val logger: Logger = LoggerFactory.getLogger("ParcelsPlugin")
|
||||||
private inline val plogger get() = logger
|
private inline val plogger get() = logger
|
||||||
@@ -49,7 +50,15 @@ class ParcelsPlugin : JavaPlugin() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
override fun onDisable() {
|
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()
|
worktimeLimiter.completeAllTasks()
|
||||||
|
if (hasWorkers) {
|
||||||
|
plogger.info("Parcels has completed the remaining jobs.")
|
||||||
|
}
|
||||||
|
|
||||||
cmdDispatcher?.unregisterFromCommandMap()
|
cmdDispatcher?.unregisterFromCommandMap()
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -124,7 +133,7 @@ class ParcelsPlugin : JavaPlugin() {
|
|||||||
|
|
||||||
private fun registerListeners() {
|
private fun registerListeners() {
|
||||||
if (listeners == null) {
|
if (listeners == null) {
|
||||||
listeners = ParcelListeners(parcelProvider, entityTracker)
|
listeners = ParcelListeners(parcelProvider, entityTracker, storage)
|
||||||
registrator.registerListeners(listeners!!)
|
registrator.registerListeners(listeners!!)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -77,7 +77,8 @@ interface PlayerProfile {
|
|||||||
interface Real : PlayerProfile {
|
interface Real : PlayerProfile {
|
||||||
override val uuid: UUID
|
override val uuid: UUID
|
||||||
override val nameOrBukkitName: String?
|
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
|
override val notNullName: String
|
||||||
get() = name ?: getPlayerNameOrDefault(uuid)
|
get() = name ?: getPlayerNameOrDefault(uuid)
|
||||||
|
|
||||||
|
|||||||
@@ -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) } }
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
@@ -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) }
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
@@ -23,7 +23,7 @@ class Schematic {
|
|||||||
val size = region.size.also { _size = it }
|
val size = region.size.also { _size = it }
|
||||||
val data = arrayOfNulls<BlockData>(region.blockCount).also { _data = it }
|
val data = arrayOfNulls<BlockData>(region.blockCount).also { _data = it }
|
||||||
//val extra = mutableMapOf<Vec3i, (Block) -> Unit>().also { extra = 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()) {
|
for ((index, vec) in blocks.withIndex()) {
|
||||||
markSuspensionPoint()
|
markSuspensionPoint()
|
||||||
@@ -39,7 +39,7 @@ class Schematic {
|
|||||||
fun getPasteTask(world: World, position: Vec3i): TimeLimitedTask = {
|
fun getPasteTask(world: World, position: Vec3i): TimeLimitedTask = {
|
||||||
if (!isLoaded) throw IllegalStateException()
|
if (!isLoaded) throw IllegalStateException()
|
||||||
val region = Region(position, _size!!)
|
val region = Region(position, _size!!)
|
||||||
val blocks = RegionTraversal.DOWNWARD.regionTraverser(region)
|
val blocks = RegionTraverser.downward.traverseRegion(region)
|
||||||
val data = _data!!
|
val data = _data!!
|
||||||
|
|
||||||
for ((index, vec) in blocks.withIndex()) {
|
for ((index, vec) in blocks.withIndex()) {
|
||||||
|
|||||||
@@ -122,8 +122,14 @@ class TickWorktimeLimiter(private val plugin: ParcelsPlugin, var options: TickWo
|
|||||||
|
|
||||||
override fun submit(task: TimeLimitedTask): Worker {
|
override fun submit(task: TimeLimitedTask): Worker {
|
||||||
val worker: WorkerContinuation = WorkerImpl(plugin.functionHelper, task)
|
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)
|
_workers.addFirst(worker)
|
||||||
if (bukkitTask == null) bukkitTask = plugin.functionHelper.scheduleRepeating(0, options.tickInterval) { tickJobs() }
|
|
||||||
return worker
|
return worker
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -5,7 +5,8 @@ import io.dico.dicore.command.EMessageType
|
|||||||
import io.dico.dicore.command.ExecutionContext
|
import io.dico.dicore.command.ExecutionContext
|
||||||
import io.dico.dicore.command.annotation.Cmd
|
import io.dico.dicore.command.annotation.Cmd
|
||||||
import io.dico.parcels2.ParcelsPlugin
|
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.Bukkit
|
||||||
import org.bukkit.Material
|
import org.bukkit.Material
|
||||||
import org.bukkit.entity.Player
|
import org.bukkit.entity.Player
|
||||||
@@ -43,7 +44,7 @@ class CommandsDebug(plugin: ParcelsPlugin) : AbstractParcelCommands(plugin) {
|
|||||||
)
|
)
|
||||||
val random = Random()
|
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)]
|
block.blockData = blockDatas[random.nextInt(7)]
|
||||||
}.onProgressUpdate(1000, 1000) { progress, elapsedTime ->
|
}.onProgressUpdate(1000, 1000) { progress, elapsedTime ->
|
||||||
context.sendMessage(EMessageType.INFORMATIVE, "Mess progress: %.02f%%, %.2fs elapsed"
|
context.sendMessage(EMessageType.INFORMATIVE, "Mess progress: %.02f%%, %.2fs elapsed"
|
||||||
|
|||||||
@@ -26,7 +26,7 @@ class CommandsGeneral(plugin: ParcelsPlugin) : AbstractParcelCommands(plugin) {
|
|||||||
val parcel = world.nextEmptyParcel()
|
val parcel = world.nextEmptyParcel()
|
||||||
?: error("This world is full, please ask an admin to upsize it")
|
?: error("This world is full, please ask an admin to upsize it")
|
||||||
parcel.owner = PlayerProfile(uuid = player.uuid)
|
parcel.owner = PlayerProfile(uuid = player.uuid)
|
||||||
player.teleport(parcel.world.getHomeLocation(parcel.id))
|
player.teleport(parcel.homeLocation)
|
||||||
return "Enjoy your new parcel!"
|
return "Enjoy your new parcel!"
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -63,7 +63,7 @@ class CommandsGeneral(plugin: ParcelsPlugin) : AbstractParcelCommands(plugin) {
|
|||||||
|
|
||||||
val match = target.getParcelSuspend(plugin.storage)
|
val match = target.getParcelSuspend(plugin.storage)
|
||||||
?: error("The specified parcel could not be matched")
|
?: error("The specified parcel could not be matched")
|
||||||
player.teleport(match.world.getHomeLocation(match.id))
|
player.teleport(match.homeLocation)
|
||||||
return ""
|
return ""
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -100,7 +100,7 @@ class CommandsGeneral(plugin: ParcelsPlugin) : AbstractParcelCommands(plugin) {
|
|||||||
if (!sure) return "Are you sure? You cannot undo this action!\n" +
|
if (!sure) return "Are you sure? You cannot undo this action!\n" +
|
||||||
"Run \"/${context.rawInput} -sure\" if you want to go through with this."
|
"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 ->
|
.onProgressUpdate(1000, 1000) { progress, elapsedTime ->
|
||||||
val alt = context.getFormat(EMessageType.NUMBER)
|
val alt = context.getFormat(EMessageType.NUMBER)
|
||||||
val main = context.getFormat(EMessageType.INFORMATIVE)
|
val main = context.getFormat(EMessageType.INFORMATIVE)
|
||||||
|
|||||||
@@ -17,11 +17,12 @@ fun getParcelCommands(plugin: ParcelsPlugin): ICommandDispatcher {
|
|||||||
.addParameterType(true, ParcelTarget.PType(plugin.parcelProvider))
|
.addParameterType(true, ParcelTarget.PType(plugin.parcelProvider))
|
||||||
|
|
||||||
.group("parcel", "plot", "plots", "p")
|
.group("parcel", "plot", "plots", "p")
|
||||||
|
.addRequiredPermission("parcels.command")
|
||||||
.registerCommands(CommandsGeneral(plugin))
|
.registerCommands(CommandsGeneral(plugin))
|
||||||
.registerCommands(CommandsAddedStatusLocal(plugin))
|
.registerCommands(CommandsAddedStatusLocal(plugin))
|
||||||
|
|
||||||
.group("option", "opt", "o")
|
.group("option", "opt", "o")
|
||||||
//.apply { CommandsParcelOptions.setGroupDescription(this) }
|
.apply { CommandsParcelOptions.setGroupDescription(this) }
|
||||||
.registerCommands(CommandsParcelOptions(plugin))
|
.registerCommands(CommandsParcelOptions(plugin))
|
||||||
.parent()
|
.parent()
|
||||||
|
|
||||||
|
|||||||
@@ -1,14 +1,13 @@
|
|||||||
package io.dico.parcels2.defaultimpl
|
package io.dico.parcels2.defaultimpl
|
||||||
|
|
||||||
import io.dico.parcels2.*
|
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.Worker
|
||||||
import io.dico.parcels2.blockvisitor.WorktimeLimiter
|
import io.dico.parcels2.blockvisitor.WorktimeLimiter
|
||||||
import io.dico.parcels2.options.DefaultGeneratorOptions
|
import io.dico.parcels2.options.DefaultGeneratorOptions
|
||||||
import io.dico.parcels2.util.*
|
import io.dico.parcels2.util.*
|
||||||
import org.bukkit.*
|
import org.bukkit.*
|
||||||
import org.bukkit.block.Biome
|
import org.bukkit.block.Biome
|
||||||
import org.bukkit.block.Block
|
|
||||||
import org.bukkit.block.BlockFace
|
import org.bukkit.block.BlockFace
|
||||||
import org.bukkit.block.Skull
|
import org.bukkit.block.Skull
|
||||||
import org.bukkit.block.data.BlockData
|
import org.bukkit.block.data.BlockData
|
||||||
@@ -18,11 +17,13 @@ import java.util.Random
|
|||||||
|
|
||||||
private val airType = Bukkit.createBlockData(Material.AIR)
|
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
|
private var _world: World? = null
|
||||||
override val world: World
|
override val world: World
|
||||||
get() {
|
get() {
|
||||||
if (_world == null) _world = Bukkit.getWorld(name)!!.also {
|
if (_world == null) _world = Bukkit.getWorld(worldName)!!.also {
|
||||||
maxHeight = it.maxHeight
|
maxHeight = it.maxHeight
|
||||||
return it
|
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)
|
return Location(world, o.offsetX + fix, o.floorHeight + 1.0, o.offsetZ + fix)
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun makeParcelBlockManager(worktimeLimiter: WorktimeLimiter): ParcelBlockManager {
|
override fun makeParcelLocatorAndBlockManager(worldId: ParcelWorldId,
|
||||||
return ParcelBlockManagerImpl(worktimeLimiter)
|
container: ParcelContainer,
|
||||||
}
|
worktimeLimiter: WorktimeLimiter): Pair<ParcelLocator, ParcelBlockManager> {
|
||||||
|
return ParcelLocatorImpl(worldId, container) to ParcelBlockManagerImpl(worldId, worktimeLimiter)
|
||||||
override fun makeParcelLocator(container: ParcelContainer): ParcelLocator {
|
|
||||||
return ParcelLocatorImpl(container)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private inline fun <T> convertBlockLocationToId(x: Int, z: Int, mapper: (Int, Int) -> T): T? {
|
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
|
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 val world: World = this@DefaultParcelGenerator.world
|
||||||
|
|
||||||
override fun getParcelAt(x: Int, z: Int): Parcel? {
|
override fun getParcelAt(x: Int, z: Int): Parcel? {
|
||||||
return convertBlockLocationToId(x, z, container::getParcelById)
|
return convertBlockLocationToId(x, z, container::getParcelById)
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun getParcelIdAt(x: Int, z: Int): ParcelId? {
|
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")
|
@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 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.x - 1) + pathOffset + o.offsetX,
|
||||||
sectionSize * (parcel.z - 1) + pathOffset + o.offsetZ
|
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)
|
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?) {
|
override fun setOwnerBlock(parcel: ParcelId, owner: PlayerProfile?) {
|
||||||
val b = getBottomBlock(parcel)
|
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 {
|
override fun clearParcel(parcel: ParcelId): Worker = worktimeLimiter.submit {
|
||||||
val bottom = getBottomBlock(parcel)
|
val region = getRegion(parcel)
|
||||||
val region = Region(Vec3i(bottom.x, 0, bottom.z), Vec3i(o.parcelSize, maxHeight + 1, o.parcelSize))
|
val blocks = parcelTraverser.traverseRegion(region)
|
||||||
val blocks = RegionTraversal.DOWNWARD.regionTraverser(region)
|
|
||||||
val blockCount = region.blockCount.toDouble()
|
val blockCount = region.blockCount.toDouble()
|
||||||
|
|
||||||
val world = world
|
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 {
|
override fun getParcelsWithOwnerBlockIn(chunk: Chunk): Collection<Vec2i> {
|
||||||
val bottom = getBottomBlock(parcel)
|
/*
|
||||||
val region = Region(Vec3i(bottom.x, 0, bottom.z), Vec3i(o.parcelSize, maxHeight + 1, o.parcelSize))
|
* Get the offsets for the world out of the way
|
||||||
val blocks = direction.regionTraverser(region)
|
* to simplify the calculation that follows.
|
||||||
val blockCount = region.blockCount.toDouble()
|
*/
|
||||||
val world = world
|
|
||||||
|
|
||||||
for ((index, vec) in blocks.withIndex()) {
|
val x = chunk.x.shl(4) - (o.offsetX + pathOffset)
|
||||||
markSuspensionPoint()
|
val z = chunk.z.shl(4) - (o.offsetZ + pathOffset)
|
||||||
operation(world[vec])
|
|
||||||
setProgress((index + 1) / blockCount)
|
/* 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) }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -47,13 +47,23 @@ class ParcelImpl(override val world: ParcelWorld,
|
|||||||
|
|
||||||
val globalAddedMap: AddedDataMap? get() = owner?.let { world.globalAddedData[it].addedMap }
|
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?
|
override var owner: PlayerProfile?
|
||||||
get() = data.owner
|
get() = data.owner
|
||||||
set(value) {
|
set(value) {
|
||||||
if (data.owner != value) {
|
if (data.owner != value) {
|
||||||
world.storage.setParcelOwner(this, value)
|
world.storage.setParcelOwner(this, value)
|
||||||
|
world.blockManager.setOwnerBlock(this, value)
|
||||||
data.owner = value
|
data.owner = value
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,8 +1,11 @@
|
|||||||
package io.dico.parcels2.defaultimpl
|
package io.dico.parcels2.defaultimpl
|
||||||
|
|
||||||
import io.dico.parcels2.*
|
import io.dico.parcels2.*
|
||||||
|
import kotlinx.coroutines.experimental.Unconfined
|
||||||
|
import kotlinx.coroutines.experimental.launch
|
||||||
import org.bukkit.Bukkit
|
import org.bukkit.Bukkit
|
||||||
import org.bukkit.WorldCreator
|
import org.bukkit.WorldCreator
|
||||||
|
import org.joda.time.DateTime
|
||||||
|
|
||||||
class ParcelProviderImpl(val plugin: ParcelsPlugin) : ParcelProvider {
|
class ParcelProviderImpl(val plugin: ParcelsPlugin) : ParcelProvider {
|
||||||
inline val options get() = plugin.options
|
inline val options get() = plugin.options
|
||||||
@@ -49,9 +52,24 @@ class ParcelProviderImpl(val plugin: ParcelsPlugin) : ParcelProvider {
|
|||||||
if (parcelWorld != null) continue
|
if (parcelWorld != null) continue
|
||||||
|
|
||||||
val generator: ParcelGenerator = getWorldGenerator(worldName)!!
|
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,
|
parcelWorld = ParcelWorldImpl(bukkitWorld, generator, worldOptions.runtime, plugin.storage,
|
||||||
plugin.globalAddedData, ::DefaultParcelContainer, plugin.worktimeLimiter)
|
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
|
_worlds[worldName] = parcelWorld
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -7,21 +7,21 @@ import io.dico.parcels2.blockvisitor.WorktimeLimiter
|
|||||||
import io.dico.parcels2.options.RuntimeWorldOptions
|
import io.dico.parcels2.options.RuntimeWorldOptions
|
||||||
import io.dico.parcels2.storage.Storage
|
import io.dico.parcels2.storage.Storage
|
||||||
import org.bukkit.World
|
import org.bukkit.World
|
||||||
|
import org.joda.time.DateTime
|
||||||
import java.util.UUID
|
import java.util.UUID
|
||||||
|
|
||||||
class ParcelWorldImpl private
|
class ParcelWorldImpl(override val world: World,
|
||||||
constructor(override val world: World,
|
|
||||||
override val generator: ParcelGenerator,
|
override val generator: ParcelGenerator,
|
||||||
override var options: RuntimeWorldOptions,
|
override var options: RuntimeWorldOptions,
|
||||||
override val storage: Storage,
|
override val storage: Storage,
|
||||||
override val globalAddedData: GlobalAddedDataManager,
|
override val globalAddedData: GlobalAddedDataManager,
|
||||||
containerFactory: ParcelContainerFactory,
|
containerFactory: ParcelContainerFactory,
|
||||||
blockManager: ParcelBlockManager)
|
worktimeLimiter: WorktimeLimiter)
|
||||||
: ParcelWorld,
|
: ParcelWorld,
|
||||||
ParcelWorldId,
|
ParcelWorldId,
|
||||||
ParcelContainer, // missing delegation
|
ParcelContainer, /* missing delegation */
|
||||||
ParcelLocator, // missing delegation
|
ParcelLocator /* missing delegation */ {
|
||||||
ParcelBlockManager by blockManager {
|
|
||||||
override val id: ParcelWorldId get() = this
|
override val id: ParcelWorldId get() = this
|
||||||
override val uid: UUID? get() = world.uid
|
override val uid: UUID? get() = world.uid
|
||||||
|
|
||||||
@@ -33,10 +33,14 @@ constructor(override val world: World,
|
|||||||
|
|
||||||
override val name: String = world.name!!
|
override val name: String = world.name!!
|
||||||
override val container: ParcelContainer = containerFactory(this)
|
override val container: ParcelContainer = containerFactory(this)
|
||||||
override val locator: ParcelLocator = generator.makeParcelLocator(container)
|
override val locator: ParcelLocator
|
||||||
override val blockManager: ParcelBlockManager = blockManager
|
override val blockManager: ParcelBlockManager
|
||||||
|
|
||||||
init {
|
init {
|
||||||
|
val pair = generator.makeParcelLocatorAndBlockManager(id, container, worktimeLimiter)
|
||||||
|
locator = pair.first
|
||||||
|
blockManager = pair.second
|
||||||
|
|
||||||
enforceOptions()
|
enforceOptions()
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -55,24 +59,13 @@ constructor(override val world: World,
|
|||||||
world.setGameRuleValue("doTileDrops", "${options.doTileDrops}")
|
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.
|
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
|
// ParcelLocator interface
|
||||||
override fun getParcelAt(x: Int, z: Int): Parcel? {
|
override fun getParcelAt(x: Int, z: Int): Parcel? {
|
||||||
return locator.getParcelAt(x, z)
|
return locator.getParcelAt(x, z)
|
||||||
|
|||||||
@@ -7,6 +7,7 @@ import io.dico.parcels2.Parcel
|
|||||||
import io.dico.parcels2.ParcelProvider
|
import io.dico.parcels2.ParcelProvider
|
||||||
import io.dico.parcels2.ParcelWorld
|
import io.dico.parcels2.ParcelWorld
|
||||||
import io.dico.parcels2.statusKey
|
import io.dico.parcels2.statusKey
|
||||||
|
import io.dico.parcels2.storage.Storage
|
||||||
import io.dico.parcels2.util.*
|
import io.dico.parcels2.util.*
|
||||||
import org.bukkit.Material.*
|
import org.bukkit.Material.*
|
||||||
import org.bukkit.World
|
import org.bukkit.World
|
||||||
@@ -16,6 +17,7 @@ import org.bukkit.block.data.Directional
|
|||||||
import org.bukkit.block.data.type.Bed
|
import org.bukkit.block.data.type.Bed
|
||||||
import org.bukkit.entity.*
|
import org.bukkit.entity.*
|
||||||
import org.bukkit.entity.minecart.ExplosiveMinecart
|
import org.bukkit.entity.minecart.ExplosiveMinecart
|
||||||
|
import org.bukkit.event.EventPriority
|
||||||
import org.bukkit.event.EventPriority.NORMAL
|
import org.bukkit.event.EventPriority.NORMAL
|
||||||
import org.bukkit.event.block.*
|
import org.bukkit.event.block.*
|
||||||
import org.bukkit.event.entity.*
|
import org.bukkit.event.entity.*
|
||||||
@@ -26,11 +28,14 @@ import org.bukkit.event.inventory.InventoryInteractEvent
|
|||||||
import org.bukkit.event.player.*
|
import org.bukkit.event.player.*
|
||||||
import org.bukkit.event.vehicle.VehicleMoveEvent
|
import org.bukkit.event.vehicle.VehicleMoveEvent
|
||||||
import org.bukkit.event.weather.WeatherChangeEvent
|
import org.bukkit.event.weather.WeatherChangeEvent
|
||||||
|
import org.bukkit.event.world.ChunkLoadEvent
|
||||||
import org.bukkit.event.world.StructureGrowEvent
|
import org.bukkit.event.world.StructureGrowEvent
|
||||||
import org.bukkit.inventory.InventoryHolder
|
import org.bukkit.inventory.InventoryHolder
|
||||||
|
|
||||||
@Suppress("NOTHING_TO_INLINE")
|
@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
|
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
|
val parcel = parcelProvider.getParcelAt(event.to) ?: return@l
|
||||||
if (parcel.isBanned(user.statusKey)) {
|
if (parcel.isBanned(user.statusKey)) {
|
||||||
parcelProvider.getParcelAt(event.from)?.also {
|
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")
|
user.sendParcelMessage(nopermit = true, message = "You are banned from this parcel")
|
||||||
} ?: run { event.to = event.from }
|
} ?: 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)
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
@@ -6,6 +6,7 @@ import kotlinx.coroutines.experimental.Deferred
|
|||||||
import kotlinx.coroutines.experimental.Job
|
import kotlinx.coroutines.experimental.Job
|
||||||
import kotlinx.coroutines.experimental.channels.ReceiveChannel
|
import kotlinx.coroutines.experimental.channels.ReceiveChannel
|
||||||
import kotlinx.coroutines.experimental.channels.SendChannel
|
import kotlinx.coroutines.experimental.channels.SendChannel
|
||||||
|
import org.joda.time.DateTime
|
||||||
import java.util.UUID
|
import java.util.UUID
|
||||||
|
|
||||||
interface Backing {
|
interface Backing {
|
||||||
@@ -30,8 +31,14 @@ interface Backing {
|
|||||||
fun shutdown()
|
fun shutdown()
|
||||||
|
|
||||||
|
|
||||||
|
fun getWorldCreationTime(worldId: ParcelWorldId): DateTime?
|
||||||
|
|
||||||
|
fun setWorldCreationTime(worldId: ParcelWorldId, time: DateTime)
|
||||||
|
|
||||||
fun getPlayerUuidForName(name: String): UUID?
|
fun getPlayerUuidForName(name: String): UUID?
|
||||||
|
|
||||||
|
fun updatePlayerName(uuid: UUID, name: String)
|
||||||
|
|
||||||
fun transmitParcelData(channel: SendChannel<DataPair>, parcels: Sequence<ParcelId>)
|
fun transmitParcelData(channel: SendChannel<DataPair>, parcels: Sequence<ParcelId>)
|
||||||
|
|
||||||
fun transmitAllParcelData(channel: SendChannel<DataPair>)
|
fun transmitAllParcelData(channel: SendChannel<DataPair>)
|
||||||
@@ -47,6 +54,8 @@ interface Backing {
|
|||||||
|
|
||||||
fun setParcelOwner(parcel: ParcelId, owner: PlayerProfile?)
|
fun setParcelOwner(parcel: ParcelId, owner: PlayerProfile?)
|
||||||
|
|
||||||
|
fun setParcelOwnerSignOutdated(parcel: ParcelId, outdated: Boolean)
|
||||||
|
|
||||||
fun setLocalPlayerStatus(parcel: ParcelId, player: PlayerProfile, status: AddedStatus)
|
fun setLocalPlayerStatus(parcel: ParcelId, player: PlayerProfile, status: AddedStatus)
|
||||||
|
|
||||||
fun setParcelAllowsInteractInventory(parcel: ParcelId, value: Boolean)
|
fun setParcelAllowsInteractInventory(parcel: ParcelId, value: Boolean)
|
||||||
|
|||||||
@@ -8,6 +8,7 @@ import kotlinx.coroutines.experimental.Job
|
|||||||
import kotlinx.coroutines.experimental.channels.ReceiveChannel
|
import kotlinx.coroutines.experimental.channels.ReceiveChannel
|
||||||
import kotlinx.coroutines.experimental.channels.SendChannel
|
import kotlinx.coroutines.experimental.channels.SendChannel
|
||||||
import kotlinx.coroutines.experimental.launch
|
import kotlinx.coroutines.experimental.launch
|
||||||
|
import org.joda.time.DateTime
|
||||||
import java.util.UUID
|
import java.util.UUID
|
||||||
|
|
||||||
typealias DataPair = Pair<ParcelId, ParcelData?>
|
typealias DataPair = Pair<ParcelId, ParcelData?>
|
||||||
@@ -22,8 +23,14 @@ interface Storage {
|
|||||||
fun shutdown(): Job
|
fun shutdown(): Job
|
||||||
|
|
||||||
|
|
||||||
|
fun getWorldCreationTime(worldId: ParcelWorldId): Deferred<DateTime?>
|
||||||
|
|
||||||
|
fun setWorldCreationTime(worldId: ParcelWorldId, time: DateTime): Job
|
||||||
|
|
||||||
fun getPlayerUuidForName(name: String): Deferred<UUID?>
|
fun getPlayerUuidForName(name: String): Deferred<UUID?>
|
||||||
|
|
||||||
|
fun updatePlayerName(uuid: UUID, name: String): Job
|
||||||
|
|
||||||
fun readParcelData(parcel: ParcelId): Deferred<ParcelData?>
|
fun readParcelData(parcel: ParcelId): Deferred<ParcelData?>
|
||||||
|
|
||||||
fun transmitParcelData(parcels: Sequence<ParcelId>): ReceiveChannel<DataPair>
|
fun transmitParcelData(parcels: Sequence<ParcelId>): ReceiveChannel<DataPair>
|
||||||
@@ -39,6 +46,8 @@ interface Storage {
|
|||||||
|
|
||||||
fun setParcelOwner(parcel: ParcelId, owner: PlayerProfile?): Job
|
fun setParcelOwner(parcel: ParcelId, owner: PlayerProfile?): Job
|
||||||
|
|
||||||
|
fun setParcelOwnerSignOutdated(parcel: ParcelId, outdated: Boolean): Job
|
||||||
|
|
||||||
fun setParcelPlayerStatus(parcel: ParcelId, player: PlayerProfile, status: AddedStatus): Job
|
fun setParcelPlayerStatus(parcel: ParcelId, player: PlayerProfile, status: AddedStatus): Job
|
||||||
|
|
||||||
fun setParcelAllowsInteractInventory(parcel: ParcelId, value: Boolean): 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 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 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 readParcelData(parcel: ParcelId) = b.launchFuture { b.readParcelData(parcel) }
|
||||||
|
|
||||||
override fun transmitParcelData(parcels: Sequence<ParcelId>) = b.openChannel<DataPair> { b.transmitParcelData(it, parcels) }
|
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 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 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) }
|
override fun setParcelAllowsInteractInventory(parcel: ParcelId, value: Boolean) = b.launchJob { b.setParcelAllowsInteractInventory(parcel, value) }
|
||||||
|
|||||||
@@ -4,10 +4,12 @@ package io.dico.parcels2.storage.exposed
|
|||||||
|
|
||||||
import com.zaxxer.hikari.HikariDataSource
|
import com.zaxxer.hikari.HikariDataSource
|
||||||
import io.dico.parcels2.*
|
import io.dico.parcels2.*
|
||||||
|
import io.dico.parcels2.PlayerProfile.Star.name
|
||||||
import io.dico.parcels2.storage.AddedDataPair
|
import io.dico.parcels2.storage.AddedDataPair
|
||||||
import io.dico.parcels2.storage.Backing
|
import io.dico.parcels2.storage.Backing
|
||||||
import io.dico.parcels2.storage.DataPair
|
import io.dico.parcels2.storage.DataPair
|
||||||
import io.dico.parcels2.util.synchronized
|
import io.dico.parcels2.util.synchronized
|
||||||
|
import io.dico.parcels2.util.toByteArray
|
||||||
import io.dico.parcels2.util.toUUID
|
import io.dico.parcels2.util.toUUID
|
||||||
import kotlinx.coroutines.experimental.*
|
import kotlinx.coroutines.experimental.*
|
||||||
import kotlinx.coroutines.experimental.channels.ArrayChannel
|
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")
|
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? {
|
override fun getPlayerUuidForName(name: String): UUID? {
|
||||||
return ProfilesT.slice(ProfilesT.uuid).select { ProfilesT.name.upperCase() eq name.toUpperCase() }
|
return ProfilesT.slice(ProfilesT.uuid).select { ProfilesT.name.upperCase() eq name.toUpperCase() }
|
||||||
.firstOrNull()?.let { it[ProfilesT.uuid]?.toUUID() }
|
.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>) {
|
override fun transmitParcelData(channel: SendChannel<DataPair>, parcels: Sequence<ParcelId>) {
|
||||||
for (parcel in parcels) {
|
for (parcel in parcels) {
|
||||||
val data = readParcelData(parcel)
|
val data = readParcelData(parcel)
|
||||||
@@ -193,6 +212,14 @@ class ExposedBacking(private val dataSourceFactory: () -> DataSource, val poolSi
|
|||||||
ParcelsT.update({ ParcelsT.id eq id }) {
|
ParcelsT.update({ ParcelsT.id eq id }) {
|
||||||
it[ParcelsT.owner_id] = owner_id
|
it[ParcelsT.owner_id] = owner_id
|
||||||
it[claim_time] = time
|
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) {
|
override fun setParcelAllowsInteractInventory(parcel: ParcelId, value: Boolean) {
|
||||||
val id = ParcelsT.getOrInitId(parcel)
|
val id = ParcelsT.getOrInitId(parcel)
|
||||||
ParcelOptionsT.upsert(ParcelOptionsT.parcel_id) {
|
ParcelOptionsT.upsert(ParcelOptionsT.parcel_id) {
|
||||||
it[ParcelOptionsT.parcel_id] = id
|
it[parcel_id] = id
|
||||||
it[ParcelOptionsT.interact_inventory] = value
|
it[interact_inventory] = value
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun setParcelAllowsInteractInputs(parcel: ParcelId, value: Boolean) {
|
override fun setParcelAllowsInteractInputs(parcel: ParcelId, value: Boolean) {
|
||||||
val id = ParcelsT.getOrInitId(parcel)
|
val id = ParcelsT.getOrInitId(parcel)
|
||||||
ParcelOptionsT.upsert(ParcelOptionsT.parcel_id) {
|
ParcelOptionsT.upsert(ParcelOptionsT.parcel_id) {
|
||||||
it[ParcelOptionsT.parcel_id] = id
|
it[parcel_id] = id
|
||||||
it[ParcelOptionsT.interact_inputs] = value
|
it[interact_inputs] = value
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -231,7 +258,8 @@ class ExposedBacking(private val dataSourceFactory: () -> DataSource, val poolSi
|
|||||||
|
|
||||||
private fun rowToParcelData(row: ResultRow) = ParcelDataHolder().apply {
|
private fun rowToParcelData(row: ResultRow) = ParcelDataHolder().apply {
|
||||||
owner = row[ParcelsT.owner_id]?.let { ProfilesT.getItem(it) }
|
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]
|
val id = row[ParcelsT.id]
|
||||||
ParcelOptionsT.select { ParcelOptionsT.parcel_id eq id }.firstOrNull()?.let { optrow ->
|
ParcelOptionsT.select { ParcelOptionsT.parcel_id eq id }.firstOrNull()?.let { optrow ->
|
||||||
|
|||||||
@@ -9,6 +9,7 @@ import io.dico.parcels2.util.toByteArray
|
|||||||
import io.dico.parcels2.util.toUUID
|
import io.dico.parcels2.util.toUUID
|
||||||
import org.jetbrains.exposed.sql.*
|
import org.jetbrains.exposed.sql.*
|
||||||
import org.jetbrains.exposed.sql.statements.UpdateBuilder
|
import org.jetbrains.exposed.sql.statements.UpdateBuilder
|
||||||
|
import org.joda.time.DateTime
|
||||||
import java.util.UUID
|
import java.util.UUID
|
||||||
|
|
||||||
sealed class IdTransactionsTable<TableT : IdTransactionsTable<TableT, QueryObj>, QueryObj>(tableName: String, columnName: String)
|
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 {
|
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?
|
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)
|
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 name = varchar("name", 50)
|
||||||
val uid = binary("uid", 16).nullable()
|
val uid = binary("uid", 16).nullable()
|
||||||
|
val creation_time = datetime("creation_time").nullable()
|
||||||
val index_name = uniqueIndexR("index_name", name)
|
val index_name = uniqueIndexR("index_name", name)
|
||||||
val index_uid = uniqueIndexR("index_uid", uid)
|
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 {
|
override fun getItem(row: ResultRow): ParcelWorldId {
|
||||||
return ParcelWorldId(row[name], row[uid]?.toUUID())
|
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") {
|
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 px = integer("px")
|
||||||
val pz = integer("pz")
|
val pz = integer("pz")
|
||||||
val owner_id = integer("owner_id").references(ProfilesT.id).nullable()
|
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 claim_time = datetime("claim_time").nullable()
|
||||||
val index_location = uniqueIndexR("index_location", world_id, px, pz)
|
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 uuid = binary("uuid", 16).nullable()
|
||||||
val name = varchar("name", 32).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 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 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) },
|
{ getId(binaryUuid) },
|
||||||
{ it[this@ProfilesT.uuid] = binaryUuid; it[this@ProfilesT.name] = name },
|
{ it[this@ProfilesT.uuid] = binaryUuid; it[this@ProfilesT.name] = name },
|
||||||
{ "profile(uuid = $uuid, name = $name)" })
|
{ "profile(uuid = $uuid, name = $name)" })
|
||||||
|
|||||||
@@ -83,6 +83,7 @@ class PlotmeMigration(val options: PlotmeMigrationOptions) : Migration {
|
|||||||
val parcel = getParcelId(PlotmePlotsT, row) ?: return@forEach
|
val parcel = getParcelId(PlotmePlotsT, row) ?: return@forEach
|
||||||
val owner = PlayerProfile.safe(row[PlotmePlotsT.owner_uuid]?.toUUID(), row[PlotmePlotsT.owner_name])
|
val owner = PlayerProfile.safe(row[PlotmePlotsT.owner_uuid]?.toUUID(), row[PlotmePlotsT.owner_name])
|
||||||
target.setParcelOwner(parcel, owner)
|
target.setParcelOwner(parcel, owner)
|
||||||
|
target.setParcelOwnerSignOutdated(parcel, true)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -2,4 +2,12 @@ package io.dico.parcels2.util
|
|||||||
|
|
||||||
data class Region(val origin: Vec3i, val size: Vec3i) {
|
data class Region(val origin: Vec3i, val size: Vec3i) {
|
||||||
val blockCount: Int get() = size.x * size.y * size.z
|
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)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
@@ -5,3 +5,7 @@ data class Vec2i(
|
|||||||
val z: Int
|
val z: Int
|
||||||
)
|
)
|
||||||
|
|
||||||
|
data class Region2i(
|
||||||
|
val bottom: Vec2i,
|
||||||
|
val top: Vec2i
|
||||||
|
)
|
||||||
@@ -3,6 +3,22 @@ package io.dico.parcels2.util
|
|||||||
import org.bukkit.World
|
import org.bukkit.World
|
||||||
import org.bukkit.block.Block
|
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(
|
data class Vec3i(
|
||||||
val x: Int,
|
val x: Int,
|
||||||
val y: Int,
|
val y: Int,
|
||||||
@@ -12,6 +28,9 @@ data class Vec3i(
|
|||||||
infix fun addX(o: Int) = Vec3i(x + o, y, z)
|
infix fun addX(o: Int) = Vec3i(x + o, y, z)
|
||||||
infix fun addY(o: Int) = Vec3i(x, y + o, z)
|
infix fun addY(o: Int) = Vec3i(x, y + o, z)
|
||||||
infix fun addZ(o: Int) = Vec3i(x, y, z + o)
|
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)
|
fun add(ox: Int, oy: Int, oz: Int) = Vec3i(x + ox, y + oy, z + oz)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -17,8 +17,8 @@ Modify home command:
|
|||||||
* Make `:` not be required if prior component cannot be parsed to an int
|
* 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
|
* Listen for command events that use plotme-style argument, and transform the command
|
||||||
|
|
||||||
Add permissions to commands (replace or fix `IContextFilter` from command lib
|
~~Add permissions to commands (replace or fix `IContextFilter` from command lib
|
||||||
to allow inheriting permissions properly).
|
to allow inheriting permissions properly).~~
|
||||||
|
|
||||||
Parcel Options
|
Parcel Options
|
||||||
-
|
-
|
||||||
@@ -39,8 +39,8 @@ This could become optional.
|
|||||||
|
|
||||||
Block Management
|
Block Management
|
||||||
-
|
-
|
||||||
Update the parcel corner with owner info when a player flies into the parcel (after migrations).
|
~~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.
|
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
|
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.
|
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
|
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.
|
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)
|
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
|
Events
|
||||||
-
|
-
|
||||||
|
|||||||
Reference in New Issue
Block a user