JasonWoof Got questions, comments, patches, etc.? Contact Jason Woofenden
client sync working (and spitting out debug cruft)
[peach-cgt.git] / server.coffee
index 9beb901..ab0ca9f 100644 (file)
@@ -2,9 +2,16 @@ listen_port = 8333
 sys = require 'sys'
 fs = require 'fs'
 http = require 'http'
+querystring = require 'querystring'
 url = require 'url'
 less = require 'less'
 coffee = require 'coffee-script'
+model = require './common.coffee'
+
+games = {}
+
+# timeout function with args in convenient order
+timeout = (ms, func) -> setTimeout func, ms
 
 css_handler = (args, out, request, url_parts) ->
        fs.readFile 'style.less', 'utf8', (err, data) ->
@@ -57,6 +64,130 @@ javascript_handler = (args, out, request, url_parts) ->
                out.end data
 
 
+get_handler = (args, out, request, url_parts) ->
+       console.log "get handler: ", args
+       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) ->
+       console.log "set handler: ", args
+       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 args.messages?
+               out.writeHead 404, "Content-Type": 'text/plain'
+               out.end '"messages" argument must be set'
+               return
+
+       unless games[args.game]?
+               out.writeHead 404, "Content-Type": 'text/plain'
+               out.end 'Game not found'
+               return
+
+       game = games[args.game]
+
+       # FIXME add error checking (json validity at least)
+       game.process_messages JSON.parse args.messages
+
+       out.writeHead 200, "Content-Type": 'text/plain'
+       out.end 'ok'
+
+# 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
+       console.log this
+
+new_game = (id) ->
+       game = games[id] = model.new 'server'
+       game.p1_waiter = false
+       game.p2_waiter = false
+       game.p1_queue = []
+       game.p2_queue = []
+
+       game.on 'move', (agent, card, x, y) ->
+               forward_events.call this, 'move', agent, card, x, y
+       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 'set_cards', (cards) ->
+               forward_events.call this, 'set_cards', cards
+
+       return game
+
+test_init = ->
+       test_game = new_game 'test'
+       timeout 4000, ->
+               test_game.set_cards [
+                       { text: "Wildabeast 2/2", x: 20, y: 140}
+                       { text: "Wild beast 2/2", x: 150, y: 140}
+                       { text: "Angora bunny 1/1", x: 300, y: 140}
+                       { text: "Ambulatory Cactus 2/1", x: 450, y: 140}
+                       { text: "Ent 0/5", x: 600, y: 140}
+               ]
+
+test_init()
+
+
 http_server = http.createServer (req, res) ->
        url_parts = url.parse req.url, true
        if url_parts.query is undefined
@@ -69,9 +200,25 @@ http_server = http.createServer (req, res) ->
        else if rel_path.substr(rel_path.length - 4) is '.css'
                res.writeHead 200, 'Content-Type': 'text/css'
                return css_handler url_parts.query, res, req, url_parts
-       else if rel_path.substr rel_path.length - 3 is '.js'
+       else if rel_path.substr(rel_path.length - 3) is '.js'
                res.writeHead 200, 'Content-Type': 'text/javascript'
                return js_handler url_parts.query, res, req, url_parts
+       else if rel_path.substr(rel_path.length - 4) is '/set'
+               data = ''
+               req.on 'data', (chunk) ->
+                       data += chunk
+               req.on 'end', ->
+                       query = url_parts.query
+                       post_args = querystring.parse data
+                       console.log data, post_args
+                       for key, parg of post_args
+                               query[key] = parg
+                       return set_handler query, res, req, url_parts
+       else if rel_path.substr(rel_path.length - 4) is '/get'
+               return get_handler url_parts.query, res, req, url_parts
+       else if rel_path.substr(rel_path.length - 4) is '.ico'
+               res.writeHead 404
+               return res.end()
 
        return html_handler url_parts.query, res, req, url_parts