Skip to content

Configuration Guide

Joshua Prince edited this page Jun 19, 2024 · 22 revisions

This guide describes how to configure CoordinateOffset to apply "offsets" to each player.

TL;DR

The default plugin config will randomize all players' (except OPs) coordinates every time they log in. Give coordinateoffset.bypass permission to see real coordinates.

Offsets

An "offset" refers to a specific shift of a player's X- and Z-coordinate that will be applied to every packet sent to that player. For example, the plugin might apply an offset of [x=1600, z=-128] to a player named SomePlayer. When SomePlayer joins the server, this is what they will see:

  • SomePlayer spawns at the Overworld spawn point, which is located at (100, 100). They press F3, and the coordinates they see are (-1500, 228).
  • SomeAdmin, who is an operator on the server, has no offset. They teleport to SomePlayer. When SomeAdmin presses F3, the coordinates they see are the "real" coordinates of the world: (100, 100).
  • SomePlayer and SomeAdmin both see the same world, and see each other. The only difference is the numbers they see in the F3 menu.

Offset values must be kept secret from players, or they will be useless. The default configuration applies a random offset to each player. Their offset changes every time they quit and re-join the server. However, there is much more flexibility for calculating and changing these offsets. This flexibility is configured through "Offset Providers".

Offset Providers

The main configuration for CoordinateOffset is through "Offset Providers" in plugins/CoordinateOffset/config.yml. In this file, you can configure any number of Offset Providers and apply them per-player, per-world, and more. Let's look at the set of Offset Providers provided in the default-generated config.yml:

offsetProviders:
  constant:
    class: ConstantOffsetProvider
    offsetX: 1024
    offsetZ: 1024
    worldScaling:
      world_nether: 0.125

  disabled:
    class: ConstantOffsetProvider
    offsetX: 0
    offsetZ: 0

  random:
    class: RandomOffsetProvider
    resetOnDeath: false
    resetOnWorldChange: false
    persistent: false
    persistenceKey: default
    randomBound: 100000
    worldAlignment:
      - world:world_nether:8

  zeroAtLocation:
    class: ZeroAtLocationOffsetProvider
    resetOnDeath: false
    resetOnWorldChange: false
    worldAlignment:
      - world:world_nether:8

Each provider consists of:

  • A name for the provider (constant, disabled, random, zeroAtLocation)
  • The provider class (ConstantOffsetProvider, RandomOffsetProvider, ZeroAtLocationOffsetProvider)
  • Configurations to apply to the provider class

You are allowed to define as many Offset Providers in this section as you want. Note that these providers will not be active unless you configure them to be applied: See Applying Offset Providers below.

The plugin offers 3 basic classes to define an Offset Provider:

ConstantOffsetProvider

The Constant Offset Provider class is the most basic way to define a static offset. There are only two required configuration keys for Constant Offset Providers:

Key Type Description
offsetX integer The X-component of the offset that will always be created from this provider.
offsetZ integer The Z-component of the offset that will always be created from this provider.
worldScaling Map Optional scaling for specific worlds. Offsets in a world in this list will be multiplied by the value specified. This is a simple way to ensure that Nether Portals don't allow players to work out this offset - it is recommended to leave world_nether: 0.125 in vanilla-like servers.

⚠️ Both offset values MUST be evenly divisible by 16! Offsets need to align with chunk boundaries.

Remember that these numbers will subtracted from players' real coordinates.

As used in the default configuration, you can create a provider that uses offset components of 0 to behave as "no offset" - that is, players will be able to see their real, unshifted coordinates.

Example use cases for ConstantOffsetProvider:

  • You want to shift the entire world so that players see the spawn point at (0, 0).
  • You want to groups of players to be able to share coordinates within their group, but not leak those coordinates to other groups (each group uses a different constant offset).

RandomOffsetProvider

The Random Offset Provider class generates a random offset for each player. This random offset can be kept constant for the same player, or re-rolled frequently to prevent coordinates from being useful.

Key Type Description
resetOnDeath boolean If true, each player's random offset will be re-rolled whenever they respawn after dying.
resetOnWorldChange boolean If true, each player's random offset will be re-rolled whenever they enter a different world for any reason (e.g. using a portal, teleporting).
persistent boolean If true, players will have the same offset even after they quit and rejoin, and across server restarts.
persistenceKey string This is a unique identifier used to save players' persistent offset. It only has an effect when persistent=true. See Using Persistence.
randomBound integer The maximum possible X or Z value of a randomly-generated offset. Offsets will be within the range (-randomBound, randomBound).
worldAlignment List Configuration for aligning worlds to use linked offsets. See Using World Alignment.

