Spaces:
Paused
Paused
| ; | |
| var __defProp = Object.defineProperty; | |
| var __getOwnPropDesc = Object.getOwnPropertyDescriptor; | |
| var __getOwnPropNames = Object.getOwnPropertyNames; | |
| var __hasOwnProp = Object.prototype.hasOwnProperty; | |
| var __export = (target, all) => { | |
| for (var name in all) | |
| __defProp(target, name, { get: all[name], enumerable: true }); | |
| }; | |
| var __copyProps = (to, from, except, desc) => { | |
| if (from && typeof from === "object" || typeof from === "function") { | |
| for (let key of __getOwnPropNames(from)) | |
| if (!__hasOwnProp.call(to, key) && key !== except) | |
| __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable }); | |
| } | |
| return to; | |
| }; | |
| var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod); | |
| var battle_stream_exports = {}; | |
| __export(battle_stream_exports, { | |
| BattlePlayer: () => BattlePlayer, | |
| BattleStream: () => BattleStream, | |
| BattleTextStream: () => BattleTextStream, | |
| getPlayerStreams: () => getPlayerStreams | |
| }); | |
| module.exports = __toCommonJS(battle_stream_exports); | |
| var import_lib = require("../lib"); | |
| var import_teams = require("./teams"); | |
| var import_battle = require("./battle"); | |
| /** | |
| * Battle Stream | |
| * Pokemon Showdown - http://pokemonshowdown.com/ | |
| * | |
| * Supports interacting with a PS battle in Stream format. | |
| * | |
| * This format is VERY NOT FINALIZED, please do not use it directly yet. | |
| * | |
| * @license MIT | |
| */ | |
| function splitFirst(str, delimiter, limit = 1) { | |
| const splitStr = []; | |
| while (splitStr.length < limit) { | |
| const delimiterIndex = str.indexOf(delimiter); | |
| if (delimiterIndex >= 0) { | |
| splitStr.push(str.slice(0, delimiterIndex)); | |
| str = str.slice(delimiterIndex + delimiter.length); | |
| } else { | |
| splitStr.push(str); | |
| str = ""; | |
| } | |
| } | |
| splitStr.push(str); | |
| return splitStr; | |
| } | |
| class BattleStream extends import_lib.Streams.ObjectReadWriteStream { | |
| constructor(options2 = {}) { | |
| super(); | |
| this.debug = !!options2.debug; | |
| this.noCatch = !!options2.noCatch; | |
| this.replay = options2.replay || false; | |
| this.keepAlive = !!options2.keepAlive; | |
| this.battle = null; | |
| } | |
| _write(chunk) { | |
| if (this.noCatch) { | |
| this._writeLines(chunk); | |
| } else { | |
| try { | |
| this._writeLines(chunk); | |
| } catch (err) { | |
| this.pushError(err, true); | |
| return; | |
| } | |
| } | |
| if (this.battle) | |
| this.battle.sendUpdates(); | |
| } | |
| _writeLines(chunk) { | |
| for (const line of chunk.split("\n")) { | |
| if (line.startsWith(">")) { | |
| const [type2, message2] = splitFirst(line.slice(1), " "); | |
| this._writeLine(type2, message2); | |
| } | |
| } | |
| } | |
| pushMessage(type2, data) { | |
| if (this.replay) { | |
| if (type2 === "update") { | |
| if (this.replay === "spectator") { | |
| const channelMessages = (0, import_battle.extractChannelMessages)(data, [0]); | |
| this.push(channelMessages[0].join("\n")); | |
| } else { | |
| const channelMessages = (0, import_battle.extractChannelMessages)(data, [-1]); | |
| this.push(channelMessages[-1].join("\n")); | |
| } | |
| } | |
| return; | |
| } | |
| this.push(`${type2} | |
| ${data}`); | |
| } | |
| _writeLine(type, message) { | |
| switch (type) { | |
| case "start": | |
| const options = JSON.parse(message); | |
| options.send = (t, data) => { | |
| if (Array.isArray(data)) | |
| data = data.join("\n"); | |
| this.pushMessage(t, data); | |
| if (t === "end" && !this.keepAlive) | |
| this.pushEnd(); | |
| }; | |
| if (this.debug) | |
| options.debug = true; | |
| this.battle = new import_battle.Battle(options); | |
| break; | |
| case "player": | |
| const [slot, optionsText] = splitFirst(message, " "); | |
| this.battle.setPlayer(slot, JSON.parse(optionsText)); | |
| break; | |
| case "p1": | |
| case "p2": | |
| case "p3": | |
| case "p4": | |
| if (message === "undo") { | |
| this.battle.undoChoice(type); | |
| } else { | |
| this.battle.choose(type, message); | |
| } | |
| break; | |
| case "forcewin": | |
| case "forcetie": | |
| this.battle.win(type === "forcewin" ? message : null); | |
| if (message) { | |
| this.battle.inputLog.push(`>forcewin ${message}`); | |
| } else { | |
| this.battle.inputLog.push(`>forcetie`); | |
| } | |
| break; | |
| case "forcelose": | |
| this.battle.lose(message); | |
| this.battle.inputLog.push(`>forcelose ${message}`); | |
| break; | |
| case "reseed": | |
| this.battle.resetRNG(message); | |
| this.battle.inputLog.push(`>reseed ${this.battle.prng.getSeed()}`); | |
| break; | |
| case "tiebreak": | |
| this.battle.tiebreak(); | |
| break; | |
| case "chat-inputlogonly": | |
| this.battle.inputLog.push(`>chat ${message}`); | |
| break; | |
| case "chat": | |
| this.battle.inputLog.push(`>chat ${message}`); | |
| this.battle.add("chat", `${message}`); | |
| break; | |
| case "eval": | |
| const battle = this.battle; | |
| battle.inputLog.push(`>${type} ${message}`); | |
| message = message.replace(/\f/g, "\n"); | |
| battle.add("", ">>> " + message.replace(/\n/g, "\n||")); | |
| try { | |
| const p1 = battle.sides[0]; | |
| const p2 = battle.sides[1]; | |
| const p3 = battle.sides[2]; | |
| const p4 = battle.sides[3]; | |
| const p1active = p1?.active[0]; | |
| const p2active = p2?.active[0]; | |
| const p3active = p3?.active[0]; | |
| const p4active = p4?.active[0]; | |
| const toID = battle.toID; | |
| const player = (input) => { | |
| input = toID(input); | |
| if (/^p[1-9]$/.test(input)) | |
| return battle.sides[parseInt(input.slice(1)) - 1]; | |
| if (/^[1-9]$/.test(input)) | |
| return battle.sides[parseInt(input) - 1]; | |
| for (const side2 of battle.sides) { | |
| if (toID(side2.name) === input) | |
| return side2; | |
| } | |
| return null; | |
| }; | |
| const pokemon = (side2, input) => { | |
| if (typeof side2 === "string") | |
| side2 = player(side2); | |
| input = toID(input); | |
| if (/^[1-9]$/.test(input)) | |
| return side2.pokemon[parseInt(input) - 1]; | |
| return side2.pokemon.find((p) => p.baseSpecies.id === input || p.species.id === input); | |
| }; | |
| let result = eval(message); | |
| if (result?.then) { | |
| result.then((unwrappedResult) => { | |
| unwrappedResult = import_lib.Utils.visualize(unwrappedResult); | |
| battle.add("", "Promise -> " + unwrappedResult); | |
| battle.sendUpdates(); | |
| }, (error) => { | |
| battle.add("", "<<< error: " + error.message); | |
| battle.sendUpdates(); | |
| }); | |
| } else { | |
| result = import_lib.Utils.visualize(result); | |
| result = result.replace(/\n/g, "\n||"); | |
| battle.add("", "<<< " + result); | |
| } | |
| } catch (e) { | |
| battle.add("", "<<< error: " + e.message); | |
| } | |
| break; | |
| case "requestlog": | |
| this.push(`requesteddata | |
| ${this.battle.inputLog.join("\n")}`); | |
| break; | |
| case "requestexport": | |
| this.push(`requesteddata | |
| ${this.battle.prngSeed} | |
| ${this.battle.inputLog.join("\n")}`); | |
| break; | |
| case "requestteam": | |
| message = message.trim(); | |
| const slotNum = parseInt(message.slice(1)) - 1; | |
| if (isNaN(slotNum) || slotNum < 0) { | |
| throw new Error(`Team requested for slot ${message}, but that slot does not exist.`); | |
| } | |
| const side = this.battle.sides[slotNum]; | |
| const team = import_teams.Teams.pack(side.team); | |
| this.push(`requesteddata | |
| ${team}`); | |
| break; | |
| case "show-openteamsheets": | |
| this.battle.showOpenTeamSheets(); | |
| break; | |
| case "version": | |
| case "version-origin": | |
| break; | |
| default: | |
| throw new Error(`Unrecognized command ">${type} ${message}"`); | |
| } | |
| } | |
| _writeEnd() { | |
| if (!this.atEOF) | |
| this.pushEnd(); | |
| this._destroy(); | |
| } | |
| _destroy() { | |
| if (this.battle) | |
| this.battle.destroy(); | |
| } | |
| } | |
| function getPlayerStreams(stream) { | |
| const streams = { | |
| omniscient: new import_lib.Streams.ObjectReadWriteStream({ | |
| write(data) { | |
| void stream.write(data); | |
| }, | |
| writeEnd() { | |
| return stream.writeEnd(); | |
| } | |
| }), | |
| spectator: new import_lib.Streams.ObjectReadStream({ | |
| read() { | |
| } | |
| }), | |
| p1: new import_lib.Streams.ObjectReadWriteStream({ | |
| write(data) { | |
| void stream.write(data.replace(/(^|\n)/g, `$1>p1 `)); | |
| } | |
| }), | |
| p2: new import_lib.Streams.ObjectReadWriteStream({ | |
| write(data) { | |
| void stream.write(data.replace(/(^|\n)/g, `$1>p2 `)); | |
| } | |
| }), | |
| p3: new import_lib.Streams.ObjectReadWriteStream({ | |
| write(data) { | |
| void stream.write(data.replace(/(^|\n)/g, `$1>p3 `)); | |
| } | |
| }), | |
| p4: new import_lib.Streams.ObjectReadWriteStream({ | |
| write(data) { | |
| void stream.write(data.replace(/(^|\n)/g, `$1>p4 `)); | |
| } | |
| }) | |
| }; | |
| (async () => { | |
| for await (const chunk of stream) { | |
| const [type2, data] = splitFirst(chunk, ` | |
| `); | |
| switch (type2) { | |
| case "update": | |
| const channelMessages = (0, import_battle.extractChannelMessages)(data, [-1, 0, 1, 2, 3, 4]); | |
| streams.omniscient.push(channelMessages[-1].join("\n")); | |
| streams.spectator.push(channelMessages[0].join("\n")); | |
| streams.p1.push(channelMessages[1].join("\n")); | |
| streams.p2.push(channelMessages[2].join("\n")); | |
| streams.p3.push(channelMessages[3].join("\n")); | |
| streams.p4.push(channelMessages[4].join("\n")); | |
| break; | |
| case "sideupdate": | |
| const [side2, sideData] = splitFirst(data, ` | |
| `); | |
| streams[side2].push(sideData); | |
| break; | |
| case "end": | |
| break; | |
| } | |
| } | |
| for (const s of Object.values(streams)) { | |
| s.pushEnd(); | |
| } | |
| })().catch((err) => { | |
| for (const s of Object.values(streams)) { | |
| s.pushError(err, true); | |
| } | |
| }); | |
| return streams; | |
| } | |
| class BattlePlayer { | |
| constructor(playerStream, debug = false) { | |
| this.stream = playerStream; | |
| this.log = []; | |
| this.debug = debug; | |
| } | |
| async start() { | |
| for await (const chunk of this.stream) { | |
| this.receive(chunk); | |
| } | |
| } | |
| receive(chunk) { | |
| for (const line of chunk.split("\n")) { | |
| this.receiveLine(line); | |
| } | |
| } | |
| receiveLine(line) { | |
| if (this.debug) | |
| console.log(line); | |
| if (!line.startsWith("|")) | |
| return; | |
| const [cmd, rest] = splitFirst(line.slice(1), "|"); | |
| if (cmd === "request") | |
| return this.receiveRequest(JSON.parse(rest)); | |
| if (cmd === "error") | |
| return this.receiveError(new Error(rest)); | |
| this.log.push(line); | |
| } | |
| receiveError(error) { | |
| throw error; | |
| } | |
| choose(choice) { | |
| void this.stream.write(choice); | |
| } | |
| } | |
| class BattleTextStream extends import_lib.Streams.ReadWriteStream { | |
| constructor(options2) { | |
| super(); | |
| this.battleStream = new BattleStream(options2); | |
| this.currentMessage = ""; | |
| void this._listen(); | |
| } | |
| async _listen() { | |
| for await (let message2 of this.battleStream) { | |
| if (!message2.endsWith("\n")) | |
| message2 += "\n"; | |
| this.push(message2 + "\n"); | |
| } | |
| this.pushEnd(); | |
| } | |
| _write(message2) { | |
| this.currentMessage += `${message2}`; | |
| const index = this.currentMessage.lastIndexOf("\n"); | |
| if (index >= 0) { | |
| void this.battleStream.write(this.currentMessage.slice(0, index)); | |
| this.currentMessage = this.currentMessage.slice(index + 1); | |
| } | |
| } | |
| _writeEnd() { | |
| return this.battleStream.writeEnd(); | |
| } | |
| } | |
| //# sourceMappingURL=battle-stream.js.map | |