1
0
mirror of https://github.com/zumbiepig/MineXLauncher.git synced 2025-06-08 09:24:48 +00:00
This commit is contained in:
zumbiepig 2024-09-07 16:10:28 -07:00
parent c57b96c48f
commit 4f9516087b
No known key found for this signature in database
GPG Key ID: 17C891BE28B953DE
41 changed files with 941 additions and 285 deletions

View File

@ -1,3 +1,2 @@
src/game/ src/game/
!src/game/web/*.html
src/resources/mods/downloads/ src/resources/mods/downloads/

View File

@ -1,5 +1,4 @@
{ {
"printWidth": 120,
"useTabs": true, "useTabs": true,
"singleQuote": true "singleQuote": true
} }

View File

@ -1,3 +1,7 @@
{ {
"recommendations": ["oven.bun-vscode", "dbaeumer.vscode-eslint", "esbenp.prettier-vscode"] "recommendations": [
"oven.bun-vscode",
"dbaeumer.vscode-eslint",
"esbenp.prettier-vscode"
]
} }

View File

@ -1,5 +1,12 @@
import { build } from 'bun'; import { build } from 'bun';
import { mkdirSync, readdirSync, statSync, writeFileSync, readFileSync, rmSync } from 'fs'; import {
mkdirSync,
readdirSync,
statSync,
writeFileSync,
readFileSync,
rmSync,
} from 'fs';
import { resolve, dirname, basename } from 'path'; import { resolve, dirname, basename } from 'path';
import { minify } from 'html-minifier'; import { minify } from 'html-minifier';
import javascriptObfuscator from 'javascript-obfuscator'; import javascriptObfuscator from 'javascript-obfuscator';
@ -34,7 +41,11 @@ srcFiles.forEach((file) => {
if (file.endsWith('.ts')) bundleFiles.push(file); if (file.endsWith('.ts')) bundleFiles.push(file);
else if (isDev) copyFiles.push(file); else if (isDev) copyFiles.push(file);
else if (/\.(html|css|js|json)$/.test(strippedPath)) { else if (/\.(html|css|js|json)$/.test(strippedPath)) {
if (strippedPath.startsWith('/game/offline/') || basename(strippedPath) === 'classes.js') copyFiles.push(file); if (
strippedPath.startsWith('/game/offline/') ||
basename(strippedPath) === 'classes.js'
)
copyFiles.push(file);
else minifyFiles.push(file); else minifyFiles.push(file);
} else copyFiles.push(file); } else copyFiles.push(file);
}); });
@ -76,7 +87,9 @@ if (!isDev) {
console.log(chalk.cyan('Obfuscating JavaScript...\n')); console.log(chalk.cyan('Obfuscating JavaScript...\n'));
bundleFiles.forEach((file) => { bundleFiles.forEach((file) => {
const outputPath = file.replace(new RegExp(`^${srcDir}`), publicDir).replace(/\.ts$/, '.js'); const outputPath = file
.replace(new RegExp(`^${srcDir}`), publicDir)
.replace(/\.ts$/, '.js');
writeFileSync( writeFileSync(
outputPath, outputPath,
javascriptObfuscator javascriptObfuscator
@ -103,7 +116,9 @@ writeFileSync(
assetsJsonPath, assetsJsonPath,
JSON.stringify( JSON.stringify(
getFiles(publicDir).map((asset) => { getFiles(publicDir).map((asset) => {
return asset.replace(new RegExp(`^${publicDir}`), '').replace(/\/index\.html$/, '/'); return asset
.replace(new RegExp(`^${publicDir}`), '')
.replace(/\/index\.html$/, '/');
}), }),
), ),
); );

View File

@ -41,7 +41,11 @@ app.use(compression());
if (isDev) app.use(errorhandler()); if (isDev) app.use(errorhandler());
app.use(serveFavicon(join(BASE_DIR, 'resources/images/icons/favicon.webp'), { maxAge: 86400 })); app.use(
serveFavicon(join(BASE_DIR, 'resources/images/icons/favicon.webp'), {
maxAge: 86400,
}),
);
app.use(serveStatic(BASE_DIR)); app.use(serveStatic(BASE_DIR));
app.use('/', indexRouter); app.use('/', indexRouter);
@ -65,7 +69,9 @@ app
.on('error', (error) => { .on('error', (error) => {
if (error.code === 'EADDRINUSE') { if (error.code === 'EADDRINUSE') {
console.error( console.error(
chalk.red('EADDRINUSE') + chalk.gray(': ') + chalk.bold(`Failed to start server. Is port ${PORT} in use?`), chalk.red('EADDRINUSE') +
chalk.gray(': ') +
chalk.bold(`Failed to start server. Is port ${PORT} in use?`),
); );
process.exit(1); process.exit(1);
} }

View File

@ -4,9 +4,17 @@
<meta charset="UTF-8" /> <meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" /> <meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>MineXLauncher</title> <title>MineXLauncher</title>
<link rel="icon" type="image/webp" href="/resources/images/icons/favicon.webp" /> <link
rel="icon"
type="image/webp"
href="/resources/images/icons/favicon.webp"
/>
<link rel="stylesheet" href="/resources/styles/themes/default.css" /> <link rel="stylesheet" href="/resources/styles/themes/default.css" />
<link rel="stylesheet" id="theme" onload="document.documentElement.style.display = ''" /> <link
rel="stylesheet"
id="theme"
onload="document.documentElement.style.display = ''"
/>
<meta name="theme-color" content="#333" /> <meta name="theme-color" content="#333" />
<script src="/resources/scripts/google-tag.js"></script> <script src="/resources/scripts/google-tag.js"></script>
<script src="/resources/scripts/main.js"></script> <script src="/resources/scripts/main.js"></script>
@ -35,7 +43,9 @@
</div> </div>
</div> </div>
<div class="bottom-bar"> <div class="bottom-bar">
<span onclick="window.open('https://discord.gg/VRwbRJjXzt')">Join the MineXLauncher Discord</span> <span onclick="window.open('https://discord.gg/VRwbRJjXzt')"
>Join the MineXLauncher Discord</span
>
<span>© 2024 MineXLauncher. All rights reserved.</span> <span>© 2024 MineXLauncher. All rights reserved.</span>
</div> </div>
</div> </div>

View File

@ -4,9 +4,17 @@
<meta charset="UTF-8" /> <meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" /> <meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>MineXLauncher</title> <title>MineXLauncher</title>
<link rel="icon" type="image/webp" href="/resources/images/icons/favicon.webp" /> <link
rel="icon"
type="image/webp"
href="/resources/images/icons/favicon.webp"
/>
<link rel="stylesheet" href="/resources/styles/themes/default.css" /> <link rel="stylesheet" href="/resources/styles/themes/default.css" />
<link rel="stylesheet" id="theme" onload="document.documentElement.style.display = ''" /> <link
rel="stylesheet"
id="theme"
onload="document.documentElement.style.display = ''"
/>
<script src="/resources/scripts/google-tag.js"></script> <script src="/resources/scripts/google-tag.js"></script>
<script src="/resources/scripts/main.js"></script> <script src="/resources/scripts/main.js"></script>
</head> </head>
@ -58,17 +66,32 @@
<div class="main-content"> <div class="main-content">
<div class="article-list"> <div class="article-list">
<div onclick="article.open('mc-server')"> <div onclick="article.open('mc-server')">
<img loading="lazy" src="/resources/images/icons/articles/mc-server.webp" /> <img
loading="lazy"
src="/resources/images/icons/articles/mc-server.webp"
/>
<div class="details"> <div class="details">
<strong>How To Make An Eaglercraft Server</strong> <strong>How To Make An Eaglercraft Server</strong>
<p>Have you ever wondered how to make an Eaglercraft Server? Read this article to find out how!</p> <p>
Have you ever wondered how to make an Eaglercraft Server?
Read this article to find out how!
</p>
</div> </div>
</div> </div>
<div onclick="article.open('cloudflare-tunnel')"> <div onclick="article.open('cloudflare-tunnel')">
<img loading="lazy" src="/resources/images/icons/articles/cloudflare-tunnel.webp" /> <img
loading="lazy"
src="/resources/images/icons/articles/cloudflare-tunnel.webp"
/>
<div class="details"> <div class="details">
<strong>Setting Up a Cloudflare Tunnel for an Eaglercraft Server</strong> <strong
<p>This brief guide walks you through configuring a Cloudflare Tunnel for your Eaglercraft server.</p> >Setting Up a Cloudflare Tunnel for an Eaglercraft
Server</strong
>
<p>
This brief guide walks you through configuring a Cloudflare
Tunnel for your Eaglercraft server.
</p>
</div> </div>
</div> </div>
</div> </div>
@ -76,7 +99,9 @@
</div> </div>
</div> </div>
<div class="bottom-bar"> <div class="bottom-bar">
<span onclick="window.open('https://discord.gg/VRwbRJjXzt')">Join the MineXLauncher Discord</span> <span onclick="window.open('https://discord.gg/VRwbRJjXzt')"
>Join the MineXLauncher Discord</span
>
<span>© 2024 MineXLauncher. All rights reserved.</span> <span>© 2024 MineXLauncher. All rights reserved.</span>
</div> </div>
</div> </div>
@ -97,13 +122,22 @@
<h1>How To Make An Eaglercraft Server</h1> <h1>How To Make An Eaglercraft Server</h1>
<h5>Written by ServerDotSo and revised by zumbiepig</h5> <h5>Written by ServerDotSo and revised by zumbiepig</h5>
<p>Have you ever wondered how to make an Eaglercraft Server?</p> <p>Have you ever wondered how to make an Eaglercraft Server?</p>
<p>In this guide, we will be going over how to setup your own Eaglercraft server.</p> <p>
In this guide, we will be going over how to setup your own
Eaglercraft server.
</p>
<h3>Prerequisites</h3> <h3>Prerequisites</h3>
<ul> <ul>
<li>A server/computer with stable internet access and decent bandwidth</li> <li>
A server/computer with stable internet access and decent bandwidth
</li>
<li> <li>
Java 17 installed (Get it from Java 17 installed (Get it from
<a href="https://adoptium.net/temurin/releases/?version=17" target="_blank">here</a>) <a
href="https://adoptium.net/temurin/releases/?version=17"
target="_blank"
>here</a
>)
</li> </li>
<li>Basic knowledge of your OS's command-line/terminal</li> <li>Basic knowledge of your OS's command-line/terminal</li>
<li>Access to your network router (for port forwarding)</li> <li>Access to your network router (for port forwarding)</li>
@ -118,33 +152,52 @@
>here</a >here</a
>. >.
</li> </li>
<li>Create a new directory for your server and place the Bungee JAR file in it</li>
<li> <li>
Create a start script (e.g., <code>start.bat</code> for Windows or <code>start.sh</code> for Linux) with Create a new directory for your server and place the Bungee JAR
the following content: file in it
</li>
<li>
Create a start script (e.g., <code>start.bat</code> for Windows or
<code>start.sh</code> for Linux) with the following content:
<pre><code>java -Xms512M -Xmx512M -jar bungee.jar</code></pre> <pre><code>java -Xms512M -Xmx512M -jar bungee.jar</code></pre>
</li> </li>
<li>Run the start script to generate configuration files, then stop the server</li> <li>
<li>Edit the <code>config.yml</code> file to configure your proxy settings</li> Run the start script to generate configuration files, then stop
the server
</li>
<li>
Edit the <code>config.yml</code> file to configure your proxy
settings
</li>
</ol> </ol>
<h3>Step 2: Set Up Backend Server</h3> <h3>Step 2: Set Up Backend Server</h3>
<ol> <ol>
<li>Download a compatible Minecraft server JAR (e.g., Paper)</li> <li>Download a compatible Minecraft server JAR (e.g., Paper)</li>
<li>Set up the backend server in a separate directory</li> <li>Set up the backend server in a separate directory</li>
<li> <li>
Create a start script (e.g., <code>start.bat</code> for Windows or <code>start.sh</code> for Linux) with Create a start script (e.g., <code>start.bat</code> for Windows or
the following content: <code>start.sh</code> for Linux) with the following content:
<pre><code>java -Xms1024M -Xmx1024M -jar server.jar</code></pre> <pre><code>java -Xms1024M -Xmx1024M -jar server.jar</code></pre>
</li> </li>
<li>Configure <code>server.properties</code> (Disable online mode, change the port, etc.)</li>
<li>Configure <code>spigot.yml</code> to have this: <code>bungeecord: true</code></li>
<li> <li>
If your server is on a different version than 1.8.8, you need to download the Configure <code>server.properties</code> (Disable online mode,
<a href="https://viaversion.com/setup" target="_blank">ViaVersion plugins</a>. change the port, etc.)
</li> </li>
<li> <li>
Add the backend server to your Bungee <code>config.yml</code> by scrolling to the servers section and Configure <code>spigot.yml</code> to have this:
editing/adding an entry for your server, such as <code>localhost:port</code>. <code>bungeecord: true</code>
</li>
<li>
If your server is on a different version than 1.8.8, you need to
download the
<a href="https://viaversion.com/setup" target="_blank"
>ViaVersion plugins</a
>.
</li>
<li>
Add the backend server to your Bungee <code>config.yml</code> by
scrolling to the servers section and editing/adding an entry for
your server, such as <code>localhost:port</code>.
</li> </li>
</ol> </ol>
<h3>Step 3: Install EaglerXBungee Plugin</h3> <h3>Step 3: Install EaglerXBungee Plugin</h3>
@ -157,14 +210,25 @@
>here</a >here</a
>. >.
</li> </li>
<li>Place the downloaded JAR file in the Bungee server's <code>plugins</code> folder</li> <li>
<li>Restart the Bungee server to generate the plugin's configuration files</li> Place the downloaded JAR file in the Bungee server's
<li>Configure the EaglerXBungee plugin as needed (disabling online mode, etc.)</li> <code>plugins</code> folder
</li>
<li>
Restart the Bungee server to generate the plugin's configuration
files
</li>
<li>
Configure the EaglerXBungee plugin as needed (disabling online
mode, etc.)
</li>
</ol> </ol>
<h3>Step 4: Set Up Caddy</h3> <h3>Step 4: Set Up Caddy</h3>
<ol> <ol>
<li> <li>
Download and install Caddy from <a href="https://caddyserver.com/download" target="_blank">here</a>. Download and install Caddy from
<a href="https://caddyserver.com/download" target="_blank">here</a
>.
</li> </li>
<li> <li>
Create a file called Caddyfile with this configuration: Create a file called Caddyfile with this configuration:
@ -175,33 +239,42 @@
<ol> <ol>
<li>Access your router's admin panel</li> <li>Access your router's admin panel</li>
<li> <li>
Set up port forwarding for port <code>8081</code> (or whichever port you configured for EaglerXBungee) to Set up port forwarding for port <code>8081</code> (or whichever
your server's local IP address port you configured for EaglerXBungee) to your server's local IP
address
</li> </li>
</ol> </ol>
<h3>Step 6: Domain Configuration (Optional)</h3> <h3>Step 6: Domain Configuration (Optional)</h3>
<p>If you want to use a custom domain:</p> <p>If you want to use a custom domain:</p>
<ol> <ol>
<li>Purchase a domain from a domain registrar</li> <li>Purchase a domain from a domain registrar</li>
<li>Set up an A record pointing to your server's public IPv4 address</li>
<li> <li>
Edit the Caddyfile you made earlier, replacing <code>example.com</code> with your domain: Set up an A record pointing to your server's public IPv4 address
</li>
<li>
Edit the Caddyfile you made earlier, replacing
<code>example.com</code> with your domain:
<pre><code>example.com {<br> reverse_proxy :8081<br>}</code></pre> <pre><code>example.com {<br> reverse_proxy :8081<br>}</code></pre>
</li> </li>
<li>Restart Caddy to apply these changes, using <code>caddy stop</code> and <code>caddy start</code>.</li> <li>
Restart Caddy to apply these changes, using
<code>caddy stop</code> and <code>caddy start</code>.
</li>
</ol> </ol>
<h3>Step 7: Connecting to Your Server</h3> <h3>Step 7: Connecting to Your Server</h3>
<p> <p>
Players can connect to your server using an Eaglercraft client such as MineXLauncher, and entering your Players can connect to your server using an Eaglercraft client such
server's IP address or domain and port. as MineXLauncher, and entering your server's IP address or domain
and port.
</p> </p>
<p>Examples:</p> <p>Examples:</p>
<li><code>wss://localhost</code></li> <li><code>wss://localhost</code></li>
<li><code>wss://example.com</code></li> <li><code>wss://example.com</code></li>
<h3>Security Considerations</h3> <h3>Security Considerations</h3>
<p> <p>
Ensure you keep all software (especially the EaglerXBungee plugin) up to date and properly secure your Ensure you keep all software (especially the EaglerXBungee plugin)
server to protect against potential vulnerabilities. up to date and properly secure your server to protect against
potential vulnerabilities.
</p> </p>
</div> </div>
</div> </div>
@ -213,47 +286,76 @@
<h1>Setting Up a Cloudflare Tunnel for an Eaglercraft Server</h1> <h1>Setting Up a Cloudflare Tunnel for an Eaglercraft Server</h1>
<h5>Written by zumbiepig and SpeedSlicer</h5> <h5>Written by zumbiepig and SpeedSlicer</h5>
<p> <p>
In this guide, we'll walk you through the steps to set up a Cloudflare Tunnel for your Eaglercraft server, In this guide, we'll walk you through the steps to set up a
which is running on <code>ws://localhost:8081</code>. Cloudflare Tunnel for your Eaglercraft server, which is running on
<code>ws://localhost:8081</code>.
</p> </p>
<h3>Prerequisites</h3> <h3>Prerequisites</h3>
<ul> <ul>
<li>An Eaglercraft server running on <code>ws://localhost:8081</code></li> <li>
<li>Access to the computer your Eaglercraft server is running on</li> An Eaglercraft server running on <code>ws://localhost:8081</code>
</li>
<li>
Access to the computer your Eaglercraft server is running on
</li>
<li>A domain already linked to Cloudflare</li> <li>A domain already linked to Cloudflare</li>
</ul> </ul>
<h3>Step 1: Access Cloudflare Tunnels</h3> <h3>Step 1: Access Cloudflare Tunnels</h3>
<ol> <ol>
<li> <li>
Go to the <a href="https://dash.cloudflare.com/" target="_blank">Dashboard</a> and log in to your Go to the
Cloudflare account. <a href="https://dash.cloudflare.com/" target="_blank"
>Dashboard</a
>
and log in to your Cloudflare account.
</li>
<li>
Navigate to the <strong>Zero Trust</strong> page using the
navigation bar on the left.
</li>
<li>
Sign up for Cloudflare Zero Trust with the Free Plan if you have
not already.
</li>
<li>
Once you are in the dashboard, navigate to
<strong>Networks</strong> > <strong>Tunnels</strong>.
</li> </li>
<li>Navigate to the <strong>Zero Trust</strong> page using the navigation bar on the left.</li>
<li>Sign up for Cloudflare Zero Trust with the Free Plan if you have not already.</li>
<li>Once you are in the dashboard, navigate to <strong>Networks</strong> > <strong>Tunnels</strong>.</li>
</ol> </ol>
<h3>Step 2: Create a Tunnel</h3> <h3>Step 2: Create a Tunnel</h3>
<ol> <ol>
<li>Click on <strong>Create a tunnel</strong>, and select <strong>Cloudflared</strong>.</li> <li>
Click on <strong>Create a tunnel</strong>, and select
<strong>Cloudflared</strong>.
</li>
<li>Click <strong>Next</strong>.</li> <li>Click <strong>Next</strong>.</li>
<li>Enter a name for the tunnel.</li> <li>Enter a name for the tunnel.</li>
<li> <li>
Follow the instructions provided on the site to install the tunnel.<br />You should do these instructions Follow the instructions provided on the site to install the
on the computer that your Eaglercraft server is running on. tunnel.<br />You should do these instructions on the computer that
your Eaglercraft server is running on.
</li> </li>
</ol> </ol>
<h3>Step 3: Configure Domain/Subdomain</h3> <h3>Step 3: Configure Domain/Subdomain</h3>
<ol> <ol>
<li>Select the domain or subdomain you want to use for your server.</li> <li>
Select the domain or subdomain you want to use for your server.
</li>
<li>Select <strong>HTTP</strong> for the type.</li> <li>Select <strong>HTTP</strong> for the type.</li>
<li>Enter <code>localhost:8081</code> (or whatever port your server is running on) as the URL.</li> <li>
Enter <code>localhost:8081</code> (or whatever port your server is
running on) as the URL.
</li>
</ol> </ol>
<h3>Step 4: Activate Tunnel and Proxy</h3> <h3>Step 4: Activate Tunnel and Proxy</h3>
<ol> <ol>
<li>Start your Eaglercraft server.</li> <li>Start your Eaglercraft server.</li>
<li>In Eaglercraft, connect to the domain or subdomain you set up.</li>
<li> <li>
You're all set! Players can now join your server using the domain that you linked your tunnel to.<br />Example: In Eaglercraft, connect to the domain or subdomain you set up.
</li>
<li>
You're all set! Players can now join your server using the domain
that you linked your tunnel to.<br />Example:
<code>wss://example.com</code> <code>wss://example.com</code>
</li> </li>
</ol> </ol>

View File

@ -4,9 +4,17 @@
<meta charset="UTF-8" /> <meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" /> <meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>MineXLauncher</title> <title>MineXLauncher</title>
<link rel="icon" type="image/webp" href="/resources/images/icons/favicon.webp" /> <link
rel="icon"
type="image/webp"
href="/resources/images/icons/favicon.webp"
/>
<link rel="stylesheet" href="/resources/styles/themes/default.css" /> <link rel="stylesheet" href="/resources/styles/themes/default.css" />
<link rel="stylesheet" id="theme" onload="document.documentElement.style.display = ''" /> <link
rel="stylesheet"
id="theme"
onload="document.documentElement.style.display = ''"
/>
<script src="/resources/scripts/google-tag.js"></script> <script src="/resources/scripts/google-tag.js"></script>
<script src="/resources/scripts/main.js"></script> <script src="/resources/scripts/main.js"></script>
</head> </head>
@ -58,7 +66,9 @@
<ul> <ul>
<li onclick="navigate.home.game()">Game</li> <li onclick="navigate.home.game()">Game</li>
<li onclick="navigate.home.clients()">Clients</li> <li onclick="navigate.home.clients()">Clients</li>
<li class="selected" onclick="navigate.home.archive()">Archive</li> <li class="selected" onclick="navigate.home.archive()">
Archive
</li>
<li onclick="navigate.home.downloads()">Offline Downloads</li> <li onclick="navigate.home.downloads()">Offline Downloads</li>
</ul> </ul>
</div> </div>
@ -67,7 +77,9 @@
<div> <div>
<label for="18-client-version">1.8 versions:</label> <label for="18-client-version">1.8 versions:</label>
<select id="18-client-version"> <select id="18-client-version">
<option disabled selected hidden value="">Select version</option> <option disabled selected hidden value="">
Select version
</option>
<optgroup label="Releases"> <optgroup label="Releases">
<option value="u35">u35</option> <option value="u35">u35</option>
<option value="u34">u34</option> <option value="u34">u34</option>
@ -118,7 +130,9 @@
<div> <div>
<label for="15-client-version">1.5 versions:</label> <label for="15-client-version">1.5 versions:</label>
<select id="15-client-version"> <select id="15-client-version">
<option disabled selected hidden value="">Select version</option> <option disabled selected hidden value="">
Select version
</option>
<optgroup label="Service Packs"> <optgroup label="Service Packs">
<option value="sp1.01">sp1.01</option> <option value="sp1.01">sp1.01</option>
<option value="sp1">sp1</option> <option value="sp1">sp1</option>
@ -198,7 +212,9 @@
<div> <div>
<label for="b13-client-version">Select Beta 1.3 version:</label> <label for="b13-client-version">Select Beta 1.3 version:</label>
<select id="b13-client-version"> <select id="b13-client-version">
<option disabled selected hidden value="">Select version</option> <option disabled selected hidden value="">
Select version
</option>
<optgroup label="Releases"> <optgroup label="Releases">
<option value="23w49a">23w49a</option> <option value="23w49a">23w49a</option>
<option value="22w22b">22w22b</option> <option value="22w22b">22w22b</option>
@ -208,13 +224,19 @@
</select> </select>
<button onclick="game.archive('b1.3')">Open</button> <button onclick="game.archive('b1.3')">Open</button>
</div> </div>
<button onclick="window.open('https://archive.eaglercraft.rip/EaglercraftX_1.8/server/')"> <button
onclick="window.open('https://archive.eaglercraft.rip/EaglercraftX_1.8/server/')"
>
1.8 EaglerXBungee Jar 1.8 EaglerXBungee Jar
</button> </button>
<button onclick="window.open('https://archive.eaglercraft.rip/Eaglercraft_1.5/server/')"> <button
onclick="window.open('https://archive.eaglercraft.rip/Eaglercraft_1.5/server/')"
>
Eaglercraft 1.5 Server Eaglercraft 1.5 Server
</button> </button>
<button onclick="window.open('https://archive.eaglercraft.rip/Eaglercraft_b1.3/server/')"> <button
onclick="window.open('https://archive.eaglercraft.rip/Eaglercraft_b1.3/server/')"
>
Eaglercraft Beta 1.3 Server Eaglercraft Beta 1.3 Server
</button> </button>
</div> </div>
@ -222,7 +244,9 @@
</div> </div>
</div> </div>
<div class="bottom-bar"> <div class="bottom-bar">
<span onclick="window.open('https://discord.gg/VRwbRJjXzt')">Join the MineXLauncher Discord</span> <span onclick="window.open('https://discord.gg/VRwbRJjXzt')"
>Join the MineXLauncher Discord</span
>
<span>© 2024 MineXLauncher. All rights reserved.</span> <span>© 2024 MineXLauncher. All rights reserved.</span>
</div> </div>
</div> </div>

View File

@ -4,9 +4,17 @@
<meta charset="UTF-8" /> <meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" /> <meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>MineXLauncher</title> <title>MineXLauncher</title>
<link rel="icon" type="image/webp" href="/resources/images/icons/favicon.webp" /> <link
rel="icon"
type="image/webp"
href="/resources/images/icons/favicon.webp"
/>
<link rel="stylesheet" href="/resources/styles/themes/default.css" /> <link rel="stylesheet" href="/resources/styles/themes/default.css" />
<link rel="stylesheet" id="theme" onload="document.documentElement.style.display = ''" /> <link
rel="stylesheet"
id="theme"
onload="document.documentElement.style.display = ''"
/>
<script src="/resources/scripts/google-tag.js"></script> <script src="/resources/scripts/google-tag.js"></script>
<script src="/resources/scripts/main.js"></script> <script src="/resources/scripts/main.js"></script>
</head> </head>
@ -57,35 +65,52 @@
<div class="top-nav"> <div class="top-nav">
<ul> <ul>
<li onclick="navigate.home.game()">Game</li> <li onclick="navigate.home.game()">Game</li>
<li class="selected" onclick="navigate.home.clients()">Clients</li> <li class="selected" onclick="navigate.home.clients()">
Clients
</li>
<li onclick="navigate.home.archive()">Archive</li> <li onclick="navigate.home.archive()">Archive</li>
<li onclick="navigate.home.downloads()">Offline Downloads</li> <li onclick="navigate.home.downloads()">Offline Downloads</li>
</ul> </ul>
</div> </div>
<div class="main-content"> <div class="main-content">
<img class="cover-image" src="/resources/images/covers/clients.webp" /> <img
class="cover-image"
src="/resources/images/covers/clients.webp"
/>
</div> </div>
<div class="installations"> <div class="installations">
<div> <div>
<span class="selector" onclick="versionSelector.toggle()">Select a client</span> <span class="selector" onclick="versionSelector.toggle()"
>Select a client</span
>
<div class="options"> <div class="options">
<div onclick="game.select('/game/web/clients/eaglerforge/', 'EaglerForge')"> <div
onclick="game.select('/game/web/clients/eaglerforge/', 'EaglerForge')"
>
<img src="/resources/images/icons/clients/eaglerforge.webp" /> <img src="/resources/images/icons/clients/eaglerforge.webp" />
<span>EaglerForge</span> <span>EaglerForge</span>
</div> </div>
<div onclick="game.select('/game/web/clients/resent/', 'Resent Client')"> <div
onclick="game.select('/game/web/clients/resent/', 'Resent Client')"
>
<img src="/resources/images/icons/clients/resent.webp" /> <img src="/resources/images/icons/clients/resent.webp" />
<span>Resent Client</span> <span>Resent Client</span>
</div> </div>
<div onclick="game.select('/game/web/clients/shadow/', 'Shadow Client')"> <div
onclick="game.select('/game/web/clients/shadow/', 'Shadow Client')"
>
<img src="/resources/images/icons/clients/shadow.webp" /> <img src="/resources/images/icons/clients/shadow.webp" />
<span>Shadow Client</span> <span>Shadow Client</span>
</div> </div>
<div onclick="game.select('/game/web/clients/astra/', 'Astra Client')"> <div
onclick="game.select('/game/web/clients/astra/', 'Astra Client')"
>
<img src="/resources/images/icons/clients/astra.webp" /> <img src="/resources/images/icons/clients/astra.webp" />
<span>Astra Client</span> <span>Astra Client</span>
</div> </div>
<div onclick="game.select('/game/web/clients/starlike/', 'Starlike Client')"> <div
onclick="game.select('/game/web/clients/starlike/', 'Starlike Client')"
>
<img src="/resources/images/icons/clients/starlike.webp" /> <img src="/resources/images/icons/clients/starlike.webp" />
<span>Starlike Client</span> <span>Starlike Client</span>
</div> </div>
@ -96,7 +121,9 @@
</div> </div>
</div> </div>
<div class="bottom-bar"> <div class="bottom-bar">
<span onclick="window.open('https://discord.gg/VRwbRJjXzt')">Join the MineXLauncher Discord</span> <span onclick="window.open('https://discord.gg/VRwbRJjXzt')"
>Join the MineXLauncher Discord</span
>
<span>© 2024 MineXLauncher. All rights reserved.</span> <span>© 2024 MineXLauncher. All rights reserved.</span>
</div> </div>
</div> </div>

View File

@ -4,9 +4,17 @@
<meta charset="UTF-8" /> <meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" /> <meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>MineXLauncher</title> <title>MineXLauncher</title>
<link rel="icon" type="image/webp" href="/resources/images/icons/favicon.webp" /> <link
rel="icon"
type="image/webp"
href="/resources/images/icons/favicon.webp"
/>
<link rel="stylesheet" href="/resources/styles/themes/default.css" /> <link rel="stylesheet" href="/resources/styles/themes/default.css" />
<link rel="stylesheet" id="theme" onload="document.documentElement.style.display = ''" /> <link
rel="stylesheet"
id="theme"
onload="document.documentElement.style.display = ''"
/>
<script src="/resources/scripts/google-tag.js"></script> <script src="/resources/scripts/google-tag.js"></script>
<script src="/resources/scripts/main.js"></script> <script src="/resources/scripts/main.js"></script>
</head> </head>
@ -59,32 +67,75 @@
<li onclick="navigate.home.game()">Game</li> <li onclick="navigate.home.game()">Game</li>
<li onclick="navigate.home.clients()">Clients</li> <li onclick="navigate.home.clients()">Clients</li>
<li onclick="navigate.home.archive()">Archive</li> <li onclick="navigate.home.archive()">Archive</li>
<li class="selected" onclick="navigate.home.downloads()">Offline Downloads</li> <li class="selected" onclick="navigate.home.downloads()">
Offline Downloads
</li>
</ul> </ul>
</div> </div>
<div class="main-content"> <div class="main-content">
<div class="downloads"> <div class="downloads">
<h3>Downloads:</h3> <h3>Downloads:</h3>
<a href="/game/offline/main/EaglercraftL_1.9.html" download>1.9.4</a> <span
<a href="/game/offline/main/EaglercraftX_1.8.html" download>1.8.8</a> onclick="downloadFile('/game/offline/main/EaglercraftL_1.9.html', 'EaglercraftL_1.9.html')"
<a href="/game/offline/main/Eaglercraft_1.5.html" download>1.5.2</a> >1.9.4</span
<a href="/game/offline/main/Eaglercraft_1.2.5.html" download>1.2.5</a> >
<a href="/game/offline/main/Eaglercraft_b1.7.3.html" download>Beta 1.7.3</a> <span
<a href="/game/offline/main/Eaglercraft_b1.3.html" download>Beta 1.3</a> onclick="downloadFile('/game/offline/main/EaglercraftX_1.8.html', 'EaglercraftX_1.8.html')"
<a href="/game/offline/main/Eaglercraft_a1.2.6.html" download>Alpha 1.2.6</a> >1.8.8</span
<a href="/game/offline/main/Eaglercraft_Indev.html" download>Indev</a> >
<span
onclick="downloadFile('/game/offline/main/Eaglercraft_1.5.html', 'Eaglercraft_1.5.html')"
>1.5.2</span
>
<span
onclick="downloadFile('/game/offline/main/Eaglercraft_1.2.5.html', 'Eaglercraft_1.2.5.html')"
>1.2.5</span
>
<span
onclick="downloadFile('/game/offline/main/Eaglercraft_b1.7.3.html', 'Eaglercraft_b1.7.3.html')"
>Beta 1.7.3</span
>
<span
onclick="downloadFile('/game/offline/main/Eaglercraft_b1.3.html', 'Eaglercraft_b1.3.html')"
>Beta 1.3</span
>
<span
onclick="downloadFile('/game/offline/main/Eaglercraft_a1.2.6.html', 'Eaglercraft_a1.2.6.html')"
>Alpha 1.2.6</span
>
<span
onclick="downloadFile('/game/offline/main/Eaglercraft_Indev.html', 'Eaglercraft_Indev.html')"
>Indev</span
>
<br /><br /> <br /><br />
<a href="/game/offline/clients/EaglerForge.html" download>EaglerForge</a> <span
<a href="/game/offline/clients/Resent_Client.html" download>Resent Client</a> onclick="downloadFile('/game/offline/clients/EaglerForge.html', 'EaglerForge.html')"
<a href="/game/offline/clients/Shadow_Client.html" download>Shadow Client</a> >EaglerForge</span
<a href="/game/offline/clients/Astra_Client.html" download>Astra Client</a> >
<a href="/game/offline/clients/Starlike_Client.html" download>Starlike Client</a> <span
onclick="downloadFile('/game/offline/clients/Resent_Client.html', 'Resent_Client.html')"
>Resent Client</span
>
<span
onclick="downloadFile('/game/offline/clients/Shadow_Client.html', 'Shadow_Client.html')"
>Shadow Client</span
>
<span
onclick="downloadFile('/game/offline/clients/Astra_Client.html', 'Astra_Client.html')"
>Astra Client</span
>
<span
onclick="downloadFile('/game/offline/clients/Starlike_Client.html', 'Starlike_Client.html')"
>Starlike Client</span
>
</div> </div>
</div> </div>
</div> </div>
</div> </div>
<div class="bottom-bar"> <div class="bottom-bar">
<span onclick="window.open('https://discord.gg/VRwbRJjXzt')">Join the MineXLauncher Discord</span> <span onclick="window.open('https://discord.gg/VRwbRJjXzt')"
>Join the MineXLauncher Discord</span
>
<span>© 2024 MineXLauncher. All rights reserved.</span> <span>© 2024 MineXLauncher. All rights reserved.</span>
</div> </div>
</div> </div>

View File

@ -4,9 +4,17 @@
<meta charset="UTF-8" /> <meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" /> <meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>MineXLauncher</title> <title>MineXLauncher</title>
<link rel="icon" type="image/webp" href="/resources/images/icons/favicon.webp" /> <link
rel="icon"
type="image/webp"
href="/resources/images/icons/favicon.webp"
/>
<link rel="stylesheet" href="/resources/styles/themes/default.css" /> <link rel="stylesheet" href="/resources/styles/themes/default.css" />
<link rel="stylesheet" id="theme" onload="document.documentElement.style.display = ''" /> <link
rel="stylesheet"
id="theme"
onload="document.documentElement.style.display = ''"
/>
<script src="/resources/scripts/google-tag.js"></script> <script src="/resources/scripts/google-tag.js"></script>
<script src="/resources/scripts/main.js"></script> <script src="/resources/scripts/main.js"></script>
</head> </head>
@ -63,11 +71,16 @@
</ul> </ul>
</div> </div>
<div class="main-content"> <div class="main-content">
<img class="cover-image" src="/resources/images/covers/minecraft.webp" /> <img
class="cover-image"
src="/resources/images/covers/minecraft.webp"
/>
</div> </div>
<div class="installations"> <div class="installations">
<div> <div>
<span class="selector" onclick="versionSelector.toggle()">Select a version</span> <span class="selector" onclick="versionSelector.toggle()"
>Select a version</span
>
<div class="options"> <div class="options">
<div onclick="game.select('/game/web/main/1.9.4/', '1.9.4')"> <div onclick="game.select('/game/web/main/1.9.4/', '1.9.4')">
<img src="/resources/images/icons/clients/all.webp" /> <img src="/resources/images/icons/clients/all.webp" />
@ -81,11 +94,15 @@
<img src="/resources/images/icons/clients/all.webp" /> <img src="/resources/images/icons/clients/all.webp" />
<span>1.5.2</span> <span>1.5.2</span>
</div> </div>
<div onclick="game.select('/game/offline/main/Eaglercraft_1.2.5.html', '1.2.5')"> <div
onclick="game.select('/game/offline/main/Eaglercraft_1.2.5.html', '1.2.5')"
>
<img src="/resources/images/icons/clients/all.webp" /> <img src="/resources/images/icons/clients/all.webp" />
<span>1.2.5</span> <span>1.2.5</span>
</div> </div>
<div onclick="game.select('/game/web/main/b1.7.3/', 'Beta 1.7.3')"> <div
onclick="game.select('/game/web/main/b1.7.3/', 'Beta 1.7.3')"
>
<img src="/resources/images/icons/clients/all.webp" /> <img src="/resources/images/icons/clients/all.webp" />
<span>Beta 1.7.3</span> <span>Beta 1.7.3</span>
</div> </div>
@ -105,7 +122,9 @@
<img src="/resources/images/icons/clients/all.webp" /> <img src="/resources/images/icons/clients/all.webp" />
<span>Indev</span> <span>Indev</span>
</div> </div>
<div onclick="game.select('/game/web/main/classic/', 'Classic')"> <div
onclick="game.select('/game/web/main/classic/', 'Classic')"
>
<img src="/resources/images/icons/clients/all.webp" /> <img src="/resources/images/icons/clients/all.webp" />
<span>Classic</span> <span>Classic</span>
</div> </div>
@ -116,7 +135,9 @@
</div> </div>
</div> </div>
<div class="bottom-bar"> <div class="bottom-bar">
<span onclick="window.open('https://discord.gg/VRwbRJjXzt')">Join the MineXLauncher Discord</span> <span onclick="window.open('https://discord.gg/VRwbRJjXzt')"
>Join the MineXLauncher Discord</span
>
<span>© 2024 MineXLauncher. All rights reserved.</span> <span>© 2024 MineXLauncher. All rights reserved.</span>
</div> </div>
</div> </div>

View File

@ -4,7 +4,11 @@
<meta charset="UTF-8" /> <meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" /> <meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>MineXLauncher</title> <title>MineXLauncher</title>
<link rel="icon" type="image/webp" href="/resources/images/icons/favicon.webp" /> <link
rel="icon"
type="image/webp"
href="/resources/images/icons/favicon.webp"
/>
<link rel="stylesheet" href="/resources/styles/index.css" /> <link rel="stylesheet" href="/resources/styles/index.css" />
<link rel="manifest" href="/manifest.json" /> <link rel="manifest" href="/manifest.json" />
<link rel="canonical" href="https://launcher.orionzleon.me/" /> <link rel="canonical" href="https://launcher.orionzleon.me/" />
@ -19,7 +23,10 @@
/> />
<meta property="og:title" content="MineXLauncher" /> <meta property="og:title" content="MineXLauncher" />
<meta property="og:type" content="website" /> <meta property="og:type" content="website" />
<meta property="og:image" content="/resources/images/icons/minexlauncher.webp" /> <meta
property="og:image"
content="/resources/images/icons/minexlauncher.webp"
/>
<meta property="og:url" content="https://launcher.orionzleon.me/" /> <meta property="og:url" content="https://launcher.orionzleon.me/" />
<meta <meta
property="og:description" property="og:description"

View File

@ -4,9 +4,17 @@
<meta charset="UTF-8" /> <meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" /> <meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>MineXLauncher</title> <title>MineXLauncher</title>
<link rel="icon" type="image/webp" href="/resources/images/icons/favicon.webp" /> <link
rel="icon"
type="image/webp"
href="/resources/images/icons/favicon.webp"
/>
<link rel="stylesheet" href="/resources/styles/themes/default.css" /> <link rel="stylesheet" href="/resources/styles/themes/default.css" />
<link rel="stylesheet" id="theme" onload="document.documentElement.style.display = ''" /> <link
rel="stylesheet"
id="theme"
onload="document.documentElement.style.display = ''"
/>
<script src="/resources/scripts/google-tag.js"></script> <script src="/resources/scripts/google-tag.js"></script>
<script src="/resources/scripts/main.js"></script> <script src="/resources/scripts/main.js"></script>
</head> </head>
@ -26,9 +34,14 @@
<div class="main-panel"> <div class="main-panel">
<span class="top-title">Mobile</span> <span class="top-title">Mobile</span>
<div class="main-content"> <div class="main-content">
<img class="cover-image" src="/resources/images/covers/minecraft.webp" /> <img
class="cover-image"
src="/resources/images/covers/minecraft.webp"
/>
<div class="installations"> <div class="installations">
<button onclick="game.play('/game/web/main/1.8.8/?mobile=true')">Play</button> <button onclick="game.play('/game/web/main/1.8.8/?mobile=true')">
Play
</button>
<a <a
class="play-button" class="play-button"
style="text-decoration: none" style="text-decoration: none"
@ -41,7 +54,9 @@
</div> </div>
</div> </div>
<div class="bottom-bar"> <div class="bottom-bar">
<span onclick="window.open('https://discord.gg/VRwbRJjXzt')">Join the MineXLauncher Discord</span> <span onclick="window.open('https://discord.gg/VRwbRJjXzt')"
>Join the MineXLauncher Discord</span
>
<span>© 2024 MineXLauncher. All rights reserved.</span> <span>© 2024 MineXLauncher. All rights reserved.</span>
</div> </div>
</div> </div>

View File

@ -4,9 +4,17 @@
<meta charset="UTF-8" /> <meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" /> <meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>MineXLauncher</title> <title>MineXLauncher</title>
<link rel="icon" type="image/webp" href="/resources/images/icons/favicon.webp" /> <link
rel="icon"
type="image/webp"
href="/resources/images/icons/favicon.webp"
/>
<link rel="stylesheet" href="/resources/styles/themes/default.css" /> <link rel="stylesheet" href="/resources/styles/themes/default.css" />
<link rel="stylesheet" id="theme" onload="document.documentElement.style.display = ''" /> <link
rel="stylesheet"
id="theme"
onload="document.documentElement.style.display = ''"
/>
<script src="/resources/scripts/google-tag.js"></script> <script src="/resources/scripts/google-tag.js"></script>
<script src="/resources/scripts/main.js"></script> <script src="/resources/scripts/main.js"></script>
</head> </head>
@ -64,12 +72,16 @@
<div class="mod-list"></div> <div class="mod-list"></div>
</div> </div>
<div class="installations"> <div class="installations">
<button onclick="game.play('/game/web/clients/eaglerforge/')">Play</button> <button onclick="game.play('/game/web/clients/eaglerforge/')">
Play
</button>
</div> </div>
</div> </div>
</div> </div>
<div class="bottom-bar"> <div class="bottom-bar">
<span onclick="window.open('https://discord.gg/VRwbRJjXzt')">Join the MineXLauncher Discord</span> <span onclick="window.open('https://discord.gg/VRwbRJjXzt')"
>Join the MineXLauncher Discord</span
>
<span>© 2024 MineXLauncher. All rights reserved.</span> <span>© 2024 MineXLauncher. All rights reserved.</span>
</div> </div>
</div> </div>

View File

@ -4,9 +4,17 @@
<meta charset="UTF-8" /> <meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" /> <meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>MineXLauncher</title> <title>MineXLauncher</title>
<link rel="icon" type="image/webp" href="/resources/images/icons/favicon.webp" /> <link
rel="icon"
type="image/webp"
href="/resources/images/icons/favicon.webp"
/>
<link rel="stylesheet" href="/resources/styles/themes/default.css" /> <link rel="stylesheet" href="/resources/styles/themes/default.css" />
<link rel="stylesheet" id="theme" onload="document.documentElement.style.display = ''" /> <link
rel="stylesheet"
id="theme"
onload="document.documentElement.style.display = ''"
/>
<script src="/resources/scripts/google-tag.js"></script> <script src="/resources/scripts/google-tag.js"></script>
<script src="/resources/scripts/main.js"></script> <script src="/resources/scripts/main.js"></script>
</head> </head>
@ -57,7 +65,9 @@
<div class="top-nav"> <div class="top-nav">
<ul> <ul>
<li onclick="navigate.mods.mods()">Mods</li> <li onclick="navigate.mods.mods()">Mods</li>
<li class="selected" onclick="navigate.mods.resourcepacks()">Resource Packs</li> <li class="selected" onclick="navigate.mods.resourcepacks()">
Resource Packs
</li>
</ul> </ul>
</div> </div>
<div class="main-content"> <div class="main-content">
@ -66,7 +76,9 @@
</div> </div>
</div> </div>
<div class="bottom-bar"> <div class="bottom-bar">
<span onclick="window.open('https://discord.gg/VRwbRJjXzt')">Join the MineXLauncher Discord</span> <span onclick="window.open('https://discord.gg/VRwbRJjXzt')"
>Join the MineXLauncher Discord</span
>
<span>© 2024 MineXLauncher. All rights reserved.</span> <span>© 2024 MineXLauncher. All rights reserved.</span>
</div> </div>
</div> </div>

View File

@ -4,9 +4,17 @@
<meta charset="UTF-8" /> <meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" /> <meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>MineXLauncher</title> <title>MineXLauncher</title>
<link rel="icon" type="image/webp" href="/resources/images/icons/favicon.webp" /> <link
rel="icon"
type="image/webp"
href="/resources/images/icons/favicon.webp"
/>
<link rel="stylesheet" href="/resources/styles/themes/default.css" /> <link rel="stylesheet" href="/resources/styles/themes/default.css" />
<link rel="stylesheet" id="theme" onload="document.documentElement.style.display = ''" /> <link
rel="stylesheet"
id="theme"
onload="document.documentElement.style.display = ''"
/>
<meta name="theme-color" content="#333" /> <meta name="theme-color" content="#333" />
<script src="/resources/scripts/google-tag.js"></script> <script src="/resources/scripts/google-tag.js"></script>
<script src="/resources/scripts/main.js"></script> <script src="/resources/scripts/main.js"></script>
@ -35,7 +43,9 @@
</div> </div>
</div> </div>
<div class="bottom-bar"> <div class="bottom-bar">
<span onclick="window.open('https://discord.gg/VRwbRJjXzt')">Join the MineXLauncher Discord</span> <span onclick="window.open('https://discord.gg/VRwbRJjXzt')"
>Join the MineXLauncher Discord</span
>
<span>© 2024 MineXLauncher. All rights reserved.</span> <span>© 2024 MineXLauncher. All rights reserved.</span>
</div> </div>
</div> </div>

View File

@ -4,9 +4,17 @@
<meta charset="UTF-8" /> <meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" /> <meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>MineXLauncher</title> <title>MineXLauncher</title>
<link rel="icon" type="image/webp" href="/resources/images/icons/favicon.webp" /> <link
rel="icon"
type="image/webp"
href="/resources/images/icons/favicon.webp"
/>
<link rel="stylesheet" href="/resources/styles/themes/default.css" /> <link rel="stylesheet" href="/resources/styles/themes/default.css" />
<link rel="stylesheet" id="theme" onload="document.documentElement.style.display = ''" /> <link
rel="stylesheet"
id="theme"
onload="document.documentElement.style.display = ''"
/>
<meta name="theme-color" content="#333" /> <meta name="theme-color" content="#333" />
<script src="/resources/scripts/google-tag.js"></script> <script src="/resources/scripts/google-tag.js"></script>
<script src="/resources/scripts/main.js"></script> <script src="/resources/scripts/main.js"></script>
@ -31,25 +39,38 @@
</ul> </ul>
</div> </div>
<div class="main-content"> <div class="main-content">
<img class="cover-image" src="/resources/images/covers/minecraft.webp" /> <img
class="cover-image"
src="/resources/images/covers/minecraft.webp"
/>
</div> </div>
<div class="installations"> <div class="installations">
<div> <div>
<span class="selector" onclick="versionSelector.toggle()">Select a version</span> <span class="selector" onclick="versionSelector.toggle()"
>Select a version</span
>
<div class="options"> <div class="options">
<div onclick="game.select('/game/offline/main/EaglercraftL_1.9.html', '1.9.4')"> <div
onclick="game.select('/game/offline/main/EaglercraftL_1.9.html', '1.9.4')"
>
<img src="/resources/images/icons/clients/all.webp" /> <img src="/resources/images/icons/clients/all.webp" />
<span>1.9.4</span> <span>1.9.4</span>
</div> </div>
<div onclick="game.select('/game/offline/main/EaglercraftX_1.8.html', '1.8.8')"> <div
onclick="game.select('/game/offline/main/EaglercraftX_1.8.html', '1.8.8')"
>
<img src="/resources/images/icons/clients/all.webp" /> <img src="/resources/images/icons/clients/all.webp" />
<span>1.8.8</span> <span>1.8.8</span>
</div> </div>
<div onclick="game.select('/game/offline/main/Eaglercraft_1.5.html', '1.5.2')"> <div
onclick="game.select('/game/offline/main/Eaglercraft_1.5.html', '1.5.2')"
>
<img src="/resources/images/icons/clients/all.webp" /> <img src="/resources/images/icons/clients/all.webp" />
<span>1.5.2</span> <span>1.5.2</span>
</div> </div>
<div onclick="game.select('/game/offline/main/Eaglercraft_1.2.5.html', '1.2.5')"> <div
onclick="game.select('/game/offline/main/Eaglercraft_1.2.5.html', '1.2.5')"
>
<img src="/resources/images/icons/clients/all.webp" /> <img src="/resources/images/icons/clients/all.webp" />
<span>1.2.5</span> <span>1.2.5</span>
</div> </div>
@ -67,11 +88,15 @@
<img src="/resources/images/icons/clients/all.webp" /> <img src="/resources/images/icons/clients/all.webp" />
<span>Beta 1.3</span> <span>Beta 1.3</span>
</div> </div>
<div onclick="game.select('/game/offline/main/Eaglercraft_a1.2.6.html', 'Alpha')"> <div
onclick="game.select('/game/offline/main/Eaglercraft_a1.2.6.html', 'Alpha')"
>
<img src="/resources/images/icons/clients/all.webp" /> <img src="/resources/images/icons/clients/all.webp" />
<span>Alpha 1.2.6</span> <span>Alpha 1.2.6</span>
</div> </div>
<div onclick="game.select('/game/offline/main/Eaglercraft_Indev.html', 'Indev')"> <div
onclick="game.select('/game/offline/main/Eaglercraft_Indev.html', 'Indev')"
>
<img src="/resources/images/icons/clients/all.webp" /> <img src="/resources/images/icons/clients/all.webp" />
<span>Indev</span> <span>Indev</span>
</div> </div>
@ -82,7 +107,9 @@
</div> </div>
</div> </div>
<div class="bottom-bar"> <div class="bottom-bar">
<span onclick="window.open('https://discord.gg/VRwbRJjXzt')">Join the MineXLauncher Discord</span> <span onclick="window.open('https://discord.gg/VRwbRJjXzt')"
>Join the MineXLauncher Discord</span
>
<span>© 2024 MineXLauncher. All rights reserved.</span> <span>© 2024 MineXLauncher. All rights reserved.</span>
</div> </div>
</div> </div>

View File

@ -78,7 +78,11 @@
}, },
{ {
"version": "1.2", "version": "1.2",
"changelog": ["Added Eaglercraft 1.2.5", "Added more mods and resource packs", "Fix and optimize mobile site"] "changelog": [
"Added Eaglercraft 1.2.5",
"Added more mods and resource packs",
"Fix and optimize mobile site"
]
}, },
{ {
"version": "1.1", "version": "1.1",

View File

@ -7,11 +7,29 @@ window.addEventListener('load', function () {
localesURI: `${window.location.pathname}/lang/`, localesURI: `${window.location.pathname}/lang/`,
serverWorkerURI: `${window.location.pathname}/worker_bootstrap.js`, serverWorkerURI: `${window.location.pathname}/worker_bootstrap.js`,
worldsFolder: 'MAIN', worldsFolder: 'MAIN',
servers: [{ serverName: 'BrickMC', serverAddress: 'wss://play.brickmc.net', hideAddress: false }], servers: [
{
serverName: 'BrickMC',
serverAddress: 'wss://play.brickmc.net',
hideAddress: false,
},
],
relays: [ relays: [
{ addr: 'wss://relay.deev.is/', name: 'lax1dude relay #1', primary: relayId === 0 }, {
{ addr: 'wss://relay.lax1dude.net/', name: 'lax1dude relay #2', primary: relayId === 1 }, addr: 'wss://relay.deev.is/',
{ addr: 'wss://relay.shhnowisnottheti.me/', name: 'ayunami relay #1', primary: relayId === 2 }, name: 'lax1dude relay #1',
primary: relayId === 0,
},
{
addr: 'wss://relay.lax1dude.net/',
name: 'lax1dude relay #2',
primary: relayId === 1,
},
{
addr: 'wss://relay.shhnowisnottheti.me/',
name: 'ayunami relay #1',
primary: relayId === 2,
},
], ],
mainMenu: { mainMenu: {
splashes: [ splashes: [

View File

@ -12,9 +12,21 @@ window.addEventListener('load', () => {
{ addr: 'wss://play.brickmc.net', name: 'BrickMC' }, { addr: 'wss://play.brickmc.net', name: 'BrickMC' },
], ],
relays: [ relays: [
{ addr: 'wss://relay.deev.is/', comment: 'lax1dude relay #1', primary: relayId === 0 }, {
{ addr: 'wss://relay.lax1dude.net/', comment: 'lax1dude relay #2', primary: relayId === 1 }, addr: 'wss://relay.deev.is/',
{ addr: 'wss://relay.shhnowisnottheti.me/', comment: 'ayunami relay #1', primary: relayId === 2 }, comment: 'lax1dude relay #1',
primary: relayId === 0,
},
{
addr: 'wss://relay.lax1dude.net/',
comment: 'lax1dude relay #2',
primary: relayId === 1,
},
{
addr: 'wss://relay.shhnowisnottheti.me/',
comment: 'ayunami relay #1',
primary: relayId === 2,
},
], ],
}; };

View File

@ -15,7 +15,10 @@ if (new URLSearchParams(window.location.search).get('mobile') === 'true') {
} catch (A) { } catch (A) {
return !1; return !1;
} }
})() || alert('WARNING: This script was created for mobile, and may break functionality in non-mobile browsers!'), })() ||
alert(
'WARNING: This script was created for mobile, and may break functionality in non-mobile browsers!',
),
(clientWindow.crouchLock = !1), (clientWindow.crouchLock = !1),
(clientWindow.sprintLock = !1), (clientWindow.sprintLock = !1),
(clientWindow.keyboardFix = !1), (clientWindow.keyboardFix = !1),
@ -136,7 +139,8 @@ if (new URLSearchParams(window.location.search).get('mobile') === 'true') {
this, this,
A, A,
function (...A) { function (...A) {
if (A[0].isValid || !clientWindow.keyboardFix) return I.apply(this, A); if (A[0].isValid || !clientWindow.keyboardFix)
return I.apply(this, A);
}, },
...g, ...g,
) )
@ -161,12 +165,18 @@ if (new URLSearchParams(window.location.search).get('mobile') === 'true') {
(I.disabled = A), (g.disabled = !A); (I.disabled = A), (g.disabled = !A);
} }
(Event.prototype.preventDefault = function (A) { (Event.prototype.preventDefault = function (A) {
('hiddenInput' != document.activeElement.id || A) && ((this._preventDefault = e), this._preventDefault()); ('hiddenInput' != document.activeElement.id || A) &&
((this._preventDefault = e), this._preventDefault());
}), }),
(clientWindow.fakelock = null), (clientWindow.fakelock = null),
Object.defineProperty(Element.prototype, 'requestPointerLock', { Object.defineProperty(Element.prototype, 'requestPointerLock', {
value: function () { value: function () {
return (clientWindow.fakelock = this), document.dispatchEvent(new Event('pointerlockchange')), d(!0), !0; return (
(clientWindow.fakelock = this),
document.dispatchEvent(new Event('pointerlockchange')),
d(!0),
!0
);
}, },
}), }),
Object.defineProperty(Document.prototype, 'pointerLockElement', { Object.defineProperty(Document.prototype, 'pointerLockElement', {
@ -176,13 +186,22 @@ if (new URLSearchParams(window.location.search).get('mobile') === 'true') {
}), }),
Object.defineProperty(Document.prototype, 'exitPointerLock', { Object.defineProperty(Document.prototype, 'exitPointerLock', {
value: function () { value: function () {
return (clientWindow.fakelock = null), document.dispatchEvent(new Event('pointerlockchange')), d(!1), !0; return (
(clientWindow.fakelock = null),
document.dispatchEvent(new Event('pointerlockchange')),
d(!1),
!0
);
}, },
}), }),
(clientWindow.fakefull = null), (clientWindow.fakefull = null),
Object.defineProperty(Element.prototype, 'requestFullscreen', { Object.defineProperty(Element.prototype, 'requestFullscreen', {
value: function () { value: function () {
return (clientWindow.fakefull = this), document.dispatchEvent(new Event('fullscreenchange')), !0; return (
(clientWindow.fakefull = this),
document.dispatchEvent(new Event('fullscreenchange')),
!0
);
}, },
}), }),
Object.defineProperty(document, 'fullscreenElement', { Object.defineProperty(document, 'fullscreenElement', {
@ -192,7 +211,11 @@ if (new URLSearchParams(window.location.search).get('mobile') === 'true') {
}), }),
Object.defineProperty(Document.prototype, 'exitFullscreen', { Object.defineProperty(Document.prototype, 'exitFullscreen', {
value: function () { value: function () {
return (clientWindow.fakefull = null), document.dispatchEvent(new Event('fullscreenchange')), !0; return (
(clientWindow.fakefull = null),
document.dispatchEvent(new Event('fullscreenchange')),
!0
);
}, },
}); });
const c = document.createElement; const c = document.createElement;
@ -202,7 +225,9 @@ if (new URLSearchParams(window.location.search).get('mobile') === 'true') {
return ( return (
'input' != A || 'input' != A ||
I || I ||
(document.querySelectorAll('#fileUpload').forEach((A) => A.parentNode.removeChild(A)), (document
.querySelectorAll('#fileUpload')
.forEach((A) => A.parentNode.removeChild(A)),
(g.id = 'fileUpload'), (g.id = 'fileUpload'),
g.addEventListener( g.addEventListener(
'change', 'change',
@ -225,7 +250,9 @@ if (new URLSearchParams(window.location.search).get('mobile') === 'true') {
); );
}; };
let n = document.createElement('style'); let n = document.createElement('style');
(n.id = 'inGameStyle'), (n.textContent = '\n.inGame {\ndisplay: none;\n}'), document.documentElement.appendChild(n); (n.id = 'inGameStyle'),
(n.textContent = '\n.inGame {\ndisplay: none;\n}'),
document.documentElement.appendChild(n);
let o = document.createElement('style'); let o = document.createElement('style');
function Z(A, I, g) { function Z(A, I, g) {
var i = document.createElement(g ?? 'button', !0); var i = document.createElement(g ?? 'button', !0);
@ -254,7 +281,8 @@ if (new URLSearchParams(window.location.search).get('mobile') === 'true') {
new Promise((A) => { new Promise((A) => {
if (document.querySelector(l)) return A(document.querySelector(l)); if (document.querySelector(l)) return A(document.querySelector(l));
const I = new MutationObserver((g) => { const I = new MutationObserver((g) => {
document.querySelector(l) && (I.disconnect(), A(document.querySelector(l))); document.querySelector(l) &&
(I.disconnect(), A(document.querySelector(l)));
}); });
I.observe(document.documentElement, { childList: !0, subtree: !0 }); I.observe(document.documentElement, { childList: !0, subtree: !0 });
})).then(() => { })).then(() => {
@ -265,9 +293,14 @@ if (new URLSearchParams(window.location.search).get('mobile') === 'true') {
function (g) { function (g) {
g.preventDefault(); g.preventDefault();
const e = g.targetTouches[0]; const e = g.targetTouches[0];
A || ((A = e.pageX), (I = e.pageY)), (g.movementX = e.pageX - A), (g.movementY = e.pageY - I); A || ((A = e.pageX), (I = e.pageY)),
(g.movementX = e.pageX - A),
(g.movementY = e.pageY - I);
var C = clientWindow.fakelock var C = clientWindow.fakelock
? new MouseEvent('mousemove', { movementX: g.movementX, movementY: g.movementY }) ? new MouseEvent('mousemove', {
movementX: g.movementX,
movementY: g.movementY,
})
: new WheelEvent('wheel', { wheelDeltaY: g.movementY }); : new WheelEvent('wheel', { wheelDeltaY: g.movementY });
i.dispatchEvent(C), (A = e.pageX), (I = e.pageY); i.dispatchEvent(C), (A = e.pageX), (I = e.pageY);
}, },
@ -282,15 +315,20 @@ if (new URLSearchParams(window.location.search).get('mobile') === 'true') {
), ),
d(null != clientWindow.fakelock); d(null != clientWindow.fakelock);
let e = Z('strafeRightButton', 'inGame', 'div'); let e = Z('strafeRightButton', 'inGame', 'div');
(e.style.cssText = 'left:20vh;bottom:20vh;'), document.body.appendChild(e); (e.style.cssText = 'left:20vh;bottom:20vh;'),
document.body.appendChild(e);
let c = Z('strafeLeftButton', 'inGame', 'div'); let c = Z('strafeLeftButton', 'inGame', 'div');
(c.style.cssText = 'left:0vh;bottom:20vh;'), document.body.appendChild(c); (c.style.cssText = 'left:0vh;bottom:20vh;'),
document.body.appendChild(c);
let n = Z('forwardButton', 'inGame', 'div'); let n = Z('forwardButton', 'inGame', 'div');
(n.style.cssText = 'left:10vh;bottom:20vh;'), (n.style.cssText = 'left:10vh;bottom:20vh;'),
n.addEventListener( n.addEventListener(
'touchstart', 'touchstart',
function (A) { function (A) {
C('w', 'keydown'), e.classList.remove('hide'), c.classList.remove('hide'), n.classList.add('active'); C('w', 'keydown'),
e.classList.remove('hide'),
c.classList.remove('hide'),
n.classList.add('active');
}, },
!1, !1,
), ),
@ -302,10 +340,17 @@ if (new URLSearchParams(window.location.search).get('mobile') === 'true') {
g || (g = I.pageX); g || (g = I.pageX);
let i = I.pageX - g; let i = I.pageX - g;
10 * i > clientWindow.innerHeight 10 * i > clientWindow.innerHeight
? (e.classList.add('active'), c.classList.remove('active'), C('d', 'keydown'), C('a', 'keyup')) ? (e.classList.add('active'),
c.classList.remove('active'),
C('d', 'keydown'),
C('a', 'keyup'))
: 10 * i < 0 - clientWindow.innerHeight : 10 * i < 0 - clientWindow.innerHeight
? (c.classList.add('active'), e.classList.remove('active'), C('a', 'keydown'), C('d', 'keyup')) ? (c.classList.add('active'),
: (e.classList.remove('active'), c.classList.remove('active')); e.classList.remove('active'),
C('a', 'keydown'),
C('d', 'keyup'))
: (e.classList.remove('active'),
c.classList.remove('active'));
}, },
!1, !1,
), ),
@ -403,7 +448,8 @@ if (new URLSearchParams(window.location.search).get('mobile') === 'true') {
C('shift', 'keydown'), C('shift', 'keydown'),
(clientWindow.crouchLock = !!clientWindow.crouchLock && null), (clientWindow.crouchLock = !!clientWindow.crouchLock && null),
(clientWindow.crouchTimer = setTimeout(function (A) { (clientWindow.crouchTimer = setTimeout(function (A) {
(clientWindow.crouchLock = null != clientWindow.crouchLock), M.classList.toggle('active'); (clientWindow.crouchLock = null != clientWindow.crouchLock),
M.classList.toggle('active');
}, 1e3)); }, 1e3));
}, },
!1, !1,
@ -412,7 +458,9 @@ if (new URLSearchParams(window.location.search).get('mobile') === 'true') {
'touchend', 'touchend',
function (A) { function (A) {
clientWindow.crouchLock || clientWindow.crouchLock ||
(C('shift', 'keyup'), M.classList.remove('active'), (clientWindow.crouchLock = !1)), (C('shift', 'keyup'),
M.classList.remove('active'),
(clientWindow.crouchLock = !1)),
clearTimeout(clientWindow.crouchTimer); clearTimeout(clientWindow.crouchTimer);
}, },
!1, !1,
@ -436,7 +484,8 @@ if (new URLSearchParams(window.location.search).get('mobile') === 'true') {
), ),
document.body.appendChild(v); document.body.appendChild(v);
let m = Z('exitButton', 'inMenu'); let m = Z('exitButton', 'inMenu');
(m.style.cssText = 'top: 0vh; margin: auto; left: 0vh; right:8vh; width: 8vh; height: 8vh;'), (m.style.cssText =
'top: 0vh; margin: auto; left: 0vh; right:8vh; width: 8vh; height: 8vh;'),
m.addEventListener( m.addEventListener(
'touchstart', 'touchstart',
function (A) { function (A) {
@ -461,28 +510,45 @@ if (new URLSearchParams(window.location.search).get('mobile') === 'true') {
'beforeinput', 'beforeinput',
function (A) { function (A) {
A.stopImmediatePropagation(), A.preventDefault(!0); A.stopImmediatePropagation(), A.preventDefault(!0);
let I = 'insertLineBreak' == A.inputType ? 'return' : null == A.data ? 'delete' : A.data.slice(-1); let I =
'insertLineBreak' == A.inputType
? 'return'
: null == A.data
? 'delete'
: A.data.slice(-1);
if ( if (
(clientWindow.lastKey || (clientWindow.lastKey ||
(clientWindow.console.warn('Enabling blocking duplicate key events. Some functionality may be lost.'), (clientWindow.console.warn(
'Enabling blocking duplicate key events. Some functionality may be lost.',
),
(clientWindow.inputFix = !0)), (clientWindow.inputFix = !0)),
clientWindow.keyboardFix) clientWindow.keyboardFix)
) )
if ('insertLineBreak' == A.inputType) C('enter', 'keydown'), C('enter', 'keyup'); if ('insertLineBreak' == A.inputType)
C('enter', 'keydown'), C('enter', 'keyup');
else { else {
const g = A.inputType.slice(0, 1); const g = A.inputType.slice(0, 1);
if ('i' == g && A.data) { if ('i' == g && A.data) {
if (clientWindow.lastKey == I && clientWindow.blockNextInput && clientWindow.inputFix) if (
clientWindow.lastKey == I &&
clientWindow.blockNextInput &&
clientWindow.inputFix
)
clientWindow.blockNextInput = !1; clientWindow.blockNextInput = !1;
else { else {
I.toLowerCase() != I I.toLowerCase() != I
? (C('shift', 'keydown'), C(I, 'keydown'), C(I, 'keyup'), C('shift', 'keyup')) ? (C('shift', 'keydown'),
C(I, 'keydown'),
C(I, 'keyup'),
C('shift', 'keyup'))
: (C(I, 'keydown'), C(I, 'keyup')), : (C(I, 'keydown'), C(I, 'keyup')),
(clientWindow.blockNextInput = !0); (clientWindow.blockNextInput = !0);
} }
} else } else
('d' != g && A.data) || ('d' != g && A.data) ||
(C('backspace', 'keydown'), C('backspace', 'keyup'), (clientWindow.blockNextInput = !1)); (C('backspace', 'keydown'),
C('backspace', 'keyup'),
(clientWindow.blockNextInput = !1));
} }
(clientWindow.lastKey = I), (h.value = ' '); (clientWindow.lastKey = I), (h.value = ' ');
}, },
@ -504,7 +570,9 @@ if (new URLSearchParams(window.location.search).get('mobile') === 'true') {
'Switching from keydown to input events due to invalid KeyboardEvent. Some functionality will be lost.', 'Switching from keydown to input events due to invalid KeyboardEvent. Some functionality will be lost.',
), ),
(clientWindow.keyboardFix = !0), (clientWindow.keyboardFix = !0),
clientWindow.lastKey && (C(clientWindow.lastKey, 'keydown'), C(clientWindow.lastKey, 'keyup'))); clientWindow.lastKey &&
(C(clientWindow.lastKey, 'keydown'),
C(clientWindow.lastKey, 'keyup')));
}, },
!1, !1,
), ),
@ -513,7 +581,8 @@ if (new URLSearchParams(window.location.search).get('mobile') === 'true') {
}), }),
document.body.appendChild(h); document.body.appendChild(h);
let a = Z('keyboardButton', 'inMenu'); let a = Z('keyboardButton', 'inMenu');
(a.style.cssText = 'top: 0vh; margin: auto; left: 8vh; right:0vh; width: 8vh; height: 8vh;'), (a.style.cssText =
'top: 0vh; margin: auto; left: 8vh; right:0vh; width: 8vh; height: 8vh;'),
a.addEventListener( a.addEventListener(
'touchstart', 'touchstart',
function (A) { function (A) {
@ -525,7 +594,9 @@ if (new URLSearchParams(window.location.search).get('mobile') === 'true') {
'touchend', 'touchend',
function (A) { function (A) {
A.preventDefault(), A.preventDefault(),
clientWindow.hiddenInputFocused ? h.blur() : (h.select(), (clientWindow.hiddenInputFocused = !0)); clientWindow.hiddenInputFocused
? h.blur()
: (h.select(), (clientWindow.hiddenInputFocused = !0));
}, },
!1, !1,
), ),
@ -626,7 +697,8 @@ if (new URLSearchParams(window.location.search).get('mobile') === 'true') {
C('r', 'keydown'), C('r', 'keydown'),
(clientWindow.sprintLock = !!clientWindow.sprintLock && null), (clientWindow.sprintLock = !!clientWindow.sprintLock && null),
(clientWindow.sprintTimer = setTimeout(function (A) { (clientWindow.sprintTimer = setTimeout(function (A) {
(clientWindow.sprintLock = null != clientWindow.sprintLock), j.classList.toggle('active'); (clientWindow.sprintLock = null != clientWindow.sprintLock),
j.classList.toggle('active');
}, 1e3)); }, 1e3));
}, },
!1, !1,
@ -635,14 +707,17 @@ if (new URLSearchParams(window.location.search).get('mobile') === 'true') {
'touchend', 'touchend',
function (A) { function (A) {
clientWindow.sprintLock || clientWindow.sprintLock ||
(C('r', 'keyup'), j.classList.remove('active'), (clientWindow.sprintLock = !1)), (C('r', 'keyup'),
j.classList.remove('active'),
(clientWindow.sprintLock = !1)),
clearTimeout(clientWindow.sprintTimer); clearTimeout(clientWindow.sprintTimer);
}, },
!1, !1,
), ),
document.body.appendChild(j); document.body.appendChild(j);
let y = Z('pauseButton', 'inGame'); let y = Z('pauseButton', 'inGame');
(y.style.cssText = 'top: 0vh; margin: auto; left: 0vh; right: 32vh; width: 8vh; height: 8vh;'), (y.style.cssText =
'top: 0vh; margin: auto; left: 0vh; right: 32vh; width: 8vh; height: 8vh;'),
y.addEventListener( y.addEventListener(
'touchstart', 'touchstart',
function (A) { function (A) {
@ -659,7 +734,8 @@ if (new URLSearchParams(window.location.search).get('mobile') === 'true') {
), ),
document.body.appendChild(y); document.body.appendChild(y);
let L = Z('chatButton', 'inGame'); let L = Z('chatButton', 'inGame');
(L.style.cssText = 'top: 0vh; margin: auto; left: 0vh; right: 16vh; width: 8vh; height: 8vh;'), (L.style.cssText =
'top: 0vh; margin: auto; left: 0vh; right: 16vh; width: 8vh; height: 8vh;'),
L.addEventListener( L.addEventListener(
'touchstart', 'touchstart',
function (A) { function (A) {
@ -669,7 +745,8 @@ if (new URLSearchParams(window.location.search).get('mobile') === 'true') {
), ),
document.body.appendChild(L); document.body.appendChild(L);
let N = Z('perspectiveButton', 'inGame'); let N = Z('perspectiveButton', 'inGame');
(N.style.cssText = 'top: 0vh; margin: auto; left: 0vh; right: 0vh; width: 8vh; height: 8vh;'), (N.style.cssText =
'top: 0vh; margin: auto; left: 0vh; right: 0vh; width: 8vh; height: 8vh;'),
N.addEventListener( N.addEventListener(
'touchstart', 'touchstart',
function (A) { function (A) {
@ -686,7 +763,8 @@ if (new URLSearchParams(window.location.search).get('mobile') === 'true') {
), ),
document.body.appendChild(N); document.body.appendChild(N);
let U = Z('screenshotButton', 'inGame'); let U = Z('screenshotButton', 'inGame');
(U.style.cssText = 'top: 0vh; margin: auto; left: 16vh; right: 0vh; width: 8vh; height: 8vh;'), (U.style.cssText =
'top: 0vh; margin: auto; left: 16vh; right: 0vh; width: 8vh; height: 8vh;'),
U.addEventListener( U.addEventListener(
'touchstart', 'touchstart',
function (A) { function (A) {
@ -703,7 +781,8 @@ if (new URLSearchParams(window.location.search).get('mobile') === 'true') {
), ),
document.body.appendChild(U); document.body.appendChild(U);
let z = Z('coordinatesButton', 'inGame'); let z = Z('coordinatesButton', 'inGame');
(z.style.cssText = 'top: 0vh; margin: auto; left: 32vh; right: 0vh; width: 8vh; height: 8vh;'), (z.style.cssText =
'top: 0vh; margin: auto; left: 32vh; right: 0vh; width: 8vh; height: 8vh;'),
z.addEventListener( z.addEventListener(
'touchstart', 'touchstart',
function (A) { function (A) {

View File

@ -13,7 +13,9 @@ if (new URLSearchParams(window.location.search).get('mobile') === 'true') {
} }
} }
if (!isMobile()) { if (!isMobile()) {
alert('WARNING: This script was created for mobile, and may break functionality in non-mobile browsers!'); alert(
'WARNING: This script was created for mobile, and may break functionality in non-mobile browsers!',
);
} }
// TODO: consolidate all of these into a single object? // TODO: consolidate all of these into a single object?
window.crouchLock = false; // Used for crouch mobile control window.crouchLock = false; // Used for crouch mobile control
@ -191,7 +193,12 @@ if (new URLSearchParams(window.location.search).get('mobile') === 'true') {
}); });
window.dispatchEvent(evt); window.dispatchEvent(evt);
} }
function mouseEvent(number, state, element, event = { clientX: 0, clientY: 0, screenX: 0, screenY: 0 }) { function mouseEvent(
number,
state,
element,
event = { clientX: 0, clientY: 0, screenX: 0, screenY: 0 },
) {
element.dispatchEvent( element.dispatchEvent(
new PointerEvent(state, { new PointerEvent(state, {
button: number, button: number,
@ -277,7 +284,9 @@ if (new URLSearchParams(window.location.search).get('mobile') === 'true') {
const element = this._createElement(type); const element = this._createElement(type);
if (type == 'input' && !ignore) { if (type == 'input' && !ignore) {
// We set the ingore flag to true when we create the hiddenInput // We set the ingore flag to true when we create the hiddenInput
document.querySelectorAll('#fileUpload').forEach((e) => e.parentNode.removeChild(e)); // Get rid of any left over fileUpload inputs document
.querySelectorAll('#fileUpload')
.forEach((e) => e.parentNode.removeChild(e)); // Get rid of any left over fileUpload inputs
element.id = 'fileUpload'; element.id = 'fileUpload';
element.addEventListener( element.addEventListener(
'change', 'change',
@ -401,9 +410,15 @@ if (new URLSearchParams(window.location.search).get('mobile') === 'true') {
function (e) { function (e) {
e.preventDefault(); // Prevents window zoom when using two fingers e.preventDefault(); // Prevents window zoom when using two fingers
let primaryTouch; let primaryTouch;
for (let touchIndex = 0; touchIndex < e.targetTouches.length; touchIndex++) { for (
let touchIndex = 0;
touchIndex < e.targetTouches.length;
touchIndex++
) {
// Iterate through our touches to find a touch event matching the primary touch ID // Iterate through our touches to find a touch event matching the primary touch ID
if (e.targetTouches[touchIndex].identifier == window.canvasPrimaryID) { if (
e.targetTouches[touchIndex].identifier == window.canvasPrimaryID
) {
primaryTouch = e.targetTouches[touchIndex]; primaryTouch = e.targetTouches[touchIndex];
break; break;
} }
@ -412,7 +427,8 @@ if (new URLSearchParams(window.location.search).get('mobile') === 'true') {
primaryTouch.distanceX = primaryTouch.clientX - canvasTouchStartX; primaryTouch.distanceX = primaryTouch.clientX - canvasTouchStartX;
primaryTouch.distanceY = primaryTouch.clientY - canvasTouchStartY; primaryTouch.distanceY = primaryTouch.clientY - canvasTouchStartY;
primaryTouch.squaredNorm = primaryTouch.squaredNorm =
primaryTouch.distanceX * primaryTouch.distanceX + primaryTouch.distanceY * primaryTouch.distanceY; primaryTouch.distanceX * primaryTouch.distanceX +
primaryTouch.distanceY * primaryTouch.distanceY;
primaryTouch.movementX = primaryTouch.clientX - canvasTouchPreviousX; primaryTouch.movementX = primaryTouch.clientX - canvasTouchPreviousX;
primaryTouch.movementY = primaryTouch.clientY - canvasTouchPreviousY; primaryTouch.movementY = primaryTouch.clientY - canvasTouchPreviousY;
if (window.canvasTouchMode == 1) { if (window.canvasTouchMode == 1) {
@ -452,7 +468,11 @@ if (new URLSearchParams(window.location.search).get('mobile') === 'true') {
); );
function canvasTouchEnd(e) { function canvasTouchEnd(e) {
for (let touchIndex = 0; touchIndex < e.changedTouches.length; touchIndex++) { for (
let touchIndex = 0;
touchIndex < e.changedTouches.length;
touchIndex++
) {
// Iterate through changed touches to find primary touch // Iterate through changed touches to find primary touch
if (e.changedTouches[touchIndex].identifier == window.canvasPrimaryID) { if (e.changedTouches[touchIndex].identifier == window.canvasPrimaryID) {
const primaryTouch = e.changedTouches[touchIndex]; const primaryTouch = e.changedTouches[touchIndex];
@ -477,11 +497,19 @@ if (new URLSearchParams(window.location.search).get('mobile') === 'true') {
canvas.addEventListener('touchcancel', canvasTouchEnd, false); // TODO: Find out why this is different than touchend canvas.addEventListener('touchcancel', canvasTouchEnd, false); // TODO: Find out why this is different than touchend
setButtonVisibility(window.fakelock != null); //Updates our mobile controls when the canvas finally loads setButtonVisibility(window.fakelock != null); //Updates our mobile controls when the canvas finally loads
// All of the touch buttons // All of the touch buttons
const strafeRightButton = createTouchButton('strafeRightButton', 'inGame', 'div'); const strafeRightButton = createTouchButton(
'strafeRightButton',
'inGame',
'div',
);
strafeRightButton.classList.add('strafeSize'); strafeRightButton.classList.add('strafeSize');
strafeRightButton.style.cssText = 'left:24vh;bottom:22vh;'; strafeRightButton.style.cssText = 'left:24vh;bottom:22vh;';
document.body.appendChild(strafeRightButton); document.body.appendChild(strafeRightButton);
const strafeLeftButton = createTouchButton('strafeLeftButton', 'inGame', 'div'); const strafeLeftButton = createTouchButton(
'strafeLeftButton',
'inGame',
'div',
);
strafeLeftButton.classList.add('strafeSize'); strafeLeftButton.classList.add('strafeSize');
strafeLeftButton.style.cssText = 'left:5.5vh;bottom:22vh;'; strafeLeftButton.style.cssText = 'left:5.5vh;bottom:22vh;';
document.body.appendChild(strafeLeftButton); document.body.appendChild(strafeLeftButton);
@ -666,7 +694,8 @@ if (new URLSearchParams(window.location.search).get('mobile') === 'true') {
document.body.appendChild(inventoryButton); document.body.appendChild(inventoryButton);
const exitButton = createTouchButton('exitButton', 'inMenu'); const exitButton = createTouchButton('exitButton', 'inMenu');
exitButton.classList.add('smallMobileControl'); exitButton.classList.add('smallMobileControl');
exitButton.style.cssText = 'top: 0.5vh; margin: auto; left: 1vh; right:8vh;'; exitButton.style.cssText =
'top: 0.5vh; margin: auto; left: 1vh; right:8vh;';
exitButton.addEventListener( exitButton.addEventListener(
'touchstart', 'touchstart',
function (e) { function (e) {
@ -712,10 +741,17 @@ if (new URLSearchParams(window.location.search).get('mobile') === 'true') {
// For some reason beforeinput doesn't have the same deletion problems that input has on Android // For some reason beforeinput doesn't have the same deletion problems that input has on Android
e.stopImmediatePropagation(); // Android ignores this and the prevent default, so this will probably be removed in the future e.stopImmediatePropagation(); // Android ignores this and the prevent default, so this will probably be removed in the future
e.preventDefault(true); // We pass a value because we've hijacked the prevent default function to have a "should bypass" boolean value e.preventDefault(true); // We pass a value because we've hijacked the prevent default function to have a "should bypass" boolean value
const inputData = e.inputType == 'insertLineBreak' ? 'return' : e.data == null ? 'delete' : e.data.slice(-1); // Saves the last key press. const inputData =
e.inputType == 'insertLineBreak'
? 'return'
: e.data == null
? 'delete'
: e.data.slice(-1); // Saves the last key press.
if (!window.lastKey) { if (!window.lastKey) {
// When we first set hiddenInput's text contents to " " we can use this to check if Duplicate Mode is needed // When we first set hiddenInput's text contents to " " we can use this to check if Duplicate Mode is needed
window.console.warn('Enabling blocking duplicate key events. Some functionality may be lost.'); window.console.warn(
'Enabling blocking duplicate key events. Some functionality may be lost.',
);
window.inputFix = true; window.inputFix = true;
} }
if (window.keyboardFix) { if (window.keyboardFix) {
@ -727,7 +763,10 @@ if (new URLSearchParams(window.location.search).get('mobile') === 'true') {
const sliceInputType = e.inputType.slice(0, 1); // Android doesn't constiently dispatch the correct inputType, but most of them either start with i for insert or d for delete, so this dumb solution should be good enough. const sliceInputType = e.inputType.slice(0, 1); // Android doesn't constiently dispatch the correct inputType, but most of them either start with i for insert or d for delete, so this dumb solution should be good enough.
if (sliceInputType == 'i' && e.data) { if (sliceInputType == 'i' && e.data) {
// Android sometimes always dispatches insertCompositionText inputTypes, so checking that e.data isn't null is necessary // Android sometimes always dispatches insertCompositionText inputTypes, so checking that e.data isn't null is necessary
const isDuplicate = window.lastKey == inputData && window.blockNextInput && window.inputFix; const isDuplicate =
window.lastKey == inputData &&
window.blockNextInput &&
window.inputFix;
if (isDuplicate) { if (isDuplicate) {
// If our key press matches the last unblocked key press and we are in duplicaye mode, ignore the input // If our key press matches the last unblocked key press and we are in duplicaye mode, ignore the input
window.blockNextInput = false; window.blockNextInput = false;
@ -793,7 +832,8 @@ if (new URLSearchParams(window.location.search).get('mobile') === 'true') {
document.body.appendChild(hiddenInput); document.body.appendChild(hiddenInput);
const keyboardButton = createTouchButton('keyboardButton', 'inMenu'); const keyboardButton = createTouchButton('keyboardButton', 'inMenu');
keyboardButton.classList.add('smallMobileControl'); keyboardButton.classList.add('smallMobileControl');
keyboardButton.style.cssText = 'top: 0.5vh; margin: auto; left: 6vh; right:0vh;'; keyboardButton.style.cssText =
'top: 0.5vh; margin: auto; left: 6vh; right:0vh;';
keyboardButton.addEventListener( keyboardButton.addEventListener(
'touchstart', 'touchstart',
function (e) { function (e) {
@ -936,7 +976,8 @@ if (new URLSearchParams(window.location.search).get('mobile') === 'true') {
document.body.appendChild(sprintButton); document.body.appendChild(sprintButton);
const pauseButton = createTouchButton('pauseButton', 'inGame'); const pauseButton = createTouchButton('pauseButton', 'inGame');
pauseButton.classList.add('smallMobileControl'); pauseButton.classList.add('smallMobileControl');
pauseButton.style.cssText = 'top: 0.5vh; margin: auto; left: 0vh; right: 0vh;'; pauseButton.style.cssText =
'top: 0.5vh; margin: auto; left: 0vh; right: 0vh;';
pauseButton.addEventListener( pauseButton.addEventListener(
'touchstart', 'touchstart',
function (e) { function (e) {
@ -954,7 +995,8 @@ if (new URLSearchParams(window.location.search).get('mobile') === 'true') {
document.body.appendChild(pauseButton); document.body.appendChild(pauseButton);
const chatButton = createTouchButton('chatButton', 'inGame'); const chatButton = createTouchButton('chatButton', 'inGame');
chatButton.classList.add('smallMobileControl'); chatButton.classList.add('smallMobileControl');
chatButton.style.cssText = 'top: 0.5vh; margin: auto; left: 0vh; right: 14vh;'; chatButton.style.cssText =
'top: 0.5vh; margin: auto; left: 0vh; right: 14vh;';
chatButton.addEventListener( chatButton.addEventListener(
'touchstart', 'touchstart',
function (e) { function (e) {
@ -965,7 +1007,8 @@ if (new URLSearchParams(window.location.search).get('mobile') === 'true') {
document.body.appendChild(chatButton); document.body.appendChild(chatButton);
const perspectiveButton = createTouchButton('perspectiveButton', 'inGame'); const perspectiveButton = createTouchButton('perspectiveButton', 'inGame');
perspectiveButton.classList.add('smallMobileControl'); perspectiveButton.classList.add('smallMobileControl');
perspectiveButton.style.cssText = 'top: 0.5vh; margin: auto; left: 0vh; right: 28vh;'; perspectiveButton.style.cssText =
'top: 0.5vh; margin: auto; left: 0vh; right: 28vh;';
perspectiveButton.addEventListener( perspectiveButton.addEventListener(
'touchstart', 'touchstart',
function (e) { function (e) {
@ -985,7 +1028,8 @@ if (new URLSearchParams(window.location.search).get('mobile') === 'true') {
document.body.appendChild(perspectiveButton); document.body.appendChild(perspectiveButton);
const screenshotButton = createTouchButton('screenshotButton', 'inGame'); const screenshotButton = createTouchButton('screenshotButton', 'inGame');
screenshotButton.classList.add('smallMobileControl'); screenshotButton.classList.add('smallMobileControl');
screenshotButton.style.cssText = 'top: 0.5vh; margin: auto; left: 28vh; right: 0vh;'; screenshotButton.style.cssText =
'top: 0.5vh; margin: auto; left: 28vh; right: 0vh;';
screenshotButton.addEventListener( screenshotButton.addEventListener(
'touchstart', 'touchstart',
function (e) { function (e) {
@ -1005,7 +1049,8 @@ if (new URLSearchParams(window.location.search).get('mobile') === 'true') {
document.body.appendChild(screenshotButton); document.body.appendChild(screenshotButton);
const coordinatesButton = createTouchButton('coordinatesButton', 'inGame'); const coordinatesButton = createTouchButton('coordinatesButton', 'inGame');
coordinatesButton.classList.add('smallMobileControl'); coordinatesButton.classList.add('smallMobileControl');
coordinatesButton.style.cssText = 'top: 0.5vh; margin: auto; left: 14vh; right: 0vh;'; coordinatesButton.style.cssText =
'top: 0.5vh; margin: auto; left: 14vh; right: 0vh;';
coordinatesButton.addEventListener( coordinatesButton.addEventListener(
'touchstart', 'touchstart',
function (e) { function (e) {

View File

@ -12,9 +12,21 @@ window.addEventListener('load', () => {
{ addr: 'wss://play.brickmc.net', name: 'BrickMC' }, { addr: 'wss://play.brickmc.net', name: 'BrickMC' },
], ],
relays: [ relays: [
{ addr: 'wss://relay.deev.is/', comment: 'lax1dude relay #1', primary: relayId === 0 }, {
{ addr: 'wss://relay.lax1dude.net/', comment: 'lax1dude relay #2', primary: relayId === 1 }, addr: 'wss://relay.deev.is/',
{ addr: 'wss://relay.shhnowisnottheti.me/', comment: 'ayunami relay #1', primary: relayId === 2 }, comment: 'lax1dude relay #1',
primary: relayId === 0,
},
{
addr: 'wss://relay.lax1dude.net/',
comment: 'lax1dude relay #2',
primary: relayId === 1,
},
{
addr: 'wss://relay.shhnowisnottheti.me/',
comment: 'ayunami relay #1',
primary: relayId === 2,
},
], ],
}; };

View File

@ -7,9 +7,21 @@ window.addEventListener('load', () => {
localesURI: `${window.location.pathname}/lang/`, localesURI: `${window.location.pathname}/lang/`,
servers: [{ addr: 'wss://eagler.xyz', name: 'TemuzX' }], servers: [{ addr: 'wss://eagler.xyz', name: 'TemuzX' }],
relays: [ relays: [
{ addr: 'wss://relay.deev.is/', comment: 'lax1dude relay #1', primary: relayId === 0 }, {
{ addr: 'wss://relay.lax1dude.net/', comment: 'lax1dude relay #2', primary: relayId === 1 }, addr: 'wss://relay.deev.is/',
{ addr: 'wss://relay.shhnowisnottheti.me/', comment: 'ayunami relay #1', primary: relayId === 2 }, comment: 'lax1dude relay #1',
primary: relayId === 0,
},
{
addr: 'wss://relay.lax1dude.net/',
comment: 'lax1dude relay #2',
primary: relayId === 1,
},
{
addr: 'wss://relay.shhnowisnottheti.me/',
comment: 'ayunami relay #1',
primary: relayId === 2,
},
], ],
}; };

View File

@ -1,6 +1,9 @@
// @ts-nocheck // @ts-nocheck
window.addEventListener('load', function () { window.addEventListener('load', function () {
window.minecraftOpts = ['game_frame', `${window.location.pathname}/assets.epk`]; window.minecraftOpts = [
'game_frame',
`${window.location.pathname}/assets.epk`,
];
const urlParams = new URLSearchParams(window.location.search); const urlParams = new URLSearchParams(window.location.search);
window.minecraftOpts.push(urlParams.get('server') ?? undefined); window.minecraftOpts.push(urlParams.get('server') ?? undefined);

View File

@ -6,7 +6,9 @@ let articleAnimationLock = false;
const theme = { const theme = {
load: function (themeToLoad?: string) { load: function (themeToLoad?: string) {
const themeElement = document.querySelector('#theme') as HTMLLinkElement | null; const themeElement = document.querySelector(
'#theme',
) as HTMLLinkElement | null;
if (themeElement) if (themeElement)
themeElement.href = themeToLoad themeElement.href = themeToLoad
? `/resources/styles/themes/${themeToLoad}.css` ? `/resources/styles/themes/${themeToLoad}.css`
@ -81,7 +83,9 @@ const game = {
'b1.3': 'b13-client-version', 'b1.3': 'b13-client-version',
}; };
const dropdown = clients[client] const dropdown = clients[client]
? (document.querySelector(`select[id='${clients[client]}']`) as HTMLSelectElement | null) ? (document.querySelector(
`select[id='${clients[client]}']`,
) as HTMLSelectElement | null)
: null; : null;
if (dropdown?.value) { if (dropdown?.value) {
selectedVersion = `https://archive.eaglercraft.rip/Eaglercraft${client === '1.8' ? 'X_1.8' : `_${client}`}/client/${dropdown.value}/index.html`; selectedVersion = `https://archive.eaglercraft.rip/Eaglercraft${client === '1.8' ? 'X_1.8' : `_${client}`}/client/${dropdown.value}/index.html`;
@ -160,8 +164,12 @@ const navigate = {
const article = { const article = {
open: function (articleId: string) { open: function (articleId: string) {
const modal = document.querySelector(`.article[data-article-id='${articleId}']`) as HTMLElement | null; const modal = document.querySelector(
const modalContent = document.querySelector(`.article[data-article-id='${articleId}'] > div`) as HTMLElement | null; `.article[data-article-id='${articleId}']`,
) as HTMLElement | null;
const modalContent = document.querySelector(
`.article[data-article-id='${articleId}'] > div`,
) as HTMLElement | null;
if (!articleAnimationLock && modal && modalContent) { if (!articleAnimationLock && modal && modalContent) {
articleAnimationLock = true; articleAnimationLock = true;
modal.style.animation = '0.5s ease-in-out 1 normal article'; modal.style.animation = '0.5s ease-in-out 1 normal article';
@ -184,7 +192,9 @@ const article = {
} }
}, },
close: function (articleId: string) { close: function (articleId: string) {
const modal = document.querySelector(`.article[data-article-id='${articleId}']`) as HTMLElement | null; const modal = document.querySelector(
`.article[data-article-id='${articleId}']`,
) as HTMLElement | null;
if (!articleAnimationLock && modal) { if (!articleAnimationLock && modal) {
articleAnimationLock = true; articleAnimationLock = true;
modal.style.animation = '0.5s ease-in-out 1 reverse article-tempfix'; modal.style.animation = '0.5s ease-in-out 1 reverse article-tempfix';
@ -238,7 +248,10 @@ const storage = {
} }
return undefined; return undefined;
}, },
set: function (key: string, value: string | number | object | boolean | null | undefined) { set: function (
key: string,
value: string | number | object | boolean | null | undefined,
) {
const item = localStorage.getItem('minexlauncher'); const item = localStorage.getItem('minexlauncher');
if (item === null) { if (item === null) {
const json: Record<string, unknown> = {}; const json: Record<string, unknown> = {};
@ -268,7 +281,10 @@ const storage = {
} }
return undefined; return undefined;
}, },
set: function (key: string, value: string | number | object | boolean | null | undefined) { set: function (
key: string,
value: string | number | object | boolean | null | undefined,
) {
let item = sessionStorage.getItem('minexlauncher'); let item = sessionStorage.getItem('minexlauncher');
if (item === null) { if (item === null) {
item = '{}'; item = '{}';
@ -309,7 +325,9 @@ const mods = {
mods.push(mod); mods.push(mod);
mods.sort(); mods.sort();
storage.local.set('mods', mods); storage.local.set('mods', mods);
const modInstallElem = document.querySelector(`.mod-list > div .links .install[data-mod-id='${modId}']`); const modInstallElem = document.querySelector(
`.mod-list > div .links .install[data-mod-id='${modId}']`,
);
if (modInstallElem) { if (modInstallElem) {
modInstallElem.textContent = 'Uninstall'; modInstallElem.textContent = 'Uninstall';
modInstallElem.classList.add('installed'); modInstallElem.classList.add('installed');
@ -317,7 +335,9 @@ const mods = {
} else { } else {
mods.splice(modIndex, 1); mods.splice(modIndex, 1);
storage.local.set('mods', mods); storage.local.set('mods', mods);
const modInstallElem = document.querySelector(`.mod-list > div .links .install[data-mod-id='${modId}']`); const modInstallElem = document.querySelector(
`.mod-list > div .links .install[data-mod-id='${modId}']`,
);
if (modInstallElem) { if (modInstallElem) {
modInstallElem.textContent = 'Install'; modInstallElem.textContent = 'Install';
modInstallElem.classList.remove('installed'); modInstallElem.classList.remove('installed');
@ -403,7 +423,9 @@ if (window.location.pathname === '/') {
? '/mobile/' ? '/mobile/'
: '/home/game/'; : '/home/game/';
document.addEventListener('DOMContentLoaded', () => document.body.appendChild(iframe)); document.addEventListener('DOMContentLoaded', () =>
document.body.appendChild(iframe),
);
/* document.addEventListener('load', () => { /* document.addEventListener('load', () => {
if (storage.local.get('offlineCache')) { if (storage.local.get('offlineCache')) {
@ -433,9 +455,12 @@ if (window.location.pathname === '/') {
const titleBarText = document.querySelector('.title-bar'); const titleBarText = document.querySelector('.title-bar');
const lastVersion = storage.local.get('lastVersion'); const lastVersion = storage.local.get('lastVersion');
const updateData = (await (await fetch('/resources/data/main.json')).json()).updates; const updateData = (await (await fetch('/resources/data/main.json')).json())
.updates;
const currentVersion = updateData[0].version; const currentVersion = updateData[0].version;
const changelog = updateData[0].changelog.map((change: string) => ` - ${change}`).join('\n'); const changelog = updateData[0].changelog
.map((change: string) => ` - ${change}`)
.join('\n');
if (profileName) profileName.textContent = storage.local.get('username'); if (profileName) profileName.textContent = storage.local.get('username');
if (titleBarText) titleBarText.textContent += ` ${currentVersion}`; if (titleBarText) titleBarText.textContent += ` ${currentVersion}`;
@ -443,9 +468,14 @@ if (window.location.pathname === '/') {
if ( if (
lastVersion && lastVersion &&
// @ts-expect-error // @ts-expect-error
gt(coerce(currentVersion, { includePrerelease: true }), coerce(lastVersion, { includePrerelease: true })) gt(
coerce(currentVersion, { includePrerelease: true }),
coerce(lastVersion, { includePrerelease: true }),
)
) { ) {
alert(`MineXLauncher has been updated to v${currentVersion}!\n\nChanges in v${currentVersion}:\n${changelog}`); alert(
`MineXLauncher has been updated to v${currentVersion}!\n\nChanges in v${currentVersion}:\n${changelog}`,
);
storage.local.set('lastVersion', currentVersion); storage.local.set('lastVersion', currentVersion);
} }
}); });
@ -473,16 +503,28 @@ if (window.location.pathname === '/') {
if (window.location.pathname === '/settings/') { if (window.location.pathname === '/settings/') {
document.addEventListener('DOMContentLoaded', async () => { document.addEventListener('DOMContentLoaded', async () => {
const profileName = document.querySelector('.username'); const profileName = document.querySelector('.username');
const usernameInput = document.querySelector('#username-input') as HTMLInputElement | null; const usernameInput = document.querySelector(
const themeSelect = document.querySelector('#theme-select') as HTMLSelectElement | null; '#username-input',
const offlineCheckbox = document.querySelector('#offline-checkbox') as HTMLInputElement | null; ) as HTMLInputElement | null;
const adsCheckbox = document.querySelector('#ads-checkbox') as HTMLInputElement | null; const themeSelect = document.querySelector(
const themeData: { id: string; name: string }[] = (await (await fetch('/resources/data/main.json')).json()).themes; '#theme-select',
) as HTMLSelectElement | null;
const offlineCheckbox = document.querySelector(
'#offline-checkbox',
) as HTMLInputElement | null;
const adsCheckbox = document.querySelector(
'#ads-checkbox',
) as HTMLInputElement | null;
const themeData: { id: string; name: string }[] = (
await (await fetch('/resources/data/main.json')).json()
).themes;
if (usernameInput) { if (usernameInput) {
usernameInput.placeholder = storage.local.get('username') ?? ''; usernameInput.placeholder = storage.local.get('username') ?? '';
usernameInput.addEventListener('input', () => { usernameInput.addEventListener('input', () => {
let username = usernameInput.value.replace(/[^A-Za-z0-9]/g, '_').substring(0, 16); let username = usernameInput.value
.replace(/[^A-Za-z0-9]/g, '_')
.substring(0, 16);
usernameInput.value = username; usernameInput.value = username;
while (username.length < 3) username += '_'; while (username.length < 3) username += '_';
@ -499,7 +541,9 @@ if (window.location.pathname === '/settings/') {
themeSelect?.appendChild(option); themeSelect?.appendChild(option);
}); });
themeSelect.value = storage.local.get('theme') ?? ''; themeSelect.value = storage.local.get('theme') ?? '';
themeSelect.addEventListener('change', () => theme.set(themeSelect.value ?? 'default')); themeSelect.addEventListener('change', () =>
theme.set(themeSelect.value ?? 'default'),
);
} }
if (offlineCheckbox) { if (offlineCheckbox) {
@ -523,22 +567,37 @@ if (window.location.pathname === '/settings/') {
adsCheckbox.addEventListener('change', () => { adsCheckbox.addEventListener('change', () => {
storage.local.set('showAds', adsCheckbox.checked); storage.local.set('showAds', adsCheckbox.checked);
const adsContainers = document.querySelectorAll('.ads-container'); const adsContainers = document.querySelectorAll('.ads-container');
adsContainers.forEach((adsContainer: Element) => ((adsContainer as HTMLElement).style.display = 'none')); adsContainers.forEach(
(adsContainer: Element) =>
((adsContainer as HTMLElement).style.display = 'none'),
);
}); });
} }
}); });
} else if (window.location.pathname === '/welcome/') { } else if (window.location.pathname === '/welcome/') {
document.addEventListener('DOMContentLoaded', async () => { document.addEventListener('DOMContentLoaded', async () => {
const setupForm = document.querySelector('#setup-form') as HTMLFormElement | null; const setupForm = document.querySelector(
const usernameInput = document.querySelector('#username-input') as HTMLInputElement | null; '#setup-form',
const themeSelect = document.querySelector('#theme-select') as HTMLSelectElement | null; ) as HTMLFormElement | null;
const offlineCheckbox = document.querySelector('#offline-checkbox') as HTMLInputElement | null; const usernameInput = document.querySelector(
const themeData: { id: string; name: string }[] = (await (await fetch('/resources/data/main.json')).json()).themes; '#username-input',
) as HTMLInputElement | null;
const themeSelect = document.querySelector(
'#theme-select',
) as HTMLSelectElement | null;
const offlineCheckbox = document.querySelector(
'#offline-checkbox',
) as HTMLInputElement | null;
const themeData: { id: string; name: string }[] = (
await (await fetch('/resources/data/main.json')).json()
).themes;
if (setupForm) { if (setupForm) {
if (usernameInput) { if (usernameInput) {
usernameInput.addEventListener('input', () => { usernameInput.addEventListener('input', () => {
const username = usernameInput.value.replace(/[^A-Za-z0-9]/g, '_').substring(0, 16); const username = usernameInput.value
.replace(/[^A-Za-z0-9]/g, '_')
.substring(0, 16);
usernameInput.value = username; usernameInput.value = username;
}); });
} }
@ -550,13 +609,17 @@ if (window.location.pathname === '/settings/') {
option.textContent = theme.name; option.textContent = theme.name;
themeSelect?.appendChild(option); themeSelect?.appendChild(option);
}); });
themeSelect.addEventListener('change', () => theme.load(themeSelect.value ?? 'default')); themeSelect.addEventListener('change', () =>
theme.load(themeSelect.value ?? 'default'),
);
} }
setupForm.addEventListener('submit', async (event) => { setupForm.addEventListener('submit', async (event) => {
event.preventDefault(); event.preventDefault();
let username = usernameInput?.value.replace(/[^A-Za-z0-9]/g, '_').substring(0, 16); let username = usernameInput?.value
.replace(/[^A-Za-z0-9]/g, '_')
.substring(0, 16);
if (usernameInput) usernameInput.value = username ?? ''; if (usernameInput) usernameInput.value = username ?? '';
if (!username) { if (!username) {
@ -572,7 +635,8 @@ if (window.location.pathname === '/settings/') {
storage.local.set('mods', []); storage.local.set('mods', []);
storage.local.set( storage.local.set(
'lastVersion', 'lastVersion',
(await (await fetch('/resources/data/main.json')).json()).updates[0].version, (await (await fetch('/resources/data/main.json')).json()).updates[0]
.version,
); );
if (offlineCheckbox?.checked) { if (offlineCheckbox?.checked) {
@ -588,9 +652,13 @@ if (window.location.pathname === '/settings/') {
}); });
} }
}); });
} else if (window.location.pathname === '/mods/mods/' || window.location.pathname === '/mods/resourcepacks/') { } else if (
window.location.pathname === '/mods/mods/' ||
window.location.pathname === '/mods/resourcepacks/'
) {
document.addEventListener('DOMContentLoaded', async () => { document.addEventListener('DOMContentLoaded', async () => {
const addonType: 'mods' | 'resourcepacks' = window.location.pathname === '/mods/mods/' ? 'mods' : 'resourcepacks'; const addonType: 'mods' | 'resourcepacks' =
window.location.pathname === '/mods/mods/' ? 'mods' : 'resourcepacks';
const data: { const data: {
id: string; id: string;
name: string; name: string;
@ -607,15 +675,17 @@ if (window.location.pathname === '/settings/') {
addon.name addon.name
}</strong><p class="author">By <a href="${addon.authorLink}" target="_blank">${addon.author}</a></p><p class="description">${addon.description}</p></div><div class="links">${ }</strong><p class="author">By <a href="${addon.authorLink}" target="_blank">${addon.author}</a></p><p class="description">${addon.description}</p></div><div class="links">${
addonType === 'mods' addonType === 'mods'
? `<span class="download" onclick="downloadFile('/resources/mods/downloads/${addon.id}.js', '${addon.name}.js')">Download</span><span class="install" data-mod-id="${addon.id}" onclick="mods.toggle('${addon.id}')">Install</span>` ? `<span class="download" onclick="downloadFile('/resources/mods/downloads/${addon.id}.js', '${addon.name.replace('\\', '\\\\').replace("'", "\\'")}.js')">Download</span><span class="install" data-mod-id="${addon.id}" onclick="mods.toggle('${addon.id}')">Install</span>`
: `<span class="download" onclick="downloadFile('/resources/mods/downloads/${addon.id}.zip' '${addon.name}.zip')">Download</span>` : `<span class="download" onclick="downloadFile('/resources/mods/downloads/${addon.id}.zip', '${addon.name.replace('\\', '\\\\').replace("'", "\\'")}.zip')">Download</span>`
}</div>`; }</div>`;
modList?.appendChild(modItem); modList?.appendChild(modItem);
}); });
if (addonType === 'mods') { if (addonType === 'mods') {
const installedMods = storage.local.get('mods') ?? []; const installedMods = storage.local.get('mods') ?? [];
const modElements = document.querySelectorAll('.mod-list > div .links .install'); const modElements = document.querySelectorAll(
'.mod-list > div .links .install',
);
modElements.forEach((element) => { modElements.forEach((element) => {
const modId = (element as HTMLElement).dataset['modId']; const modId = (element as HTMLElement).dataset['modId'];
if (installedMods.includes(`/resources/mods/downloads/${modId}.js`)) { if (installedMods.includes(`/resources/mods/downloads/${modId}.js`)) {
@ -628,8 +698,9 @@ if (window.location.pathname === '/settings/') {
} else if (window.location.pathname === '/updates/') { } else if (window.location.pathname === '/updates/') {
document.addEventListener('DOMContentLoaded', async () => { document.addEventListener('DOMContentLoaded', async () => {
const updatesContainer = document.querySelector('.updates-container'); const updatesContainer = document.querySelector('.updates-container');
const data: { version: string; changelog: string[] }[] = (await (await fetch('/resources/data/main.json')).json()) const data: { version: string; changelog: string[] }[] = (
.updates; await (await fetch('/resources/data/main.json')).json()
).updates;
data.forEach((update) => { data.forEach((update) => {
const version = document.createElement('div'); const version = document.createElement('div');
const name = document.createElement('strong'); const name = document.createElement('strong');
@ -651,5 +722,14 @@ if (window.location.pathname === '/settings/') {
if (window.location.hostname === null) { if (window.location.hostname === null) {
// Stop the minifier from removing these functions // Stop the minifier from removing these functions
console.debug([navigate, query, versionSelector, game, mods, base64Gzip, article, downloadFile]); console.debug([
navigate,
query,
versionSelector,
game,
mods,
base64Gzip,
article,
downloadFile,
]);
} }

View File

@ -77,11 +77,11 @@ body {
box-shadow: 0 -2px 4px rgba(0, 0, 0, 0.3); box-shadow: 0 -2px 4px rgba(0, 0, 0, 0.3);
} }
.downloads a { .downloads span {
background-color: #ff6600; background-color: #ff6600;
} }
.downloads a:hover { .downloads span:hover {
background-color: #ff8533; background-color: #ff8533;
} }

View File

@ -104,11 +104,11 @@ body {
color: #e5a1b8; color: #e5a1b8;
} }
.downloads a { .downloads span {
background-color: #d0808e; background-color: #d0808e;
} }
.downloads a:hover { .downloads span:hover {
background-color: #e5a1b8; background-color: #e5a1b8;
} }

View File

@ -354,7 +354,7 @@ body {
align-items: center; align-items: center;
} }
.downloads a { .downloads span {
display: inline-block; display: inline-block;
background-color: #048239; background-color: #048239;
color: #fff; color: #fff;
@ -365,7 +365,8 @@ body {
transition: background-color 0.3s; transition: background-color 0.3s;
} }
.downloads a:hover { .downloads span:hover {
cursor: pointer;
background-color: #00cc00; background-color: #00cc00;
} }

View File

@ -103,12 +103,12 @@ body {
color: #e0e0e0; color: #e0e0e0;
} }
.downloads a { .downloads span {
background-color: #333; background-color: #333;
color: #e0e0e0; color: #e0e0e0;
} }
.downloads a:hover { .downloads span:hover {
background-color: #444; background-color: #444;
} }

View File

@ -87,11 +87,11 @@ body {
color: #333; color: #333;
} }
.downloads a { .downloads span {
background-color: #4caf50; background-color: #4caf50;
} }
.downloads a:hover { .downloads span:hover {
background-color: #66bb6a; background-color: #66bb6a;
} }

View File

@ -81,11 +81,11 @@ body {
color: #ffcc00; color: #ffcc00;
} }
.downloads a { .downloads span {
background-color: #8b0000; background-color: #8b0000;
} }
.downloads a:hover { .downloads span:hover {
background-color: #ff4500; background-color: #ff4500;
} }

View File

@ -81,11 +81,11 @@ body {
color: #00ff00; color: #00ff00;
} }
.downloads a { .downloads span {
background-color: #00b000; background-color: #00b000;
} }
.downloads a:hover { .downloads span:hover {
background-color: #00ff00; background-color: #00ff00;
} }

View File

@ -31,7 +31,7 @@ body {
.installations div .selector, .installations div .selector,
.installations div .installations div .options divs, .installations div .installations div .options divs,
.installations button, .installations button,
.downloads a { .downloads span {
background-color: #111; background-color: #111;
border: 2px solid #00ff00; border: 2px solid #00ff00;
color: #00ff00; color: #00ff00;
@ -41,14 +41,14 @@ body {
.nav-bar li.selected, .nav-bar li.selected,
.installations div .options div:hover, .installations div .options div:hover,
.installations button:hover, .installations button:hover,
.downloads a:hover { .downloads span:hover {
background-color: #333; background-color: #333;
} }
.installations div .selector:hover, .installations div .selector:hover,
.installations div .selector.open, .installations div .selector.open,
.installations button:hover, .installations button:hover,
.downloads a:hover { .downloads span:hover {
box-shadow: none; box-shadow: none;
} }

View File

@ -45,10 +45,10 @@ body {
border-color: #0077aa; border-color: #0077aa;
} }
.downloads a { .downloads span {
background-color: #00ccff; background-color: #00ccff;
} }
.downloads a:hover { .downloads span:hover {
background-color: #00e6ff; background-color: #00e6ff;
} }

View File

@ -77,11 +77,11 @@ body {
color: #b300b3; color: #b300b3;
} }
.downloads a { .downloads span {
background-color: #4d0099; background-color: #4d0099;
} }
.downloads a:hover { .downloads span:hover {
background-color: #8000ff; background-color: #8000ff;
} }

View File

@ -4,9 +4,17 @@
<meta charset="UTF-8" /> <meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" /> <meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>MineXLauncher</title> <title>MineXLauncher</title>
<link rel="icon" type="image/webp" href="/resources/images/icons/favicon.webp" /> <link
rel="icon"
type="image/webp"
href="/resources/images/icons/favicon.webp"
/>
<link rel="stylesheet" href="/resources/styles/themes/default.css" /> <link rel="stylesheet" href="/resources/styles/themes/default.css" />
<link rel="stylesheet" id="theme" onload="document.documentElement.style.display = ''" /> <link
rel="stylesheet"
id="theme"
onload="document.documentElement.style.display = ''"
/>
<script src="/resources/scripts/google-tag.js"></script> <script src="/resources/scripts/google-tag.js"></script>
<script src="/resources/scripts/main.js"></script> <script src="/resources/scripts/main.js"></script>
</head> </head>
@ -62,7 +70,9 @@
</div> </div>
</div> </div>
<div class="bottom-bar"> <div class="bottom-bar">
<span onclick="window.open('https://discord.gg/VRwbRJjXzt')">Join the MineXLauncher Discord</span> <span onclick="window.open('https://discord.gg/VRwbRJjXzt')"
>Join the MineXLauncher Discord</span
>
<span>© 2024 MineXLauncher. All rights reserved.</span> <span>© 2024 MineXLauncher. All rights reserved.</span>
</div> </div>
</div> </div>

View File

@ -4,9 +4,17 @@
<meta charset="UTF-8" /> <meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" /> <meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>MineXLauncher</title> <title>MineXLauncher</title>
<link rel="icon" type="image/webp" href="/resources/images/icons/favicon.webp" /> <link
rel="icon"
type="image/webp"
href="/resources/images/icons/favicon.webp"
/>
<link rel="stylesheet" href="/resources/styles/themes/default.css" /> <link rel="stylesheet" href="/resources/styles/themes/default.css" />
<link rel="stylesheet" id="theme" onload="document.documentElement.style.display = ''" /> <link
rel="stylesheet"
id="theme"
onload="document.documentElement.style.display = ''"
/>
<script src="/resources/scripts/google-tag.js"></script> <script src="/resources/scripts/google-tag.js"></script>
<script src="/resources/scripts/main.js"></script> <script src="/resources/scripts/main.js"></script>
</head> </head>
@ -78,7 +86,9 @@
</div> </div>
</div> </div>
<div class="bottom-bar"> <div class="bottom-bar">
<span onclick="window.open('https://discord.gg/VRwbRJjXzt')">Join the MineXLauncher Discord</span> <span onclick="window.open('https://discord.gg/VRwbRJjXzt')"
>Join the MineXLauncher Discord</span
>
<span>© 2024 MineXLauncher. All rights reserved.</span> <span>© 2024 MineXLauncher. All rights reserved.</span>
</div> </div>
</div> </div>

View File

@ -4,9 +4,17 @@
<meta charset="UTF-8" /> <meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" /> <meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>MineXLauncher</title> <title>MineXLauncher</title>
<link rel="icon" type="image/webp" href="/resources/images/icons/favicon.webp" /> <link
rel="icon"
type="image/webp"
href="/resources/images/icons/favicon.webp"
/>
<link rel="stylesheet" href="/resources/styles/themes/default.css" /> <link rel="stylesheet" href="/resources/styles/themes/default.css" />
<link rel="stylesheet" id="theme" onload="document.documentElement.style.display = ''" /> <link
rel="stylesheet"
id="theme"
onload="document.documentElement.style.display = ''"
/>
<script src="/resources/scripts/google-tag.js"></script> <script src="/resources/scripts/google-tag.js"></script>
<script src="/resources/scripts/main.js"></script> <script src="/resources/scripts/main.js"></script>
</head> </head>
@ -61,7 +69,9 @@
</div> </div>
</div> </div>
<div class="bottom-bar"> <div class="bottom-bar">
<span onclick="window.open('https://discord.gg/VRwbRJjXzt')">Join the MineXLauncher Discord</span> <span onclick="window.open('https://discord.gg/VRwbRJjXzt')"
>Join the MineXLauncher Discord</span
>
<span>© 2024 MineXLauncher. All rights reserved.</span> <span>© 2024 MineXLauncher. All rights reserved.</span>
</div> </div>
</div> </div>

View File

@ -4,9 +4,17 @@
<meta charset="UTF-8" /> <meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" /> <meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>MineXLauncher</title> <title>MineXLauncher</title>
<link rel="icon" type="image/webp" href="/resources/images/icons/favicon.webp" /> <link
rel="icon"
type="image/webp"
href="/resources/images/icons/favicon.webp"
/>
<link rel="stylesheet" href="/resources/styles/themes/default.css" /> <link rel="stylesheet" href="/resources/styles/themes/default.css" />
<link rel="stylesheet" id="theme" onload="document.documentElement.style.display = ''" /> <link
rel="stylesheet"
id="theme"
onload="document.documentElement.style.display = ''"
/>
<meta name="theme-color" content="#000000" /> <meta name="theme-color" content="#000000" />
<script src="/resources/scripts/google-tag.js"></script> <script src="/resources/scripts/google-tag.js"></script>
<script src="/resources/scripts/main.js"></script> <script src="/resources/scripts/main.js"></script>
@ -22,7 +30,11 @@
<p>Let's get you setup.</p> <p>Let's get you setup.</p>
<form id="setup-form"> <form id="setup-form">
<label for="username-input">Username:</label> <label for="username-input">Username:</label>
<input id="username-input" type="text" placeholder="Enter username" /> <input
id="username-input"
type="text"
placeholder="Enter username"
/>
<label for="theme-select">Theme:</label> <label for="theme-select">Theme:</label>
<select id="theme-select"> <select id="theme-select">
<option disabled hidden value=""></option> <option disabled hidden value=""></option>
@ -36,7 +48,9 @@
</div> </div>
</div> </div>
<div class="bottom-bar"> <div class="bottom-bar">
<span onclick="window.open('https://discord.gg/VRwbRJjXzt')">Join the MineXLauncher Discord</span> <span onclick="window.open('https://discord.gg/VRwbRJjXzt')"
>Join the MineXLauncher Discord</span
>
<span>© 2024 MineXLauncher. All rights reserved.</span> <span>© 2024 MineXLauncher. All rights reserved.</span>
</div> </div>
</div> </div>

View File

@ -1,4 +1,9 @@
{ {
"extends": ["@tsconfig/bun", "@tsconfig/strictest"], "extends": ["@tsconfig/bun", "@tsconfig/strictest"],
"exclude": ["node_modules/", "public/", "src/game/", "src/resources/mods/downloads/"] "exclude": [
"node_modules/",
"public/",
"src/game/",
"src/resources/mods/downloads/"
]
} }