i18n: per-player EN/PT via Adventure GlobalTranslator (#1)
Foundation + commands module of the i18n spec. - I18n registry/loader + Lang.tr facade + reloadI18n - PT source-of-truth bundle + EN translation - CanalhandiaCommand player-facing strings migrated; admin-tuning/help/enum-labels deferred - I18nTest: parity + per-locale render + pt_BR fallback; 316/316 green 🤖 Generated with [Claude Code](https://claude.com/claude-code)
This commit was merged in pull request #1.
This commit is contained in:
@@ -2,9 +2,13 @@
|
||||
|
||||
Chat-only social features for the Canalhandia Minecraft server (Paper 26.2).
|
||||
|
||||
**Nothing here touches gameplay.** No items, no world edits, no attributes, no
|
||||
economy. Everything is chat messages, boss bars and clickable buttons, and every
|
||||
module can be switched off independently.
|
||||
**Almost nothing here touches gameplay** — no world edits, no attributes, no
|
||||
economy, and no items anywhere except one: the `luto` tribute. Pressing F to pay
|
||||
respects drops the dead player's head into the mourner's inventory as a symbolic
|
||||
memento (toggle: `luto.cabeca`). Everything else is chat messages and clickable
|
||||
buttons, and every module can be switched off independently. (The reaction-count
|
||||
boss bar was removed; live counts ride on the reactor's action bar and a closing
|
||||
tally line.)
|
||||
|
||||
All player-facing text is Portuguese (pt-BR).
|
||||
|
||||
@@ -16,11 +20,16 @@ All player-facing text is Portuguese (pt-BR).
|
||||
|---|---|
|
||||
| `curiosidades` | *"Sabia que o Fulano já minerou 5.966 blocos de Pedra?"* — a fact about a player, with reaction buttons. Fires on join by default. |
|
||||
| `adivinha` | The same fact with the name hidden, plus clickable player names. Reveals after 45s and names who guessed right. |
|
||||
| `luto` | A clickable `[F]` under each death message, with a count when the window closes. |
|
||||
| `luto` | A clickable `[F]` under each death message, with a count when the window closes. Pressing F drops the **dead player's head** into the mourner's inventory (once per mourner per death, never to the dead player themselves) — the one gameplay-touching feature; toggle with `luto.cabeca`. Bedrock types `/f`. |
|
||||
| `enquete` | `/enquete Pergunta \| A \| B` — clickable voting with a live tally on the boss bar. |
|
||||
| `ranking` | `/ranking mineracao` and friends. Covers **offline players too**. |
|
||||
| `marcos` | Announces round milestones — 100 km walked, 24 hours played — the first time someone crosses one. |
|
||||
| `ia` | `/ia <pergunta>` asks an OpenAI-compatible model in chat. Optional wiki grounding, per-player memory, operator corrections. |
|
||||
| `mortes` | Replaces each death message with a comic pt-BR line (cause-based flavor + a death counter) and sends the death coordinates **privately** to the dead player on respawn (the death screen swallows chat sent during the event), so they can run back to their dropped items. Respects `keepInventory`. `/mortes` lists your last 10 deaths with the cause and where each happened (`mortes.yml`) — your own only, since where someone died is where their stuff is. |
|
||||
| `zoacao` | A chat message matching the trigger (a pattern + a match mode) is swapped for a random line from `zoacao.mensagens` — a chat gag. The player's name still prefixes it. Default: a bare `f`/`F` (trimmed) → a random gag line. Match modes: `igual` (equals), `contem` (contains), `comeca` (starts with), `termina` (ends with), `regex`. The mode, the pattern, and the message list are all editable in-game with `/canalhandia zoacao ...`. Pure chat swap; the `luto` tribute is unaffected (paying respects still needs the `[F]` button or `/f`). Affects Bedrock chat too (it's a chat event, not a click). |
|
||||
| `notas` | Notes in chat. `/save` pins where you are; `/nota add <texto>` writes a private one (only you see it, everyone may write); `/nota publica <texto>` writes one everyone reads, and needs `canalhandia.nota.publica` (default op). Every note stores its coordinates — click-to-copy on Java. Persisted to `notas.yml`. **No teleport**: nothing here touches gameplay. |
|
||||
| `recados` | `/recado <jogador> <texto>` — a line for someone who is offline, delivered on their next join. `/recados` shows how many of yours are still unread. Persisted to `recados.yml`. |
|
||||
| `conquistas` | Named achievements beyond the numeric milestones: "Casca Grossa" (50h, under 10 deaths), "Turista" (100h, barely mined), "Imortal às Avessas". `/conquistas` lists them all and marks yours. Persisted to `conquistas.yml`. |
|
||||
| `ia` | `/ia <pergunta>` asks an OpenAI-compatible model in chat. Has a personality (`zoeiro` by default — it will tease you), sees the last few chat lines and the live server state, and grounds answers in the asker's real stats. Optional wiki lookup, per-player memory, operator corrections. |
|
||||
|
||||
Toggle any of them: `/canalhandia modulo <nome> <on|off>`
|
||||
|
||||
@@ -51,14 +60,14 @@ These shaped the design, and anyone changing the code should know them before
|
||||
|
||||
There is no vanilla way to update a message that is already in the chat log. So
|
||||
the counts baked into the reaction buttons are **frozen at send time** and never
|
||||
change. The live numbers appear on three other surfaces instead:
|
||||
change. The live numbers appear on two other surfaces instead:
|
||||
|
||||
- a **boss bar** while the window is open (`janela-reacao-segundos`, default 90)
|
||||
- an **action bar** shown to whoever just reacted
|
||||
- a **final tally line** broadcast when the window closes
|
||||
|
||||
An earlier version only had the boss bar, and it read as broken — the buttons
|
||||
showed no number at all.
|
||||
(A boss bar spanning the whole reaction window was tried and removed — it sat
|
||||
on screen for `janela-reacao-segundos` and read as clutter, and the two
|
||||
surfaces above already carry the counts.)
|
||||
|
||||
### 2. Who reacted, without filling the screen
|
||||
|
||||
@@ -150,6 +159,20 @@ Player-facing:
|
||||
/ranking [categoria] placares do servidor
|
||||
/canalhandia status mostra toda a configuração
|
||||
/canalhandia modulos lista os módulos e seu estado
|
||||
/save salva onde você está (privado)
|
||||
/save coords o mesmo, escrito por extenso
|
||||
/save <texto> salva o lugar com um texto
|
||||
/nota add <texto> anotação privada, só você vê
|
||||
/nota publica <texto> anotação pública (precisa de permissão)
|
||||
/nota listar [publicas|privadas] lista o que você pode ver
|
||||
/nota buscar <texto> procura no texto das anotações
|
||||
/nota ver <n> mostra uma anotação inteira
|
||||
/nota remover <n> apaga uma anotação sua
|
||||
/recado <jogador> <texto> recado entregue quando a pessoa entrar
|
||||
/recados quantos recados seus ainda não foram lidos
|
||||
/mortes suas últimas mortes, com causa e lugar
|
||||
/conquistas conquistas, com as suas marcadas
|
||||
/ranking semanal [métrica] só o que foi ganho nesta semana
|
||||
```
|
||||
|
||||
Admin (`canalhandia.admin`):
|
||||
@@ -158,6 +181,16 @@ Admin (`canalhandia.admin`):
|
||||
/canalhandia modulo <nome> <on|off> liga/desliga um módulo
|
||||
/canalhandia marcos força uma verificação de marcos
|
||||
/canalhandia limpar [cooldown|historico|tudo]
|
||||
/canalhandia zoacao listar mostra a regra e as frases da zoação
|
||||
/canalhandia zoacao modo <m> igual | contem | comeca | termina | regex
|
||||
/canalhandia zoacao padrao <texto> texto/regex que dispara a zoação
|
||||
/canalhandia zoacao adicionar <frase> adiciona uma frase de zoação
|
||||
/canalhandia zoacao remover <n|texto> remove uma frase de zoação
|
||||
/canalhandia zoacao limpar volta para as frases padrão
|
||||
/ia personalidade lista as personalidades da IA
|
||||
/ia personalidade <nome> zoeiro | amigao | seco | aldeao | neutro
|
||||
/ia eventos <on|off> IA comenta mortes seguidas sozinha
|
||||
/ia saudacao <on|off> IA dá as boas-vindas de quem entra
|
||||
/canalhandia reload
|
||||
/curiosidade modo <entrada|intervalo|ambos|manual>
|
||||
/curiosidade intervalo <min> intervalo do modo temporizado
|
||||
@@ -185,6 +218,9 @@ survive a restart.
|
||||
| `canalhandia.forcar` | op | trigger curiosities and guess rounds |
|
||||
| `canalhandia.admin` | op | change modules and all settings |
|
||||
| `canalhandia.isento` | nobody | never be the subject |
|
||||
| `canalhandia.nota` | everyone | `/save` and private notes |
|
||||
| `canalhandia.nota.publica` | op | write notes everyone can read |
|
||||
| `canalhandia.recado` | everyone | leave messages for other players |
|
||||
| `canalhandia.ia` | op | `/ia` (public question, broadcast to chat) |
|
||||
| `canalhandia.ia.privado` | op | `/iap` (private question, answer only to the asker) |
|
||||
| `canalhandia.ia.corrigir` | op | `/ia corrigir <resposta>` — register a correction for the last answer |
|
||||
@@ -196,6 +232,151 @@ reacting.
|
||||
|
||||
---
|
||||
|
||||
## Notes (`/save`, `/nota`)
|
||||
|
||||
Somewhere to write things down without leaving the game. Persisted to
|
||||
`plugins/Canalhandia/notas.yml`.
|
||||
|
||||
### Scopes
|
||||
|
||||
| Scope | Who reads it | Who may write it |
|
||||
|---|---|---|
|
||||
| **private** (default) | only the author | everyone — `canalhandia.nota` |
|
||||
| **public** | everyone; announced when created | `canalhandia.nota.publica` (op) |
|
||||
|
||||
Public notes are gated because a board anyone can write to becomes a graffiti
|
||||
wall. Grant it per player with LuckPerms:
|
||||
`lp user <nome> permission set canalhandia.nota.publica true`.
|
||||
|
||||
### `/save` — the quick path
|
||||
|
||||
`/save` and `/save coords` pin the spot you are standing on. `/save <texto>`
|
||||
pins it with a note. Always **private**: this is the command someone types
|
||||
without reading help first, and the safe default for that is the one that
|
||||
cannot surprise anyone by broadcasting. `/nota publica <texto>` is the explicit
|
||||
way to share.
|
||||
|
||||
### Places, not teleports
|
||||
|
||||
Every note stores the world and block coordinates where it was written — on a
|
||||
Minecraft server a note is nearly always about a *place*. On Java the
|
||||
coordinates are click-to-copy (the same affordance the death-coords message
|
||||
uses); Bedrock renders no click event and gets the same text plainly.
|
||||
|
||||
There is **no teleport**. Nothing in this module touches gameplay; a note is a
|
||||
line of text and a set of numbers.
|
||||
|
||||
### Privacy
|
||||
|
||||
**A private note is never sent to the AI, at any setting.** The AI call leaves
|
||||
this server for a third-party API, so a private note reaching it would be a
|
||||
disclosure the author never agreed to. The filter lives inside
|
||||
`Notes.publicSummary` — the only method the AI path calls — rather than at the
|
||||
call site, so a future caller cannot get it wrong by accident. A test asserts
|
||||
it directly.
|
||||
|
||||
Public notes *are* sent (`ia.contexto-notas`, default 10, 0 disables), which is
|
||||
what lets `/ia onde fica a base?` answer from what players actually wrote down.
|
||||
|
||||
Two smaller rules follow from the same principle: a note the viewer cannot see
|
||||
is reported as **missing** rather than as forbidden (saying "that one is
|
||||
private" would confirm it exists), and `/nota buscar` runs through the same
|
||||
visibility filter, so search cannot become a way to probe for someone else's
|
||||
text.
|
||||
|
||||
### Details
|
||||
|
||||
- Ids are never reused after a deletion — otherwise `/nota ver 2` would point
|
||||
at a different note than the one someone wrote down a minute ago.
|
||||
- 100 notes per player, both scopes together.
|
||||
- 256 characters per note. The section sign and control characters are
|
||||
stripped: a note is echoed into chat and could otherwise forge a line that
|
||||
looks like it came from the server.
|
||||
- Authors delete their own notes; `canalhandia.admin` deletes any, which is the
|
||||
only way to clear a public note left by someone who has stopped playing.
|
||||
- Search is case- and accent-insensitive — nobody types "após" into a chat
|
||||
search, and missing a match over an acute reads as broken.
|
||||
|
||||
---
|
||||
|
||||
## Weekly rankings
|
||||
|
||||
`/ranking semanal [métrica]` shows only what was **gained since the start of the
|
||||
week**. On a server with three regulars an all-time board is decided by whoever
|
||||
started first and then stops being a contest; subtracting a weekly baseline
|
||||
makes it one again.
|
||||
|
||||
Rotation is time-based and idempotent: the snapshot carries the timestamp it was
|
||||
taken at and is replaced only once a week has actually elapsed. **A restart
|
||||
never rotates it** — a server that restarts nightly would otherwise reset the
|
||||
week every day, which is the failure this design exists to avoid.
|
||||
|
||||
Players who did not move are dropped (the point is who is playing *this* week).
|
||||
Someone missing from the baseline counts their whole value, having joined during
|
||||
the week. A negative difference is dropped rather than shown: statistics only go
|
||||
up, so a negative means a stale baseline or a reset stats file, not a result.
|
||||
|
||||
---
|
||||
|
||||
## Spontaneous AI lines
|
||||
|
||||
Off by default (`ia.comentar-eventos`, `ia.saudacao`). The persona can comment
|
||||
on a run of deaths and greet players as they join, using their own numbers. Both
|
||||
are opt-in because a chatty AI nobody asked for is the fastest way to make
|
||||
players hate the feature.
|
||||
|
||||
`/ia eventos <on|off>` and `/ia saudacao <on|off>` toggle them live.
|
||||
|
||||
### Why the budget is strict
|
||||
|
||||
A player question is self-limiting — someone chose to spend it. A line the AI
|
||||
decides to make on its own is not, and it costs money every time. `Budget`
|
||||
enforces three limits, **all** of which must pass:
|
||||
|
||||
| Limit | Default | Why |
|
||||
|---|---|---|
|
||||
| gap between any two lines | 10 min | stops chat spam |
|
||||
| daily cap, separate from `/ia`'s | 20 | protects the spend |
|
||||
| per-subject cooldown | 30 min | one unlucky player is not narrated all evening |
|
||||
|
||||
`allows()` does not spend, so a caller that decides not to fire (nobody online,
|
||||
the model returned nothing) has burned nothing. `saySomething` spends **up
|
||||
front** rather than on success: two events landing in the same tick would
|
||||
otherwise both pass `allows()` and fire together — the exact double-message the
|
||||
gap exists to prevent.
|
||||
|
||||
A death streak decays after 15 minutes. Three deaths across an evening is not a
|
||||
streak; three in ten minutes is. Spontaneous lines are silent on failure —
|
||||
nobody asked for it, so nobody should see it fail.
|
||||
|
||||
---
|
||||
|
||||
## Notes on the BlueMap web map
|
||||
|
||||
Public notes are drawn as markers on BlueMap (`notas.no-mapa`, default on).
|
||||
Notes already carry a world and coordinates and the server already runs BlueMap,
|
||||
so this joins the two. **Private notes are never drawn, at any setting.**
|
||||
|
||||
BlueMap is an **optional** dependency. `BlueMapBridge` is the only class that
|
||||
touches its API, and every entry point catches `NoClassDefFoundError` as well as
|
||||
`Exception` — the failure mode of a missing optional dependency is a linkage
|
||||
error, not an exception — so a server without BlueMap logs one fine-level line
|
||||
and carries on.
|
||||
|
||||
The dependency is `provided` scope because BlueMap ships those classes itself; a
|
||||
second copy inside this jar would shadow them and break the real plugin.
|
||||
`preflight.sh` fails if that scope is ever dropped.
|
||||
|
||||
Markers are **rebuilt**, not incrementally patched: BlueMap discards everything
|
||||
when it unloads and expects addons to re-create markers on its enable callback,
|
||||
and a full rebuild of a tiny list cannot drift out of sync the way a missed
|
||||
delete would. Notes are matched to the map that renders their world, or a Nether
|
||||
note would be drawn at the same numeric coordinates in the overworld, pointing
|
||||
at nothing. Note text is player-written and lands in a web page, so it is
|
||||
HTML-escaped.
|
||||
|
||||
---
|
||||
|
||||
## IA (`/ia`)
|
||||
|
||||
Chat Q&A backed by an OpenAI-compatible endpoint (default MiniMax). Gated to
|
||||
@@ -215,6 +396,8 @@ executed command.
|
||||
|---|---|
|
||||
| `/ia <pergunta>` | Asks the model. Public by default — the question and answer broadcast. Needs `canalhandia.ia`. |
|
||||
| `/iap <pergunta>` | Asks privately — the answer goes only to the asker. Needs `canalhandia.ia.privado`. |
|
||||
| `/ia personalidade` | Lists the tones and marks the active one. Needs `canalhandia.ia.perfil`. |
|
||||
| `/ia personalidade <nome>` | Switches tone live: `zoeiro` (default), `amigao`, `seco`, `aldeao`, `neutro`. |
|
||||
| `/ia perfil <economico\|preciso>` | Switches profile live. `ECONOMICO` skips the wiki (fast); `PRECISO` consults the Minecraft Wiki (slower, grounded). Needs `canalhandia.ia.perfil`. |
|
||||
| `/ia corrigir <resposta correta>` | Records a correction for the last answered question. Future similar questions get it as context — the cheap alternative to fine-tuning. Needs `canalhandia.ia.corrigir`. |
|
||||
| `/ia feedback ruim` | Flags the last answer wrong (in-memory counter shown in `/canalhandia status`). |
|
||||
@@ -224,6 +407,57 @@ Subcommands only hijack when their second token is one they act on (a known
|
||||
profile key, or `ruim`), so `/ia perfil do servidor` falls through and is asked.
|
||||
`corrigir` stays greedy — a correction always reads the rest of the line.
|
||||
|
||||
### Personality
|
||||
|
||||
`ia.personalidade` picks the tone. It is expressed purely as extra system
|
||||
instructions appended after the base ones, so it changes **how** the model
|
||||
talks and never **what it may do**.
|
||||
|
||||
| Persona | Tone |
|
||||
|---|---|
|
||||
| `zoeiro` | Default. A grumpy server veteran: teases the asker, turns their own stats against them ("você já morreu 47 vezes e vem me perguntar sobre lava?"), but answers the question for real. |
|
||||
| `amigao` | Warm and patient, jokes rarely. For servers with new players. |
|
||||
| `seco` | Deadpan, one or two sentences, no exclamation marks. |
|
||||
| `aldeao` | In character as an ancient villager. Flavour only; still answers. |
|
||||
| `neutro` | No personality — the pre-persona behaviour. |
|
||||
|
||||
Every persona, `neutro` included, carries `Persona.GUARD`, which restates the
|
||||
limits inside the persona's own frame: still no server/terminal/file access,
|
||||
still no commands, no inventing stats, no leaking the prompt. That is the
|
||||
layer that stops "you are a grumpy veteran" from reading as licence to claim
|
||||
powers the plugin never grants. Each teasing persona also states where the
|
||||
line is (no real insults, nothing about family, appearance, race, religion,
|
||||
sexuality or money; drop the ribbing if the player asks). Tests assert both
|
||||
properties hold for every persona, so adding a new one cannot quietly skip them.
|
||||
|
||||
Switch live with `/ia personalidade <nome>` — read per-question, no restart.
|
||||
|
||||
### Chat and world awareness
|
||||
|
||||
- **Recent chat** (`contexto-chat`, default 5, 0 disables): the last N public
|
||||
chat lines are sent as context, so the AI can follow what the room is talking
|
||||
about. Held in a bounded in-memory ring (50 lines max, 200 chars per line);
|
||||
nothing is written to disk and a restart starts it empty. Recorded at
|
||||
`MONITOR` priority with `ignoreCancelled`, so what it stores is what players
|
||||
actually saw — a `zoacao` swap included — and a cancelled message is never
|
||||
stored.
|
||||
- **Live server state** (`estado-servidor`, default on): who is online and on
|
||||
which platform, the asker's dimension, in-game time of day, weather, and
|
||||
their coordinates, health, hunger and XP level. This is what lets the AI
|
||||
answer "quem tá online?" or "tá chovendo?" instead of insisting it has no
|
||||
access. Captured on the main thread before the async call — every field
|
||||
reads the Bukkit world API, which is not safe off it — so only the formatted
|
||||
string crosses the thread boundary.
|
||||
|
||||
### Answer styling
|
||||
|
||||
With `estilo-rico` on (default), Java players get the answer with a hover card
|
||||
showing the original question and the active persona, plus a click that
|
||||
pre-fills `/ia ` for a follow-up. The click uses `suggestCommand`, never
|
||||
`runCommand`: nothing executes without the player pressing enter. Bedrock
|
||||
renders neither hover nor click, so it always gets the plain line — built via
|
||||
`broadcastPerPlatform`, like every other interactive message here.
|
||||
|
||||
### Profile
|
||||
|
||||
`ECONOMICO` skips the wiki round trip — fast, ungrounded. `PRECISO` runs a
|
||||
@@ -246,6 +480,12 @@ no model round trip.
|
||||
- **Server context**: the `contexto:` list in `config.yml` is facts the model
|
||||
would never know (server name, Bedrock prefix, installed mods). Sent on every
|
||||
question.
|
||||
- **Asker's stats**: when `estatisticas-jogador` is on (default), the asking
|
||||
player's headline numbers (blocks mined, time played, distance walked, deaths,
|
||||
mob kills) are read from their vanilla stats JSON and injected as context, so
|
||||
"quantos blocos eu minerei?" gets a real answer instead of "não tenho
|
||||
acesso". The stats file lags by under a minute. Gated off with
|
||||
`/canalhandia` settings or `ia.estatisticas-jogador: false`.
|
||||
- **Recipes**: `RecipeBook` snapshots `Bukkit.recipeIterator()` at enable (main
|
||||
thread) and answers recipe questions from that snapshot — `explaintext` drops
|
||||
tables, so the wiki cannot supply them.
|
||||
@@ -293,9 +533,42 @@ path for a new jar — `/canalhandia reload` only re-reads `config.yml`.
|
||||
```bash
|
||||
POD=$(microk8s kubectl get pod -n minecraft -l app=crafty-controller -o name | head -1)
|
||||
SRV=/crafty/servers/6e39a8b2-300b-42d6-8139-f397c23e461b
|
||||
microk8s kubectl exec ${POD#pod/} -n minecraft -- \
|
||||
cp $SRV/plugins/Canalhandia-1.0.0.jar $SRV/plugins/Canalhandia-1.0.0.jar.bak-$(date +%F)
|
||||
microk8s kubectl cp target/Canalhandia-1.0.0.jar minecraft/${POD#pod/}:$SRV/plugins/Canalhandia-1.0.0.jar
|
||||
# The JVM runs as uid 1000 / gid 0; a root-owned jar is one it cannot read,
|
||||
# and the failure looks exactly like "the plugin just did not load".
|
||||
microk8s kubectl exec ${POD#pod/} -n minecraft -- chown 1000:0 $SRV/plugins/Canalhandia-1.0.0.jar
|
||||
```
|
||||
|
||||
### Pre-flight
|
||||
|
||||
`./preflight.sh [jar]` checks a staged deploy **before** anyone restarts
|
||||
anything. Every check is read-only; it never restarts the server, never writes
|
||||
to `plugins/`, and never touches the world.
|
||||
|
||||
```
|
||||
$ ./preflight.sh
|
||||
1. Local build jar opens, plugin.yml present, all 16 commands declared,
|
||||
config.yml bundled
|
||||
2. Tests surefire totals, 0 failures
|
||||
3. Cluster crafty pod found, plugins directory reachable
|
||||
4. Staged jar hash matches the local build, owned 1000:0, a rollback
|
||||
.bak jar exists
|
||||
5. Live config parses as YAML, carries the keys this deploy needs, has a
|
||||
.bak to roll back to
|
||||
6. Health the log is readable and free of recent ERROR lines
|
||||
|
||||
PRE-FLIGHT CLEAN — safe to restart.
|
||||
```
|
||||
|
||||
It exits non-zero on any failure. A missing YAML parser reports as *not
|
||||
checked* rather than *invalid*: a harness that cries wolf is one people learn
|
||||
to ignore.
|
||||
|
||||
Because the plugin is staged dormant (copied in, not restarted), a red line
|
||||
here is a crash-on-boot you get to fix while the server is still up.
|
||||
|
||||
---
|
||||
|
||||
## Source layout
|
||||
@@ -313,8 +586,19 @@ microk8s kubectl cp target/Canalhandia-1.0.0.jar minecraft/${POD#pod/}:$SRV/plug
|
||||
| `GuessRound.java` | "Adivinhe de quem é" round state |
|
||||
| `Poll.java` | Poll state, voting, results |
|
||||
| `Milestones.java` | Threshold tracking, persisted to `marcos.yml` |
|
||||
| `OfflineStats.java` | Reads stats JSON for offline players |
|
||||
| `OfflineStats.java` | Reads stats JSON for offline players (rankings + the asker's stat summary for the IA) |
|
||||
| `RankingMetric.java` | Leaderboard columns and their formatting |
|
||||
| `DeathFlavor.java` | Comic pt-BR verb phrases for each death cause (used by the `mortes` module) |
|
||||
| `Mail.java` | Offline messages and their YAML storage |
|
||||
| `DeathLog.java` | Recent deaths per player, for `/mortes` |
|
||||
| `Achievement.java` / `Achievements.java` | The named-achievement catalogue (pure) and its award bookkeeping |
|
||||
| `WeeklyStats.java` | Weekly ranking baseline and the delta arithmetic (pure) |
|
||||
| `Budget.java` | The three-limit gate on spontaneous AI lines (pure) |
|
||||
| `BlueMapBridge.java` | Public notes as markers on the BlueMap web map (optional dependency) |
|
||||
| `Note.java` / `Notes.java` | One note (scope, text, place, visibility rules) and its YAML storage |
|
||||
| `Persona.java` | The AI's five tones, each carrying the safety guard |
|
||||
| `ChatLog.java` | Bounded, thread-safe ring of recent public chat for the AI |
|
||||
| `ServerState.java` | Main-thread snapshot of the live world for the AI |
|
||||
| `Msg.java` | Shared chat formatting and pt-BR number/duration formatting |
|
||||
|
||||
### Adding a curiosity
|
||||
|
||||
Reference in New Issue
Block a user