Use spyglass DocAndNode to store current file data

This commit is contained in:
Misode
2024-10-16 04:36:59 +02:00
parent 7dbd533abb
commit 77d6323219
32 changed files with 244 additions and 240 deletions

View File

@@ -1,8 +1,8 @@
import config from '../Config.js'
import { Store } from '../Store.js'
import { message } from '../Utils.js'
import type { VersionId } from './Schemas.js'
import { checkVersion } from './Schemas.js'
import type { VersionId } from './Versions.js'
import { checkVersion } from './Versions.js'
const CACHE_NAME = 'misode-v2'
const CACHE_LATEST_VERSION = 'cached_latest_version'

View File

@@ -3,7 +3,7 @@ import { BlockDefinition, BlockModel, Identifier, ItemRenderer, TextureAtlas, up
import config from '../Config.js'
import { message } from '../Utils.js'
import { fetchLanguage, fetchResources } from './DataFetcher.js'
import type { VersionId } from './Schemas.js'
import type { VersionId } from './Versions.js'
const Resources: Record<string, ResourceManager | Promise<ResourceManager>> = {}

View File

@@ -3,7 +3,7 @@ import { BlockDefinition, BlockModel, Identifier, ItemRenderer, TextureAtlas, up
import config from '../Config.js'
import { message } from '../Utils.js'
import { fetchLanguage, fetchResources } from './DataFetcher.js'
import type { VersionId } from './Schemas.js'
import type { VersionId } from './Versions.js'
const Resources: Record<string, ResourceManager | Promise<ResourceManager>> = {}

View File

@@ -1,57 +0,0 @@
import config from '../Config.js'
import { message } from '../Utils.js'
import type { BlockStateData } from './DataFetcher.js'
import { fetchBlockStates, fetchRegistries } from './DataFetcher.js'
export const VersionIds = ['1.15', '1.16', '1.17', '1.18', '1.18.2', '1.19', '1.19.3', '1.19.4', '1.20', '1.20.2', '1.20.3', '1.20.5', '1.21', '1.21.2'] as const
export type VersionId = typeof VersionIds[number]
export const DEFAULT_VERSION: VersionId = '1.21'
interface VersionData {
registries: Map<string, string[]>
blockStates: Map<string, BlockStateData>
}
const Versions: Record<string, VersionData | Promise<VersionData>> = {}
async function getVersion(id: VersionId): Promise<VersionData> {
if (!Versions[id]) {
Versions[id] = (async () => {
try {
const registries = await fetchRegistries(id)
const blockStates= await fetchBlockStates(id)
Versions[id] = { registries, blockStates }
return Versions[id]
} catch (e) {
throw new Error(`Cannot get version "${id}": ${message(e)}`)
}
})()
return Versions[id]
}
return Versions[id]
}
export async function getBlockStates(version: VersionId): Promise<Map<string, BlockStateData>> {
const versionData = await getVersion(version)
return versionData.blockStates
}
export function checkVersion(versionId: string, minVersionId: string | undefined, maxVersionId?: string) {
const version = config.versions.findIndex(v => v.id === versionId)
const minVersion = minVersionId ? config.versions.findIndex(v => v.id === minVersionId) : 0
const maxVersion = maxVersionId ? config.versions.findIndex(v => v.id === maxVersionId) : config.versions.length - 1
return minVersion <= version && version <= maxVersion
}
export interface FileModel {
get text(): string
get data(): any
}
export function createMockFileModel(): FileModel {
return {
text: '{}',
data: {},
}
}

View File

@@ -1,5 +1,5 @@
import lz from 'lz-string'
import type { VersionId } from './Schemas.js'
import type { VersionId } from './Versions.js'
const API_PREFIX = 'https://snippets.misode.workers.dev'

View File

@@ -9,61 +9,87 @@ import { localize } from '@spyglassmc/locales'
import * as mcdoc from '@spyglassmc/mcdoc'
import * as nbt from '@spyglassmc/nbt'
import * as zip from '@zip.js/zip.js'
import type { ConfigVersion } from '../Config.js'
import type { ConfigGenerator, ConfigVersion } from '../Config.js'
import siteConfig from '../Config.js'
import type { VersionId } from './index.js'
import { fetchBlockStates, fetchRegistries, fetchVanillaMcdoc, getVersionChecksum } from './index.js'
import { genPath } from '../Utils.js'
import { fetchBlockStates, fetchRegistries, fetchVanillaMcdoc, getVersionChecksum } from './DataFetcher.js'
import type { VersionId } from './Versions.js'
const externals: core.Externals = {
...BrowserExternals,
archive: {
...BrowserExternals.archive,
async decompressBall(buffer, { stripLevel } = {}) {
const reader = new zip.ZipReader(new zip.BlobReader(new Blob([buffer])))
const entries = await reader.getEntries()
return await Promise.all(entries.map(async e => {
const data = await e.getData?.(new zip.Uint8ArrayWriter())
const path = stripLevel === 1 ? e.filename.substring(e.filename.indexOf('/') + 1) : e.filename
const type = e.directory ? 'dir' : 'file'
return { data, path, mtime: '', type, mode: 0 }
}))
},
},
export class Spyglass {
private static readonly INSTANCES = new Map<VersionId, Promise<Spyglass>>()
private constructor(
private readonly service: core.Service,
private readonly version: ConfigVersion,
) {}
public async setFileContents(uri: string, contents: string) {
await this.service.project.onDidOpen(uri, 'json', 1, contents)
const docAndNode = await this.service.project.ensureClientManagedChecked(uri)
if (!docAndNode) {
throw new Error('[Spyglass setFileContents] Cannot get doc and node')
}
return docAndNode
}
public getFile(uri: string): Partial<core.DocAndNode> {
return this.service.project.getClientManaged(uri) ?? {}
}
public getUnsavedFileUri(gen: ConfigGenerator) {
return `file:project/data/draft/${genPath(gen, this.version.id)}/unsaved.json`
}
public static async initialize(versionId: VersionId) {
const instance = this.INSTANCES.get(versionId)
if (instance) {
return instance
}
const promise = (async () => {
const version = siteConfig.versions.find(v => v.id === versionId)!
const service = new core.Service({
logger: console,
profilers: new core.ProfilerFactory(console, [
'project#init',
'project#ready',
]),
project: {
cacheRoot: 'file:cache/',
projectRoots: ['file:project/'],
externals: {
...BrowserExternals,
archive: {
...BrowserExternals.archive,
decompressBall,
},
},
defaultConfig: core.ConfigService.merge(core.VanillaConfig, {
env: {
gameVersion: version.ref ?? version.id,
dependencies: ['@vanilla-mcdoc'],
},
}),
initializers: [mcdoc.initialize, initialize],
},
})
await service.project.ready()
await service.project.cacheService.save()
return new Spyglass(service, version)
})()
this.INSTANCES.set(versionId, promise)
return promise
}
}
export async function setupSpyglass(versionId: VersionId) {
const version = siteConfig.versions.find(v => v.id === versionId)!
const gameVersion = version.ref ?? version.id
const logger: core.Logger = console
const profilers = new core.ProfilerFactory(logger, [
'project#init',
'project#ready',
'misode#setup',
])
const profiler = profilers.get('misode#setup')
const service = new core.Service({
logger,
profilers,
project: {
cacheRoot: 'file:cache/',
projectRoots: ['file:project/'],
externals: externals,
defaultConfig: core.ConfigService.merge(core.VanillaConfig, {
env: {
gameVersion: gameVersion,
dependencies: ['@vanilla-mcdoc'],
},
}),
initializers: [mcdoc.initialize, initialize],
},
})
await service.project.ready()
profiler.task('Project ready')
await service.project.cacheService.save()
profiler.task('Save cache')
profiler.finalize()
service.logger.info(service.project.symbols.global)
const decompressBall: core.Externals['archive']['decompressBall'] = async (buffer, options) => {
const reader = new zip.ZipReader(new zip.BlobReader(new Blob([buffer])))
const entries = await reader.getEntries()
return await Promise.all(entries.map(async e => {
const data = await e.getData?.(new zip.Uint8ArrayWriter())
const path = options?.stripLevel === 1 ? e.filename.substring(e.filename.indexOf('/') + 1) : e.filename
const type = e.directory ? 'dir' : 'file'
return { data, path, mtime: '', type, mode: 0 }
}))
}
const initialize: core.ProjectInitializer = async (ctx) => {

View File

@@ -0,0 +1,13 @@
import config from '../Config.js'
export const VersionIds = ['1.15', '1.16', '1.17', '1.18', '1.18.2', '1.19', '1.19.3', '1.19.4', '1.20', '1.20.2', '1.20.3', '1.20.5', '1.21', '1.21.2'] as const
export type VersionId = typeof VersionIds[number]
export const DEFAULT_VERSION: VersionId = '1.21'
export function checkVersion(versionId: string, minVersionId: string | undefined, maxVersionId?: string) {
const version = config.versions.findIndex(v => v.id === versionId)
const minVersion = minVersionId ? config.versions.findIndex(v => v.id === minVersionId) : 0
const maxVersion = maxVersionId ? config.versions.findIndex(v => v.id === maxVersionId) : config.versions.length - 1
return minVersion <= version && version <= maxVersion
}

View File

@@ -1,5 +1,5 @@
export * from './Article.js'
export * from './DataFetcher.js'
export * from './Schemas.js'
export * from './Sharing.js'
export * from './Source.js'
export * from './Versions.js'