Skip to content

Commit

Permalink
dashboard player charts, enable disable email, fixes
Browse files Browse the repository at this point in the history
  • Loading branch information
root committed Sep 19, 2024
1 parent 0e06daa commit 505e9a4
Show file tree
Hide file tree
Showing 13 changed files with 605 additions and 14 deletions.
259 changes: 255 additions & 4 deletions app/Http/Controllers/DashboardController.php
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@
use App\Models\SaBan;
use App\Models\SaMute;
use App\Models\SaServer;
use App\Models\ServerStats;
use Carbon\Carbon;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\Auth;
Expand Down Expand Up @@ -51,7 +52,8 @@ public function home(Request $request)
$servers = ModuleServerSetting::all();
$topPlayersData = $this->getTop5Players();
}

$playerChart = $this->getServerStatsWithSeriesFormat($request->input('interval'));
$playerMapChart = $this->getMapStatsWithSeriesFormat($request->input('interval'));
return view('admin.dashboard',
compact(
'totalBans',
Expand All @@ -64,7 +66,9 @@ public function home(Request $request)
'activeMutes',
'totalActiveBans',
'totalActiveMutes',
'servers'
'servers',
'playerChart',
'playerMapChart'
)
);
}
Expand Down Expand Up @@ -139,8 +143,7 @@ public function getTop5Players()
$totalPlayers = Ranks::count();
} else {
// New Logic
$topPlayers = ZenithPlayerStorage::orderByRaw('JSON_EXTRACT(`K4-Zenith-Ranks.storage`, "$.Points") DESC')
->take(5)
$topPlayers = ZenithPlayerStorage::orderByRaw('CAST(JSON_UNQUOTE(JSON_EXTRACT(`K4-Zenith-Ranks.storage`, "$.Points")) AS UNSIGNED) DESC') ->take(5)
->get();
$serverId = Crypt::encrypt(Session::get('Ranks_server'));
foreach ($topPlayers as $player) {
Expand All @@ -167,4 +170,252 @@ public function getTop5Players()
return ['topPlayers' => $topPlayers, 'totalPlayers' => $totalPlayers];
}


public function getServerStatsWithSeriesFormat($intervalType)
{
$maxSteps = 12;

switch ($intervalType) {
case '1hour':
$interval = 60; // 60 minutes for 1 hour
$dateFormat = 'Y-m-d H:00:00'; // Group by hour
// Adjust totalMinutes to include the current hour
$totalMinutes = ($interval * $maxSteps) - Carbon::now()->minute;
break;
case '1day':
$interval = 1440; // 1440 minutes for 1 day
$dateFormat = 'Y-m-d'; // Group by day
// Adjust totalMinutes to include the current day
$totalMinutes = ($interval * $maxSteps) - ((Carbon::now()->hour * 60) + Carbon::now()->minute);
break;
case '1month':
// Assuming 43200 minutes per month (30 days), use this as an approximation
$interval = 43200; // 30 days * 24 hours * 60 minutes = 43200 minutes per month
$dateFormat = 'Y-m'; // Group by month
// Adjust totalMinutes to approximate months, including the current month
$currentDayOfMonth = Carbon::now()->day;
$totalMinutes = ($interval * $maxSteps) - (($currentDayOfMonth - 1) * 1440 + (Carbon::now()->hour * 60) + Carbon::now()->minute);
break;
default: // '5min' or other minute intervals
$interval = 5; // 5 minutes for the default 5-minute interval
$dateFormat = 'Y-m-d H:i:00'; // Group by 5-minute intervals

$totalMinutes = ($interval * $maxSteps) - (Carbon::now()->minute % $interval);

}
if($interval == 5) {
$startTimeOriginal = Carbon::now()->subMinutes($totalMinutes);
$minute = floor($startTimeOriginal->minute / 5) * 5;
$startTime = Carbon::parse($startTimeOriginal->format("Y-m-d H:$minute"));
}
else
$startTime = Carbon::now()->subMinutes($totalMinutes);

$endTime = Carbon::now();

$serverStats = ServerStats::selectRaw('server_id, MAX(player_count) as player_count, FROM_UNIXTIME(FLOOR(UNIX_TIMESTAMP(recorded_at) / (' . ($interval * 60) . ')) * (' . ($interval * 60) . ')) as interval_time')
->where('recorded_at', '>=', $startTime)
->groupBy('server_id', 'interval_time')
->orderBy('interval_time')
->get();

$intervals = [];
$steps = 0;

while ($startTime <= $endTime && $steps < $maxSteps) {
$intervals[] = $startTime->format($dateFormat); // Format according to the interval type
$startTime->addMinutes($interval);
$steps++;
}

$groupedData = [];
$serverIds = $serverStats->pluck('server_id');
$allServers = SaServer::has('visible')->get();
foreach ($serverStats as $stat) {
$serverId = $stat->server_id;
$intervalTime = ($intervalType == '1day' || $intervalType == '1month' || $intervalType == '1hour') ? Carbon::parse($stat->interval_time)->format($dateFormat):$stat->interval_time;
$playerCount = $stat->player_count;
$groupedData[$serverId][$intervalTime] = $playerCount;
}
$seriesData = [];

foreach ($groupedData as $serverId => $intervalData) {
$series = [
'name' => $allServers->where('id', $serverId)->value('hostname'),
'data' => []
];

foreach ($intervals as $interval) {
// Check against different intervals (hours, days, weeks, months)
$series['data'][] = $intervalData[$interval] ?? 0;
}

$seriesData[] = $series;
}
// Format intervals for JavaScript before sending to the view
$formattedIntervals = array_map(function ($interval) use ($intervalType) {

// Default case for other intervals
switch ($intervalType) {
case '1hour':
$format = 'D H:00';
break;
case '1day':
$format = 'D, M j';
break;
case '5min':
$format = 'D H:i';
break;
case '1month':
$format = 'M Y';
break;
default:
$format = 'Y-m-d H:i:s'; // Default format
}

return Carbon::parse($interval, config('app.timezone'))->format($format);
}, $intervals);
// Prepare other active servers if no data
$allServerData = array_diff($allServers->pluck('id')->toArray(), $serverIds->toArray());
foreach($allServerData as $serverId) {
$data = [];
for ($i = 0; $i < count($intervals); $i++) {
$data[] = 0;
}
$seriesData[] = [
"name" => $allServers->where('id', $serverId)->value('hostname'),
'data'=> $data
];
}

return [
'seriesData' => json_encode($seriesData),
'intervals' => json_encode($formattedIntervals)
];
}

public function getMapStatsWithSeriesFormat($intervalType)
{

$maxSteps = 12;

switch ($intervalType) {
case '1hour':
$interval = 60; // 60 minutes for 1 hour
$dateFormat = 'Y-m-d H:00:00'; // Group by hour
$totalMinutes = ($interval * $maxSteps) - Carbon::now()->minute;
break;
case '1day':
$interval = 1440; // 1440 minutes for 1 day
$dateFormat = 'Y-m-d 00:00:00'; // Group by day
$totalMinutes = ($interval * $maxSteps) - ((Carbon::now()->hour * 60) + Carbon::now()->minute);
break;
case '1month':
// Assuming 43200 minutes per month (30 days), use this as an approximation
$interval = 43200; // 30 days * 24 hours * 60 minutes = 43200 minutes per month
$dateFormat = 'Y-m'; // Group by month
// Adjust totalMinutes to approximate months, including the current month
$currentDayOfMonth = Carbon::now()->day;
$totalMinutes = ($interval * $maxSteps) - (($currentDayOfMonth - 1) * 1440 + (Carbon::now()->hour * 60) + Carbon::now()->minute);
break;
default:
$interval = 5; // 5 minutes for the default 5-minute interval
$dateFormat = 'Y-m-d H:i:00'; // Group by 5-minute intervals
$totalMinutes = ($interval * $maxSteps) - (Carbon::now()->minute % $interval);
}

if($interval == 5) {
$startTimeOriginal = Carbon::now()->subMinutes($totalMinutes);
$minute = floor($startTimeOriginal->minute / 5) * 5;
$startTime = Carbon::parse($startTimeOriginal->format("Y-m-d H:$minute"));
}
else
$startTime = Carbon::now()->subMinutes($totalMinutes);
$endTime = Carbon::now();

// Fetch all unique maps from the stats table
$allMaps = ServerStats::distinct('map')->pluck('map');

// Query the stats grouped by map and interval time
$mapStats = ServerStats::selectRaw('map, MAX(player_count) as player_count, FROM_UNIXTIME(FLOOR(UNIX_TIMESTAMP(recorded_at) / (' . ($interval * 60) . ')) * (' . ($interval * 60) . ')) as interval_time')
->where('recorded_at', '>=', $startTime)
->groupBy('map', 'interval_time')
->orderBy('interval_time')
->get();

$intervals = [];
$steps = 0;

while ($startTime <= $endTime && $steps < $maxSteps) {
$intervals[] = $startTime->format($dateFormat); // Format according to the interval type
$startTime->addMinutes($interval);
$steps++;
}

// Group data by map
$groupedData = [];
$mapNames = $mapStats->pluck('map');
foreach ($mapStats as $stat) {
$map = $stat->map;
$intervalTime = ($intervalType == '1day' || $intervalType == '1month' || $intervalType == '1hour') ? Carbon::parse($stat->interval_time)->format($dateFormat):$stat->interval_time;
$playerCount = $stat->player_count;
$groupedData[$map][$intervalTime] = $playerCount;
}

// Prepare series data for each map
$seriesData = [];
foreach ($groupedData as $map => $intervalData) {
$series = [
'name' => $map, // Use map name instead of server hostname
'data' => []
];

foreach ($intervals as $interval) {
// Check for player count data for each interval, default to 0
$series['data'][] = $intervalData[$interval] ?? 0;
}

$seriesData[] = $series;
}

// Format intervals for JavaScript before sending to the view
$formattedIntervals = array_map(function ($interval) use ($intervalType) {

// Default case for other intervals
switch ($intervalType) {
case '1hour':
$format = 'D H:00';
break;
case '1day':
$format = 'D, M j';
break;
case '5min':
$format = 'D H:i';
break;
case '1month':
$format = 'M Y';
break;
default:
$format = 'Y-m-d H:i:s'; // Default format
}

return Carbon::parse($interval, config('app.timezone'))->format($format);
}, $intervals);

// Prepare all maps if no data for certain maps
$missingMapData = array_diff($allMaps->toArray(), $mapNames->toArray());
foreach ($missingMapData as $map) {
$data = array_fill(0, count($intervals), 0); // Fill with zeros for missing data
$seriesData[] = [
"name" => $map,
'data' => $data
];
}
return [
'seriesData' => json_encode($seriesData),
'intervals' => json_encode($formattedIntervals)
];
}


}
4 changes: 2 additions & 2 deletions app/Http/Controllers/K4Ranks/RanksController.php
Original file line number Diff line number Diff line change
Expand Up @@ -55,15 +55,15 @@ public function getPlayersList(Request $request)
}
} else {
// New Logic
$query = ZenithPlayerStorage::selectRaw('*, (SELECT COUNT(*) + 1 FROM zenith_player_storage AS zps WHERE JSON_EXTRACT(zps.`K4-Zenith-Ranks.storage`, "$.Points") > JSON_EXTRACT(zenith_player_storage.`K4-Zenith-Ranks.storage`, "$.Points")) AS `position`');
$query = ZenithPlayerStorage::selectRaw('*, (SELECT COUNT(*) + 1 FROM zenith_player_storage AS zps WHERE CAST(JSON_EXTRACT(zps.`K4-Zenith-Ranks.storage`, "$.Points") AS UNSIGNED) > CAST(JSON_EXTRACT(zenith_player_storage.`K4-Zenith-Ranks.storage`, "$.Points") AS UNSIGNED)) AS `position`');
if (!empty($searchValue)) {
$query->where('steam_id', 'like', '%' . $searchValue . '%')
->orWhereRaw('JSON_UNQUOTE(JSON_EXTRACT(`K4-Zenith-Ranks.storage`, "$.Rank")) like ?', ['%' . $searchValue . '%']);
}
if ($orderColumn !== null) {
$columnName = $request->input('columns.' . $orderColumn . '.data');
if ($columnName == 'points') {
$query->orderByRaw('JSON_EXTRACT(`K4-Zenith-Ranks.storage`, "$.Points") ' . $orderDirection);
$query->orderByRaw('CAST(JSON_UNQUOTE(JSON_EXTRACT(`K4-Zenith-Ranks.storage`, "$.Points")) AS UNSIGNED) ' . $orderDirection);
} else {
$query->orderBy($columnName, $orderDirection);
}
Expand Down
36 changes: 36 additions & 0 deletions app/Http/Controllers/ServerController.php
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
use App\Models\SaAdminsFlags;
use App\Models\SaBan;
use App\Models\SaServer;
use App\Models\ServerStats;
use App\Models\ServerVisibilitySetting;
use App\Services\RconService;
use Carbon\Carbon;
Expand Down Expand Up @@ -301,4 +302,39 @@ private function getServerDetails($ip, $port)
return null;
}
}


