Skip to content

Commit 0cb785c

Browse files
Create developer guide wiki page
1 parent 7d3879e commit 0cb785c

1 file changed

Lines changed: 321 additions & 0 deletions

File tree

wiki/Developer-Guide.md

Lines changed: 321 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,321 @@
1+
# Developer Guide
2+
3+
This guide is for contributors and maintainers of **FarmHub**. It covers the architecture, codebase structure, and how to work on the project.
4+
5+
## Quick Start for Developers
6+
7+
### Current Versions
8+
- **App:** 3.9.0 (`FS25_FarmDashboard_App/FS25_FarmDashboard_App/package.json`)
9+
- **Mod:** 2.3.0.0 (`FS25_FarmDashboard_Mod/FS25_FarmDashboard_Mod/modDesc.xml`)
10+
11+
### Build the Windows App
12+
13+
```bash
14+
cd FS25_FarmDashboard_App/FS25_FarmDashboard_App
15+
npm install
16+
npm run dist
17+
```
18+
19+
The installer goes to `%LOCALAPPDATA%\fs25-farm-dashboard-electron-out\`
20+
21+
### Run Dev Build
22+
23+
```bash
24+
cd FS25_FarmDashboard_App/FS25_FarmDashboard_App
25+
npm install
26+
npm start
27+
```
28+
29+
Opens the Electron app in dev mode.
30+
31+
## Architecture
32+
33+
```
34+
Game (FS25)
35+
36+
Mod writes data.json
37+
38+
Electron (main.js) watches data.json
39+
40+
Express server :8766
41+
42+
Web client (browser) polls /api/*
43+
```
44+
45+
### Key Components
46+
47+
| Component | File | Role |
48+
|-----------|------|------|
49+
| **Lua Mod** | `FS25_FarmDashboard_Mod/src/FarmDashboard.lua` | Runs in game; writes `data.json` every cycle |
50+
| **Electron Main** | `FS25_FarmDashboard_App/main.js` | Watches files, manages server, IPC bridge |
51+
| **Express Server** | `FS25_FarmDashboard_App/main.js` | HTTP/WebSocket on port 8766 |
52+
| **Data Merger** | `FS25_FarmDashboard_App/dataMerger.js` | Combines Lua data + XML savegame data |
53+
| **Rules Engine** | `FS25_FarmDashboard_App/web/assests/js/rules-engine.js` | AI field suggestions (browser-side) |
54+
| **Web Client** | `FS25_FarmDashboard_App/web/assests/js/app.js` | Main dashboard UI |
55+
56+
## Repository Layout
57+
58+
```
59+
FarmHub/
60+
├── FS25_FarmDashboard_App/
61+
│ └── FS25_FarmDashboard_App/
62+
│ ├── main.js # Electron + Express
63+
│ ├── preload.js # IPC bridge
64+
│ ├── dataMerger.js # Merge Lua + XML
65+
│ ├── package.json # v3.9.0
66+
│ ├── web/
67+
│ │ ├── index.html
68+
│ │ ├── setup.html
69+
│ │ ├── assests/
70+
│ │ │ ├── css/styles.css
71+
│ │ │ └── js/
72+
│ │ │ ├── app.js
73+
│ │ │ ├── rules-engine.js
74+
│ │ │ ├── modules/ # Sections
75+
│ │ │ └── i18n/ # Translations
76+
│ │ └── locales/
77+
│ │ ├── messages/ # Source translations
78+
│ │ └── translations.json
79+
│ └── build/
80+
│ └── installer.nsh # NSIS hooks
81+
├── FS25_FarmDashboard_Mod/
82+
│ └── FS25_FarmDashboard_Mod/
83+
│ ├── modDesc.xml # v2.3.0.0
84+
│ ├── icon.png
85+
│ └── src/
86+
│ ├── FarmDashboard.lua # Entry point
87+
│ ├── FarmDashboardDataCollector.lua
88+
│ ├── Diagnostics.lua
89+
│ └── collectors/ # Per-data-type
90+
├── tools/
91+
│ ├── app/ # Electron npm helpers
92+
│ ├── Zip-FarmDashboardMod.ps1 # Package mod zip
93+
│ └── Export-ModStoreImages.ps1 # Image extraction
94+
└── docs/ # Full documentation
95+
```
96+
97+
## The Lua Mod
98+
99+
### Mission Hook
100+
101+
`FarmDashboard.lua` registers with `addModEventListener`. On `loadMap`, if `isAuthority()` is true (host/single-player), it adds itself to updateables.
102+
103+
### Staggered Orchestration
104+
105+
`FarmDashboardDataCollector:update(dt)` divides each `collectionCycleMs` into slots — one per enabled module. This prevents lag spikes.
106+
107+
### Collectors
108+
109+
| Collector | Produces |
110+
|-----------|----------|
111+
| `AnimalDataCollector.lua` | Animals, health, type |
112+
| `VehicleDataCollector.lua` | Vehicles, fuel, damage, position |
113+
| `FieldDataCollector.lua` | Crops, growth, PF nitrogen/pH, windrows, bales, suggestions |
114+
| `WeatherDataCollector.lua` | Temperature, conditions |
115+
| `FinanceDataCollector.lua` | Money, loan, asset values |
116+
| `EconomyDataCollector.lua` | Market prices, selling stations |
117+
| `ProductionDataCollector.lua` | Production chains, fill levels |
118+
119+
### Output
120+
121+
The mod writes `data.json` to:
122+
123+
```
124+
%USERPROFILE%\Documents\My Games\FarmingSimulator2025\modSettings\FS25_FarmDashboard\<savename>\data.json
125+
```
126+
127+
This file is updated every `collectionCycleMs` (default 60 seconds).
128+
129+
## The Electron App
130+
131+
### HTTP Server
132+
133+
- Runs on port **8766** (both HTTP and WebSocket)
134+
- Binds to `127.0.0.1` by default (localhost only)
135+
- If LAN enabled, binds to `0.0.0.0` and enforces HTTP Basic Auth
136+
137+
### File Watching
138+
139+
`startLocalWatching()` watches `data.json` on local servers. If the file changes, Electron:
140+
1. Reads the new `data.json`
141+
2. Also re-reads the savegame XML
142+
3. Merges both with `dataMerger.mergeData()`
143+
4. Broadcasts to all connected browsers via WebSocket
144+
145+
### FTP Polling
146+
147+
`startFtpPollingCoordinator()` handles remote (FTP) servers. Configurable:
148+
- **Interval**: 1–25 minutes
149+
- **Schedule**: Sync (all at once) or Staggered (spread out)
150+
151+
### IPC Bridge
152+
153+
`preload.js` exposes methods to the web UI via `window.farmDashAPI`. Every method is intentionally listed — no ad-hoc access to Node APIs.
154+
155+
**Key methods:**
156+
- `saveSettings(cfg)` — Persist server config
157+
- `saveUiPreferences(prefs)` — Save theme, field clusters, etc.
158+
- `checkDesktopAppUpdates()` — Trigger `electron-updater`
159+
- `exportModStoreImages()` — Run mod-image PowerShell pipeline
160+
161+
## Data Merge (`dataMerger.js`)
162+
163+
Combines Lua live data with savegame XML static data.
164+
165+
### Precedence
166+
167+
| Domain | Lua | XML | Both |
168+
|--------|-----|-----|------|
169+
| Animals | live counts, fill |||
170+
| Fields | live agronomy, growth, suggestions | base field rows | merged |
171+
| Vehicles | live state, fuel | base list, ownership | merged |
172+
| Economy | live prices | history | merged |
173+
174+
### Anti-Regress
175+
176+
If live data is missing (game just restarted), the merger caches the last known value so the UI doesn't flicker.
177+
178+
### Timestamps
179+
180+
The merged payload includes:
181+
```json
182+
{
183+
"dataTimestamps": {
184+
"lastLuaReceivedAt": <epoch_ms>,
185+
"lastXmlReceivedAt": <epoch_ms>,
186+
"mergeComputedAt": <epoch_ms>,
187+
"liveNewerThanXml": <bool>
188+
}
189+
}
190+
```
191+
192+
Used for the top-bar data-source badge.
193+
194+
## Rules Engine (`rules-engine.js`)
195+
196+
Runs in the browser; no network calls. Provides AI-powered field work suggestions.
197+
198+
### Entry Point
199+
200+
`getLocalFieldSuggestion(field, opts)` — called by `fields.js` for each field.
201+
202+
### Thresholds
203+
204+
| Constant | Value | Meaning |
205+
|----------|-------|---------|
206+
| `MIN_WINDROW_LITERS` | 120 | Ignore windrow signal below this |
207+
| `MIN_WINDROW_AREA` | 0.0005 | Min area fraction to count windrow |
208+
| PF nitrogen band | < 0.6 × target | "Needs nitrogen" |
209+
210+
### Suggestion Priority
211+
212+
When multiple maintenance actions apply:
213+
1. **Lime** (highest priority)
214+
2. **Nitrogen**
215+
3. **Weeds**
216+
4. **Rolling** (lowest priority)
217+
218+
## Web Client
219+
220+
### Entry Point
221+
222+
`web/assests/js/app.js` defines `LivestockDashboard` and mixes in modules. Assigned to `window.dashboard`.
223+
224+
### Module Map
225+
226+
| Module | Handles |
227+
|--------|---------|
228+
| `navigation.js` | Sidebar, landing page |
229+
| `apiStorage.js` | Server tabs, `/api/*` calls |
230+
| `livestock.js` | Livestock section |
231+
| `vehicles.js` | Vehicles section |
232+
| `fields.js` | Fields section |
233+
| `economy.js` | Economy section |
234+
| `pastures.js` | Pastures section |
235+
| `productions.js` | Production chains |
236+
| `theming.js` | Color picker |
237+
| `i18n/i18n.js` | Translations |
238+
239+
### Polling
240+
241+
`app.js` has a `dashboard.pollInterval` (default 1s). On each tick:
242+
1. Fetch `/api/data`
243+
2. Merge with local state
244+
3. Call `refresh*()` on each module (incremental DOM update)
245+
246+
## Internationalization (i18n)
247+
248+
### Adding a String
249+
250+
1. Add the key to `web/locales/messages/en.json` (source of truth)
251+
2. Run `npm run i18n:sync` — copies the key to every language
252+
3. Translate non-English files
253+
4. Run `npm run i18n:build` — creates `translations.json`
254+
5. Run `npm run i18n:verify` — confirms full coverage
255+
256+
### Build & Verify
257+
258+
```bash
259+
npm run i18n:build # Create translations.json
260+
npm run i18n:verify # Check coverage
261+
npm run i18n:audit # Find orphans/duplicates
262+
```
263+
264+
## Build & Packaging
265+
266+
### npm Scripts
267+
268+
| Script | What |
269+
|--------|------|
270+
| `npm start` | Dev launch (`electron .`) |
271+
| `npm run dist` | Full NSIS installer |
272+
| `npm run pack` | Unpacked app (for testing) |
273+
| `npm test` | Run tests |
274+
| `npm run verify:electron-pack` | CI gate: verify all required files in `package.json` `build.files` |
275+
276+
### CI/CD
277+
278+
GitHub Actions runs on push/PR to `main`, `master`, or `develop`:
279+
- `npm ci` — clean install
280+
- `npm test` — unit tests
281+
- `npm run verify:electron-pack` — file verification
282+
- `npm run i18n:verify` — translation coverage
283+
284+
### Release Checklist
285+
286+
1. Build mod zip: `.\tools\Zip-FarmDashboardMod.ps1`
287+
2. Build Windows app: `npm run dist`
288+
3. Attach `.exe` and mod `.zip` to GitHub Release
289+
4. Verify auto-update works: Settings → Check for updates
290+
291+
## Debugging Checklist
292+
293+
| Issue | Check |
294+
|-------|-------|
295+
| Empty dashboard | Mod enabled + save loaded; `data.json` exists; Settings path correct |
296+
| "Waiting for field data" | `dataTimestamps.lastLuaReceivedAt` advancing? Watcher fired? |
297+
| Wrong farm shown | `activeFarmId` in merged payload; check farm dropdown |
298+
| Merge oddities | `dataMerger.js` precedence table; `liveNewerThanXml` should be `true` when fresh |
299+
| LAN 401/403 | Check `lanUsername`, `lanPassword`, IP allowlist |
300+
| FTP not polling | `intervalMinutes` must be 1–25 |
301+
| `app.asar` locked | `npm run unlock-install`, then `npm run dist` |
302+
303+
## Known Gaps from the Audits
304+
305+
1. **Livestock Statistics / Genetics tabs** — UI buttons not wired (`index.html`, `livestock.js`)
306+
2. **Electron `parseModConfigXml`** — ignores `debugBaleScan` flag (`main.js`)
307+
3. **Fields error strip** — no retry button; auto-retries every 5s
308+
4. **Notification history** — hard-codes English empty state
309+
310+
## Conventions
311+
312+
- Match existing naming, IPC channels, and merge semantics
313+
- Prefer additive JSON fields; don't reintroduce coordinate dumps
314+
- New translations → `messages/<code>.json` only; never hand-edit `translations.json`
315+
- New IPC channels: add in `main.js`, expose in `preload.js`, document in this file
316+
- Store keys: prefer `electron-store` over `localStorage` for desktop-level config
317+
- Tests: `npm test` for JS changes; Lua/game behaviour needs manual testing
318+
319+
---
320+
321+
**Questions?** Check the [full documentation](../docs/README.md) or open a GitHub issue.

0 commit comments

Comments
 (0)