Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with HTTPS or Subversion.

Download ZIP
Browse files

Percept is born.

  • Loading branch information...
commit 1d3ba99529a6f0c3e7f1e169492fc6aade4c39ef 0 parents
@gabehollombe authored
Showing with 31,214 additions and 0 deletions.
  1. +1 −0  .gitignore
  2. +3 −0  Guardfile
  3. +31 −0 README.md
  4. +2 −0  bin/run_server
  5. +32 −0 build/Cakefile
  6. +129 −0 dist/lib/client.js
  7. +27 −0 dist/lib/components/event_emitter.js
  8. +62 −0 dist/lib/components/lerpable.js
  9. +23 −0 dist/lib/message.js
  10. +7 −0 dist/lib/percept.js
  11. +193 −0 dist/lib/server.js
  12. +240 −0 dist/percept.js
  13. +5 −0 examples/simple/Guardfile
  14. +81 −0 examples/simple/client/client.coffee
  15. +39 −0 examples/simple/my_game.coffee
  16. +35 −0 examples/simple/my_game_entity.coffee
  17. +28 −0 examples/simple/my_game_server.coffee
  18. +418 −0 examples/simple/node_modules/.bin/express
  19. +7 −0 examples/simple/node_modules/express/.npmignore
  20. +755 −0 examples/simple/node_modules/express/History.md
  21. +22 −0 examples/simple/node_modules/express/LICENSE
  22. +35 −0 examples/simple/node_modules/express/Makefile
  23. +143 −0 examples/simple/node_modules/express/Readme.md
  24. +418 −0 examples/simple/node_modules/express/bin/express
  25. +2 −0  examples/simple/node_modules/express/index.js
  26. +79 −0 examples/simple/node_modules/express/lib/express.js
  27. +583 −0 examples/simple/node_modules/express/lib/http.js
  28. +52 −0 examples/simple/node_modules/express/lib/https.js
  29. +321 −0 examples/simple/node_modules/express/lib/request.js
  30. +460 −0 examples/simple/node_modules/express/lib/response.js
  31. +53 −0 examples/simple/node_modules/express/lib/router/collection.js
  32. +398 −0 examples/simple/node_modules/express/lib/router/index.js
  33. +70 −0 examples/simple/node_modules/express/lib/router/methods.js
  34. +88 −0 examples/simple/node_modules/express/lib/router/route.js
  35. +152 −0 examples/simple/node_modules/express/lib/utils.js
  36. +457 −0 examples/simple/node_modules/express/lib/view.js
  37. +40 −0 examples/simple/node_modules/express/lib/view/partial.js
  38. +210 −0 examples/simple/node_modules/express/lib/view/view.js
  39. +11 −0 examples/simple/node_modules/express/node_modules/connect/.npmignore
  40. +24 −0 examples/simple/node_modules/express/node_modules/connect/LICENSE
  41. +2 −0  examples/simple/node_modules/express/node_modules/connect/index.js
  42. +81 −0 examples/simple/node_modules/express/node_modules/connect/lib/cache.js
  43. +106 −0 examples/simple/node_modules/express/node_modules/connect/lib/connect.js
  44. +215 −0 examples/simple/node_modules/express/node_modules/connect/lib/http.js
  45. +47 −0 examples/simple/node_modules/express/node_modules/connect/lib/https.js
  46. +46 −0 examples/simple/node_modules/express/node_modules/connect/lib/index.js
  47. +93 −0 examples/simple/node_modules/express/node_modules/connect/lib/middleware/basicAuth.js
  48. +92 −0 examples/simple/node_modules/express/node_modules/connect/lib/middleware/bodyParser.js
  49. +163 −0 examples/simple/node_modules/express/node_modules/connect/lib/middleware/compiler.js
  50. +46 −0 examples/simple/node_modules/express/node_modules/connect/lib/middleware/cookieParser.js
  51. +105 −0 examples/simple/node_modules/express/node_modules/connect/lib/middleware/csrf.js
  52. +222 −0 examples/simple/node_modules/express/node_modules/connect/lib/middleware/directory.js
  53. +100 −0 examples/simple/node_modules/express/node_modules/connect/lib/middleware/errorHandler.js
  54. +76 −0 examples/simple/node_modules/express/node_modules/connect/lib/middleware/favicon.js
  55. +78 −0 examples/simple/node_modules/express/node_modules/connect/lib/middleware/limit.js
  56. +299 −0 examples/simple/node_modules/express/node_modules/connect/lib/middleware/logger.js
  57. +38 −0 examples/simple/node_modules/express/node_modules/connect/lib/middleware/methodOverride.js
  58. +100 −0 examples/simple/node_modules/express/node_modules/connect/lib/middleware/profiler.js
  59. +40 −0 examples/simple/node_modules/express/node_modules/connect/lib/middleware/query.js
  60. +34 −0 examples/simple/node_modules/express/node_modules/connect/lib/middleware/responseTime.js
  61. +379 −0 examples/simple/node_modules/express/node_modules/connect/lib/middleware/router.js
  62. +346 −0 examples/simple/node_modules/express/node_modules/connect/lib/middleware/session.js
  63. +126 −0 examples/simple/node_modules/express/node_modules/connect/lib/middleware/session/cookie.js
  64. +131 −0 examples/simple/node_modules/express/node_modules/connect/lib/middleware/session/memory.js
  65. +137 −0 examples/simple/node_modules/express/node_modules/connect/lib/middleware/session/session.js
  66. +87 −0 examples/simple/node_modules/express/node_modules/connect/lib/middleware/session/store.js
  67. +220 −0 examples/simple/node_modules/express/node_modules/connect/lib/middleware/static.js
  68. +122 −0 examples/simple/node_modules/express/node_modules/connect/lib/middleware/staticCache.js
  69. +44 −0 examples/simple/node_modules/express/node_modules/connect/lib/middleware/vhost.js
  70. +79 −0 examples/simple/node_modules/express/node_modules/connect/lib/patch.js
  71. +75 −0 examples/simple/node_modules/express/node_modules/connect/lib/public/directory.html
  72. +13 −0 examples/simple/node_modules/express/node_modules/connect/lib/public/error.html
  73. BIN  examples/simple/node_modules/express/node_modules/connect/lib/public/favicon.ico
  74. BIN  examples/simple/node_modules/express/node_modules/connect/lib/public/icons/page.png
  75. BIN  examples/simple/node_modules/express/node_modules/connect/lib/public/icons/page_add.png
  76. BIN  examples/simple/node_modules/express/node_modules/connect/lib/public/icons/page_attach.png
  77. BIN  examples/simple/node_modules/express/node_modules/connect/lib/public/icons/page_code.png
  78. BIN  examples/simple/node_modules/express/node_modules/connect/lib/public/icons/page_copy.png
  79. BIN  examples/simple/node_modules/express/node_modules/connect/lib/public/icons/page_delete.png
  80. BIN  examples/simple/node_modules/express/node_modules/connect/lib/public/icons/page_edit.png
  81. BIN  examples/simple/node_modules/express/node_modules/connect/lib/public/icons/page_error.png
  82. BIN  examples/simple/node_modules/express/node_modules/connect/lib/public/icons/page_excel.png
  83. BIN  examples/simple/node_modules/express/node_modules/connect/lib/public/icons/page_find.png
  84. BIN  examples/simple/node_modules/express/node_modules/connect/lib/public/icons/page_gear.png
  85. BIN  examples/simple/node_modules/express/node_modules/connect/lib/public/icons/page_go.png
  86. BIN  examples/simple/node_modules/express/node_modules/connect/lib/public/icons/page_green.png
  87. BIN  examples/simple/node_modules/express/node_modules/connect/lib/public/icons/page_key.png
  88. BIN  examples/simple/node_modules/express/node_modules/connect/lib/public/icons/page_lightning.png
  89. BIN  examples/simple/node_modules/express/node_modules/connect/lib/public/icons/page_link.png
  90. BIN  examples/simple/node_modules/express/node_modules/connect/lib/public/icons/page_paintbrush.png
  91. BIN  examples/simple/node_modules/express/node_modules/connect/lib/public/icons/page_paste.png
  92. BIN  examples/simple/node_modules/express/node_modules/connect/lib/public/icons/page_red.png
  93. BIN  examples/simple/node_modules/express/node_modules/connect/lib/public/icons/page_refresh.png
  94. BIN  examples/simple/node_modules/express/node_modules/connect/lib/public/icons/page_save.png
  95. BIN  examples/simple/node_modules/express/node_modules/connect/lib/public/icons/page_white.png
  96. BIN  examples/simple/node_modules/express/node_modules/connect/lib/public/icons/page_white_acrobat.png
  97. BIN  examples/simple/node_modules/express/node_modules/connect/lib/public/icons/page_white_actionscript.png
  98. BIN  examples/simple/node_modules/express/node_modules/connect/lib/public/icons/page_white_add.png
  99. BIN  examples/simple/node_modules/express/node_modules/connect/lib/public/icons/page_white_c.png
  100. BIN  examples/simple/node_modules/express/node_modules/connect/lib/public/icons/page_white_camera.png
  101. BIN  examples/simple/node_modules/express/node_modules/connect/lib/public/icons/page_white_cd.png
  102. BIN  examples/simple/node_modules/express/node_modules/connect/lib/public/icons/page_white_code.png
  103. BIN  examples/simple/node_modules/express/node_modules/connect/lib/public/icons/page_white_code_red.png
  104. BIN  examples/simple/node_modules/express/node_modules/connect/lib/public/icons/page_white_coldfusion.png
  105. BIN  examples/simple/node_modules/express/node_modules/connect/lib/public/icons/page_white_compressed.png
  106. BIN  examples/simple/node_modules/express/node_modules/connect/lib/public/icons/page_white_copy.png
  107. BIN  examples/simple/node_modules/express/node_modules/connect/lib/public/icons/page_white_cplusplus.png
  108. BIN  examples/simple/node_modules/express/node_modules/connect/lib/public/icons/page_white_csharp.png
  109. BIN  examples/simple/node_modules/express/node_modules/connect/lib/public/icons/page_white_cup.png
  110. BIN  examples/simple/node_modules/express/node_modules/connect/lib/public/icons/page_white_database.png
  111. BIN  examples/simple/node_modules/express/node_modules/connect/lib/public/icons/page_white_delete.png
  112. BIN  examples/simple/node_modules/express/node_modules/connect/lib/public/icons/page_white_dvd.png
  113. BIN  examples/simple/node_modules/express/node_modules/connect/lib/public/icons/page_white_edit.png
  114. BIN  examples/simple/node_modules/express/node_modules/connect/lib/public/icons/page_white_error.png
  115. BIN  examples/simple/node_modules/express/node_modules/connect/lib/public/icons/page_white_excel.png
  116. BIN  examples/simple/node_modules/express/node_modules/connect/lib/public/icons/page_white_find.png
  117. BIN  examples/simple/node_modules/express/node_modules/connect/lib/public/icons/page_white_flash.png
  118. BIN  examples/simple/node_modules/express/node_modules/connect/lib/public/icons/page_white_freehand.png
  119. BIN  examples/simple/node_modules/express/node_modules/connect/lib/public/icons/page_white_gear.png
  120. BIN  examples/simple/node_modules/express/node_modules/connect/lib/public/icons/page_white_get.png
  121. BIN  examples/simple/node_modules/express/node_modules/connect/lib/public/icons/page_white_go.png
  122. BIN  examples/simple/node_modules/express/node_modules/connect/lib/public/icons/page_white_h.png
  123. BIN  examples/simple/node_modules/express/node_modules/connect/lib/public/icons/page_white_horizontal.png
  124. BIN  examples/simple/node_modules/express/node_modules/connect/lib/public/icons/page_white_key.png
  125. BIN  examples/simple/node_modules/express/node_modules/connect/lib/public/icons/page_white_lightning.png
  126. BIN  examples/simple/node_modules/express/node_modules/connect/lib/public/icons/page_white_link.png
  127. BIN  examples/simple/node_modules/express/node_modules/connect/lib/public/icons/page_white_magnify.png
  128. BIN  examples/simple/node_modules/express/node_modules/connect/lib/public/icons/page_white_medal.png
  129. BIN  examples/simple/node_modules/express/node_modules/connect/lib/public/icons/page_white_office.png
  130. BIN  examples/simple/node_modules/express/node_modules/connect/lib/public/icons/page_white_paint.png
  131. BIN  examples/simple/node_modules/express/node_modules/connect/lib/public/icons/page_white_paintbrush.png
  132. BIN  examples/simple/node_modules/express/node_modules/connect/lib/public/icons/page_white_paste.png
  133. BIN  examples/simple/node_modules/express/node_modules/connect/lib/public/icons/page_white_php.png
  134. BIN  examples/simple/node_modules/express/node_modules/connect/lib/public/icons/page_white_picture.png
  135. BIN  examples/simple/node_modules/express/node_modules/connect/lib/public/icons/page_white_powerpoint.png
  136. BIN  examples/simple/node_modules/express/node_modules/connect/lib/public/icons/page_white_put.png
  137. BIN  examples/simple/node_modules/express/node_modules/connect/lib/public/icons/page_white_ruby.png
  138. BIN  examples/simple/node_modules/express/node_modules/connect/lib/public/icons/page_white_stack.png
  139. BIN  examples/simple/node_modules/express/node_modules/connect/lib/public/icons/page_white_star.png
  140. BIN  examples/simple/node_modules/express/node_modules/connect/lib/public/icons/page_white_swoosh.png
  141. BIN  examples/simple/node_modules/express/node_modules/connect/lib/public/icons/page_white_text.png
  142. BIN  examples/simple/node_modules/express/node_modules/connect/lib/public/icons/page_white_text_width.png
  143. BIN  examples/simple/node_modules/express/node_modules/connect/lib/public/icons/page_white_tux.png
  144. BIN  examples/simple/node_modules/express/node_modules/connect/lib/public/icons/page_white_vector.png
  145. BIN  examples/simple/node_modules/express/node_modules/connect/lib/public/icons/page_white_visualstudio.png
  146. BIN  examples/simple/node_modules/express/node_modules/connect/lib/public/icons/page_white_width.png
  147. BIN  examples/simple/node_modules/express/node_modules/connect/lib/public/icons/page_white_word.png
  148. BIN  examples/simple/node_modules/express/node_modules/connect/lib/public/icons/page_white_world.png
  149. BIN  examples/simple/node_modules/express/node_modules/connect/lib/public/icons/page_white_wrench.png
  150. BIN  examples/simple/node_modules/express/node_modules/connect/lib/public/icons/page_white_zip.png
  151. BIN  examples/simple/node_modules/express/node_modules/connect/lib/public/icons/page_word.png
  152. BIN  examples/simple/node_modules/express/node_modules/connect/lib/public/icons/page_world.png
  153. +141 −0 examples/simple/node_modules/express/node_modules/connect/lib/public/style.css
  154. +444 −0 examples/simple/node_modules/express/node_modules/connect/lib/utils.js
  155. +24 −0 examples/simple/node_modules/express/node_modules/connect/package.json
  156. +39 −0 examples/simple/node_modules/express/node_modules/connect/test.js
  157. +19 −0 examples/simple/node_modules/express/node_modules/mime/LICENSE
  158. +50 −0 examples/simple/node_modules/express/node_modules/mime/README.md
  159. +92 −0 examples/simple/node_modules/express/node_modules/mime/mime.js
  160. +22 −0 examples/simple/node_modules/express/node_modules/mime/package.json
  161. +79 −0 examples/simple/node_modules/express/node_modules/mime/test.js
  162. +1,479 −0 examples/simple/node_modules/express/node_modules/mime/types/mime.types
  163. +43 −0 examples/simple/node_modules/express/node_modules/mime/types/node.types
  164. +2 −0  examples/simple/node_modules/express/node_modules/mkdirp/.gitignore
  165. +2 −0  examples/simple/node_modules/express/node_modules/mkdirp/.gitignore.orig
  166. +5 −0 examples/simple/node_modules/express/node_modules/mkdirp/.gitignore.rej
  167. +21 −0 examples/simple/node_modules/express/node_modules/mkdirp/LICENSE
  168. +21 −0 examples/simple/node_modules/express/node_modules/mkdirp/README.markdown
  169. +6 −0 examples/simple/node_modules/express/node_modules/mkdirp/examples/pow.js
  170. +6 −0 examples/simple/node_modules/express/node_modules/mkdirp/examples/pow.js.orig
  171. +19 −0 examples/simple/node_modules/express/node_modules/mkdirp/examples/pow.js.rej
  172. +20 −0 examples/simple/node_modules/express/node_modules/mkdirp/index.js
  173. +23 −0 examples/simple/node_modules/express/node_modules/mkdirp/package.json
  174. +28 −0 examples/simple/node_modules/express/node_modules/mkdirp/test/mkdirp.js
  175. +41 −0 examples/simple/node_modules/express/node_modules/mkdirp/test/race.js
  176. +32 −0 examples/simple/node_modules/express/node_modules/mkdirp/test/rel.js
  177. +6 −0 examples/simple/node_modules/express/node_modules/qs/.gitmodules
  178. +52 −0 examples/simple/node_modules/express/node_modules/qs/History.md
  179. +7 −0 examples/simple/node_modules/express/node_modules/qs/Makefile
  180. +49 −0 examples/simple/node_modules/express/node_modules/qs/Readme.md
  181. +17 −0 examples/simple/node_modules/express/node_modules/qs/benchmark.js
  182. +48 −0 examples/simple/node_modules/express/node_modules/qs/examples.js
  183. +2 −0  examples/simple/node_modules/express/node_modules/qs/index.js
  184. +236 −0 examples/simple/node_modules/express/node_modules/qs/lib/querystring.js
  185. +9 −0 examples/simple/node_modules/express/node_modules/qs/package.json
  186. +3 −0  examples/simple/node_modules/express/node_modules/qs/support/expresso/.gitignore
  187. +3 −0  examples/simple/node_modules/express/node_modules/qs/support/expresso/.gitmodules
  188. +128 −0 examples/simple/node_modules/express/node_modules/qs/support/expresso/History.md
  189. +53 −0 examples/simple/node_modules/express/node_modules/qs/support/expresso/Makefile
  190. +61 −0 examples/simple/node_modules/express/node_modules/qs/support/expresso/Readme.md
  191. +856 −0 examples/simple/node_modules/express/node_modules/qs/support/expresso/bin/expresso
  192. +1,080 −0 examples/simple/node_modules/express/node_modules/qs/support/expresso/docs/api.html
  193. +377 −0 examples/simple/node_modules/express/node_modules/qs/support/expresso/docs/index.html
  194. +290 −0 examples/simple/node_modules/express/node_modules/qs/support/expresso/docs/index.md
  195. +3 −0  examples/simple/node_modules/express/node_modules/qs/support/expresso/docs/layout/foot.html
  196. +42 −0 examples/simple/node_modules/express/node_modules/qs/support/expresso/docs/layout/head.html
  197. +4 −0 examples/simple/node_modules/express/node_modules/qs/support/expresso/lib/bar.js
  198. +16 −0 examples/simple/node_modules/express/node_modules/qs/support/expresso/lib/foo.js
  199. +12 −0 examples/simple/node_modules/express/node_modules/qs/support/expresso/package.json
  200. +91 −0 examples/simple/node_modules/express/node_modules/qs/support/expresso/test/assert.test.js
  201. +12 −0 examples/simple/node_modules/express/node_modules/qs/support/expresso/test/async.test.js
  202. +13 −0 examples/simple/node_modules/express/node_modules/qs/support/expresso/test/bar.test.js
  203. +14 −0 examples/simple/node_modules/express/node_modules/qs/support/expresso/test/foo.test.js
  204. +146 −0 examples/simple/node_modules/express/node_modules/qs/support/expresso/test/http.test.js
  205. +39 −0 examples/simple/node_modules/express/node_modules/qs/support/expresso/test/serial/async.test.js
  206. +48 −0 examples/simple/node_modules/express/node_modules/qs/support/expresso/test/serial/http.test.js
  207. +3 −0  examples/simple/node_modules/express/node_modules/qs/support/should/.gitmodules
  208. +22 −0 examples/simple/node_modules/express/node_modules/qs/support/should/History.md
  209. +6 −0 examples/simple/node_modules/express/node_modules/qs/support/should/Makefile
  210. +248 −0 examples/simple/node_modules/express/node_modules/qs/support/should/Readme.md
  211. +53 −0 examples/simple/node_modules/express/node_modules/qs/support/should/examples/runner.js
  212. +2 −0  examples/simple/node_modules/express/node_modules/qs/support/should/index.js
  213. +91 −0 examples/simple/node_modules/express/node_modules/qs/support/should/lib/eql.js
  214. +548 −0 examples/simple/node_modules/express/node_modules/qs/support/should/lib/should.js
  215. +8 −0 examples/simple/node_modules/express/node_modules/qs/support/should/package.json
  216. +358 −0 examples/simple/node_modules/express/node_modules/qs/support/should/test/should.test.js
  217. +156 −0 examples/simple/node_modules/express/node_modules/qs/test/parse.test.js
  218. +95 −0 examples/simple/node_modules/express/node_modules/qs/test/stringify.test.js
  219. +39 −0 examples/simple/node_modules/express/package.json
  220. +17 −0 examples/simple/node_modules/express/testing/index.js
  221. +1 −0  examples/simple/node_modules/express/testing/views/users.jade
  222. +1 −0  examples/simple/node_modules/percept
  223. +9 −0 examples/simple/node_modules/socket.io/.gitignore
  224. +3 −0  examples/simple/node_modules/socket.io/.npmignore
  225. +161 −0 examples/simple/node_modules/socket.io/History.md
  226. +22 −0 examples/simple/node_modules/socket.io/Makefile
  227. +343 −0 examples/simple/node_modules/socket.io/Readme.md
  228. +8 −0 examples/simple/node_modules/socket.io/index.js
  229. +96 −0 examples/simple/node_modules/socket.io/lib/logger.js
  230. +959 −0 examples/simple/node_modules/socket.io/lib/manager.js
  231. +350 −0 examples/simple/node_modules/socket.io/lib/namespace.js
  232. +247 −0 examples/simple/node_modules/socket.io/lib/parser.js
  233. +136 −0 examples/simple/node_modules/socket.io/lib/socket.io.js
  234. +362 −0 examples/simple/node_modules/socket.io/lib/socket.js
  235. +374 −0 examples/simple/node_modules/socket.io/lib/static.js
  236. +98 −0 examples/simple/node_modules/socket.io/lib/store.js
  237. +143 −0 examples/simple/node_modules/socket.io/lib/stores/memory.js
  238. +266 −0 examples/simple/node_modules/socket.io/lib/stores/redis.js
  239. +534 −0 examples/simple/node_modules/socket.io/lib/transport.js
  240. +102 −0 examples/simple/node_modules/socket.io/lib/transports/flashsocket.js
  241. +82 −0 examples/simple/node_modules/socket.io/lib/transports/htmlfile.js
  242. +135 −0 examples/simple/node_modules/socket.io/lib/transports/http-polling.js
  243. +111 −0 examples/simple/node_modules/socket.io/lib/transports/http.js
  244. +12 −0 examples/simple/node_modules/socket.io/lib/transports/index.js
  245. +78 −0 examples/simple/node_modules/socket.io/lib/transports/jsonp-polling.js
  246. +36 −0 examples/simple/node_modules/socket.io/lib/transports/websocket.js
  247. +358 −0 examples/simple/node_modules/socket.io/lib/transports/websocket/default.js
  248. +604 −0 examples/simple/node_modules/socket.io/lib/transports/websocket/hybi-07-12.js
  249. +604 −0 examples/simple/node_modules/socket.io/lib/transports/websocket/hybi-16.js
  250. +11 −0 examples/simple/node_modules/socket.io/lib/transports/websocket/index.js
  251. +72 −0 examples/simple/node_modules/socket.io/lib/transports/xhr-polling.js
  252. +50 −0 examples/simple/node_modules/socket.io/lib/util.js
  253. +30 −0 examples/simple/node_modules/socket.io/package.json
  254. +3 −0  examples/simple/node_modules/underscore/.npmignore
  255. +22 −0 examples/simple/node_modules/underscore/LICENSE
  256. +19 −0 examples/simple/node_modules/underscore/README
  257. +1,698 −0 examples/simple/node_modules/underscore/index.html
  258. +1 −0  examples/simple/node_modules/underscore/index.js
  259. +12 −0 examples/simple/node_modules/underscore/package.json
  260. +29 −0 examples/simple/node_modules/underscore/underscore-min.js
  261. +931 −0 examples/simple/node_modules/underscore/underscore.js
  262. BIN  examples/simple/public/images/character_boy.png
  263. +20 −0 examples/simple/public/index.html
  264. +91 −0 examples/simple/public/javascripts/client/client.js
  265. +47 −0 examples/simple/public/javascripts/my_game.js
  266. +47 −0 examples/simple/public/javascripts/my_game_entity.js
  267. +168 −0 examples/simple/public/javascripts/vendor/jquery-1.4.4.min.js
  268. +1 −0  examples/simple/public/javascripts/vendor/percept.js
  269. +3,707 −0 examples/simple/public/javascripts/vendor/socket.io.js
  270. +2 −0  examples/simple/public/javascripts/vendor/socket.io.min.js
  271. +32 −0 examples/simple/public/javascripts/vendor/sonar_inputmanager.js
  272. +26 −0 examples/simple/public/javascripts/vendor/underscore-1.1.3.min.js
  273. +28 −0 examples/simple/vendor/sonar_inputmanager.coffee
  274. +1 −0  index.coffee
  275. +109 −0 lib/client.coffee
  276. +11 −0 lib/components/event_emitter.coffee
  277. +56 −0 lib/components/lerpable.coffee
  278. +16 −0 lib/message.coffee
  279. +15 −0 lib/percept.coffee
  280. +12 −0 lib/percept_clientside.coffee
  281. +169 −0 lib/server.coffee
  282. +18 −0 package.json
  283. +26 −0 vendor/underscore.min.js
