1-Scoreboard System


Troubleshooting

Troubleshooting

nützliche Befehle

 

sudo tail  -f  /opt/scoreboard/log/scoreboard.log

 

sudo /opt/scoreboard/stopServer.sh

sudo /opt/scoreboard/startServer.sh

 

Einleitung

Einleitung

Rückblick

Das Projekt, eine Software basierte Anzeigetafel zu entwickeln, wurde mehr oder weniger aus der Not heraus geboren. Die „alten“ elektronischen Anzeigen, hatten schon einige Jahrzehnte ihren Dienst getan. Niemand war verfügbar oder in der Lage bei Problemen der Hardware und Elektronik zu helfen. Zudem waren die Ersatzteile enorm teuer.

So kostete ein einziges Siebensegment in der Größe die wir benötigten ca. 100€
120€ das Stück. Acht davon wurden pro Anzeigetafel (Scoreboard) benötigt. Sechs von den alten Anzeigen waren im Einsatz, macht also 48 Segmente die
kaputt hätten gehen können.

Dieses Risiko und der Wunsch nach etwas Neuem, Modernem und auf
Standard Hardware basierendem, hat uns dann angetrieben die neuen Scoreboards zu designen und die Software zu entwickeln.

Ganz fertig sind wir noch nicht, aber die Anzeigen sind bei verschiedenen
Vereinen in NRW im Einsatz und haben sich nun schon mehrere Jahre bewährt.
Es fehlt noch die eine oder andere Funktion, aber das wird noch realisiert werden.

Software ist ja nie fertig ;)

Hardware

Hardware

Hardware

Hardware 

Bei der Auswahl der Hardware haben wir strikt darauf geachtet, dass wir Standard Geräte und Displays einsetzen.

Als Display bzw. Anzeigegerät kann jeder HDMI oder DVI Monitor verwendet werden. Es hat sich herausgestellt, dass ein 32 / 40 Zoll Smart TV bestens geeignet ist. Die Auflösung Full HD also 1920x1080 ist vollkommen ausreichend.

Es können auch ältere kostengünstige Geräte eingesetzt werden. Allerdings sollte man auch auf den Energiebedarf achten.


Mini-PC

Jedes Display braucht zur Darstellung einen kleinen Mini PC. Dieser wird hinter dem Display angebracht. An dieser Stelle haben wir uns für den Raspberry
PI entschieden. So groß wie eine Zigarettenschachtel, jedoch mit einer beeindruckenden Leistung

 
 Auch das Preis/Leistungsverhältnis kann als sehr gut bezeichnet werden. Je nach Version kostet dieses Gerät als Bundle mit allem was gebraucht wird ca. 70€
100€


Tastatur und Maus

Bei der Auswahl der Tastatur haben wir auf die entsprechende Reichweite und Stabilität geachtet. Aus diesem Grund setzen wir auf die Tastaturen der

Fa. Logitech. Wichtig ist hierbei, dass ein Nummernblock vorhanden ist. Der Preis für Tastatur und Maus beträgt ca. 24€

Software

Software

Eingesetzte Software

Kurzbeschreibung

Die Scoreboard Software ist als Client/Server Anwendung entwickelt. Dass heißt, das Display ist der Client und auf dem Server läuft die eigentliche Anwendung.  

Der Focus der Entwicklung liegt auf einer intuitiven Bedienung.

Die zum Betrieb des Scoreboard nötigen Daten sollten komfortabel 

konfigurierbar sein. Ein Import der Daten von verschiedenen Quellen ist vorhanden. Die Anzahl der Scoreboards auf dem Server ist erst einmal unbegrenzt und hängt nur von der Leistungsfähigkeit des Servers ab. Die Anzeige bzw. das Scoreboard wird vom Browser dargestellt. Es werden die Standardbrowser unterstützt. Die Software unterstützt sowohl den POOL Billard Bereich als auch den Karambolage Billard Bereich.


Eingesetzte Technologien

Linux als OS (Operating System)

Java als Serverbasierte Technologie

Javascript als Client Technologie

HTML zur Anzeige und Darstellung

Datenbanken zur Speicherung der Grunddaten und der Konfiguration, sowie zur Speicherung der Wettkampfspiele

Mailservices, zur Versendung der Spielberichte

Netzwerkprotokolle zur Kommunikation zwischen Client und Server

Hier nochmal etwas detaillierter für die Applikation oder Software Entwickler

Java

Entwicklung Java 1.8+ Oracle Java Berkeley DB, Jetty Jackson, Jersey, SLF4J, Logback

