Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
30 changes: 30 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,9 @@ the player to the next.
- **Pages** → MongoDB. The published Typewriter content is restored to disk before Typewriter reads it
and pushed back whenever you publish.
- **Snippets** → `snippets.yml` synced to MongoDB.
- **Fact placeholder** → read any persisted fact on *any* server (even one without the defining page)
via PlaceholderAPI: `%inkwell_fact_<entryId>%`. The value comes from the player's already-loaded fact
cache (no database hit), keyed by the entry's id.
- **Per-feature toggles** — enable only what you need; anything disabled stays on local disk (Typewriter's
default behaviour).

Expand Down Expand Up @@ -87,6 +90,24 @@ them from MongoDB **before** Typewriter loads, and pushes them back when you pub
(`StagingChangeEvent`) and on shutdown. MongoDB is the source of truth; on first run an existing local
copy is seeded into the database.

### Fact placeholder (cross-server display)

`/tw facts query <name>` only works where the fact's **entry** is defined (i.e. its page is present), so a
hub that doesn't run a floor's quest content answers `Could not find entry` even though the value followed
the player. To display such facts without copying pages between servers, enable `storage.fact_placeholder`
and use PlaceholderAPI with the entry's **id**:

```
%inkwell_fact_<entryId>%
```

The value is read straight from the connecting player's loaded fact cache (Inkwell loads every player's
facts on join regardless of whether the entry is defined locally), so there is no database hit per render.
The consuming server must run with `storage.facts: true`. Returns `fact_placeholder_default` (default
`"0"`) when the fact is unset for the player. Find an entry's id in its Typewriter page JSON (the entry's
`"id"` field) or in MongoDB: `db.facts.find({ groupId: "<player-uuid>" })`. The placeholder serves the
**online player being rendered**.

## Data model

**Facts** — one document per `(entryId, groupId)`:
Expand Down Expand Up @@ -139,6 +160,15 @@ Stack: Kotlin 2.3.20, JDK 21, Gradle 9.5, MongoDB Kotlin Coroutine driver 5.2.
warning rather than crashing.
- **No retry** on a MongoDB outage: errors are logged and the server keeps running, but data isn't
persisted while the database is unreachable.
- **Hot reload (PlugMan / `/reload`)**: works and won't lose facts — `onDisable` flushes every online
player before the plugin unloads. Inkwell deliberately does **not** close the MongoDB connection on a
runtime disable: Typewriter resolves Inkwell's `FactStorage` once via `by lazy` at *its own* startup
and caches it (`FactDatabase.storage`), so closing the client would leave that cached binding pointing
at a dead connection (`Mongo storeFacts failed: state should be: open`). Leaving it open keeps
Typewriter writing. **Caveat:** because that binding is cached, reloading Inkwell **alone** does not
re-point Typewriter at freshly-loaded code/config, and each reload leaves the previous load's
connection around until the JVM stops. For a clean state — and to pick up new code/config in the
Typewriter hook — **restart the server** (or reload Typewriter alongside Inkwell).

## License

Expand Down
2 changes: 2 additions & 0 deletions build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -12,13 +12,15 @@ repositories {
maven("https://repo.papermc.io/repository/maven-public/") { name = "papermc" }
maven("https://maven.typewritermc.com/beta") { name = "typewriter-beta" }
maven("https://maven.typewritermc.com/releases") { name = "typewriter-releases" }
maven("https://repo.extendedclip.com/releases/") { name = "placeholderapi" }
}

dependencies {
compileOnly("io.papermc.paper:paper-api:1.21.4-R0.1-SNAPSHOT")
compileOnly("com.typewritermc:engine-paper:0.9.0") { isTransitive = false }
// engine-core gives us Query (to filter persistable facts on player quit, like Typewriter does).
compileOnly("com.typewritermc:engine-core:0.9.0") { isTransitive = false }
compileOnly("me.clip:placeholderapi:2.11.6")
compileOnly("io.insert-koin:koin-core:3.5.6")
compileOnly("org.jetbrains.kotlinx:kotlinx-coroutines-core:1.8.1")
compileOnly("org.jetbrains.kotlinx:kotlinx-serialization-core:1.7.3")
Expand Down
Loading