Skip to content

Commit

Permalink
✨ feat: Add player location maps
Browse files Browse the repository at this point in the history
  • Loading branch information
DNA-styx committed Dec 23, 2024
1 parent a6c18ec commit 985e600
Show file tree
Hide file tree
Showing 5 changed files with 387 additions and 0 deletions.
227 changes: 227 additions & 0 deletions web/includes/amcharts/display_map.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,227 @@
<?php
// This allows us to access native files directly
define('IN_HLSTATS', true);
// Allows access to native database connection variables
require($_SERVER['DOCUMENT_ROOT'].'/config.php');
// Allow access to native functions
require_once('../functions.php');

// Get map and game type
if (isset($_GET['maptype'])) {
$amcharts_maptype = valid_request(strval($_GET['maptype']),false);
} else {
$amcharts_maptype = "rand";
}

if (isset($_GET['mapgame'])) {
$amcharts_mapgame = valid_request(strval($_GET['mapgame']),false);
} else {
die('MapGame not passed');
}

// Troubleshooting
// echo $amcharts_maptype . " & " . $amcharts_mapgame;

require_once('inc_amcharts_functions.php');

?>

<head>

</head>

<!-- Styles -->
<style>
#amcharts_chartdiv {
width: 100%;
height: 100%;
position: relative; /* Ensure relative positioning */
}
</style>

<!-- Resources -->
<script src="https://cdn.amcharts.com/lib/5/index.js"></script>
<script src="https://cdn.amcharts.com/lib/5/map.js"></script>
<script src="https://cdn.amcharts.com/lib/5/geodata/worldLow.js"></script>
<script src="https://cdn.amcharts.com/lib/5/themes/Animated.js"></script>

<!-- Chart code -->
<script>
am5.ready(function() {

// Create root element
// https://www.amcharts.com/docs/v5/getting-started/#Root_element
var root = am5.Root.new("amcharts_chartdiv");

// Set themes
// https://www.amcharts.com/docs/v5/concepts/themes/
root.setThemes([
am5themes_Animated.new(root)
]);

// Create the map chart
// https://www.amcharts.com/docs/v5/charts/map-chart/
var chart = root.container.children.push(am5map.MapChart.new(root, {
panX: "translateX",
panY: "translateY",
projection: am5map.geoMercator()
}));

// Remove the in-chart title and place it in HTML instead
// var title = chart.children.unshift(am5.Label.new(root, {
// text: "World Map",
// fontSize: 25,
// fontWeight: "bold",
// textAlign: "center",
// x: am5.p50,
// centerX: am5.p50,
// y: 20
// }));

var cont = chart.children.push(am5.Container.new(root, {
layout: root.horizontalLayout,
x: 20,
y: 40
}));

// Turn off map/globe switch
// can't get it to work with
// theme switcher
//
// Add labels and controls
// cont.children.push(am5.Label.new(root, {
// centerY: am5.p50,
// text: "Map"
// }));

// var switchButton = cont.children.push(am5.Button.new(root, {
// themeTags: ["switch"],
// centerY: am5.p50,
// icon: am5.Circle.new(root, {
// themeTags: ["icon"]
// })
// }));

// switchButton.on("active", function() {
// if (!switchButton.get("active")) {
// chart.set("projection", am5map.geoMercator());
// chart.set("panX", "translateX");
// chart.set("panY", "translateY");
// }
// else {
// chart.set("projection", am5map.geoOrthographic());
// chart.set("panX", "rotateX");
// chart.set("panY", "rotateY");
// }
// });

// cont.children.push(am5.Label.new(root, {
// centerY: am5.p50,
// text: "Globe"
// }));

// Create main polygon series for countries
// https://www.amcharts.com/docs/v5/charts/map-chart/map-polygon-series/
var polygonSeries = chart.series.push(am5map.MapPolygonSeries.new(root, {
geoJSON: am5geodata_worldLow
}));

var graticuleSeries = chart.series.push(am5map.GraticuleSeries.new(root, {}));
graticuleSeries.mapLines.template.setAll({
stroke: root.interfaceColors.get("alternativeBackground"),
strokeOpacity: 0.08
});

// Create line series for trajectory lines
// https://www.amcharts.com/docs/v5/charts/map-chart/map-line-series/
var lineSeries = chart.series.push(am5map.MapLineSeries.new(root, {}));
lineSeries.mapLines.template.setAll({
stroke: root.interfaceColors.get("alternativeBackground"),
strokeOpacity: 0.6
});

// destination series
var citySeries = chart.series.push(
am5map.MapPointSeries.new(root, {})
);

citySeries.bullets.push(function() {
var circle = am5.Circle.new(root, {
radius: 5,
//tooltipText: "{title}",
tooltipHTML: "{title}",
cursorOverStyle: "pointer",
tooltipY: 0,
fill: am5.color(0x7c3aed),
stroke: root.interfaceColors.get("background"),
strokeWidth: 2
});

circle.events.on("click", (e) => {
window.open(e.target.dataItem.dataContext.url);
// window.location.href = e.target.dataItem.dataContext.url;
});

return am5.Bullet.new(root, {
sprite: circle
});
});

// arrow series
var arrowSeries = chart.series.push(
am5map.MapPointSeries.new(root, {})
);

arrowSeries.bullets.push(function() {
var arrow = am5.Graphics.new(root, {
fill: am5.color(0x000000),
stroke: am5.color(0x000000),
draw: function (display) {
display.moveTo(0, -3);
display.lineTo(8, 0);
display.lineTo(0, 3);
display.lineTo(0, -3);
}
});

return am5.Bullet.new(root, {
sprite: arrow
});
});

var cities = <?php echo $ten_player_details; ?>;

citySeries.data.setAll(cities);

// prepare line series data
var destinations = <?php echo $ten_player_names; ?>;

// Server coordinates
var originLongitude = <?php echo $server_lng ?>;
var originLatitude = <?php echo $server_lat ?>;

// Remove the lines back to the server
// am5.array.each(destinations, function (did) {
// var destinationDataItem = citySeries.getDataItemById(did);
// var lineDataItem = lineSeries.pushDataItem({ geometry: { type: "LineString", coordinates: [[originLongitude, originLatitude], [destinationDataItem.get("longitude"), destinationDataItem.get("latitude")]] } });
//
// arrowSeries.pushDataItem({
// lineDataItem: lineDataItem,
// positionOnLine: 0.5,
// autoRotate: true,
// autoRotateAngle: 180
// });
// });

polygonSeries.events.on("datavalidated", function () {
chart.zoomToGeoPoint({ longitude: <?php echo $server_lng ?>, latitude: <?php echo $server_lat ?> }, 3);
});

// Make stuff animate on load
chart.appear(1000, 100);
}); // end am5.ready()
</script>