Web Entwicklung, JavaScript, AngularJS, pdfjs, JQuery, Bootstrap, FullCalendar

LightGallery

Carambol Scoreboards

Carambol Scoreboards

Classic Carambol Display

Karambolage Billard

Die Anzeigetafeln können in verschiedenen Modi betrieben werden. 

Zum einen ist dies die traditionelle Anzeige, wie sie schon seit Jahrzehnten bekannt, ist zum anderen im Survival Modus. Als weiteren Modus haben wir dann noch eine Fun-Tafel implementiert, die mit 4 Spielern im Team Modus gespielt wird. Also 2 gegen 2. Wir haben dies "Viererzug" genannt.

Der Survival Modus, der in Korea oft und gerne gespielt wird, ist ein Modus bei dem 3-4 Spieler gegeneinander spielen. Dieser Modus wird auf eine bestimmte Zeit gespielt. (2 Runden Hin/Rückrunde) je 45 Minuten) Jeder Spieler bekommt ein "Startguthaben" zum Beispiel 40 Punkte. Diese Punkte muss er bis zum Ablauf der Zeit verteidigen. Wenn ein Spieler eine Karambole löst (Dreiband) bekommt er von jedem "Gegner" einen Punkt. Mit dem Startguthaben von 40 hat er also 43 Punkte und die anderen 39 Punkte. Ein sehr dynamische Spiel bei dem es wirklich spannende Spiele gibt. Und eine schöne Möglichkeit zu dritt zu spielen.

Nach dem Start der Software wird initial die traditionelle Spielart auf der Anzeigetafel geladen. Um zwischen den Anzeige Modi zu wechseln, werden die Tasten [STRG+Pfeil nach oben/unten] gedrückt. Man wechselt also z.B. von Classic zu Survival. Dann von Survival zu Viererzug. Dann wieder von Viererzug zu Classic.

tastatur-strg-pfeil.jpg

image.png

Carambol Scoreboards

Classic Trainingsmodus

image.png

Trainingsbetrieb bedeutet, dass keine Vereinsmannschaften gegeneinander spielen. Es spielen zwei Spieler wie man oben im Bild sehen kann. Diese Spieler werden nun aus einer Liste von Spielern ausgesucht. Diese Liste ist importiert worden.

Ausgesucht werden die Spieler über die Funktionstasten F1 (Spieler 1) und F2 (Spieler 2).

Es öffnet sich ein Dialog indem die entsprechenden Spieler auszuwählen sind.

Durch die Eingabe des Namens oder Teile des Namens schränkt sich die Liste der Spieler entsprechen ein.

Im Bild unten ist dies zu sehen.

Es ist außerdem zu sehen, dass die Spieler aus dem eingestellten Verein (hier RW Krefeld) angezeigt werden. Was der eingestellte Verein bedeutet wird später erklärt. Mit der "Enter" oder auch "Return" Taste werden die ausgesuchten Spieler dann übernommen.

spielerauswahl.jpg

Die Bedienung der Anzeigetafel wird hier in einem Video  (Youtube) erklärt

Carambol Scoreboards

Classic Liga Spielbetrieb

Informationen wie Vereinsname, die Disziplin, die Distanz, die Liga und die Spielnummer benötigt und dargestellt.

Wie werden nun Verein und Spieler ausgesucht?  Nun die Auswahl des Spielers wird in der gleichen Art und Weise vorgenommen wie im Trainingsmodus, also mit F1 und F2. Mit F1 werden alle Spieler Heimspieler angezeigt. F2 zeigt dann alle Spieler des Gastvereins an.

Die nächste Abbildung zeigt ein Spiel der Verbandsliga Dreiband. Des Weiteren sehen wir hier, dass die Bezeichnung
Billard zu Brett + Nr. gewechselt hat. Hier spielt jetzt die Position 3 der Mannschaft.

image.png

Hier ein Video der Anzeige im Wettkampfbetrieb

Carambol Scoreboards

Classic Fun Board

Fun Board 

Das Fun Board ist eine Anzeigetafel für 4 Spieler die im Team gegeneinander antreten. Es geht hier nur um den Spaß und meistens auch um ein Bier

Ausgesucht werden die Spieler über die Funktionstasten F1 bis F4

Die Teams und ihre Bilder werden über die Tasten F6 für Team 1 und F7 für Team 2 bestimmt

vierzug.jpg

Hier ein kurzes Video das die Tafel in Aktion zeigt.

Carambol Scoreboards

Survival Board

