1
0
mirror of https://github.com/zumbiepig/MineXLauncher.git synced 2025-06-07 19:34:49 +00:00
This commit is contained in:
zumbiepig 2024-09-07 16:10:28 -07:00
parent bfb131d4ea
commit 01d1aebe64
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/web/*.html
src/resources/mods/downloads/

View File

@ -1,5 +1,4 @@
{
"printWidth": 120,
"useTabs": 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 { mkdirSync, readdirSync, statSync, writeFileSync, readFileSync, rmSync } from 'fs';
import {
mkdirSync,
readdirSync,
statSync,
writeFileSync,
readFileSync,
rmSync,
} from 'fs';
import { resolve, dirname, basename } from 'path';
import { minify } from 'html-minifier';
import javascriptObfuscator from 'javascript-obfuscator';
@ -34,7 +41,11 @@ srcFiles.forEach((file) => {
if (file.endsWith('.ts')) bundleFiles.push(file);
else if (isDev) copyFiles.push(file);
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 copyFiles.push(file);
});
@ -76,7 +87,9 @@ if (!isDev) {
console.log(chalk.cyan('Obfuscating JavaScript...\n'));
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(
outputPath,
javascriptObfuscator
@ -103,7 +116,9 @@ writeFileSync(
assetsJsonPath,
JSON.stringify(
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());
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('/', indexRouter);
@ -65,7 +69,9 @@ app
.on('error', (error) => {
if (error.code === 'EADDRINUSE') {
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);
}

View File

@ -4,9 +4,17 @@
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<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" id="theme" onload="document.documentElement.style.display = ''" />
<link
rel="stylesheet"
id="theme"
onload="document.documentElement.style.display = ''"
/>
<meta name="theme-color" content="#333" />
<script src="/resources/scripts/google-tag.js"></script>
<script src="/resources/scripts/main.js"></script>
@ -35,7 +43,9 @@
</div>
</div>
<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>
</div>
</div>

View File

@ -4,9 +4,17 @@
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<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" 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/main.js"></script>
</head>
@ -58,17 +66,32 @@
<div class="main-content">
<div class="article-list">
<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">
<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 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">
<strong>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>
<strong
>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>
@ -76,7 +99,9 @@
</div>
</div>
<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>
</div>
</div>
@ -97,13 +122,22 @@
<h1>How To Make An Eaglercraft Server</h1>
<h5>Written by ServerDotSo and revised by zumbiepig</h5>
<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>
<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>
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>Basic knowledge of your OS's command-line/terminal</li>
<li>Access to your network router (for port forwarding)</li>
@ -118,33 +152,52 @@
>here</a
>.
</li>
<li>Create a new directory for your server and place the Bungee JAR 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:
Create a new directory for your server and place the Bungee JAR
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>
</li>
<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>
<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>
<h3>Step 2: Set Up Backend Server</h3>
<ol>
<li>Download a compatible Minecraft server JAR (e.g., Paper)</li>
<li>Set up the backend server in a separate directory</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:
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 -Xms1024M -Xmx1024M -jar server.jar</code></pre>
</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>
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>.
Configure <code>server.properties</code> (Disable online mode,
change the port, etc.)
</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>.
Configure <code>spigot.yml</code> to have this:
<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>
</ol>
<h3>Step 3: Install EaglerXBungee Plugin</h3>
@ -157,14 +210,25 @@
>here</a
>.
</li>
<li>Place the downloaded JAR file in the Bungee server's <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>
<li>
Place the downloaded JAR file in the Bungee server's
<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>
<h3>Step 4: Set Up Caddy</h3>
<ol>
<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>
Create a file called Caddyfile with this configuration:
@ -175,33 +239,42 @@
<ol>
<li>Access your router's admin panel</li>
<li>
Set up port forwarding for port <code>8081</code> (or whichever port you configured for EaglerXBungee) to
your server's local IP address
Set up port forwarding for port <code>8081</code> (or whichever
port you configured for EaglerXBungee) to your server's local IP
address
</li>
</ol>
<h3>Step 6: Domain Configuration (Optional)</h3>
<p>If you want to use a custom domain:</p>
<ol>
<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>
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>
</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>
<h3>Step 7: Connecting to Your Server</h3>
<p>
Players can connect to your server using an Eaglercraft client such as MineXLauncher, and entering your
server's IP address or domain and port.
Players can connect to your server using an Eaglercraft client such
as MineXLauncher, and entering your server's IP address or domain
and port.
</p>
<p>Examples:</p>
<li><code>wss://localhost</code></li>
<li><code>wss://example.com</code></li>
<h3>Security Considerations</h3>
<p>
Ensure you keep all software (especially the EaglerXBungee plugin) up to date and properly secure your
server to protect against potential vulnerabilities.
Ensure you keep all software (especially the EaglerXBungee plugin)
up to date and properly secure your server to protect against
potential vulnerabilities.
</p>
</div>
</div>
@ -213,47 +286,76 @@
<h1>Setting Up a Cloudflare Tunnel for an Eaglercraft Server</h1>
<h5>Written by zumbiepig and SpeedSlicer</h5>
<p>
In this guide, we'll walk you through the steps to set up a Cloudflare Tunnel for your Eaglercraft server,
which is running on <code>ws://localhost:8081</code>.
In this guide, we'll walk you through the steps to set up a
Cloudflare Tunnel for your Eaglercraft server, which is running on
<code>ws://localhost:8081</code>.
</p>
<h3>Prerequisites</h3>
<ul>
<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>
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>
</ul>
<h3>Step 1: Access Cloudflare Tunnels</h3>
<ol>
<li>
Go to the <a href="https://dash.cloudflare.com/" target="_blank">Dashboard</a> and log in to your
Cloudflare account.
Go to the
<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>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>
<h3>Step 2: Create a Tunnel</h3>
<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>Enter a name for the tunnel.</li>
<li>
Follow the instructions provided on the site to install the tunnel.<br />You should do these instructions
on the computer that your Eaglercraft server is running on.
Follow the instructions provided on the site to install the
tunnel.<br />You should do these instructions on the computer that
your Eaglercraft server is running on.
</li>
</ol>
<h3>Step 3: Configure Domain/Subdomain</h3>
<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>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>
<h3>Step 4: Activate Tunnel and Proxy</h3>
<ol>
<li>Start your Eaglercraft server.</li>
<li>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:
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>
</li>
</ol>

View File

@ -4,9 +4,17 @@
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<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" 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/main.js"></script>
</head>
@ -58,7 +66,9 @@
<ul>
<li onclick="navigate.home.game()">Game</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>
</ul>
</div>
@ -67,7 +77,9 @@
<div>
<label for="18-client-version">1.8 versions:</label>
<select id="18-client-version">
<option disabled selected hidden value="">Select version</option>
<option disabled selected hidden value="">
Select version
</option>
<optgroup label="Releases">
<option value="u35">u35</option>
<option value="u34">u34</option>
@ -118,7 +130,9 @@
<div>
<label for="15-client-version">1.5 versions:</label>
<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">
<option value="sp1.01">sp1.01</option>
<option value="sp1">sp1</option>
@ -198,7 +212,9 @@
<div>
<label for="b13-client-version">Select Beta 1.3 version:</label>
<select id="b13-client-version">
<option disabled selected hidden value="">Select version</option>
<option disabled selected hidden value="">
Select version
</option>
<optgroup label="Releases">
<option value="23w49a">23w49a</option>
<option value="22w22b">22w22b</option>
@ -208,13 +224,19 @@
</select>
<button onclick="game.archive('b1.3')">Open</button>
</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
</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
</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
</button>
</div>
@ -222,7 +244,9 @@
</div>
</div>
<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>
</div>
</div>

View File

@ -4,9 +4,17 @@
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<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" 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/main.js"></script>
</head>
@ -57,35 +65,52 @@
<div class="top-nav">
<ul>
<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.downloads()">Offline Downloads</li>
</ul>
</div>
<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 class="installations">
<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 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" />
<span>EaglerForge</span>
</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" />
<span>Resent Client</span>
</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" />
<span>Shadow Client</span>
</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" />
<span>Astra Client</span>
</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" />
<span>Starlike Client</span>
</div>
@ -96,7 +121,9 @@
</div>
</div>
<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>
</div>
</div>

View File

@ -4,9 +4,17 @@
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<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" 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/main.js"></script>
</head>
@ -59,32 +67,75 @@
<li onclick="navigate.home.game()">Game</li>
<li onclick="navigate.home.clients()">Clients</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>
</div>
<div class="main-content">
<div class="downloads">
<h3>Downloads:</h3>
<a href="/game/offline/main/EaglercraftL_1.9.html" download>1.9.4</a>
<a href="/game/offline/main/EaglercraftX_1.8.html" download>1.8.8</a>
<a href="/game/offline/main/Eaglercraft_1.5.html" download>1.5.2</a>
<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>
<a href="/game/offline/main/Eaglercraft_b1.3.html" download>Beta 1.3</a>
<a href="/game/offline/main/Eaglercraft_a1.2.6.html" download>Alpha 1.2.6</a>
<a href="/game/offline/main/Eaglercraft_Indev.html" download>Indev</a>
<span
onclick="downloadFile('/game/offline/main/EaglercraftL_1.9.html', 'EaglercraftL_1.9.html')"
>1.9.4</span
>
<span
onclick="downloadFile('/game/offline/main/EaglercraftX_1.8.html', 'EaglercraftX_1.8.html')"
>1.8.8</span
>
<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 />
<a href="/game/offline/clients/EaglerForge.html" download>EaglerForge</a>
<a href="/game/offline/clients/Resent_Client.html" download>Resent Client</a>
<a href="/game/offline/clients/Shadow_Client.html" download>Shadow Client</a>
<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/EaglerForge.html', 'EaglerForge.html')"
>EaglerForge</span
>
<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 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>
</div>
</div>

View File

@ -4,9 +4,17 @@
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<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" 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/main.js"></script>
</head>
@ -63,11 +71,16 @@
</ul>
</div>
<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 class="installations">
<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 onclick="game.select('/game/web/main/1.9.4/', '1.9.4')">
<img src="/resources/images/icons/clients/all.webp" />
@ -81,11 +94,15 @@
<img src="/resources/images/icons/clients/all.webp" />
<span>1.5.2</span>
</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" />
<span>1.2.5</span>
</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" />
<span>Beta 1.7.3</span>
</div>
@ -105,7 +122,9 @@
<img src="/resources/images/icons/clients/all.webp" />
<span>Indev</span>
</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" />
<span>Classic</span>
</div>
@ -116,7 +135,9 @@
</div>
</div>
<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>
</div>
</div>

View File

@ -4,7 +4,11 @@
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<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="manifest" href="/manifest.json" />
<link rel="canonical" href="https://launcher.orionzleon.me/" />
@ -19,7 +23,10 @@
/>
<meta property="og:title" content="MineXLauncher" />
<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:description"

View File

@ -4,9 +4,17 @@
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<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" 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/main.js"></script>
</head>
@ -26,9 +34,14 @@
<div class="main-panel">
<span class="top-title">Mobile</span>
<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">
<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
class="play-button"
style="text-decoration: none"
@ -41,7 +54,9 @@
</div>
</div>
<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>
</div>
</div>

View File

@ -4,9 +4,17 @@
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<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" 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/main.js"></script>
</head>
@ -64,12 +72,16 @@
<div class="mod-list"></div>
</div>
<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 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>
</div>
</div>

View File

@ -4,9 +4,17 @@
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<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" 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/main.js"></script>
</head>
@ -57,7 +65,9 @@
<div class="top-nav">
<ul>
<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>
</div>
<div class="main-content">
@ -66,7 +76,9 @@
</div>
</div>
<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>
</div>
</div>

View File

@ -4,9 +4,17 @@
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<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" id="theme" onload="document.documentElement.style.display = ''" />
<link
rel="stylesheet"
id="theme"
onload="document.documentElement.style.display = ''"
/>
<meta name="theme-color" content="#333" />
<script src="/resources/scripts/google-tag.js"></script>
<script src="/resources/scripts/main.js"></script>
@ -35,7 +43,9 @@
</div>
</div>
<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>
</div>
</div>

View File

@ -4,9 +4,17 @@
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<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" id="theme" onload="document.documentElement.style.display = ''" />
<link
rel="stylesheet"
id="theme"
onload="document.documentElement.style.display = ''"
/>
<meta name="theme-color" content="#333" />
<script src="/resources/scripts/google-tag.js"></script>
<script src="/resources/scripts/main.js"></script>
@ -31,25 +39,38 @@
</ul>
</div>
<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 class="installations">
<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 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" />
<span>1.9.4</span>
</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" />
<span>1.8.8</span>
</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" />
<span>1.5.2</span>
</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" />
<span>1.2.5</span>
</div>
@ -67,11 +88,15 @@
<img src="/resources/images/icons/clients/all.webp" />
<span>Beta 1.3</span>
</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" />
<span>Alpha 1.2.6</span>
</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" />
<span>Indev</span>
</div>
@ -82,7 +107,9 @@
</div>
</div>
<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>
</div>
</div>

View File

@ -78,7 +78,11 @@
},
{
"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",

View File

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

View File

@ -12,9 +12,21 @@ window.addEventListener('load', () => {
{ addr: 'wss://play.brickmc.net', name: 'BrickMC' },
],
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.shhnowisnottheti.me/', comment: 'ayunami relay #1', primary: relayId === 2 },
{
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.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) {
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.sprintLock = !1),
(clientWindow.keyboardFix = !1),
@ -136,7 +139,8 @@ if (new URLSearchParams(window.location.search).get('mobile') === 'true') {
this,
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,
)
@ -161,12 +165,18 @@ if (new URLSearchParams(window.location.search).get('mobile') === 'true') {
(I.disabled = A), (g.disabled = !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),
Object.defineProperty(Element.prototype, 'requestPointerLock', {
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', {
@ -176,13 +186,22 @@ if (new URLSearchParams(window.location.search).get('mobile') === 'true') {
}),
Object.defineProperty(Document.prototype, 'exitPointerLock', {
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),
Object.defineProperty(Element.prototype, 'requestFullscreen', {
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', {
@ -192,7 +211,11 @@ if (new URLSearchParams(window.location.search).get('mobile') === 'true') {
}),
Object.defineProperty(Document.prototype, 'exitFullscreen', {
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;
@ -202,7 +225,9 @@ if (new URLSearchParams(window.location.search).get('mobile') === 'true') {
return (
'input' != A ||
I ||
(document.querySelectorAll('#fileUpload').forEach((A) => A.parentNode.removeChild(A)),
(document
.querySelectorAll('#fileUpload')
.forEach((A) => A.parentNode.removeChild(A)),
(g.id = 'fileUpload'),
g.addEventListener(
'change',
@ -225,7 +250,9 @@ if (new URLSearchParams(window.location.search).get('mobile') === 'true') {
);
};
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');
function Z(A, I, g) {
var i = document.createElement(g ?? 'button', !0);
@ -254,7 +281,8 @@ if (new URLSearchParams(window.location.search).get('mobile') === 'true') {
new Promise((A) => {
if (document.querySelector(l)) return A(document.querySelector(l));
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 });
})).then(() => {
@ -265,9 +293,14 @@ if (new URLSearchParams(window.location.search).get('mobile') === 'true') {
function (g) {
g.preventDefault();
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
? new MouseEvent('mousemove', { movementX: g.movementX, movementY: g.movementY })
? new MouseEvent('mousemove', {
movementX: g.movementX,
movementY: g.movementY,
})
: new WheelEvent('wheel', { wheelDeltaY: g.movementY });
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);
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');
(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');
(n.style.cssText = 'left:10vh;bottom:20vh;'),
n.addEventListener(
'touchstart',
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,
),
@ -302,10 +340,17 @@ if (new URLSearchParams(window.location.search).get('mobile') === 'true') {
g || (g = I.pageX);
let i = I.pageX - g;
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
? (c.classList.add('active'), e.classList.remove('active'), C('a', 'keydown'), C('d', 'keyup'))
: (e.classList.remove('active'), c.classList.remove('active'));
? (c.classList.add('active'),
e.classList.remove('active'),
C('a', 'keydown'),
C('d', 'keyup'))
: (e.classList.remove('active'),
c.classList.remove('active'));
},
!1,
),
@ -403,7 +448,8 @@ if (new URLSearchParams(window.location.search).get('mobile') === 'true') {
C('shift', 'keydown'),
(clientWindow.crouchLock = !!clientWindow.crouchLock && null),
(clientWindow.crouchTimer = setTimeout(function (A) {
(clientWindow.crouchLock = null != clientWindow.crouchLock), M.classList.toggle('active');
(clientWindow.crouchLock = null != clientWindow.crouchLock),
M.classList.toggle('active');
}, 1e3));
},
!1,
@ -412,7 +458,9 @@ if (new URLSearchParams(window.location.search).get('mobile') === 'true') {
'touchend',
function (A) {
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);
},
!1,
@ -436,7 +484,8 @@ if (new URLSearchParams(window.location.search).get('mobile') === 'true') {
),
document.body.appendChild(v);
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(
'touchstart',
function (A) {
@ -461,28 +510,45 @@ if (new URLSearchParams(window.location.search).get('mobile') === 'true') {
'beforeinput',
function (A) {
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 (
(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.keyboardFix)
)
if ('insertLineBreak' == A.inputType) C('enter', 'keydown'), C('enter', 'keyup');
if ('insertLineBreak' == A.inputType)
C('enter', 'keydown'), C('enter', 'keyup');
else {
const g = A.inputType.slice(0, 1);
if ('i' == g && A.data) {
if (clientWindow.lastKey == I && clientWindow.blockNextInput && clientWindow.inputFix)
if (
clientWindow.lastKey == I &&
clientWindow.blockNextInput &&
clientWindow.inputFix
)
clientWindow.blockNextInput = !1;
else {
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')),
(clientWindow.blockNextInput = !0);
}
} else
('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 = ' ');
},
@ -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.',
),
(clientWindow.keyboardFix = !0),
clientWindow.lastKey && (C(clientWindow.lastKey, 'keydown'), C(clientWindow.lastKey, 'keyup')));
clientWindow.lastKey &&
(C(clientWindow.lastKey, 'keydown'),
C(clientWindow.lastKey, 'keyup')));
},
!1,
),
@ -513,7 +581,8 @@ if (new URLSearchParams(window.location.search).get('mobile') === 'true') {
}),
document.body.appendChild(h);
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(
'touchstart',
function (A) {
@ -525,7 +594,9 @@ if (new URLSearchParams(window.location.search).get('mobile') === 'true') {
'touchend',
function (A) {
A.preventDefault(),
clientWindow.hiddenInputFocused ? h.blur() : (h.select(), (clientWindow.hiddenInputFocused = !0));
clientWindow.hiddenInputFocused
? h.blur()
: (h.select(), (clientWindow.hiddenInputFocused = !0));
},
!1,
),
@ -626,7 +697,8 @@ if (new URLSearchParams(window.location.search).get('mobile') === 'true') {
C('r', 'keydown'),
(clientWindow.sprintLock = !!clientWindow.sprintLock && null),
(clientWindow.sprintTimer = setTimeout(function (A) {
(clientWindow.sprintLock = null != clientWindow.sprintLock), j.classList.toggle('active');
(clientWindow.sprintLock = null != clientWindow.sprintLock),
j.classList.toggle('active');
}, 1e3));
},
!1,
@ -635,14 +707,17 @@ if (new URLSearchParams(window.location.search).get('mobile') === 'true') {
'touchend',
function (A) {
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);
},
!1,
),
document.body.appendChild(j);
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(
'touchstart',
function (A) {
@ -659,7 +734,8 @@ if (new URLSearchParams(window.location.search).get('mobile') === 'true') {
),
document.body.appendChild(y);
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(
'touchstart',
function (A) {
@ -669,7 +745,8 @@ if (new URLSearchParams(window.location.search).get('mobile') === 'true') {
),
document.body.appendChild(L);
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(
'touchstart',
function (A) {
@ -686,7 +763,8 @@ if (new URLSearchParams(window.location.search).get('mobile') === 'true') {
),
document.body.appendChild(N);
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(
'touchstart',
function (A) {
@ -703,7 +781,8 @@ if (new URLSearchParams(window.location.search).get('mobile') === 'true') {
),
document.body.appendChild(U);
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(
'touchstart',
function (A) {

View File

@ -13,7 +13,9 @@ if (new URLSearchParams(window.location.search).get('mobile') === 'true') {
}
}
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?
window.crouchLock = false; // Used for crouch mobile control
@ -191,7 +193,12 @@ if (new URLSearchParams(window.location.search).get('mobile') === 'true') {
});
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(
new PointerEvent(state, {
button: number,
@ -277,7 +284,9 @@ if (new URLSearchParams(window.location.search).get('mobile') === 'true') {
const element = this._createElement(type);
if (type == 'input' && !ignore) {
// 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.addEventListener(
'change',
@ -401,9 +410,15 @@ if (new URLSearchParams(window.location.search).get('mobile') === 'true') {
function (e) {
e.preventDefault(); // Prevents window zoom when using two fingers
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
if (e.targetTouches[touchIndex].identifier == window.canvasPrimaryID) {
if (
e.targetTouches[touchIndex].identifier == window.canvasPrimaryID
) {
primaryTouch = e.targetTouches[touchIndex];
break;
}
@ -412,7 +427,8 @@ if (new URLSearchParams(window.location.search).get('mobile') === 'true') {
primaryTouch.distanceX = primaryTouch.clientX - canvasTouchStartX;
primaryTouch.distanceY = primaryTouch.clientY - canvasTouchStartY;
primaryTouch.squaredNorm =
primaryTouch.distanceX * primaryTouch.distanceX + primaryTouch.distanceY * primaryTouch.distanceY;
primaryTouch.distanceX * primaryTouch.distanceX +
primaryTouch.distanceY * primaryTouch.distanceY;
primaryTouch.movementX = primaryTouch.clientX - canvasTouchPreviousX;
primaryTouch.movementY = primaryTouch.clientY - canvasTouchPreviousY;
if (window.canvasTouchMode == 1) {
@ -452,7 +468,11 @@ if (new URLSearchParams(window.location.search).get('mobile') === 'true') {
);
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
if (e.changedTouches[touchIndex].identifier == window.canvasPrimaryID) {
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
setButtonVisibility(window.fakelock != null); //Updates our mobile controls when the canvas finally loads
// All of the touch buttons
const strafeRightButton = createTouchButton('strafeRightButton', 'inGame', 'div');
const strafeRightButton = createTouchButton(
'strafeRightButton',
'inGame',
'div',
);
strafeRightButton.classList.add('strafeSize');
strafeRightButton.style.cssText = 'left:24vh;bottom:22vh;';
document.body.appendChild(strafeRightButton);
const strafeLeftButton = createTouchButton('strafeLeftButton', 'inGame', 'div');
const strafeLeftButton = createTouchButton(
'strafeLeftButton',
'inGame',
'div',
);
strafeLeftButton.classList.add('strafeSize');
strafeLeftButton.style.cssText = 'left:5.5vh;bottom:22vh;';
document.body.appendChild(strafeLeftButton);
@ -666,7 +694,8 @@ if (new URLSearchParams(window.location.search).get('mobile') === 'true') {
document.body.appendChild(inventoryButton);
const exitButton = createTouchButton('exitButton', 'inMenu');
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(
'touchstart',
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
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
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) {
// 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;
}
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.
if (sliceInputType == 'i' && e.data) {
// 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 our key press matches the last unblocked key press and we are in duplicaye mode, ignore the input
window.blockNextInput = false;
@ -793,7 +832,8 @@ if (new URLSearchParams(window.location.search).get('mobile') === 'true') {
document.body.appendChild(hiddenInput);
const keyboardButton = createTouchButton('keyboardButton', 'inMenu');
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(
'touchstart',
function (e) {
@ -936,7 +976,8 @@ if (new URLSearchParams(window.location.search).get('mobile') === 'true') {
document.body.appendChild(sprintButton);
const pauseButton = createTouchButton('pauseButton', 'inGame');
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(
'touchstart',
function (e) {
@ -954,7 +995,8 @@ if (new URLSearchParams(window.location.search).get('mobile') === 'true') {
document.body.appendChild(pauseButton);
const chatButton = createTouchButton('chatButton', 'inGame');
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(
'touchstart',
function (e) {
@ -965,7 +1007,8 @@ if (new URLSearchParams(window.location.search).get('mobile') === 'true') {
document.body.appendChild(chatButton);
const perspectiveButton = createTouchButton('perspectiveButton', 'inGame');
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(
'touchstart',
function (e) {
@ -985,7 +1028,8 @@ if (new URLSearchParams(window.location.search).get('mobile') === 'true') {
document.body.appendChild(perspectiveButton);
const screenshotButton = createTouchButton('screenshotButton', 'inGame');
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(
'touchstart',
function (e) {
@ -1005,7 +1049,8 @@ if (new URLSearchParams(window.location.search).get('mobile') === 'true') {
document.body.appendChild(screenshotButton);
const coordinatesButton = createTouchButton('coordinatesButton', 'inGame');
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(
'touchstart',
function (e) {

View File

@ -12,9 +12,21 @@ window.addEventListener('load', () => {
{ addr: 'wss://play.brickmc.net', name: 'BrickMC' },
],
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.shhnowisnottheti.me/', comment: 'ayunami relay #1', primary: relayId === 2 },
{
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.shhnowisnottheti.me/',
comment: 'ayunami relay #1',
primary: relayId === 2,
},
],
};

View File

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

View File

@ -1,6 +1,9 @@
// @ts-nocheck
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);
window.minecraftOpts.push(urlParams.get('server') ?? undefined);

View File

@ -6,7 +6,9 @@ let articleAnimationLock = false;
const theme = {
load: function (themeToLoad?: string) {
const themeElement = document.querySelector('#theme') as HTMLLinkElement | null;
const themeElement = document.querySelector(
'#theme',
) as HTMLLinkElement | null;
if (themeElement)
themeElement.href = themeToLoad
? `/resources/styles/themes/${themeToLoad}.css`
@ -81,7 +83,9 @@ const game = {
'b1.3': 'b13-client-version',
};
const dropdown = clients[client]
? (document.querySelector(`select[id='${clients[client]}']`) as HTMLSelectElement | null)
? (document.querySelector(
`select[id='${clients[client]}']`,
) as HTMLSelectElement | null)
: null;
if (dropdown?.value) {
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 = {
open: function (articleId: string) {
const modal = document.querySelector(`.article[data-article-id='${articleId}']`) as HTMLElement | null;
const modalContent = document.querySelector(`.article[data-article-id='${articleId}'] > div`) as HTMLElement | null;
const modal = document.querySelector(
`.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) {
articleAnimationLock = true;
modal.style.animation = '0.5s ease-in-out 1 normal article';
@ -184,7 +192,9 @@ const article = {
}
},
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) {
articleAnimationLock = true;
modal.style.animation = '0.5s ease-in-out 1 reverse article-tempfix';
@ -238,7 +248,10 @@ const storage = {
}
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');
if (item === null) {
const json: Record<string, unknown> = {};
@ -268,7 +281,10 @@ const storage = {
}
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');
if (item === null) {
item = '{}';
@ -309,7 +325,9 @@ const mods = {
mods.push(mod);
mods.sort();
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) {
modInstallElem.textContent = 'Uninstall';
modInstallElem.classList.add('installed');
@ -317,7 +335,9 @@ const mods = {
} else {
mods.splice(modIndex, 1);
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) {
modInstallElem.textContent = 'Install';
modInstallElem.classList.remove('installed');
@ -403,7 +423,9 @@ if (window.location.pathname === '/') {
? '/mobile/'
: '/home/game/';
document.addEventListener('DOMContentLoaded', () => document.body.appendChild(iframe));
document.addEventListener('DOMContentLoaded', () =>
document.body.appendChild(iframe),
);
/* document.addEventListener('load', () => {
if (storage.local.get('offlineCache')) {
@ -433,9 +455,12 @@ if (window.location.pathname === '/') {
const titleBarText = document.querySelector('.title-bar');
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 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 (titleBarText) titleBarText.textContent += ` ${currentVersion}`;
@ -443,9 +468,14 @@ if (window.location.pathname === '/') {
if (
lastVersion &&
// @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);
}
});
@ -473,16 +503,28 @@ if (window.location.pathname === '/') {
if (window.location.pathname === '/settings/') {
document.addEventListener('DOMContentLoaded', async () => {
const profileName = document.querySelector('.username');
const usernameInput = document.querySelector('#username-input') as HTMLInputElement | null;
const themeSelect = document.querySelector('#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;
const usernameInput = document.querySelector(
'#username-input',
) as HTMLInputElement | null;
const themeSelect = document.querySelector(
'#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) {
usernameInput.placeholder = storage.local.get('username') ?? '';
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;
while (username.length < 3) username += '_';
@ -499,7 +541,9 @@ if (window.location.pathname === '/settings/') {
themeSelect?.appendChild(option);
});
themeSelect.value = storage.local.get('theme') ?? '';
themeSelect.addEventListener('change', () => theme.set(themeSelect.value ?? 'default'));
themeSelect.addEventListener('change', () =>
theme.set(themeSelect.value ?? 'default'),
);
}
if (offlineCheckbox) {
@ -523,22 +567,37 @@ if (window.location.pathname === '/settings/') {
adsCheckbox.addEventListener('change', () => {
storage.local.set('showAds', adsCheckbox.checked);
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/') {
document.addEventListener('DOMContentLoaded', async () => {
const setupForm = document.querySelector('#setup-form') as HTMLFormElement | null;
const usernameInput = document.querySelector('#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;
const setupForm = document.querySelector(
'#setup-form',
) as HTMLFormElement | null;
const usernameInput = document.querySelector(
'#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 (usernameInput) {
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;
});
}
@ -550,13 +609,17 @@ if (window.location.pathname === '/settings/') {
option.textContent = theme.name;
themeSelect?.appendChild(option);
});
themeSelect.addEventListener('change', () => theme.load(themeSelect.value ?? 'default'));
themeSelect.addEventListener('change', () =>
theme.load(themeSelect.value ?? 'default'),
);
}
setupForm.addEventListener('submit', async (event) => {
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 (!username) {
@ -572,7 +635,8 @@ if (window.location.pathname === '/settings/') {
storage.local.set('mods', []);
storage.local.set(
'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) {
@ -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 () => {
const addonType: 'mods' | 'resourcepacks' = window.location.pathname === '/mods/mods/' ? 'mods' : 'resourcepacks';
const addonType: 'mods' | 'resourcepacks' =
window.location.pathname === '/mods/mods/' ? 'mods' : 'resourcepacks';
const data: {
id: string;
name: string;
@ -607,15 +675,17 @@ if (window.location.pathname === '/settings/') {
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">${
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}.zip' '${addon.name}.zip')">Download</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.replace('\\', '\\\\').replace("'", "\\'")}.zip')">Download</span>`
}</div>`;
modList?.appendChild(modItem);
});
if (addonType === '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) => {
const modId = (element as HTMLElement).dataset['modId'];
if (installedMods.includes(`/resources/mods/downloads/${modId}.js`)) {
@ -628,8 +698,9 @@ if (window.location.pathname === '/settings/') {
} else if (window.location.pathname === '/updates/') {
document.addEventListener('DOMContentLoaded', async () => {
const updatesContainer = document.querySelector('.updates-container');
const data: { version: string; changelog: string[] }[] = (await (await fetch('/resources/data/main.json')).json())
.updates;
const data: { version: string; changelog: string[] }[] = (
await (await fetch('/resources/data/main.json')).json()
).updates;
data.forEach((update) => {
const version = document.createElement('div');
const name = document.createElement('strong');
@ -651,5 +722,14 @@ if (window.location.pathname === '/settings/') {
if (window.location.hostname === null) {
// 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);
}
.downloads a {
.downloads span {
background-color: #ff6600;
}
.downloads a:hover {
.downloads span:hover {
background-color: #ff8533;
}

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -4,9 +4,17 @@
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<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" 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/main.js"></script>
</head>
@ -62,7 +70,9 @@
</div>
</div>
<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>
</div>
</div>

View File

@ -4,9 +4,17 @@
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<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" 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/main.js"></script>
</head>
@ -78,7 +86,9 @@
</div>
</div>
<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>
</div>
</div>

View File

@ -4,9 +4,17 @@
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<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" 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/main.js"></script>
</head>
@ -61,7 +69,9 @@
</div>
</div>
<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>
</div>
</div>

View File

@ -4,9 +4,17 @@
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<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" id="theme" onload="document.documentElement.style.display = ''" />
<link
rel="stylesheet"
id="theme"
onload="document.documentElement.style.display = ''"
/>
<meta name="theme-color" content="#000000" />
<script src="/resources/scripts/google-tag.js"></script>
<script src="/resources/scripts/main.js"></script>
@ -22,7 +30,11 @@
<p>Let's get you setup.</p>
<form id="setup-form">
<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>
<select id="theme-select">
<option disabled hidden value=""></option>
@ -36,7 +48,9 @@
</div>
</div>
<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>
</div>
</div>

View File

@ -1,4 +1,9 @@
{
"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/"
]
}