minecraft-renderer 0.1.103 → 0.1.104

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "minecraft-renderer",
3
- "version": "0.1.103",
3
+ "version": "0.1.104",
4
4
  "description": "The most Modular Minecraft world renderer with Three.js WebGL backend",
5
5
  "type": "module",
6
6
  "main": "./dist/index.js",
@@ -0,0 +1,41 @@
1
+ //@ts-nocheck
2
+ import MinecraftData, { IndexedData } from 'minecraft-data'
3
+ import pcFeatures from 'minecraft-data/minecraft-data/data/pc/common/features.json'
4
+
5
+ /**
6
+ * Version tables the mesher workers need to behave like `minecraft-data`.
7
+ * The worker bundles don't contain any (see src/mesher-shared/mcDataWorkerShim.cjs), they are supplied with the `mcData` message
8
+ * so the workers support every version the main thread's `minecraft-data` supports.
9
+ */
10
+ export type MesherVersionTables = {
11
+ /** pc protocolVersions, newest first (same order as minecraft-data); only the fields needed for comparisons */
12
+ protocolVersions: Array<{ minecraftVersion: string; majorVersion: string; dataVersion?: number; version?: number }>
13
+ /** `supportFeature(name)` resolved for the current version */
14
+ features: Record<string, unknown>
15
+ }
16
+
17
+ let protocolVersionsCache: MesherVersionTables['protocolVersions'] | undefined
18
+ const featuresCache = new WeakMap<object, MesherVersionTables['features']>()
19
+
20
+ export const getMesherVersionTables = (mcDataFull: IndexedData): MesherVersionTables => {
21
+ protocolVersionsCache ??= MinecraftData.versions.pc.map(({ minecraftVersion, majorVersion, dataVersion, version }: any) => ({
22
+ minecraftVersion,
23
+ majorVersion,
24
+ dataVersion,
25
+ version
26
+ }))
27
+
28
+ let features = featuresCache.get(mcDataFull)
29
+ if (!features) {
30
+ features = {}
31
+ // bedrock features are not used by the mesher
32
+ if (mcDataFull.type === 'pc') {
33
+ for (const { name } of pcFeatures as Array<{ name: string }>) {
34
+ features[name] = mcDataFull.supportFeature(name as any)
35
+ }
36
+ }
37
+ featuresCache.set(mcDataFull, features)
38
+ }
39
+
40
+ return { protocolVersions: protocolVersionsCache, features }
41
+ }
@@ -8,6 +8,7 @@ import { subscribeKey } from 'valtio/utils'
8
8
  import { proxy, subscribe } from 'valtio'
9
9
  import type { ResourcesManagerTransferred } from '../resourcesManager/resourcesManager'
10
10
  import { dynamicMcDataFiles } from './buildSharedConfig.mjs'
11
+ import { getMesherVersionTables } from './mesherMcDataSupply'
11
12
  import { DisplayWorldOptions, GraphicsInitOptions, RendererReactiveState, SoundSystem, UpdateCameraOptions } from '../graphicsBackend/types'