Der Survival Modus ist aus Korea zu uns nach Europa gekommen. Im Gegensatz zum traditionellen Karambolage Spiel wird hier nicht auf eine Punktzahl oder Aufnahmen gespielt, es wird eine Spielzeit vereinbart. Zudem können bis zu vier Spieler an einem Spiel teilnehmen. Sieger ist, wer nach Ablauf der vereinbarten Zeit die meisten Punkte hat. Die vereinbarte Spielzeit wird in eine Hinrunde und in eine Rückrunde unterteilt. Die Reihenfolge der Spieler wird durch einen Zufallsgenerator ausgelost. Bei Start der Rückrunde wird die Reihenfolge dann umgedreht. Zu Beginn jeder Runde bekommt jeder Spieler eine Startpunktzahl. Auch diese kann man frei vereinbaren.

Diese Startpunktzahl gilt es zu verteidigen, bzw. auszubauen. Jeder Spieler der eine gültige Karambolage erzielt, bekommt von seinen Mitspielern jeweils einen Punkt.

Bei vier Spielern bekommt also Spieler A für einen gemachten Punkt von den Spielern B, C und D jeweils einen Punkt. Also 3 Punkte. Bei einer Startpunktzahl von 40 steht es dann also A 43, B 39, C 39, D39

Dieser Modus sorgt dafür, dass ein Angriffsspiel belohnt wird. Da man bei vier Spielern nur einen Spieler durch Abwehr unter Kontrolle halten kann, macht es wesentlich mehr Sinn auf Angriff zu spielen und dadurch alle 3 Gegenspieler zu „schädigen“


Die Ursprüngliche Variante aus Korea wird mit einer Shot Clock gespielt. Dazu benötigt man aber einen Schiedsrichter. Ansonsten wird es schnell sehr stressig. Wir haben eine zusätzliche Variante programmiert. Die zur Verfügung stehende Zeit wird dabei auf alle teilnehmenden Spieler verteilt. Bei einer Rundenzeit von 40 Minuten und vier Spielern, bekommt jeder Spieler 10 Minuten.

Zudem gibt es noch die Möglichkeit einem Spieler einen Durchschnitt zuzuordnen.  Dies kann automatisch oder manuell geschehen. Automatisch dann, wenn es einen gültigen Durchschnitt desteilenehmenden Spielers im System gibt. Dies ist der Fall, wenn über die Anzeigetafel Wettkampfspiele erfasst worden sind. Dann existiert eine Tabelle mit den Durchschnitten der Spieler. Hier wird dann auch
eine Rangliste angezeigt. Je nach Durchschnitt wird dann die Startpunktzahl reduziert. Der Spieler mit dem geringsten Durchschnitt bekommt die komplette Startpunktzahl. Alle anderen Spieler eine ihrem Durchschnitt entsprechend reduzierte Punktzahl.

Das Spiel wird mit der Funktionstaste F7 gestartet. Falls nötig kann die komplette Spielzeit mit der Funktionstaste F6 gestoppt und wieder gestartet werden. Wenn das Spiel gestartet wurde läuft die Zeit von Spieler 1 runter. Solange der Focus auf Spieler 1 steht läuft dessen Zeit. Wenn von Spieler 1 auf Spieler 2 gewechselt wird, läuft die Zeit von Spieler 2. Wenn ein Spieler einen Punkt erzielt, kann
dieser mit der „+ Taste“ dem Spieler hinzugefügt werden. Jeder weitere Punkt wird mit der„ „+ Taste“ hinzugefügt. Gleichzeitig wird den anderen Spielern, solange sie sich noch im Spiel befinden, jeweils einen Punkt abgezogen. Wenn ein Spieler keine Punkte oder keine Zeit mehr zur Verfügung hat, ist er aus dem Spiel. Allerdings erst dann, wenn die komplette Aufnahme zu Ende gespielt worden ist.

Nachfolgendes Video zeigt das Survival Board in Aktion

Pool Scoreboards

Pool Scoreboards

Pool Training

Die Programmierung der Pool Boards war eine kleine Herausforderung. Im Gegensatz zu den Carambol Boards, gibt es hier verschiedene Spielarten und entsprechend viele Regeln.

Die Bedienung der Boards ist im Training wie im Ligabetrieb gleich. Allerdings werden im Liga Betrieb zusätzliche Informationen auf den Boards dargestellt. In den folgenden Videos wird dies deutlich.

Pool Training:

Pool Scoreboards

Pool Liga

Liga Modus:

 

Scoreboard Admin

Scoreboard Software Paket erstellen -- Quick View

