Skip to content
Snippets Groups Projects
.init.js 4.56 KiB
Newer Older
  • Learn to ignore specific revisions
  • Versions.registry = [];
    Versions.log = [];
    
    TonyFox's avatar
    TonyFox committed
    Versions.recordStep = step => {
    
    klorpa's avatar
    klorpa committed
    	Versions.log.push(step);
    
    TonyFox's avatar
    TonyFox committed
    };
    
    // this will be updated automatically
    Versions.latest = 0;
    Versions.register = (versionId, migration) => {
    
    klorpa's avatar
    klorpa committed
    	console.log(`Registering version schema`, typeof versionId, versionId, migration);
    
    TonyFox's avatar
    TonyFox committed
    	if (versionId !== Versions.registry.length + 1) {
    		// If we get all versions in order, we don't have to try to verify them after the fact.
    		Errors.report(
    			`Invalid migration registration. Next expected version id was ${Versions.registry.length + 1},
    			but got ${versionId} instead. Version migrations might not work as expected`
    		);
    
    klorpa's avatar
    klorpa committed
    	}
    	Versions.registry[versionId - 1] = {
    
    TonyFox's avatar
    TonyFox committed
    		migration,
    		versionId,
    	};
    
    klorpa's avatar
    klorpa committed
    	Versions.latest = Math.max(Versions.latest, versionId);
    
    TonyFox's avatar
    TonyFox committed
    };
    
    Versions.migrate = (originalVersion, state) => {
    
    klorpa's avatar
    klorpa committed
    	Versions.log = [];
    	let currentVersion = originalVersion;
    	let migrationFailed = false;
    	let nextMigration = Versions.registry[currentVersion];
    
    TonyFox's avatar
    TonyFox committed
    	while (nextMigration && !migrationFailed) {
    
    klorpa's avatar
    klorpa committed
    		migrationFailed = nextMigration.migration(originalVersion, state);
    		currentVersion = nextMigration.versionId;
    		nextMigration = Versions.registry[currentVersion];
    
    TonyFox's avatar
    TonyFox committed
    	}
    
    klorpa's avatar
    klorpa committed
    	if (migrationFailed) {
    		Errors.report(`One or more errors occurred while attempting to migrate from schema version ${originalVersion} to the latest`, Versions.log);
    	}
    	if (currentVersion !== Versions.latest && !migrationFailed) {
    
    TonyFox's avatar
    TonyFox committed
    		Errors.report(`Migration steps missing for schema version: ${originalVersion}`, Versions.log);
    
    klorpa's avatar
    klorpa committed
    	}
    	return currentVersion;
    
    TonyFox's avatar
    TonyFox committed
    };
    
    klorpa's avatar
    klorpa committed
    /**
    
    TonyFox's avatar
    TonyFox committed
     * Utility for migrations to use. Make more robust migrations + provides logging in case of later errors.
    
    klorpa's avatar
    klorpa committed
     *
    
     * Usage:
     * Versions.stepper(originalVersion, <youridhere>)
     *  .step(() => ...)
    
    klorpa's avatar
    klorpa committed
     *	  // option; critical = true;
     *	  // if it fails nothing else will execute and the migration will be marked as a failure
     *  .step(() => ..., { critical: true })
     *	  // option; name = <string>
     *	  // provides a human readable name for the purpose of logging and error reporting
    
     *  .step(() => ..., { name: 'my specific step' })
    
    klorpa's avatar
    klorpa committed
     *	  // option; requires = <function>
     *	  // ensures that the step did what was intended to be done.
     *	  // this is useful in that if the update step fails, but we're already in a good state
     *	  // then we know we can (probably) continue successfully, despite the earlier failure
    
    TonyFox's avatar
    TonyFox committed
     *  .step(() => ..., { requires: () => ... }).
    
    klorpa's avatar
    klorpa committed
     *
    
    TonyFox's avatar
    TonyFox committed
     * Note, by convention, a single stepper should be used.
    
    klorpa's avatar
    klorpa committed
    	constructor(originalVersion, currentStage) {
    		this.originalVersion = originalVersion;
    		this.currentStage = currentStage;
    		this.nextStepIdNumber = 0;
    		this.shouldContinue = true;
    	}
    
    TonyFox's avatar
    TonyFox committed
    
    	step(statement, { critical, requires, name } = {}) {
    
    klorpa's avatar
    klorpa committed
    		const errors = [];
    		let requirementsComplete = !requires;
    		if (this.shouldContinue) {
    
    TonyFox's avatar
    TonyFox committed
    			if (name === undefined) {
    				name = statement.toString();
    			}
    			const stepId = `${this.currentStage}-${this.nextStepIdNumber++}`;
    
    klorpa's avatar
    klorpa committed
    			try {
    				statement();
    
    TonyFox's avatar
    TonyFox committed
    			} catch (e) {
    
    klorpa's avatar
    klorpa committed
    				errors.push(e);
    			} finally {
    				try {
    					requirementsComplete = requires();
    
    TonyFox's avatar
    TonyFox committed
    				} catch (e) {
    
    klorpa's avatar
    klorpa committed
    					errors.push(e);
    				}
    			}
    			Versions.recordStep({
    				stepId,
    				originalVersion: this.originalVersion,
    				currentStage: this.currentStage,
    				name,
    
    TonyFox's avatar
    TonyFox committed
    				errors,
    			});
    
    klorpa's avatar
    klorpa committed
    			this.shouldContinue = requirementsComplete && !(errors.length > 0 && critical);
    		}
    		return this;
    	}
    
    TonyFox's avatar
    TonyFox committed
    };
    
    
    Versions.stepper = (originalVersion, currentStage) => {
    
    klorpa's avatar
    klorpa committed
    	return new Versions.Stepper(originalVersion, currentStage);
    
    TonyFox's avatar
    TonyFox committed
    };
    
    
    $(document).on(":archiverevive", function (e) {
    
    klorpa's avatar
    klorpa committed
    	const state = e.archive.state;
    	const version = state.variables.version;
    
    	if (!version || version.schema === undefined) {
    
    TonyFox's avatar
    TonyFox committed
    		// The sugarcube baseline version has yet to be applied. Move to a special passage to apply it. The user will be able to resume afterwords.
    		if (state.title !== "Upgrade Waiting Room" && state.title !== "Start") {
    			console.log(`Schema version number not found. Banishing the player to the waiting room.`);
    			state.variables.navigation = state.variables.navigation || {};
    
    klorpa's avatar
    klorpa committed
    			state.variables.navigation.stack = state.variables.navigation.stack || [];
    			state.variables.navigation.stack.push(state.title);
    
    TonyFox's avatar
    TonyFox committed
    			state.title = "Upgrade Waiting Room";
    
    klorpa's avatar
    klorpa committed
    		}
    	} else if (version.schema !== Versions.latest) {
    		console.log(`Migrating game state from ${schemaVersion} to ${Versions.latest}`);
    		state.variables.schemaVersion = Versions.migrate(schemaVersion === undefined ? 0 : schemaVersion, state);
    		// modified history, be sure to notify the framework by returning the history object
    		// TODO: apply class transformations to objects in state *after* migrations are complete
    	}