forked from hyperfy-xyz/hyperfy
-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathworld-clean.mjs
More file actions
129 lines (115 loc) · 3.58 KB
/
world-clean.mjs
File metadata and controls
129 lines (115 loc) · 3.58 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
import 'dotenv-flow/config'
import fs from 'fs-extra'
import path from 'path'
import Knex from 'knex'
import moment from 'moment'
import { fileURLToPath } from 'url'
const DRY_RUN = false
const world = process.env.WORLD || 'world'
const __dirname = path.dirname(fileURLToPath(import.meta.url))
const rootDir = path.join(__dirname, './')
const worldDir = path.join(rootDir, world)
const assetsDir = path.join(worldDir, '/assets')
const db = Knex({
client: 'better-sqlite3',
connection: {
filename: `./${world}/db.sqlite`,
},
useNullAsDefault: true,
})
// TODO: run any missing migrations first?
let blueprints = new Set()
const blueprintRows = await db('blueprints')
for (const row of blueprintRows) {
const blueprint = JSON.parse(row.data)
blueprints.add(blueprint)
}
const entities = []
const entityRows = await db('entities')
for (const row of entityRows) {
const entity = JSON.parse(row.data)
entities.push(entity)
}
const vrms = new Set()
const userRows = await db('users').select('avatar')
for (const user of userRows) {
if (!user.avatar) continue
const avatar = user.avatar.replace('asset://', '')
vrms.add(avatar)
}
const fileAssets = new Set()
const files = fs.readdirSync(assetsDir)
for (const file of files) {
const filePath = path.join(assetsDir, file)
const isDirectory = fs.statSync(filePath).isDirectory()
if (isDirectory) continue
const relPath = path.relative(assetsDir, filePath)
// HACK: we only want to include uploaded assets (not core/assets/*) so we do a check
// if its filename is a 64 character hash
const isAsset = relPath.split('.')[0].length === 64
if (!isAsset) continue
fileAssets.add(relPath)
}
/**
* Phase 1:
* Remove all blueprints that no entities reference any more.
* The world doesn't need them, and we shouldn't be loading them in and sending dead blueprints to all the clients.
*/
const blueprintsToDelete = []
for (const blueprint of blueprints) {
const canDelete = !entities.find(e => e.blueprint === blueprint.id)
if (canDelete) {
blueprintsToDelete.push(blueprint)
}
}
console.log(`deleting ${blueprintsToDelete.length} blueprints`)
for (const blueprint of blueprintsToDelete) {
blueprints.delete(blueprint)
if (!DRY_RUN) {
await db('blueprints').where('id', blueprint.id).delete()
}
console.log('delete blueprint:', blueprint.id)
}
/**
* Phase 2:
* Remove all asset files not referenced by a blueprint or used as an avatar.
* The world no longer uses/needs them.
*
*/
const blueprintAssets = new Set()
for (const blueprint of blueprints) {
if (blueprint.model && blueprint.model.startsWith('asset://')) {
const asset = blueprint.model.replace('asset://', '')
blueprintAssets.add(asset)
// console.log(asset)
}
if (blueprint.script && blueprint.script.startsWith('asset://')) {
const asset = blueprint.script.replace('asset://', '')
blueprintAssets.add(asset)
// console.log(asset)
}
for (const key in blueprint.props) {
const url = blueprint.props[key]?.url
if (!url) continue
const asset = url.replace('asset://', '')
blueprintAssets.add(asset)
// console.log(asset)
}
}
const filesToDelete = []
for (const fileAsset of fileAssets) {
const isUsedByBlueprint = blueprintAssets.has(fileAsset)
const isUsedByUser = vrms.has(fileAsset)
if (!isUsedByBlueprint && !isUsedByUser) {
filesToDelete.push(fileAsset)
}
}
console.log(`deleting ${filesToDelete.length} assets`)
for (const fileAsset of filesToDelete) {
const fullPath = path.join(assetsDir, fileAsset)
if (!DRY_RUN) {
fs.removeSync(fullPath)
}
console.log('delete asset:', fileAsset)
}
process.exit()