Skip to content

nonkit-plugin is a BepInEx plugin for runtime modification of The NOexistenceN of Morphean Paradox : The Forest of Silver Shallots

License

Notifications You must be signed in to change notification settings

rlaneth/nonkit-plugin

Repository files navigation

nonkit-plugin

License: Unlicense

nonkit is a non-official mod development kit for the RPG game The NOexistenceN of Morphean Paradox : The Forest of Silver Shallots.

⚠️ Disclaimer: This is a fan project and is not affiliated with the developers or team behind NONMP. It's maintained to the best of my capacities, but keep in mind that NONMP is a new game currently in Early Access and rapidly changing, sometimes receiving more than one update per day as of writing. This plugin should be considered experimental.

What is this?

nonkit-plugin is a BepInEx plugin that enables runtime modification of the game's dialogue system. The game uses Yarn Spinner as its scripting language for story content, and this plugin provides a way to dynamically load and run Yarn Spinner scripts without modifying the game's files or restarting.

The main use case is mod development. When paired with an external tool like nonkit-vsc (a VS Code extension), you can edit Yarn scripts and immediately test them in-game. This makes the iteration cycle much faster than the traditional "edit, compile, replace files, restart game" workflow.

Building

Requires .NET 6.0 SDK.

# Debug build
dotnet build

# Release build
dotnet build -c Release

The output DLL will be in bin/Debug/net6.0/ or bin/Release/net6.0/.

Installation

Drop nonkit-plugin.dll into your BepInEx plugins folder (typically BepInEx/plugins/). That's it.

Versioning

This project follows SemVer.

Architecture

The plugin is split into a few focused classes:

Plugin.cs

The BepInEx entry point. Registers the PluginUpdater MonoBehaviour and sets up logging. Not much else.

PluginUpdater.cs

A Unity MonoBehaviour that runs every frame. Handles:

  • Processing commands from the pipe server
  • Processing commands from the internal command queue

CommandBridge.cs

A static class that acts as a command queue. Other parts of the plugin (or theoretically other plugins) can enqueue commands here, and PluginUpdater will execute them on the main thread. Also holds a status string that gets updated as commands complete.

Commands are represented as a DebugCommand struct with a DebugCommandType enum:

  • RunNode - start dialogue at a named node
  • RunBytes - load a compiled Yarn program from bytes and run it
  • RunFile - load a compiled Yarn program from a file path and run it

DialogueCommands.cs

Static methods that actually do the dialogue manipulation:

  • RunNode(nodeName) - finds the game's DialogueRunner and calls StartDialogue(nodeName)
  • RunBytes(data, nodeName) - parses protobuf bytes into a Yarn.Program, merges it into the existing program, and starts dialogue
  • RunFile(path, nodeName) - reads a file and calls RunBytes
  • LoadLocalization(args) - loads localization strings into the game's Localization objects without running any dialogue
  • RunCombined(args, nodeName) - handles multiple files with localization support; uses LoadLocalization internally

The key thing here is the merging. Rather than replacing the game's Yarn program, we merge our new nodes into it using protobuf's MergeFrom. This means the game's existing dialogue still works, and we just add or override specific nodes.

For localization, the plugin finds all Localization objects in the game and adds strings to them. It tries to match locale codes (like "en", "zh-cn", "ja") and falls back to the base localization if needed.

ConfigCommands.cs

Handles injection of Luban configuration tables into the game at runtime using Harmony patches. This enables adding custom quests, conditions, and other game data without modifying the game's files.

Key components:

  • Harmony patches - A postfix patch on Table.Init() triggers config injection after the game initializes its tables
  • Table injection - Parses JSON config data and injects records into Luban tables like TbMapEvent, TbCondition, and TbLocalizationAutoGenerated
  • Icon fallback system - A postfix patch on SpriteUtils.LoadMapEventIcon() allows custom quests to borrow icons from existing events via the Nonkit.IconSourceId property

The plugin supports both int-keyed tables (using ID field) and string-keyed tables (using Key field, like localization).

Nonkit Custom Properties

Config records can include a Nonkit object with special properties that the plugin processes:

