mirror of
https://github.com/YGGverse/HLState.git
synced 2025-03-12 13:31:24 +00:00
implement players online monthly chart
This commit is contained in:
parent
4d95117912
commit
6403b2fa8c
8
.env
8
.env
@ -43,7 +43,13 @@ MESSENGER_TRANSPORT_DSN=doctrine://default?auto_setup=0
|
||||
# HLState
|
||||
|
||||
# Application version, used for API and media cache
|
||||
APP_VERSION="1.3.0"
|
||||
APP_VERSION="1.4.0"
|
||||
|
||||
# Memcached server
|
||||
APP_MEMCACHED_NAMESPACE="HLState"
|
||||
APP_MEMCACHED_HOST="localhost"
|
||||
APP_MEMCACHED_PORT=11211
|
||||
APP_MEMCACHED_TIMEOUT=3600
|
||||
|
||||
# Application name
|
||||
APP_NAME="HLState"
|
||||
|
@ -21,7 +21,7 @@ Project initially written to explore [Yggdrasil](https://github.com/yggdrasil-ne
|
||||
|
||||
## Install
|
||||
|
||||
* `apt install git composer curl php php-xml php-intl php-mbstring php-curl php-sqlite3`
|
||||
* `apt install git composer curl memcached php php-xml php-intl php-mbstring php-curl php-sqlite3 php-memcached`
|
||||
* `git clone https://github.com/YGGverse/HLState.git`
|
||||
* `cd HLState`
|
||||
* `composer install`
|
||||
@ -62,6 +62,8 @@ Please create new branch from main before make PR
|
||||
* [SVG icons](https://icons.getbootstrap.com)
|
||||
* [PHP Source Query](https://github.com/xPaw/PHP-Source-Query)
|
||||
* [HL-PHP](https://github.com/YGGverse/hl-php)
|
||||
* [JS-less Graphs PHP](https://github.com/YGGverse/graph-php)
|
||||
* [Memcached API for PHP](https://github.com/YGGverse/cache-php)
|
||||
* [Favicons](https://realfavicongenerator.net)
|
||||
|
||||
## Support
|
||||
|
@ -45,6 +45,8 @@
|
||||
"twig/intl-extra": "^3.8",
|
||||
"twig/twig": "^3.8",
|
||||
"xpaw/php-source-query-class": "dev-master",
|
||||
"yggverse/cache": "^0.3.1",
|
||||
"yggverse/graph": "^0.2.2",
|
||||
"yggverse/hl": "^1.0"
|
||||
},
|
||||
"config": {
|
||||
|
@ -4,6 +4,10 @@
|
||||
# Put parameters here that don't need to change on each machine where the app is deployed
|
||||
# https://symfony.com/doc/current/best_practices.html#use-parameters-for-application-configuration
|
||||
parameters:
|
||||
app.memcached.host: '%env(APP_MEMCACHED_HOST)%'
|
||||
app.memcached.port: '%env(APP_MEMCACHED_PORT)%'
|
||||
app.memcached.namespace: '%env(APP_MEMCACHED_NAMESPACE)%'
|
||||
app.memcached.timeout: '%env(APP_MEMCACHED_TIMEOUT)%'
|
||||
app.version: '%env(APP_VERSION)%'
|
||||
app.name: '%env(APP_NAME)%'
|
||||
app.theme: '%env(APP_THEME)%'
|
||||
|
@ -16,6 +16,8 @@
|
||||
--color-warning: #f37b21;
|
||||
--color-error: #ff6363;
|
||||
--color-default: #999;
|
||||
|
||||
--background-color-hover-default: rgba(125, 125, 125, 0.1);
|
||||
}
|
||||
|
||||
*::placeholder
|
||||
@ -65,6 +67,10 @@ table td
|
||||
padding: 4px;
|
||||
}
|
||||
|
||||
table tr:hover td {
|
||||
background-color: var(--background-color-hover-default);
|
||||
}
|
||||
|
||||
ul
|
||||
{
|
||||
margin-left: 16px;
|
||||
@ -143,6 +149,26 @@ a.color-default:visited
|
||||
color: var(--color-default);
|
||||
}
|
||||
|
||||
.background-color-success
|
||||
{
|
||||
background-color: var(--color-success);
|
||||
}
|
||||
|
||||
.background-color-warning
|
||||
{
|
||||
background-color: var(--color-warning);
|
||||
}
|
||||
|
||||
.background-color-error
|
||||
{
|
||||
background-color: var(--color-error);
|
||||
}
|
||||
|
||||
.background-color-default
|
||||
{
|
||||
background-color: var(--color-default);
|
||||
}
|
||||
|
||||
.text-align-left
|
||||
{
|
||||
text-align: left;
|
||||
@ -188,4 +214,84 @@ a.color-default:visited
|
||||
{
|
||||
margin-bottom: 8px;
|
||||
margin-top: 8px;
|
||||
}
|
||||
|
||||
/*
|
||||
* yggverse/graph UI
|
||||
*
|
||||
* for any feedback visit official page:
|
||||
* https://github.com/YGGverse/graph-php
|
||||
*
|
||||
*/
|
||||
|
||||
.calendar__month {
|
||||
overflow: hidden
|
||||
}
|
||||
|
||||
.calendar__month > .day {
|
||||
float: left;
|
||||
height: 96px;
|
||||
margin: 2px 0;
|
||||
position: relative;
|
||||
width: 14.285714286%;
|
||||
}
|
||||
|
||||
.calendar__month > .day:hover {
|
||||
background-color: var(--background-color-hover-default);
|
||||
}
|
||||
|
||||
.calendar__month > .day > .number {
|
||||
background-color: var(--background-color-hover-default);
|
||||
border-radius: 50%;
|
||||
font-size: 10px;
|
||||
height: 16px;
|
||||
left: 4px;
|
||||
line-height: 16px;
|
||||
opacity: 0.8;
|
||||
position: absolute;
|
||||
text-align: center;
|
||||
top: 4px;
|
||||
width: 16px;
|
||||
z-index: 99;
|
||||
}
|
||||
|
||||
.calendar__month > .day:hover > .number {
|
||||
opacity: 1;
|
||||
}
|
||||
|
||||
.calendar__month > .day > .layer-0 > .label {
|
||||
background-color: var(--background-color-hover-default);
|
||||
border-radius: 3px;
|
||||
display: none;
|
||||
font-size: 10px;
|
||||
padding: 0 4px;
|
||||
position: absolute;
|
||||
right: 4px;
|
||||
top: 6px;
|
||||
z-index: 99;
|
||||
}
|
||||
|
||||
.calendar__month > .day:hover > .layer-0 > .label {
|
||||
display: block;
|
||||
}
|
||||
|
||||
.calendar__month > .day > .layer-0 > .value {
|
||||
bottom: 0;
|
||||
opacity: 0.5;
|
||||
position: absolute;
|
||||
z-index: 0;
|
||||
}
|
||||
|
||||
.calendar__month > .day > .layer-1 > .label {
|
||||
display: none
|
||||
}
|
||||
|
||||
.calendar__month > .day > .layer-1 > .value {
|
||||
bottom: 0;
|
||||
position: absolute;
|
||||
z-index: 1;
|
||||
}
|
||||
|
||||
.calendar__month > .day > .layer-1 > .value:hover {
|
||||
opacity: .8;
|
||||
}
|
@ -27,9 +27,18 @@ class MainController extends AbstractController
|
||||
)]
|
||||
public function index(
|
||||
?Request $request,
|
||||
TranslatorInterface $translatorInterface,
|
||||
EntityManagerInterface $entityManagerInterface
|
||||
): Response
|
||||
{
|
||||
// Init memory
|
||||
$memory = new \Yggverse\Cache\Memory(
|
||||
$this->getParameter('app.memcached.host'),
|
||||
$this->getParameter('app.memcached.port'),
|
||||
$this->getParameter('app.memcached.namespace'),
|
||||
$this->getParameter('app.memcached.timeout') + time(),
|
||||
);
|
||||
|
||||
// Collect servers info
|
||||
$servers = [];
|
||||
|
||||
@ -119,11 +128,111 @@ class MainController extends AbstractController
|
||||
$servers
|
||||
);
|
||||
|
||||
// Online calendar
|
||||
$time = time();
|
||||
|
||||
$month = new \Yggverse\Graph\Calendar\Month($time);
|
||||
|
||||
foreach ($month->getNodes() as $day => $node)
|
||||
{
|
||||
// Skip future days processing
|
||||
if ($day > date('j'))
|
||||
{
|
||||
break;
|
||||
}
|
||||
|
||||
// Add daily stats
|
||||
$total = $memory->getByMethodCallback(
|
||||
$entityManagerInterface->getRepository(Online::class),
|
||||
'getMaxPlayersByTimeInterval',
|
||||
[
|
||||
strtotime(
|
||||
sprintf(
|
||||
'%s-%s-%s 00:00',
|
||||
date('Y', $time),
|
||||
date('n', $time),
|
||||
$day
|
||||
)
|
||||
),
|
||||
strtotime(
|
||||
'+1 day',
|
||||
strtotime(
|
||||
sprintf(
|
||||
'%s-%s-%s 00:00',
|
||||
date('Y', $time),
|
||||
date('n', $time),
|
||||
$day
|
||||
)
|
||||
)
|
||||
)
|
||||
],
|
||||
time() + ($day == date('j') ? 60 : 2592000)
|
||||
);
|
||||
|
||||
$month->addNode(
|
||||
$day,
|
||||
$total,
|
||||
sprintf(
|
||||
$translatorInterface->trans('online %d'),
|
||||
$total
|
||||
),
|
||||
null,
|
||||
0
|
||||
);
|
||||
|
||||
// Add hourly stats
|
||||
for ($hour = 0; $hour < 24; $hour++)
|
||||
{
|
||||
$total = $memory->getByMethodCallback(
|
||||
$entityManagerInterface->getRepository(Online::class),
|
||||
'getMaxPlayersByTimeInterval',
|
||||
[
|
||||
strtotime(
|
||||
sprintf(
|
||||
'%s-%s-%s %s:00',
|
||||
date('Y', $time),
|
||||
date('n', $time),
|
||||
$day,
|
||||
$hour
|
||||
)
|
||||
),
|
||||
strtotime(
|
||||
sprintf(
|
||||
'%s-%s-%s %s:00',
|
||||
date('Y', $time),
|
||||
date('n', $time),
|
||||
$day,
|
||||
$hour + 1
|
||||
)
|
||||
)
|
||||
],
|
||||
time() + ($day == date('j') ? 60 : 2592000)
|
||||
);
|
||||
|
||||
$month->addNode(
|
||||
$day,
|
||||
$total,
|
||||
sprintf(
|
||||
$translatorInterface->trans('%s:00-%s:00 online %s'),
|
||||
$hour,
|
||||
$hour + 1,
|
||||
$total
|
||||
),
|
||||
'background-color-default',
|
||||
1
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
return $this->render(
|
||||
'default/main/index.html.twig',
|
||||
[
|
||||
'request' => $request,
|
||||
'servers' => $servers
|
||||
'servers' => $servers,
|
||||
'month' =>
|
||||
[
|
||||
'online' => (array) $month->getNodes()
|
||||
]
|
||||
]
|
||||
);
|
||||
}
|
||||
|
@ -25,12 +25,27 @@ class OnlineRepository extends ServiceEntityRepository
|
||||
int $crc32server
|
||||
): int
|
||||
{
|
||||
return $this->createQueryBuilder('o')
|
||||
->select('count(o.id)')
|
||||
->where('o.crc32server = :crc32server')
|
||||
->setParameter('crc32server', $crc32server)
|
||||
->getQuery()
|
||||
->getSingleScalarResult()
|
||||
;
|
||||
return
|
||||
$this->createQueryBuilder('o')
|
||||
->select('count(o.id)')
|
||||
->where('o.crc32server = :crc32server')
|
||||
->setParameter('crc32server', $crc32server)
|
||||
->getQuery()
|
||||
->getSingleScalarResult();
|
||||
}
|
||||
|
||||
public function getMaxPlayersByTimeInterval(
|
||||
int $from,
|
||||
int $to
|
||||
): int
|
||||
{
|
||||
return (int)
|
||||
$this->createQueryBuilder('o')
|
||||
->select('max(o.players)')
|
||||
->where('o.time >= :from AND o.time <= :to')
|
||||
->setParameter('from', $from)
|
||||
->setParameter('to', $to)
|
||||
->getQuery()
|
||||
->getSingleScalarResult();
|
||||
}
|
||||
}
|
||||
|
@ -91,4 +91,35 @@
|
||||
</tr>
|
||||
{% endif %}
|
||||
</table>
|
||||
<br />
|
||||
<h2>{{ 'now' | date('M, Y') }}</h2>
|
||||
<hr />
|
||||
<div class="padding-y-8-px calendar__month">
|
||||
{% for day, node in month.online %}
|
||||
{% if day <= 'now' | date('j') %}
|
||||
<div class="day">
|
||||
<div class="number">
|
||||
{{ day }}
|
||||
</div>
|
||||
{% for i, layers in node %}
|
||||
<div class="layer layer-{{ i }}">
|
||||
<div class="label">
|
||||
{% for layer in layers %}
|
||||
<div{# class="{{ layer.class }}"#}>
|
||||
{{ layer.label }}
|
||||
</div>
|
||||
{% endfor %}
|
||||
</div>
|
||||
{% for layer in layers %}
|
||||
<div title="{{ layer.label }}"
|
||||
class="value {{ layer.class }}"
|
||||
style="width:{{ layer.width }}%;height:{{ layer.height }}%;left:{{ layer.offset }}%"></div>
|
||||
{% endfor %}
|
||||
</div>
|
||||
{% endfor %}
|
||||
</div>
|
||||
{% endif %}
|
||||
{% endfor %}
|
||||
</div>
|
||||
<hr />
|
||||
{% endblock %}
|
Loading…
x
Reference in New Issue
Block a user