Can use telnet or ssh for MUD connections

This commit is contained in:
Howard Abrams 2025-03-03 13:17:47 -08:00
parent 30af02232a
commit 6a28307eca

111
pud.org
View file

@ -2,7 +2,7 @@
#+author: Howard X. Abrams #+author: Howard X. Abrams
#+date: 2025-01-18 #+date: 2025-01-18
#+filetags: emacs hamacs #+filetags: emacs hamacs
#+lastmod: [2025-03-01 Sat] #+lastmod: [2025-03-03 Mon]
A literate programming file for a Comint-based MUD client. A literate programming file for a Comint-based MUD client.
@ -48,19 +48,21 @@ The default connects to *Moss n Puddles*, my own MUD which I invite you to jo
:group 'processes) :group 'processes)
(defcustom pud-worlds (defcustom pud-worlds
'(["Moss-n-Puddles" "howardabrams.com" 4000 "" ""]) '(["Moss-n-Puddles" telnet "howardabrams.com" 4000])
"List of worlds you play in. "List of worlds you play in.
You need to define the worlds you play in before you can get You need to define the worlds you play in before you can get
started. In most worlds, you can start playing using a guest account. started. In most worlds, you can start playing using a guest account.
Each element WORLD of the list has the following form: Each element WORLD of the list has the following form:
\[NAME HOST PORT CHARACTER PASSWORD CONNECTION-STR] \[CONN-TYPE NAME HOST PORT CHARACTER PASSWORD LOGIN-STR]
NAME identifies the connection, HOST and PORT specify the network NAME identifies the connection, HOST and PORT specify the network
connection, CHARACTER and PASSWORD are used to connect automatically. connection, CHARACTER and PASSWORD are used to connect automatically.
The CONNECTION-STR is a string with two `%s' where this substitutes The CONN-TYPE can be either 'telnet or 'ssh.
The LOGIN-STR is a string with two `%s' where this substitutes
the username and password respectively. Sends this to the server after the username and password respectively. Sends this to the server after
establishing a connection. This can be blank for the default. establishing a connection. This can be blank for the default.
If given, make sure to have a trailing `\n' to automatically send. If given, make sure to have a trailing `\n' to automatically send.
@ -70,11 +72,19 @@ The default connects to *Moss n Puddles*, my own MUD which I invite you to jo
:type '(repeat :type '(repeat
(vector :tag "Server World" (vector :tag "Server World"
(string :tag "Name") (string :tag "Name")
(string :tag "Host") (radio :tag "Type"
(integer :tag "Port") (const :tag "Telnet" :value telnet)
(string :tag "Char" :value "guest") (const :tag "SSH" :value ssh))
(string :tag "Pass") (string :tag "Hostname")
(string :tag "Connect String" :value "connect %s %s"))) (integer :tag "Port num")
(string :tag "Username" :value "guest")
(string :tag "Password")
(string :tag "Login String"
:format "%t: %v%h"
:doc "The login string to send after connection.
This should probably have a \`\\n' at the end to submit it.
If blank or nil, use the \`pud-default-connection-string'.
For example: connect %s %s\\n")))
:group 'pud) :group 'pud)
#+END_SRC #+END_SRC
@ -84,24 +94,23 @@ For instance:
(use-package pud (use-package pud
:custom :custom
(pud-worlds (pud-worlds
'(["Remote Moss-n-Puddles" "howardabrams" 4000 "bobby"] '(["Remote Moss-n-Puddles" 'ssh "howardabrams.com" 4000 "bobby"]
; ↑ No password? Should be in .authinfo.gpg ; ↑ No password? Should be in .authinfo.gpg
["Local Root" "localhost" 4000 "suzy" "some-pass"] ["Local Root" 'telnet "localhost" 4000 "suzy" "some-pass"]
; ↑ This has the password in your custom settings. ; ↑ This has the password in your custom settings.
; ↓ Password from authinfo, special connection string: ; ↓ Password from authinfo, special connection string:
["Local User" "localhost" 4000 "rick" nil "login %s %s"]))) ["Local User" 'telnet "localhost" 4000 "rick" nil "login %s %s"])))
#+END_SRC #+END_SRC
Hidden: Hidden:
#+BEGIN_SRC emacs-lisp :tangle no :eval no #+BEGIN_SRC emacs-lisp :tangle no :eval no
(setq pud-worlds (setq pud-worlds
'(["moss-n-puddles" "howardabrams.com" 4000 "howard"] '(["Moss-n-Puddles" ssh "howardabrams.com" 4004 "howard" "" "\\nconnect %s %s\\n"]
["moss-n-puddles" "howardabrams.com" 4000 "rick"] ["Moss-n-Puddles" ssh "howardabrams.com" 4004 "rick" "" "\\nconnect %s %s\\n"]
["moss-n-puddles" "howardabrams.com" 4000 "darol"] ["Local-Moss" telnet "localhost" 4000 "howard" "" ""]
["local-evennia" "localhost" 4000 "howard"] ["Local-Moss" telnet "localhost" 4000 "rick" "" ""]))
["local-evennia" "localhost" 4000 "rick"]))
#+END_SRC #+END_SRC
Seems like MUDs have a standard login sequence, but they dont have to. Here is the default that a user can override in their =pud-worlds= listing: Seems like MUDs have a standard login sequence, but they dont have to. Here is the default that a user can override in their =pud-worlds= listing:
@ -109,7 +118,7 @@ Seems like MUDs have a standard login sequence, but they dont have to. Here i
#+BEGIN_SRC emacs-lisp #+BEGIN_SRC emacs-lisp
(defcustom pud-default-connection-string "connect %s %s\n" (defcustom pud-default-connection-string "connect %s %s\n"
"The standard connection string to substitute the username and password." "The standard connection string to substitute the username and password."
:type '(string :tag "Connect String" :value "connect %s %s\n") :type '(string)
:group 'pud) :group 'pud)
#+END_SRC #+END_SRC
@ -161,14 +170,14 @@ The following functions are accessibility functions to the world entry.
(defun pud-world-name (world) (defun pud-world-name (world)
"Return the name for WORLD as a string." "Return the name for WORLD as a string."
(if (vectorp world) (if (vectorp world)
(if (or (length< world 4) (null (aref world 3)) (string-blank-p (aref world 3))) (if (or (length< world 5) (null (aref world 4)) (string-blank-p (aref world 4)))
(aref world 0) (aref world 0)
(concat (aref world 3) "@" (aref world 0))) (concat (aref world 4) "@" (aref world 0)))
world)) world))
(defun pud-world-network (world) (defun pud-world-network (world)
"Return the network details for WORLD as a cons cell (HOST . PORT)." "Return the network details for WORLD as a cons cell (HOST . PORT)."
(list (aref world 1) (format "%s" (aref world 2)))) (list (aref world 2) (format "%s" (aref world 3))))
(defun pud-world-creds (world) (defun pud-world-creds (world)
"Return the username and password from WORLD. "Return the username and password from WORLD.
@ -182,7 +191,7 @@ The following functions are accessibility functions to the world entry.
(list (plist-get auth-results :user) (list (plist-get auth-results :user)
(funcall (plist-get auth-results :secret))) (funcall (plist-get auth-results :secret)))
;; No match? Just return values from world: ;; No match? Just return values from world:
(list (aref world 3) (aref world 4))))) (list (aref world 4) (aref world 5)))))
#+END_SRC #+END_SRC
And some basic functions I should expand. And some basic functions I should expand.
@ -196,22 +205,25 @@ And some basic functions I should expand.
(should (string-equal (pud-world-name ["foobar" "localhost" "4000" "guest" "guest"]) "guest@foobar"))) (should (string-equal (pud-world-name ["foobar" "localhost" "4000" "guest" "guest"]) "guest@foobar")))
(ert-deftest pud-world-network-test () (ert-deftest pud-world-network-test ()
(should (equal (pud-world-network ["foobar" "overthere" "4000" "guest" "guest"]) '("overthere" "4000"))) (should (equal (pud-world-network ["foobar" telnet "overthere" "4000" "guest" "guest"]) '("overthere" "4000")))
(should (equal (pud-world-network ["foobar" "overthere" 4000 "guest" "guest"]) '("overthere" "4000")))) (should (equal (pud-world-network ["foobar" ssh "overthere" 4000 "guest" "guest"]) '("overthere" "4000"))))
(ert-deftest pud-world-creds-test () (ert-deftest pud-world-creds-test ()
;; Test with no match in authinfo! ;; Test with no match in authinfo!
(should (equal (should (equal
(pud-world-creds ["first" "some-home" 4000 "a-user" "a-pass"]) (pud-world-creds ["some-place" telnet "some-home" 4000 "a-user" "a-pass"])
'("a-user" "a-pass"))) '("a-user" "a-pass")))
;; This test works if the following line is in .authinfo: ;; This test works if the following line is in .authinfo:
;; machine localhost port 4000 login george password testpass ;; machine localhost port 4000 login george password testpass
(should (equal (should (equal
(pud-world-creds ["first" "localhost" 4000 "george"]) (pud-world-creds ["nudder-place" ssh "localhost" 4000 "george"])
'("george" "testpass")))) '("george" "testpass"))))
#+END_SRC #+END_SRC
* Basics * Basics
:LOGBOOK:
CLOCK: [2025-03-03 Mon 11:57]--[2025-03-03 Mon 12:10] => 0:13
:END:
Using Comint, and hoping to have the ANSI colors displayed. Using Comint, and hoping to have the ANSI colors displayed.
#+BEGIN_SRC emacs-lisp #+BEGIN_SRC emacs-lisp
@ -222,17 +234,51 @@ Using Comint, and hoping to have the ANSI colors displayed.
Im going to use good ol fashion =telnet= for the connection: Im going to use good ol fashion =telnet= for the connection:
#+BEGIN_SRC emacs-lisp #+BEGIN_SRC emacs-lisp
(defvar pud-cli-file-path "telnet" ; ssh!? (defcustom pud-telnet-path "telnet"
"Path to the program used by `run-pud'.") "Path to the program used by `run-pud' to connect using telnet."
:type '(string)
:group 'pud)
(defcustom pud-ssh-path "ssh"
"Path to the program used by `run-pud' to connect using ssh."
:type '(string)
:group 'pud)
#+END_SRC #+END_SRC
The pud-cli-arguments, holds a list of commandline arguments: the port. The pud-cli-arguments, holds a list of commandline arguments: the port.
#+BEGIN_SRC emacs-lisp #+BEGIN_SRC emacs-lisp
(defvar pud-cli-arguments nil (defvar pud-cli-arguments nil
"A list of arguments to use before the telnet location.") "A list of arguments to use before the connection.")
#+END_SRC #+END_SRC
Command string to use, given a =world= with a connection type:
#+BEGIN_SRC emacs-lisp
(defun pud-cli-command (world)
"Return a command string to pass to the shell.
The WORLD is a vector with the hostname, see `pud-worlds'."
(seq-let (host port) (pud-world-network world)
(message "Dealing with: %s %s %s" host port (aref world 1))
(cl-case (aref world 1)
(telnet (append (cons pud-telnet-path pud-cli-arguments)
(list host port)))
(ssh (append (cons pud-cli-filepath-ssh pud-cli-arguments)
(list "-p" port host)))
(t (error "Unsupported connection type")))))
#+END_SRC
Some tests:
#+BEGIN_SRC emacs-lisp :tangle no
(ert-deftest pud-cli-command-test ()
(should (equal (pud-cli-command ["some-world" telnet "world.r.us" 4000])
'("telnet" "world.r.us" "4000")))
(should (equal (pud-cli-command ["nudder-world" ssh "world.r.us" 4004])
'("ssh" "-p" "4004" "world.r.us"))))
#+END_SRC
The empty and currently disused mode map for storing our custom keybindings inherits from =comint-mode-map=, so we get the same keys exposed in =comint-mode=. The empty and currently disused mode map for storing our custom keybindings inherits from =comint-mode-map=, so we get the same keys exposed in =comint-mode=.
#+BEGIN_SRC emacs-lisp #+BEGIN_SRC emacs-lisp
@ -271,8 +317,7 @@ The main entry point to the program is the =run-pud= function:
- username (can be overridden) - username (can be overridden)
- password (should be overridden)" - password (should be overridden)"
(interactive (list (pud-get-world))) (interactive (list (pud-get-world)))
(let* ((pud-program pud-cli-file-path) (let* ((pud-cli (pud-cli-command world))
(pud-args (append pud-cli-arguments (pud-world-network world)))
(buffer (get-buffer-create (pud-buffer-name world))) (buffer (get-buffer-create (pud-buffer-name world)))
(proc-alive (comint-check-proc buffer)) (proc-alive (comint-check-proc buffer))
(process (get-buffer-process buffer))) (process (get-buffer-process buffer)))
@ -280,7 +325,7 @@ The main entry point to the program is the =run-pud= function:
;; mode. ;; mode.
(unless proc-alive (unless proc-alive
(with-current-buffer buffer (with-current-buffer buffer
(apply 'make-comint-in-buffer "Pud" buffer pud-program nil pud-args) (apply 'make-comint-in-buffer "Pud" buffer (car pud-cli) nil (cdr pud-cli))
(pud-mode) (pud-mode)
(visual-line-mode 1) (visual-line-mode 1)
(pud-reconnect world))) (pud-reconnect world)))