12
13
  import {
13
14
  HighestBlockInfo,
@@ -1587,7 +1588,8 @@ let mesherMcDataTintsMissingWarned = false
1587
1588
 
1588
1589
  export const meshersSendMcData = (workers: Worker[], version: string, mcDataKeys = dynamicMcDataFiles, mcDataFull: IndexedData) => {
1589
1590
  const mcData: { version: IndexedData['version']; tints?: unknown; [key: string]: unknown } = {
1590
- version: JSON.parse(JSON.stringify(mcDataFull.version))
1591
+ version: JSON.parse(JSON.stringify(mcDataFull.version)),
1592
+ ...getMesherVersionTables(mcDataFull)
1591
1593
  }
1592
1594
  for (const [finalKey, sourceKey] of Object.entries(mcDataKeys)) {
1593
1595
  mcData[finalKey] = mcDataFull[sourceKey]
@@ -0,0 +1,74 @@
1
+ // Version-agnostic replacement for `minecraft-data` inside the mesher worker bundles (see scripts/buildWorkerShared.mjs).
2
+ //
3
+ // The real package would bake its version tables (data.js, protocolVersions.json, features.json) into the bundle at build
4
+ // time, so a worker could only ever load versions that the renderer's devDependency knew about. Nothing version-specific is
5
+ // bundled here: everything is read lazily from `globalThis.mcData`, which the main thread supplies with the `mcData`
6
+ // message (see `meshersSendMcData`):
7
+ // - `version` IndexedData['version'] of the current version
8
+ // - `protocolVersions` pc protocolVersions list (used for version comparisons)
9
+ // - `features` `supportFeature(name)` already resolved for the current version
10
+ // - everything else data files (blocks, biomes, blockCollisionShapes, tints, ...)
11
+ const mcDataToNode = require('minecraft-data/lib/loader')
12
+ const indexer = require('minecraft-data/lib/indexer.js')
13
+
14
+ function createVersion (type, suppliedVersion, protocolVersions) {
15
+ const versions = indexer.buildIndexFromArray(protocolVersions, 'minecraftVersion') ?? {}
16
+ // Allows comparisons against majorVersion even if `other` is not present in the versions list (e.g. 1.17.0 exists but not 1.17)
17
+ for (const key in versions) {
18
+ const versionObj = versions[key]
19
+ if (versionObj.minecraftVersion.endsWith('.0')) versions[versionObj.majorVersion] = versionObj
20
+ }
21
+
22
+ const self = {}
23
+ // the supplied dataVersion is authoritative: the current version doesn't have to be present in the supplied list
24
+ self.dataVersion = suppliedVersion.dataVersion ?? versions[suppliedVersion.minecraftVersion]?.dataVersion
25
+ const v1 = self.dataVersion ?? 0
26
+ const raise = other => { throw new RangeError(`Version '${other}' not found in protocolVersions supplied by the main thread for ${type}`) }
27
+ self['>='] = other => versions[other] ? v1 >= versions[other].dataVersion : raise(other)
28
+ self['>'] = other => versions[other] ? v1 > versions[other].dataVersion : raise(other)
29
+ self['<'] = other => versions[other] ? v1 < versions[other].dataVersion : raise(other)
30
+ self['<='] = other => versions[other] ? v1 <= versions[other].dataVersion : raise(other)
31
+ self['=='] = other => versions[other] ? v1 === versions[other].dataVersion : raise(other)
32
+ self.type = type
33
+ self.majorVersion = suppliedVersion.majorVersion
34
+ return self
35
+ }
36
+
37
+ let cache = null
38
+
39
+ module.exports = function (mcVersion) {
40
+ const supplied = globalThis.mcData
41
+ const suppliedVersion = supplied?.version
42
+ if (!suppliedVersion) return null // not supplied (yet)
43
+
44
+ const requested = String(mcVersion)
45
+ // a worker only ever works with the single version it was supplied data for
46
+ if (requested !== suppliedVersion.minecraftVersion && requested !== suppliedVersion.majorVersion && requested !== String(suppliedVersion.version)) return null
47
+
48
+ if (cache?.src === supplied) return cache.value
49
+
50
+ const type = suppliedVersion.type ?? 'pc'
51
+ const protocolVersions = supplied.protocolVersions ?? []
52
+ protocolVersions.forEach((entry, i) => {
53
+ // same as minecraft-data: bottom entries are lower
54
+ if (!entry.dataVersion) entry.dataVersion = -i
55
+ })
56
+
57
+ const { version: _version, protocolVersions: _protocolVersions, features: _features, ...files } = supplied
58
+ const nmcData = mcDataToNode({ ...files, version: suppliedVersion })
59
+ nmcData.type = type
60
+ nmcData.isNewerOrEqualTo = version => nmcData.version['>='](version)
61
+ nmcData.isOlderThan = version => nmcData.version['<'](version)
62
+ nmcData.version = Object.assign(createVersion(type, suppliedVersion, protocolVersions), nmcData.version)
63
+ const features = supplied.features ?? {}
64
+ nmcData.supportFeature = featureName => features[featureName] || false
65
+
66
+ cache = { src: supplied, value: nmcData }
67
+ return nmcData
68
+ }
69
+
70
+ // Used eagerly by prismarine-block (preflat id mapping), static and version independent
71
+ module.exports.legacy = {
72
+ pc: require('minecraft-data/minecraft-data/data/pc/common/legacy.json'),
73
+ bedrock: {}
74
+ }
@@ -0,0 +1,87 @@
1
+ //@ts-nocheck
2
+ import { createRequire } from 'module'
3
+ import { afterEach, describe, expect, it } from 'vitest'
4
+ import MinecraftData from 'minecraft-data'
5
+ import { getMesherVersionTables } from '../lib/mesherMcDataSupply'
6
+
7
+ const require = createRequire(import.meta.url)
8
+ const loadShim = () => {
9
+ delete require.cache[require.resolve('./mcDataWorkerShim.cjs')]
10
+ return require('./mcDataWorkerShim.cjs') as (version: string) => any
11
+ }
12
+
13
+ /** What `meshersSendMcData` sends to the workers */
14
+ const supplyFor = (version: string) => {
15
+ const full = MinecraftData(version)
16
+ return {
17
+ full,
18
+ mcData: {
19
+ version: JSON.parse(JSON.stringify(full.version)),
20
+ ...getMesherVersionTables(full),
21
+ blocks: full.blocksArray,
22
+ biomes: full.biomesArray,
23
+ blockCollisionShapes: full.blockCollisionShapes
24
+ }
25
+ }
26
+ }
27
+
28
+ describe('mesher worker minecraft-data shim', () => {
29
+ afterEach(() => {
30
+ delete (globalThis as any).mcData
31
+ })
32
+
33
+ it('returns null until data is supplied', () => {
34
+ expect(loadShim()('1.20.4')).toBeNull()
35
+ })
36
+
37
+ it.each(['1.8.8', '1.16.5', '1.20.4', '1.21.8'])('behaves like minecraft-data for %s', version => {
38
+ const { full, mcData } = supplyFor(version)
39
+ ;(globalThis as any).mcData = mcData
40
+ const shimmed = loadShim()(version)
41
+
42
+ expect(shimmed.version.minecraftVersion).toBe(version)
43
+ expect(shimmed.type).toBe('pc')
44
+ expect(shimmed.blocksByName.stone.id).toBe(full.blocksByName.stone.id)
45
+ expect(shimmed.blocksByStateId[full.blocksByName.stone.defaultState].name).toBe('stone')
46
+ expect(shimmed.biomes[full.biomesArray[0].id].name).toBe(full.biomesArray[0].name)
47
+ for (const feature of ['blockStateId', 'theFlattening', 'usesBlockStates', 'itemsAreNotBlocks', 'sendsClientTickEndPacket']) {
48
+ expect(shimmed.supportFeature(feature)).toBe(full.supportFeature(feature as any))
49
+ }
50
+ for (const other of ['1.8', '1.12.2', '1.16.5', '1.21.1']) {
51
+ expect(shimmed.isNewerOrEqualTo(other)).toBe(full.isNewerOrEqualTo(other))
52
+ expect(shimmed.isOlderThan(other)).toBe(full.isOlderThan(other))
53
+ }
54
+ expect(shimmed.supportFeature('unknownFeature')).toBe(false)
55
+ })
56
+
57
+ it('works with a version that is not in anything bundled with the worker', () => {
58
+ const { mcData } = supplyFor('1.21.8')
59
+ const newest = mcData.protocolVersions[0]
60
+ // a version from the future: only the main thread knows it
61
+ const future = { ...mcData.version, minecraftVersion: '99.9', majorVersion: '99', dataVersion: newest.dataVersion + 1000, version: 9999 }
62
+ ;(globalThis as any).mcData = {
63
+ ...mcData,
64
+ version: future,
65
+ protocolVersions: [{ ...future }, ...mcData.protocolVersions],
66
+ features: { ...mcData.features, someFutureFeature: true }
67
+ }
68
+ const shimmed = loadShim()('99.9')
69
+
70
+ expect(shimmed).not.toBeNull()
71
+ expect(shimmed.version.minecraftVersion).toBe('99.9')
72
+ expect(shimmed.isNewerOrEqualTo('1.21.8')).toBe(true)
73
+ expect(shimmed.isOlderThan('1.21.8')).toBe(false)
74
+ expect(shimmed.supportFeature('someFutureFeature')).toBe(true)
75
+ // the worker only knows the version it was supplied
76
+ expect(loadShim()('1.21.8')).toBeNull()
77
+ })
78
+
79
+ it('picks up newly supplied data (worker reconfigure)', () => {
80
+ ;(globalThis as any).mcData = supplyFor('1.16.5').mcData
81
+ const shim = loadShim()
82
+ expect(shim('1.16.5').version.minecraftVersion).toBe('1.16.5')
83
+ ;(globalThis as any).mcData = supplyFor('1.20.4').mcData
84
+ expect(shim('1.20.4').version.minecraftVersion).toBe('1.20.4')
85
+ expect(shim('1.16.5')).toBeNull()
86
+ })
87
+ })