bans.php

main 139 lines · 3.9 KB Raw
Alex Alex Commit Initial commit 01/10/2026 09:20
1<?php require_once 'engine/init.php';
2theme_open();
3
4/**
5 * Public ban list.
6 *
7 * Two completely different shapes depending on the engine:
8 * TFS 1.x / 1.6 / Canary account_bans + ip_bans + player_namelocks
9 * TFS 0.x / OTHire one `bans` table with a type column
10 *
11 * IP bans are counted but never listed: an address is personal data and
12 * publishing it helps nobody. Names and reasons are what players expect to see.
13 *
14 * Prepared for the view:
15 * $bansSupported false when the engine has no ban tables this page can read
16 * $accountBans [name, reason, banned_at, expires_at, by]
17 * $nameLocks [name, reason, at, by]
18 * $ipBanCount how many address bans are active, as a number only
19 */
20
21$engine = znote_server_adapter()->normalizedEngine();
22$isModern = ($engine === 'TFS_10'); // TFS_16, Canary and BlackTek normalise to this
23
24$bansSupported = true;
25$accountBans = array();
26$nameLocks = array();
27$ipBanCount = 0;
28
29$now = time();
30
31if ($isModern) {
32
33 $isOthire = false;
34 $accNameCol = '`a`.`name`';
35
36 // Characters are shown rather than the account name: that is what other
37 // players recognise, and one account may have several.
38 $rows = db()->fetchAll("
39 SELECT {$accNameCol} AS `account_name`,
40 `b`.`reason`, `b`.`banned_at`, `b`.`expires_at`,
41 (SELECT `name` FROM `players` WHERE `account_id` = `b`.`account_id` ORDER BY `level` DESC LIMIT 1) AS `character_name`
42 FROM `account_bans` `b`
43 INNER JOIN `accounts` `a` ON `a`.`id` = `b`.`account_id`
44 ORDER BY `b`.`banned_at` DESC
45 LIMIT 200;
46 ");
47
48 if (is_array($rows)) {
49 foreach ($rows as $row) {
50 $accountBans[] = array(
51 'name' => (string)($row['character_name'] !== null && $row['character_name'] !== ''
52 ? $row['character_name'] : $row['account_name']),
53 'reason' => (string)$row['reason'],
54 'banned_at' => (int)$row['banned_at'],
55 'expires_at' => (int)$row['expires_at'],
56 );
57 }
58 }
59
60 $locks = db()->fetchAll("
61 SELECT `p`.`name`, `n`.`reason`, `n`.`namelocked_at`
62 FROM `player_namelocks` `n`
63 INNER JOIN `players` `p` ON `p`.`id` = `n`.`player_id`
64 ORDER BY `n`.`namelocked_at` DESC
65 LIMIT 100;
66 ");
67 if (is_array($locks)) {
68 foreach ($locks as $row) {
69 $nameLocks[] = array(
70 'name' => (string)$row['name'],
71 'reason' => (string)$row['reason'],
72 'at' => (int)$row['namelocked_at'],
73 );
74 }
75 }
76
77 $ipBanCount = (int)(db()->fetchOne("
78 SELECT COUNT(*) AS `c` FROM `ip_bans`
79 WHERE `expires_at` <= 0 OR `expires_at` > ?;
80 ", [$now])['c'] ?? 0);
81
82} elseif (in_array($engine, array('TFS_02', 'TFS_03', 'OTHIRE'), true)) {
83
84 // One table, a type column: 1 = IP, 2 = namelock, 3 = account, 5 = deletion.
85 $rows = db()->fetchAll("
86 SELECT `type`, `value`, `param`, `expires`, `added`, `comment`, `reason_id`
87 FROM `bans`
88 WHERE `active` = 1 OR `active` IS NULL
89 ORDER BY `added` DESC
90 LIMIT 200;
91 ");
92
93 if (is_array($rows)) {
94 foreach ($rows as $row) {
95 $type = (int)$row['type'];
96
97 if ($type === 1) {
98 $ipBanCount++;
99 continue;
100 }
101
102 // value is a player id for namelocks, an account id for bans.
103 $name = '';
104 if ($type === 2) {
105 $player = db()->fetchOne("SELECT `name` FROM `players` WHERE `id` = ? LIMIT 1;", [(int)$row['value']]);
106 $name = is_array($player) ? (string)$player['name'] : '';
107 } else {
108 $player = db()->fetchOne("SELECT `name` FROM `players` WHERE `account_id` = ? ORDER BY `level` DESC LIMIT 1;", [(int)$row['value']]);
109 $name = is_array($player) ? (string)$player['name'] : '';
110 }
111
112 if ($name === '') {
113 continue;
114 }
115
116 $entry = array(
117 'name' => $name,
118 'reason' => (string)$row['comment'],
119 'banned_at' => (int)$row['added'],
120 'expires_at' => (int)$row['expires'],
121 'at' => (int)$row['added'],
122 );
123
124 if ($type === 2) {
125 $nameLocks[] = $entry;
126 } else {
127 $accountBans[] = $entry;
128 }
129 }
130 }
131
132} else {
133 $bansSupported = false;
134}
135
136view('bans');
137
138theme_close();
139
Top