diff options
author | Dico <dico.karssiens@gmail.com> | 2018-09-26 07:08:42 +0100 |
---|---|---|
committer | Dico <dico.karssiens@gmail.com> | 2018-09-26 07:08:42 +0100 |
commit | 520ae530d2de076fa9e87da7f04fcf78e080f4de (patch) | |
tree | dafbe3c14d5b6cbe49eef4f87499d35d1c69676d | |
parent | 2225bdae95b3de4985347edf30ae31a28f73f35b (diff) |
Make progress
25 files changed, 435 insertions, 282 deletions
diff --git a/dicore3/command/src/main/java/io/dico/dicore/command/ChildCommandAddress.java b/dicore3/command/src/main/java/io/dico/dicore/command/ChildCommandAddress.java index 73d82ca..022904e 100644 --- a/dicore3/command/src/main/java/io/dico/dicore/command/ChildCommandAddress.java +++ b/dicore3/command/src/main/java/io/dico/dicore/command/ChildCommandAddress.java @@ -24,11 +24,20 @@ public class ChildCommandAddress extends ModifiableCommandAddress { } public static ChildCommandAddress newPlaceHolderCommand(String name, String... aliases) { - ChildCommandAddress rv = new ChildCommandAddress(DefaultGroupCommand.getInstance(), name, aliases); - HelpCommand.registerAsChild(rv); + ChildCommandAddress rv = new ChildCommandAddress(); + rv.setupAsPlaceholder(name, aliases); return rv; } + public void setupAsPlaceholder(String name, String... aliases) { + if (!hasCommand()) { + setCommand(DefaultGroupCommand.getInstance()); + } + + addNameAndAliases(name, aliases); + HelpCommand.registerAsChild(this); + } + @Override public boolean isRoot() { return false; diff --git a/dicore3/command/src/main/java/io/dico/dicore/command/Command.java b/dicore3/command/src/main/java/io/dico/dicore/command/Command.java index b66b5f5..868aa95 100644 --- a/dicore3/command/src/main/java/io/dico/dicore/command/Command.java +++ b/dicore3/command/src/main/java/io/dico/dicore/command/Command.java @@ -145,43 +145,50 @@ public abstract class Command { ExecutionContext executionContext = new ExecutionContext(sender, caller, this, buffer, false); try { - //System.out.println("In Command.execute(sender, caller, buffer)#try{"); - int i, n; - for (i = 0, n = contextFilterPostParameterIndex; i < n; i++) { - contextFilters.get(i).filterContext(executionContext); - } - - executionContext.parseParameters(); + executeWithContext(executionContext); + } catch (Throwable t) { + caller.getChatController().handleException(sender, executionContext, t); + } + } - for (n = contextFilters.size(); i < n; i++) { - contextFilters.get(i).filterContext(executionContext); - } + public void executeWithContext(ExecutionContext context) throws CommandException { + //System.out.println("In Command.execute(sender, caller, buffer)#try{"); + int i, n; + for (i = 0, n = contextFilterPostParameterIndex; i < n; i++) { + contextFilters.get(i).filterContext(context); + } - //System.out.println("Post-contextfilters"); + context.parseParameters(); - String message = execute(sender, executionContext); - caller.getChatController().sendMessage(sender, EMessageType.RESULT, message); - } catch (Throwable t) { - caller.getChatController().handleException(sender, executionContext, t); + for (n = contextFilters.size(); i < n; i++) { + contextFilters.get(i).filterContext(context); } + + //System.out.println("Post-contextfilters"); + + String message = execute(context.getSender(), context); + context.getAddress().getChatController().sendMessage(context.getSender(), EMessageType.RESULT, message); } public abstract String execute(CommandSender sender, ExecutionContext context) throws CommandException; public List<String> tabComplete(CommandSender sender, ICommandAddress caller, Location location, ArgumentBuffer buffer) { ExecutionContext executionContext = new ExecutionContext(sender, caller, this, buffer, true); - try { - int i, n; - for (i = 0, n = contextFilterPostParameterIndex; i < n; i++) { - contextFilters.get(i).filterContext(executionContext); - } + return tabCompleteWithContext(executionContext, location); } catch (CommandException ex) { return Collections.emptyList(); } + } + + public List<String> tabCompleteWithContext(ExecutionContext context, Location location) throws CommandException { + int i, n; + for (i = 0, n = contextFilterPostParameterIndex; i < n; i++) { + contextFilters.get(i).filterContext(context); + } - executionContext.parseParametersQuietly(); - return tabComplete(sender, executionContext, location); + context.parseParametersQuietly(); + return tabComplete(context.getSender(), context, location); } public List<String> tabComplete(CommandSender sender, ExecutionContext context, Location location) { diff --git a/dicore3/command/src/main/java/io/dico/dicore/command/CommandBuilder.java b/dicore3/command/src/main/java/io/dico/dicore/command/CommandBuilder.java index e72d478..76211c2 100644 --- a/dicore3/command/src/main/java/io/dico/dicore/command/CommandBuilder.java +++ b/dicore3/command/src/main/java/io/dico/dicore/command/CommandBuilder.java @@ -197,13 +197,39 @@ public final class CommandBuilder { public CommandBuilder group(String name, String... aliases) { ChildCommandAddress address = cur.getChild(name); if (address == null || !name.equals(address.getMainKey())) { - cur.addChild(address = ChildCommandAddress.newPlaceHolderCommand(name, aliases)); + address = new ChildCommandAddress(); + address.setupAsPlaceholder(name, aliases); + cur.addChild(address); } cur = address; return this; } /** + * Similar to {@link #group(String, String[])} but this will force overwrite any present group, + * using the address passed. The address MUST be an instance of {@link ChildCommandAddress}. + * + * <p>The address must not have a parent or any keys</p> + * + * @param address the address object to use + * @param name the main key + * @param aliases any aliases + * @return this + * @throws IllegalArgumentException if any of the requirements set out above aren't met + */ + public CommandBuilder group(ICommandAddress address, String name, String... aliases) { + if (address.hasParent() || address.getMainKey() != null || !(address instanceof ChildCommandAddress)) { + throw new IllegalArgumentException(); + } + + ChildCommandAddress asChild = (ChildCommandAddress) address; + asChild.setupAsPlaceholder(name, aliases); + cur.addChild(address); + cur = asChild; + return this; + } + + /** * Sets the description of a group created by {@link #group(String, String...)} * Can be called subsequently to making a call to {@link #group(String, String...)} * diff --git a/dicore3/command/src/main/java/io/dico/dicore/command/ExecutionContext.java b/dicore3/command/src/main/java/io/dico/dicore/command/ExecutionContext.java index 4450a92..0608b80 100644 --- a/dicore3/command/src/main/java/io/dico/dicore/command/ExecutionContext.java +++ b/dicore3/command/src/main/java/io/dico/dicore/command/ExecutionContext.java @@ -17,14 +17,14 @@ import java.util.*; * It is also responsible for keeping track of the parameter to complete in the case of a tab completion. */ public class ExecutionContext { - private final CommandSender sender; - private final ICommandAddress address; - private final Command command; - private final ArgumentBuffer originalBuffer; - private final ArgumentBuffer processedBuffer; + private CommandSender sender; + private ICommandAddress address; + private Command command; + private ArgumentBuffer originalBuffer; + private ArgumentBuffer processedBuffer; // caches the buffer's cursor before parsing. This is needed to provide the original input of the player. - private final int cursorStart; + private int cursorStart; // when the context starts parsing parameters, this flag is set, and any subsequent calls to #parseParameters() throw an IllegalStateException. private boolean attemptedToParse; @@ -48,8 +48,14 @@ public class ExecutionContext { // if this flag is set, any messages sent through the sendMessage methods are discarded. private boolean muted; + public ExecutionContext(CommandSender sender, boolean tabComplete) { + this.sender = Objects.requireNonNull(sender); + this.muted = tabComplete; + this.tabComplete = tabComplete; + } + /** - * Construct an execution context, making it ready to parse the parameter values. + * Construct an execution context that is ready to parse the parameter values. * * @param sender the sender * @param address the address @@ -57,11 +63,22 @@ public class ExecutionContext { * @param tabComplete true if this execution is a tab-completion */ public ExecutionContext(CommandSender sender, ICommandAddress address, Command command, ArgumentBuffer buffer, boolean tabComplete) { - this.sender = Objects.requireNonNull(sender); + this(sender, tabComplete); + targetAcquired(address, command, buffer); + } + + void requireAddressPresent(boolean present) { + //noinspection DoubleNegation + if ((address != null) != present) { + throw new IllegalStateException(); + } + } + + void targetAcquired(ICommandAddress address, Command command, ArgumentBuffer buffer) { + requireAddressPresent(false); + this.address = Objects.requireNonNull(address); this.command = Objects.requireNonNull(command); - this.muted = tabComplete; - this.tabComplete = tabComplete; // If its tab completing, keep the empty element that might be at the end of the buffer // due to a space at the end of the command. @@ -80,7 +97,8 @@ public class ExecutionContext { * * @throws CommandException if an error occurs while parsing the parameters. */ - public synchronized void parseParameters() throws CommandException { + synchronized void parseParameters() throws CommandException { + requireAddressPresent(true); if (attemptedToParse) { throw new IllegalStateException(); } @@ -101,7 +119,8 @@ public class ExecutionContext { * This method is typically used by tab completions. * After calling this method, the context is ready to provide completions. */ - public synchronized void parseParametersQuietly() { + synchronized void parseParametersQuietly() { + requireAddressPresent(true); if (attemptedToParse) { throw new IllegalStateException(); } diff --git a/dicore3/command/src/main/java/io/dico/dicore/command/ICommandAddress.java b/dicore3/command/src/main/java/io/dico/dicore/command/ICommandAddress.java index d6cd350..befdefa 100644 --- a/dicore3/command/src/main/java/io/dico/dicore/command/ICommandAddress.java +++ b/dicore3/command/src/main/java/io/dico/dicore/command/ICommandAddress.java @@ -126,6 +126,16 @@ public interface ICommandAddress { ICommandAddress getChild(String key); /** + * Query for a child at the given key, with the given context for reference. + * Can be used to override behaviour of the tree. + * + * @param key the key. The name or alias of a command. + * @param context context of a command being executed + * @return the child, or null if it's not found, altered freely by the implementation + */ + ICommandAddress getChild(String key, ExecutionContext context) throws CommandException; + + /** * Get the command dispatcher for this tree * * @return the command dispatcher diff --git a/dicore3/command/src/main/java/io/dico/dicore/command/ICommandDispatcher.java b/dicore3/command/src/main/java/io/dico/dicore/command/ICommandDispatcher.java index b18694e..055171d 100644 --- a/dicore3/command/src/main/java/io/dico/dicore/command/ICommandDispatcher.java +++ b/dicore3/command/src/main/java/io/dico/dicore/command/ICommandDispatcher.java @@ -30,9 +30,24 @@ public interface ICommandDispatcher { * @param buffer the command itself as a buffer. * @return the address that is the target of the command. */ + @Deprecated ICommandAddress getCommandTarget(CommandSender sender, ArgumentBuffer buffer); /** + * Similar to {@link #getDeepChild(ArgumentBuffer)}, + * but this method incorporates checks on the command of traversed children: + * {@link Command#isVisibleTo(CommandSender)} + * and {@link Command#takePrecedenceOverSubcommand(String, ArgumentBuffer)} + * <p> + * The target of a command is never null, however, the same instance might be returned, and the returned address might not hold a command. + * + * @param context the context of the command. The context must not have its address set. + * @param buffer the command itself as a buffer. + * @return the address that is the target of the command. + */ + ICommandAddress getCommandTarget(ExecutionContext context, ArgumentBuffer buffer) throws CommandException; + + /** * dispatch the command * * @param sender the sender diff --git a/dicore3/command/src/main/java/io/dico/dicore/command/ModifiableCommandAddress.java b/dicore3/command/src/main/java/io/dico/dicore/command/ModifiableCommandAddress.java index 8c2ab67..484eb5e 100644 --- a/dicore3/command/src/main/java/io/dico/dicore/command/ModifiableCommandAddress.java +++ b/dicore3/command/src/main/java/io/dico/dicore/command/ModifiableCommandAddress.java @@ -120,6 +120,11 @@ public abstract class ModifiableCommandAddress implements ICommandAddress { return children.get(key); } + @Override + public ChildCommandAddress getChild(String key, ExecutionContext context) throws CommandException { + return getChild(key); + } + public void addChild(ICommandAddress child) { if (!(child instanceof ChildCommandAddress)) { throw new IllegalArgumentException("Argument must be a ChildCommandAddress"); diff --git a/dicore3/command/src/main/java/io/dico/dicore/command/RootCommandAddress.java b/dicore3/command/src/main/java/io/dico/dicore/command/RootCommandAddress.java index 6d38174..7577808 100644 --- a/dicore3/command/src/main/java/io/dico/dicore/command/RootCommandAddress.java +++ b/dicore3/command/src/main/java/io/dico/dicore/command/RootCommandAddress.java @@ -123,8 +123,6 @@ public class RootCommandAddress extends ModifiableCommandAddress implements ICom @Override public ModifiableCommandAddress getCommandTarget(CommandSender sender, ArgumentBuffer buffer) { - //System.out.println("Buffer cursor upon getCommandTarget: " + buffer.getCursor()); - ModifiableCommandAddress cur = this; ChildCommandAddress child; while (buffer.hasNext()) { @@ -139,16 +137,25 @@ public class RootCommandAddress extends ModifiableCommandAddress implements ICom cur = child; } - /* - if (!cur.hasCommand() && cur.hasHelpCommand()) { - cur = cur.getHelpCommand(); - } else { - while (!cur.hasCommand() && cur.hasParent()) { - cur = cur.getParent(); + return cur; + } + + @Override + public ModifiableCommandAddress getCommandTarget(ExecutionContext context, ArgumentBuffer buffer) throws CommandException { + CommandSender sender = context.getSender(); + ModifiableCommandAddress cur = this; + ChildCommandAddress child; + while (buffer.hasNext()) { + child = cur.getChild(buffer.next(), context); + if (child == null + || (child.hasCommand() && !child.getCommand().isVisibleTo(sender)) + || (cur.hasCommand() && cur.getCommand().takePrecedenceOverSubcommand(buffer.peekPrevious(), buffer.getUnaffectingCopy()))) { buffer.rewind(); + break; } + + cur = child; } - */ return cur; } @@ -165,18 +172,32 @@ public class RootCommandAddress extends ModifiableCommandAddress implements ICom @Override public boolean dispatchCommand(CommandSender sender, ArgumentBuffer buffer) { - ModifiableCommandAddress targetAddress = getCommandTarget(sender, buffer); - Command target = targetAddress.getCommand(); - - if (target == null || target instanceof DefaultGroupCommand) { - if (targetAddress.hasHelpCommand()) { - target = targetAddress.getHelpCommand().getCommand(); - } else if (target == null){ - return false; + ExecutionContext context = new ExecutionContext(sender, false); + + ModifiableCommandAddress targetAddress = null; + + try { + targetAddress = getCommandTarget(context, buffer); + Command target = targetAddress.getCommand(); + + if (target == null || target instanceof DefaultGroupCommand) { + if (targetAddress.hasHelpCommand()) { + target = targetAddress.getHelpCommand().getCommand(); + } else if (target == null){ + return false; + } + } + + context.targetAcquired(targetAddress, target, buffer); + target.executeWithContext(context); + + } catch (Throwable t) { + if (targetAddress == null) { + targetAddress = this; } + targetAddress.getChatController().handleException(sender, context, t); } - target.execute(sender, targetAddress, buffer); return true; } @@ -192,28 +213,38 @@ public class RootCommandAddress extends ModifiableCommandAddress implements ICom @Override public List<String> getTabCompletions(CommandSender sender, Location location, ArgumentBuffer buffer) { - ICommandAddress target = getCommandTarget(sender, buffer); - List<String> out = target.hasCommand() ? target.getCommand().tabComplete(sender, target, location, buffer.getUnaffectingCopy()) : Collections.emptyList(); - - int cursor = buffer.getCursor(); - String input; - if (cursor >= buffer.size()) { - input = ""; - } else { - input = buffer.get(cursor).toLowerCase(); - } + ExecutionContext context = new ExecutionContext(sender, true); + + try { + ICommandAddress target = getCommandTarget(context, buffer); + List<String> out = target.hasCommand() + ? target.getCommand().tabComplete(sender, target, location, buffer.getUnaffectingCopy()) + : Collections.emptyList(); + + int cursor = buffer.getCursor(); + String input; + if (cursor >= buffer.size()) { + input = ""; + } else { + input = buffer.get(cursor).toLowerCase(); + } - boolean wrapped = false; - for (String child : target.getChildren().keySet()) { - if (child.toLowerCase().startsWith(input)) { - if (!wrapped) { - out = new ArrayList<>(out); - wrapped = true; + boolean wrapped = false; + for (String child : target.getChildren().keySet()) { + if (child.toLowerCase().startsWith(input)) { + if (!wrapped) { + out = new ArrayList<>(out); + wrapped = true; + } + out.add(child); } - out.add(child); } + + return out; + + } catch (CommandException ex) { + return Collections.emptyList(); } - return out; } } diff --git a/dicore3/command/src/main/java/io/dico/dicore/command/registration/reflect/ReflectiveRegistration.java b/dicore3/command/src/main/java/io/dico/dicore/command/registration/reflect/ReflectiveRegistration.java index 0495ba9..0c64533 100644 --- a/dicore3/command/src/main/java/io/dico/dicore/command/registration/reflect/ReflectiveRegistration.java +++ b/dicore3/command/src/main/java/io/dico/dicore/command/registration/reflect/ReflectiveRegistration.java @@ -177,10 +177,12 @@ public class ReflectiveRegistration { GroupEntry matchEntry = matchEntries[i]; if (patterns[i].matcher(name).matches()) { if (addresses[i] == null) { - addresses[i] = ChildCommandAddress.newPlaceHolderCommand(matchEntry.group(), matchEntry.groupAliases()); - groupRootAddress.addChild(addresses[i]); - generateCommands(addresses[i], matchEntry.generatedCommands()); - setDescription(addresses[i], matchEntry.description(), matchEntry.shortDescription()); + ChildCommandAddress placeholder = new ChildCommandAddress(); + placeholder.setupAsPlaceholder(matchEntry.group(), matchEntry.groupAliases()); + addresses[i] = placeholder; + groupRootAddress.addChild(placeholder); + generateCommands(placeholder, matchEntry.generatedCommands()); + setDescription(placeholder, matchEntry.description(), matchEntry.shortDescription()); } return addresses[i]; } diff --git a/src/main/kotlin/io/dico/parcels2/ParcelGenerator.kt b/src/main/kotlin/io/dico/parcels2/ParcelGenerator.kt index c11d557..4e0aeb4 100644 --- a/src/main/kotlin/io/dico/parcels2/ParcelGenerator.kt +++ b/src/main/kotlin/io/dico/parcels2/ParcelGenerator.kt @@ -37,12 +37,12 @@ abstract class ParcelGenerator : ChunkGenerator() { abstract fun makeParcelLocatorAndBlockManager(worldId: ParcelWorldId, container: ParcelContainer, coroutineScope: CoroutineScope, - worktimeLimiter: WorktimeLimiter): Pair<ParcelLocator, ParcelBlockManager> + workDispatcher: WorkDispatcher): Pair<ParcelLocator, ParcelBlockManager> } interface ParcelBlockManager { val world: World - val worktimeLimiter: WorktimeLimiter + val workDispatcher: WorkDispatcher val parcelTraverser: RegionTraverser // fun getBottomBlock(parcel: ParcelId): Vec2i @@ -61,7 +61,7 @@ interface ParcelBlockManager { fun swapParcels(parcel1: ParcelId, parcel2: ParcelId): Worker - fun submitBlockVisitor(vararg parcelIds: ParcelId, task: TimeLimitedTask): Worker + fun submitBlockVisitor(vararg parcelIds: ParcelId, task: WorkerTask): Worker /** * Used to update owner blocks in the corner of the parcel diff --git a/src/main/kotlin/io/dico/parcels2/ParcelsPlugin.kt b/src/main/kotlin/io/dico/parcels2/ParcelsPlugin.kt index 3e12ba5..7af7468 100644 --- a/src/main/kotlin/io/dico/parcels2/ParcelsPlugin.kt +++ b/src/main/kotlin/io/dico/parcels2/ParcelsPlugin.kt @@ -3,8 +3,8 @@ package io.dico.parcels2 import io.dico.dicore.Registrator import io.dico.dicore.command.EOverridePolicy import io.dico.dicore.command.ICommandDispatcher -import io.dico.parcels2.blockvisitor.TickWorktimeLimiter -import io.dico.parcels2.blockvisitor.WorktimeLimiter +import io.dico.parcels2.blockvisitor.BukkitWorkDispatcher +import io.dico.parcels2.blockvisitor.WorkDispatcher import io.dico.parcels2.command.getParcelCommands import io.dico.parcels2.defaultimpl.GlobalPrivilegesManagerImpl import io.dico.parcels2.defaultimpl.ParcelProviderImpl @@ -44,7 +44,7 @@ class ParcelsPlugin : JavaPlugin(), CoroutineScope, PluginScheduler { override val coroutineContext: CoroutineContext = MainThreadDispatcher(this) override val plugin: Plugin get() = this - val worktimeLimiter: WorktimeLimiter by lazy { TickWorktimeLimiter(this, options.tickWorktime) } + val workDispatcher: WorkDispatcher by lazy { BukkitWorkDispatcher(this, options.tickWorktime) } override fun onEnable() { plogger.info("Debug enabled: ${plogger.isDebugEnabled}") @@ -55,11 +55,11 @@ class ParcelsPlugin : JavaPlugin(), CoroutineScope, PluginScheduler { } override fun onDisable() { - val hasWorkers = worktimeLimiter.workers.isNotEmpty() + val hasWorkers = workDispatcher.workers.isNotEmpty() if (hasWorkers) { - plogger.warn("Parcels is attempting to complete all ${worktimeLimiter.workers.size} remaining jobs before shutdown...") + plogger.warn("Parcels is attempting to complete all ${workDispatcher.workers.size} remaining jobs before shutdown...") } - worktimeLimiter.completeAllTasks() + workDispatcher.completeAllTasks() if (hasWorkers) { plogger.info("Parcels has completed the remaining jobs.") } diff --git a/src/main/kotlin/io/dico/parcels2/blockvisitor/ExtraBlockChange.kt b/src/main/kotlin/io/dico/parcels2/blockvisitor/ExtraBlockChange.kt new file mode 100644 index 0000000..3f7e070 --- /dev/null +++ b/src/main/kotlin/io/dico/parcels2/blockvisitor/ExtraBlockChange.kt @@ -0,0 +1,38 @@ +package io.dico.parcels2.blockvisitor + +import org.bukkit.block.Block +import org.bukkit.block.BlockState +import org.bukkit.block.Sign +import kotlin.reflect.KClass + +interface ExtraBlockChange { + fun update(block: Block) +} + +abstract class BlockStateChange<T : BlockState> : ExtraBlockChange { + abstract val stateClass: KClass<T> + + abstract fun update(state: T) + + override fun update(block: Block) { + val state = block.state + if (stateClass.isInstance(state)) { + @Suppress("UNCHECKED_CAST") + update(state as T) + } + } +} + +class SignStateChange(state: Sign) : BlockStateChange<Sign>() { + val lines = state.lines + + override val stateClass: KClass<Sign> + get() = Sign::class + + override fun update(state: Sign) { + for (i in lines.indices) { + val line = lines[i] + state.setLine(i, line) + } + } +} diff --git a/src/main/kotlin/io/dico/parcels2/blockvisitor/RegionTraverser.kt b/src/main/kotlin/io/dico/parcels2/blockvisitor/RegionTraverser.kt index 8f7f8f8..5326baa 100644 --- a/src/main/kotlin/io/dico/parcels2/blockvisitor/RegionTraverser.kt +++ b/src/main/kotlin/io/dico/parcels2/blockvisitor/RegionTraverser.kt @@ -235,87 +235,3 @@ inline class TraverseDirection(val bits: Int) { } } - -/* -private typealias Scope = SequenceScope<Vec3i> -private typealias ScopeAction = suspend Scope.(Int, Int, Int) -> Unit - -@Suppress("NON_EXHAUSTIVE_WHEN") -suspend fun Scope.traverserLogic( - region: Region, - order: TraverseOrder, - direction: TraverseDirection -) = with(direction) { - val (primary, secondary, tertiary) = order.toArray() - val (origin, size) = region - - when (order.primary) { - Dimension.X -> - when (order.secondary) { - Dimension.Y -> { - directionOf(primary).traverse(primary.extract(size)) { p -> - directionOf(secondary).traverse(secondary.extract(size)) { s -> - directionOf(tertiary).traverse(tertiary.extract(size)) { t -> - yield(origin.add(p, s, t)) - } - } - } - } - Dimension.Z -> { - directionOf(primary).traverse(primary.extract(size)) { p -> - directionOf(secondary).traverse(secondary.extract(size)) { s -> - directionOf(tertiary).traverse(tertiary.extract(size)) { t -> - yield(origin.add(p, t, s)) - } - } - } - } - } - - Dimension.Y -> - when (order.secondary) { - Dimension.X -> { - directionOf(primary).traverse(primary.extract(size)) { p -> - directionOf(secondary).traverse(secondary.extract(size)) { s -> - directionOf(tertiary).traverse(tertiary.extract(size)) { t -> - yield(origin.add(s, p, t)) - } - } - } - } - Dimension.Z -> { - directionOf(primary).traverse(primary.extract(size)) { p -> - directionOf(secondary).traverse(secondary.extract(size)) { s -> - directionOf(tertiary).traverse(tertiary.extract(size)) { t -> - yield(origin.add(t, p, s)) - } - } - } - } - } - - Dimension.Z -> - when (order.secondary) { - Dimension.X -> { - directionOf(primary).traverse(primary.extract(size)) { p -> - directionOf(secondary).traverse(secondary.extract(size)) { s -> - directionOf(tertiary).traverse(tertiary.extract(size)) { t -> - yield(origin.add(s, t, p)) - } - } - } - } - Dimension.Y -> { - directionOf(primary).traverse(primary.extract(size)) { p -> - directionOf(secondary).traverse(secondary.extract(size)) { s -> - directionOf(tertiary).traverse(tertiary.extract(size)) { t -> - yield(origin.add(t, s, p)) - } - } - } - } - } - } -} - -*/
\ No newline at end of file diff --git a/src/main/kotlin/io/dico/parcels2/blockvisitor/Schematic.kt b/src/main/kotlin/io/dico/parcels2/blockvisitor/Schematic.kt index 9d9a2aa..9f88fd9 100644 --- a/src/main/kotlin/io/dico/parcels2/blockvisitor/Schematic.kt +++ b/src/main/kotlin/io/dico/parcels2/blockvisitor/Schematic.kt @@ -6,12 +6,11 @@ import io.dico.parcels2.util.get import org.bukkit.Bukkit import org.bukkit.Material import org.bukkit.World -import org.bukkit.block.Block +import org.bukkit.block.Sign import org.bukkit.block.data.BlockData private val air = Bukkit.createBlockData(Material.AIR) -// TODO order paste such that attachables are placed after the block they depend on class Schematic { val size: Vec3i get() = _size!! private var _size: Vec3i? = null @@ -21,7 +20,7 @@ class Schematic { } private var blockDatas: Array<BlockData?>? = null - private val extra = mutableMapOf<Vec3i, (Block) -> Unit>() + private val extra = mutableListOf<Pair<Vec3i, ExtraBlockChange>>() private var isLoaded = false; private set private val traverser: RegionTraverser = RegionTraverser.upward @@ -32,7 +31,7 @@ class Schematic { val blocks = traverser.traverseRegion(region) val total = region.blockCount.toDouble() - for ((index, vec) in blocks.withIndex()) { + loop@ for ((index, vec) in blocks.withIndex()) { markSuspensionPoint() setProgress(index / total) @@ -40,6 +39,14 @@ class Schematic { if (block.y > 255) continue val blockData = block.blockData data[index] = blockData + + val extraChange = when (blockData.material) { + Material.SIGN, + Material.WALL_SIGN -> SignStateChange(block.state as Sign) + else -> continue@loop + } + + extra += (vec - region.origin) to extraChange } isLoaded = true @@ -47,53 +54,65 @@ class Schematic { suspend fun WorkerScope.paste(world: World, position: Vec3i) { if (!isLoaded) throw IllegalStateException() + val region = Region(position, _size!!) val blocks = traverser.traverseRegion(region, worldHeight = world.maxHeight) val blockDatas = blockDatas!! var postponed = hashMapOf<Vec3i, BlockData>() - // 90% of the progress of this job is allocated to this code block - delegateWork(0.9) { - for ((index, vec) in blocks.withIndex()) { - markSuspensionPoint() - val block = world[vec] - val type = blockDatas[index] ?: air - if (type !== air && isAttachable(type.material)) { - val supportingBlock = vec + getSupportingBlock(type) - - if (!postponed.containsKey(supportingBlock) && traverser.comesFirst(vec, supportingBlock)) { - block.blockData = type - } else { - postponed[vec] = type - } + val total = region.blockCount.toDouble() + var processed = 0 - } else { + for ((index, vec) in blocks.withIndex()) { + markSuspensionPoint() + setProgress(index / total) + + val block = world[vec] + val type = blockDatas[index] ?: air + if (type !== air && isAttachable(type.material)) { + val supportingBlock = vec + getSupportingBlock(type) + + if (!postponed.containsKey(supportingBlock) && traverser.comesFirst(vec, supportingBlock)) { block.blockData = type + setProgress(++processed / total) + } else { + postponed[vec] = type } + + } else { + block.blockData = type + setProgress(++processed / total) } } - delegateWork { - while (!postponed.isEmpty()) { - val newMap = hashMapOf<Vec3i, BlockData>() - for ((vec, type) in postponed) { - val supportingBlock = vec + getSupportingBlock(type) - if (supportingBlock in postponed && supportingBlock != vec) { - newMap[vec] = type - } else { - world[vec].blockData = type - } + while (!postponed.isEmpty()) { + markSuspensionPoint() + val newMap = hashMapOf<Vec3i, BlockData>() + for ((vec, type) in postponed) { + val supportingBlock = vec + getSupportingBlock(type) + if (supportingBlock in postponed && supportingBlock != vec) { + newMap[vec] = type + } else { + world[vec].blockData = type + setProgress(++processed / total) } - postponed = newMap } + postponed = newMap + } + + // Should be negligible so we don't track progress + for ((vec, extraChange) in extra) { + markSuspensionPoint() + val block = world[position + vec] + extraChange.update(block) } } - fun getLoadTask(world: World, region: Region): TimeLimitedTask = { + fun getLoadTask(world: World, region: Region): WorkerTask = { load(world, region) } - fun getPasteTask(world: World, position: Vec3i): TimeLimitedTask = { + fun getPasteTask(world: World, position: Vec3i): WorkerTask = { paste(world, position) } diff --git a/src/main/kotlin/io/dico/parcels2/blockvisitor/WorktimeLimiter.kt b/src/main/kotlin/io/dico/parcels2/blockvisitor/WorkDispatcher.kt index 553362e..7201a06 100644 --- a/src/main/kotlin/io/dico/parcels2/blockvisitor/WorktimeLimiter.kt +++ b/src/main/kotlin/io/dico/parcels2/blockvisitor/WorkDispatcher.kt @@ -16,17 +16,17 @@ import kotlin.coroutines.intrinsics.COROUTINE_SUSPENDED import kotlin.coroutines.intrinsics.suspendCoroutineUninterceptedOrReturn import kotlin.coroutines.resume -typealias TimeLimitedTask = suspend WorkerScope.() -> Unit +typealias WorkerTask = suspend WorkerScope.() -> Unit typealias WorkerUpdateLister = Worker.(Double, Long) -> Unit data class TickWorktimeOptions(var workTime: Int, var tickInterval: Int) -interface WorktimeLimiter { +interface WorkDispatcher { /** * Submit a [task] that should be run synchronously, but limited such that it does not stall the server * a bunch */ - fun submit(task: TimeLimitedTask): Worker + fun dispatch(task: WorkerTask): Worker /** * Get a list of all workers @@ -39,14 +39,20 @@ interface WorktimeLimiter { fun completeAllTasks() } -interface Timed { +interface WorkerAndScopeMembersUnion { /** * The time that elapsed since this worker was dispatched, in milliseconds */ val elapsedTime: Long + + /** + * A value indicating the progress of this worker, in the range 0.0 <= progress <= 1.0 + * with no guarantees to its accuracy. + */ + val progress: Double } -interface Worker : Timed { +interface Worker : WorkerAndScopeMembersUnion { /** * The coroutine associated with this worker */ @@ -64,12 +70,6 @@ interface Worker : Timed { val completionException: Throwable? /** - * A value indicating the progress of this worker, in the range 0.0 <= progress <= 1.0 - * with no guarantees to its accuracy. - */ - val progress: Double - - /** * Calls the given [block] whenever the progress of this worker is updated, * if [minInterval] milliseconds expired since the last call. * The first call occurs after at least [minDelay] milliseconds in a likewise manner. @@ -96,19 +96,13 @@ interface Worker : Timed { //val attachment: Any? } -interface WorkerScope : Timed { +interface WorkerScope : WorkerAndScopeMembersUnion { /** * A task should call this frequently during its execution, such that the timer can suspend it when necessary. */ suspend fun markSuspensionPoint() /** - * A value indicating the progress of this worker, in the range 0.0 <= progress <= 1.0 - * with no guarantees to its accuracy. - */ - val progress: Double - - /** * A task should call this method to indicate its progress */ fun setProgress(progress: Double) @@ -152,14 +146,14 @@ interface WorkerInternal : Worker, WorkerScope { * There is a configurable maxiumum amount of milliseconds that can be allocated to all workers together in each server tick * This object attempts to split that maximum amount of milliseconds equally between all jobs */ -class TickWorktimeLimiter(private val plugin: ParcelsPlugin, var options: TickWorktimeOptions) : WorktimeLimiter { +class BukkitWorkDispatcher(private val plugin: ParcelsPlugin, var options: TickWorktimeOptions) : WorkDispatcher { // The currently registered bukkit scheduler task private var bukkitTask: BukkitTask? = null // The workers. private val _workers = LinkedList<WorkerInternal>() override val workers: List<Worker> = _workers - override fun submit(task: TimeLimitedTask): Worker { + override fun dispatch(task: WorkerTask): Worker { val worker: WorkerInternal = WorkerImpl(plugin, task) if (bukkitTask == null) { @@ -209,7 +203,7 @@ class TickWorktimeLimiter(private val plugin: ParcelsPlugin, var options: TickWo } -private class WorkerImpl(scope: CoroutineScope, task: TimeLimitedTask) : WorkerInternal { +private class WorkerImpl(scope: CoroutineScope, task: WorkerTask) : WorkerInternal { override val job: Job = scope.launch(start = LAZY) { task() } private var continuation: Continuation<Unit>? = null @@ -239,7 +233,7 @@ private class WorkerImpl(scope: CoroutineScope, task: TimeLimitedTask) : WorkerI // report any error that occurred completionException = exception?.also { if (it !is CancellationException) - logger.error("TimeLimitedTask generated an exception", it) + logger.error("WorkerTask generated an exception", it) } // convert to elapsed time here @@ -316,6 +310,7 @@ private class WorkerImpl(scope: CoroutineScope, task: TimeLimitedTask) : WorkerI return true } + isStarted = true startTimeOrElapsedTime = System.currentTimeMillis() job.start() @@ -348,14 +343,3 @@ private class WorkerImpl(scope: CoroutineScope, task: TimeLimitedTask) : WorkerI this@WorkerImpl.delegateWork(this.portion, portion) } } - -/* -/** - * While the implementation of [kotlin.coroutines.experimental.intrinsics.intercepted] is intrinsic, it should look something like this - * We don't care for intercepting the coroutine as we want it to resume immediately when we call resume(). - * Thus, above, we use an unintercepted suspension. It's not necessary as the dispatcher (or interceptor) also calls it synchronously, but whatever. - */ -private fun <T> Continuation<T>.interceptedImpl(): Continuation<T> { - return context[ContinuationInterceptor]?.interceptContinuation(this) ?: this -} - */ diff --git a/src/main/kotlin/io/dico/parcels2/command/CommandsDebug.kt b/src/main/kotlin/io/dico/parcels2/command/CommandsDebug.kt index 969d964..b6c7acd 100644 --- a/src/main/kotlin/io/dico/parcels2/command/CommandsDebug.kt +++ b/src/main/kotlin/io/dico/parcels2/command/CommandsDebug.kt @@ -8,7 +8,6 @@ import io.dico.dicore.command.annotation.Cmd import io.dico.parcels2.ParcelsPlugin import io.dico.parcels2.Privilege import io.dico.parcels2.blockvisitor.RegionTraverser -import io.dico.parcels2.blockvisitor.TickWorktimeLimiter import io.dico.parcels2.doBlockOperation import org.bukkit.Bukkit import org.bukkit.Material @@ -78,15 +77,15 @@ class CommandsDebug(plugin: ParcelsPlugin) : AbstractParcelCommands(plugin) { @Cmd("visitors") fun cmdVisitors(): Any? { - val workers = plugin.worktimeLimiter.workers + val workers = plugin.workDispatcher.workers println(workers.map { it.job }.joinToString(separator = "\n")) return "Task count: ${workers.size}" } @Cmd("force_visitors") fun cmdForceVisitors(): Any? { - val workers = plugin.worktimeLimiter.workers - plugin.worktimeLimiter.completeAllTasks() + val workers = plugin.workDispatcher.workers + plugin.workDispatcher.completeAllTasks() return "Task count: ${workers.size}" } diff --git a/src/main/kotlin/io/dico/parcels2/command/CommandsGeneral.kt b/src/main/kotlin/io/dico/parcels2/command/CommandsGeneral.kt index 750fd96..67b873b 100644 --- a/src/main/kotlin/io/dico/parcels2/command/CommandsGeneral.kt +++ b/src/main/kotlin/io/dico/parcels2/command/CommandsGeneral.kt @@ -16,7 +16,7 @@ import io.dico.parcels2.util.ext.uuid import org.bukkit.block.Biome import org.bukkit.entity.Player -class CommandsGeneral(plugin: ParcelsPlugin) : AbstractParcelCommands(plugin) { +class CommandsGeneral(plugin: ParcelsPlugin, parent: SpecialCommandAddress) : AbstractParcelCommands(plugin) { @Cmd("auto") @Desc( @@ -43,6 +43,10 @@ class CommandsGeneral(plugin: ParcelsPlugin) : AbstractParcelCommands(plugin) { ) fun ParcelScope.cmdInfo(player: Player) = parcel.infoString + init { + parent.addSpeciallyTreatedKeys("home", "h") + } + @Cmd("home", aliases = ["h"]) @Desc( "Teleports you to your parcels,", diff --git a/src/main/kotlin/io/dico/parcels2/command/ParcelCommandBuilder.kt b/src/main/kotlin/io/dico/parcels2/command/ParcelCommandBuilder.kt index 1eddf97..1f1e4a7 100644 --- a/src/main/kotlin/io/dico/parcels2/command/ParcelCommandBuilder.kt +++ b/src/main/kotlin/io/dico/parcels2/command/ParcelCommandBuilder.kt @@ -1,8 +1,6 @@ package io.dico.parcels2.command -import io.dico.dicore.command.CommandBuilder -import io.dico.dicore.command.ICommandAddress -import io.dico.dicore.command.ICommandDispatcher +import io.dico.dicore.command.* import io.dico.dicore.command.registration.reflect.ReflectiveRegistration import io.dico.parcels2.Interactables import io.dico.parcels2.ParcelsPlugin @@ -13,14 +11,16 @@ import java.util.Queue @Suppress("UsePropertyAccessSyntax") fun getParcelCommands(plugin: ParcelsPlugin): ICommandDispatcher = with(CommandBuilder()) { + val parcelsAddress = SpecialCommandAddress() + setChatController(ParcelsChatController()) addParameterType(false, ParcelParameterType(plugin.parcelProvider)) addParameterType(false, ProfileParameterType()) - addParameterType(true, ParcelTarget.PType(plugin.parcelProvider)) + addParameterType(true, ParcelTarget.PType(plugin.parcelProvider, parcelsAddress)) - group("parcel", "plot", "plots", "p") { + group(parcelsAddress, "parcel", "plot", "plots", "p") { addRequiredPermission("parcels.command") - registerCommands(CommandsGeneral(plugin)) + registerCommands(CommandsGeneral(plugin, parcelsAddress)) registerCommands(CommandsPrivilegesLocal(plugin)) group("option", "opt", "o") { @@ -63,6 +63,12 @@ inline fun CommandBuilder.group(name: String, vararg aliases: String, config: Co parent() } +inline fun CommandBuilder.group(address: ICommandAddress, name: String, vararg aliases: String, config: CommandBuilder.() -> Unit) { + group(address, name, *aliases) + config() + parent() +} + private fun CommandBuilder.generateHelpAndSyntaxCommands(): CommandBuilder { generateCommands(dispatcher as ICommandAddress, "help", "syntax") return this @@ -80,3 +86,37 @@ private fun generateCommands(address: ICommandAddress, vararg names: String) { } } } + +class SpecialCommandAddress : ChildCommandAddress() { + private val speciallyTreatedKeys = mutableListOf<String>() + + // Used to allow /p h:1 syntax, which is the same as what PlotMe uses. + var speciallyParsedIndex: Int? = null; private set + + fun addSpeciallyTreatedKeys(vararg keys: String) { + for (key in keys) { + speciallyTreatedKeys.add(key + ":") + } + } + + @Throws(CommandException::class) + override fun getChild(key: String, context: ExecutionContext): ChildCommandAddress? { + speciallyParsedIndex = null + + for (specialKey in speciallyTreatedKeys) { + if (key.startsWith(specialKey)) { + val result = getChild(specialKey.substring(0, specialKey.length - 1)) + ?: return null + + val text = key.substring(specialKey.length) + val num = text.toIntOrNull() ?: throw CommandException("$text is not a number") + speciallyParsedIndex = num + + return result + } + } + + return super.getChild(key) + } + +} diff --git a/src/main/kotlin/io/dico/parcels2/command/ParcelTarget.kt b/src/main/kotlin/io/dico/parcels2/command/ParcelTarget.kt index 956da94..8c0d718 100644 --- a/src/main/kotlin/io/dico/parcels2/command/ParcelTarget.kt +++ b/src/main/kotlin/io/dico/parcels2/command/ParcelTarget.kt @@ -83,7 +83,7 @@ sealed class ParcelTarget(val world: ParcelWorld, val parsedKind: Int, val isDef } } - class PType(val parcelProvider: ParcelProvider) : ParameterType<ParcelTarget, Int>(ParcelTarget::class.java, Config) { + class PType(val parcelProvider: ParcelProvider, val parcelAddress: SpecialCommandAddress? = null) : ParameterType<ParcelTarget, Int>(ParcelTarget::class.java, Config) { override fun parse(parameter: Parameter<ParcelTarget, Int>, sender: CommandSender, buffer: ArgumentBuffer): ParcelTarget { var input = buffer.next() @@ -124,11 +124,25 @@ sealed class ParcelTarget(val world: ParcelWorld, val parsedKind: Int, val isDef val ownerString: String val index: Int? + val speciallyParsedIndex = parcelAddress?.speciallyParsedIndex + if (splitIdx == -1) { - // just the index. - index = input.toIntOrNull() - ownerString = if (index == null) input else "" + + if (speciallyParsedIndex == null) { + // just the index. + index = input.toIntOrNull() + ownerString = if (index == null) input else "" + } else { + // just the owner. + index = speciallyParsedIndex + ownerString = input + } + } else { + if (speciallyParsedIndex != null) { + invalidInput(parameter, "Duplicate home index") + } + ownerString = input.substring(0, splitIdx) val indexString = input.substring(splitIdx + 1) @@ -165,7 +179,7 @@ sealed class ParcelTarget(val world: ParcelWorld, val parsedKind: Int, val isDef return ByID(world, id, kind, true) } - return ByOwner(world, PlayerProfile(player), 0, kind, true) + return ByOwner(world, PlayerProfile(player), parcelAddress?.speciallyParsedIndex ?: 0, kind, true) } } diff --git a/src/main/kotlin/io/dico/parcels2/defaultimpl/DefaultParcelGenerator.kt b/src/main/kotlin/io/dico/parcels2/defaultimpl/DefaultParcelGenerator.kt index dddb221..90cadc6 100644 --- a/src/main/kotlin/io/dico/parcels2/defaultimpl/DefaultParcelGenerator.kt +++ b/src/main/kotlin/io/dico/parcels2/defaultimpl/DefaultParcelGenerator.kt @@ -119,9 +119,9 @@ class DefaultParcelGenerator( worldId: ParcelWorldId, container: ParcelContainer, coroutineScope: CoroutineScope, - worktimeLimiter: WorktimeLimiter + workDispatcher: WorkDispatcher ): Pair<ParcelLocator, ParcelBlockManager> { - return ParcelLocatorImpl(worldId, container) to ParcelBlockManagerImpl(worldId, coroutineScope, worktimeLimiter) + return ParcelLocatorImpl(worldId, container) to ParcelBlockManagerImpl(worldId, coroutineScope, workDispatcher) } private inline fun <T> convertBlockLocationToId(x: Int, z: Int, mapper: (Int, Int) -> T): T? { @@ -156,7 +156,7 @@ class DefaultParcelGenerator( private inner class ParcelBlockManagerImpl( val worldId: ParcelWorldId, coroutineScope: CoroutineScope, - override val worktimeLimiter: WorktimeLimiter + override val workDispatcher: WorkDispatcher ) : ParcelBlockManagerBase(), CoroutineScope by coroutineScope { override val world: World = this@DefaultParcelGenerator.world override val parcelTraverser: RegionTraverser = RegionTraverser.convergingTo(o.floorHeight) @@ -222,12 +222,12 @@ class DefaultParcelGenerator( return world.getParcelById(parcelId) } - override fun submitBlockVisitor(vararg parcelIds: ParcelId, task: TimeLimitedTask): Worker { + override fun submitBlockVisitor(vararg parcelIds: ParcelId, task: WorkerTask): Worker { val parcels = parcelIds.mapNotNull { getParcel(it) } - if (parcels.isEmpty()) return worktimeLimiter.submit(task) + if (parcels.isEmpty()) return workDispatcher.dispatch(task) if (parcels.any { it.hasBlockVisitors }) throw IllegalArgumentException("This parcel already has a block visitor") - val worker = worktimeLimiter.submit(task) + val worker = workDispatcher.dispatch(task) for (parcel in parcels) { launch(start = UNDISPATCHED) { @@ -277,10 +277,10 @@ class DefaultParcelGenerator( } override fun swapParcels(parcel1: ParcelId, parcel2: ParcelId): Worker = submitBlockVisitor(parcel1, parcel2) { - val schematicOf1 = delegateWork(0.15) { Schematic().apply { load(world, getRegion(parcel1)) } } - val schematicOf2 = delegateWork(0.15) { Schematic().apply { load(world, getRegion(parcel2)) } } - delegateWork(0.35) { with(schematicOf1) { paste(world, getRegion(parcel2).origin) } } - delegateWork(0.35) { with(schematicOf2) { paste(world, getRegion(parcel1).origin) } } + val schematicOf1 = delegateWork(0.25) { Schematic().apply { load(world, getRegion(parcel1)) } } + val schematicOf2 = delegateWork(0.25) { Schematic().apply { load(world, getRegion(parcel2)) } } + delegateWork(0.25) { with(schematicOf1) { paste(world, getRegion(parcel2).origin) } } + delegateWork(0.25) { with(schematicOf2) { paste(world, getRegion(parcel1).origin) } } } override fun getParcelsWithOwnerBlockIn(chunk: Chunk): Collection<Vec2i> { diff --git a/src/main/kotlin/io/dico/parcels2/defaultimpl/ParcelProviderImpl.kt b/src/main/kotlin/io/dico/parcels2/defaultimpl/ParcelProviderImpl.kt index 1112047..ff2fcd4 100644 --- a/src/main/kotlin/io/dico/parcels2/defaultimpl/ParcelProviderImpl.kt +++ b/src/main/kotlin/io/dico/parcels2/defaultimpl/ParcelProviderImpl.kt @@ -59,7 +59,7 @@ class ParcelProviderImpl(val plugin: ParcelsPlugin) : ParcelProvider { else WorldCreator(worldName).generator(generator).createWorld().also { logger.info("Creating world $worldName") } parcelWorld = ParcelWorldImpl(bukkitWorld, generator, worldOptions.runtime, plugin.storage, - plugin.globalPrivileges, ::DefaultParcelContainer, plugin, plugin.worktimeLimiter) + plugin.globalPrivileges, ::DefaultParcelContainer, plugin, plugin.workDispatcher) if (!worldExists) { val time = DateTime.now() diff --git a/src/main/kotlin/io/dico/parcels2/defaultimpl/ParcelWorldImpl.kt b/src/main/kotlin/io/dico/parcels2/defaultimpl/ParcelWorldImpl.kt index c143ff6..24bad79 100644 --- a/src/main/kotlin/io/dico/parcels2/defaultimpl/ParcelWorldImpl.kt +++ b/src/main/kotlin/io/dico/parcels2/defaultimpl/ParcelWorldImpl.kt @@ -3,7 +3,7 @@ package io.dico.parcels2.defaultimpl import io.dico.parcels2.* -import io.dico.parcels2.blockvisitor.WorktimeLimiter +import io.dico.parcels2.blockvisitor.WorkDispatcher import io.dico.parcels2.options.RuntimeWorldOptions import io.dico.parcels2.storage.Storage import kotlinx.coroutines.CoroutineScope @@ -18,7 +18,7 @@ class ParcelWorldImpl(override val world: World, override val globalPrivileges: GlobalPrivilegesManager, containerFactory: ParcelContainerFactory, coroutineScope: CoroutineScope, - worktimeLimiter: WorktimeLimiter) + workDispatcher: WorkDispatcher) : ParcelWorld, ParcelWorldId, ParcelContainer, /* missing delegation */ @@ -39,7 +39,7 @@ class ParcelWorldImpl(override val world: World, override val blockManager: ParcelBlockManager init { - val pair = generator.makeParcelLocatorAndBlockManager(id, container, coroutineScope, worktimeLimiter) + val pair = generator.makeParcelLocatorAndBlockManager(id, container, coroutineScope, workDispatcher) locator = pair.first blockManager = pair.second diff --git a/src/main/kotlin/io/dico/parcels2/listener/ParcelListeners.kt b/src/main/kotlin/io/dico/parcels2/listener/ParcelListeners.kt index 9d91cda..f838950 100644 --- a/src/main/kotlin/io/dico/parcels2/listener/ParcelListeners.kt +++ b/src/main/kotlin/io/dico/parcels2/listener/ParcelListeners.kt @@ -198,8 +198,8 @@ class ParcelListeners( val type = clickedBlock.type val interactableClass = Interactables[type] - if (interactableClass != null && (parcel.effectiveInteractableConfig.isInteractable(type) || (parcel != null && parcel.canBuild(user)))) { - user.sendParcelMessage(nopermit = true, message = "You cannot interact with ${interactableClass.name} in this parcel") + if (interactableClass != null && !parcel.effectiveInteractableConfig.isInteractable(type) && (parcel == null || !parcel.canBuild(user))) { + user.sendParcelMessage(nopermit = true, message = "You cannot interact with ${interactableClass.name} here") event.isCancelled = true return@l } @@ -595,4 +595,16 @@ class ParcelListeners( storage.updatePlayerName(event.player.uuid, event.player.name) } + /** + * Attempts to prevent redstone contraptions from breaking while they are being swapped + * Might remove if it causes lag + */ + @ListenerMarker + val onBlockRedstoneEvent = RegistratorListener<BlockRedstoneEvent> l@{ event -> + val (_, area) = getWorldAndArea(event.block) ?: return@l + if (area == null || area.hasBlockVisitors) { + event.newCurrent = event.oldCurrent + } + } + }
\ No newline at end of file diff --git a/src/main/kotlin/io/dico/parcels2/util/Vec3i.kt b/src/main/kotlin/io/dico/parcels2/util/Vec3i.kt index 36a51c1..af71dc4 100644 --- a/src/main/kotlin/io/dico/parcels2/util/Vec3i.kt +++ b/src/main/kotlin/io/dico/parcels2/util/Vec3i.kt @@ -26,6 +26,7 @@ data class Vec3i( val z: Int ) { operator fun plus(o: Vec3i) = Vec3i(x + o.x, y + o.y, z + o.z) + operator fun minus(o: Vec3i) = Vec3i(x - o.x, y - o.y, z - o.z) 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) @@ -6,16 +6,16 @@ Basically all admin commands. * ~~setowner~~ * ~~dispose~~ * ~~reset~~ -* swap +* ~~swap~~ * New admin commands that I can't think of right now. Also * ~~setbiome~~ * random -Modify home command: +~~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 +* ~~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).~~ @@ -23,32 +23,34 @@ to allow inheriting permissions properly).~~ Parcel Options - -Parcel options apply to any player with `DEFAULT` added status. +Parcel options apply to any player with `DEFAULT` added status. They affect what their permissions might be within the parcel. Apart from `/p option inputs`, `/p option inventory`, the following might be considered. -Move existing options to "interact" namespace (`/p o interact`) +~~Move existing options to "interact" namespace (`/p o interact`) +Add classes for different things you can interact with~~ -Then, -* Split `/p option interact inputs` into a list of interactible block types. -The list could include container blocks, merging the existing inventory option. -* Players cannot launch projectiles in locations where they can't build. +~~Then,~~ +~~* Split `/p option interact inputs` into a list of interactible block types.~~ +~~The list could include container blocks, merging the existing inventory option.~~ +* Players cannot launch projectiles in locations where they can't build.~~ This could become optional. -* Option to control spreading and/or forming of blocks such as grass and ice within the parcel. + +* Option to control spreading and/or forming of blocks such as grass and ice within the parcel.~~ 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.~~ -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. -In general, spamming the commands must be caught at all cost to avoid lots of lag. +In general, spamming the commands must be caught at all cost to avoid lots of lag.~~ -Swap - schematic is in place, but proper placement order must be enforced to make sure that attachable +~~Swap - schematic is in place, but proper placement order must be enforced to make sure that attachable 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. This could apply to clearing of plots, for example. It would be better if the bottom 64 (floor height) @@ -79,6 +81,6 @@ Implement a container that doesn't require loading all parcel data on startup (C ~~Update player profiles in the database on join to account for name changes.~~ -Store player status on parcel (allowed, default banned) as a number to allow for future additions to this set of possibilities +~~Store player status on parcel (allowed, default banned) as a number to allow for future additions to this set of possibilities~~ |