Skip to content

Tips and Tricks

Let me tell you what I did wrong - so you don't have to 👍


Case sensitivity

"Player" ≠ "player"

Normalize keys.

map.put(name.toLowerCase(), value);

Enum.values()

values() creates every time a new array.

Cache it.

public static final MyEnum[] VALUES = values();

Even better: use maps instead of iterating (O(1)).

public static final Map<Integer, MyEnum> BY_ID = new HashMap<>();

static {
    for (MyEnum enumValue : VALUES) {
        BY_ID.put(enumValue.getId(), enumValue);
    }
}

Chunk loading side effects

Some methods will load chunks implicitly. This can cause unexpected lag.

Loads chunk

  • Location#getChunk()
  • World#getChunkAt(x, z)
  • World#getChunkAt(Block)
  • World#getChunkAt(Location)
  • Location#getBlock()

No chunk loading

  • World#isChunkLoaded(x, z)

Many API calls are not "read-only" - they trigger chunk loads.


Storing entities directly

Don’t store entities as objects (e.g. LivingEntity, Skeleton) in maps. Breaks as soon as the chunk unloads.

Better

Store identifiers instead.

UUID uuid = entity.getUniqueId();

Access later

Entity entity = Bukkit.getEntity(uuid);

Before using:

  • check if entity exists
  • check if the chunk is loaded

Entity references are not stable.


Player-based rendering

Don’t simulate everything globally. React to what the player actually sees (packets).

Example: Disco Fish\ → only track & tick spawned entities

Deeper dive: [[Disco Fish System|Monty's-Book/Disco-Fish-System]]