@slippi/slippi-js
Version:
Official Project Slippi Javascript SDK
151 lines (148 loc) • 5.09 kB
JavaScript
import { format } from 'date-fns';
import path from 'path';
import { Writable } from 'stream';
import { Command } from '../../common/types.esm.js';
import { SlpStream, SlpStreamEvent } from '../../common/utils/slpStream.esm.js';
import { SlpFile } from './slpFile.esm.js';
/**
* The default function to use for generating new SLP files.
*/
function getNewFilePath(folder, date) {
return path.join(folder, `Game_${format(date, "yyyyMMdd")}T${format(date, "HHmmss")}.slp`);
}
const defaultSettings = {
outputFiles: true,
folderPath: ".",
consoleNickname: "unknown",
newFilename: getNewFilePath,
};
var SlpFileWriterEvent;
(function (SlpFileWriterEvent) {
SlpFileWriterEvent["NEW_FILE"] = "new-file";
SlpFileWriterEvent["FILE_COMPLETE"] = "file-complete";
})(SlpFileWriterEvent || (SlpFileWriterEvent = {}));
/**
* SlpFileWriter lets us not only emit events as an SlpStream but also
* writes the data that is being passed in to an SLP file. Use this if
* you want to process Slippi data in real time but also want to be able
* to write out the data to an SLP file.
*
* @export
* @class SlpFileWriter
* @extends {Writable}
*/
class SlpFileWriter extends Writable {
/**
* Creates an instance of SlpFileWriter.
*/
constructor(options, opts) {
super(opts);
this.options = Object.assign({}, defaultSettings, options);
this.processor = new SlpStream(options);
this._setupListeners();
}
/**
* Access the underlying SlpStream processor for event listening
*/
getProcessor() {
return this.processor;
}
// Implement _write to handle incoming data
// eslint-disable-next-line @typescript-eslint/no-unused-vars
_write(chunk, _encoding, callback) {
try {
this.processor.process(new Uint8Array(chunk));
callback();
}
catch (err) {
callback(err instanceof Error ? err : new Error(String(err)));
}
}
_writePayload(payload) {
// Write data to the current file
if (this.currentFile) {
// Convert Uint8Array to Buffer for Node.js fs operations
const buffer = Buffer.from(payload);
this.currentFile.write(buffer);
}
}
_setupListeners() {
this.processor.on(SlpStreamEvent.RAW, (data) => {
const { command, payload } = data;
switch (command) {
case Command.MESSAGE_SIZES:
// Create the new game first before writing the payload
this._handleNewGame();
this._writePayload(payload);
break;
case Command.GAME_END:
// Write payload first before ending the game
this._writePayload(payload);
this._handleEndGame();
break;
default:
this._writePayload(payload);
break;
}
});
}
/**
* Return the name of the SLP file currently being written or undefined if
* no file is being written to currently.
*
* @returns {(string | undefined)}
* @memberof SlpFileWriter
*/
getCurrentFilename() {
if (this.currentFile != null) {
return path.resolve(this.currentFile.path());
}
return undefined;
}
/**
* Ends the current file being written to.
*
* @returns {(string | undefined)}
* @memberof SlpFileWriter
*/
endCurrentFile() {
this._handleEndGame();
}
/**
* Updates the settings to be the desired ones passed in.
*
* @param {Partial<SlpFileWriterOptions>} settings
* @memberof SlpFileWriter
*/
updateSettings(settings) {
this.options = Object.assign({}, this.options, settings);
}
_handleNewGame() {
// Only create a new file if we're outputting files
if (this.options.outputFiles) {
const filePath = this.options.newFilename(this.options.folderPath, new Date());
// Pass the processor to SlpFile so it can listen to events
this.currentFile = new SlpFile(filePath, this.processor);
// console.log(`Creating new file at: ${filePath}`);
this.emit(SlpFileWriterEvent.NEW_FILE, filePath);
}
}
_handleEndGame() {
// End the stream
if (this.currentFile) {
const filePath = this.currentFile.path();
// Set the console nickname
this.currentFile.setMetadata({
consoleNickname: this.options.consoleNickname,
});
// Wait for the file to actually finish writing before emitting FILE_COMPLETE
this.currentFile.once("finish", () => {
this.emit(SlpFileWriterEvent.FILE_COMPLETE, filePath);
});
this.currentFile.end();
// Clear current file
this.currentFile = undefined;
}
}
}
export { SlpFileWriter, SlpFileWriterEvent };