# DirtSimpleP2P

DirtSimpleP2P is a Minecraft networking plugin that lets a public BungeeCord or Waterfall proxy reach backend Paper/Spigot servers that are behind NAT.

It is designed for server owners who want to host a backend Minecraft server at home or on a private network without opening the backend Minecraft port on their router.

The backend server connects outbound to the public proxy. Bungee/Waterfall then connects to a local port on the proxy machine, and DirtSimpleP2P carries the Minecraft TCP connection through the tunnel.

## Features

- One jar for both the proxy and backend server
- No home router port forwarding for backend Minecraft servers
- NAT-safe outbound tunnel from backend to proxy
- Multiple simultaneous Minecraft player connections
- Multiplexed framed TCP protocol
- Secret-token authentication
- Automatic token generation
- Optional TLS encryption
- Self-signed TLS certificate generation
- TLS certificate pinning on backend servers
- Fast reconnect with heartbeat checks
- Multiple public proxies can connect to one backend
- One proxy can expose multiple named backend servers
- `/dsp2p status` and `/dsp2p doctor` commands
- Temporary browser setup wizard with console-generated login key
- Simple `config.properties` setup

## Requirements

- Java 21
- Maven 3.8+
- BungeeCord or Waterfall for the public proxy side
- Paper or Spigot for the backend server side

## How It Works

Normal Minecraft proxy setup expects Bungee/Waterfall to connect directly to a backend server.

That does not work well when the backend server is at home behind NAT, unless you forward ports on the home router.

DirtSimpleP2P avoids that:

1. The proxy server runs DirtSimpleP2P in `frontend` mode.
2. The backend server runs DirtSimpleP2P in `backend` mode.
3. The backend opens an outbound tunnel to the public proxy.
4. The proxy exposes a local port such as `127.0.0.1:25566`.
5. Bungee/Waterfall sends players to that local port.
6. DirtSimpleP2P carries the TCP traffic through the tunnel to the backend server.

The public proxy needs an open tunnel port. The home/backend router does not need a Minecraft port forward.

## Build

From the project folder:

```bash
mvn clean package
```

The plugin jar is created at:

```bash
plugin/target/DirtSimpleP2P-0.1.0-SNAPSHOT.jar
```

Use this same jar on both the proxy and backend server.

## Basic Setup

You need:

- A public BungeeCord/Waterfall proxy, usually on a VPS
- A backend Paper/Spigot server, which can be at home or on a private network

### 1. Install On The Proxy

Put the jar in your BungeeCord/Waterfall `plugins` folder:

```text
DirtSimpleP2P-0.1.0-SNAPSHOT.jar
```

Start the proxy once, then stop it.

DirtSimpleP2P creates:

```text
plugins/DirtSimpleP2P/config.properties
```

### 2. Configure The Proxy

Use this on the BungeeCord/Waterfall side:

```properties
role=frontend
node.name=bungee-frontend

tunnel.listenHost=0.0.0.0
tunnel.listenPort=24445
tunnel.authToken=GENERATED_AUTOMATICALLY

tunnel.connectTimeoutMillis=5000
tunnel.heartbeatIntervalMillis=2000
tunnel.heartbeatTimeoutMillis=8000
tunnel.heartbeatMissesBeforeDisconnect=4
tunnel.reconnectInitialMillis=250
tunnel.reconnectMaxMillis=10000

tunnel.tls.enabled=false
tunnel.tls.allowInsecure=true
tunnel.tls.autoGenerate=true
tunnel.tls.keyStore=certs/frontend.p12
tunnel.tls.keyStorePassword=
tunnel.tls.requireClientAuth=false

webwizard.bindHost=127.0.0.1
webwizard.port=8765

routes=minecraft
route.minecraft.frontendBindHost=127.0.0.1
route.minecraft.frontendBindPort=25566
route.minecraft.backendNode=paper-backend
```

Notes:

- `tunnel.listenPort` is the public tunnel port that the backend connects to.
- `tunnel.authToken` is generated automatically on first start.
- Copy the generated token to the backend config.
- `route.minecraft.frontendBindPort` is the local port Bungee/Waterfall should use for this backend.
- `route.minecraft.backendNode` must match the backend server's `node.name`.

### 3. Point Bungee/Waterfall At The Local Tunnel

In your BungeeCord/Waterfall server config, set the backend address to:

```text
127.0.0.1:25566
```

Do not put your home server IP in the Bungee/Waterfall config. Bungee connects locally, and DirtSimpleP2P carries the traffic through the tunnel.

### 4. Install On The Backend

