Updated to use URL registry.

This commit is contained in:
flash 2024-10-05 15:25:27 +00:00
parent f0ddb6727e
commit da130e5855
10 changed files with 89 additions and 51 deletions

View file

@ -4,6 +4,7 @@ namespace Mince;
use Index\CsrfToken; use Index\CsrfToken;
use Index\Http\Routing\HttpRouter; use Index\Http\Routing\HttpRouter;
use Index\Templating\TplEnvironment; use Index\Templating\TplEnvironment;
use Index\Urls\ArrayUrlRegistry;
require_once __DIR__ . '/../mince.php'; require_once __DIR__ . '/../mince.php';
@ -36,13 +37,25 @@ $authorisations->prune();
$verifications = new Verifications($db); $verifications = new Verifications($db);
$verifications->prune(); $verifications->prune();
$urls = new ArrayUrlRegistry;
$templating->addFunction('url', $urls->format(...));
$router = new HttpRouter(errorHandler: new RouterErrorHandler($templating)); $router = new HttpRouter(errorHandler: new RouterErrorHandler($templating));
$router->use('/', function($response, $request) { $response->setPoweredBy('Mince'); }); $router->use('/', function($response, $request) { $response->setPoweredBy('Mince'); });
$router->register(new RpcRoutes($users, $accountLinks, $authorisations, $verifications, $cfg->getString('rpc:secret'), $cfg->getString('urls:clients'))); $router->register(new RpcRoutes($users, $accountLinks, $authorisations, $verifications, $cfg->getString('rpc:secret'), $cfg->getString('urls:clients')));
$router->register(new HomeRoutes($templating, new Servers($db), $authInfo, $cfg->getString('site:login')));
$router->register(new ClientsRoutes($templating, $accountLinks, $authorisations, $verifications, $csrfp, $authInfo)); $homeRoutes = new HomeRoutes($templating, $urls, new Servers($db), $authInfo, $cfg->getString('site:login'));
$router->register(new SkinsRoutes($templating, $accountLinks, new Skins($db), new Capes($db), $csrfp, $authInfo, $cfg->getString('urls:skins_base'))); $router->register($homeRoutes);
$urls->register($homeRoutes);
$clientRoutes = new ClientsRoutes($templating, $urls, $accountLinks, $authorisations, $verifications, $csrfp, $authInfo);
$router->register($clientRoutes);
$urls->register($clientRoutes);
$skinsRoutes = new SkinsRoutes($templating, $urls, $accountLinks, new Skins($db), new Capes($db), $csrfp, $authInfo, $cfg->getString('urls:skins_base'));
$router->register($skinsRoutes);
$urls->register($skinsRoutes);
MojangInterop::registerRoutes($router); MojangInterop::registerRoutes($router);

View file

