1
0
mirror of https://github.com/zumbiepig/MineXLauncher.git synced 2025-06-08 09:24:48 +00:00

shaun pls fix, the function works but not the button

This commit is contained in:
zumbiepig 2024-09-09 21:19:29 -07:00
parent 369020cb53
commit 22ae488d08
No known key found for this signature in database
GPG Key ID: 17C891BE28B953DE
17 changed files with 441 additions and 126 deletions

BIN
bun.lockb

Binary file not shown.

View File

@ -20,6 +20,7 @@
"errorhandler": "^1.5.1", "errorhandler": "^1.5.1",
"express": "^4.19.2", "express": "^4.19.2",
"helmet": "^7.1.0", "helmet": "^7.1.0",
"indexeddb-export-import": "^2.1.5",
"morgan": "^1.10.0", "morgan": "^1.10.0",
"pako": "^2.1.0", "pako": "^2.1.0",
"semver": "^7.6.3", "semver": "^7.6.3",

View File

@ -55,7 +55,7 @@
<img src="/resources/images/icons/nav/articles.webp" /> <img src="/resources/images/icons/nav/articles.webp" />
<span>Tutorials</span> <span>Tutorials</span>
</li> </li>
<li onclick="navigate.settings()"> <li onclick="navigate.settings.general()">
<img src="/resources/images/icons/nav/settings.webp" /> <img src="/resources/images/icons/nav/settings.webp" />
<span>Settings</span> <span>Settings</span>
</li> </li>

105
src/console/index.html Normal file
View File

@ -0,0 +1,105 @@
<!doctype html>
<html lang="en" style="display: none">
<head>
<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="stylesheet" href="/resources/styles/themes/default.css" />
<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>
<style>
.console .debug {
color: darkgray;
}
.console .log {
color: black;
}
.console .info {
color: blue;
}
.console .warn {
color: orange;
}
.console .error {
color: red;
}
</style>
</head>
<body>
<div class="ads-container">
<ins
class="adsbygoogle"
style="display: block"
data-ad-client="ca-pub-1132419379737567"
data-ad-slot="3280170072"
data-ad-format="auto"
data-full-width-responsive="true"
></ins>
</div>
<div class="launcher">
<span class="title-bar">MineXLauncher</span>
<div class="content">
<div class="sidebar">
<span class="username"></span>
<nav class="nav-bar">
<li onclick="navigate.updates()">
<img src="/resources/images/icons/nav/updates.webp" />
<span>Updates</span>
</li>
<li class="selected" onclick="navigate.home.game()">
<img src="/resources/images/icons/nav/game.webp" />
<span>Game</span>
</li>
<li onclick="navigate.servers()">
<img src="/resources/images/icons/nav/servers.webp" />
<span>Server List</span>
</li>
<li onclick="navigate.mods.mods()">
<img src="/resources/images/icons/nav/mods.webp" />
<span>Mods</span>
</li>
<li onclick="navigate.articles()">
<img src="/resources/images/icons/nav/articles.webp" />
<span>Tutorials</span>
</li>
<li onclick="navigate.settings.general()">
<img src="/resources/images/icons/nav/settings.webp" />
<span>Settings</span>
</li>
</nav>
</div>
<div class="main-panel">
<div class="main-content">
<div class="console"></div>
</div>
</div>
</div>
<div class="bottom-bar">
<span onclick="window.open('https://discord.gg/VRwbRJjXzt')"
>Join the MineXLauncher Discord</span
>
<span>© 2024 MineXLauncher. All rights reserved.</span>
</div>
</div>
<div class="ads-container">
<ins
class="adsbygoogle"
style="display: block"
data-ad-client="ca-pub-1132419379737567"
data-ad-slot="3280170072"
data-ad-format="auto"
data-full-width-responsive="true"
></ins>
</div>
</body>
</html>

View File

