Files
d4rkbot/docs/ls-feed.md
T
D4rkst3randClaude Opus 5 1ef06f747c
Deploy / check (push) Has been cancelled
Deploy / deploy (push) Has been cancelled
Modliste: melden was sich geaendert hat, einzeln laden
Bisher gab es fuer Mods nur einen Link auf das Sammelpaket des Hosters — vier
Gigabyte, auch wenn sich zwei Dateien geaendert haben. Jetzt liest der Bot die
Modliste aus der Statusabfrage, die er ohnehin holt, und meldet im Kanal, was
neu, aktualisiert oder entfernt wurde. Beim ersten Durchlauf bleibt es still,
sonst kaeme eine Meldung ueber 110 "neue" Mods.

Geaendert heisst: andere Version oder anderer Hash. Beides einzeln reicht
nicht — manche Modder bessern nach, ohne die Version zu erhoehen.

Dazu die Seite /mods/<server>: jeder Mod mit Version und Groesse, Suche ueber
Titel, Dateiname und Autor, und ein Download je Zeile. Der laeuft durch den
Bot, damit der Zugangs-Code des Spielservers nicht in einem Link landet — mit
dem Code liesse sich auch der ganze Spielstand lesen. Angefragt wird nur, was
wirklich in der Modliste steht; ueber den Dateinamen kommt man an nichts
anderes heran.

Groessen lernt der Bot beim Weiterleiten. Der Spielserver beantwortet kein
HEAD (501) und ignoriert Range, es gibt also keinen billigen Weg, sie vorher
zu erfahren — und 4 GB nur fuers Anzeigen zu holen waere keiner.

Nebenbei gefunden und behoben: 'server.mods' und 'commands.tag' gab es je
zweimal in den Sprachdateien. Der spaetere Eintrag gewinnt still, deshalb
stand auf dem neuen Knopf woertlich "%s Mods (110)" und auf der Befehlsseite
die Beschreibung von /tag statt der Kopfzeile.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-11 11:22:55 +02:00

