+
+
+get_handler = (args, out, request, url_parts) ->
+ unless args.game?.length
+ out.writeHead 404, "Content-Type": 'text/plain'
+ out.end 'Missing (or empty) "game" argument'
+ return
+
+ unless args.agent is 'p1' or args.agent is 'p2'
+ out.writeHead 404, "Content-Type": 'text/plain'
+ out.end '"agent" argument must be set to p1 or p2'
+ return
+
+ unless games[args.game]?
+ out.writeHead 404, "Content-Type": 'text/plain'
+ out.end 'Game not found'
+ return
+
+ game = games[args.game]
+
+ waiter = games["#{args.agent}_waiter"]
+ if waiter?
+ waiter.writeHead 200, 'Content-Type': 'text/javascript'
+ waiter.end '[]'
+
+ game["#{args.agent}_waiter"] = out
+
+ answer_soon game # in case there's something queued already
+
+set_handler = (args, out, request, url_parts) ->
+ unless args.game?.length
+ out.writeHead 404, "Content-Type": 'text/plain'
+ out.end '{"status":1,"text_status":"Missing (or empty) game argument"}'
+ return
+
+ unless args.agent is 'p1' or args.agent is 'p2'
+ out.writeHead 404, "Content-Type": 'text/plain'
+ out.end '{"status":2,"text_status":"agent argument must be set to p1 or p2"}'
+ return
+
+ unless args.messages?
+ out.writeHead 404, "Content-Type": 'text/plain'
+ out.end '{"status":3,"text_status":"messages argument must be set"}'
+ return
+
+ try
+ messages = JSON.parse args.messages
+ catch e
+ out.writeHead 400, "Content-Type": 'text/plain'
+ out.end '{"status":4,"text_status":"Invalid JSON"}'
+ return
+
+ # special handling of 'new_game' api, because for this one we don't have a
+ # game object to pass the message to
+ if messages?[0]?[0] is 'new_game'
+ message = messages.shift()
+ slug = message[1]
+ if games[slug]?
+ out.writeHead 403, "Content-Type": 'text/plain'
+ out.end '{"status":6,"text_status":"Game already exists"}'
+ return
+ game = games[slug] = new_game slug, 'server'
+ game.last_seen = now_s()
+ console.log "new game: #{slug}"
+ expire_old_games()
+
+ unless games[args.game]?
+ out.writeHead 404, "Content-Type": 'text/plain'
+ out.end '{"status":5,"text_status":"Game not found"}'
+ return
+
+ game = games[args.game]
+
+ game.last_seen = now_s()
+
+ game.process_messages messages
+
+ out.writeHead 200, "Content-Type": 'text/plain'
+ out.end '{"status":0,"text_status":"Success"}'
+
+# don't call this directly, call answer_soon instead
+answer_now = (game) ->
+ if game.p1_waiter and game.p1_queue.length
+ waiter = game.p1_waiter
+ queue = game.p1_queue
+ game.p1_waiter = false
+ game.p1_queue = []
+ waiter.writeHead 200, 'Content-Type': 'text/javascript'
+ waiter.end JSON.stringify queue
+ if game.p2_waiter and game.p2_queue.length
+ waiter = game.p2_waiter
+ queue = game.p2_queue
+ game.p2_waiter = false
+ game.p2_queue = []
+ waiter.writeHead 200, 'Content-Type': 'text/javascript'
+ waiter.end JSON.stringify queue
+
+# this marks a game as "dirty" and makes sure there's exactly one timeout
+# that'll respond to any clients that are waiting, and now have messages.
+answer_soon = (game) ->
+ unless game.replier_id
+ game.replier_id = timeout 1, ->
+ delete game.replier_id
+ answer_now game
+
+forward_events = (message...) ->
+ unless message[1] is 'p1'
+ @p1_queue.push message
+ answer_soon this
+ unless message[1] is 'p2'
+ @p2_queue.push message
+ answer_soon this
+
+new_game = (slug) ->
+ game = games[slug] = model.new slug, 'server'
+ game.p1_waiter = false
+ game.p2_waiter = false
+ game.p1_queue = []
+ game.p2_queue = []
+
+ game.on 'move', (agent, card, x, y, z, pile) ->
+ forward_events.call this, 'move', agent, card, x, y, z, pile
+ game.on 'mark', (agent, card, state) ->
+ forward_events.call this, 'mark', agent, card, state
+ game.on 'flip', (agent, card, state) ->
+ forward_events.call this, 'flip', agent, card, state
+ game.on 'new_cards', (agent, cards) ->
+ # server assigns card numbers, and tells both clients
+ # (unlike all other api calls, sending agent expects to get this one back)
+ forward_events.call this, 'new_cards', 'server', cards
+ game.on 'set_cards', (agent, cards) ->
+ forward_events.call this, 'set_cards', agent, cards
+ game.on 'send_state', (agent) ->
+ timeout 10, =>
+ if agent is 'p1'
+ @p1_queue.push ['set_cards', 'server', @cards]
+ answer_soon this
+ if agent is 'p2'
+ @p2_queue.push ['set_cards', 'server', @cards]
+ answer_soon this
+
+ return game