Put the same jar in your Paper/Spigot `plugins` folder:

```text
DirtSimpleP2P-0.1.0-SNAPSHOT.jar
```

Start the backend server once, then stop it.

DirtSimpleP2P creates:

```text
plugins/DirtSimpleP2P/config.properties
```

### 5. Configure The Backend

Use this on the Paper/Spigot side:

```properties
role=backend
node.name=paper-backend

frontends=proxy1
frontend.proxy1.connectHost=YOUR_PROXY_IP_OR_DOMAIN
frontend.proxy1.connectPort=24445
frontend.proxy1.tls.pinnedCertificateSha256=

tunnel.authToken=PASTE_THE_PROXY_TOKEN_HERE

tunnel.connectTimeoutMillis=5000
tunnel.heartbeatIntervalMillis=2000
tunnel.heartbeatTimeoutMillis=8000
tunnel.heartbeatMissesBeforeDisconnect=4
tunnel.reconnectInitialMillis=250
tunnel.reconnectMaxMillis=10000

tunnel.tls.enabled=false
tunnel.tls.allowInsecure=true
tunnel.tls.trustOnFirstUse=true

webwizard.bindHost=127.0.0.1
webwizard.port=8765

routes=minecraft
route.minecraft.backendTargetHost=127.0.0.1
route.minecraft.backendTargetPort=25565
```

Change:

- `frontend.proxy1.connectHost` to your public proxy IP or domain
- `frontend.proxy1.connectPort` to the proxy `tunnel.listenPort`
- `tunnel.authToken` to the token from the proxy config
- `route.minecraft.backendTargetPort` if your Paper/Spigot server is not listening on `25565`

### 6. Start Everything

Start the public proxy first.

Then start the backend Paper/Spigot server.

If the tunnel connects, the proxy log should show that a backend authenticated. Players connect to your normal public proxy address.

## TLS

TLS is optional, but recommended for real deployments.

On the proxy:

```properties
tunnel.tls.enabled=true
tunnel.tls.allowInsecure=false
tunnel.tls.autoGenerate=true
```

When TLS is enabled, the proxy can generate:

```text
plugins/DirtSimpleP2P/certs/frontend.p12
```

On the backend:

```properties
tunnel.tls.enabled=true
tunnel.tls.allowInsecure=false
tunnel.tls.trustOnFirstUse=true
```

On the first successful TLS connection, the backend saves the proxy certificate fingerprint:

```properties
frontend.proxy1.tls.pinnedCertificateSha256=
```

After that, the backend only trusts that same certificate.

For stricter security, copy the certificate fingerprint from the proxy log and paste it into the backend config before the first connection.

## Multi-Server Setups

### Two Public Proxies, One Backend

Use this when two public BungeeCord/Waterfall proxies should both reach the same backend server.

Backend config:

```properties
role=backend
node.name=survival-backend

frontends=proxy1,proxy2
frontend.proxy1.connectHost=proxy1.example.com
frontend.proxy1.connectPort=24445
frontend.proxy1.tls.pinnedCertificateSha256=
frontend.proxy2.connectHost=proxy2.example.com
frontend.proxy2.connectPort=24445
frontend.proxy2.tls.pinnedCertificateSha256=

routes=minecraft
route.minecraft.backendTargetHost=127.0.0.1
route.minecraft.backendTargetPort=25565
```

Each proxy config:

```properties
role=frontend
node.name=proxy1

routes=minecraft
route.minecraft.frontendBindHost=127.0.0.1
route.minecraft.frontendBindPort=25566
route.minecraft.backendNode=survival-backend
```

Each proxy listens for tunnel connections. The backend connects outbound to both proxies.

### One Proxy, Multiple Backends

Use this when one proxy should expose more than one backend server.

Proxy config:

```properties
role=frontend
node.name=main-proxy

routes=survival,lobby
route.survival.frontendBindHost=127.0.0.1
route.survival.frontendBindPort=25566
route.survival.backendNode=survival-backend
route.lobby.frontendBindHost=127.0.0.1
route.lobby.frontendBindPort=25567
route.lobby.backendNode=lobby-backend
```

Bungee/Waterfall server entries:

```text
survival -> 127.0.0.1:25566
lobby    -> 127.0.0.1:25567
```

Survival backend config:

```properties
role=backend
node.name=survival-backend

frontends=main
frontend.main.connectHost=YOUR_PROXY_IP
frontend.main.connectPort=24445

routes=survival
route.survival.backendTargetHost=127.0.0.1
route.survival.backendTargetPort=25565
```

