Sessions and stories
A player session connects a Minecraft player to NarrativeCraft's current gameplay and story state. Always obtain sessions from NarrativeCraft; do not implement IPlayerSession yourself.
Find a player session
Use the session manager with a Minecraft Player:
NarrativeCraftAPI api = NarrativeCraftAPI.getInstance();
IPlayerSession session = api.getPlayerSessionManager().getByPlayer(player);
if (session == null) {
return;
}getByPlayer() returns null when NarrativeCraft has not created a session for that player.
The session exposes:
| Method | Purpose |
|---|---|
getPlayer() | Get the associated ServerPlayer |
isGameplayMode() | Check whether normal player gameplay is enabled |
setGameplayMode(boolean) | Change gameplay mode |
isClientSide() | Check whether this is the client-side session |
getStoryHandler() | Get the running story, or null |
getActiveClientInkActions() | Get client Ink actions that are still active |
getActiveClientInkActions() is primarily useful from client-side Ink action code. Treat the returned list as NarrativeCraft-owned state.
Start a story
Start the story at its first chapter and scene:
IStoryHandlerManager stories = api.getStoryHandlerManager();
try {
stories.start(session);
} catch (Exception exception) {
LOGGER.error("Could not start the story", exception);
}Or start at a specific Ink knot:
stories.start("chapter_2_village", session);The knot must resolve to an existing NarrativeCraft chapter and scene. Both start() overloads may throw when the story cannot be loaded or the requested path is invalid.
The session passed to IStoryHandlerManager must come from IPlayerSessionManager. Passing a custom implementation causes an IllegalArgumentException.
Stop a story
api.getStoryHandlerManager().stop(session);Stopping clears the running narrative state and removes story characters. Calling it when no story is running has no effect.
You can also retrieve the handler and stop it directly:
IStoryHandler story = session.getStoryHandler();
if (story != null) {
story.stop();
}Prefer the manager when all you have is a session.
Inspect the running story
IStoryHandler story = session.getStoryHandler();
if (story == null || story.isEnded()) {
return;
}
Entity mainCharacter = story.getMainCharacterEntity();
String lastSpeaker = story.getLastCharacterSpoke();
Map<String, Entity> activeCharacters = story.getCharacterEntities();Character map keys are normalized character names. Use the character helpers when possible:
ICharacter guard = api.getCharacterManager().resolveCharacter("Guard", currentScene);
if (guard != null && story.characterInStory(guard)) {
Entity guardEntity = story.getEntityFromCharacter(guard);
}getMainCharacterEntity() and getEntityFromCharacter() return null when the character has no active entity.
Play a stitch
playStitch() plays a stitch in the current scene:
story.playStitch("merchant_greeting");Pass the stitch name, not its fully qualified Ink path. NarrativeCraft resolves it relative to the current scene.
Track interactions
Story handlers remember interaction UUIDs:
if (!story.hasAlreadyInteracted(interactionId)) {
story.addInteractionId(interactionId);
}getInteractionIds() exposes the current set for inspection. Use addInteractionId() to add an entry instead of mutating that set directly.
Story completion
There is a difference between stopping a running story and marking the full story as finished:
story.finish();finish() marks the story as completed, saves that state, and stops playback.
The completion flag is also exposed directly:
boolean completed = story.hasFinishedStory();
story.setFinishedStory(false);Use setFinishedStory() when implementing a deliberate reset or save-management feature. Calling stop() alone does not mark the story as completed.
Handler method reference
| Method | Intended use |
|---|---|
start(String knotPath) | Start this handler at an Ink knot; the manager is preferred for normal startup |
stop() | Stop and clear the running story |
playStitch(String) | Play a stitch in the current scene |
finish() | Save completion and stop |
getPlayerSession() | Return the owner session |
isEnded() | Check whether the handler has ended |
getMainCharacterEntity() | Find the active main-character entity |
getCharacterEntities() | Inspect all active character entities |
getLastCharacterSpoke() | Read the last dialog speaker |
getEntityFromCharacter() | Resolve an API character to its active entity |
characterInStory() | Check whether a character is active |
getInteractionIds() | Inspect remembered interaction UUIDs |
hasAlreadyInteracted() | Test one interaction UUID |
addInteractionId() | Remember one interaction UUID |
hasFinishedStory() | Read the saved completion flag |
setFinishedStory() | Change the completion flag |
onChoiceSelected(int) and onTagsDrained() are public lifecycle callbacks used by NarrativeCraft after a player selects a choice or the Ink action queue finishes. Addons normally observe these moments through events or implement their behavior as Ink actions instead of invoking the callbacks manually.