diff --git a/apps/app-frontend/public/models/netherstar.png b/apps/app-frontend/public/models/netherstar.png new file mode 100644 index 0000000..9a84733 Binary files /dev/null and b/apps/app-frontend/public/models/netherstar.png differ diff --git a/apps/app-frontend/src/components/home/HomePinnedServers.vue b/apps/app-frontend/src/components/home/HomePinnedServers.vue index c3e26a7..dd07f0a 100644 --- a/apps/app-frontend/src/components/home/HomePinnedServers.vue +++ b/apps/app-frontend/src/components/home/HomePinnedServers.vue @@ -27,7 +27,6 @@ import { kill } from '@/helpers/instance' import type { GameInstance } from '@/helpers/types' import { type ServerWorld, - PROTECTED_SERVER_ADDRESS, set_world_display_status, start_join_server, type WorldWithInstance, @@ -79,10 +78,6 @@ const messages = defineMessages({ id: 'app.home.servers.more-options', defaultMessage: 'More options', }, - protectedServerName: { - id: 'app.home.servers.protected-name', - defaultMessage: 'Starlight Server', - }, }) const startingServerKey = ref(null) @@ -90,29 +85,13 @@ const startingServerKey = ref(null) const instanceById = computed( () => new Map(props.instances.map((instance) => [instance.id, instance])), ) -const servers = computed(() => { - const favoriteServers = favoriteWorlds.value.flatMap((world) => { - if (world.type !== 'server' || world.address === PROTECTED_SERVER_ADDRESS) return [] +const servers = computed(() => + favoriteWorlds.value.flatMap((world) => { + if (world.type !== 'server') return [] const instance = instanceById.value.get(world.instance_id) return instance ? [{ instance, world: world as ServerWorld & WorldWithInstance }] : [] - }) - const protectedInstance = - props.instances.find((instance) => instance.install_stage === 'installed') ?? props.instances[0] - if (!protectedInstance) return favoriteServers - - const protectedServer: ServerWorld & WorldWithInstance = { - instance_id: protectedInstance.id, - name: formatMessage(messages.protectedServerName), - last_played: undefined, - icon: undefined, - display_status: 'favorite', - type: 'server', - index: -1, - address: PROTECTED_SERVER_ADDRESS, - pack_status: 'prompt', - } - return [{ instance: protectedInstance, world: protectedServer }, ...favoriteServers] -}) + }), +) function serverKey(world: ServerWorld & WorldWithInstance): string { return `${world.instance_id}:${world.address}` @@ -261,7 +240,6 @@ async function unpinServer(world: ServerWorld & WorldWithInstance) { import * as THREE from 'three' -import { type GLTF, GLTFLoader } from 'three/examples/jsm/Addons.js' import { onMounted, onScopeDispose, useTemplateRef } from 'vue' import { useTheming } from '@/store/theme' @@ -17,20 +16,6 @@ function isDarkMode() { return ['dark', 'oled'].includes(themeStore.selectedTheme) } -function loadGLTF(url: string): Promise { - return new Promise((res, rej) => { - const loader = new GLTFLoader() - loader.load( - url, - (data) => { - res(data) - }, - undefined, - rej, - ) - }) -} - function createTip(position: THREE.Vector3, color: THREE.ColorRepresentation = 0x00ff00) { const tipGeometry = new THREE.SphereGeometry(2) const tipMaterial = new THREE.MeshBasicMaterial({ color }) @@ -224,43 +209,16 @@ function main() { scene.add(createWater(waterMaterial, new THREE.Vector3(2, -8, -10))) scene.add(createWater(waterMaterial, new THREE.Vector3(16, -8, -26))) - async function load() { - const axlGLTF = await loadGLTF('/models/axolotl.gltf') + // 静止的下界之星(替换原动态美西螈) + const starTexture = new THREE.TextureLoader().load('/models/netherstar.png') + starTexture.colorSpace = THREE.SRGBColorSpace + const starMaterial = new THREE.SpriteMaterial({ map: starTexture, transparent: true }) + const starSprite = new THREE.Sprite(starMaterial) + starSprite.scale.set(8, 8, 1) + starSprite.position.set(0, -2.5, 0) + scene.add(starSprite) - const axlModel = axlGLTF.scene - axlModel.scale.multiplyScalar(5) - axlModel.rotateY(Math.PI / 2) - axlModel.position.add(new THREE.Vector3(0, -2.5, 0)) - scene.add(axlModel) - - const mixer = new THREE.AnimationMixer(axlModel) - const axlSwimAnim = axlGLTF.animations.filter((a) => a.name === 'swim')[0] - if (!axlSwimAnim) return console.error('Missing animation swim') - mixer.clipAction(axlSwimAnim).play() - - // // Axl Label - // const axlLabelGLTF = await loadGLTF('/models/axl_label.glb') - // const axlLabel = axlLabelGLTF.scene - // axlLabel.scale.multiplyScalar(8) - // axlLabel.rotateY(-Math.PI / 2) - // axlLabel.position.set(0, 5.2, 0) - // scene.add(axlLabel) - - const originAxlModelPosition = axlModel.position.clone() - return function (deltaTime: number, elapsedTime: number) { - axlModel.position.set( - originAxlModelPosition.x, - originAxlModelPosition.y + Math.sin(elapsedTime), - originAxlModelPosition.z, - ) - axlModel.rotation.y = Math.sin(elapsedTime * 0.3) * 0.2 + (Math.PI * 100) / 180 - mixer.update(deltaTime) - } - } let updateGLTF = (_deltaTime: number, _elapsedTime: number) => {} - load().then((updateFn) => { - if (updateFn) updateGLTF = updateFn - }) const circleMaterial = createCircleMaterial() circleMaterial.uniforms.color.value = new THREE.Color(accentColor).multiplyScalar( diff --git a/apps/app-frontend/src/components/ui/world/WorldItem.vue b/apps/app-frontend/src/components/ui/world/WorldItem.vue index bc5c98d..d6cc159 100644 --- a/apps/app-frontend/src/components/ui/world/WorldItem.vue +++ b/apps/app-frontend/src/components/ui/world/WorldItem.vue @@ -51,7 +51,6 @@ import type { } from '@/helpers/worlds.ts' import { getWorldIdentifier, - PROTECTED_SERVER_ADDRESS, set_world_display_status, } from '@/helpers/worlds.ts' @@ -149,9 +148,6 @@ const managed = computed(() => props.managed) const shortcutInstanceId = computed(() => props.shortcutInstanceId ?? props.instanceId) const homePinTarget = computed(() => props.shortcutInstanceId ?? props.instanceId) const pinnedToHome = computed(() => props.world.display_status === 'favorite') -const protectedServer = computed( - () => props.world.type === 'server' && props.world.address === PROTECTED_SERVER_ADDRESS, -) async function updateHomePin() { if (!homePinTarget.value) return @@ -519,8 +515,8 @@ const messages = defineMessages({ { id: 'edit', action: () => emit('edit'), - shown: !instanceId && !protectedServer, - disabled: locked || managed || protectedServer, + shown: !instanceId, + disabled: locked || managed, tooltip: locked ? formatMessage(messages.worldInUse) : managed @@ -538,7 +534,7 @@ const messages = defineMessages({ }, { id: pinnedToHome ? 'unpin-home' : 'pin-home', - shown: !!homePinTarget && !protectedServer, + shown: !!homePinTarget, action: updateHomePin, }, { @@ -555,8 +551,8 @@ const messages = defineMessages({ color: 'red', hoverFilled: true, action: () => emit('delete'), - shown: !instanceId && !protectedServer, - disabled: locked || managed || protectedServer, + shown: !instanceId, + disabled: locked || managed, tooltip: locked ? formatMessage(messages.worldInUse) : managed diff --git a/apps/app-frontend/src/helpers/worlds.ts b/apps/app-frontend/src/helpers/worlds.ts index 650e3ca..bcbdb2f 100644 --- a/apps/app-frontend/src/helpers/worlds.ts +++ b/apps/app-frontend/src/helpers/worlds.ts @@ -18,7 +18,6 @@ type BaseWorld = { export type WorldType = 'singleplayer' | 'server' export type DisplayStatus = 'normal' | 'hidden' | 'favorite' -export const PROTECTED_SERVER_ADDRESS = 'slv4.starlight.cool' export type SingleplayerWorld = BaseWorld & { type: 'singleplayer' diff --git a/tools/icon_work/add_star.py b/tools/icon_work/add_star.py new file mode 100644 index 0000000..206b3b8 --- /dev/null +++ b/tools/icon_work/add_star.py @@ -0,0 +1,20 @@ + +p = r"D:\Project\Starlight_Lancher\apps\app-frontend\src\components\ui\AboutScene.vue" +t = open(p, encoding="utf-8").read() + +old = "\tlet updateGLTF = (_deltaTime: number, _elapsedTime: number) => {}" +new = """\t// 静止的下界之星(替换原动态美西螈) +\tconst starTexture = new THREE.TextureLoader().load('/models/netherstar.png') +\tstarTexture.colorSpace = THREE.SRGBColorSpace +\tconst starMaterial = new THREE.SpriteMaterial({ map: starTexture, transparent: true }) +\tconst starSprite = new THREE.Sprite(starMaterial) +\tstarSprite.scale.set(8, 8, 1) +\tstarSprite.position.set(0, -2.5, 0) +\tscene.add(starSprite) + +\tlet updateGLTF = (_deltaTime: number, _elapsedTime: number) => {}""" + +assert old in t, "找不到 updateGLTF 空行" +t = t.replace(old, new, 1) +open(p, "w", encoding="utf-8", newline="\n").write(t) +print("done") diff --git a/tools/icon_work/del_dead.py b/tools/icon_work/del_dead.py new file mode 100644 index 0000000..daea601 --- /dev/null +++ b/tools/icon_work/del_dead.py @@ -0,0 +1,32 @@ + +p = r"D:\Project\Starlight_Lancher\apps\app-frontend\src\components\ui\AboutScene.vue" +t = open(p, encoding="utf-8").read() + +# 1) 删 import 行 +old_imp = "import { type GLTF, GLTFLoader } from 'three/examples/jsm/Addons.js'\n" +assert old_imp in t, "import 行找不到" +t = t.replace(old_imp, "", 1) + +# 2) 删 loadGLTF 函数(含前后空行) +old_fn = """function loadGLTF(url: string): Promise { + return new Promise((res, rej) => { + const loader = new GLTFLoader() + loader.load( + url, + (data) => { + res(data) + }, + undefined, + rej, + ) + }) +} + +""" +assert old_fn in t, "loadGLTF 函数找不到" +t = t.replace(old_fn, "", 1) + +open(p, "w", encoding="utf-8", newline="\n").write(t) +print("done") +print("残留 GLTFLoader:", "GLTFLoader" in t) +print("残留 loadGLTF:", "loadGLTF" in t) diff --git a/tools/icon_work/fix_pinned.py b/tools/icon_work/fix_pinned.py new file mode 100644 index 0000000..061537e --- /dev/null +++ b/tools/icon_work/fix_pinned.py @@ -0,0 +1,72 @@ + +p = r"D:\Project\Starlight_Lancher\apps\app-frontend\src\components\home\HomePinnedServers.vue" +t = open(p, encoding="utf-8").read() + +# 1) servers computed 简化为"只返回收藏服务器" +old_servers = """const servers = computed(() => { + const favoriteServers = favoriteWorlds.value.flatMap((world) => { + if (world.type !== 'server' || world.address === PROTECTED_SERVER_ADDRESS) return [] + const instance = instanceById.value.get(world.instance_id) + return instance ? [{ instance, world: world as ServerWorld & WorldWithInstance }] : [] + }) + const protectedInstance = + props.instances.find((instance) => instance.install_stage === 'installed') ?? props.instances[0] + if (!protectedInstance) return favoriteServers + + const protectedServer: ServerWorld & WorldWithInstance = { + instance_id: protectedInstance.id, + name: formatMessage(messages.protectedServerName), + last_played: undefined, + icon: undefined, + display_status: 'favorite', + type: 'server', + index: -1, + address: PROTECTED_SERVER_ADDRESS, + pack_status: 'prompt', + } + return [{ instance: protectedInstance, world: protectedServer }, ...favoriteServers] +})""" +new_servers = """const servers = computed(() => + favoriteWorlds.value.flatMap((world) => { + if (world.type !== 'server') return [] + const instance = instanceById.value.get(world.instance_id) + return instance ? [{ instance, world: world as ServerWorld & WorldWithInstance }] : [] + }), +)""" +assert old_servers in t, "servers computed 找不到" +t = t.replace(old_servers, new_servers, 1) + +# 2) 删 protectedServerName message +old_msg = """ protectedServerName: { + id: 'app.home.servers.protected-name', + defaultMessage: 'Starlight Server', + }, +""" +assert old_msg in t, "protectedServerName message 找不到" +t = t.replace(old_msg, "", 1) + +# 3) import 去掉 PROTECTED_SERVER_ADDRESS +t = t.replace("\tPROTECTED_SERVER_ADDRESS,\n", "", 1) + +# 4) 模板:去掉 v-if(让所有服务器都能取消固定) +t = t.replace( + """ """, + """ """, + 1, +) + +open(p, "w", encoding="utf-8", newline="\n").write(t) +print("HomePinnedServers done") +print(" 残留 PROTECTED:", "PROTECTED" in t) +print(" 残留 protectedServerName:", "protectedServerName" in t) diff --git a/tools/icon_work/fix_worlditem.py b/tools/icon_work/fix_worlditem.py new file mode 100644 index 0000000..0bb3770 --- /dev/null +++ b/tools/icon_work/fix_worlditem.py @@ -0,0 +1,26 @@ + +p = r"D:\Project\Starlight_Lancher\apps\app-frontend\src\components\ui\world\WorldItem.vue" +t = open(p, encoding="utf-8").read() + +# 1) 删 protectedServer 定义 +old_def = """const protectedServer = computed( + () => props.world.type === 'server' && props.world.address === PROTECTED_SERVER_ADDRESS, +) +""" +assert old_def in t, "protectedServer 定义找不到" +t = t.replace(old_def, "", 1) + +# 2) import 去掉 PROTECTED_SERVER_ADDRESS +t = t.replace("\tPROTECTED_SERVER_ADDRESS,\n", "", 1) + +# 3) 逻辑里去掉 protectedServer +t = t.replace("shown: !instanceId && !protectedServer,", "shown: !instanceId,", 1) # edit +t = t.replace("disabled: locked || managed || protectedServer,", "disabled: locked || managed,", 1) # edit +t = t.replace("shown: !!homePinTarget && !protectedServer,", "shown: !!homePinTarget,", 1) # pin +t = t.replace("shown: !instanceId && !protectedServer,", "shown: !instanceId,", 1) # delete +t = t.replace("disabled: locked || managed || protectedServer,", "disabled: locked || managed,", 1) # delete + +open(p, "w", encoding="utf-8", newline="\n").write(t) +print("WorldItem done") +print(" 残留 protectedServer:", "protectedServer" in t) +print(" 残留 PROTECTED_SERVER:", "PROTECTED_SERVER" in t) diff --git a/tools/icon_work/remove_axo.py b/tools/icon_work/remove_axo.py new file mode 100644 index 0000000..7dd5e22 --- /dev/null +++ b/tools/icon_work/remove_axo.py @@ -0,0 +1,48 @@ + +p = r"D:\Project\Starlight_Lancher\apps\app-frontend\src\components\ui\AboutScene.vue" +t = open(p, encoding="utf-8").read() + +old = """ async function load() { + const axlGLTF = await loadGLTF('/models/axolotl.gltf') + + const axlModel = axlGLTF.scene + axlModel.scale.multiplyScalar(5) + axlModel.rotateY(Math.PI / 2) + axlModel.position.add(new THREE.Vector3(0, -2.5, 0)) + scene.add(axlModel) + + const mixer = new THREE.AnimationMixer(axlModel) + const axlSwimAnim = axlGLTF.animations.filter((a) => a.name === 'swim')[0] + if (!axlSwimAnim) return console.error('Missing animation swim') + mixer.clipAction(axlSwimAnim).play() + + // // Axl Label + // const axlLabelGLTF = await loadGLTF('/models/axl_label.glb') + // const axlLabel = axlLabelGLTF.scene + // axlLabel.scale.multiplyScalar(8) + // axlLabel.rotateY(-Math.PI / 2) + // axlLabel.position.set(0, 5.2, 0) + // scene.add(axlLabel) + + const originAxlModelPosition = axlModel.position.clone() + return function (deltaTime: number, elapsedTime: number) { + axlModel.position.set( + originAxlModelPosition.x, + originAxlModelPosition.y + Math.sin(elapsedTime), + originAxlModelPosition.z, + ) + axlModel.rotation.y = Math.sin(elapsedTime * 0.3) * 0.2 + (Math.PI * 100) / 180 + mixer.update(deltaTime) + } + } + let updateGLTF = (_deltaTime: number, _elapsedTime: number) => {} + load().then((updateFn) => { + if (updateFn) updateGLTF = updateFn + })""" + +new = """ let updateGLTF = (_deltaTime: number, _elapsedTime: number) => {}""" + +assert old in t, "找不到美西螈加载块" +t = t.replace(old, new, 1) +open(p, "w", encoding="utf-8", newline="\n").write(t) +print("done")