1.15.2 Linking Overworld, Nether, End

Discussion in 'Spigot Plugin Development' started by wand555, Mar 26, 2020.

  1. I have created 3 worlds with the types NORMAL, NETHER, END and now I want a player when he uses a portal to teleport to that world. But server still teleports the player to the "regular world" (world_nether).
    Instead of teleporting the player to "world", "world_nether", "world_the_end" I want them to teleport to "custom_overworld", "custom_nether", "custom_end".
    Is there a way to do this besides basically doing what Minecraft does when creating a portal?
     
  2. You can use Player#teleport(Location). The constructor for the location takes a world as argument. Is that what you‘ve been asking for?
     
  3. I know, but thats not what I'm looking for. Bukkit/Spigot links the standard worlds (world, world_nether, world_the_end) together. So when a player uses a portal they get teleported to the world accordingly. Now I have created 3 other worlds (normal, nether, end) and I want to have the same portal-warp logic applied here. However when a player uses a netherportal it teleports them to the standard world_nether. Preferably I'd like to have a way to just change the linking, but I haven't found something so far.
    My approach to this (for overworld -> nether and vice-versa) would be store the location before the warp, calculating the nether location (overworldLocation*8), finding a place to spawn a portal, spawn it and then spawn the player. Though this seems to be kinda tidious and maybe a bit resource intensive...
     
  4. Have you looked into events for portal teleportion?

    i.e. 1. cancel normal nether portal events 2. World.getWorld("world name"). 3. Teleport to same coords in other world.
     
  5. I think people are missunderstanding my goal or my thread question is too vague.
    I know I can teleport people into other worlds, what I want to do is recreate the portal etc. Basically everything the TravelAgent/Minecraft does. Now I'm asking if there's a way to "change the existing linking" because otherwise I have to recreate every portal logic and behaviour myself (which I already started with because I didnt find anything I could use)
    EDIT: Also to your 3rd point: That won't work. The other nether world is completely different. How do I ensure the location isn't in blocks, etc. (besides having a triple for loop checking area and so on)
     
  6. U can make a easy plugin with event when player use the portal and cancell it and change the world u want teleport
     
    • Like Like x 1
  7. NO THATS NOT WHAT I MEAN.
    Sorry for caps but I'm getting a bit mad at these responses :D
    To the player building and entering the netherportal it should feel seemless. I have to create a nether portal in the nether dimension as well. And for that I cannot just get the coords and teleport the player there on the event.
    What I'm currently working on is:
    Player enters nether portal -> event cancels and set spawning in "normal nether world" to false -> get player coords and divide by 8 -> take that as center point in "own nether world" -> search for a spot where the portal would fit (in a set radius) -> spawn nether portal -> teleport player to the center of that portal
    To be more specific, I'm working on the "search for a spot where the portal would fit" part because it's not super easy since I can't just check in a rectangle/or circle directly, because it should search from the center outwards.
     
  8. Hey Wand555, to be honest I think we are all struggling to understand your posts because they seem all jumbled up.
    Please can you let me know whether I've interpreted this correctly:
    1. Player enters portal in the Normal Over-world. Normal functionality of teleportation to the Nether is cancelled.
    2. Player coords are recorded in cache.
    3. If you want to keep normal Nether functionality then Player coords are multiplied by 8.
    4. Then check that block in the Nether world.getHighestBlockAt(playerX*8, playerZ*8). If this doesn't work then you may need to create your own function to search for the highest block within the nether safe area.
    5. Then check all surrounding blocks for the portal.
    6. Create portal and teleport player in-front of the portal.
    Otherwise, wouldn't a world management plugin handle this for you?
     
  9. Divided by 8. When a player enters a nether portal with 800/Y/800 in the overworld, he spawns at 100/Y/100 in the nether.
    I need to do my own, as getHighestBlockAt(...) returns the highest block he can find, not the nearest to the where the portal should be created.
    Speaking of that I now have a working method the get the nearest free spot to create a portal. It's not super pretty but it's working I think.
    Code (Java):
    public static Location findFreeSpot(World worldTo, Location loc, int length, int height, BlockFace direction) {
            int netherX = loc.getBlockX()/8;
            int netherY = loc.getBlockY();
            int netherZ = loc.getBlockZ()/8;
            int radius = 128;
           
            //iterates like a spiral (and inverted) staircase (at least its supposed to :P)
            for(int y = 0,x = 0,z = 0; (y<=124-height && -y+netherY>=4) && x <=radius && z<=radius; y++, x++, z++) {
                //these are just to understand it, they're by no means nessecary
                int toAboveY = y;
                int toBelowY = -y;
                int toRightX = x;
                int toLeftX = -x;
                int toRightZ = z;
                int toLeftZ = -z;
               
                Block xToRightZToRightAboveBlock = worldTo.getBlockAt(netherX+toRightX, netherY+toAboveY, netherZ+toRightZ);
                if(!checkedLocs.contains(xToRightZToRightAboveBlock.getLocation())) {
                    if(findEntirePortalFitInArea(worldTo,
                            xToRightZToRightAboveBlock.getX(),
                            xToRightZToRightAboveBlock.getY(),
                            xToRightZToRightAboveBlock.getZ(), length, height, direction)) {
                        return spawnPortal(xToRightZToRightAboveBlock.getLocation(), length, height, direction);
                    }
                }
               
                Block xToRightZToRightBelowBlock = worldTo.getBlockAt(netherX+toRightX, netherY+toBelowY, netherZ+toRightZ);
                if(!checkedLocs.contains(xToRightZToRightBelowBlock.getLocation())) {
                    if(findEntirePortalFitInArea(worldTo,
                            xToRightZToRightBelowBlock.getX(),
                            xToRightZToRightBelowBlock.getY(),
                            xToRightZToRightBelowBlock.getZ(), length, height, direction)) {
                        return spawnPortal(xToRightZToRightBelowBlock.getLocation(), length, height, direction);
                    }
                }
               
                Block xToRightZToLeftAboveBlock = worldTo.getBlockAt(netherX+toRightX, netherY+toAboveY, netherZ+toLeftZ);
                if(!checkedLocs.contains(xToRightZToLeftAboveBlock.getLocation())) {
                    if(findEntirePortalFitInArea(worldTo,
                            xToRightZToLeftAboveBlock.getX(),
                            xToRightZToLeftAboveBlock.getY(),
                            xToRightZToLeftAboveBlock.getZ(), length, height, direction)) {
                        return spawnPortal(xToRightZToLeftAboveBlock.getLocation(), length, height, direction);
                    }
                }
               
                Block xToRightZToLeftBelowBlock = worldTo.getBlockAt(netherX+toRightX, netherY+toBelowY, netherZ+toLeftZ);
                if(!checkedLocs.contains(xToRightZToLeftBelowBlock.getLocation())) {
                    if(findEntirePortalFitInArea(worldTo,
                            xToRightZToLeftBelowBlock.getX(),
                            xToRightZToLeftBelowBlock.getY(),
                            xToRightZToLeftBelowBlock.getZ(), length, height, direction)) {
                        return spawnPortal(xToRightZToLeftBelowBlock.getLocation(), length, height, direction);
                    }
                }
               
               
                Block xToLeftZToRightAboveBlock = worldTo.getBlockAt(netherX+toLeftX, netherY+toAboveY, netherZ+toRightZ);
                if(!checkedLocs.contains(xToLeftZToRightAboveBlock.getLocation())) {
                    if(findEntirePortalFitInArea(worldTo,
                            xToLeftZToRightAboveBlock.getX(),
                            xToLeftZToRightAboveBlock.getY(),
                            xToLeftZToRightAboveBlock.getZ(), length, height, direction)) {
                        return spawnPortal(xToLeftZToRightAboveBlock.getLocation(), length, height, direction);
                    }
                }
               
                Block xToLeftZToRightBelowBlock  = worldTo.getBlockAt(netherX+toLeftX, netherY+toBelowY, netherZ+toRightZ);
                if(!checkedLocs.contains(xToLeftZToRightBelowBlock.getLocation())) {
                    if(findEntirePortalFitInArea(worldTo,
                            xToLeftZToRightBelowBlock.getX(),
                            xToLeftZToRightBelowBlock.getY(),
                            xToLeftZToRightBelowBlock.getZ(), length, height, direction)) {
                        return spawnPortal(xToLeftZToRightBelowBlock.getLocation(), length, height, direction);
                    }
                }
               
               
                Block xToLeftZToLeftAboveBlock = worldTo.getBlockAt(netherX+toLeftX, netherY+toAboveY, netherZ+toLeftZ);
                if(!checkedLocs.contains(xToLeftZToLeftAboveBlock.getLocation())) {
                    if(findEntirePortalFitInArea(worldTo,
                            xToLeftZToLeftAboveBlock.getX(),
                            xToLeftZToLeftAboveBlock.getY(),
                            xToLeftZToLeftAboveBlock.getZ(), length, height, direction)) {
                        return spawnPortal(xToLeftZToLeftAboveBlock.getLocation(), length, height, direction);
                    }
                }
               
                Block xToLeftZToLeftBelowBlock = worldTo.getBlockAt(netherX+toLeftX, netherY+toBelowY, netherZ+toLeftZ);
                if(!checkedLocs.contains(xToLeftZToLeftBelowBlock.getLocation())) {
                    if(findEntirePortalFitInArea(worldTo,
                            xToLeftZToLeftBelowBlock.getX(),
                            xToLeftZToLeftBelowBlock.getY(),
                            xToLeftZToLeftBelowBlock.getZ(), length, height, direction)) {
                        return spawnPortal(xToLeftZToLeftBelowBlock.getLocation(), length, height, direction);
                    }
                }  
            }
            return null;
        }
    Code (Java):
    private static boolean findEntirePortalFitInArea(World world, int netherX, int netherY, int netherZ, int length, int height, BlockFace direction) {
           
            //increment X and Y
            if(direction == BlockFace.NORTH) {
                for(int l=0; l<=length; l++) {
                    for(int h=0; h<=height; h++) {
                        if(l == 0 && h == 0) continue;
                        Block b = world.getBlockAt(netherX+l, netherY+h, netherZ);
                        System.out.println("X: " + (netherX+l) + " Y: " + (netherY+h) + " Z: " + netherZ);
                        world.spawnParticle(Particle.BARRIER, b.getLocation(), 10);
                        if(b.getType() != Material.AIR) {
                            System.out.println("returned false");
                            return false;
                        }
                        else {
                            checkedLocs.add(b.getLocation());
                        }
                    }
                }
                return true;
            }
            //increment Z and Y
            else if(direction == BlockFace.EAST) {
                for(int l=0; l<=length; l++) {
                    for(int h=0; h<=height; h++) {
                        if(l == 0 && h == 0) continue;
                        if(world.getBlockAt(netherX, netherY+h, netherZ+l).getType() != Material.AIR) {
                            return false;
                        }
                    }
                }
                return true;
            }
            //decrement X and increment Y
            else if(direction == BlockFace.SOUTH) {
                for(int l=0; l<=length; l++) {
                    for(int h=0; h<=height; h++) {
                        if(l == 0 && h == 0) continue;
                        if(world.getBlockAt(netherX-l, netherY+h, netherZ).getType() != Material.AIR) {
                            return false;
                        }
                    }
                }
                return true;
            }
            //decrement Z and increment Y
            else if(direction == BlockFace.WEST) {
                for(int l=0; l<=length; l++) {
                    for(int h=0; h<=height; h++) {
                        if(l == 0 && h == 0) continue;
                        if(world.getBlockAt(netherX, netherY+h, netherZ-l).getType() != Material.AIR) {
                            return false;
                        }
                    }
                }
                return true;
            }
           
            return false;  
        }
    Code (Text):
    public static Location spawnPortal(Location loc, int length, int height, BlockFace direction) {
            World world = loc.getWorld();
            int rootX = loc.getBlockX();
            int rootY = loc.getBlockY();
            int rootZ = loc.getBlockZ();
            System.out.println("X: " + rootX + "Y: " + rootY + "Z: " + rootZ);
            //increment X and Y
            if(direction == BlockFace.NORTH) {
                for(int l=0; l<=length; l++) {
                    for(int h=0; h<=height; h++) {
                        //obsidian walls
                        if(l == 0 || h == 0 || l == length || h == height) {
                            world.getBlockAt(rootX+l, rootY+h, rootZ).setType(Material.OBSIDIAN, false);
                        }
                        //portal block
                        else {
                            world.getBlockAt(rootX+l, rootY+h, rootZ).setType(Material.NETHER_PORTAL, false);
                        }
                    }
                }
                return loc.add(length/2d, 1, 0);
            }
            //increment Z and Y
            else if(direction == BlockFace.EAST) {
                for(int l=0; l<=length; l++) {
                    for(int h=0; h<=height; h++) {
                        //obsidian walls
                        if(l == 0 || h == 0 || l == length || h == height) {
                            world.getBlockAt(rootX, rootY+h, rootZ+l).setType(Material.OBSIDIAN, false);
                        }
                        //portal block
                        else {
                            world.getBlockAt(rootX, rootY+h, rootZ+l).setType(Material.NETHER_PORTAL, false);
                        }
                    }
                }
            }
            //decrement X and increment Y
            else if(direction == BlockFace.SOUTH) {
                for(int l=0; l<=length; l++) {
                    for(int h=0; h<=height; h++) {
                        //obsidian walls
                        if(l == 0 || h == 0 || l == length || h == height) {
                            world.getBlockAt(rootX-l, rootY+h, rootZ).setType(Material.OBSIDIAN, false);
                        }
                        //portal block
                        else {
                            world.getBlockAt(rootX-l, rootY+h, rootZ).setType(Material.NETHER_PORTAL, false);
                        }
                    }
                }
            }
            //decrement Z and increment Y
            else if(direction == BlockFace.WEST) {
                for(int l=0; l<=length; l++) {
                    for(int h=0; h<=height; h++) {
                        //obsidian walls
                        if(l == 0 || h == 0 || l == length || h == height) {
                            world.getBlockAt(rootX, rootY+h, rootZ-l).setType(Material.OBSIDIAN, false);
                        }
                        //portal block
                        else {
                            world.getBlockAt(rootX, rootY+h, rootZ-l).setType(Material.NETHER_PORTAL, false);
                        }
                    }
                }
            }
            return null;
        }
    (The code right now only makes sense when direction == BlockFace.NORTH)
    Firstly I iterate over all blocks in the radius and check if the entire portal fits in there, if that's the case I spawn the portal. Now while it kinda works, what annoys me, is that it often just spawns somewhere in the air. I'd like it, so favours spawning "on floor" if available, but I have no idea where to start with that...
     
  10. "I'd like it, so favours spawning "on floor" if available, but I have no idea where to start with that..."

    Could you possibly look at where it gets the highestBlockAt in your code and then iternate down the Y axis until it isn't air?
     
  11. Okay... sorry for the late reply, was testing around with it a bit. I now use a proper spiral algorithm to iterate over the blocks. I decided to favour height over distance, meaning that when it has a X/Z point, it checks its entire Y axis for a possible spot (top to bottom).

    Code (Java):
    public static Location findFreeSpot(World worldTo, Location loc, int length, int height, BlockFace direction) {
            int netherX = loc.getBlockX();
            int netherY = loc.getBlockY();
            int netherZ = loc.getBlockZ();
            int radius = 128;
           
            int x = 0;
            int z = 0;
            int dx = 0;
            int dz = -1;
            int t = Math.max(Math.abs(netherX), Math.abs(netherZ));
            //System.out.println(t);
            for(int i=0; i<Math.pow(radius, 2); i++) {
                if((-Math.abs(netherX)/2 <= x) && (x <= Math.abs(netherX)/2) && (-Math.abs(netherZ)/2 <= z) && (z <= Math.abs(netherZ)/2)) {
                    //System.out.println((x+netherX)+","+(z+netherZ));
                    Location firstHeightFree = findFirstFreeHeight(worldTo, direction, x+netherX, netherY, z+netherZ, height);
                    if(firstHeightFree != null) {
                        //System.out.println("X: " + firstHeightFree.getBlockX() + " Y: " + firstHeightFree.getBlockY() + " Z: " + firstHeightFree.getBlockZ());
                       
                        if(findEntirePortalFitInArea(worldTo, firstHeightFree.getBlockX(), firstHeightFree.getBlockY(), firstHeightFree.getBlockZ(), length, height, direction)) {
                            return spawnPortal(firstHeightFree, length, height, direction);
                        }
                    }
                }
               
                if((x == z) || ((x < 0) && (x == -z)) || ((x > 0) && (x == 1-z))) {
                    t = dx;
                    dx = -dz;
                    dz = t;
                }
                 x += dx;
                 z += dz;
                i++;
            }  
            return null;
        }
    On a note: getHighestBlockAtY isn't useful as it returns 128 over bedrock all the time.
    I will bump this thread again once I run into problems with connecting and checking if portals are in a range so a new one isn't always created.
     
  12. Thanks, I will take a look at it. Though after a first glance you store two "gates" (individual portals) in a "Portal" class to keep them together. I rather get the distance between all portals in a world, check if their distance is below LINKING_MAX_RANGE or something like that and then teleport them only there. Your way is probably better but with this plugin only a few players will use it at the same time and even in total like once or twice per hour.
    But now I have other troubles with detecting whether a portal has been destroyed so I remove it from my portal set. BlockBreakEvent seemed to be the way to go here, but even things like water flowing into a portal breaks it and as it seems that there's no such thing as a PortalBreakEvent, I'm stranded again :p
    EDIT: BlockPhysicsEvent triggers it, but that is called like every tick and I just cannot check if any portal in that world contains the location from the event.
     
  13. the way i designed mine is using it from the start of a world. if its loaded into a pre-existing world, then it likely wont have the same affect (granted im using a whole new portal to go to a whole new world, so a pre-existing world is basically non-existent in terms of this plugin). it keeps track of all gates everywhere and forcibly pairs two of them together (this avoids the one portal linking to another existing portal and then going back to a portal you did not place).

    honestly, the way id go about detecting broken portals, is have a task that cleans up the list every so often. since storing portals only affects memory/disk capacity, broken portals simply cant be used in game so they dont affect anything (unless theres a new thing where only some portal blocks get destroyed), so they dont cause any bugs. id say its fine to ignore broken portals and clean them up every so often.
     
  14. @Warren1001 A bit off-topic, but I found out about PortalTravelAgent. I just can't figure out how to use it, do you have any idea?
    (Also found NetherTravelTrigger, but that's only for the client)
     
  15. afaik that was removed in 1.14. i dont know if its in 1.15 (cant find any information on it). unless you mean some NMS classes, which i havent bothered to look at.