installer

This commit is contained in:
Asaki Yuki 2026-01-30 01:33:05 +07:00
parent 3158c3cec8
commit 6b5cc23888
8 changed files with 127 additions and 48 deletions

View file

@ -1,40 +1,42 @@
import { AnimationKeyframe } from "../components/AnimationKeyframe.js" import { AnimationKeyframe } from "../components/AnimationKeyframe.js";
import { AnimType } from "../types/enums/AnimType.js" import { AnimType } from "../types/enums/AnimType.js";
import { KeyframeAnimationProperties } from "../types/properties/element/Animation.js" import { KeyframeAnimationProperties } from "../types/properties/element/Animation.js";
import { Binding } from "../types/properties/value.js" import { Binding } from "../types/properties/value.js";
export function FormatProperties(properties: any) { export function FormatProperties(properties: any) {
const property_bag: Record<Binding, any> = {} const property_bag: Record<Binding, any> = {};
for (const key in properties) { for (const key in properties) {
const value = properties[key] const value = properties[key];
if (key.startsWith("#")) { if (key.startsWith("#")) {
property_bag[<Binding>key] = value property_bag[<Binding>key] = value;
delete properties[key] delete properties[key];
} }
} }
if (properties.anchor) { if (properties.anchor) {
properties.anchor_from = properties.anchor_to = properties.anchor properties.anchor_from = properties.anchor_to = properties.anchor;
delete properties.anchor delete properties.anchor;
} }
if (Object.keys(property_bag).length) { if (Object.keys(property_bag).length) {
if (properties.property_bag) { if (properties.property_bag) {
properties.property_bag = { ...property_bag, ...properties.property_bag } properties.property_bag = { ...property_bag, ...properties.property_bag };
} else { } else {
properties.property_bag = property_bag properties.property_bag = property_bag;
} }
} }
return properties return properties;
} }
export function FormatAnimationProperties(properties: KeyframeAnimationProperties<AnimType>) { export function FormatAnimationProperties(
if (properties.next instanceof AnimationKeyframe) { properties: KeyframeAnimationProperties<AnimType>,
properties.next = `${properties.next}` ) {
} if (properties.next instanceof AnimationKeyframe) {
properties.next = `${properties.next}`;
}
return properties return properties;
} }

View file

