Skip to content

Commit

Permalink
Merge pull request supertuxkart#131 from supertuxkart/geolocation
Browse files Browse the repository at this point in the history
Geolocation
  • Loading branch information
vampy authored May 15, 2019
2 parents 5d65435 + 6012de5 commit e6e913c
Show file tree
Hide file tree
Showing 6 changed files with 137 additions and 79 deletions.
1 change: 1 addition & 0 deletions api/server.php
Original file line number Diff line number Diff line change
Expand Up @@ -159,6 +159,7 @@
$output->writeAttribute("port", $request['port']);
$output->writeAttribute("aes-key", $request['aes_key']);
$output->writeAttribute("aes-iv", $request['aes_iv']);
$output->writeAttribute("country-code", $request['country_code']);
$output->endElement();
}
$output->endElement();
Expand Down
2 changes: 2 additions & 0 deletions api/user.php
Original file line number Diff line number Diff line change
Expand Up @@ -56,6 +56,7 @@
$session = ClientSession::create($username, $password, $save_session === "true");
// Clear previous joined server if any
$session->clearUserJoinedServer();
$session->updateUserGeolocation();
$achievements_string = $session->getAchievements();

$output->startElement('connect');
Expand Down Expand Up @@ -87,6 +88,7 @@
// Clear previous joined server if any
$session->clearUserJoinedServer();
$session->setOnline();
$session->updateUserGeolocation();
User::updateLoginTime($session->getUser()->getId());

