Prepare webpage to serve app
Setting up the webpage for app service¶
App users go through the following steps to install and run the app:
- Click the app launch button on the webpage where the app is being serviced, and download and install the Crossplay Launcher.
- Run the Crossplay Launcher.
- The Crossplay Launcher automatically downloads and installs the app.
- The Crossplay Launcher automatically updates and runs the app.
The following is the entire process of servicing the app from the webpage using the Crossplay Launcher.
The webpage for servicing app can be provided in the following two ways:
- Games (https://withhive.com/)
- a webpage built by the company who developed the app.
This guide provides examples and guidance on what needs to be implemented on the webpage when using the Crossplay Launcher to service the app from a webpage built by the app developer.
Checking app service conditions¶
When a user accesses the webpage, check if the user's PC environment, such as the OS version, is supported.
- Supported OS: Windows 10 or above
After checking the conditions, if there are any issues, an error popup should be displayed to the user.
Implementing app launch button and Crossplay Launcher download popup¶
Implement the app launch button on the webpage.
Implement a popup window for downloading the Crossplay Launcher installation file. The popup window must include guides for installing the Crossplay Launcher.
Implement clicking the app launch button on the webpage displays the popup window. The popup window should have a button to download the Crossplay Launcher installation file.
Checking for Crossplay Launcher installation¶
When a user clicks the app launch button on the webpage, it's necessary to check if the Crossplay Launcher is installed on the user's PC. Execute the following JavaScript code on the webpage to check if the Crossplay Launcher is installed on the user's PC.
JavaScript Code
!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?'
)
}
})
})
If the Crossplay Launcher is not installed on the user's PC, implement the feature to install both the Crossplay Launcher and the app together. If the Crossplay Launcher was already installed, implement the feature to run the Crossplay Launcher.
Install both Crossplay Launcher and app together¶
During the installation process of the Crossplay Launcher, the app can also be automatically installed and run.
Implement event for downloading Crossplay Launcher installation file: download the installation file¶
After implementing the guides above to create the app launch button and popup, implement the feature for users to download the Crossplay Launcher installation file, hivecrossplay-fn.qpyou.cn/hivecrossplay/p/w/Installer.exe, when they click the download button on the popup.
Implement event for downloading Crossplay Launcher installation file: automatic app installation¶
When users click the download button, implement the feature to copy the Auto Installation URI to the user's PC clipboard. After installing the Crossplay Launcher, the Crossplay Launcher automatically searches the clipboard for the game installation URI. If found, it installs the app corresponding to the URI. If the game installation URI is not found in the clipboard, only the Crossplay Launcher is installed on the user's PC. To install the app, the user needs to return to the webpage and click the app launch button again. Refer to Run the Crossplay Launcher for more details.
Obtaining the auto installation URI¶
Obtain the automatic installation URI from Hive Console (Sandbox or Commercial) > Crossplay Launcher > App Management > Download Setting > Launcher Installation/Launch URI.
# Example
hivelauncher:?app_id=com.com2us.hivesdk.windows.microsoftstore.global.normal&start_point=9 f387268b8ea6c3e0016c8fd41562ed53d4c38338
Copying the URI to clipboard¶
Implement copying the obtained automatic installation URI to the user's PC clipboard space.
Run the Crossplay Launcher¶
Once the Crossplay Launcher is installed on the user's PC, execute the following JavaScript code when an event that the user clicks the app launch button on the webpage occurs. The JavaScript code launches the Crossplay Launcher with the launch URI, and the Crossplay Launcher automatically installs and runs the app.
Obtain the launch URI¶
Obtain the launch URI from Hive Console (Sandbox or Live) > Crossplay Launcher > App Management > Download Setting > Launcher Installation/Launch URI.
# Example
hivelauncher:?app_id=com.com2us.hivesdk.windows.microsoftstore.global.normal&start_point=9
Implement and run Javascript code¶
When the Crossplay Launcher is installed on the user's PC, implement the execution of JavaScript code when the user clicks the app launch button on the webpage.
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
}
});
Below is an example of the result when the "Play on PC" button is pressed, triggering JavaScript code execution. As the Crossplay Launcher in run, a browser notification prompts whether to execute the Crossplay Updater, which updates the Crossplay Launcher itself.
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.