@ -55,7 +55,7 @@
<img src="/resources/images/icons/nav/articles.webp" /> <img src="/resources/images/icons/nav/articles.webp" />
<span>Tutorials</span> <span>Tutorials</span>
</li> </li>
<li onclick="navigate.settings()"> <li onclick="navigate.settings.general()">
<img src="/resources/images/icons/nav/settings.webp" /> <img src="/resources/images/icons/nav/settings.webp" />
<span>Settings</span> <span>Settings</span>
</li> </li>

View File

@ -55,7 +55,7 @@
<img src="/resources/images/icons/nav/articles.webp" /> <img src="/resources/images/icons/nav/articles.webp" />
<span>Tutorials</span> <span>Tutorials</span>
</li> </li>
<li onclick="navigate.settings()"> <li onclick="navigate.settings.general()">
<img src="/resources/images/icons/nav/settings.webp" /> <img src="/resources/images/icons/nav/settings.webp" />
<span>Settings</span> <span>Settings</span>
</li> </li>

View File

@ -55,7 +55,7 @@
<img src="/resources/images/icons/nav/articles.webp" /> <img src="/resources/images/icons/nav/articles.webp" />
<span>Tutorials</span> <span>Tutorials</span>
</li> </li>
<li onclick="navigate.settings()"> <li onclick="navigate.settings.general()">
<img src="/resources/images/icons/nav/settings.webp" /> <img src="/resources/images/icons/nav/settings.webp" />
<span>Settings</span> <span>Settings</span>
</li> </li>

View File

@ -55,7 +55,7 @@
<img src="/resources/images/icons/nav/articles.webp" /> <img src="/resources/images/icons/nav/articles.webp" />
<span>Tutorials</span> <span>Tutorials</span>
</li> </li>
<li onclick="navigate.settings()"> <li onclick="navigate.settings.general()">
<img src="/resources/images/icons/nav/settings.webp" /> <img src="/resources/images/icons/nav/settings.webp" />
<span>Settings</span> <span>Settings</span>
</li> </li>

View File

@ -55,7 +55,7 @@
<img src="/resources/images/icons/nav/articles.webp" /> <img src="/resources/images/icons/nav/articles.webp" />
<span>Tutorials</span> <span>Tutorials</span>
</li> </li>
<li onclick="navigate.settings()"> <li onclick="navigate.settings.general()">
<img src="/resources/images/icons/nav/settings.webp" /> <img src="/resources/images/icons/nav/settings.webp" />
<span>Settings</span> <span>Settings</span>
</li> </li>

View File

@ -55,7 +55,7 @@
<img src="/resources/images/icons/nav/articles.webp" /> <img src="/resources/images/icons/nav/articles.webp" />
<span>Tutorials</span> <span>Tutorials</span>
</li> </li>
<li onclick="navigate.settings()"> <li onclick="navigate.settings.general()">
<img src="/resources/images/icons/nav/settings.webp" /> <img src="/resources/images/icons/nav/settings.webp" />
<span>Settings</span> <span>Settings</span>
</li> </li>

View File

@ -1,57 +0,0 @@
// @ts-nocheck
window.addEventListener('load', () => {
const relayId = Math.floor(Math.random() * 3);
window.eaglercraftXOpts = {
container: 'game_frame',
assetsURI: `${window.location.pathname}/assets.epk`,
localesURI: `${window.location.pathname}/lang/`,
servers: [
{ addr: 'wss://mc.ricenetwork.xyz', name: 'Rice Network' },
{ addr: 'wss://mc.lamplifesteal.xyz', name: 'LampLifesteal' },
{ addr: 'wss://electronmc.club', name: 'Electron Network' },
{ 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,
},
],
};
const storage = {
local: {
get: function (key: string) {
const item = localStorage.getItem('minexlauncher');
if (item !== null) {
const json = JSON.parse(item);
if (json[key] !== undefined) {
return json[key];
} else {
return null;
}
} else {
return null;
}
},
},
};
const urlParams = new URLSearchParams(window.location.search);
window.eaglercraftXOpts.joinServer = urlParams.get('server') ?? undefined;
window.eaglercraftXOpts.Mods = storage.local.get('mods') ?? [];
history.replaceState({}, '', '/play');
main();
});

