X-Git-Url: http://git.pond.sub.org/?p=eow;a=blobdiff_plain;f=empire.lisp;h=c32696c3504258bd0f4bf78d61806f3b419c11c0;hp=2a0c137a79684a8419573dff9a0e48cd3a6edafd;hb=fc5e0903e87b50a4931b66a039f75feb95b78602;hpb=c9ddb9951cd5e39280f14d1f316afb251db74087 diff --git a/empire.lisp b/empire.lisp index 2a0c137..c32696c 100644 --- a/empire.lisp +++ b/empire.lisp @@ -33,12 +33,6 @@ :initarg :stream :accessor network-stream :documentation "Stream used to talk to the empire server.") - (logging-stream - :initarg :logging-stream - :accessor logging-stream - :initform t - :documentation "Messages coming back from the server are sent to -this stream.") (connection-handler :accessor connection-handler :initform nil @@ -50,7 +44,11 @@ this stream.") (session :accessor session :initarg :session - :documentation "web session to which this connection belongs"))) + :documentation "web session to which this connection belongs") + (send-queue + :accessor send-queue + :initform (locked-queue:create) + :documentation "lines outstanding to be sent at next prompt"))) (defun make-connection (&key (user nil) (password nil) @@ -58,7 +56,6 @@ this stream.") (server-port *default-empire-server-port*) (socket nil) (network-stream nil) - (logging-stream nil) session) (make-instance 'connection :user user @@ -67,7 +64,6 @@ this stream.") :server-port server-port :socket socket :stream network-stream - :logging-stream logging-stream :session session)) ;;; interface @@ -75,7 +71,6 @@ this stream.") (password nil) (server-name *default-empire-server*) (server-port *default-empire-server-port*) - (logging-stream *standard-output*) session) "Connect to server and return a connection object." (let* ((socket (usocket:socket-connect server-name server-port)) @@ -84,30 +79,34 @@ this stream.") :password password :socket socket :network-stream stream - :logging-stream logging-stream :server-name server-name :server-port server-port :session session)) (handler (spawn-with-name (format nil "empire-handler-~a" user) #'handle-connection connection))) (setf (connection-handler connection) handler) + (empire-log:info "~a: connect" connection) connection)) (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)) +(defmethod connected-p ((c connection)) + (sb-thread:thread-alive-p (connection-handler c))) + (defmethod reconnect ((c connection)) (with-slots (server-name server-port user password socket stream connection-handler) c (let* ((sock (usocket:socket-connect server-name server-port)) - (s (usocket:socket-stream sock)) - (handler (spawn-with-name (format nil "empire-handler-~a" user) - #'handle-connection c))) + (s (usocket:socket-stream sock))) (setf socket sock stream s - connection-handler handler)))) + connection-handler (spawn-with-name (format nil "empire-handler-~a" user) + #'handle-connection c)))) + (empire-log:info "~a: reconnect" c)) (defparameter +C_CMDOK+ "0") (defparameter +C_DATA+ "1") @@ -139,59 +138,77 @@ this stream.") (defmethod set-new-mode ((m base-mode) new-mode) (let* ((c (connection m)) (mode (make-mode c new-mode))) + (empire-log:info "~a: set-new-mode ~a -> ~a" c (connection-mode c) new-mode) (setf (connection-mode c) mode))) (defgeneric handle-data (base-mode message)) (defgeneric handle-init (base-mode message)) (defgeneric handle-exit (base-mode message)) (defgeneric handle-prompt (base-mode message)) +(defgeneric handle-flush (base-mode message)) (defgeneric handle-simple-message (base-mode message)) +(defgeneric handle-ignore (base-mode message)) (defmethod handle-init ((m init-mode) message) (declare (ignorable message)) (with-slots (play-sent-p) m (if (not play-sent-p) - (progn - (play (connection m)) + (let ((c (connection m))) + (login-and-play c) (setf play-sent-p t)) (set-new-mode m 'play-mode)))) (defmethod handle-exit ((m init-mode) message) (declare (ignorable message)) - (quit (connection m))) + t) (defmethod handle-exit ((m play-mode) message) (declare (ignorable message)) (quit (connection m))) (defmethod handle-data ((m play-mode) message) - (let* ((c (connection m)) - (log (logging-stream c))) - (format log "handle-data ~a~%" message) + (let* ((c (connection m))) (empire-web:data (session c) message))) (defmethod handle-simple-message ((m play-mode) message) - (let* ((c (connection m)) - (log (logging-stream c))) - (format log "handle-simple-message ~a~%" message) + (let* ((c (connection m))) (empire-web:data (session c) message))) +(defmethod handle-flush ((m play-mode) message) + (let* ((c (connection m))) + (empire-web:prompt (session c) message) + (send-next-line c))) + +(defmethod send-next-line ((c connection)) + (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 c mode)))))) + (defmethod handle-prompt ((m play-mode) message) - (let* ((c (connection m)) - (log (logging-stream c))) + (let* ((c (connection m))) (with-input-from-string (s message) (let* ((minutes (read s)) ;;FIXME DANGEROUS (btus (read s))) - (format log "[~a:~a]: ~%" minutes btus) - (empire-web:prompt (session c) minutes btus))))) + (empire-web:prompt (session c) + (format nil "[~a,~a]: " minutes btus)))) + (send-next-line c))) + +(defmethod handle-ignore ((m init-mode) message) + (declare (ignorable message)) + t) (defparameter *line-type-dispatch* (list `(,+C_DATA+ . handle-data) `(,+C_INIT+ . handle-init) `(,+C_EXIT+ . handle-exit) - `(,+C_FLUSH+ . handle-simple-message) + `(,+C_FLUSH+ . handle-flush) `(,+C_BADCMD+ . handle-simple-message) `(,+C_FLASH+ . handle-simple-message) + `(,+C_CMDOK+ . handle-ignore) `(,+C_PROMPT+ . handle-prompt))) (defun parse-server-line (line) @@ -222,9 +239,8 @@ this stream.") (defmethod read-message ((c connection)) (handler-case (let* ((s (network-stream c)) - (log (logging-stream c)) (line (read-line s))) - (format log "< ~a~%" line) + (empire-log:info "~a: < ~a" c line) (multiple-value-bind (message type) (parse-server-line line) (let ((handler (lookup-handler type)) (mode (connection-mode c))) @@ -239,30 +255,47 @@ this stream.") (write-sequence message s) (terpri s) (force-output s)) - -(defmethod send-message ((c connection) message) - (let ((s (network-stream c)) - (log (logging-stream c))) - (format log "< ~a~%" message) - (let ((sent-p nil) - (tries 3)) - (loop - while (and (not sent-p) - (> tries 0)) - do (handler-case - (progn - (raw-send-message s message) - (setf sent-p t)) - (sb-int:closed-stream-error () - (progn - (decf tries) - (reconnect c)))))) - message)) - -(defgeneric play (connection)) -(defmethod play ((c connection)) + +(defgeneric send-message-one (connection string)) +(defmethod send-message-one ((c connection) message) + (empire-log:info "~a: > ~a" c message) + (let ((s (network-stream c))) + (raw-send-message s message))) + +(defmethod send-message ((c connection) message &key next-mode) + (if (not (connected-p c)) + (reconnect c)) + (if next-mode + (locked-queue:enqueue (send-queue c) (cons message next-mode)) + (locked-queue:enqueue (send-queue c) message))) + +(defparameter +special-command-char+ #\;) + +(defun special-command-p (line) + (and (> (length line) 0) + (char= +special-command-char+ (aref line 0)))) + +(defmethod special-xup ((c connection)) + t) + +(defmethod special-command ((c connection) line) + (cond ((string= line "xup") (special-xup c)) + (t (empire-web:data (session c) "Unknown special command"))) + line) + +(defmethod command ((c connection) line) + (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 c (format nil "play FIXME ~a ~a" user password)))) + (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)))