216 lines
10 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# Was ein LS-Server über sich verrät
Aufgeschrieben, damit die Frage „können wir nicht auch …" nicht dreimal
gestellt wird. Alles hier ist an einem echten FS25-Server (1.21.0.0) geprüft,
nicht aus der Dokumentation abgeschrieben — die schweigt zu den Einzelheiten.
## Die Endpunkte
Alle brauchen `?code=<Zugangs-Code>`. Der Code steht in der Abfrage-URL, die
der Hoster ausgibt; der Port ist **nicht** immer 8080.
| Endpunkt | Grösse | Inhalt |
|---|---|---|
| `dedicated-server-stats.xml` | ~49 KB | Server, Spieler, Mods, Parzellen, Felder, Fahrzeuge |
| `dedicated-server-stats-map.jpg` | 51 KB 1,5 MB | Luftbild, `size=` 512…4096, `quality=` 1…100 |
| `dedicated-server-savegame.html?file=careerSavegame` | ~20 KB | Einstellungen, Geld, Spielzeit, Modliste |
| `dedicated-server-savegame.html?file=vehicles` | ~39 KB | jedes Fahrzeug mit Preis, `farmId`, Betriebsstunden |
| `dedicated-server-savegame.html?file=economy` | ~305 KB | Preiskurve je Fruchtart über zwölf Perioden |
| `mods/<name>.zip` | 0,07 1381 MB | die Mod-Datei selbst, **nicht** unter `/feed` |
Andere Dateinamen antworten mit `204` und leerem Rumpf. Geprüft und **nicht**
verfügbar: `farms`, `farmland`, `farmlands`, `fields`, `field`, `items`,
`placeables`, `environment`, `sales`, `players`, `production`, `productions`,
`missions`, `densityMapHeight`, `treePlants`, `growth`, `weeds`, `fillTypes`.
## Was in der Statusabfrage steht
```xml
<Server version="1.21.0.0" mapSize="4096" mapName="…" dayTime="46057948">
<Slots capacity="4" numUsed="3">
<Player isUsed="true" isAdmin="true" uptime="128" x="…" y="…" z="…">Name</Player>
</Slots>
<Mods> <Mod name="…" author="…" version="…">Titel</Mod> <!-- 110x -->
<Farmlands><Farmland id="1" owner="0" area="22.3" price="1340352" x="…" z="…"/>
<Fields> <Field id="1" x="…" z="…" isOwned="false"/>
<Vehicles> <Vehicle name="…" type="…" x="…" z="…" fillTypes="…" fillLevels="…" isAIActive="false"/>
```
`owner` ist die Hof-**Nummer**, nicht der Spielername. Welcher Spieler zu
welchem Hof gehört, steht in keinem Endpunkt — deshalb die Zuordnung im Panel.
## Koordinaten
**x nach rechts, z nach unten, und die Welt läuft von `-mapSize` bis
`+mapSize`** — also doppelt so weit, wie der Name vermuten lässt.
Das ist die Stelle, an der man zweimal falsch abbiegt:
`-mapSize/2 … +mapSize/2` wirkt naheliegend und sieht auf den ersten Blick
sogar richtig aus, weil die Parzellen zueinander stimmen. Tatsächlich zieht es
jede Parzelle um Faktor zwei vom Mittelpunkt weg, und am Ende liegen die
Felder im Wald.
Ein Test, der die Koordinaten auf ihre eigene Bounding-Box normiert, merkt das
**nicht** — er prüft nur die Anordnung und besteht bei jedem Maßstab.
### Woran man es festmacht
Viele Karten tragen eingebrannte Marken `H1…H8` mit einer Legende, welche
Parzelle das ist (`131 H1`, `132 H2`, …). Das sind acht kostenlose Passpunkte.
Mit dem richtigen Maßstab liegen die Parzellen-Schwerpunkte **unter 15 px** von
ihrer Marke entfernt, gemessen auf einem 1024er Bild; mit dem falschen rund
200 px. Der Unterschied ist so deutlich, dass eine einzige Prüfung reicht.
## Mods
Die Statusabfrage listet jeden Mod mit `name`, `author`, `version` und `hash`.
Der Name ist der Dateiname ohne `.zip` — damit lässt sich jede Datei einzeln
laden, ohne das Sammelpaket über alles zu ziehen. Auf dem Testserver: **109 von
110 Mods abrufbar, zusammen 4,06 GB.**
Der `hash` ist GIANTS' eigener Fingerabdruck. Was er ist, steht nirgends, und
gemessen ist er **weder** die MD5 der ausgelieferten Zip **noch** die der
`modDesc.xml` darin. Als Prüfsumme für heruntergeladene Dateien taugt er also
nicht — als Melder „hier hat sich etwas geändert" schon.
Was beim Bauen wichtig ist:
| | |
|---|---|
| `HEAD` | **501** — nicht unterstützt, die Grösse gibt es nur per GET |
| `Range` | wird ignoriert (200 statt 206) — **kein Fortsetzen** |
| `Last-Modified` | vorhanden, aber der Zeitpunkt des Hochladens, nicht der Version |
| `ETag` | keiner |
| Zwei Abrufe | byte-gleich — statische Dateien, kein Neupacken |
Kein Fortsetzen ist der wunde Punkt: die grösste Datei auf dem Testserver ist
**1,38 GB**. Bricht der Download ab, fängt er von vorn an. Wer das besser
machen will, muss selbst zwischenspeichern.
**Gekaufte Inhalte antworten mit 403.** `pdlc_strawHarvestPack` ist das
einzige der 110, das der Server nicht herausgibt — richtig so, es ist bezahltes
DLC und kommt über Steam.
**Der Dateiname muss nichts mit dem Mod zu tun haben.** Auf dem Testserver
heisst ein Eintrag `all_mods_download`, ist aber „Bale Autoload Pack" — da hat
jemand den Sammel-Download des Hosters in den Mod-Ordner gelegt. Das Spiel
lädt ihn anstandslos, aber jeder Abgleich über den Namen stolpert darüber.
## Was in der Fahrzeugdatei steht
`file=vehicles` ist die ergiebigste der drei Savegame-Dateien. Je `<vehicle>`:
| Attribut | Bedeutung |
|---|---|
| `filename` | Pfad zum Modell — `data/vehicles/newHolland/t7/t7.xml` |
| `farmId` | wem es gehört |
| `propertyState` | `OWNED` oder gemietet |
| `price` | Zeitwert |
| `age` | Alter in Spieltagen |
| `operatingTime` | Betriebszeit in **Sekunden** |
| `modName` | gesetzt, wenn es aus einem Mod stammt |
In Kindelementen: `<wearable damage>` (Verschleiss, 0…1), `<dirtNode amount>`
(Schmutz, 0…1) und `<unit fillType fillLevel>` je Tank oder Ladefläche.
Zwei Fallen dabei:
- **Blockweise lesen, nicht attributweise.** Verschleiss und Schmutz stehen in
Kindelementen. Wer im ganzen Dokument nach `damage="…"` sucht, hängt sie
irgendeinem Fahrzeug an.
- **Gemietete Missionsfahrzeuge zählen nicht zum Hof.** Sie tauchen nur auf,
solange der Auftrag läuft, und verfälschen Anzahl und Wert. Auf dem
Testserver war das rund ein Drittel aller Einträge.
Nicht drin steht die **Tankgrösse**. Die Datei nennt Liter, keine Prozent —
„276 l Diesel" geht, „38 % voll" nicht.
## Was es nicht gibt
**Tierställe und ihr Zustand.** Ställe sind Placeables, und `placeables`
antwortet mit `204`. In den drei ausgelieferten Dateien kommt kein einziges
Tier vor: jeder Treffer auf `cow`, `pig` oder `animal` ist ein **Modname**
(„Old Cowshed", „Modern Pigsty"), kein Bestand. Damit gibt es weder Anzahl noch
Produktivität, Futter oder Sauberkeit.
**Welche Frucht auf einem Feld steht und wie weit sie ist.** Das liegt in
`densityMap_fruits.gdm`, einer Binärdatei ohne öffentliche Spezifikation, die
nur im kompletten Savegame-ZIP steckt — und das lädt man über die
Admin-Oberfläche, nicht über den Code.
Daran ändert auch kein eigener Mod etwas. Die Lua-Umgebung des Spiels läuft in
einer Sandbox — GIANTS schreibt in der Skript-Dokumentation, allgemeine
System-Ein- und -Ausgabe sei abgeschaltet. Ein Mod kann also weder eine Datei
schreiben, die der Feed ausliefert, noch selbst etwas an den Bot schicken.
Wer wirklich an die Rohdaten will, braucht **keinen Mod, sondern FTP**. Das ist
der Weg von [FS19_Webstats](https://github.com/J0hnHawk/FS19_Webstats): die
Web-API sei nicht erweitert worden, der einzige Weg an alle Daten führe über
FTP-Zugriff auf den Spielstand-Ordner. Damit läge das komplette Savegame vor,
`densityMap_fruits.gdm` inklusive — nur eben weiterhin ohne Spezifikation für
dessen Inhalt.
[Crop Growth Stage
Info](https://www.farming-simulator.com/mod.php?mod_id=232903) und [Additional
Field Info](https://github.com/yumi-modding/FS25_additionalFieldInfo) berechnen
genau diese Werte, sind aber reine HUD-Overlays: Lua, `modDesc.xml`, ein Icon —
nichts, was eine Datei schreibt oder einen Port öffnet.
Bestätigt durch die Konkurrenz. Keiner dieser Bots zeigt Feldfrüchte, obwohl
einer davon Geld kostet:
- [GamePuls](https://bot.dein-gamepuls.de) — kommerziell, mit Premium-Stufe
- [Farm Sim Game](https://www.farmsimgame.com/) — gehostet
- [IceEagle132](https://github.com/IceEagle132/Farming-Simulator-25-Discord-Bot)
- [DJWoodZ](https://github.com/DJWoodZ/Farming-Simulator-Discord-Bot)
- [cloudmaker97](https://github.com/cloudmaker97/FS25-Discord-Bot)
**Der aktuelle Monat.** Er steht in keinem Endpunkt.
`playTime` aus dem Savegame taugt nicht dafür: die Datei wird nur beim
Autosave geschrieben und hinkt bis zu einer Viertelstunde hinterher. Über zwei
Stunden gemessen stieg `playTime` um 50, während die Spieluhr 18,7 Stunden
weiterlief — daraus lässt sich nichts rechnen.
`dayTime` in der Statusabfrage zählt Millisekunden seit Tagesbeginn und ist
die brauchbarste Uhr im Feed — aber **auch nur ein Schnappschuss**. Der Server
schreibt das XML im „Web API Interval" neu, dazwischen steht derselbe Wert.
Gemessen: fünf Abrufe über zwei Minuten, ein Spieler online, alle identisch
(`dayTime=37028620`).
Bei `plannedDaysPerPeriod = 1` ist jeder Rücksprung um Mitternacht ein
Monatswechsel. Also: Monat einmal im Panel eintragen, danach zählt der Bot an
dieser Uhr selbst weiter. Steht der Server leer, steht auch die Spielzeit — und
der Monat bleibt richtigerweise stehen.
**Ein Rücksprung ist nicht automatisch Mitternacht.** Wer einfach auf
`jetzt < vorher` prüft, ruft bei jeder kaputten Antwort einen neuen Monat aus:
eine fehlende `dayTime` wird schnell zu `0`, und `0` ist kleiner als jeder
Vortagswert. Ein echter Wechsel geht vom späten Abend in den frühen Morgen —
also beide Werte prüfen, nicht nur ihre Differenz.
Bleibt ein Restrisiko: ein Serverneustart lädt den Spielstand und setzt die Uhr
auf den gespeicherten Zeitpunkt zurück. Fällt der zufällig in den frühen
Morgen, während vorher später Abend gemessen wurde, zählt der Bot einen Monat
zu viel. Dann im Panel den richtigen eintragen — von da an läuft es weiter.
Die zwölf Perioden sind übrigens schlicht Monate: `EARLY_SPRING` ist März,
`EARLY_AUTUMN` September.
**`growthMode`** sagt, ob überhaupt etwas wächst (`2` = pausiert). Auf die
Preise hat das keinen Einfluss — die Kurve hängt am Monat, nicht am Wachstum.
## Fallstricke
- **Parzellen haben nur Schwerpunkt und Fläche, keine Umrisse.** Die Karte
zeichnet flächengleiche Kreise. Bei langen schmalen Feldern sieht man das.
- **Die Modliste steht vor den Parzellen.** Wer nur die ersten Zeilen
anschaut, hält den Feed für eine reine Statusabfrage.
- **Spargel sprengt jede Preisliste.** Er schwankt zwischen 26 und 7540 € und
verdrängt nach Schwankungsbreite sortiert jede echte Feldfrucht. Deshalb
arbeitet die Preisliste mit einer Auswahl statt mit einem Ranking.
- **Das Luftbild als PNG ist zehnmal so gross wie nötig** — 2,5 MB statt
250 KB. Es ist ein Foto, also JPEG.