interop.ts 5.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161
  1. //
  2. // Copyright (c) 2014-2015, THUNDERBEAST GAMES LLC All rights reserved
  3. // LICENSE: Atomic Game Engine Editor and Tools EULA
  4. // Please see LICENSE_ATOMIC_EDITOR_AND_TOOLS.md in repository root for
  5. // license information: https://github.com/AtomicGameEngine/AtomicGameEngine
  6. //
  7. // This is the interop file, exposing functions that can be called by the host game engine
  8. import editor from "./editor/editor";
  9. import * as editorConfig from "./editor/editorConfig";
  10. /**
  11. * Port to attach Chrome Dev Tools to
  12. * @type {Number}
  13. */
  14. const DEBUG_PORT = 3335;
  15. /**
  16. * Display "Attach dev tools now" alert on startup if this is set to true
  17. * @type {Boolean}
  18. */
  19. const DEBUG_ALERT = false;
  20. /**
  21. * Promise version of atomic query
  22. * @param {string} message the query to use to pass to atomicQuery. If there is no payload, this will be passed directly, otherwise it will be passed in a data object
  23. * @param {any} payload optional data to send
  24. * @return {Promise}
  25. */
  26. function atomicQueryPromise(message: any): Promise<{}> {
  27. return new Promise(function(resolve, reject) {
  28. let queryMessage = message;
  29. // if message is coming in as an object then let's stringify it
  30. if (typeof (message) != "string") {
  31. queryMessage = JSON.stringify(message);
  32. }
  33. window.atomicQuery({
  34. request: queryMessage,
  35. persistent: false,
  36. onSuccess: resolve,
  37. onFailure: (error_code, error_message) => reject({ error_code: error_code, error_message: error_message })
  38. });
  39. });
  40. }
  41. export default class HostInteropType {
  42. static EDITOR_SAVE_CODE = "editorSaveCode";
  43. static EDITOR_SAVE_FILE = "editorSaveFile";
  44. static EDITOR_LOAD_COMPLETE = "editorLoadComplete";
  45. static EDITOR_CHANGE = "editorChange";
  46. private static _inst: HostInteropType = null;
  47. static getInstance(): HostInteropType {
  48. if (HostInteropType._inst == null) {
  49. HostInteropType._inst = new HostInteropType();
  50. }
  51. return HostInteropType._inst;
  52. }
  53. constructor() {
  54. // Set up the window object so the host can call into it
  55. window.HOST_loadCode = this.loadCode.bind(this);
  56. window.HOST_saveCode = this.saveCode.bind(this);
  57. }
  58. /**
  59. * Called from the host to notify the client what file to load
  60. * @param {string} codeUrl
  61. */
  62. loadCode(codeUrl: string) {
  63. console.log("Load Code called for :" + codeUrl);
  64. const fileExt = codeUrl.split(".").pop();
  65. const filename = codeUrl.replace("atomic://", "");
  66. // go ahead and set the theme prior to pulling the file across
  67. editorConfig.configure(fileExt, filename);
  68. // get the code
  69. this.getResource(codeUrl).then((src: string) => {
  70. editorConfig.loadCodeIntoEditor(src, filename, fileExt);
  71. }).catch((e: Editor.ClientExtensions.AtomicErrorMessage) => {
  72. console.log("Error loading code: " + e.error_message);
  73. });
  74. }
  75. /**
  76. * Save the contents of the editor
  77. * @return {Promise}
  78. */
  79. saveCode(): Promise<{}> {
  80. return atomicQueryPromise({
  81. message: HostInteropType.EDITOR_SAVE_CODE,
  82. payload: editor.session.getValue()
  83. });
  84. }
  85. /**
  86. * Save the contents of a file as filename
  87. * @param {string} filename
  88. * @param {string} fileContents
  89. * @return {Promise}
  90. */
  91. saveFile(filename: string, fileContents: string): Promise<{}> {
  92. return atomicQueryPromise({
  93. message: HostInteropType.EDITOR_SAVE_FILE,
  94. filename: filename,
  95. payload: fileContents
  96. });
  97. }
  98. /**
  99. * Call this function when the client is fully loaded up. This will notify the host that
  100. * it can start loading code
  101. */
  102. editorLoaded() {
  103. if (DEBUG_ALERT) {
  104. alert(`Attach chrome dev tools to this instance by navigating to http://localhost:${DEBUG_PORT}`);
  105. }
  106. atomicQueryPromise(HostInteropType.EDITOR_LOAD_COMPLETE);
  107. }
  108. /**
  109. * Queries the host for a particular resource and returns it in a promise
  110. * @param {string} codeUrl
  111. * @return {Promise}
  112. */
  113. getResource(codeUrl: string): Promise<{}> {
  114. return new Promise(function(resolve, reject) {
  115. const xmlHttp = new XMLHttpRequest();
  116. xmlHttp.onreadystatechange = () => {
  117. if (xmlHttp.readyState == 4 && xmlHttp.status == 200) {
  118. resolve(xmlHttp.responseText);
  119. }
  120. };
  121. xmlHttp.open("GET", codeUrl, true); // true for asynchronous
  122. xmlHttp.send(null);
  123. });
  124. }
  125. /**
  126. * Returns a file resource from the resources directory
  127. * @param {string} filename name and path of file under the project directory or a fully qualified file name
  128. * @return {Promise}
  129. */
  130. getFileResource(filename: string): Promise<{}> {
  131. return this.getResource(`atomic://${filename}`);
  132. }
  133. /**
  134. * Notify the host that the contents of the editor has changed
  135. */
  136. notifyEditorChange() {
  137. atomicQueryPromise(HostInteropType.EDITOR_CHANGE).catch((e: Editor.ClientExtensions.AtomicErrorMessage) => {
  138. console.log("Error on change: " + e.error_message);
  139. });
  140. }
  141. }
  142. HostInteropType.getInstance().editorLoaded();