/******************************************************************************* * Copyright (C) 2014 Travis Ralston (turt2live) * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see <http://www.gnu.org/licenses/>. ******************************************************************************/ package com.turt2live.antishare.bukkit.dev.check; import com.turt2live.antishare.bukkit.dev.AntiShare; import com.turt2live.antishare.bukkit.dev.CheckBase; import org.bukkit.*; import org.bukkit.block.Block; import org.bukkit.entity.FallingBlock; import org.bukkit.event.EventHandler; import org.bukkit.event.Listener; import org.bukkit.event.entity.EntityChangeBlockEvent; import org.bukkit.event.entity.ItemSpawnEvent; import java.io.BufferedWriter; import java.io.File; import java.io.FileWriter; import java.io.IOException; import java.util.ArrayList; import java.util.HashMap; import java.util.List; import java.util.Map; public class FallingSandCheck extends CheckBase implements Listener { private enum SandState { DISSOLVE, // Sand "eats" or "dissolves" the test block BREAK, // Sand breaks when landing on the test block STOP // Sand is stopped by test block (landing on top) } private boolean enabled = false; private int nextMaterial = 0; // AIR private Material[] materials = Material.values(); private Location blockLocation, sandLocation; private Map<SandState, List<Material>> results = new HashMap<>(); private int nextFall = 0; private List<Material> falling = new ArrayList<>(); public FallingSandCheck(AntiShare plugin) { super(plugin); plugin.getServer().getPluginManager().registerEvents(this, plugin); for (Material material : materials) { if (material.hasGravity()) { falling.add(material); } } } @Override public void begin() { Bukkit.broadcastMessage(ChatColor.GREEN + "Running falling sand check..."); plugin.getServer().broadcastMessage(ChatColor.LIGHT_PURPLE + "WARNING: SERVER LAG INCOMING"); World world = plugin.getServer().getWorlds().get(0); blockLocation = new Location(world, 0, 70, 0); sandLocation = blockLocation.clone().add(0, 5, 0); prepareCube(10, blockLocation); // Also sets floor // Start us off enabled = true; spawnNextSand(); } private void prepareCube(int radius, Location location) { World world = location.getWorld(); for (int x = 0; x < radius; x++) { for (int y = radius; y >= 0; y--) { for (int z = 0; z < radius; z++) { // Set ceiling if (y == radius) { Location ceilingBlock = new Location(world, x + location.getBlockX() - 5, y + location.getBlockY() + 1, z + location.getBlockZ() - 5); Block block = ceilingBlock.getBlock(); block.setType(Material.STONE); } // Clear area Location possibleBlock = new Location(world, x + location.getBlockX() - 5, y + location.getBlockY(), z + location.getBlockZ() - 5); Block block = possibleBlock.getBlock(); block.setType(Material.AIR); // Set floor if (y == 0) { Location floorBlock = new Location(world, x + location.getBlockX() - 5, location.getBlockY() - 1, z + location.getBlockZ() - 5); block = floorBlock.getBlock(); block.setType(Material.STONE); } } } } } private void writeResults() { try { for (SandState state : SandState.values()) { File outputFile = new File(plugin.getDataFolder(), falling.get(nextFall).name() + "_" + state.name() + ".txt"); if (!outputFile.getParentFile().exists()) outputFile.getParentFile().mkdirs(); List<Material> materials1 = results.get(state); if (materials1 != null) { BufferedWriter writer = new BufferedWriter(new FileWriter(outputFile, false)); for (Material material : materials1) { writer.write(material.name()); writer.newLine(); } writer.flush(); writer.close(); } } } catch (IOException e) { e.printStackTrace(); } } private void spawnNextSand() { Material test; do { nextMaterial++; // Go to next material if (nextMaterial >= materials.length) { plugin.getServer().broadcastMessage(ChatColor.GREEN + "Done " + falling.get(nextFall).name() + " test"); writeResults(); // Reset nextFall++; if (nextFall >= falling.size()) { plugin.getServer().broadcastMessage(ChatColor.DARK_GREEN + "All tests done"); enabled = false; return; } nextMaterial = 1; // Skip AIR } test = materials[nextMaterial]; } while (!test.isBlock() || invalid(test)); prepareCube(10, blockLocation); blockLocation.getBlock().setType(test); plugin.getServer().broadcastMessage(ChatColor.AQUA + "Testing " + test.name()); blockLocation.getWorld().spawnFallingBlock(sandLocation, falling.get(nextFall), (byte) 0x0); } private boolean invalid(Material material) { switch (material) { case PISTON_EXTENSION: case PISTON_MOVING_PIECE: case ENDER_PORTAL: case PORTAL: return true; } return false; } private void addResult(SandState state) { Material test = materials[nextMaterial]; // Current test if (!results.containsKey(state)) results.put(state, new ArrayList<Material>()); results.get(state).add(test); } @EventHandler public void onChange(EntityChangeBlockEvent event) { if (!enabled) return; if (event.getEntity() instanceof FallingBlock) { double distanceThreshold = 3 * 3; // 3 blocks if (event.getEntity().getLocation().distanceSquared(blockLocation) <= distanceThreshold) { if (event.getTo() != null && event.getTo() == falling.get(nextFall)) { Location spawned = event.getBlock().getLocation(); SandState state = null; if (spawned.getBlockY() == blockLocation.getBlockY()) { state = SandState.DISSOLVE; } else if (spawned.getBlockY() == blockLocation.getBlockY() + 1) { state = SandState.STOP; } else { plugin.getServer().broadcastMessage(ChatColor.DARK_RED + "WARNING: UNKNOWN STOP LOCATION!"); } if (state != null) addResult(state); plugin.getServer().getScheduler().runTaskLater(plugin, new Runnable() { @Override public void run() { spawnNextSand(); } }, 20L); // 1 second } else event.setCancelled(true); } } } @EventHandler public void onItem(ItemSpawnEvent event) { if (!enabled) return; double distanceThreshold = 3 * 3; // 3 blocks if (event.getEntity().getLocation().distanceSquared(blockLocation) <= distanceThreshold) { if (event.getEntity().getItemStack().getType() == falling.get(nextFall)) { addResult(SandState.BREAK); plugin.getServer().getScheduler().runTaskLater(plugin, new Runnable() { @Override public void run() { spawnNextSand(); } }, 20L); // 1 second } else event.setCancelled(true); } } }