Skip to content
Merged
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
36 changes: 30 additions & 6 deletions CREDITS.md
Original file line number Diff line number Diff line change
@@ -1,14 +1,38 @@
# Credits

Third-party pixel-art assets used by the project.
Third-party assets used by the project, grouped by license. Paths are relative to
`lg_rpg_server/public/assets/`.

## CC BY 4.0 International ([license](https://creativecommons.org/licenses/by/4.0/))

- chierit — Elementals: Water Priestess — https://chierit.itch.io/elementals-water-priestess
- **Elementals: Water Priestess** by chierit
- Source: https://chierit.itch.io/elementals-water-priestess
- Used for: the player character (`players/cherit/`)
- Changes: repacked/resized into a game-ready sprite sheet and animation tileset metadata.

## CC0 1.0 ([license](https://creativecommons.org/publicdomain/zero/1.0/))

- Pixel-Boy and AAA — Ninja Adventure Asset Pack — https://pixel-boy.itch.io/ninja-adventure-asset-pack
- Niji (based on 0x72's Dungeon Tileset II) — Dungeon Tileset II Extended — https://nijikokun.itch.io/dungeontileset-ii-extended
- FoozleCC — Lucifer Pickups — https://foozlecc.itch.io/lucifer-pickups
- LuizMelo — Monsters Creatures Fantasy 1 and 2 — https://luizmelo.itch.io/
- **Ninja Adventure Asset Pack** by Pixel-Boy and AAA
- Source: https://pixel-boy.itch.io/ninja-adventure-asset-pack
- Used for: music and sound effects (`audio/`), weather, buff and attack effects (`fx/`),
and the map tilesets under `maps/shared_assets/ninja_adventure/`
- **Monsters Creatures Fantasy 1 and 2** by LuizMelo
- Source: https://luizmelo.itch.io/
- Used for: every enemy except the boss — bat, flying eye, goblin, mimic, mushroom,
rat, skeleton and slime (`enemies/`)
- **Debts in the Depths Asset Pack** by Reaktori
- Source: https://reaktori.itch.io/debts-in-the-depths-asset-pack
- Used for: the dragon boss and its fire projectile (`enemies/boss/`), and the huntress
special-attack projectiles — firebolt, explosion, acid, magic bolt, ghost orb, sparkle —
repacked into the `players/huntress/proj_*.png` sheets
- Changes: effect strips combined into uniform flight+impact sprite sheets, some frames
upscaled with nearest-neighbor
- **Dungeon Tileset II Extended** by Niji (based on 0x72's Dungeon Tileset II)
- Source: https://nijikokun.itch.io/dungeontileset-ii-extended
- Used for: map tilesets under `maps/shared_assets/dungeon/`
- **Lucifer Pickups** by FoozleCC
- Source: https://foozlecc.itch.io/lucifer-pickups
- Used for: gem and gold pickups under `maps/shared_assets/lucifer_pickups/`
- **Ninja Jail Castle whatever Platformer Tiles** by R3tr0BoiDX
- Source: https://opengameart.org/content/ninja-jail-castle-whatever-platformer-tiles
- Used for: the wall tileset in both PvP maps (`maps/shared_assets/pvp/wall.png`)
49 changes: 10 additions & 39 deletions ISSUES.md
Original file line number Diff line number Diff line change
@@ -1,12 +1,11 @@
# Networking: Known Issues

If the phone can't reach the server over the local network, it's almost always one of these three things. Check them whenever you set up on a new network.
If the phone can't reach the server over the local network, it's almost always one of these two things. Check them whenever you set up on a new network.

| # | Quick symptom | Jump to |
|---|---|---|
| 1 | Hotspot phone times out, other phones are fine | [Hotspot trap](#1--phone-hosting-the-hotspot-cant-connect) |
| 2 | Server is up, port is open, still no connection | [VirtualBox routing](#2--virtualbox-sends-replies-out-the-wrong-adapter) |
| 3 | Worked before, broke after a reboot | [Firewall](#3--firewall-blocks-port-3000-after-reboot) |
| 2 | Worked before, broke after a reboot | [Firewall](#2--firewall-blocks-the-port-after-reboot) |

---

Expand All @@ -20,56 +19,28 @@ If the phone can't reach the server over the local network, it's almost always o

---

## 2 — VirtualBox sends replies out the wrong adapter

**Symptom:** The server is running and port 3000 is open, but the app still times out.

**Why:** The LG VM usually has two adapters — NAT (`eth0`, internal `10.0.2.x`) and host/bridged (`eth1`, same subnet as the phones). If NAT has the **lower metric**, the VM replies through NAT, an address no phone can reach.

**Check:** SSH into the LG machine and run `route -n`. Look at the `Metric` column on the `0.0.0.0` (default route) rows — lower metric wins. The **host adapter (`eth1`) must win**.

```
Destination Gateway Genmask Flags Metric Iface
0.0.0.0 10.110.111.1 0.0.0.0 UG 100 eth1 ← host network should win
0.0.0.0 10.0.2.2 0.0.0.0 UG 700 eth0 ← NAT should lose
```

**Fix (survives reboots):** edit `/etc/network/interfaces` so the host adapter has the lower metric:

```
auto eth0
iface eth0 inet dhcp
metric 700

auto eth1
iface eth1 inet dhcp
metric 100
```

Then apply: `sudo ifdown eth0 && sudo ifup eth0` and re-check with `route -n`.

---

## 3 — Firewall blocks port 3000 after reboot
## 2 — Firewall blocks the port after reboot

**Symptom:** After a VM reboot the app gets `Socket Unreachable`, even though the server started fine.

**Why:** LG rigs block all non-whitelisted ports with `iptables`, and those rules live in RAM — every reboot wipes the port-3000 rule.
**Why:** LG rigs block all non-whitelisted ports with `iptables`, and those rules live in RAM — every reboot wipes any rule you added by hand.

The server runs on **8111**, which is whitelisted on a stock rig, so this should not bite you. It only comes back if you override `PORT` to something outside the whitelist.

**Check:** `sudo iptables -L INPUT -n --line-numbers | grep 3000` — no output means the port is blocked.
**Check:** `sudo iptables -L INPUT -n --line-numbers | grep 8111`, and confirm what the server is actually listening on with `ss -ltnp | grep node`.

**Fix — quick (one session):**

```bash
sudo iptables -I INPUT 1 -p tcp --dport 3000 -j ACCEPT
sudo iptables -I INPUT 1 -p tcp --dport 8111 -j ACCEPT
```

**Fix — permanent (survives reboots):** add the rule *first*, then save it.

```bash
sudo iptables -I INPUT 1 -p tcp --dport 3000 -j ACCEPT
sudo iptables -I INPUT 1 -p tcp --dport 8111 -j ACCEPT
sudo apt-get install -y iptables-persistent # answer "Yes" to save current rules
sudo netfilter-persistent save # writes /etc/iptables/rules.v4
```

**Fix — automatic:** you usually don't need to do any of the above. When you tap **Start Server** in the controller's Settings page, the app opens port 3000 over SSH right before launching the server, so it self-heals after a reboot.
**Fix — automatic:** tapping **Start Server** in the controller's Settings page also inserts the rule over SSH before launching the server. It's best-effort — if `sudo` fails there, the app logs a warning and carries on, because 8111 is normally open anyway.
162 changes: 79 additions & 83 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ A real-time multiplayer RPG built for [Liquid Galaxy](https://www.liquidgalaxy.e
│ │
│ ┌──────────────┐ ┌───────────────────┐│
│ │ Node.js │◄──│ Chromium ││
│ │ Server :3000│ │ (across screens) ││
│ │ Server :8111│ │ (across screens) ││
│ └──────┬───────┘ └───────────────────┘│
└─────────┼──────────────────────────────────┘
│ Socket.IO (WebSocket)
Expand All @@ -31,74 +31,54 @@ A real-time multiplayer RPG built for [Liquid Galaxy](https://www.liquidgalaxy.e

---

## Prerequisites
## Installation

### On the Liquid Galaxy machine
Installation happens once, on the **Liquid Galaxy master** (Ubuntu 16.04). After
that, everything — starting/stopping the server and launching the LG screens —
is driven from the controller app.

- Node.js v16.20.2 (via [nvm](https://github.com/nvm-sh/nvm) or [Volta](https://volta.sh/))
- `npm`
- `curl` (for the server health-check script)
### 1. Install Node.js 16 via nvm (on the LG master)

### On the player's phone

- Android 5.0+ device
- Flutter SDK ≥ 3.0.0 (only if building from source)

---

## Setup

You only install things once. After that, everything — starting/stopping the
server, opening the firewall port, and launching the LG screens — is driven from
the controller app. You don't normally touch a terminal on the rig.
```bash
curl -o- https://raw.githubusercontent.com/nvm-sh/nvm/v0.40.1/install.sh | bash
source ~/.nvm/nvm.sh
nvm install 16
nvm alias default 16
node -v # must print v16.x
npm -v # must print 8.x
```

### 1. Install the server (one-time, on the Liquid Galaxy machine)
### 2. Get the code

The controller expects the server scripts at `~/lg-rpg-server/scripts/`, so clone
the repo and create that symlink:
If a previous install exists and you want a clean reinstall, first run:
`pkill -9 -f server.js; sudo rm -rf ~/lg-rpg`

```bash
git clone https://github.com/LiquidGalaxyLAB/lg-rpg.git ~/lg-rpg
ln -s ~/lg-rpg/lg_rpg_server ~/lg-rpg-server
ln -sfn ~/lg-rpg/lg_rpg_server ~/lg-rpg-server
cd ~/lg-rpg-server
cp .env.example .env # configure it — see below
npm install
cp .env.example .env
```

That's it on the LG side — **don't** start the server or open the port by hand;
the controller does both for you (next section).

### Configuration (`.env`)
The `~/lg-rpg-server` symlink is required — the controller app expects the
server at that path.

| Variable | Purpose |
|---|---|
| `PORT` | Server port (default `3000`). |
| `TOTAL_SCREENS` | Number of LG screens (game screens + 1 leaderboard screen). |
| `MAX_PLAYERS` | Max players allowed in a lobby. |
| `CORS_ORIGIN` | Allowed web origin (`*` is fine for a local rig). |

#### AI Cheerleader (optional)
### 3. Verify the server — do not skip this step

The game ships with an AI commentary duo (Curly & Julie) that reacts to the match
live. It's **optional** — the game runs fine without it. It uses **Google Gemini**
to write the dialogue and **AWS Polly** to voice it, so it needs API keys:

| Variable | Purpose |
|---|---|
| `CHEERLEADER_ENABLED` | Set to `true` to turn the commentary on. |
| `GEMINI_API_KEY` | Google Gemini API key — **required** for the cheerleader to run; without it the commentary stays off. |
| `AWS_ACCESS_KEY_ID` | AWS access key for Polly text-to-speech. |
| `AWS_SECRET_ACCESS_KEY` | AWS secret key for Polly. |
| `AWS_REGION` | AWS region for Polly (defaults to `ap-south-1`). |
```bash
source ~/.nvm/nvm.sh
cd ~/lg-rpg-server
node server.js
```

Notes:
- With `CHEERLEADER_ENABLED=true` **and** a valid `GEMINI_API_KEY`, the commentary turns on. If either is missing, it silently stays off and the game plays normally.
- The Gemini key drives the **text**; the AWS keys drive the **voice**. If the AWS keys are missing, lines are still generated but go unvoiced (logged as a warning).
- Get a Gemini key from [Google AI Studio](https://aistudio.google.com/apikey), and AWS keys from the [AWS IAM console](https://console.aws.amazon.com/iam/) (the user needs `polly:SynthesizeSpeech` permission).
You must see the line `Server is running at port 8111`. Press `Ctrl+C` and
continue. Don't leave it running — the controller app starts and stops the
server for you.

### 2. Install the controller (on your phone)
### 4. Install the controller app (on your phone)

Install the APK, or build from source:
Install the APK on your Android phone, or build from source:

```bash
cd lg_rpg_controller
Expand All @@ -108,54 +88,70 @@ flutter run

---

## Running a session (from the controller)
## Configuration (`.env`) — optional

The game runs fine with the default `.env`. Editing `~/lg-rpg-server/.env` is
only needed for the **AI cheerleader** — a live commentary duo that reacts to
the match. To enable it, set:

1. Open the app → **Settings** → enter the LG machine's **IP, SSH username,
password, and screen count** → **Connect**.
2. Tap **Start the Server**. This automatically opens port 3000 on the rig (it
injects the firewall rule over SSH) and starts the Node server.
3. Tap **Launch Browser** to open the game across the LG screens.
4. Go back to **Home** → **Connect to Server** → pick a mode → **Play**.
5. When you're done, use **Stop the Server** and **Close Browser** from Settings.
- `CHEERLEADER_ENABLED=true`
- `GEMINI_API_KEY=` your key from [Google AI Studio](https://aistudio.google.com/apikey)

Because the controller opens the port every time you press **Start the Server**,
the firewall self-heals after a reboot — no manual `iptables` step needed.
If either is missing, the commentary stays off and the game plays normally. The
voice uses free Microsoft Edge TTS — no extra key needed.

---

## Running the server manually (optional / for development)
## The controller app

If you'd rather run the server yourself on the LG instead of from the controller,
you must open the firewall port yourself first — this is the same rule the
controller injects automatically:
| Page | What it does |
|---|---|
| **Home** | Once connected to the server and master, select a game mode and press Play Game. |
| **Loadout** | Pick your character and up to 4 items (power-ups + health) to use during matches. Each has a cooldown; tap a selected item again to deselect it. |
| **Map** | Map and Google Earth sync — fly to locations with 3D KML and orbit. |
| **LG Tasks** | Basic Liquid Galaxy tasks: show logo, clean KML, relaunch, reboot. |

```bash
sudo iptables -I INPUT 1 -p tcp --dport 3000 -j ACCEPT # only needed for manual starts
cd ~/lg-rpg-server
npm start
```
---

The server is then available at `http://<LG-IP>:3000`. To make the port survive
reboots without the controller, see [ISSUES.md](./ISSUES.md).
## Running the game

---
1. Open the app → **Settings** → fill in the master's **IP**, **username** (`lg`),
**password**, **SSH port** (`22`), and **screen number** (**3 or 5 only**) →
**Connect to Master**.
2. Tap **Start Server**. If anything is wrong (Node missing, invalid screen
number, crashed server), a message saying exactly what failed appears within
a few seconds.
3. Tap **Launch Browser** — the game appears across the LG screens.
4. Go back to **Home** → enter your player name → **Connect to Server**.
5. Select the game mode (the first player to connect is the host and picks the
mode) → **Play Game**.

## Networking: Known Issues
### Multiplayer

If a phone can't reach the server over the local network, it's usually one of three things: a hotspot routing trap, a VirtualBox adapter priority issue, or the firewall blocking port 3000 after a reboot.
- On **each** phone: **Settings** → same details (same screen number) → **Connect to Master**.
- From **one** phone: **Start Server** → **Launch Browser**.
- On **every** phone: **Home** → **Connect to Server**.
- The first phone to connect is the host — it picks the mode (and teams, in PvP) and presses **Play Game**.

See **[ISSUES.md](./ISSUES.md)** for symptoms, checks, and fixes for each.
### Changing the screen number (3 ↔ 5)

---
Order matters:

## Third-Party Assets
**Close Browser** → **Stop Server** → **Disconnect from Master** → enter the new
screen number → **Connect to Master** → **Start Server** → **Launch Browser**.

This project uses pixel-art assets created by independent artists under open licenses.
---

See **[CREDITS.md](./CREDITS.md)** for the full list of contributors, asset links, and license details.
## If something goes wrong

Error messages in the app state the actual cause — read them first. Server logs
live on the master in `~/lg-rpg-server/logs/`. For known issues and fixes, see
**[ISSUES.md](./ISSUES.md)**.

---

## Repository
## Third-Party Assets

This project uses pixel-art assets created by independent artists under open licenses.

[https://github.com/LiquidGalaxyLAB/lg-rpg](https://github.com/LiquidGalaxyLAB/lg-rpg)
See **[CREDITS.md](./CREDITS.md)** for the full list of contributors, asset links, and license details.
Loading