View File

@ -1,5 +1,6 @@
import { gt, coerce } from 'semver'; import { gt, coerce } from 'semver';
import { inflate, deflate } from 'pako'; import { inflate, deflate } from 'pako';
import idbExportImport from 'indexeddb-export-import';
let selectedVersion: string | undefined = undefined; let selectedVersion: string | undefined = undefined;
let articleAnimationLock = false; let articleAnimationLock = false;
@ -47,23 +48,78 @@ const versionSelector = {
}, },
}; };
function consoleLog(
type: 'debug' | 'log' | 'info' | 'warn' | 'error',
msg: string,
) {
const consoleElement = document.querySelector('.console');
if (consoleElement) {
const messageElement = document.createElement('p');
messageElement.classList.add(type);
messageElement.innerText = msg;
consoleElement.appendChild(messageElement);
storage.session.set(
'console',
base64Gzip.compress(consoleElement.innerHTML),
);
consoleElement.scroll(0, consoleElement.scrollHeight);
}
}
const game = { const game = {
play: function (version?: string) { play: function (version?: string) {
if (version) { version = version ?? selectedVersion;
document.body.style.display = 'none'; if (!version) {
storage.session.set('lastGame', version);
// @ts-expect-error
window.top.location.href = version;
} else if (selectedVersion) {
document.body.style.display = 'none';
storage.session.set('lastGame', selectedVersion);
// @ts-expect-error
window.top.location.href = selectedVersion;
} else {
alert('Please select a version to play.'); alert('Please select a version to play.');
return; return;
} }
document.body.style.display = 'none';
storage.session.set('lastGame', selectedVersion);
window.open(version, '_blank', 'popup=true');
/*window.top.instanceWindow = window.open(version);
window.top.instanceWindow.onload = function () {
window.top.instanceWindow.history.replaceState({}, '', '/play');
window.top.instanceWindow.console.debug = (msg: string) =>
consoleLog('debug', msg);
window.top.instanceWindow.console.log = (msg: string) =>
consoleLog('log', msg);
window.top.instanceWindow.console.info = (msg: string) =>
consoleLog('info', msg);
window.top.instanceWindow.console.warn = (msg: string) =>
consoleLog('warn', msg);
window.top.instanceWindow.console.error = (msg: string) =>
consoleLog('error', msg);
};
const waitForCrash = setInterval(() => {
if (window.top.instanceWindow.closed) {
clearInterval(waitForCrash);
game.stop();
} else {
window.top.instanceWindow.document
.querySelectorAll('div')
.forEach((element: HTMLElement) => {
if (element.innerText.includes('Game Crashed!')) {
clearInterval(waitForCrash);
game.stop(element.innerHTML);
}
});
}
}, 50);*/
}, },
/*stop: function (error?: string) {
if (window.top.instanceWindow) {
window.top.instanceWindow.onbeforeunload = null;
window.top.instanceWindow.close();
if (error) {
consoleLog('error', '\n\nMineXLauncher: Game crashed with error:');
consoleLog('error', error);
} else {
consoleLog('error', '\n\nMineXLauncher: Game process killed by user');
}
}
},*/
select: function (path: string, name?: string) { select: function (path: string, name?: string) {
selectedVersion = path; selectedVersion = path;
const selector = document.querySelector('.installations div .selector'); const selector = document.querySelector('.installations div .selector');
@ -135,6 +191,18 @@ const navigate = {
window.location.href = navUrl; window.location.href = navUrl;
}, },
}, },
settings: {
general: function () {
const navUrl = '/settings/general/';
storage.session.set('lastPage', navUrl);
window.location.href = navUrl;
},
backups: function () {
const navUrl = '/settings/backups/';
storage.session.set('lastPage', navUrl);
window.location.href = navUrl;
},
},
articles: function () { articles: function () {
const navUrl = '/articles/'; const navUrl = '/articles/';
storage.session.set('lastPage', navUrl); storage.session.set('lastPage', navUrl);
@ -155,11 +223,6 @@ const navigate = {
storage.session.set('lastPage', navUrl); storage.session.set('lastPage', navUrl);
window.location.href = navUrl; window.location.href = navUrl;
}, },
settings: function () {
const navUrl = '/settings/';
storage.session.set('lastPage', navUrl);
window.location.href = navUrl;
},
}; };
const article = { const article = {
@ -346,6 +409,117 @@ const mods = {
}, },
}; };
const backups = {
export: async function () {
const exportData: {
cookies: Record<string, string>;
localStorage: Record<string, string>;
indexedDb: Record<string, string>;
} = {
cookies: {},
localStorage: { ...localStorage },
indexedDb: {},
};
document.cookie.split('; ').forEach((cookie) => {
const pair = cookie.split('=');
if (pair[0] !== undefined && pair[1] !== undefined)
exportData.cookies[pair[0]] = pair[1];
});
for (const dbInfo of await indexedDB.databases()) {
if (dbInfo.name)
indexedDB.open(dbInfo.name).onsuccess = (event) =>
(exportData.indexedDb[dbInfo.name ?? ''] =
idbExportImport.exportToJsonString(
(event.target as IDBOpenDBRequest).result,
));
}
const url = URL.createObjectURL(
new Blob([base64Gzip.compress(JSON.stringify(exportData))]),
);
const a = document.createElement('a');
a.href = url;
a.target = '_blank';
a.download = `MineXLauncher_${new Date().toLocaleDateString('en-GB').replace(/\//g, '-')}.backup`;
document.body.appendChild(a);
a.click();
document.body.removeChild(a);
URL.revokeObjectURL(url);
},
import: async function () {
await new Promise<File>((resolve, reject) => {
const fileInput = document.createElement('input');
fileInput.type = 'file';
fileInput.accept = '.backup';
fileInput.multiple = false;
fileInput.onchange = (event) => {
const file = (event.target as HTMLInputElement).files?.[0];
if (file) resolve(file);
else reject();
};
fileInput.oncancel = () => reject();
document.body.appendChild(fileInput);
fileInput.click();
document.body.removeChild(fileInput);
})
.then(async (file) => {
const importData = JSON.parse(base64Gzip.decompress(await file.text()));
// @ts-expect-error
if (typeof cookieStore !== 'undefined')
// @ts-expect-error
await cookieStore.getAll().then((cookies: object[]) =>
// @ts-expect-error
cookies.forEach((cookie) => cookieStore.delete(cookie)),
);
else
document.cookie
.split('; ')
.forEach(
(cookie) =>
(document.cookie = `${cookie.split('=')[0]}=; Path=/; Max-Age=0`),
);
localStorage.clear();
for (const dbInfo of await indexedDB.databases())
if (dbInfo.name) indexedDB.deleteDatabase(dbInfo.name);
for (const key in importData.localStorage)
if (importData.localStorage[key] !== undefined)
localStorage.setItem(key, importData.localStorage[key]);
for (const key in importData.cookies)
document.cookie = `${key}=${importData.cookies[key]}; Max-Age=${365 * 24 * 60 * 60}; Path=/; SameSite=Lax; Secure`;
if (Object.keys(importData.indexedDb).length > 0)
for (const dbName in importData.indexedDb) {
const dbRequest = indexedDB.open(dbName);
dbRequest.onsuccess = async (event) => {
const db = (event.target as IDBOpenDBRequest).result;
const transaction = db.transaction(
Array.from(db.objectStoreNames),
'readwrite',
);
for (const storeName of Array.from(db.objectStoreNames))
transaction.objectStore(storeName).clear();
transaction.oncomplete = () =>
idbExportImport.importFromJsonString(
db,
importData.indexedDb[dbName],
);
};
}
window.top?.location.reload();
})
.catch((error) => console.error(error));
},
};
const sw = { const sw = {
register: function (url: string) { register: function (url: string) {
if ('serviceWorker' in navigator) { if ('serviceWorker' in navigator) {
@ -365,40 +539,16 @@ const sw = {
}; };
const base64Gzip = { const base64Gzip = {
decode: function (base64: string) { compress: function (string: string): string {
// Decode Base64 to binary string return btoa(
const binaryString = atob(base64); String.fromCharCode(...deflate(new TextEncoder().encode(string))),
);
// Convert binary string to Uint8Array
const len = binaryString.length;
const bytes = new Uint8Array(len);
for (let i = 0; i < len; i++) {
bytes[i] = binaryString.charCodeAt(i);
}
// Use pako to decompress the Uint8Array
const decompressed = inflate(bytes, { to: 'string' });
return decompressed;
}, },
encode: function (inputString: string) { decompress: function (string: string): string {
// Convert the input string to a Uint8Array return inflate(
const encoder = new TextEncoder(); Uint8Array.from(atob(string), (c) => c.charCodeAt(0)),
const inputBytes = encoder.encode(inputString); { to: 'string' },
);
// Use pako to compress the Uint8Array
const compressedBytes = deflate(inputBytes);
// Convert the compressed Uint8Array to a binary string
let binaryString = '';
for (const byte of compressedBytes) {
binaryString += String.fromCharCode(byte);
}
// Encode the binary string to Base64
const base64String = btoa(binaryString);
return base64String;
}, },
}; };
@ -435,6 +585,7 @@ if (window.location.pathname === '/') {
} }
}); */ }); */
document.addEventListener('load', () => sw.register('/sw.js')); document.addEventListener('load', () => sw.register('/sw.js'));
window.addEventListener('beforeunload', () => game.stop());
window.addEventListener('beforeinstallprompt', (event) => { window.addEventListener('beforeinstallprompt', (event) => {
// @ts-expect-error // @ts-expect-error
@ -500,7 +651,7 @@ if (window.location.pathname === '/') {
} */ } */
} }
if (window.location.pathname === '/settings/') { if (window.location.pathname === '/settings/general/') {
document.addEventListener('DOMContentLoaded', async () => { document.addEventListener('DOMContentLoaded', async () => {
const profileName = document.querySelector('.username'); const profileName = document.querySelector('.username');
const usernameInput = document.querySelector( const usernameInput = document.querySelector(
@ -731,5 +882,6 @@ if (window.location.hostname === null) {
base64Gzip, base64Gzip,
article, article,
downloadFile, downloadFile,
backups,
]); ]);
} }

View File

@ -699,6 +699,10 @@ body {
outline: none; outline: none;
} }
.settings form[id='settings-backups'] {
padding-bottom: 60px;
}
.settings form button[type='submit'] { .settings form button[type='submit'] {
width: 100%; width: 100%;
padding: 10px; padding: 10px;

View File

@ -55,7 +55,7 @@
<img src="/resources/images/icons/nav/articles.webp" /> <img src="/resources/images/icons/nav/articles.webp" />
<span>Tutorials</span> <span>Tutorials</span>
</li> </li>
<li onclick="navigate.settings()"> <li onclick="navigate.settings.general()">
<img src="/resources/images/icons/nav/settings.webp" /> <img src="/resources/images/icons/nav/settings.webp" />
<span>Settings</span> <span>Settings</span>
</li> </li>

View File

@ -0,0 +1,107 @@
<!doctype html>
<html lang="en" style="display: none">
<head>
<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="stylesheet" href="/resources/styles/themes/default.css" />
<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>
<body>
<div class="ads-container">
<ins
class="adsbygoogle"
style="display: block"
data-ad-client="ca-pub-1132419379737567"
data-ad-slot="3280170072"
data-ad-format="auto"
data-full-width-responsive="true"
></ins>
</div>
<div class="launcher">
<span class="title-bar">MineXLauncher</span>
<div class="content">
<div class="sidebar">
<span class="username"></span>
<nav class="nav-bar">
<li onclick="navigate.updates()">
<img src="/resources/images/icons/nav/updates.webp" />
<span>Updates</span>
</li>
<li onclick="navigate.home.game()">
<img src="/resources/images/icons/nav/game.webp" />
<span>Game</span>
</li>
<li onclick="navigate.servers()">
<img src="/resources/images/icons/nav/servers.webp" />
<span>Server List</span>
</li>
<li onclick="navigate.mods.mods()">
<img src="/resources/images/icons/nav/mods.webp" />
<span>Mods</span>
</li>
<li onclick="navigate.articles()">
<img src="/resources/images/icons/nav/articles.webp" />
<span>Tutorials</span>
</li>
<li class="selected" onclick="navigate.settings.general()">
<img src="/resources/images/icons/nav/settings.webp" />
<span>Settings</span>
</li>
</nav>
</div>
<div class="main-panel">
<div class="top-nav">
<ul>
<li onclick="navigate.settings.general()">General</li>
<li class="selected" onclick="navigate.settings.backups()">
Backups
</li>
</ul>
</div>
<div class="main-content">
<div class="settings">
<form id="settings-backups">
<h3>Backups</h3>
<p>
Save a backup of your launcher data.<br />Note: Backing up
worlds is not yet supported. Please export them from
Eaglercraft for now.
</p>
<button onclick="backups.export()">Export backup</button>
<button onclick="backups.import()">Import backup</button>
</form>
</div>
</div>
</div>
</div>
<div class="bottom-bar">
<span onclick="window.open('https://discord.gg/VRwbRJjXzt')"
>Join the MineXLauncher Discord</span
>
<span>© 2024 MineXLauncher. All rights reserved.</span>
</div>
</div>
<div class="ads-container">
<ins
class="adsbygoogle"
style="display: block"
data-ad-client="ca-pub-1132419379737567"
data-ad-slot="3280170072"
data-ad-format="auto"
data-full-width-responsive="true"
></ins>
</div>
</body>
</html>

View File

@ -55,14 +55,21 @@
<img src="/resources/images/icons/nav/articles.webp" /> <img src="/resources/images/icons/nav/articles.webp" />
<span>Tutorials</span> <span>Tutorials</span>
</li> </li>
<li class="selected" onclick="navigate.settings()"> <li class="selected" onclick="navigate.settings.general()">
<img src="/resources/images/icons/nav/settings.webp" /> <img src="/resources/images/icons/nav/settings.webp" />
<span>Settings</span> <span>Settings</span>
</li> </li>
</nav> </nav>
</div> </div>
<div class="main-panel"> <div class="main-panel">
<span class="top-title">Settings</span> <div class="top-nav">
<ul>
<li class="selected" onclick="navigate.settings.general()">
General
</li>
<li onclick="navigate.settings.backups()">Backups</li>
</ul>
</div>
<div class="main-content"> <div class="main-content">
<div class="settings"> <div class="settings">
<form> <form>
@ -72,15 +79,11 @@
<select id="theme-select"> <select id="theme-select">
<option disabled hidden value=""></option> <option disabled hidden value=""></option>
</select> </select>
<!-- <label for="offline-checkbox">Enable offline use:</label>
<input id="offline-checkbox" type="checkbox" /> -->
<!-- <label for="ads-checkbox">Show ads:</label>
<input id="ads-checkbox" type="checkbox" /> -->
</form> </form>
<!-- <div>
<label for="offline-checkbox">Enable offline use:</label>
<input id="offline-checkbox" type="checkbox" />
</div> -->
<!-- <div>
<label for="ads-checkbox">Show ads:</label>
<input id="ads-checkbox" type="checkbox" />
</div> -->
</div> </div>
</div> </div>
</div> </div>

View File

@ -55,7 +55,7 @@
<img src="/resources/images/icons/nav/articles.webp" /> <img src="/resources/images/icons/nav/articles.webp" />
<span>Tutorials</span> <span>Tutorials</span>
</li> </li>
<li onclick="navigate.settings()"> <li onclick="navigate.settings.general()">
<img src="/resources/images/icons/nav/settings.webp" /> <img src="/resources/images/icons/nav/settings.webp" />
<span>Settings</span> <span>Settings</span>
</li> </li>