mocking out some interpreter functions

This commit is contained in:
Shoofle 2024-10-10 11:13:41 -04:00
parent 940fc15b90
commit 12f1283e4e
10 changed files with 112 additions and 73 deletions

View File

@ -3,19 +3,27 @@
margin-bottom: 2rem; margin-bottom: 2rem;
margin-left: auto; margin-left: auto;
margin-right: auto; margin-right: auto;
background: lightblue; padding: 0;
background: lightgreen;
position: fixed; position: fixed;
bottom: 0; bottom: 0;
left: 20%; left: 20%;
right: 20%; right: 20%;
border-radius: 1rem;
}
.commandline form {
display: flex;
flex-direction: row;
width: 100%;
} }
.commandline input { .commandline input {
flex: 10;
font-size: 16pt; font-size: 16pt;
border: none; border: none;
padding: 1rem; padding: 2rem;
width: calc(100% - 2rem);
margin-left: 0;
margin-right: 0;
background: transparent; background: transparent;
} }
.commandline button {
}

View File

@ -1,10 +1,16 @@
import { useForm } from "react-hook-form";
import './CommandEntry.css'; import './CommandEntry.css';
function CommandEntry({command, onChange, onSubmit, ...props}) { function CommandEntry({ onSubmit }) {
const { register, handleSubmit, setValue } = useForm();
return ( return (
<div className="commandline"> <div className="commandline">
<input type="text" placeholder="Enter a command!" value={command} onChange={onChange} {...props}/> <form onSubmit={handleSubmit((data) => { setValue('command', ""); onSubmit(data.command); })}>
<button onClick={() => onSubmit(command)}>Do</button> <input {...register("command")} autoFocus type="text" placeholder="Enter a command!" />
<button type="submit">Do</button>
</form>
</div> </div>
); );
} }

View File

