Skip to content

Commit f138489

Browse files
committed
Add world state json server to clj
1 parent ea15ea7 commit f138489

2 files changed

Lines changed: 108 additions & 14 deletions

File tree

clj/src/n01se/ws.clj

Lines changed: 9 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,4 @@
11
(ns n01se.ws
2-
(:require [clj-json.core :as json])
32
(:import [org.eclipse.jetty.websocket
43
WebSocket$OnTextMessage WebSocketClientFactory WebSocket$Connection
54
WebSocketServlet]
@@ -28,21 +27,23 @@
2827
(onMessage [this data] (onmessage this data))))
2928
(.get 10 TimeUnit/SECONDS))))
3029

31-
(defn sendjson [ws obj]
32-
(.sendMessage ^WebSocket$Connection ws (json/generate-string obj)))
30+
(defn send-message [^WebSocket$Connection ws text]
31+
(.sendMessage ws text))
3332

33+
(defn close [^WebSocket$Connection ws]
34+
(close ^WebSocket$Connection ws))
3435

35-
(defn ws-server
36+
(defn ^Server ws-server
3637
[& {:keys [port onopen onclose onmessage ws-path]
3738
:or {port 8080
3839
ws-path "/"}}]
3940
(let [onopen (or onopen (fn [this conn] (println "onopen:" conn)))
4041
onclose (or onclose (fn [this code msg] (println "onclose:" code msg)))
41-
onmessage (or onmessage (fn [this data] (println "onmessage:" data)))
42+
onmessage (or onmessage (fn [this data] (println "onmessage:" this data)))
4243
servlet (proxy [WebSocketServlet] []
4344
(doGet [request response]
44-
(.. (proxy-super getServletContext)
45-
(getNamedDispatcher (proxy-super getServletName))
45+
(.. (.getServletContext ^WebSocketServlet this)
46+
(getNamedDispatcher (.getServletName ^WebSocketServlet this))
4647
(forward request response)))
4748
(doWebSocketConnect [request response]
4849
(reify WebSocket$OnTextMessage
@@ -51,7 +52,7 @@
5152
(onMessage [this data] (onmessage this data)))))
5253
context (doto (ServletContextHandler.)
5354
(.setContextPath "/")
54-
(.addServlet (ServletHolder. servlet) ws-path))
55+
(.addServlet (ServletHolder. servlet) ^String ws-path))
5556
connector (doto (SelectChannelConnector.)
5657
(.setPort port)
5758
(.setMaxIdleTime Integer/MAX_VALUE))

clj/src/n01se/xkcd1110.clj

Lines changed: 99 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,16 @@
11
(ns n01se.xkcd1110
22
(:require [clj-json.core :as json]
3-
[n01se.ws :as ws]))
3+
[n01se.ws :as ws] ))
44

55
(set! *warn-on-reflection* true)
66

7+
(def max-clients 20)
8+
(def max-msg-length 500)
9+
(def push-interval 75)
10+
11+
(defn send-one [ws obj]
12+
(ws/send-message ws (json/generate-string obj)))
13+
714
(defn round [num places]
815
(let [factor (Math/pow 10 places)]
916
(/ (Math/round (* num factor)) factor)))
@@ -15,10 +22,10 @@
1522
speed 0.2
1623
delay 150
1724
halfpi (/ Math/PI 2)
18-
ws (ws/ws-client uri :onmessage (fn [_]))]
19-
(ws/sendjson ws {:nick (str "circlebot " i)})
25+
ws (ws/ws-client uri :onmessage (fn [_ _]))]
26+
(send-one ws {:nick (str "circlebot " i)})
2027
(loop [theta (* 0.3 i)]
21-
(ws/sendjson ws
28+
(send-one ws
2229
{:dx (round (* (Math/cos (+ theta halfpi)) speed) 4)
2330
:dy (round (* (Math/sin (+ theta halfpi)) speed) 4)
2431
:x (Math/round (+ -823 (* (Math/cos theta) radius)))
@@ -27,14 +34,100 @@
2734
(Thread/sleep delay)
2835
(recur (+ theta (/ (* speed delay) 150))))))
2936

37+
;; server state:
38+
(defonce last-id (atom 0))
39+
(defonce client-struct (ref {})) ;; ref of map of client obj to thing
40+
;; could be another ref in the struct:
41+
(defonce changes (ref {})) ;; ref of map of client obj to client-delta
42+
(defonce publisher (agent 0))
43+
44+
(defn get-world []
45+
(dosync
46+
(into {} (for [{:keys [id data-ref]} (vals @client-struct)]
47+
[id @data-ref]))))
48+
49+
(defn send-all [obj]
50+
(let [txt (json/generate-string obj)]
51+
(doseq [struct (vals @client-struct)]
52+
(try
53+
(ws/send-message ^WebSocket$Connection (:conn struct) txt)
54+
(catch java.io.IOException e
55+
;; presumably this connection will be closed shortly...
56+
nil)))))
57+
58+
59+
(defn get-id [client]
60+
(get-in @client-struct [client :id]))
61+
62+
(defn publish [i]
63+
(when (seq @changes)
64+
(let [old-changes
65+
(dosync
66+
(let [old-changes @changes]
67+
(alter changes empty)
68+
old-changes))]
69+
(send-all {:change
70+
(zipmap (map get-id (keys old-changes))
71+
(vals old-changes))})))
72+
;; do it again:
73+
(Thread/sleep push-interval)
74+
(send-off *agent* publish)
75+
(inc i))
76+
77+
(defn remove-client [client]
78+
(let [id (get-id client)]
79+
(dosync
80+
(commute client-struct dissoc client)
81+
(commute changes dissoc client))
82+
(println "Client count:" (count @client-struct))
83+
(send-all {:delete id})))
84+
85+
(defn server-onopen [client conn]
86+
(let [id (swap! last-id inc)
87+
struct {:id id, :conn conn, :data-ref (ref {})} ]
88+
(println "New client ID" id conn)
89+
(dosync
90+
(commute client-struct assoc client struct))
91+
(send-one conn
92+
{:id id, :all (get-world)})))
93+
94+
(defn server-onclose [client code msg]
95+
(println "Client ID" (get-id client) "disconnected")
96+
(remove-client client))
97+
98+
(defn server-onmessage [client data]
99+
(if (> (count data) max-msg-length)
100+
(do
101+
(println "Client ID" (get-id client) "sent oversize message"
102+
(count data) "chars")
103+
(ws/close (get-in @client-struct [client :conn])))
104+
(let [id (get-id client)
105+
msg (json/parse-string data true)]
106+
(when-not (:whomp msg)
107+
(dosync
108+
(commute changes update-in [client] merge msg)
109+
(let [data-ref (get-in @client-struct [client :data-ref])]
110+
(alter data-ref merge msg)))))))
111+
112+
(defn carefully [f]
113+
(fn [& args]
114+
(try
115+
(apply f args)
116+
(catch Exception e
117+
(.printStackTrace e)
118+
(throw e)))))
119+
30120
(defn -main [& [mode arg1 arg2]]
31-
(condp = mode
121+
(case mode
32122
"server"
33123
(let [port (if arg1 (Integer/parseInt arg1) 8090)
34124
server (ws/ws-server :port port
35-
:onopen (fn [this conn] (println this conn)))]
125+
:onopen (carefully #'server-onopen)
126+
:onclose (carefully #'server-onclose)
127+
:onmessage #'server-onmessage)]
36128
(prn "server" :port port)
37129
(.start server)
130+
(send-off publisher publish)
38131
server)
39132

40133
"client"

0 commit comments

Comments
 (0)