1  .gitignore
@@ -0,0 +1 @@
+*.sw*
3  Guardfile
@@ -0,0 +1,3 @@
+guard 'coffeescript', :output => 'dist/' do
+ watch(%r{(.+\.coffee)})
+end
31 README.md
@@ -0,0 +1,31 @@
+# Percept.js handles the crazy multiplayer network stuff. <br/> You focus on making your damn game.
+
+Inspired by the [Valve Source Multiplayer Networking Whitepaper](http://developer.valvesoftware.com/wiki/Source_Multiplayer_Networking), I set out to implement the ideas using Coffeescript and Node.js.
+
+## This is still really Alpha
+Right now, the documentation is either non-existant, or really lacking. But I'm working on it, because I know that code without documentation (and tests, also currently non-existant) feels ghetto and unsavoury to use.
+
+That said, take a look at examples/simple and see if you can figure things out. It's really pretty cool.
+
+## How you can help
+Right now, what I really need is to learn how to package this in a way that's elegant and will play nicely in the browser and server-side in Node.js. If you want to mentor me here, _please_ [get in touch](http://twitter.com/gabehollombe).
+
+Maybe that means CommonJS modules, or RequireJS, or something else. For now, I have an NPM package for the server-side and a ghetto build task in the Cakefile to compile the client-side. I don't like it, but it works for now. Remember, this is way alpha.
+
+## TODO
+- Documentation
+- Tests
+- Server-side rewinding/replaying events (right now we only broadcast events out to clients as we get them)
+- Optimization
+ - Circular buffers for states on entities
+
+## License
+(The MIT License)
+
+Copyright (c) 2011 Gabe Hollombe <gabe@avantbard.com>
+
+Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the 'Software'), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
2  bin/run_server
@@ -0,0 +1,2 @@
+#!/bin/sh
+./node_modules/coffee-script/bin/coffee server.coffee
32 build/Cakefile
@@ -0,0 +1,32 @@
+fs = require 'fs'
+{exec} = require 'child_process'
+
+client_files = [
+ 'components/event_emitter'
+ 'components/lerpable'
+ 'message'
+ 'client'
+ 'percept_clientside'
+]
+
+input_path = '../lib'
+output_path = '../dist'
+output_file = 'percept' #build will auto add .js
+
+task 'build', 'Build a percept.js file suitable for client-side in-browser usage.', ->
+ appContents = new Array remaining = client_files.length
+ for file, index in client_files then do (file, index) ->
+ fs.readFile "#{input_path}/#{file}.coffee", 'utf8', (err, fileContents) ->
+ throw err if err
+ appContents[index] = fileContents
+ process() if --remaining is 0
+ process = ->
+ fs.writeFile "#{output_path}/#{output_file}.coffee", appContents.join('\n\n'), 'utf8', (err) ->
+ throw err if err
+ exec "coffee --compile #{output_path}/#{output_file}.coffee", (err, stdout, stderr) ->
+ throw err if err
+ console.log stdout + stderr
+ fs.unlink "#{output_path}/#{output_file}.coffee", (err) ->
+ throw err if err
+ console.log 'Done.'
+
129 dist/lib/client.js
@@ -0,0 +1,129 @@
+(function() {
+ var Client, exports;
+ var __bind = function(fn, me){ return function(){ return fn.apply(me, arguments); }; };
+ exports = this;
+ exports.Client = Client = (function() {
+ function Client(args) {
+ this.args = args;
+ this.LERP = 100;
+ this.update_messages_by_entity_id = {};
+ this.update_entities_messages = [];
+ this.queued_messages = [];
+ this.entities_by_id = {};
+ this.player_entity_id = null;
+ this.next_player_command_id = 0;
+ this.unacknowledged_player_commands = [];
+ this.last_acked_player_state = null;
+ this.socket = io.connect('http://localhost', {
+ 'force new connection': true,
+ reconnect: false
+ });
+ this.socket.on('connect', function() {
+ return console.log('connected');
+ });
+ this.socket.on('message', __bind(function(data) {
+ var msg;
+ msg = JSON.parse(data);
+ return this.queued_messages.push(msg);
+ }, this));
+ this.socket.on('disconnect', function() {
+ return console.log('disconnected');
+ });
+ }
+ Client.prototype.new_frame = function() {
+ var entity, id, messages_to_handle, msg, player, prop, val, _i, _j, _len, _len2, _ref, _ref2, _ref3, _ref4;
+ _ref = [this.queued_messages, []], messages_to_handle = _ref[0], this.queued_messages = _ref[1];
+ for (_i = 0, _len = messages_to_handle.length; _i < _len; _i++) {
+ msg = messages_to_handle[_i];
+ this.handle_message(msg);
+ }
+ player = this.player_entity;
+ _ref2 = this.last_acked_player_state;
+ for (prop in _ref2) {
+ val = _ref2[prop];
+ player[prop] = val;
+ }
+ _ref3 = this.unacknowledged_player_commands;
+ for (_j = 0, _len2 = _ref3.length; _j < _len2; _j++) {
+ msg = _ref3[_j];
+ player[msg.command](msg.args);
+ }
+ _ref4 = this.entities_by_id;
+ for (id in _ref4) {
+ entity = _ref4[id];
+ if (id !== this.player_entity_id) {
+ entity.lerp_update(this.render_time);
+ }
+ }
+ return this.render_time = new Date().getTime() - this.gametime_offset - this.LERP;
+ };
+ Client.prototype.handle_message = function(message) {
+ var entity, prop, val, _ref;
+ switch (message.message_type) {
+ case Percept.Message.MESSAGE_TYPES.ACK:
+ return this.on_ack(message);
+ case Percept.Message.MESSAGE_TYPES.GAME_INIT:
+ this.player_entity_id = message.your_entity_id;
+ entity = this.args.create_entity(message.your_entity_id, message.your_entity_state);
+ entity.id = message.your_entity_id;
+ this.entities_by_id[entity.id] = entity;
+ this.player_entity = entity;
+ console.log("my ent id " + entity.id);
+ _ref = message.your_entity_state;
+ for (prop in _ref) {
+ val = _ref[prop];
+ entity[prop] = val;
+ }
+ this.last_acked_player_state = message.your_entity_state;
+ this.gametime_offset = new Date().getTime() - message.at_gametime;
+ return this.render_time = message.at_gametime - this.LERP - this.FAKE_LAG;
+ case Percept.Message.MESSAGE_TYPES.UPDATE_ENTITIES:
+ return this.update_entities_states(message);
+ case Percept.Message.REMOVE_ENTITY:
+ return this.remove_entity(message.entity_id);
+ }
+ };
+ Client.prototype.send_message = function(msg) {
+ return this.socket.send(JSON.stringify(msg));
+ };
+ Client.prototype.update_entities_states = function(msg) {
+ var entity, id, state, _ref, _results;
+ _ref = msg.entity_states_by_entity_id;
+ _results = [];
+ for (id in _ref) {
+ state = _ref[id];
+ entity = this.entities_by_id[id];
+ if (!entity) {
+ entity = this.args.create_entity(id, state);
+ this.entities_by_id[id] = entity;
+ }
+ state.gametime = msg.at_gametime;
+ _results.push(entity._received_states.push(state));
+ }
+ return _results;
+ };
+ Client.prototype.remove_entity = function(id) {
+ this.entities_by_id[id].destroy();
+ return delete this.entities_by_id[id];
+ };
+ Client.prototype.on_ack = function(message) {
+ var cmd;
+ if (message.command_id) {
+ this.unacknowledged_player_commands = (function() {
+ var _i, _len, _ref, _results;
+ _ref = this.unacknowledged_player_commands;
+ _results = [];
+ for (_i = 0, _len = _ref.length; _i < _len; _i++) {
+ cmd = _ref[_i];
+ if (cmd.command_id > message.command_id) {
+ _results.push(cmd);
+ }
+ }
+ return _results;
+ }).call(this);
+ return this.last_acked_player_state = message.entity_state;
+ }
+ };
+ return Client;
+ })();
+}).call(this);
27 dist/lib/components/event_emitter.js
@@ -0,0 +1,27 @@
+(function() {
+ var exports;
+ var __slice = Array.prototype.slice;
+ exports = this;
+ exports.EventEmitter = function() {
+ return {
+ _events: {},
+ on: function(event_name, callback) {
+ var _base;
+ return ((_base = this._events)[event_name] || (_base[event_name] = [])).push(callback);
+ },
+ emit: function() {
+ var args, event_name, handler, _i, _len, _ref, _results;
+ event_name = arguments[0], args = 2 <= arguments.length ? __slice.call(arguments, 1) : [];
+ if (this._events[event_name]) {
+ _ref = this._events[event_name];
+ _results = [];
+ for (_i = 0, _len = _ref.length; _i < _len; _i++) {
+ handler = _ref[_i];
+ _results.push(handler.apply(this, args));
+ }
+ return _results;
+ }
+ }
+ };
+ };
+}).call(this);
62 dist/lib/components/lerpable.js
@@ -0,0 +1,62 @@
+(function() {
+ var exports;
+ exports = this;
+ exports.Lerpable = function() {
+ return {
+ _received_states: [],
+ lerp_update: function(render_time) {
+ var distance_too_great, distance_xy, dont_interpolate, from_state, interpolation_percentage, max_interpolation_distance, offset_time, prop, self, time_between_states, to_state, val, _ref;
+ _ref = this.get_states_for_lerping(render_time), from_state = _ref[0], to_state = _ref[1];
+ self = this;
+ for (prop in from_state) {
+ val = from_state[prop];
+ self[prop] = val;
+ }
+ if (!((from_state != null) && (to_state != null))) {
+ return;
+ }
+ max_interpolation_distance = 300;
+ distance_xy = [Math.abs(to_state.x - from_state.x), Math.abs(to_state.y - from_state.y)];
+ if (distance_xy[0] > max_interpolation_distance || distance_xy[1] > max_interpolation_distance) {
+ distance_too_great = true;
+ }
+ if (distance_too_great) {
+ dont_interpolate = true;
+ }
+ if (dont_interpolate) {
+ return interpolation_percentage = 1.0;
+ } else {
+ offset_time = render_time - from_state.gametime;
+ time_between_states = to_state.gametime - from_state.gametime;
+ interpolation_percentage = (offset_time / time_between_states) * 1.0;
+ if (interpolation_percentage < 0.0) {
+ interpolation_percentage = 0;
+ }
+ if (interpolation_percentage > 1.0) {
+ interpolation_percentage = 1;
+ }
+ this.x += Math.round((to_state.x - from_state.x) * interpolation_percentage);
+ return this.y += Math.round((to_state.y - from_state.y) * interpolation_percentage);
+ }
+ },
+ get_states_for_lerping: function(render_time) {
+ var from_state, index, state, to_state, _len, _ref;
+ from_state = null;
+ to_state = null;
+ _ref = this._received_states;
+ for (index = 0, _len = _ref.length; index < _len; index++) {
+ state = _ref[index];
+ if (state.gametime > render_time) {
+ to_state = state;
+ from_state = this._received_states[index - 1];
+ break;
+ }
+ }
+ if (to_state && !from_state) {
+ from_state = to_state;
+ }
+ return [from_state, to_state];
+ }
+ };
+ };
+}).call(this);
23 dist/lib/message.js
@@ -0,0 +1,23 @@
+(function() {
+ var Message, exports;
+ exports = this;
+ exports.Message = Message = (function() {
+ Message.MESSAGE_TYPES = {
+ NEW_PLAYER: 1,
+ ACK: 2,
+ UPDATE_ENTITY: 3,
+ PLAYER_COMMAND: 4,
+ GAME_INIT: 5,
+ UPDATE_ENTITIES: 6,
+ REMOVE_ENTITY: 7
+ };
+ function Message(attrs) {
+ var prop, val;
+ for (prop in attrs) {
+ val = attrs[prop];
+ this[prop] = val;
+ }
+ }
+ return Message;
+ })();
+}).call(this);
7 dist/lib/percept.js
@@ -0,0 +1,7 @@
+(function() {
+ exports.EventEmitter = (require('./components/event_emitter')).EventEmitter;
+ exports.Lerpable = (require('./components/lerpable')).Lerpable;
+ exports.Message = (require('./message')).Message;
+ exports.Client = (require('./client')).Client;
+ exports.Server = (require('./server')).Server;
+}).call(this);
193 dist/lib/server.js
@@ -0,0 +1,193 @@
+(function() {
+ var Server, exports;
+ var __bind = function(fn, me){ return function(){ return fn.apply(me, arguments); }; };
+ exports = this;
+ exports.Server = Server = (function() {
+ function Server(args) {
+ var io;
+ this.args = args != null ? args : {};
+ this.queue_entity_state_change = __bind(this.queue_entity_state_change, this);
+ this.on_game_tick_complete = __bind(this.on_game_tick_complete, this);
+ io = this.args.io;
+ this.sockets_by_id = {};
+ this.queued_messages_for_socket_id = {};
+ this.changed_entity_states_by_entity_id = {};
+ this.game = this.args.game;
+ this.game.on('entity_created', __bind(function(entity) {
+ return _.extend(entity, new Lerpable());
+ }, this));
+ this.game.on('entity_changed', __bind(function(changed_state) {
+ return this.queue_entity_state_change(changed_state);
+ }, this));
+ this.game.on('tick', __bind(function(args) {
+ return this.on_game_tick_complete({
+ gametime: args.gametime
+ });
+ }, this));
+ io.sockets.on('connection', __bind(function(socket) {
+ return this.on_connect(socket);
+ }, this));
+ }
+ Server.prototype.on_connect = function(socket) {
+ var player;
+ player = this.game.add_player(socket.id);
+ socket.player = player;
+ player.socket = socket;
+ this.sockets_by_id[socket.id] = socket;
+ this.send_init_game_info_to_socket(socket);
+ this.send_all_entities_to_socket(socket);
+ this.send_new_player_to_other_sockets(player);
+ socket.on('message', __bind(function(message) {
+ return this.on_message(socket, message);
+ }, this));
+ return socket.on('disconnect', __bind(function() {
+ return this.on_disconnect(socket);
+ }, this));
+ };
+ Server.prototype.on_message = function(socket, message) {
+ var msg;
+ this.log("IN: " + message);
+ msg = JSON.parse(message);
+ return this.handle_message(socket, msg);
+ };
+ Server.prototype.on_disconnect = function(socket) {
+ var msg;
+ this.game.remove_player(socket.player.id);
+ delete this.sockets_by_id[socket.id];
+ delete this.queued_messages_for_socket_id[socket.id];
+ delete this.changed_entity_states_by_entity_id[socket.player.id];
+ msg = new Message({
+ message_type: Message.REMOVE_ENTITY,
+ entity_id: socket.player.id
+ });
+ return this.send_message_to_all_except_socket(msg, socket);
+ };
+ Server.prototype.handle_message = function(socket, msg) {
+ var state;
+ switch (msg.message_type) {
+ case Message.MESSAGE_TYPES.ACK:
+ return this.on_ack(socket, msg);
+ case Message.MESSAGE_TYPES.PLAYER_COMMAND:
+ socket.player[msg.command](msg.args);
+ state = socket.player.get_state();
+ msg = new Message({
+ message_type: Message.MESSAGE_TYPES.ACK,
+ command_id: msg.command_id,
+ recieved_at: new Date().getTime(),
+ entity_state: state
+ });
+ return this.queue_message_for_socket(msg, socket);
+ }
+ };
+ Server.prototype.queue_message_for_socket = function(msg, socket) {
+ var _base, _name;
+ return ((_base = this.queued_messages_for_socket_id)[_name = socket.id] || (_base[_name] = [])).push(msg);
+ };
+ Server.prototype.send_message_to_socket = function(msg, socket) {
+ if (!socket) {
+ return;
+ }
+ msg = JSON.stringify(msg);
+ socket.send(msg);
+ return this.log("OUT: " + msg);
+ };
+ Server.prototype.send_all_entities_to_socket = function(socket) {
+ var entity, entity_id, entity_states_by_entity_id, msg, _ref;
+ entity_states_by_entity_id = {};
+ _ref = this.game.entities_by_id;
+ for (entity_id in _ref) {
+ entity = _ref[entity_id];
+ entity_states_by_entity_id[entity_id] = entity.get_state();
+ }
+ msg = new Message({
+ message_type: Message.MESSAGE_TYPES.UPDATE_ENTITIES,
+ at_gametime: this.game.gametime,
+ entity_states_by_entity_id: entity_states_by_entity_id
+ });
+ return this.queue_message_for_socket(msg, socket);
+ };
+ Server.prototype.send_init_game_info_to_socket = function(socket) {
+ var msg;
+ msg = new Message({
+ at_gametime: this.game.gametime,
+ message_type: Message.MESSAGE_TYPES.GAME_INIT,
+ your_entity_id: socket.player.id,
+ your_entity_state: socket.player.get_state()
+ });
+ return this.queue_message_for_socket(msg, socket);
+ };
+ Server.prototype.send_new_player_to_other_sockets = function(player) {
+ var changed_state, id, socket, _ref, _results;
+ _ref = this.sockets_by_id;
+ _results = [];
+ for (id in _ref) {
+ socket = _ref[id];
+ if (socket !== player.socket) {
+ changed_state = {
+ entity_id: player.id,
+ entity_state: player.get_state()
+ };
+ _results.push(this.queue_entity_state_change(changed_state));
+ }
+ }
+ return _results;
+ };
+ Server.prototype.send_message_to_all_except_socket = function(msg, except_socket) {
+ var id, socket, _ref, _results;
+ _ref = this.sockets_by_id;
+ _results = [];
+ for (id in _ref) {
+ socket = _ref[id];
+ if (socket !== except_socket) {
+ _results.push(this.send_message_to_socket(msg, socket));
+ }
+ }
+ return _results;
+ };
+ Server.prototype.on_game_tick_complete = function(args) {
+ var id, message, messages_to_send, msg, queued_messages, socket, socket_id, _i, _len, _ref, _ref2, _ref3, _results;
+ _ref = this.queued_messages_for_socket_id;
+ for (socket_id in _ref) {
+ queued_messages = _ref[socket_id];
+ socket = this.sockets_by_id[socket_id];
+ _ref2 = [queued_messages, []], messages_to_send = _ref2[0], this.queued_messages_for_socket_id[socket_id] = _ref2[1];
+ for (_i = 0, _len = messages_to_send.length; _i < _len; _i++) {
+ message = messages_to_send[_i];
+ this.send_message_to_socket(message, socket);
+ }
+ }
+ if (this.changed_entity_states) {
+ msg = new Message({
+ message_type: Message.MESSAGE_TYPES.UPDATE_ENTITIES,
+ at_gametime: args.gametime,
+ entity_states_by_entity_id: this.changed_entity_states_by_entity_id
+ });
+ this.changed_entity_states_by_entity_id = {};
+ this.changed_entity_states = false;
+ _ref3 = this.sockets_by_id;
+ _results = [];
+ for (id in _ref3) {
+ socket = _ref3[id];
+ _results.push(this.send_message_to_socket(msg, socket));
+ }
+ return _results;
+ }
+ };
+ Server.prototype.queue_entity_state_change = function(changed_entity_state) {
+ var changed_prop, changed_value, merged_entity_state, _base, _name, _ref;
+ merged_entity_state = ((_base = this.changed_entity_states_by_entity_id)[_name = changed_entity_state.entity_id] || (_base[_name] = {
+ entity_id: changed_entity_state.entity_id
+ }));
+ _ref = changed_entity_state.entity_state;
+ for (changed_prop in _ref) {
+ changed_value = _ref[changed_prop];
+ merged_entity_state[changed_prop] = changed_value;
+ }
+ return this.changed_entity_states = true;
+ };
+ Server.prototype.log = function(data) {
+ return sys.log(data);
+ };
+ return Server;
+ })();
+}).call(this);
240 dist/percept.js
@@ -0,0 +1,240 @@
+(function() {
+ var Client, Message, exports;
+ var __slice = Array.prototype.slice, __bind = function(fn, me){ return function(){ return fn.apply(me, arguments); }; };
+ exports = this;
+ exports.EventEmitter = function() {
+ return {
+ _events: {},
+ on: function(event_name, callback) {
+ var _base;
+ return ((_base = this._events)[event_name] || (_base[event_name] = [])).push(callback);
+ },
+ emit: function() {
+ var args, event_name, handler, _i, _len, _ref, _results;
+ event_name = arguments[0], args = 2 <= arguments.length ? __slice.call(arguments, 1) : [];
+ if (this._events[event_name]) {
+ _ref = this._events[event_name];
+ _results = [];
+ for (_i = 0, _len = _ref.length; _i < _len; _i++) {
+ handler = _ref[_i];
+ _results.push(handler.apply(this, args));
+ }
+ return _results;
+ }
+ }
+ };
+ };
+ exports = this;
+ exports.Lerpable = function() {
+ return {
+ _received_states: [],
+ lerp_update: function(render_time) {
+ var distance_too_great, distance_xy, dont_interpolate, from_state, interpolation_percentage, max_interpolation_distance, offset_time, prop, self, time_between_states, to_state, val, _ref;
+ _ref = this.get_states_for_lerping(render_time), from_state = _ref[0], to_state = _ref[1];
+ self = this;
+ for (prop in from_state) {
+ val = from_state[prop];
+ self[prop] = val;
+ }
+ if (!((from_state != null) && (to_state != null))) {
+ return;
+ }
+ max_interpolation_distance = 300;
+ distance_xy = [Math.abs(to_state.x - from_state.x), Math.abs(to_state.y - from_state.y)];
+ if (distance_xy[0] > max_interpolation_distance || distance_xy[1] > max_interpolation_distance) {
+ distance_too_great = true;
+ }
+ if (distance_too_great) {
+ dont_interpolate = true;
+ }
+ if (dont_interpolate) {
+ return interpolation_percentage = 1.0;
+ } else {
+ offset_time = render_time - from_state.gametime;
+ time_between_states = to_state.gametime - from_state.gametime;
+ interpolation_percentage = (offset_time / time_between_states) * 1.0;
+ if (interpolation_percentage < 0.0) {
+ interpolation_percentage = 0;
+ }
+ if (interpolation_percentage > 1.0) {
+ interpolation_percentage = 1;
+ }
+ this.x += Math.round((to_state.x - from_state.x) * interpolation_percentage);
+ return this.y += Math.round((to_state.y - from_state.y) * interpolation_percentage);
+ }
+ },
+ get_states_for_lerping: function(render_time) {
+ var from_state, index, state, to_state, _len, _ref;
+ from_state = null;
+ to_state = null;
+ _ref = this._received_states;
+ for (index = 0, _len = _ref.length; index < _len; index++) {
+ state = _ref[index];
+ if (state.gametime > render_time) {
+ to_state = state;
+ from_state = this._received_states[index - 1];
+ break;
+ }
+ }
+ if (to_state && !from_state) {
+ from_state = to_state;
+ }
+ return [from_state, to_state];
+ }
+ };
+ };
+ exports = this;
+ exports.Message = Message = (function() {
+ Message.MESSAGE_TYPES = {
+ NEW_PLAYER: 1,
+ ACK: 2,
+ UPDATE_ENTITY: 3,
+ PLAYER_COMMAND: 4,
+ GAME_INIT: 5,
+ UPDATE_ENTITIES: 6,
+ REMOVE_ENTITY: 7
+ };
+ function Message(attrs) {
+ var prop, val;
+ for (prop in attrs) {
+ val = attrs[prop];
+ this[prop] = val;
+ }
+ }
+ return Message;
+ })();
+ exports = this;
+ exports.Client = Client = (function() {
+ function Client(args) {
+ this.args = args;
+ this.LERP = 100;
+ this.update_messages_by_entity_id = {};
+ this.update_entities_messages = [];
+ this.queued_messages = [];
+ this.entities_by_id = {};
+ this.player_entity_id = null;
+ this.next_player_command_id = 0;
+ this.unacknowledged_player_commands = [];
+ this.last_acked_player_state = null;
+ this.socket = io.connect('http://localhost', {
+ 'force new connection': true,
+ reconnect: false
+ });
+ this.socket.on('connect', function() {
+ return console.log('connected');
+ });
+ this.socket.on('message', __bind(function(data) {
+ var msg;
+ msg = JSON.parse(data);
+ return this.queued_messages.push(msg);
+ }, this));
+ this.socket.on('disconnect', function() {
+ return console.log('disconnected');
+ });
+ }
+ Client.prototype.new_frame = function() {
+ var entity, id, messages_to_handle, msg, player, prop, val, _i, _j, _len, _len2, _ref, _ref2, _ref3, _ref4;
+ _ref = [this.queued_messages, []], messages_to_handle = _ref[0], this.queued_messages = _ref[1];
+ for (_i = 0, _len = messages_to_handle.length; _i < _len; _i++) {
+ msg = messages_to_handle[_i];
+ this.handle_message(msg);
+ }
+ player = this.player_entity;
+ _ref2 = this.last_acked_player_state;
+ for (prop in _ref2) {
+ val = _ref2[prop];
+ player[prop] = val;
+ }
+ _ref3 = this.unacknowledged_player_commands;
+ for (_j = 0, _len2 = _ref3.length; _j < _len2; _j++) {
+ msg = _ref3[_j];
+ player[msg.command](msg.args);
+ }
+ _ref4 = this.entities_by_id;
+ for (id in _ref4) {
+ entity = _ref4[id];
+ if (id !== this.player_entity_id) {
+ entity.lerp_update(this.render_time);
+ }
+ }
+ return this.render_time = new Date().getTime() - this.gametime_offset - this.LERP;
+ };
+ Client.prototype.handle_message = function(message) {
+ var entity, prop, val, _ref;
+ switch (message.message_type) {
+ case Percept.Message.MESSAGE_TYPES.ACK:
+ return this.on_ack(message);
+ case Percept.Message.MESSAGE_TYPES.GAME_INIT:
+ this.player_entity_id = message.your_entity_id;
+ entity = this.args.create_entity(message.your_entity_id, message.your_entity_state);
+ entity.id = message.your_entity_id;
+ this.entities_by_id[entity.id] = entity;
+ this.player_entity = entity;
+ console.log("my ent id " + entity.id);
+ _ref = message.your_entity_state;
+ for (prop in _ref) {
+ val = _ref[prop];
+ entity[prop] = val;
+ }
+ this.last_acked_player_state = message.your_entity_state;
+ this.gametime_offset = new Date().getTime() - message.at_gametime;
+ return this.render_time = message.at_gametime - this.LERP - this.FAKE_LAG;
+ case Percept.Message.MESSAGE_TYPES.UPDATE_ENTITIES:
+ return this.update_entities_states(message);
+ case Percept.Message.REMOVE_ENTITY:
+ return this.remove_entity(message.entity_id);
+ }
+ };
+ Client.prototype.send_message = function(msg) {
+ return this.socket.send(JSON.stringify(msg));
+ };
+ Client.prototype.update_entities_states = function(msg) {
+ var entity, id, state, _ref, _results;
+ _ref = msg.entity_states_by_entity_id;
+ _results = [];
+ for (id in _ref) {
+ state = _ref[id];
+ entity = this.entities_by_id[id];
+ if (!entity) {
+ entity = this.args.create_entity(id, state);
+ this.entities_by_id[id] = entity;
+ }
+ state.gametime = msg.at_gametime;
+ _results.push(entity._received_states.push(state));
+ }
+ return _results;
+ };
+ Client.prototype.remove_entity = function(id) {
+ var entity;
+ entity = this.entities_by_id[id];
+ delete this.entities_by_id[id];
+ return this.args.destroy_entity(entity);
+ };
+ Client.prototype.on_ack = function(message) {
+ var cmd;
+ if (message.command_id) {
+ this.unacknowledged_player_commands = (function() {
+ var _i, _len, _ref, _results;
+ _ref = this.unacknowledged_player_commands;
+ _results = [];
+ for (_i = 0, _len = _ref.length; _i < _len; _i++) {
+ cmd = _ref[_i];
+ if (cmd.command_id > message.command_id) {
+ _results.push(cmd);
+ }
+ }
+ return _results;
+ }).call(this);
+ return this.last_acked_player_state = message.entity_state;
+ }
+ };
+ return Client;
+ })();
+ exports = this;
+ exports.Percept = {
+ EventEmitter: EventEmitter,
+ Lerpable: Lerpable,
+ Message: Message,
+ Client: Client
+ };
+}).call(this);
5 examples/simple/Guardfile
@@ -0,0 +1,5 @@
+# A sample Guardfile
+# More info at https://github.com/guard/guard#readme
+guard 'coffeescript', :output => 'public/javascripts' do
+ watch(%r{(.+\.coffee)})
+end
81 examples/simple/client/client.coffee
@@ -0,0 +1,81 @@
+window.requestAnimationFrame = (->
+ window.requestAnimationFrame or window.webkitRequestAnimationFrame or window.mozRequestAnimationFrame or window.oRequestAnimationFrame or window.msRequestAnimationFrame or ((callback, element) -> window.setTimeout callback, 1000 / 60)
+)()
+
+
+window.client =
+ init: ->
+ create_entity = (id, state) ->
+ # Make a new instance of the MyGameEntity class that we share between
+ # client and server, then add in our client-side dom-specific
+ # rendering stuff (an image, so we have something to move around when we get updates)
+ e = new window.MyGameEntity()
+
+ # Add in an image attr which we'll use to render
+ e.img = '/images/character_boy.png'
+
+ # You MUST give each entity an instance of the Percept.Lerpable properties
+ _.extend(e, new Percept.Lerpable())
+
+ # Make this in the DOM
+ e.dom_node = $("<img src='#{e.img}'>").css({position: 'absolute'; top: "#{e.x}px", left: "#{e.y}px"}).appendTo('body')
+
+ return e
+
+ destroy_entity = (entity) -> $(entity.dom_node).remove()
+
+ # Here's where the network magic happens:
+ # Whenever the server-side game ticks for an update, it sends deltas down the wire to each client.
+ # The Percept Client calls lerp_update() on each entity that changed.
+ @percept_client = new Percept.Client
+ # You MUST provide callbacks to your client code to create and destroy entities.
+ create_entity: create_entity
+ destroy_entity: destroy_entity
+
+ @input = new window.InputManager()
+
+ poll_input: ->
+ # Update our input manager
+ @input.update()
+
+ # Send player movement commands on keyboard input
+ movement_keys = {
+ up: 87 #w
+ down: 83 #s
+ left: 65 #a
+ right: 68 #d
+ }
+
+ dir = null
+ dir = 'up' if @input.isKeyDown(movement_keys.up)
+ dir = 'down' if @input.isKeyDown(movement_keys.down)
+ dir = 'left' if @input.isKeyDown(movement_keys.left)
+ dir = 'right' if @input.isKeyDown(movement_keys.right)
+
+ if dir?
+ [command_id, @percept_client.next_player_command_id] = [@percept_client.next_player_command_id, @percept_client.next_player_command_id + 1]
+ msg = new Percept.Message
+ message_type: Percept.Message.MESSAGE_TYPES.PLAYER_COMMAND
+ command: 'move'
+ command_id: command_id
+ args: { dir: dir }
+
+ # Store this command locally for replays until the server acknowledges handling it
+ @percept_client.unacknowledged_player_commands.push msg
+
+ # And, of course, send the command to the server
+ @percept_client.send_message(msg)
+
+ redraw_entities: ->
+ ($(e.dom_node).css(top: e.y, left: e.x)) for id, e of @percept_client.entities_by_id
+
+
+window.render_frame = (time) ->
+ window.requestAnimationFrame render_frame
+ window.client.poll_input()
+ window.client.percept_client.new_frame()
+ window.client.redraw_entities()
+
+$ ->
+ window.client.init()
+ window.render_frame()
39 examples/simple/my_game.coffee
@@ -0,0 +1,39 @@
+exports = this
+
+sys = require 'sys'
+_ = require('underscore')
+
+percept = require('percept')
+event_emitter = percept.EventEmitter
+Entity = require('./my_game_entity').MyGameEntity
+
+
+exports.MyGame = class MyGame
+ constructor: (@args={}) ->
+ _.extend(this, new event_emitter())
+ @entities_by_id = {}
+ @tick = 0
+ @gametime = new Date().getTime() #this gets updated with each tick
+ @start_ticking()
+
+
+ add_player: (id) ->
+ player = new Entity id: id
+ @entities_by_id[id] = player
+ # Your game MUST emit an entity_changed event whenever an entity's state changes, if you want that to be replicated to all connected players.
+ player.on 'changed', (changed_state) => @emit('entity_changed', changed_state)
+ @emit 'entity_created', player
+ player
+
+
+ remove_player: (id) ->
+ delete @entities_by_id[id]
+
+
+ start_ticking: () ->
+ @tick_interval = setInterval (=> @do_tick()), (1000 / 20)
+
+
+ do_tick: () ->
+ @gametime = new Date().getTime()
+ @emit 'tick', gametime: @gametime
35 examples/simple/my_game_entity.coffee
@@ -0,0 +1,35 @@
+exports = this
+
+# Ugly hack using || to use existing vars (client-side via this., which maps to window) or require them (server-side)
+_ = this._ || require('underscore')
+percept = this.Percept || require('percept')
+event_emitter = percept.EventEmitter
+
+
+exports.MyGameEntity = class MyGameEntity
+ constructor: (@args={}) ->
+ _.extend(this, new event_emitter())
+ @id = @args.id
+ @speed = 3
+ @x = @args.x? || 0
+ @y = @args.y? || 0
+
+
+ # PlayerCommand messages sent by players will be passed directly to the player's associated
+ # entity object, so you'll probably want something to handle movement. =-)
+ move: (args) ->
+ switch args.dir
+ when 'up' then @y -= @speed
+ when 'down' then @y += @speed
+ when 'left' then @x -= @speed
+ when 'right' then @x += @speed
+
+ # Entities MUST emit a changed event when they change
+ @emit 'changed', entity_id: @id, entity_state: {x: @x, y: @y}
+
+
+ # Entities MUST have a get_state() method that returns an object suitable for replication to connected clients
+ get_state: ->
+ id: @id
+ x: @x
+ y: @y
28 examples/simple/my_game_server.coffee
@@ -0,0 +1,28 @@
+coffee = require('coffee-script')
+http = require('http')
+url = require('url')
+fs = require('fs')
+express = require('express')
+io = require('socket.io')
+path = require('path')
+sys = require('sys')
+
+percept = require('percept')
+
+my_game = require('./my_game')
+
+HOSTNAME = 'localhost'
+WEBROOT = path.join(path.dirname(__filename), '.')
+
+web = express.createServer()
+io = io.listen(web)
+web.use(express.static(__dirname + '/public'))
+web.use(express.errorHandler({ dumpExceptions: true, showStack: true }))
+web.use(express.logger({ format: ':method :url' }))
+web.listen(8000)
+
+
+game = new my_game.MyGame()
+server = new percept.Server
+ io: io
+ game: game
418 examples/simple/node_modules/.bin/express
@@ -0,0 +1,418 @@
+#!/usr/bin/env node
+
+/**
+ * Module dependencies.
+ */
+
+var fs = require('fs')
+ , exec = require('child_process').exec
+ , mkdirp = require('mkdirp');
+
+/**
+ * Framework version.
+ */
+
+var version = '2.4.7';
+
+/**
+ * Add session support.
+ */
+
+var sessions = false;
+
+/**
+ * CSS engine to utilize.
+ */
+
+var cssEngine;
+
+/**
+ * Template engine to utilize.
+ */
+
+var templateEngine = 'jade';
+
+/**
+ * Usage documentation.
+ */
+
+var usage = ''
+ + '\n'
+ + ' Usage: express [options] [path]\n'
+ + '\n'
+ + ' Options:\n'
+ + ' -s, --sessions add session support\n'
+ + ' -t, --template <engine> add template <engine> support (jade|ejs). default=jade\n'
+ + ' -c, --css <engine> add stylesheet <engine> support (less|sass|stylus). default=plain css\n'
+ + ' -v, --version output framework version\n'
+ + ' -h, --help output help information\n'
+ ;
+
+/**
+ * Jade layout template.
+ */
+
+var jadeLayout = [
+ '!!!'
+ , 'html'
+ , ' head'
+ , ' title= title'
+ , ' link(rel=\'stylesheet\', href=\'/stylesheets/style.css\')'
+ , ' body!= body'
+].join('\n');
+
+/**
+ * Jade index template.
+ */
+
+var jadeIndex = [
+ 'h1= title'
+ , 'p Welcome to #{title}'
+].join('\n');
+
+/**
+ * EJS layout template.
+ */
+
+var ejsLayout = [
+ '<!DOCTYPE html>'
+ , '<html>'
+ , ' <head>'
+ , ' <title><%= title %></title>'
+ , ' <link rel=\'stylesheet\' href=\'/stylesheets/style.css\' />'
+ , ' </head>'
+ , ' <body>'
+ , ' <%- body %>'
+ , ' </body>'
+ , '</html>'
+].join('\n');
+
+/**
+ * EJS index template.
+ */
+
+var ejsIndex = [
+ '<h1><%= title %></h1>'
+ , '<p>Welcome to <%= title %></p>'
+ ].join('\n');
+
+/**
+ * Default css template.
+ */
+
+var css = [
+ 'body {'
+ , ' padding: 50px;'
+ , ' font: 14px "Lucida Grande", Helvetica, Arial, sans-serif;'
+ , '}'
+ , ''
+ , 'a {'
+ , ' color: #00B7FF;'
+ , '}'
+].join('\n');
+
+/**
+ * Default less template.
+ */
+
+var less = [
+ 'body {'
+ , ' padding: 50px;'
+ , ' font: 14px "Lucida Grande", Helvetica, Arial, sans-serif;'
+ , '}'
+ , ''
+ , 'a {'
+ , ' color: #00B7FF;'
+ , '}'
+].join('\n');
+
+/**
+ * Default sass template.
+ */
+
+var sass = [
+ 'body'
+ , ' :padding 50px'
+ , ' :font 14px "Lucida Grande", Helvetica, Arial, sans-serif'
+ , 'a'
+ , ' :color #00B7FF'
+].join('\n');
+
+/**
+ * Default stylus template.
+ */
+
+var stylus = [
+ 'body'
+ , ' padding: 50px'
+ , ' font: 14px "Lucida Grande", Helvetica, Arial, sans-serif'
+ , 'a'
+ , ' color: #00B7FF'
+].join('\n');
+
+/**
+ * App template.
+ */
+
+var app = [
+ ''
+ , '/**'
+ , ' * Module dependencies.'
+ , ' */'
+ , ''
+ , 'var express = require(\'express\');'
+ , ''
+ , 'var app = module.exports = express.createServer();'
+ , ''
+ , '// Configuration'
+ , ''
+ , 'app.configure(function(){'
+ , ' app.set(\'views\', __dirname + \'/views\');'
+ , ' app.set(\'view engine\', \':TEMPLATE\');'
+ , ' app.use(express.bodyParser());'
+ , ' app.use(express.methodOverride());{sess}{css}'
+ , ' app.use(app.router);'
+ , ' app.use(express.static(__dirname + \'/public\'));'
+ , '});'
+ , ''
+ , 'app.configure(\'development\', function(){'
+ , ' app.use(express.errorHandler({ dumpExceptions: true, showStack: true })); '
+ , '});'
+ , ''
+ , 'app.configure(\'production\', function(){'
+ , ' app.use(express.errorHandler()); '
+ , '});'
+ , ''
+ , '// Routes'
+ , ''
+ , 'app.get(\'/\', function(req, res){'
+ , ' res.render(\'index\', {'
+ , ' title: \'Express\''
+ , ' });'
+ , '});'
+ , ''
+ , 'app.listen(3000);'
+ , 'console.log("Express server listening on port %d in %s mode", app.address().port, app.settings.env);'
+ , ''
+].join('\n');
+
+// Parse arguments
+
+var args = process.argv.slice(2)
+ , path = '.';
+
+while (args.length) {
+ var arg = args.shift();
+ switch (arg) {
+ case '-h':
+ case '--help':
+ abort(usage);
+ break;
+ case '-v':
+ case '--version':
+ abort(version);
+ break;
+ case '-s':
+ case '--session':
+ case '--sessions':
+ sessions = true;
+ break;
+ case '-c':
+ case '--css':
+ args.length
+ ? (cssEngine = args.shift())
+ : abort('--css requires an argument');
+ break;
+ case '-t':
+ case '--template':
+ args.length
+ ? (templateEngine = args.shift())
+ : abort('--template requires an argument');
+ break;
+ default:
+ path = arg;
+ }
+}
+
+// Generate application
+
+(function createApplication(path) {
+ emptyDirectory(path, function(empty){
+ if (empty) {
+ createApplicationAt(path);
+ } else {
+ confirm('destination is not empty, continue? ', function(ok){
+ if (ok) {
+ process.stdin.destroy();
+ createApplicationAt(path);
+ } else {
+ abort('aborting');
+ }
+ });
+ }
+ });
+})(path);
+
+/**
+ * Create application at the given directory `path`.
+ *
+ * @param {String} path
+ */
+
+function createApplicationAt(path) {
+ mkdir(path, function(){
+ mkdir(path + '/public/javascripts');
+ mkdir(path + '/public/images');
+ mkdir(path + '/public/stylesheets', function(){
+ switch (cssEngine) {
+ case 'stylus':
+ write(path + '/public/stylesheets/style.styl', stylus);
+ break;
+ case 'less':
+ write(path + '/public/stylesheets/style.less', less);
+ break;
+ case 'sass':
+ write(path + '/public/stylesheets/style.sass', sass);
+ break;
+ default:
+ write(path + '/public/stylesheets/style.css', css);
+ }
+ });
+ mkdir(path + '/views', function(){
+ switch (templateEngine) {
+ case 'ejs':
+ write(path + '/views/layout.ejs', ejsLayout);
+ write(path + '/views/index.ejs', ejsIndex);
+ break;
+ case 'jade':
+ write(path + '/views/layout.jade', jadeLayout);
+ write(path + '/views/index.jade', jadeIndex);
+ break;
+ }
+ });
+
+ // CSS Engine support
+ switch (cssEngine) {
+ case 'sass':
+ case 'less':
+ app = app.replace('{css}', '\n app.use(express.compiler({ src: __dirname + \'/public\', enable: [\'' + cssEngine + '\'] }));');
+ break;
+ case 'stylus':
+ app = app.replace('{css}', '\n app.use(require(\'stylus\').middleware({ src: __dirname + \'/public\' }));');
+ break;
+ default:
+ app = app.replace('{css}', '');
+ }
+
+ // Session support
+ app = app.replace('{sess}', sessions
+ ? '\n app.use(express.cookieParser());\n app.use(express.session({ secret: \'your secret here\' }));'
+ : '');
+
+ // Template support
+ app = app.replace(':TEMPLATE', templateEngine);
+
+ // package.json
+ var json = '{\n';
+ json += ' "name": "application-name"\n';
+ json += ' , "version": "0.0.1"\n';
+ json += ' , "private": true\n';
+ json += ' , "dependencies": {\n';
+ json += ' "express": "' + version + '"\n';
+ if (cssEngine) json += ' , "' + cssEngine + '": ">= 0.0.1"\n';
+ if (templateEngine) json += ' , "' + templateEngine + '": ">= 0.0.1"\n';
+ json += ' }\n';
+ json += '}';
+
+
+ write(path + '/package.json', json);
+ write(path + '/app.js', app);
+ });
+}
+
+/**
+ * Check if the given directory `path` is empty.
+ *
+ * @param {String} path
+ * @param {Function} fn
+ */
+
+function emptyDirectory(path, fn) {
+ fs.readdir(path, function(err, files){
+ if (err && 'ENOENT' != err.code) throw err;
+ fn(!files || !files.length);
+ });
+}
+
+/**
+ * echo str > path.
+ *
+ * @param {String} path
+ * @param {String} str
+ */
+
+function write(path, str) {
+ fs.writeFile(path, str);
+ console.log(' \x1b[36mcreate\x1b[0m : ' + path);
+}
+
+/**
+ * Prompt confirmation with the given `msg`.
+ *
+ * @param {String} msg
+ * @param {Function} fn
+ */
+
+function confirm(msg, fn) {
+ prompt(msg, function(val){
+ fn(/^ *y(es)?/i.test(val));
+ });
+}
+
+/**
+ * Prompt input with the given `msg` and callback `fn`.
+ *
+ * @param {String} msg
+ * @param {Function} fn
+ */
+
+function prompt(msg, fn) {
+ // prompt
+ if (' ' == msg[msg.length - 1]) {
+ process.stdout.write(msg);
+ } else {
+ console.log(msg);
+ }
+
+ // stdin
+ process.stdin.setEncoding('ascii');
+ process.stdin.once('data', function(data){
+ fn(data);
+ }).resume();
+}
+
+/**
+ * Mkdir -p.
+ *
+ * @param {String} path
+ * @param {Function} fn
+ */
+
+function mkdir(path, fn) {
+ mkdirp(path, 0755, function(err){
+ if (err) throw err;
+ console.log(' \033[36mcreate\033[0m : ' + path);
+ fn && fn();
+ });
+}
+
+/**
+ * Exit with the given `str`.
+ *
+ * @param {String} str
+ */
+
+function abort(str) {
+ console.error(str);
+ process.exit(1);
+}
7 examples/simple/node_modules/express/.npmignore
@@ -0,0 +1,7 @@
+.git*
+docs/
+examples/
+support/
+test/
+testing.js
+.DS_Store
755 examples/simple/node_modules/express/History.md
@@ -0,0 +1,755 @@
+
+2.4.7 / 2011-10-05
+==================
+
+ * Added mkdirp to express(1). Closes #795
+ * Added simple _json-config_ example
+ * Added shorthand for the parsed request's pathname via `req.path`
+ * Changed connect dep to 1.7.x to fix npm issue...
+ * Fixed `res.redirect()` __HEAD__ support. [reported by xerox]
+ * Fixed `req.flash()`, only escape args
+ * Fixed absolute path checking on windows. Closes #829 [reported by andrewpmckenzie]
+
+2.4.6 / 2011-08-22
+==================
+
+ * Fixed multiple param callback regression. Closes #824 [reported by TroyGoode]
+
+2.4.5 / 2011-08-19
+==================
+
+ * Added support for routes to handle errors. Closes #809
+ * Added `app.routes.all()`. Closes #803
+ * Added "basepath" setting to work in conjunction with reverse proxies etc.
+ * Refactored `Route` to use a single array of callbacks
+ * Added support for multiple callbacks for `app.param()`. Closes #801
+Closes #805
+ * Changed: removed .call(self) for route callbacks
+ * Dependency: `qs >= 0.3.1`
+ * Fixed `res.redirect()` on windows due to `join()` usage. Closes #808
+
+2.4.4 / 2011-08-05
+==================
+
+ * Fixed `res.header()` intention of a set, even when `undefined`
+ * Fixed `*`, value no longer required
+ * Fixed `res.send(204)` support. Closes #771
+
+2.4.3 / 2011-07-14
+==================
+
+ * Added docs for `status` option special-case. Closes #739
+ * Fixed `options.filename`, exposing the view path to template engines
+
+2.4.2. / 2011-07-06
+==================
+
+ * Revert "removed jsonp stripping" for XSS
+
+2.4.1 / 2011-07-06
+==================
+
+ * Added `res.json()` JSONP support. Closes #737
+ * Added _extending-templates_ example. Closes #730
+ * Added "strict routing" setting for trailing slashes
+ * Added support for multiple envs in `app.configure()` calls. Closes #735
+ * Changed: `res.send()` using `res.json()`
+ * Changed: when cookie `path === null` don't default it
+ * Changed; default cookie path to "home" setting. Closes #731
+ * Removed _pids/logs_ creation from express(1)
+
+2.4.0 / 2011-06-28
+==================
+
+ * Added chainable `res.status(code)`
+ * Added `res.json()`, an explicit version of `res.send(obj)`
+ * Added simple web-service example
+
+2.3.12 / 2011-06-22
+==================
+
+ * \#express is now on freenode! come join!
+ * Added `req.get(field, param)`
+ * Added links to Japanese documentation, thanks @hideyukisaito!
+ * Added; the `express(1)` generated app outputs the env
+ * Added `content-negotiation` example
+ * Dependency: connect >= 1.5.1 < 2.0.0
+ * Fixed view layout bug. Closes #720
+ * Fixed; ignore body on 304. Closes #701
+
+2.3.11 / 2011-06-04
+==================
+
+ * Added `npm test`
+ * Removed generation of dummy test file from `express(1)`
+ * Fixed; `express(1)` adds express as a dep
+ * Fixed; prune on `prepublish`
+
+2.3.10 / 2011-05-27
+==================
+
+ * Added `req.route`, exposing the current route
+ * Added _package.json_ generation support to `express(1)`
+ * Fixed call to `app.param()` function for optional params. Closes #682
+
+2.3.9 / 2011-05-25
+==================
+
+ * Fixed bug-ish with `../' in `res.partial()` calls
+
+2.3.8 / 2011-05-24
+==================
+
+ * Fixed `app.options()`
+
+2.3.7 / 2011-05-23
+==================
+
+ * Added route `Collection`, ex: `app.get('/user/:id').remove();`
+ * Added support for `app.param(fn)` to define param logic
+ * Removed `app.param()` support for callback with return value
+ * Removed module.parent check from express(1) generated app. Closes #670
+ * Refactored router. Closes #639
+
+2.3.6 / 2011-05-20
+==================
+
+ * Changed; using devDependencies instead of git submodules
+ * Fixed redis session example
+ * Fixed markdown example
+ * Fixed view caching, should not be enabled in development
+
+2.3.5 / 2011-05-20
+==================
+
+ * Added export `.view` as alias for `.View`
+
+2.3.4 / 2011-05-08
+==================
+
+ * Added `./examples/say`
+ * Fixed `res.sendfile()` bug preventing the transfer of files with spaces
+
+2.3.3 / 2011-05-03
+==================
+
+ * Added "case sensitive routes" option.
+ * Changed; split methods supported per rfc [slaskis]
+ * Fixed route-specific middleware when using the same callback function several times
+
+2.3.2 / 2011-04-27
+==================
+
+ * Fixed view hints
+
+2.3.1 / 2011-04-26
+==================
+
+ * Added `app.match()` as `app.match.all()`
+ * Added `app.lookup()` as `app.lookup.all()`
+ * Added `app.remove()` for `app.remove.all()`
+ * Added `app.remove.VERB()`
+ * Fixed template caching collision issue. Closes #644
+ * Moved router over from connect and started refactor
+
+2.3.0 / 2011-04-25
+==================
+
+ * Added options support to `res.clearCookie()`
+ * Added `res.helpers()` as alias of `res.locals()`
+ * Added; json defaults to UTF-8 with `res.send()`. Closes #632. [Daniel * Dependency `connect >= 1.4.0`
+ * Changed; auto set Content-Type in res.attachement [Aaron Heckmann]
+ * Renamed "cache views" to "view cache". Closes #628
+ * Fixed caching of views when using several apps. Closes #637
+ * Fixed gotcha invoking `app.param()` callbacks once per route middleware.
+Closes #638
+ * Fixed partial lookup precedence. Closes #631
+Shaw]
+
+2.2.2 / 2011-04-12
+==================
+
+ * Added second callback support for `res.download()` connection errors
+ * Fixed `filename` option passing to template engine
+
+2.2.1 / 2011-04-04
+==================
+
+ * Added `layout(path)` helper to change the layout within a view. Closes #610
+ * Fixed `partial()` collection object support.
+ Previously only anything with `.length` would work.
+ When `.length` is present one must still be aware of holes,
+ however now `{ collection: {foo: 'bar'}}` is valid, exposes
+ `keyInCollection` and `keysInCollection`.
+
+ * Performance improved with better view caching
+ * Removed `request` and `response` locals
+ * Changed; errorHandler page title is now `Express` instead of `Connect`
+
+2.2.0 / 2011-03-30
+==================
+
+ * Added `app.lookup.VERB()`, ex `app.lookup.put('/user/:id')`. Closes #606
+ * Added `app.match.VERB()`, ex `app.match.put('/user/12')`. Closes #606
+ * Added `app.VERB(path)` as alias of `app.lookup.VERB()`.
+ * Dependency `connect >= 1.2.0`
+
+2.1.1 / 2011-03-29
+==================
+
+ * Added; expose `err.view` object when failing to locate a view
+ * Fixed `res.partial()` call `next(err)` when no callback is given [reported by aheckmann]
+ * Fixed; `res.send(undefined)` responds with 204 [aheckmann]
+
+2.1.0 / 2011-03-24
+==================
+
+ * Added `<root>/_?<name>` partial lookup support. Closes #447
+ * Added `request`, `response`, and `app` local variables
+ * Added `settings` local variable, containing the app's settings
+ * Added `req.flash()` exception if `req.session` is not available
+ * Added `res.send(bool)` support (json response)
+ * Fixed stylus example for latest version
+ * Fixed; wrap try/catch around `res.render()`
+
+2.0.0 / 2011-03-17
+==================
+
+ * Fixed up index view path alternative.
+ * Changed; `res.locals()` without object returns the locals
+
+2.0.0rc3 / 2011-03-17
+==================
+
+ * Added `res.locals(obj)` to compliment `res.local(key, val)`
+ * Added `res.partial()` callback support
+ * Fixed recursive error reporting issue in `res.render()`
+
+2.0.0rc2 / 2011-03-17
+==================
+
+ * Changed; `partial()` "locals" are now optional
+ * Fixed `SlowBuffer` support. Closes #584 [reported by tyrda01]
+ * Fixed .filename view engine option [reported by drudge]
+ * Fixed blog example
+ * Fixed `{req,res}.app` reference when mounting [Ben Weaver]
+
+2.0.0rc / 2011-03-14
+==================
+
+ * Fixed; expose `HTTPSServer` constructor
+ * Fixed express(1) default test charset. Closes #579 [reported by secoif]
+ * Fixed; default charset to utf-8 instead of utf8 for lame IE [reported by NickP]
+
+2.0.0beta3 / 2011-03-09
+==================
+
+ * Added support for `res.contentType()` literal
+ The original `res.contentType('.json')`,
+ `res.contentType('application/json')`, and `res.contentType('json')`
+ will work now.
+ * Added `res.render()` status option support back
+ * Added charset option for `res.render()`
+ * Added `.charset` support (via connect 1.0.4)
+ * Added view resolution hints when in development and a lookup fails
+ * Added layout lookup support relative to the page view.
+ For example while rendering `./views/user/index.jade` if you create
+ `./views/user/layout.jade` it will be used in favour of the root layout.
+ * Fixed `res.redirect()`. RFC states absolute url [reported by unlink]
+ * Fixed; default `res.send()` string charset to utf8
+ * Removed `Partial` constructor (not currently used)
+
+2.0.0beta2 / 2011-03-07
+==================
+
+ * Added res.render() `.locals` support back to aid in migration process
+ * Fixed flash example
+
+2.0.0beta / 2011-03-03
+==================
+
+ * Added HTTPS support
+ * Added `res.cookie()` maxAge support
+ * Added `req.header()` _Referrer_ / _Referer_ special-case, either works
+ * Added mount support for `res.redirect()`, now respects the mount-point
+ * Added `union()` util, taking place of `merge(clone())` combo
+ * Added stylus support to express(1) generated app
+ * Added secret to session middleware used in examples and generated app
+ * Added `res.local(name, val)` for progressive view locals
+ * Added default param support to `req.param(name, default)`
+ * Added `app.disabled()` and `app.enabled()`
+ * Added `app.register()` support for omitting leading ".", either works
+ * Added `res.partial()`, using the same interface as `partial()` within a view. Closes #539
+ * Added `app.param()` to map route params to async/sync logic
+ * Added; aliased `app.helpers()` as `app.locals()`. Closes #481
+ * Added extname with no leading "." support to `res.contentType()`
+ * Added `cache views` setting, defaulting to enabled in "production" env
+ * Added index file partial resolution, eg: partial('user') may try _views/user/index.jade_.
+ * Added `req.accepts()` support for extensions
+ * Changed; `res.download()` and `res.sendfile()` now utilize Connect's
+ static file server `connect.static.send()`.
+ * Changed; replaced `connect.utils.mime()` with npm _mime_ module
+ * Changed; allow `req.query` to be pre-defined (via middleware or other parent
+ * Changed view partial resolution, now relative to parent view
+ * Changed view engine signature. no longer `engine.render(str, options, callback)`, now `engine.compile(str, options) -> Function`, the returned function accepts `fn(locals)`.
+ * Fixed `req.param()` bug returning Array.prototype methods. Closes #552
+ * Fixed; using `Stream#pipe()` instead of `sys.pump()` in `res.sendfile()`
+ * Fixed; using _qs_ module instead of _querystring_
+ * Fixed; strip unsafe chars from jsonp callbacks
+ * Removed "stream threshold" setting
+
+1.0.8 / 2011-03-01
+==================
+
+ * Allow `req.query` to be pre-defined (via middleware or other parent app)
+ * "connect": ">= 0.5.0 < 1.0.0". Closes #547
+ * Removed the long deprecated __EXPRESS_ENV__ support
+
+1.0.7 / 2011-02-07
+==================
+
+ * Fixed `render()` setting inheritance.
+ Mounted apps would not inherit "view engine"
+
+1.0.6 / 2011-02-07
+==================
+
+ * Fixed `view engine` setting bug when period is in dirname
+
+1.0.5 / 2011-02-05
+==================
+
+ * Added secret to generated app `session()` call
+
+1.0.4 / 2011-02-05
+==================
+
+ * Added `qs` dependency to _package.json_
+ * Fixed namespaced `require()`s for latest connect support
+
+1.0.3 / 2011-01-13
+==================
+
+ * Remove unsafe characters from JSONP callback names [Ryan Grove]
+
+1.0.2 / 2011-01-10
+==================
+
+ * Removed nested require, using `connect.router`
+
+1.0.1 / 2010-12-29
+==================
+
+ * Fixed for middleware stacked via `createServer()`
+ previously the `foo` middleware passed to `createServer(foo)`
+ would not have access to Express methods such as `res.send()`
+ or props like `req.query` etc.
+
+1.0.0 / 2010-11-16
+==================
+
+ * Added; deduce partial object names from the last segment.
+ For example by default `partial('forum/post', postObject)` will
+ give you the _post_ object, providing a meaningful default.
+ * Added http status code string representation to `res.redirect()` body
+ * Added; `res.redirect()` supporting _text/plain_ and _text/html_ via __Accept__.
+ * Added `req.is()` to aid in content negotiation
+ * Added partial local inheritance [suggested by masylum]. Closes #102
+ providing access to parent template locals.
+ * Added _-s, --session[s]_ flag to express(1) to add session related middleware
+ * Added _--template_ flag to express(1) to specify the
+ template engine to use.
+ * Added _--css_ flag to express(1) to specify the
+ stylesheet engine to use (or just plain css by default).
+ * Added `app.all()` support [thanks aheckmann]
+ * Added partial direct object support.
+ You may now `partial('user', user)` providing the "user" local,
+ vs previously `partial('user', { object: user })`.
+ * Added _route-separation_ example since many people question ways
+ to do this with CommonJS modules. Also view the _blog_ example for
+ an alternative.
+ * Performance; caching view path derived partial object names
+ * Fixed partial local inheritance precedence. [reported by Nick Poulden] Closes #454
+ * Fixed jsonp support; _text/javascript_ as per mailinglist discussion
+
+1.0.0rc4 / 2010-10-14
+==================
+
+ * Added _NODE_ENV_ support, _EXPRESS_ENV_ is deprecated and will be removed in 1.0.0
+ * Added route-middleware support (very helpful, see the [docs](http://expressjs.com/guide.html#Route-Middleware))
+ * Added _jsonp callback_ setting to enable/disable jsonp autowrapping [Dav Glass]
+ * Added callback query check on response.send to autowrap JSON objects for simple webservice implementations [Dav Glass]
+ * Added `partial()` support for array-like collections. Closes #434
+ * Added support for swappable querystring parsers
+ * Added session usage docs. Closes #443
+ * Added dynamic helper caching. Closes #439 [suggested by maritz]
+ * Added authentication example
+ * Added basic Range support to `res.sendfile()` (and `res.download()` etc)
+ * Changed; `express(1)` generated app using 2 spaces instead of 4
+ * Default env to "development" again [aheckmann]
+ * Removed _context_ option is no more, use "scope"
+ * Fixed; exposing _./support_ libs to examples so they can run without installs
+ * Fixed mvc example
+
+1.0.0rc3 / 2010-09-20
+==================
+
+ * Added confirmation for `express(1)` app generation. Closes #391
+ * Added extending of flash formatters via `app.flashFormatters`
+ * Added flash formatter support. Closes #411
+ * Added streaming support to `res.sendfile()` using `sys.pump()` when >= "stream threshold"
+ * Added _stream threshold_ setting for `res.sendfile()`
+ * Added `res.send()` __HEAD__ support
+ * Added `res.clearCookie()`
+ * Added `res.cookie()`
+ * Added `res.render()` headers option
+ * Added `res.redirect()` response bodies
+ * Added `res.render()` status option support. Closes #425 [thanks aheckmann]
+ * Fixed `res.sendfile()` responding with 403 on malicious path
+ * Fixed `res.download()` bug; when an error occurs remove _Content-Disposition_
+ * Fixed; mounted apps settings now inherit from parent app [aheckmann]
+ * Fixed; stripping Content-Length / Content-Type when 204
+ * Fixed `res.send()` 204. Closes #419
+ * Fixed multiple _Set-Cookie_ headers via `res.header()`. Closes #402
+ * Fixed bug messing with error handlers when `listenFD()` is called instead of `listen()`. [thanks guillermo]
+
+
+1.0.0rc2 / 2010-08-17
+==================
+
+ * Added `app.register()` for template engine mapping. Closes #390
+ * Added `res.render()` callback support as second argument (no options)
+ * Added callback support to `res.download()`
+ * Added callback support for `res.sendfile()`
+ * Added support for middleware access via `express.middlewareName()` vs `connect.middlewareName()`
+ * Added "partials" setting to docs
+ * Added default expresso tests to `express(1)` generated app. Closes #384
+ * Fixed `res.sendfile()` error handling, defer via `next()`
+ * Fixed `res.render()` callback when a layout is used [thanks guillermo]
+ * Fixed; `make install` creating ~/.node_libraries when not present
+ * Fixed issue preventing error handlers from being defined anywhere. Closes #387
+
+1.0.0rc / 2010-07-28
+==================
+
+ * Added mounted hook. Closes #369
+ * Added connect dependency to _package.json_
+
+ * Removed "reload views" setting and support code
+ development env never caches, production always caches.
+
+ * Removed _param_ in route callbacks, signature is now
+ simply (req, res, next), previously (req, res, params, next).
+ Use _req.params_ for path captures, _req.query_ for GET params.
+
+ * Fixed "home" setting
+ * Fixed middleware/router precedence issue. Closes #366
+ * Fixed; _configure()_ callbacks called immediately. Closes #368
+
+1.0.0beta2 / 2010-07-23
+==================
+
+ * Added more examples
+ * Added; exporting `Server` constructor
+ * Added `Server#helpers()` for view locals
+ * Added `Server#dynamicHelpers()` for dynamic view locals. Closes #349
+ * Added support for absolute view paths
+ * Added; _home_ setting defaults to `Server#route` for mounted apps. Closes #363
+ * Added Guillermo Rauch to the contributor list
+ * Added support for "as" for non-collection partials. Closes #341
+ * Fixed _install.sh_, ensuring _~/.node_libraries_ exists. Closes #362 [thanks jf]
+ * Fixed `res.render()` exceptions, now passed to `next()` when no callback is given [thanks guillermo]
+ * Fixed instanceof `Array` checks, now `Array.isArray()`
+ * Fixed express(1) expansion of public dirs. Closes #348
+ * Fixed middleware precedence. Closes #345
+ * Fixed view watcher, now async [thanks aheckmann]
+
+1.0.0beta / 2010-07-15
+==================
+
+ * Re-write
+ - much faster
+ - much lighter
+ - Check [ExpressJS.com](http://expressjs.com) for migration guide and updated docs
+
+0.14.0 / 2010-06-15
+==================
+
+ * Utilize relative requires
+ * Added Static bufferSize option [aheckmann]
+ * Fixed caching of view and partial subdirectories [aheckmann]
+ * Fixed mime.type() comments now that ".ext" is not supported
+ * Updated haml submodule
+ * Updated class submodule
+ * Removed bin/express
+
+0.13.0 / 2010-06-01
+==================
+
+ * Added node v0.1.97 compatibility
+ * Added support for deleting cookies via Request#cookie('key', null)
+ * Updated haml submodule
+ * Fixed not-found page, now using using charset utf-8
+ * Fixed show-exceptions page, now using using charset utf-8
+ * Fixed view support due to fs.readFile Buffers
+ * Changed; mime.type() no longer accepts ".type" due to node extname() changes
+
+0.12.0 / 2010-05-22
+==================
+