Skip to content

Commit fd7f71e

Browse files
fix(nrc_cosmetics): export companion files under resource-path-safe names
Particle JSON, particle textures and sounds kept whatever name they had on disk. Minecraft rejects anything outside [a-z0-9/._-] in a resource path at render time, so an uppercase particle texture crashed the game ingame. Normalize the stems on export, keep the Snowstorm texture field and the sound id in sync with the renamed files. Bump to 1.13.0 and rebuild.
1 parent 0f7c91f commit fd7f71e

5 files changed

Lines changed: 69 additions & 25 deletions

File tree

‎plugins/nrc_cosmetics/nrc_cosmetics.js‎

Lines changed: 34 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -3788,7 +3788,7 @@ __webpack_require__.r(__webpack_exports__);
37883788

37893789

37903790
function soundEffectId(namespace, folder, id, fileName) {
3791-
return namespace + ':' + folder + '/' + id + '/' + (0,_effectKeyframes__WEBPACK_IMPORTED_MODULE_3__.withoutSuffix)(fileName);
3791+
return namespace + ':' + folder + '/' + id + '/' + (0,_effectKeyframes__WEBPACK_IMPORTED_MODULE_3__.companionStem)(fileName);
37923792
}
37933793
function particleEffectId(namespace, particleJson) {
37943794
const meta = (0,_particleTexture__WEBPACK_IMPORTED_MODULE_1__.readParticleMeta)(particleJson);
@@ -3925,11 +3925,11 @@ function listEffectFiles(channels) {
39253925
return [];
39263926
const seen = new Set();
39273927
const refs = [];
3928-
const add = (path, name) => {
3928+
const add = (path, name, textureName) => {
39293929
if (seen.has(path))
39303930
return;
39313931
seen.add(path);
3932-
refs.push({ path, name });
3932+
refs.push({ path, name, textureName });
39333933
};
39343934
for (const keyframe of (0,_effectKeyframes__WEBPACK_IMPORTED_MODULE_3__.allEffectKeyframes)()) {
39353935
if (channels && channels.indexOf(keyframe.channel) === -1)
@@ -3939,15 +3939,16 @@ function listEffectFiles(channels) {
39393939
const full = point.file;
39403940
if (!full || (0,_vanillaSounds__WEBPACK_IMPORTED_MODULE_2__.isVanillaSoundPath)(full))
39413941
continue;
3942-
let name = _core_node__WEBPACK_IMPORTED_MODULE_0__.nodePath.basename(full);
3943-
if (suffix && !(0,_effectKeyframes__WEBPACK_IMPORTED_MODULE_3__.isCompanionFile)(name))
3944-
name = (0,_effectKeyframes__WEBPACK_IMPORTED_MODULE_3__.withoutSuffix)(name) + suffix;
3945-
add(full, name);
3946-
if (keyframe.channel !== 'particle')
3942+
const name = (0,_effectKeyframes__WEBPACK_IMPORTED_MODULE_3__.companionStem)(_core_node__WEBPACK_IMPORTED_MODULE_0__.nodePath.basename(full)) + suffix;
3943+
if (keyframe.channel !== 'particle') {
3944+
add(full, name);
39473945
continue;
3946+
}
39483947
const texture = (0,_particleTexture__WEBPACK_IMPORTED_MODULE_1__.particleTextureOf)(full);
3948+
const textureName = texture ? (0,_effectKeyframes__WEBPACK_IMPORTED_MODULE_3__.companionStem)(_core_node__WEBPACK_IMPORTED_MODULE_0__.nodePath.basename(texture)) : undefined;
3949+
add(full, name, textureName);
39493950
if (texture)
3950-
add(texture, _core_node__WEBPACK_IMPORTED_MODULE_0__.nodePath.basename(texture));
3951+
add(texture, textureName + '.png');
39513952
}
39523953
}
39533954
return refs;
@@ -3956,14 +3957,28 @@ function collectEffectFiles(channels) {
39563957
const out = [];
39573958
for (const ref of listEffectFiles(channels)) {
39583959
try {
3959-
out.push({ name: ref.name, content: _core_node__WEBPACK_IMPORTED_MODULE_0__.fs.readFileSync(ref.path) });
3960+
const content = _core_node__WEBPACK_IMPORTED_MODULE_0__.fs.readFileSync(ref.path);
3961+
out.push({ name: ref.name, content: ref.textureName ? withTextureName(content, ref.textureName) : content });
39603962
}
39613963
catch (e) {
39623964
(0,_core_log__WEBPACK_IMPORTED_MODULE_4__.log)('effect files: could not read ' + ref.path + ': ' + e);
39633965
}
39643966
}
39653967
return out;
39663968
}
3969+
function withTextureName(content, textureName) {
3970+
try {
3971+
const json = JSON.parse(content.toString('utf8'));
3972+
const params = json.particle_effect.description.basic_render_parameters;
3973+
if (String(params.texture || '').split('/').pop() === textureName)
3974+
return content;
3975+
params.texture = textureName;
3976+
return JSON.stringify(json, null, 2);
3977+
}
3978+
catch (e) {
3979+
return content;
3980+
}
3981+
}
39673982

39683983

39693984
/***/ },
@@ -3979,6 +3994,7 @@ __webpack_require__.r(__webpack_exports__);
39793994
/* harmony export */ CHANNEL_SUFFIX: () => (/* binding */ CHANNEL_SUFFIX),
39803995
/* harmony export */ EFFECT_CHANNELS: () => (/* binding */ EFFECT_CHANNELS),
39813996
/* harmony export */ allEffectKeyframes: () => (/* binding */ allEffectKeyframes),
3997+
/* harmony export */ companionStem: () => (/* binding */ companionStem),
39823998
/* harmony export */ countEffectKeyframes: () => (/* binding */ countEffectKeyframes),
39833999
/* harmony export */ effectsAnimator: () => (/* binding */ effectsAnimator),
39844000
/* harmony export */ findEffectKeyframe: () => (/* binding */ findEffectKeyframe),
@@ -3999,6 +4015,10 @@ function isCompanionFile(name) {
39994015
function withoutSuffix(name) {
40004016
return name.replace(SUFFIX_PATTERN, '').toLowerCase();
40014017
}
4018+
// Minecraft resource paths: [a-z0-9/._-] only, enforced at render time.
4019+
function companionStem(name) {
4020+
return withoutSuffix(name).replace(/\.png$/i, '').replace(/[^a-z0-9._-]+/g, '_');
4021+
}
40024022
function effectsAnimator(animation, create = false) {
40034023
if (!animation || !animation.animators)
40044024
return null;
@@ -4586,6 +4606,8 @@ __webpack_require__.r(__webpack_exports__);
45864606
/* harmony export */ });
45874607
/* harmony import */ var _core_node__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ../core/node */ "./ts/core/node.ts");
45884608
/* harmony import */ var _core_log__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! ../core/log */ "./ts/core/log.ts");
4609+
/* harmony import */ var _effectKeyframes__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(/*! ./effectKeyframes */ "./ts/platform/effectKeyframes.ts");
4610+
45894611

