diff --git a/src/app/components/generator/PreviewPanel.tsx b/src/app/components/generator/PreviewPanel.tsx
index 05f499d9..cd165319 100644
--- a/src/app/components/generator/PreviewPanel.tsx
+++ b/src/app/components/generator/PreviewPanel.tsx
@@ -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,
diff --git a/src/app/components/generator/SourcePanel.tsx b/src/app/components/generator/SourcePanel.tsx
index c6df44ac..0811de7c 100644
--- a/src/app/components/generator/SourcePanel.tsx
+++ b/src/app/components/generator/SourcePanel.tsx
@@ -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,
diff --git a/src/app/components/generator/Tree.tsx b/src/app/components/generator/Tree.tsx
index e813b53a..34c35416 100644
--- a/src/app/components/generator/Tree.tsx
+++ b/src/app/components/generator/Tree.tsx
@@ -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) {
diff --git a/src/app/components/previews/BiomeSourcePreview.tsx b/src/app/components/previews/BiomeSourcePreview.tsx
index b2c433a8..be5b7e3d 100644
--- a/src/app/components/previews/BiomeSourcePreview.tsx
+++ b/src/app/components/previews/BiomeSourcePreview.tsx
@@ -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
diff --git a/src/app/components/previews/DecoratorPreview.tsx b/src/app/components/previews/DecoratorPreview.tsx
index 9ab535a6..1da8ae35 100644
--- a/src/app/components/previews/DecoratorPreview.tsx
+++ b/src/app/components/previews/DecoratorPreview.tsx
@@ -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 <>
diff --git a/src/app/components/previews/DensityFunctionPreview.tsx b/src/app/components/previews/DensityFunctionPreview.tsx
index 8b726ec9..3661d896 100644
--- a/src/app/components/previews/DensityFunctionPreview.tsx
+++ b/src/app/components/previews/DensityFunctionPreview.tsx
@@ -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 <>
diff --git a/src/app/components/previews/NoisePreview.tsx b/src/app/components/previews/NoisePreview.tsx
index 36e14799..fcc3a9dc 100644
--- a/src/app/components/previews/NoisePreview.tsx
+++ b/src/app/components/previews/NoisePreview.tsx
@@ -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
diff --git a/src/app/components/previews/NoiseSettingsPreview.tsx b/src/app/components/previews/NoiseSettingsPreview.tsx
index 8ac88a3d..b61a6fb5 100644
--- a/src/app/components/previews/NoiseSettingsPreview.tsx
+++ b/src/app/components/previews/NoiseSettingsPreview.tsx
@@ -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])
diff --git a/src/app/hooks/useAsync.ts b/src/app/hooks/useAsync.ts
index e0499022..5cce9276 100644
--- a/src/app/hooks/useAsync.ts
+++ b/src/app/hooks/useAsync.ts
@@ -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
(
- fn: () => Promise,
+export function useAsync(
+ fn: () => Promise,
inputs: Inputs = [],
-): AsyncState {
- const [state, callback] = useAsyncFn Promise>(fn, inputs, { loading: true })
+ initialState: AsyncState = { loading: true },
+): AsyncState {
+ const [state, callback] = useAsyncFn Promise>(fn, inputs, initialState)
useEffect(() => {
callback()
diff --git a/src/app/hooks/useAsyncFn.ts b/src/app/hooks/useAsyncFn.ts
index 5fe877e5..e736c87b 100644
--- a/src/app/hooks/useAsyncFn.ts
+++ b/src/app/hooks/useAsyncFn.ts
@@ -20,11 +20,13 @@ export type AsyncState = {
value: T,
}
-export function useAsyncFn Promise>(
+export const AsyncCancel = Symbol('async-cancel')
+
+export function useAsyncFn Promise>(
fn: T,
inputs: Inputs = [],
initialState: AsyncState = { loading: false },
-): [AsyncState, (...args: Parameters) => Promise] {
+): [AsyncState, (...args: Parameters) => Promise] {
const [state, setState] = useState>(initialState)
const isMounted = useRef(false)
const lastCallId = useRef(0)
@@ -34,7 +36,7 @@ export function useAsyncFn Promise>(
return () => isMounted.current = false
}, [])
- const callback = useCallback((...args: Parameters): Promise => {
+ const callback = useCallback((...args: Parameters): Promise => {
const callId = ++lastCallId.current
if (!state.loading) {
setState(prev => ({ ...prev, loading: true }))
@@ -42,7 +44,7 @@ export function useAsyncFn Promise>(
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
diff --git a/src/app/hooks/useSearchParam.ts b/src/app/hooks/useSearchParam.ts
index fe7961c0..5bfedbd5 100644
--- a/src/app/hooks/useSearchParam.ts
+++ b/src/app/hooks/useSearchParam.ts
@@ -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)
diff --git a/src/app/pages/Generator.tsx b/src/app/pages/Generator.tsx
index 9873af74..eeac48bc 100644
--- a/src/app/pages/Generator.tsx
+++ b/src/app/pages/Generator.tsx
@@ -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(null)
- const [blockStates, setBlockStates] = useState(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, () => {
diff --git a/src/app/previews/Deepslate.ts b/src/app/previews/Deepslate.ts
index 58e7393b..ddb29ee9 100644
--- a/src/app/previews/Deepslate.ts
+++ b/src/app/previews/Deepslate.ts
@@ -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))
}