minecraft-renderer 0.1.0

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.
Files changed (187) hide show
  1. package/README.md +297 -0
  2. package/dist/index.html +83 -0
  3. package/dist/static/image/arrow.6f27b59f.png +0 -0
  4. package/dist/static/image/blocksAtlasLatest.7850afa3.png +0 -0
  5. package/dist/static/image/blocksAtlasLegacy.5c76823d.png +0 -0
  6. package/dist/static/image/itemsAtlasLatest.36036f95.png +0 -0
  7. package/dist/static/image/itemsAtlasLegacy.dcb1b58d.png +0 -0
  8. package/dist/static/image/tipped_arrow.6f27b59f.png +0 -0
  9. package/dist/static/js/365.f05233ab.js +8462 -0
  10. package/dist/static/js/365.f05233ab.js.LICENSE.txt +52 -0
  11. package/dist/static/js/async/738.efa27644.js +1 -0
  12. package/dist/static/js/index.092ec5be.js +56 -0
  13. package/dist/static/js/lib-polyfill.98986ac5.js +1 -0
  14. package/dist/static/js/lib-react.5c9129e0.js +2 -0
  15. package/dist/static/js/lib-react.5c9129e0.js.LICENSE.txt +39 -0
  16. package/package.json +104 -0
  17. package/src/assets/destroy_stage_0.png +0 -0
  18. package/src/assets/destroy_stage_1.png +0 -0
  19. package/src/assets/destroy_stage_2.png +0 -0
  20. package/src/assets/destroy_stage_3.png +0 -0
  21. package/src/assets/destroy_stage_4.png +0 -0
  22. package/src/assets/destroy_stage_5.png +0 -0
  23. package/src/assets/destroy_stage_6.png +0 -0
  24. package/src/assets/destroy_stage_7.png +0 -0
  25. package/src/assets/destroy_stage_8.png +0 -0
  26. package/src/assets/destroy_stage_9.png +0 -0
  27. package/src/examples/README.md +146 -0
  28. package/src/examples/appViewerExample.ts +205 -0
  29. package/src/examples/initialMenuStart.ts +161 -0
  30. package/src/graphicsBackend/appViewer.ts +297 -0
  31. package/src/graphicsBackend/config.ts +119 -0
  32. package/src/graphicsBackend/index.ts +10 -0
  33. package/src/graphicsBackend/playerState.ts +61 -0
  34. package/src/graphicsBackend/types.ts +143 -0
  35. package/src/index.ts +97 -0
  36. package/src/lib/DebugGui.ts +190 -0
  37. package/src/lib/animationController.ts +85 -0
  38. package/src/lib/buildSharedConfig.mjs +1 -0
  39. package/src/lib/cameraBobbing.ts +94 -0
  40. package/src/lib/canvas2DOverlay.example.ts +361 -0
  41. package/src/lib/canvas2DOverlay.quickstart.ts +242 -0
  42. package/src/lib/canvas2DOverlay.ts +381 -0
  43. package/src/lib/cleanupDecorator.ts +29 -0
  44. package/src/lib/createPlayerObject.ts +55 -0
  45. package/src/lib/frameTimingCollector.ts +164 -0
  46. package/src/lib/guiRenderer.ts +283 -0
  47. package/src/lib/items.ts +140 -0
  48. package/src/lib/mesherlogReader.ts +131 -0
  49. package/src/lib/moreBlockDataGenerated.json +714 -0
  50. package/src/lib/preflatMap.json +1741 -0
  51. package/src/lib/simpleUtils.ts +40 -0
  52. package/src/lib/smoothSwitcher.ts +168 -0
  53. package/src/lib/spiral.ts +29 -0
  54. package/src/lib/ui/newStats.ts +120 -0
  55. package/src/lib/utils/proxy.ts +23 -0
  56. package/src/lib/utils/skins.ts +63 -0
  57. package/src/lib/utils.ts +76 -0
  58. package/src/lib/workerProxy.ts +342 -0
  59. package/src/lib/worldrendererCommon.ts +1088 -0
  60. package/src/mesher/mesher.ts +253 -0
  61. package/src/mesher/models.ts +769 -0
  62. package/src/mesher/modelsGeometryCommon.ts +142 -0
  63. package/src/mesher/shared.ts +80 -0
  64. package/src/mesher/standaloneRenderer.ts +270 -0
  65. package/src/mesher/test/a.ts +3 -0
  66. package/src/mesher/test/mesherTester.ts +76 -0
  67. package/src/mesher/test/playground.ts +19 -0
  68. package/src/mesher/test/test-perf.ts +74 -0
  69. package/src/mesher/test/tests.test.ts +56 -0
  70. package/src/mesher/world.ts +294 -0
  71. package/src/mesher/worldConstants.ts +1 -0
  72. package/src/modules/index.ts +11 -0
  73. package/src/modules/starfield.ts +313 -0
  74. package/src/modules/types.ts +110 -0
  75. package/src/playerState/playerState.ts +78 -0
  76. package/src/playerState/types.ts +36 -0
  77. package/src/playground/allEntitiesDebug.ts +170 -0
  78. package/src/playground/baseScene.ts +587 -0
  79. package/src/playground/mobileControls.tsx +268 -0
  80. package/src/playground/playground.html +83 -0
  81. package/src/playground/playground.ts +11 -0
  82. package/src/playground/playgroundUi.tsx +140 -0
  83. package/src/playground/reactUtils.ts +71 -0
  84. package/src/playground/scenes/allEntities.ts +13 -0
  85. package/src/playground/scenes/entities.ts +37 -0
  86. package/src/playground/scenes/floorRandom.ts +33 -0
  87. package/src/playground/scenes/frequentUpdates.ts +148 -0
  88. package/src/playground/scenes/geometryExport.ts +142 -0
  89. package/src/playground/scenes/index.ts +12 -0
  90. package/src/playground/scenes/lightingStarfield.ts +40 -0
  91. package/src/playground/scenes/main.ts +313 -0
  92. package/src/playground/scenes/railsCobweb.ts +14 -0
  93. package/src/playground/scenes/rotationIssue.ts +7 -0
  94. package/src/playground/scenes/slabsOptimization.ts +16 -0
  95. package/src/playground/scenes/transparencyIssue.ts +11 -0
  96. package/src/playground/shared.ts +79 -0
  97. package/src/resourcesManager/index.ts +5 -0
  98. package/src/resourcesManager/resourcesManager.ts +314 -0
  99. package/src/shims/minecraftData.ts +41 -0
  100. package/src/sign-renderer/index.html +21 -0
  101. package/src/sign-renderer/index.ts +216 -0
  102. package/src/sign-renderer/noop.js +1 -0
  103. package/src/sign-renderer/playground.ts +38 -0
  104. package/src/sign-renderer/tests.test.ts +69 -0
  105. package/src/sign-renderer/vite.config.ts +10 -0
  106. package/src/three/appShared.ts +75 -0
  107. package/src/three/bannerRenderer.ts +275 -0
  108. package/src/three/cameraShake.ts +120 -0
  109. package/src/three/cinimaticScript.ts +350 -0
  110. package/src/three/documentRenderer.ts +491 -0
  111. package/src/three/entities.ts +1580 -0
  112. package/src/three/entity/EntityMesh.ts +707 -0
  113. package/src/three/entity/animations.js +171 -0
  114. package/src/three/entity/armorModels.json +204 -0
  115. package/src/three/entity/armorModels.ts +36 -0
  116. package/src/three/entity/entities.json +6230 -0
  117. package/src/three/entity/exportedModels.js +38 -0
  118. package/src/three/entity/externalTextures.json +1 -0
  119. package/src/three/entity/models/allay.obj +325 -0
  120. package/src/three/entity/models/arrow.obj +60 -0
  121. package/src/three/entity/models/axolotl.obj +509 -0
  122. package/src/three/entity/models/blaze.obj +601 -0
  123. package/src/three/entity/models/boat.obj +417 -0
  124. package/src/three/entity/models/camel.obj +1061 -0
  125. package/src/three/entity/models/cat.obj +509 -0
  126. package/src/three/entity/models/chicken.obj +371 -0
  127. package/src/three/entity/models/cod.obj +371 -0
  128. package/src/three/entity/models/creeper.obj +279 -0
  129. package/src/three/entity/models/dolphin.obj +371 -0
  130. package/src/three/entity/models/ender_dragon.obj +2993 -0
  131. package/src/three/entity/models/enderman.obj +325 -0
  132. package/src/three/entity/models/endermite.obj +187 -0
  133. package/src/three/entity/models/fox.obj +463 -0
  134. package/src/three/entity/models/frog.obj +739 -0
  135. package/src/three/entity/models/ghast.obj +463 -0
  136. package/src/three/entity/models/goat.obj +601 -0
  137. package/src/three/entity/models/guardian.obj +1015 -0
  138. package/src/three/entity/models/horse.obj +1061 -0
  139. package/src/three/entity/models/llama.obj +509 -0
  140. package/src/three/entity/models/minecart.obj +233 -0
  141. package/src/three/entity/models/parrot.obj +509 -0
  142. package/src/three/entity/models/piglin.obj +739 -0
  143. package/src/three/entity/models/pillager.obj +371 -0
  144. package/src/three/entity/models/rabbit.obj +555 -0
  145. package/src/three/entity/models/sheep.obj +555 -0
  146. package/src/three/entity/models/shulker.obj +141 -0
  147. package/src/three/entity/models/sniffer.obj +693 -0
  148. package/src/three/entity/models/spider.obj +509 -0
  149. package/src/three/entity/models/tadpole.obj +95 -0
  150. package/src/three/entity/models/turtle.obj +371 -0
  151. package/src/three/entity/models/vex.obj +325 -0
  152. package/src/three/entity/models/villager.obj +509 -0
  153. package/src/three/entity/models/warden.obj +463 -0
  154. package/src/three/entity/models/witch.obj +647 -0
  155. package/src/three/entity/models/wolf.obj +509 -0
  156. package/src/three/entity/models/zombie_villager.obj +463 -0
  157. package/src/three/entity/objModels.js +1 -0
  158. package/src/three/fireworks.ts +661 -0
  159. package/src/three/fireworksRenderer.ts +434 -0
  160. package/src/three/globals.d.ts +7 -0
  161. package/src/three/graphicsBackend.ts +274 -0
  162. package/src/three/graphicsBackendOffThread.ts +107 -0
  163. package/src/three/hand.ts +89 -0
  164. package/src/three/holdingBlock.ts +926 -0
  165. package/src/three/index.ts +20 -0
  166. package/src/three/itemMesh.ts +427 -0
  167. package/src/three/modules.d.ts +14 -0
  168. package/src/three/panorama.ts +308 -0
  169. package/src/three/panoramaShared.ts +1 -0
  170. package/src/three/renderSlot.ts +82 -0
  171. package/src/three/skyboxRenderer.ts +406 -0
  172. package/src/three/starField.ts +13 -0
  173. package/src/three/threeJsMedia.ts +731 -0
  174. package/src/three/threeJsMethods.ts +15 -0
  175. package/src/three/threeJsParticles.ts +160 -0
  176. package/src/three/threeJsSound.ts +95 -0
  177. package/src/three/threeJsUtils.ts +90 -0
  178. package/src/three/waypointSprite.ts +435 -0
  179. package/src/three/waypoints.ts +163 -0
  180. package/src/three/world/cursorBlock.ts +172 -0
  181. package/src/three/world/vr.ts +257 -0
  182. package/src/three/worldGeometryExport.ts +259 -0
  183. package/src/three/worldGeometryHandler.ts +279 -0
  184. package/src/three/worldRendererThree.ts +1381 -0
  185. package/src/worldView/index.ts +6 -0
  186. package/src/worldView/types.ts +66 -0
  187. package/src/worldView/worldView.ts +424 -0