Scoreboard Software Paket erstellen -- Quick View

Short Run Book

# Runbook: Neues Scoreboard-Paket bauen und ausrollen

Kurzfassung. Ausführliche Erklärungen stehen im
ausführlichen Runbook.

---

Teil 1 – Auf dem Packagebuilder

1. Quellverzeichnis aktualisieren**

Den Stand aus `/opt/scoreboard` nach `source` übernehmen –
so wie bisher, zum Beispiel:

```bash
sudo rsync -a --delete /opt/scoreboard/ \
    /opt/package-build/scoreboard-system/source/
```

2. Versionsnummer hochsetzen**

```bash
sudo nano /opt/package-build/scoreboard-system/build.conf
```

Nur diese eine Zeile ändern:

```bash
PACKAGE_VERSION="3.0.7"
```

Die Nummer muss größer sein als die installierte, sonst
verweigert dpkg das Update.

3. Bauen**

```bash
cd /opt/package-build/scoreboard-system
sudo ./buildpackage.sh
```

Das Skript prüft am Ende selbst, ob im fertigen Paket die
Version aus der `build.conf` steht, und bricht sonst ab.

4. Ergebnis**

```
dist/scoreboard-system_3.0.7_arm64.deb
dist/install-package.sh
```

---

Teil 2 – Auf dem Zielrechner

5. Beide Dateien hinüberkopieren**

```bash
scp dist/scoreboard-system_3.0.7_arm64.deb \
    dist/install-package.sh \
    benutzer@zielrechner:/tmp/
```

6. Installieren**

```bash
cd /tmp
sudo ./install-package.sh scoreboard-system_3.0.7_arm64.deb
```

Nicht `dpkg -i` direkt verwenden.

7. Kontrollieren**

```bash
dpkg -s scoreboard-system | grep ^Version
systemctl status scoreboard
```

---

Wenn etwas schiefgeht

`install-package.sh` legt vor einem riskanten Update eine
Sicherung an:

```bash
ls -lt /var/backups/scoreboard-system-*.tar.gz
```

Zurückspielen:

```bash
sudo systemctl stop scoreboard
sudo tar xzf /var/backups/scoreboard-system-3.0.6-<zeitstempel>.tar.gz -C /opt
sudo systemctl start scoreboard
```

---

Merksätze

- Die Versionsnummer steht **nur** in der `build.conf`.
- Gebaut wird immer aus `source`, nie direkt aus `/opt/scoreboard`.
- Auf dem Zielrechner immer `install-package.sh`, nie `dpkg -i`.
- `/opt/scoreboard` wird beim Bauen nicht angefasst und beim
  Update nicht gelöscht.

Runbook: Scoreboard-System Paketbau – ausführlich


Runbook: Scoreboard-System Paketbau – ausführlich

1. Grundidee


Die Anwendung liegt auf dem Zielrechner unter `/opt/scoreboard`.
Dort stehen zwei Sorten von Dingen nebeneinander:

- **Programmteile**, die bei jedem Update ersetzt werden sollen
- **Nutzdaten**, die ein Update auf keinen Fall anfassen darf

Deshalb wird jedes Verzeichnis genau einer von vier Kategorien
zugeordnet. Die Zuordnung steht in der `build.conf`:

| Kategorie | Verzeichnisse | Bedeutung |
|---|---|---|
| `REPLACE_DIRS` | `lib`, `etc`, `web` | Gehören dem Paket. dpkg ersetzt sie bei jedem Update. |
| `INITIAL_COPY_DIRS` | `data`, `auto-sync`, `scripte`, `tokens` | Werden nur bei der Erstinstallation angelegt. Ein Update lässt sie in Ruhe. |
| `CREATE_ONLY_DIRS` | `doc`, `temp`, `log`, `backup` | Werden nur als leeres Verzeichnis angelegt. |
| `APPLICATION_DIRS` | `db` | Legt die Anwendung selbst an. Das Paket fasst sie nicht an. |

Technisch ist der Unterschied wichtig: Nur was aus `REPLACE_DIRS`
kommt, landet im Paket unter `/opt/scoreboard` und gehört damit
dpkg. Alles aus `INITIAL_COPY_DIRS` wird nach
`/usr/share/scoreboard-system/initial/` gelegt und erst von
`postinstall.sh` an seinen Platz kopiert – und zwar nur, wenn dort
noch nichts steht.

**Merksatz:** Was dpkg gehört, wird ersetzt. Was dpkg nicht
gehört, bleibt.

---

