Refactor generator to prevent duplicate reloading

This commit is contained in:
Misode
2022-05-08 16:24:39 +02:00
parent 2772d967e0
commit a432479672
13 changed files with 78 additions and 75 deletions

View File

@@ -9,7 +9,7 @@ import { BiomeSourcePreview, DecoratorPreview, DensityFunctionPreview, NoisePrev
export const HasPreview = ['dimension', 'worldgen/density_function', 'worldgen/noise', 'worldgen/noise_settings', 'worldgen/configured_feature', 'worldgen/placed_feature']
type PreviewPanelProps = {
model: DataModel | null,
model: DataModel | undefined,
version: VersionId,
id: string,
shown: boolean,

View File

@@ -52,8 +52,8 @@ interface Editor {
type SourcePanelProps = {
name: string,
model: DataModel | null,
blockStates: BlockStateRegistry | null,
model: DataModel | undefined,
blockStates: BlockStateRegistry | undefined,
doCopy?: number,
doDownload?: number,
doImport?: number,

View File

@@ -7,8 +7,8 @@ import type { BlockStateRegistry, VersionId } from '../../services'
type TreePanelProps = {
version: VersionId,
model: DataModel | null,
blockStates: BlockStateRegistry | null,
model: DataModel | undefined,
blockStates: BlockStateRegistry | undefined,
onError: (message: string) => unknown,
}
export function Tree({ version, model, blockStates, onError }: TreePanelProps) {

View File

@@ -58,14 +58,14 @@ export const BiomeSourcePreview = ({ model, data, shown, version }: PreviewProps
onLeave() {
setFocused(undefined)
},
}, [state, scale, configuredSeed, layers])
}, [version, state, scale, configuredSeed, layers])
useEffect(() => {
if (shown) {
res.current = type === 'multi_noise' ? 4 : 1
redraw()
}
}, [state, scale, configuredSeed, layers, shown])
}, [version, state, scale, configuredSeed, layers, shown])
const changeScale = (newScale: number) => {
offset.current[0] = offset.current[0] * scale / newScale

View File

@@ -11,6 +11,8 @@ export const DecoratorPreview = ({ data, version, shown }: PreviewProps) => {
const [scale, setScale] = useState(4)
const [seed, setSeed] = useState(randomSeed())
const state = JSON.stringify(data)
const { canvas, redraw } = useCanvas({
size() {
return [scale * 16, scale * 16]
@@ -18,14 +20,13 @@ export const DecoratorPreview = ({ data, version, shown }: PreviewProps) => {
async draw(img) {
decorator(data, img, { seed, version, size: [scale * 16, 128, scale * 16] })
},
})
}, [version, state, seed])
const state = JSON.stringify(data)
useEffect(() => {
if (shown) {
redraw()
}
}, [state, scale, seed, shown])
}, [version, state, scale, seed, shown])
return <>
<div class="controls preview-controls">

View File

@@ -36,7 +36,7 @@ export const DensityFunctionPreview = ({ data, shown, version }: PreviewProps) =
onLeave() {
setFocused(undefined)
},
}, [state, seed])
}, [version, state, seed])
useEffect(() => {
if (scrollInterval.current) {
@@ -51,7 +51,7 @@ export const DensityFunctionPreview = ({ data, shown, version }: PreviewProps) =
}, 100) as any
}
}
}, [state, seed, shown, autoScroll])
}, [version, state, seed, shown, autoScroll])
return <>
<div class="controls preview-controls">

View File

@@ -26,13 +26,13 @@ export const NoisePreview = ({ data, shown, version }: PreviewProps) => {
offset.current[1] = offset.current[1] + dy * 256
redraw()
},
}, [state, scale, seed])
}, [version, state, scale, seed])
useEffect(() => {
if (shown) {
redraw()
}
}, [state, scale, seed, shown])
}, [version, state, scale, seed, shown])
const changeScale = (newScale: number) => {
offset.current[0] = offset.current[0] * scale / newScale

View File

@@ -62,7 +62,7 @@ export const NoiseSettingsPreview = ({ data, shown, version }: PreviewProps) =>
}
})()
}
}, [state, seed, shown, biome, biomeScale, biomeDepth, autoScroll])
}, [version, state, seed, shown, biome, biomeScale, biomeDepth, autoScroll])
const allBiomes = useMemo(() => CachedCollections?.get('worldgen/biome') ?? [], [version])

