4

I am making a plugin for the game called Minecraft with Bukkit API.

I have a database table called Reinforcements with the following fields: x integer, y integer, z integer. A reinforcement block is a protected block, meaning it cannot be destroyed.

I am using the EntityExplodeEvent to check for TNT explosions.

I loop through event.blocklist() and compare each block to entries in the Reinforcements table. If it exists then prevent damage to Reinforced block in explosion using event.blocklist().remove.

I can do this by getting the smallest and largest of each coordinate (x,y,z) and then checking database rows in between these two numbers. The problem with this is that it's a cube. I should be checking for a sphere. How do I do this?

Here is what I got so far, note: I am aware that this is not exactly a problem with a select statement seeing as I can compare the returned rows with event.blocklist() but I will need to know how to do this when I go to make an update statement later on.

The reason why I need to know how to check rows in a sphere is because eventually I will add an extra field to the Reinforcements table called 'durability integer' which will decrement after every explosion. Since an explosion is a sphere the update query should only update the rows in that sphere, not a cube.

Anyone? Thank you

Current MySQL Query

"SELECT X, Y, Z FROM REINFORCEMENTS WHERE DURABILITY >= 1 " +
                "AND x<=? AND x>=? AND y<=? AND y>=? AND z<=? AND z>=? AND world=?");

Complete code

@EventHandler
    public void checkForExplosion(EntityExplodeEvent event){

        if(event.isCancelled()){
            return;
        }

        //Store blocks that are inside explosion's blast radius
        List<Block> blastRadiusBlocks = event.blockList();

        //If explosion occurs in mid air it returns 0 so no need to go any
        //further since there are no blocks inside the explosions radius
        if(blastRadiusBlocks.size() < 1){
            return;
        }

        HashMap<Coordinate, Block> affectedBlocks = new HashMap<Coordinate, Block>();

        //Initialize min & max X,Y,Z coordinates
        int smallestX = blastRadiusBlocks.get(0).getX();
        int largestX = smallestX;
        int smallestY = blastRadiusBlocks.get(0).getY();
        int largestY = smallestY;
        int smallestZ = blastRadiusBlocks.get(0).getZ();
        int largestZ = smallestZ;

        //World Name
        String worldName = blastRadiusBlocks.get(0).getWorld().getName();
        World world = this.myPlugin.getServer().getWorld(worldName);

        //Find min & max X,Y,Z coordinates
        for(int i = 0; i < blastRadiusBlocks.size(); i++){
            Block block = blastRadiusBlocks.get(i);
            int blockX = block.getX();
            int blockY = block.getY();
            int blockZ = block.getZ();

            if(blockX < smallestX){
                smallestX = blockX;
            }

            if(blockX > largestX){
                largestX = blockX;
            }

            if(blockY < smallestY){
                smallestY = blockY;
            }

            if(blockY > largestY){
                largestY = blockY;
            }

            if(blockZ < smallestZ){
                smallestZ = blockZ;
            }

            if(blockZ > largestZ){
                largestZ = blockZ;
            }

            //Instantiate Coordinate class passing in parameters
            Coordinate coordinate = new Coordinate(world, blockX, blockY, blockZ);
            //Put a new entry of type Coordinate as key and type Block as value
            affectedBlocks.put(coordinate, block);
        }

        try {
            //Query database for any reinforced blocks that may be in the blast radius
            //Reinforced blocks should have a durability > 0 (aka >= 1)
            PreparedStatement ask = this.conn.prepareStatement(
                "SELECT X, Y, Z FROM REINFORCEMENTS WHERE DURABILITY >= 1 " +
                "AND x<=? AND x>=? AND y<=? AND y>=? AND z<=? AND z>=? AND world=?");
            ask.setInt(1, largestX);
            ask.setInt(2, smallestX);
            ask.setInt(3, largestY);
            ask.setInt(4, smallestY);
            ask.setInt(5, largestZ);
            ask.setInt(6, smallestZ);
            ask.setString(7, worldName);
            ask.execute();
            ResultSet result = ask.getResultSet();

            //If there was some found, loop through each one
            while(result.next()){
                //Get X,Y,Z coords of reinforced block
                int x = result.getInt(1);
                int y = result.getInt(2);
                int z = result.getInt(3);

                //Pass in x, y, z of reinforced block into affectedBlocks HashMap to instantiate a Block
                Block protectedBlock = affectedBlocks.get(new Coordinate(world, x, y, z));
                //Then remove the protectedBlock from explosion list
                event.blockList().remove(protectedBlock);
            }

            result.close();
            ask.close();

        } catch (SQLException e) {
            System.err.println("Citadel - Select Reinforcement can't keep up (possibly too many explosions):\n" + e);
        }
}
Jonathan
  • 3,016
  • 9
  • 43
  • 74
  • maybe post as a math question of intersecting spheres. – Randy Mar 20 '12 at 22:35
  • 1
    Have a look at http://janmatuschek.de/LatitudeLongitudeBoundingCoordinates and extend it by a third coordinate, there's also some nice optimization so the database can use indices to rule out any points outside the bounding cube. – Philipp Reichart Mar 20 '12 at 22:38

2 Answers2

2

if your sphere is centred at x0, y0 z0 and has radius r, then you need:

select ... from ... where ((x-x0)*(x-x0)+(y-y0)*(y-y0)+(z-z0)*(z-z0) < r*r);

(this is just pythagoras in 3d).

andrew cooke
  • 45,717
  • 10
  • 93
  • 143
2
SELECT X, Y, Z FROM REINFORCEMENTS 
WHERE DURABILITY >= 1 
AND world=?
AND (POW((X-?),2)+POW((Y-?),2)+POW((Z-?),2))<POW(?,2)

With the parameters being woldID, X,Y,Z of explosion and radius of explosion

Eugen Rieck
  • 64,175
  • 10
  • 70
  • 92
  • This is what I meant: The first parameter `world=?` is the world ID, the 2nd parameter `X-?` is the X of the explosion center, Y and Z analogous, the last is the radius of the sphere – Eugen Rieck Mar 21 '12 at 00:31
  • There is a typo in the 3rd POW. Replace > with Z. – Attila Dec 08 '14 at 11:38