English | 中文
A native Palworld server management tab for GameAP. Uses WASM backend to proxy the official Palworld REST API, so passwords never reach the browser.
| Server | Players | Announce |
|---|---|---|
![]() |
![]() |
![]() |
| Player Management | World Settings |
|---|---|
![]() |
![]() |
- View server status, online players, performance metrics
- Kick, ban, unban players
- Send announcements
- Save world, graceful shutdown, force stop
- Independent REST config per server instance
- Admin-only operations
- Go 1.26+
- Node.js 20 or 22
- pnpm
- Docker Desktop (with WSL 2 enabled)
GameAP-Palworld-Plugin\
├─ frontend\ # Vue frontend
│ ├─ src\
│ │ ├─ index.ts
│ │ └─ PalworldTab.vue
│ ├─ dist\ # Frontend build output
│ ├─ package.json
│ └─ vite.config.js
├─ main.go # WASM backend
├─ go.mod
├─ go.sum
├─ dist\ # WASM build output
│ └─ gameap-palworld-plugin.wasm
├─ docs\ # Screenshots
├─ build.ps1 # One-click build script
└─ README.md
.\build.ps1Install frontend dependencies:
cd frontend
pnpm installIf pnpm reports esbuild error:
node node_modules\esbuild\install.jsBuild frontend:
pnpm run buildBuild WASM:
cd ..
$env:GOOS = 'wasip1'
$env:GOARCH = 'wasm'
$env:GOCACHE = "$(pwd)\go-cache"
$env:GOPROXY = 'https://proxy.golang.org,direct'
New-Item -ItemType Directory -Force 'dist' | Out-Null
go build -buildvcs=false -buildmode=c-shared -o 'dist\gameap-palworld-plugin.wasm' .If you can't access the official Go Proxy, use a mirror:
$env:GOPROXY = 'https://goproxy.cn,direct'Verify the build output:
Get-Item 'dist\gameap-palworld-plugin.wasm'- Login to GameAP
- Admin → Plugins → Upload/Install
- Select
dist\gameap-palworld-plugin.wasm - Click Install
$gameapUrl = 'http://localhost:8025'
$gameapAdmin = 'admin'
$gameapPassword = 'your-gameap-password'
$wasm = 'dist\gameap-palworld-plugin.wasm'
# Login to get token
$auth = Invoke-RestMethod -Method Post -Uri "$gameapUrl/api/auth/login" `
-ContentType 'application/json' `
-Body (@{login=$gameapAdmin;password=$gameapPassword} | ConvertTo-Json)
$token = if ($auth.token) {$auth.token} elseif ($auth.data.token) {$auth.data.token} else {$auth.access_token}
# Upload plugin
curl.exe -sS -X POST `
-H "Authorization: Bearer $token" `
-F "file=@$wasm;type=application/wasm" `
"$gameapUrl/api/admin/plugins/upload/install"After installing the plugin, configure the Palworld REST connection:
$headers = @{Authorization="Bearer $token"}
$pluginId = 'h4fkd4cukuk6o' # Check actual ID in plugin management page
$serverId = 1
$palPassword = 'your-palworld-admin-password'
$config = @{
base_url = 'http://host.docker.internal:8212/v1/api'
username = 'admin'
password = $palPassword
} | ConvertTo-Json
# Write config
Invoke-RestMethod -Method Post `
-Uri "$gameapUrl/api/plugins/$pluginId/servers/$serverId/config" `
-Headers $headers -ContentType 'application/json' -Body $config
# Verify password is not returned
Invoke-RestMethod `
-Uri "$gameapUrl/api/plugins/$pluginId/servers/$serverId/config" `
-Headers $headersResponse should only contain configured, base_url, username - no password.
Add to docker-compose.local.yml:
services:
gameap:
environment:
PLUGIN_HTTP_ALLOWED_SCHEMES: http,https
PLUGIN_HTTP_ALLOWED_HOSTS: host.docker.internalOnly allow host.docker.internal, do not open internal network access.
Read:
$base = "$gameapUrl/api/plugins/$pluginId/servers/$serverId"
Invoke-RestMethod "$base/info" -Headers $headers # Server info
Invoke-RestMethod "$base/players" -Headers $headers # Online players
Invoke-RestMethod "$base/settings" -Headers $headers # Settings
Invoke-RestMethod "$base/metrics" -Headers $headers # MetricsManagement:
# Send announcement
Invoke-RestMethod -Method Post -Uri "$base/announce" `
-Headers $headers -ContentType 'application/json' `
-Body (@{message='Test announcement'} | ConvertTo-Json)
# Save world
Invoke-RestMethod -Method Post -Uri "$base/save" -Headers $headers
# Kick player (use userId, not nickname)
$userId = 'steam_00000000000000000'
Invoke-RestMethod -Method Post -Uri "$base/kick" `
-Headers $headers -ContentType 'application/json' `
-Body (@{userid=$userId;message='Violation'} | ConvertTo-Json)
# Ban player
Invoke-RestMethod -Method Post -Uri "$base/ban" `
-Headers $headers -ContentType 'application/json' `
-Body (@{userid=$userId;message='Violation'} | ConvertTo-Json)
# Unban player
Invoke-RestMethod -Method Post -Uri "$base/unban" `
-Headers $headers -ContentType 'application/json' `
-Body (@{userid=$userId} | ConvertTo-Json)Server control:
# Graceful shutdown (stops GameAP service wrapper)
Invoke-RestMethod -Method Post -Uri "$base/shutdown" -Headers $headers
# Force stop
Invoke-RestMethod -Method Post -Uri "$base/stop" -Headers $headers
# Start server
Invoke-RestMethod -Method Post -Uri "$gameapUrl/api/servers/$serverId/start" -Headers $headersGraceful shutdown and force stop require GameAP service coordination, do not call Palworld REST directly.
When Palworld REST returns 401/403, the WASM backend converts it to 502, which won't trigger GameAP logout. If you experience this, check if the frontend is calling Palworld REST directly.
- Announcement cannot be empty
- Kick/ban must select online players
- Unban requires full
userId - Refresh page to check for errors
- Browser
Ctrl+F5to clear cache
This is caused by the Shawl wrapper auto-restart. Correct flow:
- Call Palworld
/shutdown - Immediately call GameAP
/api/servers/{id}/stop - Wait for service to stop and process count to be 0
Current Palworld version doesn't support this feature, plugin returns 501. Other features work normally.
cd frontend
node node_modules\esbuild\install.js
node node_modules\vite\bin\vite.js build$env:GOCACHE = "$(pwd)\go-cache"
go clean -cache
go build -buildvcs=false -buildmode=c-shared -o 'dist\gameap-palworld-plugin.wasm' .After modifying frontend or backend code:
# Build
.\build.ps1
# Uninstall old plugin in GameAP
# Upload new wasm
# Reconfigure REST connection
# Restart container
docker restart gameap
# Browser Ctrl+F5View logs:
docker logs gameap --tail 100 -f
docker logs gameap 2>&1 | Select-String -Pattern "plugin|palworld"Test Palworld REST connectivity:
$pair = [Convert]::ToBase64String([Text.Encoding]::ASCII.GetBytes("admin:your-password"))
Invoke-RestMethod 'http://localhost:8212/v1/api/info' -Headers @{Authorization="Basic $pair"}Check installed plugins:
Invoke-RestMethod "$gameapUrl/api/admin/plugins/loaded" -Headers $headersMIT