@ -14,24 +14,27 @@ import CommandEntry from './CommandEntry.jsx';
function Live({editing, ...props}) { function Live({editing, ...props}) {
const navigate = useNavigate(); const navigate = useNavigate();
const loggedIn = useLoggedIn(); const loggedIn = useLoggedIn();
const { pagenumber } = useParams();
const [command, setCommand] = useState("");
const [ messageHistory, setMessageHistory ] = useState([]); const [ messageHistory, setMessageHistory ] = useState([]);
const [ currentNumber, setCurrentNumber ] = useState(1); const [ currentNumber, setCurrentNumber ] = useState(1);
const [ connecting, setConnecting ] = useState(true); const [ connecting, setConnecting ] = useState(true);
const { sendMessage, lastMessage, readyState } = useWebSocket(`${wsUrl}/embody`, { const { sendMessage, lastMessage, readyState } = useWebSocket(`${wsUrl}/embody`, {
onClose: () => setConnecting(false) onClose: () => setConnecting(false)
}, connecting); }, connecting);
useEffect(() => { useEffect(() => {
if (lastMessage !== null) { if (lastMessage !== null) {
console.log(lastMessage);
setMessageHistory((prev) => prev.concat(lastMessage)); setMessageHistory((prev) => prev.concat(lastMessage));
if (lastMessage.data.startsWith("location change to: ")) {
const num = Number(lastMessage.data.replace("location change to: #", ""));
setCurrentNumber(num);
}
} }
}, [lastMessage]); }, [lastMessage]);
useEffect(() => { sendMessage("what the fuk is up"); }, []);
function handleSendMessage() { function handleSendMessage() {
console.log("sending a message..."); console.log("sending a message...");
sendMessage("button got clicked"); sendMessage("button got clicked");
@ -43,16 +46,14 @@ function Live({editing, ...props}) {
<MessageFeed messages={messageHistory}> <MessageFeed messages={messageHistory}>
<button disabled={readyState!=ReadyState.OPEN} onClick={handleSendMessage}>send a message to the server!</button> <button disabled={readyState!=ReadyState.OPEN} onClick={handleSendMessage}>send a message to the server!</button>
<button disabled={connecting} onClick={() => setConnecting(true)}>Reconnect</button> <button disabled={connecting} onClick={() => setConnecting(true)}>Reconnect</button>
<button onClick={()=> setMessageHistory([])}>Clear History</button>
</MessageFeed> </MessageFeed>
<Sidebar pagenumber={pagenumber} hidden="true" sendWord={(word) => setCommand((command + " " + word).trim())}> <Sidebar pagenumber={currentNumber} hidden="true" sendWord={(word) => setCommand((command + " " + word).trim())}>
{!editing && <li><button onClick={() => navigate(`/${pagenumber}/edit`)}>edit</button></li>} {!editing && <li><button onClick={() => navigate(`/${currentNumber}/edit`)}>edit</button></li>}
{editing && <li><button disabled={postMutation.isPending} onClick={submitChanges}>save</button></li>} {editing && <li><button disabled={postMutation.isPending} onClick={submitChanges}>save</button></li>}
{editing && <li><button onClick={() => navigate(`/${pagenumber}`)}>return</button></li>} {editing && <li><button onClick={() => navigate(`/${currentNumber}`)}>return</button></li>}
</Sidebar> </Sidebar>
<CommandEntry <CommandEntry onSubmit={sendMessage}/>
command={command}
onChange={(e) => setCommand(e.target.value)}
onSubmit={(e) => { setCommand(""); sendMessage(e); }}/>
</> </>
); );
} }

View File

@ -1,25 +1,25 @@
.messages-tray { .messages-tray {
display: flex;
flex-direction: column;
transition: all 0.1s linear; transition: all 0.1s linear;
margin: 0; margin: 0;
background: lightblue; background: lightgreen;
position: fixed; position: fixed;
width: 30ch; width: 30ch;
height: 100%; height: 100%;
left: 0; left: 0;
top: 0; top: 0;
overflow: hidden;
} }
.messages-hidden { .messages-hidden {
transform: translateX(-20ch); transform: translateX(-20ch);
} }
.sidebar li { .messages-tray ol {
flex: 10;
padding-left: 1ch;
overflow: auto;
}
.messages-tray li {
list-style: none; list-style: none;
} }
.sidebar li button {
text-transform: uppercase;
}
.sidebar-hidden li {
display: none;
}

View File

@ -1,4 +1,4 @@
import { useEffect, useState, useCallback } from 'react'; import { useEffect, useState, useRef } from 'react';
import { useForm } from "react-hook-form"; import { useForm } from "react-hook-form";
import { useNavigate } from 'react-router-dom'; import { useNavigate } from 'react-router-dom';
@ -6,16 +6,18 @@ import './MessageFeed.css';
function MessageFeed({messages=[], children}) { function MessageFeed({messages=[], children}) {
const [ open, setOpen ] = useState(false); const [ open, setOpen ] = useState(false);
const listContainer = useRef(null);
useEffect(() => listContainer.current.scrollTo(0, listContainer.current.scrollHeight), [listContainer, messages])
return ( return (
<div className={`messages-tray ${!open?'messages-hidden':''}`}> <div className={`messages-tray ${!open?'messages-hidden':''}`}>
<button style={{float: 'right'}} onClick={() => setOpen(!open)}>{open?'Hide':'Show'}</button> <button style={{float: 'right'}} onClick={() => setOpen(!open)}>{open?'Hide':'Show'}</button>
<h2>Message history:</h2> <h2>Message history:</h2>
<ol> <ol ref={listContainer}>
{messages.map((message, idx) => {messages.map((message, idx) =>
<li key={idx}> <li key={idx}>
{message.data} {message.data}
<code style={{background: "lightgrey"}}>{JSON.stringify(message)}</code>
</li> </li>
)} )}
</ol> </ol>

View File

@ -1,7 +1,7 @@
.sidebar { .sidebar {
transition: all 0.1s linear; transition: all 0.1s linear;
margin: 0; margin: 0;
background: lightblue; background: lightgreen;
position: fixed; position: fixed;
width: 30ch; width: 30ch;
height: 100%; height: 100%;

View File

@ -9,10 +9,10 @@ import Page from './Page.jsx';
import './Pages.css'; import './Pages.css';
function GhostPage({editing, ...props}) { function GhostPage({editing, ...props}) {
const { pagenumber } = useParams(); const { pagenumber, editid } = useParams();
return ( return (
<Page number={pagenumber} editing={editing} {...props}/> <Page number={pagenumber} editid={editid} editing={editing} {...props}/>
); );
} }

View File

@ -6,17 +6,15 @@ import { useLoggedIn } from '../AuthProvider.jsx';
import './Pages.css'; import './Pages.css';
function Page({ editing, number }) { function Page({ editing, number, editid=null}) {
const queryClient = useQueryClient(); const queryClient = useQueryClient();
const navigate = useNavigate(); const navigate = useNavigate();
const { editid } = useParams();
const pagenumber = number
const loggedIn = useLoggedIn(); const loggedIn = useLoggedIn();
const noLoad = useFixLinks(); const noLoad = useFixLinks();
const fetchQuery = useQuery({ // fetch the currrent values const fetchQuery = useQuery({ // fetch the currrent values
queryKey: ['page', pagenumber, editid], queryKey: ['page', number, editid],
queryFn: () => editid ? fetchPageAtEdit(pagenumber, editid) : fetchPage(pagenumber) queryFn: () => editid ? fetchPageAtEdit(number, editid) : fetchPage(number)
}) })
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
@ -46,16 +44,16 @@ function Page({ editing, number }) {
const newTitle = document.querySelector('span').innerHTML; const newTitle = document.querySelector('span').innerHTML;
const newText = document.querySelector('pre').innerHTML; const newText = document.querySelector('pre').innerHTML;
postMutation.mutate({ postMutation.mutate({
id: pagenumber, id: number,
title: newTitle, title: newTitle,
description: newText description: newText
}); });
navigate(`/${pagenumber}`, {replace: true}) navigate(`/${number}`, {replace: true})
} }
function submitDelete(e) { function submitDelete(e) {
e.preventDefault(); e.preventDefault();
deleteMutation.mutate(pagenumber); deleteMutation.mutate(number);
navigate(`/`); navigate(`/`);
} }
@ -64,7 +62,7 @@ function Page({ editing, number }) {
<header> <header>
<h1> <h1>
<a href="/" {...noLoad}>🌳</a> <a href="/" {...noLoad}>🌳</a>
{pagenumber}.&nbsp; {number}.&nbsp;
<span <span
contentEditable={editing} contentEditable={editing}
dangerouslySetInnerHTML={{__html: readyToShow ? title : "..." }} /> dangerouslySetInnerHTML={{__html: readyToShow ? title : "..." }} />
@ -89,7 +87,7 @@ function Page({ editing, number }) {
} }
</section> </section>
<button <button
onClick={() => navigate(`/${pagenumber}/history`)}> onClick={() => navigate(`/${number}/history`)}>
History History
</button> </button>
{editing && ( {editing && (
@ -101,7 +99,7 @@ function Page({ editing, number }) {
{!editing && !editid && ( {!editing && !editid && (
<button <button
disabled={!loggedIn} disabled={!loggedIn}
onClick={() => navigate(`/${pagenumber}/edit`)}> onClick={() => navigate(`/${number}/edit`)}>
Edit Page Edit Page
</button>)} </button>)}
{loggedIn && ( {loggedIn && (

View File

@ -3,31 +3,57 @@ const db = new sqlite('the_big_db.db', { verbose: console.log });
var sockets = new Map(); var sockets = new Map();
function interpret(context, subject, command) { function interpret(context, player, command) {
const words = command.split(' '); // interpret and execute a command entered by a player.
//
// context: the location the player has entered the command in from.
// may be null if it's executing from a script?
// player: the id of the player object who typed in the command.
// may? be null if it's executing from a script? used for output.
// command: the full string typed in by the player
const socket = sockets.get(player)
const words = command.trim().split(' ');
socket?.send(`interpreting and executing the received command: ${command}`);
const verbs = findAvailableVerbs(context, player, command);
socket?.send(`found these verbs: ${Array.from(verbs.keys()).join(', ')}`)
const verbs = findVerbs(context, subject);
// first word is either a subject or a verb. either way there must be a verb. // first word is either a subject or a verb. either way there must be a verb.
// check if the first word is in the list of verbs.
const [first, second, third, ...rest] = words; const [first, second, third, ...rest] = words;
// if the second word is a verb, but the first word is also a verb, what do we do?
if (second in verbs) { if (second in verbs) {
executeVerb(verbs.get(second), first, third, ...rest); // for now, assume that if the second word is a verb, it's the verb we want.
executeVerb(player, verbs.get(second), first, third, ...rest);
} else { } else {
executeVerb(verbs.get(first), subject, second, third, ...rest) // if the second word is not a verb, then the first word is the verb, and the player is the implied subject.
executeVerb(player, verbs.get(first), player, second, third, ...rest)
} }
} }
function findVerbs(location, actor) { function findAvailableVerbs(location, actor, command) {
// check for verbs on actor // check for verbs on actor
// check for verbs on objects in location // check for verbs on objects in location
// check for verbs on location // check for verbs on location
const out = new Map(); const out = new Map();
out.set("look", 29); out.set("look", 29);
out.set("warp", 31);
return out; return out;
} }
function executeVerb(verb, subject, object, ...rest) { function executeVerb(outputObject, verbId, subject, object, ...rest) {
lookUpObject(verb).fn(subject, object, ...rest) const fullVerb = lookUpObject(verbId);
if (!fullVerb) {
return sockets.get(outputObject)?.send(`verb not found`);
}
sockets.get(outputObject)?.send(`verb found as ${fullVerb.name}`);
const func = fullVerb.fn;
if (!func) {
return sockets.get(outputObject)?.send(`verb was found but didn't have a fn attribute`);
}
return fullVerb.fn(outputObject, subject, object, ...rest);
} }
const objectQuery = db.prepare('select * from pages where id=?'); const objectQuery = db.prepare('select * from pages where id=?');
@ -36,12 +62,22 @@ function lookUpObject(id) {
if (id == 30) return {name: "shoofle", contents: "this is a shoofle", location: 1}; if (id == 30) return {name: "shoofle", contents: "this is a shoofle", location: 1};
if (id == 29) return { if (id == 29) return {
name: "look", name: "look",
verb: false,
contents: "send description of direct object to subject's socket", contents: "send description of direct object to subject's socket",
fn: (subject, object, ...rest) => { fn: (logReceiver, subject, object, ...rest) => {
sockets.get(subject)?.send(`you looked around! subject: ${subject} object: ${object} args: ${rest}`); sockets.get(logReceiver)?.send(`you looked around! subject ${subject} object: ${object} args: ${rest}`);
console.log(`${subject} looked at ${object} with args ${rest}`); console.log(`${subject} looked at ${object} with args ${rest}, and output was directed to ${logReceiver}`);
} }
}; }
if (id == 31) return {
name: "warp",
verb: true,
contents: "this built-in function changes the player's location, as well as telling their client to move",
fn: (logReceiver, subject, object, ...rest) => {
// TODO: change subject's location
sockets.get(logReceiver)?.send(`location change to: ${object}`);
}
}
} }
module.exports = { interpret, sockets, lookUpObject }; module.exports = { interpret, sockets, lookUpObject };

View File

@ -9,17 +9,6 @@ const { loginRequired } = require('../authStuff.js');
const { interpret, sockets, lookUpObject } = require('../interpreter.js'); const { interpret, sockets, lookUpObject } = require('../interpreter.js');
const clockListeners = new Set();
const clock = setInterval(() => {
if (clockListeners.size == 0) return;
console.log(`sending a ping to all ${clockListeners.size} connections`);
clockListeners.forEach((x) => x());
}, 5000);
console.log(`set up the clock: ${clock}`);
app.ws('/embody', (ws, req) => { app.ws('/embody', (ws, req) => {
@ -38,5 +27,4 @@ app.ws('/embody', (ws, req) => {
module.exports = app; module.exports = app;