chore: initial commit

This commit is contained in:
2026-10-22 17:07:08 +08:00
commit 0acef64f48
200 changed files with 35546 additions and 0 deletions
+149
View File
@@ -0,0 +1,149 @@
import assert from 'node:assert/strict';
import {createRequire} from 'node:module';
import {test} from 'node:test';
import {tsImport} from 'tsx/esm/api';
const {parse_inventory_item: parse} = await tsImport('../utils/inventory_item.ts', import.meta.url);
const require = createRequire(import.meta.url);
const mf = createRequire(require.resolve('@wp2508/mineflayer'));
const registry = mf('prismarine-registry')('1.21.4');
const Item = mf('prismarine-item')(registry);
const component = (type, data) => ({type, data});
const tag = (type, value) => ({type, value});
const plain = item => parse(item, 36, registry);
test('modern custom names and lore become rich logs with explicit false and no executable events', () => {
const item = new Item(registry.itemsByName.clock.id, 8);
item.components = [component('custom_name', tag('compound', {
text: tag('string', ''), bold: tag('byte', 1), italic: tag('byte', 1),
extra: tag('list', {type: 'compound', value: [{
text: tag('string', '菜单'), color: tag('string', 'gold'),
bold: tag('byte', 0), italic: tag('byte', 0), underlined: tag('byte', 0),
clickEvent: tag('compound', {action: tag('string', 'run_command'), value: tag('string', '/op me')}),
}]}),
})), component('lore', [tag('compound', {text: tag('string', '右键打开'), color: tag('string', 'gray')}), tag('string', '')])];
const result = plain(item);
assert.equal(result.count, 8);
assert.deepEqual(result.i18n, [{lang: 'zh-CN', name: '时钟', description: ''}]);
assert.equal(result.displayName[0].message, '菜单');
assert.equal(result.displayName[0].font_color, '#FFAA00');
assert.deepEqual(result.displayName[0].font_style, ['normal']);
assert.equal(result.displayName[0].clickEvent, undefined);
assert.equal(result.lore[0][0].message, '右键打开');
assert.deepEqual(result.lore[1], []);
assert.equal('components' in result, false);
assert.equal('nbt' in result, false);
assert.equal(result.truncated, false);
});
test('legacy NBT resolves names enchants damage and remaining unknown tags', () => {
const item = {
type: registry.itemsByName.diamond_sword.id, name: 'diamond_sword', count: 1, displayName: 'Diamond Sword',
nbt: tag('compound', {
display: tag('compound', {Name: tag('string', '{"text":"剑","color":"aqua"}'), Lore: tag('list', {type: 'string', value: ['{"text":"描述"}']})}),
Enchantments: tag('list', {type: 'compound', value: [{id: tag('string', 'minecraft:sharpness'), lvl: tag('short', 5)}]}),
Damage: tag('int', 20), Unbreakable: tag('byte', 1), HideFlags: tag('int', 1),
plugin: tag('compound', {menu: tag('string', 'main'), timestamp: tag('long', 123n)}),
}),
};
const result = plain(item);
assert.equal(result.displayName[0].message, '剑');
assert.equal(result.lore[0][0].message, '描述');
assert.deepEqual(result.enchants, [{name: 'sharpness', i18n: [{lang: 'zh-CN', name: '锋利', description: ''}], level: 5, hidden: true}]);
assert.equal(result.durability.used, 20);
assert.equal(result.durability.remaining, result.durability.max - 20);
assert.equal(result.unbreakable, true);
assert.deepEqual(result.details.extra.legacy.plugin, {menu: 'main', timestamp: '123'});
assert.equal(result.details.extra.legacy.Enchantments, undefined);
});
test('modern enchantments potions attributes and flags have structured values', () => {
const item = new Item(registry.itemsByName.diamond_sword.id, 1);
const id = registry.enchantmentsByName.sharpness.id;
item.components = [
component('item_name', {text: '物品名称'}),
component('enchantments', {enchantments: [{id, level: 4}], showTooltip: false}),
component('max_damage', 100), component('damage', 12), component('unbreakable', {}),
component('enchantment_glint_override', false),
component('potion_contents', {potionId: 3, customColor: 255, customEffects: [{id: 0, amplifier: 1, duration: 200}]}),
component('attribute_modifiers', {showTooltip: false, attributes: [{typeId: 1, name: 'bonus', value: 3, operation: 'add', slot: 'main_hand'}]}),
];
const result = plain(item);
assert.equal(result.displayName[0].message, '物品名称');
assert.deepEqual(result.enchants, [{id, name: 'sharpness', i18n: [{lang: 'zh-CN', name: '锋利', description: ''}], level: 4, hidden: true}]);
assert.deepEqual(result.durability, {used: 12, max: 100, remaining: 88});
assert.equal(result.glint, false);
assert.equal(result.details.potion.effects[0].amplifier, 1);
assert.equal(result.details.potion.effects[0].duration, 200);
assert.deepEqual(result.details.attributes.modifiers[0], {type: 1, name: 'bonus', amount: 3, operation: 'add', slot: 'main_hand', hidden: false, displayName: []});
});
test('nested modern containers and legacy bundles use parsed items rather than protocol slots', () => {
const item = new Item(registry.itemsByName.shulker_box.id, 1);
item.components = [component('container', {contents: [{itemCount: 4, itemId: registry.itemsByName.clock.id,
components: [component('custom_name', {text: '嵌套时钟'})]}]})];
const result = plain(item);
assert.equal(result.details.container[0].slot, 0);
assert.equal(result.details.container[0].count, 4);
assert.equal(result.details.container[0].displayName[0].message, '嵌套时钟');
const legacy = {type: registry.itemsByName.bundle.id, name: 'bundle', count: 1,
nbt: tag('compound', {Items: tag('list', {type: 'compound', value: [{id: tag('string', 'minecraft:stone'), Count: tag('byte', 3)}]})})};
assert.equal(plain(legacy).details.bundle[0].name, 'stone');
assert.equal(plain(legacy).details.bundle[0].count, 3);
});
test('written and writable books retain different page semantics and truncation is explicit', () => {
const written = new Item(registry.itemsByName.written_book.id, 1);
written.components = [component('written_book_content', {rawTitle: 'Book', author: 'Author', pages: [{content: tag('compound', {text: tag('string', 'Page')})}]})];
assert.equal(plain(written).book.pages[0][0].message, 'Page');
const writable = new Item(registry.itemsByName.writable_book.id, 1);
writable.components = [component('writable_book_content', {pages: [{content: '{"text":"literal"}'}]})];
assert.equal(plain(writable).book.pages[0][0].message, '{"text":"literal"}');
written.components.push(component('custom_data', {long: 'x'.repeat(100_000)}));
assert.equal(plain(written).truncated, true);
assert.doesNotThrow(() => JSON.stringify(plain(written)));
const cycle = {}; cycle.self = cycle;
written.components = [component('custom_data', cycle)];
assert.equal(plain(written).details.extra.custom_data.self, null);
assert.equal(plain(written).truncated, true);
});
test('legacy section colors and current tooltip component IDs are resolved', () => {
const legacy = {type: 1, count: 1, name: 'stone', displayName: 'Stone',
nbt: tag('compound', {display: tag('compound', {Name: tag('string', '§6§l菜单§r 普通')})})};
const result = plain(legacy);
assert.equal(result.displayName[0].message, '菜单');
assert.equal(result.displayName[0].font_color, '#FFAA00');
assert.deepEqual(result.displayName[0].font_style, ['bold']);
const latest = mf('prismarine-registry')('26.2');
const mappings = latest.protocol.types.SlotComponentType[1].mappings;
const enchantComponentId = Number(Object.keys(mappings).find(id => mappings[id] === 'enchantments'));
const item = {type: latest.itemsByName.diamond_sword.id, count: 1, components: [
component('enchantments', {enchantments: [{id: 0, level: 1}]}),
component('tooltip_display', {hideTooltip: false, hiddenComponents: [enchantComponentId]}),
component('attribute_modifiers', [{typeId: 0, name: 'test', value: 2, operation: 'add', slot: 'main_hand', display: {type: 'hidden'}}]),
]};
const parsed = parse(item, 0, latest);
assert.equal(parsed.enchants[0].hidden, true);
assert.equal(parsed.details.attributes.modifiers[0].amount, 2);
assert.equal(parsed.details.attributes.modifiers[0].hidden, true);
assert.deepEqual(parsed.details.tooltip.hiddenComponents, ['enchantments']);
});
test('item Chinese names use block keys then item keys and fall back to the identifier', () => {
assert.equal(plain({type: 1, name: 'stone', count: 1}).i18n[0].name, '石头');
assert.equal(plain({type: 1, name: 'diamond_sword', count: 1}).i18n[0].name, '钻石剑');
assert.equal(plain({type: 1, name: 'plugin_unknown_item', count: 1}).i18n[0].name, 'plugin_unknown_item');
const nested = plain({type: registry.itemsByName.bundle.id, name: 'bundle', count: 1,
components: [component('bundle_contents', {contents: [{itemId: 1, itemCount: 2}]})]});
assert.equal(nested.details.bundle[0].i18n[0].name, '石头');
});
test('stored and unknown enchantments include translated names or identifier fallbacks', () => {
const parsed = plain({type: registry.itemsByName.enchanted_book.id, count: 1, components: [component('stored_enchantments', {
enchantments: [{id: 'minecraft:mending', level: 1}, {id: 'plugin_custom', level: 2}, {id: 999999, level: 3}],
})]});
assert.equal(parsed.enchants[0].i18n[0].name, '经验修补');
assert.equal(parsed.enchants[1].i18n[0].name, 'plugin_custom');
assert.equal(parsed.enchants[2].i18n[0].name, '999999');
});