Skip to content

Commit

Permalink
Refactor Player Syncing
Browse files Browse the repository at this point in the history
  • Loading branch information
Insax committed Feb 6, 2024
1 parent 75d75ab commit 9dd534e
Show file tree
Hide file tree
Showing 4 changed files with 248 additions and 84 deletions.
6 changes: 2 additions & 4 deletions .github/workflows/main.yml
Original file line number Diff line number Diff line change
Expand Up @@ -46,13 +46,11 @@ jobs:
key: '${{ runner.OS }}-build-${{ hashFiles(''**/composer.lock'') }}'
- name: Copy .env
run: php -r "file_exists('.env') || copy('.env.example', '.env');" # If .env exist, we use that, if otherwise, copy .env.example to .env and use that instead
- name: Create DB File
run: touch database/database.sqlite
- name: Install Dependencies
if: steps.vendor-cache.outputs.cache-hit != 'true'
run: composer install -q --no-ansi --no-interaction --no-dev --no-progress --prefer-dist
- name: Generate key
run: php artisan key:generate
- name: Clear Config
run: php artisan config:clear
- name: Create an Archive For Release
uses: montudor/action-zip@v0.1.0
with:
Expand Down
85 changes: 65 additions & 20 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,14 +1,47 @@
# Palworld Admin Toll
# Palworld Admin Tool

## Description

An Admin tool receives information via rcon and displays them.

## Prerequisites

The following packages need to be installed:

