mirror of https://github.com/YGGverse/HLState.git
ghost
10 months ago
15 changed files with 795 additions and 484 deletions
@ -0,0 +1,31 @@
@@ -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'); |
||||
} |
||||
} |
@ -0,0 +1,183 @@
@@ -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 |
||||
] |
||||
] |
||||
); |
||||
} |
||||
} |
@ -0,0 +1,111 @@
@@ -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; |
||||
} |
||||
} |
@ -0,0 +1,23 @@
@@ -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); |
||||
} |
||||
} |
@ -0,0 +1,171 @@
@@ -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…
Reference in new issue