diff --git a/hackathon-infra/README.md b/hackathon-infra/README.md index 1391a023..3bfbe6aa 100644 --- a/hackathon-infra/README.md +++ b/hackathon-infra/README.md @@ -16,12 +16,12 @@ This repository deploys the complete virtual event infrastructure for nf-core hackathons: -| Service | Description | URL | -|---------|-------------|-----| -| **WorkAdventure** | Virtual office for the hackathon | [app.hackathon.nf-co.re](https://app.hackathon.nf-co.re) | -| **LiveKit** | Proximity audio/video chat | [livekit.hackathon.nf-co.re](https://livekit.hackathon.nf-co.re) | -| **Coturn** | TURN server for NAT traversal | `turn.hackathon.nf-co.re` | -| **Jitsi** | Video conferencing for meeting rooms | [jitsi.hackathon.nf-co.re](https://jitsi.hackathon.nf-co.re) | +| Service | Description | URL | +| ----------------- | ------------------------------------ | ---------------------------------------------------------------- | +| **WorkAdventure** | Virtual office for the hackathon | [app.hackathon.nf-co.re](https://app.hackathon.nf-co.re) | +| **LiveKit** | Proximity audio/video chat | [livekit.hackathon.nf-co.re](https://livekit.hackathon.nf-co.re) | +| **Coturn** | TURN server for NAT traversal | `turn.hackathon.nf-co.re` | +| **Jitsi** | Video conferencing for meeting rooms | [jitsi.hackathon.nf-co.re](https://jitsi.hackathon.nf-co.re) | Built with open source software: [WorkAdventure](https://workadventu.re/), [LiveKit](https://livekit.io/), [Coturn](https://github.com/coturn/coturn), and [Jitsi](https://jitsi.org/). @@ -35,9 +35,9 @@ This repository is designed for AI-assisted operation. Clone the repo, open it w Maps define the virtual office layout - rooms, interactive zones, and navigation. Edit them with the [Tiled Map Editor](https://www.mapeditor.org/). -> *"I want to add a new meeting room called 'Quiet Zone' to the map"* +> _"I want to add a new meeting room called 'Quiet Zone' to the map"_ > -> *"Sync the latest map changes to the live server"* +> _"Sync the latest map changes to the live server"_ See [`maps/README.md`](maps/README.md) for the full map editing guide. @@ -45,26 +45,26 @@ See [`maps/README.md`](maps/README.md) for the full map editing guide. Manage the infrastructure stack with natural language: -> *"Deploy the hackathon infrastructure"* +> _"Deploy the hackathon infrastructure"_ > -> *"OAuth isn't working, can you help debug?"* +> _"OAuth isn't working, can you help debug?"_ > -> *"Tear down everything, the event is over"* +> _"Tear down everything, the event is over"_ The AI reads `CLAUDE.md` for safety rules and loads task-specific skills automatically: -| Skill | When to Use | -|-------|-------------| -| `/deploy` | Setting up for a new event, first-time deployment, recovering from destruction | -| `/teardown` | Event is over, cleanup needed, starting fresh | -| `/debug` | Services unhealthy, users report problems, OAuth/video/maps not working | -| `/maps` | Working with map files, syncing changes, setting up interactive zones | +| Skill | When to Use | +| ----------- | ------------------------------------------------------------------------------ | +| `/deploy` | Setting up for a new event, first-time deployment, recovering from destruction | +| `/teardown` | Event is over, cleanup needed, starting fresh | +| `/debug` | Services unhealthy, users report problems, OAuth/video/maps not working | +| `/maps` | Working with map files, syncing changes, setting up interactive zones |
For AI Agents - Read `CLAUDE.md` for core safety rules and guidelines -- Skills are in `.claude/skills//SKILL.md` +- Skills are in `.agents/skills/hackathon-/SKILL.md` - Always validate each step before proceeding to the next - Ask for confirmation before any destructive operations @@ -78,30 +78,31 @@ Everything can also be done manually without AI assistance. ### Helper Scripts -| Script | Description | -|--------|-------------| -| `./scripts/validate-env.sh` | Check environment is configured | -| `./scripts/bootstrap.sh` | Create Terraform backend (S3 + DynamoDB) | -| `./scripts/sync-maps.sh` | Sync maps to server | -| `./scripts/status.sh` | Check health of all services | -| `./scripts/ssh.sh ` | SSH to instance (wa\|lk\|turn\|jitsi) | +| Script | Description | +| ---------------------------- | ---------------------------------------- | +| `./scripts/validate-env.sh` | Check environment is configured | +| `./scripts/bootstrap.sh` | Create Terraform backend (S3 + DynamoDB) | +| `./scripts/sync-maps.sh` | Sync maps to server | +| `./scripts/status.sh` | Check health of all services | +| `./scripts/ssh.sh ` | SSH to instance (wa\|lk\|turn\|jitsi) | ### Terraform Commands -| Command | Description | -|---------|-------------| -| `terraform init` | Initialize Terraform | -| `terraform plan` | Preview changes | -| `terraform apply` | Apply changes (creates/updates infrastructure) | -| `terraform destroy` | Destroy all infrastructure | +| Command | Description | +| ------------------- | ---------------------------------------------- | +| `terraform init` | Initialize Terraform | +| `terraform plan` | Preview changes | +| `terraform apply` | Apply changes (creates/updates infrastructure) | +| `terraform destroy` | Destroy all infrastructure | ### Detailed Procedures For step-by-step instructions, read the skill files directly: -- **Deploy:** `.claude/skills/deploy/SKILL.md` -- **Teardown:** `.claude/skills/teardown/SKILL.md` -- **Debug:** `.claude/skills/debug/SKILL.md` -- **Maps:** `.claude/skills/maps/SKILL.md` + +- **Deploy:** `.agents/skills/hackathon-deploy/SKILL.md` +- **Teardown:** `.agents/skills/hackathon-teardown/SKILL.md` +- **Debug:** `.agents/skills/hackathon-debug/SKILL.md` +- **Maps:** `.agents/skills/hackathon-maps/SKILL.md` ## Architecture @@ -141,12 +142,12 @@ flowchart TB ## Documentation -| Document | Description | -|----------|-------------| -| [docs/architecture.md](docs/architecture.md) | VPC layout, service roles, design rationale | -| [docs/costs.md](docs/costs.md) | Instance sizing, daily costs, tracking via AWS Cost Explorer | -| [docs/branding.md](docs/branding.md) | Customizing logos, sign-in page, social sharing | -| [maps/README.md](maps/README.md) | Map editing guide for Tiled | +| Document | Description | +| -------------------------------------------- | ------------------------------------------------------------ | +| [docs/architecture.md](docs/architecture.md) | VPC layout, service roles, design rationale | +| [docs/costs.md](docs/costs.md) | Instance sizing, daily costs, tracking via AWS Cost Explorer | +| [docs/branding.md](docs/branding.md) | Customizing logos, sign-in page, social sharing | +| [maps/README.md](maps/README.md) | Map editing guide for Tiled | ## Directory Structure @@ -158,12 +159,12 @@ flowchart TB │ ├── architecture.md # Design rationale and VPC layout │ ├── costs.md # Cost estimates and sizing │ └── branding.md # Customization guide -├── .claude/ -│ └── skills/ # Task-specific AI skills -│ ├── deploy/ -│ ├── teardown/ -│ ├── debug/ -│ └── maps/ +├── .agents/ +│ └── skills/ # Task-specific AI skills (at repo root) +│ ├── hackathon-deploy/ +│ ├── hackathon-teardown/ +│ ├── hackathon-debug/ +│ └── hackathon-maps/ ├── terraform/ # Infrastructure code │ ├── main.tf │ ├── variables.tf diff --git a/hackathon-infra/maps/README.md b/hackathon-infra/maps/README.md index d0fc6950..2f18167f 100644 --- a/hackathon-infra/maps/README.md +++ b/hackathon-infra/maps/README.md @@ -11,7 +11,7 @@ The `maps/` folder in this repository is the **source of truth** for all map ass ## Workflow - **Humans:** Edit maps using Tiled Map Editor (this guide) -- **AI Agents:** Validate maps and sync to server (see `.claude/skills/maps/SKILL.md`) +- **AI Agents:** Validate maps and sync to server (see `.agents/skills/hackathon-maps/SKILL.md`) ## Quick Start @@ -67,6 +67,7 @@ maps/ Interactive features are implemented using the [WorkAdventure Scripting API](https://docs.workadventu.re/developer/map-scripting/). The script provides: + - **Welcome message** - Displays when users enter the map - **Help zone popup** - Links to Slack, Documentation, GitHub (triggered by `needHelp` zone) - **Social links popup** - Links to Mastodon, Bluesky, YouTube, LinkedIn (triggered by `followUs` zone) @@ -75,6 +76,7 @@ The script provides: ### Script Architecture The script is built using TypeScript and Vite: + - **Source**: `src/main.ts` - TypeScript with WorkAdventure API types - **Wrapper**: `src/index.html` - Loads WorkAdventure iframe API before script - **Output**: `dist/` - Pre-built HTML and JS files (committed to git, served as `script/`) @@ -100,6 +102,7 @@ git commit -m "Rebuild map scripts" ``` For development with hot reload: + ```bash npm run dev ``` @@ -107,6 +110,7 @@ npm run dev ### Adding Interactive Zones To add interactive zones to the map: + 1. In Tiled, create an Object Layer 2. Draw a rectangle where you want the interaction 3. Set the object's `name` property to match the zone name in your script (e.g., `needHelp`) @@ -116,6 +120,7 @@ To add interactive zones to the map: ### How It Works When the map loads: + 1. WorkAdventure reads the `script` property from `map.json` (points to `script/index.html`) 2. WorkAdventure loads the HTML in a sandboxed iframe 3. The HTML loads `iframe_api.js` which provides the global `WA` object @@ -158,6 +163,7 @@ Layers panel (top = rendered last): 4. **Paint** using the Stamp Brush tool (B) or fill with Bucket Fill (F) **Tips:** + - Furniture that should appear above players goes in `abovePlayer/` group - Furniture below players goes in `furniture` layer - Add `collides: true` to tiles that block movement @@ -167,12 +173,14 @@ Layers panel (top = rendered last): To make tiles solid (players can't walk through): **Method 1: Per-tile (recommended)** + 1. Select a tileset in the Tilesets panel 2. Click "Edit Tileset" (wrench icon) 3. Select the tile(s) to make solid 4. In Properties panel, add: `collides` (bool) = `true` **Method 2: Per-layer** + 1. Create a new tile layer named `collisions` 2. Add property: `collides` = `true` 3. Paint blocking tiles on this layer (they'll be invisible in-game) @@ -191,42 +199,43 @@ Add these as custom properties on tiles, objects, or layers: ### Movement & Collision -| Property | Type | Description | -|----------|------|-------------| -| `collides` | bool | Blocks player movement | -| `start` | bool | Legacy spawn point (use `startLayer` instead) | -| `startLayer` | bool | Marks layer as spawn area | +| Property | Type | Description | +| ------------ | ---- | --------------------------------------------- | +| `collides` | bool | Blocks player movement | +| `start` | bool | Legacy spawn point (use `startLayer` instead) | +| `startLayer` | bool | Marks layer as spawn area | ### Audio & Video -| Property | Type | Description | -|----------|------|-------------| -| `playAudio` | string | URL/path to background audio | -| `playAudioLoop` | bool | Loop the audio (default: true) | -| `silent` | bool | Mutes proximity video/audio in zone | -| `jitsiRoom` | string | Jitsi meeting room name | -| `jitsiTrigger` | string | `onaction` = click to join, else auto-join | -| `jitsiWidth` | int | Jitsi iframe width (%) | +| Property | Type | Description | +| --------------- | ------ | ------------------------------------------ | +| `playAudio` | string | URL/path to background audio | +| `playAudioLoop` | bool | Loop the audio (default: true) | +| `silent` | bool | Mutes proximity video/audio in zone | +| `jitsiRoom` | string | Jitsi meeting room name | +| `jitsiTrigger` | string | `onaction` = click to join, else auto-join | +| `jitsiWidth` | int | Jitsi iframe width (%) | ### Web Integration -| Property | Type | Description | -|----------|------|-------------| -| `openWebsite` | string | URL to open in iframe | -| `openWebsiteTrigger` | string | `onaction` = click to open | -| `openWebsiteAllowApi` | bool | Allow WorkAdventure scripting API | -| `openTab` | string | URL to open in new browser tab | +| Property | Type | Description | +| --------------------- | ------ | --------------------------------- | +| `openWebsite` | string | URL to open in iframe | +| `openWebsiteTrigger` | string | `onaction` = click to open | +| `openWebsiteAllowApi` | bool | Allow WorkAdventure scripting API | +| `openTab` | string | URL to open in new browser tab | ### Navigation -| Property | Type | Description | -|----------|------|-------------| -| `exitUrl` | string | Teleport to another map (full URL) | +| Property | Type | Description | +| -------------- | ------ | ---------------------------------------------- | +| `exitUrl` | string | Teleport to another map (full URL) | | `exitSceneUrl` | string | Teleport to map on same server (relative path) | ### Examples **Meeting room zone:** + ``` Object properties: jitsiRoom: "standup-room" @@ -234,6 +243,7 @@ Object properties: ``` **Website kiosk:** + ``` Object properties: openWebsite: "https://nf-co.re" @@ -241,12 +251,14 @@ Object properties: ``` **Quiet zone (no proximity chat):** + ``` Layer or object property: silent: true ``` **Background music area:** + ``` Object properties: playAudio: "assets/bensound-thelounge.mp3" @@ -306,6 +318,7 @@ Maps are validated before syncing. To validate without syncing: ``` Checks performed: + - JSON syntax is valid - All tileset images exist - Start/spawn layer is present @@ -332,6 +345,7 @@ curl -I https://app.hackathon.nf-co.re/maps/default/map.json ### Tilesets not loading in Tiled If Tiled shows red X for tilesets: + 1. Ensure you opened `map.json` from the `maps/default/` directory 2. Check that `assets/` folder contains all PNG files 3. Verify tileset paths in map.json match actual filenames @@ -358,6 +372,7 @@ The hackathon map includes LimeZu modern interior tilesets. For additional asset - [Kenney Assets](https://kenney.nl/assets) - Free game assets **Requirements for new tilesets:** + - PNG format - 32x32 pixel tiles - Place in `maps/default/assets/` diff --git a/hackathon-infra/maps/default/.gitignore b/hackathon-infra/maps/default/.gitignore index c2658d7d..0554e521 100644 --- a/hackathon-infra/maps/default/.gitignore +++ b/hackathon-infra/maps/default/.gitignore @@ -1 +1,6 @@ node_modules/ + +# NOTE: dist/ is intentionally tracked (not ignored). +# Pre-built scripts are committed so the EC2 instance can serve them +# directly from the git clone without requiring a build step on deploy. +# Rebuild with `npm run build` only when modifying src/main.ts. diff --git a/hackathon-infra/terraform/modules/livekit/templates/user_data.sh b/hackathon-infra/terraform/modules/livekit/templates/user_data.sh index 990aab3c..88620968 100644 --- a/hackathon-infra/terraform/modules/livekit/templates/user_data.sh +++ b/hackathon-infra/terraform/modules/livekit/templates/user_data.sh @@ -7,15 +7,13 @@ echo "Starting LiveKit installation..." # Update system dnf update -y -dnf install -y docker jq +dnf install -y docker docker-compose-plugin jq # Start Docker systemctl start docker systemctl enable docker -# Install docker-compose -curl -L "https://github.com/docker/compose/releases/latest/download/docker-compose-$(uname -s)-$(uname -m)" -o /usr/local/bin/docker-compose -chmod +x /usr/local/bin/docker-compose +# docker compose v2 is included as a docker plugin via dnf # Get metadata TOKEN=$(curl -s -X PUT "http://169.254.169.254/latest/api/token" -H "X-aws-ec2-metadata-token-ttl-seconds: 21600") @@ -76,7 +74,7 @@ volumes: EOF # Start services -docker-compose up -d +docker compose up -d echo "LiveKit installation complete!" echo "LiveKit domain: $LIVEKIT_DOMAIN" diff --git a/hackathon-infra/terraform/modules/workadventure/templates/user_data.sh b/hackathon-infra/terraform/modules/workadventure/templates/user_data.sh index 3c1bf4f7..dd720e8d 100644 --- a/hackathon-infra/terraform/modules/workadventure/templates/user_data.sh +++ b/hackathon-infra/terraform/modules/workadventure/templates/user_data.sh @@ -5,18 +5,21 @@ exec > >(tee /var/log/user-data.log|logger -t user-data -s 2>/dev/console) 2>&1 echo "Starting WorkAdventure installation..." dnf update -y -dnf install -y docker git jq +dnf install -y docker docker-compose-plugin git jq systemctl start docker systemctl enable docker -curl -L "https://github.com/docker/compose/releases/latest/download/docker-compose-$(uname -s)-$(uname -m)" -o /usr/local/bin/docker-compose -chmod +x /usr/local/bin/docker-compose - mkdir -p /opt/workadventure cd /opt/workadventure -git clone --depth 1 https://github.com/nf-core/hackathon-infra.git +# Sparse checkout: clone only hackathon-infra/ from nf-core/ops monorepo +git clone --depth 1 --filter=blob:none --sparse https://github.com/nf-core/ops.git ops-repo +cd ops-repo +git sparse-checkout set hackathon-infra +cd /opt/workadventure +# Symlink so all existing paths (/opt/workadventure/hackathon-infra/) keep working +ln -s /opt/workadventure/ops-repo/hackathon-infra /opt/workadventure/hackathon-infra git clone --depth 1 https://github.com/workadventure/workadventure.git cd workadventure/contrib/docker @@ -168,7 +171,7 @@ cat >> docker-compose.override.yaml << EOF - "traefik.http.services.play-static.loadbalancer.server.port=3000" EOF -docker-compose -f docker-compose.prod.yaml -f docker-compose.override.yaml up -d +docker compose -f docker-compose.prod.yaml -f docker-compose.override.yaml up -d sleep 30 docker exec docker-play-1 ln -sf /usr/src/play/dist/public/resources/objects/webrtc-in-ding.mp3 /usr/src/play/dist/public/resources/objects/webrtc-in.mp3 2>/dev/null || true docker exec docker-play-1 sed -i 's|{{ title }}|nf-core/hackathon|g' /usr/src/play/dist/public/index.html 2>/dev/null || true @@ -179,5 +182,5 @@ if [ -d "/opt/workadventure/hackathon-infra/overrides" ]; then docker cp /opt/workadventure/hackathon-infra/overrides/. docker-play-1:/usr/src/play/dist/public/ fi -docker-compose -f docker-compose.prod.yaml -f docker-compose.override.yaml ps +docker compose -f docker-compose.prod.yaml -f docker-compose.override.yaml ps echo "WorkAdventure installation complete! Access: https://app.${domain}"