1. php (8.1 or higher) with the following extensions
## Overview
1. [Installation Variations](#install-possibilities)
2. [Installation via Release (1 click install)](#installation-via-release)
3. [Full Installation](#full-installation)
1. [Prerequisites full Installation](#prerequisites-developerfull-installation)
2. [Install Steps](#installation-steps)
3. [Updating](#updating)

## Installation Variations
You can install this tool in the following ways
- Install via docker-compose
- Head over to https://github.com/Insax/palworld-admin-tool-docker and read the installation instructions
- Install using the latest [release](https://github.com/Insax/palworld-admin-tool/releases/latest) for Windows Users.
- Go to [Installation via Release](#installation-via-release) and follow the steps (1 click install)
- Install cloning the repository (Advanced/Developer Installation)
- Go to [Developer Setup](#developer-installation)


## Installation via Release
> :warning: **This way of installing does not support updating your installation and has limitations.**
#### This way of installing provides a fully working instance for testing purposes, its not meant to be used in real production.

#### If you like it go for the docker version or the full install.

Steps
1. Download the release as zip and extract it somewhere
2. Run the script install-start.ps1 using powershell.
3. Visit http://localhost

> :warning: **Once again, this is more of a Test installation than anything else.**
## Full Installation
This will provide you with everything to update or develop the app yourself.

### Prerequisites Developer/Full Installation
This application has some requirements that must be fullfilled in order to for everything to work properly.

1. PHP 8.1 with the following extensions enabled:**
- ctype
- curl
- dom
Expand All @@ -22,13 +55,14 @@ The following packages need to be installed:
- session
- tokenizer
- xml
15. composer (https://getcomposer.org/)
16. npm (20 or higher) https://nodejs.org/en/download
17. Supervisord or an equivalent or http://supervisord.org/
18. Nginx or an equivalent https://nginx.org/en/download.html
19. Any Mysql or Postgres Database that supports column type `enum`
- sqlite
1. composer (https://getcomposer.org/)
2. npm (20 or higher) https://nodejs.org/en/download
3. Supervisord or an equivalent http://supervisord.org/
4. Nginx or an equivalent https://nginx.org/en/download.html
5. [Optional] Any Mysql or Postgres Database, alternatively sqlite can be used.

## Installation
### Installation Steps

1. Clone the repository

Expand All @@ -50,24 +84,35 @@ The following packages need to be installed:
npm run build
```
4. Copy .env.example to .env and adjust the DB_HOST, DB_PORT, DB_USER, DB_DATABASE, DB_PASSWORD so it matches your setup

1. If you would like to use SQLITE set `DB_CONNECTION` to `sqlite` and delete the `DB_DATABASE` line.

```bash
cp .env.example .env
```
4. Generate an application key

5. Create the database tables in your already created database.

```bash
php artisan migrate --force
```

6. Generate an application key

```bash
php artisan key:generate
```
4. Create a job in supervisor or an equivalent tool that auto restarts and runs

7. Create a job in supervisor or an equivalent tool that auto restarts and runs

```bash
php artisan short-schedule:run --lifetime=60
```
5. Adjust the connections in config/rcon.php so they match you servers. Do not edit the default entry, it will not show up the application.
php artisan short-schedule:run
```
8. Configure your webserver, the content root is in `public`


6. Configure your webserver, the content root is in `public`
7. Visit the website, the installer should pop up.
## Updating
Rerun steps 2 - 5

## Running Tests

Expand Down
159 changes: 99 additions & 60 deletions app/Console/Commands/SyncPlayersCommand.php
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,8 @@

namespace App\Console\Commands;

use App\Gameserver\Communication\Responses\Response;
use App\Gameserver\Communication\Responses\ShowPlayersResponse;
use App\Models\JoinLeaveLog;
use App\Models\Player;
use App\Models\Server;
Expand All @@ -15,15 +17,11 @@ class SyncPlayersCommand extends Command

/**
* The name and signature of the console command.
*
* @var string
*/
protected $signature = 'pal:sync';

/**
* The console command description.
*
* @var string
*/
protected $description = 'Synchronizes Players from a PalWorldServer';

Expand All @@ -32,66 +30,107 @@ class SyncPlayersCommand extends Command
*/
public function handle()
{
$this->servers = Server::whereActive(true);
foreach ($this->servers as $server)
{
$result = Rcon::showPlayers($server);
var_dump($result);
}
/*
foreach (Server::whereActive(true)->get() as $server)
{
$onlinePlayers = array();
$result = RCON::getPlayers($server->rcon);
foreach ($result as $player) {
if($player['player_id'] == 00000000)
continue;
$onlinePlayers[] = $player['player_id'];
$player['online'] = true;
$player['server_id'] = $server->id;
$players = Player::where(['player_id' => $player['player_id'], 'server_id' => $player['server_id']])->first();
if(is_null($players))
{
$newPlayer = Player::create($player);
JoinLeaveLog::create(['player_id' => $newPlayer->id, 'action' => JoinLeaveLog::$PLAYER_JOINED]);
}
else
{
if($players->online == false)
{
JoinLeaveLog::create(['player_id' => $players->id, 'action' => JoinLeaveLog::$PLAYER_JOINED]);
}
$players->update($player);
}
}
$offlinePlayers = Player::where('server_id', $server->id)->whereNotIn('player_id', $onlinePlayers)->whereOnline(true)->get();
$this->servers = Server::whereActive(true)->with(['rconData', 'serverWhitelists', 'players'])->get();

foreach ($offlinePlayers as $offlinePlayer)
{
$offlinePlayer->update(['online' => false]);
JoinLeaveLog::create(['player_id' => $offlinePlayer->id, 'action' => JoinLeaveLog::$PLAYER_LEFT]);
foreach ($this->servers as $server) {
$response = Rcon::info($server);
if($response->getError() != 0) {
$this->handleUnreachableServer($server);
continue;
}

if($server->uses_whitelist)
{
$whitelist = ServerWhitelist::where('server_id', $server->id)->get();
$whitelistPlayers = array();
foreach ($whitelist as $whitelistItem) {
$whitelistPlayers[] = $whitelistItem->player_id;
}
$notWhitelistedPlayers = Player::where('server_id', $server->id)->whereNotIn('player_id', $whitelistPlayers)->get();
foreach ($notWhitelistedPlayers as $notWhitelistedPlayer) {
RCON::kickPlayer($server->rcon, $notWhitelistedPlayer->player_id);
$notWhitelistedPlayer->update(['online' => false]);
JoinLeaveLog::create(['player_id' => $notWhitelistedPlayer->id, 'action' => JoinLeaveLog::$PLAYER_KICKED_WHITELIST]);
}
if(!$server->online)
$server->update(['online' => true]);

$this->syncServerPlayers($server);
}
}

private function handleUnreachableServer(Server $server)
{
$server->shutting_down = false;
$server->online = false;
$this->handleOfflinePlayers($server, []);
}

private function syncServerPlayers(Server $server)
{
$onlinePlayers = $this->getOnlinePlayers($server);


$this->handleOfflinePlayers($server, $onlinePlayers);

if ($server->uses_whitelist) {
$this->handleNotWhitelistedPlayers($server);
}
}

private function getOnlinePlayers(Server $server): array
{
$onlinePlayersIDs = [];
$result = Rcon::showPlayers($server);

foreach ($result->getResult() as $player) {
if ($player['player_id'] === 00000000) continue;

$onlinePlayersIDs[] = $player['player_id'];
$this->updatePlayerStatus($player, $server);
}

return $onlinePlayersIDs;
}

private function updatePlayerStatus(array $player, Server $server): void
{
$playerData = [
'online' => true,
'server_id' => $server->id,
'player_id' => $player['player_id']
];

$playerModel = Player::firstOrNew($playerData);

if ($playerModel->exists){
if (!$playerModel->online) {
$this->logPlayerAction($playerModel, JoinLeaveLog::$PLAYER_JOINED);
}
$playerModel->update($playerData);
} else {
$playerModel = Player::create($playerData);
$this->logPlayerAction($playerModel, JoinLeaveLog::$PLAYER_JOINED);
}
}

private function handleOfflinePlayers(Server $server, array $onlinePlayers): void
{
$offlinePlayers = Player::where('server_id', $server->id)
->whereNotIn('player_id', $onlinePlayers)
->whereOnline(true)
->get();

foreach ($offlinePlayers as $offlinePlayer) {
$offlinePlayer->update(['online' => false]);
$this->logPlayerAction($offlinePlayer, JoinLeaveLog::$PLAYER_LEFT);
}
}

private function handleNotWhitelistedPlayers(Server $server): void
{
$whitelistIDs = $server->serverWhitelists->pluck('player_id')->toArray();

$notWhitelistedPlayers = Player::whereServerId($server->id)
->whereNotIn('player_id', $whitelistIDs)
->get();

foreach ($notWhitelistedPlayers as $player) {
Rcon::kickPlayer($server, $player->player_id);
$player->update(['online' => false]);
$this->logPlayerAction($player, JoinLeaveLog::$PLAYER_KICKED_WHITELIST);
}
*/
}

private function logPlayerAction(Player $player, string $action): void
{
JoinLeaveLog::create(['player_id' => $player->id, 'action' => $action]);
}
}
Loading

0 comments on commit 9dd534e

Please sign in to comment.