Files
yourpart-daemon/docs/FALUKANT_PRODUCTION_CERTIFICATE.md

5.2 KiB
Raw Blame History

Falukant: Produktionszertifikate (Daemon)

Die Zertifikatslogik läuft im FalukantFamilyWorker (run_iteration) etwa 1× pro Minute (CERTIFICATE_RECALC_INTERVAL), nicht in einem eigenen Worker-Thread. Der 24h-Daily-Block enthält weiter Schuldturm und (bei Schema) Liebhaber/Ehe/Monatslogik — Zertifikat ist davon getrennt. Sie schreibt falukant_user.certificate fort (max. +1 pro erfolgreicher Prüfung, keine normale Herabstufung außer Bankrott / Erbfolge).

Implementierung: src/worker/falukant_certificate.rs (run_daily).

SQL

  • QUERY_GET_PRODUCTION_CERTIFICATE_INPUT_ROWS Eingangsdaten je Falukant-User (Spielercharakter, Wissen, Produktionen, Ämter, Haus …)
  • QUERY_UPDATE_FALUKANT_USER_CERTIFICATE Stufe + certificate_productions_count_since = NOW() (Migration 014)

Logik (Kurz, Spec §4)

  • certificateScore (Gewichte): Wissen 0,45 · Produktion 0,30 · Amt 0,08 · Adel 0,05 · Ruf 0,07 · Haus 0,05
  • raw_target aus Score-Schwellen: <0,9 → 1, ≥0,9 → 2, ≥1,8 → 3, ≥2,8 → 4, ≥3,8 → 5
  • effective_target mit Mindestanforderungen je Stufe (Spec §4.5)
  • Aufstieg nur wenn effective_target > currentcurrent + 1 (gegen effective_target und 5 begrenzt)
  • Bankrott (money <= -5000): Zertifikat auf 1, mit Event

Wichtig: UI vs. Daemon („48 h kein Aufstieg“)

Die Mindestanforderungen (z.B. Wissen ≥ 28, Produktionen ≥ 15 für Stufe 3) sind nur ein Teil. Zusätzlich gilt eine Obergrenze aus der gewichteten Wertung (certificateScoreraw_target): Es wird die höchste Stufe ≤ raw_target, die alle Mindestanforderungen erfüllt (effective_certificate_target in falukant_certificate.rs).

Typische Folge: Die UI zeigt nur zwei grüne Häkchen (Wissen/Produktionen), der Spieler bleibt aber auf Stufe 2, weil:

  1. raw_target = 2 (Wertung unter 1,8) — dann ist Stufe 3 fachlich ausgeschlossen, auch wenn die Mindestzahlen für Stufe 3 erfüllt sind. Oft liegt die Wertung knapp unter 1,8, wenn z.B. Produktionspunkte im Daemon niedrig sind (Bucket < 20 abgeschlossene Produktionen in falukant_log.production trotz höherer Anzeige in der UI).
  2. Abweichende Eingangsdaten gegenüber der UI: anderer gewählter Charakter (DISTINCT ON … c.id DESC), andere Zählung falukant_log.production (producer_id), Geld/Bankrott, etc.

Diagnose: Daemon mit YPDAEMON_CERT_VERBOSE=1 starten. Wenn jemand die Mindestanforderungen für die nächste Stufe erfüllt, aber nicht aufsteigt, erscheint eine Zeile [falukant_certificate] fu_id=… bleibt auf Stufe … mit certificate_score, raw_target, effective_target und Rohwerten.

SQL: QUERY_GET_PRODUCTION_CERTIFICATE_INPUT_ROWS in src/worker/sql.rs — für einen Betroffenen falukant_user.id filtern und mit der UI abgleichen.

Politische Ämter

Rang aus political_office_type.name (Substring-Heuristik im Daemon, ohne DB-Änderung). Anpassung über political_name_to_rank in falukant_certificate.rs.

Kirchliche Ämter

officePoints aus max(hierarchy_level) der aktiven church_office-Zeilen (gekappt 05).

Abgeschlossene Produktionen

SUM(completion_count) aus falukant_log.production mit producer_id = falukant_user.id oder character.id, und Zeitstempel >= certificate_productions_count_since (Spalte auf falukant_user, Migration 014). completion_count (Migration 015): pro abgeschlossener Produktion +1; das Log aggregiert noch immer pro Tag/Produkt/Region (quantity per UPSERT), daher reicht COUNT(*) der Zeilen nicht — ohne Summe bliebe der Zähler bei vielen Abschlüssen gleich. NULL bei certificate_productions_count_since: alle passenden Log-Zeilen (Bestand bis zur ersten Stufenänderung nach Migration).

Bei jedem Aufstieg, Bankrott (Stufe 1) und Erbfolge ohne Erben setzt der Daemon certificate_productions_count_since = NOW() — die Mindestanforderungen für die nächste Stufe gelten damit nur für neu abgeschlossene Produktionen. Logs werden dafür nicht gelöscht (bleiben u.a. für Wissens-Updates / Preise); optional räumt QUERY_DELETE_OLD_PRODUCTIONS nur sehr alte Zeilen auf (aktuell 30 Tage Retention, Speicherbegrenzung).

UI: Dieselbe Filterlogik wie der Daemon verwenden (>= certificate_productions_count_since), sonst weichen Anzeige und Aufstieg voneinander ab.

Gewählter Charakter pro User

Bei mehreren lebenden Charakteren: DISTINCT ON (fu.id) … ORDER BY fu.id, c.id DESC — der Charakter mit der höchsten character.id (typisch zuletzt genutzter Slot), damit Wissen/Ruf/Ämter näher an der UI liegen.

Events (WebSocket)

Bei Änderung der Stufe:

  1. falukantUpdateProductionCertificate mit reason, old_certificate, new_certificate
  2. falukantUpdateStatus

reason: daily_recalculation (normaler Aufstieg), bankruptcy (Geld ≤ 5000), succession_no_heir (Tod ohne Erben → Stufe 1).

user_id in den Events: app_user_id aus der Query (COALESCE(fu.user_id, fu.id)), sonst Fallback falukant_user_id.

Nicht umgesetzt (optional / später)

  • Feinere Bankrott-Definition
  • political_office_history (nicht im Repo)