X-Git-Url: http://git.pond.sub.org/?p=eow;a=blobdiff_plain;f=empire.lisp;h=f4fb8b2a7619525dd1337fd97ce5c662251d3fa7;hp=eb75bfcf77d9ee1b3838a2d08bea733ba97b2927;hb=54e14006564ff34c8249b9ddb357201759d77101;hpb=258dcd42f864242f861ccc6bab86ad31cd1722e3 diff --git a/empire.lisp b/empire.lisp index eb75bfc..f4fb8b2 100644 --- a/empire.lisp +++ b/empire.lisp @@ -9,6 +9,8 @@ (defvar *default-empire-server* "localhost") (defvar *default-empire-server-port* 6665) +(defvar *last-active-connection* nil) + (defclass connection () ((user :initarg :user @@ -48,7 +50,8 @@ (send-queue :accessor send-queue :initform (locked-queue:create) - :documentation "lines outstanding to be sent at next prompt"))) + :documentation "lines outstanding to be sent at next prompt") + (xdump :accessor xdump :initform (xdump:make-parser)))) (defun make-connection (&key (user nil) (password nil) @@ -91,7 +94,7 @@ (defgeneric quit (connection)) (defgeneric handle-connection (connection)) (defgeneric read-message (connection)) -(defgeneric send-message (connection message)) +(defgeneric send-message (connection message &key next-mode)) (defgeneric connected-p (connection)) (defgeneric reconnect (conncetion)) @@ -127,8 +130,8 @@ (defclass base-mode () ((connection :initarg :connection :accessor connection))) (defclass init-mode (base-mode) - ((play-sent-p :initform nil - :documentation "Have we already sent the play command?"))) + ((phase :initform :initial + :documentation "Initialization phase"))) (defclass play-mode (base-mode) nil) (defun make-mode (connection mode) @@ -141,6 +144,7 @@ (empire-log:info "~a: set-new-mode ~a -> ~a" c (connection-mode c) new-mode) (setf (connection-mode c) mode))) +(defgeneric handle-cmdok (base-mode message)) (defgeneric handle-data (base-mode message)) (defgeneric handle-init (base-mode message)) (defgeneric handle-exit (base-mode message)) @@ -151,14 +155,35 @@ (defmethod handle-init ((m init-mode) message) (declare (ignorable message)) - (with-slots (play-sent-p) m - (if (not play-sent-p) - (let ((c (connection m))) - (login-and-play c) - (setf play-sent-p t)) - (set-new-mode m 'play-mode)))) + (with-slots (phase connection) m + (ecase phase + (:initial (handle-cmdok m message)) + (:play-sent (set-new-mode m 'play-mode))))) + +(defmethod handle-cmdok ((m init-mode) message) + (declare (ignorable message)) + (with-slots (phase connection) m + (flet ((init-phase (mode send-args next-phase) + (with-slots (phase connection) mode + (send-message-one connection (apply #'format nil send-args)) + (setf phase next-phase)))) + (with-slots (user password) (connection m) + (ecase phase + (:initial (init-phase m `("client eow ~a" ,*version*) :client-sent)) + (:client-sent (init-phase m `("coun ~a" ,user) :coun-sent)) + (:coun-sent (init-phase m `("pass ~a" ,password) :pass-sent)) + (:pass-sent (init-phase m '("play") :play-sent))))))) (defmethod handle-exit ((m init-mode) message) + (declare (ignorable message)) + (with-slots (phase connection) m + (ecase phase + (:play-sent (send-message-one connection "kill") + (setf phase :kill-sent)) + (:kill-sent (send-message-one connection "play") + (setf phase :play-sent))))) + +(defmethod handle-data ((m init-mode) message) (declare (ignorable message)) t) @@ -176,16 +201,27 @@ (defmethod handle-flush ((m play-mode) message) (let* ((c (connection m))) - (empire-web:prompt (session c) message))) + (empire-web:prompt (session c) message) + (send-next-line c))) (defmethod send-next-line ((c connection)) - (send-message-one c (locked-queue:dequeue (send-queue c)))) + (let ((next-event (locked-queue:dequeue (send-queue c)))) + (etypecase next-event + (string (send-message-one c next-event)) + (cons (let ((message (car next-event)) + (mode (cdr next-event))) + (send-message-one c message) + (set-new-mode (connection-mode c) mode)))))) + +(defun read-no-eval (stream) + (let ((*read-eval* nil)) + (read stream))) (defmethod handle-prompt ((m play-mode) message) (let* ((c (connection m))) (with-input-from-string (s message) - (let* ((minutes (read s)) ;;FIXME DANGEROUS - (btus (read s))) + (let* ((minutes (read-no-eval s)) + (btus (read-no-eval s))) (empire-web:prompt (session c) (format nil "[~a,~a]: " minutes btus)))) (send-next-line c))) @@ -201,7 +237,7 @@ `(,+C_FLUSH+ . handle-flush) `(,+C_BADCMD+ . handle-simple-message) `(,+C_FLASH+ . handle-simple-message) - `(,+C_CMDOK+ . handle-ignore) + `(,+C_CMDOK+ . handle-cmdok) `(,+C_PROMPT+ . handle-prompt))) (defun parse-server-line (line) @@ -234,6 +270,7 @@ (let* ((s (network-stream c)) (line (read-line s))) (empire-log:info "~a: < ~a" c line) + (setf *last-active-connection* c) (multiple-value-bind (message type) (parse-server-line line) (let ((handler (lookup-handler type)) (mode (connection-mode c))) @@ -242,6 +279,7 @@ (error 'no-handler :mode mode :type type)))) line) (sb-int:closed-stream-error () nil) + (sb-int:simple-stream-error () nil) (end-of-file () nil))) (defun raw-send-message (s message) @@ -252,13 +290,16 @@ (defgeneric send-message-one (connection string)) (defmethod send-message-one ((c connection) message) (empire-log:info "~a: > ~a" c message) + (setf *last-active-connection* c) (let ((s (network-stream c))) (raw-send-message s message))) -(defmethod send-message ((c connection) message) +(defmethod send-message ((c connection) message &key next-mode) (if (not (connected-p c)) (reconnect c)) - (locked-queue:enqueue (send-queue c) message)) + (if next-mode + (locked-queue:enqueue (send-queue c) (cons message next-mode)) + (locked-queue:enqueue (send-queue c) message))) (defparameter +special-command-char+ #\;) @@ -266,8 +307,58 @@ (and (> (length line) 0) (char= +special-command-char+ (aref line 0)))) +(defclass xdump-mode (play-mode) + ((phase :initform :meta-meta) + (dump-queue :initform nil) + (dump-index :initform 0))) + +(defmethod handle-data ((m xdump-mode) message) + (with-slots (connection phase dump-queue dump-index) m + (xdump:with-parser (xdump connection) + (if (xdump:parse-line (xdump connection) message) + ;;XXX consider something like a 'pop-mode function + (ccase phase + (:meta-meta + (setf phase :meta-table) + (send-message connection "xdump meta table")) + (:meta-table + (setf phase :table-table) + (send-message connection "xdump table *")) + (:table-table + (setf phase :meta-type) + (send-message connection "xdump meta meta-type")) + (:meta-type + (setf phase :type-table) + (send-message connection "xdump meta-type *")) + (:type-table + (setf phase :tables-meta + dump-queue (xdump::table-entries (xdump:table "table")) + dump-index 0) + (send-message connection (format nil "xdump meta ~a" (xdump-data:name (aref dump-queue 0))))) + (:tables-meta + (setf phase :tables-content) + (send-message connection (format nil "xdump ~a *" (xdump-data:name (aref dump-queue dump-index))))) + (:tables-content + (if (< dump-index (fill-pointer dump-queue)) + (progn (setf phase :tables-meta) + (incf dump-index) + (loop while (and (< dump-index (fill-pointer dump-queue)) + (let ((dump-queue-entry (aref dump-queue dump-index))) + (or (null dump-queue-entry) + (string= "meta" + (xdump-data:name dump-queue-entry))))) + do (incf dump-index)) + (if (and (<= dump-index (fill-pointer dump-queue)) + (not (null (aref dump-queue dump-index)))) + (send-message connection (format nil "xdump meta ~a" (xdump-data:name (aref dump-queue dump-index)))))) + (progn + (xdump:checkpoint) + (set-new-mode (connection-mode connection) 'play-mode))))))))) + (defmethod special-xup ((c connection)) - t) + (let ((user-log (empire-log:open-user-log (user c)))) + (setf (xdump c) (xdump:make-parser :user-log user-log)) + (send-message c "xdump meta meta" :next-mode 'xdump-mode))) (defmethod special-command ((c connection) line) (cond ((string= line "xup") (special-xup c)) @@ -278,20 +369,5 @@ (cond ((special-command-p line) (special-command c (subseq line 1))) (t (send-message c line)))) -(defgeneric login-and-play (connection)) -(defmethod login-and-play ((c connection)) - (send-message-one c (format nil "client eow ~a" *version*)) - (send-message-one c "user FIXME") - (with-slots (user password) c - (send-message-one c (format nil "coun ~a" user)) - (send-message-one c (format nil "pass ~a" password))) - (send-message-one c "kill") - (send-message-one c "play")) - (defmethod quit ((c connection)) (usocket:socket-close (socket c))) - - -;;; Tests -(defvar *l1* "2 Empire server ready") -;; (parse-server-line *l1*)