diff --git a/sidebars.ts b/sidebars.ts
index 294492c..2309f3a 100644
--- a/sidebars.ts
+++ b/sidebars.ts
@@ -99,6 +99,7 @@ const sidebars: SidebarsConfig = {
'geyser/custom-items',
'geyser/custom-blocks',
'geyser/custom-skulls',
+ 'geyser/custom-waypoints'
]
},
{
diff --git a/wiki/geyser/custom-resource-packs.md b/wiki/geyser/custom-resource-packs.md
index 066226b..e90e950 100644
--- a/wiki/geyser/custom-resource-packs.md
+++ b/wiki/geyser/custom-resource-packs.md
@@ -6,4 +6,5 @@ description: How to use custom resource packs for Geyser.
Geyser supports custom resource packs for use with custom items, blocks, and skulls. See:
- [Custom items](/wiki/geyser/custom-items)
- [Custom blocks](/wiki/geyser/custom-blocks)
-- [Custom skulls](/wiki/geyser/custom-skulls)
\ No newline at end of file
+- [Custom skulls](/wiki/geyser/custom-skulls)
+- [Custom waypoint icons](/wiki/geyser/custom-waypoints)
diff --git a/wiki/geyser/custom-waypoints.mdx b/wiki/geyser/custom-waypoints.mdx
new file mode 100644
index 0000000..1e19f1f
--- /dev/null
+++ b/wiki/geyser/custom-waypoints.mdx
@@ -0,0 +1,221 @@
+---
+title: Custom Waypoint Icons
+description: Geyser supports custom locator bar icons (also known as waypoint icons), though they have to be mapped manually.
+---
+
+import Tabs from "@theme/Tabs";
+import TabItem from "@theme/TabItem";
+
+Minecraft: Java Edition supports showing custom icons on the locator bar. These are known as custom waypoints and, on
+Java Edition, implemented through custom "waypoint styles" in a resource pack. These waypoint styles specify a near
+and far distance in blocks, and a list of sprites to display on the locator bar.
+
+Bedrock Edition, on the other hand, does not natively support custom waypoint icons through behaviour or resource packs.
+However, in the network layer, this is supported, and as such Geyser can make use out of it to support Java's custom
+waypoint icons. These are implemented through custom JSON mappings, similar to custom block or item mappings.
+
+Geyser extensions can also make use out of Bedrock's support for custom waypoint icons. Unlike JSON mappings however,
+extensions have more freedom in how icons are displayed to the client.
+
+When an unknown custom waypoint style is sent to Geyser by the Java server,
+Geyser will display the waypoint using vanilla's default icons.
+
+:::info
+This guide only describes the steps required to write custom waypoint style mappings. You still need to create a resource pack
+for Bedrock Edition, add the waypoint icons to it, and use it in Geyser. Geyser cannot automatically pull icons from
+Java Edition resource packs.
+:::
+
+:::warning
+As with custom blocks and items, Geyser does not convert resource packs from Java Edition, and also does not generate
+custom waypoint mappings automatically. However, automatic tools such as [Rainbow](/wiki/other/rainbow/) are planning
+support for these conversions.
+:::
+
+## Simple icons
+
+Custom waypoint icons following Java's system of having a near- and far-distance should be mapped using JSON mappings, or,
+when using Geyser extensions, Geyser's `CustomWaypointStyle.VanillaBuilder` helper builder. Both of these methods follow
+the same format as Java's custom waypoint styles and should feel very familiar.
+
+
+
+ JSON mappings have to specify the `format_version` set to `1`, and list all custom waypoint styles under the
+ `waypoint_styles` key. This key holds an object in which keys are identifiers of waypoint styles, and each
+ value is the definition of that waypoint style.
+
+ For example, take the following `waypoint_mappings.json` file:
+ ```json
+ {
+ "format_version": 1,
+ "waypoint_styles": {
+ "geyser:my_first_waypoint": {
+ "near_distance": 10,
+ "far_distance": 100,
+ "sprites": [
+ "geyser:custom_waypoint_0",
+ "geyser:custom_waypoint_1"
+ ]
+ },
+ "geyser:my_second_waypoint": {
+ "sprites": [
+ "geyser:second_waypoint/texture_1",
+ "geyser:second_waypoint/texture_2",
+ "geyser:second_waypoint/texture_3",
+ "geyser:second_waypoint/texture_4"
+ ]
+ },
+ "geyser:the_best_waypoint": {
+ "near_distance": 50,
+ "sprites": [
+ "geyser:ultimate_waypoint/near",
+ "geyser:ultimate_waypoint/far"
+ ]
+ }
+ }
+ }
+ ```
+
+ These mappings specify 3 custom waypoint styles, `geyser:my_first_waypoint`, `geyser:my_second_waypoint`, and
+ `geyser:the_best_waypoint`.
+
+ Just like on Java Edition, the default value for `near_distance` when not specified is 128, and for `far_distance`
+ it is 332. And, just like on Java Edition, at least one texture sprite must be specified.
+
+ Since namespaces do not exist in Bedrock Edition's resource packs, Geyser must flatten the texture sprite identifier
+ to be used in the resource pack on Bedrock Edition. Geyser does this using the following format:
+
+ > `textures/ui/<namespace&rt;/locator_bar_dot/<path&rt;`
+
+ As such, in the example above, the `geyser:my_first_waypoint` waypoint style refers to the following textures in the Bedrock resource pack:
+
+ - `textures/ui/geyser/locator_bar_dot/custom_waypoint_0`
+ - `textures/ui/geyser/locator_bar_dot/custom_waypoint_1`
+
+
+ The Geyser API provides a helper builder, called `VanillaBuilder` in `CustomWaypointStyle`, to implement custom
+ waypoint icons that are similar to Java Edition's custom icons. The builder is similar to JSON-mappings in use
+ and requires the same fields to be specified.
+
+ Custom waypoint styles are registered per-connection in the `SessionDefineCustomWaypointsEvent`,
+ and as such you can check the `GeyserConnection` before registering a custom waypoint style. If a custom waypoint
+ style is used across multiple connections, it can be useful to only create it once to save memory.
+
+ ```java
+ @Subscribe
+ public void onSessionDefineCustomWaypoints(SessionDefineCustomWaypointsEvent event) {
+ event.register(Identifier.of("geyser:my_first_waypoint"), CustomWaypointStyle.vanillaLike(10, 100)
+ .withTexture(Identifier.of("geyser:custom_waypoint_0"))
+ .withTexture(Identifier.of("geyser:custom_waypoint_1"))
+ .build());
+
+ event.register(Identifier.of("geyser:my_second_waypoint"), CustomWaypointStyle.vanillaLike(128, 332)
+ .withTexture(Identifier.of("geyser:second_waypoint/texture_1"))
+ .withTexture(Identifier.of("geyser:second_waypoint/texture_2"))
+ .withTexture(Identifier.of("geyser:second_waypoint/texture_3"))
+ .withTexture(Identifier.of("geyser:second_waypoint/texture_4"))
+ .build());
+
+ event.register(Identifier.of("geyser:the_best_waypoint"), CustomWaypointStyle.vanillaLike(50, 332)
+ .withTexture(Identifier.of("geyser:ultimate_waypoint/near"))
+ .withTexture(Identifier.of("geyser:ultimate_waypoint/far"))
+ .build());
+ }
+ ```
+
+ As with JSON mappings, Geyser flattens the texture identifier into a string using the following format:
+
+ > `textures/ui/<namespace&rt;/locator_bar_dot/<path&rt;`
+
+ However, you can also pass a literal texture string to `withTexture`. This string must not be prefixed with
+ `textures/` and must also not be suffixed with the texture extension (e.g. `.png`).
+
+ You can register the same custom waypoint style for multiple waypoint style identifiers. However, each
+ waypoint style identifier may only have one custom waypoint style attached to it. If a duplicate is registered,
+ a `CustomWaypointStyleRegisterException` will be thrown.
+
+
+
+---
+
+
+ Changing vanilla waypoint styles
+ Creating custom waypoint styles for vanilla waypoint styles, like `minecraft:default`, is allowed, and can be done
+ as follows:
+
+
+
+ ```json
+ {
+ "format_version": 1,
+ "waypoint_styles": {
+ "minecraft:default": {
+ "sprites": [
+ "geyser:custom_vanilla_0",
+ "geyser:custom_vanilla_1",
+ "geyser:custom_vanilla_2",
+ "geyser:custom_vanilla_3",
+ "geyser:custom_vanilla_4"
+ ]
+ }
+ }
+ }
+ ```
+
+
+ ```java
+ @Subscribe
+ public void onSessionDefineCustomWaypoints(SessionDefineCustomWaypointsEvent event) {
+ event.register(Identifier.of("minecraft:default"), CustomWaypointStyle.vanillaLike(128, 332)
+ .withTexture(Identifier.of("geyser:custom_vanilla_0"))
+ .withTexture(Identifier.of("geyser:custom_vanilla_1"))
+ .withTexture(Identifier.of("geyser:custom_vanilla_2"))
+ .withTexture(Identifier.of("geyser:custom_vanilla_3"))
+ .withTexture(Identifier.of("geyser:custom_vanilla_4"))
+ .build());
+ }
+ ```
+
+
+
+
+
+## Advanced icons
+
+Geyser API users may write custom implementations of `CustomWaypointStyle`, to run custom checks for waypoint textures
+and waypoint icon sizes. Just like waypoint styles created using the `VanillaBuilder`, these can be registered for one
+or more custom waypoint style identifiers in the `SessionDefineCustomWaypointsEvent`. For example, the following
+implementation will always use the same texture and icon size, no matter the distance:
+
+```java
+public final class MyCustomWaypointStyle implements CustomWaypointStyle {
+ public static final CustomWaypointStyle INSTANCE = new MyCustomWaypointStyle();
+ private static final Vector2f SIZE = Vector2f.from(0.5, 0.5);
+
+ private MyCustomWaypointStyle() {}
+
+ @Override
+ public String texturePath(Identifier style, float distance) {
+ return "ui/geyser/locator_bar_dot/my_icon";
+ }
+
+ @Override
+ public Vector2f textureSize(Identifier style, float distance) {
+ return SIZE;
+ }
+}
+```
+
+It can then be registered through the `SessionDefineCustomWaypointsEvent` as follows:
+
+```java
+@Subscribe
+public void onSessionDefineCustomWaypoints(SessionDefineCustomWaypointsEvent event) {
+ event.register(Identifier.of("geyser:my_style", MyCustomWaypointStyle.INSTANCE));
+}
+```
+
+Like with `VanillaBuilder`, should you use a custom waypoint style across multiple connections,
+it can be useful to only keep one instance of it, to save memory. Also, avoid running time-taking operations in your
+`texturePath` or `textureSize` implementations, since these methods are called frequently. For full implementation details,
+refer to the Javadocs of `CustomWaypointStyle`.