summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorDico <dico.karssiens@gmail.com>2018-09-26 07:08:42 +0100
committerDico <dico.karssiens@gmail.com>2018-09-26 07:08:42 +0100
commit520ae530d2de076fa9e87da7f04fcf78e080f4de (patch)
treedafbe3c14d5b6cbe49eef4f87499d35d1c69676d
parent2225bdae95b3de4985347edf30ae31a28f73f35b (diff)
Make progress
-rw-r--r--dicore3/command/src/main/java/io/dico/dicore/command/ChildCommandAddress.java13
-rw-r--r--dicore3/command/src/main/java/io/dico/dicore/command/Command.java51
-rw-r--r--dicore3/command/src/main/java/io/dico/dicore/command/CommandBuilder.java28
-rw-r--r--dicore3/command/src/main/java/io/dico/dicore/command/ExecutionContext.java43
-rw-r--r--dicore3/command/src/main/java/io/dico/dicore/command/ICommandAddress.java10
-rw-r--r--dicore3/command/src/main/java/io/dico/dicore/command/ICommandDispatcher.java15
-rw-r--r--dicore3/command/src/main/java/io/dico/dicore/command/ModifiableCommandAddress.java5
-rw-r--r--dicore3/command/src/main/java/io/dico/dicore/command/RootCommandAddress.java103
-rw-r--r--dicore3/command/src/main/java/io/dico/dicore/command/registration/reflect/ReflectiveRegistration.java10
-rw-r--r--src/main/kotlin/io/dico/parcels2/ParcelGenerator.kt6
-rw-r--r--src/main/kotlin/io/dico/parcels2/ParcelsPlugin.kt12
-rw-r--r--src/main/kotlin/io/dico/parcels2/blockvisitor/ExtraBlockChange.kt38
-rw-r--r--src/main/kotlin/io/dico/parcels2/blockvisitor/RegionTraverser.kt84
-rw-r--r--src/main/kotlin/io/dico/parcels2/blockvisitor/Schematic.kt83
-rw-r--r--src/main/kotlin/io/dico/parcels2/blockvisitor/WorkDispatcher.kt (renamed from src/main/kotlin/io/dico/parcels2/blockvisitor/WorktimeLimiter.kt)50
-rw-r--r--src/main/kotlin/io/dico/parcels2/command/CommandsDebug.kt7
-rw-r--r--src/main/kotlin/io/dico/parcels2/command/CommandsGeneral.kt6
-rw-r--r--src/main/kotlin/io/dico/parcels2/command/ParcelCommandBuilder.kt52
-rw-r--r--src/main/kotlin/io/dico/parcels2/command/ParcelTarget.kt24
-rw-r--r--src/main/kotlin/io/dico/parcels2/defaultimpl/DefaultParcelGenerator.kt20
-rw-r--r--src/main/kotlin/io/dico/parcels2/defaultimpl/ParcelProviderImpl.kt2
-rw-r--r--src/main/kotlin/io/dico/parcels2/defaultimpl/ParcelWorldImpl.kt6
-rw-r--r--src/main/kotlin/io/dico/parcels2/listener/ParcelListeners.kt16
-rw-r--r--src/main/kotlin/io/dico/parcels2/util/Vec3i.kt1
-rw-r--r--todo.md32
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)
diff --git a/todo.md b/todo.md
index 93e9c02..03d491b 100644
--- a/todo.md
+++ b/todo.md
@@ -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~~