<!-- HTML -->
<div id="amcharts_chartdiv">
</div>
140 changes: 140 additions & 0 deletions web/includes/amcharts/inc_amcharts_functions.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,140 @@
<?php
// Database configuration
// Using HLStatsx native Variables
$host = DB_ADDR;
$dbname = DB_NAME;
$username = DB_USER;
$password = DB_PASS;

// Declare vars
$amcharts_rank_get = 0;
$amcharts_rank_display = "";
$amcharts__rank_count = 0;


// Which map are we displaying?
// echo ">" .$amcharts_maptype ."<";

switch ($amcharts_maptype) {
case "top":
$amcharts_server_query = "SELECT name,lat,lng FROM `hlstats_Servers` WHERE lat IS NOT NULL LIMIT 1";
$amcharts_player_query = "SELECT playerId,skill,lastName,lat,lng FROM `hlstats_Players` WHERE (lat IS NOT NULL && game = '$amcharts_mapgame' && !hideranking) ORDER BY skill DESC LIMIT 50";
$amcharts_title = "Top Players";
$amcharts_rank_get = 1;
break;

case "active":
$amcharts_server_query = "SELECT name,lat,lng FROM `hlstats_Servers` WHERE (lat IS NOT NULL && game = '$amcharts_mapgame') LIMIT 1";
$amcharts_player_query = "SELECT player_Id AS playerId,skill,`Name` AS lastName,cli_lat AS lat,cli_lng AS lng FROM `hlstats_Livestats` WHERE (cli_lng IS NOT NULL)";
$amcharts_title = "Active Players";
break;

case "recent":
$amcharts_server_query = "SELECT name,lat,lng FROM `hlstats_Servers` WHERE lat IS NOT NULL LIMIT 1";
$amcharts_player_query = "SELECT playerId,skill,lastName,lat,lng FROM `hlstats_Players` WHERE (lat IS NOT NULL && game = '$amcharts_mapgame' && !hideranking) ORDER BY last_event DESC LIMIT 20";
$amcharts_title = "Recent Players";
break;

case "random":
default:
$amcharts_server_query = "SELECT name,lat,lng FROM `hlstats_Servers` WHERE lat IS NOT NULL LIMIT 1";
$amcharts_player_query = "SELECT lastName,lat,lng FROM `hlstats_Players` WHERE lat IS NOT NULL ORDER BY RAND() LIMIT 20";
$amcharts_title = "Ten Random Players";
break;
}


