diff --git a/pom.xml b/pom.xml
index 27e4519..3799391 100644
--- a/pom.xml
+++ b/pom.xml
@@ -6,7 +6,7 @@
me.bteuk
TeachingTutorials
- 1.1.0-Beta8.1
+ 1.1.0-Beta8.1.2
jar
TeachingTutorials
diff --git a/src/main/java/teachingtutorials/TeachingTutorials.java b/src/main/java/teachingtutorials/TeachingTutorials.java
index 5869dcb..d8d47ac 100644
--- a/src/main/java/teachingtutorials/TeachingTutorials.java
+++ b/src/main/java/teachingtutorials/TeachingTutorials.java
@@ -18,6 +18,7 @@
import teachingtutorials.newlocation.NewLocation;
import teachingtutorials.tutorials.*;
import teachingtutorials.utils.*;
+import teachingtutorials.utils.plugins.WorldEditImplementation;
import java.io.BufferedReader;
import java.io.File;
@@ -54,6 +55,9 @@ public class TeachingTutorials extends JavaPlugin
//A list of all virtual blocks
public HashMap virtualBlocks;
+ //Identifies which world edit is being used
+ public WorldEditImplementation worldEditImplementation;
+
@Override
public void onEnable()
{
@@ -80,6 +84,29 @@ public void onEnable()
return;
}
+ if (Bukkit.getPluginManager().isPluginEnabled("FastAsyncWorldEdit"))
+ {
+ worldEditImplementation = WorldEditImplementation.FAWE;
+ getLogger().info("WorldEdit implementation detected as FAWE");
+ }
+ else if (Bukkit.getPluginManager().isPluginEnabled("WorldEdit"))
+ {
+ worldEditImplementation = WorldEditImplementation.WorldEdit;
+ getLogger().severe("WorldEdit implementation detected as WorldEdit (not FAWE). Please change to FAWE to continue.");
+ getLogger().severe("Contact the authors of the plugin for support");
+ getLogger().severe("*** This plugin will be disabled. ***");
+ this.setEnabled(false);
+ return;
+ }
+ else
+ {
+ worldEditImplementation = WorldEditImplementation.NONE;
+ getLogger().severe("*** No type of WorldEdit is loaded on the server. ***");
+ getLogger().severe("*** This plugin will be disabled. ***");
+ this.setEnabled(false);
+ return;
+ }
+
// Plugin startup logic
TeachingTutorials.instance = this;
TeachingTutorials.config = this.getConfig();
@@ -247,6 +274,27 @@ public void run()
}, 0, 10);
+ //-----------------------------------------
+ //------ Performs calculation events ------
+ //-----------------------------------------
+ this.getServer().getScheduler().scheduleSyncRepeatingTask(this, () ->
+ {
+ if (!WorldEdit.isCurrentCalculationOngoing())
+ {
+ WorldEditCalculation worldEditCalculation = WorldEdit.pendingCalculations.peek();
+ if (worldEditCalculation !=null)
+ {
+ Bukkit.getConsoleSender().sendMessage(ChatColor.YELLOW +"[TeachingTutorials] Calculation not already in progress, a new one has been detected");
+ WorldEdit.setCalculationInProgress();
+ worldEditCalculation.runCalculation();
+ }
+ }
+ else
+ {
+ Bukkit.getConsoleSender().sendMessage(ChatColor.YELLOW +"[TeachingTutorials] Calculation ongoing, not initiating a new one");
+ }
+ }, 0, 4);
+
//---------------------------------------
//---------------Listeners---------------
//---------------------------------------
diff --git a/src/main/java/teachingtutorials/fundamentalTasks/Command.java b/src/main/java/teachingtutorials/fundamentalTasks/Command.java
index 7e5a542..4257340 100644
--- a/src/main/java/teachingtutorials/fundamentalTasks/Command.java
+++ b/src/main/java/teachingtutorials/fundamentalTasks/Command.java
@@ -333,9 +333,9 @@ else if (!(tasks.get(this.iOrder - 2) instanceof Selection))
RegionSelector regionSelector = new CuboidRegionSelector(BukkitAdapter.adapt(world), selectionPoint1, selectionPoint2);
//Calculates the virtual blocks
- WorldEdit.BlocksCalculator(iTaskID, virtualBlocks, regionSelector, szTargetCommand, szTargetCommandArgs.split(" "), world, player, parentGroup.parentStep.parentStage.tutorialPlaythrough);
- }
+ WorldEdit.BlocksCalculator(iTaskID, virtualBlocks, regionSelector, szTargetCommand, szTargetCommandArgs.split(" "), parentGroup.parentStep.parentStage.tutorialPlaythrough);
- Bukkit.getConsoleSender().sendMessage(ChatColor.AQUA +"[TeachingTutorials] All virtual blocks for this task: "+virtualBlocks.size());
+ //It will create a new calculation object and add this to the queue. The plugin will calculate the blocks when available
+ }
}
}
diff --git a/src/main/java/teachingtutorials/tutorials/Step.java b/src/main/java/teachingtutorials/tutorials/Step.java
index d2f8c42..674c6d3 100644
--- a/src/main/java/teachingtutorials/tutorials/Step.java
+++ b/src/main/java/teachingtutorials/tutorials/Step.java
@@ -232,14 +232,14 @@ public void run() {
for (i = 0; i < iGroups; i++)
{
final int I = i;
- //Registers each group 0.2 seconds apart from each other - this is to allow all world edit block calculations to complete
- //WE block calculations involves running the WE command over the console and detecting and recording changes, before
- // then removing those changes from the actual world. This involves delicate use of listeners, hence this 0.1 second control
- Bukkit.getScheduler().runTaskLater(TeachingTutorials.getInstance(), () ->
- {
+// //Registers each group GroupRegistrationTimeGapTicks/20 seconds apart from each other - this is to allow all world edit block calculations to complete
+// //WE block calculations involves running the WE command over the console and detecting and recording changes, before
+// // then removing those changes from the actual world. This involves delicate use of listeners, hence this 0.1 second control
+// Bukkit.getScheduler().runTaskLater(TeachingTutorials.getInstance(), () ->
+// {
groups.get(I).initialRegister();
Bukkit.getConsoleSender().sendMessage(ChatColor.AQUA +"[TeachingTutorials] Registered group "+(I+1));
- }, i*4L);
+// }, i*plugin.getConfig().getLong("GroupRegistrationTimeGapTicks"));
}
//TP to start location, and store this location for later use
diff --git a/src/main/java/teachingtutorials/utils/VirtualBlock.java b/src/main/java/teachingtutorials/utils/VirtualBlock.java
index 2602a95..70eadcc 100644
--- a/src/main/java/teachingtutorials/utils/VirtualBlock.java
+++ b/src/main/java/teachingtutorials/utils/VirtualBlock.java
@@ -1,7 +1,6 @@
package teachingtutorials.utils;
import org.bukkit.Location;
-import org.bukkit.Material;
import org.bukkit.World;
import org.bukkit.block.data.BlockData;
import org.bukkit.entity.Player;
@@ -40,10 +39,10 @@ public VirtualBlock(TutorialPlaythrough tutorialPlaythrough, Player player, Worl
/**
* Constructs the virtual block. Use this if the bukkit location object for this block has already been created and initialised
- * @param tutorialPlaythrough
- * @param player
- * @param location
- * @param blockData
+ * @param tutorialPlaythrough The tutorial playthrough instance that this virtual block belongs to
+ * @param player The player to display the virtual block for
+ * @param location The location of the virtual block
+ * @param blockData The block data for the virtual block
*/
public VirtualBlock(TutorialPlaythrough tutorialPlaythrough, Player player, Location location, BlockData blockData)
{
diff --git a/src/main/java/teachingtutorials/utils/WorldEdit.java b/src/main/java/teachingtutorials/utils/WorldEdit.java
index 400870c..4078342 100644
--- a/src/main/java/teachingtutorials/utils/WorldEdit.java
+++ b/src/main/java/teachingtutorials/utils/WorldEdit.java
@@ -1,31 +1,38 @@
package teachingtutorials.utils;
import com.google.common.base.Joiner;
-import com.sk89q.worldedit.IncompleteRegionException;
-import com.sk89q.worldedit.bukkit.BukkitAdapter;
-import com.sk89q.worldedit.event.extent.EditSessionEvent;
-import com.sk89q.worldedit.extension.platform.Actor;
-import com.sk89q.worldedit.extent.AbstractDelegateExtent;
-import com.sk89q.worldedit.math.BlockVector3;
-import com.sk89q.worldedit.regions.CuboidRegion;
import com.sk89q.worldedit.regions.RegionSelector;
-import com.sk89q.worldedit.util.eventbus.Subscribe;
-import com.sk89q.worldedit.world.block.BlockStateHolder;
import org.bukkit.Bukkit;
import org.bukkit.ChatColor;
-import org.bukkit.Location;
-import org.bukkit.World;
-import org.bukkit.block.Block;
-import org.bukkit.block.data.BlockData;
-import org.bukkit.entity.Player;
-import teachingtutorials.TeachingTutorials;
import teachingtutorials.TutorialPlaythrough;
import java.util.HashSet;
+import java.util.LinkedList;
public class WorldEdit
{
- private static Object worldEditEditEvent;
+ /**
+ * A queue of pending calculations to be performed
+ */
+ public static LinkedList pendingCalculations = new LinkedList<>();
+ private static boolean bCurrentCalculationOngoing = false;
+
+ public static boolean isCurrentCalculationOngoing()
+ {
+ return bCurrentCalculationOngoing;
+ }
+
+ public static void setCalculationInProgress()
+ {
+ bCurrentCalculationOngoing = true;
+ Bukkit.getConsoleSender().sendMessage(ChatColor.RED +"[TeachingTutorials] The calculations queue has been blocked - calculation in progress");
+ }
+
+ public static void setCalculationFinished()
+ {
+ bCurrentCalculationOngoing = false;
+ Bukkit.getConsoleSender().sendMessage(ChatColor.GREEN +"[TeachingTutorials] The calculations queue has been unblocked!");
+ }
/**
* Creates a world edit event listener which catches the block changes arising from world edit commands owned by the given player
@@ -34,143 +41,28 @@ public class WorldEdit
* @param virtualBlocks The list of virtual blocks for the calling task
* @param szCommandLabel The command label (first word) of the command
* @param szCommandArgs The command args
- * @param bukkitWorld The bukkit world for this set of blocks
- * @param player The player doing the task
* @param tutorialPlaythrough The tutorial playthrough which this task belongs to
* @return
*/
- public static void BlocksCalculator(int iTaskID, final HashSet virtualBlocks, RegionSelector correctSelectionRegion, String szCommandLabel, String[] szCommandArgs, World bukkitWorld, Player player, TutorialPlaythrough tutorialPlaythrough)
+ public static void BlocksCalculator(int iTaskID, final HashSet virtualBlocks, RegionSelector correctSelectionRegion, String szCommandLabel, String[] szCommandArgs, TutorialPlaythrough tutorialPlaythrough)
{
- //Get instance
- com.sk89q.worldedit.WorldEdit worldEdit = com.sk89q.worldedit.WorldEdit.getInstance();
-
- //Get the console actor
- Actor consoleActor = BukkitAdapter.adapt(Bukkit.getConsoleSender());
-
- //Modifies the command
+ //1. Modifies the command
//This code is taken from WorldEdit - See https://enginehub.org/
int plSep = szCommandLabel.indexOf(':');
if (plSep >= 0 && plSep < szCommandLabel.length() +1)
{
szCommandLabel = szCommandLabel.substring(plSep + 1);
}
-// StringBuilder sb = new StringBuilder("/").append(szCommandLabel);
StringBuilder sb = new StringBuilder(szCommandLabel);
-// if (szCommandArgs.length > 0)
-// sb.append(" ");
String szWorldEditCommand = Joiner.on(" ").appendTo(sb, szCommandArgs).toString();
//The command is now fully formatted correctly
-// Bukkit.getConsoleSender().sendMessage(ChatColor.AQUA +"[TeachingTutorial] Command being run via the API: "+szWorldEditCommand);
- Bukkit.getConsoleSender().sendMessage(ChatColor.AQUA +"[TeachingTutorial] Command being run via the console: "+szWorldEditCommand);
-
- //Create the new event listener
- worldEditEditEvent = new Object()
- {
- // The following code is extracted from LogBlock under creative commons.
- // http://creativecommons.org/licenses/by-nc-sa/3.0/
-
- @Subscribe
- public void onEditSessionEvent(EditSessionEvent event)
- {
- final Actor actor = event.getActor();
-
- if (actor.getName().equals(consoleActor.getName()))
- {
- System.out.println("Edit session event detected belonging to the actor we are listening for - at stage: "+event.getStage().toString());
- }
- else
- {
- System.out.println("Edit session event detected but doesn't belong to the correct actor, so ignoring");
- return;
- }
-
- //Creates the new extent
- AbstractDelegateExtent blockChangeRecorderExtent = new AbstractDelegateExtent(event.getExtent())
- {
- @Override
- public > boolean setBlock(BlockVector3 position, B block) {
- Bukkit.getConsoleSender().sendMessage("\nA world edit block change has been detected, belonging to the listener for task " +iTaskID +". Recording to the given virtual blocks list");
- onBlockChange(position, block);
- //return super.setBlock(position, block);
- return false; // It's unclear whether this should really be used.
- // We don't want it to actually set the block so we can just cancel the whole event, but we do also want to set the block or at least try to
- }
-
-
- protected > void onBlockChange(BlockVector3 pt, B block)
- {
-// //This should only ever be for one of the 3 stages anyway right?
-// if (event.getStage() != EditSession.Stage.BEFORE_CHANGE) {
-// return;
-// }
- //Unregisters the event after 0.1 seconds (2 ticks)
- Bukkit.getScheduler().runTaskLater(TeachingTutorials.getInstance(), () ->
- {
- Bukkit.getConsoleSender().sendMessage(ChatColor.AQUA +"[TeachingTutorials] Unregistering world edit listener for task "+iTaskID);
- worldEdit.getEventBus().unregister(worldEditEditEvent);
- Bukkit.getConsoleSender().sendMessage(ChatColor.AQUA +"[TeachingTutorials] Unregistered world edit listener for task "+iTaskID);
-
- int iSize = virtualBlocks.size();
-
- Bukkit.getConsoleSender().sendMessage(ChatColor.AQUA +"[TeachingTutorials] There were " +iSize + " block changes detected for task "+iTaskID);
- }, 2L);
-
-
- //Calculates the old block
- Location location = BukkitAdapter.adapt(bukkitWorld, pt);
- Block blockBefore = location.getBlock();
- BlockData blockDataBefore = blockBefore.getBlockData();
-
- //Gets the new block
- BlockData blockDataNew = BukkitAdapter.adapt(block);
-
- //If there is actually a change of block
- if (!blockDataBefore.equals(blockDataNew))
- {
- Bukkit.getConsoleSender().sendMessage("There was a change of block: ");
- Bukkit.getConsoleSender().sendMessage("New block: " +blockDataNew.getMaterial());
- Bukkit.getConsoleSender().sendMessage("Location: " +location.toString());
-
- //Creates a virtual block
- VirtualBlock virtualBlock = new VirtualBlock(tutorialPlaythrough, player, location, blockDataNew);
- //Adds it to the new list
- virtualBlocks.add(virtualBlock);
- }
- }
- };
-
- //Sets the new extent into the event
- Bukkit.getConsoleSender().sendMessage(ChatColor.AQUA +"[TeachingTutorials] Setting the extent");
- event.setExtent(blockChangeRecorderExtent);
- }
- };
-
- Bukkit.getConsoleSender().sendMessage(ChatColor.AQUA +"[TeachingTutorials] Sending command: "+szWorldEditCommand);
-
- Bukkit.getScheduler().runTask(TeachingTutorials.getInstance(), () ->
- {
- try
- {
- //Sets the selection
- Bukkit.getConsoleSender().sendMessage(ChatColor.AQUA +"[TeachingTutorials] Adjusting the selection");
- Bukkit.dispatchCommand(Bukkit.getConsoleSender(), "/world "+tutorialPlaythrough.getLocation().getLocationID());
- Bukkit.dispatchCommand(Bukkit.getConsoleSender(), "/pos1 " + ((CuboidRegion) correctSelectionRegion.getRegion()).getPos1().toParserString());
- Bukkit.dispatchCommand(Bukkit.getConsoleSender(), "/pos2 " + ((CuboidRegion) correctSelectionRegion.getRegion()).getPos2().toParserString());
-
- //Registers the world change event listener
- worldEdit.getEventBus().register(worldEditEditEvent);
- Bukkit.getConsoleSender().sendMessage(ChatColor.AQUA +"[TeachingTutorials] World edit change event listener registered");
+ Bukkit.getConsoleSender().sendMessage(ChatColor.AQUA +"[TeachingTutorials] Upcoming command being run via the console: "+szWorldEditCommand);
- //Runs the command
- Bukkit.getConsoleSender().sendMessage(ChatColor.AQUA +"[TeachingTutorials] Sending the command");
- Bukkit.dispatchCommand(Bukkit.getConsoleSender(), szWorldEditCommand);
- Bukkit.getConsoleSender().sendMessage(ChatColor.AQUA +"[TeachingTutorials] Command sent");
- }
- catch (IncompleteRegionException e)
- {
- }
- });
+ //Create a new calculation manager for this calculation and add to the list
+ WorldEditCalculation newCalculation = new WorldEditCalculation(szWorldEditCommand, correctSelectionRegion, tutorialPlaythrough, iTaskID, virtualBlocks);
+ WorldEdit.pendingCalculations.add(newCalculation);
+ Bukkit.getConsoleSender().sendMessage(ChatColor.AQUA +"[TeachingTutorials] This calculation has been added to the queue: "+szWorldEditCommand);
}
}
diff --git a/src/main/java/teachingtutorials/utils/WorldEditCalculation.java b/src/main/java/teachingtutorials/utils/WorldEditCalculation.java
new file mode 100644
index 0000000..7a3403d
--- /dev/null
+++ b/src/main/java/teachingtutorials/utils/WorldEditCalculation.java
@@ -0,0 +1,448 @@
+package teachingtutorials.utils;
+
+import com.fastasyncworldedit.core.extent.processor.ProcessorScope;
+import com.fastasyncworldedit.core.queue.IBatchProcessor;
+import com.fastasyncworldedit.core.queue.IChunk;
+import com.fastasyncworldedit.core.queue.IChunkGet;
+import com.fastasyncworldedit.core.queue.IChunkSet;
+import com.fastasyncworldedit.core.util.ExtentTraverser;
+import com.sk89q.worldedit.WorldEdit;
+import com.sk89q.worldedit.bukkit.BukkitAdapter;
+import com.sk89q.worldedit.event.extent.EditSessionEvent;
+import com.sk89q.worldedit.extension.platform.Actor;
+import com.sk89q.worldedit.extent.AbstractDelegateExtent;
+import com.sk89q.worldedit.extent.Extent;
+import com.sk89q.worldedit.math.BlockVector2;
+import com.sk89q.worldedit.math.BlockVector3;
+import com.sk89q.worldedit.regions.CuboidRegion;
+import com.sk89q.worldedit.regions.RegionSelector;
+import com.sk89q.worldedit.util.eventbus.Subscribe;
+import com.sk89q.worldedit.world.block.BlockState;
+import com.sk89q.worldedit.world.block.BlockStateHolder;
+import com.sk89q.worldedit.world.block.BlockTypes;
+import com.sk89q.worldedit.world.block.BlockTypesCache;
+import org.bukkit.*;
+import org.bukkit.block.data.BlockData;
+import org.bukkit.entity.Player;
+import teachingtutorials.TeachingTutorials;
+import teachingtutorials.TutorialPlaythrough;
+import teachingtutorials.utils.plugins.WorldEditImplementation;
+
+import java.util.HashSet;
+
+/**
+ * Stores the details of a world edit block change calculation which is to be made
+ */
+public class WorldEditCalculation
+{
+ private Object worldEditEventListener;
+ private final String szWorldEditCommand;
+ private final RegionSelector regionSelector;
+ private final TutorialPlaythrough tutorialPlaythrough;
+
+ public Player getPlayer()
+ {
+ return tutorialPlaythrough.getCreatorOrStudent().player;
+ }
+
+ public World getWorld()
+ {
+ return tutorialPlaythrough.getLocation().getWorld();
+ }
+
+ public TutorialPlaythrough getTutorialPlaythrough()
+ {
+ return tutorialPlaythrough;
+ }
+
+ public WorldEditCalculation(String szWorldEditCommand, RegionSelector regionSelector, TutorialPlaythrough tutorialPlaythrough, int iTaskID, HashSet virtualBlocks)
+ {
+ this.szWorldEditCommand = szWorldEditCommand;
+ this.regionSelector = regionSelector;
+ this.tutorialPlaythrough = tutorialPlaythrough;
+
+ //2. Create the new event listener
+ setUpListener(iTaskID, virtualBlocks);
+ }
+
+ public Object getEditSessionListener()
+ {
+ return this.worldEditEventListener;
+ }
+
+ /**
+ * Creates a listener to pick up the EditSessionEvent event for the command being run. This listener will replace
+ * the 'extent' of the EditSession to one which records the changes and blocks them from being put onto the world.
+ * @param iTaskID The TaskID of the task
+ * @param virtualBlocks A reference to the list of virtual blocks to record block changes into
+ */
+ private void setUpListener(int iTaskID, HashSet virtualBlocks)
+ {
+ //Get the console actor
+ Actor consoleActor = BukkitAdapter.adapt(Bukkit.getConsoleSender());
+
+ //Get a reference to this object
+ WorldEditCalculation thisCalculation = this;
+
+ //Creates a listener to listen out for world edit events and insert the recorder extent into the correct one
+ worldEditEventListener = new Object()
+ {
+ @Subscribe
+ public void onEditSessionEvent(EditSessionEvent event)
+ {
+ final Actor actor = event.getActor();
+
+ if (actor == null)
+ {
+ Bukkit.getConsoleSender().sendMessage(ChatColor.RED + "Edit session event detected belonging a null actor (assuming console) - at stage: "+event.getStage().toString());
+ }
+ else if (actor.getName().equals(consoleActor.getName()))
+ {
+ Bukkit.getConsoleSender().sendMessage(ChatColor.RED + "Edit session event detected belonging to the actor we are listening for - at stage: "+event.getStage().toString());
+ }
+ else
+ {
+ Bukkit.getConsoleSender().sendMessage(ChatColor.RED +"Edit session event detected but doesn't belong to the correct actor, so ignoring");
+ return;
+ }
+
+ //Creates the new recorder extent
+ AbstractDelegateExtent blockChangeRecorderExtent;
+
+// if (TeachingTutorials.getInstance().worldEditImplementation.equals(WorldEditImplementation.FAWE))
+ blockChangeRecorderExtent = new BlockChangeRecorderExtentFAWE(event.getExtent(), thisCalculation, virtualBlocks, iTaskID);
+// else
+// blockChangeRecorderExtent = new BlockChangeRecorderExtentWE(event.getExtent(), thisCalculation, virtualBlocks, iTaskID);
+
+ //Updates the extent of the edit session to be that of a block recording extent
+ //The block recording extent means that block changes are blocked and recorded in the set block mechanism
+ //Sets the new extent into the event
+ Bukkit.getConsoleSender().sendMessage(ChatColor.AQUA +"[TeachingTutorials] Setting the extent");
+ event.setExtent(blockChangeRecorderExtent);
+
+ //Once the extent has been set we don't need the listener anymore since any world edit changes under that extent will be recorded in the correct list
+ unregisterWorldChangeListener();
+ }
+ };
+ Bukkit.getConsoleSender().sendMessage(ChatColor.GREEN +"[TeachingTutorials] WorldEdit event listener has been initialised");
+ }
+
+ /**
+ * Runs the block calculation. Ultimately this will send the world edit command through the console,
+ * and catch it with the event. This event has already been defined and contains the list of virtual blocks which
+ * it will add detected block changes to.
+ * @return True if the calculation was implemented successfully, False if there was an error.
+ */
+ public void runCalculation()
+ {
+ //Console output
+ Bukkit.getConsoleSender().sendMessage(ChatColor.YELLOW +"[TeachingTutorials] Starting a new WE block change calculation");
+
+ //Runs the world edit command through the console and registers the listener to identify the resulting operation
+ Bukkit.getScheduler().runTask(TeachingTutorials.getInstance(), () ->
+ {
+ //Sets the selection
+ Bukkit.getConsoleSender().sendMessage(ChatColor.AQUA +"[TeachingTutorials] Adjusting the selection");
+ Bukkit.dispatchCommand(Bukkit.getConsoleSender(), "/world "+tutorialPlaythrough.getLocation().getLocationID());
+ Bukkit.dispatchCommand(Bukkit.getConsoleSender(), "/pos1 " + ((CuboidRegion) regionSelector.getRegion()).getPos1().toParserString());
+ Bukkit.dispatchCommand(Bukkit.getConsoleSender(), "/pos2 " + ((CuboidRegion) regionSelector.getRegion()).getPos2().toParserString());
+
+ //Registers the world change event listener
+ try
+ {
+ WorldEdit.getInstance().getEventBus().register(worldEditEventListener);
+ }
+ catch (Exception e)
+ {
+ Bukkit.getConsoleSender().sendMessage(ChatColor.RED +"[Teaching Tutorials] Error registering WorldEdit event listener: "+e.getMessage());
+ Bukkit.getConsoleSender().sendMessage(ChatColor.RED +"[Teaching Tutorials] :" +e.getCause());
+ Bukkit.getConsoleSender().sendMessage(ChatColor.RED +"[Teaching Tutorials] :" +e);
+ e.printStackTrace();
+ }
+
+ Bukkit.getConsoleSender().sendMessage(ChatColor.AQUA +"[TeachingTutorials] World edit change event listener registered");
+
+ //Runs the command
+ Bukkit.getConsoleSender().sendMessage(ChatColor.AQUA +"[TeachingTutorials] Sending command: "+szWorldEditCommand);
+ Bukkit.dispatchCommand(Bukkit.getConsoleSender(), szWorldEditCommand);
+ Bukkit.getConsoleSender().sendMessage(ChatColor.AQUA +"[TeachingTutorials] Command sent");
+ });
+ }
+
+ /**
+ * Unregisters the listener and unblocks the calculation queue
+ */
+ protected void unregisterWorldChangeListener()
+ {
+ //Unregisters the WorldEdit event listener
+ Bukkit.getConsoleSender().sendMessage(ChatColor.AQUA +"[TeachingTutorials] Unregistering world edit listener");
+ com.sk89q.worldedit.WorldEdit.getInstance().getEventBus().unregister(this.getEditSessionListener());
+
+ //Updates the queue system, unblocking the queue
+ teachingtutorials.utils.WorldEdit.pendingCalculations.remove(this);
+ teachingtutorials.utils.WorldEdit.setCalculationFinished();
+ Bukkit.getConsoleSender().sendMessage(ChatColor.GREEN +"[TeachingTutorials] There are " +teachingtutorials.utils.WorldEdit.pendingCalculations.size() +" calculations remaining in the queue");
+
+ Bukkit.getConsoleSender().sendMessage(ChatColor.AQUA +"[TeachingTutorials] Unregistered world edit listener");
+ }
+}
+
+/**
+ * A new type of extent which records block changes and blocks them from being placed into the world
+ */
+class BlockChangeRecorderExtentFAWE extends AbstractDelegateExtent implements IBatchProcessor
+{
+ WorldEditCalculation worldEditCalculation;
+ HashSet virtualBlocks;
+ int iTaskID;
+
+ public BlockChangeRecorderExtentFAWE(Extent originalExtent, WorldEditCalculation worldEditCalculation, HashSet virtualBlocks, int iTaskID)
+ {
+ super(originalExtent);
+ this.worldEditCalculation = worldEditCalculation;
+ this.virtualBlocks = virtualBlocks;
+ this.iTaskID = iTaskID;
+
+ super.addProcessor(this);
+ super.addPostProcessor(this);
+ }
+
+ /**
+ * Is called when processing the changes of a given chunk in the operation.
+ * This is where blocks are recorded and blocked from being placed on the world.
+ * @param chunk An object representing the chunk
+ * @param get An object representing the state of the chunk before the operation
+ * @param set An object representing the state of the chunk after the operation
+ * @return An object representing the required state of the chunk after the operation
+ */
+ @Override
+ public IChunkSet processSet(IChunk chunk, IChunkGet get, IChunkSet set)
+ {
+ //Set:
+ // Presumably an implementation of CharSetBlocks
+ // Layers are height layers of a chunk
+ // A layer is a 16x16x16 piece of a chunk which stack up vertically to make up the chunk, There are 15 above 0 and 8 below 0.
+ // The layer index is layer = y >> 4
+
+ //The index of a chunk is calculated by = (y & 15) << 8 | z << 4 | x;
+ //This is just an index to a 16*16*16 array with Index = Y*256 + Z*16 + X
+
+ Bukkit.getConsoleSender().sendMessage(ChatColor.AQUA +"\n[TeachingTutorials] Processing chunk: " +chunk.getChunkBlockCoord().toString());
+ Bukkit.getConsoleSender().sendMessage(ChatColor.AQUA +"[TeachingTutorials] Y range for chunk: " +chunk.getMinY() +" to " +chunk.getMaxY());
+
+ //Declare the variables for the block coordinate markers
+ int Y, dX, dZ;
+
+ //Declare the variables for the new and old block states
+ BlockState newBlock;
+ BlockState oldBlock;
+
+ //Initialise the chunk vector - a 2d vector to the 0,0 point of the chunk
+ BlockVector2 chunkVector;
+ chunkVector = BlockVector2.at(chunk.getX()*16, chunk.getZ()*16);
+
+ //Stores the ID of the 'reserved' block state locally
+ final int iReservedState = BlockTypesCache.ReservedIDs.__RESERVED__;
+
+ //Check whether the chunk is empty
+ if (set.isEmpty())
+ {
+ //Skip this chunk
+ Bukkit.getConsoleSender().sendMessage("Chunk has no edits");
+ return set;
+ }
+
+ //Goes through every 'layer' in the new chunk
+ final int iMaxSectionPosition = set.getMaxSectionPosition();
+ for (int iLayer = set.getMinSectionPosition() ; iLayer <= iMaxSectionPosition ; iLayer++)
+ {
+ //Get the layer
+ char[] blocks = set.loadIfPresent(iLayer);
+ if (blocks == null)
+ {
+ //Layer had no blocks sections
+ Bukkit.getConsoleSender().sendMessage("Layer "+iLayer +" of chunk " +chunk.getChunkBlockCoord().toString() +" had no block sections");
+ //Do nothing
+ }
+ else
+ {
+ //Layer had block sections
+ Bukkit.getConsoleSender().sendMessage("Layer "+iLayer +" of chunk " +chunk.getChunkBlockCoord().toString() +" had block sections, identifying blocks in this layer now");
+
+ //Goes through every block in the layer
+ int iNumBlocksInLayer = blocks.length;
+ for (int index = 0 ; index < iNumBlocksInLayer ; index++)
+ {
+ //Extracts the ID of the block
+ char block = blocks[index];
+
+ //Check whether there is a block change
+ if (block == iReservedState)
+ {
+ //No block change recorded
+ }
+ else
+ {
+ //Record this block change
+
+ //Extract the block coordinates
+ //Index works as if you are indexing a 3D matrix but on a 1D scale
+
+ //In the index, Y is taken and timesed by 256, add Z by 16 and X is added on at the end
+
+ //To get X we need to take index % 16
+ //To get Z we need to take index/16 % 16
+ //To get Y we need to take index/256 % 16
+
+ //The following code is equivalent to using mod and div by uses bitwise operations to extract the appropriate bits instead
+ dX = index & 15;
+ dZ = (index >> 4) & 15;
+ Y = (index >> 8) & 15; //the Y within the layer
+
+ Y = Y + iLayer * 16; //This should shift the Y back
+
+ //Extract the block type
+ newBlock = BlockTypesCache.states[block];
+
+ //Creates a vector to the block
+ BlockVector3 block3Vector = BlockVector3.at(chunkVector.getX() + dX, Y, chunkVector.getBlockZ() +dZ);
+
+ //Gets the old block
+ oldBlock = get.getBlock(dX, Y, dZ);
+
+ //Records the block change
+ adaptAndRecordBlockChange(block3Vector, oldBlock, newBlock);
+
+ //Cancels the world change
+ blocks[index] = iReservedState;
+ }
+ }
+ }
+ }
+
+ return set;
+ }
+
+ @Override
+ public void postProcess(final IChunk chunk, final IChunkGet get, final IChunkSet set)
+ {
+ Bukkit.getConsoleSender().sendMessage(ChatColor.AQUA +"\n[TeachingTutorials] Post processing of chunk: " +chunk.getChunkBlockCoord().toString());
+
+ //Print out the number of new virtual blocks
+ int iSize = virtualBlocks.size();
+ Bukkit.getConsoleSender().sendMessage(ChatColor.AQUA +"[TeachingTutorials] There were " +iSize + " block changes recorded");
+
+ }
+
+ @Override
+ public Extent construct(final Extent child) {
+ if (getExtent() != child) {
+ new ExtentTraverser(this).setNext(child);
+ }
+ return this;
+ }
+
+ @Override
+ public ProcessorScope getScope() {
+ return ProcessorScope.READING_SET_BLOCKS;
+ }
+
+ /**
+ * Takes a vector, and WorldEdit BlockState objects for the old and new blocks and records the change of block
+ * @param vector A vector pointing to the location of the block change
+ * @param blockDataOld WorldEdit BlockState object for the old block
+ * @param blockDataNew WorldEdit BlockState object for the new block
+ */
+ private void adaptAndRecordBlockChange(BlockVector3 vector, BlockState blockDataOld, BlockState blockDataNew)
+ {
+ recordBlockChange(BukkitAdapter.adapt(worldEditCalculation.getWorld(), vector), BukkitAdapter.adapt(blockDataOld), BukkitAdapter.adapt(blockDataNew));
+ }
+
+ /**
+ * Creates a new virtual block and adds it to the list for this task
+ * @param location The location of the virtual block
+ * @param blockDataOld The old block's block data
+ * @param blockDataNew The new block's block data
+ */
+ private void recordBlockChange(Location location, BlockData blockDataOld, BlockData blockDataNew)
+ {
+ //Notify the console of a block change
+ Bukkit.getConsoleSender().sendMessage("[TeachingTutorials] Block change detected | Old block: " +blockDataOld.getMaterial() +", New block: " +blockDataNew.getMaterial());
+
+ //Creates a virtual block
+ VirtualBlock virtualBlock = new VirtualBlock(worldEditCalculation.getTutorialPlaythrough(), worldEditCalculation.getPlayer(), location, blockDataNew);
+
+ //Adds it to the new list
+ virtualBlocks.add(virtualBlock);
+ }
+}
+
+///**
+// * A new type of extent which records block changes and blocks them from being placed into the world
+// */
+//class BlockChangeRecorderExtentWE extends AbstractDelegateExtent
+//{
+// WorldEditCalculation worldEditCalculation;
+// HashSet virtualBlocks;
+// int iTaskID;
+//
+// public BlockChangeRecorderExtentWE(Extent originalExtent, WorldEditCalculation worldEditCalculation, HashSet virtualBlocks, int iTaskID)
+// {
+// super(originalExtent);
+// this.worldEditCalculation = worldEditCalculation;
+// this.virtualBlocks = virtualBlocks;
+// this.iTaskID = iTaskID;
+// }
+//
+// /**
+// * Creates a new virtual block and adds it to the list for this task
+// * @param location The location of the virtual block
+// * @param blockDataOld The old block's block data
+// * @param blockDataNew The new block's block data
+// */
+// private void recordBlockChange(Location location, BlockData blockDataOld, BlockData blockDataNew)
+// {
+// //Notify the console of a block change
+// Bukkit.getConsoleSender().sendMessage("[TeachingTutorials] Block change detected | Old block: " +blockDataOld.getMaterial() +", New block: " +blockDataNew.getMaterial());
+//
+// //Creates a virtual block
+// VirtualBlock virtualBlock = new VirtualBlock(worldEditCalculation.getTutorialPlaythrough(), worldEditCalculation.getPlayer(), location, blockDataNew);
+//
+// //Adds it to the new list
+// virtualBlocks.add(virtualBlock);
+// }
+//
+// @Override
+// public > boolean setBlock(BlockVector3 position, B block)
+// {
+// Bukkit.getConsoleSender().sendMessage(ChatColor.AQUA +"\n[TeachingTutorials] A world edit block change has been detected, belonging to the listener for task " +iTaskID +". Recording to the given virtual blocks list");
+// if (!block.getBlockType().equals(BlockTypes.__RESERVED__))
+// calculateBlockChange(position.getX(), position.getY(), position.getZ(), block, worldEditCalculation);
+// //return super.setBlock(position, block);
+// return false;
+// }
+//
+// @Override
+// public > boolean setBlock(int x, int y, int z, B block)
+// {
+// Bukkit.getConsoleSender().sendMessage(ChatColor.AQUA +"\n[TeachingTutorials] A world edit block change has been detected, belonging to the listener for task " +iTaskID +". Recording to the given virtual blocks list");
+// if (!block.getBlockType().equals(BlockTypes.__RESERVED__))
+// calculateBlockChange(x, y, z, block, worldEditCalculation);
+// //return super.setBlock(position, block);
+// return false;
+// }
+//
+// @Deprecated
+// private > void calculateBlockChange(int x, int y, int z, B blockDataNew, WorldEditCalculation worldEditCalculation)
+// {
+// //Creates a location object
+// Location location = new Location(worldEditCalculation.getWorld(), x, y, z);
+//
+// //Gets the original block
+// BlockData blockDataBefore = location.getBlock().getBlockData();
+//
+// //Records the change
+// recordBlockChange(location, blockDataBefore, BukkitAdapter.adapt(blockDataNew));
+// }
+//}
+//
diff --git a/src/main/java/teachingtutorials/utils/plugins/WorldEditImplementation.java b/src/main/java/teachingtutorials/utils/plugins/WorldEditImplementation.java
new file mode 100644
index 0000000..7bc61be
--- /dev/null
+++ b/src/main/java/teachingtutorials/utils/plugins/WorldEditImplementation.java
@@ -0,0 +1,6 @@
+package teachingtutorials.utils.plugins;
+
+public enum WorldEditImplementation
+{
+ WorldEdit, FAWE, NONE
+}