Skip to content

(Outdated v5) API

Joshua Prince edited this page Nov 16, 2025 · 1 revision

This guide is for versions 5 and below; please see the updated API Guide

CoordinateOffset offers an API so that other plugins can interact with it. With it, you can:

  • Get the current offset of a player so your plugin can (for example) send meaningful coordinates in messages
  • Define your own offset providers to customize exactly how offsets are applied

Prefer an example? Check out the example-api-plugin in this repository.

Getting Started

Add the CoordinateOffset API dependency:

// build.gradle.kts
repositories {
    mavenCentral() // For latest release
    maven("https://central.sonatype.com/repository/maven-snapshots/") // For snapshots
}
dependencies {
    // Pick one option; do not shade the API into your plugin
    compileOnly("com.jtprince.coordinateoffset:coordinateoffset-api:v5.0.0")
    compileOnly("com.jtprince.coordinateoffset:coordinateoffset-api:v5.0.1-SNAPSHOT")
}

Add CoordinateOffset as a dependency to your plugin:

# plugin.yml
depend: [ CoordinateOffset ]  # Make CoordinateOffset a required plugin
softdepend: [ CoordinateOffset ]  # Make CoordinateOffset optional
# paper-plugin.yml
dependencies:
  server:
    CoordinateOffset:
      load: BEFORE
      required: true  # or false

Using the API

All accesses to the API are done through a singleton CoordinateOffsetAPI object. Get the singleton object through CoordinateOffset.api().

import com.jtprince.coordinateoffset.api.CoordinateOffset;
import com.jtprince.coordinateoffset.api.CoordinateOffsetAPI;

CoordinateOffsetAPI coApi;
try {
    coApi = CoordinateOffset.api();
} catch (NoClassDefFoundError e) {
    // CoordinateOffset is not installed.
    // Handle this case if you are making CoordinateOffset an optional dependency.
}

Get a player's current offset

CoordinateOffset has an adapter layer to individual platforms, like Paper. Adapt any Player objects to an OffsetPlayer for use with the API.

org.bukkit.entity.Player player = /* ... */;
OffsetPlayer coPlayer = coApi.adaptPlayer(player);
Offset offset = coApi.getOffset(coPlayer);

Apply the offset to a Bukkit Location to determine which coordinates the Player sees:

org.bukkit.Location realBlockLocation = /* ... */.getLocation();
org.bukkit.Location locationPlayerSees = offset.apply(realBlockLocation);

/* ... */.getPlayer().sendMessage("You see coordinates " + locationToString(locationPlayerSees) + ".");
/* ... */.getPlayer().sendMessage("The real coordinates of that thing are " + locationToString(realBlockLocation) + ", but you might not know that!");

Or reverse the offset if you're parsing a location that a Player entered:

org.bukkit.Location locationPlayerSees = parseMessage(/* ... */);
org.bukkit.Location realLocation = offset.unapply(locationPlayerSees);

Create your own Offset Provider

To define offsets more flexibly than providers in config.yml, you can implement your own OffsetProvider class. A complete example is available here.

Register your provider in your plugin's onEnable. You must pass a deserialize function (see example below) so that CoordinateOffset knows how to parse your provider in config.yml.

public void onEnable() {
    CoordinateOffset.api().registerOffsetProviderClass("MyCustomOffsetProvider", MyCustomOffsetProvider::deserialize);
}

Implement your provider and a deserialize function:

import com.jtprince.coordinateoffset.provider.*;

public class MyCustomOffsetProvider extends OffsetProvider {
    public MyCustomOffsetProvider(String userDefinedProviderName) {
        super(userDefinedProviderName);
    }

    @Override
    public Offset provideOffset(OffsetProviderContext context) {
        // Put your offset generation logic here. "context" provides a player, world, location, etc.
        int x = /* ... */;
        int z = /* ... */
        return new Offset(x, z);
        return Offset.ZERO;
        return Offset.random(12345); // Randomize each component between +/- the provided parameter
        return Offset.align(x, z); // Round components to multiples of 16 (CO throws an exception if they aren't)
    }

    public static MyCustomOffsetProvider deserialize(OffsetProviderConfig config) {
        // Implement config parsing here. Or, to keep things simple with no config, just return.
        return new MyCustomOffsetProvider(config.getUserDefinedProviderName());
    }

    @Override
    public SequencedMap<String, *> serialize() {
        // Implement the reverse of your deserialize logic here. If not parsing config, just return.
        return new LinkedHashMap<String, Object>();
    }
}

Apply your new provider. It will not be active unless configured in the CoordinateOffset configuration.

# plugins/CoordinateOffset/config.yml
defaultOffsetProvider: myCustomProvider
offsetProviders:
  myCustomProvider:
    class: MyCustomOffsetProvider

Clone this wiki locally