index.js 6.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210
  1. var Q = require("q");
  2. var fs = require("fs");
  3. var ncp = require("ncp").ncp;
  4. var os = require("os");
  5. var path = require("path");
  6. var spawn = require("child_process").spawn;
  7. var wrench = require("wrench");
  8. var DATA_DIR = __dirname + "/data/";
  9. exports.DATA_DIR = DATA_DIR;
  10. var ATOMIC_TOOL_BIN = __dirname + "/data/bin/Mac/AtomicTool";
  11. var HTTP_PORT = 4000;
  12. var SOCKET_PORT = HTTP_PORT+1;
  13. exports.VERSION = JSON.parse(fs.readFileSync(__dirname + "/package.json")).version;
  14. var exec = function (command, flags, opts) {
  15. opts = opts || {};
  16. if (opts.verbose !== false) {
  17. console.log([command].concat(flags).join(" "));
  18. }
  19. // Run everything through cmd.exe on Windows to be able to find .bat files
  20. if (process.platform == "win32" && opts.windowsCmd !== false) {
  21. flags.unshift("/c", command);
  22. command = "cmd";
  23. }
  24. var deferred = Q.defer();
  25. var child = spawn(command, flags, {stdio: (opts.output === false) ? "ignore" : "inherit"});
  26. child.on("close", function (code) {
  27. if (code && opts.check !== false) {
  28. deferred.reject();
  29. }
  30. deferred.resolve(code);
  31. });
  32. child.on("error", function (error) {
  33. deferred.reject(error);
  34. });
  35. return deferred.promise;
  36. };
  37. exports.exec = exec;
  38. var atomictool = function (flags, opts) {
  39. opts = opts || {};
  40. opts.windowsCmd = false;
  41. flags.unshift(DATA_DIR);
  42. flags.unshift("--cli-data-path");
  43. return exec(ATOMIC_TOOL_BIN, flags, opts);
  44. };
  45. exports.atomictool = atomictool
  46. exports.newProject = function (output) {
  47. return atomictool(["new", output], {output:true});
  48. };
  49. exports.build = function (platform) {
  50. return atomictool(["build", platform], {output:true});
  51. };
  52. exports.addPlatform = function (platform) {
  53. return atomictool(["platform-add", platform], {output:true});
  54. };
  55. // Web Server (from flambe: https://raw.githubusercontent.com/aduros/flambe/master/command/index.js)
  56. var Server = function () {
  57. };
  58. exports.Server = Server;
  59. Server.prototype.start = function () {
  60. var self = this;
  61. var connect = require("connect");
  62. var url = require("url");
  63. var websocket = require("websocket");
  64. // Fire up a Haxe compiler server, ignoring all output. It's fine if this command fails, the
  65. // build will fallback to not using a compiler server
  66. // spawn("haxe", ["--wait", HAXE_COMPILER_PORT], {stdio: "ignore"});
  67. // Start a static HTTP server
  68. var host = "0.0.0.0";
  69. var staticServer = connect()
  70. .use(function (req, res, next) {
  71. var parsed = url.parse(req.url, true);
  72. if (parsed.pathname == "/_api") {
  73. // Handle API requests
  74. req.setEncoding("utf8");
  75. req.on("data", function (chunk) {
  76. self._onAPIMessage(chunk)
  77. .then(function (result) {
  78. res.end(JSON.stringify({result: result}));
  79. })
  80. .catch(function (error) {
  81. res.end(JSON.stringify({error: error}));
  82. });
  83. });
  84. } else {
  85. if (parsed.query.v) {
  86. // Forever-cache assets
  87. var expires = new Date(Date.now() + 1000*60*60*24*365*25);
  88. res.setHeader("Expires", expires.toUTCString());
  89. res.setHeader("Cache-Control", "max-age=315360000");
  90. }
  91. next();
  92. }
  93. })
  94. .use(connect.logger("tiny"))
  95. .use(connect.compress())
  96. .use(connect.static("Build/Web-Build"))
  97. .listen(HTTP_PORT, host);
  98. console.log("Serving on http://localhost:%s", HTTP_PORT);
  99. this._wsServer = new websocket.server({
  100. httpServer: staticServer,
  101. autoAcceptConnections: true,
  102. });
  103. this._wsServer.on("connect", function (connection) {
  104. connection.on("message", function (message) {
  105. if (message.type == "utf8") {
  106. self._onMessage(message.utf8Data);
  107. }
  108. });
  109. });
  110. var net = require("net");
  111. this._connections = [];
  112. this._socketServer = net.createServer(function (connection) {
  113. self._connections.push(connection);
  114. connection.on("end", function () {
  115. self._connections.splice(self._connections.indexOf(connection, 1));
  116. });
  117. connection.on("data", function (data) {
  118. data = data.toString();
  119. self._onMessage(data);
  120. });
  121. });
  122. this._socketServer.listen(SOCKET_PORT, host);
  123. var watch = require("watch");
  124. var crypto = require("crypto");
  125. watch.createMonitor("Resources", {interval: 200}, function (monitor) {
  126. monitor.on("changed", function (file) {
  127. console.log("Asset changed: " + file);
  128. var output = "build/web/"+file;
  129. if (fs.existsSync(output)) {
  130. var contents = fs.readFileSync(file);
  131. fs.writeFileSync(output, contents);
  132. self.broadcast("file_changed", {
  133. name: path.relative("Resources", file),
  134. md5: crypto.createHash("md5").update(contents).digest("hex"),
  135. });
  136. }
  137. });
  138. });
  139. };
  140. /** Broadcast an event to all clients. */
  141. Server.prototype.broadcast = function (type, params) {
  142. var event = {type: type};
  143. if (params) {
  144. for (var k in params) {
  145. event[k] = params[k];
  146. }
  147. }
  148. var message = JSON.stringify(event);
  149. this._wsServer.broadcast(message);
  150. this._connections.forEach(function (connection) {
  151. connection.write(message);
  152. });
  153. };
  154. /** Handle messages from connected game clients. */
  155. Server.prototype._onMessage = function (message) {
  156. try {
  157. var event = JSON.parse(message);
  158. // switch (event.type) {
  159. // case "restart":
  160. // this.broadcast("restart");
  161. // }
  162. } catch (error) {
  163. console.warn("Received badly formed message", error);
  164. }
  165. };
  166. /** Handle web API messages. */
  167. Server.prototype._onAPIMessage = function (message) {
  168. try {
  169. message = JSON.parse(message);
  170. } catch (error) {
  171. return Q.reject("Badly formed JSON");
  172. }
  173. switch (message.method) {
  174. case "restart":
  175. this.broadcast("restart");
  176. return Q.resolve({
  177. htmlClients: this._wsServer.connections.length,
  178. flashClients: this._connections.length,
  179. });
  180. default:
  181. return Q.reject("Unknown method: " + message.method);
  182. }
  183. };