$output->startElement('saved-session');
Expand Down
39 changes: 38 additions & 1 deletion include/ClientSession.class.php
Original file line number Diff line number Diff line change
Expand Up @@ -346,10 +346,12 @@ public function getServerConnectionRequests($ip, int $port, int $current_players
// Get all connection requests 45 seconds before
$timeout = time() - 45;
$connection_requests = DBConnection::get()->query(
"SELECT `user_id`, `server_id`, `ip`, `port`, `aes_key`, `aes_iv`, `username`
"SELECT `user_id`, `server_id`, `ip`, `port`, `aes_key`, `aes_iv`, `username`, `country_code`
FROM `{DB_VERSION}_server_conn`
INNER JOIN `{DB_VERSION}_users`
ON `{DB_VERSION}_server_conn`.user_id = `{DB_VERSION}_users`.id
INNER JOIN `{DB_VERSION}_client_sessions`
ON `{DB_VERSION}_server_conn`.user_id = `{DB_VERSION}_client_sessions`.uid
WHERE `server_id` = :server_id AND `connected_since` > :timeout",
DBConnection::FETCH_ALL,
[
Expand Down Expand Up @@ -854,4 +856,39 @@ public function updateServerConfig($ip, int $port, int $new_difficulty, int $new
throw new ServerException(_h("Failed to update server config."));
}
}
/**
* Update the geolocation of user based on his IP, called when the user login.
*/
public function updateUserGeolocation()
{
$user_geolocation = Util::getIPGeolocationFromString(Util::getClientIp());
try
{
DBConnection::get()->query(
"UPDATE `{DB_VERSION}_client_sessions`
SET `latitude` = :latitude, `longitude` = :longitude, `country_code` = :country_code
WHERE `uid`= :uid AND `cid` = :cid",
DBConnection::NOTHING,
[
':latitude' => $user_geolocation[0],
':longitude' => $user_geolocation[1],
':country_code' => $user_geolocation[2],
':uid' => $this->user->getId(),
':cid' => $this->getSessionID()
],
[
':latitude' => DBConnection::PARAM_STR,
':longitude' => DBConnection::PARAM_STR,
':country_code' => DBConnection::PARAM_STR,
':uid' => DBConnection::PARAM_STR,
':cid' => DBConnection::PARAM_STR
]
);
}
catch (DBException $e)
{
throw new ClientSessionException($e->getMessage());
}
}

}
102 changes: 24 additions & 78 deletions include/Server.class.php
Original file line number Diff line number Diff line change
Expand Up @@ -108,6 +108,12 @@ class Server implements IAsXML
*/
private $longitude;

/**
* 2-letter country code of server location
* @var string
*/
private $country_code;

/**
* Current track playing in server
* @var string
Expand Down Expand Up @@ -143,6 +149,7 @@ private function __construct(array $data, array $player_info = [])
$this->game_started = (int)$data["game_started"];
$this->latitude = $data["latitude"];
$this->longitude = $data["longitude"];
$this->country_code = $data["country_code"];
$this->current_track = $data["current_track"];
$this->players_info = $player_info;
}
Expand Down Expand Up @@ -179,82 +186,15 @@ public function getMaxPlayers()
return $this->max_players;
}

/**
* Get the latitude and longitude of an IP
* @return array of latitude and longitude.
* If location does not exist it returns coordinates [0, 0] (null island)
*
* @param int $ip
*/
public static function getIPCoordinates($ip)
{
try
{
$result = DBConnection::get()->query(
"SELECT * FROM `{DB_VERSION}_ipv4_mapping`
WHERE `ip_start` <= :ip AND `ip_end` >= :ip ORDER BY `ip_start` DESC LIMIT 1;",
DBConnection::FETCH_FIRST,
[':ip' => $ip],
[":ip" => DBConnection::PARAM_INT]
);
}
catch (DBException $e)
{
Debug::addException($e);
return [0.0, 0.0];
}

if (!$result)
{
return [0.0, 0.0];
}

return [$result["latitude"], $result["longitude"]];
}

/**
* Get the latitude and longitude of an IP represented as a string
* @return float[] of latitude and longitude in string.
* If location does not exist it returns coordinates [0, 0] (null island)
*
* @param string $ip_string eg: 127.0.0.1
*/
public static function getIPCoordinatesFromString($ip_string)
{
try
{
$result = DBConnection::get()->query(
"SELECT * FROM `{DB_VERSION}_ipv4_mapping`
WHERE `ip_start` <= INET_ATON(:ip) AND `ip_end` >= INET_ATON(:ip)
ORDER BY `ip_start` DESC LIMIT 1;",
DBConnection::FETCH_FIRST,
[':ip' => $ip_string],
[":ip" => DBConnection::PARAM_STR]
);
}
catch (DBException $e)
{
Debug::addException($e);
return [0.0, 0.0];
}

if (!$result)
{
return [0.0, 0.0];
}

return [$result["latitude"], $result["longitude"]];
}

/**
* Get server as xml output
*
* @return string
*/
public function asXML()
{
$client_coordinates = Server::getIPCoordinatesFromString(Util::getClientIp());
return $this->asXMLFromClientLocation($client_coordinates[0], $client_coordinates[1]);
$client_geolocation = Util::getIPGeolocationFromString(Util::getClientIp());
return $this->asXMLFromClientLocation($client_geolocation[0], $client_geolocation[1]);
}

/**
Expand Down Expand Up @@ -283,6 +223,7 @@ public function asXMLFromClientLocation($client_latitude, $client_longitude)
$server_xml->writeAttribute("password", $this->password);
$server_xml->writeAttribute("version", $this->version);
$server_xml->writeAttribute("game_started", $this->game_started);
$server_xml->writeAttribute("country_code", $this->country_code);
$server_xml->writeAttribute("current_track", $this->current_track);
$server_xml->writeAttribute(
"distance",
Expand All @@ -300,6 +241,7 @@ public function asXMLFromClientLocation($client_latitude, $client_longitude)
$server_xml->writeAttribute("username", $player["username"]);
$time_played = (float)(time() - (int)$player["connected_since"]) / 60.0;
$server_xml->writeAttribute("time-played", $time_played);
$server_xml->writeAttribute("country-code", $player["player_country_code"]);
if ($player["rank"] !== null)
{
$server_xml->writeAttribute("rank", $player["rank"]);
Expand Down Expand Up @@ -375,14 +317,14 @@ public static function create(
throw new ServerException(_('Specified server already exists.'));
}

$server_coordinates = Server::getIPCoordinates($ip);
$server_geolocation = Util::getIPGeolocation($ip);
$result = DBConnection::get()->query(
"INSERT INTO `{DB_VERSION}_servers` (host_id, name,
last_poll_time, ip, port, private_port, max_players,
difficulty, game_mode, password, version, latitude, longitude)
difficulty, game_mode, password, version, latitude, longitude, country_code)
VALUES (:host_id, :name, :last_poll_time, :ip, :port,
:private_port, :max_players, :difficulty, :game_mode,
:password, :version, :latitude, :longitude)",
:password, :version, :latitude, :longitude, :country_code)",
DBConnection::ROW_COUNT,
[
':host_id' => $user_id,
Expand All @@ -397,8 +339,9 @@ public static function create(
':game_mode' => $game_mode,
':password' => $password,
':version' => $version,
':latitude' => $server_coordinates[0],
':longitude' => $server_coordinates[1]
':latitude' => $server_geolocation[0],
':longitude' => $server_geolocation[1],
':country_code' => $server_geolocation[2]
],
[
':host_id' => DBConnection::PARAM_INT,
Expand All @@ -413,7 +356,8 @@ public static function create(
':password' => DBConnection::PARAM_INT,
':version' => DBConnection::PARAM_INT,
':latitude' => DBConnection::PARAM_STR,
':longitude' => DBConnection::PARAM_STR
':longitude' => DBConnection::PARAM_STR,
':country_code' => DBConnection::PARAM_STR
]
);
}
Expand Down Expand Up @@ -527,9 +471,11 @@ public static function getAllServerConnectionsWithUsers(): array
`{DB_VERSION}_servers`.max_players, `{DB_VERSION}_servers`.difficulty, `{DB_VERSION}_servers`.game_mode,
`{DB_VERSION}_servers`.current_players, `{DB_VERSION}_servers`.password, `{DB_VERSION}_servers`.version,
`{DB_VERSION}_servers`.game_started, `{DB_VERSION}_servers`.latitude, `{DB_VERSION}_servers`.longitude,
`{DB_VERSION}_servers`.country_code,
`{DB_VERSION}_servers`.current_track, `{DB_VERSION}_server_conn`.user_id, `{DB_VERSION}_server_conn`.connected_since,
`{DB_VERSION}_users`.username, rank, scores, max_scores, num_races_done,
UNIX_TIMESTAMP(`{DB_VERSION}_client_sessions`.`last-online`) AS online_since
UNIX_TIMESTAMP(`{DB_VERSION}_client_sessions`.`last-online`) AS online_since,
`{DB_VERSION}_client_sessions`.country_code AS player_country_code
FROM `{DB_VERSION}_servers`
LEFT JOIN `{DB_VERSION}_server_conn` ON `{DB_VERSION}_servers`.id = `{DB_VERSION}_server_conn`.server_id
LEFT JOIN `{DB_VERSION}_users` ON `{DB_VERSION}_users`.id = `{DB_VERSION}_server_conn`.user_id
Expand Down Expand Up @@ -566,7 +512,7 @@ public static function getAllServerConnectionsWithUsers(): array
public static function getServersAsXML(int $skip_non_polled_clients_seconds = 180): string
{
$servers_with_users = static::getAllServerConnectionsWithUsers();
$client_coordinates = static::getIPCoordinatesFromString(Util::getClientIp());
$client_geolocation = Util::getIPGeolocationFromString(Util::getClientIp());

$servers = [];
$users = [[]];
Expand Down Expand Up @@ -602,7 +548,7 @@ public static function getServersAsXML(int $skip_non_polled_clients_seconds = 18
{
$server = new self($server_result, $users[$user_index]);
$user_index++;
$partial_output->insert($server->asXMLFromClientLocation($client_coordinates[0], $client_coordinates[1]));
$partial_output->insert($server->asXMLFromClientLocation($client_geolocation[0], $client_geolocation[1]));
}
$partial_output->endElement();

Expand Down
68 changes: 68 additions & 0 deletions include/Util.class.php
Original file line number Diff line number Diff line change
Expand Up @@ -821,4 +821,72 @@ public static function getDistance(

return $angle * $earth_radius;
}

/**
* Get the latitude, longitude and 2-letter country code of an IP
* @return array of latitude, longitude and 2-letter country code.
* If location does not exist it returns coordinates [0, 0, ""] (null island)
*
* @param int $ip
*/
public static function getIPGeolocation($ip)
{
try
{
$result = DBConnection::get()->query(
"SELECT * FROM `{DB_VERSION}_ipv4_mapping`
WHERE `ip_start` <= :ip AND `ip_end` >= :ip ORDER BY `ip_start` DESC LIMIT 1;",
DBConnection::FETCH_FIRST,
[':ip' => $ip],
[":ip" => DBConnection::PARAM_INT]
);
}
catch (DBException $e)
{
Debug::addException($e);
return [0.0, 0.0, ""];
}

if (!$result)
{
return [0.0, 0.0, ""];
}

return [$result["latitude"], $result["longitude"], $result["country_code"]];
}

/**
* Get the latitude, longitude and 2-letter country code of an IP represented as a string
* @return array of latitude, longitude and 2-letter country code.
* If location does not exist it returns coordinates [0, 0, ""] (null island)
*
* @param string $ip_string eg: 127.0.0.1
*/
public static function getIPGeolocationFromString($ip_string)
{
try
{
$result = DBConnection::get()->query(
"SELECT * FROM `{DB_VERSION}_ipv4_mapping`
WHERE `ip_start` <= INET_ATON(:ip) AND `ip_end` >= INET_ATON(:ip)
ORDER BY `ip_start` DESC LIMIT 1;",
DBConnection::FETCH_FIRST,
[':ip' => $ip_string],
[":ip" => DBConnection::PARAM_STR]
);
}
catch (DBException $e)
{
Debug::addException($e);
return [0.0, 0.0, ""];
}

if (!$result)
{
return [0.0, 0.0, ""];
}

return [$result["latitude"], $result["longitude"], $result["country_code"]];
}

}
4 changes: 4 additions & 0 deletions install/install.sql
Original file line number Diff line number Diff line change
Expand Up @@ -299,6 +299,9 @@ CREATE TABLE IF NOT EXISTS `v3_client_sessions` (
`is_online` BOOL NOT NULL DEFAULT '1',
`is_save` BOOL NOT NULL DEFAULT '0',
`last-online` TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,
`latitude` FLOAT NOT NULL DEFAULT '0.0',
`longitude` FLOAT NOT NULL DEFAULT '0.0',
`country_code` VARCHAR(2) NOT NULL DEFAULT '',
PRIMARY KEY (`uid`),
UNIQUE KEY `key_session` (`uid`, `cid`)
)
Expand Down Expand Up @@ -327,6 +330,7 @@ CREATE TABLE IF NOT EXISTS `v3_servers` (
`game_started` TINYINT UNSIGNED NOT NULL DEFAULT '0',
`latitude` FLOAT NOT NULL DEFAULT '0.0',
`longitude` FLOAT NOT NULL DEFAULT '0.0',
`country_code` VARCHAR(2) NOT NULL DEFAULT '',
`current_track` VARCHAR(64) NOT NULL DEFAULT '',
PRIMARY KEY (`id`),
KEY `key_hostid` (`host_id`),
Expand Down

0 comments on commit e6e913c

Please sign in to comment.