Runbook: Scoreboard-System Paketbau – ausführlich

2. Verzeichnisse auf dem Packagebuilder

 

Alles unterhalb von `/opt/package-build/scoreboard-system/`:

```
build.conf              Zentrale Konfiguration
nfpm.yaml               Paketbeschreibung für nfpm
preparepackage.sh       Baut package-root zusammen
buildpackage.sh         Ruft preparepackage.sh + nfpm auf
install-package.sh      Für den Zielrechner, nicht für hier
LIESMICH.md             Kurzübersicht

scripts/                Maintainer-Skripte fürs Paket
  preinstall.sh
  postinstall.sh
  preremove.sh
  postremove.sh

source/                 Kopie des Live-Systems, Quelle des Builds
systemd/                scoreboard.service
templates/              (derzeit ungenutzt, siehe Abschnitt 9)

package-root/           wird bei jedem Build neu erzeugt
dist/                   fertige Pakete
```

`source/` pflegst du aus `/opt/scoreboard`. Gebaut wird
ausschließlich aus `source/` – `preparepackage.sh` bricht ab,
falls `SOURCE_DIR` direkt auf `/opt/scoreboard` zeigt.

Runbook: Scoreboard-System Paketbau – ausführlich

3. Die Dateien im Einzelnen

3.1 `build.conf`

Liegt auf dem Builder und wird zusätzlich ins Paket kopiert, nach
`/usr/share/scoreboard-system/build.conf`. Dadurch können die
Maintainer-Skripte auf dem Zielrechner dieselben Werte lesen –
Pfade und Listen stehen nur an dieser einen Stelle.

Die Datei ist von `set -a` … `set +a` umschlossen. Dadurch werden
alle Zuweisungen exportiert, sodass nfpm als eigener Prozess sie
in der `nfpm.yaml` als `${...}` einsetzen kann. Ohne das würde
`source build.conf` nur Shell-Variablen setzen, die nfpm nie
sieht.

Arrays exportiert `set -a` nicht – Bash kann das nicht. Das ist
in Ordnung, denn Arrays braucht nur, wer die Datei sourct.

Aktiv genutzte Werte:

| Variable | Wird gelesen von |
|---|---|
| `PACKAGE_NAME` | `nfpm.yaml`, `preparepackage.sh` |
| `PACKAGE_VERSION` | `nfpm.yaml`, `buildpackage.sh` |
| `PACKAGE_ARCH` | `nfpm.yaml`, `buildpackage.sh` |
| `JAVA_VERSION`, `JAVA_PACKAGE` | `postinstall.sh`, `nfpm.yaml` |
| `INSTALL_PATH` | alle Maintainer-Skripte, `preparepackage.sh` |
| `SERVICE_NAME` | `postinstall.sh`, `preremove.sh`, `preparepackage.sh` |
| `BUILD_ROOT`, `SOURCE_DIR`, `PACKAGE_ROOT` | `preparepackage.sh`, `buildpackage.sh` |
| die vier Verzeichnislisten | `preparepackage.sh`, `postinstall.sh` |
| `TEMPLATE_FILES` | `preparepackage.sh`, `postinstall.sh` |

 

3.2 `preparepackage.sh`

Läuft auf dem Builder. Baut `package-root/` neu auf:

1. Prüft alle Pfade, bevor irgendetwas gelöscht wird
2. Löscht `package-root/` und legt es neu an
3. `REPLACE_DIRS` → `package-root/opt/scoreboard/`
4. `INITIAL_COPY_DIRS` → `package-root/usr/share/scoreboard-system/initial/`
5. `TEMPLATE_FILES` → `package-root/usr/share/scoreboard-system/templates/`
6. `systemd/scoreboard.service` → `.../systemd/`
7. Die vier Maintainer-Skripte → `.../scripts/`
8. `build.conf` → `.../`

Die Prüfungen in Schritt 1 sind der Grund, warum das Skript
gefahrlos ein `rm -rf` enthalten darf:

- Alle Variablen müssen gesetzt sein
- `PACKAGE_ROOT` muss tatsächlich unterhalb von `BUILD_ROOT` liegen
- Weder `PACKAGE_ROOT` noch `BUILD_ROOT` dürfen im Live-Baum liegen
- `SOURCE_DIR` darf nicht `/opt/scoreboard` selbst sein
- Symlinks werden vorher aufgelöst, damit sie die Prüfung nicht
  aushebeln

