コンテンツにスキップ

アプリを提供するためのウェブページの準備

アプリサービスのウェブページを設定する

アプリユーザーは、アプリをインストールして実行するために以下の手順を踏みます:

 

  1. アプリが提供されているウェブページでアプリ起動ボタンをクリックし、Crossplay Launcherをダウンロードしてインストールします。
  2. Crossplay Launcherを実行します。
  3. Crossplay Launcherが自動的にアプリをダウンロードしてインストールします。
  4. Crossplay Launcherが自動的にアプリを更新して実行します。

 

以下は、Crossplay Launcherを使用してウェブページからアプリをサービスする全プロセスです。

 

 

アプリのサービス用ウェブページは、貴社によって独自に構築されなければなりません。ウェブページを構築する際にCrossplay Launcherを使用してアプリをサービスするには、以下の指示に従ってください。以下は、開発者がウェブページに含める必要のある重要な実装の例を示しています。

アプリサービス条件の確認

ユーザーがウェブページにアクセスしたとき、ユーザーのPC環境、例えばOSのバージョンがサポートされているか確認します。

  • サポートされているOS: Windows 10以上

条件を確認した後、問題がある場合は、エラーポップアップがユーザーに表示されるべきです。

アプリ起動ボタンとクロスプレイランチャーのダウンロードポップアップを実装する

ウェブページにアプリ起動ボタンを実装します。

 

クロスプレイランチャーのインストールファイルをダウンロードするためのポップアップウィンドウを実装します。ポップアップウィンドウには、クロスプレイランチャーのインストールガイドが含まれている必要があります。

 

ウェブページ上のアプリ起動ボタンをクリックすると、ポップアップウィンドウが表示されます。ポップアップウィンドウには、Crossplay Launcherのインストールファイルをダウンロードするためのボタンが必要です。

クロスプレイランチャーのインストールを確認する

ユーザーがウェブページのアプリ起動ボタンをクリックすると、ユーザーのPCクロスプレイランチャーがインストールされているかどうかを確認する必要があります。ウェブページで以下のJavaScriptコードを実行して、ユーザーのPCクロスプレイランチャーがインストールされているかどうかを確認します。

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 ' +
        'window.chromeは未定義\n    // そして新しいOpera 30はwindow.c' +
        'hromeに対してtrueを出力します\n    // しかし、window.oprが未定義でないか確認する必要があります\n    // そして新しいIE Edgeはwindow.chromeに対してtrueを出力します\n    // そしてiOS Chromeでない場合は確認します' +
        '    var isChromium = window.chrome;\n    var winNav = window.navigator;\n  ' +
        '  var vendorName = winNav.vendor;\n    var isOpera = typeof window.opr !== "未定義";\n    var isIEedge = winNav.userAgent.indexOf("Edge") > -1;\n    var ' +
        'isIOSChrome = winNav.userAgent.match("CriOS");\n    return isChromium !== null' +
        ' && typeof isChromium !== "未定義" && 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); //iframeで実行中のページを処理します(blurはトップレベルウィンドウに登録する必要があります)\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?'
    )
}
})
})

ユーザーのPCCrossplay Launcherがインストールされていない場合は、Crossplay Launcherとアプリを一緒にインストールする機能を実装してください。Crossplay Launcherがすでにインストールされている場合は、Crossplay Launcherを実行する機能を実装してください。

Crossplay Launcherとアプリの両方を同時にインストール

Crossplay Launcherのインストールプロセス中に、アプリも自動的にインストールされ、実行されることがあります。

クロスプレイランチャーインストールファイルのダウンロードイベントを実装する: インストールファイルをダウンロードする

アプリ起動ボタンとポップアップを作成するための上記のガイドを実装した後、ユーザーがポップアップのダウンロードボタンをクリックしたときに、クロスプレイランチャーインストールファイル、hivecrossplay-fn.qpyou.cn/hivecrossplay/p/w/Installer.exeをダウンロードできる機能を実装します。

クロスプレイランチャーインストールファイルのダウンロードイベントを実装する: 自動アプリインストール

ユーザーがダウンロードボタンをクリックすると、自動インストール URIをユーザーのPCのクリップボードにコピーする機能を実装します。クロスプレイランチャーをインストールした後、クロスプレイランチャーはクリップボード内をゲームインストールURIを自動的に検索します。見つかった場合、対応するアプリをURIに基づいてインストールします。ゲームインストールURIがクリップボードに見つからない場合、ユーザーのPCにはクロスプレイランチャーのみがインストールされます。アプリをインストールするには、ユーザーはウェブページに戻り、再度アプリ起動ボタンをクリックする必要があります。詳細については、クロスプレイランチャーを実行するCrossplay Launcherを参照してください。

自動インストールURIの取得

自動インストールURIHive コンソール (サンドボックスまたは商用) > クロスプレイランチャー > アプリ管理 > ダウンロード設定 > ランチャーインストール/起動URIから取得します。

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

URIをクリップボードにコピーする

取得した自動インストールのURIをユーザーのPCのクリップボードにコピーする機能を実装します。

クロスプレイランチャーを実行する

ユーザーのPCクロスプレイランチャーがインストールされたら、ユーザーがウェブページ上のアプリ起動ボタンをクリックするイベントが発生したときに、次のJavaScriptコードを実行します。このJavaScriptコードは、起動URIを使用してクロスプレイランチャーを起動し、クロスプレイランチャーは自動的にアプリをインストールして実行します。

ランチURIを取得する

URIHive コンソール (サンドボックスまたはライブ) > クロスプレイランチャー > アプリ管理 > ダウンロード設定 > ランチャーインストール/ランチURIから取得します。

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

(オプション) 実行URIにカスタムパラメータを使用する

開発者が望む場合、Hive コンソールから取得したゲーム起動 URI にカスタムパラメータを追加できます。以下は、カスタムパラメータが追加された起動 URI の例です。

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


ゲームの自動インストールURIは、元々Hiveコンソールから取得できます。しかし、ゲーム実行URIにカスタムパラメータを追加した場合は、自分で自動インストールURIを作成して使用する必要があります。以下に、実行URIをSHA1でハッシュ化し、このハッシュ値を実行URI自体に追加します。その後、ハッシュ値を含む全体の文字列を自動インストールURIとして使用します。

# Hash the execution URI with custom parameters.
hivelauncher:?app_id=com.com2us.hivesdk.windows.microsoftstore.global.normal&start_point=9&custom_param1=value1&custom_param2=value2 

# The hashed value is as follows:
CFF1F0C2FC15E4CF3C06A81A41E1E079B2BD7A9D

# Append the hash value to the execution URI as shown below. Use the entire string as the auto-installation URI.
hivelauncher:?app_id=com.com2us.hivesdk.windows.microsoftstore.global.normal&start_point=9&custom_param1=value1&custom_param2=value2 CFF1F0C2FC15E4CF3C06A81A41E1E079B2BD7A9D
Note

ゲームクライアントで実行URIに入力されたカスタムパラメータ値を確認できます。

JavaScriptコードを実装して実行する

ユーザーのPCCrossplay 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
        }
    });

 

以下は、「Play on 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.