Each world has its own independently-generated random offset. These offsets will only be linked together by using worldAlignment rules.

Example use cases for RandomOffsetProvider:

  • You want to prevent players from sharing coordinates, while still allowing them to use their own coordinates for navigation, world-map mods, and waypoints. (persistent=true, resetOn*=false)
  • You want to buff in-game Death Compasses by shuffling coordinates when a player dies, preventing them from using "death point" mods. (persistent=true, resetOnDeath=true, resetOn*=false)
  • You want to completely negate the ability to use coordinates, in conjunction with /gamerule reducedDebugInfo, by randomizing the offset as frequently as possible. (persistent=false, resetOn*=true)

Using Persistence

When persistent is enabled, each player's random offsets are saved within their NBT data. You can use a tool like NBTExplorer to modify these offsets, or delete them so that they will be re-rolled.

Offsets are stored at the following location in player NBT, which is stored within <main world folder>/playerdata:

BukkitValues:coordinateoffset.random-persistence.<persistenceKey>

This NBT entry acts like a Map, where "keys" are world names and "values" are offsets.

NBTExplorer screenshot

⚠️ If you change persistenceKey on a provider, all players who previously had a persistent offset will have a different offset the next time they log in. Similarly, providers using the same persistence key will provide the same offsets to the same player. The provider will only load saved offsets that have a matching persistence key.

ZeroAtLocationOffsetProvider

The Zero at Location Offset Provider class always generates an offset equal to the player's coordinates when the offset is generated. This means that players will see themselves at reasonably-sized coordinates while still not seeing their real coordinates.

Key Type Description
resetOnDeath boolean If true, each player's coordinate system will be re-centered on their spawn point after they die.
resetOnWorldChange boolean If true, each player's coordinate system will be re-centered on the place where they first enter a world whenever their world changes.
worldAlignment List Configuration for aligning worlds to use linked offsets. See Using World Alignment.

Example use cases for ZeroAtLocationOffsetProvider:

  • You want to obfuscate coordinates between players and across sessions, but you don't want players to see their coordinates at the absurdly high values generated by the Random provider.
  • You want to buff in-game Recovery Compasses by re-centering coordinates when a player dies, preventing them from using "death point" mods. (resetOnDeath=true)

Applying Offset Providers

After creating or configuring your Offset Providers, they will have no effect until you apply them. There are a few ways to do this:

Default

Unless otherwise specified, players will use the Offset Provider specified in defaultOffsetProvider.

Configuration Overrides

You can optionally define a list of override rules in the configuration key offsetProviderOverrides. Overrides look like this:

offsetProviderOverrides:
  - provider: constant
    permission: coordinateoffset.provider.my_custom_permission
    world: world
  - provider: zeroAtLocation
    world: world_nether
    playerUuid: 00000000-0000-0000-0000-000000000000

Override rules are processed in the order they are listed in the config. The first override for which all optional keys match the offset context will be selected. If no overrides match, the default offset provider will be selected.

Rule Name Effect
provider (Required) Name of the provider configured in offsetProviders that any matches to this rule will use.
world Filter this rule to a specific world by name (e.g. world, world_nether, world_the_end by default)
playerUuid Filter this rule to only apply to a specific player. You can determine a player's UUID here.
permission Filter this rule to only apply players that have a specific permission node. It is highly recommended that you define your own permission nodes here with the naming convention coordinateoffset.provider.something, and then give those permissions nodes to their intended groups, rather than trying to use a permission node defined by another plugin.

⚠️ Some permission plugins allow permissions to have "context", such as only applying to players in a specific world. If you specify a permission node and give it to players, do not add context to the permission. You must use the world filter rule in an override to apply different offset providers in different worlds.

Why?

CoordinateOffset needs to calculate a new offset very early in the world-change process so that the server can start sending new chunks to the player. At the point these permission checks run, most permissions plugins have not yet updated their context for that player to the new world. That means that when we ask "does this player have permission X?", the answer will apply to the old world, but the offset we're calculating will apply to the new world. On the other hand, the world filter is correctly compared with the world the player is going to.

Examples

Randomize everyone's offset in the Overworld and Nether, but display real coordinates in The End:

defaultOffsetProvider: random
offsetProviderOverrides:
  - provider: disabled
    world: world_the_end

