index.js 7.7 KB

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