Zusätzlich wird geprüft, ob bei jedem Maintainer-Skript der
Shebang in Zeile 1 steht. Steht davor ein Kommentar, kann der
Kernel die Datei nicht als Bash starten und dpkg fällt still auf
`/bin/sh` zurück.

3.3 `nfpm.yaml`

Beschreibt das Paket. `name`, `version`, `arch` und `depends`
kommen per `${...}` aus der Umgebung, also aus der `build.conf`.

Die Pfade unter `contents:` sind bewusst **relativ**. Stünde dort
`${PACKAGE_ROOT}/opt` und die Umgebung fehlte, würde nfpm das zu
`/opt` auflösen und das echte `/opt` der Maschine einpacken.
Deshalb muss nfpm aus `BUILD_ROOT` heraus aufgerufen werden –
`buildpackage.sh` erledigt das.

Zwei Einträge:

| Aus `package-root` | Wird zu |
|---|---|
| `opt/` | `/opt` (also `/opt/scoreboard/{etc,lib,web}`) |
| `usr/share/scoreboard-system/` | `/usr/share/scoreboard-system` |

3.4 `buildpackage.sh`

Klammer um den ganzen Build:

1. `build.conf` sourcen
2. `preparepackage.sh` aufrufen
3. Nach `BUILD_ROOT` wechseln und nfpm aufrufen
4. Prüfen, ob das erzeugte `.deb` wirklich die erwartete Version
   meldet, sonst abbrechen
5. `install-package.sh` mit nach `dist/` legen

Schritt 4 ist die Kontrolle gegen genau den Fehler, mit dem das
alles angefangen hat: eine Versionsnummer in der `build.conf`,
die nicht im Paket ankommt.

Wenn du ein eigenes Build-Skript verwenden willst, muss es
mindestens die Schritte 1 bis 3 in dieser Reihenfolge tun.

3.5 `scripts/preinstall.sh` → `preinst`

Läuft auf dem Zielrechner **vor** dem Entpacken.

Einziger Zweck ist eine einmalige Migration. Früher gehörte
`tokens` zum Paket. Jetzt nicht mehr – und beim Upgrade auf die
erste Version ohne diesen Eintrag betrachtet dpkg das Verzeichnis
als verwaiste Paketdatei und löscht es, bevor `postinstall.sh`
überhaupt startet.

Das Skript legt deshalb vorher unter
`/opt/scoreboard/.pre-upgrade/` eine Sicherung an. Sie arbeitet
mit Hardlinks und kostet praktisch keinen Platz.
`postinstall.sh` holt sie zurück und räumt sie weg.

Sobald alle Zielrechner über diese Version hinaus sind, kann die
Liste `MIGRATE_DIRS` oben im Skript geleert werden.

3.6 `scripts/postinstall.sh` → `postinst`

Läuft **nach** dem Entpacken und macht die eigentliche
Einrichtung:

1. Prüft, ob `$1` überhaupt `configure` ist
2. Lädt `/usr/share/scoreboard-system/build.conf`
3. Prüft, ob Java vorhanden und alt genug ist
4. Legt die `CREATE_ONLY_DIRS` an
5. Legt die `INITIAL_COPY_DIRS` an – aber nur, wenn sie fehlen.
   Reihenfolge: vorhanden → nichts tun; Sicherung aus `preinst`
   vorhanden → zurückholen; sonst → Seed aus dem Paket
6. Kopiert fehlende `TEMPLATE_FILES`, vorhandene bleiben
7. Setzt die Ausführungsrechte auf die Start-/Stopp-Skripte
8. Installiert die systemd-Unit und startet den Dienst

Die `systemctl`-Aufrufe laufen nur, wenn systemd wirklich aktiv
ist (`/run/systemd/system` existiert). In einem Container oder
chroot würde sonst die ganze Installation daran scheitern.

3.7 `scripts/preremove.sh` → `prerm`

Läuft **vor** dem Entfernen von Dateien – und zwar auch bei
jedem Upgrade. dpkg teilt den Grund über `$1` mit:

| `$1` | Verhalten |
|---|---|
| `upgrade` | Nur den Dienst stoppen. Sonst nichts. |
| `remove` | Dienst stoppen und deaktivieren. |
| alles andere | Nichts tun. |

Gelöscht wird in keinem Fall etwas. Die Paketdateien räumt dpkg
selbst ab; alles andere sind Nutzdaten und bleiben stehen.

Das war die Stelle mit dem ursprünglichen Datenverlust: Das alte
Skript wertete `$1` nicht aus und führte deshalb bei jedem
Upgrade `rm -rf /opt/scoreboard` aus.

