Skip to main content
This documentation is written for NameplateBuilder >= v4.260326.7 with API >= v2.2.0.

Project Modules

NameplateBuilder is split into three Gradle modules:

File Tree

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.