Skip to content

Player Counter: Added Palworld Support#125

Draft
makepeacej wants to merge 3 commits intopelican-dev:mainfrom
makepeacej:feature/palworld-player-count-support
Draft

Player Counter: Added Palworld Support#125
makepeacej wants to merge 3 commits intopelican-dev:mainfrom
makepeacej:feature/palworld-player-count-support

Conversation

@makepeacej
Copy link
Copy Markdown

@makepeacej makepeacej commented Apr 24, 2026

This pull request introduces support for Palworld dedicated servers in the Player Counter plugin. Palworld does not
implement the Steam A2S query protocol despite binding the query port, so this adds a new interface and schema to
support REST API-based querying with server credential context.

  • Added a Palworld Schema
  • Adjusted the setup to grab the admin password from the server's environment.

*Assisted by Claude to match existing patterns

Relates to #124

Summary by CodeRabbit

  • New Features
    • Added support for Palworld game servers. The application can now query and display server hostname, current and maximum player counts, and active player information from Palworld servers.

@coderabbitai
Copy link
Copy Markdown
Contributor

coderabbitai Bot commented Apr 24, 2026

Warning

Rate limit exceeded

@makepeacej has exceeded the limit for the number of commits that can be reviewed per hour. Please wait 42 minutes and 18 seconds before requesting another review.

Your organization is not enrolled in usage-based pricing. Contact your admin to enable usage-based pricing to continue reviews beyond the rate limit, or try again in 42 minutes and 18 seconds.

⌛ How to resolve this issue?

After the wait time has elapsed, a review can be triggered using the @coderabbitai review command as a PR comment. Alternatively, push new commits to this PR.

We recommend that you space out your commits to avoid hitting the rate limit.

🚦 How do rate limits work?

CodeRabbit enforces hourly rate limits for each developer per organization.

Our paid plans have higher rate limits than the trial, open-source and free plans. In all cases, we re-allow further reviews after a brief timeout.

Please see our FAQ for further information.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: a21b7184-3edd-4936-a96c-0b755c6eb2e2

📥 Commits

Reviewing files that changed from the base of the PR and between 9c05266 and 63ccc36.

📒 Files selected for processing (1)
  • player-counter/src/Extensions/Query/Schemas/PalworldQueryTypeSchema.php
📝 Walkthrough

Walkthrough

This PR introduces support for Palworld servers by adding a new REST-based query schema that authenticates with the server's admin password and retrieves player and server metrics from the Palworld API. It extends the query architecture with a ServerAwareQueryTypeSchemaInterface to enable schemas that require server context, updates the query execution logic to route to server-aware schemas appropriately, and registers the new Palworld schema in the plugin provider.

Changes

Cohort / File(s) Summary
Palworld Query Schema
src/Extensions/Query/Schemas/PalworldQueryTypeSchema.php
New schema implementing authenticated REST API integration for Palworld servers. Retrieves admin password from server variables, makes requests to /v1/api/players and /v1/api/metrics endpoints with 5-second timeout, normalizes response data into hostname, map, player counts, and player list format.
Server-Aware Interface
src/Extensions/Query/ServerAwareQueryTypeSchemaInterface.php
New interface extending QueryTypeSchemaInterface defining contract for processWithServer(Server, ip, port) method to enable schemas requiring server context beyond IP and port.
Query Execution Logic
src/Models/GameQuery.php
Updated runQuery method to detect and invoke ServerAwareQueryTypeSchemaInterface implementations with server context; existing schemas continue using process(ip, port) path.
Service Registration
src/Providers/PlayerCounterPluginProvider.php
Registers PalworldQueryTypeSchema in QueryTypeService singleton during plugin provider initialization.

Sequence Diagram

sequenceDiagram
    participant Client as Client
    participant GameQuery as GameQuery
    participant Schema as PalworldQueryTypeSchema
    participant API as Palworld API
    participant DB as Server Model

    Client->>GameQuery: runQuery(Server)
    GameQuery->>Schema: Determine schema type
    alt ServerAwareQueryTypeSchemaInterface
        GameQuery->>DB: Retrieve server config
        GameQuery->>Schema: processWithServer(Server, IP, Port)
        Schema->>DB: Extract ADMIN_PASSWORD
        Schema->>API: GET /v1/api/players (with auth)
        API-->>Schema: Players data
        Schema->>API: GET /v1/api/metrics (with auth)
        API-->>Schema: Server metrics
        Schema->>Schema: Transform & normalize response
        Schema-->>GameQuery: Normalized snapshot
    else Standard QueryTypeSchemaInterface
        GameQuery->>Schema: process(IP, Port)
        Schema-->>GameQuery: Result
    end
    GameQuery-->>Client: Query result
Loading

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~20 minutes

Possibly related issues

Possibly related PRs

Poem

🐰 Hop hop, the Palworld arrives at last!
With REST APIs and admin passwords cast—
Server-aware schemas take their rightful flight,
Players and metrics gleaming bright! ✨
A counter fit for every Pal in sight. 🎮

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 37.50% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Title check ✅ Passed The title accurately summarizes the primary change: adding Palworld support to the Player Counter plugin, which is the main objective of this changeset.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 2