3.8 `scripts/postremove.sh` → `postrm`

Läuft **nach** dem Entfernen. Entfernt die systemd-Unit aus
`/etc/systemd/system/` und lädt systemd neu – aber nur bei
`remove` und `purge`. Bei `upgrade` muss die Unit liegen bleiben.

`/opt/scoreboard` wird auch bei `purge` nicht gelöscht, sondern
nur ein Hinweis ausgegeben. Wer das anders möchte, findet die
Stelle im Skript kommentiert.

Dieses Skript lädt die `build.conf` bewusst nicht: Zum Zeitpunkt
von `postrm` hat dpkg die Paketdateien schon entfernt.

3.9 `install-package.sh`

Läuft auf dem **Zielrechner**, nicht auf dem Builder.

Der Grund für seine Existenz: Bei einem Upgrade führt dpkg das
`prerm` des **bereits installierten** Pakets aus, nicht das aus
dem neuen. Auf einer Maschine, auf der noch eine Version mit dem
alten `preremove.sh` läuft, liefe also weiterhin
`rm -rf /opt/scoreboard`, egal wie korrekt das neue Paket ist.
Aus dem neuen Paket heraus lässt sich das nicht verhindern, weil
zu diesem Zeitpunkt noch keine Zeile daraus gelaufen ist.

Ablauf:

1. Ist überhaupt etwas installiert? Wenn nein: direkt `dpkg -i`
2. Enthält das installierte `prerm` noch die gefährliche Zeile?
   Wenn nein: direkt `dpkg -i`
3. Wenn ja: Sicherung nach `/var/backups/` anlegen
4. Das `prerm` gegen die Fassung aus dem neuen `.deb` tauschen,
   die alte als `.vor-update` daneben legen
5. `dpkg -i`

Ab dem zweiten Update ist es ein reiner Durchläufer. Du kannst es
deshalb dauerhaft verwenden und musst dir nicht merken, welche
Maschine schon umgestellt ist.

---

Runbook: Scoreboard-System Paketbau – ausführlich

4. Was wo im Paket landet


| Im Paket | Auf dem Zielrechner | Wer kopiert |
|---|---|---|
| `opt/scoreboard/{etc,lib,web}` | `/opt/scoreboard/…` | dpkg |
| `usr/share/scoreboard-system/initial/` | nach `/opt/scoreboard/` | `postinstall.sh`, nur wenn fehlend |
| `usr/share/scoreboard-system/templates/` | nach `/opt/scoreboard/` | `postinstall.sh`, nur wenn fehlend |
| `usr/share/scoreboard-system/systemd/` | `/etc/systemd/system/` | `postinstall.sh` |
| `usr/share/scoreboard-system/build.conf` | bleibt liegen | – |

---

Runbook: Scoreboard-System Paketbau – ausführlich

5. Der dpkg-Ablauf

Erstinstallation

```
preinst  install        -> beendet sich sofort
Dateien entpacken
postinst configure      -> Verzeichnisse, Templates, Service
```

Upgrade

```
altes prerm   upgrade <neu>   -> Dienst stoppen
neues preinst upgrade <alt>   -> tokens sichern
Dateien entpacken, verwaiste Paketdateien entfernen
altes postrm  upgrade <neu>   -> nichts tun
neues postinst configure <alt> -> einrichten, Dienst starten
```

Wichtig: Schritt 1 kommt aus dem **alten** Paket. Deshalb
`install-package.sh`.

Entfernen (`dpkg -r`)

```
prerm  remove   -> Dienst stoppen und deaktivieren
Paketdateien entfernen (etc, lib, web)
postrm remove   -> systemd-Unit entfernen
```

Nutzdaten bleiben liegen.

Restlos entfernen (`dpkg -P`)

Wie oben, danach `postrm purge`. `/opt/scoreboard` bleibt trotzdem
stehen, es wird nur ein Hinweis ausgegeben.

---

Runbook: Scoreboard-System Paketbau – ausführlich

6. Was muss ich wann anpassen?


| Anlass | Was ändern |
|---|---|
| Neue Programmversion | `PACKAGE_VERSION` in `build.conf` |
| Neues Verzeichnis in der Anwendung | Eintrag in die passende Liste in `build.conf` |
| Neue Template-Datei | `TEMPLATE_FILES` in `build.conf` |
| Andere Java-Version | `JAVA_VERSION` **und** `JAVA_PACKAGE` in `build.conf` |
| Anderer Zielrechnertyp | `PACKAGE_ARCH` in `build.conf` |
| Anderer Installationspfad | `INSTALL_PATH` in `build.conf` |
| systemd-Unit geändert | `systemd/scoreboard.service` auf dem Builder |