@ -6,13 +6,15 @@ use RuntimeException;
use Index\CsrfToken; use Index\CsrfToken;
use Index\Http\Routing\{HttpGet,HttpMiddleware,HttpPost,RouteHandler,RouteHandlerTrait}; use Index\Http\Routing\{HttpGet,HttpMiddleware,HttpPost,RouteHandler,RouteHandlerTrait};
use Index\Templating\TplEnvironment; use Index\Templating\TplEnvironment;
use Index\Urls\{UrlFormat,UrlRegistry,UrlSource,UrlSourceTrait};
use Ramsey\Uuid\Uuid; use Ramsey\Uuid\Uuid;
class ClientsRoutes implements RouteHandler { class ClientsRoutes implements RouteHandler, UrlSource {
use RouteHandlerTrait; use RouteHandlerTrait, UrlSourceTrait;
public function __construct( public function __construct(
private TplEnvironment $templating, private TplEnvironment $templating,
private UrlRegistry $urls,
private AccountLinks $accountLinks, private AccountLinks $accountLinks,
private Authorisations $authorisations, private Authorisations $authorisations,
private Verifications $verifications, private Verifications $verifications,
@ -44,6 +46,7 @@ class ClientsRoutes implements RouteHandler {
]; ];
#[HttpGet('/clients')] #[HttpGet('/clients')]
#[UrlFormat('clients:index', '/clients', ['error' => '<error>'])]
public function getClients($response, $request) { public function getClients($response, $request) {
$template = $this->templating->load('clients/index'); $template = $this->templating->load('clients/index');
@ -76,16 +79,17 @@ class ClientsRoutes implements RouteHandler {
} }
#[HttpPost('/clients/link')] #[HttpPost('/clients/link')]
#[UrlFormat('clients:link', '/clients/link')]
public function postLink($response, $request) { public function postLink($response, $request) {
if($this->accountLinks->checkHasLink($this->authInfo->user_id)) { if($this->accountLinks->checkHasLink($this->authInfo->user_id)) {
$response->redirect('/clients?error=link:already'); $response->redirect($this->urls->format('clients:index', ['error' => 'link:already']));
return; return;
} }
$body = $request->getContent(); $body = $request->getContent();
$code = (string)$body->getParam('code'); $code = (string)$body->getParam('code');
if(strlen($code) !== 10) { if(strlen($code) !== 10) {
$response->redirect('/clients?error=link:format'); $response->redirect($this->urls->format('clients:index', ['error' => 'link:format']));
return; return;
} }
@ -94,7 +98,7 @@ class ClientsRoutes implements RouteHandler {
try { try {
$verifyInfo = $this->verifications->getVerification(code: $code); $verifyInfo = $this->verifications->getVerification(code: $code);
} catch(RuntimeException $ex) { } catch(RuntimeException $ex) {
$response->redirect('/clients?error=link:code'); $response->redirect($this->urls->format('clients:index', ['error' => 'link:code']));
return; return;
} }
@ -102,16 +106,18 @@ class ClientsRoutes implements RouteHandler {
$this->accountLinks->createLink($this->authInfo->user_id, $verifyInfo); $this->accountLinks->createLink($this->authInfo->user_id, $verifyInfo);
$this->authorisations->createAuthorisation($verifyInfo, grant: true); $this->authorisations->createAuthorisation($verifyInfo, grant: true);
$response->redirect('/clients'); $response->redirect($this->urls->format('clients:index'));
} }
#[HttpPost('/clients/unlink')] #[HttpPost('/clients/unlink')]
#[UrlFormat('clients:unlink', '/clients/unlink')]
public function postUnlink($response) { public function postUnlink($response) {
$this->accountLinks->deleteLink(userInfo: $this->authInfo->user_id); $this->accountLinks->deleteLink(userInfo: $this->authInfo->user_id);
$response->redirect('/clients'); $response->redirect($this->urls->format('clients:index'));
} }
#[HttpPost('/clients/authorise')] #[HttpPost('/clients/authorise')]
#[UrlFormat('clients:authorise', '/clients/authorise')]
public function postAuthorise($response, $request) { public function postAuthorise($response, $request) {
$body = $request->getContent(); $body = $request->getContent();
$authId = (string)$body->getParam('auth'); $authId = (string)$body->getParam('auth');
@ -137,10 +143,11 @@ class ClientsRoutes implements RouteHandler {
$this->authorisations->setAuthorisationGranted($authInfo); $this->authorisations->setAuthorisationGranted($authInfo);
$response->redirect('/clients'); $response->redirect($this->urls->format('clients:index'));
} }
#[HttpPost('/clients/deauthorise')] #[HttpPost('/clients/deauthorise')]
#[UrlFormat('clients:deauthorise', '/clients/deauthorise')]
public function postDeauthorise($response, $request) { public function postDeauthorise($response, $request) {
$body = $request->getContent(); $body = $request->getContent();
$authId = (string)$body->getParam('auth'); $authId = (string)$body->getParam('auth');
@ -170,6 +177,6 @@ class ClientsRoutes implements RouteHandler {
$this->authorisations->deleteAuthorisations(authInfo: $authInfo); $this->authorisations->deleteAuthorisations(authInfo: $authInfo);
} }
$response->redirect('/clients'); $response->redirect($this->urls->format('clients:index'));
} }
} }

View file

@ -2,19 +2,22 @@
namespace Mince; namespace Mince;
use Index\Http\Routing\{HttpGet,RouteHandler,RouteHandlerTrait}; use Index\Http\Routing\{HttpGet,RouteHandler,RouteHandlerTrait};
use Index\Urls\{UrlFormat,UrlRegistry,UrlSource,UrlSourceTrait};
use Index\Templating\TplEnvironment; use Index\Templating\TplEnvironment;
class HomeRoutes implements RouteHandler { class HomeRoutes implements RouteHandler, UrlSource {
use RouteHandlerTrait; use RouteHandlerTrait, UrlSourceTrait;
public function __construct( public function __construct(
private TplEnvironment $templating, private TplEnvironment $templating,
private UrlRegistry $urls,
private Servers $servers, private Servers $servers,
private object $userInfo, private object $userInfo,
private string $loginUrl private string $loginUrl
) {} ) {}
#[HttpGet('/')] #[HttpGet('/')]
#[UrlFormat('index', '/')]
public function getIndex($response, $request) { public function getIndex($response, $request) {
return $this->templating->render('index', [ return $this->templating->render('index', [
'servers' => iterator_to_array($this->servers->getServers(deleted: false)), 'servers' => iterator_to_array($this->servers->getServers(deleted: false)),
@ -22,22 +25,25 @@ class HomeRoutes implements RouteHandler {
} }
#[HttpGet('/login')] #[HttpGet('/login')]
#[UrlFormat('login', '/login')]
public function getLogin($response) { public function getLogin($response) {
$response->redirect($this->userInfo->success ? '/' : $this->loginUrl); $response->redirect($this->userInfo->success ? $this->urls->format('index') : $this->loginUrl);
} }
#[HttpGet('/downloads')] #[HttpGet('/downloads')]
#[UrlFormat('downloads', '/downloads')]
public function getDownloads() { public function getDownloads() {
return $this->templating->render('downloads'); return $this->templating->render('downloads');
} }
#[HttpGet('/guide')] #[HttpGet('/guide')]
#[UrlFormat('guide', '/guide')]
public function getGuide() { public function getGuide() {
return $this->templating->render('guide'); return $this->templating->render('guide');
} }
#[HttpGet('/index.php')] #[HttpGet('/index.php')]
public function getRedirect($response) { public function getRedirect($response) {
$response->redirect('/', true); $response->redirect($this->urls->format('index'), true);
} }
} }

View file

@ -9,10 +9,11 @@ use RuntimeException;
use Index\{CsrfToken,XString}; use Index\{CsrfToken,XString};
use Index\Http\Routing\{HttpGet,HttpMiddleware,HttpPost,RouteHandler,RouteHandlerTrait}; use Index\Http\Routing\{HttpGet,HttpMiddleware,HttpPost,RouteHandler,RouteHandlerTrait};
use Index\Templating\TplEnvironment; use Index\Templating\TplEnvironment;
use Index\Urls\{UrlFormat,UrlRegistry,UrlSource,UrlSourceTrait};
use Ramsey\Uuid\{Uuid,UuidInterface}; use Ramsey\Uuid\{Uuid,UuidInterface};
class SkinsRoutes implements RouteHandler { class SkinsRoutes implements RouteHandler, UrlSource {
use RouteHandlerTrait; use RouteHandlerTrait, UrlSourceTrait;
private const TEXTURES_DIR = '/textures'; private const TEXTURES_DIR = '/textures';
private const TEXTURES_PATH = MCR_DIR_PUB . self::TEXTURES_DIR; private const TEXTURES_PATH = MCR_DIR_PUB . self::TEXTURES_DIR;
@ -21,6 +22,7 @@ class SkinsRoutes implements RouteHandler {
public function __construct( public function __construct(
private TplEnvironment $templating, private TplEnvironment $templating,
private UrlRegistry $urls,
private AccountLinks $accountLinks, private AccountLinks $accountLinks,
private Skins $skins, private Skins $skins,
private Capes $capes, private Capes $capes,
@ -61,7 +63,7 @@ class SkinsRoutes implements RouteHandler {
try { try {
$this->linkInfo = $this->accountLinks->getLink(userInfo: $this->authInfo->user_id); $this->linkInfo = $this->accountLinks->getLink(userInfo: $this->authInfo->user_id);
} catch(RuntimeException $ex) { } catch(RuntimeException $ex) {
$response->redirect('/clients'); $response->redirect($this->urls->format('clients:index'));
return true; return true;
} }
@ -88,6 +90,7 @@ class SkinsRoutes implements RouteHandler {
]; ];
#[HttpGet('/skins')] #[HttpGet('/skins')]
#[UrlFormat('skins:index', '/skins', ['error' => '<error>'])]
public function getSkins($response, $request) { public function getSkins($response, $request) {
$skinInfo = $this->skins->getSkin($this->linkInfo); $skinInfo = $this->skins->getSkin($this->linkInfo);
$skinPath = $skinInfo === null ? null : $this->getRemotePath($skinInfo->getHash(), false); $skinPath = $skinInfo === null ? null : $this->getRemotePath($skinInfo->getHash(), false);
@ -121,6 +124,7 @@ class SkinsRoutes implements RouteHandler {
} }
#[HttpPost('/skins/upload-skin')] #[HttpPost('/skins/upload-skin')]
#[UrlFormat('skins:skin:upload', '/skins/upload-skin')]
public function postUploadSkin($response, $request) { public function postUploadSkin($response, $request) {
$body = $request->getContent(); $body = $request->getContent();
if(!$body->hasUploadedFile('texture')) if(!$body->hasUploadedFile('texture'))
@ -130,12 +134,12 @@ class SkinsRoutes implements RouteHandler {
$model = (string)$body->getParam('model'); $model = (string)$body->getParam('model');
if(!in_array($model, Skins::MODELS)) { if(!in_array($model, Skins::MODELS)) {
$response->redirect('/skins?error=skin:model'); $response->redirect($this->urls->format('skins:index', ['error' => 'skin:model']));
return; return;
} }
if($texture->getSize() > 512000) { if($texture->getSize() > 512000) {
$response->redirect('/skins?error=skin:size'); $response->redirect($this->urls->format('skins:index', ['error' => 'skin:size']));
return; return;
} }
@ -153,7 +157,7 @@ class SkinsRoutes implements RouteHandler {
$imagick->writeImage(); $imagick->writeImage();
$imagick->destroy(); $imagick->destroy();
} catch(ImagickException $ex) { } catch(ImagickException $ex) {
$response->redirect('/skins?error=skin:format'); $response->redirect($this->urls->format('skins:index', ['error' => 'skin:format']));
return; return;
} }
@ -180,10 +184,11 @@ class SkinsRoutes implements RouteHandler {
$this->deleteLocalFileMaybe($hash); $this->deleteLocalFileMaybe($hash);
} }
$response->redirect('/skins'); $response->redirect($this->urls->format('skins:index'));
} }
#[HttpPost('/skins/delete-skin')] #[HttpPost('/skins/delete-skin')]
#[UrlFormat('skins:skin:delete', '/skins/delete-skin')]
public function postDeleteSkin($response) { public function postDeleteSkin($response) {
$skinInfo = $this->skins->getSkin($this->linkInfo); $skinInfo = $this->skins->getSkin($this->linkInfo);
if($skinInfo !== null) { if($skinInfo !== null) {
@ -191,10 +196,11 @@ class SkinsRoutes implements RouteHandler {
$this->deleteLocalFileMaybe($skinInfo->getHash()); $this->deleteLocalFileMaybe($skinInfo->getHash());
} }
$response->redirect('/skins'); $response->redirect($this->urls->format('skins:index'));
} }
#[HttpPost('/skins/upload-cape')] #[HttpPost('/skins/upload-cape')]
#[UrlFormat('skins:cape:upload', '/skins/upload-cape')]
public function postUploadCape($response, $request) { public function postUploadCape($response, $request) {
$body = $request->getContent(); $body = $request->getContent();
if(!$body->hasUploadedFile('texture')) if(!$body->hasUploadedFile('texture'))
@ -202,7 +208,7 @@ class SkinsRoutes implements RouteHandler {
$texture = $body->getUploadedFile('texture'); $texture = $body->getUploadedFile('texture');
if($texture->getSize() > 256000) { if($texture->getSize() > 256000) {
$response->redirect('/skins?error=cape:size'); $response->redirect($this->urls->format('skins:index', ['error' => 'cape:size']));
return; return;
} }
@ -217,7 +223,7 @@ class SkinsRoutes implements RouteHandler {
$imagick->writeImage(); $imagick->writeImage();
$imagick->destroy(); $imagick->destroy();
} catch(ImagickException $ex) { } catch(ImagickException $ex) {
$response->redirect('/skins?error=cape:format'); $response->redirect($this->urls->format('skins:index', ['error' => 'cape:format']));
return; return;
} }
@ -243,10 +249,11 @@ class SkinsRoutes implements RouteHandler {
$this->deleteLocalFileMaybe($hash); $this->deleteLocalFileMaybe($hash);
} }
$response->redirect('/skins'); $response->redirect($this->urls->format('skins:index'));
} }
#[HttpPost('/skins/delete-cape')] #[HttpPost('/skins/delete-cape')]
#[UrlFormat('skins:cape:delete', '/skins/delete-cape')]
public function postDeleteCape($response) { public function postDeleteCape($response) {
$capeInfo = $this->capes->getCape($this->linkInfo); $capeInfo = $this->capes->getCape($this->linkInfo);
if($capeInfo !== null) { if($capeInfo !== null) {
@ -254,10 +261,11 @@ class SkinsRoutes implements RouteHandler {
$this->deleteLocalFileMaybe($capeInfo->getHash()); $this->deleteLocalFileMaybe($capeInfo->getHash());
} }
$response->redirect('/skins'); $response->redirect($this->urls->format('skins:index'));
} }
#[HttpPost('/skins/import')] #[HttpPost('/skins/import')]
#[UrlFormat('skins:import', '/skins/import')]
public function postImport($response, $request) { public function postImport($response, $request) {
$body = $request->getContent(); $body = $request->getContent();
$userAgent = $request->getHeaderLine('User-Agent'); $userAgent = $request->getHeaderLine('User-Agent');
@ -324,7 +332,7 @@ class SkinsRoutes implements RouteHandler {
} }
} }
$response->redirect('/skins'); $response->redirect($this->urls->format('skins:index'));
} }
private function getProfileInfo(UuidInterface $uuid, bool $includeProfileActions) { private function getProfileInfo(UuidInterface $uuid, bool $includeProfileActions) {

View file

@ -17,7 +17,7 @@
<p style="color: red;">{{ error.message }}</p> <p style="color: red;">{{ error.message }}</p>
{% endif %} {% endif %}
<form method="post" action="/clients/link"> <form method="post" action="{{ url('clients:link') }}">
<input type="hidden" name="csrfp" value="{{ globals.csrfp }}"> <input type="hidden" name="csrfp" value="{{ globals.csrfp }}">
<label> <label>
<div class="label-header">Link code</div> <div class="label-header">Link code</div>
@ -69,13 +69,13 @@
</td> </td>
<td class="actions"> <td class="actions">
{% if client.isPending %} {% if client.isPending %}
<form method="post" action="/clients/authorise"> <form method="post" action="{{ url('clients:authorise') }}">
<input type="hidden" name="csrfp" value="{{ globals.csrfp }}"> <input type="hidden" name="csrfp" value="{{ globals.csrfp }}">
<input type="hidden" name="auth" value="{{ client.id }}"> <input type="hidden" name="auth" value="{{ client.id }}">
<input class="action action-authorise js-authorise-button" type="submit" value="Approve" disabled onclick="return confirm('Are you sure?');"> <input class="action action-authorise js-authorise-button" type="submit" value="Approve" disabled onclick="return confirm('Are you sure?');">
</form> </form>
{% endif %} {% endif %}
<form method="post" action="/clients/deauthorise"> <form method="post" action="{{ url('clients:deauthorise') }}">
<input type="hidden" name="csrfp" value="{{ globals.csrfp }}"> <input type="hidden" name="csrfp" value="{{ globals.csrfp }}">
<input type="hidden" name="auth" value="{{ client.id }}"> <input type="hidden" name="auth" value="{{ client.id }}">
<input class="action action-deauthorise" type="submit" value="{% if client.isPending %}Deny{% else %}Deauthorise{% endif %}"> <input class="action action-deauthorise" type="submit" value="{% if client.isPending %}Deny{% else %}Deauthorise{% endif %}">
@ -92,7 +92,7 @@
<div class="section accmegadeauth"> <div class="section accmegadeauth">
<h2>Crowd Control</h2> <h2>Crowd Control</h2>
<p>Provided for those who prefer the nuclear option. Pressing the first button will deauthorise all clients, the other one will only remove pending ones.</p> <p>Provided for those who prefer the nuclear option. Pressing the first button will deauthorise all clients, the other one will only remove pending ones.</p>
<form method="post" action="/clients/deauthorise"> <form method="post" action="{{ url('clients:deauthorise') }}">
<input type="hidden" name="csrfp" value="{{ globals.csrfp }}"> <input type="hidden" name="csrfp" value="{{ globals.csrfp }}">
<button class="form-btn-red" name="auth" value="all">Deauthorise all clients</button> <button class="form-btn-red" name="auth" value="all">Deauthorise all clients</button>
<button class="form-btn-green" name="auth" value="pending">Deny pending clients</button> <button class="form-btn-green" name="auth" value="pending">Deny pending clients</button>
@ -103,7 +103,7 @@
<h2>Linked Minecraft account</h2> <h2>Linked Minecraft account</h2>
<p>This is the Minecraft account currently associated with your Flashii ID. Revoking revoke your access to the servers. <strong>If you're planning on changing your username, please keep in mind that your stats and inventory on the servers WILL NOT carry over automatically.</strong></p> <p>This is the Minecraft account currently associated with your Flashii ID. Revoking revoke your access to the servers. <strong>If you're planning on changing your username, please keep in mind that your stats and inventory on the servers WILL NOT carry over automatically.</strong></p>
<p>Your account has been linked with <b>{{ link.name }}</b> since <b>{{ link.createdTime|date('Y-m-d H:i:s T') }}</b>.</p> <p>Your account has been linked with <b>{{ link.name }}</b> since <b>{{ link.createdTime|date('Y-m-d H:i:s T') }}</b>.</p>
<form method="post" action="/clients/unlink"> <form method="post" action="{{ url('clients:unlink') }}">
<input type="hidden" name="csrfp" value="{{ globals.csrfp }}"> <input type="hidden" name="csrfp" value="{{ globals.csrfp }}">
<input type="submit" value="Unlink account"> <input type="submit" value="Unlink account">
</form> </form>

View file

@ -21,7 +21,7 @@
<div class="section downloads-item"> <div class="section downloads-item">
<h2>MultiMC/PolyMC/Prism Launcher spoofed accounts file</h2> <h2>MultiMC/PolyMC/Prism Launcher spoofed accounts file</h2>
<p>This file is used in the <a href="/guide">guide</a> to allow you to add an Offline account of your own.</p> <p>This file is used in the <a href="{{ url('guide') }}">guide</a> to allow you to add an Offline account of your own.</p>
<ul> <ul>
<li><a href="/dl/accounts.json" download>Download accounts.json</a></li> <li><a href="/dl/accounts.json" download>Download accounts.json</a></li>

View file

@ -94,7 +94,7 @@
<p>Press the <strong>Add Instance</strong> button on the top left.</p> <p>Press the <strong>Add Instance</strong> button on the top left.</p>
<p> <p>
Under <strong>Custom</strong> make sure the version the server you want to connect to is currently running is selected. Under <strong>Custom</strong> make sure the version the server you want to connect to is currently running is selected.
The versions of the servers are listing on the <a href="/">home page</a> along with their addresses for your convenience. The versions of the servers are listing on the <a href="{{ url('index') }}">home page</a> along with their addresses for your convenience.
</p> </p>
<p>If you plan on using our client side mod, select <strong>Fabric</strong> under <strong>Mod Loader</strong> and just pick whatever the latest version is, which is also what should be selected by default.</p> <p>If you plan on using our client side mod, select <strong>Fabric</strong> under <strong>Mod Loader</strong> and just pick whatever the latest version is, which is also what should be selected by default.</p>
<p>When you've done those things, press OK.</p> <p>When you've done those things, press OK.</p>
@ -104,7 +104,7 @@
<div class="section" id="gac5"> <div class="section" id="gac5">
<h3><a href="#gac5">Step 5. Installing the Flashii Extensions client side mod (Optional)</a></h3> <h3><a href="#gac5">Step 5. Installing the Flashii Extensions client side mod (Optional)</a></h3>
<p> <p>
You can download the Flashii Extensions mod from the <a href="/downloads">downloads page</a>, the latest version of the mod is the first thing linked in the list. You can download the Flashii Extensions mod from the <a href="{{ url('downloads') }}">downloads page</a>, the latest version of the mod is the first thing linked in the list.
Save it anywhere you like as long as you remember where you saved it. Save it anywhere you like as long as you remember where you saved it.
</p> </p>
<p> <p>
@ -124,7 +124,7 @@
<div class="section" id="ctos"> <div class="section" id="ctos">
<h2><a href="#ctos">Connecting to a server</a></h2> <h2><a href="#ctos">Connecting to a server</a></h2>
<p>We're assuming you've already picked a server to play from from the <a href="/">home page</a> and have added it to your server list.</p> <p>We're assuming you've already picked a server to play from from the <a href="{{ url('index') }}">home page</a> and have added it to your server list.</p>
</div> </div>
<div class="section" id="ctos1"> <div class="section" id="ctos1">

View file

@ -35,7 +35,11 @@
<div class="section"> <div class="section">
<h2>Authentication and Skins</h2> <h2>Authentication and Skins</h2>
<p>Because of the deprecation of Mojang/Minecraft accounts and the general distrust in Microsoft's ability to not suspend your account for no reason, our servers run in offline mode with an authentication plugin. This means you can play the game with any username you want without logging in with a Microsoft account in your launcher, so long as someone else isn't already using that name. You don't have to install any mods on your client for the authentication side of things to work, in order to be able to see skins of other players you'll need to install a mod though. You can grab the mod through the <a href="/downloads">downloads</a> page and if you're not sure what to do you can follow the <a href="/guide">guide</a> page!</p> <p>
Because of the deprecation of Mojang/Minecraft accounts and the general distrust in Microsoft's ability to not suspend your account for no reason, our servers run in offline mode with an authentication plugin.
This means you can play the game with any username you want without logging in with a Microsoft account in your launcher, so long as someone else isn't already using that name.
You don't have to install any mods on your client for the authentication side of things to work, in order to be able to see skins of other players you'll need to install a mod though.
You can grab the mod through the <a href="{{ url('downloads') }}">downloads</a> page and if you're not sure what to do you can follow the <a href="{{ url('guide') }}">guide</a> page!</p>
</div> </div>
{# {#

View file

@ -14,19 +14,19 @@
<a href="/"><img src="/assets/weblogo.png" alt="Flashii Minecraft"></a> <a href="/"><img src="/assets/weblogo.png" alt="Flashii Minecraft"></a>
</div> </div>
<div class="header-menu"> <div class="header-menu">
<a href="/">Home</a> <a href="{{ url('index') }}">Home</a>
<a href="/downloads">Downloads</a> <a href="{{ url('downloads') }}">Downloads</a>
<a href="/guide">Guide</a> <a href="{{ url('guide') }}">Guide</a>
{% if globals.is_authed %} {% if globals.is_authed %}
<a href="/clients">Clients</a> <a href="{{ url('clients:index') }}">Clients</a>
<a href="/skins">Skins</a> <a href="{{ url('skins:index') }}">Skins</a>
{% endif %} {% endif %}
</div> </div>
<div class="header-user"> <div class="header-user">
{% if globals.is_authed %} {% if globals.is_authed %}
Logged in as <span style="color: {{ globals.user.colour }}">{{ globals.user.name }}</span> Logged in as <span style="color: {{ globals.user.colour }}">{{ globals.user.name }}</span>
{% else %} {% else %}
<a href="/login">Log in</a> <a href="{{ url('login') }}">Log in</a>
{% endif %} {% endif %}
</div> </div>
</div> </div>

View file

@ -5,7 +5,7 @@
{% block content %} {% block content %}
<div class="section skins-header"> <div class="section skins-header">
<h1>Skins</h1> <h1>Skins</h1>
<p>Because our servers run in offline mode with our own authentication plugin, skins are downloaded from Mojang's servers. To make up for this we also have a client side mod that substitutes the skin and cape server with our own. You can find the mod on the <a href="/downloads">downloads page</a>. Please keep your textures clean.</p> <p>Because our servers run in offline mode with our own authentication plugin, skins are downloaded from Mojang's servers. To make up for this we also have a client side mod that substitutes the skin and cape server with our own. You can find the mod on the <a href="{{ url('downloads') }}">downloads page</a>. Please keep your textures clean.</p>
</div> </div>
<div class="section skin"> <div class="section skin">
@ -17,7 +17,7 @@
{% endif %} {% endif %}
<div class="skins-form"> <div class="skins-form">
<form method="post" action="/skins/upload-skin" enctype="multipart/form-data"> <form method="post" action="{{ url('skins:skin:upload') }}" enctype="multipart/form-data">
<input type="hidden" name="csrfp" value="{{ globals.csrfp }}"> <input type="hidden" name="csrfp" value="{{ globals.csrfp }}">
<input type="file" name="texture"> <input type="file" name="texture">
<select name="model"> <select name="model">
@ -28,7 +28,7 @@
<input type="submit" value="Upload Skin" class="skins-form-upload"> <input type="submit" value="Upload Skin" class="skins-form-upload">
</form> </form>
{% if skin is not null %} {% if skin is not null %}
<form method="post" action="/skins/delete-skin"> <form method="post" action="{{ url('skins:skin:delete') }}">
<input type="hidden" name="csrfp" value="{{ globals.csrfp }}"> <input type="hidden" name="csrfp" value="{{ globals.csrfp }}">
<input type="submit" value="Delete Skin" class="skins-form-delete"> <input type="submit" value="Delete Skin" class="skins-form-delete">
</form> </form>
@ -51,13 +51,13 @@
{% endif %} {% endif %}
<div class="skins-form"> <div class="skins-form">
<form method="post" action="/skins/upload-cape" enctype="multipart/form-data"> <form method="post" action="{{ url('skins:cape:upload') }}" enctype="multipart/form-data">
<input type="hidden" name="csrfp" value="{{ globals.csrfp }}"> <input type="hidden" name="csrfp" value="{{ globals.csrfp }}">
<input type="file" name="texture"> <input type="file" name="texture">
<input type="submit" value="Upload Cape" class="skins-form-upload"> <input type="submit" value="Upload Cape" class="skins-form-upload">
</form> </form>
{% if cape is not null %} {% if cape is not null %}
<form method="post" action="/skins/delete-cape"> <form method="post" action="{{ url('skins:cape:delete') }}">
<input type="hidden" name="csrfp" value="{{ globals.csrfp }}"> <input type="hidden" name="csrfp" value="{{ globals.csrfp }}">
<input type="submit" value="Delete Cape" class="skins-form-delete"> <input type="submit" value="Delete Cape" class="skins-form-delete">
</form> </form>
@ -74,7 +74,7 @@
<div class="section skinimport"> <div class="section skinimport">
<h2>Import from Mojang account</h2> <h2>Import from Mojang account</h2>
<p>Import skin and cape textures from a Mojang Minecraft account. If you linked the same username as your actual Minecraft account, you can just press Import without anything.</p> <p>Import skin and cape textures from a Mojang Minecraft account. If you linked the same username as your actual Minecraft account, you can just press Import without anything.</p>
<form method="post" action="/skins/import"> <form method="post" action="{{ url('skins:import') }}">
<input type="hidden" name="csrfp" value="{{ globals.csrfp }}"> <input type="hidden" name="csrfp" value="{{ globals.csrfp }}">
<label> <label>
<div class="label-header">Minecraft Username</div> <div class="label-header">Minecraft Username</div>