Lobby backend config:

```properties
role=backend
node.name=lobby-backend

frontends=main
frontend.main.connectHost=YOUR_PROXY_IP
frontend.main.connectPort=24445

routes=lobby
route.lobby.backendTargetHost=127.0.0.1
route.lobby.backendTargetPort=25565
```

All nodes in the same private tunnel group must use the same `tunnel.authToken`.

## Commands

Run from the server console or in game:

```text
/dsp2p status
```

Shows role, node name, whether the tunnel is connected, connected tunnel count, active streams, TLS state, and the latest event.

```text
/dsp2p doctor
```

Checks the config and prints setup/security hints.

```text
/dsp2p webwizard
```

Toggles a temporary browser setup wizard.

When the wizard starts, the server console prints:

```text
Please sign in with this key: <temporary-key>
```

Open the printed URL in a browser and sign in with the temporary key.

The wizard provides guided sections for:

- server mode and node name
- proxy listener settings
- backend proxy connections
- Minecraft routes
- tunnel token and TLS
- heartbeat and reconnect settings
- advanced raw config keys

The web UI includes:

- `Save Config`
- `Save + Reload DirtSimpleP2P`
- `Reload Current Config`
- `Generate New Token`

Reloading from the wizard restarts the DirtSimpleP2P tunnel agent with the latest config. It does not run a full Minecraft server reload.

Run `/dsp2p webwizard` again to stop the wizard when setup is complete.

By default, the wizard binds to:

```properties
webwizard.bindHost=127.0.0.1
webwizard.port=8765
```

Keep it on `127.0.0.1` unless you understand the risk of exposing a config editor over the network.

Permission:

```text
dirtsimplep2p.command
```

On Paper/Spigot, the permission defaults to server operators. On BungeeCord/Waterfall, give this permission to staff who should see tunnel diagnostics.

## Firewall Notes

On the public proxy/VPS, allow the tunnel port:

```text
24445/tcp
```

On the home router, you do not need to forward the backend Minecraft port:

```text
25565/tcp
```

The backend server must be allowed to make outbound TCP connections to the proxy.

## Reconnect Behavior

DirtSimpleP2P is tuned to detect real drops quickly while still tolerating short latency spikes.

Default behavior:

- Heartbeat every `2` seconds
- Status warning if heartbeats are delayed
- Disconnect after about `8` seconds without tunnel traffic
- First reconnect retry after about `250ms`
- Exponential backoff up to `10` seconds while the proxy is unreachable

Useful config values:

```properties
tunnel.heartbeatIntervalMillis=2000
tunnel.heartbeatTimeoutMillis=8000
tunnel.heartbeatMissesBeforeDisconnect=4
tunnel.reconnectInitialMillis=250
tunnel.reconnectMaxMillis=10000
```

If your network has frequent latency spikes, increase:

```properties
tunnel.heartbeatMissesBeforeDisconnect=5
```

## Troubleshooting

### Token Problems

The token must match on both sides.

If the plugin generated a token on the proxy, copy that generated value into the backend config.

### Backend Says Connection Refused

Check that:

- The proxy server is running
- DirtSimpleP2P started on the proxy
- `frontend.proxy1.connectHost` points to the proxy IP or domain
- `frontend.proxy1.connectPort` matches the proxy `tunnel.listenPort`
- The proxy firewall allows the tunnel port

### Bungee Cannot Reach The Backend

Make sure the Bungee/Waterfall backend server entry points to the local tunnel port:

```text
127.0.0.1:25566
```

Also check that the backend Paper/Spigot server is running and the tunnel is connected.

### Players Disconnect Or Freeze

Check both server logs for:

- reconnect messages
- heartbeat timeout messages
- stream reset messages
- backend target connection failed

## Security Notes

Keep `tunnel.authToken` private. Anyone with the token can attempt to authenticate to the tunnel.

For local testing, insecure mode is available:

```properties
tunnel.tls.enabled=false
tunnel.tls.allowInsecure=true
```

For real deployments, enable TLS:

```properties
tunnel.tls.enabled=true
tunnel.tls.allowInsecure=false
```

Trust-on-first-use is convenient, but the first TLS connection is the sensitive moment. For the strictest setup, manually copy the proxy certificate fingerprint into the backend config before the first connection.

The Web Wizard is intended as a temporary setup tool. It uses a random key printed to the server console and should be stopped when configuration is complete.

The reload button reloads DirtSimpleP2P itself. It intentionally does not run Paper/Bukkit `/reload`, because full server reloads are risky on production Minecraft servers.