🧹 Nitpick comments (1)
player-counter/src/Extensions/Query/Schemas/PalworldQueryTypeSchema.php (1)

60-62: Hardcoded map name and player cap fallback.

'Palpagos Islands' is Palworld's vanilla world name and 32 is the default server cap; both can differ for modded/custom servers. If a suitable field is returned by /v1/api/metrics (or another endpoint), prefer dynamic values; otherwise, consider translating the map label or making it configurable. Non-blocking.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@player-counter/src/Extensions/Query/Schemas/PalworldQueryTypeSchema.php`
around lines 60 - 62, The code currently hardcodes the map name and fallback max
player cap ('Palpagos Islands' and 32) in the array built in
PalworldQueryTypeSchema; replace those literals with dynamic values by checking
$metrics (e.g. use $metrics['map'] or $metrics['world_name'] if present) and
prefer a configurable fallback from a config/service (or translate/label the map
name) and use $metrics['maxplayers'] ?? config('palworld.default_max_players')
instead of the literal 32; update the array keys 'map' and 'max_players' to pull
from these sources while leaving 'current_players' => count($players) unchanged.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@player-counter/src/Extensions/Query/Schemas/PalworldQueryTypeSchema.php`:
- Around line 47-64: The array and return key/value pairs use vertical alignment
around the => operator; update the mappings in the array_map callback (the
$players mapping using 'id' => and 'name' =>) and the returned associative array
keys ('hostname' =>, 'map' =>, 'current_players' =>, 'max_players' =>, 'players'
=>) to remove extra spaces so each => has a single space on both sides (collapse
multiple spaces to one) to conform with Pint's default preset.
- Around line 53-64: The code reads wrong metric keys and skips status checks:
before calling ->json() on the metrics response, check ->ok() (like the players
call) and handle non-OK responses; then read the actual API fields from the
decoded $metrics array—use 'currentplayernum' for current players and
'maxplayernum' for max players (fall back to count($players) and 32 respectively
if absent), and do not attempt to read a non-existent 'servername' (use
$server->name as the hostname fallback). Locate the metrics HTTP call and the
return array in PalworldQueryTypeSchema (the $metrics variable and the returned
'hostname','current_players','max_players' entries) and apply these changes.

---

Nitpick comments:
In `@player-counter/src/Extensions/Query/Schemas/PalworldQueryTypeSchema.php`:
- Around line 60-62: The code currently hardcodes the map name and fallback max
player cap ('Palpagos Islands' and 32) in the array built in
PalworldQueryTypeSchema; replace those literals with dynamic values by checking
$metrics (e.g. use $metrics['map'] or $metrics['world_name'] if present) and
prefer a configurable fallback from a config/service (or translate/label the map
name) and use $metrics['maxplayers'] ?? config('palworld.default_max_players')
instead of the literal 32; update the array keys 'map' and 'max_players' to pull
from these sources while leaving 'current_players' => count($players) unchanged.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: 85bff950-3d20-413b-84be-549eb638dbad

📥 Commits

Reviewing files that changed from the base of the PR and between 1eb679c and 9c05266.

📒 Files selected for processing (4)
  • player-counter/src/Extensions/Query/Schemas/PalworldQueryTypeSchema.php
  • player-counter/src/Extensions/Query/ServerAwareQueryTypeSchemaInterface.php
  • player-counter/src/Models/GameQuery.php
  • player-counter/src/Providers/PlayerCounterPluginProvider.php
📜 Review details
🧰 Additional context used
🪛 GitHub Actions: Lint
player-counter/src/Extensions/Query/Schemas/PalworldQueryTypeSchema.php

[error] 1-1: Laravel Pint style check failed: 1 style issue found in 210 files. Failed at PalworldQueryTypeSchema.php.

🪛 PHPMD (2.15.0)
player-counter/src/Extensions/Query/Schemas/PalworldQueryTypeSchema.php

[warning] 22-22: Avoid unused parameters such as '$ip'. (undefined)

(UnusedFormalParameter)


[warning] 22-22: Avoid unused parameters such as '$port'. (undefined)

(UnusedFormalParameter)

🔇 Additional comments (4)
player-counter/src/Extensions/Query/Schemas/PalworldQueryTypeSchema.php (1)

22-25: Intentional null-return fallback — PHPMD warnings are false positives.

The PHPMD UnusedFormalParameter warnings on $ip/$port can be safely ignored here: the method signature is fixed by QueryTypeSchemaInterface::process(), and returning null from the non-server-aware path is the documented fallback for when auth context is unavailable.

player-counter/src/Extensions/Query/ServerAwareQueryTypeSchemaInterface.php (1)

1-11: LGTM!

Clean interface design. Extending QueryTypeSchemaInterface preserves backward compatibility (implementors must still provide process()), and the return-type contract on processWithServer() matches the parent's process() signature so both code paths in GameQuery::runQuery() yield the same shape.

player-counter/src/Providers/PlayerCounterPluginProvider.php (1)

14-14: LGTM!

Registration follows the same pattern as the other default schemas; QueryTypeService::register() accepts any QueryTypeSchemaInterface, so ServerAwareQueryTypeSchemaInterface implementors are fine here.

Also applies to: 39-39

player-counter/src/Models/GameQuery.php (1)

63-68: LGTM — branching preserves null-safety.

Since QueryTypeService::get() can return null, the instanceof check cleanly falls through to the existing $schema?->process($ip, $port) null-safe call. Behavior for all previously registered (non-server-aware) schemas is unchanged.

Comment on lines +53 to +64
$metrics = Http::timeout(5)
->withBasicAuth('admin', $adminPassword)
->get("http://{$ip}:{$port}/v1/api/metrics")
->json();

return [
'hostname' => $metrics['servername'] ?? $server->name,
'map' => 'Palpagos Islands',
'current_players' => count($players),
'max_players' => $metrics['maxplayers'] ?? 32,
'players' => $players,
];
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🔴 Critical

🧩 Analysis chain

🌐 Web query:

Palworld dedicated server REST API endpoints /v1/api/players /v1/api/metrics response fields

💡 Result:

Palworld dedicated server provides a REST API at base URL http://localhost:8212/v1/api (configurable port). Enable with RESTAPIEnabled=True in PalWorldSettings.ini and uses HTTP Basic Auth (username: admin, password: AdminPassword). /v1/api/players (GET): Returns list of online players. Response schema (array of objects): - name: string (player name) - accountName: string (platform account name) - playerId: string - userId: string - ip: string (IP address) - ping: number - location_x: number - location_y: number - level: integer (game level) - building_count: integer (buildings owned) Confirmed in official docs (result 9) and hosting guide (result 10). /v1/api/metrics (GET): Returns server performance metrics. Response schema: - serverfps: integer (server FPS) - currentplayernum: integer (current players) - serverframetime: number (frame time ms) - maxplayernum: integer (max players) - uptime: integer (uptime seconds) - basecampnum: integer (base camps) - days: integer (in-game days) Confirmed in official docs (result 11) and multiple references (results 4,10,14). API not for public internet exposure; use LAN or proxy. Other endpoints include /info, /settings, /kick, /ban, etc.

Citations:


Fix incorrect Palworld metrics API field names—the code will silently fail.

The metrics response contains two critical issues:

  1. Field names are incorrect and don't exist in the actual API response:

    • Code accesses $metrics['servername']—the /v1/api/metrics endpoint does not return a servername field
    • Code accesses $metrics['maxplayers']—the field is actually maxplayernum in the API response
    • Full actual response schema: serverfps, currentplayernum, serverframetime, maxplayernum, uptime, basecampnum, days
    • This causes the code to always fall back to defaults ($server->name and 32), never retrieving actual server metrics
  2. Missing .ok() check (like the players call at line 42): If the endpoint returns 4xx/5xx, ->json() may decode an error body and yield unexpected values. Add a response status check before decoding.

Suggested fix
-            $metrics = Http::timeout(5)
-                ->withBasicAuth('admin', $adminPassword)
-                ->get("http://{$ip}:{$port}/v1/api/metrics")
-                ->json();
-
+            $metricsResponse = Http::timeout(5)
+                ->withBasicAuth('admin', $adminPassword)
+                ->get("http://{$ip}:{$port}/v1/api/metrics");
+            $metrics = $metricsResponse->ok() ? (array) $metricsResponse->json() : [];
+
             return [
-                'hostname'        => $metrics['servername'] ?? $server->name,
+                'hostname'        => $server->name,
                 'map'             => 'Palpagos Islands',
                 'current_players' => count($players),
-                'max_players'     => $metrics['maxplayers'] ?? 32,
+                'max_players'     => $metrics['maxplayernum'] ?? 32,
                 'players'         => $players,
             ];

Note: The API does not provide a servername field, only server performance metrics.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@player-counter/src/Extensions/Query/Schemas/PalworldQueryTypeSchema.php`
around lines 53 - 64, The code reads wrong metric keys and skips status checks:
before calling ->json() on the metrics response, check ->ok() (like the players
call) and handle non-OK responses; then read the actual API fields from the
decoded $metrics array—use 'currentplayernum' for current players and
'maxplayernum' for max players (fall back to count($players) and 32 respectively
if absent), and do not attempt to read a non-existent 'servername' (use
$server->name as the hostname fallback). Locate the metrics HTTP call and the
return array in PalworldQueryTypeSchema (the $metrics variable and the returned
'hostname','current_players','max_players' entries) and apply these changes.

makepeaceJ and others added 2 commits April 24, 2026 15:35
- hostname now uses $server->name directly (metrics has no servername field)
- max_players now reads maxplayernum from metrics (not maxplayers)

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
@makepeacej makepeacej marked this pull request as draft April 24, 2026 21:45
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant