mirror of
https://github.com/dscalzi/HeliosLauncher.git
synced 2024-12-22 03:32:12 -08:00
Add new dlAsync function and mod validation messages
Implemented a new `dlAsync.js` file and updated the `en_US.toml` with new mod validation messages. This includes the logic for mod verification and error handling, ensuring the integrity of mods before launching the game. Ajout prochainement de l'utilitaire npm run athshield
This commit is contained in:
parent
9813802b1c
commit
5f3e229360
1030
ajouts/ancien code HeliosLauncher/landing.js
Normal file
1030
ajouts/ancien code HeliosLauncher/landing.js
Normal file
File diff suppressed because it is too large
Load Diff
305
ajouts/ancien code athena shield/dlAsync.js
Normal file
305
ajouts/ancien code athena shield/dlAsync.js
Normal file
@ -0,0 +1,305 @@
|
|||||||
|
/**
|
||||||
|
* @Name dlAsync Function
|
||||||
|
* @async
|
||||||
|
* @param {boolean} login
|
||||||
|
* @returns {Promise<void>}
|
||||||
|
*
|
||||||
|
* @author Sandro642
|
||||||
|
* @Cheating Athena's Shield
|
||||||
|
* @Ajout Liste blanche des mods
|
||||||
|
*/
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @Révision le XX.XX.2024 périme le 01.01.2025
|
||||||
|
* @Bug découvert : 0
|
||||||
|
* @Athena's Shield
|
||||||
|
* @Sandro642
|
||||||
|
*/
|
||||||
|
|
||||||
|
// ▄▄▄ ▄▄▄█████▓ ██░ ██ ▓█████ ███▄ █ ▄▄▄ ██████ ██████ ██░ ██ ██▓▓█████ ██▓ ▓█████▄
|
||||||
|
// ▒████▄ ▓ ██▒ ▓▒▓██░ ██▒▓█ ▀ ██ ▀█ █ ▒████▄ ▒██ ▒ ▒██ ▒ ▓██░ ██▒▓██▒▓█ ▀ ▓██▒ ▒██▀ ██▌
|
||||||
|
// ▒██ ▀█▄ ▒ ▓██░ ▒░▒██▀▀██░▒███ ▓██ ▀█ ██▒▒██ ▀█▄ ░ ▓██▄ ░ ▓██▄ ▒██▀▀██░▒██▒▒███ ▒██░ ░██ █▌
|
||||||
|
// ░██▄▄▄▄██░ ▓██▓ ░ ░▓█ ░██ ▒▓█ ▄ ▓██▒ ▐▌██▒░██▄▄▄▄██ ▒ ██▒ ▒ ██▒░▓█ ░██ ░██░▒▓█ ▄ ▒██░ ░▓█▄ ▌
|
||||||
|
// ▓█ ▓██▒ ▒██▒ ░ ░▓█▒░██▓░▒████▒▒██░ ▓██░ ▓█ ▓██▒▒██████▒▒ ▒██████▒▒░▓█▒░██▓░██░░▒████▒░██████▒░▒████▓
|
||||||
|
// ▒▒ ▓▒█░ ▒ ░░ ▒ ░░▒░▒░░ ▒░ ░░ ▒░ ▒ ▒ ▒▒ ▓▒█░▒ ▒▓▒ ▒ ░ ▒ ▒▓▒ ▒ ░ ▒ ░░▒░▒░▓ ░░ ▒░ ░░ ▒░▓ ░ ▒▒▓ ▒
|
||||||
|
// ▒ ▒▒ ░ ░ ▒ ░▒░ ░ ░ ░ ░░ ░░ ░ ▒░ ▒ ▒▒ ░░ ░▒ ░ ░ ░ ░▒ ░ ░ ▒ ░▒░ ░ ▒ ░ ░ ░ ░░ ░ ▒ ░ ░ ▒ ▒
|
||||||
|
// ░ ▒ ░ ░ ░░ ░ ░ ░ ░ ░ ░ ▒ ░ ░ ░ ░ ░ ░ ░ ░░ ░ ▒ ░ ░ ░ ░ ░ ░ ░
|
||||||
|
// ░ ░ ░ ░ ░ ░ ░ ░ ░ ░ ░ ░ ░ ░ ░ ░ ░ ░ ░ ░ ░
|
||||||
|
// ░
|
||||||
|
|
||||||
|
// Keep reference to Minecraft Process
|
||||||
|
let proc;
|
||||||
|
// Is DiscordRPC enabled
|
||||||
|
let hasRPC = false;
|
||||||
|
// Joined server regex
|
||||||
|
const GAME_JOINED_REGEX = /\[.+\]: Sound engine started/;
|
||||||
|
const GAME_LAUNCH_REGEX = /^\[.+\]: (?:MinecraftForge .+ Initialized|ModLauncher .+ starting: .+|Loading Minecraft .+ with Fabric Loader .+)$/
|
||||||
|
const MIN_LINGER = 5000;
|
||||||
|
|
||||||
|
// List of mods to exclude from validation
|
||||||
|
const EXCLUDED_MODS = [
|
||||||
|
];
|
||||||
|
|
||||||
|
async function dlAsync(login = true) {
|
||||||
|
const loggerLaunchSuite = LoggerUtil.getLogger('LaunchSuite');
|
||||||
|
const loggerLanding = LoggerUtil.getLogger('Landing');
|
||||||
|
setLaunchDetails(Lang.queryJS('landing.dlAsync.loadingServerInfo'));
|
||||||
|
|
||||||
|
let distro;
|
||||||
|
|
||||||
|
try {
|
||||||
|
distro = await DistroAPI.refreshDistributionOrFallback();
|
||||||
|
onDistroRefresh(distro);
|
||||||
|
} catch (err) {
|
||||||
|
loggerLaunchSuite.error('Unable to refresh distribution index.', err);
|
||||||
|
showLaunchFailure(Lang.queryJS('landing.dlAsync.fatalError'), Lang.queryJS('landing.dlAsync.unableToLoadDistributionIndex'));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const serv = distro.getServerById(ConfigManager.getSelectedServer());
|
||||||
|
|
||||||
|
if (login) {
|
||||||
|
if (ConfigManager.getSelectedAccount() == null) {
|
||||||
|
loggerLanding.error('You must be logged into an account.');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// --------- Mod Verification Logic ---------
|
||||||
|
const modsDir = path.join(ConfigManager.getDataDirectory(), 'instances', serv.rawServer.id, 'mods');
|
||||||
|
|
||||||
|
// Check if mods directory exists, if not, create it
|
||||||
|
if (!fs.existsSync(modsDir)) {
|
||||||
|
fs.mkdirSync(modsDir, { recursive: true });
|
||||||
|
}
|
||||||
|
|
||||||
|
const distroMods = {};
|
||||||
|
const mdls = serv.modules;
|
||||||
|
|
||||||
|
// Populate expected mod identities and log them
|
||||||
|
mdls.forEach(mdl => {
|
||||||
|
if (mdl.rawModule.name.endsWith('.jar')) {
|
||||||
|
const modPath = path.join(modsDir, mdl.rawModule.name);
|
||||||
|
const modIdentity = mdl.rawModule.identity || mdl.rawModule.MD5;
|
||||||
|
loggerLanding.info(`Expected Identity from Distribution for ${mdl.rawModule.name}: ${modIdentity}`);
|
||||||
|
distroMods[modPath] = modIdentity;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
// Function to extract mod identity from the jar file
|
||||||
|
const extractModIdentity = (filePath) => {
|
||||||
|
loggerLanding.info(`Extracting identity for mod at: ${filePath}`);
|
||||||
|
const zip = new AdmZip(filePath);
|
||||||
|
const manifestEntry = zip.getEntry('META-INF/MANIFEST.MF');
|
||||||
|
|
||||||
|
if (manifestEntry) {
|
||||||
|
const manifestContent = manifestEntry.getData().toString('utf8');
|
||||||
|
const lines = manifestContent.split('\n');
|
||||||
|
const identityLine = lines.find(line => line.startsWith('Mod-Id:') || line.startsWith('Implementation-Title:'));
|
||||||
|
if (identityLine) {
|
||||||
|
loggerLanding.info(`Found identity in manifest for ${filePath}: ${identityLine}`);
|
||||||
|
return identityLine.split(':')[1].trim();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Fall back to a hash if no identity is found
|
||||||
|
const fileBuffer = fs.readFileSync(filePath);
|
||||||
|
const hashSum = crypto.createHash('md5'); // Use MD5 to match the distribution configuration
|
||||||
|
hashSum.update(fileBuffer);
|
||||||
|
const hash = hashSum.digest('hex');
|
||||||
|
loggerLanding.info(`No identity found in manifest for ${filePath}, using hash: ${hash}`);
|
||||||
|
return hash;
|
||||||
|
};
|
||||||
|
|
||||||
|
// Validate mods function
|
||||||
|
const validateMods = () => {
|
||||||
|
loggerLanding.info("Starting mod validation...");
|
||||||
|
const installedMods = fs.readdirSync(modsDir);
|
||||||
|
let valid = true;
|
||||||
|
|
||||||
|
for (let mod of installedMods) {
|
||||||
|
const modPath = path.join(modsDir, mod);
|
||||||
|
|
||||||
|
// Skip validation for mods in the excluded list
|
||||||
|
if (EXCLUDED_MODS.includes(mod)) {
|
||||||
|
loggerLanding.info(`Skipping validation for excluded mod: ${mod}`);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
const expectedIdentity = distroMods[modPath];
|
||||||
|
loggerLanding.info(`Validating mod: ${mod}`);
|
||||||
|
|
||||||
|
if (expectedIdentity) {
|
||||||
|
const modIdentity = extractModIdentity(modPath);
|
||||||
|
loggerLanding.info(`Expected Identity: ${expectedIdentity}, Calculated Identity for ${mod}: ${modIdentity}`);
|
||||||
|
if (modIdentity !== expectedIdentity) {
|
||||||
|
loggerLanding.error(`Mod identity mismatch! Mod: ${mod}, Expected: ${expectedIdentity}, Found: ${modIdentity}`);
|
||||||
|
valid = false;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
loggerLanding.warn(`No expected identity found for mod: ${mod}. Marking as invalid.`);
|
||||||
|
valid = false;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
loggerLanding.info("Mod validation completed.");
|
||||||
|
return valid;
|
||||||
|
};
|
||||||
|
|
||||||
|
// Perform mod validation before proceeding
|
||||||
|
if (!validateMods()) {
|
||||||
|
const errorMessage = `Athena's Shield a détecté des mods non valides. Veuillez supprimer le dossier .folder et redémarrer le lanceur.`;
|
||||||
|
loggerLanding.error(errorMessage);
|
||||||
|
showLaunchFailure(errorMessage, null);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
// --------- End of Mod Verification Logic ---------
|
||||||
|
|
||||||
|
setLaunchDetails(Lang.queryJS('landing.dlAsync.pleaseWait'));
|
||||||
|
toggleLaunchArea(true);
|
||||||
|
setLaunchPercentage(0, 100);
|
||||||
|
|
||||||
|
const fullRepairModule = new FullRepair(
|
||||||
|
ConfigManager.getCommonDirectory(),
|
||||||
|
ConfigManager.getInstanceDirectory(),
|
||||||
|
ConfigManager.getLauncherDirectory(),
|
||||||
|
ConfigManager.getSelectedServer(),
|
||||||
|
DistroAPI.isDevMode()
|
||||||
|
);
|
||||||
|
|
||||||
|
fullRepairModule.spawnReceiver();
|
||||||
|
|
||||||
|
fullRepairModule.childProcess.on('error', (err) => {
|
||||||
|
loggerLaunchSuite.error('Error during launch', err);
|
||||||
|
showLaunchFailure(Lang.queryJS('landing.dlAsync.errorDuringLaunchTitle'), err.message || Lang.queryJS('landing.dlAsync.errorDuringLaunchText'));
|
||||||
|
});
|
||||||
|
fullRepairModule.childProcess.on('close', (code, _signal) => {
|
||||||
|
if(code !== 0){
|
||||||
|
loggerLaunchSuite.error(`Full Repair Module exited with code ${code}, assuming error.`);
|
||||||
|
showLaunchFailure(Lang.queryJS('landing.dlAsync.errorDuringLaunchTitle'), Lang.queryJS('landing.dlAsync.seeConsoleForDetails'));
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
loggerLaunchSuite.info('Validating files.');
|
||||||
|
setLaunchDetails(Lang.queryJS('landing.dlAsync.validatingFileIntegrity'));
|
||||||
|
let invalidFileCount = 0;
|
||||||
|
try {
|
||||||
|
invalidFileCount = await fullRepairModule.verifyFiles(percent => {
|
||||||
|
setLaunchPercentage(percent);
|
||||||
|
});
|
||||||
|
setLaunchPercentage(100);
|
||||||
|
} catch (err) {
|
||||||
|
loggerLaunchSuite.error('Error during file validation.');
|
||||||
|
showLaunchFailure(Lang.queryJS('landing.dlAsync.errorDuringFileVerificationTitle'), err.displayable || Lang.queryJS('landing.dlAsync.seeConsoleForDetails'));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if(invalidFileCount > 0) {
|
||||||
|
loggerLaunchSuite.info('Downloading files.');
|
||||||
|
setLaunchDetails(Lang.queryJS('landing.dlAsync.downloadingFiles'));
|
||||||
|
setLaunchPercentage(0);
|
||||||
|
try {
|
||||||
|
await fullRepairModule.download(percent => {
|
||||||
|
setDownloadPercentage(percent);
|
||||||
|
});
|
||||||
|
setDownloadPercentage(100);
|
||||||
|
} catch(err) {
|
||||||
|
loggerLaunchSuite.error('Error during file download.');
|
||||||
|
showLaunchFailure(Lang.queryJS('landing.dlAsync.errorDuringFileDownloadTitle'), err.displayable || Lang.queryJS('landing.dlAsync.seeConsoleForDetails'));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
loggerLaunchSuite.info('No invalid files, skipping download.');
|
||||||
|
}
|
||||||
|
|
||||||
|
// Remove download bar.
|
||||||
|
remote.getCurrentWindow().setProgressBar(-1);
|
||||||
|
|
||||||
|
fullRepairModule.destroyReceiver();
|
||||||
|
|
||||||
|
setLaunchDetails(Lang.queryJS('landing.dlAsync.preparingToLaunch'));
|
||||||
|
|
||||||
|
const mojangIndexProcessor = new MojangIndexProcessor(
|
||||||
|
ConfigManager.getCommonDirectory(),
|
||||||
|
serv.rawServer.minecraftVersion);
|
||||||
|
const distributionIndexProcessor = new DistributionIndexProcessor(
|
||||||
|
ConfigManager.getCommonDirectory(),
|
||||||
|
distro,
|
||||||
|
serv.rawServer.id
|
||||||
|
);
|
||||||
|
|
||||||
|
const modLoaderData = await distributionIndexProcessor.loadModLoaderVersionJson(serv);
|
||||||
|
const versionData = await mojangIndexProcessor.getVersionJson();
|
||||||
|
|
||||||
|
if(login) {
|
||||||
|
const authUser = ConfigManager.getSelectedAccount();
|
||||||
|
loggerLaunchSuite.info(`Sending selected account (${authUser.displayName}) to ProcessBuilder.`);
|
||||||
|
let pb = new ProcessBuilder(serv, versionData, modLoaderData, authUser, remote.app.getVersion());
|
||||||
|
setLaunchDetails(Lang.queryJS('landing.dlAsync.launchingGame'));
|
||||||
|
|
||||||
|
const SERVER_JOINED_REGEX = new RegExp(`\\[.+\\]: \\[CHAT\\] ${authUser.displayName} joined the game`);
|
||||||
|
|
||||||
|
const onLoadComplete = () => {
|
||||||
|
toggleLaunchArea(false);
|
||||||
|
|
||||||
|
proc.stdout.removeListener('data', tempListener);
|
||||||
|
proc.stderr.removeListener('data', gameErrorListener);
|
||||||
|
};
|
||||||
|
const start = Date.now();
|
||||||
|
|
||||||
|
// Attach a temporary listener to the client output.
|
||||||
|
const tempListener = function(data){
|
||||||
|
if(GAME_LAUNCH_REGEX.test(data.trim())){
|
||||||
|
const diff = Date.now()-start;
|
||||||
|
if(diff < MIN_LINGER) {
|
||||||
|
setTimeout(onLoadComplete, MIN_LINGER-diff);
|
||||||
|
} else {
|
||||||
|
onLoadComplete();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
const gameErrorListener = function(data){
|
||||||
|
if(data.trim().toLowerCase().includes('error')){
|
||||||
|
loggerLaunchSuite.error(`Game error: ${data}`);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
proc = pb.build();
|
||||||
|
|
||||||
|
proc.stdout.on('data', tempListener);
|
||||||
|
proc.stderr.on('data', gameErrorListener);
|
||||||
|
|
||||||
|
proc.stdout.on('data', function(data){
|
||||||
|
if(SERVER_JOINED_REGEX.test(data.trim())){
|
||||||
|
DiscordWrapper.updateDetails('Exploring the World');
|
||||||
|
} else if(GAME_JOINED_REGEX.test(data.trim())) {
|
||||||
|
DiscordWrapper.updateDetails('Main Menu');
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
proc.on('close', (code, _signal) => {
|
||||||
|
if (hasRPC) {
|
||||||
|
DiscordWrapper.shutdownRPC();
|
||||||
|
hasRPC = false;
|
||||||
|
}
|
||||||
|
loggerLaunchSuite.info(`Game process exited with code ${code}.`);
|
||||||
|
if(code !== 0){
|
||||||
|
showLaunchFailure(Lang.queryJS('landing.dlAsync.gameExitedAbnormal'), Lang.queryJS('landing.dlAsync.seeConsoleForDetails'));
|
||||||
|
}
|
||||||
|
proc = null;
|
||||||
|
});
|
||||||
|
|
||||||
|
proc.on('error', (err) => {
|
||||||
|
loggerLaunchSuite.error('Error during game launch', err);
|
||||||
|
showLaunchFailure(Lang.queryJS('landing.dlAsync.errorDuringLaunchTitle'), err.message || Lang.queryJS('landing.dlAsync.errorDuringLaunchText'));
|
||||||
|
proc = null;
|
||||||
|
});
|
||||||
|
|
||||||
|
setTimeout(() => {
|
||||||
|
loggerLaunchSuite.info('Waiting for game window...');
|
||||||
|
}, MIN_LINGER);
|
||||||
|
}
|
||||||
|
}
|
306
ajouts/version code final/dlAsync.js
Normal file
306
ajouts/version code final/dlAsync.js
Normal file
@ -0,0 +1,306 @@
|
|||||||
|
/**
|
||||||
|
* @Name dlAsync Function
|
||||||
|
* @async
|
||||||
|
* @param {boolean} login
|
||||||
|
* @returns {Promise<void>}
|
||||||
|
*
|
||||||
|
* @author Sandro642
|
||||||
|
* @Cheating Athena's Shield
|
||||||
|
* @Ajout Liste blanche des mods
|
||||||
|
*/
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @Révision le XX.XX.2024 périme le 01.01.2025
|
||||||
|
* @Bug découvert : 0
|
||||||
|
* @Athena's Shield
|
||||||
|
* @Sandro642
|
||||||
|
*/
|
||||||
|
|
||||||
|
// ▄▄▄ ▄▄▄█████▓ ██░ ██ ▓█████ ███▄ █ ▄▄▄ ██████ ██████ ██░ ██ ██▓▓█████ ██▓ ▓█████▄
|
||||||
|
// ▒████▄ ▓ ██▒ ▓▒▓██░ ██▒▓█ ▀ ██ ▀█ █ ▒████▄ ▒██ ▒ ▒██ ▒ ▓██░ ██▒▓██▒▓█ ▀ ▓██▒ ▒██▀ ██▌
|
||||||
|
// ▒██ ▀█▄ ▒ ▓██░ ▒░▒██▀▀██░▒███ ▓██ ▀█ ██▒▒██ ▀█▄ ░ ▓██▄ ░ ▓██▄ ▒██▀▀██░▒██▒▒███ ▒██░ ░██ █▌
|
||||||
|
// ░██▄▄▄▄██░ ▓██▓ ░ ░▓█ ░██ ▒▓█ ▄ ▓██▒ ▐▌██▒░██▄▄▄▄██ ▒ ██▒ ▒ ██▒░▓█ ░██ ░██░▒▓█ ▄ ▒██░ ░▓█▄ ▌
|
||||||
|
// ▓█ ▓██▒ ▒██▒ ░ ░▓█▒░██▓░▒████▒▒██░ ▓██░ ▓█ ▓██▒▒██████▒▒ ▒██████▒▒░▓█▒░██▓░██░░▒████▒░██████▒░▒████▓
|
||||||
|
// ▒▒ ▓▒█░ ▒ ░░ ▒ ░░▒░▒░░ ▒░ ░░ ▒░ ▒ ▒ ▒▒ ▓▒█░▒ ▒▓▒ ▒ ░ ▒ ▒▓▒ ▒ ░ ▒ ░░▒░▒░▓ ░░ ▒░ ░░ ▒░▓ ░ ▒▒▓ ▒
|
||||||
|
// ▒ ▒▒ ░ ░ ▒ ░▒░ ░ ░ ░ ░░ ░░ ░ ▒░ ▒ ▒▒ ░░ ░▒ ░ ░ ░ ░▒ ░ ░ ▒ ░▒░ ░ ▒ ░ ░ ░ ░░ ░ ▒ ░ ░ ▒ ▒
|
||||||
|
// ░ ▒ ░ ░ ░░ ░ ░ ░ ░ ░ ░ ▒ ░ ░ ░ ░ ░ ░ ░ ░░ ░ ▒ ░ ░ ░ ░ ░ ░ ░
|
||||||
|
// ░ ░ ░ ░ ░ ░ ░ ░ ░ ░ ░ ░ ░ ░ ░ ░ ░ ░ ░ ░ ░
|
||||||
|
// ░
|
||||||
|
|
||||||
|
// Keep reference to Minecraft Process
|
||||||
|
let proc
|
||||||
|
// Is DiscordRPC enabled
|
||||||
|
let hasRPC = false
|
||||||
|
// Joined server regex
|
||||||
|
const GAME_JOINED_REGEX = /\[.+\]: Sound engine started/
|
||||||
|
const GAME_LAUNCH_REGEX = /^\[.+\]: (?:MinecraftForge .+ Initialized|ModLauncher .+ starting: .+|Loading Minecraft .+ with Fabric Loader .+)$/
|
||||||
|
const MIN_LINGER = 5000
|
||||||
|
|
||||||
|
// List of mods to exclude from validation
|
||||||
|
const EXCLUDED_MODS = [
|
||||||
|
]
|
||||||
|
|
||||||
|
async function dlAsync(login = true) {
|
||||||
|
const loggerLaunchSuite = LoggerUtil.getLogger('LaunchSuite')
|
||||||
|
const loggerLanding = LoggerUtil.getLogger('Landing')
|
||||||
|
setLaunchDetails(Lang.queryJS('landing.dlAsync.loadingServerInfo'))
|
||||||
|
|
||||||
|
let distro
|
||||||
|
|
||||||
|
try {
|
||||||
|
distro = await DistroAPI.refreshDistributionOrFallback()
|
||||||
|
onDistroRefresh(distro)
|
||||||
|
} catch (err) {
|
||||||
|
loggerLanding.error(Lang.queryJS('landing.dlAsync.unableToLoadDistributionIndex'))
|
||||||
|
showLaunchFailure(Lang.queryJS('landing.dlAsync.fatalError'), Lang.queryJS('landing.dlAsync.unableToLoadDistributionIndex'))
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
const serv = distro.getServerById(ConfigManager.getSelectedServer())
|
||||||
|
|
||||||
|
if (login) {
|
||||||
|
if (ConfigManager.getSelectedAccount() == null) {
|
||||||
|
loggerLanding.error(Lang.queryJS('landing.dlAsync.accountLoginNeeded'))
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// --------- Mod Verification Logic ---------
|
||||||
|
const modsDir = path.join(ConfigManager.getDataDirectory(), 'instances', serv.rawServer.id, 'mods')
|
||||||
|
|
||||||
|
// Check if mods directory exists, if not, create it
|
||||||
|
if (!fs.existsSync(modsDir)) {
|
||||||
|
fs.mkdirSync(modsDir, { recursive: true })
|
||||||
|
}
|
||||||
|
|
||||||
|
const distroMods = {}
|
||||||
|
const mdls = serv.modules
|
||||||
|
|
||||||
|
// Populate expected mod identities and log them
|
||||||
|
mdls.forEach(mdl => {
|
||||||
|
if (mdl.rawModule.name.endsWith('.jar')) {
|
||||||
|
const modPath = path.join(modsDir, mdl.rawModule.name)
|
||||||
|
const modIdentity = mdl.rawModule.identity || mdl.rawModule.MD5
|
||||||
|
loggerLanding.info(Lang.queryJS('landing.dlAsync.AthShield.distributionIdentityError', {'moduleName': mdl.rawModule.name, 'moduleIdentity': modIdentity}))
|
||||||
|
distroMods[modPath] = modIdentity
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
// Function to extract mod identity from the jar file
|
||||||
|
const extractModIdentity = (filePath) => {
|
||||||
|
loggerLanding.info(Lang.queryJS('landing.dlAsync.AthShield.modIdentityExtraction', {'filePath': filePath}))
|
||||||
|
const zip = new AdmZip(filePath)
|
||||||
|
const manifestEntry = zip.getEntry('META-INF/MANIFEST.MF')
|
||||||
|
|
||||||
|
if (manifestEntry) {
|
||||||
|
const manifestContent = manifestEntry.getData().toString('utf8')
|
||||||
|
const lines = manifestContent.split('\n')
|
||||||
|
const identityLine = lines.find(line => line.startsWith('Mod-Id:') || line.startsWith('Implementation-Title:'))
|
||||||
|
if (identityLine) {
|
||||||
|
loggerLanding.info(Lang.queryJS('landing.dlAsync.AthShield.manifestIdentityFound', {'filePath': filePath, 'identityLine': identityLine}))
|
||||||
|
return identityLine.split(':')[1].trim()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Fall back to a hash if no identity is found
|
||||||
|
const fileBuffer = fs.readFileSync(filePath)
|
||||||
|
const hashSum = crypto.createHash('md5') // Use MD5 to match the distribution configuration
|
||||||
|
hashSum.update(fileBuffer)
|
||||||
|
const hash = hashSum.digest('hex')
|
||||||
|
loggerLanding.info(Lang.queryJS('landing.dlAsync.AthShield.identityNotFoundInManifest', {'filePath': filePath, 'hash': hash}))
|
||||||
|
return hash
|
||||||
|
}
|
||||||
|
|
||||||
|
// Validate mods function
|
||||||
|
const validateMods = () => {
|
||||||
|
loggerLanding.info(Lang.queryJS('landing.dlAsync.AthShield.startingModValidation'))
|
||||||
|
const installedMods = fs.readdirSync(modsDir)
|
||||||
|
let valid = true
|
||||||
|
|
||||||
|
for (let mod of installedMods) {
|
||||||
|
const modPath = path.join(modsDir, mod)
|
||||||
|
|
||||||
|
// Skip validation for mods in the excluded list
|
||||||
|
if (EXCLUDED_MODS.includes(mod)) {
|
||||||
|
loggerLanding.info(Lang.queryJS('landing.dlAsync.AthShield.modValidationBypassed', {'mod': mod}))
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
const expectedIdentity = distroMods[modPath]
|
||||||
|
loggerLanding.info(Lang.queryJS('landing.dlAsync.AthShield.validatingMod', {'mod': mod}))
|
||||||
|
|
||||||
|
if (expectedIdentity) {
|
||||||
|
const modIdentity = extractModIdentity(modPath)
|
||||||
|
loggerLanding.info(Lang.queryJS('landing.dlAsync.AthShield.expectedAndCalculatedIdentity', {'expectedIdentity': expectedIdentity, 'mod': mod, 'modIdentity': modIdentity}))
|
||||||
|
|
||||||
|
if (modIdentity !== expectedIdentity) {
|
||||||
|
loggerLanding.error(Lang.queryJS('landing.dlAsync.AthShield.modIdentityMismatchError', {'mod': mod, 'expectedIdentity': expectedIdentity, 'modIdentity': modIdentity}))
|
||||||
|
valid = false
|
||||||
|
break
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
loggerLanding.warn(Lang.queryJS('landing.dlAsync.AthShield.expectedIdentityNotFound', {'mod': mod}))
|
||||||
|
valid = false
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
loggerLanding.info(Lang.queryJS('landing.dlAsync.AthShield.modValidationCompleted'))
|
||||||
|
return valid
|
||||||
|
}
|
||||||
|
|
||||||
|
// Perform mod validation before proceeding
|
||||||
|
if (!validateMods()) {
|
||||||
|
const errorMessage = Lang.queryJS('landing.dlAsync.AthShield.invalidModsDetectedMessage', {'folder': dataPath})
|
||||||
|
loggerLanding.error(errorMessage)
|
||||||
|
showLaunchFailure(errorMessage, null)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
// --------- End of Mod Verification Logic ---------
|
||||||
|
|
||||||
|
setLaunchDetails(Lang.queryJS('landing.dlAsync.pleaseWait'))
|
||||||
|
toggleLaunchArea(true)
|
||||||
|
setLaunchPercentage(0, 100)
|
||||||
|
|
||||||
|
const fullRepairModule = new FullRepair(
|
||||||
|
ConfigManager.getCommonDirectory(),
|
||||||
|
ConfigManager.getInstanceDirectory(),
|
||||||
|
ConfigManager.getLauncherDirectory(),
|
||||||
|
ConfigManager.getSelectedServer(),
|
||||||
|
DistroAPI.isDevMode()
|
||||||
|
)
|
||||||
|
|
||||||
|
fullRepairModule.spawnReceiver()
|
||||||
|
|
||||||
|
fullRepairModule.childProcess.on('error', (err) => {
|
||||||
|
loggerLaunchSuite.error(Lang.queryJS('landing.dlAsync.errorDuringLaunchText') + err)
|
||||||
|
showLaunchFailure(Lang.queryJS('landing.dlAsync.errorDuringLaunchTitle'), err.message || Lang.queryJS('landing.dlAsync.errorDuringLaunchText'))
|
||||||
|
})
|
||||||
|
fullRepairModule.childProcess.on('close', (code, _signal) => {
|
||||||
|
if(code !== 0){
|
||||||
|
loggerLaunchSuite.error(Lang.queryJS('landing.dlAsync.fullRepairMode', {'code': code}))
|
||||||
|
showLaunchFailure(Lang.queryJS('landing.dlAsync.errorDuringLaunchTitle'), Lang.queryJS('landing.dlAsync.seeConsoleForDetails'))
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
loggerLaunchSuite.info(Lang.queryJS('landing.dlAsync.validatingFileIntegrity'))
|
||||||
|
setLaunchDetails(Lang.queryJS('landing.dlAsync.validatingFileIntegrity'))
|
||||||
|
let invalidFileCount = 0
|
||||||
|
try {
|
||||||
|
invalidFileCount = await fullRepairModule.verifyFiles(percent => {
|
||||||
|
setLaunchPercentage(percent)
|
||||||
|
})
|
||||||
|
setLaunchPercentage(100)
|
||||||
|
} catch (err) {
|
||||||
|
loggerLaunchSuite.error(Lang.queryJS('landing.dlAsync.errFileVerification'))
|
||||||
|
showLaunchFailure(Lang.queryJS('landing.dlAsync.errorDuringFileVerificationTitle'), err.displayable || Lang.queryJS('landing.dlAsync.seeConsoleForDetails'))
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
if(invalidFileCount > 0) {
|
||||||
|
loggerLaunchSuite.info('Downloading files.')
|
||||||
|
setLaunchDetails(Lang.queryJS('landing.dlAsync.downloadingFiles'))
|
||||||
|
setLaunchPercentage(0)
|
||||||
|
try {
|
||||||
|
await fullRepairModule.download(percent => {
|
||||||
|
setDownloadPercentage(percent)
|
||||||
|
})
|
||||||
|
setDownloadPercentage(100)
|
||||||
|
} catch(err) {
|
||||||
|
loggerLaunchSuite.error(Lang.queryJS('landing.dlAsync.errorDuringFileDownloadTitle'))
|
||||||
|
showLaunchFailure(Lang.queryJS('landing.dlAsync.errorDuringFileDownloadTitle'), err.displayable || Lang.queryJS('landing.dlAsync.seeConsoleForDetails'))
|
||||||
|
return
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
loggerLaunchSuite.info(Lang.queryJS('landing.dlAsync.AthShield.downloadingFiles'))
|
||||||
|
}
|
||||||
|
|
||||||
|
// Remove download bar.
|
||||||
|
remote.getCurrentWindow().setProgressBar(-1)
|
||||||
|
|
||||||
|
fullRepairModule.destroyReceiver()
|
||||||
|
|
||||||
|
setLaunchDetails(Lang.queryJS('landing.dlAsync.preparingToLaunch'))
|
||||||
|
|
||||||
|
const mojangIndexProcessor = new MojangIndexProcessor(
|
||||||
|
ConfigManager.getCommonDirectory(),
|
||||||
|
serv.rawServer.minecraftVersion)
|
||||||
|
const distributionIndexProcessor = new DistributionIndexProcessor(
|
||||||
|
ConfigManager.getCommonDirectory(),
|
||||||
|
distro,
|
||||||
|
serv.rawServer.id
|
||||||
|
)
|
||||||
|
|
||||||
|
const modLoaderData = await distributionIndexProcessor.loadModLoaderVersionJson(serv)
|
||||||
|
const versionData = await mojangIndexProcessor.getVersionJson()
|
||||||
|
|
||||||
|
if(login) {
|
||||||
|
const authUser = ConfigManager.getSelectedAccount()
|
||||||
|
loggerLaunchSuite.info(Lang.queryJS('landing.dlAsync.accountToProcessBuilder', {'userDisplayName': authUser.displayName}))
|
||||||
|
let pb = new ProcessBuilder(serv, versionData, modLoaderData, authUser, remote.app.getVersion())
|
||||||
|
setLaunchDetails(Lang.queryJS('landing.dlAsync.launchingGame'))
|
||||||
|
|
||||||
|
const SERVER_JOINED_REGEX = new RegExp(`\\[.+\\]: \\[CHAT\\] ${authUser.displayName} joined the game`)
|
||||||
|
|
||||||
|
const onLoadComplete = () => {
|
||||||
|
toggleLaunchArea(false)
|
||||||
|
|
||||||
|
proc.stdout.removeListener('data', tempListener)
|
||||||
|
proc.stderr.removeListener('data', gameErrorListener)
|
||||||
|
}
|
||||||
|
const start = Date.now()
|
||||||
|
|
||||||
|
// Attach a temporary listener to the client output.
|
||||||
|
const tempListener = function(data){
|
||||||
|
if(GAME_LAUNCH_REGEX.test(data.trim())){
|
||||||
|
const diff = Date.now()-start
|
||||||
|
if(diff < MIN_LINGER) {
|
||||||
|
setTimeout(onLoadComplete, MIN_LINGER-diff)
|
||||||
|
} else {
|
||||||
|
onLoadComplete()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const gameErrorListener = function(data){
|
||||||
|
if(data.trim().toLowerCase().includes('error')){
|
||||||
|
loggerLaunchSuite.error(Lang.queryJS('landing.dlAsync.gameError', {'data': data}))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
proc = pb.build()
|
||||||
|
|
||||||
|
proc.stdout.on('data', tempListener)
|
||||||
|
proc.stderr.on('data', gameErrorListener)
|
||||||
|
|
||||||
|
proc.stdout.on('data', function(data){
|
||||||
|
if(SERVER_JOINED_REGEX.test(data.trim())){
|
||||||
|
DiscordWrapper.updateDetails('Exploring the World')
|
||||||
|
} else if(GAME_JOINED_REGEX.test(data.trim())) {
|
||||||
|
DiscordWrapper.updateDetails('Main Menu')
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
proc.on('close', (code, _signal) => {
|
||||||
|
if (hasRPC) {
|
||||||
|
DiscordWrapper.shutdownRPC()
|
||||||
|
hasRPC = false
|
||||||
|
}
|
||||||
|
loggerLaunchSuite.info(Lang.queryJS('landing.dlAsync.gameExited', {'code': code}))
|
||||||
|
if(code !== 0){
|
||||||
|
showLaunchFailure(Lang.queryJS('landing.dlAsync.gameExitedAbnormal'), Lang.queryJS('landing.dlAsync.seeConsoleForDetails'))
|
||||||
|
}
|
||||||
|
proc = null
|
||||||
|
})
|
||||||
|
|
||||||
|
proc.on('error', (err) => {
|
||||||
|
loggerLaunchSuite.error(Lang.queryJS('landing.dlAsync.gameErrorDuringLaunch', {'error': err}))
|
||||||
|
showLaunchFailure(Lang.queryJS('landing.dlAsync.errorDuringLaunchTitle'), err.message || Lang.queryJS('landing.dlAsync.errorDuringLaunchText'))
|
||||||
|
proc = null
|
||||||
|
})
|
||||||
|
|
||||||
|
setTimeout(() => {
|
||||||
|
loggerLaunchSuite.info(Lang.queryJS('landing.dlAsync.launchingGame'))
|
||||||
|
}, MIN_LINGER)
|
||||||
|
}
|
||||||
|
}
|
1139
ajouts/version code final/landing.js
Normal file
1139
ajouts/version code final/landing.js
Normal file
File diff suppressed because it is too large
Load Diff
@ -11,6 +11,15 @@ const dataPath = path.join(sysRoot, '.helioslauncher')
|
|||||||
|
|
||||||
const launcherDir = require('@electron/remote').app.getPath('userData')
|
const launcherDir = require('@electron/remote').app.getPath('userData')
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The path to the data directory used by the application.
|
||||||
|
* This variable can be used to retrieve or set the location
|
||||||
|
* where the application's data files are stored.
|
||||||
|
*
|
||||||
|
* @type {string}
|
||||||
|
*/
|
||||||
|
exports.dataPath = dataPath
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Retrieve the absolute path of the launcher directory.
|
* Retrieve the absolute path of the launcher directory.
|
||||||
*
|
*
|
||||||
|
@ -435,6 +435,8 @@ async function downloadJava(effectiveJavaOptions, launchAfter = true) {
|
|||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
////////////////////////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
// Keep reference to Minecraft Process
|
// Keep reference to Minecraft Process
|
||||||
let proc
|
let proc
|
||||||
// Is DiscordRPC enabled
|
// Is DiscordRPC enabled
|
||||||
@ -636,6 +638,8 @@ async function dlAsync(login = true) {
|
|||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
////////////////////////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* News Loading Functions
|
* News Loading Functions
|
||||||
*/
|
*/
|
||||||
|
@ -206,6 +206,29 @@ launchingGame = "Launching game.."
|
|||||||
launchWrapperNotDownloaded = "The main file, LaunchWrapper, failed to download properly. As a result, the game cannot launch.<br><br>To fix this issue, temporarily turn off your antivirus software and launch the game again.<br><br>If you have time, please <a href=\"https://github.com/dscalzi/HeliosLauncher/issues\">submit an issue</a> and let us know what antivirus software you use. We'll contact them and try to straighten things out."
|
launchWrapperNotDownloaded = "The main file, LaunchWrapper, failed to download properly. As a result, the game cannot launch.<br><br>To fix this issue, temporarily turn off your antivirus software and launch the game again.<br><br>If you have time, please <a href=\"https://github.com/dscalzi/HeliosLauncher/issues\">submit an issue</a> and let us know what antivirus software you use. We'll contact them and try to straighten things out."
|
||||||
doneEnjoyServer = "Done. Enjoy the server!"
|
doneEnjoyServer = "Done. Enjoy the server!"
|
||||||
checkConsoleForDetails = "Please check the console (CTRL + Shift + i) for more details."
|
checkConsoleForDetails = "Please check the console (CTRL + Shift + i) for more details."
|
||||||
|
accountLoginNeeded = "You must be logged into an account."
|
||||||
|
fullRepairMode = "Full Repair Module exited with code {code}, assuming error."
|
||||||
|
errFileVerification = "Error during file validation."
|
||||||
|
accountToProcessBuilder = "Sending selected account ({userDisplayName}) to ProcessBuilder."
|
||||||
|
gameError = "Game error: {data}"
|
||||||
|
gameExited = "Game process exited with code {code}."
|
||||||
|
gameErrorDuringLaunch = "Error during game launch {error}."
|
||||||
|
launchingGame = "Waiting for game window..."
|
||||||
|
|
||||||
|
[js.landing.dlAsync.AthShield]
|
||||||
|
distributionIdentityError = "Expected Identity from Distribution for {moduleName}: {moduleIdentity}."
|
||||||
|
modIdentityExtraction = "Extracting identity for mod at: {filePath}."
|
||||||
|
manifestIdentityFound = "Found identity in manifest for {filePath}: {identityLine}"
|
||||||
|
identityNotFoundInManifest = "No identity found in manifest for {filePath}, using hash: {hash}"
|
||||||
|
startingModValidation = "Starting mod validation..."
|
||||||
|
modValidationBypassed = "Skipping validation for excluded mod: {mod}"
|
||||||
|
validatingMod = "Validating mod: {mod}"
|
||||||
|
expectedAndCalculatedIdentity = "Expected Identity: {expectedIdentity}, Calculated Identity for {mod}: {modIdentity}"
|
||||||
|
modIdentityMismatchError = "Mod identity mismatch! Mod: {mod}, Expected: {expectedIdentity}, Found: {modIdentity}"
|
||||||
|
expectedIdentityNotFound = "No expected identity found for mod: {mod}. Marking as invalid."
|
||||||
|
modValidationCompleted = "Mod validation completed."
|
||||||
|
invalidModsDetectedMessage = "Athena's Shield has detected invalid mods. Please delete the {folder} folder and restart the launcher."
|
||||||
|
downloadingFiles = "No invalid files, skipping download."
|
||||||
|
|
||||||
[js.landing.news]
|
[js.landing.news]
|
||||||
checking = "Checking for News"
|
checking = "Checking for News"
|
||||||
|
Loading…
Reference in New Issue
Block a user