aboutsummaryrefslogtreecommitdiffstats
path: root/Lisp/moxie/world.lisp
blob: af6ee655df1d545948e63f5e14eb1233c49bf589 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
;;; -*- Lisp -*-
;; $Id: world.lisp 48 2006-01-09 00:27:16Z bjc $
(in-package :moxie)

(defvar *worlds* (make-hash-table)
  "The world environments, keyed on world id.")

(let ((next-world-id 0))
  (defclass world ()
    ((id :initarg :id :initform (incf next-world-id)
         :accessor world-id
         :documentation "The world id.")
     (vars :initarg :vars :initform nil
           :accessor world-vars
           :documentation "Savable settings.")
     (save-path :initarg :save-path :initform nil
                :accessor world-save-path
                :documentation "File path.")
     (stream :initarg :stream :initform nil
             :accessor world-stream
             :documentation "Connection to server.")
     (connected :initarg :connected :initform nil
                :accessor world-connected
                :documentation "Are we currently connected?"))
    (:documentation "All associated world information.")))

(defgeneric load-world-state (world &key path &allow-other-keys)
  (:documentation "Returns an ALIST from WORLD's disk location, or PATH (if set)."))

(defgeneric save-world-state (world &key path as-copy &allow-other-keys)
  (:documentation "Saves WORLD's state to its disk location or PATH (if set)."))

(defgeneric world-event-handler (event &rest args)
  (:documentation "Handle EVENT (w/ ARGS) for *WORLD*."))

(defmethod initialize-instance ((instance world) &rest initargs)
  (declare (ignore initargs))
  (format t "initialize-instance world~%")
  (add-world (call-next-method)))

(defmethod world-event-handler (event &rest args)
  "Default handler doesn't know about anything, so it logs, instead."
  (format t "Don't know how to handle event ~S ~S from world ~S.~%"
          event args (world-id *world*)))

(defun add-world (world)
  (setf (gethash (world-id world) *worlds*) world))

(defun remove-world (world)
  (remhash (world-id world) *worlds*))

(defun map-worlds (fn)
  (let ((result nil))
    (maphash (lambda (k v)
               (declare (ignore k))
               (setf result (cons (funcall fn v) result)))
             *worlds*)
    (nreverse result)))

(defun map-world-vars (fn &optional (world *world*))
  (mapcar (lambda (list)
            (funcall fn (car list) (cdr list)))
          (world-vars world)))

(defun world-var (name &optional (world *world*))
  "Returns the value for NAME in WORLD's environment."
  (cdr (assoc name (world-vars world))))

(defun set-world-var (name value &optional (world *world*))
  "Sets the value of NAME to VALUE in WORLD's environment."
  (setf (world-vars world)
        (cons (cons name value)
              (remove-if (lambda (x)
                           (eql (car x) name))
                         (world-vars world)))))

(defsetf world-var (name &optional (world '*world*)) (value)
  `(set-world-var ,name ,value ,world))

(defun close-world (&optional (world *world*))
  "Closes WORLD."
  (world-disconnect world)
  (remove-world world)
  (let ((*world* world))
    (run-hook :world-closed-hook)))

(defun world-connect (&optional (world *world*))
  "Connects WORLD to the host and port specified."
  (awhen (aand (world-var :hostname world) (world-var :port world)
               (open-connection (world-var :hostname world) (world-var :port world)))
    (add-input-handler it
                       (lambda (stream)
                         (let ((*world* world))
                           (handler-case
                               (while (listen stream)
                                 (multiple-value-bind (line missing-newline-p) (read-line stream)
                                   (run-hook :output-from-server-hook line)
                                   (when missing-newline-p
                                     (signal 'end-of-file))))
                             (end-of-file ()
                               (world-disconnect world))))))
    (setf (world-stream world) it)
    (setf (world-connected world) t)
    (let ((*world* world))
      (run-hook :world-connected-hook))))

(defun world-disconnect (&optional (world *world*))
  "Closes the connection, if opened, for WORLD."
  (let ((*world* world))
    (when (world-connected *world*)
      (close-connection (world-stream *world*))
      (setf (world-stream *world*) nil)
      (setf (world-connected *world*) nil)
      (run-hook :world-disconnected-hook))))

(defun world-event (world-id &rest args)
  (format t "DEBUG: world-event ~S ~S~%" world-id args)
  (let ((*world* (or (gethash world-id *worlds*)
                     (make-instance 'world :id world-id))))
    (apply #'world-event-handler args)))