/
connector.lisp
130 lines (92 loc) · 4.23 KB
/
connector.lisp
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
121
122
123
124
125
126
127
128
129
130
(in-package :mortar-combat)
(define-constant +supported-server-version+ 1)
(declaim (special *message*))
(defstruct (server-identity
(:constructor make-server-identity (id name)))
(id nil :read-only t)
(name nil :read-only t))
(defun connection-stream-of (connector)
(usocket:socket-stream (connection-of connector)))
(defclass connector (lockable disposable dispatcher)
((enabled-p :initform t)
(connection :initarg :connection :reader connection-of)
(message-counter :initform 0)
(message-table :initform (make-hash-table :test 'eql))))
(defmethod initialize-instance :after ((this connector) &key)
(with-slots (connection message-table enabled-p) this
(in-new-thread "connector-thread"
(loop while enabled-p
do (log-errors
(usocket:wait-for-input connection)
(let* ((stream (connection-stream-of this))
(message (decode-message stream)))
(if-let ((reply-id (getf message :reply-for)))
(with-instance-lock-held (this)
(if-let ((handler (gethash reply-id message-table)))
(progn
(remhash reply-id message-table)
(funcall handler message))
(log:error "Handler not found for message with id ~A" reply-id)))
(progn
(encode-message (process-command (getf message :command) message) stream)
(force-output stream)))))
finally (usocket:socket-close connection)))))
(defun connect-to-server (host port)
(make-instance 'connector
:connection (usocket:socket-connect host port
:element-type '(unsigned-byte 8)
:timeout 30)))
(defun disconnect-from-server (connector)
(with-slots (enabled-p) connector
(setf enabled-p nil)))
(defun check-response (message expected-command)
(let ((command (getf message :command)))
(when (eq command :error)
(error "Server error of type ~A: ~A" (getf message :type) (getf message :text)))
(unless (eq command expected-command)
(error "Unexpected command received from server: wanted ~A, but ~A received"
expected-command command))))
(defun send-command (connector &rest properties &key &allow-other-keys)
(let ((stream (connection-stream-of connector)))
(encode-message properties stream)
(finish-output stream)))
(defmacro with-response (command-name (&rest properties) response &body body)
`(destructuring-bind (&key ,@properties &allow-other-keys) ,response
(check-response ,response ,command-name)
,@body))
(defmethod dispatch ((this connector) (task function) invariant &rest keys
&key &allow-other-keys)
(with-slots (message-table message-counter) this
(with-instance-lock-held (this)
(let ((next-id (incf message-counter)))
(flet ((response-callback (message)
(let ((*message* message))
(funcall task))))
(setf (gethash next-id message-table) #'response-callback)
(apply #'send-command this :message-id next-id keys))))))
(defun server-version (connector)
(-> (connector :command :version) ()
(with-response :version (version) *message*
version)))
(defun identify (connector name)
(-> (connector :command :identify :name name) ()
(with-response :identified (id name) *message*
(make-server-identity id name))))
(defun create-arena (connector name)
(-> (connector :command :create-arena :name name) ()
(with-response :ok () *message*)))
(defun join-arena (connector name)
(-> (connector :command :join-arena :name name) ()
(with-response :ok () *message*)))
(defun get-arena-list (connector)
(-> (connector :command :get-arena-list) ()
(with-response :arena-list (list) *message*
list)))
(defun register-game-stream (connector peer-id)
(-> (connector :command :register-game-stream :peer-id peer-id) ()
(with-response :ok () *message*)))
(defun ping-peer (connector)
(-> (connector :command :ping) ()
(with-response :ok () *message*)))
(defmethod process-command ((command (eql :ping)) message)
+ok-reply+)