跳轉至

准备网页以提供应用

為應用服務設置網頁

應用用戶按照以下步驟安裝和運行應用:

 

  1. 在應用程式服務的網頁上點擊應用程式啟動按鈕,下載並安裝Crossplay Launcher
  2. 運行Crossplay Launcher
  3. Crossplay Launcher會自動下載並安裝應用程式。
  4. Crossplay Launcher會自動更新並運行應用程式。

 

以下是使用Crossplay Launcher從網頁服務應用程序的整個過程。

 

 

服務應用程式的網頁可以通過以下兩種方式提供:

  1. 遊戲 (https://withhive.com/)
  2. 由開發該應用程式的公司建立的網頁。

本指南提供了示例和指导,说明在使用Crossplay Launcher从网页服务应用时,网页上需要实现什么内容,由应用开发者构建

檢查應用程式服務條件

當用戶訪問網頁時,檢查用戶的PC環境,例如OS版本,是否受支持。

  • 支持的OSWindows 10或更高版本

檢查條件後,如果有任何問題,應該向用戶顯示錯誤彈出窗口。

實現應用啟動按鈕和跨平台啟動器下載彈出窗口

在網頁上實現應用啟動按鈕。

 

實現一個彈出窗口,用於下載Crossplay Launcher安裝文件。彈出窗口必須包含安裝Crossplay Launcher的指南。

 

在網頁上點擊應用啟動按鈕會顯示彈出窗口。彈出窗口應該有一個按鈕來下載Crossplay Launcher安裝文件。

檢查 Crossplay Launcher 安裝情況

當用戶在網頁上點擊應用啟動按鈕時,有必要檢查用戶的 PC 上是否安裝了 Crossplay Launcher。在網頁上執行以下 JavaScript 代碼以檢查用戶的 PC 上是否安裝了 Crossplay Launcher

JavaScript 代碼
!function (b, a) {
"object" == typeof exports && "object" == typeof module
? module.exports = a()
: "function" == typeof define && define.amd
    ? define("customProtocolCheck", [], a)
    : "object" == typeof exports
        ? exports.customProtocolCheck = a()
        : b.customProtocolCheck = a()
}(window, function () {
return function (b) {
var c = {};
function a(d) {
    if (c[d]) 
        return c[d].exports;
    var e = c[d] = {
        i: d,
        l: !1,
        exports: {}
    };
    return b[d].call(e.exports, e, e.exports, a),
    e.l = !0,
    e.exports
}
return a.m = b,
a.c = c,
a.d = function (b, c, d) {
    a.o(b, c) || Object.defineProperty(b, c, {
        enumerable: !0,
        get: d
    })
},
a.r = function (a) {
    "undefined" != typeof Symbol && Symbol.toStringTag && Object.defineProperty(
        a,
        Symbol.toStringTag,
        {value: "Module"}
    ),
    Object.defineProperty(a, "__esModule", {
        value: !0
    })
},
a.t = function (b, c) {
    if (
        1 & c && (b = a(b)),
        8 & c || 4 & c && "object" == typeof b && b && b.__esModule
    ) 
        return b;
    var d = Object.create(null);
    if (a.r(d), Object.defineProperty(d, "default", {
        enumerable: !0,
        value: b
    }), 2 & c && "string" != typeof b) 
        for (var e in b) 
            a.d(d, e, (function (a) {
                return b[a]
            }).bind(null, e));
return d
},
a.n = function (c) {
    var b = c && c.__esModule
        ? function () {
            return c.default
        }
        : function () {
            return c
        };
    return a.d(b, "a", b),
    b
},
a.o = function (a, b) {
    return Object
        .prototype
        .hasOwnProperty
        .call(a, b)
},
a.p = "",
a(a.s = 0)
}({
"./index.js": function (module, exports) {
    eval(
        'var browser = {\n  getUserAgent: function getUserAgent() {\n    return window.' +
        'navigator.userAgent;\n  },\n  userAgentContains: function userAgentContains(br' +
        'owserName) {\n    browserName = browserName.toLowerCase();\n    return this.ge' +
        'tUserAgent().toLowerCase().indexOf(browserName) > -1;\n  },\n  isOSX: function' +
        ' isOSX() {\n    return this.userAgentContains("Macintosh");\n  },\n  isFirefox' +
        ': function isFirefox() {\n    return this.userAgentContains("firefox");\n  },' +
        '\n  isInternetExplorer: function isInternetExplorer() {\n    return this.userA' +
        'gentContains("trident");\n  },\n\n  /**\r\n   * Detects IE 11 and older\r\n   ' +
        '* @return {Boolean} Returns true when IE 11 and older\r\n   */\n  isIE: functi' +
        'on isIE() {\n    var ua = this.getUserAgent().toLowerCase(); // Test values.\n' +
        '    // Uncomment to check result\n    // IE 10\n    // ua = \'Mozilla/5.0 (com' +
        'patible; MSIE 10.0; Windows NT 6.2; Trident/6.0)\';\n    // IE 11\n    // ua =' +
        ' \'Mozilla/5.0 (Windows NT 6.3; Trident/7.0; rv:11.0) like Gecko/20100101 Fire' +
        'fox/12.0\';\n\n    var msie = ua.indexOf("msie");\n\n    if (msie > 0) {\n    ' +
        '  // IE 10 or older\n      return true;\n    }\n\n    var trident = ua.indexOf' +
        '("trident/");\n\n    if (trident > 0) {\n      // IE 11\n      return true;\n ' +
        '   } // other browser\n\n\n    return false;\n  },\n  isEdge: function isEdge(' +
        ') {\n    var ua = this.getUserAgent().toLowerCase(); // Test values.\n    // U' +
        'ncomment to check result\n    // Edge\n    // ua = \'Mozilla/5.0 (Windows NT 1' +
        '0.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/42.0.2311.135 S' +
        'afari/537.36 Edge/12.10240\';\n\n    var edge = ua.indexOf("edge");\n\n    if ' +
        '(edge > 0) {\n      return true;\n    }\n\n    return false;\n  },\n  isChrome' +
        ': function isChrome() {\n    // IE11 returns undefined for window.chrome\n    ' +
        '// and new Opera 30 outputs true for window.chrome\n    // but needs to check ' +
        'if window.opr is not undefined\n    // and new IE Edge outputs to true for win' +
        'dow.chrome\n    // and if not iOS Chrome check\n    var isChromium = window.ch' +
        'rome;\n    var winNav = window.navigator;\n    var vendorName = winNav.vendor;' +

        '\n    var isOpera = typeof window.opr !== "undefined";\n    var isIEedge = win' +
        'Nav.userAgent.indexOf("Edge") > -1;\n    var isIOSChrome = winNav.userAgent.ma' +
        'tch("CriOS");\n    return isChromium !== null && typeof isChromium !== "undefi' +
        'ned" && vendorName === "Google Inc." && isOpera === false && isIEedge === fals' +
        'e || isIOSChrome;\n  },\n  isWhale: function isWhale() {\n    // IE11 returns ' +
        'undefined for window.chrome\n    // and new Opera 30 outputs true for window.c' +
        'hrome\n    // but needs to check if window.opr is not undefined\n    // and ne' +
        'w IE Edge outputs to true for window.chrome\n    // and if not iOS Chrome chec' +
        'k\n    var isChromium = window.chrome;\n    var winNav = window.navigator;\n  ' +
        '  var vendorName = winNav.vendor;\n    var isOpera = typeof window.opr !== "un' +
        'defined";\n    var isIEedge = winNav.userAgent.indexOf("Edge") > -1;\n    var ' +
        'isIOSChrome = winNav.userAgent.match("CriOS");\n    return isChromium !== null' +
        ' && typeof isChromium !== "undefined" && vendorName === "NAVER Corp." && isOpe' +
        'ra === false && isIEedge === false || isIOSChrome;\n  },\n  isOpera: function ' +
        'isOpera() {\n    return this.userAgentContains(" OPR/");\n  }\n};\nvar DEFAULT' +
        '_CUSTOM_PROTOCOL_FAIL_CALLBACK_TIMEOUT;\n\nvar registerEvent = function regist' +
        'erEvent(target, eventType, cb) {\n  if (target.addEventListener) {\n    target' +
        '.addEventListener(eventType, cb);\n    return {\n      remove: function remove' +
        '() {\n        target.removeEventListener(eventType, cb);\n      }\n    };\n  }' +
        ' else {\n    target.attachEvent(eventType, cb);\n    return {\n      remove: f' +
        'unction remove() {\n        target.detachEvent(eventType, cb);\n      }\n    }' +
        ';\n  }\n};\n\nvar createHiddenIframe = function createHiddenIframe(target, uri' +
        ') {\n  var iframe = document.createElement("iframe");\n  iframe.src = uri;\n  ' +
        'iframe.id = "hiddenIframe";\n  iframe.style.display = "none";\n  target.append' +
        'Child(iframe);\n  return iframe;\n};\n\nvar openUriWithHiddenFrame = function ' +
        'openUriWithHiddenFrame(uri, failCb, successCb) {\n  var timeout = setTimeout(f' +
        'unction () {\n    failCb();\n    handler.remove();\n  }, DEFAULT_CUSTOM_PROTOC' +
        'OL_FAIL_CALLBACK_TIMEOUT);\n  var iframe = document.querySelector("#hiddenIfra' +
        'me");\n\n  if (!iframe) {\n    iframe = createHiddenIframe(document.body, "abo' +
        'ut:blank");\n  }\n\n  onBlur = function onBlur() {\n    clearTimeout(timeout);' +
        '\n    handler.remove();\n    successCb();\n  };\n\n  var handler = registerEve' +
        'nt(window, "blur", onBlur);\n  iframe.contentWindow.location.href = uri;\n};\n' +
        '\nvar openUriWithTimeoutHack = function openUriWithTimeoutHack(uri, failCb, su' +
        'ccessCb) {\n  var timeout = setTimeout(function () {\n    failCb();\n    handl' +
        'er.remove();\n  }, DEFAULT_CUSTOM_PROTOCOL_FAIL_CALLBACK_TIMEOUT); //handle pa' +
        'ge running in an iframe (blur must be registered with top level window)\n\n  v' +
        'ar target = window;\n\n  while (target.parent && target != target.parent) {\n ' +
        '   target = target.parent;\n  }\n\n  onBlur = function onBlur() {\n    clearTi' +
        'meout(timeout);\n    handler.remove();\n    successCb();\n  };\n\n  var handle' +
        'r = registerEvent(target, "blur", onBlur);\n  window.location = uri;\n};\n\nva' +
        'r openUriUsingFirefox = function openUriUsingFirefox(uri, failCb, successCb) {' +
        '\n  var iframe = document.querySelector("#hiddenIframe");\n\n  if (!iframe) {' +
        '\n    iframe = createHiddenIframe(document.body, "about:blank");\n  }\n\n  try' +
        ' {\n    iframe.contentWindow.location.href = uri;\n    successCb();\n  } catch' +
        ' (e) {\n    if (e.name == "NS_ERROR_UNKNOWN_PROTOCOL") {\n      failCb();\n   ' +
        ' }\n  }\n};\n\nvar openUriWithMsLaunchUri = function openUriWithMsLaunchUri(ur' +
        'i, failCb, successCb) {\n  navigator.msLaunchUri(uri, successCb, failCb);\n};' +
        '\n\nvar getBrowserVersion = function getBrowserVersion() {\n  var ua = window.' +
        'navigator.userAgent;\n  var tem,\n      M = ua.match(/(opera|chrome|safari|fir' +
        'efox|msie|trident(?=\\/))\\/?\\s*(\\d+)/i) || [];\n\n  if (/trident/i.test(M[1' +
        '])) {\n    tem = /\\brv[ :]+(\\d+)/g.exec(ua) || [];\n    return parseFloat(te' +
        'm[1]) || "";\n  }\n\n  if (M[1] === "Chrome") {\n    tem = ua.match(/\\b(OPR|E' +
        'dge)\\/(\\d+)/);\n\n    if (tem != null) {\n      return parseFloat(tem[2]);\n' +
        '    }\n  }\n\n  M = M[2] ? [M[1], M[2]] : [window.navigator.appName, window.na' +
        'vigator.appVersion, "-?"];\n  if ((tem = ua.match(/version\\/(\\d+)/i)) != nul' +
        'l) M.splice(1, 1, tem[1]);\n  return parseFloat(M[1]);\n};\n\nvar protocolChec' +
        'k = function protocolCheck(uri, failCb, successCb) {\n  var timeout = argument' +
        's.length > 3 && arguments[3] !== undefined ? arguments[3] : 2000;\n  var unsup' +
        'portedCb = arguments.length > 4 ? arguments[4] : undefined;\n\n  var failCallb' +
        'ack = function failCallback() {\n    failCb && failCb();\n  };\n\n  var succes' +
        'sCallback = function successCallback() {\n    successCb && successCb();\n  };' +
        '\n\n  var unsupportedCallback = function unsupportedCallback() {\n    unsuppor' +
        'tedCb && unsupportedCb();\n  };\n\n  var openUri = function openUri() {\n    i' +
        'f (browser.isFirefox()) {\n      var browserVersion = getBrowserVersion();\n\n' +
        '      if (browserVersion >= 64) {\n        openUriWithHiddenFrame(uri, failCal' +
        'lback, successCallback);\n      } else {\n        openUriUsingFirefox(uri, fai' +
        'lCallback, successCallback);\n      }\n    } else if (browser.isWhale()) {\n  ' +
        '    openUriWithTimeoutHack(uri, failCallback, successCallback);\n    } else if' +
        ' (browser.isChrome()) {\n      openUriWithTimeoutHack(uri, failCallback, succe' +
        'ssCallback);\n    } else if (browser.isOSX()) {\n      openUriWithHiddenFrame(' +
        'uri, failCallback, successCallback);\n    } else {\n      //not supported, imp' +
        'lement please\n      unsupportedCallback();\n    }\n  };\n\n  if (timeout) {\n' +
        '    DEFAULT_CUSTOM_PROTOCOL_FAIL_CALLBACK_TIMEOUT = timeout;\n  }\n\n  if (bro' +
        'wser.isEdge() || browser.isIE()) {\n    //for IE and Edge in Win 8 and Win 10' +
        '\n    openUriWithMsLaunchUri(uri, failCb, successCb);\n  } else {\n    if (doc' +
        'ument.hasFocus()) {\n      openUri();\n    } else {\n      var focusHandler = ' +
        'registerEvent(window, "focus", function () {\n        focusHandler.remove();\n' +
        '        openUri();\n      });\n    }\n  }\n};\n\nmodule.exports = protocolChec' +
        'k;\n\n//# sourceURL=webpack://customProtocolCheck/./index.js?'
    )
},
0: function (module, exports, __webpack_require__) {
    eval(
        'module.exports = __webpack_require__(/*! /Users/shahv/Viresh/work/rnd/custom-p' +
        'rotocol-check/index.js */"./index.js");\n\n\n//# sourceURL=webpack://customPro' +
        'tocolCheck/multi_./index.js?'
    )
}
})
})

如果Crossplay Launcher尚未安裝在用戶的PC上,請實現功能同時安裝Crossplay Launcher和應用程序。如果Crossplay Launcher已經安裝,請實現功能運行Crossplay Launcher

同時安裝 Crossplay Launcher 和應用程式

在安裝 Crossplay Launcher 的過程中,應用程式也可以自動安裝並運行。

實現下載 Crossplay Launcher 安裝檔案的事件:下載安裝檔案

在實現上述指南以創建應用啟動按鈕和彈出窗口後,實現用戶在彈出窗口上點擊下載按鈕時下載Crossplay Launcher安裝檔案hivecrossplay-fn.qpyou.cn/hivecrossplay/p/w/Installer.exe的功能。

實現下載 Crossplay Launcher 安裝文件的事件:自動應用安裝

當用戶點擊下載按鈕時,實現將 自動安裝 URI 複製到用戶的 PC 剪貼板的功能。在安裝 Crossplay Launcher 後,Crossplay Launcher 會自動搜索剪貼板中的遊戲安裝 URI。如果找到,它會安裝對應於 URI 的應用。如果在剪貼板中未找到遊戲安裝 URI,則僅在用戶的 PC 上安裝 Crossplay Launcher。要安裝應用,用戶需要返回網頁並再次點擊應用啟動按鈕。請參考 運行 Crossplay Launcher 獲取更多詳細信息。

獲取自動安裝 URI

Hive 控制台 (沙盒或商業) > 跨平台啟動器 > 應用管理 > 下載設置 > 啟動器安裝/啟動 URI獲取自動安裝URI

# Example
hivelauncher:?app_id=com.com2us.hivesdk.windows.microsoftstore.global.normal&start_point=9 f387268b8ea6c3e0016c8fd41562ed53d4c38338

URI 複製到剪貼簿

實現將獲得的自動安裝 URI 複製到用戶的 PC 剪貼簿空間。

運行跨平台啟動器

一旦跨平台啟動器安裝在用戶的PC上,當用戶在網頁上點擊應用啟動按鈕的事件發生時,執行以下JavaScript代碼。JavaScript代碼使用啟動URI啟動跨平台啟動器,並且跨平台啟動器會自動安裝並運行應用。

獲取啟動 URI

URI中獲取啟動Hive 控制台(沙盒或實時) > 跨平台啟動器 > 應用管理 > 下載設置 > 啟動器安裝/啟動 URI

# Example
hivelauncher:?app_id=com.com2us.hivesdk.windows.microsoftstore.global.normal&start_point=9

實現並運行 Javascript 代碼

當用戶的 PC 上安裝了 Crossplay Launcher 時,實現當用戶在網頁上點擊應用啟動按鈕時執行 JavaScript 代碼。

    event.preventDefault
        ? event.preventDefault()
        : (event.returnValue = false);
    window.customProtocolCheck({
        # Implement executing the launch URI.
        # The launch URI launches the Crossplay Launcher. The Crossplay Launcher automatically installs and runs the app.
    }, function (e) {
        console.log("FAIL");
        {
            # Handling Crossplay Launcher Execution Failure
        }
    }, function (e) {
        console.log("SUCCESS");
    }, 2500, function (e) {
        console.log("Not Supported");
        {
            # Handling No Response After 2500ms from Crossplay Launcher Execution Request
        }
    });

 

以下是按下“在PC上播放”按钮时的结果示例,这会触发JavaScript代码执行。当Crossplay Launcher运行时,浏览器通知会提示是否执行Crossplay Updater,该更新程序会更新Crossplay Launcher本身。

Note

Due to the difference between JAVA's URLEncoder.encode() and JavaScript's encodeURIComponent(), when implementing the execution of the app in JAVA, the replaceAll("\+", "%20") operation is required.