diff --git a/BauSystem_Main/build.gradle b/BauSystem_Main/build.gradle
index 3f4576bc..afbca351 100644
--- a/BauSystem_Main/build.gradle
+++ b/BauSystem_Main/build.gradle
@@ -62,4 +62,6 @@ dependencies {
compileOnly swdep('WorldEdit-1.15')
compileOnly swdep('SpigotCore')
annotationProcessor swdep('SpigotCore')
-}
+
+ compileOnly swdep('FastAsyncWorldEdit-1.18')
+}
\ No newline at end of file
diff --git a/BauSystem_Main/src/de/steamwar/bausystem/features/worldedit/ColorReplaceCommand.java b/BauSystem_Main/src/de/steamwar/bausystem/features/worldedit/ColorReplaceCommand.java
index 1a4b4850..849d5891 100644
--- a/BauSystem_Main/src/de/steamwar/bausystem/features/worldedit/ColorReplaceCommand.java
+++ b/BauSystem_Main/src/de/steamwar/bausystem/features/worldedit/ColorReplaceCommand.java
@@ -27,6 +27,7 @@ import com.sk89q.worldedit.util.formatting.text.Component;
import com.sk89q.worldedit.util.formatting.text.TextComponent;
import com.sk89q.worldedit.util.formatting.text.TranslatableComponent;
import de.steamwar.bausystem.features.world.WorldEditListener;
+import de.steamwar.bausystem.features.worldedit.utils.SpecialReplace;
import de.steamwar.bausystem.region.Color;
import de.steamwar.bausystem.shared.Pair;
import de.steamwar.bausystem.utils.WorldEditUtils;
diff --git a/BauSystem_Main/src/de/steamwar/bausystem/features/worldedit/TypeReplaceCommand.java b/BauSystem_Main/src/de/steamwar/bausystem/features/worldedit/TypeReplaceCommand.java
index 4535a241..93b784b7 100644
--- a/BauSystem_Main/src/de/steamwar/bausystem/features/worldedit/TypeReplaceCommand.java
+++ b/BauSystem_Main/src/de/steamwar/bausystem/features/worldedit/TypeReplaceCommand.java
@@ -27,6 +27,7 @@ import com.sk89q.worldedit.util.formatting.text.Component;
import com.sk89q.worldedit.util.formatting.text.TextComponent;
import com.sk89q.worldedit.util.formatting.text.TranslatableComponent;
import de.steamwar.bausystem.features.world.WorldEditListener;
+import de.steamwar.bausystem.features.worldedit.utils.SpecialReplace;
import de.steamwar.bausystem.shared.Pair;
import de.steamwar.bausystem.utils.WorldEditUtils;
import de.steamwar.command.SWCommand;
diff --git a/BauSystem_Main/src/de/steamwar/bausystem/features/worldedit/mask/above/AboveMask.java b/BauSystem_Main/src/de/steamwar/bausystem/features/worldedit/mask/above/AboveMask.java
new file mode 100644
index 00000000..34a5a4c1
--- /dev/null
+++ b/BauSystem_Main/src/de/steamwar/bausystem/features/worldedit/mask/above/AboveMask.java
@@ -0,0 +1,49 @@
+/*
+ * This file is a part of the SteamWar software.
+ *
+ * Copyright (C) 2022 SteamWar.de-Serverteam
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program. If not, see .
+ */
+
+package de.steamwar.bausystem.features.worldedit.mask.above;
+
+import com.sk89q.worldedit.function.mask.Mask;
+import com.sk89q.worldedit.math.BlockVector3;
+
+public class AboveMask implements Mask {
+
+ private Mask mask;
+ private int distance;
+
+ public AboveMask(Mask mask, int distance) {
+ this.mask = mask;
+ this.distance = distance;
+ }
+
+ @Override
+ public boolean test(BlockVector3 vector) {
+ for (int i = 1; i <= distance; i++) {
+ if (mask.test(vector.subtract(0, i, 0))) {
+ return true;
+ }
+ }
+ return false;
+ }
+
+ @Override
+ public Mask copy() {
+ return new AboveMask(mask, distance);
+ }
+}
diff --git a/BauSystem_Main/src/de/steamwar/bausystem/features/worldedit/mask/above/FAWEAboveMaskParser.java b/BauSystem_Main/src/de/steamwar/bausystem/features/worldedit/mask/above/FAWEAboveMaskParser.java
new file mode 100644
index 00000000..dc9476c6
--- /dev/null
+++ b/BauSystem_Main/src/de/steamwar/bausystem/features/worldedit/mask/above/FAWEAboveMaskParser.java
@@ -0,0 +1,70 @@
+/*
+ * This file is a part of the SteamWar software.
+ *
+ * Copyright (C) 2022 SteamWar.de-Serverteam
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program. If not, see .
+ */
+
+package de.steamwar.bausystem.features.worldedit.mask.above;
+
+import com.fastasyncworldedit.core.extension.factory.parser.RichParser;
+import com.sk89q.worldedit.WorldEdit;
+import com.sk89q.worldedit.command.util.SuggestionHelper;
+import com.sk89q.worldedit.extension.input.InputParseException;
+import com.sk89q.worldedit.extension.input.ParserContext;
+import com.sk89q.worldedit.function.mask.Mask;
+import de.steamwar.bausystem.utils.WorldEditUtils;
+import de.steamwar.linkage.Linked;
+import de.steamwar.linkage.PluginCheck;
+import de.steamwar.linkage.api.Plain;
+
+import javax.annotation.Nonnull;
+import java.util.stream.Stream;
+
+@Linked
+@PluginCheck("FastAsyncWorldEdit")
+public class FAWEAboveMaskParser extends RichParser implements Plain {
+
+ public FAWEAboveMaskParser() {
+ super(WorldEdit.getInstance(), "#above");
+ WorldEditUtils.addMaskParser(this);
+ }
+
+ @Override
+ protected Stream getSuggestions(String argumentInput, int index) {
+ if (index == 0) {
+ return WorldEdit.getInstance().getPatternFactory().getSuggestions(argumentInput).stream();
+ }
+ if (index == 1) {
+ return SuggestionHelper.suggestPositiveIntegers(argumentInput);
+ }
+ return Stream.empty();
+ }
+
+ @Override
+ protected Mask parseFromInput(@Nonnull String[] arguments, ParserContext context) throws InputParseException {
+ if (arguments.length != 2) {
+ throw new InputParseException("Expected 2 arguments");
+ }
+ Mask mask = WorldEdit.getInstance().getMaskFactory().parseFromInput(arguments[0], context);
+ int distance;
+ try {
+ distance = Integer.parseInt(arguments[1]);
+ } catch (NumberFormatException e) {
+ throw new InputParseException("Distance must be a number");
+ }
+ return new AboveMask(mask, distance);
+ }
+}
diff --git a/BauSystem_Main/src/de/steamwar/bausystem/features/worldedit/mask/below/BelowMask.java b/BauSystem_Main/src/de/steamwar/bausystem/features/worldedit/mask/below/BelowMask.java
new file mode 100644
index 00000000..7407b51a
--- /dev/null
+++ b/BauSystem_Main/src/de/steamwar/bausystem/features/worldedit/mask/below/BelowMask.java
@@ -0,0 +1,49 @@
+/*
+ * This file is a part of the SteamWar software.
+ *
+ * Copyright (C) 2022 SteamWar.de-Serverteam
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program. If not, see .
+ */
+
+package de.steamwar.bausystem.features.worldedit.mask.below;
+
+import com.sk89q.worldedit.function.mask.Mask;
+import com.sk89q.worldedit.math.BlockVector3;
+
+public class BelowMask implements Mask {
+
+ private Mask mask;
+ private int distance;
+
+ public BelowMask(Mask mask, int distance) {
+ this.mask = mask;
+ this.distance = distance;
+ }
+
+ @Override
+ public boolean test(BlockVector3 vector) {
+ for (int i = 1; i <= distance; i++) {
+ if (mask.test(vector.add(0, i, 0))) {
+ return true;
+ }
+ }
+ return false;
+ }
+
+ @Override
+ public Mask copy() {
+ return new BelowMask(mask, distance);
+ }
+}
diff --git a/BauSystem_Main/src/de/steamwar/bausystem/features/worldedit/mask/below/FAWEBelowMaskParser.java b/BauSystem_Main/src/de/steamwar/bausystem/features/worldedit/mask/below/FAWEBelowMaskParser.java
new file mode 100644
index 00000000..2de798c4
--- /dev/null
+++ b/BauSystem_Main/src/de/steamwar/bausystem/features/worldedit/mask/below/FAWEBelowMaskParser.java
@@ -0,0 +1,70 @@
+/*
+ * This file is a part of the SteamWar software.
+ *
+ * Copyright (C) 2022 SteamWar.de-Serverteam
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program. If not, see .
+ */
+
+package de.steamwar.bausystem.features.worldedit.mask.below;
+
+import com.fastasyncworldedit.core.extension.factory.parser.RichParser;
+import com.sk89q.worldedit.WorldEdit;
+import com.sk89q.worldedit.command.util.SuggestionHelper;
+import com.sk89q.worldedit.extension.input.InputParseException;
+import com.sk89q.worldedit.extension.input.ParserContext;
+import com.sk89q.worldedit.function.mask.Mask;
+import de.steamwar.bausystem.utils.WorldEditUtils;
+import de.steamwar.linkage.Linked;
+import de.steamwar.linkage.PluginCheck;
+import de.steamwar.linkage.api.Plain;
+
+import javax.annotation.Nonnull;
+import java.util.stream.Stream;
+
+@Linked
+@PluginCheck("FastAsyncWorldEdit")
+public class FAWEBelowMaskParser extends RichParser implements Plain {
+
+ public FAWEBelowMaskParser() {
+ super(WorldEdit.getInstance(), "#below");
+ WorldEditUtils.addMaskParser(this);
+ }
+
+ @Override
+ protected Stream getSuggestions(String argumentInput, int index) {
+ if (index == 0) {
+ return WorldEdit.getInstance().getPatternFactory().getSuggestions(argumentInput).stream();
+ }
+ if (index == 1) {
+ return SuggestionHelper.suggestPositiveIntegers(argumentInput);
+ }
+ return Stream.empty();
+ }
+
+ @Override
+ protected Mask parseFromInput(@Nonnull String[] arguments, ParserContext context) throws InputParseException {
+ if (arguments.length != 2) {
+ throw new InputParseException("Expected 2 arguments");
+ }
+ Mask mask = WorldEdit.getInstance().getMaskFactory().parseFromInput(arguments[0], context);
+ int distance;
+ try {
+ distance = Integer.parseInt(arguments[1]);
+ } catch (NumberFormatException e) {
+ throw new InputParseException("Distance must be a number");
+ }
+ return new BelowMask(mask, distance);
+ }
+}
diff --git a/BauSystem_Main/src/de/steamwar/bausystem/features/worldedit/mask/checkerboard/CheckerboardMask.java b/BauSystem_Main/src/de/steamwar/bausystem/features/worldedit/mask/checkerboard/CheckerboardMask.java
new file mode 100644
index 00000000..646b169c
--- /dev/null
+++ b/BauSystem_Main/src/de/steamwar/bausystem/features/worldedit/mask/checkerboard/CheckerboardMask.java
@@ -0,0 +1,42 @@
+/*
+ * This file is a part of the SteamWar software.
+ *
+ * Copyright (C) 2022 SteamWar.de-Serverteam
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program. If not, see .
+ */
+
+package de.steamwar.bausystem.features.worldedit.mask.checkerboard;
+
+import com.sk89q.worldedit.function.mask.Mask;
+import com.sk89q.worldedit.math.BlockVector3;
+
+public class CheckerboardMask implements Mask {
+
+ private int size;
+
+ public CheckerboardMask(int size) {
+ this.size = size;
+ }
+
+ @Override
+ public boolean test(BlockVector3 vector) {
+ return (vector.getBlockX() / size + vector.getBlockZ() / size) % 2 == 0;
+ }
+
+ @Override
+ public Mask copy() {
+ return new CheckerboardMask(size);
+ }
+}
diff --git a/BauSystem_Main/src/de/steamwar/bausystem/features/worldedit/mask/checkerboard/FAWECheckerboardMaskParser.java b/BauSystem_Main/src/de/steamwar/bausystem/features/worldedit/mask/checkerboard/FAWECheckerboardMaskParser.java
new file mode 100644
index 00000000..fe4984e0
--- /dev/null
+++ b/BauSystem_Main/src/de/steamwar/bausystem/features/worldedit/mask/checkerboard/FAWECheckerboardMaskParser.java
@@ -0,0 +1,64 @@
+/*
+ * This file is a part of the SteamWar software.
+ *
+ * Copyright (C) 2022 SteamWar.de-Serverteam
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program. If not, see .
+ */
+
+package de.steamwar.bausystem.features.worldedit.mask.checkerboard;
+
+import com.fastasyncworldedit.core.extension.factory.parser.RichParser;
+import com.sk89q.worldedit.WorldEdit;
+import com.sk89q.worldedit.command.util.SuggestionHelper;
+import com.sk89q.worldedit.extension.input.InputParseException;
+import com.sk89q.worldedit.extension.input.ParserContext;
+import com.sk89q.worldedit.function.mask.Mask;
+import de.steamwar.bausystem.utils.WorldEditUtils;
+import de.steamwar.linkage.Linked;
+import de.steamwar.linkage.PluginCheck;
+import de.steamwar.linkage.api.Plain;
+
+import javax.annotation.Nonnull;
+import java.util.stream.Stream;
+
+@Linked
+@PluginCheck("FastAsyncWorldEdit")
+public class FAWECheckerboardMaskParser extends RichParser implements Plain {
+
+ public FAWECheckerboardMaskParser() {
+ super(WorldEdit.getInstance(), "#checkerboard");
+ WorldEditUtils.addMaskParser(this);
+ }
+
+ @Override
+ protected Stream getSuggestions(String argumentInput, int index) {
+ if (index == 0) {
+ return SuggestionHelper.suggestPositiveIntegers(argumentInput);
+ }
+ return Stream.empty();
+ }
+
+ @Override
+ protected Mask parseFromInput(@Nonnull String[] arguments, ParserContext context) throws InputParseException {
+ if (arguments.length != 1) {
+ return new CheckerboardMask(1);
+ }
+ try {
+ return new CheckerboardMask(Integer.parseInt(arguments[0]));
+ } catch (NumberFormatException e) {
+ throw new InputParseException("Invalid number: " + arguments[0]);
+ }
+ }
+}
diff --git a/BauSystem_Main/src/de/steamwar/bausystem/features/worldedit/mask/checkerboard3d/Checkerboard3DMask.java b/BauSystem_Main/src/de/steamwar/bausystem/features/worldedit/mask/checkerboard3d/Checkerboard3DMask.java
new file mode 100644
index 00000000..6695b612
--- /dev/null
+++ b/BauSystem_Main/src/de/steamwar/bausystem/features/worldedit/mask/checkerboard3d/Checkerboard3DMask.java
@@ -0,0 +1,42 @@
+/*
+ * This file is a part of the SteamWar software.
+ *
+ * Copyright (C) 2022 SteamWar.de-Serverteam
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program. If not, see .
+ */
+
+package de.steamwar.bausystem.features.worldedit.mask.checkerboard3d;
+
+import com.sk89q.worldedit.function.mask.Mask;
+import com.sk89q.worldedit.math.BlockVector3;
+
+public class Checkerboard3DMask implements Mask {
+
+ private int size;
+
+ public Checkerboard3DMask(int size) {
+ this.size = size;
+ }
+
+ @Override
+ public boolean test(BlockVector3 vector) {
+ return (vector.getBlockX() / size + vector.getBlockY() / size + vector.getBlockZ() / size) % 2 == 0;
+ }
+
+ @Override
+ public Mask copy() {
+ return new Checkerboard3DMask(size);
+ }
+}
diff --git a/BauSystem_Main/src/de/steamwar/bausystem/features/worldedit/mask/checkerboard3d/FAWECheckerboard3DMaskParser.java b/BauSystem_Main/src/de/steamwar/bausystem/features/worldedit/mask/checkerboard3d/FAWECheckerboard3DMaskParser.java
new file mode 100644
index 00000000..10e1eafe
--- /dev/null
+++ b/BauSystem_Main/src/de/steamwar/bausystem/features/worldedit/mask/checkerboard3d/FAWECheckerboard3DMaskParser.java
@@ -0,0 +1,64 @@
+/*
+ * This file is a part of the SteamWar software.
+ *
+ * Copyright (C) 2022 SteamWar.de-Serverteam
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program. If not, see .
+ */
+
+package de.steamwar.bausystem.features.worldedit.mask.checkerboard3d;
+
+import com.fastasyncworldedit.core.extension.factory.parser.RichParser;
+import com.sk89q.worldedit.WorldEdit;
+import com.sk89q.worldedit.command.util.SuggestionHelper;
+import com.sk89q.worldedit.extension.input.InputParseException;
+import com.sk89q.worldedit.extension.input.ParserContext;
+import com.sk89q.worldedit.function.mask.Mask;
+import de.steamwar.bausystem.utils.WorldEditUtils;
+import de.steamwar.linkage.Linked;
+import de.steamwar.linkage.PluginCheck;
+import de.steamwar.linkage.api.Plain;
+
+import javax.annotation.Nonnull;
+import java.util.stream.Stream;
+
+@Linked
+@PluginCheck("FastAsyncWorldEdit")
+public class FAWECheckerboard3DMaskParser extends RichParser implements Plain {
+
+ public FAWECheckerboard3DMaskParser() {
+ super(WorldEdit.getInstance(), "#checkerboard3d");
+ WorldEditUtils.addMaskParser(this);
+ }
+
+ @Override
+ protected Stream getSuggestions(String argumentInput, int index) {
+ if (index == 0) {
+ return SuggestionHelper.suggestPositiveIntegers(argumentInput);
+ }
+ return Stream.empty();
+ }
+
+ @Override
+ protected Mask parseFromInput(@Nonnull String[] arguments, ParserContext context) throws InputParseException {
+ if (arguments.length != 1) {
+ return new Checkerboard3DMask(1);
+ }
+ try {
+ return new Checkerboard3DMask(Integer.parseInt(arguments[0]));
+ } catch (NumberFormatException e) {
+ throw new InputParseException("Invalid number: " + arguments[0]);
+ }
+ }
+}
diff --git a/BauSystem_Main/src/de/steamwar/bausystem/features/worldedit/mask/grid/FAWEGridMaskParser.java b/BauSystem_Main/src/de/steamwar/bausystem/features/worldedit/mask/grid/FAWEGridMaskParser.java
new file mode 100644
index 00000000..8ca46641
--- /dev/null
+++ b/BauSystem_Main/src/de/steamwar/bausystem/features/worldedit/mask/grid/FAWEGridMaskParser.java
@@ -0,0 +1,83 @@
+/*
+ * This file is a part of the SteamWar software.
+ *
+ * Copyright (C) 2022 SteamWar.de-Serverteam
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program. If not, see .
+ */
+
+package de.steamwar.bausystem.features.worldedit.mask.grid;
+
+import com.fastasyncworldedit.core.extension.factory.parser.RichParser;
+import com.sk89q.worldedit.WorldEdit;
+import com.sk89q.worldedit.command.util.SuggestionHelper;
+import com.sk89q.worldedit.extension.input.InputParseException;
+import com.sk89q.worldedit.extension.input.ParserContext;
+import com.sk89q.worldedit.function.mask.Mask;
+import de.steamwar.bausystem.utils.WorldEditUtils;
+import de.steamwar.linkage.Linked;
+import de.steamwar.linkage.PluginCheck;
+import de.steamwar.linkage.api.Plain;
+
+import javax.annotation.Nonnull;
+import java.util.stream.Stream;
+
+@Linked
+@PluginCheck("FastAsyncWorldEdit")
+public class FAWEGridMaskParser extends RichParser implements Plain {
+
+ public FAWEGridMaskParser() {
+ super(WorldEdit.getInstance(), "#grid");
+ WorldEditUtils.addMaskParser(this);
+ }
+
+ @Override
+ protected Stream getSuggestions(String argumentInput, int index) {
+ if (index < 3) {
+ return SuggestionHelper.suggestPositiveIntegers(argumentInput);
+ }
+ if (index == 3) {
+ return Stream.of("and", "or");
+ }
+ return Stream.empty();
+ }
+
+ @Override
+ protected Mask parseFromInput(@Nonnull String[] arguments, ParserContext context) throws InputParseException {
+ if (arguments.length < 3 || arguments.length > 4) {
+ throw new InputParseException("Expected 3 or 4 arguments");
+ }
+ int x;
+ int y;
+ int z;
+ try {
+ x = Integer.parseInt(arguments[0]);
+ y = Integer.parseInt(arguments[1]);
+ z = Integer.parseInt(arguments[2]);
+ } catch (NumberFormatException e) {
+ throw new InputParseException("Expected 3 or 4 numbers");
+ }
+ boolean and = true;
+ if (arguments.length == 4) {
+ if (arguments[3].equalsIgnoreCase("and")) {
+ and = true;
+ } else if (arguments[3].equalsIgnoreCase("or")) {
+ and = false;
+ } else {
+ throw new InputParseException("Expected 'and' or 'or'");
+ }
+ }
+ return new GridMask(x, y, z, and);
+ }
+}
diff --git a/BauSystem_Main/src/de/steamwar/bausystem/features/worldedit/mask/grid/GridMask.java b/BauSystem_Main/src/de/steamwar/bausystem/features/worldedit/mask/grid/GridMask.java
new file mode 100644
index 00000000..4e6570ec
--- /dev/null
+++ b/BauSystem_Main/src/de/steamwar/bausystem/features/worldedit/mask/grid/GridMask.java
@@ -0,0 +1,52 @@
+/*
+ * This file is a part of the SteamWar software.
+ *
+ * Copyright (C) 2022 SteamWar.de-Serverteam
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program. If not, see .
+ */
+
+package de.steamwar.bausystem.features.worldedit.mask.grid;
+
+import com.sk89q.worldedit.function.mask.Mask;
+import com.sk89q.worldedit.math.BlockVector3;
+
+public class GridMask implements Mask {
+
+ private int x;
+ private int y;
+ private int z;
+ private boolean and;
+
+ public GridMask(int x, int y, int z, boolean and) {
+ this.x = x;
+ this.y = y;
+ this.z = z;
+ this.and = and;
+ }
+
+ @Override
+ public boolean test(BlockVector3 vector) {
+ if (and) {
+ return vector.getBlockX() % x == 0 && vector.getBlockY() % y == 0 && vector.getBlockZ() % z == 0;
+ } else {
+ return vector.getBlockX() % x == 0 || vector.getBlockY() % y == 0 || vector.getBlockZ() % z == 0;
+ }
+ }
+
+ @Override
+ public Mask copy() {
+ return new GridMask(x, y, z, and);
+ }
+}
diff --git a/BauSystem_Main/src/de/steamwar/bausystem/features/worldedit/pattern/gradient/FAWEGradientPatternParser.java b/BauSystem_Main/src/de/steamwar/bausystem/features/worldedit/pattern/gradient/FAWEGradientPatternParser.java
new file mode 100644
index 00000000..a1f0e450
--- /dev/null
+++ b/BauSystem_Main/src/de/steamwar/bausystem/features/worldedit/pattern/gradient/FAWEGradientPatternParser.java
@@ -0,0 +1,140 @@
+/*
+ * This file is a part of the SteamWar software.
+ *
+ * Copyright (C) 2022 SteamWar.de-Serverteam
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program. If not, see .
+ */
+
+package de.steamwar.bausystem.features.worldedit.pattern.gradient;
+
+import com.fastasyncworldedit.core.extension.factory.parser.RichParser;
+import com.sk89q.worldedit.WorldEdit;
+import com.sk89q.worldedit.extension.input.InputParseException;
+import com.sk89q.worldedit.extension.input.ParserContext;
+import com.sk89q.worldedit.function.pattern.Pattern;
+import com.sk89q.worldedit.regions.Region;
+import de.steamwar.bausystem.utils.WorldEditUtils;
+import de.steamwar.linkage.Linked;
+import de.steamwar.linkage.PluginCheck;
+import de.steamwar.linkage.api.Plain;
+import org.bukkit.Axis;
+
+import javax.annotation.Nonnull;
+import java.util.stream.Stream;
+
+@Linked
+@PluginCheck("FastAsyncWorldEdit")
+public class FAWEGradientPatternParser extends RichParser implements Plain {
+
+ public FAWEGradientPatternParser() {
+ super(WorldEdit.getInstance(), "#gradient");
+ WorldEditUtils.addPatternParser(this);
+ }
+
+ /*
+ @Override
+ public Pattern parseFromInput(String s, ParserContext parserContext) throws InputParseException {
+ System.out.println("parse " + s);
+ if (!s.startsWith("#gradient")) return null;
+ Extent extent = parserContext.requireExtent();
+ Axis axis = null;
+ if (s.startsWith("#gradientx")) {
+ axis = Axis.X;
+ } else if (s.startsWith("#gradienty")) {
+ axis = Axis.Y;
+ } else if (s.startsWith("#gradientz")) {
+ axis = Axis.Z;
+ }
+ if (axis == null) {
+ throw new InputParseException("No axis given for gradient pattern");
+ }
+ s = s.substring(10);
+ if (!s.startsWith("[") && s.endsWith("]")) {
+ throw new InputParseException("No pattern given for gradient pattern");
+ }
+ s = s.substring(1, s.length() - 1);
+ String[] patterns = s.split("]\\[");
+ if (patterns.length < 2) {
+ throw new InputParseException("Not enough patterns given for gradient pattern");
+ }
+ if (patterns.length > 2) {
+ throw new InputParseException("Too many patterns given for gradient pattern");
+ }
+ Pattern from = WorldEditUtils.getPatternFactory().parseFromInput(patterns[0], parserContext);
+ Pattern to = WorldEditUtils.getPatternFactory().parseFromInput(patterns[1], parserContext);
+ int size = 0;
+ switch (axis) {
+ case X:
+ size = extent.getMaximumPoint().getBlockX() - extent.getMinimumPoint().getBlockX();
+ break;
+ case Y:
+ size = extent.getMaximumPoint().getBlockY() - extent.getMinimumPoint().getBlockY();
+ break;
+ case Z:
+ size = extent.getMaximumPoint().getBlockZ() - extent.getMinimumPoint().getBlockZ();
+ break;
+ }
+ return new GradientPattern(axis, from, to, extent.getMinimumPoint(), size);
+ }
+ */
+
+ @Override
+ protected Stream getSuggestions(String input, int index) {
+ if (index == 0) {
+ return Stream.of("x", "y", "z");
+ }
+ if (index == 1 || index == 2) {
+ return WorldEdit.getInstance().getPatternFactory().getSuggestions(input).stream();
+ }
+ return Stream.empty();
+ }
+
+ @Override
+ protected Pattern parseFromInput(@Nonnull String[] arguments, ParserContext context) throws InputParseException {
+ if (arguments.length < 2) {
+ throw new InputParseException("Not enough arguments given for gradient pattern");
+ }
+ Axis axis = null;
+ switch (arguments[0]) {
+ case "x":
+ axis = Axis.X;
+ break;
+ case "y":
+ axis = Axis.Y;
+ break;
+ case "z":
+ axis = Axis.Z;
+ break;
+ default:
+ throw new InputParseException("No axis given for gradient pattern");
+ }
+ Region region = context.getSelection();
+ Pattern from = WorldEditUtils.getPatternFactory().parseFromInput(arguments[1], context);
+ Pattern to = WorldEditUtils.getPatternFactory().parseFromInput(arguments[2], context);
+ int size = 0;
+ switch (axis) {
+ case X:
+ size = region.getWidth();
+ break;
+ case Y:
+ size = region.getHeight();
+ break;
+ case Z:
+ size = region.getLength();
+ break;
+ }
+ return new GradientPattern(axis, from, to, region.getMinimumPoint(), size);
+ }
+}
diff --git a/BauSystem_Main/src/de/steamwar/bausystem/features/worldedit/pattern/gradient/GradientPattern.java b/BauSystem_Main/src/de/steamwar/bausystem/features/worldedit/pattern/gradient/GradientPattern.java
new file mode 100644
index 00000000..c712cf38
--- /dev/null
+++ b/BauSystem_Main/src/de/steamwar/bausystem/features/worldedit/pattern/gradient/GradientPattern.java
@@ -0,0 +1,66 @@
+/*
+ * This file is a part of the SteamWar software.
+ *
+ * Copyright (C) 2022 SteamWar.de-Serverteam
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program. If not, see .
+ */
+
+package de.steamwar.bausystem.features.worldedit.pattern.gradient;
+
+import com.sk89q.worldedit.function.pattern.Pattern;
+import com.sk89q.worldedit.math.BlockVector3;
+import com.sk89q.worldedit.world.block.BaseBlock;
+import org.bukkit.Axis;
+
+import java.util.Random;
+
+public class GradientPattern implements Pattern {
+
+ private final Axis axis;
+ private final Pattern first;
+ private final Pattern second;
+ private final BlockVector3 min;
+ private final int size;
+ private final Random random = new Random();
+
+ public GradientPattern(Axis axis, Pattern first, Pattern second, BlockVector3 min, int size) {
+ this.axis = axis;
+ this.first = first;
+ this.second = second;
+ this.min = min;
+ this.size = size;
+ }
+
+ @Override
+ public BaseBlock applyBlock(BlockVector3 position) {
+ int percent = 0;
+ switch (axis) {
+ case X:
+ percent = (int) ((position.getX() - min.getX()) / (double) size * 100);
+ break;
+ case Y:
+ percent = (int) ((position.getY() - min.getY()) / (double) size * 100);
+ break;
+ case Z:
+ percent = (int) ((position.getZ() - min.getZ()) / (double) size * 100);
+ break;
+ }
+ if (random.nextInt(100) <= Math.abs(percent)) {
+ return first.applyBlock(position);
+ } else {
+ return second.applyBlock(position);
+ }
+ }
+}
diff --git a/BauSystem_Main/src/de/steamwar/bausystem/features/worldedit/SpecialReplace.java b/BauSystem_Main/src/de/steamwar/bausystem/features/worldedit/utils/SpecialReplace.java
similarity index 97%
rename from BauSystem_Main/src/de/steamwar/bausystem/features/worldedit/SpecialReplace.java
rename to BauSystem_Main/src/de/steamwar/bausystem/features/worldedit/utils/SpecialReplace.java
index f3b988b8..e665d1af 100644
--- a/BauSystem_Main/src/de/steamwar/bausystem/features/worldedit/SpecialReplace.java
+++ b/BauSystem_Main/src/de/steamwar/bausystem/features/worldedit/utils/SpecialReplace.java
@@ -17,7 +17,7 @@
* along with this program. If not, see .
*/
-package de.steamwar.bausystem.features.worldedit;
+package de.steamwar.bausystem.features.worldedit.utils;
import com.sk89q.worldedit.EditSession;
import com.sk89q.worldedit.blocks.SignBlock;
@@ -101,4 +101,9 @@ public class SpecialReplace implements Mask, Pattern {
}
}
}
+
+ @Override
+ public Mask copy() {
+ return this;
+ }
}
diff --git a/BauSystem_Main/src/de/steamwar/bausystem/utils/WorldEditUtils.java b/BauSystem_Main/src/de/steamwar/bausystem/utils/WorldEditUtils.java
index af281501..cc3a6ef6 100644
--- a/BauSystem_Main/src/de/steamwar/bausystem/utils/WorldEditUtils.java
+++ b/BauSystem_Main/src/de/steamwar/bausystem/utils/WorldEditUtils.java
@@ -23,6 +23,10 @@ import com.sk89q.worldedit.EditSession;
import com.sk89q.worldedit.IncompleteRegionException;
import com.sk89q.worldedit.WorldEdit;
import com.sk89q.worldedit.bukkit.BukkitAdapter;
+import com.sk89q.worldedit.extension.factory.PatternFactory;
+import com.sk89q.worldedit.function.mask.Mask;
+import com.sk89q.worldedit.function.pattern.Pattern;
+import com.sk89q.worldedit.internal.registry.InputParser;
import com.sk89q.worldedit.math.BlockVector3;
import com.sk89q.worldedit.regions.Region;
import com.sk89q.worldedit.regions.RegionSelector;
@@ -53,6 +57,26 @@ public class WorldEditUtils {
return regionSelector.getRegion();
}
+ @SneakyThrows
+ public Mask getMask(Player player) {
+ return WorldEdit.getInstance()
+ .getSessionManager()
+ .get(BukkitAdapter.adapt(player))
+ .getMask();
+ }
+
+ public void addMaskParser(InputParser maskInputParser) {
+ WorldEdit.getInstance().getMaskFactory().register(maskInputParser);
+ }
+
+ public void addPatternParser(InputParser patternInputParser) {
+ WorldEdit.getInstance().getPatternFactory().register(patternInputParser);
+ }
+
+ public PatternFactory getPatternFactory() {
+ return WorldEdit.getInstance().getPatternFactory();
+ }
+
public Pair getSelection(Player player) {
RegionSelector regionSelector = WorldEdit.getInstance()
.getSessionManager()
diff --git a/build.gradle b/build.gradle
index af6a0ec3..c00c8a83 100644
--- a/build.gradle
+++ b/build.gradle
@@ -20,8 +20,6 @@
import org.apache.tools.ant.taskdefs.condition.Os
-import java.util.function.BiConsumer
-
plugins {
// Adding the base plugin fixes the following gradle warnings in IntelliJ:
//
@@ -151,21 +149,21 @@ build.finalizedBy(finalizeProject)
if (steamwarProperties.containsKey("hostname")) {
String hostname = steamwarProperties.get("hostname")
- String uploadPath = steamwarProperties.getOrDefault("uploadPath", "~")
- String server = steamwarProperties.getOrDefault("server", "Dev1.15")
- String serverStartFlags = steamwarProperties.getOrDefault("serverStartFlags", "")
+ String type = steamwarProperties.getOrDefault("type", "Bau19")
+ String world = steamwarProperties.getOrDefault("world", "")
+ String pluginFolder = steamwarProperties.getOrDefault("pluginFolder", "")
task uploadProject {
description 'Upload this project'
group "Steamwar"
doLast {
- await(shell("scp ${libs}/${jarName} ${hostname}:${uploadPath}/${server}/plugins"))
+ await(shell("scp ${libs}/${jarName} ${hostname}:${pluginFolder}"))
if (steamwarProperties.getOrDefault("directStart", "false") == "false" && !answer("Start ${server} server?")) {
return
}
- serverStart(server, serverStartFlags, hostname)
+ serverStart(type, hostname, world, pluginFolder)
}
}
uploadProject.dependsOn(buildProject)
@@ -175,7 +173,7 @@ if (steamwarProperties.containsKey("hostname")) {
group "Steamwar"
doLast {
- serverStart(server, serverStartFlags, hostname)
+ serverStart(server, "", hostname)
}
}
}
@@ -195,8 +193,8 @@ private def shell(String command) {
}
}
-private def serverStart(String serverName, String serverFlags, String hostname) {
- def proc = shell("ssh -t ${hostname} \"./mc ${serverFlags} ${serverName}\"")
+private def serverStart(String type, String hostname, String worldName, String pluginFolder) {
+ def proc = shell("ssh -t ${hostname} \"./binarys/dev.py ${type} -w ${worldName} -p ${pluginFolder}\"")
Set strings = new HashSet<>()
File file = new File("${projectDir}/ignoredlog");
@@ -225,7 +223,7 @@ private def serverStart(String serverName, String serverFlags, String hostname)
// Ignored
}
})
- outputThread.setName("${serverName} - OutputThread")
+ outputThread.setName("${type} - OutputThread")
outputThread.start()
Writer writer
@@ -242,7 +240,7 @@ private def serverStart(String serverName, String serverFlags, String hostname)
// Ignored
}
})
- inputThread.setName("${serverName} - InputThread")
+ inputThread.setName("${type} - InputThread")
inputThread.start()
gradle.buildFinished { buildResult ->