Obfuscate most players' coordinates, but allow trusted players to see their real coordinates:

defaultOffsetProvider: zeroAtLocation
offsetProviderOverrides:
  - provider: disabled
    permission: coordinateoffset.provider.see_real_coords  # give this permission to the "Trusted" group

Only obfuscate coordinates for one player:

defaultOffsetProvider: disabled
offsetProviderOverrides:
  - provider: random
    playerUuid: 57d28dac-55f7-4584-97a7-7fb303c01a95

Give two opposing factions different offsets that players within the faction all share, but let everyone in the End see real coordinates:

defaultOffsetProvider: random
offsetProviderOverrides:
  - provider: disabled  # must come first, otherwise the faction providers would always apply to members.
    world: world_the_end
  - provider: constant_factionA
    permission: coordinateoffset.provider.factionA  # give this permission to Faction A
  - provider: constant_factionB
    permission: coordinateoffset.provider.factionB  # give this permission to Faction B
offsetProviders:
  disabled:
    class: ConstantOffsetProvider
    offsetX: 0
    offsetZ: 0
  constant_factionA:
    class: ConstantOffsetProvider
    offsetX: -30336
    offsetZ: 14816
    worldScaling:
      world_nether: 0.125
  constant_factionB:
    class: ConstantOffsetProvider
    offsetX: -89008
    offsetZ: -36096
    worldScaling:
      world_nether: 0.125

Using World Alignment

One potential downside of obfuscating coordinates is that it can make multiworld scenarios difficult to navigate. For example, the Nether is commonly used for fast travel because one block of movement in the Nether will translate to eight blocks in the Overworld. Linking up Nether portals often involves coordinate conversions, by multiplying or dividing by 8.

World Alignment is an optional provider configuration for RandomOffsetProvider and ZeroAtLocationOffsetProvider. It allows you to specify a list of world pairs that use linked coordinate systems. The default on each of these providers will behave predictably with the Vanilla behavior of the Overworld and Nether:

    worldAlignment:
      - world:world_nether:8

With this config, any player who enters world_nether while they already have an offset for world will receive an offset that is 1/8th of their offset in world. Likewise, any player who enters world while they already have an offset for world_nether will receive an offset that is 8 times larger than their offset in world_nether. This means that players are fully able to use any coordinate translation techniques on the coordinates that they see, as if the offset were not configured at all.

⚠️ Be sure to disable the provider's resetOnWorldChange setting, or world alignment will not work at all since the offsets do not persist across worlds.

If you have multiple sets of worlds, or worlds using different names than the defaults, you can add any number of pairs to this list:

    worldAlignment:
      - spawnworld:spawnworld_nether:8
      - premiumworld:premiumworld_nether:8

If you have worlds that link coordinates 1:1 (meaning that there is no scaling), you can use a scale factor of 1:

    worldAlignment:
      - world:world_parallel:1

If you use Multiverse-NetherPortals scaling, you can match different scaling factors per world. Note that you need to list all possible pairs of worlds that someone might travel between:

    worldAlignment:
      - world:world_nether:8
      - world_nether:world_supernether:8
      - world:world_supernether:64

⚠️ Scale factors must be a power of two (1, 2, 4, 8, 16...).

Global Settings

Config Description
fixCollision If true, the blocks listed (bamboo and dripstone) will be patched to have no server-side collision. This fixes a movement glitch near these blocks described in #8. If false, the block will not be patched. It is strongly recommended to leave this enabled unless it causes problems, because disabling it makes moving through bamboo and dripstone extremely glitchy while an offset is applied.
bypassByPermission If true, players with the coordinateoffset.bypass permission will bypass all providers and see their real coordinates. If false, all players are subject to the standard offset rules.

It may be useful to disable this while testing the plugin, since server operators have this permission by default. However, for a live server, you should really enable this and give the permission to moderators or admins, so they will be able to see where they are and use commands that take coordinates.
obfuscateWorldBorder If true, world border packets will be modified so that only nearby "border walls" can be determined. See here for more details. ⚠️ Only turn this off if you understand the implications - you might potentially leak real coordinates!
verbose Enables extra logging whenever the plugin generates an offset for a player. Use this to confirm which provider is being selected for a player, to debug overrides.
allowUnsafeResetOnDistantTeleport This option allows the use of resetOnDistantTeleport as a provider option in Random and ZeroAtLocation providers - see here for why it is considered unsafe.