diff --git a/assets/launcher-ui/package-lock.json b/assets/launcher-ui/package-lock.json index 6bc6ca8..87021e2 100644 --- a/assets/launcher-ui/package-lock.json +++ b/assets/launcher-ui/package-lock.json @@ -14,6 +14,7 @@ "@fortawesome/free-solid-svg-icons": "^6.6.0", "@fortawesome/vue-fontawesome": "^3.0.8", "@vueuse/sound": "^2.0.1", + "dedent": "^1.5.3", "saucer-app": "^4.2.3", "vue": "^3.4.29", "vue-router": "^4.3.3" @@ -21,6 +22,7 @@ "devDependencies": { "@tsconfig/node20": "^20.1.4", "@types/node": "^20.14.5", + "@types/wicg-file-system-access": "^2023.10.5", "@vitejs/plugin-vue": "^5.0.5", "@vitejs/plugin-vue-jsx": "^4.0.0", "@vue/tsconfig": "^0.5.1", @@ -1427,6 +1429,12 @@ "undici-types": "~5.26.4" } }, + "node_modules/@types/wicg-file-system-access": { + "version": "2023.10.5", + "resolved": "https://registry.npmmirror.com/@types/wicg-file-system-access/-/wicg-file-system-access-2023.10.5.tgz", + "integrity": "sha512-e9kZO9kCdLqT2h9Tw38oGv9UNzBBWaR1MzuAavxPcsV/7FJ3tWbU6RI3uB+yKIDPGLkGVbplS52ub0AcRLvrhA==", + "dev": true + }, "node_modules/@typescript-eslint/parser": { "version": "7.16.1", "resolved": "https://registry.npmmirror.com/@typescript-eslint/parser/-/parser-7.16.1.tgz", @@ -2293,6 +2301,19 @@ } } }, + "node_modules/dedent": { + "version": "1.5.3", + "resolved": "https://registry.npmmirror.com/dedent/-/dedent-1.5.3.tgz", + "integrity": "sha512-NHQtfOOW68WD8lgypbLA5oT+Bt0xXJhiYvoR6SmmNXZfpzOGXwdKWmcwG8N7PwVVWV3eF/68nmD9BaJSsTBhyQ==", + "peerDependencies": { + "babel-plugin-macros": "^3.1.0" + }, + "peerDependenciesMeta": { + "babel-plugin-macros": { + "optional": true + } + } + }, "node_modules/deep-is": { "version": "0.1.4", "resolved": "https://registry.npmmirror.com/deep-is/-/deep-is-0.1.4.tgz", diff --git a/assets/launcher-ui/package.json b/assets/launcher-ui/package.json index a9c438d..3d1b7f2 100644 --- a/assets/launcher-ui/package.json +++ b/assets/launcher-ui/package.json @@ -18,6 +18,7 @@ "@fortawesome/free-solid-svg-icons": "^6.6.0", "@fortawesome/vue-fontawesome": "^3.0.8", "@vueuse/sound": "^2.0.1", + "dedent": "^1.5.3", "saucer-app": "^4.2.3", "vue": "^3.4.29", "vue-router": "^4.3.3" @@ -25,6 +26,7 @@ "devDependencies": { "@tsconfig/node20": "^20.1.4", "@types/node": "^20.14.5", + "@types/wicg-file-system-access": "^2023.10.5", "@vitejs/plugin-vue": "^5.0.5", "@vitejs/plugin-vue-jsx": "^4.0.0", "@vue/tsconfig": "^0.5.1", diff --git a/assets/launcher-ui/src/assets/base.css b/assets/launcher-ui/src/assets/base.css index 42cda74..aebb5e3 100644 --- a/assets/launcher-ui/src/assets/base.css +++ b/assets/launcher-ui/src/assets/base.css @@ -1,3 +1,13 @@ +@font-face { + font-family: 'Noble Scarlet'; + src: url('./msyhsb.woff2') format('woff2'); +} + +@font-face { + font-family: 'SGM'; + src: url('./sgm.woff2') format('woff2'); +} + *, *::before, *::after { @@ -8,27 +18,24 @@ user-select: none; -webkit-user-drag: none; color-scheme: dark; + font-family: + SGM, + 'Noble Scarlet', + 'Segoe UI', + sans-serif; } -body { - background: #212121; - line-height: 1.6; +option { font-family: - Inter, - -apple-system, - BlinkMacSystemFont, + 'Noble Scarlet', 'Segoe UI', - Roboto, - Oxygen, - Ubuntu, - Cantarell, - 'Fira Sans', - 'Droid Sans', - 'Helvetica Neue', sans-serif; +} + +body { + background: #212121; + line-height: 1.6; font-size: 15px; - text-rendering: optimizeLegibility; - -webkit-font-smoothing: antialiased; - -moz-osx-font-smoothing: grayscale; + text-rendering: optimizeSpeed; overflow: hidden; -} +} \ No newline at end of file diff --git a/assets/launcher-ui/src/assets/msyhsb.woff2 b/assets/launcher-ui/src/assets/msyhsb.woff2 new file mode 100644 index 0000000..0e36ede Binary files /dev/null and b/assets/launcher-ui/src/assets/msyhsb.woff2 differ diff --git a/assets/launcher-ui/src/assets/sgm.woff2 b/assets/launcher-ui/src/assets/sgm.woff2 new file mode 100644 index 0000000..cef0579 Binary files /dev/null and b/assets/launcher-ui/src/assets/sgm.woff2 differ diff --git a/assets/launcher-ui/src/components/Alert.vue b/assets/launcher-ui/src/components/Alert.vue index dd85057..2a5a41c 100644 --- a/assets/launcher-ui/src/components/Alert.vue +++ b/assets/launcher-ui/src/components/Alert.vue @@ -5,15 +5,16 @@ const message = ref(''); const color = ref(''); const element: Ref = ref(undefined); +const queue: { msg: string, colour: string, timeout: number }[] = []; let timer: number | undefined = undefined; -window.laochan.alert.__cb = (msg, colour, timeout) => { +function show(data: { msg: string, colour: string, timeout: number }) { if (!element.value) return; clearTimeout(timer); - color.value = colour; - message.value = msg; + color.value = data.colour; + message.value = data.msg; element.value.classList.remove('out'); element.value.classList.add('in'); @@ -24,8 +25,22 @@ window.laochan.alert.__cb = (msg, colour, timeout) => { element.value.classList.add('out'); clearTimeout(timer); - timer = undefined; - }, timeout); + + // wait for out animation + setTimeout(() => { + timer = undefined; + + const next = queue.shift(); + if (next) show(next); + }, 250); + }, data.timeout); +}; + +window.laochan.alert.__cb = (msg, colour, timeout) => { + if (timer === undefined) + show({ msg, colour, timeout }); + else + queue.push({ msg, colour, timeout }); }; @@ -76,7 +91,7 @@ window.laochan.alert.__cb = (msg, colour, timeout) => { .in { animation-name: in; - animation-duration: 0.5s; + animation-duration: 0.25s; animation-direction: normal; animation-fill-mode: both; animation-timing-function: ease; @@ -84,7 +99,7 @@ window.laochan.alert.__cb = (msg, colour, timeout) => { .out { animation-name: out; - animation-duration: 0.5s; + animation-duration: 0.25s; animation-direction: normal; animation-fill-mode: both; animation-timing-function: ease; diff --git a/assets/launcher-ui/src/components/Version.vue b/assets/launcher-ui/src/components/Version.vue index 131c81f..cdfcdb0 100644 --- a/assets/launcher-ui/src/components/Version.vue +++ b/assets/launcher-ui/src/components/Version.vue @@ -21,7 +21,7 @@ function num() { z-index: 30; left: 2em; bottom: 1em; - opacity: 60%; - font-family: 'Courier New', Courier, monospace; + color: #555; + mix-blend-mode: screen; } diff --git a/assets/launcher-ui/src/env.d.ts b/assets/launcher-ui/src/env.d.ts index 4e752d8..72cd117 100644 --- a/assets/launcher-ui/src/env.d.ts +++ b/assets/launcher-ui/src/env.d.ts @@ -3,6 +3,12 @@ interface Ref { value: T; } +interface DisplayMode { + width: number; + height: number; + hz: number; +} + interface Window { saucer: { _idc: number; @@ -38,6 +44,8 @@ interface Window { /// -2 = failed to get default audio device /// -3 = failed to active wasapi audio client checkWasapiDevice: () => Promise; + queryDisplayModes: () => Promise; + selfPath: () => Promise; num: () => void; alert: { show: (message: string, color: string, timeout: number) => void; diff --git a/assets/launcher-ui/src/main.ts b/assets/launcher-ui/src/main.ts index 9bcf48f..7230a9a 100644 --- a/assets/launcher-ui/src/main.ts +++ b/assets/launcher-ui/src/main.ts @@ -58,6 +58,16 @@ window.laochan = { checkWasapiDevice() { return window.saucer.call('checkWasapiDeviceStatus', []); }, + queryDisplayModes() { + return window.saucer.call('queryDisplayModes', []) + .then(modes => modes.map(v => { + const [width, height, hz] = JSON.parse(v); + return { width, height, hz }; + })); + }, + selfPath: () => { + return window.saucer.call('selfPath', []); + }, num: () => {}, ctx: { gamePaths: ref([[], [], []]), diff --git a/assets/launcher-ui/src/modules/iidx.ts b/assets/launcher-ui/src/modules/iidx.ts index 1165e17..7dc7328 100644 --- a/assets/launcher-ui/src/modules/iidx.ts +++ b/assets/launcher-ui/src/modules/iidx.ts @@ -1,11 +1,11 @@ import { ref, type Ref } from "vue"; import { launcher } from "./launcher"; +import dedent from "dedent"; export enum IIDXDisplayMode { Fullscreen = 0, - Windowed720p = 1, - Windowed900p = 2, - Windowed1080p = 3, + BorderlessWindowed = 1, + Windowed = 1, } export enum IIDXSoundMode { @@ -21,6 +21,10 @@ export enum IIDXLanguage { export interface IIDXConfig { displayMode: IIDXDisplayMode; + resolution: { + w: number; + h: number; + }; soundMode: IIDXSoundMode; asioDevice: string; useGsm: boolean; @@ -38,7 +42,7 @@ export class IIDX { installed() { return !!window.laochan.ctx.gamePaths.value[0].length; } - + get installPath() { if (!this.installed()) { return; @@ -61,7 +65,8 @@ export class IIDX { const { devices } = await window.laochan.getAsioDeviceList(); this._config.value = { - displayMode: IIDXDisplayMode.Windowed1080p, + displayMode: IIDXDisplayMode.BorderlessWindowed, + resolution: { w: 0, h: 0 }, soundMode: IIDXSoundMode.Wasapi, asioDevice: devices[0], useGsm: true, @@ -94,13 +99,15 @@ export class IIDX { this._config.value = JSON.parse(configJson); } - if (!this._config.value || - this._config.value.asioDevice == undefined || + if (!this._config.value || + this._config.value.asioDevice == undefined || this._config.value.displayMode === undefined || + this._config.value.resolution === undefined || this._config.value.soundMode === undefined || - this._config.value.useGsm === undefined || + this._config.value.useGsm === undefined || this._config.value.language === undefined ) { + window.laochan.alert.show('IIDX 设置已被重置, 请前往额外设置重新设置', '#B64040', 2000); await this.resetConfig(); } @@ -121,6 +128,9 @@ export class IIDX { window.laochan.setParam('IIDX_SOUND_MODE', JSON.stringify(config.soundMode)), window.laochan.setParam('IIDX_USE_GSM', JSON.stringify(+config.useGsm)), window.laochan.setParam('IIDX_LANGUAGE', JSON.stringify(+config.language)), + + window.laochan.setParam('IIDX_RESOLTION_W', JSON.stringify(config.resolution.w)), + window.laochan.setParam('IIDX_RESOLTION_H', JSON.stringify(config.resolution.h)), ]); } @@ -155,6 +165,45 @@ export class IIDX { window.laochan.shellExecute(installPath + '\\launcher\\modules\\bm2dx_updater.exe'); } + + async generateBat() { + const config = this._config.value; + if (!config) { + return ''; + } + + const selfPath = await window.laochan.selfPath(); + + const result = dedent` + REM generated by Laochan Launcher + + ${await launcher.exportBatParams()} + + REM IIDX Options + SET IIDX_ASIO_DEVICE="${config.asioDevice}" + SET IIDX_DISPLAY_MODE=${config.displayMode} + SET IIDX_SOUND_MODE=${config.soundMode} + SET IIDX_USE_GSM=${+config.useGsm} + SET IIDX_LANGUAGE=${+config.language} + SET IIDX_RESOLTION_W=${config.resolution.w} + SET IIDX_RESOLTION_H=${config.resolution.h} + + start "" "${selfPath}" "-iidx" + `; + + const handle = await window.showSaveFilePicker({ + suggestedName: 'laochan-iidx.bat', + startIn: 'desktop', + types: [{ + description: 'Batch File', + accept: { 'application/bat': ['.bat', '.cmd'] } + }] + }) + + const writable = await handle.createWritable(); + await writable.write(result); + await writable.close(); + } }; export const iidx = new IIDX(); diff --git a/assets/launcher-ui/src/modules/launcher.ts b/assets/launcher-ui/src/modules/launcher.ts index d24337a..dc6c9eb 100644 --- a/assets/launcher-ui/src/modules/launcher.ts +++ b/assets/launcher-ui/src/modules/launcher.ts @@ -1,3 +1,4 @@ +import dedent from "dedent"; import { ref } from "vue"; export interface LauncherConfig { @@ -80,6 +81,19 @@ export class Launcher { await window.laochan.setParam('LAOCHAN_ENABLE_CONSOLE', JSON.stringify(+this._config.value.enableConsole)); await window.laochan.setParam('LAOCHAN_ENABLE_STEAM_OVERLAY', JSON.stringify(+this._config.value.enableSteamOverlay)); } + + async exportBatParams() { + if (!this._config.value) + return; + + return dedent` + REM Laochan Launcher Params + SET LAOCHAN_TOKEN="${this._config.value.token}" + SET LAOCHAN_SERVER_URL="${this._config.value.serverUrl}" + SET LAOCHAN_ENABLE_CONSOLE=${+this._config.value.enableConsole} + SET LAOCHAN_ENABLE_STEAM_OVERLAY=${+this._config.value.enableSteamOverlay} + ` + } } export const launcher = new Launcher(); diff --git a/assets/launcher-ui/src/views/HomeView.vue b/assets/launcher-ui/src/views/HomeView.vue index 55d3470..504da58 100644 --- a/assets/launcher-ui/src/views/HomeView.vue +++ b/assets/launcher-ui/src/views/HomeView.vue @@ -36,6 +36,8 @@ import { sdvx } from '@/modules/sdvx';
游戏设置
更新器
自定义选项
+
+
生成快速启动 BAT
@@ -78,8 +80,9 @@ main>div { left: 0; width: 100%; height: 100%; + background-color: rgba(0, 0, 0, 0.5); background-position: center; - backdrop-filter: brightness(50%); + filter: brightness(0.5); transition: 0.1s ease; z-index: -1; } @@ -87,16 +90,12 @@ main>div { .game>.text { text-align: center; margin: auto; - margin-top: 250px; - font-size: 24px; - font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif; + margin-top: 225px; + font-size: 22px; font-weight: 100; transition: 0.1s ease; -} - -.game:not(.disable):hover>.text { - font-weight: normal; - text-shadow: 0 0 10px black; + color: #ccc; + mix-blend-mode: screen; } .game:not(.disable):hover { @@ -140,6 +139,15 @@ main>div { color: #fff; } +.game>.options>.gap { + height: 2em; +} + +.game>.options>.opt.small { + padding: 0.5em; + font-size: 10px; +} + @for $i from 1 through 10 { .game>.options>.opt:nth-child(#{$i}) { margin-right: calc($i * 9px); @@ -157,10 +165,9 @@ main>div { .sdvx>.background { background-image: url(@/assets/sdvx.jpg); - filter: brightness(0.4); } -.sdvx:hover>.background { - filter: brightness(0.5); +.game:hover>.background { + filter: brightness(0.65); } diff --git a/assets/launcher-ui/src/views/IIDXSettingView.vue b/assets/launcher-ui/src/views/IIDXSettingView.vue index 51b463e..9194647 100644 --- a/assets/launcher-ui/src/views/IIDXSettingView.vue +++ b/assets/launcher-ui/src/views/IIDXSettingView.vue @@ -1,13 +1,25 @@ - + +
diff --git a/src/client/std_include.hpp b/src/client/std_include.hpp index a8c9cc7..7531b86 100644 --- a/src/client/std_include.hpp +++ b/src/client/std_include.hpp @@ -107,8 +107,8 @@ #pragma comment(lib, "winhttp.lib") #pragma comment(lib, "Version.lib") #pragma comment(lib, "dwmapi.lib") -// prevent loading old client -#pragma comment(lib, "dinput8.lib") +#pragma comment(lib, "dxgi.lib") +#pragma comment(lib, "d3d9.lib") #include "resource.hpp"