Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
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
17 changes: 17 additions & 0 deletions .env.example
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
# Initial spawn location for the flight simulator
# Default: Gothenburg, Sweden

# Longitude in degrees (-180 to 180)
VITE_LONGITUDE=11.9746

# Latitude in degrees (-90 to 90)
VITE_LATITUDE=57.7089

# Altitude in meters
VITE_ALTITUDE=200

# API Tokens (required)
# Get your Cesium Ion token from: https://ion.cesium.com/tokens
# Get your Mapbox token from: https://account.mapbox.com/access-tokens/
VITE_MAPBOX_TOKEN=your_mapbox_token_here
VITE_CESIUM_TOKEN=your_cesium_token_here
59 changes: 59 additions & 0 deletions .github/workflows/docker-build.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
name: Build and Push Docker Image

on:
schedule:
- cron: '0 0 1 * *'
push:
tags:
- 'v*'

env:
REGISTRY: ghcr.io
IMAGE_NAME: ${{ github.repository }}

jobs:
build-and-push:
runs-on: ubuntu-latest
permissions:
contents: read
packages: write
steps:
- name: Checkout repository
uses: actions/checkout@v4

- name: Set up Docker Buildx
uses: docker/setup-buildx-action@v3

- name: Log in to GitHub Container Registry
if: github.event_name != 'pull_request'
uses: docker/login-action@v3
with:
registry: ${{ env.REGISTRY }}
username: ${{ github.actor }}
password: ${{ secrets.GITHUB_TOKEN }}

- name: Extract metadata (tags, labels)
id: meta
uses: docker/metadata-action@v5
with:
images: ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}
tags: |
type=ref,event=branch
type=ref,event=pr
type=semver,pattern={{version}}
type=semver,pattern={{major}}.{{minor}}
type=semver,pattern={{major}}
type=sha
type=raw,value=latest,enable={{is_default_branch}}

- name: Build and push Docker image
uses: docker/build-push-action@v5
with:
context: .
file: ./Dockerfile
push: ${{ github.event_name != 'pull_request' }}
tags: ${{ steps.meta.outputs.tags }}
labels: ${{ steps.meta.outputs.labels }}
cache-from: type=gha
cache-to: type=gha,mode=max
platforms: linux/amd64,linux/arm64
31 changes: 31 additions & 0 deletions Dockerfile
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
# ---- Build Stage ----
FROM node:20-alpine AS builder

WORKDIR /app/packages/web

COPY packages/web/package.json ./
COPY packages/web/tsconfig.json ./
COPY packages/web/vite.config.ts ./
COPY packages/web/postcss.config.js ./
COPY packages/web/tailwind.config.js ./
COPY packages/web/index.html ./
COPY packages/web/public ./public
COPY packages/web/src ./src

RUN npm install
RUN npm run build

# ---- Runtime Stage ----
FROM node:20-alpine AS runner

WORKDIR /app

# Copy only the built output and static assets from builder
COPY --from=builder /app/packages/web/dist ./dist
COPY --from=builder /app/packages/web/public ./public
COPY --from=builder /app/packages/web/package.json ./package.json
COPY --from=builder /app/packages/web/package-lock.json ./package-lock.json

EXPOSE 5173

CMD ["npx", "serve", "-s", "dist", "-l", "5173"]
22 changes: 22 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,28 @@ VITE_CESIUM_TOKEN=your_cesium_token_here

Both services are free for development use.

### Docker

You can build and run the project in a Docker container:

```bash
# Build the Docker image (from project root)
docker build -t cesium-flight-simulator .
```

Then run the container with your API tokens:
```bash
# Create a .env file from the example. You must edit it to add your tokens.
copy .env.example .env

# Run the container (exposes port 5173 by default)
docker run -p 5173:5173 --env-file .env cesium-flight-simulator
```

Then open http://localhost:5173 in your browser.

You will still need to provide your Mapbox and Cesium API tokens on first launch or via a `.env` file as described above.

## Controls

| Key | Action |
Expand Down
17 changes: 17 additions & 0 deletions packages/web/.env.example
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
# Initial spawn location for the flight simulator
# Default: Gothenburg, Sweden

# Longitude in degrees (-180 to 180)
VITE_LONGITUDE=11.9746

# Latitude in degrees (-90 to 90)
VITE_LATITUDE=57.7089

# Altitude in meters
VITE_ALTITUDE=200