Property Table Description
IconSourceId TbMapEvent Borrows the icon from another event ID (useful since custom events don't have icon assets)

Example:

{
  "ID": 900001,
  "Title": "My Quest",
  "Nonkit": {
    "IconSourceId": 10084
  }
}

PipeServer.cs

A named pipe server that listens on nonmp_debug for commands from external tools. Uses a length-prefixed JSON protocol:

  1. Client connects
  2. Server sends {"type":"hello","version":"0.2.0"}
  3. Client sends commands like {"cmd":"run_combined","args":{...}}
  4. Server sends responses like {"type":"status","message":"OK"} or {"type":"error","message":"..."}

The pipe server runs on a background thread. Commands are queued and processed on the main Unity thread (via PluginUpdater). Responses are queued and sent back on a separate response thread.

IPC Protocol

Messages are length-prefixed: 4 bytes (little-endian int32) followed by that many bytes of UTF-8 JSON.

Commands

Command Args Description
run_node name Run an existing node by name
run_bytes data, node Load compiled Yarn from base64 bytes, run specified node
run_file path, node Load compiled Yarn from file path, run specified node
load_localization localization Load localization strings without running a Yarn script
load_scripts files Load compiled Yarn scripts without running (for preloading)
load_config config Load Luban config tables (quests, conditions, etc.)
run_combined files, node, localization, config Load scripts, localization, and config, then run

load_localization format

The localization arg is keyed by locale code:

{
  "localization": {
    "en": {
      "line:abc123": "Hello, world!"
    },
    "zh-cn": {
      "line:abc123": "..."
    }
  }
}

This is useful for preloading translation strings before running dialogue, or for updating strings independently of script changes.

run_combined format

The files arg is an array of objects:

{
  "name": "MyScript.yarn",
  "data": "<base64 compiled yarn>",
  "strings": {
    "line:abc123": "Hello, world!"
  }
}

The localization arg is keyed by locale code:

{
  "zh-cn": {
    "line:abc123": "..."
  },
  "ja": {
    "line:abc123": "..."
  }
}

load_config format

The config arg is an array of table objects:

{
  "config": [
    {
      "tableName": "TbMapEvent",
      "records": [
        {
          "ID": 900001,
          "PlaceID": 9,
          "Title": "My Quest",
          "DialogID": "MyQuest_Start",
          "Nonkit": {
            "IconSourceId": 10084
          }
        }
      ]
    },
    {
      "tableName": "TbLocalizationAutoGenerated",
      "records": [
        {
          "Key": "MapEvent_900001_Title",
          "CH": "我的任务",
          "EN": "My Quest"
        }
      ]
    }
  ]
}

Supported Tables

Table Key Type Description
TbMapEvent int (ID) Quest/event definitions
TbCondition int (ID) Unlock/lock conditions for events
TbLocalizationAutoGenerated string (Key) Localization strings for UI elements

Quest Localization Keys

The game looks up quest titles and descriptions using these key patterns in TbLocalizationAutoGenerated:

  • MapEvent_{ID}_Title - Quest title displayed in UI
  • MapEvent_{ID}_Desc - Quest description

For example, a quest with ID 900001 needs:

  • MapEvent_900001_Title for its title
  • MapEvent_900001_Desc for its description

What it touches

The plugin interacts with:

  • DialogueRunner - Unity component from Yarn Spinner that runs dialogue
  • YarnProject.Program - the compiled Yarn program (nodes, instructions, etc.)
  • Dialogue - the Yarn VM instance
  • Localization - Yarn Spinner's localization objects for string lookup
  • Table.tables - Luban configuration tables (for config injection)
  • SpriteUtils - sprite loading utilities (patched for icon fallback)

The plugin uses Harmony to patch Table.Init() (to inject configs after table initialization) and SpriteUtils.LoadMapEventIcon() (to enable icon borrowing for custom events). It doesn't modify game files on disk. Everything happens in memory at runtime.

License

nonkit-plugin is published under the Unlicense.

About

nonkit-plugin is a BepInEx plugin for runtime modification of The NOexistenceN of Morphean Paradox : The Forest of Silver Shallots

Resources

License

Stars

Watchers

Forks

Packages

No packages published

Languages