View File

@@ -1,13 +1,14 @@
import type { Inputs } from 'preact/hooks'
import { useEffect } from 'preact/hooks'
import type { AsyncState } from './useAsyncFn'
import type { AsyncCancel, AsyncState } from './useAsyncFn'
import { useAsyncFn } from './useAsyncFn'
export function useAsync<T>(
fn: () => Promise<T>,
export function useAsync<R>(
fn: () => Promise<R | typeof AsyncCancel>,
inputs: Inputs = [],
): AsyncState<T> {
const [state, callback] = useAsyncFn<T, () => Promise<T>>(fn, inputs, { loading: true })
initialState: AsyncState<R> = { loading: true },
): AsyncState<R> {
const [state, callback] = useAsyncFn<R, () => Promise<R | typeof AsyncCancel>>(fn, inputs, initialState)
useEffect(() => {
callback()

View File

@@ -20,11 +20,13 @@ export type AsyncState<T> = {
value: T,
}
export function useAsyncFn<R, T extends (...args: any[]) => Promise<R>>(
export const AsyncCancel = Symbol('async-cancel')
export function useAsyncFn<R, T extends (...args: any[]) => Promise<R | typeof AsyncCancel>>(
fn: T,
inputs: Inputs = [],
initialState: AsyncState<R> = { loading: false },
): [AsyncState<R>, (...args: Parameters<T>) => Promise<R | undefined>] {
): [AsyncState<R>, (...args: Parameters<T>) => Promise<R | typeof AsyncCancel | undefined>] {
const [state, setState] = useState<AsyncState<R>>(initialState)
const isMounted = useRef<boolean>(false)
const lastCallId = useRef(0)
@@ -34,7 +36,7 @@ export function useAsyncFn<R, T extends (...args: any[]) => Promise<R>>(
return () => isMounted.current = false
}, [])
const callback = useCallback((...args: Parameters<T>): Promise<R | undefined> => {
const callback = useCallback((...args: Parameters<T>): Promise<R | typeof AsyncCancel | undefined> => {
const callId = ++lastCallId.current
if (!state.loading) {
setState(prev => ({ ...prev, loading: true }))
@@ -42,7 +44,7 @@ export function useAsyncFn<R, T extends (...args: any[]) => Promise<R>>(
return fn(...args).then(
value => {
if (isMounted.current && callId === lastCallId.current) {
if (isMounted.current && callId === lastCallId.current && value !== AsyncCancel) {
setState({ value, loading: false })
}
return value

View File

@@ -25,6 +25,7 @@ export function useSearchParam(param: string): [string | undefined, (value: stri
const changeValue = useCallback((newValue: string | undefined, replace?: boolean) => {
if (newValue !== value) {
setValue(newValue)
const params = new URLSearchParams(location.search)
if (newValue === undefined || newValue.length === 0) {
params.delete(param)

View File

@@ -5,9 +5,9 @@ import config from '../../config.json'
import { Analytics } from '../Analytics'
import { Ad, Btn, BtnMenu, ErrorPanel, HasPreview, Octicon, PreviewPanel, SearchList, SourcePanel, TextInput, Tree } from '../components'
import { useLocale, useProject, useTitle, useVersion } from '../contexts'
import { useActiveTimeout, useModel, useSearchParam } from '../hooks'
import { AsyncCancel, useActiveTimeout, useAsync, useModel, useSearchParam } from '../hooks'
import { getOutput } from '../schema/transformOutput'
import type { BlockStateRegistry, VersionId } from '../services'
import type { VersionId } from '../services'
import { checkVersion, fetchPreset, getBlockStates, getCollections, getModel, getSnippet, shareSnippet, SHARE_KEY } from '../services'
import { Store } from '../Store'
import { cleanUrl, deepEqual, getGenerator } from '../Utils'
@@ -50,35 +50,6 @@ export function Generator({}: Props) {
const [currentPreset, setCurrentPreset] = useSearchParam('preset')
const [sharedSnippetId, setSharedSnippetId] = useSearchParam(SHARE_KEY)
const ignoreChange = useRef(false)
useEffect(() => {
if (model && currentPreset) {
loadPreset(currentPreset).then(preset => {
ignoreChange.current = true
model.reset(DataModel.wrapLists(preset), false)
setSharedSnippetId(undefined)
})
} else if (model && sharedSnippetId) {
getSnippet(sharedSnippetId).then(s => loadSnippet(model, s))
}
}, [currentPreset, sharedSnippetId])
const loadSnippet = (model: DataModel, snippet: any) => {
if (snippet.version && snippet.version !== version) {
changeVersion(snippet.version, false)
}
if (snippet.type && snippet.type !== gen.id) {
const snippetGen = config.generators.find(g => g.id === snippet.type)
if (snippetGen) {
route(`${cleanUrl(snippetGen.url)}?${SHARE_KEY}=${snippet.id}`)
}
}
if (snippet.show_preview && !previewShown) {
setPreviewShown(true)
setSourceShown(false)
}
model.reset(DataModel.wrapLists(snippet.data), false)
}
const backup = useMemo(() => Store.getBackup(gen.id), [gen.id])
const loadBackup = () => {
@@ -87,27 +58,51 @@ export function Generator({}: Props) {
}
}
const [model, setModel] = useState<DataModel | null>(null)
const [blockStates, setBlockStates] = useState<BlockStateRegistry | null>(null)
useEffect(() => {
setError(null)
setModel(null)
getBlockStates(version)
.then(b => setBlockStates(b))
getModel(version, gen.id)
.then(async m => {
Analytics.setGenerator(gen.id)
if (currentPreset) {
const preset = await loadPreset(currentPreset)
m.reset(DataModel.wrapLists(preset), false)
} else if (sharedSnippetId) {
const snippet = await getSnippet(sharedSnippetId)
loadSnippet(m, snippet)
const { value } = useAsync(async () => {
let data: unknown = undefined
if (currentPreset && sharedSnippetId) {
setSharedSnippetId(undefined)
return AsyncCancel
}
if (currentPreset) {
data = await loadPreset(currentPreset)
} else if (sharedSnippetId) {
const snippet = await getSnippet(sharedSnippetId)
let cancel = false
if (snippet.version && snippet.version !== version) {
changeVersion(snippet.version, false)
cancel = true
}
if (snippet.type && snippet.type !== gen.id) {
const snippetGen = config.generators.find(g => g.id === snippet.type)
if (snippetGen) {
route(`${cleanUrl(snippetGen.url)}?${SHARE_KEY}=${snippet.id}`)
cancel = true
}
setModel(m)
})
.catch(e => { console.error(e); setError(e) })
}, [version, gen.id])
}
if (cancel) {
return AsyncCancel
}
if (snippet.show_preview && !previewShown) {
setPreviewShown(true)
setSourceShown(false)
}
data = snippet.data
}
const [model, blockStates] = await Promise.all([
getModel(version, gen.id),
getBlockStates(version),
])
if (data) {
ignoreChange.current = true
model.reset(DataModel.wrapLists(data), false)
}
Analytics.setGenerator(gen.id)
return { model, blockStates }
}, [gen.id, version, sharedSnippetId, currentPreset])
const model = value?.model
const blockStates = value?.blockStates
const [dirty, setDirty] = useState(false)
useModel(model, () => {

View File

@@ -60,6 +60,7 @@ export class Deepslate {
}
public generateChunks(minX: number, width: number, biome = 'unknown') {
minX = Math.floor(minX)
if (!this.settingsCache) {
throw new Error('Tried to generate chunks before settings are loaded')
}
@@ -108,6 +109,8 @@ export class Deepslate {
}
public getBlockState(x: number, y: number) {
x = Math.floor(x)
y = Math.floor(y)
const chunk = this.chunksCache.find(c => this.d.ChunkPos.minBlockX(c.pos) <= x && this.d.ChunkPos.maxBlockX(c.pos) >= x)
return chunk?.getBlockState(this.d.BlockPos.create(x, y, Deepslate.Z))
}