aslain.dev
0%
01 Hizmetler 02 Hakkımda 03 Projeler 04 Stack 05 Blog 06 İletişim
← Tüm makaleler Metin2

Build a Metin2 Ranking Site with PHP and MySQL

A Metin2 ranking site is a small but powerful web application that pulls player and guild data from your server's database and shows it to visitors as ordered tables. These pages let players see their own level, their guild's standing and their PvP statistics; they keep the community alive, fuel competition and give your server a professional touch. In this article I walk through, step by step, how to build a secure and performant ranking page from scratch using PHP and MySQL.

Understanding the Metin2 database structure

Metin2 servers split their data across several MySQL databases: player information usually lives in the player database, while accounts live in the account database. For a ranking site, the two tables we care about are:

  • player.player — holds each character's name, level, exp, playtime, job (class) and guild_id.
  • player.guild — stores the guild name (name), its level, exp and guild war results (win, draw, loss).

One important note: a ranking site should never modify the tables the game server writes to. The safest approach is to create a read-only MySQL user that can only run SELECT queries.

CREATE USER 'ranking'@'localhost' IDENTIFIED BY 'a_strong_password';
GRANT SELECT ON player.player TO 'ranking'@'localhost';
GRANT SELECT ON player.guild TO 'ranking'@'localhost';
FLUSH PRIVILEGES;

A secure database connection (PDO)

The modern, safe way to connect to MySQL in PHP is PDO. Thanks to prepared statements, we are protected against SQL injection. Keeping the connection in a single file makes it easier to manage later:

<?php
// db.php
$host = '127.0.0.1';
$db   = 'player';
$user = 'ranking';
$pass = 'a_strong_password';

$dsn = "mysql:host=$host;dbname=$db;charset=utf8mb4";
$options = [
    PDO::ATTR_ERRMODE            => PDO::ERRMODE_EXCEPTION,
    PDO::ATTR_DEFAULT_FETCH_MODE => PDO::FETCH_ASSOC,
    PDO::ATTR_EMULATE_PREPARES   => false,
];

try {
    $pdo = new PDO($dsn, $user, $pass, $options);
} catch (PDOException $e) {
    http_response_code(500);
    exit('Could not connect to the database.');
}

Instead of hard-coding the database password in your source, it is safer to read it from a .env file or a server environment variable in production.

Fetching the player ranking

The most classic Metin2 ranking site table orders players by level and experience. To rank a character's true progress, we sort by level first and fall back to exp on a tie:

<?php
$stmt = $pdo->prepare(
    "SELECT name, level, exp, playtime
     FROM player
     ORDER BY level DESC, exp DESC
     LIMIT :limit"
);
$stmt->bindValue(':limit', 50, PDO::PARAM_INT);
$stmt->execute();
$players = $stmt->fetchAll();

Showing 50 rows per page is both readable and performant. If you want to show more, consider adding pagination.

Displaying the data in an HTML table

Every piece of text coming from the database must be escaped with htmlspecialchars() before it reaches the page; otherwise player names with special characters could open an XSS hole. The playtime column is stored in minutes, so we convert it to hours for readability:

<table>
  <thead>
    <tr><th>#</th><th>Name</th><th>Level</th><th>Time</th></tr>
  </thead>
  <tbody>
  <?php foreach ($players as $i => $p): ?>
    <tr>
      <td><?= $i + 1 ?></td>
      <td><?= htmlspecialchars($p['name']) ?></td>
      <td><?= (int) $p['level'] ?></td>
      <td><?= round($p['playtime'] / 60) ?> h</td>
    </tr>
  <?php endforeach; ?>
  </tbody>
</table>

Adding a guild ranking

Placing a guild ranking next to the player table strengthens community competition. We can order guilds by level and the number of guild wars won:

<?php
$stmt = $pdo->query(
    "SELECT name, level, win, draw, loss
     FROM guild
     ORDER BY level DESC, win DESC
     LIMIT 20"
);
$guilds = $stmt->fetchAll();

You can also join the player and guild tables on guild_id to produce extra tables such as "most populated guilds".

Adding pagination and search

As your server grows, you don't want players to settle for the top 50 only; everyone is curious about their own rank. That is why pagination and a simple name search make the ranking site far more useful. We read the page number from the URL while still casting it to keep things safe:

<?php
$page    = max(1, (int) ($_GET['page'] ?? 1));
$perPage = 50;
$offset  = ($page - 1) * $perPage;

$stmt = $pdo->prepare(
    "SELECT name, level, exp
     FROM player
     ORDER BY level DESC, exp DESC
     LIMIT :limit OFFSET :offset"
);
$stmt->bindValue(':limit', $perPage, PDO::PARAM_INT);
$stmt->bindValue(':offset', $offset, PDO::PARAM_INT);
$stmt->execute();

For name search, never drop user input straight into the query; use LIKE in a prepared statement and bind it as a parameter. This is both safe and lets players quickly find their own characters.

Matching the page to your server's theme

Presentation matters as much as the technical foundation. A ranking page looks professional when it uses the same color palette, font and logo language as the server's main site. Highlighting the top three with gold/silver/bronze colors, showing guild emblems and designing a mobile-friendly table noticeably improves the visitor experience. Wrapping the table in a horizontally scrollable container on small screens prevents overflow.

Performance and caching

Running a live query for every visitor strains the database as your player count grows. Since rankings do not change every second, caching the results for a short time makes a lot of sense:

  • Store the query result in a file or Redis cache for a few minutes and refresh it when it expires.
  • Make sure the columns you sort by (level, exp) have a MySQL index.
  • Host the ranking site separately from the game server machine and connect only with the read-only user.

Frequently Asked Questions

Will the ranking site slow down the game server?

Not when set up correctly. If you use a read-only user, indexed queries and a few minutes of caching, the database load stays negligible.

Why do special characters in player names look broken?

It is usually a character set mismatch. Make sure you use charset=utf8mb4 in the PDO connection and that your HTML page includes <meta charset="utf-8">.

Which fields should I rank by?

The most common choices are level + experience, guild level and PvP/kill statistics. Depending on your server type, fields like playtime or gold can also be interesting.

Want a professional ranking site for your server? Using PHP and MySQL I can design and build secure, fast ranking pages that match your server's theme. Get in touch to talk about your project.

Bu kategorideki tüm yazılar →

Devamı için