@ -1,6 +1,6 @@
import { AnimationKeyframe } from "../components/AnimationKeyframe.js" import { AnimationKeyframe } from "../components/AnimationKeyframe.js"
import { Class } from "../components/Class.js" import { Class } from "../components/Class.js"
import { ModifyUI, UI } from "../components/UI.js" import { UI } from "../components/UI.js"
import { AnimType } from "../types/enums/AnimType.js" import { AnimType } from "../types/enums/AnimType.js"
import { Renderer } from "../types/enums/Renderer.js" import { Renderer } from "../types/enums/Renderer.js"
import { Type } from "../types/enums/Type.js" import { Type } from "../types/enums/Type.js"
@ -40,9 +40,7 @@ export class Memory extends Class {
Memory.files.entries().forEach(([path, { elements, namespace }]) => { Memory.files.entries().forEach(([path, { elements, namespace }]) => {
const record: Record<string, any> = {} const record: Record<string, any> = {}
elements.forEach(element => { elements.forEach(element => (record[element.name] = element))
record[element.name] = element
})
data.set(path, { data.set(path, {
namespace, namespace,

View file

@ -1,8 +1,10 @@
import { isBuildMode } from "../Configuration.js" import { isBuildMode } from "../Configuration.js"
import { Memory } from "../Memory.js" import { Memory } from "../Memory.js"
import { createBuildFolder } from "./linker.js" import { createBuildFolder, linkToGame } from "./linker.js"
import fs from "fs/promises"
import { genManifest } from "./manifest.js" import { genManifest } from "./manifest.js"
import { UI } from "../../components/UI.js"
import { Type } from "../../types/enums/Type.js"
import fs from "fs/promises"
async function buildUI() { async function buildUI() {
const build = Memory.build() const build = Memory.build()
@ -11,6 +13,10 @@ async function buildUI() {
ui_defs: Array.from(build.keys()), ui_defs: Array.from(build.keys()),
}) })
build.set("build.json", {
files: Array.from(build.keys()),
})
const out = await Promise.all( const out = await Promise.all(
build.entries().map(async ([file, value]) => { build.entries().map(async ([file, value]) => {
const outFile = `build/build/${file}` const outFile = `build/build/${file}`
@ -18,12 +24,30 @@ async function buildUI() {
.stat(outFile.split(/\\|\//g).slice(0, -1).join("/")) .stat(outFile.split(/\\|\//g).slice(0, -1).join("/"))
.catch(async () => await fs.mkdir(outFile.split(/\\|\//g).slice(0, -1).join("/"), { recursive: true })) .catch(async () => await fs.mkdir(outFile.split(/\\|\//g).slice(0, -1).join("/"), { recursive: true }))
await fs.writeFile(outFile, JSON.stringify(value), "utf-8") await fs.writeFile(
outFile,
JSON.stringify(
Object.fromEntries(
Object.entries(value).map(([key, value]: [string, any]) => {
const extend = (value as UI<Type>).extend
return [extend ? key + String(extend) : key, value]
}),
),
),
"utf-8",
)
build.delete(file) build.delete(file)
return file
}), }),
) )
await fs.writeFile("build/build/manifest.json", await genManifest(), "utf-8") await Promise.all([
fs.writeFile("build/build/manifest.json", await genManifest(), "utf-8"),
fs.writeFile("build/build/.gitignore", [...out, "manifest.json"].join("\n"), "utf-8"),
fs
.stat("build/build/pack_icon.png")
.catch(() => fs.copyFile("node_modules/asajs/resources/pack_icon.png", "build/build/pack_icon.png")),
])
return out.length return out.length
} }
@ -34,6 +58,7 @@ if (isBuildMode) {
if (first) { if (first) {
await createBuildFolder() await createBuildFolder()
await buildUI() await buildUI()
await linkToGame()
} }
first = false first = false
}) })

View file

@ -1,7 +1,7 @@
import os from "os" import os from "os"
import path from "path" import path from "path"
function getGamedataPath() { export function getGamedataPath() {
switch (os.platform()) { switch (os.platform()) {
case "win32": { case "win32": {
if (/Windows (10|11)/.test(os.version())) { if (/Windows (10|11)/.test(os.version())) {

View file

@ -1,6 +1,9 @@
import fs from "fs/promises" import fs from "fs/promises"
import { BuildCache } from "./buildcache.js" import { BuildCache } from "./buildcache.js"
import { RandomString } from "../../components/Utils.js" import { RandomString } from "../../components/Utils.js"
import { prevData } from "./prevdata.js"
import path from "path"
import { getGamedataPath } from "./installer.js"
const HEX: string[] = Array.from({ length: 256 }, (_, i) => i.toString(16).padStart(2, "0")) const HEX: string[] = Array.from({ length: 256 }, (_, i) => i.toString(16).padStart(2, "0"))
@ -22,7 +25,7 @@ function genUUID(): string {
} }
export async function clearBuild() { export async function clearBuild() {
await fs.rm("build/build", { recursive: true, force: true }) await Promise.all(prevData.files.map(file => fs.rm(`build/build/${file}`).catch(() => null)))
} }
export async function createBuildFolder() { export async function createBuildFolder() {
@ -36,6 +39,14 @@ export async function getBuildFolderName() {
return await BuildCache.getWithSetDefault("build-key", () => RandomString(16)) return await BuildCache.getWithSetDefault("build-key", () => RandomString(16))
} }
export async function linkToGame() {
const sourcePath = path.resolve("build/build")
const targetPath = path.resolve(getGamedataPath(), "development_resource_packs", await getBuildFolderName())
await fs.stat(targetPath).catch(async () => {
await fs.symlink(sourcePath, targetPath, "junction")
})
}
export async function getUUID(): Promise<[string, string]> { export async function getUUID(): Promise<[string, string]> {
return await BuildCache.getWithSetDefault("uuid", () => { return await BuildCache.getWithSetDefault("uuid", () => {
return [genUUID(), genUUID()] return [genUUID(), genUUID()]

View file

@ -0,0 +1,11 @@
import fs from "fs"
export let prevData: {
files: string[]
}
try {
prevData = JSON.parse(fs.readFileSync("build/build/build.json", "utf-8"))
} catch (error) {
prevData = { files: [] }
}

View file

@ -15,12 +15,18 @@ import { RandomNamespace } from "../compilers/Random.js"
import util from "node:util" import util from "node:util"
interface ExtendUI {
name: string
namespace: string
toString(): string
}
export class UI<T extends Type, K extends Renderer | null = null> extends Class { export class UI<T extends Type, K extends Renderer | null = null> extends Class {
readonly path: string readonly path: string
readonly name: string readonly name: string
readonly namespace: string readonly namespace: string
readonly extend?: UI<Type, Renderer | null> readonly extend?: UI<Type, Renderer | null> | ExtendUI
readonly extendable: boolean readonly extendable: boolean
protected readonly controls = new Map<string, [UI<Type, Renderer | null>, Properties<Type, Renderer | null>]>() protected readonly controls = new Map<string, [UI<Type, Renderer | null>, Properties<Type, Renderer | null>]>()
@ -53,7 +59,7 @@ export class UI<T extends Type, K extends Renderer | null = null> extends Class
this.name = name?.match(/^(\w|\/)+/)?.[0] || RandomString(16) this.name = name?.match(/^(\w|\/)+/)?.[0] || RandomString(16)
this.namespace = namespace || RandomNamespace() this.namespace = namespace || RandomNamespace()
if (!path) this.path = `@/${this.namespace}.json` if (!path) this.path = `asajs/${this.namespace}.json`
else this.path = path else this.path = path
this.extendable = this.name.search("/") === -1 this.extendable = this.name.search("/") === -1
@ -132,8 +138,15 @@ export class UI<T extends Type, K extends Renderer | null = null> extends Class
* @param namespace * @param namespace
* @returns * @returns
*/ */
clone(properties?: Properties<T, K>, name?: string, namespace?: string) { createExtends(properties?: Properties<T, K>, name?: string, namespace?: string) {
return ExtendsOf(this, properties, name, namespace) if (!this.extendable) throw new Error("This element is not extendable")
const ui = new UI<T, K>(undefined, name, namespace)
if (properties) ui.setProperties(properties)
// @ts-ignore
ui.extend = this
// @ts-ignore
ui.extendType = this.type || this.extendType
return ui
} }
protected toString() { protected toString() {

View file

@ -37,7 +37,7 @@ import { AnimationKeyframe } from "./AnimationKeyframe.js"
import { AnimationProperties, KeyframeAnimationProperties } from "../types/properties/element/Animation.js" import { AnimationProperties, KeyframeAnimationProperties } from "../types/properties/element/Animation.js"
import { Animation } from "./Animation.js" import { Animation } from "./Animation.js"
import { SmartAnimation } from "../types/enums/SmartAnimation.js" import { SmartAnimation } from "../types/enums/SmartAnimation.js"
import { Memory, MemoryModify } from "../compilers/Memory.js" import { MemoryModify } from "../compilers/Memory.js"
type CompileBinding = `[${string}]` type CompileBinding = `[${string}]`
@ -250,9 +250,7 @@ export function ExtendsOf<T extends Type, K extends Renderer | null>(
namespace?: string, namespace?: string,
name?: string, name?: string,
) { ) {
if (!element.extendable) { if (!element.extendable) throw new Error("Cannot extend a UI that cannot be extended")
throw new Error("Cannot extend a UI that cannot be extended")
}
const ui = new UI<T, K>(undefined, name, namespace) const ui = new UI<T, K>(undefined, name, namespace)
if (properties) ui.setProperties(properties) if (properties) ui.setProperties(properties)
// @ts-ignore // @ts-ignore
@ -262,6 +260,27 @@ export function ExtendsOf<T extends Type, K extends Renderer | null>(
return ui as typeof element return ui as typeof element
} }
export function VanillaExtendsOf<T extends Namespace, K extends Exclude<Element<T>, `${string}/${string}`>>(
originNamespace: T,
originName: K,
// @ts-ignore
properties?: Properties<VanillaType<T, K>, null>,
namespace?: string,
name?: string,
) {
// @ts-ignore
const ui = new UI<VanillaType<T, K>, null>(undefined, name, namespace)
if (properties) ui.setProperties(properties)
// @ts-ignore
ui.extend = {
name: originName,
namespace: originNamespace,
toString: () => `@${originNamespace}.${originName}`,
}
return ui
}
// Quick Keyframe // Quick Keyframe
export function KeyframeOffset( export function KeyframeOffset(
properties?: KeyframeAnimationProperties<AnimType.OFFSET>, properties?: KeyframeAnimationProperties<AnimType.OFFSET>,
@ -359,7 +378,7 @@ export function AnimationAlpha(...keyframes: AnimWithSmartAnim<AnimType.ALPHA>)
return new Animation(AnimType.ALPHA, ...keyframes) return new Animation(AnimType.ALPHA, ...keyframes)
} }
// Animation Extendof // Animation ExtendsOf
export function AnimationExtendsOf<T extends AnimType>( export function AnimationExtendsOf<T extends AnimType>(
animation: AnimationKeyframe<T> | Animation<T>, animation: AnimationKeyframe<T> | Animation<T>,
properties?: AnimationProperties<T>, properties?: AnimationProperties<T>,