801b43610f
Renames the plugin from Curiosidades to Canalhandia (the server name) now that it covers more than curiosities, and adds: - adivinha: the curiosity with the name hidden and clickable player names, revealing who guessed right. Curiosity sentences never contained the subject's name, so the same generator feeds both with no redaction step. - luto: a clickable [F] under death messages with a closing count. - enquete: polls with clickable options and a live boss-bar tally. - ranking + marcos: leaderboards covering offline players, and one-time milestone announcements persisted to marcos.yml. Fixes the reaction counter being invisible. Chat cannot be edited after sending, so the button counts are necessarily frozen; previously the only live surface was the boss bar and the buttons showed no number at all. Counts now appear in the buttons at send time, on the boss bar, on the reactor's action bar, and as a final tally line when the window closes. Late clicks used to be dropped silently once the 90s window passed. The last 8 reaction sets now stay live for reacao-validade-minutos (15). Rankings read the stats JSON directly because Bukkit only exposes statistics for online players. Adds README.md documenting the design constraints that are easy to mistake for bugs. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01ActvLGJApdxEAd2yfKPwqv
202 lines
8.1 KiB
Markdown
202 lines
8.1 KiB
Markdown
# Canalhandia
|
|
|
|
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.
|
|
|
|
All player-facing text is Portuguese (pt-BR).
|
|
|
|
---
|
|
|
|
## Modules
|
|
|
|
| Module | What it does |
|
|
|---|---|
|
|
| `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. |
|
|
| `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. |
|
|
|
|
Toggle any of them: `/canalhandia modulo <nome> <on|off>`
|
|
|
|
---
|
|
|
|
## Where the data comes from
|
|
|
|
Everything is derived from **vanilla statistics**. No database, no tracking code,
|
|
no extra writes — the server was already recording all of it.
|
|
|
|
- **Online players** use the Bukkit API (`Player#getStatistic`).
|
|
- **Rankings** read `<world>/players/stats/<uuid>.json` directly, because Bukkit
|
|
only exposes statistics for players who are online. `usercache.json` maps those
|
|
UUIDs back to names, including Floodgate/Bedrock players whose UUIDs start with
|
|
`00000000-0000-0000-0009`.
|
|
|
|
Note the path: Paper writes to `<world>/players/stats`, **not** `<world>/stats`.
|
|
`OfflineStats` checks both.
|
|
|
|
---
|
|
|
|
## Two constraints worth knowing
|
|
|
|
These shaped the design, and anyone changing the code should know them before
|
|
"fixing" what looks odd.
|
|
|
|
### 1. Chat messages cannot be edited after sending
|
|
|
|
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:
|
|
|
|
- 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.
|
|
|
|
### 2. Clicks arrive late
|
|
|
|
People scroll back and click minutes after a message. Reactions therefore keep
|
|
counting for `reacao-validade-minutos` (default 15) even after the boss bar is
|
|
gone, and the last 8 reaction sets stay in memory for that reason. Silently
|
|
dropping a late click looks like a bug to the player.
|
|
|
|
### 3. Names are translated by the client, not by us
|
|
|
|
Block, item and mob names are emitted as **translatable components**
|
|
(`Component.translatable(material.translationKey())`), so a pt-BR client renders
|
|
"Pedra" and an en-US client renders "Stone" from the same broadcast. There is no
|
|
translation table to maintain.
|
|
|
|
The consequence: the client only supplies the **singular** form. Every sentence
|
|
is therefore phrased so the number never has to agree with the noun —
|
|
*"5.966 blocos de Pedra"*, never *"5.966 Pedras"*. Keep that rule when adding
|
|
sentences to `CuriosityFactory`.
|
|
|
|
Note that the **server console** renders translatable components in English, so
|
|
`[Curiosidade] ... 16 unidades de Copper Pickaxe` in `latest.log` does not mean
|
|
players saw English.
|
|
|
|
---
|
|
|
|
## Commands
|
|
|
|
Player-facing:
|
|
|
|
```
|
|
/curiosidade anuncia uma curiosidade agora
|
|
/curiosidade <jogador> anuncia sobre alguém específico
|
|
/curiosidade ver [jogador] mostra só para você
|
|
/curiosidade listar [jogador] lista todas as curiosidades disponíveis
|
|
/curiosidade toggle entra/sai do sorteio
|
|
/adivinha inicia uma rodada de "adivinhe de quem é"
|
|
/enquete Pergunta | A | B abre uma enquete
|
|
/enquete encerrar encerra a enquete aberta
|
|
/ranking [categoria] placares do servidor
|
|
/canalhandia status mostra toda a configuração
|
|
/canalhandia modulos lista os módulos e seu estado
|
|
```
|
|
|
|
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 reload
|
|
/curiosidade modo <entrada|intervalo|ambos|manual>
|
|
/curiosidade intervalo <min> intervalo do modo temporizado
|
|
/curiosidade atraso <seg> espera após o jogador entrar
|
|
/curiosidade cooldown <min> mínimo entre citar o mesmo jogador
|
|
/curiosidade repetir <n> quantas recentes evitar repetir
|
|
/curiosidade janela <seg> duração da barra de reações
|
|
/curiosidade validade <min> por quanto tempo cliques ainda contam
|
|
/curiosidade reacoes <on|off>
|
|
/curiosidade reacao add <chave> <rótulo>
|
|
/curiosidade reacao remover <chave>
|
|
/curiosidade categoria <nome> <on|off>
|
|
```
|
|
|
|
Every setter **writes through to `config.yml` immediately**, so in-game changes
|
|
survive a restart.
|
|
|
|
### Permissions
|
|
|
|
| Permission | Default | Grants |
|
|
|---|---|---|
|
|
| `canalhandia.reagir` | everyone | react, press F |
|
|
| `canalhandia.ver` | everyone | `ver`, `listar`, `/ranking` |
|
|
| `canalhandia.enquete` | everyone | open polls |
|
|
| `canalhandia.forcar` | op | trigger curiosities and guess rounds |
|
|
| `canalhandia.admin` | op | change modules and all settings |
|
|
| `canalhandia.isento` | nobody | never be the subject |
|
|
|
|
Permissions are **declared explicitly** in `plugin.yml`. An undeclared Bukkit
|
|
permission falls back to op-only, which would silently stop normal players from
|
|
reacting.
|
|
|
|
---
|
|
|
|
## Building
|
|
|
|
Requires **JDK 25**. Paper 26.2's API ships Java 25 class files, and JDK 21 fails
|
|
with a misleading `cannot access org.bukkit.Bukkit` — that phrasing means the
|
|
class-file version is too new, not that the dependency is missing.
|
|
|
|
```bash
|
|
docker run --rm -v "$PWD":/work -v "$HOME/.m2":/root/.m2 -w /work \
|
|
maven:3.9-eclipse-temurin-25 mvn -B package
|
|
```
|
|
|
|
Output: `target/Canalhandia-1.0.0.jar`
|
|
|
|
The dependency uses Paper's newer coordinate scheme:
|
|
`io.papermc.paper:paper-api:26.2.build.92-stable`.
|
|
|
|
### Deploying
|
|
|
|
Copy the jar into the server's `plugins/` and restart. There is no hot-reload
|
|
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 cp target/Canalhandia-1.0.0.jar minecraft/${POD#pod/}:$SRV/plugins/Canalhandia-1.0.0.jar
|
|
```
|
|
|
|
---
|
|
|
|
## Source layout
|
|
|
|
| File | Role |
|
|
|---|---|
|
|
| `Canalhandia.java` | Plugin entry point, scheduling, broadcasting, listeners |
|
|
| `CanalhandiaCommand.java` | Every command and all click callbacks |
|
|
| `Settings.java` | Typed config access; all setters persist immediately |
|
|
| `Module.java` / `Category.java` / `Mode.java` | Toggleable feature, fact group, trigger mode |
|
|
| `CuriosityFactory.java` | Builds the Portuguese sentences from statistics |
|
|
| `Stats.java` | Defensive Bukkit statistics access |
|
|
| `Fact.java` | One sentence plus its category |
|
|
| `Reactions.java` | Reaction state, buttons, boss bar, tally |
|
|
| `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 |
|
|
| `RankingMetric.java` | Leaderboard columns and their formatting |
|
|
| `Msg.java` | Shared chat formatting and pt-BR number/duration formatting |
|
|
|
|
### Adding a curiosity
|
|
|
|
Add one line to `CuriosityFactory.facts(...)` using the existing helpers
|
|
(`material`, `entities`, `distance`, `time`, `count`), pick a `Category`, and
|
|
phrase it so the count never has to agree with a translated noun.
|
|
|
|
Statistic constants get renamed between Minecraft releases, so resolve them via
|
|
`Stats.resolve("NEW_NAME", "OLD_NAME")` — a rename then degrades one curiosity
|
|
instead of breaking the whole announcement.
|