CitizensManager

CitizensManager is the runtime API surface for creating, updating, querying, and controlling citizens.

CitizensManager manager = HyCitizensPlugin.get().getCitizensManager();
Note

About the save parameter

Most mutation methods accept save. Use true for persistent changes. Use false while batching multiple edits, then call saveCitizen(citizen) once.

Lifecycle

Create

void addCitizen(@Nonnull CitizenData citizen, boolean save)

Registers a citizen and spawns it in-world.

Save

void saveCitizen(@Nonnull CitizenData citizen)
void saveCitizen(@Nonnull CitizenData citizen, boolean respawnIfRoleChanged)

Writes the citizen to disk. The second overload can force an NPC respawn when role data changes.

Remove Permanently

void removeCitizen(@Nonnull String citizenId)

Deletes from registry and disk, removes role file, and despawns live entities. Cross-world removal is handled with deferred cleanup when needed.

Spawn/Despawn Runtime Only

void spawnCitizen(CitizenData citizen, boolean save)
void despawnCitizen(CitizenData citizen)

Use these for temporary runtime visibility control without deleting the citizen record.

Choosing The Correct Update Method

MethodWhat it changes
updateCitizen(citizen, save) Full respawn of citizen representation (NPC plus nametag mode).
updateCitizenNPC(citizen, save) NPC-side behavior/model/combat/path updates.
updateCitizenHologram(citizen, save) Name and nametag presentation updates (inline nameplate or separate entities).
updateCitizenNPCItems(citizen) Live equipment sync without NPC respawn.
CitizenData c = manager.getCitizen("town_captain");
if (c != null) {
    c.setName("Captain Rhea");
    c.setNametagOffset(0f);
    manager.updateCitizenHologram(c, true);

    c.setNpcHand("Weapon_Sword_Steel");
    manager.updateCitizenNPCItems(c);
    manager.saveCitizen(c);
}

Nametag APIs

boolean shouldUseSeparateNametagEntities(@Nonnull CitizenData citizen)
void refreshNpcNameplate(@Nonnull CitizenData citizen)

These methods expose the adaptive nametag logic used internally:

  • Separate entities when hideNpc=true, name has multiple non-empty lines, or nametagOffset != 0.
  • Inline NPC Nameplate when NPC is visible, one non-empty line, and offset is zero.
  • hideNametag=true disables nametag rendering.

Most plugins should call updateCitizenHologram(citizen, save) instead of these directly.

Query APIs

@Nullable CitizenData getCitizen(@Nonnull String citizenId)
@Nonnull List<CitizenData> getAllCitizens()
int getCitizenCount()
boolean citizenExists(@Nonnull String citizenId)
@Nonnull List<CitizenData> getCitizensNear(@Nonnull Vector3d position, double maxDistance)

getCitizensNear uses configured spawn position (citizen.getPosition()), not live movement position.

One-Call Config Helpers

void setCitizenCombatConfig(@Nonnull String citizenId, @Nonnull CombatConfig combatConfig)
void setCitizenDetectionConfig(@Nonnull String citizenId, @Nonnull DetectionConfig detectionConfig)
void setCitizenPathConfig(@Nonnull String citizenId, @Nonnull PathConfig pathConfig)

These update the config object, save, and respawn NPC-side state.

Movement And Patrol

void moveCitizenToPosition(@Nonnull String citizenId, @Nonnull Vector3d position)
void stopCitizenMovement(@Nonnull String citizenId)
void startCitizenPatrol(@Nonnull String citizenId, @Nonnull String pathName)
void stopCitizenPatrol(@Nonnull String citizenId)
boolean isCitizenPatrolling(@Nonnull String citizenId)
String getCitizenActivePatrolPath(@Nonnull String citizenId)
PatrolManager getPatrolManager()

Events

void addCitizenInteractListener(CitizenInteractListener listener)
void removeCitizenInteractListener(CitizenInteractListener listener)
void addCitizenDeathListener(CitizenDeathListener listener)
void removeCitizenDeathListener(CitizenDeathListener listener)
void addCitizenAddedListener(CitizenAddedListener listener)
void removeCitizenAddedListener(CitizenAddedListener listener)
void addCitizenRemovedListener(CitizenRemovedListener listener)
void removeCitizenRemovedListener(CitizenRemovedListener listener)

See Events for detailed listener payloads and cancellation behavior.

Skin And Appearance Runtime

PlayerSkin determineSkin(CitizenData citizen)
void updateCitizenSkin(CitizenData citizen, boolean save)
void updateCitizenSkinFromPlayer(CitizenData citizen, PlayerRef playerRef, boolean save)
void applySkinPreview(CitizenData citizen, PlayerSkin skin)

Groups

List<String> getAllGroups()
void createGroup(@Nonnull String groupName)
void deleteGroup(@Nonnull String groupName)
boolean groupExists(@Nonnull String groupName)
List<CitizenData> getCitizensByGroup(@Nullable String groupName)

Combat Helpers

void autoResolveAttackType(@Nonnull CitizenData citizen)
void forceAttackEntity(@Nonnull CitizenData citizen, @Nonnull String attackInteractionId)

Internal Integration Hooks

boolean isCitizenSpawning(@Nonnull String citizenId)
void processPendingHologramRemovals(@Nonnull World world, long chunkIndex)
void processPendingNpcRemovals(@Nonnull World world, long chunkIndex)

These are primarily used by HyCitizens listeners (for chunk-load recovery/cleanup). Most external plugins should not call them directly.

Common Safe Pattern

CitizenData citizen = manager.getCitizen(id);
if (citizen == null) {
    return;
}

// Batch edits
citizen.setHideNpc(false);
citizen.setHideNametag(false);
citizen.setName("Gate Guard");
citizen.setNametagOffset(0f);

// Apply nametag mode + save once
manager.updateCitizenHologram(citizen, true);