mirror of
https://github.com/He4eT/cheap-glkote.git
synced 2026-05-05 00:47:28 +00:00
Dirty initial commit
This commit is contained in:
commit
eab87d32a9
13 changed files with 7039 additions and 0 deletions
3
.gitignore
vendored
Normal file
3
.gitignore
vendored
Normal file
|
|
@ -0,0 +1,3 @@
|
||||||
|
node_modules
|
||||||
|
npm-debug.log*
|
||||||
|
|
||||||
21
LICENSE
Normal file
21
LICENSE
Normal file
|
|
@ -0,0 +1,21 @@
|
||||||
|
The MIT License
|
||||||
|
|
||||||
|
Copyright (c) 2008-2018, Andrew Plotkin, Dannii Willis
|
||||||
|
|
||||||
|
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
|
of this software and associated documentation files (the "Software"), to deal
|
||||||
|
in the Software without restriction, including without limitation the rights
|
||||||
|
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||||
|
copies of the Software, and to permit persons to whom the Software is
|
||||||
|
furnished to do so, subject to the following conditions:
|
||||||
|
|
||||||
|
The above copyright notice and this permission notice shall be included in
|
||||||
|
all copies or substantial portions of the Software.
|
||||||
|
|
||||||
|
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
|
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||||
|
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||||
|
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||||
|
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||||
|
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||||
|
THE SOFTWARE.
|
||||||
55
package-lock.json
generated
Normal file
55
package-lock.json
generated
Normal file
|
|
@ -0,0 +1,55 @@
|
||||||
|
{
|
||||||
|
"name": "cheap-glkote",
|
||||||
|
"version": "0.1.0",
|
||||||
|
"lockfileVersion": 1,
|
||||||
|
"requires": true,
|
||||||
|
"dependencies": {
|
||||||
|
"ansi-escapes": {
|
||||||
|
"version": "4.3.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/ansi-escapes/-/ansi-escapes-4.3.1.tgz",
|
||||||
|
"integrity": "sha512-JWF7ocqNrp8u9oqpgV+wH5ftbt+cfvv+PTjOvKLT3AdYly/LmORARfEVT1iyjwN+4MqE5UmVKoAdIBqeoCHgLA==",
|
||||||
|
"dev": true,
|
||||||
|
"requires": {
|
||||||
|
"type-fest": "^0.11.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"emglken": {
|
||||||
|
"version": "0.3.3",
|
||||||
|
"resolved": "https://registry.npmjs.org/emglken/-/emglken-0.3.3.tgz",
|
||||||
|
"integrity": "sha512-JLDmbI9chxKU3DGtPxJjAHjoWuK99qJ0ipuWU5Cy60ZpkhUb2gHGFIIJAKkLjjBMMVcpQcDjX0pdA38LfE6kOA==",
|
||||||
|
"dev": true,
|
||||||
|
"requires": {
|
||||||
|
"glkote-term": "^0.4.4",
|
||||||
|
"minimist": "^1.2.5",
|
||||||
|
"mute-stream": "0.0.8"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"glkote-term": {
|
||||||
|
"version": "0.4.4",
|
||||||
|
"resolved": "https://registry.npmjs.org/glkote-term/-/glkote-term-0.4.4.tgz",
|
||||||
|
"integrity": "sha512-5l2t4QC9Pr4DgMz/OBGojgaAZJ3p0yf+e8pIYuz63kT0gBaHqsAuASYWQVqSkj60v6nUxKYJRzE0GQucf9PDxg==",
|
||||||
|
"dev": true,
|
||||||
|
"requires": {
|
||||||
|
"ansi-escapes": "^4.0.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"minimist": {
|
||||||
|
"version": "1.2.5",
|
||||||
|
"resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.5.tgz",
|
||||||
|
"integrity": "sha512-FM9nNUYrRBAELZQT3xeZQ7fmMOBg6nWNmJKTcgsJeaLstP/UODVpGsr5OhXhhXg6f+qtJ8uiZ+PUxkDWcgIXLw==",
|
||||||
|
"dev": true
|
||||||
|
},
|
||||||
|
"mute-stream": {
|
||||||
|
"version": "0.0.8",
|
||||||
|
"resolved": "https://registry.npmjs.org/mute-stream/-/mute-stream-0.0.8.tgz",
|
||||||
|
"integrity": "sha512-nnbWWOkoWyUsTjKrhgD0dcz22mdkSnpYqbEjIm2nhwhuxlSkpywJmBo8h0ZqJdkp73mb90SssHkN4rsRaBAfAA==",
|
||||||
|
"dev": true
|
||||||
|
},
|
||||||
|
"type-fest": {
|
||||||
|
"version": "0.11.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.11.0.tgz",
|
||||||
|
"integrity": "sha512-OdjXJxnCN1AvyLSzeKIgXTXxV+99ZuXl3Hpo9XpJAv9MBcHrrJOQ5kV7ypXOuQie+AmWG25hLbiKdwYTifzcfQ==",
|
||||||
|
"dev": true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
24
package.json
Normal file
24
package.json
Normal file
|
|
@ -0,0 +1,24 @@
|
||||||
|
{
|
||||||
|
"name": "cheap-glkote",
|
||||||
|
"version": "0.1.0",
|
||||||
|
"description": "Abstract JavaScript implementation of GlkOte",
|
||||||
|
"author": "He4eT <He4eTHb1u@gmail.com>",
|
||||||
|
"license": "MIT",
|
||||||
|
"keywords": [
|
||||||
|
"glk",
|
||||||
|
"glkote",
|
||||||
|
"interactive fiction",
|
||||||
|
"interactive-fiction"
|
||||||
|
],
|
||||||
|
"main": "src/index.js",
|
||||||
|
"dependencies": {},
|
||||||
|
"devDependencies": {
|
||||||
|
"ansi-escapes": "^4.0.0",
|
||||||
|
"emglken": "^0.3.3",
|
||||||
|
"minimist": "^1.2.5",
|
||||||
|
"mute-stream": "0.0.8"
|
||||||
|
},
|
||||||
|
"scripts": {
|
||||||
|
"test": "./tests/runtests.sh"
|
||||||
|
}
|
||||||
|
}
|
||||||
106
src/cheapGlkOte.js
Normal file
106
src/cheapGlkOte.js
Normal file
|
|
@ -0,0 +1,106 @@
|
||||||
|
const GlkOte = require('./glkOte/glkote-term')
|
||||||
|
|
||||||
|
class CheapGlkOte extends GlkOte {
|
||||||
|
constructor(handlers) {
|
||||||
|
super()
|
||||||
|
|
||||||
|
this.window = null
|
||||||
|
this.current_input_type = null
|
||||||
|
|
||||||
|
this.handlers = handlers
|
||||||
|
}
|
||||||
|
|
||||||
|
sendFn(message) {
|
||||||
|
this.send_response(
|
||||||
|
this.current_input_type,
|
||||||
|
this.window,
|
||||||
|
message)
|
||||||
|
this.current_input_type = null
|
||||||
|
}
|
||||||
|
|
||||||
|
init(iface) {
|
||||||
|
/* Only one window can be opened */
|
||||||
|
const glk_window_open = iface.Glk.glk_window_open
|
||||||
|
iface.Glk.glk_window_open = (splitwin, ...args) =>
|
||||||
|
splitwin
|
||||||
|
? null
|
||||||
|
: glk_window_open(splitwin, ...args)
|
||||||
|
|
||||||
|
this.handlers.onInit()
|
||||||
|
super.init(iface)
|
||||||
|
}
|
||||||
|
|
||||||
|
update_inputs(data) {
|
||||||
|
if (!data.length) return null
|
||||||
|
|
||||||
|
const {type} = data[0]
|
||||||
|
if (['char', 'line'].includes(type)) {
|
||||||
|
this.current_input_type = type
|
||||||
|
this.handlers.onUpdateInputs(type)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
accept_specialinput(data) {
|
||||||
|
if (data.type === 'fileref_prompt') {
|
||||||
|
const callback = ref =>
|
||||||
|
this.send_response(
|
||||||
|
'specialresponse', null, 'fileref_prompt', ref)
|
||||||
|
|
||||||
|
this.interface.Dialog.open(
|
||||||
|
data.filemode !== 'read',
|
||||||
|
data.filetype,
|
||||||
|
data.gameid,
|
||||||
|
callback)
|
||||||
|
} else {
|
||||||
|
this.error(
|
||||||
|
'Request for unknown special input type: ' + data.type)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
update_content(messages) {
|
||||||
|
const filtered =
|
||||||
|
messages.filter(content =>
|
||||||
|
content.id === this.window.id)[0]
|
||||||
|
|
||||||
|
this.handlers.onUpdateContent(filtered)
|
||||||
|
}
|
||||||
|
|
||||||
|
exit() {
|
||||||
|
this.handlers.onExit()
|
||||||
|
super.exit()
|
||||||
|
}
|
||||||
|
|
||||||
|
cancel_inputs(data) {
|
||||||
|
if (data.length === 0) {
|
||||||
|
this.current_input_type = null
|
||||||
|
this.handlers.onUpdateInputs(null)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
disable(disable) {
|
||||||
|
this.disabled = disable
|
||||||
|
this.handlers.onDisable(disable)
|
||||||
|
}
|
||||||
|
|
||||||
|
update_windows(data) {
|
||||||
|
data.forEach(win => {
|
||||||
|
if (win.type === 'buffer') {
|
||||||
|
this.window = win
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
log(msg) {
|
||||||
|
console.log(`[log]: ${msg}`)
|
||||||
|
}
|
||||||
|
|
||||||
|
warning(msg) {
|
||||||
|
console.warn(`[warning]: ${msg}`)
|
||||||
|
}
|
||||||
|
|
||||||
|
error(message) {
|
||||||
|
console.error(`[error]: ${message}`)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
module.exports = CheapGlkOte
|
||||||
36
src/fakeDialog.js
Normal file
36
src/fakeDialog.js
Normal file
|
|
@ -0,0 +1,36 @@
|
||||||
|
class FakeDialog {
|
||||||
|
constructor(handlers) {
|
||||||
|
this.streaming = false
|
||||||
|
this.handlers = handlers
|
||||||
|
this.path = 'fake/path'
|
||||||
|
}
|
||||||
|
|
||||||
|
file_ref_exists = ref => false
|
||||||
|
|
||||||
|
file_construct_ref(filename, usage, gameid) {
|
||||||
|
return {
|
||||||
|
filename: [this.path, filename].join('/'),
|
||||||
|
usage: usage || ''
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
file_read(dirent, israw) {
|
||||||
|
console.log('fake_file_read', dirent, israw)
|
||||||
|
return 'content'
|
||||||
|
}
|
||||||
|
|
||||||
|
file_write(dirent, content, israw) {
|
||||||
|
if (content.length === 0) return (void null)
|
||||||
|
console.log('fake_file_write', dirent, israw, content.length)
|
||||||
|
}
|
||||||
|
|
||||||
|
open(tosave, usage, gameid, callback) {
|
||||||
|
this.handlers.onFileNameRequest(tosave, usage, callback)
|
||||||
|
}
|
||||||
|
|
||||||
|
log(message) {
|
||||||
|
console.log(message)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
module.exports = FakeDialog
|
||||||
6396
src/glkOte/glkapi.js
Normal file
6396
src/glkOte/glkapi.js
Normal file
File diff suppressed because it is too large
Load diff
145
src/glkOte/glkote-term.js
Normal file
145
src/glkOte/glkote-term.js
Normal file
|
|
@ -0,0 +1,145 @@
|
||||||
|
class GlkOte {
|
||||||
|
constructor() {
|
||||||
|
this.current_metrics = null
|
||||||
|
this.disabled = false
|
||||||
|
this.generation = 0
|
||||||
|
this.interface = null
|
||||||
|
this.version = require('../../package.json').version
|
||||||
|
}
|
||||||
|
|
||||||
|
measure_window() {
|
||||||
|
return {
|
||||||
|
buffercharheight: 1,
|
||||||
|
buffercharwidth: 1,
|
||||||
|
buffermarginx: 0,
|
||||||
|
buffermarginy: 0,
|
||||||
|
graphicsmarginx: 0,
|
||||||
|
graphicsmarginy: 0,
|
||||||
|
gridcharheight: 1,
|
||||||
|
gridcharwidth: 1,
|
||||||
|
gridmarginx: 0,
|
||||||
|
gridmarginy: 0,
|
||||||
|
height: 0,
|
||||||
|
inspacingx: 0,
|
||||||
|
inspacingy: 0,
|
||||||
|
outspacingx: 0,
|
||||||
|
outspacingy: 0,
|
||||||
|
width: 0,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
getinterface() {
|
||||||
|
return this.interface
|
||||||
|
}
|
||||||
|
|
||||||
|
init(iface) {
|
||||||
|
if (!iface) {
|
||||||
|
this.error('No game interface object has been provided.')
|
||||||
|
}
|
||||||
|
if (!iface.accept) {
|
||||||
|
this.error('The game interface object must have an accept() function.')
|
||||||
|
}
|
||||||
|
|
||||||
|
this.interface = iface
|
||||||
|
this.current_metrics = this.measure_window()
|
||||||
|
|
||||||
|
this.send_response('init', null, this.current_metrics)
|
||||||
|
}
|
||||||
|
|
||||||
|
update(data) {
|
||||||
|
if (data.type === 'error') {
|
||||||
|
this.error(data.message)
|
||||||
|
}
|
||||||
|
if (data.type === 'pass') {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if (data.type !== 'update' && data.type !== 'exit') {
|
||||||
|
this.log(`Ignoring unknown message type: ${data.type}`)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if (data.gen === this.generation) {
|
||||||
|
this.log(`Ignoring repeated generation number: ${data.gen}`)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if (data.gen < this.generation) {
|
||||||
|
this.log(
|
||||||
|
`Ignoring out-of-order generation number: got ${data.gen}, currently at ${this.generation}`
|
||||||
|
)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
this.generation = data.gen
|
||||||
|
|
||||||
|
if (this.disabled) {
|
||||||
|
this.disable(false)
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Handle the update */
|
||||||
|
if (data.input != null) {
|
||||||
|
this.cancel_inputs(data.input)
|
||||||
|
}
|
||||||
|
if (data.windows != null) {
|
||||||
|
this.update_windows(data.windows)
|
||||||
|
}
|
||||||
|
if (data.content != null && data.content.length) {
|
||||||
|
this.update_content(data.content)
|
||||||
|
}
|
||||||
|
if (data.input != null) {
|
||||||
|
this.update_inputs(data.input)
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Disable everything if requested */
|
||||||
|
this.disabled = false
|
||||||
|
if (data.disabled || data.specialinput) {
|
||||||
|
this.disable(true)
|
||||||
|
}
|
||||||
|
|
||||||
|
if (data.specialinput != null) {
|
||||||
|
this.accept_specialinput(data.specialinput)
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Detach all handlers and exit */
|
||||||
|
if (data.type === 'exit') {
|
||||||
|
this.exit()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
send_response(type, win, val, val2) {
|
||||||
|
const res = {
|
||||||
|
type: type,
|
||||||
|
gen: this.generation,
|
||||||
|
}
|
||||||
|
|
||||||
|
if (win) {
|
||||||
|
res.window = win.id
|
||||||
|
}
|
||||||
|
|
||||||
|
if (type === 'init' || type === 'arrange') {
|
||||||
|
res.metrics = val
|
||||||
|
}
|
||||||
|
|
||||||
|
if (type === 'init') {
|
||||||
|
res.support = this.support()
|
||||||
|
}
|
||||||
|
|
||||||
|
if (type === 'char') {
|
||||||
|
res.value = val
|
||||||
|
}
|
||||||
|
|
||||||
|
if (type === 'line') {
|
||||||
|
res.value = val
|
||||||
|
}
|
||||||
|
|
||||||
|
if (type === 'specialresponse') {
|
||||||
|
res.response = val
|
||||||
|
res.value = val2
|
||||||
|
}
|
||||||
|
|
||||||
|
this.interface.accept(res)
|
||||||
|
}
|
||||||
|
|
||||||
|
support() {
|
||||||
|
return []
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
module.exports = GlkOte
|
||||||
33
src/index.js
Normal file
33
src/index.js
Normal file
|
|
@ -0,0 +1,33 @@
|
||||||
|
const FakeDialog = require('./fakeDialog')
|
||||||
|
const CheapGlkOte = require('./cheapGlkOte')
|
||||||
|
|
||||||
|
const noop = () => void null
|
||||||
|
|
||||||
|
const noopHandlers = [
|
||||||
|
'onInit',
|
||||||
|
'onUpdateContent',
|
||||||
|
'onDisable',
|
||||||
|
'onUpdateInputs',
|
||||||
|
'onFileNameRequest',
|
||||||
|
'onExit',
|
||||||
|
'setSend'
|
||||||
|
].reduce((acc, x) => (acc[x] = noop, acc), {})
|
||||||
|
|
||||||
|
module.exports = handlers_ => {
|
||||||
|
const handlers =
|
||||||
|
Object.assign({}, noopHandlers, handlers_)
|
||||||
|
|
||||||
|
const Dialog = new FakeDialog(handlers)
|
||||||
|
const GlkOte = new CheapGlkOte(handlers)
|
||||||
|
|
||||||
|
const sendFn = GlkOte.sendFn.bind(GlkOte)
|
||||||
|
|
||||||
|
return {
|
||||||
|
sendFn,
|
||||||
|
glkInterface: {
|
||||||
|
Dialog,
|
||||||
|
GlkOte,
|
||||||
|
Glk: {}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
133
src/stdio.js
Normal file
133
src/stdio.js
Normal file
|
|
@ -0,0 +1,133 @@
|
||||||
|
const readline = require('readline')
|
||||||
|
const MuteStream = require('mute-stream')
|
||||||
|
const ansiEscapes = require('ansi-escapes')
|
||||||
|
|
||||||
|
const stdin = process.stdin
|
||||||
|
const stdout = new MuteStream()
|
||||||
|
stdout.pipe(process.stdout)
|
||||||
|
const rl = readline.createInterface({
|
||||||
|
input: stdin,
|
||||||
|
output: stdout,
|
||||||
|
prompt: ''
|
||||||
|
})
|
||||||
|
|
||||||
|
let send = _ => _
|
||||||
|
|
||||||
|
const setSend = fn => {
|
||||||
|
send = fn
|
||||||
|
}
|
||||||
|
|
||||||
|
const onInit = () => {
|
||||||
|
if (stdin.isTTY) {
|
||||||
|
stdin.setRawMode(true)
|
||||||
|
}
|
||||||
|
readline.emitKeypressEvents(stdin)
|
||||||
|
rl.resume()
|
||||||
|
}
|
||||||
|
|
||||||
|
const onUpdateContent = messages =>
|
||||||
|
messages.text.forEach(({append, content}) => {
|
||||||
|
if (!append) {
|
||||||
|
stdout.write('\n')
|
||||||
|
}
|
||||||
|
|
||||||
|
if (content) {
|
||||||
|
content.forEach(x => {
|
||||||
|
if (x.text === '>') return null
|
||||||
|
|
||||||
|
if (x.style === 'input') {
|
||||||
|
if (stdout.isTTY) {
|
||||||
|
stdout.write(ansiEscapes.eraseLines(2))
|
||||||
|
stdout.write('> ')
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
stdout.write(
|
||||||
|
typeof x === 'string'
|
||||||
|
? x
|
||||||
|
: x.text)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
const onUpdateInputs = type => {
|
||||||
|
type
|
||||||
|
? attach_handlers(type)
|
||||||
|
: detach_handlers()
|
||||||
|
}
|
||||||
|
|
||||||
|
const onExit = () => {
|
||||||
|
detach_handlers()
|
||||||
|
rl.close()
|
||||||
|
stdout.write('\n')
|
||||||
|
}
|
||||||
|
|
||||||
|
const onDisable = disable =>
|
||||||
|
disable
|
||||||
|
? detach_handlers()
|
||||||
|
: attach_handlers()
|
||||||
|
|
||||||
|
const onFileNameRequest = (tosave, usage, callback) => {
|
||||||
|
stdout.write('\n')
|
||||||
|
rl.question(
|
||||||
|
'Please enter a file name (without an extension): ',
|
||||||
|
filename => callback(filename
|
||||||
|
? {filename, usage}
|
||||||
|
: null))
|
||||||
|
}
|
||||||
|
|
||||||
|
const handle_char_input = (str, key) => {
|
||||||
|
const key_replacements = {
|
||||||
|
'\x7F': 'delete',
|
||||||
|
'\t': 'tab',
|
||||||
|
}
|
||||||
|
|
||||||
|
detach_handlers()
|
||||||
|
|
||||||
|
// Make sure this char isn't being remembered for the next line input
|
||||||
|
rl._line_buffer = null
|
||||||
|
rl.line = ''
|
||||||
|
|
||||||
|
// Process special keys
|
||||||
|
const res =
|
||||||
|
key_replacements[str] ||
|
||||||
|
str ||
|
||||||
|
key.name.replace(/f(\d+)/, 'func$1')
|
||||||
|
|
||||||
|
send(res)
|
||||||
|
}
|
||||||
|
|
||||||
|
const attach_handlers = type => {
|
||||||
|
if (type === 'char') {
|
||||||
|
stdout.mute()
|
||||||
|
stdin.on('keypress', handle_char_input)
|
||||||
|
}
|
||||||
|
if (type === 'line') {
|
||||||
|
rl.on('line', handle_line_input)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const detach_handlers = () => {
|
||||||
|
stdin.removeListener('keypress', handle_char_input)
|
||||||
|
rl.removeListener('line', handle_line_input)
|
||||||
|
stdout.unmute()
|
||||||
|
}
|
||||||
|
|
||||||
|
const handle_line_input = line => {
|
||||||
|
if (stdout.isTTY) {
|
||||||
|
stdout.write(ansiEscapes.eraseLines(1))
|
||||||
|
}
|
||||||
|
detach_handlers()
|
||||||
|
|
||||||
|
send(line)
|
||||||
|
}
|
||||||
|
|
||||||
|
module.exports.handlers = {
|
||||||
|
onInit,
|
||||||
|
onUpdateContent,
|
||||||
|
onDisable,
|
||||||
|
onUpdateInputs,
|
||||||
|
onFileNameRequest,
|
||||||
|
onExit,
|
||||||
|
setSend
|
||||||
|
}
|
||||||
82
tests/player.stdio.js
Normal file
82
tests/player.stdio.js
Normal file
|
|
@ -0,0 +1,82 @@
|
||||||
|
#!/usr/bin/env node
|
||||||
|
|
||||||
|
/*
|
||||||
|
|
||||||
|
Emglken runner
|
||||||
|
==============
|
||||||
|
|
||||||
|
Copyright (c) 2020 Dannii Willis
|
||||||
|
MIT licenced
|
||||||
|
https://github.com/curiousdannii/emglken
|
||||||
|
|
||||||
|
*/
|
||||||
|
|
||||||
|
const fs = require('fs')
|
||||||
|
const minimist = require('minimist')
|
||||||
|
|
||||||
|
const CheapGlkOte = require('../src/')
|
||||||
|
const { handlers } = require('../src/stdio')
|
||||||
|
|
||||||
|
const formats = [
|
||||||
|
{
|
||||||
|
id: 'bocfel',
|
||||||
|
extensions: /z([3458]|blorb)$/,
|
||||||
|
engine: 'bocfel.js',
|
||||||
|
},
|
||||||
|
|
||||||
|
{
|
||||||
|
id: 'glulxe',
|
||||||
|
extensions: /(gblorb|ulx)$/,
|
||||||
|
engine: 'glulxe.js',
|
||||||
|
},
|
||||||
|
|
||||||
|
{
|
||||||
|
id: 'git',
|
||||||
|
extensions: /(gblorb|ulx)$/,
|
||||||
|
engine: 'git.js',
|
||||||
|
},
|
||||||
|
|
||||||
|
{
|
||||||
|
id: 'hugo',
|
||||||
|
extensions: /hex$/,
|
||||||
|
engine: 'hugo.js',
|
||||||
|
},
|
||||||
|
|
||||||
|
{
|
||||||
|
id: 'tads',
|
||||||
|
extensions: /(gam|t3)$/,
|
||||||
|
engine: 'tads.js',
|
||||||
|
},
|
||||||
|
]
|
||||||
|
|
||||||
|
function run() {
|
||||||
|
const argv = minimist(process.argv.slice(2))
|
||||||
|
|
||||||
|
const storyfile = argv._[0]
|
||||||
|
|
||||||
|
let format
|
||||||
|
for (const formatspec of formats) {
|
||||||
|
if (
|
||||||
|
formatspec.id === argv.vm ||
|
||||||
|
(!argv.vm && formatspec.extensions.test(storyfile))
|
||||||
|
) {
|
||||||
|
format = formatspec
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!format) {
|
||||||
|
console.error('Unknown storyfile format')
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
const { glkInterface, sendFn } = CheapGlkOte(handlers)
|
||||||
|
handlers.setSend(sendFn)
|
||||||
|
|
||||||
|
const engine = require('emglken/src/' + format.engine)
|
||||||
|
const vm = new engine()
|
||||||
|
vm.prepare(fs.readFileSync(storyfile), glkInterface)
|
||||||
|
vm.start()
|
||||||
|
}
|
||||||
|
|
||||||
|
run()
|
||||||
BIN
tests/praxix.z5
Normal file
BIN
tests/praxix.z5
Normal file
Binary file not shown.
5
tests/runtests.sh
Executable file
5
tests/runtests.sh
Executable file
|
|
@ -0,0 +1,5 @@
|
||||||
|
#!/bin/bash
|
||||||
|
|
||||||
|
cd "$(dirname "$0")"
|
||||||
|
|
||||||
|
node player.stdio.js praxix.z5 < <(sleep 1 && echo 'all')
|
||||||
Loading…
Add table
Add a link
Reference in a new issue