try {
// Create a new PDO instance
$pdo = new PDO("mysql:host=$host;dbname=$dbname;charset=utf8", $username, $password);
$pdo->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);

// Prepare an array to hold the JSON objects
$result = [];
$item1List = [];

// Query to fetch server details
$singleRowSql = $amcharts_server_query;
$singleRowStmt = $pdo->query($singleRowSql);
$singleRow = $singleRowStmt->fetch(PDO::FETCH_ASSOC);


if ($singleRow) {
// Add the server row data to the start of $result
$result[] = [
'id' => $singleRow['name'],
'title' => $singleRow['name'],
'geometry' => [
'type' => 'Point',
'coordinates' => [
floatval($singleRow['lng']), // Convert to float for proper JSON formatting
floatval($singleRow['lat'])
]
]
];

$server_lng = $singleRow['lng'];
$server_lat = $singleRow['lat'];

} else {
// Always return something
// But if geo lookup has failed for server
// it's likely failed for players as well
$result[] = [
'id' => 'Error Getting Server Location',
'title' => 'Error Getting Server Location',
'geometry' => [
'type' => 'Point',
'coordinates' => [
0, 0
]
]
];

$server_lng = 0;
$server_lat = 0;

}

// Query to fetch data
$sql = $amcharts_player_query;
$stmt = $pdo->query($sql);

// Fetch data row by row
while ($row = $stmt->fetch(PDO::FETCH_ASSOC)) {
$amcharts__rank_count ++;

if ($amcharts_rank_get) {
$amcharts_rank_display = "Rank: #" . $amcharts__rank_count . '<br>';
}


$result[] = [
'id' => $row['lastName'],
'title' => '<center>' . $row['lastName'] . '<br><small>' . $amcharts_rank_display . 'Points: ' . $row['skill'] . '<br>(Click me!)</small></center>',
'url' => '/hlstats.php?mode=playerinfo&player=' . $row['playerId'] ,
'geometry' => [
'type' => 'Point',
'coordinates' => [
floatval(round($row['lng'],2)), // Convert to float for proper JSON formatting
floatval(round($row['lat'],2))
]
]
];
$item1List[] = $row['lastName'];
}

// Encode the result as a JSON string
// echo "JSON with full data:\n";
// echo json_encode($result, JSON_PRETTY_PRINT | JSON_UNESCAPED_SLASHES);
$ten_player_details = json_encode($result, JSON_PRETTY_PRINT | JSON_UNESCAPED_SLASHES);

// echo "\n\nJSON with item1 list:\n";
// echo json_encode($item1List, JSON_PRETTY_PRINT | JSON_UNESCAPED_SLASHES);
$ten_player_names = json_encode($item1List, JSON_PRETTY_PRINT | JSON_UNESCAPED_SLASHES);

} catch (PDOException $e) {
echo "Error: " . $e->getMessage();
}
?>
5 changes: 5 additions & 0 deletions web/includes/inc_windmill_functions.php
Original file line number Diff line number Diff line change
Expand Up @@ -269,7 +269,12 @@ function display_ingame_menu() {

}

function display_amcharts_map($mapgame, $maptype) {

echo "<div class=\"hidden md:block\">\n";
echo " <iframe src=\"" . INCLUDE_PATH . "/amcharts/display_map.php?mapgame=" . $mapgame . "&maptype=" . $maptype . "\" height=\"400\" width=\"1500\"></iframe>\n";
echo "</div>\n";

}

?>
8 changes: 8 additions & 0 deletions web/pages/game.php
Original file line number Diff line number Diff line change
Expand Up @@ -195,6 +195,14 @@
<!-- end Card Section -->
<?php echo display_page_subtitle("Game Servers"); ?>

<?php
if ($g_options["show_google_map"] == 1) {
echo " <!-- start Demo Map -->\n";
display_amcharts_map($game,"active");
echo " <!-- End Top Players -->\n";
}
?>

<div class="w-full overflow-hidden rounded-lg shadow-xs">

<div class="w-full overflow-x-auto">
Expand Down
Loading

0 comments on commit 985e600

Please sign in to comment.