Worlds, locations and movement
Coordinates, worlds, directions and moving things around.
Every thing in Minecraft is somewhere. On this page you will learn how Paper describes a position, how to measure distances and directions, how to launch players through the air, how to read and change the time, the weather and the game rules of a world, and how to teleport without dropping anyone into lava. You will build launch pads and a /top command.
Coordinates: x, y, z, yaw and pitch
You already know the F3 screen. It shows three numbers for where you stand and two for where you look. Paper uses exactly the same system:
- x goes east (bigger) and west (smaller).
- y goes up (bigger) and down (smaller). Sea level is at 63 in a normal world.
- z goes south (bigger) and north (smaller). Yes, north is the negative direction; this surprises everyone once.
- yaw is the direction you look sideways, in degrees: 0 is south, 90 is west, 180 is north and 270 (or -90) is east.
- pitch is the direction you look up or down: -90 is straight up, 0 is level and 90 is straight down.
Exact positions and block positions
A player can stand anywhere inside a block, so an exact position has decimals, like 100.73. A block position is a whole number: the number of the block you are inside. Paper gives you both, and you must pick the one that fits the job.
The block number is the exact number rounded down (floor), so a player at x -0.5 is inside block -1, not block 0. The middle of block 100 is 100.5, which is why teleport code often adds 0.5: it puts the player in the middle of the block instead of on its edge.
Location: a place in a world
A Location bundles a World, the three coordinates and the two look angles into one object. Players, entities and blocks all have one, and you hand a Location to anything that needs to know where:
Where this file livesworlds-locations-snippetssrcmainjavacomexampleworldslocationssnippetsLocationSnippets.java
The package com.example.worldslocationssnippets is the folder path com/example/worldslocationssnippets inside src/main/java: every dot in the package name is one folder. IntelliJ creates these folders for you when you make a new package.
- worlds-locations-snippets/
- src/main/java/com/example/worldslocationssnippets/Package com.example.worldslocationssnippets
- LocationSnippets.javayou are hereHelper class
- build.gradle.ktsThe build recipe: Paper 26.3 API, Java 25, how the jar is made
- gradle.propertiesVersion numbers used by the build
- gradlew.batRuns Gradle on Windows without installing it
- settings.gradle.ktsThe project's name
- src/main/java/com/example/worldslocationssnippets/Package com.example.worldslocationssnippets
Gray files come with the project template; you rarely edit them. Open the whole project in the Compile Lab, or download it from the project page.
13void coordinates(Player player) {14 Location where = player.getLocation();15 double exactX = where.getX();16 int blockX = where.getBlockX();17 float yaw = where.getYaw();18 float pitch = where.getPitch();19 World world = where.getWorld();20}- The player's current position. You get a copy, so changing it does not move the player.
- The exact x as a
double(a number with decimals).getY()andgetZ()work the same way. - The x of the block the player is inside, as a whole number (
int). - Yaw and pitch are
floats, another kind of decimal number.
To make a location yourself, you need the world. Yaw and pitch are optional (they default to 0, which means looking south and level):
Where this file livesworlds-locations-snippetssrcmainjavacomexampleworldslocationssnippetsLocationSnippets.java
The package com.example.worldslocationssnippets is the folder path com/example/worldslocationssnippets inside src/main/java: every dot in the package name is one folder. IntelliJ creates these folders for you when you make a new package.
- worlds-locations-snippets/
- src/main/java/com/example/worldslocationssnippets/Package com.example.worldslocationssnippets
- LocationSnippets.javayou are hereHelper class
- build.gradle.ktsThe build recipe: Paper 26.3 API, Java 25, how the jar is made
- gradle.propertiesVersion numbers used by the build
- gradlew.batRuns Gradle on Windows without installing it
- settings.gradle.ktsThe project's name
- src/main/java/com/example/worldslocationssnippets/Package com.example.worldslocationssnippets
Gray files come with the project template; you rarely edit them. Open the whole project in the Compile Lab, or download it from the project page.
22void buildLocation(World world) {23 Location mid = new Location(world, 100.5, 64, -20.5);24 Location facingEast = new Location(world, 100.5, 64, -20.5, -90f, 0f);25}- The middle of block (100, 64, -20) in
world. The.5values put you in the center of the block. - Yaw -90 means facing east and pitch 0 means looking level. The
fafter a number marks it as afloat.
Changing a Location, and why clone matters
location.add(x, y, z) moves a location by that much. It is a very common call, and it hides a trap: add changes the location you call it on, and also returns it. It does not make a new one.
1Location base = player.getLocation();2Location up = base.add(0, 2, 0);3Location east = base.add(1, 0, 0);All three variables end up pointing at one single Location, which was changed twice. You wanted three different places.
The fix is clone(), which makes an independent copy to change:
Where this file livesworlds-locations-snippetssrcmainjavacomexampleworldslocationssnippetsLocationSnippets.java
The package com.example.worldslocationssnippets is the folder path com/example/worldslocationssnippets inside src/main/java: every dot in the package name is one folder. IntelliJ creates these folders for you when you make a new package.
- worlds-locations-snippets/
- src/main/java/com/example/worldslocationssnippets/Package com.example.worldslocationssnippets
- LocationSnippets.javayou are hereHelper class
- build.gradle.ktsThe build recipe: Paper 26.3 API, Java 25, how the jar is made
- gradle.propertiesVersion numbers used by the build
- gradlew.batRuns Gradle on Windows without installing it
- settings.gradle.ktsThe project's name
- src/main/java/com/example/worldslocationssnippets/Package com.example.worldslocationssnippets
Gray files come with the project template; you rarely edit them. Open the whole project in the Compile Lab, or download it from the project page.
27void addAndClone(Player player) {28 Location here = player.getLocation();29 Location twoAbove = here.clone().add(0, 2, 0);30 Location hundredEast = here.clone().add(100, 0, 0);31}- A fresh copy of
here. Changing the copy leavesherealone. - Two blocks up. Chaining the calls (
clone().add(...)) is the usual way to write it.
subtract works like add in the other direction, and setX, setY, setZ, setYaw and setPitch change one value. location.getBlock() gives the block at that spot, and location.getWorld() the world.
Distance
a.distance(b) gives the straight-line distance in blocks. It has two catches:
- If the two locations are in different worlds, it throws an
IllegalArgumentException. Compare the worlds first. - It uses a square root, which is slow compared with ordinary math. For a simple "is he closer than 10 blocks?" check, use
distanceSquaredand compare with 10 times 10.
Where this file livesworlds-locations-snippetssrcmainjavacomexampleworldslocationssnippetsLocationSnippets.java
The package com.example.worldslocationssnippets is the folder path com/example/worldslocationssnippets inside src/main/java: every dot in the package name is one folder. IntelliJ creates these folders for you when you make a new package.
- worlds-locations-snippets/
- src/main/java/com/example/worldslocationssnippets/Package com.example.worldslocationssnippets
- LocationSnippets.javayou are hereHelper class
- build.gradle.ktsThe build recipe: Paper 26.3 API, Java 25, how the jar is made
- gradle.propertiesVersion numbers used by the build
- gradlew.batRuns Gradle on Windows without installing it
- settings.gradle.ktsThe project's name
- src/main/java/com/example/worldslocationssnippets/Package com.example.worldslocationssnippets
Gray files come with the project template; you rarely edit them. Open the whole project in the Compile Lab, or download it from the project page.
33void distance(Player first, Player second) {34 Location a = first.getLocation();35 Location b = second.getLocation();36 if (!a.getWorld().equals(b.getWorld())) {37 return;38 }39 double blocks = a.distance(b);40 boolean withinTen = a.distanceSquared(b) <= 10 * 10;41}- "If the worlds are not the same, stop." Comparing worlds with
equalsis the safe way to avoid the exception. - The exact distance, such as
7.07. Good for showing a number to the player. - The squared distance has no square root, so it is fast. Comparing it with the radius multiplied by itself gives the same answer as
distance(b) <= 10.
Vectors: direction and speed
A Vector is three numbers (x, y, z) that describe a push or a direction, not a place. You use it to say "go this way, this fast". Two vectors matter most:
location.getDirection()is the way something is looking, as a vector of length exactly 1 (a "unit vector").entity.setVelocity(vector)gives an entity a push, measured in blocks per tick. Walking is about 0.2, a strong launch about 1.5 to 3.
Where this file livesworlds-locations-snippetssrcmainjavacomexampleworldslocationssnippetsLocationSnippets.java
The package com.example.worldslocationssnippets is the folder path com/example/worldslocationssnippets inside src/main/java: every dot in the package name is one folder. IntelliJ creates these folders for you when you make a new package.
- worlds-locations-snippets/
- src/main/java/com/example/worldslocationssnippets/Package com.example.worldslocationssnippets
- LocationSnippets.javayou are hereHelper class
- build.gradle.ktsThe build recipe: Paper 26.3 API, Java 25, how the jar is made
- gradle.propertiesVersion numbers used by the build
- gradlew.batRuns Gradle on Windows without installing it
- settings.gradle.ktsThe project's name
- src/main/java/com/example/worldslocationssnippets/Package com.example.worldslocationssnippets
Gray files come with the project template; you rarely edit them. Open the whole project in the Compile Lab, or download it from the project page.
43void vectors(Player player) {44 Vector direction = player.getLocation().getDirection();45 Vector push = direction.multiply(2);46 player.setVelocity(push);47}- Where the player is looking, as an arrow of length 1. Looking straight south gives
(0, 0, 1). - Makes the arrow twice as long, so twice as fast. Like
Location, aVectorchanges itself when you callmultiply,addornormalize. - Throws the player: they now move with this velocity, so they fly in the direction they were looking.
Vectors have the usual math methods: add, subtract, multiply, length, normalize (turn into a length-1 arrow) and clone to copy, because they change themselves just like locations do:
Where this file livesworlds-locations-snippetssrcmainjavacomexampleworldslocationssnippetsLocationSnippets.java
The package com.example.worldslocationssnippets is the folder path com/example/worldslocationssnippets inside src/main/java: every dot in the package name is one folder. IntelliJ creates these folders for you when you make a new package.
- worlds-locations-snippets/
- src/main/java/com/example/worldslocationssnippets/Package com.example.worldslocationssnippets
- LocationSnippets.javayou are hereHelper class
- build.gradle.ktsThe build recipe: Paper 26.3 API, Java 25, how the jar is made
- gradle.propertiesVersion numbers used by the build
- gradlew.batRuns Gradle on Windows without installing it
- settings.gradle.ktsThe project's name
- src/main/java/com/example/worldslocationssnippets/Package com.example.worldslocationssnippets
Gray files come with the project template; you rarely edit them. Open the whole project in the Compile Lab, or download it from the project page.
49void vectorMath() {50 Vector start = new Vector(0, 64, 0);51 Vector step = new Vector(1, 0, 0);52 Vector end = start.clone().add(step.multiply(5));53 double length = end.length();54 Vector unit = end.clone().normalize();55}- Copies
start, then adds a step that is five times longer.step.multiply(5)also changesstepitself, to(5, 0, 0). - How long the arrow is, a number.
- Shrinks a copy of the vector to length 1 without changing its direction.
Worlds
A World is one dimension on your server: the overworld, the Nether, the End, or any extra world a plugin made. You get one in several ways, depending on what you have:
player.getWorld()orlocation.getWorld(): the world something is in.Bukkit.getWorld("world_nether"): a world by its name. It returns null when no world has that name.Bukkit.getWorlds(): a list of all loaded worlds. The first one is normally the main overworld.
Where this file livesworlds-locations-snippetssrcmainjavacomexampleworldslocationssnippetsLocationSnippets.java
The package com.example.worldslocationssnippets is the folder path com/example/worldslocationssnippets inside src/main/java: every dot in the package name is one folder. IntelliJ creates these folders for you when you make a new package.
- worlds-locations-snippets/
- src/main/java/com/example/worldslocationssnippets/Package com.example.worldslocationssnippets
- LocationSnippets.javayou are hereHelper class
- build.gradle.ktsThe build recipe: Paper 26.3 API, Java 25, how the jar is made
- gradle.propertiesVersion numbers used by the build
- gradlew.batRuns Gradle on Windows without installing it
- settings.gradle.ktsThe project's name
- src/main/java/com/example/worldslocationssnippets/Package com.example.worldslocationssnippets
Gray files come with the project template; you rarely edit them. Open the whole project in the Compile Lab, or download it from the project page.
57void findWorlds() {58 World nether = Bukkit.getWorld("world_nether");59 World main = Bukkit.getWorlds().get(0);60 String name = main.getName();61 World.Environment kind = main.getEnvironment();62}- The Nether of a default server is named
world_nether. The result can benullif the name is wrong, so check it before use. - The first world in the list.
get(0)means "item number zero", because Java counts from 0. - Which kind of dimension it is:
NORMAL,NETHER,THE_ENDorCUSTOM.
The default names are world, world_nether and world_the_end. Every world also has a spawn point, world.getSpawnLocation(), and a list of the players inside it, world.getPlayers().
Time and weather
Time in a world is counted in ticks, 20 per second. A full Minecraft day is 24,000 ticks, which is 20 minutes of real time. world.getTime() and setTime() use the time of the current day:
| Time value | What it looks like |
|---|---|
0 | Sunrise (the start of the day) |
6000 | Noon, the sun is highest |
12000 | Sunset |
13000 | Night begins |
18000 | Midnight |
Where this file livesworlds-locations-snippetssrcmainjavacomexampleworldslocationssnippetsLocationSnippets.java
The package com.example.worldslocationssnippets is the folder path com/example/worldslocationssnippets inside src/main/java: every dot in the package name is one folder. IntelliJ creates these folders for you when you make a new package.
- worlds-locations-snippets/
- src/main/java/com/example/worldslocationssnippets/Package com.example.worldslocationssnippets
- LocationSnippets.javayou are hereHelper class
- build.gradle.ktsThe build recipe: Paper 26.3 API, Java 25, how the jar is made
- gradle.propertiesVersion numbers used by the build
- gradlew.batRuns Gradle on Windows without installing it
- settings.gradle.ktsThe project's name
- src/main/java/com/example/worldslocationssnippets/Package com.example.worldslocationssnippets
Gray files come with the project template; you rarely edit them. Open the whole project in the Compile Lab, or download it from the project page.
64void timeAndWeather(World world) {65 world.setTime(6000);66 boolean isDay = world.isDayTime();67 world.setStorm(true);68 world.setThundering(false);69 world.setWeatherDuration(6000);70}- Jumps to noon.
- True while it is day. Handy for "only at night" features.
- Starts rain (or snow in cold biomes).
falseclears the sky. - Thunder is its own switch, separate from rain.
- How long this weather lasts, in ticks: 6000 ticks is five minutes.
Game rules
A game rule is a world setting like keepInventory, which you may know from the /gamerule command. In code, each rule is a constant in the GameRules class, and you read or change it on the world:
Where this file livesworlds-locations-snippetssrcmainjavacomexampleworldslocationssnippetsLocationSnippets.java
The package com.example.worldslocationssnippets is the folder path com/example/worldslocationssnippets inside src/main/java: every dot in the package name is one folder. IntelliJ creates these folders for you when you make a new package.
- worlds-locations-snippets/
- src/main/java/com/example/worldslocationssnippets/Package com.example.worldslocationssnippets
- LocationSnippets.javayou are hereHelper class
- build.gradle.ktsThe build recipe: Paper 26.3 API, Java 25, how the jar is made
- gradle.propertiesVersion numbers used by the build
- gradlew.batRuns Gradle on Windows without installing it
- settings.gradle.ktsThe project's name
- src/main/java/com/example/worldslocationssnippets/Package com.example.worldslocationssnippets
Gray files come with the project template; you rarely edit them. Open the whole project in the Compile Lab, or download it from the project page.
72void gameRules(World world) {73 world.setGameRule(GameRules.KEEP_INVENTORY, true);74 Boolean keepsItems = world.getGameRuleValue(GameRules.KEEP_INVENTORY);75}- The rule that keeps items when a player dies. The constants are named like the rules in the game, written in capital letters.
- Reading a rule gives a
Booleanfor true/false rules, or anIntegerfor number rules. Autocomplete shows which.
The highest block
world.getHighestBlockYAt(x, z) returns the y of the highest block you cannot walk through at that column. Put a player at that y plus 1 and they stand on the surface. world.getHighestBlockAt(x, z) returns the Block itself.
Where this file livesworlds-locations-snippetssrcmainjavacomexampleworldslocationssnippetsLocationSnippets.java
The package com.example.worldslocationssnippets is the folder path com/example/worldslocationssnippets inside src/main/java: every dot in the package name is one folder. IntelliJ creates these folders for you when you make a new package.
- worlds-locations-snippets/
- src/main/java/com/example/worldslocationssnippets/Package com.example.worldslocationssnippets
- LocationSnippets.javayou are hereHelper class
- build.gradle.ktsThe build recipe: Paper 26.3 API, Java 25, how the jar is made
- gradle.propertiesVersion numbers used by the build
- gradlew.batRuns Gradle on Windows without installing it
- settings.gradle.ktsThe project's name
- src/main/java/com/example/worldslocationssnippets/Package com.example.worldslocationssnippets
Gray files come with the project template; you rarely edit them. Open the whole project in the Compile Lab, or download it from the project page.
77void highestBlock(World world) {78 int topY = world.getHighestBlockYAt(100, 200);79 Block topBlock = world.getHighestBlockAt(100, 200);80 Location spawn = world.getSpawnLocation();81}- The surface height at x 100, z 200 as a number.
- The same spot as a
Block, which you can ask for its type (see Blocks and block data).
Teleporting safely
You met teleport and teleportAsync on the players page. Both move an entity to a Location. Two beginner problems come with teleporting:
- Unsafe destinations. If the place is inside a wall, in lava or above a hole, the player suffocates, burns or falls. Check the destination first.
- Pausing the server. If the target chunk is not loaded,
teleportloads it on the spot and the server waits.teleportAsyncloads it in the background and then moves the player.
A destination is safe when the player has free space to stand in (feet and head blocks are empty and not liquid) and something solid under the feet. This helper checks exactly that:
Where this file livesworlds-locations-demosrcmainjavacomexampleworldslocationsdemoSafeSpot.java
The package com.example.worldslocationsdemo is the folder path com/example/worldslocationsdemo inside src/main/java: every dot in the package name is one folder. IntelliJ creates these folders for you when you make a new package.
- worlds-locations-demo/
- src/main/
- java/com/example/worldslocationsdemo/Package com.example.worldslocationsdemo
- LaunchCommand.javaCommand (BasicCommand)
- LaunchPadListener.javaListener: reacts to events
- Launcher.javaHelper class
- SafeSpot.javayou are hereHelper class
- TopCommand.javaCommand (BasicCommand)
- WorldsLocationsDemoPlugin.javaMain class: Paper starts here (named as main in plugin.yml)
- resources/Files copied into the jar as they are
- plugin.ymlTells Paper the plugin's name, version and main class
- java/com/example/worldslocationsdemo/Package com.example.worldslocationsdemo
- build.gradle.ktsThe build recipe: Paper 26.3 API, Java 25, how the jar is made
- gradle.propertiesVersion numbers used by the build
- gradlew.batRuns Gradle on Windows without installing it
- settings.gradle.ktsThe project's name
- src/main/
Gray files come with the project template; you rarely edit them. Open the whole project in the Compile Lab, or download it from the project page.
1package com.example.worldslocationsdemo;2 3import org.bukkit.Location;4import org.bukkit.block.Block;5 6final class SafeSpot {7 8 private SafeSpot() {9 }10 11 static boolean isSafe(Location spot) {12 Block feet = spot.getBlock();13 Block head = spot.clone().add(0, 1, 0).getBlock();14 Block ground = spot.clone().subtract(0, 1, 0).getBlock();15 16 boolean roomToStand = feet.isPassable() && !feet.isLiquid() && head.isPassable() && !head.isLiquid();17 return roomToStand && ground.getType().isSolid();18 }19}- The block where the feet will be.
- One block higher, where the head will be. We clone first so
spotstays the same. - One block lower, the ground the player will stand on.
- True for blocks you can walk through, like air, grass and torches.
- True for water and lava. Passable but not a good place to arrive.
- The ground must be a solid block, not air or a flower.
Reacting to movement
PlayerMoveEvent fires whenever a player moves or just turns their head. That is many times per second for every player, so a handler for it must be tiny. The most important trick is to leave early unless the player really moved to a new block:
event.hasChangedBlock()is true when the player is now in a different block than before.event.hasChangedPosition()is true when any coordinate changed, even inside the same block.event.getFrom()andevent.getTo()are the old and the new location.
Demo plugin: launch pads and /top
This plugin has three features. Stepping onto an emerald block throws you forward, /launch does the same on command, and /top teleports you to the surface above you. The throwing code sits in one small class, so the command and the pad share it:
Where this file livesworlds-locations-demosrcmainjavacomexampleworldslocationsdemoLauncher.java
The package com.example.worldslocationsdemo is the folder path com/example/worldslocationsdemo inside src/main/java: every dot in the package name is one folder. IntelliJ creates these folders for you when you make a new package.
- worlds-locations-demo/
- src/main/
- java/com/example/worldslocationsdemo/Package com.example.worldslocationsdemo
- LaunchCommand.javaCommand (BasicCommand)
- LaunchPadListener.javaListener: reacts to events
- Launcher.javayou are hereHelper class
- SafeSpot.javaHelper class
- TopCommand.javaCommand (BasicCommand)
- WorldsLocationsDemoPlugin.javaMain class: Paper starts here (named as main in plugin.yml)
- resources/Files copied into the jar as they are
- plugin.ymlTells Paper the plugin's name, version and main class
- java/com/example/worldslocationsdemo/Package com.example.worldslocationsdemo
- build.gradle.ktsThe build recipe: Paper 26.3 API, Java 25, how the jar is made
- gradle.propertiesVersion numbers used by the build
- gradlew.batRuns Gradle on Windows without installing it
- settings.gradle.ktsThe project's name
- src/main/
Gray files come with the project template; you rarely edit them. Open the whole project in the Compile Lab, or download it from the project page.
1package com.example.worldslocationsdemo;2 3import org.bukkit.Sound;4import org.bukkit.entity.Player;5import org.bukkit.util.Vector;6 7final class Launcher {8 9 private static final double MINIMUM_UPWARD_SPEED = 0.8;10 11 private Launcher() {12 }13 14 static void launch(Player player, double strength) {15 Vector push = player.getLocation().getDirection().multiply(strength);16 push.setY(Math.max(push.getY(), MINIMUM_UPWARD_SPEED));17 player.setVelocity(push);18 player.playSound(player.getLocation(), Sound.ENTITY_FIREWORK_ROCKET_LAUNCH, 1.0f, 1.0f);19 }20}- A named number instead of a bare
0.8in the code.static finalmeans it is one shared value that never changes. - A private constructor stops anyone from creating a
Launcherobject. This class only holds a method, so there is nothing to create. - Where the player looks, stretched by
strength.getDirection()gives a fresh vector, so changing it is safe. - If the player looks level, the push has no upward part, and they would only slide. This makes sure there is always at least a little lift.
- The launch itself.
- Plays a firework sound at the player's position. See Titles, action bars, boss bars and sounds.
The pad listener only wakes up when the player enters a new block, then looks at the block just under their feet:
Where this file livesworlds-locations-demosrcmainjavacomexampleworldslocationsdemoLaunchPadListener.java
The package com.example.worldslocationsdemo is the folder path com/example/worldslocationsdemo inside src/main/java: every dot in the package name is one folder. IntelliJ creates these folders for you when you make a new package.
- worlds-locations-demo/
- src/main/
- java/com/example/worldslocationsdemo/Package com.example.worldslocationsdemo
- LaunchCommand.javaCommand (BasicCommand)
- LaunchPadListener.javayou are hereListener: reacts to events
- Launcher.javaHelper class
- SafeSpot.javaHelper class
- TopCommand.javaCommand (BasicCommand)
- WorldsLocationsDemoPlugin.javaMain class: Paper starts here (named as main in plugin.yml)
- resources/Files copied into the jar as they are
- plugin.ymlTells Paper the plugin's name, version and main class
- java/com/example/worldslocationsdemo/Package com.example.worldslocationsdemo
- build.gradle.ktsThe build recipe: Paper 26.3 API, Java 25, how the jar is made
- gradle.propertiesVersion numbers used by the build
- gradlew.batRuns Gradle on Windows without installing it
- settings.gradle.ktsThe project's name
- src/main/
Gray files come with the project template; you rarely edit them. Open the whole project in the Compile Lab, or download it from the project page.
1package com.example.worldslocationsdemo;2 3import org.bukkit.Location;4import org.bukkit.Material;5import org.bukkit.event.EventHandler;6import org.bukkit.event.Listener;7import org.bukkit.event.player.PlayerMoveEvent;8 9public final class LaunchPadListener implements Listener {10 11 @EventHandler12 public void onMove(PlayerMoveEvent event) {13 if (!event.hasChangedBlock()) {14 return;15 }16 17 Location belowFeet = event.getTo().clone().subtract(0, 0.5, 0);18 if (belowFeet.getBlock().getType() == Material.EMERALD_BLOCK) {19 Launcher.launch(event.getPlayer(), 2.0);20 }21 }22}- The cheap guard. Turning your head or shuffling inside one block returns here immediately.
- We clone because
getTo()is the real destination object, andsubtractwould change where the player is going. - Half a block below the feet. That finds the block the player stands on, and also works on slabs.
- The pad block.
Materiallists every block and item type. - A stronger launch than the command, because pads are for big jumps.
Where this file livesworlds-locations-demosrcmainjavacomexampleworldslocationsdemoTopCommand.java
The package com.example.worldslocationsdemo is the folder path com/example/worldslocationsdemo inside src/main/java: every dot in the package name is one folder. IntelliJ creates these folders for you when you make a new package.
- worlds-locations-demo/
- src/main/
- java/com/example/worldslocationsdemo/Package com.example.worldslocationsdemo
- LaunchCommand.javaCommand (BasicCommand)
- LaunchPadListener.javaListener: reacts to events
- Launcher.javaHelper class
- SafeSpot.javaHelper class
- TopCommand.javayou are hereCommand (BasicCommand)
- WorldsLocationsDemoPlugin.javaMain class: Paper starts here (named as main in plugin.yml)
- resources/Files copied into the jar as they are
- plugin.ymlTells Paper the plugin's name, version and main class
- java/com/example/worldslocationsdemo/Package com.example.worldslocationsdemo
- build.gradle.ktsThe build recipe: Paper 26.3 API, Java 25, how the jar is made
- gradle.propertiesVersion numbers used by the build
- gradlew.batRuns Gradle on Windows without installing it
- settings.gradle.ktsThe project's name
- src/main/
Gray files come with the project template; you rarely edit them. Open the whole project in the Compile Lab, or download it from the project page.
1package com.example.worldslocationsdemo;2 3import io.papermc.paper.command.brigadier.BasicCommand;4import io.papermc.paper.command.brigadier.CommandSourceStack;5import org.bukkit.Location;6import org.bukkit.World;7import org.bukkit.entity.Player;8 9public final class TopCommand implements BasicCommand {10 11 @Override12 public void execute(CommandSourceStack source, String[] args) {13 if (!(source.getSender() instanceof Player player)) {14 source.getSender().sendRichMessage("<red>Only players can use /top.");15 return;16 }17 18 Location here = player.getLocation();19 World world = here.getWorld();20 if (world.getEnvironment() == World.Environment.NETHER) {21 player.sendRichMessage("<red>The Nether has a roof, so /top is turned off here.");22 return;23 }24 25 int topY = world.getHighestBlockYAt(here);26 Location top = new Location(world, here.getBlockX() + 0.5, topY + 1, here.getBlockZ() + 0.5,27 here.getYaw(), here.getPitch());28 if (!SafeSpot.isSafe(top)) {29 player.sendRichMessage("<red>There is no safe place to stand up there.");30 return;31 }32 33 player.teleportAsync(top).thenAccept(success -> {34 if (success) {35 player.sendRichMessage("<green>Whoosh! You are on top of the world.");36 }37 });38 }39}- Refuses in the Nether, where the "highest block" is the bedrock roof.
- The surface height above the player's column.
- The center of the surface block, one block up (
topY + 1) so the player stands on it. The last two arguments keep the direction the player was looking. - Skips unsafe landings, for example when the surface is water.
- Loads the destination without freezing the server, then teleports.
- Runs after the teleport finished.
successtells you whether it worked.
Finally, the main class registers everything:
Where this file livesworlds-locations-demosrcmainjavacomexampleworldslocationsdemoWorldsLocationsDemoPlugin.java
The package com.example.worldslocationsdemo is the folder path com/example/worldslocationsdemo inside src/main/java: every dot in the package name is one folder. IntelliJ creates these folders for you when you make a new package.
- worlds-locations-demo/
- src/main/
- java/com/example/worldslocationsdemo/Package com.example.worldslocationsdemo
- LaunchCommand.javaCommand (BasicCommand)
- LaunchPadListener.javaListener: reacts to events
- Launcher.javaHelper class
- SafeSpot.javaHelper class
- TopCommand.javaCommand (BasicCommand)
- WorldsLocationsDemoPlugin.javayou are hereMain class: Paper starts here (named as main in plugin.yml)
- resources/Files copied into the jar as they are
- plugin.ymlTells Paper the plugin's name, version and main class
- java/com/example/worldslocationsdemo/Package com.example.worldslocationsdemo
- build.gradle.ktsThe build recipe: Paper 26.3 API, Java 25, how the jar is made
- gradle.propertiesVersion numbers used by the build
- gradlew.batRuns Gradle on Windows without installing it
- settings.gradle.ktsThe project's name
- src/main/
Gray files come with the project template; you rarely edit them. Open the whole project in the Compile Lab, or download it from the project page.
1package com.example.worldslocationsdemo;2 3import org.bukkit.plugin.java.JavaPlugin;4 5public final class WorldsLocationsDemoPlugin extends JavaPlugin {6 7 @Override8 public void onEnable() {9 registerCommand("launch", "Throws you forward and up", new LaunchCommand());10 registerCommand("top", "Takes you to the highest block above you", new TopCommand());11 getServer().getPluginManager().registerEvents(new LaunchPadListener(), this);12 }13}- Adds
/launch. Commands are covered in Commands, part 1. - Registers the pad listener, as in Events and listeners.
- worlds-locations-demo/
- src/
- main/
- java/
- com/example/worldslocationsdemo/
- WorldsLocationsDemoPlugin.javaThe main class: registers commands and the listener
- Launcher.javaThe shared throwing code
- LaunchCommand.javaThe /launch command
- LaunchPadListener.javaEmerald blocks launch players who step on them
- TopCommand.javaThe /top command
- SafeSpot.javaChecks whether a place is safe to stand on
- com/example/worldslocationsdemo/
- resources/
- plugin.ymlPlugin name, version and main class
- java/
- main/
- src/
Download the demo as a Gradle project, place an emerald block on your test world, and walk onto it.
Mistakes to avoid
- Changing a
Locationwithout cloning it.add,subtractandmultiplychange the object they are called on. - Measuring distance across worlds.
distancethrows anIllegalArgumentExceptionif the worlds differ. ComparegetWorld()first. - Mixing up block and exact coordinates. Use
getBlockX()to find a block andgetX()for exact math. - Forgetting that north is negative z. Walking north makes z smaller.
- Doing heavy work in
PlayerMoveEvent. Leave early withhasChangedBlock(). - Trusting
Bukkit.getWorld(name). It returnsnullfor an unknown name. - Using the old
GameRulenames. Pick the constants fromGameRules.
A /distance command
Write /distance (name). It tells the player how many blocks away another online player is. If the other player is in a different world, say so instead of crashing.
Hint 1
Find the other player with Bukkit.getPlayerExact and remember the null check from the players page.
Hint 2
Get both locations with getLocation(), compare their worlds with equals, and only then call distance.
Show the solution
The command leaves early four times (not a player, no argument, unknown player, different worlds). After those guards, distance is safe to call.
Where this file livesworlds-locations-exercisesrcmainjavacomexampleworldslocationsexerciseDistanceCommand.java
The package com.example.worldslocationsexercise is the folder path com/example/worldslocationsexercise inside src/main/java: every dot in the package name is one folder. IntelliJ creates these folders for you when you make a new package.
- worlds-locations-exercise/
- src/main/
- java/com/example/worldslocationsexercise/Package com.example.worldslocationsexercise
- DistanceCommand.javayou are hereCommand (BasicCommand)
- WorldsLocationsExercisePlugin.javaMain class: Paper starts here (named as main in plugin.yml)
- resources/Files copied into the jar as they are
- plugin.ymlTells Paper the plugin's name, version and main class
- java/com/example/worldslocationsexercise/Package com.example.worldslocationsexercise
- build.gradle.ktsThe build recipe: Paper 26.3 API, Java 25, how the jar is made
- gradle.propertiesVersion numbers used by the build
- gradlew.batRuns Gradle on Windows without installing it
- settings.gradle.ktsThe project's name
- src/main/
Gray files come with the project template; you rarely edit them. Open the whole project in the Compile Lab, or download it from the project page.
1package com.example.worldslocationsexercise;2 3import io.papermc.paper.command.brigadier.BasicCommand;4import io.papermc.paper.command.brigadier.CommandSourceStack;5import net.kyori.adventure.text.minimessage.tag.resolver.Placeholder;6import org.bukkit.Bukkit;7import org.bukkit.Location;8import org.bukkit.entity.Player;9 10public final class DistanceCommand implements BasicCommand {11 12 @Override13 public void execute(CommandSourceStack source, String[] args) {14 if (!(source.getSender() instanceof Player player)) {15 source.getSender().sendRichMessage("<red>Only players can measure distances.");16 return;17 }18 if (args.length == 0) {19 player.sendRichMessage("<red>Usage: /distance (name)");20 return;21 }22 23 Player other = Bukkit.getPlayerExact(args[0]);24 if (other == null) {25 player.sendRichMessage("<red>Nobody called <name> is online.",26 Placeholder.unparsed("name", args[0]));27 return;28 }29 30 Location mine = player.getLocation();31 Location theirs = other.getLocation();32 if (!mine.getWorld().equals(theirs.getWorld())) {33 player.sendRichMessage("<yellow><name> is in a different world.",34 Placeholder.unparsed("name", other.getName()));35 return;36 }37 38 long blocks = Math.round(mine.distance(theirs));39 player.sendRichMessage("<green><name> is <blocks> blocks away.",40 Placeholder.unparsed("name", other.getName()),41 Placeholder.unparsed("blocks", String.valueOf(blocks)));42 }43}- The check that prevents the
IllegalArgumentException. - Rounds to a whole number, so the message says
42and not41.7384....
A /day command
Add a /day command that sets the time to morning (1000 ticks) and clears the rain and thunder in the world of the player who typed it.
Hint
Get the world with player.getWorld(), then use setTime, setStorm and setThundering.
Show the solution
Inside execute, after you made sure the sender is a player:
1World world = player.getWorld();2world.setTime(1000);3world.setStorm(false);4world.setThundering(false);5player.sendRichMessage("<yellow>A new day begins!");Rain and thunder are separate switches, so the code turns both off.
Recap
- x goes east, y goes up and z goes south. Yaw is the sideways look angle, pitch the up and down angle.
- A
Locationholds a world, exact coordinates and look angles.getBlockX()gives the block number,getX()the exact number. add,subtractandmultiplychange the object itself. Callclone()first when you need to keep the original.distancethrows across worlds;distanceSquaredis faster when you only compare.getDirection()plusmultiplyplussetVelocitylaunches things.- A
Worldhas time (0 to 24000), weather, game rules (GameRules), a spawn point and an environment. - Check the destination before teleporting, and use
teleportAsyncto avoid pauses. - In
PlayerMoveEvent, return early unlesshasChangedBlock()is true.
Quick quiz
A player stands at x = 12.9. What does
getBlockX()return?The block number is the exact number rounded down. The player is inside block 12 (which covers 12.0 up to 13.0), sogetBlockX()returns the whole number 12.What is the bug in
Location up = base.add(0, 2, 0);if you still needbaseafterwards?addedits the location you call it on and returns the same object. Writebase.clone().add(0, 2, 0)to keepbaseunchanged.Which direction is the smaller z value?
North is negative z and south is positive z. East is positive x and west is negative x.Why should a
PlayerMoveEventhandler start withif (!event.hasChangedBlock()) return;?The guard returns early and costs almost nothing. Without it, your real work would run for every tiny movement of every player.What does
player.setVelocity(direction.multiply(2))do whendirectioncomes fromgetDirection()?A velocity is a push, not a place. The direction vector has length 1, and multiplying by 2 makes the push twice as strong.
Next steps
- Blocks and block data: read and change the blocks at a location.
- Entities and mobs: spawn creatures at a location and move them with velocity.
- The scheduler: run code later or every few ticks, for example to heal a player every second.
- Project: Parkour course with timer: uses checkpoints, distances and teleporting.