Vendetta Online has a Lisp environment (using SBCL) which controls much of its NPC behavior and will soon be in charge of generating player and NPC missions. Partly in order to get around some thread-safety issues, and partly for convenience we built an REPL into a secret chat channel. (it only responds to developer accounts)


It also handles errors by printing a quick and dirty stack trace:

(defun trap-and-log-error-handler (condition)
(format t "Error signalled: ~A~%" condition)
;; this (write-string (with-output-to-string (s) ...) thing is odd,
;; but seemingly necessary because print-frame-call seems
;; uncooperative when using t for a stream argument.
(write-string (with-output-to-string (s)
(do ((frame (sb-di:top-frame) (sb-di:frame-down frame))
(i 0 (1+ i)))
((null frame))
(format s "~a: " i)
(sb-debug::print-frame-call frame s)
(format s "~%")))))
(defmacro trap-and-log-errors (&body body)
`(ignore-errors
(handler-bind ((error #'trap-and-log-error-handler))
,@body)))
and here's the REPL code (trap-and-log-errors is somewhere upstream
from where this is called)
;;; this is what interprets "eval" requests from developers; we have a
;;; simple color code tag involving the non-printable character
;;; (code-char 127), so return values show up in red (like in SLIME)
(define-server-function eval (&rest cmd)
(flet ((format-return-value (return-val)
(with-output-to-string (os)
(with-input-from-string (s (format nil "~s~&" return-val))
(loop for line = (read-line s nil)
while line do
(format os "~aff0000~a~%" (code-char 127) line))))))
(let ((return-val (multiple-value-list (eval `(progn
(in-package :com.guildsoftware.deliverator)
,@cmd)))))
(format t "~&")
(dolist (val return-val)
(write-string (format-return-value val))))))
Why didn't I just use sb-debug:backtrace? I didn't know
about it. I just looked at how SLIME did it.