Skip to content

Commit

Permalink
Overhauling file system structure.
Browse files Browse the repository at this point in the history
Common files such as assets, libraries, and mods have been externalized into a 'common' folder. Each server now has its own instance folder to allow saving per version files. This resolves issues with resourcepacks and mod configurations being overriden, and still preserves our optimizations in storing libraries and mods maven style.
  • Loading branch information
dscalzi committed Jun 4, 2018
1 parent 97e9c15 commit 0cc861f
Show file tree
Hide file tree
Showing 5 changed files with 61 additions and 38 deletions.
2 changes: 1 addition & 1 deletion app/assets/js/assetexec.js
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
const {AssetGuard} = require('./assetguard.js')

const tracker = new AssetGuard(process.argv[2], process.argv[3], process.argv[4])
const tracker = new AssetGuard(process.argv[2], process.argv[3], process.argv[4], process.argv[5])
console.log('AssetExec Started')

// Temporary for debug purposes.
Expand Down
46 changes: 24 additions & 22 deletions app/assets/js/assetguard.js
Original file line number Diff line number Diff line change
Expand Up @@ -179,12 +179,13 @@ class AssetGuard extends EventEmitter {
* On creation the object's properties are never-null default
* values. Each identifier is resolved to an empty DLTracker.
*
* @param {string} basePath The base path for asset validation (game root).
* @param {string} commonPath The common path for shared game files.
* @param {string} launcherPath The root launcher directory.
* @param {string} javaexec The path to a java executable which will be used
* to finalize installation.
* @param {string} instancePath The path to the instances directory.
*/
constructor(basePath, launcherPath, javaexec){
constructor(commonPath, launcherPath, javaexec, instancePath){
super()
this.totaldlsize = 0
this.progress = 0
Expand All @@ -194,9 +195,10 @@ class AssetGuard extends EventEmitter {
this.forge = new DLTracker([], 0)
this.java = new DLTracker([], 0)
this.extractQueue = []
this.basePath = basePath
this.commonPath = commonPath
this.launcherPath = launcherPath
this.javaexec = javaexec
this.instancePath = instancePath
}

// Static Utility Functions
Expand Down Expand Up @@ -557,10 +559,10 @@ class AssetGuard extends EventEmitter {
* in a promise.
*
* @param {Asset} asset The Asset object representing Forge.
* @param {string} basePath Base path for asset validation (game root).
* @param {string} commonPath The common path for shared game files.
* @returns {Promise.<Object>} A promise which resolves to the contents of forge's version.json.
*/
static _finalizeForgeAsset(asset, basePath){
static _finalizeForgeAsset(asset, commonPath){
return new Promise((resolve, reject) => {
fs.readFile(asset.to, (err, data) => {
const zip = new AdmZip(data)
Expand All @@ -569,7 +571,7 @@ class AssetGuard extends EventEmitter {
for(let i=0; i<zipEntries.length; i++){
if(zipEntries[i].entryName === 'version.json'){
const forgeVersion = JSON.parse(zip.readAsText(zipEntries[i]))
const versionPath = path.join(basePath, 'versions', forgeVersion.id)
const versionPath = path.join(commonPath, 'versions', forgeVersion.id)
const versionFile = path.join(versionPath, forgeVersion.id + '.json')
if(!fs.existsSync(versionFile)){
mkpath.sync(versionPath)
Expand Down Expand Up @@ -1202,7 +1204,7 @@ class AssetGuard extends EventEmitter {
return new Promise((resolve, reject) => {
const name = version + '.json'
const url = 'https://s3.amazonaws.com/Minecraft.Download/versions/' + version + '/' + name
const versionPath = path.join(self.basePath, 'versions', version)
const versionPath = path.join(self.commonPath, 'versions', version)
const versionFile = path.join(versionPath, name)
if(!fs.existsSync(versionFile) || force){
//This download will never be tracked as it's essential and trivial.
Expand Down Expand Up @@ -1255,7 +1257,7 @@ class AssetGuard extends EventEmitter {
//Asset index constants.
const assetIndex = versionData.assetIndex
const name = assetIndex.id + '.json'
const indexPath = path.join(self.basePath, 'assets', 'indexes')
const indexPath = path.join(self.commonPath, 'assets', 'indexes')
const assetIndexLoc = path.join(indexPath, name)

let data = null
Expand Down Expand Up @@ -1291,7 +1293,7 @@ class AssetGuard extends EventEmitter {

//Asset constants
const resourceURL = 'http://resources.download.minecraft.net/'
const localPath = path.join(self.basePath, 'assets')
const localPath = path.join(self.commonPath, 'assets')
const indexPath = path.join(localPath, 'indexes')
const objectPath = path.join(localPath, 'objects')

Expand Down Expand Up @@ -1338,7 +1340,7 @@ class AssetGuard extends EventEmitter {
return new Promise((resolve, reject) => {

const libArr = versionData.libraries
const libPath = path.join(self.basePath, 'libraries')
const libPath = path.join(self.commonPath, 'libraries')

const libDlQueue = []
let dlSize = 0
Expand Down Expand Up @@ -1394,7 +1396,7 @@ class AssetGuard extends EventEmitter {
return new Promise((resolve, reject) => {
const clientData = versionData.downloads.client
const version = versionData.id
const targetPath = path.join(self.basePath, 'versions', version)
const targetPath = path.join(self.commonPath, 'versions', version)
const targetFile = version + '.jar'

let client = new Asset(version + ' client', clientData.sha1, clientData.size, clientData.url, path.join(targetPath, targetFile))
Expand All @@ -1421,7 +1423,7 @@ class AssetGuard extends EventEmitter {
return new Promise((resolve, reject) => {
const client = versionData.logging.client
const file = client.file
const targetPath = path.join(self.basePath, 'assets', 'log_configs')
const targetPath = path.join(self.commonPath, 'assets', 'log_configs')

let logConfig = new Asset(file.id, file.sha1, file.size, file.url, path.join(targetPath, file.id))

Expand Down Expand Up @@ -1456,21 +1458,21 @@ class AssetGuard extends EventEmitter {
console.error('Invalid server pack id:', serverpackid)
}

self.forge = self._parseDistroModules(serv.modules, serv.mc_version)
self.forge = self._parseDistroModules(serv.modules, serv.mc_version, serv.id)
// Correct our workaround here.
let decompressqueue = self.forge.callback
self.extractQueue = decompressqueue
self.forge.callback = (asset, self) => {
if(asset.type === 'forge-hosted' || asset.type === 'forge'){
AssetGuard._finalizeForgeAsset(asset, self.basePath)
AssetGuard._finalizeForgeAsset(asset, self.commonPath)
}
}
resolve(serv)
})
})
}

_parseDistroModules(modules, version){
_parseDistroModules(modules, version, servid){
let alist = []
let asize = 0;
let decompressqueue = []
Expand All @@ -1483,19 +1485,19 @@ class AssetGuard extends EventEmitter {
case 'forge-hosted':
case 'forge':
case 'library':
obPath = path.join(this.basePath, 'libraries', obPath)
obPath = path.join(this.commonPath, 'libraries', obPath)
break
case 'forgemod':
//obPath = path.join(this.basePath, 'mods', obPath)
obPath = path.join(this.basePath, 'modstore', obPath)
obPath = path.join(this.commonPath, 'modstore', obPath)
break
case 'litemod':
//obPath = path.join(this.basePath, 'mods', version, obPath)
obPath = path.join(this.basePath, 'modstore', obPath)
obPath = path.join(this.commonPath, 'modstore', obPath)
break
case 'file':
default:
obPath = path.join(this.basePath, obPath)
obPath = path.join(this.instancePath, servid, obPath)
}
let artifact = new DistroModule(ob.id, obArtifact.MD5, obArtifact.size, obArtifact.url, obPath, obType)
const validationPath = obPath.toLowerCase().endsWith('.pack.xz') ? obPath.substring(0, obPath.toLowerCase().lastIndexOf('.pack.xz')) : obPath
Expand All @@ -1506,7 +1508,7 @@ class AssetGuard extends EventEmitter {
}
//Recursively process the submodules then combine the results.
if(ob.sub_modules != null){
let dltrack = this._parseDistroModules(ob.sub_modules, version)
let dltrack = this._parseDistroModules(ob.sub_modules, version, servid)
asize += dltrack.dlsize*1
alist = alist.concat(dltrack.dlqueue)
decompressqueue = decompressqueue.concat(dltrack.callback)
Expand Down Expand Up @@ -1542,9 +1544,9 @@ class AssetGuard extends EventEmitter {
const ob = modules[i]
if(ob.type === 'forge-hosted' || ob.type === 'forge'){
let obArtifact = ob.artifact
let obPath = obArtifact.path == null ? path.join(self.basePath, 'libraries', AssetGuard._resolvePath(ob.id, obArtifact.extension)) : obArtifact.path
let obPath = obArtifact.path == null ? path.join(self.commonPath, 'libraries', AssetGuard._resolvePath(ob.id, obArtifact.extension)) : obArtifact.path
let asset = new DistroModule(ob.id, obArtifact.MD5, obArtifact.size, obArtifact.url, obPath, ob.type)
let forgeData = await AssetGuard._finalizeForgeAsset(asset, self.basePath)
let forgeData = await AssetGuard._finalizeForgeAsset(asset, self.commonPath)
resolve(forgeData)
return
}
Expand Down
19 changes: 18 additions & 1 deletion app/assets/js/configmanager.js
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,6 @@ const DEFAULT_CONFIG = {
],
},
game: {
directory: path.join(dataPath, 'game'),
resWidth: 1280,
resHeight: 720,
fullscreen: false,
Expand All @@ -43,6 +42,8 @@ const DEFAULT_CONFIG = {
},
launcher: {}
},
commonDirectory: path.join(dataPath, 'common'),
instanceDirectory: path.join(dataPath, 'instances'),
clientToken: uuidV4().replace(/-/g, ''),
selectedServer: null, // Resolved
selectedAccount: null,
Expand Down Expand Up @@ -139,6 +140,22 @@ exports.getTempNativeFolder = function(){

// System Settings (Unconfigurable on UI)

/**
* Retrieve the common directory for shared
* game files (assets, libraries, etc).
*/
exports.getCommonDirectory = function(){
return config.commonDirectory
}

/**
* Retrieve the instance directory for the per
* server game directories.
*/
exports.getInstanceDirectory = function(){
return config.instanceDirectory
}

/**
* Retrieve the launcher's Client Token.
* There is no default client token.
Expand Down
20 changes: 11 additions & 9 deletions app/assets/js/processbuilder.js
Original file line number Diff line number Diff line change
Expand Up @@ -16,14 +16,15 @@ const {URL} = require('url')

class ProcessBuilder {

constructor(gameDirectory, distroServer, versionData, forgeData, authUser){
this.dir = gameDirectory
constructor(distroServer, versionData, forgeData, authUser){
this.gameDir = path.join(ConfigManager.getInstanceDirectory(), distroServer.id)
this.commonDir = ConfigManager.getCommonDirectory()
this.server = distroServer
this.versionData = versionData
this.forgeData = forgeData
this.authUser = authUser
this.fmlDir = path.join(this.dir, 'versions', this.server.id + '.json')
this.libPath = path.join(this.dir, 'libraries')
this.fmlDir = path.join(this.commonDir, 'versions', this.server.id + '.json')
this.libPath = path.join(this.commonDir, 'libraries')
}

static shouldInclude(mdle){
Expand All @@ -35,6 +36,7 @@ class ProcessBuilder {
* Convienence method to run the functions typically used to build a process.
*/
build(){
mkpath.sync(this.gameDir)
const tempNativePath = path.join(os.tmpdir(), ConfigManager.getTempNativeFolder(), crypto.pseudoRandomBytes(16).toString('hex'))
process.throwDeprecation = true
const mods = this.resolveDefaultMods()
Expand All @@ -44,7 +46,7 @@ class ProcessBuilder {
console.log(args)

const child = child_process.spawn(ConfigManager.getJavaExecutable(), args, {
cwd: ConfigManager.getGameDirectory(),
cwd: this.gameDir,
detached: ConfigManager.isLaunchDetached()
})

Expand Down Expand Up @@ -90,7 +92,7 @@ class ProcessBuilder {

constructFMLModList(mods, save = false){
const modList = {}
modList.repositoryRoot = path.join(this.dir, 'modstore')
modList.repositoryRoot = path.join(this.commonDir, 'modstore')
const ids = []
for(let i=0; i<mods.length; ++i){
ids.push(mods[i].id)
Expand Down Expand Up @@ -156,10 +158,10 @@ class ProcessBuilder {
val = this.server.id
break
case 'game_directory':
val = this.dir
val = this.gameDir
break
case 'assets_root':
val = path.join(this.dir, 'assets')
val = path.join(this.commonDir, 'assets')
break
case 'assets_index_name':
val = this.versionData.assets
Expand Down Expand Up @@ -223,7 +225,7 @@ class ProcessBuilder {

// Add the version.jar to the classpath.
const version = this.versionData.id
cpArgs.push(path.join(this.dir, 'versions', version, version + '.jar'))
cpArgs.push(path.join(this.commonDir, 'versions', version, version + '.jar'))

// Resolve the Mojang declared libraries.
const mojangLibs = this._resolveMojangLibraries(tempNativePath)
Expand Down
12 changes: 7 additions & 5 deletions app/assets/js/scripts/landing.js
Original file line number Diff line number Diff line change
Expand Up @@ -257,9 +257,10 @@ function asyncSystemScan(launchAfter = true){

// Fork a process to run validations.
sysAEx = cp.fork(path.join(__dirname, 'assets', 'js', 'assetexec.js'), [
ConfigManager.getGameDirectory(),
ConfigManager.getCommonDirectory(),
ConfigManager.getLauncherDirectory(),
ConfigManager.getJavaExecutable()
ConfigManager.getJavaExecutable(),
ConfigManager.getInstanceDirectory()
], {
stdio: 'pipe'
})
Expand Down Expand Up @@ -436,9 +437,10 @@ function dlAsync(login = true){

// Start AssetExec to run validations and downloads in a forked process.
aEx = cp.fork(path.join(__dirname, 'assets', 'js', 'assetexec.js'), [
ConfigManager.getGameDirectory(),
ConfigManager.getCommonDirectory(),
ConfigManager.getLauncherDirectory(),
ConfigManager.getJavaExecutable()
ConfigManager.getJavaExecutable(),
ConfigManager.getInstanceDirectory()
], {
stdio: 'pipe'
})
Expand Down Expand Up @@ -581,7 +583,7 @@ function dlAsync(login = true){
//}
const authUser = ConfigManager.getSelectedAccount()
console.log('authu', authUser)
let pb = new ProcessBuilder(ConfigManager.getGameDirectory(), serv, versionData, forgeData, authUser)
let pb = new ProcessBuilder(serv, versionData, forgeData, authUser)
setLaunchDetails('Launching game..')
try {
// Build Minecraft process.
Expand Down

0 comments on commit 0cc861f

Please sign in to comment.