Skip to content
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
14 changes: 14 additions & 0 deletions backend/src/api/admin_routes.py
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@
from src.api.auth_routes import _config, _no_store, current_user, require_same_origin, session_cookie
from src.auth import admin, audit, players, roles, users
from src.coreprotect.reader import CoreProtectConfig
from src.rail import network as rail

admin_router = APIRouter(prefix="/admin", tags=["admin"])

Expand Down Expand Up @@ -166,6 +167,19 @@ def get_movement(request: Request, response: Response,
"view_player_movement")


@admin_router.get("/rail")
def get_rail(request: Request, response: Response, map: str = Query("main", max_length=32)):
_capable(request, "view_rail")
_no_store(response)
try:
return rail.load_network(rail.RailConfig.from_env(), map)
except ValueError:
raise HTTPException(400, detail="bad_map") from None
Comment thread
coderabbitai[bot] marked this conversation as resolved.
except rail.RailUnavailable as exc:
return {"status": exc.code, "world": rail.RailConfig.from_env().world, "updated_at": None,
"unreadable_files": 0, "lines": [], "tracks": [], "junctions": [], "stops": []}


@admin_router.post("/accounts/{user_id}/role")
def post_role(user_id: int, body: RoleBody, request: Request, response: Response):
_no_store(response)
Expand Down
2 changes: 2 additions & 0 deletions backend/src/auth/roles.py
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,8 @@
"view_player_messages": "admin",
# Where players went, minute by minute; every view is audited.
"view_player_movement": "admin",
# The rail network as VehicleFramework saved it.
"view_rail": "admin",
"revoke_sessions": "mod",
"change_role": "admin",
# In-game LuckPerms ranks: everyone on staff sees them; what an admin may
Expand Down
40 changes: 40 additions & 0 deletions backend/src/rail/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
# Rail network

The staff panel's Rail tab draws the Minecraft server's rail network on the
live map: every VehicleFramework track, its junctions and ends, broken or
damaged stretches, and the stops along it. `GET /admin/rail?map=<id>` serves
it to admins and the owner (`view_rail`). Track positions are not personal
data, so views are not audited.

VehicleFramework saves one JSON file per track in
`plugins/VehicleFramework/data/tracks/<world>/`, with switches in
`junctions/` beside them. A track holds a sample about every block and one
segment per pair of samples; a segment can be broken or damaged. The backend
reads the files on request and keeps the result until any file, the map's
`map_markers.json` or its `province_id_runs.bin.gz` changes. A file caught
mid-save is skipped and counted, so the page can say so.

- **Stops** are the settlements whose provinces a track crosses. Each sits
where the track comes closest to the settlement inside its provinces.
- **Lines** are tracks joined by junctions. A line is named after its first and
last stop along its longest track; one with no stops is an "Unnamed line".
- Tracks are simplified (Douglas-Peucker, 0.35 blocks) before they are sent.

## Settings

| Variable | Meaning |
| --- | --- |
| `RAIL_TRACKS_DIR` | VehicleFramework's `data/tracks` folder inside the backend container. Unset: the tab says the site is not set up to read the network. |
| `RAIL_WORLD` | The world folder to read: the server's `level-name`. Defaults to `COREPROTECT_MAP_WORLD`, then `TFMC_Map`. |

Mount the folder read-only in the host's `docker-compose.override.yml`, like
CoreProtect's:

```yaml
services:
backend:
volumes:
- /home/amp/.ampdata/instances/TFMCMain01/Minecraft/plugins/VehicleFramework/data/tracks:/vehicleframework-tracks:ro
environment:
- RAIL_TRACKS_DIR=/vehicleframework-tracks
```
Empty file added backend/src/rail/__init__.py
Empty file.
18 changes: 18 additions & 0 deletions backend/src/rail/conftest.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
import sys
from pathlib import Path
import pytest

BACKEND = Path(__file__).resolve().parents[2]
if str(BACKEND) not in sys.path:
sys.path.insert(0, str(BACKEND))


@pytest.fixture
def database(tmp_path, monkeypatch):
from src.skins import db
monkeypatch.setattr(db, "DB_PATH", tmp_path / "province.db")
monkeypatch.setattr(db, "DATA_DIR", tmp_path)
for name in ("SKINS_DIR", "WARDROBE_DIR", "DRINKS_DIR"):
monkeypatch.setattr(db, name, tmp_path / name.lower())
db.migrate()
return db
Loading