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.
- package/README.md +297 -0
- package/dist/index.html +83 -0
- package/dist/static/image/arrow.6f27b59f.png +0 -0
- package/dist/static/image/blocksAtlasLatest.7850afa3.png +0 -0
- package/dist/static/image/blocksAtlasLegacy.5c76823d.png +0 -0
- package/dist/static/image/itemsAtlasLatest.36036f95.png +0 -0
- package/dist/static/image/itemsAtlasLegacy.dcb1b58d.png +0 -0
- package/dist/static/image/tipped_arrow.6f27b59f.png +0 -0
- package/dist/static/js/365.f05233ab.js +8462 -0
- package/dist/static/js/365.f05233ab.js.LICENSE.txt +52 -0
- package/dist/static/js/async/738.efa27644.js +1 -0
- package/dist/static/js/index.092ec5be.js +56 -0
- package/dist/static/js/lib-polyfill.98986ac5.js +1 -0
- package/dist/static/js/lib-react.5c9129e0.js +2 -0
- package/dist/static/js/lib-react.5c9129e0.js.LICENSE.txt +39 -0
- package/package.json +104 -0
- package/src/assets/destroy_stage_0.png +0 -0
- package/src/assets/destroy_stage_1.png +0 -0
- package/src/assets/destroy_stage_2.png +0 -0
- package/src/assets/destroy_stage_3.png +0 -0
- package/src/assets/destroy_stage_4.png +0 -0
- package/src/assets/destroy_stage_5.png +0 -0
- package/src/assets/destroy_stage_6.png +0 -0
- package/src/assets/destroy_stage_7.png +0 -0
- package/src/assets/destroy_stage_8.png +0 -0
- package/src/assets/destroy_stage_9.png +0 -0
- package/src/examples/README.md +146 -0
- package/src/examples/appViewerExample.ts +205 -0
- package/src/examples/initialMenuStart.ts +161 -0
- package/src/graphicsBackend/appViewer.ts +297 -0
- package/src/graphicsBackend/config.ts +119 -0
- package/src/graphicsBackend/index.ts +10 -0
- package/src/graphicsBackend/playerState.ts +61 -0
- package/src/graphicsBackend/types.ts +143 -0
- package/src/index.ts +97 -0
- package/src/lib/DebugGui.ts +190 -0
- package/src/lib/animationController.ts +85 -0
- package/src/lib/buildSharedConfig.mjs +1 -0
- package/src/lib/cameraBobbing.ts +94 -0
- package/src/lib/canvas2DOverlay.example.ts +361 -0
- package/src/lib/canvas2DOverlay.quickstart.ts +242 -0
- package/src/lib/canvas2DOverlay.ts +381 -0
- package/src/lib/cleanupDecorator.ts +29 -0
- package/src/lib/createPlayerObject.ts +55 -0
- package/src/lib/frameTimingCollector.ts +164 -0
- package/src/lib/guiRenderer.ts +283 -0
- package/src/lib/items.ts +140 -0
- package/src/lib/mesherlogReader.ts +131 -0
- package/src/lib/moreBlockDataGenerated.json +714 -0
- package/src/lib/preflatMap.json +1741 -0
- package/src/lib/simpleUtils.ts +40 -0
- package/src/lib/smoothSwitcher.ts +168 -0
- package/src/lib/spiral.ts +29 -0
- package/src/lib/ui/newStats.ts +120 -0
- package/src/lib/utils/proxy.ts +23 -0
- package/src/lib/utils/skins.ts +63 -0
- package/src/lib/utils.ts +76 -0
- package/src/lib/workerProxy.ts +342 -0
- package/src/lib/worldrendererCommon.ts +1088 -0
- package/src/mesher/mesher.ts +253 -0
- package/src/mesher/models.ts +769 -0
- package/src/mesher/modelsGeometryCommon.ts +142 -0
- package/src/mesher/shared.ts +80 -0
- package/src/mesher/standaloneRenderer.ts +270 -0
- package/src/mesher/test/a.ts +3 -0
- package/src/mesher/test/mesherTester.ts +76 -0
- package/src/mesher/test/playground.ts +19 -0
- package/src/mesher/test/test-perf.ts +74 -0
- package/src/mesher/test/tests.test.ts +56 -0
- package/src/mesher/world.ts +294 -0
- package/src/mesher/worldConstants.ts +1 -0
- package/src/modules/index.ts +11 -0
- package/src/modules/starfield.ts +313 -0
- package/src/modules/types.ts +110 -0
- package/src/playerState/playerState.ts +78 -0
- package/src/playerState/types.ts +36 -0
- package/src/playground/allEntitiesDebug.ts +170 -0
- package/src/playground/baseScene.ts +587 -0
- package/src/playground/mobileControls.tsx +268 -0
- package/src/playground/playground.html +83 -0
- package/src/playground/playground.ts +11 -0
- package/src/playground/playgroundUi.tsx +140 -0
- package/src/playground/reactUtils.ts +71 -0
- package/src/playground/scenes/allEntities.ts +13 -0
- package/src/playground/scenes/entities.ts +37 -0
- package/src/playground/scenes/floorRandom.ts +33 -0
- package/src/playground/scenes/frequentUpdates.ts +148 -0
- package/src/playground/scenes/geometryExport.ts +142 -0
- package/src/playground/scenes/index.ts +12 -0
- package/src/playground/scenes/lightingStarfield.ts +40 -0
- package/src/playground/scenes/main.ts +313 -0
- package/src/playground/scenes/railsCobweb.ts +14 -0
- package/src/playground/scenes/rotationIssue.ts +7 -0
- package/src/playground/scenes/slabsOptimization.ts +16 -0
- package/src/playground/scenes/transparencyIssue.ts +11 -0
- package/src/playground/shared.ts +79 -0
- package/src/resourcesManager/index.ts +5 -0
- package/src/resourcesManager/resourcesManager.ts +314 -0
- package/src/shims/minecraftData.ts +41 -0
- package/src/sign-renderer/index.html +21 -0
- package/src/sign-renderer/index.ts +216 -0
- package/src/sign-renderer/noop.js +1 -0
- package/src/sign-renderer/playground.ts +38 -0
- package/src/sign-renderer/tests.test.ts +69 -0
- package/src/sign-renderer/vite.config.ts +10 -0
- package/src/three/appShared.ts +75 -0
- package/src/three/bannerRenderer.ts +275 -0
- package/src/three/cameraShake.ts +120 -0
- package/src/three/cinimaticScript.ts +350 -0
- package/src/three/documentRenderer.ts +491 -0
- package/src/three/entities.ts +1580 -0
- package/src/three/entity/EntityMesh.ts +707 -0
- package/src/three/entity/animations.js +171 -0
- package/src/three/entity/armorModels.json +204 -0
- package/src/three/entity/armorModels.ts +36 -0
- package/src/three/entity/entities.json +6230 -0
- package/src/three/entity/exportedModels.js +38 -0
- package/src/three/entity/externalTextures.json +1 -0
- package/src/three/entity/models/allay.obj +325 -0
- package/src/three/entity/models/arrow.obj +60 -0
- package/src/three/entity/models/axolotl.obj +509 -0
- package/src/three/entity/models/blaze.obj +601 -0
- package/src/three/entity/models/boat.obj +417 -0
- package/src/three/entity/models/camel.obj +1061 -0
- package/src/three/entity/models/cat.obj +509 -0
- package/src/three/entity/models/chicken.obj +371 -0
- package/src/three/entity/models/cod.obj +371 -0
- package/src/three/entity/models/creeper.obj +279 -0
- package/src/three/entity/models/dolphin.obj +371 -0
- package/src/three/entity/models/ender_dragon.obj +2993 -0
- package/src/three/entity/models/enderman.obj +325 -0
- package/src/three/entity/models/endermite.obj +187 -0
- package/src/three/entity/models/fox.obj +463 -0
- package/src/three/entity/models/frog.obj +739 -0
- package/src/three/entity/models/ghast.obj +463 -0
- package/src/three/entity/models/goat.obj +601 -0
- package/src/three/entity/models/guardian.obj +1015 -0
- package/src/three/entity/models/horse.obj +1061 -0
- package/src/three/entity/models/llama.obj +509 -0
- package/src/three/entity/models/minecart.obj +233 -0
- package/src/three/entity/models/parrot.obj +509 -0
- package/src/three/entity/models/piglin.obj +739 -0
- package/src/three/entity/models/pillager.obj +371 -0
- package/src/three/entity/models/rabbit.obj +555 -0
- package/src/three/entity/models/sheep.obj +555 -0
- package/src/three/entity/models/shulker.obj +141 -0
- package/src/three/entity/models/sniffer.obj +693 -0
- package/src/three/entity/models/spider.obj +509 -0
- package/src/three/entity/models/tadpole.obj +95 -0
- package/src/three/entity/models/turtle.obj +371 -0
- package/src/three/entity/models/vex.obj +325 -0
- package/src/three/entity/models/villager.obj +509 -0
- package/src/three/entity/models/warden.obj +463 -0
- package/src/three/entity/models/witch.obj +647 -0
- package/src/three/entity/models/wolf.obj +509 -0
- package/src/three/entity/models/zombie_villager.obj +463 -0
- package/src/three/entity/objModels.js +1 -0
- package/src/three/fireworks.ts +661 -0
- package/src/three/fireworksRenderer.ts +434 -0
- package/src/three/globals.d.ts +7 -0
- package/src/three/graphicsBackend.ts +274 -0
- package/src/three/graphicsBackendOffThread.ts +107 -0
- package/src/three/hand.ts +89 -0
- package/src/three/holdingBlock.ts +926 -0
- package/src/three/index.ts +20 -0
- package/src/three/itemMesh.ts +427 -0
- package/src/three/modules.d.ts +14 -0
- package/src/three/panorama.ts +308 -0
- package/src/three/panoramaShared.ts +1 -0
- package/src/three/renderSlot.ts +82 -0
- package/src/three/skyboxRenderer.ts +406 -0
- package/src/three/starField.ts +13 -0
- package/src/three/threeJsMedia.ts +731 -0
- package/src/three/threeJsMethods.ts +15 -0
- package/src/three/threeJsParticles.ts +160 -0
- package/src/three/threeJsSound.ts +95 -0
- package/src/three/threeJsUtils.ts +90 -0
- package/src/three/waypointSprite.ts +435 -0
- package/src/three/waypoints.ts +163 -0
- package/src/three/world/cursorBlock.ts +172 -0
- package/src/three/world/vr.ts +257 -0
- package/src/three/worldGeometryExport.ts +259 -0
- package/src/three/worldGeometryHandler.ts +279 -0
- package/src/three/worldRendererThree.ts +1381 -0
- package/src/worldView/index.ts +6 -0
- package/src/worldView/types.ts +66 -0
- package/src/worldView/worldView.ts +424 -0
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
export async function getBufferFromStream (stream) {
|
|
2
|
+
return new Promise((resolve, reject) => {
|
|
3
|
+
let buffer = Buffer.from([])
|
|
4
|
+
stream.on('data', buf => {
|
|
5
|
+
buffer = Buffer.concat([buffer, buf])
|
|
6
|
+
})
|
|
7
|
+
stream.on('end', () => resolve(buffer))
|
|
8
|
+
stream.on('error', reject)
|
|
9
|
+
})
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
// used for custom blocks
|
|
13
|
+
export function getBlockPosKey (pos: { x: number, y: number, z: number }) {
|
|
14
|
+
return `${pos.x},${pos.y},${pos.z}`
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
export function openURL (url, newTab = true) {
|
|
18
|
+
if (newTab) {
|
|
19
|
+
window.open(url, '_blank', 'noopener,noreferrer')
|
|
20
|
+
} else {
|
|
21
|
+
window.open(url, '_self')
|
|
22
|
+
}
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
export const isMobile = () => {
|
|
26
|
+
return window.matchMedia('(pointer: coarse)').matches || navigator.userAgent.includes('Mobile')
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
export function chunkPos (pos: { x: number, z: number }) {
|
|
30
|
+
const x = Math.floor(pos.x / 16)
|
|
31
|
+
const z = Math.floor(pos.z / 16)
|
|
32
|
+
return [x, z]
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
export function sectionPos (pos: { x: number, y: number, z: number }) {
|
|
36
|
+
const x = Math.floor(pos.x / 16)
|
|
37
|
+
const y = Math.floor(pos.y / 16)
|
|
38
|
+
const z = Math.floor(pos.z / 16)
|
|
39
|
+
return [x, y, z]
|
|
40
|
+
}
|
|
@@ -0,0 +1,168 @@
|
|
|
1
|
+
import * as tweenJs from '@tweenjs/tween.js'
|
|
2
|
+
import { AnimationController } from './animationController'
|
|
3
|
+
|
|
4
|
+
export type StateProperties = Record<string, number>
|
|
5
|
+
export type StateGetterFn = () => StateProperties
|
|
6
|
+
export type StateSetterFn = (property: string, value: number) => void
|
|
7
|
+
|
|
8
|
+
// Speed in units per second for each property type
|
|
9
|
+
const DEFAULT_SPEEDS = {
|
|
10
|
+
x: 3000, // pixels/units per second
|
|
11
|
+
y: 3000,
|
|
12
|
+
z: 3000,
|
|
13
|
+
rotation: Math.PI, // radians per second
|
|
14
|
+
scale: 1, // scale units per second
|
|
15
|
+
default: 3000 // default speed for unknown properties
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
export class SmoothSwitcher {
|
|
19
|
+
private readonly animationController = new AnimationController()
|
|
20
|
+
// private readonly currentState: StateProperties = {}
|
|
21
|
+
private readonly defaultState: StateProperties
|
|
22
|
+
private readonly speeds: Record<string, number>
|
|
23
|
+
public currentStateName = ''
|
|
24
|
+
public transitioningToStateName = ''
|
|
25
|
+
|
|
26
|
+
constructor (
|
|
27
|
+
public getState: StateGetterFn,
|
|
28
|
+
public setState: StateSetterFn,
|
|
29
|
+
speeds?: Partial<Record<string, number>>
|
|
30
|
+
) {
|
|
31
|
+
|
|
32
|
+
// Initialize speeds with defaults and overrides
|
|
33
|
+
this.speeds = { ...DEFAULT_SPEEDS }
|
|
34
|
+
if (speeds) {
|
|
35
|
+
Object.assign(this.speeds, speeds)
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
// Store initial values
|
|
39
|
+
this.defaultState = this.getState()
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
/**
|
|
43
|
+
* Calculate transition duration based on the largest property change
|
|
44
|
+
*/
|
|
45
|
+
private calculateDuration (newState: Partial<StateProperties>): number {
|
|
46
|
+
let maxDuration = 0
|
|
47
|
+
const currentState = this.getState()
|
|
48
|
+
|
|
49
|
+
for (const [key, targetValue] of Object.entries(newState)) {
|
|
50
|
+
const currentValue = currentState[key]
|
|
51
|
+
const diff = Math.abs(targetValue! - currentValue)
|
|
52
|
+
const speed = this.getPropertySpeed(key)
|
|
53
|
+
const duration = (diff / speed) * 1000 // Convert to milliseconds
|
|
54
|
+
|
|
55
|
+
maxDuration = Math.max(maxDuration, duration)
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
// Ensure minimum duration of 50ms and maximum of 2000ms
|
|
59
|
+
return Math.min(Math.max(maxDuration, 200), 2000)
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
private getPropertySpeed (property: string): number {
|
|
63
|
+
// Check for specific property speed
|
|
64
|
+
if (property in this.speeds) {
|
|
65
|
+
return this.speeds[property]
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
// Check for property type (rotation, scale, etc.)
|
|
69
|
+
if (property.toLowerCase().includes('rotation')) return this.speeds.rotation
|
|
70
|
+
if (property.toLowerCase().includes('scale')) return this.speeds.scale
|
|
71
|
+
if (property.toLowerCase() === 'x' || property.toLowerCase() === 'y' || property.toLowerCase() === 'z') {
|
|
72
|
+
return this.speeds[property]
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
return this.speeds.default
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
/**
|
|
79
|
+
* Start a transition to a new state
|
|
80
|
+
* @param newState Partial state - only need to specify properties that change
|
|
81
|
+
* @param easing Easing function to use
|
|
82
|
+
*/
|
|
83
|
+
startTransition (
|
|
84
|
+
newState: Partial<StateProperties>,
|
|
85
|
+
stateName?: string,
|
|
86
|
+
onEnd?: () => void,
|
|
87
|
+
easing: (amount: number) => number = tweenJs.Easing.Linear.None,
|
|
88
|
+
onCancelled?: () => void
|
|
89
|
+
): void {
|
|
90
|
+
if (this.isTransitioning) {
|
|
91
|
+
this.animationController.forceFinish(false)
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
this.transitioningToStateName = stateName ?? ''
|
|
95
|
+
const state = this.getState()
|
|
96
|
+
|
|
97
|
+
const duration = this.calculateDuration(newState)
|
|
98
|
+
// console.log('duration', duration, JSON.stringify(state), JSON.stringify(newState))
|
|
99
|
+
|
|
100
|
+
void this.animationController.startAnimation(() => {
|
|
101
|
+
const group = new tweenJs.Group()
|
|
102
|
+
new tweenJs.Tween(state, group)
|
|
103
|
+
.to(newState, duration)
|
|
104
|
+
.easing(easing)
|
|
105
|
+
.onUpdate((obj) => {
|
|
106
|
+
for (const key of Object.keys(obj)) {
|
|
107
|
+
this.setState(key, obj[key])
|
|
108
|
+
}
|
|
109
|
+
})
|
|
110
|
+
.onComplete(() => {
|
|
111
|
+
this.animationController.forceFinish()
|
|
112
|
+
this.currentStateName = this.transitioningToStateName
|
|
113
|
+
this.transitioningToStateName = ''
|
|
114
|
+
onEnd?.()
|
|
115
|
+
})
|
|
116
|
+
.start()
|
|
117
|
+
return group
|
|
118
|
+
}, onCancelled)
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
/**
|
|
122
|
+
* Reset to default state
|
|
123
|
+
*/
|
|
124
|
+
reset (): void {
|
|
125
|
+
this.startTransition(this.defaultState)
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
|
|
129
|
+
/**
|
|
130
|
+
* Update the animation (should be called in your render/update loop)
|
|
131
|
+
*/
|
|
132
|
+
update (): void {
|
|
133
|
+
this.animationController.update()
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
/**
|
|
137
|
+
* Force finish the current transition
|
|
138
|
+
*/
|
|
139
|
+
forceFinish (): void {
|
|
140
|
+
this.animationController.forceFinish()
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
/**
|
|
144
|
+
* Start a new transition to the specified state
|
|
145
|
+
*/
|
|
146
|
+
transitionTo (
|
|
147
|
+
newState: Partial<StateProperties>,
|
|
148
|
+
stateName?: string,
|
|
149
|
+
onEnd?: () => void,
|
|
150
|
+
onCancelled?: () => void
|
|
151
|
+
): void {
|
|
152
|
+
this.startTransition(newState, stateName, onEnd, tweenJs.Easing.Linear.None, onCancelled)
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
/**
|
|
156
|
+
* Get the current value of a property
|
|
157
|
+
*/
|
|
158
|
+
getCurrentValue (property: string): number {
|
|
159
|
+
return this.getState()[property]
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
/**
|
|
163
|
+
* Check if currently transitioning
|
|
164
|
+
*/
|
|
165
|
+
get isTransitioning (): boolean {
|
|
166
|
+
return this.animationController.isActive
|
|
167
|
+
}
|
|
168
|
+
}
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
export function generateSpiralMatrix(distance: number): [number, number][] {
|
|
2
|
+
const n = distance * 2 + 1
|
|
3
|
+
if (n <= 0) {
|
|
4
|
+
return []
|
|
5
|
+
}
|
|
6
|
+
|
|
7
|
+
const matrix: [number, number][] = []
|
|
8
|
+
let x = 0
|
|
9
|
+
let y = 0
|
|
10
|
+
let dx = 0
|
|
11
|
+
let dy = -1
|
|
12
|
+
|
|
13
|
+
for (let i = 0; i < n * n; i++) {
|
|
14
|
+
if (Math.abs(x) <= n / 2 && Math.abs(y) <= n / 2) {
|
|
15
|
+
matrix.push([x, y])
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
if (x === y || (x < 0 && x === -y) || (x > 0 && x === 1 - y)) {
|
|
19
|
+
const temp = dx
|
|
20
|
+
dx = -dy
|
|
21
|
+
dy = temp
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
x += dx
|
|
25
|
+
y += dy
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
return matrix
|
|
29
|
+
}
|
|
@@ -0,0 +1,120 @@
|
|
|
1
|
+
/* eslint-disable unicorn/prefer-dom-node-text-content */
|
|
2
|
+
import { isWebWorker } from '../../three/documentRenderer'
|
|
3
|
+
|
|
4
|
+
const rightOffset = 0
|
|
5
|
+
|
|
6
|
+
const stats = {}
|
|
7
|
+
|
|
8
|
+
let lastY = 40
|
|
9
|
+
export const addNewStat = (id: string, width = 80, x = rightOffset, y = lastY) => {
|
|
10
|
+
if (isWebWorker) return { updateText () {}, setVisibility () {} }
|
|
11
|
+
|
|
12
|
+
const pane = document.createElement('div')
|
|
13
|
+
pane.style.position = 'fixed'
|
|
14
|
+
pane.style.top = `${y ?? lastY}px`
|
|
15
|
+
pane.style.right = `${x}px`
|
|
16
|
+
// gray bg
|
|
17
|
+
pane.style.backgroundColor = 'rgba(0, 0, 0, 0.7)'
|
|
18
|
+
pane.style.color = 'white'
|
|
19
|
+
pane.style.padding = '2px'
|
|
20
|
+
pane.style.fontFamily = 'monospace'
|
|
21
|
+
pane.style.fontSize = '12px'
|
|
22
|
+
pane.style.zIndex = '100'
|
|
23
|
+
pane.style.pointerEvents = 'none'
|
|
24
|
+
document.body.appendChild(pane)
|
|
25
|
+
stats[id] = pane
|
|
26
|
+
if (y === undefined && x === rightOffset) { // otherwise it's a custom position
|
|
27
|
+
// rightOffset += width
|
|
28
|
+
lastY += 20
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
return {
|
|
32
|
+
updateText (text: string) {
|
|
33
|
+
if (pane.innerText === text) return
|
|
34
|
+
pane.innerText = text
|
|
35
|
+
},
|
|
36
|
+
setVisibility (visible: boolean) {
|
|
37
|
+
pane.style.display = visible ? 'block' : 'none'
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
export const addNewStat2 = (id: string, { top, bottom, right, left, displayOnlyWhenWider }: { top?: number, bottom?: number, right?: number, left?: number, displayOnlyWhenWider?: number }) => {
|
|
43
|
+
if (isWebWorker) return { updateText () {}, setVisibility () {} }
|
|
44
|
+
|
|
45
|
+
if (top === undefined && bottom === undefined) top = 0
|
|
46
|
+
const pane = document.createElement('div')
|
|
47
|
+
pane.style.position = 'fixed'
|
|
48
|
+
if (top !== undefined) {
|
|
49
|
+
pane.style.top = `${top}px`
|
|
50
|
+
}
|
|
51
|
+
if (bottom !== undefined) {
|
|
52
|
+
pane.style.bottom = `${bottom}px`
|
|
53
|
+
}
|
|
54
|
+
if (left !== undefined) {
|
|
55
|
+
pane.style.left = `${left}px`
|
|
56
|
+
}
|
|
57
|
+
if (right !== undefined) {
|
|
58
|
+
pane.style.right = `${right}px`
|
|
59
|
+
}
|
|
60
|
+
// gray bg
|
|
61
|
+
pane.style.backgroundColor = 'rgba(0, 0, 0, 0.7)'
|
|
62
|
+
pane.style.color = 'white'
|
|
63
|
+
pane.style.padding = '2px'
|
|
64
|
+
pane.style.fontFamily = 'monospace'
|
|
65
|
+
pane.style.fontSize = '12px'
|
|
66
|
+
pane.style.zIndex = '10000'
|
|
67
|
+
pane.style.pointerEvents = 'none'
|
|
68
|
+
document.body.appendChild(pane)
|
|
69
|
+
stats[id] = pane
|
|
70
|
+
|
|
71
|
+
const resizeCheck = () => {
|
|
72
|
+
if (!displayOnlyWhenWider) return
|
|
73
|
+
pane.style.display = window.innerWidth > displayOnlyWhenWider ? 'block' : 'none'
|
|
74
|
+
}
|
|
75
|
+
window.addEventListener('resize', resizeCheck)
|
|
76
|
+
resizeCheck()
|
|
77
|
+
|
|
78
|
+
return {
|
|
79
|
+
updateText (text: string) {
|
|
80
|
+
pane.innerText = text
|
|
81
|
+
},
|
|
82
|
+
setVisibility (visible: boolean) {
|
|
83
|
+
pane.style.display = visible ? 'block' : 'none'
|
|
84
|
+
}
|
|
85
|
+
}
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
export const updateStatText = (id, text) => {
|
|
89
|
+
if (isWebWorker || !stats[id]) return
|
|
90
|
+
stats[id].innerText = text
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
export const updatePanesVisibility = (visible: boolean) => {
|
|
94
|
+
if (isWebWorker) return
|
|
95
|
+
// eslint-disable-next-line guard-for-in
|
|
96
|
+
for (const id in stats) {
|
|
97
|
+
stats[id].style.display = visible ? 'block' : 'none'
|
|
98
|
+
}
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
export const removeAllStats = () => {
|
|
102
|
+
if (isWebWorker) return
|
|
103
|
+
// eslint-disable-next-line guard-for-in
|
|
104
|
+
for (const id in stats) {
|
|
105
|
+
removeStat(id)
|
|
106
|
+
}
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
export const removeStat = (id) => {
|
|
110
|
+
if (isWebWorker || !stats[id]) return
|
|
111
|
+
stats[id].remove()
|
|
112
|
+
delete stats[id]
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
if (typeof customEvents !== 'undefined' && !isWebWorker) {
|
|
116
|
+
customEvents.on('gameLoaded', () => {
|
|
117
|
+
const chunksLoaded = addNewStat('chunks-loaded', 80, 0, 0)
|
|
118
|
+
const chunksTotal = addNewStat('chunks-read', 80, 0, 0)
|
|
119
|
+
})
|
|
120
|
+
}
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
import { subscribeKey } from 'valtio/utils'
|
|
2
|
+
|
|
3
|
+
// eslint-disable-next-line max-params
|
|
4
|
+
export function watchProperty<T extends Record<string, any>, K> (asyncGetter: (value: T[keyof T]) => Promise<K>, valtioProxy: T, key: keyof T, readySetter: (res: K) => void, cleanup?: (res: K) => void) {
|
|
5
|
+
let i = 0
|
|
6
|
+
let lastRes: K | undefined
|
|
7
|
+
const request = async () => {
|
|
8
|
+
const req = ++i
|
|
9
|
+
const res = await asyncGetter(valtioProxy[key])
|
|
10
|
+
if (req === i) {
|
|
11
|
+
if (lastRes) {
|
|
12
|
+
cleanup?.(lastRes)
|
|
13
|
+
}
|
|
14
|
+
readySetter(res)
|
|
15
|
+
lastRes = res
|
|
16
|
+
} else {
|
|
17
|
+
// rejected
|
|
18
|
+
cleanup?.(res)
|
|
19
|
+
}
|
|
20
|
+
}
|
|
21
|
+
void request()
|
|
22
|
+
return subscribeKey(valtioProxy, key, request)
|
|
23
|
+
}
|
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
import { loadSkinToCanvas } from 'skinview-utils'
|
|
2
|
+
import * as THREE from 'three'
|
|
3
|
+
import stevePng from 'mc-assets/dist/other-textures/latest/entity/player/wide/steve.png'
|
|
4
|
+
import { loadThreeJsTextureFromUrl } from '../../three/threeJsUtils'
|
|
5
|
+
import { createCanvas, loadImageFromUrl } from '../utils'
|
|
6
|
+
|
|
7
|
+
export const stevePngUrl = stevePng
|
|
8
|
+
export const steveTexture = loadThreeJsTextureFromUrl(stevePngUrl)
|
|
9
|
+
|
|
10
|
+
const config = {
|
|
11
|
+
apiEnabled: true,
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
export const setSkinsConfig = (newConfig: Partial<typeof config>) => {
|
|
15
|
+
Object.assign(config, newConfig)
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
export async function loadSkinFromUsername (username: string, type: 'skin' | 'cape'): Promise<string | undefined> {
|
|
19
|
+
if (!config.apiEnabled) return
|
|
20
|
+
|
|
21
|
+
if (type === 'cape') return
|
|
22
|
+
const url = `https://playerdb.co/api/player/minecraft/${username}`
|
|
23
|
+
const response = await fetch(url)
|
|
24
|
+
if (!response.ok) return
|
|
25
|
+
|
|
26
|
+
const data: {
|
|
27
|
+
data: {
|
|
28
|
+
player: {
|
|
29
|
+
skin_texture: string
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
} = await response.json()
|
|
33
|
+
return data.data.player.skin_texture
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
export const parseSkinTexturesValue = (value: string) => {
|
|
37
|
+
const decodedData: {
|
|
38
|
+
textures: {
|
|
39
|
+
SKIN: {
|
|
40
|
+
url: string
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
} = JSON.parse(Buffer.from(value, 'base64').toString())
|
|
44
|
+
return decodedData.textures?.SKIN?.url
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
export async function loadSkinImage (skinUrl: string): Promise<{ canvas: OffscreenCanvas, image: ImageBitmap }> {
|
|
48
|
+
if (!skinUrl.startsWith('data:')) {
|
|
49
|
+
skinUrl = await fetchAndConvertBase64Skin(skinUrl.replace('http://', 'https://'))
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
const image = await loadImageFromUrl(skinUrl)
|
|
53
|
+
const skinCanvas = createCanvas(64, 64)
|
|
54
|
+
loadSkinToCanvas(skinCanvas, image)
|
|
55
|
+
return { canvas: skinCanvas, image }
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
const fetchAndConvertBase64Skin = async (skinUrl: string) => {
|
|
59
|
+
const response = await fetch(skinUrl, { })
|
|
60
|
+
const arrayBuffer = await response.arrayBuffer()
|
|
61
|
+
const base64 = Buffer.from(arrayBuffer).toString('base64')
|
|
62
|
+
return `data:image/png;base64,${base64}`
|
|
63
|
+
}
|
package/src/lib/utils.ts
ADDED
|
@@ -0,0 +1,76 @@
|
|
|
1
|
+
export const loadScript = async function (scriptSrc: string, highPriority = true): Promise<HTMLScriptElement> {
|
|
2
|
+
const existingScript = document.querySelector<HTMLScriptElement>(`script[src="${scriptSrc}"]`)
|
|
3
|
+
if (existingScript) {
|
|
4
|
+
return existingScript
|
|
5
|
+
}
|
|
6
|
+
|
|
7
|
+
return new Promise((resolve, reject) => {
|
|
8
|
+
const scriptElement = document.createElement('script')
|
|
9
|
+
scriptElement.src = scriptSrc
|
|
10
|
+
|
|
11
|
+
if (highPriority) {
|
|
12
|
+
scriptElement.fetchPriority = 'high'
|
|
13
|
+
}
|
|
14
|
+
scriptElement.async = true
|
|
15
|
+
|
|
16
|
+
scriptElement.addEventListener('load', () => {
|
|
17
|
+
resolve(scriptElement)
|
|
18
|
+
})
|
|
19
|
+
|
|
20
|
+
scriptElement.onerror = (error) => {
|
|
21
|
+
reject(new Error(typeof error === 'string' ? error : (error as any).message))
|
|
22
|
+
scriptElement.remove()
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
document.head.appendChild(scriptElement)
|
|
26
|
+
})
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
const detectFullOffscreenCanvasSupport = () => {
|
|
30
|
+
if (typeof OffscreenCanvas === 'undefined') return false
|
|
31
|
+
try {
|
|
32
|
+
const canvas = new OffscreenCanvas(1, 1)
|
|
33
|
+
// Try to get a WebGL context - this will fail on iOS where only 2D is supported (iOS 16)
|
|
34
|
+
const gl = canvas.getContext('webgl2') || canvas.getContext('webgl')
|
|
35
|
+
return gl !== null
|
|
36
|
+
} catch (e) {
|
|
37
|
+
return false
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
const hasFullOffscreenCanvasSupport = detectFullOffscreenCanvasSupport()
|
|
42
|
+
|
|
43
|
+
export const createCanvas = (width: number, height: number): OffscreenCanvas => {
|
|
44
|
+
if (hasFullOffscreenCanvasSupport) {
|
|
45
|
+
return new OffscreenCanvas(width, height)
|
|
46
|
+
}
|
|
47
|
+
const canvas = document.createElement('canvas')
|
|
48
|
+
canvas.width = width
|
|
49
|
+
canvas.height = height
|
|
50
|
+
return canvas as unknown as OffscreenCanvas // todo-low
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
export async function loadImageFromUrl(imageUrl: string): Promise<ImageBitmap> {
|
|
54
|
+
const response = await fetch(imageUrl)
|
|
55
|
+
const blob = await response.blob()
|
|
56
|
+
return createImageBitmap(blob)
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
export const versionToNumber = (ver: string) => {
|
|
60
|
+
const [x, y = '0', z = '0'] = ver.split('.')
|
|
61
|
+
return +`${x.padStart(2, '0')}${y.padStart(2, '0')}${z.padStart(2, '0')}`
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
export const versionToMajor = (version: string) => {
|
|
65
|
+
const [x, y = '0'] = version.split('.')
|
|
66
|
+
return `${x.padStart(2, '0')}.${y.padStart(2, '0')}`
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
export const versionsMapToMajor = <T>(versionsMap: Record<string, T>) => {
|
|
70
|
+
const majorVersions = {} as Record<string, T>
|
|
71
|
+
for (const [ver, data] of Object.entries(versionsMap)) {
|
|
72
|
+
const major = versionToMajor(ver)
|
|
73
|
+
majorVersions[major] = data
|
|
74
|
+
}
|
|
75
|
+
return majorVersions
|
|
76
|
+
}
|