In den Skripten selbst musst du im Normalfall nichts anfassen.

Ein Verzeichnis von `REPLACE_DIRS` nach `INITIAL_COPY_DIRS` zu
verschieben ist der einzige Sonderfall: Dann muss es einmalig in
`MIGRATE_DIRS` in `preinstall.sh` eingetragen werden, sonst wirft
dpkg beim Umstellungs-Update den vorhandenen Inhalt weg.

---

Runbook: Scoreboard-System Paketbau – ausführlich

7. Kontrollbefehle


Auf dem Builder, am fertigen Paket:

```bash
dpkg-deb -I dist/scoreboard-system_3.0.7_arm64.deb    # Kopfdaten
dpkg-deb -c dist/scoreboard-system_3.0.7_arm64.deb    # Inhalt
```

Auf dem Zielrechner:

```bash
dpkg -s scoreboard-system          # Status und Version
dpkg -L scoreboard-system          # welche Dateien gehören dem Paket
systemctl status scoreboard
journalctl -u scoreboard -n 50
```

`dpkg -L` ist die ehrlichste Auskunft darüber, was ein Update
ersetzen wird. Was dort nicht auftaucht, fasst dpkg nicht an.

---

Runbook: Scoreboard-System Paketbau – ausführlich

8. Fehlersuche


| Meldung | Ursache |
|---|---|
| `FEHLER: PACKAGE_NAME ist nicht gesetzt` | `build.conf` wurde nicht geladen |
| `FEHLER: PACKAGE_ROOT liegt nicht unterhalb von BUILD_ROOT` | `BUILD_ROOT` in `build.conf` passt nicht zum echten Pfad |
| `FEHLER: SOURCE_DIR zeigt direkt auf /opt/scoreboard` | Es soll aus `source/` gebaut werden, nicht aus dem Live-Baum |
| `FEHLER: … In Zeile 1 fehlt der Shebang` | Vor `#!/bin/bash` steht ein Kommentar |
| `FEHLER: Paket meldet Version X, erwartet war Y` | nfpm hat die `build.conf` nicht gesehen |
| nfpm: `version is required` | `source build.conf` fehlt vor dem nfpm-Aufruf |
| dpkg: Downgrade-Warnung | Versionsnummer wurde nicht erhöht |
| Installation bricht mit Java-Meldung ab | `openjdk-17-jre` fehlt auf dem Zielrechner |

Bei einem missglückten Update liegt die Sicherung hier:

```bash
ls -lt /var/backups/scoreboard-system-*.tar.gz
sudo systemctl stop scoreboard
sudo tar xzf /var/backups/scoreboard-system-<version>-<zeit>.tar.gz -C /opt
sudo systemctl start scoreboard

Runbook: Scoreboard-System Paketbau – ausführlich

9. Offene Punkte


**`doc` liefert keinen Inhalt aus.** Das Verzeichnis steht in
`CREATE_ONLY_DIRS`, hat im Live-System aber Inhalt
(`Einzelmeisterschaften/KEM`, `LEM`). Auf dem Zielrechner entsteht
nur ein leeres Verzeichnis. Soll der Inhalt mit, gehört `doc` nach
`INITIAL_COPY_DIRS`.

**`data` bringt echte Daten mit.** Der Seed im Paket enthält die
Importdaten aus dem Live-System, inklusive der Jahrgänge ab 2017.
Als Startbestand für eine neue Maschine ist das gewollt – nur
sollte man wissen, dass diese Daten im Paket liegen.

**Ungenutzte Werte in `build.conf`.** `PRODUCT_NAME`,
`TEMPLATE_DIR`, `SCRIPT_DIR`, `RUNTIME_FILES`, `IGNORE_FILES` und
`APPLICATION_DIRS` werden von keinem Skript gelesen. Sie stören
nicht, dokumentieren aber Absichten, die nirgends umgesetzt sind.
`TEMPLATE_DIR` ist dabei besonders irreführend: Die Templates
werden aus `source/` geholt, nicht aus `templates/`.

**`SCRIPT_DIR` ist ein Name mit Vorgeschichte.** Die `build.conf`
belegt ihn mit `${BUILD_ROOT}/scripts`. Skripte, die die Datei
sourcen, dürfen den Namen deshalb nicht für ihr eigenes
Verzeichnis verwenden – `preparepackage.sh` benutzt dafür
`SELF_DIR`.