How to check if a player is between two areas?

Discussion in 'Plugin Development' started by -_Husky_-, May 29, 2012.

Thread Status:
Not open for further replies.
  1. Offline

    -_Husky_-

    Hey, im trying to find out how to check if a player is between 2 set locations so like..

    int ax = config.getDouble("ax");
    Int ay '. '("ay");
    Int az '. '("az")

    int bx
    int by
    int bz

    (same sort of thing for them all, they are already in a config.)
    How do i check if their location is InBetween those 6 coords?
     
  2. Code:java
    1. int px = player.getLocation().getBlockX();
    2. if(px >= ax && px <= bx)
    3. //WIN

    One important thing: ax and bx have to be sorted (at best when they are filled): If bx is smaller than ax (like: ax: 20, bx: 10) the code will always fail.
    Same for the y and z axis.
     
    -_Husky_- likes this.
  3. Offline

    -_Husky_-

    Is there a way to counter that V10lator
     
  4. -_Husky_- counter what? The amount of players inside of the area?
    Code:java
    1. public int getAmountOfPlayersInAnArea(World world, int ax, int bx, int ay, int by, int az, int bz)
    2. {
    3. Location loc;
    4. int px py pz;
    5. int c = 0;
    6. for(Player player: world.getPlayers()
    7. {
    8. loc = player.getLocation();
    9. px = loc.getBlockX();
    10. py = loc.getBlockY();
    11. pz = loc.getBlockZ();
    12. if(px >= ax && px <= bx &&
    13. py >= ay && py <= by &&
    14. pz >= az && pz <= bz)
    15. c++;
    16. }
    17. return c;
    18. }
     
    -_Husky_- likes this.
  5. Offline

    -_Husky_-

    By counter, i mean like counter-attack or fix that problem

    Sorry for not explaining ( im on ipad )

    EDIT by Moderator: merged posts, please use the edit button instead of double posting.
     
    Last edited by a moderator: May 26, 2016
  6. Sorting is easy, for performance reasons you should just sort one time: When you fill ax, bx, ... (not when you check if a player is inside...).
    Like this:
    Code:java
    1. int ax = preSavedAx();
    2. int bx = event.getClickedBlock().getX();
    3. if(bx < ax)
    4. {
    5. int tmp = ax;
    6. ax = bx;
    7. bx = tmp;
    8. }

    Like always: Same for the y and z values. :)
     
    -_Husky_- likes this.
  7. Offline

    -_Husky_-

  8. Np, but what does the emoticon tell me? Don't you understand the code or was it so simple that you're facepalming? ;) If the first:
    Code:java
    1. int ax = preSavedAx(); //Get ax. In this _example_ from some presaved location inside of your plugin.
    2. int bx = event.getClickedBlock().getX(); // Get bx. In this _example_ from the block of a PlayerInteractEvent.
    3. if(bx < ax) // If bx is smaller than ax...
    4. {
    5. int tmp = ax; // ...a temporary value gets the value of ax...
    6. ax = bx; // ...then we set ax to the value of bx...
    7. bx = tmp; // ... and finally we set bx to what ax was before with the help of the temprary value.
    8. }
     
  9. Offline

    ZachBora

    Use
    ax = Math.Min(x1, x2)
    bx = Math.Max(x1,x2)

    It's what someone taught me.
     
    nicholasntp and desht like this.
  10. ZachBora Like this:
    Code:java
    1. int ax = preSavedAx();
    2. int bx = event.getClickedBlock().getX();
    3. if(bx < ax)
    4. {
    5. int tmp = Math.Min(ax,bx);
    6. bx = Math.max(ax, bx);
    7. ax = tmp;
    8. }

    ? That doesn't sound faster/better to me. But as we don't really know the codes where he wants to use this maybe there's really a advantage to use Math.
     
  11. Offline

    desht

    Nah, to establish the bounds of a cuboid (which is basically what the OP wants), given two Location objects (which could be any two corners):
    PHP:
    int x1 Math.min(l1.getBlockX(), l2.getBlockX());
    int y1 Math.min(l1.getBlockY(), l2.getBlockY());
    int z1 Math.min(l1.getBlockZ(), l2.getBlockZ());
    int x2 Math.max(l1.getBlockX(), l2.getBlockX());
    int y2 Math.max(l1.getBlockY(), l2.getBlockY());
    int z2 Math.max(l1.getBlockZ(), l2.getBlockZ());
    Location l1 = new Location(worldx1y1z1);
    Location l2 = new Location(worldx2y2z2);
    That's the basics of how you'd construct of a cuboid-style object. From there it's really easy to write methods which check if a point is in range, iteratate over all the blocks in the Cuboid, extend/contract/move the Cuboid etc.

    E.g. a contains() method looks like:
    PHP:
    public boolean contains(Location loc) {
      return 
    loc.getBlockX() >= l1.getBlockX() && loc.getBlockX() <= l2.getBlockX()
          && 
    loc.getBlockY() >= l1.getBlockY() && loc.getBlockY() <= l2.getBlockY()
          && 
    loc.getBlockZ() >= l1.getBlockZ() && loc.getBlockZ() <= l2.getBlockZ();
    }
     
    Neodork likes this.
  12. @deshtbAre you sure it's save to store locations like that? They have a reference to the world, I think. So what if the world unloads?
    Creating two new locations every time would be more performance intensive than a simple if check like at my example. An your contains code does exactly the same, with the only difference that you never sort the x, y and z variable, so someone could easily create a damaged cuboid:

    The cuboid:
    l1: x=0, y=256. z=0
    l2: x=10, y=0; z=10

    The player:
    loc: x=5, y=128, z = 5;

    seems like the player is inside of the cuboid? But your code will fail cause
    && loc.getBlockY() >= l1.getBlockY() && loc.getBlockY() <= l2.getBlockY()
    will always return false.
     
  13. Offline

    desht

    But that won't happen because I do sort the x,y,z variables (with the calls to Math.min() and Math.max()) :)

    Good point on the advisability of storing Locations, though. My "in production" Cuboid class doesn't store Locations, although it does store a World, which admittedly has the same problems with world unloading. Ideally it should only store the world name, and throw some kind of exception (IllegalStateException perhaps?) if I attempt to pull an actual Location or Block from the Cuboid after its world has been unloaded. Of course the user of the class then needs a lot of extra logic to know what to do if the world it's working on has suddenly vanished...

    It's on my TODO list ;)
     
  14. Arrgh, I overlooked that. :)

    Surely your method has some extra advantages if you want to do more than simple location checking, but I'm still unsure what method would have the better performance. I think I'm going to profile both some day. ;)
     
  15. Offline

    ZachBora

    desht V10lator In my latest plugin that creates plots, I stole in the database the World, minX, maxX, minZ and maxZ (not the Y because I don't put more than 1 plot on the Y axis.

    - Then when a player tries to build, first I check if he's in a plot world.
    - Then I check if he's admin (build anywhere)
    - Then I check if he's in a plot with a formula (if hes not then he can't build there). It doesnt verify EVERY plots to see if he's in one, it instead determines the plot id he would currently be sitting in. It is somewhat : X / plotsize and Z / plotsize (but more complex) and gives me the plot id in X;Z format. Then I just check that plot's minX, maxX, minZ, maxZ.
    - Then I check if he owns the plot.
     
    desht likes this.
  16. Offline

    desht

    Yeah, my Cuboid class is a more general solution (although performance should still be pretty good) - it does a lot more than just checking if a location is in range.

    But the same principle applies - easiest way to see if a given Location falls between two other Locations is to first normalise the two corners with Math.min() & Math.max() on each of the X,Y,Z coords, and it's then a simple arithmetic check.

    This method will do the basic range-checking:
    PHP:
    /*
      * Check if Location loc is within the cuboid with corners l1 and l2.
      * l1 and l2 can be any two (opposite) corners of the cuboid.
      */
    public boolean isInside(Location locLocation l1Location l2) {
            
    int x1 Math.min(l1.getBlockX(), l2.getBlockX());
            
    int y1 Math.min(l1.getBlockY(), l2.getBlockY());
            
    int z1 Math.min(l1.getBlockZ(), l2.getBlockZ());
            
    int x2 Math.max(l1.getBlockX(), l2.getBlockX());
            
    int y2 Math.max(l1.getBlockY(), l2.getBlockY());
            
    int z2 Math.max(l1.getBlockZ(), l2.getBlockZ());
     
            return 
    >= x1 && <= x2 && >= y1 && <= y2 && >= z1 && <= z2;
    }
     
    if (
    isInside(locl1l2)) {
      
    // ...
    }
    or with my Cuboid class:
    PHP:
    if (new Cuboid(l1,l2).contains(loc)) {
     
    // ...
    }
    but I can also do things like:
    PHP:
    // a quick 3x2x3 slab of stone
    new Cuboid(l1).outset(Direction.Horizontal1).expand(Direction.Up1).set(Material.STONEfalse);
    :)
     
    -_Husky_- likes this.
  17. Offline

    -_Husky_-

    Oh and V10lator, my ipad changed the face to something else.. Lol thats why it was :eek: it was ment to be :D

    Sweet, thanks guys, now, is there a easy way i can check if the player is Leaving that area?

    EDIT by Moderator: merged posts, please use the edit button instead of double posting.
     
    Last edited by a moderator: May 26, 2016
  18. -_Husky_- Sure... you have an if, so you can attach an else... compared with a HashSet containing the player names that are in...
    A better explaination:
    player X enters a region: The if will catch it and put the name of X into the set.
    player X exits the region: The else will catch it and remove the name of X from the set.
    Now with a simple set.contains(player.getName()) you can always check if a player is inside of it. Of course you'll have to make the first check in onPlayerJoin, remove the name in onPlayerQuit and re-check in some lightweight way.
    Maybe there are better solutions, just try to find the best one for your specific case.

    desht Looks nice, but you're sorting the axises every time a check is perfomed. I don't know all your codes but maybe it would be smarter to sort when the cuboid is created and just re-use the pre-sorted coords if a check is perfomed?
    BTW: Here are some codes of BananaRegion to check if a block is part of a region:
    Code:java
    1. public boolean isRegion(Block block)
    2. {
    3. String world = block.getWorld().getName();
    4. worldRegions = plugin.regionHash.get(world);
    5. if(worldRegions == null)
    6. return false;
    7. return worldRegions.containsKey(world+"."+block.getX()+","+block.getZ());
    8. }

    As you see this uses a comletely other solution: Having a big HashMap (worldRegions) containing a lot of data.... Even if I improved that sheme (one map for each world, ... and yes, the world is used twice, holding comaptiblity for older save files) this is basically code from codename_B and it's damn good code. That's why I always like to play with 3rd party code: You'll see something where you may first think: "wtf" but later you'll see that the code isn't that bad... ;)
     
  19. Offline

    desht

    That isInside() method isn't part of my cuboid class - just a one-off standalone sample implementation for the benefit of the OP. Wrapping everything inside a proper cuboid class is the better way to do it.

    Yep, that's a viable way too - definitely a speed/memory tradeoff though. Maintaining a hash keyed by X,Z coords could get quite memory-intensive. Also, the use of the world name in the key check of worldRegions seems redundant, since worldRegions has already been pulled from regionHash with the world name as a key...
     
  20. Memory: Yes. Speed, not really. Its pretty fast, especially as there are some map clearing methods in the background. In the best case the list holds the regions in loaded chunks only. ;)
    True, and right now I'm wondering why I'm doing this. The statement that it's for save file compatibility is wrong: BR has no save file. Must be some leftover from the old way (one list for all worlds).
     
  21. how I did it at my own plot plugin, I am having all the plots the size 128X128, so I get the chunk, and dive it by 8, then I have the plot location, and then I get from the database if the player is owner, if hes not, check if admin, if he owner, then give them permission
     
  22. Offline

    codename_B

    Heh, V10lator haven't seen that code in a while ^^
     
    V10lator likes this.
Thread Status:
Not open for further replies.

Share This Page