From c04e944466b8566188621fb4f68926c7e6d28df3 Mon Sep 17 00:00:00 2001 From: William Bergh Date: Wed, 29 Jul 2020 02:45:16 +0200 Subject: [PATCH] A bridge for TF-FAWE Made a bridge for a TotalFreedom version of FastAsyncWorldEdit that will work better with our needs and not collide with other plugins. --- .../totalfreedommod/TotalFreedomMod.java | 3 + .../totalfreedommod/bridge/FAWEBridge.java | 199 ++++++++++++++++++ 2 files changed, 202 insertions(+) create mode 100644 src/main/java/me/totalfreedom/totalfreedommod/bridge/FAWEBridge.java diff --git a/src/main/java/me/totalfreedom/totalfreedommod/TotalFreedomMod.java b/src/main/java/me/totalfreedom/totalfreedommod/TotalFreedomMod.java index c888872d..d3d0624e 100644 --- a/src/main/java/me/totalfreedom/totalfreedommod/TotalFreedomMod.java +++ b/src/main/java/me/totalfreedom/totalfreedommod/TotalFreedomMod.java @@ -20,6 +20,7 @@ import me.totalfreedom.totalfreedommod.blocking.command.CommandBlocker; import me.totalfreedom.totalfreedommod.bridge.BukkitTelnetBridge; import me.totalfreedom.totalfreedommod.bridge.CoreProtectBridge; import me.totalfreedom.totalfreedommod.bridge.EssentialsBridge; +import me.totalfreedom.totalfreedommod.bridge.FAWEBridge; import me.totalfreedom.totalfreedommod.bridge.LibsDisguisesBridge; import me.totalfreedom.totalfreedommod.bridge.TFGuildsBridge; import me.totalfreedom.totalfreedommod.bridge.WorldEditBridge; @@ -146,6 +147,7 @@ public class TotalFreedomMod extends JavaPlugin public CoreProtectBridge cpb; public TFGuildsBridge tfg; public WorldEditBridge web; + public FAWEBridge fab; public WorldGuardBridge wgb; @Override @@ -279,6 +281,7 @@ public class TotalFreedomMod extends JavaPlugin ldb = new LibsDisguisesBridge(); tfg = new TFGuildsBridge(); web = new WorldEditBridge(); + fab = new FAWEBridge(); wgb = new WorldGuardBridge(); for (FreedomService service : fsh.getServices()) diff --git a/src/main/java/me/totalfreedom/totalfreedommod/bridge/FAWEBridge.java b/src/main/java/me/totalfreedom/totalfreedommod/bridge/FAWEBridge.java new file mode 100644 index 00000000..a7cc03ce --- /dev/null +++ b/src/main/java/me/totalfreedom/totalfreedommod/bridge/FAWEBridge.java @@ -0,0 +1,199 @@ +package me.totalfreedom.totalfreedommod.bridge; + +import com.google.gson.Gson; +import com.sk89q.worldedit.EditSession; +import com.sk89q.worldedit.function.pattern.Pattern; +import com.sk89q.worldedit.math.BlockVector3; +import com.sk89q.worldedit.regions.Region; +import com.sk89q.worldedit.world.block.BaseBlock; +import java.util.AbstractMap; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import me.totalfreedom.totalfreedommod.FreedomService; +import net.coreprotect.CoreProtectAPI; +import org.bukkit.Bukkit; +import org.bukkit.Location; +import org.bukkit.Material; +import org.bukkit.World; +import org.bukkit.block.Block; +import org.bukkit.block.data.BlockData; + +/** + * Links TF-FAWE and the CoreProtect API together with two functions. + * @author CoolJWB + */ +public class FAWEBridge extends FreedomService +{ + private CoreProtectAPI api; + private World world = null; + private final Map, Map> blocksBroken = new HashMap<>(); + private final Map, Map.Entry>> blocksPlaced = new HashMap<>(); + + @Override + public void onStart() + { + api = plugin.cpb.getCoreProtectAPI(); + + /* + * Iterates over blocks placed by GenerationCommands (in the EditSession) and adds them to the CoreProtect logs. + */ + Bukkit.getScheduler().scheduleSyncRepeatingTask(plugin, new Runnable() + { + @Override + public void run() + { + if (!(blocksBroken.isEmpty() && blocksPlaced.isEmpty())) + { + // Send all broken blocks from the last ticks to the CoreProtect API. + Map.Entry playerAndSessionEntry = null; + for (Map.Entry, Map> entry : blocksBroken.entrySet()) + { + playerAndSessionEntry = entry.getKey(); + Map dataAndVectorEntry = entry.getValue(); + List blockVector3List = new ArrayList<>(); + blockVector3List.addAll(dataAndVectorEntry.keySet()); // Deep clone the block vector to avoid a change later in the code. + + for (BlockVector3 blockVector3 : blockVector3List) + { + if (blockVector3 != null) + { + EditSession editSession = playerAndSessionEntry.getValue(); + World world = Bukkit.getWorld(editSession.getWorld().getName()); + Location location = new Location(world, blockVector3.getX(), blockVector3.getY(), blockVector3.getZ()); + BlockData blockData = Bukkit.createBlockData(dataAndVectorEntry.get(blockVector3)); + api.logRemoval(playerAndSessionEntry.getKey(), location, blockData.getMaterial(), blockData); + } + } + } + + // Clear after broken blocks have been updated. + blocksBroken.values().clear(); + blocksBroken.putIfAbsent(playerAndSessionEntry, new HashMap<>()); + + // Send all blocks placed to the CoreProtect API (except from the air as it's only a broken block). + for (Map.Entry, Map.Entry>> entry : blocksPlaced.entrySet()) + { + playerAndSessionEntry = entry.getKey(); + Map.Entry> patternAndListEntry = entry.getValue(); + Pattern pattern = patternAndListEntry.getKey(); + List blockVector3List = new ArrayList<>(); + blockVector3List.addAll(patternAndListEntry.getValue()); // Deep clone the block vector to avoid a change later in the code. + + for (BlockVector3 blockVector3 : blockVector3List) + { + if (blockVector3 != null && !pattern.apply(blockVector3).getBlockType().getMaterial().isAir()) + { + World world = Bukkit.getWorld(playerAndSessionEntry.getValue().getWorld().getName()); + Location location = new Location(world, blockVector3.getX(), blockVector3.getY(), blockVector3.getZ()); + BaseBlock block = pattern.apply(blockVector3); + Material material = Material.getMaterial(block.getBlockType().getId().replaceFirst("minecraft:", "").toUpperCase()); + api.logPlacement(playerAndSessionEntry.getKey(), location, material, material.createBlockData()); + } + } + } + + blocksPlaced.values().forEach(collection -> collection.getValue().clear()); + } + } + }, 0L, 40L); + } + + @Override + public void onStop() + { + + } + + /** + * Logs a single block edit to CoreProtect. + * @param playerName The name of the player. + * @param editSession A WorldEdit edit session to find the world. + * @param pattern A WorldEdit pattern for the new block placed. + * @param blockVector3 A WorldEdit BlockVector3 for coordinates. + */ + public void logBlockEdit(String playerName, EditSession editSession, Pattern pattern, BlockVector3 blockVector3) + { + // Cache the world used for the next iterations to come. + if(world == null || !world.getName().equals(editSession.getWorld().getName())) + { + world = Bukkit.getWorld(editSession.getWorld().getName()); + } + + Map.Entry playerAndSessionEntry = new AbstractMap.SimpleEntry(playerName, editSession); + Block block = world.getBlockAt(blockVector3.getBlockX(), blockVector3.getBlockY(), blockVector3.getBlockZ()); + + // Add the broken block to CoreProtect if it's not air. + if(!block.getType().isAir()) + { + String blockData = block.getBlockData().getAsString(); + blockData = new Gson().fromJson(new Gson().toJson(blockData), blockData.getClass()); // Overwrite original with deep clones. + blockVector3 = new Gson().fromJson(new Gson().toJson(blockVector3), blockVector3.getClass()); // Overwrite original with deep clones. + blocksBroken.putIfAbsent(playerAndSessionEntry, new HashMap<>()); + blocksBroken.get(playerAndSessionEntry).put(blockVector3, blockData); + } + + // Add the placed block to CoreProtect if it's not air. + if(!pattern.apply(blockVector3).getBlockType().getMaterial().isAir()) + { + blocksPlaced.putIfAbsent(playerAndSessionEntry, new AbstractMap.SimpleEntry<>(pattern, new ArrayList<>())); + blocksPlaced.get(playerAndSessionEntry).getValue().add(new Gson().fromJson(new Gson().toJson(blockVector3), blockVector3.getClass())); + } + } + + /** + * Logs a pattern within a region to CoreProtect. + * @param playerName The name of the player. + * @param editSession A WorldEdit edit session to find the world. + * @param region A region of blocks. + * @param pattern A WorldEdit pattern for the new blocks placed. + */ + public void logBlockEdits(String playerName, EditSession editSession, Region region, Pattern pattern) + { + // Add the broken blocks to CoreProtect. + World world = Bukkit.getWorld(region.getWorld().getName()); + List blocks = new ArrayList<>(); + + for (BlockVector3 blockVector3 : region) + { + blocks.add(world.getBlockAt(blockVector3.getBlockX(), blockVector3.getBlockY(), blockVector3.getBlockZ())); + } + + logBlockEdit(playerName, editSession, pattern, blocks); + } + + /** + * Logs a list of block edits to CoreProtect. + * @param playerName The name of the player. + * @param editSession A WorldEdit edit session to find the world. + * @param pattern A WorldEdit pattern for the new block placed. + * @param blocks A list of blocks. + */ + public void logBlockEdit(String playerName, EditSession editSession, Pattern pattern, List blocks) + { + Map.Entry playerAndSessionEntry = new AbstractMap.SimpleEntry(playerName, editSession); + + Bukkit.getScheduler().scheduleSyncDelayedTask(plugin, () -> + { + for(Block block : blocks) + { + BlockVector3 blockVector3 = BlockVector3.at(block.getX(), block.getY(), block.getZ()); + + // Add the broken block to CoreProtect if it's not air. + if (!block.getType().isAir()) + { + api.logRemoval(playerAndSessionEntry.getKey(), block.getLocation(), block.getType(), block.getBlockData()); + } + + // Add the placed block to CoreProtect if it's not air. + BaseBlock baseBlock = pattern.apply(blockVector3); + if (!baseBlock.getBlockType().getMaterial().isAir()) + { + Material material = Material.getMaterial(baseBlock.getBlockType().getId().replaceFirst("minecraft:", "").toUpperCase()); + api.logPlacement(playerAndSessionEntry.getKey(), block.getLocation(), material, material.createBlockData()); + } + } + }, 0L); + } +} \ No newline at end of file