# API Tokens (required)
# Get your Cesium Ion token from: https://ion.cesium.com/tokens
# Get your Mapbox token from: https://account.mapbox.com/access-tokens/
VITE_MAPBOX_TOKEN=your_mapbox_token_here
VITE_CESIUM_TOKEN=your_cesium_token_here
5 changes: 4 additions & 1 deletion packages/web/src/cesium/bootstrap/main.ts
Original file line number Diff line number Diff line change
Expand Up @@ -93,7 +93,10 @@ export class CesiumVehicleGame {
}

public async startCinematicSequence(): Promise<void> {
const spawnPosition = Cesium.Cartesian3.fromDegrees(11.9746, 57.7089, 200);
const lng = Number(import.meta.env.VITE_LONGITUDE) || 11.9746;
const lat = Number(import.meta.env.VITE_LATITUDE) || 57.7089;
const alt = Number(import.meta.env.VITE_ALTITUDE) || 200;
const spawnPosition = Cesium.Cartesian3.fromDegrees(lng, lat, alt);

console.log('🎬 Starting cinematic sequence...');

Expand Down
5 changes: 4 additions & 1 deletion packages/web/src/cesium/bridge/GameBridge.ts
Original file line number Diff line number Diff line change
Expand Up @@ -178,7 +178,10 @@ export class GameBridge extends TypedEventEmitter<GameEvents> {
if (vehicle && vehicle instanceof Aircraft && vehicle.isCrashed()) {
vehicle.resetCrash();
// Reset to spawn position
const spawnPosition = Cesium.Cartesian3.fromDegrees(11.9746, 57.7089, 200);
const lng = Number(import.meta.env.VITE_LONGITUDE) || 11.9746;
const lat = Number(import.meta.env.VITE_LATITUDE) || 57.7089;
const alt = Number(import.meta.env.VITE_ALTITUDE) || 200;
const spawnPosition = Cesium.Cartesian3.fromDegrees(lng, lat, alt);
const currentState = vehicle.getState();
vehicle.setState({
...currentState,
Expand Down
12 changes: 7 additions & 5 deletions packages/web/src/cesium/managers/VehicleManager.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,10 +7,12 @@ import { Updatable } from '../core/GameLoop';
import { InputManager } from '../input/InputManager';

const DEFAULT_SPAWN_LOCATION = {
lng: 11.9746,
lat: 57.7089
lng: Number(import.meta.env.VITE_LONGITUDE) || 11.9746,
lat: Number(import.meta.env.VITE_LATITUDE) || 57.7089
};

const DEFAULT_ALTITUDE = Number(import.meta.env.VITE_ALTITUDE) || 200;

export class VehicleManager implements Updatable {
private vehicles: Map<string, Vehicle> = new Map();
private activeVehicle: Vehicle | null = null;
Expand Down Expand Up @@ -144,7 +146,7 @@ export class VehicleManager implements Updatable {
const spawnPosition = position || Cesium.Cartesian3.fromDegrees(
DEFAULT_SPAWN_LOCATION.lng,
DEFAULT_SPAWN_LOCATION.lat,
100
Math.max(100, DEFAULT_ALTITUDE)
);

const car = new Car(id, {
Expand All @@ -164,7 +166,7 @@ export class VehicleManager implements Updatable {
const spawnPosition = position || Cesium.Cartesian3.fromDegrees(
DEFAULT_SPAWN_LOCATION.lng,
DEFAULT_SPAWN_LOCATION.lat,
200
DEFAULT_ALTITUDE
);

const aircraft = new Aircraft(id, {
Expand Down Expand Up @@ -203,7 +205,7 @@ export class VehicleManager implements Updatable {
const originalSpawn = Cesium.Cartesian3.fromDegrees(
DEFAULT_SPAWN_LOCATION.lng,
DEFAULT_SPAWN_LOCATION.lat,
isAircraft ? 200 : 100
isAircraft ? DEFAULT_ALTITUDE : Math.max(100, DEFAULT_ALTITUDE)
);
const heading = 0;

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,9 +12,9 @@ export interface VehiclePosition {
export function useVehiclePosition() {
const vehicleState = useGameEvent('vehicleStateChanged');
const [position, setPosition] = useState<VehiclePosition>({
longitude: 11.9746,
latitude: 57.7089,
altitude: 200,
longitude: Number(import.meta.env.VITE_LONGITUDE) || 11.9746,
latitude: Number(import.meta.env.VITE_LATITUDE) || 57.7089,
altitude: Number(import.meta.env.VITE_ALTITUDE) || 200,
heading: 0,
});

Expand Down