mirror of
https://github.com/YGGverse/HLState.git
synced 2025-02-05 19:54:13 +00:00
implement separated server pages, replace hlservers to masters tracking
This commit is contained in:
parent
6f760e8ac7
commit
dc0048d32d
4
.env
4
.env
@ -58,7 +58,3 @@ APP_THEME="default"
|
||||
|
||||
# Masters list, server:port comma separated
|
||||
APP_MASTERS=""
|
||||
|
||||
# Servers registry, URL or filepath
|
||||
# https://github.com/YGGverse/HLServers
|
||||
APP_HLSERVERS="https://raw.githubusercontent.com/YGGverse/HLServers/main/config.json"
|
||||
|
@ -44,7 +44,8 @@
|
||||
"twig/extra-bundle": "^3.8",
|
||||
"twig/intl-extra": "^3.8",
|
||||
"twig/twig": "^3.8",
|
||||
"xpaw/php-source-query-class": "^2.1"
|
||||
"xpaw/php-source-query-class": "^2.1",
|
||||
"yggverse/hl": "^1.0"
|
||||
},
|
||||
"config": {
|
||||
"allow-plugins": {
|
||||
|
@ -8,7 +8,6 @@ parameters:
|
||||
app.name: '%env(APP_NAME)%'
|
||||
app.theme: '%env(APP_THEME)%'
|
||||
app.masters: '%env(APP_MASTERS)%'
|
||||
app.hlservers: '%env(APP_HLSERVERS)%'
|
||||
app.meta.title: '%env(APP_META_TITLE)%'
|
||||
app.meta.description: '%env(APP_META_DESCRIPTION)%'
|
||||
app.meta.keywords: '%env(APP_META_KEYWORDS)%'
|
||||
|
31
migrations/Version20240111202908.php
Normal file
31
migrations/Version20240111202908.php
Normal file
@ -0,0 +1,31 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace DoctrineMigrations;
|
||||
|
||||
use Doctrine\DBAL\Schema\Schema;
|
||||
use Doctrine\Migrations\AbstractMigration;
|
||||
|
||||
/**
|
||||
* Auto-generated Migration: Please modify to your needs!
|
||||
*/
|
||||
final class Version20240111202908 extends AbstractMigration
|
||||
{
|
||||
public function getDescription(): string
|
||||
{
|
||||
return '';
|
||||
}
|
||||
|
||||
public function up(Schema $schema): void
|
||||
{
|
||||
// this up() migration is auto-generated, please modify it to your needs
|
||||
$this->addSql('CREATE TABLE server (id INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, crc32server BIGINT NOT NULL, added BIGINT NOT NULL, updated BIGINT NOT NULL, online BIGINT NOT NULL, host VARCHAR(255) NOT NULL, port INTEGER NOT NULL)');
|
||||
}
|
||||
|
||||
public function down(Schema $schema): void
|
||||
{
|
||||
// this down() migration is auto-generated, please modify it to your needs
|
||||
$this->addSql('DROP TABLE server');
|
||||
}
|
||||
}
|
@ -11,6 +11,8 @@ use Symfony\Component\HttpFoundation\Request;
|
||||
|
||||
use App\Entity\Online;
|
||||
use App\Entity\Player;
|
||||
use App\Entity\Server;
|
||||
|
||||
use Doctrine\ORM\EntityManagerInterface;
|
||||
|
||||
class CrontabController extends AbstractController
|
||||
@ -25,37 +27,133 @@ class CrontabController extends AbstractController
|
||||
)]
|
||||
public function index(
|
||||
?Request $request,
|
||||
TranslatorInterface $translatorInterface,
|
||||
EntityManagerInterface $entityManagerInterface
|
||||
): Response
|
||||
{
|
||||
// Get HLServers config
|
||||
if ($hlservers = file_get_contents($this->getParameter('app.hlservers')))
|
||||
// Prevent multi-thread execution
|
||||
$semaphore = sem_get(
|
||||
crc32(
|
||||
__DIR__ . '.controller.crontab.index',
|
||||
), 1
|
||||
);
|
||||
|
||||
if (false === sem_acquire($semaphore, true))
|
||||
{
|
||||
$hlservers = json_decode($hlservers);
|
||||
return new Response(
|
||||
$translatorInterface->trans('Process locked by another thread')
|
||||
);
|
||||
}
|
||||
|
||||
else
|
||||
// Get new servers from masters
|
||||
foreach ((array) explode(',', $this->getParameter('app.masters')) as $master)
|
||||
{
|
||||
$hlservers = [];
|
||||
if (!$host = parse_url($master, PHP_URL_HOST)) // @TODO IPv6 https://bugs.php.net/bug.php?id=72811
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
if (!$port = parse_url($master, PHP_URL_PORT))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
// Connect master node
|
||||
$node = new \Yggverse\Hl\Xash3D\Master($host, $port, 1);
|
||||
|
||||
foreach ((array) $node->getServersIPv6() as $key => $value)
|
||||
{
|
||||
// Generate server identity
|
||||
$crc32server = crc32(
|
||||
$key
|
||||
);
|
||||
|
||||
// Check server does not exist yet
|
||||
$server = $entityManagerInterface->getRepository(Server::class)->findOneBy(
|
||||
[
|
||||
'crc32server' => $crc32server
|
||||
]
|
||||
);
|
||||
|
||||
// Server exist, just update
|
||||
if ($server)
|
||||
{
|
||||
$server->setUpdated(
|
||||
time()
|
||||
);
|
||||
|
||||
$server->setOnline(
|
||||
time()
|
||||
);
|
||||
|
||||
$entityManagerInterface->persist(
|
||||
$server
|
||||
);
|
||||
|
||||
$entityManagerInterface->flush();
|
||||
|
||||
continue;
|
||||
}
|
||||
|
||||
// Server does not exist, create new record
|
||||
$server = new Server();
|
||||
|
||||
$server->setCrc32server(
|
||||
$crc32server
|
||||
);
|
||||
|
||||
$server->setHost(
|
||||
$value['host']
|
||||
);
|
||||
|
||||
$server->setPort(
|
||||
$value['port']
|
||||
);
|
||||
|
||||
$server->setAdded(
|
||||
time()
|
||||
);
|
||||
|
||||
$server->setUpdated(
|
||||
time()
|
||||
);
|
||||
|
||||
$server->setOnline(
|
||||
time()
|
||||
);
|
||||
|
||||
$entityManagerInterface->persist(
|
||||
$server
|
||||
);
|
||||
|
||||
$entityManagerInterface->flush();
|
||||
}
|
||||
}
|
||||
|
||||
// Collect servers info
|
||||
$servers = [];
|
||||
|
||||
foreach ($hlservers as $hlserver)
|
||||
foreach ((array) $entityManagerInterface->getRepository(Server::class)->findBy(
|
||||
[
|
||||
'crc32server' => (int) $request->get('crc32server')
|
||||
],
|
||||
[
|
||||
'id' => 'ASC'
|
||||
],
|
||||
) as $server)
|
||||
{
|
||||
try
|
||||
{
|
||||
$server = new \xPaw\SourceQuery\SourceQuery();
|
||||
$query = new \xPaw\SourceQuery\SourceQuery();
|
||||
|
||||
$server->Connect(
|
||||
$hlserver->host,
|
||||
$hlserver->port
|
||||
$query->Connect(
|
||||
false === filter_var($server->getHost(), FILTER_VALIDATE_IP, FILTER_FLAG_IPV6) ? $server->getHost() : "[{$server->getHost()}]",
|
||||
$server->port
|
||||
);
|
||||
|
||||
if ($server->Ping())
|
||||
if ($query->Ping())
|
||||
{
|
||||
if ($info = (array) $server->GetInfo())
|
||||
if ($info = (array) $query->GetInfo())
|
||||
{
|
||||
// Filter response
|
||||
$bots = isset($info['Bots']) && $info['Bots'] > 0 ? (int) $info['Bots'] : 0;
|
||||
@ -64,7 +162,7 @@ class CrontabController extends AbstractController
|
||||
|
||||
// Generate CRC32 server ID
|
||||
$crc32server = crc32(
|
||||
$hlserver->host . ':' . $hlserver->port
|
||||
$server->host . ':' . $server->port
|
||||
);
|
||||
|
||||
// Get last online value
|
||||
@ -121,7 +219,7 @@ class CrontabController extends AbstractController
|
||||
// Update player stats
|
||||
if ($players)
|
||||
{
|
||||
foreach ((array) $server->GetPlayers() as $session)
|
||||
foreach ((array) $query->GetPlayers() as $session)
|
||||
{
|
||||
// Validate fields
|
||||
if
|
||||
@ -231,7 +329,7 @@ class CrontabController extends AbstractController
|
||||
|
||||
finally
|
||||
{
|
||||
$server->Disconnect();
|
||||
$query->Disconnect();
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -11,6 +11,8 @@ use Symfony\Component\HttpFoundation\Request;
|
||||
|
||||
use App\Entity\Online;
|
||||
use App\Entity\Player;
|
||||
use App\Entity\Server;
|
||||
|
||||
use Doctrine\ORM\EntityManagerInterface;
|
||||
|
||||
class MainController extends AbstractController
|
||||
@ -28,139 +30,56 @@ class MainController extends AbstractController
|
||||
EntityManagerInterface $entityManagerInterface
|
||||
): Response
|
||||
{
|
||||
// Prepare request
|
||||
if ('online' == $request->get('sort') && in_array($request->get('field'), ['time','players','bots','total']))
|
||||
{
|
||||
$field = $request->get('field');
|
||||
}
|
||||
|
||||
else if ('players' == $request->get('sort') && in_array($request->get('field'), ['name','frags','joined','online']))
|
||||
{
|
||||
$field = $request->get('field');
|
||||
}
|
||||
|
||||
else
|
||||
{
|
||||
$field = 'time';
|
||||
}
|
||||
|
||||
if (in_array($request->get('order'), ['asc','desc']))
|
||||
{
|
||||
$order = $request->get('order');
|
||||
}
|
||||
|
||||
else
|
||||
{
|
||||
$order = 'desc';
|
||||
}
|
||||
|
||||
// Get HLServers config
|
||||
if ($hlservers = file_get_contents($this->getParameter('app.hlservers')))
|
||||
{
|
||||
$hlservers = json_decode($hlservers);
|
||||
}
|
||||
|
||||
else
|
||||
{
|
||||
$hlservers = [];
|
||||
}
|
||||
|
||||
// Collect servers info
|
||||
$servers = [];
|
||||
|
||||
foreach ($hlservers as $hlserver)
|
||||
foreach ((array) $entityManagerInterface->getRepository(Server::class)->findAll() as $server)
|
||||
{
|
||||
// Init defaults
|
||||
$info = [];
|
||||
$session = [];
|
||||
$online = [];
|
||||
$players = [];
|
||||
$status = false;
|
||||
|
||||
// Generate CRC32 ID
|
||||
$crc32server = crc32(
|
||||
$hlserver->host . ':' . $hlserver->port
|
||||
);
|
||||
|
||||
// Prepare aliases
|
||||
$aliases = [];
|
||||
|
||||
foreach ($hlserver->alias as $value)
|
||||
{
|
||||
$alias = new \xPaw\SourceQuery\SourceQuery();
|
||||
|
||||
$alias->Connect(
|
||||
$value->host,
|
||||
$value->port
|
||||
);
|
||||
|
||||
$aliases[] = [
|
||||
'host' => $value->host,
|
||||
'port' => $value->port,
|
||||
'status' => $alias->Ping()
|
||||
];
|
||||
}
|
||||
$info =
|
||||
[
|
||||
'Protocol' => null,
|
||||
'HostName' => null,
|
||||
'Map' => null,
|
||||
'ModDir' => null,
|
||||
'ModDesc' => null,
|
||||
'AppID' => null,
|
||||
'Players' => null,
|
||||
'MaxPlayers' => null,
|
||||
'Bots' => null,
|
||||
'Dedicated' => null,
|
||||
'Os' => null,
|
||||
'Password' => null,
|
||||
'Secure' => null,
|
||||
'Version' => null
|
||||
];
|
||||
|
||||
// Request server info
|
||||
try
|
||||
{
|
||||
$server = new \xPaw\SourceQuery\SourceQuery();
|
||||
$node = new \xPaw\SourceQuery\SourceQuery();
|
||||
|
||||
$server->Connect(
|
||||
$hlserver->host,
|
||||
$hlserver->port
|
||||
$node->Connect(
|
||||
false === filter_var($server->getHost(), FILTER_VALIDATE_IP, FILTER_FLAG_IPV6) ? $server->getHost() : "[{$server->getHost()}]",
|
||||
$server->getPort()
|
||||
);
|
||||
|
||||
if ($server->Ping())
|
||||
if ($node->Ping())
|
||||
{
|
||||
if ($info = (array) $server->GetInfo())
|
||||
{
|
||||
// Get session
|
||||
$session = empty($info['Players']) ? [] : (array) $server->GetPlayers();
|
||||
|
||||
// Sort by players by frags
|
||||
if ($session)
|
||||
{
|
||||
array_multisort(
|
||||
array_column(
|
||||
$session,
|
||||
'Frags'
|
||||
),
|
||||
SORT_DESC,
|
||||
$session
|
||||
);
|
||||
}
|
||||
|
||||
// Get online
|
||||
$online = $entityManagerInterface->getRepository(Online::class)->findBy(
|
||||
[
|
||||
'crc32server' => $crc32server
|
||||
],
|
||||
'online' == $request->get('sort') && $crc32server == $request->get('crc32server') ? [$field => $order] : ['time' => 'DESC'],
|
||||
10
|
||||
);
|
||||
|
||||
// Get players
|
||||
$players = $entityManagerInterface->getRepository(Player::class)->findBy(
|
||||
[
|
||||
'crc32server' => $crc32server
|
||||
],
|
||||
'players' == $request->get('sort') && $crc32server == $request->get('crc32server') ? [$field => $order] : ['frags' => 'DESC'],
|
||||
10
|
||||
);
|
||||
}
|
||||
|
||||
$status = true;
|
||||
}
|
||||
|
||||
else
|
||||
{
|
||||
$status = false;
|
||||
foreach ((array) $node->GetInfo() as $key => $value)
|
||||
{
|
||||
$info[$key] = $value;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
catch (Exception $error)
|
||||
{
|
||||
continue;
|
||||
throw $this->createNotFoundException();
|
||||
}
|
||||
|
||||
catch (\Throwable $error)
|
||||
@ -170,20 +89,18 @@ class MainController extends AbstractController
|
||||
|
||||
finally
|
||||
{
|
||||
$server->Disconnect();
|
||||
$node->Disconnect();
|
||||
}
|
||||
|
||||
// Add server
|
||||
$servers[] = [
|
||||
'crc32server' => $crc32server,
|
||||
'host' => $hlserver->host,
|
||||
'port' => $hlserver->port,
|
||||
'description' => $hlserver->description,
|
||||
'aliases' => $aliases,
|
||||
'crc32server' => $server->getCrc32server(),
|
||||
'host' => $server->getHost(),
|
||||
'port' => $server->getPort(),
|
||||
'added' => $server->getAdded(),
|
||||
'updated' => $server->getUpdated(),
|
||||
'online' => $server->getOnline(),
|
||||
'info' => $info,
|
||||
'session' => $session,
|
||||
'online' => $online,
|
||||
'players' => $players,
|
||||
'status' => $status
|
||||
];
|
||||
}
|
||||
|
@ -11,6 +11,8 @@ use Symfony\Component\HttpFoundation\Request;
|
||||
|
||||
use App\Entity\Online;
|
||||
use App\Entity\Player;
|
||||
use App\Entity\Server;
|
||||
|
||||
use Doctrine\ORM\EntityManagerInterface;
|
||||
|
||||
class RssController extends AbstractController
|
||||
@ -32,78 +34,47 @@ class RssController extends AbstractController
|
||||
EntityManagerInterface $entityManagerInterface
|
||||
): Response
|
||||
{
|
||||
// Get HLServers config
|
||||
if ($hlservers = file_get_contents($this->getParameter('app.hlservers')))
|
||||
$online = [];
|
||||
|
||||
foreach ($entityManagerInterface->getRepository(Online::class)->findBy(
|
||||
[
|
||||
'crc32server' => $request->get('crc32server')
|
||||
],
|
||||
[
|
||||
'id' => 'DESC' // same as online.time but faster
|
||||
],
|
||||
10
|
||||
) as $value)
|
||||
{
|
||||
$hlservers = json_decode($hlservers);
|
||||
$online[] =
|
||||
[
|
||||
'id' => $value->getId(),
|
||||
'bots' => $value->getBots(),
|
||||
'players' => $value->getPlayers(),
|
||||
'total' => $value->getTotal(),
|
||||
'time' => $value->getTime()
|
||||
];
|
||||
}
|
||||
|
||||
else
|
||||
{
|
||||
$hlservers = [];
|
||||
}
|
||||
// Response
|
||||
$response = new Response();
|
||||
|
||||
// Find server info
|
||||
foreach ($hlservers as $hlserver)
|
||||
{
|
||||
// Generate CRC32 server ID
|
||||
$crc32server = crc32(
|
||||
$hlserver->host . ':' . $hlserver->port
|
||||
);
|
||||
$response->headers->set(
|
||||
'Content-Type',
|
||||
'text/xml'
|
||||
);
|
||||
|
||||
// Skip servers not registered in HLServers
|
||||
if ($crc32server != $request->get('crc32server'))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
// Get last online value
|
||||
$online = $entityManagerInterface->getRepository(Online::class)->findBy(
|
||||
return $this->render(
|
||||
'default/rss/online.xml.twig',
|
||||
[
|
||||
'server' =>
|
||||
[
|
||||
'crc32server' => $crc32server
|
||||
],
|
||||
[
|
||||
'id' => 'DESC' // same as online.time but faster
|
||||
],
|
||||
10
|
||||
);
|
||||
|
||||
$result = [];
|
||||
|
||||
foreach ($online as $value)
|
||||
{
|
||||
$result[] =
|
||||
[
|
||||
'id' => $value->getId(),
|
||||
'bots' => $value->getBots(),
|
||||
'players' => $value->getPlayers(),
|
||||
'total' => $value->getTotal(),
|
||||
'time' => $value->getTime()
|
||||
];
|
||||
}
|
||||
|
||||
// Response
|
||||
$response = new Response();
|
||||
|
||||
$response->headers->set(
|
||||
'Content-Type',
|
||||
'text/xml'
|
||||
);
|
||||
|
||||
return $this->render(
|
||||
'default/rss/online.xml.twig',
|
||||
[
|
||||
'server' =>
|
||||
[
|
||||
'crc32' => $crc32server,
|
||||
'host' => $hlserver->host,
|
||||
'port' => $hlserver->port,
|
||||
],
|
||||
'online' => $result
|
||||
],
|
||||
$response
|
||||
);
|
||||
}
|
||||
'crc32server' => $request->get('crc32server'),
|
||||
'online' => $online
|
||||
]
|
||||
],
|
||||
$response
|
||||
);
|
||||
|
||||
throw $this->createNotFoundException();
|
||||
}
|
||||
@ -125,77 +96,44 @@ class RssController extends AbstractController
|
||||
EntityManagerInterface $entityManagerInterface
|
||||
): Response
|
||||
{
|
||||
// Get HLServers config
|
||||
if ($hlservers = file_get_contents($this->getParameter('app.hlservers')))
|
||||
$players = [];
|
||||
|
||||
foreach ($entityManagerInterface->getRepository(Player::class)->findBy(
|
||||
[
|
||||
'crc32server' => $request->get('crc32server')
|
||||
],
|
||||
[
|
||||
'id' => 'DESC'
|
||||
],
|
||||
10
|
||||
) as $value)
|
||||
{
|
||||
$hlservers = json_decode($hlservers);
|
||||
$result[] =
|
||||
[
|
||||
'id' => $value->getId(),
|
||||
'name' => $value->getName(),
|
||||
'joined' => $value->getJoined()
|
||||
];
|
||||
}
|
||||
|
||||
else
|
||||
{
|
||||
$hlservers = [];
|
||||
}
|
||||
// Response
|
||||
$response = new Response();
|
||||
|
||||
// Find server info
|
||||
foreach ($hlservers as $hlserver)
|
||||
{
|
||||
// Generate CRC32 server ID
|
||||
$crc32server = crc32(
|
||||
$hlserver->host . ':' . $hlserver->port
|
||||
);
|
||||
$response->headers->set(
|
||||
'Content-Type',
|
||||
'text/xml'
|
||||
);
|
||||
|
||||
// Skip servers not registered in HLServers
|
||||
if ($crc32server != $request->get('crc32server'))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
// Get last players
|
||||
$players = $entityManagerInterface->getRepository(Player::class)->findBy(
|
||||
return $this->render(
|
||||
'default/rss/players.xml.twig',
|
||||
[
|
||||
'server' =>
|
||||
[
|
||||
'crc32server' => $crc32server
|
||||
],
|
||||
[
|
||||
'id' => 'DESC'
|
||||
],
|
||||
10
|
||||
);
|
||||
|
||||
$result = [];
|
||||
|
||||
foreach ($players as $value)
|
||||
{
|
||||
$result[] =
|
||||
[
|
||||
'id' => $value->getId(),
|
||||
'name' => $value->getName(),
|
||||
'joined' => $value->getJoined()
|
||||
];
|
||||
}
|
||||
|
||||
// Response
|
||||
$response = new Response();
|
||||
|
||||
$response->headers->set(
|
||||
'Content-Type',
|
||||
'text/xml'
|
||||
);
|
||||
|
||||
return $this->render(
|
||||
'default/rss/players.xml.twig',
|
||||
[
|
||||
'server' =>
|
||||
[
|
||||
'crc32' => $crc32server,
|
||||
'host' => $hlserver->host,
|
||||
'port' => $hlserver->port,
|
||||
],
|
||||
'players' => $result
|
||||
],
|
||||
$response
|
||||
);
|
||||
}
|
||||
|
||||
throw $this->createNotFoundException();
|
||||
'crc32server' => $request->get('crc32server'),
|
||||
'players' => $players
|
||||
]
|
||||
],
|
||||
$response
|
||||
);
|
||||
}
|
||||
}
|
183
src/Controller/ServerController.php
Normal file
183
src/Controller/ServerController.php
Normal file
@ -0,0 +1,183 @@
|
||||
<?php
|
||||
|
||||
namespace App\Controller;
|
||||
|
||||
use Symfony\Bundle\FrameworkBundle\Controller\AbstractController;
|
||||
use Symfony\Contracts\Translation\TranslatorInterface;
|
||||
|
||||
use Symfony\Component\Routing\Annotation\Route;
|
||||
use Symfony\Component\HttpFoundation\Response;
|
||||
use Symfony\Component\HttpFoundation\Request;
|
||||
|
||||
use App\Entity\Online;
|
||||
use App\Entity\Player;
|
||||
use App\Entity\Server;
|
||||
use Doctrine\ORM\EntityManagerInterface;
|
||||
|
||||
class ServerController extends AbstractController
|
||||
{
|
||||
#[Route(
|
||||
'/server/{crc32server}',
|
||||
name: 'server_index',
|
||||
requirements:
|
||||
[
|
||||
'crc32server' => '\d+',
|
||||
],
|
||||
methods:
|
||||
[
|
||||
'GET'
|
||||
]
|
||||
)]
|
||||
public function index(
|
||||
?Request $request,
|
||||
EntityManagerInterface $entityManagerInterface
|
||||
): Response
|
||||
{
|
||||
// Validate server
|
||||
$server = $entityManagerInterface->getRepository(Server::class)->findOneBy(
|
||||
[
|
||||
'crc32server' => $request->get('crc32server')
|
||||
]
|
||||
);
|
||||
|
||||
if (!$server)
|
||||
{
|
||||
throw $this->createNotFoundException();
|
||||
}
|
||||
|
||||
// Prepare request
|
||||
if ('online' == $request->get('sort') && in_array($request->get('field'), ['time','players','bots','total']))
|
||||
{
|
||||
$field = $request->get('field');
|
||||
}
|
||||
|
||||
else if ('players' == $request->get('sort') && in_array($request->get('field'), ['name','frags','joined','online']))
|
||||
{
|
||||
$field = $request->get('field');
|
||||
}
|
||||
|
||||
else
|
||||
{
|
||||
$field = 'time';
|
||||
}
|
||||
|
||||
if (in_array($request->get('order'), ['asc','desc']))
|
||||
{
|
||||
$order = $request->get('order');
|
||||
}
|
||||
|
||||
else
|
||||
{
|
||||
$order = 'desc';
|
||||
}
|
||||
|
||||
// Init defaults
|
||||
$info = [];
|
||||
$session = [];
|
||||
$online = [];
|
||||
$players = [];
|
||||
|
||||
// Format name
|
||||
if (false === filter_var($server->getHost(), FILTER_VALIDATE_IP, FILTER_FLAG_IPV6))
|
||||
{
|
||||
$name = "{$server->getHost()}:{$server->getPort()}";
|
||||
}
|
||||
|
||||
else
|
||||
{
|
||||
$name = "[{$server->getHost()}]:{$server->getPort()}";
|
||||
}
|
||||
|
||||
// Request server info
|
||||
try
|
||||
{
|
||||
$node = new \xPaw\SourceQuery\SourceQuery();
|
||||
|
||||
$node->Connect(
|
||||
false === filter_var($server->getHost(), FILTER_VALIDATE_IP, FILTER_FLAG_IPV6) ? $server->getHost() : "[{$server->getHost()}]",
|
||||
$server->getPort()
|
||||
);
|
||||
|
||||
if ($node->Ping())
|
||||
{
|
||||
if ($info = (array) $node->GetInfo())
|
||||
{
|
||||
// Get session
|
||||
$session = empty($info['Players']) ? [] : (array) $server->GetPlayers();
|
||||
|
||||
// Sort by players by frags
|
||||
if ($session)
|
||||
{
|
||||
array_multisort(
|
||||
array_column(
|
||||
$session,
|
||||
'Frags'
|
||||
),
|
||||
SORT_DESC,
|
||||
$session
|
||||
);
|
||||
}
|
||||
|
||||
// Get online
|
||||
$online = $entityManagerInterface->getRepository(Online::class)->findBy(
|
||||
[
|
||||
'crc32server' => $server->getCrc32Server()
|
||||
],
|
||||
'online' == $request->get('sort') ? [$field => $order] : ['time' => 'DESC'],
|
||||
10
|
||||
);
|
||||
|
||||
// Get players
|
||||
$players = $entityManagerInterface->getRepository(Player::class)->findBy(
|
||||
[
|
||||
'crc32server' => $server->getCrc32Server()
|
||||
],
|
||||
'players' == $request->get('sort') ? [$field => $order] : ['frags' => 'DESC'],
|
||||
10
|
||||
);
|
||||
}
|
||||
|
||||
$status = true;
|
||||
}
|
||||
|
||||
else
|
||||
{
|
||||
$status = false;
|
||||
}
|
||||
}
|
||||
|
||||
catch (Exception $error)
|
||||
{
|
||||
throw $this->createNotFoundException();
|
||||
}
|
||||
|
||||
catch (\Throwable $error)
|
||||
{
|
||||
$status = false;
|
||||
}
|
||||
|
||||
finally
|
||||
{
|
||||
$node->Disconnect();
|
||||
}
|
||||
|
||||
return $this->render(
|
||||
'default/server/index.html.twig',
|
||||
[
|
||||
'request' => $request,
|
||||
'server' =>
|
||||
[
|
||||
'name' => $name,
|
||||
'crc32server' => $server->getCrc32Server(),
|
||||
'host' => $server->getHost(),
|
||||
'port' => $server->getPort(),
|
||||
'info' => $info,
|
||||
'session' => $session,
|
||||
'online' => $online,
|
||||
'players' => $players,
|
||||
'status' => $status
|
||||
]
|
||||
]
|
||||
);
|
||||
}
|
||||
}
|
111
src/Entity/Server.php
Normal file
111
src/Entity/Server.php
Normal file
@ -0,0 +1,111 @@
|
||||
<?php
|
||||
|
||||
namespace App\Entity;
|
||||
|
||||
use App\Repository\ServerRepository;
|
||||
use Doctrine\DBAL\Types\Types;
|
||||
use Doctrine\ORM\Mapping as ORM;
|
||||
|
||||
#[ORM\Entity(repositoryClass: ServerRepository::class)]
|
||||
class Server
|
||||
{
|
||||
#[ORM\Id]
|
||||
#[ORM\GeneratedValue]
|
||||
#[ORM\Column]
|
||||
private ?int $id = null;
|
||||
|
||||
#[ORM\Column(type: Types::BIGINT)]
|
||||
private ?string $crc32server = null;
|
||||
|
||||
#[ORM\Column(type: Types::BIGINT)]
|
||||
private ?string $added = null;
|
||||
|
||||
#[ORM\Column(type: Types::BIGINT)]
|
||||
private ?string $updated = null;
|
||||
|
||||
#[ORM\Column(type: Types::BIGINT)]
|
||||
private ?string $online = null;
|
||||
|
||||
#[ORM\Column(length: 255)]
|
||||
private ?string $host = null;
|
||||
|
||||
#[ORM\Column(type: Types::INTEGER)]
|
||||
private ?int $port = null;
|
||||
|
||||
public function getId(): ?int
|
||||
{
|
||||
return $this->id;
|
||||
}
|
||||
|
||||
public function getCrc32server(): ?string
|
||||
{
|
||||
return $this->crc32server;
|
||||
}
|
||||
|
||||
public function setCrc32server(string $crc32server): static
|
||||
{
|
||||
$this->crc32server = $crc32server;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function getAdded(): ?int
|
||||
{
|
||||
return $this->added;
|
||||
}
|
||||
|
||||
public function setAdded(int $added): static
|
||||
{
|
||||
$this->added = $added;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function getUpdated(): ?int
|
||||
{
|
||||
return $this->updated;
|
||||
}
|
||||
|
||||
public function setUpdated(int $updated): static
|
||||
{
|
||||
$this->updated = $updated;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function getOnline(): ?int
|
||||
{
|
||||
return $this->online;
|
||||
}
|
||||
|
||||
public function setOnline(int $online): static
|
||||
{
|
||||
$this->online = $online;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function getHost(): ?string
|
||||
{
|
||||
return $this->host;
|
||||
}
|
||||
|
||||
public function setHost(string $host): static
|
||||
{
|
||||
$this->host = $host;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function getPort(): ?int
|
||||
{
|
||||
return $this->port;
|
||||
}
|
||||
|
||||
public function setPort(int $port): static
|
||||
{
|
||||
$this->port = $port;
|
||||
|
||||
return $this;
|
||||
}
|
||||
}
|
23
src/Repository/ServerRepository.php
Normal file
23
src/Repository/ServerRepository.php
Normal file
@ -0,0 +1,23 @@
|
||||
<?php
|
||||
|
||||
namespace App\Repository;
|
||||
|
||||
use App\Entity\Server;
|
||||
use Doctrine\Bundle\DoctrineBundle\Repository\ServiceEntityRepository;
|
||||
use Doctrine\Persistence\ManagerRegistry;
|
||||
|
||||
/**
|
||||
* @extends ServiceEntityRepository<Server>
|
||||
*
|
||||
* @method Server|null find($id, $lockMode = null, $lockVersion = null)
|
||||
* @method Server|null findOneBy(array $criteria, array $orderBy = null)
|
||||
* @method Server[] findAll()
|
||||
* @method Server[] findBy(array $criteria, array $orderBy = null, $limit = null, $offset = null)
|
||||
*/
|
||||
class ServerRepository extends ServiceEntityRepository
|
||||
{
|
||||
public function __construct(ManagerRegistry $registry)
|
||||
{
|
||||
parent::__construct($registry, Server::class);
|
||||
}
|
||||
}
|
@ -18,7 +18,7 @@
|
||||
{% block header_container %}
|
||||
<header>
|
||||
{% block header_content %}
|
||||
<a href="https://store.steampowered.com/app/70/HalfLife/" title="{{ 'Get Half-Life' | trans }}" target="_blank">
|
||||
<a href="{{ path('main_index') }}">
|
||||
<svg version="1.1" width="20" height="20" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" viewBox="0 0 364.707 364.707" enable-background="new 0 0 364.707 364.707" xml:space="preserve">
|
||||
<path fill="currentColor" d="M223.864,272.729l-38.608-97.848l-56.603,89.184H93.166l79.052-127.654l-8.875-25.229h-30.781V81.12h52.691l60.521,153.899l26.608-8.668l8.867,29.813L223.864,272.729z"/>
|
||||
<path fill="none" stroke="currentColor" stroke-width="34" d="M337.623,182.198c0,85.579-69.363,154.934-154.934,154.934c-85.571,0-154.936-69.354-154.936-154.934c0-85.569,69.363-154.933,154.936-154.933C268.259,27.265,337.623,96.629,337.623,182.198z"/>
|
||||
@ -40,7 +40,7 @@
|
||||
{% block footer_content %}
|
||||
{% if app.masters %}
|
||||
<small class="float-left">
|
||||
<strong>{{ 'Master' | trans }}:</strong>
|
||||
<strong>{{ 'Master tracker' | trans }}:</strong>
|
||||
{% for i, master in app.masters | split(',') %}
|
||||
{% if i %}•{% endif %} {{ master }}
|
||||
{% endfor %}
|
||||
|
@ -1,96 +1,23 @@
|
||||
{% macro sort(request, crc32server, sort, field) %}
|
||||
{% if sort == request.get('sort') and crc32server == request.get('crc32server') %}
|
||||
{% if field == request.get('field') %}
|
||||
{% if 'asc' == request.get('order') %}
|
||||
<a href="{{ path('main_index', { sort : sort, field : field, order : 'desc', crc32server : crc32server, '_fragment' : '%s-%d' | format(sort, crc32server) }) }}" rel="nofollow"><svg xmlns="http://www.w3.org/2000/svg" width="12" height="12" fill="currentColor" viewBox="0 0 16 16">
|
||||
<path d="M0 2a2 2 0 0 1 2-2h12a2 2 0 0 1 2 2v12a2 2 0 0 1-2 2H2a2 2 0 0 1-2-2zm4 4a.5.5 0 0 0-.374.832l4 4.5a.5.5 0 0 0 .748 0l4-4.5A.5.5 0 0 0 12 6z"/>
|
||||
</svg></a>
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="12" height="12" fill="currentColor" viewBox="0 0 16 16">
|
||||
<path d="M0 2a2 2 0 0 1 2-2h12a2 2 0 0 1 2 2v12a2 2 0 0 1-2 2H2a2 2 0 0 1-2-2zm4 9h8a.5.5 0 0 0 .374-.832l-4-4.5a.5.5 0 0 0-.748 0l-4 4.5A.5.5 0 0 0 4 11"/>
|
||||
</svg>
|
||||
{% else %}
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="12" height="12" fill="currentColor" viewBox="0 0 16 16">
|
||||
<path d="M0 2a2 2 0 0 1 2-2h12a2 2 0 0 1 2 2v12a2 2 0 0 1-2 2H2a2 2 0 0 1-2-2zm4 4a.5.5 0 0 0-.374.832l4 4.5a.5.5 0 0 0 .748 0l4-4.5A.5.5 0 0 0 12 6z"/>
|
||||
</svg>
|
||||
<a href="{{ path('main_index', { sort : sort, field : field, order : 'asc', crc32server : crc32server, '_fragment' : '%s-%d' | format(sort, crc32server) }) }}" rel="nofollow"><svg xmlns="http://www.w3.org/2000/svg" width="12" height="12" fill="currentColor" viewBox="0 0 16 16">
|
||||
<path d="M0 2a2 2 0 0 1 2-2h12a2 2 0 0 1 2 2v12a2 2 0 0 1-2 2H2a2 2 0 0 1-2-2zm4 9h8a.5.5 0 0 0 .374-.832l-4-4.5a.5.5 0 0 0-.748 0l-4 4.5A.5.5 0 0 0 4 11"/>
|
||||
</svg></a>
|
||||
{% endif %}
|
||||
{% else %}
|
||||
<a href="{{ path('main_index', { sort : sort, field : field, order : 'desc', crc32server : crc32server, '_fragment' : '%s-%d' | format(sort, crc32server) }) }}" rel="nofollow"><svg xmlns="http://www.w3.org/2000/svg" width="12" height="12" fill="currentColor" viewBox="0 0 16 16">
|
||||
<path d="M0 2a2 2 0 0 1 2-2h12a2 2 0 0 1 2 2v12a2 2 0 0 1-2 2H2a2 2 0 0 1-2-2zm4 4a.5.5 0 0 0-.374.832l4 4.5a.5.5 0 0 0 .748 0l4-4.5A.5.5 0 0 0 12 6z"/>
|
||||
</svg></a>
|
||||
<a href="{{ path('main_index', { sort : sort, field : field, order : 'asc', crc32server : crc32server, '_fragment' : '%s-%d' | format(sort, crc32server) }) }}" rel="nofollow"><svg xmlns="http://www.w3.org/2000/svg" width="12" height="12" fill="currentColor" viewBox="0 0 16 16">
|
||||
<path d="M0 2a2 2 0 0 1 2-2h12a2 2 0 0 1 2 2v12a2 2 0 0 1-2 2H2a2 2 0 0 1-2-2zm4 9h8a.5.5 0 0 0 .374-.832l-4-4.5a.5.5 0 0 0-.748 0l-4 4.5A.5.5 0 0 0 4 11"/>
|
||||
</svg></a>
|
||||
{% endif %}
|
||||
{% else %}
|
||||
{% if 'online' == sort and 'time' == field %}
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="12" height="12" fill="currentColor" viewBox="0 0 16 16">
|
||||
<path d="M0 2a2 2 0 0 1 2-2h12a2 2 0 0 1 2 2v12a2 2 0 0 1-2 2H2a2 2 0 0 1-2-2zm4 4a.5.5 0 0 0-.374.832l4 4.5a.5.5 0 0 0 .748 0l4-4.5A.5.5 0 0 0 12 6z"/>
|
||||
</svg>
|
||||
{% elseif 'players' == sort and 'frags' == field %}
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="12" height="12" fill="currentColor" viewBox="0 0 16 16">
|
||||
<path d="M0 2a2 2 0 0 1 2-2h12a2 2 0 0 1 2 2v12a2 2 0 0 1-2 2H2a2 2 0 0 1-2-2zm4 4a.5.5 0 0 0-.374.832l4 4.5a.5.5 0 0 0 .748 0l4-4.5A.5.5 0 0 0 12 6z"/>
|
||||
</svg>
|
||||
{% else %}
|
||||
<a href="{{ path('main_index', { sort : sort, field : field, order : 'desc', crc32server : crc32server, '_fragment' : '%s-%d' | format(sort, crc32server) }) }}" rel="nofollow"><svg xmlns="http://www.w3.org/2000/svg" width="12" height="12" fill="currentColor" viewBox="0 0 16 16">
|
||||
<path d="M0 2a2 2 0 0 1 2-2h12a2 2 0 0 1 2 2v12a2 2 0 0 1-2 2H2a2 2 0 0 1-2-2zm4 4a.5.5 0 0 0-.374.832l4 4.5a.5.5 0 0 0 .748 0l4-4.5A.5.5 0 0 0 12 6z"/>
|
||||
</svg></a>
|
||||
{% endif %}
|
||||
<a href="{{ path('main_index', { sort : sort, field : field, order : 'asc', crc32server : crc32server, '_fragment' : '%s-%d' | format(sort, crc32server) }) }}" rel="nofollow"><svg xmlns="http://www.w3.org/2000/svg" width="12" height="12" fill="currentColor" viewBox="0 0 16 16">
|
||||
<path d="M0 2a2 2 0 0 1 2-2h12a2 2 0 0 1 2 2v12a2 2 0 0 1-2 2H2a2 2 0 0 1-2-2zm4 9h8a.5.5 0 0 0 .374-.832l-4-4.5a.5.5 0 0 0-.748 0l-4 4.5A.5.5 0 0 0 4 11"/>
|
||||
</svg></a>
|
||||
{% endif %}
|
||||
{% endmacro %}
|
||||
{% from _self import sort %}
|
||||
{% extends 'default/layout.html.twig' %}
|
||||
{% block main_content %}
|
||||
{% for server in servers %}
|
||||
<div class="border-default padding-8-px margin-y-8-px">
|
||||
{% if server.info.HostName is defined %}
|
||||
<h2>{{ server.info.HostName }} [#{{ server.crc32server }}]</h2>
|
||||
{% else %}
|
||||
<h2>#{{ server.crc32server }}</h2>
|
||||
{% endif %}
|
||||
<hr />
|
||||
{% if server.description.title is defined %}
|
||||
<div class="margin-y-8-px">
|
||||
{{ server.description.title }}
|
||||
</div>
|
||||
{% endif %}
|
||||
{% if server.description.links %}
|
||||
<div class="margin-y-8-px">
|
||||
<ul>
|
||||
{% for link in server.description.links %}
|
||||
<li class="margin-y-8-px"><a href="{{ link }}">{{ link }}</a></li>
|
||||
{% endfor %}
|
||||
</ul>
|
||||
</div>
|
||||
{% endif %}
|
||||
<h3 class="padding-y-8-px text-align-right">{{ 'Address' | trans }}</h3>
|
||||
<div>
|
||||
{{ server.host }}:{{ server.port }}
|
||||
{% if server.status %}
|
||||
<span class="color-success" title="{{ 'Online' | trans }}">
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="10" height="10" fill="currentColor" viewBox="0 0 16 16">
|
||||
<path fill-rule="evenodd" d="M8 13A5 5 0 1 0 8 3a5 5 0 0 0 0 10"/>
|
||||
</svg>
|
||||
</span>
|
||||
{% else %}
|
||||
<span class="color-error" title="{{ 'Offline' | trans }}">
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="10" height="10" fill="currentColor" viewBox="0 0 16 16">
|
||||
<path fill-rule="evenodd" d="M8 13A5 5 0 1 0 8 3a5 5 0 0 0 0 10"/>
|
||||
</svg>
|
||||
</span>
|
||||
{% endif %}
|
||||
</div>
|
||||
{% if server.aliases %}
|
||||
<h3 class="padding-y-8-px text-align-right">{{ 'Alias' | trans }}</h3>
|
||||
{% for alias in server.aliases %}
|
||||
<div class="margin-y-8-px">
|
||||
{{ alias.host }}:{{ alias.port }}
|
||||
{% if alias.status %}
|
||||
<h2>{{ 'Servers' | trans }}</h2>
|
||||
<table class="margin-y-8-px">
|
||||
<tr>
|
||||
<th class="text-align-center">{{ 'Status' | trans }}</th>
|
||||
<th class="text-align-left">{{ 'Name' | trans }}</th>
|
||||
<th class="text-align-left">{{ 'Map' | trans }}</th>
|
||||
<th class="text-align-left">{{ 'Added' | trans }}</th>
|
||||
<th class="text-align-left">{{ 'Online' | trans }}</th>
|
||||
<th class="text-align-center">{{ 'Players' | trans }}</th>
|
||||
<th class="text-align-center">{{ 'Max' | trans }}</th>
|
||||
<th class="text-align-center">{{ 'Bots' | trans }}</th>
|
||||
<th class="text-align-center">{{ 'Actions' | trans }}</th>
|
||||
</tr>
|
||||
{% if servers %}
|
||||
{% for server in servers %}
|
||||
<tr>
|
||||
<td class="text-align-center">
|
||||
{% if server.status %}
|
||||
<span class="color-success" title="{{ 'Online' | trans }}">
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="10" height="10" fill="currentColor" viewBox="0 0 16 16">
|
||||
<path fill-rule="evenodd" d="M8 13A5 5 0 1 0 8 3a5 5 0 0 0 0 10"/>
|
||||
@ -103,111 +30,25 @@
|
||||
</svg>
|
||||
</span>
|
||||
{% endif %}
|
||||
</div>
|
||||
{% endfor %}
|
||||
{% endif %}
|
||||
{% if server.info %}
|
||||
<h3 class="padding-y-8-px text-align-right">{{ 'Info' | trans }}</h3>
|
||||
<table>
|
||||
{% for key, value in server.info %}
|
||||
<tr>
|
||||
<td class="text-align-left">{{ key }}</td>
|
||||
<td class="text-align-left">{{ value }}</td>
|
||||
</tr>
|
||||
{% endfor %}
|
||||
</table>
|
||||
{% endif %}
|
||||
{% if server.session %}
|
||||
<h3 class="padding-y-8-px text-align-right">{{ 'Session' | trans }}</h3>
|
||||
<table>
|
||||
<tr>
|
||||
<th class="text-align-left">{{ 'Player' | trans }}</th>
|
||||
<th class="text-align-center">{{ 'Frags' | trans }}</th>
|
||||
<th class="text-align-center">{{ 'Time' | trans }}</th>
|
||||
</tr>
|
||||
{% for player in server.session %}
|
||||
<tr>
|
||||
<td class="text-align-left">{{ player.Name }}</td>
|
||||
<td class="text-align-center">{{ player.Frags }}</td>
|
||||
<td class="text-align-center">
|
||||
{% if player.TimeF == '59:59' %}
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="12" height="12" fill="currentColor" viewBox="0 0 16 16">
|
||||
<path d="M5.68 5.792 7.345 7.75 5.681 9.708a2.75 2.75 0 1 1 0-3.916ZM8 6.978 6.416 5.113l-.014-.015a3.75 3.75 0 1 0 0 5.304l.014-.015L8 8.522l1.584 1.865.014.015a3.75 3.75 0 1 0 0-5.304l-.014.015zm.656.772 1.663-1.958a2.75 2.75 0 1 1 0 3.916z"/>
|
||||
</svg>
|
||||
{% else %}
|
||||
{{ player.TimeF }}
|
||||
{% endif %}
|
||||
</td>
|
||||
</tr>
|
||||
{% endfor %}
|
||||
</table>
|
||||
{% endif %}
|
||||
{% if server.online %}
|
||||
<a name="{{ '%s-%d' | format('online', server.crc32server) }}"></a>
|
||||
<h3 class="padding-y-8-px text-align-right">
|
||||
{{ 'Online' | trans }}
|
||||
<a href="{{ path('rss_online', { crc32server : server.crc32server }) }}" title="{{ 'RSS' | trans }}"><svg xmlns="http://www.w3.org/2000/svg" width="12" height="12" fill="currentColor" viewBox="0 0 16 16">
|
||||
<path d="M2 0a2 2 0 0 0-2 2v12a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V2a2 2 0 0 0-2-2zm1.5 2.5c5.523 0 10 4.477 10 10a1 1 0 1 1-2 0 8 8 0 0 0-8-8 1 1 0 0 1 0-2m0 4a6 6 0 0 1 6 6 1 1 0 1 1-2 0 4 4 0 0 0-4-4 1 1 0 0 1 0-2m.5 7a1.5 1.5 0 1 1 0-3 1.5 1.5 0 0 1 0 3"/>
|
||||
</svg></a>
|
||||
</h3>
|
||||
<table>
|
||||
<tr>
|
||||
<th class="text-align-left">
|
||||
{{ 'Time' | trans }} {{ sort(request, server.crc32server, 'online', 'time') }}
|
||||
</th>
|
||||
<th class="text-align-center">
|
||||
{{ 'Players' | trans }} {{ sort(request, server.crc32server, 'online', 'players') }}
|
||||
</th>
|
||||
<th class="text-align-center">
|
||||
{{ 'Bots' | trans }} {{ sort(request, server.crc32server, 'online', 'bots') }}
|
||||
</th>
|
||||
<th class="text-align-center">
|
||||
{{ 'Total' | trans }} {{ sort(request, server.crc32server, 'online', 'total') }}
|
||||
</th>
|
||||
</tr>
|
||||
{% for online in server.online %}
|
||||
<tr>
|
||||
<td class="text-align-left">{{ online.time | date('r') }}</td>
|
||||
<td class="text-align-center">{{ online.players }}</td>
|
||||
<td class="text-align-center">{{ online.bots }}</td>
|
||||
<td class="text-align-center">{{ online.total }}</td>
|
||||
</tr>
|
||||
{% endfor %}
|
||||
</table>
|
||||
{% endif %}
|
||||
{% if server.players %}
|
||||
<a name="{{ '%s-%d' | format('players', server.crc32server) }}"></a>
|
||||
<h3 class="padding-y-8-px text-align-right">
|
||||
{{ 'Players' | trans }}
|
||||
<a href="{{ path('rss_players', { crc32server : server.crc32server }) }}" title="{{ 'RSS' | trans }}"><svg xmlns="http://www.w3.org/2000/svg" width="12" height="12" fill="currentColor" viewBox="0 0 16 16">
|
||||
<path d="M2 0a2 2 0 0 0-2 2v12a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V2a2 2 0 0 0-2-2zm1.5 2.5c5.523 0 10 4.477 10 10a1 1 0 1 1-2 0 8 8 0 0 0-8-8 1 1 0 0 1 0-2m0 4a6 6 0 0 1 6 6 1 1 0 1 1-2 0 4 4 0 0 0-4-4 1 1 0 0 1 0-2m.5 7a1.5 1.5 0 1 1 0-3 1.5 1.5 0 0 1 0 3"/>
|
||||
</svg></a>
|
||||
</h3>
|
||||
<table>
|
||||
<tr>
|
||||
<th class="text-align-center">
|
||||
{{ 'Frags' | trans }} {{ sort(request, server.crc32server, 'players', 'frags') }}
|
||||
</th>
|
||||
<th class="text-align-left">
|
||||
{{ 'Name' | trans }} {{ sort(request, server.crc32server, 'players', 'name') }}
|
||||
</th>
|
||||
<th class="text-align-left">
|
||||
{{ 'Online' | trans }} {{ sort(request, server.crc32server, 'players', 'online') }}
|
||||
</th>
|
||||
<th class="text-align-left">
|
||||
{{ 'Joined' | trans }} {{ sort(request, server.crc32server, 'players', 'joined') }}
|
||||
</th>
|
||||
</tr>
|
||||
{% for player in server.players %}
|
||||
<tr>
|
||||
<td class="text-align-center">{{ player.frags }}</td>
|
||||
<td class="text-align-left">{{ player.name }}</td>
|
||||
<td class="text-align-left">{{ player.online | format_date }}</td>
|
||||
<td class="text-align-left">{{ player.joined | format_date }}</td>
|
||||
</tr>
|
||||
{% endfor %}
|
||||
</table>
|
||||
{% endif %}
|
||||
</div>
|
||||
{% endfor %}
|
||||
</td>
|
||||
<td class="text-align-left">{{ server.info.HostName }}</td>
|
||||
<td class="text-align-left">{{ server.info.Map }}</td>
|
||||
<td class="text-align-left">{{ server.added | format_date }}</td>
|
||||
<td class="text-align-left">{{ server.online | format_date }}</td>
|
||||
<td class="text-align-center">{{ server.info.Players }}</td>
|
||||
<td class="text-align-center">{{ server.info.MaxPlayers }}</td>
|
||||
<td class="text-align-center">{{ server.info.Bots }}</td>
|
||||
<td class="text-align-center">
|
||||
<a href="{{ path('server_index', { crc32server : server.crc32server }) }}" title="{{ 'Stats' | trans }}"><svg xmlns="http://www.w3.org/2000/svg" width="12" height="12" fill="currentColor" viewBox="0 0 16 16">
|
||||
<path d="M11 2a1 1 0 0 1 1-1h2a1 1 0 0 1 1 1v12h.5a.5.5 0 0 1 0 1H.5a.5.5 0 0 1 0-1H1v-3a1 1 0 0 1 1-1h2a1 1 0 0 1 1 1v3h1V7a1 1 0 0 1 1-1h2a1 1 0 0 1 1 1v7h1z"/>
|
||||
</svg></a>
|
||||
</td>
|
||||
</tr>
|
||||
{% endfor %}
|
||||
{% else %}
|
||||
<tr>
|
||||
<td class="text-align-center" colspan="9">{{ 'Not found' | trans }}</td>
|
||||
</tr>
|
||||
{% endif %}
|
||||
</table>
|
||||
{% endblock %}
|
@ -1,13 +1,14 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
|
||||
<channel>
|
||||
<atom:link href="{{ url('main_index') }}" rel="self" type="application/rss+xml"></atom:link>
|
||||
<title>{{ server.host }}:{{ server.port }} - {{ app.name }}</title>
|
||||
<link>{{ url('main_index') }}</link>
|
||||
{% for value in online %}
|
||||
<atom:link href="{{ url('server_index', { crc32server : server.crc32server }) }}" rel="self" type="application/rss+xml"></atom:link>
|
||||
<title>#{{ server.crc32server }} - {{ 'Online' | trans }} - {{ app.name }}</title>
|
||||
<link>{{ url('server_index', { crc32server : server.crc32server, _fragment : 'online' }) }}</link>
|
||||
{% for value in server.online %}
|
||||
<item>
|
||||
<title>{{ 'Online changed!' | trans }}</title>
|
||||
<guid>online#{{ value.id }}</guid>
|
||||
<guid>{{ url('server_index', { crc32server : server.crc32server, _fragment : 'online' }) }}-{{ value.id }}</guid>
|
||||
<link>{{ url('server_index', { crc32server : server.crc32server, _fragment : 'online' }) }}</link>
|
||||
<description>
|
||||
{{ 'Players' | trans }}: {{ value.players }}
|
||||
{{ 'Bots' | trans }}: {{ value.bots }}
|
||||
|
@ -1,13 +1,14 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
|
||||
<channel>
|
||||
<atom:link href="{{ url('main_index') }}" rel="self" type="application/rss+xml"></atom:link>
|
||||
<title>{{ server.host }}:{{ server.port }} - {{ 'Players' | trans }} - {{ app.name }}</title>
|
||||
<link>{{ url('main_index') }}</link>
|
||||
{% for player in players %}
|
||||
<atom:link href="{{ url('server_index', { crc32server : server.crc32server }) }}" rel="self" type="application/rss+xml"></atom:link>
|
||||
<title>#{{ server.crc32server }} - {{ 'Players' | trans }} - {{ app.name }}</title>
|
||||
<link>{{ url('server_index', { crc32server : server.crc32server, _fragment : 'online' }) }}</link>
|
||||
{% for player in server.players %}
|
||||
<item>
|
||||
<title>{{ player.name }} {{ 'joined' | trans }} {{ app.name }}!</title>
|
||||
<guid>player#{{ player.id }}</guid>
|
||||
<guid>{{ url('server_index', { crc32server : server.crc32server, _fragment : 'players' }) }}-{{ value.id }}</guid>
|
||||
<link>{{ url('server_index', { crc32server : server.crc32server, _fragment : 'players' }) }}</link>
|
||||
<pubDate>{{ player.joined | date("D, d M Y h:i:s O") }}</pubDate>
|
||||
</item>
|
||||
{% endfor %}
|
||||
|
171
templates/default/server/index.html.twig
Normal file
171
templates/default/server/index.html.twig
Normal file
@ -0,0 +1,171 @@
|
||||
{% macro sort(request, crc32server, sort, field) %}
|
||||
{% if sort == request.get('sort') and crc32server == request.get('crc32server') %}
|
||||
{% if field == request.get('field') %}
|
||||
{% if 'asc' == request.get('order') %}
|
||||
<a href="{{ path('main_index', { sort : sort, field : field, order : 'desc', crc32server : crc32server, '_fragment' : '%s-%d' | format(sort, crc32server) }) }}" rel="nofollow"><svg xmlns="http://www.w3.org/2000/svg" width="12" height="12" fill="currentColor" viewBox="0 0 16 16">
|
||||
<path d="M0 2a2 2 0 0 1 2-2h12a2 2 0 0 1 2 2v12a2 2 0 0 1-2 2H2a2 2 0 0 1-2-2zm4 4a.5.5 0 0 0-.374.832l4 4.5a.5.5 0 0 0 .748 0l4-4.5A.5.5 0 0 0 12 6z"/>
|
||||
</svg></a>
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="12" height="12" fill="currentColor" viewBox="0 0 16 16">
|
||||
<path d="M0 2a2 2 0 0 1 2-2h12a2 2 0 0 1 2 2v12a2 2 0 0 1-2 2H2a2 2 0 0 1-2-2zm4 9h8a.5.5 0 0 0 .374-.832l-4-4.5a.5.5 0 0 0-.748 0l-4 4.5A.5.5 0 0 0 4 11"/>
|
||||
</svg>
|
||||
{% else %}
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="12" height="12" fill="currentColor" viewBox="0 0 16 16">
|
||||
<path d="M0 2a2 2 0 0 1 2-2h12a2 2 0 0 1 2 2v12a2 2 0 0 1-2 2H2a2 2 0 0 1-2-2zm4 4a.5.5 0 0 0-.374.832l4 4.5a.5.5 0 0 0 .748 0l4-4.5A.5.5 0 0 0 12 6z"/>
|
||||
</svg>
|
||||
<a href="{{ path('main_index', { sort : sort, field : field, order : 'asc', crc32server : crc32server, '_fragment' : '%s-%d' | format(sort, crc32server) }) }}" rel="nofollow"><svg xmlns="http://www.w3.org/2000/svg" width="12" height="12" fill="currentColor" viewBox="0 0 16 16">
|
||||
<path d="M0 2a2 2 0 0 1 2-2h12a2 2 0 0 1 2 2v12a2 2 0 0 1-2 2H2a2 2 0 0 1-2-2zm4 9h8a.5.5 0 0 0 .374-.832l-4-4.5a.5.5 0 0 0-.748 0l-4 4.5A.5.5 0 0 0 4 11"/>
|
||||
</svg></a>
|
||||
{% endif %}
|
||||
{% else %}
|
||||
<a href="{{ path('main_index', { sort : sort, field : field, order : 'desc', crc32server : crc32server, '_fragment' : '%s-%d' | format(sort, crc32server) }) }}" rel="nofollow"><svg xmlns="http://www.w3.org/2000/svg" width="12" height="12" fill="currentColor" viewBox="0 0 16 16">
|
||||
<path d="M0 2a2 2 0 0 1 2-2h12a2 2 0 0 1 2 2v12a2 2 0 0 1-2 2H2a2 2 0 0 1-2-2zm4 4a.5.5 0 0 0-.374.832l4 4.5a.5.5 0 0 0 .748 0l4-4.5A.5.5 0 0 0 12 6z"/>
|
||||
</svg></a>
|
||||
<a href="{{ path('main_index', { sort : sort, field : field, order : 'asc', crc32server : crc32server, '_fragment' : '%s-%d' | format(sort, crc32server) }) }}" rel="nofollow"><svg xmlns="http://www.w3.org/2000/svg" width="12" height="12" fill="currentColor" viewBox="0 0 16 16">
|
||||
<path d="M0 2a2 2 0 0 1 2-2h12a2 2 0 0 1 2 2v12a2 2 0 0 1-2 2H2a2 2 0 0 1-2-2zm4 9h8a.5.5 0 0 0 .374-.832l-4-4.5a.5.5 0 0 0-.748 0l-4 4.5A.5.5 0 0 0 4 11"/>
|
||||
</svg></a>
|
||||
{% endif %}
|
||||
{% else %}
|
||||
{% if 'online' == sort and 'time' == field %}
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="12" height="12" fill="currentColor" viewBox="0 0 16 16">
|
||||
<path d="M0 2a2 2 0 0 1 2-2h12a2 2 0 0 1 2 2v12a2 2 0 0 1-2 2H2a2 2 0 0 1-2-2zm4 4a.5.5 0 0 0-.374.832l4 4.5a.5.5 0 0 0 .748 0l4-4.5A.5.5 0 0 0 12 6z"/>
|
||||
</svg>
|
||||
{% elseif 'players' == sort and 'frags' == field %}
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="12" height="12" fill="currentColor" viewBox="0 0 16 16">
|
||||
<path d="M0 2a2 2 0 0 1 2-2h12a2 2 0 0 1 2 2v12a2 2 0 0 1-2 2H2a2 2 0 0 1-2-2zm4 4a.5.5 0 0 0-.374.832l4 4.5a.5.5 0 0 0 .748 0l4-4.5A.5.5 0 0 0 12 6z"/>
|
||||
</svg>
|
||||
{% else %}
|
||||
<a href="{{ path('main_index', { sort : sort, field : field, order : 'desc', crc32server : crc32server, '_fragment' : '%s-%d' | format(sort, crc32server) }) }}" rel="nofollow"><svg xmlns="http://www.w3.org/2000/svg" width="12" height="12" fill="currentColor" viewBox="0 0 16 16">
|
||||
<path d="M0 2a2 2 0 0 1 2-2h12a2 2 0 0 1 2 2v12a2 2 0 0 1-2 2H2a2 2 0 0 1-2-2zm4 4a.5.5 0 0 0-.374.832l4 4.5a.5.5 0 0 0 .748 0l4-4.5A.5.5 0 0 0 12 6z"/>
|
||||
</svg></a>
|
||||
{% endif %}
|
||||
<a href="{{ path('main_index', { sort : sort, field : field, order : 'asc', crc32server : crc32server, '_fragment' : '%s-%d' | format(sort, crc32server) }) }}" rel="nofollow"><svg xmlns="http://www.w3.org/2000/svg" width="12" height="12" fill="currentColor" viewBox="0 0 16 16">
|
||||
<path d="M0 2a2 2 0 0 1 2-2h12a2 2 0 0 1 2 2v12a2 2 0 0 1-2 2H2a2 2 0 0 1-2-2zm4 9h8a.5.5 0 0 0 .374-.832l-4-4.5a.5.5 0 0 0-.748 0l-4 4.5A.5.5 0 0 0 4 11"/>
|
||||
</svg></a>
|
||||
{% endif %}
|
||||
{% endmacro %}
|
||||
{% from _self import sort %}
|
||||
{% extends 'default/layout.html.twig' %}
|
||||
{% block head_title_content %}{{ server.name }} - {{ app.name }}{% endblock %}
|
||||
{% block main_content %}
|
||||
<div class="border-default padding-8-px margin-y-8-px">
|
||||
<h2>
|
||||
{{ server.name }}
|
||||
{% if server.status %}
|
||||
<span class="color-success" title="{{ 'Online' | trans }}">
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="10" height="10" fill="currentColor" viewBox="0 0 16 16">
|
||||
<path fill-rule="evenodd" d="M8 13A5 5 0 1 0 8 3a5 5 0 0 0 0 10"/>
|
||||
</svg>
|
||||
</span>
|
||||
{% else %}
|
||||
<span class="color-error" title="{{ 'Offline' | trans }}">
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="10" height="10" fill="currentColor" viewBox="0 0 16 16">
|
||||
<path fill-rule="evenodd" d="M8 13A5 5 0 1 0 8 3a5 5 0 0 0 0 10"/>
|
||||
</svg>
|
||||
</span>
|
||||
{% endif %}
|
||||
</h2>
|
||||
<hr />
|
||||
{% if server.info %}
|
||||
<h3 class="padding-y-8-px text-align-right">{{ 'Info' | trans }}</h3>
|
||||
<table>
|
||||
{% for key, value in server.info %}
|
||||
<tr>
|
||||
<td class="text-align-left">{{ key }}</td>
|
||||
<td class="text-align-left">{{ value }}</td>
|
||||
</tr>
|
||||
{% endfor %}
|
||||
</table>
|
||||
{% endif %}
|
||||
{% if server.session %}
|
||||
<h3 class="padding-y-8-px text-align-right">{{ 'Session' | trans }}</h3>
|
||||
<table>
|
||||
<tr>
|
||||
<th class="text-align-left">{{ 'Player' | trans }}</th>
|
||||
<th class="text-align-center">{{ 'Frags' | trans }}</th>
|
||||
<th class="text-align-center">{{ 'Time' | trans }}</th>
|
||||
</tr>
|
||||
{% for player in server.session %}
|
||||
<tr>
|
||||
<td class="text-align-left">{{ player.Name }}</td>
|
||||
<td class="text-align-center">{{ player.Frags }}</td>
|
||||
<td class="text-align-center">
|
||||
{% if player.TimeF == '59:59' %}
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="12" height="12" fill="currentColor" viewBox="0 0 16 16">
|
||||
<path d="M5.68 5.792 7.345 7.75 5.681 9.708a2.75 2.75 0 1 1 0-3.916ZM8 6.978 6.416 5.113l-.014-.015a3.75 3.75 0 1 0 0 5.304l.014-.015L8 8.522l1.584 1.865.014.015a3.75 3.75 0 1 0 0-5.304l-.014.015zm.656.772 1.663-1.958a2.75 2.75 0 1 1 0 3.916z"/>
|
||||
</svg>
|
||||
{% else %}
|
||||
{{ player.TimeF }}
|
||||
{% endif %}
|
||||
</td>
|
||||
</tr>
|
||||
{% endfor %}
|
||||
</table>
|
||||
{% endif %}
|
||||
{% if server.online %}
|
||||
<a name="online"></a>
|
||||
<h3 class="padding-y-8-px text-align-right">
|
||||
{{ 'Online' | trans }}
|
||||
<a href="{{ path('rss_online', { crc32server : server.crc32server }) }}" title="{{ 'RSS' | trans }}"><svg xmlns="http://www.w3.org/2000/svg" width="12" height="12" fill="currentColor" viewBox="0 0 16 16">
|
||||
<path d="M2 0a2 2 0 0 0-2 2v12a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V2a2 2 0 0 0-2-2zm1.5 2.5c5.523 0 10 4.477 10 10a1 1 0 1 1-2 0 8 8 0 0 0-8-8 1 1 0 0 1 0-2m0 4a6 6 0 0 1 6 6 1 1 0 1 1-2 0 4 4 0 0 0-4-4 1 1 0 0 1 0-2m.5 7a1.5 1.5 0 1 1 0-3 1.5 1.5 0 0 1 0 3"/>
|
||||
</svg></a>
|
||||
</h3>
|
||||
<table>
|
||||
<tr>
|
||||
<th class="text-align-left">
|
||||
{{ 'Time' | trans }} {{ sort(request, server.crc32server, 'online', 'time') }}
|
||||
</th>
|
||||
<th class="text-align-center">
|
||||
{{ 'Players' | trans }} {{ sort(request, server.crc32server, 'online', 'players') }}
|
||||
</th>
|
||||
<th class="text-align-center">
|
||||
{{ 'Bots' | trans }} {{ sort(request, server.crc32server, 'online', 'bots') }}
|
||||
</th>
|
||||
<th class="text-align-center">
|
||||
{{ 'Total' | trans }} {{ sort(request, server.crc32server, 'online', 'total') }}
|
||||
</th>
|
||||
</tr>
|
||||
{% for online in server.online %}
|
||||
<tr>
|
||||
<td class="text-align-left">{{ online.time | date('r') }}</td>
|
||||
<td class="text-align-center">{{ online.players }}</td>
|
||||
<td class="text-align-center">{{ online.bots }}</td>
|
||||
<td class="text-align-center">{{ online.total }}</td>
|
||||
</tr>
|
||||
{% endfor %}
|
||||
</table>
|
||||
{% endif %}
|
||||
{% if server.players %}
|
||||
<a name="players"></a>
|
||||
<h3 class="padding-y-8-px text-align-right">
|
||||
{{ 'Players' | trans }}
|
||||
<a href="{{ path('rss_players', { crc32server : server.crc32server }) }}" title="{{ 'RSS' | trans }}"><svg xmlns="http://www.w3.org/2000/svg" width="12" height="12" fill="currentColor" viewBox="0 0 16 16">
|
||||
<path d="M2 0a2 2 0 0 0-2 2v12a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V2a2 2 0 0 0-2-2zm1.5 2.5c5.523 0 10 4.477 10 10a1 1 0 1 1-2 0 8 8 0 0 0-8-8 1 1 0 0 1 0-2m0 4a6 6 0 0 1 6 6 1 1 0 1 1-2 0 4 4 0 0 0-4-4 1 1 0 0 1 0-2m.5 7a1.5 1.5 0 1 1 0-3 1.5 1.5 0 0 1 0 3"/>
|
||||
</svg></a>
|
||||
</h3>
|
||||
<table>
|
||||
<tr>
|
||||
<th class="text-align-center">
|
||||
{{ 'Frags' | trans }} {{ sort(request, server.crc32server, 'players', 'frags') }}
|
||||
</th>
|
||||
<th class="text-align-left">
|
||||
{{ 'Name' | trans }} {{ sort(request, server.crc32server, 'players', 'name') }}
|
||||
</th>
|
||||
<th class="text-align-left">
|
||||
{{ 'Online' | trans }} {{ sort(request, server.crc32server, 'players', 'online') }}
|
||||
</th>
|
||||
<th class="text-align-left">
|
||||
{{ 'Joined' | trans }} {{ sort(request, server.crc32server, 'players', 'joined') }}
|
||||
</th>
|
||||
</tr>
|
||||
{% for player in server.players %}
|
||||
<tr>
|
||||
<td class="text-align-center">{{ player.frags }}</td>
|
||||
<td class="text-align-left">{{ player.name }}</td>
|
||||
<td class="text-align-left">{{ player.online | format_date }}</td>
|
||||
<td class="text-align-left">{{ player.joined | format_date }}</td>
|
||||
</tr>
|
||||
{% endfor %}
|
||||
</table>
|
||||
{% endif %}
|
||||
</div>
|
||||
{% endblock %}
|
Loading…
x
Reference in New Issue
Block a user