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:
Sandro642 2024-10-23 19:17:23 +02:00
parent 9813802b1c
commit 5f3e229360
7 changed files with 2816 additions and 0 deletions

File diff suppressed because it is too large Load Diff

View 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);
}
}

View 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)
}
}

File diff suppressed because it is too large Load Diff

View File

@ -11,6 +11,15 @@ const dataPath = path.join(sysRoot, '.helioslauncher')
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.
*

View File

@ -435,6 +435,8 @@ async function downloadJava(effectiveJavaOptions, launchAfter = true) {
}
////////////////////////////////////////////////////////////////////////////////////
// Keep reference to Minecraft Process
let proc
// Is DiscordRPC enabled
@ -636,6 +638,8 @@ async function dlAsync(login = true) {
}
////////////////////////////////////////////////////////////////////////////////////
/**
* News Loading Functions
*/

View File

@ -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."
doneEnjoyServer = "Done. Enjoy the server!"
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]
checking = "Checking for News"