├── .github ├── dependabot.yml └── workflows │ └── maven.yml ├── .gitignore ├── .idea ├── compiler.xml ├── dictionaries │ └── Ghost_chu.xml ├── misc.xml └── vcs.xml ├── CoreProtectTNT.iml ├── README.md ├── pom.xml └── src └── main ├── java └── com │ └── mcsunnyside │ └── coreprotecttnt │ ├── CTNTQueue.java │ ├── Main.java │ └── Util.java └── resources ├── config.yml └── plugin.yml /.github/dependabot.yml: -------------------------------------------------------------------------------- 1 | version: 2 2 | updates: 3 | - package-ecosystem: maven 4 | directory: "/" 5 | schedule: 6 | interval: daily 7 | open-pull-requests-limit: 10 8 | -------------------------------------------------------------------------------- /.github/workflows/maven.yml: -------------------------------------------------------------------------------- 1 | # This workflow will build a Java project with Maven, and cache/restore any dependencies to improve the workflow execution time 2 | # For more information see: https://help.github.com/actions/language-and-framework-guides/building-and-testing-java-with-maven 3 | 4 | name: Java CI with Maven 5 | 6 | on: 7 | push: 8 | branches: [ master ] 9 | pull_request: 10 | branches: [ master ] 11 | 12 | jobs: 13 | build: 14 | 15 | runs-on: ubuntu-latest 16 | 17 | steps: 18 | - uses: actions/checkout@v2 19 | - name: Set up JDK 16 20 | uses: actions/setup-java@v2 21 | with: 22 | java-version: '16' 23 | distribution: 'adopt' 24 | cache: maven 25 | - name: Build with Maven 26 | run: mvn -B package --file pom.xml 27 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | *.class 2 | .settings/ 3 | target/ 4 | .idea/ 5 | *.iml 6 | .classpath 7 | .project 8 | /bin/ 9 | /.idea/ 10 | -------------------------------------------------------------------------------- /.idea/compiler.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | -------------------------------------------------------------------------------- /.idea/dictionaries/Ghost_chu.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | -------------------------------------------------------------------------------- /.idea/misc.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 34 | 35 | 40 | 41 | 42 | 43 | 44 | 45 | 47 | -------------------------------------------------------------------------------- /.idea/vcs.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /CoreProtectTNT.iml: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # CoreProtectTNT 2 | CoreProtect addon allow you log the tnt and creeper explosion souce 3 | 4 | Please check https://github.com/Ghost-chu/CoreProtectTNT for new version 5 | -------------------------------------------------------------------------------- /pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 5 | 4.0.0 6 | 7 | com.mcsunnyside.coreprotecttnt 8 | CoreProtectTNT 9 | 2.0 10 | 11 | 12 | 13 | spigot-repo 14 | https://hub.spigotmc.org/nexus/content/repositories/snapshots/ 15 | 16 | 17 | coreprotect-repo 18 | https://maven.playpro.com/ 19 | 20 | 21 | 22 | 23 | UTF-8 24 | 25 | 26 | 27 | 28 | org.spigotmc 29 | spigot-api 30 | 1.16.5-R0.1-SNAPSHOT 31 | provided 32 | 33 | 34 | net.coreprotect 35 | coreprotect 36 | 20.4 37 | provided 38 | 39 | 40 | 41 | 42 | 43 | org.apache.maven.plugins 44 | maven-compiler-plugin 45 | 3.9.0 46 | 47 | 1.8 48 | 1.8 49 | UTF-8 50 | 51 | 52 | 53 | org.apache.maven.plugins 54 | maven-javadoc-plugin 55 | 3.3.2 56 | 57 | UTF-8 58 | 61 | 62 | 63 | 64 | attach-javadocs 65 | 66 | jar 67 | 68 | 69 | false 70 | 71 | 72 | 73 | 74 | 75 | 76 | 77 | org.apache.maven.plugins 78 | maven-source-plugin 79 | 3.2.1 80 | 81 | 82 | attach-sources 83 | 84 | jar 85 | 86 | 87 | 88 | 89 | 90 | 91 | 92 | true 93 | src/main/resources 94 | 95 | 96 | 97 | -------------------------------------------------------------------------------- /src/main/java/com/mcsunnyside/coreprotecttnt/CTNTQueue.java: -------------------------------------------------------------------------------- 1 | //package com.mcsunnyside.coreprotecttnt; 2 | // 3 | //import net.coreprotect.consumer.Queue; 4 | //import org.bukkit.Location; 5 | //import org.bukkit.Material; 6 | //import org.bukkit.block.Block; 7 | //import org.bukkit.block.BlockState; 8 | //import org.bukkit.entity.EntityType; 9 | // 10 | //import java.lang.reflect.InvocationTargetException; 11 | //import java.lang.reflect.Method; 12 | //import java.util.HashMap; 13 | //import java.util.List; 14 | //import java.util.Map; 15 | // 16 | //public class CTNTQueue { 17 | // private static Map methodMapping = new HashMap<>(); 18 | // static{ 19 | // Class clazz = Queue.class; 20 | // for (Method declaredMethod : clazz.getDeclaredMethods()) { 21 | // declaredMethod.setAccessible(true); 22 | // methodMapping.put(declaredMethod.getName(),declaredMethod); 23 | // } 24 | // } 25 | // 26 | // protected static synchronized int getChestId(String id) { 27 | // try { 28 | // return (int)methodMapping.get("getChestId").invoke(null,id); 29 | // } catch (IllegalAccessException | InvocationTargetException e) { 30 | // e.printStackTrace(); 31 | // } 32 | // return 0; 33 | // } 34 | // 35 | // protected static synchronized int getItemId(String id) { 36 | // try { 37 | // return (int)methodMapping.get("getItemId").invoke(null,id); 38 | // } catch (IllegalAccessException | InvocationTargetException e) { 39 | // e.printStackTrace(); 40 | // } 41 | // return 0; 42 | // } 43 | // protected static synchronized void queueContainerTransaction(String user, Location location, Material type, Object inventory, int chestId) { 44 | // try { 45 | // methodMapping.get("queueContainerTransaction").invoke(null,user,location,type,inventory,chestId); 46 | // } catch (IllegalAccessException | InvocationTargetException e) { 47 | // e.printStackTrace(); 48 | // } 49 | // } 50 | // 51 | // protected static void queueItemTransaction(String user, Location location, int time, int itemId) { 52 | // try { 53 | // methodMapping.get("queueItemTransaction").invoke(null,user,location,time,itemId); 54 | // } catch (IllegalAccessException | InvocationTargetException e) { 55 | // e.printStackTrace(); 56 | // } 57 | // } 58 | // 59 | // protected static void queueEntityInsert(int id, String name) { 60 | // try { 61 | // methodMapping.get("queueEntityInsert").invoke(null,id,name); 62 | // } catch (IllegalAccessException | InvocationTargetException e) { 63 | // e.printStackTrace(); 64 | // } 65 | // } 66 | // protected static void queueNaturalBlockBreak(String user, BlockState block, Block relative, Material type, int data) { 67 | // try { 68 | // methodMapping.get("queueNaturalBlockBreak").invoke(null,user,block,relative,type,data); 69 | // } catch (IllegalAccessException | InvocationTargetException e) { 70 | // e.printStackTrace(); 71 | // } 72 | // } 73 | // protected static void queueEntityKill(String user, Location location, List data, EntityType type) { 74 | // try { 75 | // methodMapping.get("queueEntityKill").invoke(null,user,location,data,type); 76 | // } catch (IllegalAccessException | InvocationTargetException e) { 77 | // e.printStackTrace(); 78 | // } 79 | // } 80 | // 81 | // protected static void queueEntitySpawn(String user, BlockState block, EntityType type, int data) { 82 | // try { 83 | // methodMapping.get("queueEntitySpawn").invoke(null,user,block,type,data); 84 | // } catch (IllegalAccessException | InvocationTargetException e) { 85 | // e.printStackTrace(); 86 | // } 87 | // } 88 | // 89 | // protected static void queueHangingRemove(String user, BlockState block, int delay) { 90 | // try { 91 | // methodMapping.get("queueHangingRemove").invoke(null,user,block,delay); 92 | // } catch (IllegalAccessException | InvocationTargetException e) { 93 | // e.printStackTrace(); 94 | // } 95 | // } 96 | // 97 | // protected static void queueHangingSpawn(String user, BlockState block, Material type, int data, int delay) { 98 | // try { 99 | // methodMapping.get("queueHangingSpawn").invoke(null,user,block,delay); 100 | // } catch (IllegalAccessException | InvocationTargetException e) { 101 | // e.printStackTrace(); 102 | // } 103 | // } 104 | // 105 | // protected static void queuePlayerInteraction(String user, BlockState block) { 106 | // try { 107 | // methodMapping.get("queuePlayerInteraction").invoke(null,user,block); 108 | // } catch (IllegalAccessException | InvocationTargetException e) { 109 | // e.printStackTrace(); 110 | // } 111 | // } 112 | // 113 | // protected static void queuePlayerKill(String user, Location location, String player) { 114 | // try { 115 | // methodMapping.get("queuePlayerKill").invoke(null,user,location,player); 116 | // } catch (IllegalAccessException | InvocationTargetException e) { 117 | // e.printStackTrace(); 118 | // } 119 | // } 120 | // 121 | // 122 | // protected static void queueRollbackUpdate(String user, Location location, List list, int action) { 123 | // try { 124 | // methodMapping.get("queueRollbackUpdate").invoke(null,user,location,list,action); 125 | // } catch (IllegalAccessException | InvocationTargetException e) { 126 | // e.printStackTrace(); 127 | // } 128 | // } 129 | // 130 | // protected static void queueSignText(String user, Location location, int action, int color, boolean glowing, String line1, String line2, String line3, String line4, int offset) { 131 | // try { 132 | // methodMapping.get("queueSignText").invoke(null,user,location,action,color,glowing,line1,line2,line3,line4,offset); 133 | // } catch (IllegalAccessException | InvocationTargetException e) { 134 | // e.printStackTrace(); 135 | // } 136 | // } 137 | // 138 | // protected static void queueSignUpdate(String user, BlockState block, int action, int time) { 139 | // try { 140 | // methodMapping.get("queueSignUpdate").invoke(null,user,block,action,time); 141 | // } catch (IllegalAccessException | InvocationTargetException e) { 142 | // e.printStackTrace(); 143 | // } 144 | // } 145 | // 146 | // protected static void queueSkullUpdate(String user, BlockState block, int rowId) { 147 | // try { 148 | // methodMapping.get("queueSkullUpdate").invoke(null,block,rowId); 149 | // } catch (IllegalAccessException | InvocationTargetException e) { 150 | // e.printStackTrace(); 151 | // } 152 | // } 153 | // 154 | // protected static void queueStructureGrow(String user, BlockState block, List blockList, int replacedListSize) { 155 | // try { 156 | // methodMapping.get("queueStructureGrow").invoke(null,block,blockList,replacedListSize); 157 | // } catch (IllegalAccessException | InvocationTargetException e) { 158 | // e.printStackTrace(); 159 | // } 160 | // } 161 | // 162 | // protected static void queueWorldInsert(int id, String world) { 163 | // try { 164 | // methodMapping.get("queueWorldInsert").invoke(null,id,world); 165 | // } catch (IllegalAccessException | InvocationTargetException e) { 166 | // e.printStackTrace(); 167 | // } 168 | // } 169 | //} 170 | -------------------------------------------------------------------------------- /src/main/java/com/mcsunnyside/coreprotecttnt/Main.java: -------------------------------------------------------------------------------- 1 | package com.mcsunnyside.coreprotecttnt; 2 | 3 | import com.google.common.cache.Cache; 4 | import com.google.common.cache.CacheBuilder; 5 | import net.coreprotect.CoreProtect; 6 | import net.coreprotect.CoreProtectAPI; 7 | import org.bukkit.Bukkit; 8 | import org.bukkit.Location; 9 | import org.bukkit.Material; 10 | import org.bukkit.block.Block; 11 | import org.bukkit.block.data.type.Bed; 12 | import org.bukkit.block.data.type.RespawnAnchor; 13 | import org.bukkit.configuration.ConfigurationSection; 14 | import org.bukkit.entity.*; 15 | import org.bukkit.entity.minecart.ExplosiveMinecart; 16 | import org.bukkit.event.EventHandler; 17 | import org.bukkit.event.EventPriority; 18 | import org.bukkit.event.Listener; 19 | import org.bukkit.event.block.*; 20 | import org.bukkit.event.entity.*; 21 | import org.bukkit.event.hanging.HangingBreakEvent; 22 | import org.bukkit.event.player.PlayerInteractEntityEvent; 23 | import org.bukkit.event.player.PlayerInteractEvent; 24 | import org.bukkit.inventory.ItemStack; 25 | import org.bukkit.plugin.Plugin; 26 | import org.bukkit.plugin.java.JavaPlugin; 27 | import org.bukkit.projectiles.ProjectileSource; 28 | 29 | import java.util.ArrayList; 30 | import java.util.List; 31 | import java.util.Locale; 32 | import java.util.Map; 33 | import java.util.concurrent.TimeUnit; 34 | 35 | public class Main extends JavaPlugin implements Listener { 36 | private CoreProtectAPI api; 37 | private final Cache probablyCache = CacheBuilder 38 | .newBuilder() 39 | .expireAfterAccess(1, TimeUnit.HOURS) 40 | .concurrencyLevel(4) // Sync and Async threads 41 | .maximumSize(50000) // Drop objects if too much, because it will cost expensive lookup. 42 | .recordStats() 43 | .build(); 44 | 45 | @Override 46 | public void onEnable() { 47 | Bukkit.getPluginManager().registerEvents(this, this); 48 | saveDefaultConfig(); 49 | Plugin depend = Bukkit.getPluginManager().getPlugin("CoreProtect"); 50 | if (depend == null) { 51 | getPluginLoader().disablePlugin(this); 52 | return; 53 | } 54 | api = ((CoreProtect) depend).getAPI(); 55 | } 56 | 57 | // Bed/RespawnAnchor explosion (tracing) 58 | @EventHandler(ignoreCancelled = true, priority = EventPriority.MONITOR) 59 | public void onPlayerInteractBedOrRespawnAnchorExplosion(PlayerInteractEvent e) { 60 | if (e.getAction() != Action.RIGHT_CLICK_BLOCK) 61 | return; 62 | Block clickedBlock = e.getClickedBlock(); 63 | Location locationHead = clickedBlock.getLocation(); 64 | if (clickedBlock.getBlockData() instanceof Bed) { 65 | Bed bed = (Bed) clickedBlock.getBlockData(); 66 | Location locationFoot = locationHead.clone().subtract(bed.getFacing().getDirection()); 67 | if (bed.getPart() == Bed.Part.FOOT) { 68 | locationHead.add(bed.getFacing().getDirection()); 69 | } 70 | String reason = "#bed-" + e.getPlayer().getName(); 71 | probablyCache.put(locationHead, reason); 72 | probablyCache.put(locationFoot, reason); 73 | } 74 | if (clickedBlock.getBlockData() instanceof RespawnAnchor) { 75 | probablyCache.put(clickedBlock.getLocation(), "#respawnanchor-" + e.getPlayer().getName()); 76 | } 77 | } 78 | 79 | // Creeper ignite (tracing) 80 | @EventHandler(ignoreCancelled = true, priority = EventPriority.MONITOR) 81 | public void onPlayerInteractCreeper(PlayerInteractEntityEvent e) { 82 | if (!(e.getRightClicked() instanceof Creeper)) 83 | return; 84 | probablyCache.put(e.getRightClicked(), "#ignitecreeper-" + e.getPlayer().getName()); 85 | } 86 | 87 | // Block explode (logger) 88 | @EventHandler(ignoreCancelled = true, priority = EventPriority.MONITOR) 89 | public void onBlockExplode(BlockExplodeEvent e) { 90 | ConfigurationSection section = Util.bakeConfigSection(getConfig(), "block-explosion"); 91 | if (!section.getBoolean("enable", true)) 92 | return; 93 | Location location = e.getBlock().getLocation(); 94 | String probablyCauses = probablyCache.getIfPresent(e.getBlock()); 95 | if (probablyCauses == null) 96 | probablyCauses = probablyCache.getIfPresent(location); 97 | if (probablyCauses == null) { 98 | if (section.getBoolean("disable-unknown", true)) { 99 | e.blockList().clear(); 100 | Util.broadcastNearPlayers(location, section.getString("alert")); 101 | } 102 | } 103 | // Found causes, let's begin for logging 104 | for (Block block : e.blockList()) { 105 | api.logRemoval(probablyCauses, block.getLocation(), block.getType(), block.getBlockData()); 106 | } 107 | } 108 | 109 | @EventHandler(ignoreCancelled = true, priority = EventPriority.MONITOR) 110 | public void onBlockPlaceOnHanging(BlockPlaceEvent event) { 111 | // We can't check the hanging in this event, may cause server lagging, just store it 112 | probablyCache.put(event.getBlock().getLocation(), event.getPlayer().getName()); 113 | } 114 | 115 | @EventHandler(ignoreCancelled = true, priority = EventPriority.MONITOR) 116 | public void onBlockBreak(BlockPlaceEvent event) { 117 | // We can't check the hanging in this event, may cause server lagging, just store it 118 | // Maybe a player break the tnt and a plugin igniting it? 119 | probablyCache.put(event.getBlock().getLocation(), event.getPlayer().getName()); 120 | } 121 | 122 | // Player item put into ItemFrame / Rotate ItemFrame (logger) 123 | @EventHandler(ignoreCancelled = true, priority = EventPriority.MONITOR) 124 | public void onClickItemFrame(PlayerInteractEntityEvent e) { // Add item to item-frame or rotating 125 | if (!(e.getRightClicked() instanceof ItemFrame)) 126 | return; 127 | ConfigurationSection section = Util.bakeConfigSection(getConfig(), "itemframe"); 128 | if (!section.getBoolean("enable", true)) 129 | return; 130 | ItemFrame itemFrame = (ItemFrame) e.getRightClicked(); 131 | // Player interacted itemframe 132 | api.logInteraction(e.getPlayer().getName(), e.getRightClicked().getLocation()); 133 | // Check item I/O 134 | if (itemFrame.getItem().getType().isAir()) { // Probably put item now 135 | ItemStack mainItem = e.getPlayer().getInventory().getItemInMainHand(); 136 | ItemStack offItem = e.getPlayer().getInventory().getItemInOffHand(); 137 | ItemStack putIn = mainItem.getType().isAir() ? offItem : mainItem; 138 | if (!putIn.getType().isAir()) { 139 | // Put in item 140 | api.logPlacement("#additem-" + e.getPlayer().getName(), e.getRightClicked().getLocation(), putIn.getType(), null); 141 | return; 142 | } 143 | } 144 | // Probably rotating ItemFrame 145 | api.logRemoval("#rotate-" + e.getPlayer().getName(), e.getRightClicked().getLocation(), itemFrame.getItem().getType(), null); 146 | api.logPlacement("#rotate-" + e.getPlayer().getName(), e.getRightClicked().getLocation(), itemFrame.getItem().getType(), null); 147 | } 148 | 149 | // Any projectile shoot (listener) 150 | @EventHandler(ignoreCancelled = true, priority = EventPriority.MONITOR) 151 | public void onProjectileLaunch(ProjectileLaunchEvent e) { 152 | if (e.getEntity().getShooter() == null) 153 | return; 154 | ProjectileSource projectileSource = e.getEntity().getShooter(); 155 | String source = ""; 156 | if (!(projectileSource instanceof Player)) 157 | source += "#"; // We only hope non-player object use hashtag 158 | source += e.getEntity().getName() + "-"; 159 | if (projectileSource instanceof Entity) { 160 | if (projectileSource instanceof Mob && ((Mob) projectileSource).getTarget() != null) { 161 | source += ((Mob) projectileSource).getTarget().getName(); 162 | } else { 163 | source += ((Entity) projectileSource).getName(); 164 | } 165 | probablyCache.put(projectileSource, source); 166 | } else { 167 | if (projectileSource instanceof Block) { 168 | source += ((Block) projectileSource).getType().name(); 169 | probablyCache.put(((Block) projectileSource).getLocation(), source); 170 | } else { 171 | source += projectileSource.getClass().getName(); 172 | probablyCache.put(projectileSource, source); 173 | } 174 | } 175 | } 176 | 177 | // TNT ignites by Player (listener) 178 | @EventHandler(ignoreCancelled = true, priority = EventPriority.MONITOR) 179 | public void onIgniteTNT(EntitySpawnEvent e) { 180 | Entity tnt = e.getEntity(); 181 | if (!(e.getEntity() instanceof TNTPrimed)) 182 | return; 183 | TNTPrimed tntPrimed = (TNTPrimed) e.getEntity(); 184 | Entity source = tntPrimed.getSource(); 185 | if (source != null) { 186 | //Bukkit has given the ignition source, track it directly. 187 | if (probablyCache.getIfPresent(source) != null) { 188 | probablyCache.put(tnt, probablyCache.getIfPresent(source)); 189 | } 190 | if (source.getType() == EntityType.PLAYER) { 191 | probablyCache.put(tntPrimed, source.getName()); 192 | return; 193 | } 194 | } 195 | Location blockCorner = tnt.getLocation().clone().subtract(0.5, 0, 0.5); 196 | for (Map.Entry entry : probablyCache.asMap().entrySet()) { 197 | if (entry.getKey() instanceof Location) { 198 | Location loc = (Location) entry.getKey(); 199 | if (loc.getWorld().equals(blockCorner.getWorld()) && loc.distance(blockCorner) < 0.5) { 200 | probablyCache.put(tnt, entry.getValue()); 201 | break; 202 | } 203 | } 204 | } 205 | } 206 | 207 | // HangingBreak (logger) 208 | @EventHandler(ignoreCancelled = true, priority = EventPriority.MONITOR) 209 | public void onHangingBreak(HangingBreakEvent e) { 210 | ConfigurationSection section = Util.bakeConfigSection(getConfig(), "hanging"); 211 | if (!section.getBoolean("enable", true)) 212 | return; 213 | if (e.getCause() == HangingBreakEvent.RemoveCause.PHYSICS || e.getCause() == HangingBreakEvent.RemoveCause.DEFAULT) 214 | return; // We can't track them tho. 215 | 216 | Block hangingPosBlock = e.getEntity().getLocation().getBlock(); 217 | String reason = probablyCache.getIfPresent(hangingPosBlock.getLocation()); 218 | if (reason != null) { 219 | api.logRemoval("#" + e.getCause().name() + "-" + reason, hangingPosBlock.getLocation(), Material.matchMaterial(e.getEntity().getType().name()), null); 220 | } 221 | } 222 | 223 | // EndCrystal rigged by entity (listener) 224 | @EventHandler(ignoreCancelled = true, priority = EventPriority.LOWEST) 225 | public void onEndCrystalHit(EntityDamageByEntityEvent e) { 226 | if (!(e.getEntity() instanceof EnderCrystal)) 227 | return; 228 | if (e.getDamager() instanceof Player) { 229 | probablyCache.put(e.getEntity(), e.getDamager().getName()); 230 | } else { 231 | if (probablyCache.getIfPresent(e.getDamager()) != null) { 232 | probablyCache.put(e.getEntity(), probablyCache.getIfPresent(e.getDamager())); 233 | } else if (e.getDamager() instanceof Projectile) { 234 | Projectile projectile = (Projectile) e.getDamager(); 235 | if (projectile.getShooter() != null && projectile.getShooter() instanceof Player) { 236 | probablyCache.put(e.getEntity(), ((Player) projectile.getShooter()).getName()); 237 | } 238 | } 239 | } 240 | } 241 | 242 | // Haning hit by entity (logger) 243 | @EventHandler(ignoreCancelled = true, priority = EventPriority.MONITOR) 244 | public void onHangingHit(EntityDamageByEntityEvent e) { 245 | if (!(e.getEntity() instanceof Hanging)) 246 | return; 247 | ConfigurationSection section = Util.bakeConfigSection(getConfig(), "itemframe"); 248 | if (!section.getBoolean("enable", true)) 249 | return; 250 | ItemFrame itemFrame = (ItemFrame) e.getEntity(); 251 | if (itemFrame.getItem().getType().isAir() || itemFrame.isInvulnerable()) 252 | return; 253 | if (e.getDamager() instanceof Player) { 254 | probablyCache.put(e.getEntity(), e.getDamager().getName()); 255 | api.logInteraction(e.getDamager().getName(), itemFrame.getLocation()); 256 | api.logRemoval(e.getDamager().getName(), itemFrame.getLocation(), itemFrame.getItem().getType(), null); 257 | } else { 258 | String cause = probablyCache.getIfPresent(e.getDamager()); 259 | if (cause != null) { 260 | String reason = "#" + e.getDamager().getName() + "-" + cause; 261 | probablyCache.put(e.getEntity(), reason); 262 | api.logRemoval(reason, itemFrame.getLocation(), itemFrame.getItem().getType(), null); 263 | } 264 | } 265 | } 266 | 267 | @EventHandler(ignoreCancelled = true, priority = EventPriority.MONITOR) 268 | public void onPaintingHit(EntityDamageByEntityEvent e) { 269 | if (!(e.getEntity() instanceof Painting)) 270 | return; 271 | ConfigurationSection section = Util.bakeConfigSection(getConfig(), "painting"); 272 | if (!section.getBoolean("enable", true)) 273 | return; 274 | ItemFrame itemFrame = (ItemFrame) e.getEntity(); 275 | if (itemFrame.getItem().getType().isAir() || itemFrame.isInvulnerable()) 276 | return; 277 | 278 | if (e.getDamager() instanceof Player) { 279 | api.logInteraction(e.getDamager().getName(), itemFrame.getLocation()); 280 | api.logRemoval(e.getDamager().getName(), itemFrame.getLocation(), itemFrame.getItem().getType(), null); 281 | } else { 282 | String reason = probablyCache.getIfPresent(e.getDamager()); 283 | if (reason != null) { 284 | api.logInteraction("#" + e.getDamager().getName() + "-" + reason, itemFrame.getLocation()); 285 | api.logRemoval("#" + e.getDamager().getName() + "-" + reason, itemFrame.getLocation(), itemFrame.getItem().getType(), null); 286 | } else { 287 | if (section.getBoolean("disable-unknown")) { 288 | e.setCancelled(true); 289 | e.setDamage(0.0d); 290 | Util.broadcastNearPlayers(e.getEntity().getLocation(), section.getString("alert")); 291 | } 292 | } 293 | } 294 | } 295 | 296 | 297 | @EventHandler(ignoreCancelled = true, priority = EventPriority.LOWEST) 298 | public void onEntityHitByProjectile(EntityDamageByEntityEvent e) { 299 | if (e.getDamager() instanceof Projectile) { 300 | Projectile projectile = (Projectile)e.getDamager(); 301 | if(projectile.getShooter() instanceof Player){ 302 | probablyCache.put(e.getEntity(),((Player) projectile.getShooter()).getName()); 303 | return; 304 | } 305 | String reason = probablyCache.getIfPresent(e.getDamager()); 306 | if (reason != null) { 307 | probablyCache.put(e.getEntity(),reason); 308 | return; 309 | } 310 | probablyCache.put(e.getEntity(),e.getDamager().getName()); 311 | } 312 | } 313 | 314 | @EventHandler(ignoreCancelled = true, priority = EventPriority.MONITOR) 315 | public void onBlockIgnite(BlockIgniteEvent e) { 316 | if (e.getIgnitingEntity() != null) { 317 | if (e.getIgnitingEntity().getType() == EntityType.PLAYER) { 318 | probablyCache.put(e.getBlock().getLocation(), e.getPlayer().getName()); 319 | // Don't add it to probablyIgnitedThisTick because it's the simplest case and is logged by Core Protect 320 | return; 321 | } 322 | if (probablyCache.getIfPresent(e.getIgnitingEntity()) != null) { 323 | probablyCache.put(e.getBlock().getLocation(), probablyCache.getIfPresent(e.getIgnitingEntity())); 324 | return; 325 | } else if (e.getIgnitingEntity() instanceof Projectile) { 326 | if (((Projectile) e.getIgnitingEntity()).getShooter() != null) { 327 | ProjectileSource shooter = ((Projectile) e.getIgnitingEntity()).getShooter(); 328 | if (shooter instanceof Player) { 329 | probablyCache.put(e.getBlock().getLocation(), ((Player) shooter).getName()); 330 | return; 331 | } 332 | } 333 | } 334 | } 335 | if (e.getIgnitingBlock() != null) { 336 | if (probablyCache.getIfPresent(e.getIgnitingBlock().getLocation()) != null) { 337 | probablyCache.put(e.getBlock().getLocation(), probablyCache.getIfPresent(e.getIgnitingBlock().getLocation())); 338 | return; 339 | } 340 | } 341 | ConfigurationSection section = Util.bakeConfigSection(getConfig(), "fire"); 342 | if (!section.getBoolean("enable", true)) 343 | return; 344 | if (!section.getBoolean("disable-unknown", true)) 345 | e.setCancelled(true); 346 | } 347 | 348 | @EventHandler(ignoreCancelled = true, priority = EventPriority.MONITOR) 349 | public void onBlockBurn(BlockBurnEvent e) { 350 | ConfigurationSection section = Util.bakeConfigSection(getConfig(), "fire"); 351 | if (!section.getBoolean("enable", true)) 352 | return; 353 | if (e.getIgnitingBlock() != null) { 354 | if (probablyCache.getIfPresent(e.getIgnitingBlock().getLocation()) != null) { 355 | probablyCache.put(e.getBlock().getLocation(), probablyCache.getIfPresent(e.getIgnitingBlock().getLocation())); 356 | api.logRemoval("#fire-" + probablyCache.getIfPresent(e.getIgnitingBlock().getLocation()), e.getBlock().getLocation(), e.getBlock().getType(), e.getBlock().getBlockData()); 357 | } else if (section.getBoolean("disable-unknown", true)) { 358 | e.setCancelled(true); 359 | Util.broadcastNearPlayers(e.getIgnitingBlock().getLocation(), section.getString("alert")); 360 | } 361 | } 362 | } 363 | 364 | @EventHandler(ignoreCancelled = true, priority = EventPriority.LOWEST) 365 | public void onBombHit(ProjectileHitEvent e) { 366 | if (e.getHitEntity() instanceof ExplosiveMinecart || e.getEntityType() == EntityType.ENDER_CRYSTAL) { 367 | if (e.getEntity().getShooter() != null && e.getEntity().getShooter() instanceof Player) { 368 | if (probablyCache.getIfPresent(e.getEntity()) != null) { 369 | probablyCache.put(e.getHitEntity(), probablyCache.getIfPresent(e.getEntity())); 370 | } else { 371 | if (e.getEntity().getShooter() != null && e.getEntity().getShooter() instanceof Player) { 372 | probablyCache.put(e.getHitEntity(), ((Player) e.getEntity().getShooter()).getName()); 373 | } 374 | } 375 | } 376 | } 377 | } 378 | 379 | 380 | @EventHandler(ignoreCancelled = true, priority = EventPriority.MONITOR) 381 | public void onExplode(EntityExplodeEvent e) { 382 | Entity entity = e.getEntity(); 383 | List blockList = e.blockList(); 384 | if (blockList.isEmpty()) 385 | return; 386 | List pendingRemoval = new ArrayList<>(); 387 | String entityName = e.getEntityType().name().toLowerCase(Locale.ROOT); 388 | ConfigurationSection section = Util.bakeConfigSection(getConfig(), "entity-explosion"); 389 | if (!section.getBoolean("enable", true)) 390 | return; 391 | String track = probablyCache.getIfPresent(entity); 392 | // TNT or EnderCrystal 393 | if (entity instanceof TNTPrimed || entity instanceof EnderCrystal) { 394 | if (track != null) { 395 | String reason = "#" + entityName + "-" + track; 396 | for (Block block : blockList) { 397 | api.logRemoval(reason, block.getLocation(), block.getType(), block.getBlockData()); 398 | probablyCache.put(block.getLocation(), reason); 399 | } 400 | pendingRemoval.add(entity); 401 | } else { 402 | //Notify players this tnt or end crystal won't break any blocks 403 | if (!section.getBoolean("disable-unknown", true)) 404 | return; 405 | e.blockList().clear(); 406 | e.getEntity().remove(); 407 | Util.broadcastNearPlayers(entity.getLocation(), section.getString("alert")); 408 | } 409 | pendingRemoval.forEach(probablyCache::invalidate); 410 | return; 411 | } 412 | // Creeper... aww man 413 | if (entity instanceof Creeper) { 414 | // New added: Player ignite creeper 415 | if (track != null) { 416 | for (Block block : blockList) { 417 | api.logRemoval(track, block.getLocation(), block.getType(), block.getBlockData()); 418 | } 419 | } else { 420 | Creeper creeper = (Creeper) entity; 421 | LivingEntity creeperTarget = creeper.getTarget(); 422 | if (creeperTarget != null) { 423 | for (Block block : blockList) { 424 | api.logRemoval("#creeper-" + creeperTarget.getName(), block.getLocation(), block.getType(), block.getBlockData()); 425 | probablyCache.put(block.getLocation(), "#creeper-" + creeperTarget.getName()); 426 | } 427 | } else { 428 | //Notify players this creeper won't break any blocks 429 | if (!section.getBoolean("disable-unknown")) 430 | return; 431 | e.blockList().clear(); 432 | e.getEntity().remove(); 433 | Util.broadcastNearPlayers(e.getLocation(), section.getString("alert")); 434 | return; 435 | } 436 | } 437 | return; 438 | } 439 | if (entity instanceof Fireball) { 440 | if (track != null) { 441 | String reason = "#fireball-" + track; 442 | for (Block block : blockList) { 443 | api.logRemoval(reason, block.getLocation(), block.getType(), block.getBlockData()); 444 | probablyCache.put(block.getLocation(), reason); 445 | } 446 | pendingRemoval.add(entity); 447 | } else { 448 | if (section.getBoolean("disable-unknown")) { 449 | e.blockList().clear(); 450 | e.getEntity().remove(); 451 | Util.broadcastNearPlayers(entity.getLocation(), section.getString("alert")); 452 | } 453 | } 454 | pendingRemoval.forEach(probablyCache::invalidate); 455 | return; 456 | } 457 | if (entity instanceof ExplosiveMinecart) { 458 | boolean isLogged = false; 459 | Location blockCorner = entity.getLocation().clone().subtract(0.5, 0, 0.5); 460 | for (Map.Entry entry : probablyCache.asMap().entrySet()) { 461 | if (entry.getKey() instanceof Location) { 462 | Location loc = (Location) entry.getKey(); 463 | if (loc.getWorld().equals(blockCorner.getWorld()) && loc.distance(blockCorner) < 1) { 464 | for (Block block : blockList) { 465 | api.logRemoval("#tntminecart-" + entry.getValue(), block.getLocation(), block.getType(), block.getBlockData()); 466 | probablyCache.put(block.getLocation(), "#tntminecart-" + entry.getValue()); 467 | } 468 | isLogged = true; 469 | break; 470 | } 471 | } 472 | } 473 | if (!isLogged) { 474 | if (probablyCache.getIfPresent(entity) != null) { 475 | String reason = "#tntminecart-" + probablyCache.getIfPresent(entity); 476 | for (Block block : blockList) { 477 | api.logRemoval(reason, block.getLocation(), block.getType(), block.getBlockData()); 478 | probablyCache.put(block.getLocation(), reason); 479 | } 480 | pendingRemoval.add(entity); 481 | } else if (section.getBoolean("disable-unknown")) { 482 | e.blockList().clear(); 483 | Util.broadcastNearPlayers(entity.getLocation(), section.getString("alert")); 484 | } 485 | } 486 | pendingRemoval.forEach(probablyCache::invalidate); 487 | return; 488 | } 489 | if (track == null || track.isEmpty()) { 490 | if (e.getEntity() instanceof Mob && ((Mob) e.getEntity()).getTarget() != null) 491 | track = ((Mob) e.getEntity()).getTarget().getName(); 492 | } 493 | // No matches, plugin explode or cannot to track? 494 | if (track == null || track.isEmpty()) { 495 | EntityDamageEvent cause = e.getEntity().getLastDamageCause(); 496 | if (cause != null) { 497 | if (cause instanceof EntityDamageByEntityEvent) { 498 | track = "#" + e.getEntity().getName() + "-" + ((EntityDamageByEntityEvent) cause).getDamager().getName(); 499 | } 500 | } 501 | } 502 | 503 | if (track != null && !track.isEmpty()) { 504 | for (Block block : e.blockList()) { 505 | api.logRemoval(track, block.getLocation(), block.getType(), block.getBlockData()); 506 | } 507 | } else if (section.getBoolean("disable-unknown")) { 508 | e.blockList().clear(); 509 | e.getEntity().remove(); 510 | Util.broadcastNearPlayers(entity.getLocation(), section.getString("alert")); 511 | } 512 | } 513 | } -------------------------------------------------------------------------------- /src/main/java/com/mcsunnyside/coreprotecttnt/Util.java: -------------------------------------------------------------------------------- 1 | package com.mcsunnyside.coreprotecttnt; 2 | 3 | import org.bukkit.ChatColor; 4 | import org.bukkit.Location; 5 | import org.bukkit.configuration.Configuration; 6 | import org.bukkit.configuration.ConfigurationSection; 7 | import org.bukkit.entity.Entity; 8 | import org.bukkit.entity.Player; 9 | 10 | public class Util { 11 | public static void broadcastNearPlayers(Location location, String message) { 12 | if (message == null || message.isEmpty()) { 13 | return; // Do not send empty message 14 | } 15 | String msg = ChatColor.translateAlternateColorCodes('&', message); 16 | for (Entity around : location.getWorld().getNearbyEntities(location, 15, 15, 15, (entity) -> entity instanceof Player)) { 17 | around.sendMessage(msg); 18 | } 19 | 20 | } 21 | 22 | public static ConfigurationSection bakeConfigSection(Configuration configuration, String path) { 23 | ConfigurationSection section = configuration.getConfigurationSection(path); 24 | if (section == null) { 25 | // Create default section with default values 26 | section = configuration.createSection(path); 27 | section.set("enable", true); 28 | section.set("disable-unknown", true); 29 | section.set("alert", ChatColor.RED + "Failed to read translation, configuration section missing!"); 30 | } 31 | return section; 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /src/main/resources/config.yml: -------------------------------------------------------------------------------- 1 | block-explosion: 2 | # Should we log and trace this type of explosion? 3 | enable: true 4 | # Cancel the explosion if its source can't be tracked. 5 | disable-unknown: true 6 | # The message that will be broadcast to players. 7 | alert: "&cAn explosion near you has been blocked because it was caused by an unknown object." 8 | entity-explosion: 9 | enable: true 10 | disable-unknown: true 11 | alert: "&cAn explosion near you has been blocked because that caused by unknown object." 12 | fire: 13 | enable: true 14 | disable-unknown: true 15 | alert: "&cAn fire spreading/burning has been blocked because that caused by unknown object." 16 | itemframe: 17 | enable: true 18 | disable-unknown: true 19 | alert: "&cBlocked item-frame destroy because that caused by unknown object." 20 | hanging: 21 | enable: true 22 | disable-unknown: true 23 | alert: "&cBlocked hanging destroy because that caused by unknown object." -------------------------------------------------------------------------------- /src/main/resources/plugin.yml: -------------------------------------------------------------------------------- 1 | name: CoreProtectTNT 2 | main: com.mcsunnyside.coreprotecttnt.Main 3 | version: 1.0 4 | api-version: 1.16 5 | depend: 6 | - CoreProtect --------------------------------------------------------------------------------