feat(all): add verified gacha flows and native client studio
This commit is contained in:
@@ -3,10 +3,6 @@
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<meta name="viewport" content="width=device-width,initial-scale=1">
|
||||
<meta name="bd2-session" content="{{.Token}}">
|
||||
<meta name="bd2-platform" content="{{.Platform}}">
|
||||
<meta name="bd2-initial-cdn" content="{{.CDNMode}}">
|
||||
<meta name="bd2-auto-open" content="{{.AutoOpen}}">
|
||||
<meta name="theme-color" content="#e8decc">
|
||||
<title>BD2 Client Studio</title>
|
||||
<style>
|
||||
@@ -34,11 +30,11 @@ main{max-width:1324px;margin:auto;padding:0 58px}.scene{position:relative}.intro
|
||||
<main>
|
||||
<section class="scene intro-scene is-entering" id="directoryScene" aria-labelledby="directoryTitle">
|
||||
<div><div class="eyebrow reveal" style="--i:1">01 / GAME DIRECTORY</div><h1 class="reveal" style="--i:2" id="directoryTitle" tabindex="-1">先从游戏目录<br>开始。</h1><p class="lead reveal" style="--i:3">选择已安装的游戏文件夹。<br>我们会检查游戏程序和资源文件,再继续设置连接。</p><div class="intro-label reveal" style="--i:4"><i></i>BrownDust II · 客户端配置</div></div>
|
||||
<div class="form-card reveal" style="--i:4"><div class="card-number" aria-hidden="true">01</div><h2>选择游戏目录</h2><p class="description">请选择包含 <code>BrownDust II.exe</code> 的文件夹。</p><div id="directoryHome"><div id="directoryFields"><label class="field-label" for="gameDir">游戏安装目录</label><div class="input-row"><input id="gameDir" type="text" value="{{.GameDirectory}}" placeholder="例如 E:\Games\BrownDust II" autocomplete="off" spellcheck="false"><button class="btn" id="browse">浏览</button></div><div class="form-tools"><span id="directoryHint">可直接填写路径,也可以浏览目录。</span><button class="text-btn" id="inspect">检查目录</button></div><p class="inline-result" id="directoryResult" role="status"></p></div></div><button class="btn primary wide" id="directoryNext"><span>继续设置服务器</span><span class="arrow" aria-hidden="true">→</span></button><p class="small-note">此步骤只检查文件,不会修改游戏资源。</p></div>
|
||||
<div class="form-card reveal" style="--i:4"><div class="card-number" aria-hidden="true">01</div><h2>选择游戏目录</h2><p class="description">请选择包含 <code>BrownDust II.exe</code> 的文件夹。</p><div id="directoryHome"><div id="directoryFields"><label class="field-label" for="gameDir">游戏安装目录</label><div class="input-row"><input id="gameDir" type="text" value="" placeholder="例如 E:\Games\BrownDust II" autocomplete="off" spellcheck="false"><button class="btn" id="browse">浏览</button></div><div class="form-tools"><span id="directoryHint">可直接填写路径,也可以浏览目录。</span><button class="text-btn" id="inspect">检查目录</button></div><p class="inline-result" id="directoryResult" role="status"></p></div></div><button class="btn primary wide" id="directoryNext"><span>继续设置服务器</span><span class="arrow" aria-hidden="true">→</span></button><p class="small-note">此步骤只检查文件,不会修改游戏资源。</p></div>
|
||||
</section>
|
||||
<section class="scene intro-scene" id="serverScene" hidden aria-labelledby="serverTitle">
|
||||
<div><div class="eyebrow reveal" style="--i:1">02 / SERVER CONNECTION</div><h1 class="reveal" style="--i:2" id="serverTitle" tabindex="-1">接下来,<br>设置服务器。</h1><p class="lead reveal" style="--i:3">填写要连接的服务端地址。<br>进入工作台后,你可以选择资源来源、应用补丁和安装插件。</p><div class="intro-label reveal" style="--i:4"><i></i>目录已选择 · 之后仍可修改</div></div>
|
||||
<div class="form-card reveal" style="--i:4"><div class="card-number" aria-hidden="true">02</div><h2>服务器地址</h2><p class="description">填写完整地址,可以包含端口。</p><div id="serverHome"><div id="serverFields"><label class="field-label" for="origin">地址与端口</label><input id="origin" type="text" value="{{.ServerOrigin}}" placeholder="https://play.example.com" autocomplete="url" spellcheck="false" aria-describedby="originHint"><p class="small-note" id="originHint">例如 <code>http://192.168.1.8:8080</code><br>不要添加 <code>/game/</code> 或其他路径。真实连接地址由 LI 插件读取。</p><p class="inline-result" id="serverResult" role="status"></p></div></div><button class="btn primary wide" id="serverNext"><span>进入工作台</span><span class="arrow" aria-hidden="true">→</span></button><div class="server-back"><button class="text-btn" id="serverBack">← 返回目录设置</button></div></div>
|
||||
<div class="form-card reveal" style="--i:4"><div class="card-number" aria-hidden="true">02</div><h2>服务器地址</h2><p class="description">填写完整地址,可以包含端口。</p><div id="serverHome"><div id="serverFields"><label class="field-label" for="origin">地址与端口</label><input id="origin" type="text" value="" placeholder="https://play.example.com" autocomplete="url" spellcheck="false" aria-describedby="originHint"><p class="small-note" id="originHint">例如 <code>http://192.168.1.8:8080</code><br>不要添加 <code>/game/</code> 或其他路径。真实连接地址由 LI 插件读取。</p><p class="inline-result" id="serverResult" role="status"></p></div></div><button class="btn primary wide" id="serverNext"><span>进入工作台</span><span class="arrow" aria-hidden="true">→</span></button><div class="server-back"><button class="text-btn" id="serverBack">← 返回目录设置</button></div></div>
|
||||
</section>
|
||||
<section class="scene desk-scene" id="deskScene" hidden aria-labelledby="deskTitle">
|
||||
<header class="desk-header reveal" style="--i:0"><div><div class="eyebrow">03 / WORKSPACE</div><h1 id="deskTitle" tabindex="-1">客户端工作台</h1><p>管理连接设置,完成游戏启动前的准备。</p></div><div class="desk-actions"><div class="connection-chip"><span class="dot" id="pulse"></span><span id="overallStatus">等待检查</span></div><button class="btn primary" id="launch">启动游戏 <span aria-hidden="true">▶</span></button></div></header>
|
||||
@@ -48,9 +44,9 @@ main{max-width:1324px;margin:auto;padding:0 58px}.scene{position:relative}.intro
|
||||
<label class="route"><input type="radio" name="cdn" value="official" checked><span class="route-content"><span class="route-top"><span class="route-code">OFFICIAL</span><span class="radio-dot"></span></span><strong>官方 CDN</strong><small>从官方节点下载资源,不占用服主的大文件流量。</small></span></label>
|
||||
<label class="route"><input type="radio" name="cdn" value="local"><span class="route-content"><span class="route-top"><span class="route-code">LOCAL</span><span class="radio-dot"></span></span><strong>本机已下载资源</strong><small>直接读取电脑上的 ServerData 和 GameData,不再下载相同文件。</small></span></label>
|
||||
<label class="route"><input type="radio" name="cdn" value="server"><span class="route-content"><span class="route-top"><span class="route-code">SERVER</span><span class="radio-dot"></span></span><strong>服主资源</strong><small>由服务器提供资源地址;服主可自建 CDN,也可以反代官方 CDN。</small></span></label>
|
||||
</div><div class="local-resource" id="localResource" hidden><label class="field-label" for="localResourceDir">本机 CDN 根目录</label><div class="input-row"><input id="localResourceDir" type="text" value="{{.LocalResourceDirectory}}" placeholder="例如 E:\bd2\dl" autocomplete="off" spellcheck="false"><button class="btn" id="browseResources">浏览</button></div><p class="small-note">所选目录内必须同时包含 <code>ServerData</code> 和 <code>GameData</code>。</p></div><p class="route-hint" id="routeHint">官方模式由客户端直连版本锁定的官方 CDN。</p><div class="route-actions"><button class="text-btn" id="checkResources">验证资源来源 ↗</button><button class="btn" id="save">保存连接设置</button></div><p class="unsaved" id="dirtyNotice">当前设置尚未保存。</p><div class="policy" id="policy" role="status" hidden></div></section>
|
||||
</div><div class="local-resource" id="localResource" hidden><label class="field-label" for="localResourceDir">本机 CDN 根目录</label><div class="input-row"><input id="localResourceDir" type="text" value="" placeholder="例如 E:\bd2\dl" autocomplete="off" spellcheck="false"><button class="btn" id="browseResources">浏览</button></div><p class="small-note">所选目录内必须同时包含 <code>ServerData</code> 和 <code>GameData</code>。</p></div><p class="route-hint" id="routeHint">官方模式由客户端直连版本锁定的官方 CDN。</p><div class="route-actions"><button class="text-btn" id="checkResources">验证资源来源 ↗</button><button class="btn" id="save">保存连接设置</button></div><p class="unsaved" id="dirtyNotice">当前设置尚未保存。</p><div class="policy" id="policy" role="status" hidden></div></section>
|
||||
<section class="panel-section"><div class="section-title"><span class="index">03</span><h2>补丁与插件</h2></div><p class="prepare-note">操作前请先关闭游戏。补丁只修改内置 Intro 地址,并保留 <code>resources.assets.bak</code> 备份。安装插件前,请先手动安装 BepInEx。</p><div class="prepare-actions"><button class="btn rust" id="patch">应用客户端补丁 <span aria-hidden="true">↗</span></button><button class="btn primary" id="install">安装 / 更新插件 <span aria-hidden="true">↗</span></button></div></section>
|
||||
</div><aside class="status-panel reveal" style="--i:3" aria-label="客户端状态"><div class="status-title"><h2>当前状态</h2><span>LOCAL STATUS</span></div><div><div class="check"><span>游戏程序</span><span id="sExe">等待检查</span></div><div class="check"><span>资源文件</span><span id="sAssets">等待检查</span></div><div class="check"><span>BepInEx</span><span id="sBep">等待检查</span></div><div class="check"><span>连接配置</span><span id="sConfig">尚未写入</span></div><p class="status-foot">状态来自本机文件检查,不代表服务器在线。<br>诊断日志:<code id="persistentLogPath">{{.LogPath}}</code></p><button class="text-btn" style="font-size:11px" id="refresh">重新检查 ↻</button></div><div class="log-wrap"><div class="log-heading"><span>最近操作</span><time id="logTime">—</time></div><div class="log" id="log" role="status"><strong>等待操作</strong><span>选择游戏目录后即可开始检查。</span></div><details class="history"><summary>查看操作记录</summary><ol id="historyList"></ol></details></div></aside></div>
|
||||
</div><aside class="status-panel reveal" style="--i:3" aria-label="客户端状态"><div class="status-title"><h2>当前状态</h2><span>LOCAL STATUS</span></div><div><div class="check"><span>游戏程序</span><span id="sExe">等待检查</span></div><div class="check"><span>资源文件</span><span id="sAssets">等待检查</span></div><div class="check"><span>BepInEx</span><span id="sBep">等待检查</span></div><div class="check"><span>连接配置</span><span id="sConfig">尚未写入</span></div><p class="status-foot">状态来自本机文件检查,不代表服务器在线。<br>诊断日志:<code id="persistentLogPath"></code></p><button class="text-btn" style="font-size:11px" id="refresh">重新检查 ↻</button></div><div class="log-wrap"><div class="log-heading"><span>最近操作</span><time id="logTime">—</time></div><div class="log" id="log" role="status"><strong>等待操作</strong><span>选择游戏目录后即可开始检查。</span></div><details class="history"><summary>查看操作记录</summary><ol id="historyList"></ol></details></div></aside></div>
|
||||
<footer class="desk-footer reveal" style="--i:4"><span>BD2 CLIENT STUDIO <span aria-hidden="true">/</span> 配置仅保存在你的电脑上</span><span>不会保存账号凭据或服主密钥</span></footer>
|
||||
</section>
|
||||
<section class="scene quit-scene" id="quitScene" hidden aria-labelledby="quitTitle"><div class="eyebrow reveal">CLIENT STUDIO</div><h1 class="reveal" id="quitTitle" tabindex="-1">工具已退出</h1><p class="reveal">现在可以关闭这个页面。</p></section>
|
||||
@@ -61,10 +57,7 @@ main{max-width:1324px;margin:auto;padding:0 58px}.scene{position:relative}.intro
|
||||
<noscript><p style="padding:20px">请启用 JavaScript 以使用客户端设置工具。</p></noscript>
|
||||
<script>
|
||||
'use strict';
|
||||
const SESSION=document.querySelector('meta[name="bd2-session"]').content;
|
||||
const HOST_PLATFORM=document.querySelector('meta[name="bd2-platform"]').content;
|
||||
const INITIAL_CDN=document.querySelector('meta[name="bd2-initial-cdn"]').content;
|
||||
const AUTO_OPEN=document.querySelector('meta[name="bd2-auto-open"]').content==='true';
|
||||
let HOST_PLATFORM='',INITIAL_CDN='official',AUTO_OPEN=false;
|
||||
const $=id=>document.getElementById(id);
|
||||
const reduced=window.matchMedia('(prefers-reduced-motion: reduce)');
|
||||
const CHINA_TIME_ZONES=new Set(['Asia/Shanghai','Asia/Chongqing','Asia/Harbin','Asia/Urumqi','Asia/Hong_Kong','Asia/Macau','Asia/Taipei']);
|
||||
@@ -82,14 +75,15 @@ if(!zhCN){
|
||||
document.querySelectorAll('[aria-label]').forEach(element=>{let value=element.getAttribute('aria-label');for(const [source,target] of translations)value=value.replaceAll(source,target);element.setAttribute('aria-label',value)});
|
||||
$('gameDir').placeholder='Example: E:\\Games\\Brown Dust II';$('origin').placeholder='https://play.example.com';$('localResourceDir').placeholder='Example: E:\\bd2\\dl';
|
||||
}
|
||||
if(HOST_PLATFORM==='darwin'){$('gameDir').placeholder=localized('例如 /Applications/BrownDust II.app','Example: /Applications/BrownDust II.app');$('directoryScene').querySelector('.description').textContent=localized('请选择 BrownDust II.app 或它所在的文件夹。','Select BrownDust II.app or its parent folder.')}
|
||||
let scene='directory',busy=false,transitioning=false,toastTimer,editorKind=null,savedSignature=null;
|
||||
const status={executable:null,resources:null,bepinex:null,config:null};
|
||||
function payload(){const mode=document.querySelector('input[name="cdn"]:checked').value;return{game_directory:$('gameDir').value.trim(),server_origin:$('origin').value.trim(),cdn_mode:mode,local_resource_directory:mode==='local'?$('localResourceDir').value.trim():'',ui_language:zhCN?'zh-CN':'en'}}
|
||||
function signature(){return JSON.stringify(payload())}
|
||||
const backendMessages={'Selection cancelled':'已取消选择','Game client found':'已找到游戏客户端','Local resource directory found':'已找到本机资源目录','Game directory is valid':'游戏目录有效','The client will use the release-locked official CDN':'将由客户端直接连接版本锁定的官方 CDN','Local resources verified':'本机资源已验证','Server resource policy verified':'服务器资源策略已确认','Connection settings saved':'连接设置已保存','Client entry point patched; the original backup was retained':'客户端入口补丁完成;原文件备份已保留','Client patch is already complete; no asset file was rewritten':'客户端补丁已经完成,未再次改写资源文件','BD2 client plugins installed or updated':'BD2 客户端插件已安装或更新','BD2 client plugins are already up to date; no DLL was rewritten':'BD2 客户端插件已经是最新版本,未再次改写 DLL','Brown Dust II started':'Brown Dust II 已启动','Brown Dust II is already running':'Brown Dust II 已经在运行','Client tool exited':'客户端工具已退出'};
|
||||
const localizeMessage=message=>zhCN?(backendMessages[message]||message):message;
|
||||
async function api(path,body={}){const res=await fetch(path,{method:'POST',headers:{'Content-Type':'application/json','X-BD2-Session':SESSION},body:JSON.stringify(body)});let data;try{data=await res.json()}catch{throw new Error(localized('客户端工具返回了无法解析的响应','The client tool returned an unreadable response'))}if(!res.ok||!data.ok)throw new Error(localizeMessage(data.message)||localized(`操作失败(HTTP ${res.status})`,`Operation failed (HTTP ${res.status})`));if(data.message)data.message=localizeMessage(data.message);return data}
|
||||
const nativeMethods={browse:'Browse',browseResources:'BrowseResources',inspect:'Inspect',resources:'Resources',save:'Save',patch:'Patch',install:'Install',launch:'Launch'};
|
||||
function bridge(){const bindings=window.go&&window.go.app&&window.go.app.Studio;if(!bindings)throw new Error(localized('客户端原生接口尚未就绪','The native client interface is unavailable'));return bindings}
|
||||
async function api(operation,body={}){let data;try{data=await bridge()[nativeMethods[operation]](body)}catch(error){throw new Error(localizeMessage(error.message||String(error)))}if(!data||!data.ok)throw new Error(localizeMessage(data&&data.message)||localized('操作失败','Operation failed'));if(data.message)data.message=localizeMessage(data.message);return data}
|
||||
function show(message,error=false){message=message||(error?localized('操作未完成','Operation failed'):localized('操作已完成','Operation completed'));clearTimeout(toastTimer);const t=$('toast');t.textContent=message;t.className='toast show'+(error?' error':'');toastTimer=setTimeout(()=>t.className='toast',5200);$('log').replaceChildren();const title=document.createElement('strong');title.textContent=error?localized('操作未完成','Operation failed'):localized('操作已完成','Operation completed');const desc=document.createElement('span');desc.textContent=message;$('log').append(title,desc);const now=new Date(),time=now.toLocaleTimeString(zhCN?'zh-CN':'en-US',{hour:'2-digit',minute:'2-digit',second:'2-digit'});$('logTime').textContent=time;const row=document.createElement('li');if(error)row.className='error';const stamp=document.createElement('time');stamp.dateTime=now.toISOString();stamp.textContent=time;row.append(stamp,document.createTextNode(message));$('historyList').prepend(row);while($('historyList').children.length>20)$('historyList').lastElementChild.remove()}
|
||||
function mark(id,value,yes=localized('已就绪','Ready'),no=localized('未检测到','Not found')){const e=$(id);e.textContent=value===null?localized('等待检查','Waiting'):value?yes:no;e.className=value===null?'':value?'ok':'warn'}
|
||||
function renderStatus(){mark('sExe',status.executable);mark('sAssets',status.resources);mark('sBep',status.bepinex,localized('已安装','Installed'),localized('需手动安装','Manual install required'));mark('sConfig',status.config,localized('已写入','Written'),localized('尚未写入','Not written'));const checked=status.executable!==null&&status.resources!==null;const ready=status.executable&&status.resources;$('pulse').className='dot'+(checked?(ready?' good':' bad'):'');$('overallStatus').textContent=checked?(ready?localized('游戏文件已就绪','Game files ready'):localized('游戏文件待检查','Game files need attention')):localized('等待检查','Waiting');if(savedSignature&&signature()!==savedSignature){$('sConfig').textContent=localized('有未保存修改','Unsaved changes');$('sConfig').className='warn'}}
|
||||
@@ -103,7 +97,7 @@ function setBusy(value){busy=value;document.querySelectorAll('button,input').for
|
||||
async function action(id,fn){if(busy||transitioning)return;const b=$(id),old=b.innerHTML;setBusy(true);b.textContent=localized('请稍候…','Please wait…');try{await fn()}catch(e){const message=e.message||String(e);show(message,true);if(scene==='directory'||editorKind==='directory'){$('directoryResult').textContent=message;$('directoryResult').className='inline-result error'}if(scene==='server'||editorKind==='server'){$('serverResult').textContent=message;$('serverResult').className='inline-result error'}}finally{b.innerHTML=old;setBusy(false)}}
|
||||
const delay=ms=>new Promise(resolve=>setTimeout(resolve,ms));
|
||||
async function go(next){if(transitioning||scene===next)return;transitioning=true;const old=$(scene+'Scene'),target=$(next+'Scene');if(!reduced.matches){old.classList.add('is-leaving');$('veil').classList.add('play');await delay(570)}old.hidden=true;old.classList.remove('is-leaving','is-entering');scene=next;document.body.dataset.scene=next;target.hidden=false;target.classList.add('is-entering');$('journey').hidden=next==='desk'||next==='quit';document.querySelectorAll('[data-step]').forEach(e=>{const active=e.dataset.step===next;e.classList.toggle('active',active);if(active)e.setAttribute('aria-current','step');else e.removeAttribute('aria-current')});window.scrollTo({top:0,behavior:'instant'});$(next==='directory'?'directoryTitle':next==='server'?'serverTitle':next==='desk'?'deskTitle':'quitTitle').focus({preventScroll:true});if(!reduced.matches)await delay(680);$('veil').classList.remove('play');transitioning=false}
|
||||
async function inspect(){requireDirectory();const r=await api('/api/inspect',payload());applyStatus(r.data);show(r.message);return r}
|
||||
async function inspect(){requireDirectory();const r=await api('inspect',payload());applyStatus(r.data);show(r.message);return r}
|
||||
function openEditor(kind){if(busy||transitioning)return;editorKind=kind;$('editorTitle').textContent=kind==='directory'?localized('修改游戏目录','Change game directory'):localized('修改服务器地址','Change server address');$('editorContent').append($(kind+'Fields'));$('editor').showModal();$(kind==='directory'?'gameDir':'origin').focus()}
|
||||
function closeEditor(){if(busy)return;$('editor').close()}
|
||||
$('editor').addEventListener('close',()=>{if(editorKind)$(editorKind+'Home').append($(editorKind+'Fields'));editorKind=null;sync()});
|
||||
@@ -112,22 +106,22 @@ $('closeEditor').onclick=closeEditor;
|
||||
$('doneEditor').onclick=()=>{try{if(editorKind==='server')requireOrigin();else requireDirectory();closeEditor()}catch(e){show(e.message,true)}};
|
||||
$('editDirectory').onclick=()=>openEditor('directory');$('editServer').onclick=()=>openEditor('server');
|
||||
$('gameDir').addEventListener('input',()=>invalidate(true));$('origin').addEventListener('input',()=>{$('serverResult').textContent='';invalidate()});$('localResourceDir').addEventListener('input',()=>invalidate());document.querySelectorAll('input[name="cdn"]').forEach(e=>e.addEventListener('change',()=>invalidate()));
|
||||
$('browse').onclick=()=>action('browse',async()=>{const previous=payload().game_directory;const r=await api('/api/browse',payload());if(r.data){applyStatus(r.data);if(payload().game_directory!==previous)$('policy').hidden=true}show(r.message)});
|
||||
$('browseResources').onclick=()=>action('browseResources',async()=>{const r=await api('/api/browse-resources',payload());if(r.data){$('localResourceDir').value=r.data.local_directory||'';invalidate()}show(r.message)});
|
||||
$('browse').onclick=()=>action('browse',async()=>{const previous=payload().game_directory;const r=await api('browse',payload());if(r.data){applyStatus(r.data);if(payload().game_directory!==previous)$('policy').hidden=true}show(r.message)});
|
||||
$('browseResources').onclick=()=>action('browseResources',async()=>{const r=await api('browseResources',payload());if(r.data){$('localResourceDir').value=r.data.local_directory||'';invalidate()}show(r.message)});
|
||||
$('inspect').onclick=()=>action('inspect',inspect);$('refresh').onclick=()=>action('refresh',inspect);
|
||||
$('directoryNext').onclick=()=>action('directoryNext',async()=>{await inspect();sync();await go('server')});
|
||||
$('serverBack').onclick=()=>{if(!busy)go('directory')};
|
||||
$('serverNext').onclick=()=>action('serverNext',async()=>{requireOrigin();sync();await go('desk')});
|
||||
$('checkResources').onclick=()=>action('checkResources',async()=>{requireDirectory();requireOrigin();requireResources();const r=await api('/api/resources',payload()),p=r.data||{},missing=localized('未返回','Missing');$('policy').hidden=false;$('policy').textContent=p.mode==='official'?localized('OFFICIAL · 客户端使用版本锁定的官方地址','OFFICIAL · The client uses release-locked official URLs'):p.mode==='local'?`LOCAL · ${p.local_directory||payload().local_resource_directory}\nServerData · ${p.server_data_url||missing}\nGameData · ${p.game_data_url||missing}`:`SERVER · ${localized('由服务器提供资源地址','Resource URLs supplied by the server')}\nServerData · ${p.server_data_url||missing}\nGameData · ${p.game_data_url||missing}\nBundle · ${p.bundle_version||missing}\nGameData · ${p.game_data_version||missing}`;show(r.message)});
|
||||
$('checkResources').onclick=()=>action('checkResources',async()=>{requireDirectory();requireOrigin();requireResources();const r=await api('resources',payload()),p=r.data||{},missing=localized('未返回','Missing');$('policy').hidden=false;$('policy').textContent=p.mode==='official'?localized('OFFICIAL · 客户端使用版本锁定的官方地址','OFFICIAL · The client uses release-locked official URLs'):p.mode==='local'?`LOCAL · ${p.local_directory||payload().local_resource_directory}\nServerData · ${p.server_data_url||missing}\nGameData · ${p.game_data_url||missing}`:`SERVER · ${localized('由服务器提供资源地址','Resource URLs supplied by the server')}\nServerData · ${p.server_data_url||missing}\nGameData · ${p.game_data_url||missing}\nBundle · ${p.bundle_version||missing}\nGameData · ${p.game_data_version||missing}`;show(r.message)});
|
||||
function saved(){savedSignature=signature();status.config=true;sync()}
|
||||
$('save').onclick=()=>action('save',async()=>{requireDirectory();requireOrigin();requireResources();const r=await api('/api/save',payload());saved();show(r.message)});
|
||||
$('patch').onclick=()=>action('patch',async()=>{requireDirectory();requireOrigin();requireResources();const r=await api('/api/patch',payload());saved();show(r.message)});
|
||||
$('install').onclick=()=>action('install',async()=>{requireDirectory();requireOrigin();requireResources();const r=await api('/api/install',payload());status.bepinex=true;saved();show(r.message)});
|
||||
$('launch').onclick=()=>action('launch',async()=>{requireDirectory();requireOrigin();requireResources();const r=await api('/api/launch',payload());saved();show(r.message)});
|
||||
$('quit').onclick=()=>action('quit',async()=>{await api('/api/quit');clearTimeout(toastTimer);$('toast').className='toast';await go('quit');$('quit').hidden=true});
|
||||
$('save').onclick=()=>action('save',async()=>{requireDirectory();requireOrigin();requireResources();const r=await api('save',payload());saved();show(r.message)});
|
||||
$('patch').onclick=()=>action('patch',async()=>{requireDirectory();requireOrigin();requireResources();const r=await api('patch',payload());saved();show(r.message)});
|
||||
$('install').onclick=()=>action('install',async()=>{requireDirectory();requireOrigin();requireResources();const r=await api('install',payload());status.bepinex=true;saved();show(r.message)});
|
||||
$('launch').onclick=()=>action('launch',async()=>{requireDirectory();requireOrigin();requireResources();const r=await api('launch',payload());saved();show(r.message)});
|
||||
$('quit').onclick=()=>{if(!busy){clearTimeout(toastTimer);bridge().Quit().catch(error=>show(error.message||String(error),true))}};
|
||||
$('gameDir').addEventListener('keydown',e=>{if(e.key==='Enter'){e.preventDefault();(editorKind?$('inspect'):$('directoryNext')).click()}});$('origin').addEventListener('keydown',e=>{if(e.key==='Enter'){e.preventDefault();(editorKind?$('doneEditor'):$('serverNext')).click()}});
|
||||
const initialRadio=document.querySelector(`input[name="cdn"][value="${CSS.escape(INITIAL_CDN)}"]`);if(initialRadio)initialRadio.checked=true;
|
||||
sync();if($('gameDir').value.trim()){action('inspect',async()=>{await inspect();if(AUTO_OPEN){saved();await go('desk')}})}
|
||||
async function initialize(){setBusy(true);try{const initial=await bridge().Initialize();HOST_PLATFORM=initial.platform;INITIAL_CDN=initial.cdn_mode;AUTO_OPEN=initial.auto_open;$('gameDir').value=initial.game_directory||'';$('origin').value=initial.server_origin||'http://127.0.0.1:8080';$('localResourceDir').value=initial.local_resource_directory||'';$('persistentLogPath').textContent=initial.log_path||'';const initialRadio=document.querySelector(`input[name="cdn"][value="${CSS.escape(INITIAL_CDN)}"]`);if(initialRadio)initialRadio.checked=true;if(HOST_PLATFORM==='darwin'){$('gameDir').placeholder=localized('例如 /Applications/BrownDust II.app','Example: /Applications/BrownDust II.app');$('directoryScene').querySelector('.description').textContent=localized('请选择 BrownDust II.app 或它所在的文件夹。','Select BrownDust II.app or its parent folder.')}sync();if($('gameDir').value.trim()){await inspect();if(AUTO_OPEN){saved();await go('desk')}}}catch(error){show(error.message||String(error),true)}finally{setBusy(false)}}
|
||||
initialize();
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
|
||||
Reference in New Issue
Block a user