@@ -0,0 +1,11 @@
1
+ import { BasePlaygroundScene } from '../baseScene'
2
+
3
+ export default class extends BasePlaygroundScene {
4
+ setupWorld () {
5
+ this.addWorldBlock(0, 0, 0, 'water')
6
+ this.addWorldBlock(0, 1, 0, 'lime_stained_glass')
7
+ this.addWorldBlock(0, 0, -1, 'lime_stained_glass')
8
+ this.addWorldBlock(0, -1, 0, 'lime_stained_glass')
9
+ this.addWorldBlock(0, -1, -1, 'stone')
10
+ }
11
+ }
@@ -0,0 +1,79 @@
1
+ import WorldLoader, { world } from 'prismarine-world'
2
+ import ChunkLoader from 'prismarine-chunk'
3
+
4
+ export type BlockFaceType = {
5
+ side: number
6
+ textureIndex: number
7
+ tint?: [number, number, number]
8
+ isTransparent?: boolean
9
+
10
+ // for testing
11
+ face?: string
12
+ neighbor?: string
13
+ light?: number
14
+ }
15
+
16
+ export type BlockType = {
17
+ faces: BlockFaceType[]
18
+
19
+ // for testing
20
+ block: string
21
+ }
22
+
23
+ export const makeError = (str: string) => {
24
+ reportError?.(str)
25
+ }
26
+ export const makeErrorCritical = (str: string) => {
27
+ throw new Error(str)
28
+ }
29
+
30
+ export const getSyncWorld = (version: string): world.WorldSync => {
31
+ const World = (WorldLoader as any)(version)
32
+ const Chunk = (ChunkLoader as any)(version)
33
+
34
+ const world = new World(version).sync
35
+
36
+ const methods = getAllMethods(world)
37
+ for (const method of methods) {
38
+ if (method.startsWith('set') && method !== 'setColumn') {
39
+ const oldMethod = world[method].bind(world)
40
+ world[method] = (...args) => {
41
+ const arg = args[0]
42
+ if (arg.x !== undefined && !world.getColumnAt(arg)) {
43
+ world.setColumn(Math.floor(arg.x / 16), Math.floor(arg.z / 16), new Chunk(undefined as any))
44
+ }
45
+ oldMethod(...args)
46
+ }
47
+ }
48
+ }
49
+
50
+ return world
51
+ }
52
+
53
+ function getAllMethods (obj) {
54
+ const methods = new Set()
55
+ let currentObj = obj
56
+
57
+ do {
58
+ for (const name of Object.getOwnPropertyNames(currentObj)) {
59
+ if (typeof obj[name] === 'function' && name !== 'constructor') {
60
+ methods.add(name)
61
+ }
62
+ }
63
+ } while ((currentObj = Object.getPrototypeOf(currentObj)))
64
+
65
+ return [...methods] as string[]
66
+ }
67
+
68
+ export const delayedIterator = async <T> (arr: T[], delay: number, exec: (item: T, index: number) => Promise<void>, chunkSize = 1) => {
69
+ // if delay is 0 then don't use setTimeout
70
+ for (let i = 0; i < arr.length; i += chunkSize) {
71
+ if (delay) {
72
+ // eslint-disable-next-line no-await-in-loop
73
+ await new Promise(resolve => {
74
+ setTimeout(resolve, delay)
75
+ })
76
+ }
77
+ await exec(arr[i], i)
78
+ }
79
+ }
@@ -0,0 +1,5 @@
1
+ /**
2
+ * ResourcesManager module - handles Minecraft assets loading and management.
3
+ */
4
+
5
+ export * from './resourcesManager'
@@ -0,0 +1,314 @@
1
+ import { EventEmitter } from 'events'
2
+ import TypedEmitter from 'typed-emitter'
3
+ import blocksAtlases from 'mc-assets/dist/blocksAtlases.json'
4
+ import itemsAtlases from 'mc-assets/dist/itemsAtlases.json'
5
+ import itemDefinitionsJson from 'mc-assets/dist/itemDefinitions.json'
6
+ import blocksAtlasLatest from 'mc-assets/dist/blocksAtlasLatest.png'
7
+ import blocksAtlasLegacy from 'mc-assets/dist/blocksAtlasLegacy.png'
8
+ import itemsAtlasLatest from 'mc-assets/dist/itemsAtlasLatest.png'
9
+ import itemsAtlasLegacy from 'mc-assets/dist/itemsAtlasLegacy.png'
10
+ import christmasPack from 'mc-assets/dist/textureReplacements/christmas'
11
+ import { AtlasParser, ItemsAtlasesOutputJson } from 'mc-assets/dist/atlasParser'
12
+ import worldBlockProvider, { WorldBlockProvider } from 'mc-assets/dist/worldBlockProvider'
13
+ import { ItemsRenderer } from 'mc-assets/dist/itemsRenderer'
14
+ import { getLoadedItemDefinitionsStore } from 'mc-assets'
15
+
16
+ type ResourceManagerEvents = {
17
+ assetsTexturesUpdated: () => void
18
+ assetsInventoryStarted: () => void
19
+ assetsInventoryReady: () => void
20
+ }
21
+
22
+ export class LoadedResourcesTransferrable {
23
+ // todo transfer instead!
24
+ readonly sourceItemDefinitionsJson: any = itemDefinitionsJson
25
+ readonly itemsDefinitionsStore = getLoadedItemDefinitionsStore(this.sourceItemDefinitionsJson)
26
+
27
+ allReady = false
28
+ // Atlas parsers
29
+ itemsAtlasImage!: ImageBitmap
30
+ blocksAtlasImage!: ImageBitmap
31
+ blocksAtlasJson!: ItemsAtlasesOutputJson
32
+ // User data (specific to current resourcepack/version)
33
+ customBlockStates?: Record<string, any>
34
+ customModels?: Record<string, any>
35
+ /** array where the index represents the custom model data value, and the element at that index is the model path to use */
36
+ customItemModelNames: Record<string, string[]> = {}
37
+ customTextures: {
38
+ items?: { tileSize: number | undefined, textures: Record<string, HTMLImageElement> }
39
+ blocks?: { tileSize: number | undefined, textures: Record<string, HTMLImageElement> }
40
+ armor?: { tileSize: number | undefined, textures: Record<string, HTMLImageElement> }
41
+ } = {}
42
+ guiAtlas: { json: any, image: ImageBitmap } | null = null
43
+ guiAtlasVersion = 0
44
+
45
+ itemsRenderer: ItemsRenderer | undefined
46
+ worldBlockProvider?: WorldBlockProvider
47
+ blockstatesModels: any = null
48
+
49
+ version!: string
50
+ texturesVersion!: string
51
+
52
+ constructor(data?: any) {
53
+ if (data) {
54
+ Object.assign(this, data)
55
+ // this.itemsRenderer = new ItemsRenderer(
56
+ // this.version,
57
+ // this.blockstatesModels,
58
+ // this.itemsAtlasParser,
59
+ // this.blocksAtlasParser
60
+ // )
61
+ }
62
+ }
63
+
64
+ prepareForTransfer() {
65
+ delete this.itemsRenderer
66
+ delete this.worldBlockProvider
67
+ this.customTextures = {}
68
+ return this
69
+ }
70
+
71
+ }
72
+
73
+ export interface ResourcesCurrentConfig {
74
+ version: string
75
+ texturesVersion?: string
76
+ // noBlockstatesModels?: boolean
77
+ noInventoryGui?: boolean
78
+ includeOnlyBlocks?: string[]
79
+ }
80
+
81
+ export interface UpdateAssetsRequest {
82
+ _?: false
83
+ }
84
+
85
+ export interface ResourcesManagerTransferred extends TypedEmitter<ResourceManagerEvents> {
86
+ currentResources: LoadedResourcesTransferrable
87
+ }
88
+ export interface ResourcesManagerCommon extends TypedEmitter<ResourceManagerEvents> {
89
+ currentResources: LoadedResourcesTransferrable | undefined
90
+ }
91
+
92
+ const STABLE_MODELS_VERSION = '1.21.4'
93
+ export class ResourcesManager extends (EventEmitter as new () => TypedEmitter<ResourceManagerEvents>) {
94
+ static restorerName = 'ResourcesManager'
95
+
96
+ static restoreTransferred(data: any, worker?: Worker) {
97
+ const resourcesManager = new ResourcesManager()
98
+ const upResources = (data) => {
99
+ resourcesManager.currentResources = new LoadedResourcesTransferrable(data)
100
+ }
101
+ upResources(data.currentResources)
102
+ if (worker) {
103
+ worker.addEventListener('message', ({ data }) => {
104
+ if (data.class === ResourcesManager.restorerName) {
105
+ if (data.type === 'newResources') {
106
+ console.log('[worker] got new resources')
107
+ upResources(data.currentResources)
108
+ }
109
+ if (data.type === 'event') {
110
+ resourcesManager.emit(data.eventName, ...data.args)
111
+ }
112
+ }
113
+ })
114
+ }
115
+ return resourcesManager
116
+ }
117
+
118
+ prepareForTransfer(worker?: Worker) {
119
+ if (worker) {
120
+ // todo do it automatically
121
+ const oldEmit = this.emit.bind(this) as any
122
+ this.emit = ((eventName: keyof ResourceManagerEvents, ...args: any[]) => {
123
+ oldEmit(eventName, ...args)
124
+ worker.postMessage({
125
+ class: ResourcesManager.restorerName,
126
+ type: 'event',
127
+ eventName,
128
+ args,
129
+ })
130
+ // todo handle assetsInventoryReady
131
+ if (eventName === 'assetsTexturesUpdated' || eventName === 'assetsInventoryReady') {
132
+ worker.postMessage({
133
+ class: ResourcesManager.restorerName,
134
+ type: 'newResources',
135
+ currentResources: this.currentResources?.prepareForTransfer(),
136
+ })
137
+ }
138
+ }) as any
139
+ }
140
+ return {
141
+ __restorer: ResourcesManager.restorerName,
142
+ currentResources: this.currentResources?.prepareForTransfer(),
143
+ }
144
+ }
145
+
146
+ // Source data (imported, not changing)
147
+ sourceBlockStatesModels: any = null
148
+ readonly sourceBlocksAtlases: any = blocksAtlases
149
+ readonly sourceItemsAtlases: any = itemsAtlases
150
+
151
+ currentResources: LoadedResourcesTransferrable | undefined
152
+ itemsAtlasParser!: AtlasParser
153
+ blocksAtlasParser!: AtlasParser
154
+ currentConfig: ResourcesCurrentConfig | undefined
155
+ abortController = new AbortController()
156
+ _promiseAssetsReadyResolvers = Promise.withResolvers<void>()
157
+ get promiseAssetsReady() {
158
+ return this._promiseAssetsReadyResolvers.promise
159
+ }
160
+
161
+ async loadSourceData(version: string) {
162
+ // TODO
163
+ this.sourceBlockStatesModels ??= (await import('mc-assets/dist/blockStatesModels.json')).default
164
+ }
165
+
166
+ resetResources() {
167
+ this.currentResources = new LoadedResourcesTransferrable()
168
+ }
169
+
170
+ async updateAssetsData(request: UpdateAssetsRequest, unstableSkipEvent = false) {
171
+ if (!this.currentConfig) throw new Error('No config loaded')
172
+ this._promiseAssetsReadyResolvers = Promise.withResolvers()
173
+ const abortController = new AbortController()
174
+ await this.loadSourceData(this.currentConfig.version)
175
+ if (abortController.signal.aborted) return
176
+
177
+ const resources = this.currentResources ?? new LoadedResourcesTransferrable()
178
+ resources.version = this.currentConfig.version
179
+ resources.texturesVersion = this.currentConfig.texturesVersion ?? resources.version
180
+
181
+ resources.blockstatesModels = {
182
+ blockstates: {},
183
+ models: {}
184
+ }
185
+ // todo-low resolve version
186
+ resources.blockstatesModels.blockstates.latest = {
187
+ ...this.sourceBlockStatesModels.blockstates.latest,
188
+ ...resources.customBlockStates
189
+ }
190
+
191
+ resources.blockstatesModels.models.latest = {
192
+ ...this.sourceBlockStatesModels.models.latest,
193
+ ...resources.customModels
194
+ }
195
+
196
+ console.time('recreateAtlases')
197
+ await Promise.all([
198
+ this.recreateBlockAtlas(resources),
199
+ this.recreateItemsAtlas(resources)
200
+ ])
201
+ console.timeEnd('recreateAtlases')
202
+
203
+ if (abortController.signal.aborted) return
204
+
205
+ if (resources.version && resources.blockstatesModels && this.itemsAtlasParser && this.blocksAtlasParser) {
206
+ resources.itemsRenderer = new ItemsRenderer(
207
+ resources.version,
208
+ resources.blockstatesModels,
209
+ this.itemsAtlasParser,
210
+ this.blocksAtlasParser
211
+ )
212
+ }
213
+
214
+ if (abortController.signal.aborted) return
215
+
216
+ this.currentResources = resources
217
+ resources.allReady = true
218
+ if (!unstableSkipEvent) { // todo rework resourcepack optimization
219
+ this.emit('assetsTexturesUpdated')
220
+ }
221
+
222
+ if (this.currentConfig.noInventoryGui) {
223
+ this._promiseAssetsReadyResolvers.resolve()
224
+ } else {
225
+ this.emit('assetsInventoryStarted')
226
+ void this.generateGuiTextures().then(() => {
227
+ if (abortController.signal.aborted) return
228
+ if (!unstableSkipEvent) {
229
+ this.emit('assetsInventoryReady')
230
+ }
231
+ this._promiseAssetsReadyResolvers.resolve()
232
+ })
233
+ }
234
+ }
235
+
236
+ async recreateBlockAtlas(resources: LoadedResourcesTransferrable = this.currentResources!) {
237
+ const blockTexturesChanges = {} as Record<string, string>
238
+ const date = new Date()
239
+ if ((date.getMonth() === 11 && date.getDate() >= 24) || (date.getMonth() === 0 && date.getDate() <= 6)) {
240
+ Object.assign(blockTexturesChanges, christmasPack)
241
+ }
242
+
243
+ const blocksAssetsParser = new AtlasParser(this.sourceBlocksAtlases, blocksAtlasLatest, blocksAtlasLegacy)
244
+
245
+ const customBlockTextures = Object.keys(resources.customTextures.blocks?.textures ?? {})
246
+ console.time('createBlocksAtlas')
247
+ const { atlas: blocksAtlas, canvas: blocksCanvas } = await blocksAssetsParser.makeNewAtlas(
248
+ resources.texturesVersion,
249
+ (textureName) => {
250
+ if (this.currentConfig!.includeOnlyBlocks && !this.currentConfig!.includeOnlyBlocks.includes(textureName)) return false
251
+ const texture = resources.customTextures.blocks?.textures[textureName]
252
+ return blockTexturesChanges[textureName] ?? texture
253
+ },
254
+ undefined,
255
+ undefined,
256
+ customBlockTextures,
257
+ {
258
+ needHorizontalIndexes: !!this.currentConfig!.includeOnlyBlocks,
259
+ }
260
+ )
261
+ console.timeEnd('createBlocksAtlas')
262
+
263
+ this.blocksAtlasParser = new AtlasParser({ latest: blocksAtlas }, blocksCanvas.toDataURL())
264
+ resources.blocksAtlasImage = await createImageBitmap(blocksCanvas)
265
+ resources.blocksAtlasJson = this.blocksAtlasParser.atlas.latest
266
+
267
+ resources.worldBlockProvider = worldBlockProvider(
268
+ resources.blockstatesModels,
269
+ this.blocksAtlasParser.atlas,
270
+ STABLE_MODELS_VERSION
271
+ )
272
+ }
273
+
274
+ async recreateItemsAtlas(resources: LoadedResourcesTransferrable = this.currentResources!) {
275
+ const itemsAssetsParser = new AtlasParser(this.sourceItemsAtlases, itemsAtlasLatest, itemsAtlasLegacy)
276
+ const customItemTextures = Object.keys(resources.customTextures.items?.textures ?? {})
277
+ const { atlas: itemsAtlas, canvas: itemsCanvas } = await itemsAssetsParser.makeNewAtlas(
278
+ resources.texturesVersion,
279
+ (textureName) => {
280
+ const texture = resources.customTextures.items?.textures[textureName]
281
+ if (!texture) return
282
+ return texture
283
+ },
284
+ resources.customTextures.items?.tileSize,
285
+ undefined,
286
+ customItemTextures
287
+ )
288
+
289
+ this.itemsAtlasParser = new AtlasParser({ latest: itemsAtlas }, itemsCanvas.toDataURL())
290
+ resources.itemsAtlasImage = await createImageBitmap(itemsCanvas)
291
+ }
292
+
293
+ async generateGuiTextures() {
294
+ // TODO!
295
+ // await generateGuiAtlas()
296
+ }
297
+
298
+ async downloadDebugAtlas(isItems = false) {
299
+ const resources = this.currentResources
300
+ if (!resources) throw new Error('No resources loaded')
301
+ const atlasParser = (isItems ? this.itemsAtlasParser : this.blocksAtlasParser)!
302
+ const dataUrl = await atlasParser.createDebugImage(true)
303
+ const a = document.createElement('a')
304
+ a.href = dataUrl
305
+ a.download = `atlas-debug-${isItems ? 'items' : 'blocks'}.png`
306
+ a.click()
307
+ }
308
+
309
+ destroy() {
310
+ this.abortController.abort()
311
+ this.currentResources = undefined
312
+ this.abortController = new AbortController()
313
+ }
314
+ }
@@ -0,0 +1,41 @@
1
+ /**
2
+ * Minecraft Data Shim
3
+ *
4
+ * Provides a minimal minecraft-data implementation that only loads
5
+ * the versions specified in globalThis.includedVersions.
6
+ */
7
+
8
+ const VERSION = '1.16.5'
9
+
10
+ // Lazy-loaded data for each data type
11
+ const createLazyData = (version: string) => ({
12
+ get attributes() { return require("minecraft-data/minecraft-data/data/pc/1.16/attributes.json") },
13
+ get blocks() { return require("minecraft-data/minecraft-data/data/pc/1.16.2/blocks.json") },
14
+ get blockCollisionShapes() { return require("minecraft-data/minecraft-data/data/pc/1.16.1/blockCollisionShapes.json") },
15
+ get biomes() { return require("minecraft-data/minecraft-data/data/pc/1.16.2/biomes.json") },
16
+ get effects() { return require("minecraft-data/minecraft-data/data/pc/1.16.1/effects.json") },
17
+ get items() { return require("minecraft-data/minecraft-data/data/pc/1.16.2/items.json") },
18
+ get enchantments() { return require("minecraft-data/minecraft-data/data/pc/1.16.4/enchantments.json") },
19
+ get recipes() { return require("minecraft-data/minecraft-data/data/pc/1.16.2/recipes.json") },
20
+ get instruments() { return require("minecraft-data/minecraft-data/data/pc/1.16.1/instruments.json") },
21
+ get materials() { return require("minecraft-data/minecraft-data/data/pc/1.16.2/materials.json") },
22
+ get language() { return require("minecraft-data/minecraft-data/data/pc/1.16.1/language.json") },
23
+ get entities() { return require("minecraft-data/minecraft-data/data/pc/1.16.2/entities.json") },
24
+ get protocol() { return require("minecraft-data/minecraft-data/data/pc/1.16.2/protocol.json") },
25
+ get windows() { return require("minecraft-data/minecraft-data/data/pc/1.16.1/windows.json") },
26
+ get version() { return require("minecraft-data/minecraft-data/data/pc/1.16.5/version.json") },
27
+ get foods() { return require("minecraft-data/minecraft-data/data/pc/1.16.1/foods.json") },
28
+ get particles() { return require("minecraft-data/minecraft-data/data/pc/1.16/particles.json") },
29
+ get blockLoot() { return require("minecraft-data/minecraft-data/data/pc/1.16.2/blockLoot.json") },
30
+ get entityLoot() { return require("minecraft-data/minecraft-data/data/pc/1.16.2/entityLoot.json") },
31
+ get loginPacket() { return require("minecraft-data/minecraft-data/data/pc/1.16.2/loginPacket.json") },
32
+ get tints() { return require("minecraft-data/minecraft-data/data/pc/1.16.2/tints.json") },
33
+ get mapIcons() { return require("minecraft-data/minecraft-data/data/pc/1.16/mapIcons.json") },
34
+ get sounds() { return require("minecraft-data/minecraft-data/data/pc/1.16/sounds.json") }
35
+ })
36
+
37
+ module.exports = {
38
+ pc: {
39
+ [VERSION]: createLazyData(VERSION),
40
+ }
41
+ }
@@ -0,0 +1,21 @@
1
+ <!DOCTYPE html>
2
+ <html>
3
+
4
+ <head>
5
+ <meta charset="UTF-8" />
6
+ <meta name="viewport" content="width=device-width, initial-scale=1.0, shrink-to-fit=no, viewport-fit=cover" />
7
+ <title>%VITE_NAME%</title>
8
+ <style>
9
+ @font-face {
10
+ font-family: mojangles;
11
+ src: url(../../../assets/mojangles.ttf);
12
+ }
13
+ </style>
14
+ </head>
15
+
16
+ <body>
17
+ <div id="root" style="font-family: mojangles;">test</div>
18
+ <script src="./playground.ts" type="module"></script>
19
+ </body>
20
+
21
+ </html>
@@ -0,0 +1,216 @@
1
+ import type { ChatMessage } from 'prismarine-chat'
2
+ import { createCanvas } from '../lib/utils'
3
+
4
+ type SignBlockEntity = {
5
+ Color?: string
6
+ GlowingText?: 0 | 1
7
+ Text1?: string
8
+ Text2?: string
9
+ Text3?: string
10
+ Text4?: string
11
+ } | {
12
+ // todo
13
+ is_waxed?: 0 | 1
14
+ front_text: {
15
+ color: string
16
+ messages: string[]
17
+ // todo
18
+ has_glowing_text?: 0 | 1
19
+ }
20
+ // todo
21
+ // back_text: {}
22
+ }
23
+
24
+ type JsonEncodedType = string | null | Record<string, any>
25
+
26
+ const parseSafe = (text: string, task: string) => {
27
+ try {
28
+ return JSON.parse(text)
29
+ } catch (e) {
30
+ console.warn(`Failed to parse ${task}`, e)
31
+ return null
32
+ }
33
+ }
34
+
35
+ const LEGACY_COLORS = {
36
+ black: '#000000',
37
+ dark_blue: '#0000AA',
38
+ dark_green: '#00AA00',
39
+ dark_aqua: '#00AAAA',
40
+ dark_red: '#AA0000',
41
+ dark_purple: '#AA00AA',
42
+ gold: '#FFAA00',
43
+ gray: '#AAAAAA',
44
+ dark_gray: '#555555',
45
+ blue: '#5555FF',
46
+ green: '#55FF55',
47
+ aqua: '#55FFFF',
48
+ red: '#FF5555',
49
+ light_purple: '#FF55FF',
50
+ yellow: '#FFFF55',
51
+ white: '#FFFFFF',
52
+ }
53
+
54
+ export const renderSign = (
55
+ blockEntity: SignBlockEntity,
56
+ isHanging: boolean,
57
+ PrismarineChat: typeof ChatMessage,
58
+ ctxHook = (ctx) => { },
59
+ canvasCreator = (width, height): OffscreenCanvas => { return createCanvas(width, height) }
60
+ ) => {
61
+ // todo don't use texture rendering, investigate the font rendering when possible
62
+ // or increase factor when needed
63
+ const factor = 40
64
+ const fontSize = 1.6 * factor
65
+ const signboardY = [16, 9]
66
+ const heightOffset = signboardY[0] - signboardY[1]
67
+ const heightScalar = heightOffset / 16
68
+ // todo the text should be clipped based on it's render width (needs investigate)
69
+
70
+ const texts = 'front_text' in blockEntity ? /* > 1.20 */ blockEntity.front_text.messages : [
71
+ blockEntity.Text1,
72
+ blockEntity.Text2,
73
+ blockEntity.Text3,
74
+ blockEntity.Text4
75
+ ]
76
+
77
+ if (!texts.some((text) => text !== 'null')) {
78
+ return undefined
79
+ }
80
+
81
+ const canvas = canvasCreator(16 * factor, heightOffset * factor)
82
+
83
+ const _ctx = canvas.getContext('2d')!
84
+
85
+ ctxHook(_ctx)
86
+ const defaultColor = ('front_text' in blockEntity ? blockEntity.front_text.color : blockEntity.Color) || 'black'
87
+ for (const [lineNum, text] of texts.slice(0, 4).entries()) {
88
+ if (text === 'null') continue
89
+ renderComponent(text, PrismarineChat, canvas, fontSize, defaultColor, fontSize * (lineNum + 1) + (isHanging ? 0 : -8))
90
+ }
91
+ return canvas
92
+ }
93
+
94
+ export const renderComponent = (
95
+ text: JsonEncodedType | string | undefined,
96
+ PrismarineChat: typeof ChatMessage,
97
+ canvas: OffscreenCanvas,
98
+ fontSize: number,
99
+ defaultColor: string,
100
+ offset = 0
101
+ ) => {
102
+ // todo: in pre flatenning it seems the format was not json
103
+ const parsed = typeof text === 'string' && (text?.startsWith('{') || text?.startsWith('"')) ? parseSafe(text ?? '""', 'sign text') : text
104
+ if (!parsed || (typeof parsed !== 'object' && typeof parsed !== 'string')) return
105
+ // todo fix type
106
+
107
+ const ctx = canvas.getContext('2d')!
108
+ if (!ctx) throw new Error('Could not get 2d context')
109
+ ctx.imageSmoothingEnabled = false
110
+ ctx.font = `${fontSize}px mojangles`
111
+
112
+ type Formatting = {
113
+ color: string | undefined
114
+ underlined: boolean | undefined
115
+ strikethrough: boolean | undefined
116
+ bold: boolean | undefined
117
+ italic: boolean | undefined
118
+ }
119
+
120
+ type Message = ChatMessage & Formatting & { text: string }
121
+
122
+ const message = new PrismarineChat(parsed) as Message
123
+
124
+ const toRenderCanvas: Array<{
125
+ fontStyle: string
126
+ fillStyle: string
127
+ underlineStyle: boolean
128
+ strikeStyle: boolean
129
+ offset: number
130
+ text: string
131
+ }> = []
132
+ let visibleFormatting = false
133
+ let plainText = ''
134
+ let textOffset = offset
135
+ const textWidths: number[] = []
136
+
137
+ const renderText = (component: Message, parentFormatting?: Formatting | undefined) => {
138
+ const { text } = component
139
+ const formatting = {
140
+ color: component.color ?? parentFormatting?.color,
141
+ underlined: component.underlined ?? parentFormatting?.underlined,
142
+ strikethrough: component.strikethrough ?? parentFormatting?.strikethrough,
143
+ bold: component.bold ?? parentFormatting?.bold,
144
+ italic: component.italic ?? parentFormatting?.italic
145
+ }
146
+ visibleFormatting = visibleFormatting || formatting.underlined || formatting.strikethrough || false
147
+ if (text?.includes('\n')) {
148
+ for (const line of text.split('\n')) {
149
+ addTextPart(line, formatting)
150
+ textOffset += fontSize
151
+ plainText = ''
152
+ }
153
+ } else if (text) {
154
+ addTextPart(text, formatting)
155
+ }
156
+ if (component.extra) {
157
+ for (const child of component.extra) {
158
+ renderText(child as Message, formatting)
159
+ }
160
+ }
161
+ }
162
+
163
+ const addTextPart = (text: string, formatting: Formatting) => {
164
+ plainText += text
165
+ textWidths[textOffset] = ctx.measureText(plainText).width
166
+ let color = formatting.color ?? defaultColor
167
+ if (!color.startsWith('#')) {
168
+ color = LEGACY_COLORS[color.toLowerCase()] || color
169
+ }
170
+ toRenderCanvas.push({
171
+ fontStyle: `${formatting.bold ? 'bold' : ''} ${formatting.italic ? 'italic' : ''}`,
172
+ fillStyle: color,
173
+ underlineStyle: formatting.underlined ?? false,
174
+ strikeStyle: formatting.strikethrough ?? false,
175
+ offset: textOffset,
176
+ text
177
+ })
178
+ }
179
+
180
+ renderText(message)
181
+
182
+ // skip rendering empty lines
183
+ if (!visibleFormatting && !message.toString().trim()) return
184
+
185
+ let renderedWidth = 0
186
+ let previousOffsetY = 0
187
+ for (const { fillStyle, fontStyle, underlineStyle, strikeStyle, offset: offsetY, text } of toRenderCanvas) {
188
+ if (previousOffsetY !== offsetY) {
189
+ renderedWidth = 0
190
+ }
191
+ previousOffsetY = offsetY
192
+ ctx.fillStyle = fillStyle
193
+ ctx.textRendering = 'optimizeLegibility'
194
+ ctx.font = `${fontStyle} ${fontSize}px mojangles`
195
+ const textWidth = textWidths[offsetY] ?? ctx.measureText(text).width
196
+ const offsetX = (canvas.width - textWidth) / 2 + renderedWidth
197
+ ctx.fillText(text, offsetX, offsetY)
198
+ if (strikeStyle) {
199
+ ctx.lineWidth = fontSize / 8
200
+ ctx.strokeStyle = fillStyle
201
+ ctx.beginPath()
202
+ ctx.moveTo(offsetX, offsetY - ctx.lineWidth * 2.5)
203
+ ctx.lineTo(offsetX + ctx.measureText(text).width, offsetY - ctx.lineWidth * 2.5)
204
+ ctx.stroke()
205
+ }
206
+ if (underlineStyle) {
207
+ ctx.lineWidth = fontSize / 8
208
+ ctx.strokeStyle = fillStyle
209
+ ctx.beginPath()
210
+ ctx.moveTo(offsetX, offsetY + ctx.lineWidth)
211
+ ctx.lineTo(offsetX + ctx.measureText(text).width, offsetY + ctx.lineWidth)
212
+ ctx.stroke()
213
+ }
214
+ renderedWidth += ctx.measureText(text).width
215
+ }
216
+ }
@@ -0,0 +1 @@
1
+ module.exports = {}