130 lines
48 KiB
HTML
130 lines
48 KiB
HTML
<!doctype html>
|
||
<html lang="zh-CN">
|
||
<head>
|
||
<meta charset="utf-8">
|
||
<meta name="viewport" content="width=device-width,initial-scale=1">
|
||
<meta name="theme-color" content="#e8decc">
|
||
<title>BD2 Client Studio</title>
|
||
<style>
|
||
:root{color-scheme:light;--paper:#eee7da;--ink:#382e26;--muted:#796c5d;--line:rgba(84,62,42,.18);--rust:#965437;--olive:#63704d;--bad:#9d4034;--ease:cubic-bezier(.22,1,.36,1);--serif:Georgia,'Songti SC','Noto Serif CJK SC',SimSun,serif;--sans:'Segoe UI','PingFang SC','Microsoft YaHei',sans-serif}
|
||
*{box-sizing:border-box}body{margin:0;background:#e7dcc7;color:var(--ink);font:14px/1.65 var(--sans);overflow-x:hidden}button,input{font:inherit}button{cursor:pointer}button:disabled{cursor:wait;opacity:.5}button,input,a{-webkit-tap-highlight-color:transparent}button:focus-visible,input:focus-visible,summary:focus-visible{outline:2px solid var(--rust);outline-offset:5px}[hidden]{display:none!important}::selection{background:#b9764c;color:#fff9ef}
|
||
.landscape{position:fixed;inset:0;z-index:-3;background:linear-gradient(130deg,#f1e9d9 0%,#e7d5b6 42%,#bc8665 100%);overflow:hidden}.landscape svg{position:absolute;width:100%;height:100%;transition:transform 2s var(--ease),opacity 1.6s;transform:scale(1.04);opacity:.87}.landscape:after{content:'';position:absolute;inset:0;background:linear-gradient(90deg,rgba(244,237,222,.8),rgba(240,228,207,.2) 65%,transparent),linear-gradient(0deg,rgba(73,48,31,.14),transparent 48%)}body[data-scene=server] .landscape svg{transform:scale(1.14) translateX(-2%)}body[data-scene=desk] .landscape svg{transform:scale(1.2) translateY(-3%);opacity:.27}body[data-scene=desk] .landscape:after{background:rgba(240,234,221,.55)}
|
||
.grain{position:fixed;inset:0;pointer-events:none;z-index:20;opacity:.038;background-image:url("data:image/svg+xml,%3Csvg viewBox='0 0 180 180' xmlns='http://www.w3.org/2000/svg'%3E%3Cfilter id='grain'%3E%3CfeTurbulence type='fractalNoise' baseFrequency='.82' numOctaves='3' stitchTiles='stitch'/%3E%3C/filter%3E%3Cpath fill='%23000' filter='url(%23grain)' d='M0 0h180v180H0z'/%3E%3C/svg%3E")}.sunlight{position:fixed;inset:-40%;pointer-events:none;z-index:-2;background:radial-gradient(ellipse at 70% 23%,#fff7d777,transparent 38%);animation:drift 18s ease-in-out infinite alternate}
|
||
.topbar{height:94px;max-width:1440px;margin:auto;padding:0 58px;display:flex;align-items:center;justify-content:space-between;border-bottom:1px solid var(--line);gap:24px}.brand{display:flex;gap:13px;align-items:center;flex-shrink:0}.emblem{width:36px;height:36px;border:1px solid #7d624a;transform:rotate(45deg);display:grid;place-items:center}.emblem span{font:20px var(--serif);transform:rotate(-45deg)}.brand-name{font-size:13px;letter-spacing:.19em;line-height:1.4}.brand-name small{display:block;font-size:9px;letter-spacing:.28em;color:var(--muted);margin-top:4px}.top-note{font-size:11px;color:var(--muted);letter-spacing:.07em}.text-btn{background:none;border:0;color:var(--muted);padding:8px 0;border-bottom:1px solid transparent}.text-btn:hover{color:var(--rust);border-bottom-color:var(--rust)}
|
||
main{max-width:1324px;margin:auto;padding:0 58px}.scene{position:relative}.intro-scene{min-height:calc(100svh - 178px);display:grid;grid-template-columns:1.1fr .9fr;align-items:center;gap:8%;padding:66px 0 82px}.eyebrow{font-size:10px;letter-spacing:.22em;text-transform:uppercase;display:flex;align-items:center;gap:12px;color:#81634c;margin-bottom:24px}.eyebrow:before{content:'';height:1px;width:32px;background:currentColor}h1,h2,h3,p{margin:0}h1{font:400 clamp(42px,4.8vw,68px)/1.28 var(--serif);letter-spacing:.035em}.lead{max-width:380px;font-size:14px;color:var(--muted);line-height:1.95;margin-top:26px}.intro-label{display:flex;gap:14px;align-items:center;margin-top:44px;font-size:11px;color:#8a7059}.intro-label i{height:1px;width:30px;background:#987759}.form-card{position:relative;background:linear-gradient(135deg,rgba(250,246,237,.89),rgba(238,225,203,.7));border:1px solid rgba(255,255,255,.6);box-shadow:0 24px 70px -35px #65472c77,inset 0 0 0 1px rgba(116,89,55,.035);backdrop-filter:blur(24px);border-radius:4px;padding:34px 32px}.card-number{font:italic 46px/1 var(--serif);color:#b2997e;opacity:.6;margin-bottom:30px}.form-card h2{font:400 23px var(--serif);margin-bottom:9px}.description{font-size:12px;line-height:1.8;color:var(--muted);margin-bottom:26px}code{font:11px/1.8 Consolas,monospace;overflow-wrap:anywhere}label.field-label{display:block;font-size:11px;margin-bottom:9px;color:#6f5c49}.input-row{display:flex;gap:8px}input[type=text]{width:100%;min-width:0;border:1px solid #aa927755;border-radius:3px;padding:13px 12px;background:#fffcf57d;color:var(--ink);outline:none;transition:background .2s,border-color .2s;font-size:12px}input[type=text]:focus{border-color:var(--rust);background:#fffaf0}.btn{border:1px solid #81664a55;border-radius:3px;padding:11px 18px;display:inline-flex;align-items:center;justify-content:center;gap:12px;min-height:43px;background:#fffcf533;color:var(--ink);transition:background .25s,box-shadow .25s,transform .25s;white-space:nowrap;font-size:12px}.btn:hover:not(:disabled){background:#fff9eb99;transform:translateY(-2px)}.btn.primary{background:#4b3a2c;color:#fff5e4;border-color:#4b3a2c}.btn.primary:hover:not(:disabled){background:#654932;box-shadow:0 6px 17px #4b3a2c24}.btn.rust{background:#985e40;color:#fff6e8;border-color:#985e40}.btn.rust:hover:not(:disabled){background:#ad6c49}.arrow{font-size:20px;font-weight:300;line-height:1}.wide{width:100%;justify-content:space-between;margin-top:28px;padding:14px 18px}.small-note{font-size:11px;color:#877460;margin-top:12px;line-height:1.8}.inline-result{font-size:11px;min-height:22px;margin-top:10px;color:var(--muted)}.inline-result.error{color:var(--bad)}.form-tools{display:flex;align-items:center;justify-content:space-between;margin-top:4px;gap:8px;font-size:11px}.server-back{margin-top:14px;text-align:center}.server-back .text-btn{font-size:11px}.bottom-bar{max-width:1324px;margin:auto;padding:0 58px 28px;display:flex;justify-content:space-between;align-items:center;gap:24px}.steps{display:flex;list-style:none;padding:0;margin:0;gap:26px;font-size:11px;color:#8f7d69}.steps li{display:flex;gap:8px;align-items:center}.steps .num{font:italic 14px var(--serif)}.steps li.active{color:#503b2b}.steps li.active:after{content:'';width:19px;height:1px;background:#86573b}.bottom-note{font-size:10px;color:#897963;letter-spacing:.04em}
|
||
.scene.is-entering .reveal{animation:reveal 1.3s var(--ease) both;animation-delay:calc(var(--i,0)*110ms)}.scene.is-leaving{animation:leave .55s cubic-bezier(.4,0,.6,1) both;pointer-events:none}.transition-veil{position:fixed;inset:0;background:linear-gradient(115deg,#eee4d4ed,#dfc7a8e8);z-index:30;pointer-events:none;opacity:0;backdrop-filter:blur(8px)}.transition-veil.play{animation:veil 1.25s ease-in-out both}.opening-curtain{position:fixed;inset:0;background:#e8decc;pointer-events:none;z-index:40;animation:opening 1.65s ease forwards}
|
||
.desk-scene{padding:46px 0 34px}.desk-header{display:flex;justify-content:space-between;align-items:end;gap:24px;margin-bottom:30px}.desk-header .eyebrow{margin-bottom:13px}.desk-header h1{font-size:42px}.desk-header p{margin-top:12px;font-size:12px;color:var(--muted)}.connection-chip{display:flex;align-items:center;gap:9px;font-size:11px;color:#74634f;border:1px solid var(--line);padding:8px 13px;border-radius:30px}.dot{width:6px;height:6px;border-radius:50%;background:#a99b85;display:inline-block;flex-shrink:0}.dot.good{background:var(--olive);box-shadow:0 0 0 4px #68744912}.dot.bad{background:var(--bad)}.desk-grid{display:grid;grid-template-columns:minmax(0,1fr) 298px;gap:24px}.main-panel{background:#f9f5ec94;border:1px solid #fff9;border-radius:4px;box-shadow:0 12px 50px -40px #5c412c55}.panel-section{padding:25px 28px;border-bottom:1px solid var(--line)}.panel-section:last-child{border-bottom:0}.section-title{display:flex;align-items:center;gap:11px;margin-bottom:22px}.section-title .index{font:italic 14px var(--serif);color:#a08569}.section-title h2{font-size:15px;font-weight:500}.section-title .end{margin-left:auto;font-size:10px;color:var(--muted)}.setting-row{display:flex;align-items:center;justify-content:space-between;gap:20px;margin-top:18px}.setting-name{font-size:10px;color:var(--muted);margin-bottom:4px}.setting-value{font-size:12px;overflow-wrap:anywhere}.setting-row>div{min-width:0}.setting-row .text-btn{font-size:11px;flex-shrink:0;color:var(--rust)}.route-options{display:grid;grid-template-columns:repeat(3,minmax(0,1fr));gap:10px}.route{position:relative;cursor:pointer}.route input{position:absolute;opacity:0;inset:0;width:100%;height:100%;margin:0}.route-content{display:block;position:relative;pointer-events:none;min-height:139px;padding:15px 14px;border:1px solid var(--line);border-radius:3px;transition:.25s;background:#f8f3e83d}.route-content .route-top{display:flex;align-items:center;justify-content:space-between;margin-bottom:16px}.route-code{font:10px var(--serif);letter-spacing:.08em;color:#98826b}.radio-dot{width:11px;height:11px;border-radius:50%;border:1px solid #aa9680;display:block}.route-content strong{font-size:12px;font-weight:500;display:block;margin-bottom:5px}.route-content small{font-size:10px;line-height:1.75;display:block;color:var(--muted)}.route input:checked+.route-content{border-color:#9e7551;background:#b9986920;box-shadow:inset 0 0 0 1px #9e755118}.route input:checked+.route-content .radio-dot{background:#916c4a;border:3px solid #e8d9c3;box-shadow:0 0 0 1px #916c4a}.route input:focus-visible+.route-content{outline:2px solid var(--rust);outline-offset:4px}.route input:disabled+.route-content{opacity:.55}.route-actions{display:flex;align-items:center;justify-content:space-between;gap:10px;margin-top:18px}.route-actions .text-btn{font-size:11px}.route-hint{font-size:10px;color:#927d64;margin-top:13px}.policy{font:11px/1.9 Consolas,monospace;white-space:pre-wrap;overflow-wrap:anywhere;background:#dfd2bc50;border-left:2px solid #a3835e;padding:13px;margin-top:16px}.prepare-note{font-size:11px;color:var(--muted);margin:-8px 0 19px;line-height:1.8}.prepare-actions{display:flex;gap:10px;flex-wrap:wrap}.prepare-actions .btn{flex:1}.status-panel{align-self:start;padding:25px 23px;background:#dfd1ba66;border:1px solid #9875501c;border-radius:4px}.status-title{display:flex;align-items:center;justify-content:space-between;margin-bottom:6px}.status-title h2{font:400 19px var(--serif)}.status-title span{font-size:9px;color:#927d65;letter-spacing:.1em}.check{display:flex;align-items:center;justify-content:space-between;padding:14px 0;border-bottom:1px solid var(--line);font-size:11px;gap:8px}.check span:last-child{color:#948471;font-size:10px}.check span.ok{color:var(--olive)}.check span.warn{color:var(--bad)}.status-foot{font-size:10px;color:#8d7a63;margin-top:14px;line-height:1.8}.log-wrap{border-top:1px solid var(--line);margin-top:24px;padding-top:19px}.log-heading{font-size:10px;color:#8d745b;display:flex;align-items:center;justify-content:space-between;margin-bottom:12px}.log{font-size:11px;color:#7e6c56;line-height:1.8;overflow-wrap:anywhere;min-height:65px}.log strong{display:block;color:var(--ink);font-size:12px;font-weight:500;margin-bottom:5px}.history{margin-top:14px;border-top:1px solid var(--line);padding-top:12px;font-size:10px;color:#806e59}.history summary{cursor:pointer}.history ol{list-style:none;padding:0;margin:10px 0 0;max-height:180px;overflow:auto}.history li{padding:7px 0;border-top:1px solid #a487651a;overflow-wrap:anywhere}.history time{display:block;color:#9a876f;font-size:9px}.history li.error{color:var(--bad)}.unsaved{font-size:10px;color:var(--rust);margin-top:12px}.desk-footer{display:flex;justify-content:space-between;gap:20px;margin-top:24px;color:#8a765e;font-size:10px}.exit-btn{font-size:11px}
|
||
.desk-actions{display:flex;align-items:center;gap:10px;flex-wrap:wrap;justify-content:flex-end}.desk-actions .btn{min-height:38px;padding:8px 16px}.local-resource{margin-top:14px;padding:14px;border:1px solid var(--line);background:#f8f3e83d;border-radius:3px}.local-resource[hidden]{display:none}.local-resource .small-note{margin:8px 0 0}
|
||
.toast{position:fixed;bottom:28px;left:50%;transform:translate(-50%,18px);max-width:min(590px,calc(100% - 32px));width:max-content;padding:13px 21px;background:#483a2e;color:#fff4e0;box-shadow:0 10px 40px #483a2e26;border:1px solid #c9ac82;border-radius:3px;font-size:12px;opacity:0;pointer-events:none;transition:.35s var(--ease);z-index:70}.toast.show{opacity:1;transform:translate(-50%,0)}.toast.error{background:#793e30;color:#fff0e3}dialog{width:min(490px,calc(100% - 32px));padding:30px;border:1px solid #fff9;border-radius:5px;background:#f0e7d7;color:var(--ink);box-shadow:0 30px 140px #37271955}dialog::backdrop{background:#40322266;backdrop-filter:blur(9px)}dialog[open]{animation:reveal .55s var(--ease)}.dialog-head{display:flex;align-items:center;justify-content:space-between;margin-bottom:25px}.dialog-head h2{font:24px var(--serif)}.dialog-close{background:none;border:0;font-size:23px;color:var(--muted);padding:0 7px}.dialog-actions{display:flex;justify-content:flex-end;margin-top:20px}.quit-scene{min-height:65svh;display:flex;flex-direction:column;justify-content:center;align-items:center;text-align:center}.quit-scene h1{font-size:44px}.quit-scene p{color:var(--muted);margin-top:20px;font-size:13px}
|
||
@keyframes reveal{from{opacity:0;filter:blur(10px);transform:translateY(24px) scale(.986)}to{opacity:1;filter:blur(0);transform:none}}@keyframes leave{to{opacity:0;filter:blur(8px);transform:translateY(-15px) scale(1.014)}}@keyframes veil{0%,100%{opacity:0}45%,58%{opacity:1}}@keyframes opening{0%,15%{opacity:1}100%{opacity:0}}@keyframes drift{to{transform:translate(-4%,3%) scale(1.05)}}
|
||
@media(min-width:1500px){.intro-scene{gap:13%}.form-card{padding:40px}.intro-scene h1{font-size:72px}}@media(max-width:1050px){.topbar{padding:0 30px}main,.bottom-bar{padding-left:30px;padding-right:30px}.intro-scene{gap:5%}.form-card{padding:27px 23px}.desk-grid{grid-template-columns:minmax(0,1fr) 250px;gap:16px}.panel-section{padding:23px 20px}.status-panel{padding:23px 18px}.route-content{padding:12px 10px}.top-note{display:none}}@media(max-width:760px){.topbar{height:77px;padding:0 22px}main,.bottom-bar{padding-left:22px;padding-right:22px}.intro-scene{grid-template-columns:1fr;gap:32px;padding:42px 0;min-height:auto}.intro-scene h1{font-size:43px}.eyebrow{margin-bottom:17px}.lead{margin-top:18px;max-width:100%}.intro-label{display:none}.form-card{padding:25px}.card-number{position:absolute;right:25px;top:25px;font-size:31px}.form-card h2{padding-right:45px}.bottom-bar{align-items:start;flex-direction:column;gap:15px}.steps{gap:21px}.desk-scene{padding-top:31px}.desk-header{align-items:start}.desk-header h1{font-size:34px}.connection-chip{font-size:10px;padding:6px 10px;margin-top:4px}.desk-grid{grid-template-columns:1fr}.status-panel{display:grid;grid-template-columns:1fr 1fr;gap:0 30px}.status-title{grid-column:1/-1}.log-wrap{margin-top:10px;border-top:0}.status-foot{margin-bottom:14px}.desk-footer{flex-direction:column;gap:7px}.bottom-note{font-size:9px}.landscape svg{width:180%;left:-60%}.desk-header p{max-width:240px}.route-content{min-height:145px}}@media(max-width:400px){main,.bottom-bar{padding-left:16px;padding-right:16px}.form-card{padding:23px 18px}.route-options{grid-template-columns:1fr}.route-content{min-height:0;padding:12px 14px}.route-content .route-top{float:right;margin:0 0 10px 20px;gap:12px}.route-content small{max-width:90%}.status-panel{display:block}.desk-header{flex-direction:column;gap:14px}.panel-section{padding:22px 16px}.btn{padding-left:13px;padding-right:13px}.steps{gap:15px}.route-actions{align-items:stretch}.prepare-actions{flex-direction:column}}
|
||
@media(prefers-reduced-motion:reduce){*,*:before,*:after{animation:none!important;transition:none!important;scroll-behavior:auto!important}.opening-curtain{display:none}}
|
||
</style>
|
||
</head>
|
||
<body data-scene="directory">
|
||
<div class="landscape" aria-hidden="true">
|
||
<svg viewBox="0 0 1600 1000" preserveAspectRatio="xMidYMid slice" xmlns="http://www.w3.org/2000/svg"><defs><linearGradient id="sky" x2="0" y2="1"><stop stop-color="#f3e6c9"/><stop offset=".7" stop-color="#dab88e"/><stop offset="1" stop-color="#b98560"/></linearGradient><linearGradient id="mesa" x2="0.2" y2="1"><stop stop-color="#b48360"/><stop offset="1" stop-color="#8f624b"/></linearGradient><linearGradient id="sand" x2=".6" y2="1"><stop stop-color="#b8946e"/><stop offset="1" stop-color="#674b39"/></linearGradient><filter id="soft"><feGaussianBlur stdDeviation="18"/></filter></defs><path fill="url(#sky)" d="M0 0h1600v1000H0z"/><circle cx="1150" cy="270" r="91" fill="#fff4ce" opacity=".4" filter="url(#soft)"/><circle cx="1150" cy="270" r="68" fill="#fff0c7" opacity=".8"/><path d="M0 640 240 590 370 610 520 558 670 580 820 510 950 560 1120 492 1280 560 1420 482 1600 530V1000H0Z" fill="#bea17f" opacity=".6"/><path d="m835 710 60-133 43-18 17-104 32-8 17-45 130-2 18 48 32 6 15 112 35 14 36 114 58 38 212 18V1000H790Z" fill="url(#mesa)" opacity=".65"/><path d="m1004 400 15 162 24 39-12 110 75 112 18-203-23-111 33-109z" fill="#d3a079" opacity=".38"/><path d="m1280 760 81-113 24-160 47-12 13-59 122 3 14 67 19 18v496h-320Z" fill="#9a6b4f" opacity=".6"/><path d="M0 746Q260 652 525 746T1030 775 1600 724V1000H0Z" fill="#c8aa80"/><path d="M0 869Q300 687 660 860T1300 814 1600 830V1000H0Z" fill="url(#sand)" opacity=".65"/><path d="M860 1000q185-97 375-124t365-13v137Z" fill="#826045" opacity=".7"/><path d="M870 998q146-128 235-165t69-90" fill="none" stroke="#e1c397" stroke-width="3" opacity=".24"/></svg>
|
||
</div>
|
||
<div class="sunlight" aria-hidden="true"></div><div class="grain" aria-hidden="true"></div><div class="opening-curtain" aria-hidden="true"></div><div id="veil" class="transition-veil" aria-hidden="true"></div>
|
||
<header class="topbar"><div class="brand"><div class="emblem" aria-hidden="true"><span>B</span></div><div class="brand-name">BD2 CLIENT STUDIO<small>CLIENT CONFIGURATION</small></div></div><div class="top-note">客户端设置工具 · 配置保存在本机</div><button class="text-btn exit-btn" id="quit">退出工具 ↗</button></header>
|
||
<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="" 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="" 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>
|
||
<div class="desk-grid"><div class="main-panel reveal" style="--i:1">
|
||
<section class="panel-section"><div class="section-title"><span class="index">01</span><h2>连接设置</h2><span class="end">随时可以修改</span></div><div class="setting-row"><div><div class="setting-name">游戏目录</div><div class="setting-value" id="directorySummary">尚未选择</div></div><button class="text-btn" id="editDirectory">修改目录 ↗</button></div><div class="setting-row"><div><div class="setting-name">服务器地址</div><div class="setting-value" id="serverSummary">尚未填写</div></div><button class="text-btn" id="editServer">修改地址 ↗</button></div></section>
|
||
<section class="panel-section"><div class="section-title"><span class="index">02</span><h2>资源来源</h2><span class="end">CDN</span></div><div class="route-options" role="radiogroup" aria-label="CDN 类型">
|
||
<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="" 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"></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>
|
||
</main>
|
||
<footer class="bottom-bar" id="journey"><ol class="steps" aria-label="设置进度"><li data-step="directory" class="active" aria-current="step"><span class="num">01</span>选择目录</li><li data-step="server"><span class="num">02</span>设置服务器</li><li data-step="desk"><span class="num">03</span>客户端工作台</li></ol><span class="bottom-note">BROWN DUST II · CLIENT UTILITY</span></footer>
|
||
<dialog id="editor" aria-labelledby="editorTitle"><div class="dialog-head"><h2 id="editorTitle">修改设置</h2><button class="dialog-close" id="closeEditor" aria-label="关闭设置">×</button></div><div id="editorContent"></div><p class="small-note">修改后请在工作台保存连接设置。</p><div class="dialog-actions"><button class="btn primary" id="doneEditor">完成修改</button></div></dialog>
|
||
<div class="toast" id="toast" role="status" aria-live="polite"></div>
|
||
<noscript><p style="padding:20px">请启用 JavaScript 以使用客户端设置工具。</p></noscript>
|
||
<script>
|
||
(()=>{
|
||
'use strict';
|
||
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']);
|
||
let detectedTimeZone='';try{detectedTimeZone=Intl.DateTimeFormat().resolvedOptions().timeZone||''}catch{}
|
||
const zhCN=CHINA_TIME_ZONES.has(detectedTimeZone);
|
||
const localized=(zh,en)=>zhCN?zh:en;
|
||
if(!zhCN){
|
||
document.documentElement.lang='en';
|
||
document.title='BD2 Client Studio';
|
||
const translations=new Map(Object.entries({
|
||
'客户端设置工具 · 配置保存在本机':'Client setup utility · Settings stay on this computer','退出工具 ↗':'Exit tool ↗','先从游戏目录':'Start with the game directory','开始。':'first.','选择已安装的游戏文件夹。':'Select the installed game folder.','我们会检查游戏程序和资源文件,再继续设置连接。':'We will inspect the game executable and assets before configuring the connection.','客户端配置':'Client configuration','选择游戏目录':'Select game directory','请选择包含':'Select the folder containing','的文件夹。':'.','游戏安装目录':'Game installation directory','浏览':'Browse','可直接填写路径,也可以浏览目录。':'Enter a path or browse for a directory.','检查目录':'Inspect directory','继续设置服务器':'Continue to server setup','此步骤只检查文件,不会修改游戏资源。':'This step only inspects files and does not modify game assets.','接下来,':'Next,','设置服务器。':'configure the server.','填写要连接的服务端地址。':'Enter the server you want to connect to.','进入工作台后,你可以选择资源来源、应用补丁和安装插件。':'In the workspace you can select a resource source, apply the patch, and install plugins.','目录已选择 · 之后仍可修改':'Directory selected · You can change it later','服务器地址':'Server address','填写完整地址,可以包含端口。':'Enter the complete origin, including a port when needed.','地址与端口':'Address and port','例如':'Example','不要添加':'Do not add','或其他路径。真实连接地址由 LI 插件读取。':'or another path. The LI plugin reads the actual connection address.','进入工作台':'Open workspace','返回目录设置':'Back to directory setup','客户端工作台':'Client workspace','管理连接设置,完成游戏启动前的准备。':'Manage connection settings and prepare the game client.','启动游戏':'Launch game','等待检查':'Waiting','连接设置':'Connection settings','随时可以修改':'Editable at any time','游戏目录':'Game directory','尚未选择':'Not selected','修改目录 ↗':'Change directory ↗','尚未填写':'Not entered','修改地址 ↗':'Change address ↗','资源来源':'Resource source','官方 CDN':'Official CDN','从官方节点下载资源,不占用服主的大文件流量。':'Download from official nodes without using the server owner’s bandwidth.','本机已下载资源':'Downloaded local resources','直接读取电脑上的 ServerData 和 GameData,不再下载相同文件。':'Read ServerData and GameData already stored on this computer.','服主资源':'Server-provided resources','由服务器提供资源地址;服主可自建 CDN,也可以反代官方 CDN。':'The server supplies resource URLs backed by either a private CDN or an official-CDN proxy.','本机 CDN 根目录':'Local CDN root directory','所选目录内必须同时包含':'The selected directory must contain both',' 和 ':' and ','。':'.','官方模式由客户端直连版本锁定的官方 CDN。':'Official mode connects directly to the release-locked official CDN.','验证资源来源 ↗':'Verify resource source ↗','保存连接设置':'Save connection settings','当前设置尚未保存。':'Current settings have not been saved.','补丁与插件':'Patch and plugins','操作前请先关闭游戏。补丁只修改内置 Intro 地址,并保留':'Close the game first. The patch only changes the built-in Intro address and retains a','备份。安装插件前,请先手动安装 BepInEx。':'backup. Install BepInEx manually before installing the plugins.','应用客户端补丁':'Apply client patch','安装 / 更新插件':'Install / update plugins','当前状态':'Current status','游戏程序':'Game executable','资源文件':'Asset file','需手动安装':'Manual install required','连接配置':'Connection configuration','尚未写入':'Not written','状态来自本机文件检查,不代表服务器在线。':'Status is based on local files and does not indicate server availability.','诊断日志:':'Diagnostic log:','重新检查 ↻':'Inspect again ↻','最近操作':'Recent operation','等待操作':'Waiting for an operation','选择游戏目录后即可开始检查。':'Select the game directory to begin.','查看操作记录':'View operation history','配置仅保存在你的电脑上':'Settings are stored only on your computer','不会保存账号凭据或服主密钥':'Account credentials and server-owner secrets are never stored','工具已退出':'Tool exited','现在可以关闭这个页面。':'You can close this page now.','选择目录':'Select directory','设置服务器':'Configure server','修改设置':'Edit settings','修改后请在工作台保存连接设置。':'Save changes from the workspace when finished.','完成修改':'Finish editing','请启用 JavaScript 以使用客户端设置工具。':'Enable JavaScript to use the client setup tool.','CDN 类型':'CDN source','客户端状态':'Client status','设置进度':'Setup progress','关闭设置':'Close settings'
|
||
}));
|
||
const walker=document.createTreeWalker(document.body,NodeFilter.SHOW_TEXT);let node;
|
||
while(node=walker.nextNode()){if(node.parentElement&&['SCRIPT','STYLE'].includes(node.parentElement.tagName))continue;let value=node.nodeValue;for(const [source,target] of translations)value=value.replaceAll(source,target);node.nodeValue=value}
|
||
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';
|
||
}
|
||
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;
|
||
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'}}
|
||
function sync(){const p=payload();$('directorySummary').textContent=p.game_directory||localized('尚未选择','Not selected');$('serverSummary').textContent=p.server_origin||localized('尚未填写','Not entered');$('localResource').hidden=p.cdn_mode!=='local';$('routeHint').textContent=p.cdn_mode==='official'?localized('官方模式由客户端直连版本锁定的官方 CDN。','Official mode connects directly to the release-locked official CDN.'):p.cdn_mode==='local'?localized('本机模式不会请求服务器资源接口;资源版本仍必须与当前客户端一致。','Local mode does not call the server resource API; resources must still match this client release.'):localized('服主自建与反代在客户端中是同一种来源,地址统一由服务器返回。','Private hosting and reverse proxying are one client mode; the server returns the resource URLs.');$('dirtyNotice').hidden=signature()===savedSignature;renderStatus()}
|
||
function applyStatus(s){if(!s)return;if(s.game_directory)$('gameDir').value=s.game_directory;for(const key of Object.keys(status))status[key]=Boolean(s[key]);sync();$('directoryResult').textContent=status.executable&&status.resources?localized('游戏程序和资源文件已检测到。','Game executable and asset file found.'):localized('检查已完成,请在工作台查看缺失项。','Inspection completed; review missing items in the workspace.');$('directoryResult').className='inline-result'+(status.executable&&status.resources?'':' error')}
|
||
function invalidate(directory=false){if(directory){for(const key of Object.keys(status))status[key]=null;$('directoryResult').textContent=localized('目录已修改,请重新检查。','The directory changed; inspect it again.')}$('policy').hidden=true;sync()}
|
||
function requireDirectory(){if(!payload().game_directory){$('gameDir').focus();throw new Error(localized('请先选择游戏安装目录','Select the game installation directory first'))}}
|
||
function requireOrigin(){const value=$('origin').value.trim();let u;try{u=new URL(value)}catch{throw new Error(localized('请输入完整服务器地址,例如 http://127.0.0.1:8080','Enter a complete server origin, for example http://127.0.0.1:8080'))}if(!['http:','https:'].includes(u.protocol)||u.username||u.password||u.search||u.hash||(u.pathname!=='/'&&u.pathname!==''))throw new Error(localized('服务器地址仅填写 http(s) 地址与端口,不要包含路径、查询参数或账号信息','Enter only an HTTP(S) origin and port, without a path, query, or credentials'));const loopback=['localhost','127.0.0.1','[::1]'].includes(u.hostname.toLowerCase());if(u.protocol!=='https:'&&!loopback)throw new Error(localized('公网或局域网服务器必须使用 HTTPS;只有本机 loopback 可以使用 HTTP','Public and LAN servers must use HTTPS; only loopback may use HTTP'));$('origin').value=value.replace(/\/$/,'');$('serverResult').textContent='';sync()}
|
||
function requireResources(){const p=payload();if(p.cdn_mode==='local'&&!p.local_resource_directory){$('localResourceDir').focus();throw new Error(localized('请选择包含 ServerData 和 GameData 的本机 CDN 目录','Select a local CDN directory containing ServerData and GameData'))}}
|
||
function setBusy(value){busy=value;document.querySelectorAll('button,input').forEach(e=>{e.disabled=value});document.body.setAttribute('aria-busy',String(value))}
|
||
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 navigateScene(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('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()});
|
||
$('editor').addEventListener('cancel',e=>{if(busy)e.preventDefault()});
|
||
$('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('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 navigateScene('server')});
|
||
$('serverBack').onclick=()=>{if(!busy)navigateScene('directory')};
|
||
$('serverNext').onclick=()=>action('serverNext',async()=>{requireOrigin();sync();await navigateScene('desk')});
|
||
$('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('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()}});
|
||
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 navigateScene('desk')}}}catch(error){show(error.message||String(error),true)}finally{setBusy(false)}}
|
||
initialize();
|
||
})();
|
||
</script>
|
||
</body>
|
||
</html>
|