diff --git a/database/2023_08_23_233728_remove_whitelist_tables.php b/database/2023_08_23_233728_remove_whitelist_tables.php new file mode 100644 index 0000000..5655620 --- /dev/null +++ b/database/2023_08_23_233728_remove_whitelist_tables.php @@ -0,0 +1,10 @@ +execute('DROP TABLE whitelist'); + $conn->execute('DROP TABLE whitelist_2022'); + } +} diff --git a/mince.php b/mince.php index 72f7233..cfd2cfa 100644 --- a/mince.php +++ b/mince.php @@ -31,5 +31,3 @@ try { } $db->execute('SET SESSION time_zone = "+00:00", sql_mode = "STRICT_TRANS_TABLES,NO_ZERO_IN_DATE,NO_ZERO_DATE,ERROR_FOR_DIVISION_BY_ZERO,NO_ENGINE_SUBSTITUTION"'); - -$remote = new RemoteV2($config['remotev2_url'], $config['remotev2_secret']); diff --git a/private/remote.php b/private/remote.php deleted file mode 100644 index 2fe1e19..0000000 --- a/private/remote.php +++ /dev/null @@ -1,294 +0,0 @@ - ':rcon:conn']; - - extract(unpack('Vlength', fread($conn, 4))); - if($length < 10) - return ['error' => ':rcon:length']; - - extract(unpack('VreqId/Vopcode', fread($conn, 8))); - $body = substr(fread($conn, $length - 8), 0, -2); - - return compact('reqId', 'opcode', 'body'); -} - -function rcon_send($conn, int $opcode, string $text): int { - if(!is_resource($conn)) - return -1; - $length = 10 + strlen($text); - $reqId = random_int(1, 0x7FFFFFFF); - fwrite($conn, pack('VVV', $length, $reqId, $opcode) . $text . "\0\0"); - return $reqId; -} - -function rcon_send_large($conn, int $opcode, string $text): array { - if(!is_resource($conn)) - return ['error' => ':rcon:conn']; - - $reqId = rcon_send($conn, $opcode, $text); - if($reqId < 1) return ['error' => ':rcon:request']; - - $trailer = rcon_send($conn, 2, 'time query gametime'); - if($trailer < 1) return ['error' => ':rcon:trailer']; - - $opcode = 0; - $body = ''; - - for(;;) { - $resp = rcon_recv($conn); - if(!empty($resp['error'])) - return $resp; - - if($resp['reqId'] === $trailer) break; - if($resp['reqId'] !== $reqId) continue; - - if($resp['opcode'] !== 0) - return ['error' => ':rcon:opcode']; - - $body .= $resp['body']; - } - - return compact('reqId', 'trailer', 'opcode', 'body'); -} - -function rcon_open_props(array $props) { - if(empty($props['rcon.port'])) - die('{"error":":conf:rcon-port"}'); - if(empty($props['rcon.password'])) - die('{"error":":conf:rcon-passwd"}'); - - return rcon_open($props['rcon.port']); -} - -function rcon_auth_props($conn, array $props): void { - if(empty($props['rcon.password'])) - die('{"error":":conf:rcon-passwd"}'); - - rcon_send($conn, 3, $props['rcon.password']); - $resp = rcon_recv($conn); - if(!empty($resp['error'])) - die(json_encode(['error' => $resp['error']])); -} - -function rcon_get_whitelist($conn): array { - $resp = rcon_send_large($conn, 2, 'whitelist list'); - if(!empty($resp['error'])) return $resp; - - $halfs = explode(':', $resp['body'], 2); - if(empty($halfs[1])) return []; - - $names = explode(',', $halfs[1]); - foreach($names as &$name) - $name = trim($name); - - return array_values(array_filter($names)); -} - -function rcon_add_whitelist($conn, array $userNames): array { - $results = []; - - foreach($userNames as $name) { - rcon_send($conn, 2, 'whitelist add ' . $name); // todo: sanitise username - $resp = rcon_recv($conn); - - $results[$name] = [ - 'success' => str_starts_with($resp['body'], 'Added '), - 'message' => $resp['body'], - ]; - } - - rcon_send($conn, 2, 'whitelist reload'); - rcon_recv($conn); // discard reload message - - return compact('results'); -} - -function rcon_remove_whitelist($conn, array $userNames): array { - $results = []; - - foreach($userNames as $name) { - rcon_send($conn, 2, 'whitelist remove ' . $name); // todo: sanitise username - $resp = rcon_recv($conn); - - $results[$name] = [ - 'success' => str_starts_with($resp['body'], 'Removed '), - 'message' => $resp['body'], - ]; - } - - rcon_send($conn, 2, 'whitelist reload'); - rcon_recv($conn); // discard reload message - - return compact('results'); -} - -function read_server_props(string $serverPath, string $fileName = 'server.properties'): array { - $props = []; - - $path = realpath($serverPath . '/' . $fileName); - if(is_file($path)) { - $lines = file($path, FILE_IGNORE_NEW_LINES | FILE_SKIP_EMPTY_LINES); - foreach($lines as $line) { - if(empty($line) || str_starts_with($line, '#')) - continue; - - $parts = explode('=', $line, 2); - if(count($parts) == 2) { - $value = $parts[1]; - if($value === 'false' || $value === 'true') - $value = $value === 'true'; - elseif(ctype_digit($value)) - $value = (int)$value; - elseif(is_numeric($value)) - $value = (float)$value; - - $props[$parts[0]] = $value; - } - } - } - - return $props; -} - -function create_request_hash(int $time = -1, ?array $params = null, ?string $method = null, ?string $path = null): string { - if($time < 0) - $time = time(); - if($params === null) - $params = $_REQUEST ?? []; - if($method === null) - $method = $GLOBALS['reqMethod'] ?? ''; - if($path === null) - $path = $GLOBALS['reqPath'] ?? ''; - - ksort($params); - $compare = []; - - $stringify = null; // use() gets sad if it's not defined yet - $stringify = function(array $arr, string $prefix = '') use(&$compare, &$stringify) { - foreach($arr as $name => $value) { - if(is_array($value)) - $stringify($value, $name . ';'); - else - $compare[] = "{$name}:{$value}"; - } - }; - - $stringify($params); - $input = "{$time}%{$method} {$path}%" . implode('#', $compare); - - return hash_hmac('sha256', $input, SRV_REQ_SEC, true); -} - -function verify_request_hash(): bool { - $realTime = time(); - $lifeTimeHalf = (int)ceil(SRV_REQ_LIFE / 2); - - $userHash = base64url_decode((string)filter_input(INPUT_SERVER, 'HTTP_X_MINCE_SIGNATURE')); - $userTime = (int)filter_input(INPUT_SERVER, 'HTTP_X_MINCE_TIMESTAMP', FILTER_SANITIZE_NUMBER_INT); - if(strlen($userHash) !== 32 || $userTime < ($realTime - $lifeTimeHalf) || $userTime > ($realTime + $lifeTimeHalf)) - return false; - - return hash_equals(create_request_hash($userTime), $userHash); -} - -function get_server_path(string $serverId): string { - if(empty($serverId) || !ctype_alnum($serverId)) - die('{"error":":req:id"}'); - - $path = sprintf(SRV_DIR_FMT, $serverId); - if(!is_dir($path)) - die('{"error":":req:server"}'); - - return $path; -} - -$reqMethod = (string)filter_input(INPUT_SERVER, 'REQUEST_METHOD'); -$reqPath = '/' . trim(parse_url((string)filter_input(INPUT_SERVER, 'REQUEST_URI'), PHP_URL_PATH), '/'); - -header('Content-Type: application/json; charset=utf-8'); - -if(!verify_request_hash()) - die('{"error":":request:verification"}'); - -if($reqMethod === 'GET' && $reqPath === '/') { - $dirs = glob(__DIR__ . '/../*'); - $servers = []; - - foreach($dirs as $dir) - if(is_file($dir . '/server.properties') && !is_file($dir . '/.dead')) - $servers[] = basename($dir); - - echo json_encode(compact('servers')); - return; -} - -if($reqMethod === 'GET' && $reqPath === '/whitelist') { - $sPath = get_server_path((string)filter_input(INPUT_GET, 'server')); - $sProps = read_server_props($sPath); - $rcon = rcon_open_props($sProps); - - try { - rcon_auth_props($rcon, $sProps); - echo json_encode(['list' => rcon_get_whitelist($rcon)]); - } finally { - rcon_close($rcon); - } - return; -} - -if($reqMethod === 'POST' && $reqPath === '/whitelist') { - $wNames = json_decode((string)filter_input(INPUT_POST, 'names')); - $sPath = get_server_path((string)filter_input(INPUT_POST, 'server')); - $sProps = read_server_props($sPath); - $rcon = rcon_open_props($sProps); - - try { - rcon_auth_props($rcon, $sProps); - echo json_encode(rcon_add_whitelist($rcon, $wNames)); - } finally { - rcon_close($rcon); - } - return; -} - -if($reqMethod === 'DELETE' && $reqPath === '/whitelist') { - $wNames = json_decode((string)filter_input(INPUT_GET, 'names')); - $sPath = get_server_path((string)filter_input(INPUT_GET, 'server')); - $sProps = read_server_props($sPath); - $rcon = rcon_open_props($sProps); - - try { - rcon_auth_props($rcon, $sProps); - echo json_encode(rcon_remove_whitelist($rcon, $wNames)); - } finally { - rcon_close($rcon); - } - return; -} - -echo '{"error":":request:notfound"}'; diff --git a/public/index.php b/public/index.php index 83678e5..4a25097 100644 --- a/public/index.php +++ b/public/index.php @@ -8,7 +8,7 @@ require_once __DIR__ . '/../mince.php'; // replace this with id.flashii.net shit $authToken = (string)filter_input(INPUT_COOKIE, 'msz_auth'); -$authInfo = ChatAuth::attempt($db, $config['chat_endpoint'], $config['chat_secret'], $authToken); +$authInfo = ChatAuth::attempt($config['chat_endpoint'], $config['chat_secret'], $authToken); $users = new Users($db); if($authInfo->success) { @@ -29,7 +29,6 @@ $templating->addVars([ 'title' => 'Flashii Minecraft Servers', ], 'is_authed' => $userInfo !== null, - 'auth' => $authInfo, 'user' => $userInfo, 'csrfp' => $csrfp->createToken(), ]); @@ -58,7 +57,6 @@ $router->setDefaultErrorHandler(function($response, $request, $code, $text) use (new HomeRoutes(new Servers($db), $templating, $authInfo, $config['login_url']))->register($router); (new ClientsRoutes($templating, $accountLinks, $authorisations, $verifications, $csrfp, $authInfo))->register($router); (new SkinsRoutes($templating, $accountLinks, new Skins($db), new Capes($db), $csrfp, $authInfo, $config['skins_base_url']))->register($router); -(new WhitelistRoutes(new Whitelist($db), $csrfp, $authInfo))->register($router); MojangInterop::registerRoutes($router); diff --git a/public/mince.css b/public/mince.css index 7bf4ee9..2a19293 100644 --- a/public/mince.css +++ b/public/mince.css @@ -103,8 +103,7 @@ h1, h2 { } .form-btn-green, -.acclink input[type="submit"], -.whitelist input[type="submit"] { +.acclink input[type="submit"] { font-size: 1.5em; margin: 5px; border-radius: 4px; @@ -117,20 +116,16 @@ h1, h2 { .form-btn-green:hover, .form-btn-green:focus, .acclink input[type="submit"]:hover, -.acclink input[type="submit"]:focus, -.whitelist input[type="submit"]:hover, -.whitelist input[type="submit"]:focus { +.acclink input[type="submit"]:focus { background-color: #272; } .form-btn-green:active, -.acclink input[type="submit"]:active, -.whitelist input[type="submit"]:active { +.acclink input[type="submit"]:active { background-color: #232; } .form-btn-red, -.accunlink input[type="submit"], -.unwhitelist input[type="submit"] { +.accunlink input[type="submit"] { font-size: 1.5em; margin: 5px; border-radius: 4px; @@ -143,14 +138,11 @@ h1, h2 { .form-btn-red:hover, .form-btn-red:focus, .accunlink input[type="submit"]:hover, -.accunlink input[type="submit"]:focus, -.unwhitelist input[type="submit"]:hover, -.unwhitelist input[type="submit"]:focus { +.accunlink input[type="submit"]:focus { background-color: #722; } .form-btn-red:active, -.accunlink input[type="submit"]:active, -.unwhitelist input[type="submit"]:active { +.accunlink input[type="submit"]:active { background-color: #322; } diff --git a/src/ChatAuth.php b/src/ChatAuth.php index e024b65..9871cd0 100644 --- a/src/ChatAuth.php +++ b/src/ChatAuth.php @@ -2,10 +2,9 @@ namespace Mince; use stdClass; -use Index\Data\IDbConnection; final class ChatAuth { - public static function attempt(IDbConnection $db, string $endPoint, string $secret, string $cookie): object { + public static function attempt(string $endPoint, string $secret, string $cookie): object { if(!empty($cookie)) { $method = 'Misuzu'; $signature = sprintf('verify#%s#%s#%s', $method, $cookie, $_SERVER['REMOTE_ADDR']); @@ -48,21 +47,6 @@ final class ChatAuth { $userInfo->rank = 0; $userInfo->hierarchy = 0; $userInfo->perms = 0; - $userInfo->mc_username = null; - $userInfo->mc_whitelisted = 0; - } else { - $getWhitelist = $db->prepare('SELECT `minecraft_username`, UNIX_TIMESTAMP(`whitelist_added`) AS `whitelist_added` FROM `whitelist_2022` WHERE `flashii_id` = ?'); - $getWhitelist->addParameter(1, $userInfo->user_id); - $getWhitelist->execute(); - $whitelist = $getWhitelist->getResult(); - - if($whitelist->next()) { - $userInfo->mc_username = $whitelist->getString(0); - $userInfo->mc_whitelisted = $whitelist->getInteger(1); - } else { - $userInfo->mc_username = null; - $userInfo->mc_whitelisted = 0; - } } return $userInfo; diff --git a/src/HomeRoutes.php b/src/HomeRoutes.php index 045604a..3933e9e 100644 --- a/src/HomeRoutes.php +++ b/src/HomeRoutes.php @@ -22,44 +22,8 @@ class HomeRoutes { } public function getIndex($response, $request) { - $name = (string)$request->getParam('name'); - $error = (string)$request->getParam('error'); - - if(!empty($error) && ctype_lower($error)) { - $errors = [ - 'request' => ['Invalid request type.', 'Try to reload the page and try again.'], - 'verify' => ['Request verification failed.', 'Try to reload the page and try again.'], - 'itainthappenin' => ['Haha', 'No'], - 'short' => ['Invalid username', 'The provided name is too short.'], - 'long' => ['Invalid username', 'The provided name is too long.'], - 'invalid' => ['Invalid username', 'The provided name contains invalid characters.'], - 'conflict' => ['Username conflict', 'This username is already whitelisted with someone, contact flashwave if this is unexpected.'], - 'connect' => ['Failed to connect to the server', 'The server is probably offline, pope flashwave if this is not expected.'], - 'not-listed' => ['You have not been whitelisted yet', 'Add yourself to the whitelist before trying to remove yourself from it.'], - ]; - - if(array_key_exists($error, $errors)) { - $errTitle = $errors[$error][0]; - $errBody = $errors[$error][1]; - } else { - $errTitle = 'Unexpected response from server'; - $errBody = $error; - } - - $this->templating->addVars([ - 'error' => [ - 'title' => $errTitle, - 'body' => $errBody, - ], - ]); - } - - if($this->userInfo->mc_whitelisted > 0) - $this->templating->setVar('whitelist_pending', floor($this->userInfo->mc_whitelisted / 300) === floor(time() / 300)); - return $this->templating->render('index', [ 'servers' => $this->servers->getServers(deleted: false), - 'wladdform_username' => $name, ]); } diff --git a/src/RemoteV2.php b/src/RemoteV2.php deleted file mode 100644 index 8840bff..0000000 --- a/src/RemoteV2.php +++ /dev/null @@ -1,131 +0,0 @@ -getRequest('GET', '/'); - } - - public function getWhitelist(string $serverId): object { - return $this->getRequest('GET', '/whitelist', ['server' => $serverId]); - } - - public function addToWhitelist(string $serverId, array $userNames): object { - return $this->postRequest('POST', '/whitelist', [ - 'server' => $serverId, - 'names' => json_encode($userNames), - ]); - } - - public function removeFromWhitelist(string $serverId, array $userNames): object { - return $this->getRequest('DELETE', '/whitelist', [ - 'server' => $serverId, - 'names' => json_encode($userNames), - ]); - } - - public function createSignature(string $method, string $path, array $params, int $time = -1): string { - if($time < 0) - $time = time(); - - ksort($params); - $compare = []; - - // other sides supports arrays, not gonna bother here - foreach($params as $name => $value) - $compare[] = "{$name}:{$value}"; - - $input = "{$time}%{$method} {$path}%" . implode('#', $compare); - - return UriBase64::encode( - hash_hmac('sha256', $input, $this->secretKey, true) - ); - } - - public function getRequest(string $method, string $path, array $params = []): mixed { - $time = time(); - $sign = $this->createSignature($method, $path, $params, $time); - - $url = $this->endPoint . $path; - if(!empty($params)) - $url .= '?' . http_build_query($params); - - $request = curl_init($url); - curl_setopt_array($request, [ - CURLOPT_AUTOREFERER => false, - CURLOPT_FAILONERROR => false, - CURLOPT_FOLLOWLOCATION => true, - CURLOPT_HEADER => false, - CURLOPT_RETURNTRANSFER => true, - CURLOPT_TCP_FASTOPEN => true, - CURLOPT_CONNECTTIMEOUT => 2, - CURLOPT_MAXREDIRS => 2, - CURLOPT_PROTOCOLS => CURLPROTO_HTTPS, - CURLOPT_TIMEOUT => 5, - CURLOPT_CUSTOMREQUEST => $method, - CURLOPT_USERAGENT => 'mc.flashii.net', - CURLOPT_HTTPHEADER => [ - "X-Mince-Signature: {$sign}", - "X-Mince-Timestamp: {$time}", - ], - ]); - $response = curl_exec($request); - curl_close($request); - - if(empty($response)) - throw new RuntimeException('Empty response.'); - - $response = json_decode($response); - if(!empty($response->error)) - throw new RuntimeException($response->error); - - return $response; - } - - public function postRequest(string $method, string $path, array $params): mixed { - $time = time(); - $sign = $this->createSignature($method, $path, $params, $time); - $url = $this->endPoint . $path; - - $request = curl_init($url); - curl_setopt_array($request, [ - CURLOPT_AUTOREFERER => false, - CURLOPT_FAILONERROR => false, - CURLOPT_FOLLOWLOCATION => true, - CURLOPT_HEADER => false, - CURLOPT_POST => true, - CURLOPT_POSTFIELDS => $params, - CURLOPT_RETURNTRANSFER => true, - CURLOPT_TCP_FASTOPEN => true, - CURLOPT_CONNECTTIMEOUT => 2, - CURLOPT_MAXREDIRS => 2, - CURLOPT_PROTOCOLS => CURLPROTO_HTTPS, - CURLOPT_TIMEOUT => 5, - CURLOPT_CUSTOMREQUEST => $method, - CURLOPT_USERAGENT => 'mc.flashii.net', - CURLOPT_HTTPHEADER => [ - "X-Mince-Signature: {$sign}", - "X-Mince-Timestamp: {$time}", - ], - ]); - $response = curl_exec($request); - curl_close($request); - - if(empty($response)) - throw new RuntimeException('Empty response.'); - - $response = json_decode($response); - if(!empty($response->error)) - throw new RuntimeException($response->error); - - return $response; - } -} diff --git a/src/Whitelist.php b/src/Whitelist.php deleted file mode 100644 index 277e783..0000000 --- a/src/Whitelist.php +++ /dev/null @@ -1,71 +0,0 @@ -dbConn->query('SELECT minecraft_username FROM whitelist_2022'); - while($getNames->next()) - $names[] = $getNames->getString(0); - - return $names; - } - - public function add(object $userInfo, string $userName): string { - $length = strlen($userName); - if($length < 3) - return 'short'; - if($length > 16) - return 'long'; - if(!preg_match('#^([a-zA-Z0-9_]{3,16})$#', $userName)) - return 'invalid'; - - $dupeCheck = $this->dbConn->prepare('SELECT COUNT(`flashii_id`) > 0 FROM `whitelist_2022` WHERE `minecraft_username` = ?'); - $dupeCheck->addParameter(1, $userName, DbType::STRING); - $dupeCheck->execute(); - $dupeResult = $dupeCheck->getResult(); - - if(!$dupeResult->next()) - return 'dupefail'; - if($dupeResult->getInteger(0)) - return 'conflict'; - - if(!empty($userInfo->mc_whitelisted) || !empty($userInfo->mc_username)) { - $resp = $this->remove($userInfo); - if($resp !== '') - return $resp; - } - - $insert = $this->dbConn->prepare('INSERT INTO `whitelist_2022` (`flashii_id`, `minecraft_username`) VALUES (?, ?)'); - $insert->addParameter(1, $userInfo->user_id); - $insert->addParameter(2, $userName, DbType::STRING); - $insert->execute(); - - $userInfo->mc_username = $userName; - $userInfo->mc_whitelisted = time(); - - return ''; - } - - public function remove(object $userInfo): string { - if(empty($userInfo->mc_whitelisted) || empty($userInfo->mc_username)) - return 'not-listed'; - - $delete = $this->dbConn->prepare('DELETE FROM `whitelist_2022` WHERE `flashii_id` = ?'); - $delete->addParameter(1, $userInfo->user_id); - $delete->execute(); - - $userInfo->mc_username = null; - $userInfo->mc_whitelisted = 0; - - return ''; - } -} diff --git a/src/WhitelistRoutes.php b/src/WhitelistRoutes.php deleted file mode 100644 index 5e6c6be..0000000 --- a/src/WhitelistRoutes.php +++ /dev/null @@ -1,61 +0,0 @@ -use('/whitelist', [$this, 'verifyRequest']); - $router->post('/whitelist/add', [$this, 'postAdd']); - $router->post('/whitelist/remove', [$this, 'postRemove']); - } - - public function verifyRequest($response, $request) { - if(!$request->isFormContent()) { - $response->redirect('/?error=request'); - return true; - } - - $body = $request->getContent(); - - if(!$body->hasParam('csrfp') || !$this->csrfp->verifyToken((string)$body->getParam('csrfp'))) { - $response->redirect('/?error=verify'); - return true; - } - } - - public function postAdd($response, $request) { - if($this->userInfo->user_id == 45) { - $response->redirect('/?error=itainthappenin'); - return true; - } - - $body = $request->getContent(); - $name = (string)$body->getParam('name'); - $resp = $this->whitelist->add($this->userInfo, $name); - - if($resp === '') - $response->redirect('/'); - else { - if($resp === 'invalid') - $name = ''; - $response->redirect("/?error={$resp}&name={$name}"); - } - } - - public function postRemove($response) { - $resp = $this->whitelist->remove($this->userInfo); - - if($resp === '') - $response->redirect('/'); - else - $response->redirect("/?error={$resp}"); - } -} diff --git a/templates/index.twig b/templates/index.twig index 1840d42..4d452b3 100644 --- a/templates/index.twig +++ b/templates/index.twig @@ -1,96 +1,50 @@ {% extends 'master.twig' %} {% block content %} - {% if error is defined %} -
-

{{ error.title }}

-

{{ error.body|default('No further details provided.') }}

-
- {% endif %} - - {% if not is_authed %} -
-

You must be logged in to use this website!

-

This website allows you to whitelist yourself on our Minecraft servers, for which you need to be logged in.

-

So it doesn't make sense to display the details either.

-
- {% else %} - {% if auth.mc_whitelisted < 1 %} -
-

Add to Whitelist

-

This will give you access to the server. The whitelist is being removed in favour of an authentication plugin, go to the clients page for more info.

-
- - - -
-
- {% endif %} - -
-

Servers

- - - - - - - {# #} - {# #} - - - - - {% if servers is empty %} - - {% else %} - {% for server in servers %} - - - - - {# #} - {# #} - - - {% endfor %} - {% endif %} - -
NameAddressVersionBedrock addressBedrock versionDetails
There are currently no active servers, check back later!
{{ server.name }}{% if server.hasJavaAddress %}{{ server.javaAddress }}{% else %}N/A{% endif %}{% if server.hasJavaVersion %}{{ server.javaVersion }}{% else %}N/A{% endif %}{% if server.hasBedrockAddress %}{{ server.bedrockAddress }}{% else %}N/A{% endif %}{% if server.hasBedrockVersion %}{{ server.bedrockVersion }}{% else %}N/A{% endif %}{{ server.details|raw }}
-
- - {% if auth.mc_whitelisted > 0 %} -
-

Remove from Whitelist

-

This will revoke your access to the server.

-

You are currently whitelisted as {{ auth.mc_username }} on {{ auth.mc_whitelisted|date('Y-m-d H:i:s T') }}.

- {% if whitelist_pending %} -

The whitelists are synchronised once every 5 minutes, you'll be able to play soon!

-

If you're playing a modpack, take that time to start the game up; it'll take a while.

+
+

Servers

+ + + + + + + {# #} + {# #} + + + + + {% if servers is empty %} + + {% else %} + {% for server in servers %} + + + + + {# #} + {# #} + + + {% endfor %} {% endif %} - - - - - - {% endif %} + +
NameAddressVersionBedrock addressBedrock versionDetails
There are currently no active servers, check back later!
{{ server.name }}{% if server.hasJavaAddress %}{{ server.javaAddress }}{% else %}N/A{% endif %}{% if server.hasJavaVersion %}{{ server.javaVersion }}{% else %}N/A{% endif %}{% if server.hasBedrockAddress %}{{ server.bedrockAddress }}{% else %}N/A{% endif %}{% if server.hasBedrockVersion %}{{ server.bedrockVersion }}{% else %}N/A{% endif %}{{ server.details|raw }}
+
-
-

Bedrock versions

-

Through the black magic bestowed upon us by GeyserMC it's possible to play on the server through any of the updated Bedrock versions of Minecraft.

-

This should allow you to play on the server from a phone, a tablet or a console, provided you also have an account for the original version of the game.

-

You will need to link your Minecraft and Bedrock accounts, you can do this by connecting to link.geysermc.org in both versions of the game and following the on-screen instructions.

-

Do note that this only works for servers where both a Java and Bedrock version number is listed!

-
+
+

Bedrock versions

+

Through the black magic bestowed upon us by GeyserMC it's possible to play on the server through any of the updated Bedrock versions of Minecraft.

+

This should allow you to play on the server from a phone, a tablet or a console, provided you also have an account for the original version of the game.

+

You will need to link your Minecraft and Bedrock accounts, you can do this by connecting to link.geysermc.org in both versions of the game and following the on-screen instructions.

+

Do note that this only works for servers where both a Java and Bedrock version number is listed!

+
-
-

Rules

-

1. Observe Global Rules.

-

2. Don't be an asshole.

-

3. Don't flood.

-
- {% endif %} +
+

Rules

+

1. Observe Global Rules.

+

2. Don't be an asshole.

+

3. Don't flood.

+
{% endblock %} diff --git a/tools/sync b/tools/sync deleted file mode 100755 index 4a4fd95..0000000 --- a/tools/sync +++ /dev/null @@ -1,45 +0,0 @@ -#!/usr/bin/env php -getInfo(); -if(empty($rInfo->servers)) { - echo 'There are no active servers.' . PHP_EOL; - return; -} - -echo 'Fetching master list from database...' . PHP_EOL; -$myNames = (new Whitelist($db))->getNames(); - -foreach($rInfo->servers as $serverId) { - try { - echo "[{$serverId}] Fetching list on server..." . PHP_EOL; - $rWhitelist = $remote->getWhitelist($serverId); - $rNames = $rWhitelist->list ?? []; - - echo "[{$serverId}] Filtering..." . PHP_EOL; - $addNames = array_values(array_udiff($myNames, $rNames, 'strcasecmp')); - $removeNames = array_values(array_udiff($rNames, $myNames, $addNames, 'strcasecmp')); - - if(!empty($addNames)) { - echo "[{$serverId}] Adding names..." . PHP_EOL; - $addResult = $remote->addToWhitelist($serverId, $addNames); - foreach($addResult->results as $name => $result) - echo "[{$serverId}] [{$name}] {$result->message}" . PHP_EOL; - } - - if(!empty($removeNames)) { - echo "[{$serverId}] Removing names..." . PHP_EOL; - $removeResult = $remote->removeFromWhitelist($serverId, $removeNames); - foreach($removeResult->results as $name => $result) - echo "[{$serverId}] [{$name}] {$result->message}" . PHP_EOL; - } - } catch(RuntimeException $ex) { - var_dump((string)$ex); - echo PHP_EOL; - } -}