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 +}