45904612

45914613
const PLACEHOLDER_NAME = 'missing_particle.png';
@@ -4738,7 +4760,7 @@ function setParticleTexture(particleFile, pngPath) {
47384760
(0,_core_log__WEBPACK_IMPORTED_MODULE_1__.log)('setParticleTexture: could not read ' + particleFile + ': ' + e);
47394761
return false;
47404762
}
4741-
const name = _core_node__WEBPACK_IMPORTED_MODULE_0__.nodePath.basename(pngPath).replace(/\.png$/i, '');
4763+
const name = (0,_effectKeyframes__WEBPACK_IMPORTED_MODULE_2__.companionStem)(_core_node__WEBPACK_IMPORTED_MODULE_0__.nodePath.basename(pngPath));
47424764
const target = _core_node__WEBPACK_IMPORTED_MODULE_0__.nodePath.join(_core_node__WEBPACK_IMPORTED_MODULE_0__.nodePath.dirname(particleFile), name + '.png');
47434765
try {
47444766
if (_core_node__WEBPACK_IMPORTED_MODULE_0__.nodePath.resolve(pngPath) !== _core_node__WEBPACK_IMPORTED_MODULE_0__.nodePath.resolve(target)) {
@@ -64709,7 +64731,7 @@ if ( typeof window !== 'undefined' ) {
6470964731
\**********************/
6471064732
(module) {
6471164733

64712-
module.exports = /*#__PURE__*/JSON.parse('{"name":"nrc_cosmetics","version":"1.12.0","private":true,"description":"NoRisk Client Cosmetics","main":"index.js","scripts":{"build":"npm run build:only","build:only":"webpack","start":"webpack --watch --mode=development","tsc":"tsc --noEmit"},"author":"NoRisk Client","license":"MIT","blockbenchConfig":{"title":"NoRisk Client Cosmetics","author":"NoRisk Client","icon":"fa-shapes","description":"Author NoRisk Client emotes against the player rig and export NRC-ready animation + cosmetic metadata.","min_version":"4.11.0","variant":"both"},"devDependencies":{"blockbench-types":"^4.9.0","ts-loader":"^9.4.4","typescript":"^4.9.5","webpack":"^5.88.2","webpack-cli":"^5.1.4"},"dependencies":{"@noriskclient/nrc-skin-renderer":"^0.3.1","fflate":"^0.8.3","three":"^0.160.1"}}');
64734+
module.exports = /*#__PURE__*/JSON.parse('{"name":"nrc_cosmetics","version":"1.13.0","private":true,"description":"NoRisk Client Cosmetics","main":"index.js","scripts":{"build":"npm run build:only","build:only":"webpack","start":"webpack --watch --mode=development","tsc":"tsc --noEmit"},"author":"NoRisk Client","license":"MIT","blockbenchConfig":{"title":"NoRisk Client Cosmetics","author":"NoRisk Client","icon":"fa-shapes","description":"Author NoRisk Client emotes against the player rig and export NRC-ready animation + cosmetic metadata.","min_version":"4.11.0","variant":"both"},"devDependencies":{"blockbench-types":"^4.9.0","ts-loader":"^9.4.4","typescript":"^4.9.5","webpack":"^5.88.2","webpack-cli":"^5.1.4"},"dependencies":{"@noriskclient/nrc-skin-renderer":"^0.3.1","fflate":"^0.8.3","three":"^0.160.1"}}');
6471364735

6471464736
/***/ },
6471564737

‎plugins/nrc_cosmetics/src/package.json‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{
22
"name": "nrc_cosmetics",
3-
"version": "1.12.0",
3+
"version": "1.13.0",
44
"private": true,
55
"description": "NoRisk Client Cosmetics",
66
"main": "index.js",

‎plugins/nrc_cosmetics/src/ts/platform/effectFiles.ts‎

Lines changed: 27 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@ import { fs, nodePath, tempDir, toBuffer } from '../core/node';
33
import { particleTextureOf, readParticleMeta } from './particleTexture';
44
import { isVanillaSoundPath } from './vanillaSounds';
55
import {
6-
CHANNEL_SUFFIX, allEffectKeyframes, isCompanionFile, withoutSuffix,
6+
CHANNEL_SUFFIX, allEffectKeyframes, companionStem, withoutSuffix,
77
} from './effectKeyframes';
88
import { log } from '../core/log';
99

@@ -13,7 +13,7 @@ export interface CompanionFile {
1313
}
1414

1515
export function soundEffectId(namespace: string, folder: string, id: string, fileName: string): string {
16-
return namespace + ':' + folder + '/' + id + '/' + withoutSuffix(fileName);
16+
return namespace + ':' + folder + '/' + id + '/' + companionStem(fileName);
1717
}
1818

1919
export function particleEffectId(namespace: string, particleJson: string): string | null {
@@ -144,17 +144,18 @@ export function linkEffectFiles(scope: string, files: CompanionFile[]) {
144144
export interface EffectFileRef {
145145
name: string;
146146
path: string;
147+
textureName?: string;
147148
}
148149

149150
export function listEffectFiles(channels?: string[]): EffectFileRef[] {
150151
if (!fs || !nodePath) return [];
151152

152153
const seen = new Set<string>();
153154
const refs: EffectFileRef[] = [];
154-
const add = (path: string, name: string) => {
155+
const add = (path: string, name: string, textureName?: string) => {
155156
if (seen.has(path)) return;
156157
seen.add(path);
157-
refs.push({ path, name });
158+
refs.push({ path, name, textureName });
158159
};
159160

160161
for (const keyframe of allEffectKeyframes()) {
@@ -165,13 +166,15 @@ export function listEffectFiles(channels?: string[]): EffectFileRef[] {
165166
const full = point.file;
166167
if (!full || isVanillaSoundPath(full)) continue;
167168

168-
let name = nodePath.basename(full);
169-
if (suffix && !isCompanionFile(name)) name = withoutSuffix(name) + suffix;
170-
add(full, name);
171-
172-
if (keyframe.channel !== 'particle') continue;
169+
const name = companionStem(nodePath.basename(full)) + suffix;
170+
if (keyframe.channel !== 'particle') {
171+
add(full, name);
172+
continue;
173+
}
173174
const texture = particleTextureOf(full);
174-
if (texture) add(texture, nodePath.basename(texture));
175+
const textureName = texture ? companionStem(nodePath.basename(texture)) : undefined;
176+
add(full, name, textureName);
177+
if (texture) add(texture, textureName + '.png');
175178
}
176179
}
177180
return refs;
@@ -181,10 +184,23 @@ export function collectEffectFiles(channels?: string[]): OutFile[] {
181184
const out: OutFile[] = [];
182185
for (const ref of listEffectFiles(channels)) {
183186
try {
184-
out.push({ name: ref.name, content: fs.readFileSync(ref.path) });
187+
const content = fs.readFileSync(ref.path);
188+
out.push({ name: ref.name, content: ref.textureName ? withTextureName(content, ref.textureName) : content });
185189
} catch (e) {
186190
log('effect files: could not read ' + ref.path + ': ' + e);
187191
}
188192
}
189193
return out;
190194
}
195+
196+
function withTextureName(content: any, textureName: string): Uint8Array | string {
197+
try {
198+
const json = JSON.parse(content.toString('utf8'));
199+
const params = json.particle_effect.description.basic_render_parameters;
200+
if (String(params.texture || '').split('/').pop() === textureName) return content;
201+
params.texture = textureName;
202+
return JSON.stringify(json, null, 2);
203+
} catch (e) {
204+
return content;
205+
}
206+
}

‎plugins/nrc_cosmetics/src/ts/platform/effectKeyframes.ts‎

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,11 @@ export function withoutSuffix(name: string): string {
1515
return name.replace(SUFFIX_PATTERN, '').toLowerCase();
1616
}
1717

18+
// Minecraft resource paths: [a-z0-9/._-] only, enforced at render time.
19+
export function companionStem(name: string): string {
20+
return withoutSuffix(name).replace(/\.png$/i, '').replace(/[^a-z0-9._-]+/g, '_');
21+
}
22+
1823
export function effectsAnimator(animation: any, create = false): any {
1924
if (!animation || !animation.animators) return null;
2025
if (!animation.animators.effects && create) {

‎plugins/nrc_cosmetics/src/ts/platform/particleTexture.ts‎

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
import { fs, nodePath, tempDir, toBuffer } from '../core/node';
22
import { log } from '../core/log';
3+
import { companionStem } from './effectKeyframes';
34

45
const PLACEHOLDER_NAME = 'missing_particle.png';
56
const PLACEHOLDER_SIZE = 128;
@@ -159,7 +160,7 @@ export function setParticleTexture(particleFile: string, pngPath: string): boole
159160
return false;
160161
}
161162

162-
const name = nodePath.basename(pngPath).replace(/\.png$/i, '');
163+
const name = companionStem(nodePath.basename(pngPath));
163164
const target = nodePath.join(nodePath.dirname(particleFile), name + '.png');
164165

165166
try {

0 commit comments

Comments
 (0)