command interpreting now bassically works.
This commit is contained in:
parent
534ffeaf8e
commit
43c06e39f5
9
client/package-lock.json
generated
9
client/package-lock.json
generated
@ -18,6 +18,7 @@
|
|||||||
"graphology": "^0.25.4",
|
"graphology": "^0.25.4",
|
||||||
"graphology-layout": "^0.6.1",
|
"graphology-layout": "^0.6.1",
|
||||||
"graphology-layout-force": "^0.2.4",
|
"graphology-layout-force": "^0.2.4",
|
||||||
|
"highlight.js": "^11.10.0",
|
||||||
"react": "^18.3.1",
|
"react": "^18.3.1",
|
||||||
"react-dom": "^18.3.1",
|
"react-dom": "^18.3.1",
|
||||||
"react-hook-form": "^7.53.0",
|
"react-hook-form": "^7.53.0",
|
||||||
@ -2507,6 +2508,14 @@
|
|||||||
"node": ">= 0.4"
|
"node": ">= 0.4"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/highlight.js": {
|
||||||
|
"version": "11.10.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/highlight.js/-/highlight.js-11.10.0.tgz",
|
||||||
|
"integrity": "sha512-SYVnVFswQER+zu1laSya563s+F8VDGt7o35d4utbamowvUNLLMovFqwCLSocpZTz3MgaSRA1IbqRWZv97dtErQ==",
|
||||||
|
"engines": {
|
||||||
|
"node": ">=12.0.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/indent-string": {
|
"node_modules/indent-string": {
|
||||||
"version": "4.0.0",
|
"version": "4.0.0",
|
||||||
"resolved": "https://registry.npmjs.org/indent-string/-/indent-string-4.0.0.tgz",
|
"resolved": "https://registry.npmjs.org/indent-string/-/indent-string-4.0.0.tgz",
|
||||||
|
@ -13,6 +13,7 @@
|
|||||||
"graphology": "^0.25.4",
|
"graphology": "^0.25.4",
|
||||||
"graphology-layout": "^0.6.1",
|
"graphology-layout": "^0.6.1",
|
||||||
"graphology-layout-force": "^0.2.4",
|
"graphology-layout-force": "^0.2.4",
|
||||||
|
"highlight.js": "^11.10.0",
|
||||||
"react": "^18.3.1",
|
"react": "^18.3.1",
|
||||||
"react-dom": "^18.3.1",
|
"react-dom": "^18.3.1",
|
||||||
"react-hook-form": "^7.53.0",
|
"react-hook-form": "^7.53.0",
|
||||||
|
@ -50,10 +50,10 @@ export async function fetchPageAtEdit(number, id) {
|
|||||||
return shoofetch(`${apiUrl}/page/${number}/${id}`, {method: 'GET'});
|
return shoofetch(`${apiUrl}/page/${number}/${id}`, {method: 'GET'});
|
||||||
}
|
}
|
||||||
|
|
||||||
export async function postPage({id, title, description}) {
|
export async function postPage({id, title, description, type}) {
|
||||||
return shoofetch(`${apiUrl}/page/${id}`, {
|
return shoofetch(`${apiUrl}/page/${id}`, {
|
||||||
method: 'POST',
|
method: 'POST',
|
||||||
body: JSON.stringify({id: id, title: title, description: description}),
|
body: JSON.stringify({id: id, title: title, description: description, type: type}),
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -99,3 +99,7 @@ export async function postLogOut() {
|
|||||||
export async function fetchProfile() {
|
export async function fetchProfile() {
|
||||||
return shoofetch(`${apiUrl}/user`, {method: 'GET'});
|
return shoofetch(`${apiUrl}/user`, {method: 'GET'});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export async function fetchCurrentVerbs() {
|
||||||
|
return shoofetch(`${apiUrl}/embody/verbs`, {method: 'GET'});
|
||||||
|
}
|
@ -64,7 +64,7 @@ function Live({editing, ...props}) {
|
|||||||
</MessageFeed>
|
</MessageFeed>
|
||||||
<Sidebar pagenumber={currentNumber} hidden="true" sendWord={(word) => setCommand((command + " " + word).trim())}>
|
<Sidebar pagenumber={currentNumber} hidden="true" sendWord={(word) => setCommand((command + " " + word).trim())}>
|
||||||
</Sidebar>
|
</Sidebar>
|
||||||
<CommandEntry onSubmit={sendMessage}/>
|
<CommandEntry onSubmit={(w) => { console.log(w); sendMessage(w); } }/>
|
||||||
</>
|
</>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
@ -23,3 +23,6 @@
|
|||||||
.messages-tray li {
|
.messages-tray li {
|
||||||
list-style: none;
|
list-style: none;
|
||||||
}
|
}
|
||||||
|
.messages-tray li:nth-child(even) {
|
||||||
|
background: lightgray;
|
||||||
|
}
|
@ -1,5 +1,7 @@
|
|||||||
import { useState } from 'react';
|
import { useState } from 'react';
|
||||||
|
import { useQuery } from '@tanstack/react-query';
|
||||||
import { useLoggedIn } from '../AuthProvider.jsx';
|
import { useLoggedIn } from '../AuthProvider.jsx';
|
||||||
|
import { fetchCurrentVerbs } from '../apiTools.jsx';
|
||||||
|
|
||||||
import './Sidebar.css';
|
import './Sidebar.css';
|
||||||
|
|
||||||
@ -7,13 +9,18 @@ function Sidebar({children, pagenumber, hidden=false, sendWord=(()=>null)}) {
|
|||||||
const [open, setOpen] = useState(!hidden);
|
const [open, setOpen] = useState(!hidden);
|
||||||
const loggedIn = useLoggedIn();
|
const loggedIn = useLoggedIn();
|
||||||
|
|
||||||
const verbs = ["go", "take"];
|
const { isPending, isError, error, data } = useQuery({ // fetch the currrent values
|
||||||
|
queryKey: ['my verbs'],
|
||||||
|
queryFn: fetchCurrentVerbs,
|
||||||
|
});
|
||||||
|
|
||||||
|
const verbs = data;
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className={`sidebar ${!open && "sidebar-hidden"}`}>
|
<div className={`sidebar ${!open && "sidebar-hidden"}`}>
|
||||||
<button onClick={() => setOpen(!open)}>{open ? "hide" : "show"}</button>
|
<button onClick={() => setOpen(!open)}>{open ? "hide" : "show"}</button>
|
||||||
<ul>
|
<ul>
|
||||||
{verbs.map( (name) => (
|
{(verbs || []).map( (name) => (
|
||||||
<li key={name}>
|
<li key={name}>
|
||||||
<button onClick={() => sendWord(name)}>
|
<button onClick={() => sendWord(name)}>
|
||||||
{name}
|
{name}
|
||||||
|
@ -1,3 +1,4 @@
|
|||||||
|
import { useState, useEffect } from 'react';
|
||||||
import { useQuery, useMutation, useQueryClient } from '@tanstack/react-query';
|
import { useQuery, useMutation, useQueryClient } from '@tanstack/react-query';
|
||||||
import { useParams, useNavigate } from 'react-router-dom';
|
import { useParams, useNavigate } from 'react-router-dom';
|
||||||
import { apiUrl, fetchPage, fetchPageAtEdit, postPage, deletePage } from '../apiTools.jsx';
|
import { apiUrl, fetchPage, fetchPageAtEdit, postPage, deletePage } from '../apiTools.jsx';
|
||||||
@ -5,6 +6,7 @@ import { useFixLinks } from '../clientStuff.jsx';
|
|||||||
import { useLoggedIn } from '../AuthProvider.jsx';
|
import { useLoggedIn } from '../AuthProvider.jsx';
|
||||||
|
|
||||||
import './Pages.css';
|
import './Pages.css';
|
||||||
|
import 'highlight.js/styles/a11y-dark.min.css';
|
||||||
|
|
||||||
function Page({ editing, number, editid=null, linkClick=()=>{} }) {
|
function Page({ editing, number, editid=null, linkClick=()=>{} }) {
|
||||||
const queryClient = useQueryClient();
|
const queryClient = useQueryClient();
|
||||||
@ -17,10 +19,10 @@ function Page({ editing, number, editid=null, linkClick=()=>{} }) {
|
|||||||
})
|
})
|
||||||
|
|
||||||
const postMutation = useMutation({ // for changing the value when we're done with it
|
const postMutation = useMutation({ // for changing the value when we're done with it
|
||||||
mutationFn: ({id, title, description}) => postPage({id, title, description}),
|
mutationFn: ({id, title, description, type}) => postPage({id, title, description, type}),
|
||||||
onSettled: async (data, error, variables) => {
|
onSettled: async (data, error, variables) => {
|
||||||
// Invalidate and refetch
|
// Invalidate and refetch
|
||||||
await queryClient.invalidateQueries({ queryKey: ['page', variables.id, undefined] })
|
await queryClient.invalidateQueries({ queryKey: ['page', variables.id, null] })
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
@ -34,18 +36,22 @@ function Page({ editing, number, editid=null, linkClick=()=>{} }) {
|
|||||||
|
|
||||||
const readyToShow = !(fetchQuery.error || fetchQuery.isPending);
|
const readyToShow = !(fetchQuery.error || fetchQuery.isPending);
|
||||||
|
|
||||||
let {id, title, description, html, time, author} = fetchQuery.data || {};
|
let {id, title, description, html, lua, time, author, type} = fetchQuery.data || {};
|
||||||
|
const [verb, setVerb] = useState(false);
|
||||||
if (!title) title = "[no title]";
|
if (!title) title = "[no title]";
|
||||||
if (!html) html = "[body missing]";
|
if (!html) html = "[body missing]";
|
||||||
|
if (!lua) lua = "[no definition]";
|
||||||
if (!description) description = "[body missing]";
|
if (!description) description = "[body missing]";
|
||||||
|
useEffect(() => setVerb(type == 1), [type])
|
||||||
|
|
||||||
function submitChanges(e) {
|
function submitChanges(e) {
|
||||||
const newTitle = document.querySelector('span').innerHTML;
|
const newTitle = document.querySelector('span').innerHTML;
|
||||||
const newText = document.querySelector('pre').innerHTML;
|
const newText = document.querySelector('pre').innerHTML.replace(/<br>/g, "\n");
|
||||||
postMutation.mutate({
|
postMutation.mutate({
|
||||||
id: number,
|
id: number,
|
||||||
title: newTitle,
|
title: newTitle,
|
||||||
description: newText
|
description: newText,
|
||||||
|
type: verb ? 1 : 0,
|
||||||
});
|
});
|
||||||
navigate(`/${number}`, {replace: true})
|
navigate(`/${number}`, {replace: true})
|
||||||
}
|
}
|
||||||
@ -56,6 +62,7 @@ function Page({ editing, number, editid=null, linkClick=()=>{} }) {
|
|||||||
navigate(`/`);
|
navigate(`/`);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="main-column">
|
<div className="main-column">
|
||||||
<header>
|
<header>
|
||||||
@ -66,7 +73,11 @@ function Page({ editing, number, editid=null, linkClick=()=>{} }) {
|
|||||||
contentEditable={editing}
|
contentEditable={editing}
|
||||||
dangerouslySetInnerHTML={{__html: readyToShow ? title : "..." }} />
|
dangerouslySetInnerHTML={{__html: readyToShow ? title : "..." }} />
|
||||||
</h1>
|
</h1>
|
||||||
{readyToShow && editid && `saved at ${time} by user #${author}`}
|
<p>{readyToShow && editid && `saved at ${time} by user #${author}`}</p>
|
||||||
|
{ editing ? <label>
|
||||||
|
<input type="checkbox" checked={verb} onChange={() => setVerb(!verb)}/>
|
||||||
|
{verb ? "verb" : "noun"}
|
||||||
|
</label> : <br/> }
|
||||||
<hr/>
|
<hr/>
|
||||||
</header>
|
</header>
|
||||||
<section className="page-contents">
|
<section className="page-contents">
|
||||||
@ -75,12 +86,17 @@ function Page({ editing, number, editid=null, linkClick=()=>{} }) {
|
|||||||
editing ?
|
editing ?
|
||||||
<pre
|
<pre
|
||||||
contentEditable="true"
|
contentEditable="true"
|
||||||
dangerouslySetInnerHTML={{__html: description}} />
|
dangerouslySetInnerHTML={{__html: description.replace(/\n/g, "<br>")}} />
|
||||||
|
:
|
||||||
|
(
|
||||||
|
verb ?
|
||||||
|
<pre><code dangerouslySetInnerHTML={{__html: lua.replace(/\n/g, "<br>")}}></code></pre>
|
||||||
:
|
:
|
||||||
<div
|
<div
|
||||||
dangerouslySetInnerHTML={{__html: html}}
|
dangerouslySetInnerHTML={{__html: html}}
|
||||||
onClick={linkClick} />
|
onClick={linkClick} />
|
||||||
)
|
)
|
||||||
|
)
|
||||||
:
|
:
|
||||||
"..."
|
"..."
|
||||||
}
|
}
|
||||||
|
@ -1,6 +1,27 @@
|
|||||||
|
const { LuaFactory } = require('wasmoon');
|
||||||
|
|
||||||
const sqlite = require('better-sqlite3');
|
const sqlite = require('better-sqlite3');
|
||||||
const db = new sqlite('the_big_db.db', { verbose: console.log });
|
const db = new sqlite('the_big_db.db', { verbose: console.log });
|
||||||
|
|
||||||
|
const factory = new LuaFactory();
|
||||||
|
|
||||||
|
let lua;
|
||||||
|
|
||||||
|
async function makeLua() {
|
||||||
|
lua = await factory.createEngine();
|
||||||
|
lua.global.set('executeVerb', executeVerb);
|
||||||
|
lua.global.set('getAttribute', luaSafe(getAttribute));
|
||||||
|
lua.global.set('setAttribute', luaSafe(setAttribute));
|
||||||
|
lua.global.set('deleteAttribute', luaSafe(deleteAttribute));
|
||||||
|
lua.global.set('hasOwnAttribute', luaSafe(hasOwnAttribute));
|
||||||
|
lua.global.set('verifyObjectReference', luaSafe(verifyObjectReference));
|
||||||
|
lua.global.set('lookUpObject', luaSafe(lookUpObject));
|
||||||
|
lua.global.set('lookUpObjectAttributes', luaSafe(lookUpObjectAttributes));
|
||||||
|
lua.global.set('interpret', interpret);
|
||||||
|
}
|
||||||
|
|
||||||
|
makeLua();
|
||||||
|
|
||||||
var sockets = new Map();
|
var sockets = new Map();
|
||||||
|
|
||||||
function interpret(context, player, command) {
|
function interpret(context, player, command) {
|
||||||
@ -14,20 +35,32 @@ function interpret(context, player, command) {
|
|||||||
// command: the full string typed in by the player
|
// command: the full string typed in by the player
|
||||||
|
|
||||||
const socket = sockets.get(player)
|
const socket = sockets.get(player)
|
||||||
const words = command.trim().split(' ');
|
const wordsAndQuotes = tokenizeQuotes(command.trim());
|
||||||
|
|
||||||
try {
|
try {
|
||||||
socket?.send(`interpreting and executing the received command: ${command}`);
|
|
||||||
|
|
||||||
//for the moment we'll only allow statements of the form "go north" with implicit subject: the player
|
//for the moment we'll only allow statements of the form "go north" with implicit subject: the player
|
||||||
|
|
||||||
|
const verb = findVerb(wordsAndQuotes[0], context, player);
|
||||||
|
|
||||||
|
if (verb) {
|
||||||
|
let prepositions = getAttribute(verb, "prepositions");
|
||||||
|
|
||||||
|
const wordsAndPreps = tokenizePrepositions(wordsAndQuotes, prepositions || []);
|
||||||
|
let words = wordsAndPreps.filter((x) => typeof(x) == "string");
|
||||||
|
let preps = wordsAndPreps.filter((x) => typeof(x) == "object");
|
||||||
|
let prepMap = {};
|
||||||
|
for (const obj of preps) {
|
||||||
|
prepMap[obj.preposition] = obj.contents;
|
||||||
|
}
|
||||||
|
|
||||||
const [first, second, third, ...rest] = words;
|
const [first, second, third, ...rest] = words;
|
||||||
|
|
||||||
const verb = findVerb(first, context, player);
|
executeVerb(command, player, verb, prepMap, player, second, third, ...rest)
|
||||||
|
} else {
|
||||||
console.log(`executing verb ${verb} towards ${player} on ${player} ${second} ${third}`);
|
interpret(1,1, `system_send_message '{"error": "verb ${verbId} not found in ${fullCommand}"}' to ${player}`);
|
||||||
executeVerb(player, verb, player, second, third, ...rest)
|
}
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
socket?.send(`got an error! ${error.mesage}`);
|
interpret(1,1, `system_send_message '{"error": "error found: ${error}"}' to ${player}`);
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
@ -42,18 +75,79 @@ function interpret(context, player, command) {
|
|||||||
*/
|
*/
|
||||||
}
|
}
|
||||||
|
|
||||||
|
async function executeVerb(fullCommand, outputObject, verbId, prepositions, subject, object, ...rest) {
|
||||||
|
if (verbId == 2) {
|
||||||
|
// todo: make this more intelligently get the rright thing to send
|
||||||
|
let theObject;
|
||||||
|
try {
|
||||||
|
theObject = verifyObjectReference(object);
|
||||||
|
} catch (error) {
|
||||||
|
theObject = verifyObjectReference(outputObject);
|
||||||
|
}
|
||||||
|
if (prepositions["to"]) {
|
||||||
|
let destination = verifyObjectReference(prepositions["to"])
|
||||||
|
return sockets.get(destination)?.send(object);
|
||||||
|
}
|
||||||
|
return sockets
|
||||||
|
.get(theObject)
|
||||||
|
?.send(`full command: ${fullCommand}. verb id: ${verbId}. subject: ${subject} object: ${object}`);
|
||||||
|
}
|
||||||
|
|
||||||
|
const fullVerb = lookUpObject(verbId);
|
||||||
|
if (!fullVerb) {
|
||||||
|
return interpret(1, 1, `system_send_message '{"error": "verb ${verbId} not found in ${fullCommand}"}' to ${outputObject}`);
|
||||||
|
}
|
||||||
|
|
||||||
|
// generate a temp name for this verb, then define it and execute.
|
||||||
|
const verbName = "verb" + Math.random().toString(36).substring(2);
|
||||||
|
const body = fullVerb.description.replace(/ /g, " ");
|
||||||
|
const verbDeclaration = `
|
||||||
|
function ${verbName} (fullCommand, outputObject, prepositionMap, subject, object, ...)
|
||||||
|
${body}
|
||||||
|
end`;
|
||||||
|
console.log("verb we're running:");
|
||||||
|
console.log(verbDeclaration);
|
||||||
|
await lua.doString(verbDeclaration);
|
||||||
|
|
||||||
|
|
||||||
|
let returnValue = await lua.global.get(verbName)(fullCommand, outputObject, prepositions, subject, object, ...rest);
|
||||||
|
// maybe unset it so we dno't have an ever-growing set of functions cluttering up the space?
|
||||||
|
//lua.global.set(verbName, null);
|
||||||
|
|
||||||
|
return returnValue;
|
||||||
|
}
|
||||||
|
|
||||||
|
const objectQuery = db.prepare('select * from pages where number=? order by time desc');
|
||||||
|
function lookUpObject(number) {
|
||||||
|
return objectQuery.get(number);
|
||||||
|
}
|
||||||
|
|
||||||
|
const attributeQuery = db.prepare(`select * from attributes where number=?`);
|
||||||
|
function lookUpObjectAttributes(number) {
|
||||||
|
return JSON.parse(attributeQuery.get(number).contents);
|
||||||
|
}
|
||||||
|
|
||||||
function findVerb(word, location, actor) {
|
function findVerb(word, location, actor) {
|
||||||
// returns the number of a verb which matches the requested word.
|
// returns the number of a verb which matches the requested word.
|
||||||
// check for verbs on actor
|
// check for verbs on actor
|
||||||
let verb = findVerbOnObject(word, actor);
|
let verb = [];
|
||||||
|
|
||||||
|
if (verifyObjectReference(word)) {
|
||||||
|
return verifyObjectReference(word);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (actor) {
|
||||||
|
verb = findVerbOnObject(word, actor);
|
||||||
if (verb) return verb;
|
if (verb) return verb;
|
||||||
|
|
||||||
const actorContents = getAttribute(actor, "contents");
|
let actorContents = getAttribute(actor, "contents");
|
||||||
for (const obj of (actorContents || [])) {
|
for (const obj of (actorContents || [])) {
|
||||||
verb = findVerbOnObject(word, obj);
|
verb = findVerbOnObject(word, obj);
|
||||||
if (verb) return verb;
|
if (verb) return verb;
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (location) {
|
||||||
const locationContents = getAttribute(location, "contents");
|
const locationContents = getAttribute(location, "contents");
|
||||||
for (const obj of (locationContents || [])) {
|
for (const obj of (locationContents || [])) {
|
||||||
verb = findVerbOnObject(word, obj);
|
verb = findVerbOnObject(word, obj);
|
||||||
@ -62,6 +156,7 @@ function findVerb(word, location, actor) {
|
|||||||
|
|
||||||
verb = findVerbOnObject(word, location);
|
verb = findVerbOnObject(word, location);
|
||||||
if (verb) return verb;
|
if (verb) return verb;
|
||||||
|
}
|
||||||
|
|
||||||
return undefined;
|
return undefined;
|
||||||
}
|
}
|
||||||
@ -89,75 +184,65 @@ function findVerbOnObject(word, object) {
|
|||||||
return undefined;
|
return undefined;
|
||||||
}
|
}
|
||||||
|
|
||||||
function executeVerb(outputObject, verbId, subject, object, ...rest) {
|
function findAllVerbsOnObject(object) {
|
||||||
const fullVerb = lookUpObject(verbId);
|
let verbs = [];
|
||||||
if (!fullVerb) {
|
let focus = object;
|
||||||
return sockets.get(outputObject)?.send(`verb not found`);
|
|
||||||
}
|
while (focus) {
|
||||||
sockets.get(outputObject)?.send(`verb found as ${fullVerb.name}`);
|
const newVerbs = (getAttribute(focus, "verbs") || []).filter((verb) => !(verb in verbs));
|
||||||
const func = fullVerb.fn;
|
verbs = verbs.concat(newVerbs);
|
||||||
if (!func) {
|
|
||||||
return sockets.get(outputObject)?.send(`verb was found but didn't have a fn attribute`);
|
focus = getAttribute(focus, "parent");
|
||||||
}
|
|
||||||
return fullVerb.fn(outputObject, subject, object, ...rest);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
const objectQuery = db.prepare('select * from pages where number=? order by time desc');
|
return verbs;
|
||||||
function lookUpObject(number) {
|
|
||||||
return hardCodedObjects(number) || objectQuery.get(number);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
const attributeQuery = db.prepare(`select * from attributes where number=?`);
|
function findAllVerbsInArea(location, actor) {
|
||||||
function lookUpObjectAttributes(number) {
|
// returns all verb ids in an area or on an actor
|
||||||
return JSON.parse(attributeQuery.get(number).contents);
|
let verbs = findAllVerbsOnObject(actor);
|
||||||
|
|
||||||
|
const actorContents = getAttribute(actor, "contents");
|
||||||
|
for (const obj of (actorContents || [])) {
|
||||||
|
verbs = concatWithoutDuplicates(verbs, findAllVerbsOnObject(obj));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (location) {
|
||||||
function hardCodedObjects(id) {
|
const locationContents = getAttribute(location, "contents");
|
||||||
// return objectQuery.get(id);
|
for (const obj of (locationContents || [])) {
|
||||||
if (id == 29) return {
|
verbs = concatWithoutDuplicates(verbs, findAllVerbsOnObject(obj));
|
||||||
title: "look",
|
|
||||||
verb: true,
|
|
||||||
description: "send description of direct object to subject's socket",
|
|
||||||
fn: (logReceiver, subject, object, ...rest) => {
|
|
||||||
sockets.get(logReceiver)?.send(`you looked around! subject ${subject} object: ${object} args: ${rest}`);
|
|
||||||
console.log(`${subject} looked at ${object} with args ${rest}, and output was directed to ${logReceiver}`);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (id == 31) return {
|
|
||||||
title: "warp",
|
|
||||||
verb: true,
|
|
||||||
description: "this built-in function changes the player's location, as well as telling their client to move",
|
|
||||||
fn: (logReceiver, subject, object, ...rest) => {
|
|
||||||
const objectNum = verifyObjectReference(object);
|
|
||||||
const subjectNum = verifyObjectReference(subject);
|
|
||||||
setAttribute(subjectNum, "location", objectNum);
|
|
||||||
|
|
||||||
sockets.get(logReceiver)?.send(`location change to: ${object}`);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
verbs = concatWithoutDuplicates(verbs, findAllVerbsOnObject(location));
|
||||||
|
}
|
||||||
|
|
||||||
|
return verbs;
|
||||||
|
}
|
||||||
|
|
||||||
const pullAttribute = db.prepare(`select * from attributes where number=?`);
|
const pullAttribute = db.prepare(`select * from attributes where number=?`);
|
||||||
function getAttribute(obj, attributeName) {
|
function getAttribute(obj, attributeName) {
|
||||||
const attributeStore = pullAttribute.get(verifyObjectReference(obj));
|
if (!verifyObjectReference(obj)) return undefined;
|
||||||
const contents = JSON.parse(attributeStore.contents);
|
|
||||||
|
let attributeStore = pullAttribute.get(verifyObjectReference(obj));
|
||||||
|
|
||||||
|
let contents = JSON.parse(attributeStore.contents);
|
||||||
|
|
||||||
if (contents.hasOwnProperty(attributeName)) {
|
if (contents.hasOwnProperty(attributeName)) {
|
||||||
return contents[attributeName];
|
return contents[attributeName];
|
||||||
}
|
}
|
||||||
|
|
||||||
if (contents.hasOwnProperty("parent")) {
|
if (contents.hasOwnProperty("parent")) {
|
||||||
return getAttribute(contents.parent, attributeName);
|
if (contents["parent"]) {
|
||||||
|
return getAttribute(verifyObjectReference(contents["parent"]), attributeName);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return undefined;
|
return undefined;
|
||||||
}
|
}
|
||||||
|
|
||||||
function hasOwnAttribute(obj, attributeName) {
|
function hasOwnAttribute(obj, attributeName) {
|
||||||
|
if (!verifyObjectReference(obj)) return undefined;
|
||||||
|
|
||||||
const attributeStore = pullAttribute.get(verifyObjectReference(obj));
|
const attributeStore = pullAttribute.get(verifyObjectReference(obj));
|
||||||
const contents = JSON.parse(attributeStore.contents);
|
const contents = JSON.parse(attributeStore.contents);
|
||||||
|
|
||||||
@ -166,6 +251,8 @@ function hasOwnAttribute(obj, attributeName) {
|
|||||||
|
|
||||||
const insertAttribute = db.prepare(`update or replace attributes set contents=:contents where number=:number`);
|
const insertAttribute = db.prepare(`update or replace attributes set contents=:contents where number=:number`);
|
||||||
function setAttribute(obj, attributeName, value) {
|
function setAttribute(obj, attributeName, value) {
|
||||||
|
if (!verifyObjectReference(obj)) return undefined;
|
||||||
|
|
||||||
const attributeStore = pullAttribute.get(verifyObjectReference(obj));
|
const attributeStore = pullAttribute.get(verifyObjectReference(obj));
|
||||||
const contents = JSON.parse(attributeStore.contents);
|
const contents = JSON.parse(attributeStore.contents);
|
||||||
|
|
||||||
@ -175,10 +262,12 @@ function setAttribute(obj, attributeName, value) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
function deleteAttribute(obj, attributeName) {
|
function deleteAttribute(obj, attributeName) {
|
||||||
|
if (!verifyObjectReference(obj)) return undefined;
|
||||||
|
|
||||||
const attributeStore = pullAttribute.get(verifyObjectReference(obj));
|
const attributeStore = pullAttribute.get(verifyObjectReference(obj));
|
||||||
const contents = JSON.parse(attributeStore.contents);
|
const contents = JSON.parse(attributeStore.contents);
|
||||||
|
|
||||||
delete contents["attributeName"];
|
delete contents[attributeName];
|
||||||
|
|
||||||
insertAttribute.run({...attributeStore, contents: JSON.stringify(contents)});
|
insertAttribute.run({...attributeStore, contents: JSON.stringify(contents)});
|
||||||
}
|
}
|
||||||
@ -192,9 +281,68 @@ function verifyObjectReference(obj) {
|
|||||||
} else if (typeof(obj.number) == "number") {
|
} else if (typeof(obj.number) == "number") {
|
||||||
return obj.number;
|
return obj.number;
|
||||||
}
|
}
|
||||||
|
return undefined;
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
throw new Error("Tried to get an attribute from something which wasn't an object");
|
throw new Error(`Tried to get an attribute from something which wasn't an object: ${obj}`);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
module.exports = { interpret, sockets, lookUpObject, getAttribute, setAttribute, deleteAttribute, hasOwnAttribute };
|
function concatWithoutDuplicates(a, b) {
|
||||||
|
let b2 = b.filter((x) => !(x in a));
|
||||||
|
return a.concat(b2);
|
||||||
|
}
|
||||||
|
function luaSafe(func) {
|
||||||
|
return (...all) => {
|
||||||
|
let value = func(...all);
|
||||||
|
if (value == null) {
|
||||||
|
return undefined;
|
||||||
|
}
|
||||||
|
return value;
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
function tokenizeQuotes(string) {
|
||||||
|
let arr = string.split("'");
|
||||||
|
let out = [];
|
||||||
|
let inQuotes = false;
|
||||||
|
for (const bit of arr) {
|
||||||
|
if (inQuotes) {
|
||||||
|
out.push(bit);
|
||||||
|
} else {
|
||||||
|
if (bit != '') {
|
||||||
|
out = out.concat(bit.trim().split(" ").filter((x) => x != ''));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
inQuotes = !inQuotes;
|
||||||
|
}
|
||||||
|
return out;
|
||||||
|
}
|
||||||
|
|
||||||
|
function tokenizePrepositions(sequence, prepositions) {
|
||||||
|
let out = [];
|
||||||
|
|
||||||
|
let current = {};
|
||||||
|
|
||||||
|
let inPreposition = false;
|
||||||
|
|
||||||
|
for (const bit of sequence) {
|
||||||
|
if (inPreposition) {
|
||||||
|
current.contents = bit;
|
||||||
|
out.push(current);
|
||||||
|
current = {};
|
||||||
|
inPreposition = false;
|
||||||
|
} else if (prepositions.includes(bit)) {
|
||||||
|
current.preposition = bit;
|
||||||
|
inPreposition = true;
|
||||||
|
} else {
|
||||||
|
out.push(bit);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return out;
|
||||||
|
}
|
||||||
|
|
||||||
|
module.exports = { interpret, sockets,
|
||||||
|
lookUpObject,
|
||||||
|
getAttribute, setAttribute, deleteAttribute, hasOwnAttribute,
|
||||||
|
findVerbOnObject, findAllVerbsOnObject, findAllVerbsInArea };
|
||||||
|
19
server/package-lock.json
generated
19
server/package-lock.json
generated
@ -24,7 +24,8 @@
|
|||||||
"highlight.js": "^11.10.0",
|
"highlight.js": "^11.10.0",
|
||||||
"jsdom": "^25.0.0",
|
"jsdom": "^25.0.0",
|
||||||
"nodemon": "^3.1.5",
|
"nodemon": "^3.1.5",
|
||||||
"showdown": "^2.1.0"
|
"showdown": "^2.1.0",
|
||||||
|
"wasmoon": "^1.16.0"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/@libsql/client": {
|
"node_modules/@libsql/client": {
|
||||||
@ -172,6 +173,11 @@
|
|||||||
"node": ">=10"
|
"node": ">=10"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/@types/emscripten": {
|
||||||
|
"version": "1.39.10",
|
||||||
|
"resolved": "https://registry.npmjs.org/@types/emscripten/-/emscripten-1.39.10.tgz",
|
||||||
|
"integrity": "sha512-TB/6hBkYQJxsZHSqyeuO1Jt0AB/bW6G7rHt9g7lML7SOF6lbgcHvw/Lr+69iqN0qxgXLhWKScAon73JNnptuDw=="
|
||||||
|
},
|
||||||
"node_modules/@types/node": {
|
"node_modules/@types/node": {
|
||||||
"version": "22.7.3",
|
"version": "22.7.3",
|
||||||
"resolved": "https://registry.npmjs.org/@types/node/-/node-22.7.3.tgz",
|
"resolved": "https://registry.npmjs.org/@types/node/-/node-22.7.3.tgz",
|
||||||
@ -2327,6 +2333,17 @@
|
|||||||
"node": ">=18"
|
"node": ">=18"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/wasmoon": {
|
||||||
|
"version": "1.16.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/wasmoon/-/wasmoon-1.16.0.tgz",
|
||||||
|
"integrity": "sha512-FlRLb15WwAOz1A9OQDbf6oOKKSiefi5VK0ZRF2wgH9xk3o5SnU11tNPaOnQuAh1Ucr66cwwvVXaeVRaFdRBt5g==",
|
||||||
|
"dependencies": {
|
||||||
|
"@types/emscripten": "1.39.10"
|
||||||
|
},
|
||||||
|
"bin": {
|
||||||
|
"wasmoon": "bin/wasmoon"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/web-streams-polyfill": {
|
"node_modules/web-streams-polyfill": {
|
||||||
"version": "3.3.3",
|
"version": "3.3.3",
|
||||||
"resolved": "https://registry.npmjs.org/web-streams-polyfill/-/web-streams-polyfill-3.3.3.tgz",
|
"resolved": "https://registry.npmjs.org/web-streams-polyfill/-/web-streams-polyfill-3.3.3.tgz",
|
||||||
|
@ -29,6 +29,7 @@
|
|||||||
"highlight.js": "^11.10.0",
|
"highlight.js": "^11.10.0",
|
||||||
"jsdom": "^25.0.0",
|
"jsdom": "^25.0.0",
|
||||||
"nodemon": "^3.1.5",
|
"nodemon": "^3.1.5",
|
||||||
"showdown": "^2.1.0"
|
"showdown": "^2.1.0",
|
||||||
|
"wasmoon": "^1.16.0"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -12,8 +12,8 @@ app.use(page_routes);
|
|||||||
const user_routes = require('./users.js');
|
const user_routes = require('./users.js');
|
||||||
app.use(user_routes);
|
app.use(user_routes);
|
||||||
|
|
||||||
const socket_routes = require('./sockets.js');
|
const live_connection_routes = require('./live.js');
|
||||||
app.use(socket_routes);
|
app.use(live_connection_routes);
|
||||||
|
|
||||||
const graphQuery = db.prepare(`
|
const graphQuery = db.prepare(`
|
||||||
select p.number, p.html, p.time
|
select p.number, p.html, p.time
|
||||||
|
47
server/routes/live.js
Normal file
47
server/routes/live.js
Normal file
@ -0,0 +1,47 @@
|
|||||||
|
const express = require('express');
|
||||||
|
const app = express.Router();
|
||||||
|
const expressWs = require('express-ws')(app);
|
||||||
|
|
||||||
|
const sqlite = require('better-sqlite3');
|
||||||
|
const db = new sqlite('the_big_db.db', { verbose: console.log });
|
||||||
|
|
||||||
|
const { loginRequired } = require('../authStuff.js');
|
||||||
|
|
||||||
|
const { interpret, sockets, lookUpObject,
|
||||||
|
getAttribute, setAttribute, hasOwnAttribute, deleteAttribute,
|
||||||
|
findAllVerbsInArea } = require('../interpreter.js');
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
app.ws('/embody', (ws, req) => {
|
||||||
|
const character = req.session.characterId;
|
||||||
|
if (!character) {
|
||||||
|
ws.send(`user ${req.session.userId} does not have an associated character object`);
|
||||||
|
ws.close();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
sockets.set(character, ws);
|
||||||
|
console.log("sending location change, should get attribute for 30");
|
||||||
|
ws.send(`location change to: #${getAttribute(character, "location")}`);
|
||||||
|
|
||||||
|
ws.on('message', (msg) => {
|
||||||
|
const location = getAttribute(character, "location");
|
||||||
|
|
||||||
|
interpret(location, character, msg);
|
||||||
|
});
|
||||||
|
|
||||||
|
ws.on('close', () => sockets.delete(character));
|
||||||
|
});
|
||||||
|
|
||||||
|
app.get('/embody/verbs', loginRequired, (req, res) => {
|
||||||
|
const location = getAttribute(req.session.characterId, "location");
|
||||||
|
const verbs = findAllVerbsInArea(location, req.session.characterId);
|
||||||
|
|
||||||
|
const verbWords = Array.from(verbs.map((v) => lookUpObject(v).title));
|
||||||
|
|
||||||
|
res.status(200).json(verbWords);
|
||||||
|
});
|
||||||
|
|
||||||
|
|
||||||
|
module.exports = app;
|
@ -9,6 +9,9 @@ const { loginRequired } = require('../authStuff.js');
|
|||||||
const showdown = require('showdown');
|
const showdown = require('showdown');
|
||||||
const converter = new showdown.Converter();
|
const converter = new showdown.Converter();
|
||||||
|
|
||||||
|
const hljs = require('highlight.js/lib/core');
|
||||||
|
hljs.registerLanguage('lua', require('highlight.js/lib/languages/lua'));
|
||||||
|
|
||||||
const pageListQuery = db.prepare(`
|
const pageListQuery = db.prepare(`
|
||||||
select p.number, p.title, p.time
|
select p.number, p.title, p.time
|
||||||
from pages p
|
from pages p
|
||||||
@ -30,10 +33,13 @@ app.get('/pages', (req, res) => {
|
|||||||
|
|
||||||
app.post('/page/new', loginRequired, (req, res) => {
|
app.post('/page/new', loginRequired, (req, res) => {
|
||||||
try {
|
try {
|
||||||
const maxPage = db.prepare('select max(number) as maximum from pages').get()
|
let maxPage = db.prepare('select max(number) as maximum from pages').get()
|
||||||
const newPageNumber = maxPage.maximum + 1;
|
let newPageNumber = maxPage.maximum + 1;
|
||||||
const newPage = db.prepare('insert into pages (number, title, description, author) values (?, ?, ?, ?) returning number')
|
let newPage = db.prepare('insert into pages (number, title, description, author) values (?, ?, ?, ?) returning number')
|
||||||
.get(newPageNumber, "new page", "this page is new!", req.session.userId);
|
.get(newPageNumber, "new page", "this page is new!", req.session.userId);
|
||||||
|
|
||||||
|
let newAttributes = db.prepare('insert into attributes (number, contents) values (?, ?)')
|
||||||
|
.run(newPageNumber, JSON.stringify({"parent": 3}));
|
||||||
res.status(200).json(newPageNumber);
|
res.status(200).json(newPageNumber);
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
res.status(500).json({"error": error});
|
res.status(500).json({"error": error});
|
||||||
@ -53,17 +59,21 @@ app.get('/page/:number', (req, res) => {
|
|||||||
app.post('/page/:number', loginRequired, (req, res) => {
|
app.post('/page/:number', loginRequired, (req, res) => {
|
||||||
try {
|
try {
|
||||||
const html = converter.makeHtml(req.body.description);
|
const html = converter.makeHtml(req.body.description);
|
||||||
const changes = db.prepare('insert into pages (number, title, description, html, author) values (?, ?, ?, ?, ?)')
|
const lua = hljs.highlight(req.body.description, {language: 'lua'}).value;
|
||||||
.run(req.params.number, req.body.title, req.body.description, html, req.session.userId);
|
console.log(lua);
|
||||||
|
const changes = db.prepare('insert into pages (number, title, description, html, lua, author, type) values (?, ?, ?, ?, ?, ?, ?)')
|
||||||
|
.run(req.params.number, req.body.title, req.body.description, html, lua, req.session.userId, req.body.type ? 1 : 0);
|
||||||
res.status(200).json(changes);
|
res.status(200).json(changes);
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
|
console.log(error);
|
||||||
res.status(500).json({"error": error});
|
res.status(500).json({"error": error});
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
app.delete('/page/:number', loginRequired, (req, res) => {
|
app.delete('/page/:number', loginRequired, (req, res) => {
|
||||||
try {
|
try {
|
||||||
const changes = db.prepare('delete from pages where number = ?').run(req.params.number);
|
db.prepare('delete from pages where number = ?').run(req.params.number);
|
||||||
|
db.prepare('delete from attributes where number = ?').run(req.params.number);
|
||||||
res.status(200).json({id: req.params.id});
|
res.status(200).json({id: req.params.id});
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
res.status(500).json({"error": error});
|
res.status(500).json({"error": error});
|
||||||
|
@ -1,32 +0,0 @@
|
|||||||
const express = require('express');
|
|
||||||
const app = express.Router();
|
|
||||||
const expressWs = require('express-ws')(app);
|
|
||||||
|
|
||||||
const sqlite = require('better-sqlite3');
|
|
||||||
const db = new sqlite('the_big_db.db', { verbose: console.log });
|
|
||||||
|
|
||||||
const { loginRequired } = require('../authStuff.js');
|
|
||||||
|
|
||||||
const { interpret, sockets, lookUpObject,
|
|
||||||
getAttribute, setAttribute, hasOwnAttribute, deleteAttribute } = require('../interpreter.js');
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
app.ws('/embody', (ws, req) => {
|
|
||||||
// const { playerObject } = db.prepare('select playerObject from users where id=?').get(req.session.userId)
|
|
||||||
const playerObjectId = 30; // mocked out for now!
|
|
||||||
sockets.set(playerObjectId, ws);
|
|
||||||
ws.send(`location change to: #${getAttribute(playerObjectId, "location")}`);
|
|
||||||
|
|
||||||
ws.on('message', (msg) => {
|
|
||||||
const location = lookUpObject(playerObjectId).location;
|
|
||||||
|
|
||||||
interpret(location, playerObjectId, msg);
|
|
||||||
});
|
|
||||||
|
|
||||||
ws.on('close', () => sockets.delete(playerObjectId));
|
|
||||||
});
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
module.exports = app;
|
|
@ -50,6 +50,8 @@ app.post('/login', async (req, res) => {
|
|||||||
// set the session cookie and rreturn 200!
|
// set the session cookie and rreturn 200!
|
||||||
req.session.name = name;
|
req.session.name = name;
|
||||||
req.session.userId = storedUser.id;
|
req.session.userId = storedUser.id;
|
||||||
|
req.session.characterId = db.prepare('select character from users where id=?').get(req.session.userId)?.character;
|
||||||
|
|
||||||
console.log('setting req.session.name! : ', req.session);
|
console.log('setting req.session.name! : ', req.session);
|
||||||
return res.status(200).json({message: "successfully logged in!", id: storedUser.id, name: name});
|
return res.status(200).json({message: "successfully logged in!", id: storedUser.id, name: name});
|
||||||
});
|
});
|
||||||
|
Loading…
Reference in New Issue
Block a user