Skip to main content

Documentation Index

Fetch the complete documentation index at: https://docs.nameplatebuilder.frotty27.com/llms.txt

Use this file to discover all available pages before exploring further.

This documentation is written for NameplateBuilder >= v4.260326.7 with API >= v2.2.0.

Project Modules

NameplateBuilder is split into three Gradle modules:
ModulePurpose
nameplate-apiLightweight API jar for mod developers. Contains NameplateAPI, NameplateData, SegmentTarget, and exception classes.
nameplate-serverThe server plugin. Contains the aggregator, UI, persistence, admin config, and all internal systems.
nameplate-example-modWorking example mod demonstrating all API patterns.

File Tree

NameplateBuilder/
  nameplate-api/
    src/main/java/.../api/
      NameplateAPI.java                    Static entry point
      NameplateData.java                   ECS component (Map<String, String>)
      SegmentTarget.java                   Entity target enum
      INameplateRegistry.java              Internal registry interface
      NameplateException.java              Base exception
      NameplateNotInitializedException.java
      NameplateArgumentException.java

  nameplate-server/
    src/main/java/.../server/
      NameplateBuilderPlugin.java          Server plugin entry point
      NameplateAggregatorSystem.java       Per-tick compositor + death cleanup
      DefaultSegmentSystem.java            Built-in segments
      NameplateRegistry.java               Segment metadata store
      NameplateBuilderPage.java            Player UI page
      NameplateBuilderCommand.java         /npb command
      NameplateBenchmarkCommand.java       /npbbench benchmark command
      NameplateIdentifyCommand.java        /npbid entity identification
      NameplatePreferenceStore.java        Per-player JSON persistence
      AdminConfigStore.java                Admin JSON config persistence
      AnchorEntityManager.java             Anchor entity lifecycle
      EntitySourceService.java             NPC source detection (vanilla vs mod)
      SimpleJson.java                      Minimal JSON reader/writer
      SegmentKey.java                      record(pluginId, segmentId)
      SegmentBuilderImpl.java              SegmentBuilder implementation
      ActiveTab.java                       Sidebar tab enum
      AdminSubTab.java                     Admin sub-tab enum
      UiState.java                         UI state snapshot
      SettingsData.java                    Client/server UI events
      SegmentView.java                     Segment metadata view
    src/main/resources/
      Common/UI/Custom/Pages/
        NameplateBuilder_Editor.ui         UI layout definition

  nameplate-example-mod/
    src/main/java/.../example/
      NameplateExamplePlugin.java          Describe + register systems
      ArchaeopteryxNameplateSystem.java    NPC spawn + live health
      LifetimeNameplateSystem.java         Per-entity tick updates

Data Flow

Registration Flow

  1. Mods call NameplateAPI.define() during setup() to register UI metadata and optional resolvers
  2. NameplateRegistry stores display names, targets, examples, variant lists, and resolver functions
  3. The UI reads from NameplateRegistry to populate Available Blocks

Runtime Flow

  1. Resolvers compute text per entity automatically, or mods call NameplateAPI.setText() / write to NameplateData directly
  2. NameplateData stores segment text as Map<String, String> on the entity
  3. NameplateAggregatorSystem ticks every frame

Aggregation Flow (Per Frame)

For each visible entity with NameplateData:
  1. Check per-viewer Enable Nameplates toggle - skip entirely if disabled for this viewer (sends EMPTY_UPDATE immediately when disabled)
  2. Check crouch state via MovementStatesComponent.crouching - skip player entities that are crouching
  3. Check regex pattern blacklist - skip NPC entities whose role name matches any configured pattern
  4. Resolve segment keys from the component (skip _-prefixed and admin-disabled keys)
  5. Apply viewer preferences: ordering, enabled/disabled, format variants
  6. Resolve variant text: check suffixed key, fall back to base key
  7. Apply prefix/suffix wrapping and bar fill replacement
  8. Enforce admin-required segments
  9. Composite all segments with per-block separators
  10. Queue nameplate update to each viewer

Entity Source Detection

The aggregator identifies entity sources to determine which entities receive nameplates. Hytale-namespace NPCs are auto-seeded as before. Mod-provided NPCs are detected by inspecting the entity’s source plugin, allowing NameplateBuilder to correctly attribute NPC entities that originate from other mods.

Resolver Key Cache Invalidation

Resolver results are cached per entity based on cacheTicks(). When the admin configuration changes (e.g. a segment is required or disabled), all resolver key caches are invalidated immediately. This ensures that admin config changes take effect without waiting for individual cache timers to expire.

Death Cleanup

When an entity receives a DeathComponent:
  1. Aggregator sends empty nameplate to all viewers
  2. NameplateData component is removed from the entity
  3. If anchor entities exist for this entity, they are cleaned up

Anchor Entities

When a player configures a vertical offset for NPC entities:
  1. An invisible “anchor” entity (ProjectileComponent + Intangible + TransformComponent + NetworkId) is spawned above the real NPC
  2. Nameplate text is routed to the anchor instead of the real entity
  3. Anchor follows the real entity every tick using velocity-based predictive positioning to compensate for the 1-tick CommandBuffer delay
  4. Anchors use AddReason.LOAD to prevent disk persistence during chunk saves
  5. No explicit removal: when entities die or are removed, anchors are simply untracked and left in the world with blank nameplates until chunk unload (avoids chunk serialization race conditions)
Player entities always use vanilla client-side nameplate rendering and never create anchor entities, regardless of offset configuration. This ensures zero lag for player nameplates.

Crouch Detection

Player nameplates are hidden when the player is crouching. The aggregator checks MovementStatesComponent.crouching on the target entity each tick. When crouching is true, the nameplate is replaced with an empty update for all viewers.

View-Cone Filtering

When enabled per-viewer (applies to NPC nameplates only - player nameplates are always visible):
  1. Dot-product math checks if the viewer is looking at the entity
  2. ~25 degree half-angle cone, up to 12 blocks range
  3. Entities outside the cone receive an empty nameplate update

Key Design Decisions

Why ECS Components?

NameplateData is a standard Hytale ECS component. This means:
  • It participates in archetype queries - the aggregator only ticks entities that have nameplate data
  • It’s automatically cleaned up when entities are destroyed
  • Multiple mods can read/write to it without coordination
  • The CommandBuffer pattern handles structural changes safely

Why Per-Viewer?

Each player sees their own customized view of every nameplate. This enables:
  • Personal segment ordering and selection
  • Format variant choices per segment per player
  • Admin-required segments overlaid on personal preferences
  • View-cone filtering based on the viewer’s camera direction

Why Tick-Based?

The aggregator runs every frame to ensure nameplate text always reflects the latest data. Since setText() is just a HashMap.put(), the cost of frequent updates is minimal.