public function trackServerPlayerCounts(Request $request)
{
$token = $request->query('token');

// Validate the token
if ($token !== env('_token')) {
Log::warning('Unauthorized access attempt with token: ' . $token);
return response()->json(['status' => 'error', 'message' => 'Invalid API token'], 403);
}
$servers = SaServer::has('visible')->get();
foreach ($servers as $server) {
list($serverIp, $serverPort) = explode(":", $server->address);

try {
$serverDetails = $this->getServerDetails($serverIp, $serverPort);
if ($serverDetails) {
// Save the player count using the ServerStats model
if($serverDetails['players'] > 0 ) {
ServerStats::create([
'server_id' => $server->id,
'player_count' => $serverDetails['players'],
'map' => $serverDetails['map'],
'recorded_at' => now(),
]);
}
}
} catch (\Exception $e) {
Log::error('Steam Web API Error: ' . $e->getMessage());
}
}

return response()->json(['status' => 'success', 'message' => 'Player counts recorded']);
}
}
22 changes: 22 additions & 0 deletions app/Models/ServerStats.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
<?php

namespace App\Models;

use Illuminate\Database\Eloquent\Factories\HasFactory;
use Illuminate\Database\Eloquent\Model;

class ServerStats extends Model
{
use HasFactory;

public $table = 'server_player_stats';

public $timestamps = false;

protected $fillable = ['server_id', 'player_count', 'map', 'recorded_at'];

public function server()
{
return $this->belongsTo(SaServer::class, 'server_id');
}
}
2 changes: 1 addition & 1 deletion config/app.php
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@
| Application Version
|--------------------------------------------------------------------------
*/
'version' => '4.7',
'version' => '4.8',
/*
|--------------------------------------------------------------------------
| Application Name
Expand Down
Loading

0 comments on commit 505e9a4

Please sign in to comment.