From 15d1f8392b87d576484cf3ccb34a9664e186ae1e Mon Sep 17 00:00:00 2001 From: MiaoWoo Date: Mon, 20 Jun 2022 00:48:00 +0800 Subject: [PATCH] release: v0.23.0 1. add item api 2. support rollup source map 3. fix database drvice error 4. support loliserver 5. support 1.19 bukkit chat 6. config add migrate options 7. Signed-off-by: MiaoWoo --- lerna.json | 3 + package.json | 2 +- packages/api/src/console.ts | 193 ++- packages/api/src/constants.ts | 15 +- packages/api/src/database.ts | 4 + packages/api/src/index.ts | 2 + packages/api/src/item.ts | 20 + packages/api/src/plugin.ts | 10 +- packages/bukkit/src/event.ts | 11 +- packages/bukkit/src/index.ts | 1 + packages/bukkit/src/internal/chat.ts | 43 +- packages/bukkit/src/internal/item.ts | 2270 ++++++++++++++++++++++++++ packages/bukkit/src/item.ts | 126 ++ packages/bukkit/tsconfig.json | 5 +- packages/common/src/reflect.ts | 31 +- packages/common/src/tellraw.ts | 74 +- packages/core/src/index.ts | 18 +- packages/database/src/database.ts | 17 +- packages/database/src/decorators.ts | 7 - packages/database/src/model.ts | 11 - packages/i18n/languages/zh_cn.yml | 2 +- packages/plugin/src/config.ts | 2 +- packages/plugin/src/decorators.ts | 1 + packages/plugin/src/interfaces.ts | 4 + packages/plugin/src/manager.ts | 2 +- packages/polyfill/src/node-shim.ts | 66 +- 26 files changed, 2752 insertions(+), 188 deletions(-) create mode 100644 packages/api/src/item.ts create mode 100644 packages/bukkit/src/internal/item.ts create mode 100644 packages/bukkit/src/item.ts delete mode 100644 packages/database/src/decorators.ts delete mode 100644 packages/database/src/model.ts diff --git a/lerna.json b/lerna.json index c0ae5fbc..64a699d1 100644 --- a/lerna.json +++ b/lerna.json @@ -9,5 +9,8 @@ "run": { "stream": true } + }, + "publishConfig": { + "access": "public" } } diff --git a/package.json b/package.json index a85a008b..2cd53b3e 100644 --- a/package.json +++ b/package.json @@ -14,7 +14,7 @@ "np": "./script/push.sh", "lsp": "npm login --registry=https://registry.npmjs.org --scope=@ccms", "lp": "lerna publish --registry https://registry.npmjs.org", - "lpb": "lerna publish --registry https://registry.npmjs.org --canary --pre-dist-tag beta" + "lpb": "lerna publish --registry https://registry.npmjs.org --canary --preid beta --pre-dist-tag beta" }, "workspaces": [ "packages/*" diff --git a/packages/api/src/console.ts b/packages/api/src/console.ts index b70fa0b1..5b0a929f 100644 --- a/packages/api/src/console.ts +++ b/packages/api/src/console.ts @@ -5,7 +5,7 @@ import * as base64 from 'base64-js' const Arrays = Java.type('java.util.Arrays') const Level = Java.type('java.util.logging.Level') const Paths = Java.type('java.nio.file.Paths') -const ignoreLogPrefix = ['java.', 'javax.', 'sun.', 'net.minecraft.', 'org.bukkit.', 'jdk.nashorn.', 'org.openjdk.nashorn', 'io.netty.', 'org.spongepowered.', 'org.apache', 'org.springframework'] +const ignoreLogPrefix = ['java.', 'javax.', 'sun.', 'net.minecraft.', 'org.bukkit.', 'jdk.internal.dynalink.', 'jdk.nashorn.', 'org.openjdk.nashorn', 'io.netty.', 'org.spongepowered.', 'org.apache', 'org.springframework'] enum LogLevel { ALL, @@ -18,12 +18,114 @@ enum LogLevel { OFF } +const sourceMaps: { [key: string]: SourceMapBuilder } = {} +const sourceFileMaps: { [key: string]: string } = {} + +global.setGlobal('MiaoScriptSourceMaps', sourceMaps) +global.setGlobal('MiaoScriptSourceFileMaps', sourceFileMaps) + +export namespace jsconsole { + export function readSourceMap(fileName: string, lineNumber: any) { + try { + if (fileName.endsWith('js') || fileName.endsWith('ms')) { + if (sourceMaps[fileName] === undefined) { + sourceMaps[fileName] = null + let sourceLine = base.read(fileName).split('\n') + let lastLine = sourceLine[sourceLine.length - 1] || sourceLine[sourceLine.length - 2] + // lastLine is similar //# sourceMappingURL=data:application/json;base64, + if (lastLine.startsWith('//# sourceMappingURL=')) { + let sourceContent = null + let sourceMappingURL = lastLine.split('sourceMappingURL=', 2)[1] + if (sourceMappingURL.startsWith('data:application/json;base64,')) { + sourceContent = String.fromCharCode(...Array.from(base64.toByteArray(sourceMappingURL.split(',', 2)[1]))) + } else if (sourceMappingURL.startsWith('http://') || sourceMappingURL.startsWith('https://')) { + // TODO + } else { + let file = Paths.get(Paths.get(fileName, '..', sourceMappingURL).toFile().getCanonicalPath()).toFile() + if (file.exists()) { + sourceContent = base.read(file) + } else if (global.debug) { + console.debug('readSourceMap can\'t found', fileName, 'source map file', sourceMappingURL) + } + } + if (sourceContent) { + sourceMaps[fileName] = new SourceMapBuilder(JSON.parse(sourceContent)) + sourceFileMaps[fileName] = Paths.get(fileName, '..', sourceMaps[fileName].sources[0]).toFile().getCanonicalPath() + } + } + } + if (sourceMaps[fileName]) { + let sourceMapping = sourceMaps[fileName].getSource(lineNumber, 25, true, true) + if (sourceMapping) { + fileName = Paths.get(fileName, '..', sourceMapping.sourcePath).toFile().getCanonicalPath() + lineNumber = sourceMapping.mapping.sourceLine + } + } + } + } catch (error: any) { + console.debug('search source map', fileName, 'line', lineNumber, 'error:', error) + if (global.debug) { + console.ex(error) + } + } + return { + fileName, + lineNumber + } + } + + export function getStackTrace(ex: Error, color: boolean = true): string[] { + if (!ex) return [] + let stack = ex.getStackTrace() + let cache = [(color ? '§c' : '') + ex] + //@ts-ignore + if (stack.class) { + stack = Arrays.asList(stack) + } + stack.forEach(trace => { + if (!trace.fileName || trace.fileName.startsWith('jar:file:') || trace.fileName.startsWith('file:')) { return } + if (trace.className.startsWith('<')) { + let { fileName, lineNumber } = readSourceMap(trace.fileName, trace.lineNumber) + if (fileName.startsWith(root)) { fileName = fileName.split(root)[1] } + if (color) { + cache.push(` §e->§c ${fileName}:${lineNumber} => §4${trace.methodName}`) + } else { + cache.push(` -> ${fileName}:${lineNumber} => ${trace.methodName}`) + } + } else { + let className = trace.className + var fileName = trace.fileName as string + var lineNumber = trace.lineNumber + if (className.startsWith('jdk.nashorn.internal.scripts') || className.startsWith('org.openjdk.nashorn.internal.scripts')) { + className = className.substr(className.lastIndexOf('$') + 1) + var { fileName, lineNumber } = readSourceMap(fileName, lineNumber) + if (fileName.startsWith(root)) { fileName = fileName.split(root)[1] } + } else { + if (!global.debug) { + for (let prefix in ignoreLogPrefix) { + if (className.startsWith(ignoreLogPrefix[prefix])) { + return + } + } + } else if (className.startsWith('jdk.nashorn.internal.') || className.startsWith('org.openjdk.nashorn.internal.')) { + return + } + } + if (color) { + cache.push(` §e->§c ${className}.${trace.methodName}(§4${fileName}:${lineNumber}§c)`) + } else { + cache.push(` -> ${className}.${trace.methodName}(${fileName}:${lineNumber})`) + } + } + }) + return cache + } +} + export class MiaoScriptConsole implements Console { Console: any memory: any - private static sourceMaps: { [key: string]: SourceMapBuilder } = {} - private static sourceFileMaps: { [key: string]: string } = {} private _name: string = '' private _level: LogLevel = LogLevel.INFO @@ -92,91 +194,8 @@ export class MiaoScriptConsole implements Console { ex(ex: Error) { this.stack(ex).forEach(line => this.console(line)) } - readSourceMap(fileName: string, lineNumber: any) { - try { - if (fileName.endsWith('js')) { - if (MiaoScriptConsole.sourceMaps[fileName] === undefined) { - MiaoScriptConsole.sourceMaps[fileName] = null - let sourceLine = base.read(fileName).split('\n') - let lastLine = sourceLine[sourceLine.length - 1] - // lastLine is similar //# sourceMappingURL=data:application/json;base64, - if (lastLine.startsWith('//# sourceMappingURL=')) { - let sourceContent = null - let sourceMappingURL = lastLine.split('sourceMappingURL=', 2)[1] - if (sourceMappingURL.startsWith('data:application/json;base64,')) { - sourceContent = String.fromCharCode(...Array.from(base64.toByteArray(sourceMappingURL.split(',', 2)[1]))) - } else if (sourceMappingURL.startsWith('http://') || sourceMappingURL.startsWith('https://')) { - // TODO - } else { - let file = Paths.get(Paths.get(fileName, '..', sourceMappingURL).toFile().getCanonicalPath()).toFile() - if (file.exists()) { sourceContent = base.read(file) } - } - if (sourceContent) { - MiaoScriptConsole.sourceMaps[fileName] = new SourceMapBuilder(JSON.parse(sourceContent)) - MiaoScriptConsole.sourceFileMaps[fileName] = Paths.get(fileName, '..', MiaoScriptConsole.sourceMaps[fileName].sources[0]).toFile().getCanonicalPath() - } - } - } - if (MiaoScriptConsole.sourceMaps[fileName]) { - let sourceMapping = MiaoScriptConsole.sourceMaps[fileName].getSource(lineNumber, 25, true, true) - fileName = MiaoScriptConsole.sourceFileMaps[fileName] - if (sourceMapping && lineNumber != sourceMapping.mapping.sourceLine) { lineNumber = sourceMapping.mapping.sourceLine } - } - } - } catch (error: any) { - console.debug('search source map', fileName, 'line', lineNumber, 'error:', error) - if (global.debug) { - console.ex(error) - } - } - return { - fileName, - lineNumber - } - } stack(ex: Error, color: boolean = true): string[] { - if (!ex) return [] - let stack = ex.getStackTrace() - let cache = [(color ? '§c' : '') + ex] - //@ts-ignore - if (stack.class) { - stack = Arrays.asList(stack) - } - stack.forEach(trace => { - if (!trace.fileName || trace.fileName.startsWith('jar:file:') || trace.fileName.startsWith('file:')) { return } - if (trace.className.startsWith('<')) { - let { fileName, lineNumber } = this.readSourceMap(trace.fileName, trace.lineNumber) - if (fileName.startsWith(root)) { fileName = fileName.split(root)[1] } - if (color) { - cache.push(` §e->§c ${fileName}:${lineNumber} => §4${trace.methodName}`) - } else { - cache.push(` -> ${fileName}:${lineNumber} => ${trace.methodName}`) - } - } else { - let className = trace.className - var fileName = trace.fileName as string - var lineNumber = trace.lineNumber - if (className.startsWith('jdk.nashorn.internal.scripts') || className.startsWith('org.openjdk.nashorn.internal.scripts')) { - className = className.substr(className.lastIndexOf('$') + 1) - var { fileName, lineNumber } = this.readSourceMap(fileName, lineNumber) - if (fileName.startsWith(root)) { fileName = fileName.split(root)[1] } - } else { - if (!global.debug) { - for (let prefix in ignoreLogPrefix) { - if (className.startsWith(ignoreLogPrefix[prefix])) { - return - } - } - } - } - if (color) { - cache.push(` §e->§c ${className}.${trace.methodName}(§4${fileName}:${lineNumber}§c)`) - } else { - cache.push(` -> ${className}.${trace.methodName}(${fileName}:${lineNumber})`) - } - } - }) - return cache + return jsconsole.getStackTrace(ex, color) } assert(value: any, message?: string, ...optionalParams: any[]): void { throw new Error("Method not implemented.") diff --git a/packages/api/src/constants.ts b/packages/api/src/constants.ts index 172eaac0..d906c13a 100644 --- a/packages/api/src/constants.ts +++ b/packages/api/src/constants.ts @@ -11,10 +11,21 @@ export namespace constants { } export namespace Reflect { export const Method = { - getServerConnection: [/*spigot 1.8.8*/'aq',/*spigot 1.12.2*/ 'an', /*spigot 1.14.4+*/'getServerConnection', /*catserver 1.12.2*/'func_147137_ag'] + getServerConnection: [ + /*spigot 1.8.8*/'aq', + /*spigot 1.12.2*/ 'an', + /*spigot 1.14.4+*/'getServerConnection', + /*spigot 1.19+*/'ad', + /*catserver 1.12.2*/'func_147137_ag' + ] } export const Field = { - listeningChannels: [/*spigot 1.8.8-1.12.2*/'g', /*spigot 1.14.4*/'f', /*spigot 1.15.2+*/'listeningChannels', /*catserver 1.12.2*/'field_151274_e'] + listeningChannels: [ + /*spigot 1.8.8-1.12.2*/'g', + /*spigot 1.14.4*/'f', + /*spigot 1.15.2+*/'listeningChannels', + /*catserver 1.12.2*/'field_151274_e' + ] } } export enum ServerType { diff --git a/packages/api/src/database.ts b/packages/api/src/database.ts index d6008d15..86b850e6 100644 --- a/packages/api/src/database.ts +++ b/packages/api/src/database.ts @@ -6,6 +6,10 @@ export namespace database { * 数据库配置 */ export interface DataBaseConfig { + /** + * 数据库类型 + */ + type: 'h2' | 'mysql' | 'mongodb' | 'sqlite' | 'postgres' | 'redis' /** * 数据库连接串 */ diff --git a/packages/api/src/index.ts b/packages/api/src/index.ts index d5e087ff..bab572e6 100644 --- a/packages/api/src/index.ts +++ b/packages/api/src/index.ts @@ -4,11 +4,13 @@ export * from './web' export * from './amqp' export * from './chat' export * from './task' +export * from './item' export * from './event' export * from './proxy' export * from './plugin' export * from './server' export * from './console' +export { jsconsole as console } from './console' export * from './channel' export * from './command' export * from './database' diff --git a/packages/api/src/item.ts b/packages/api/src/item.ts new file mode 100644 index 00000000..05dffe78 --- /dev/null +++ b/packages/api/src/item.ts @@ -0,0 +1,20 @@ +import { injectable } from '@ccms/container' + +export namespace item { + @injectable() + export abstract class Item { + abstract builder(): ItemBuilder + abstract toJson(item: any): string + abstract fromJSON(json: string): any + } + export interface ItemBuilder { + from(item: any): ItemBuilder + create(type: string | number): ItemBuilder + name(name: string): ItemBuilder + lore(...lores: string[]): ItemBuilder + amount(amount: number): ItemBuilder + durability(durability: number): ItemBuilder + clone(): any + build(): any + } +} diff --git a/packages/api/src/plugin.ts b/packages/api/src/plugin.ts index eea1d95c..48a4984e 100644 --- a/packages/api/src/plugin.ts +++ b/packages/api/src/plugin.ts @@ -149,7 +149,15 @@ export namespace plugin { /** * 插件名称 不填默认为类名 */ - name?: string + name: string + /** + * 插件中文名称 + */ + cname?: string + /** + * 插件等级 付费插件自动注入 + */ + level?: number /** * 前缀 */ diff --git a/packages/bukkit/src/event.ts b/packages/bukkit/src/event.ts index 40e1daae..03be2e66 100644 --- a/packages/bukkit/src/event.ts +++ b/packages/bukkit/src/event.ts @@ -2,6 +2,7 @@ import { event, plugin } from '@ccms/api' import { inject, provideSingleton } from '@ccms/container' import * as reflect from '@ccms/common/dist/reflect' +const URL = Java.type('java.net.URL') const Bukkit = Java.type("org.bukkit.Bukkit") const Event = Java.type("org.bukkit.event.Event") const Modifier = Java.type("java.lang.reflect.Modifier") @@ -19,7 +20,15 @@ export class BukkitEvent extends event.Event { } getJarFile(resource: string) { - return super.getJarFile('org/bukkit/Bukkit.class', Bukkit.class.classLoader) + try { + return super.getJarFile('org/bukkit/Bukkit.class', Bukkit.class.classLoader) + } catch (error) { + // 兼容 LoliServer 的 Bukkit 包无法获取的问题 + let ModList = Java.type('net.minecraftforge.fml.ModList').get() + let forgeFile = ModList.getModFileById("forge").getFile().getFilePath() + let jarPath = `jar:file:${forgeFile}!/org/bukkit/Bukkit.class` + return new URL(jarPath).openConnection().jarFile + } } isValidEvent(clazz: any): boolean { // 继承于 org.bukkit.event.Event diff --git a/packages/bukkit/src/index.ts b/packages/bukkit/src/index.ts index 1cddaca8..14818ad0 100644 --- a/packages/bukkit/src/index.ts +++ b/packages/bukkit/src/index.ts @@ -4,6 +4,7 @@ import { server } from '@ccms/api' import { Container } from '@ccms/container' import { BukkitConsole } from './console' +import './item' import './chat' import './task' import './event' diff --git a/packages/bukkit/src/internal/chat.ts b/packages/bukkit/src/internal/chat.ts index 23cde23f..05cbd7fd 100644 --- a/packages/bukkit/src/internal/chat.ts +++ b/packages/bukkit/src/internal/chat.ts @@ -6,12 +6,14 @@ let bukkitChatInvoke: BukkitChatInvoke abstract class BukkitChatInvoke { private downgrade: boolean = false protected RemapUtils: any + protected ComponentSerializer:any protected ChatSerializer: any protected nmsChatSerializerMethodName: string protected PacketPlayOutChat: any protected chatMessageTypes: any protected playerConnectionFieldName: string + protected playerFieldName: string protected sendPacketMethodName: string constructor(private nmsVersion) { @@ -20,12 +22,12 @@ abstract class BukkitChatInvoke { init() { try { try { + this.ComponentSerializer = Java.type('net.md_5.bungee.chat.ComponentSerializer') this.RemapUtils = Java.type('catserver.server.remapper.RemapUtils') } catch (ex: any) { } let nmsChatSerializerClass = this.getNmsChatSerializerClass() - let nmsChatSerializerMethod = this.remapMethod(nmsChatSerializerClass, 'a', 'func_150699_a', base.getClass('java.lang.String')) - this.nmsChatSerializerMethodName = nmsChatSerializerMethod.getName() + this.nmsChatSerializerMethodName = this.getNmsChatSerializerMethodName(nmsChatSerializerClass) this.ChatSerializer = Java.type(nmsChatSerializerClass.getName()) let packetTypeClass = this.getPacketPlayOutChatClass() this.PacketPlayOutChat = Java.type(packetTypeClass.getName()) @@ -42,7 +44,7 @@ abstract class BukkitChatInvoke { } let playerConnectionField = this.getPlayerConnectionField() this.playerConnectionFieldName = playerConnectionField.getName() - this.sendPacketMethodName = this.remapMethod(playerConnectionField.getType(), 'sendPacket', 'func_179290_a', this.getPacketClass()).getName() + this.sendPacketMethodName = this.getSendPacketMethodName(playerConnectionField.getType()) } catch (ex: any) { org.bukkit.Bukkit.getConsoleSender().sendMessage(`§6[§cMS§6][§bbukkit§6][§achat§6] §cNMS Inject Error §4${ex} §cDowngrade to Command Mode...`) this.downgrade = true @@ -50,10 +52,12 @@ abstract class BukkitChatInvoke { } abstract getNmsChatSerializerClass() + abstract getNmsChatSerializerMethodName(nmsChatSerializerClass: any) abstract getPacketPlayOutChatClass() abstract getPacketPlayOutChat(sender: any, json: any, type: number) abstract getPlayerConnectionField() abstract getPacketClass() + abstract getSendPacketMethodName(playerConnectionClass: any) nmsCls(name: string) { return base.getClass(['net.minecraft.server', this.nmsVersion, name].join('.')) @@ -83,9 +87,9 @@ abstract class BukkitChatInvoke { } } - json(sender: { name: string }, json: string) { + json(sender: any, json: string) { if (this.downgrade) { - return '/tellraw ' + sender.name + ' ' + json + return sender.spigot().sendMessage(this.ComponentSerializer.parse(json)) } else { this.send(sender, json, 0) return false @@ -100,6 +104,13 @@ abstract class BukkitChatInvoke { } class BukkitChatInvokeBase extends BukkitChatInvoke { + getSendPacketMethodName(playerConnectionClass: any) { + return this.remapMethod(playerConnectionClass, 'sendPacket', 'func_179290_a', this.getPacketClass()).getName() + } + getNmsChatSerializerMethodName(nmsChatSerializerClass: any) { + let nmsChatSerializerMethod = this.remapMethod(nmsChatSerializerClass, 'a', 'func_150699_a', base.getClass('java.lang.String')) + return nmsChatSerializerMethod.getName() + } getPacketPlayOutChat(sender: any, json: any, type: number) { return new this.PacketPlayOutChat(this.ChatSerializer[this.nmsChatSerializerMethodName](json), type) } @@ -149,17 +160,31 @@ class BukkitChatInvoke_1_17_1 extends BukkitChatInvoke_1_16_5 { } } +class BukkitChatInvoke_1_19 extends BukkitChatInvoke_1_17_1 { + getSendPacketMethodName(playerConnectionClass: any) { + return playerConnectionClass.getMethod('a', this.getPacketClass()).getName() + } + getPacketPlayOutChatClass() { + return base.getClass('net.minecraft.network.protocol.game.ClientboundSystemChatPacket') + } + getPacketPlayOutChat(sender: any, json: any, type: number) { + return new this.PacketPlayOutChat(this.ChatSerializer[this.nmsChatSerializerMethodName](json), type == 0 ? 1 : type) + } +} + try { let Bukkit: typeof org.bukkit.Bukkit = Java.type('org.bukkit.Bukkit') // @ts-ignore let nmsVersion = Bukkit.getServer().class.name.split('.')[3] let nmsSubVersion = nmsVersion.split("_")[1] - if (nmsSubVersion >= 8) { - bukkitChatInvoke = new BukkitChatInvoke_1_8(nmsVersion) - } else if (nmsSubVersion >= 16) { - bukkitChatInvoke = new BukkitChatInvoke_1_16_5(nmsVersion) + if (nmsSubVersion >= 19) { + bukkitChatInvoke = new BukkitChatInvoke_1_19(nmsVersion) } else if (nmsSubVersion >= 17) { bukkitChatInvoke = new BukkitChatInvoke_1_17_1(nmsVersion) + } else if (nmsSubVersion >= 16) { + bukkitChatInvoke = new BukkitChatInvoke_1_16_5(nmsVersion) + } else if (nmsSubVersion >= 8) { + bukkitChatInvoke = new BukkitChatInvoke_1_8(nmsVersion) } else { bukkitChatInvoke = new BukkitChatInvoke_1_7_10(nmsVersion) } diff --git a/packages/bukkit/src/internal/item.ts b/packages/bukkit/src/internal/item.ts new file mode 100644 index 00000000..f046075a --- /dev/null +++ b/packages/bukkit/src/internal/item.ts @@ -0,0 +1,2270 @@ +export const itemIds = [ + "AIR", + "STONE", + "GRASS", + "DIRT", + "COBBLESTONE", + "WOOD", + "SAPLING", + "BEDROCK", + "WATER", + "STATIONARY_WATER", + "LAVA", + "STATIONARY_LAVA", + "SAND", + "GRAVEL", + "GOLD_ORE", + "IRON_ORE", + "COAL_ORE", + "LOG", + "LEAVES", + "SPONGE", + "GLASS", + "LAPIS_ORE", + "LAPIS_BLOCK", + "DISPENSER", + "SANDSTONE", + "NOTE_BLOCK", + "BED_BLOCK", + "POWERED_RAIL", + "DETECTOR_RAIL", + "PISTON_STICKY_BASE", + "WEB", + "LONG_GRASS", + "DEAD_BUSH", + "PISTON_BASE", + "PISTON_EXTENSION", + "WOOL", + "PISTON_MOVING_PIECE", + "YELLOW_FLOWER", + "RED_ROSE", + "BROWN_MUSHROOM", + "RED_MUSHROOM", + "GOLD_BLOCK", + "IRON_BLOCK", + "DOUBLE_STEP", + "STEP", + "BRICK", + "TNT", + "BOOKSHELF", + "MOSSY_COBBLESTONE", + "OBSIDIAN", + "TORCH", + "FIRE", + "MOB_SPAWNER", + "WOOD_STAIRS", + "CHEST", + "REDSTONE_WIRE", + "DIAMOND_ORE", + "DIAMOND_BLOCK", + "WORKBENCH", + "CROPS", + "SOIL", + "FURNACE", + "BURNING_FURNACE", + "SIGN_POST", + "WOODEN_DOOR", + "LADDER", + "RAILS", + "COBBLESTONE_STAIRS", + "WALL_SIGN", + "LEVER", + "STONE_PLATE", + "IRON_DOOR_BLOCK", + "WOOD_PLATE", + "REDSTONE_ORE", + "GLOWING_REDSTONE_ORE", + "REDSTONE_TORCH_OFF", + "REDSTONE_TORCH_ON", + "STONE_BUTTON", + "SNOW", + "ICE", + "SNOW_BLOCK", + "CACTUS", + "CLAY", + "SUGAR_CANE_BLOCK", + "JUKEBOX", + "FENCE", + "PUMPKIN", + "NETHERRACK", + "SOUL_SAND", + "GLOWSTONE", + "PORTAL", + "JACK_O_LANTERN", + "CAKE_BLOCK", + "DIODE_BLOCK_OFF", + "DIODE_BLOCK_ON", + "STAINED_GLASS", + "TRAP_DOOR", + "MONSTER_EGGS", + "SMOOTH_BRICK", + "HUGE_MUSHROOM_1", + "HUGE_MUSHROOM_2", + "IRON_FENCE", + "THIN_GLASS", + "MELON_BLOCK", + "PUMPKIN_STEM", + "MELON_STEM", + "VINE", + "FENCE_GATE", + "BRICK_STAIRS", + "SMOOTH_STAIRS", + "MYCEL", + "WATER_LILY", + "NETHER_BRICK", + "NETHER_FENCE", + "NETHER_BRICK_STAIRS", + "NETHER_WARTS", + "ENCHANTMENT_TABLE", + "BREWING_STAND", + "CAULDRON", + "ENDER_PORTAL", + "ENDER_PORTAL_FRAME", + "ENDER_STONE", + "DRAGON_EGG", + "REDSTONE_LAMP_OFF", + "REDSTONE_LAMP_ON", + "WOOD_DOUBLE_STEP", + "WOOD_STEP", + "COCOA", + "SANDSTONE_STAIRS", + "EMERALD_ORE", + "ENDER_CHEST", + "TRIPWIRE_HOOK", + "TRIPWIRE", + "EMERALD_BLOCK", + "SPRUCE_WOOD_STAIRS", + "BIRCH_WOOD_STAIRS", + "JUNGLE_WOOD_STAIRS", + "COMMAND", + "BEACON", + "COBBLE_WALL", + "FLOWER_POT", + "CARROT", + "POTATO", + "WOOD_BUTTON", + "SKULL", + "ANVIL", + "TRAPPED_CHEST", + "GOLD_PLATE", + "IRON_PLATE", + "REDSTONE_COMPARATOR_OFF", + "REDSTONE_COMPARATOR_ON", + "DAYLIGHT_DETECTOR", + "REDSTONE_BLOCK", + "QUARTZ_ORE", + "HOPPER", + "QUARTZ_BLOCK", + "QUARTZ_STAIRS", + "ACTIVATOR_RAIL", + "DROPPER", + "STAINED_CLAY", + "STAINED_GLASS_PANE", + "LEAVES_2", + "LOG_2", + "ACACIA_STAIRS", + "DARK_OAK_STAIRS", + "SLIME_BLOCK", + "BARRIER", + "IRON_TRAPDOOR", + "PRISMARINE", + "SEA_LANTERN", + "HAY_BLOCK", + "CARPET", + "HARD_CLAY", + "COAL_BLOCK", + "PACKED_ICE", + "DOUBLE_PLANT", + "STANDING_BANNER", + "WALL_BANNER", + "DAYLIGHT_DETECTOR_INVERTED", + "RED_SANDSTONE", + "RED_SANDSTONE_STAIRS", + "DOUBLE_STONE_SLAB2", + "STONE_SLAB2", + "SPRUCE_FENCE_GATE", + "BIRCH_FENCE_GATE", + "JUNGLE_FENCE_GATE", + "DARK_OAK_FENCE_GATE", + "ACACIA_FENCE_GATE", + "SPRUCE_FENCE", + "BIRCH_FENCE", + "JUNGLE_FENCE", + "DARK_OAK_FENCE", + "ACACIA_FENCE", + "SPRUCE_DOOR", + "BIRCH_DOOR", + "JUNGLE_DOOR", + "ACACIA_DOOR", + "DARK_OAK_DOOR", + "END_ROD", + "CHORUS_PLANT", + "CHORUS_FLOWER", + "PURPUR_BLOCK", + "PURPUR_PILLAR", + "PURPUR_STAIRS", + "PURPUR_DOUBLE_SLAB", + "PURPUR_SLAB", + "END_BRICKS", + "BEETROOT_BLOCK", + "GRASS_PATH", + "END_GATEWAY", + "COMMAND_REPEATING", + "COMMAND_CHAIN", + "FROSTED_ICE", + "MAGMA", + "NETHER_WART_BLOCK", + "RED_NETHER_BRICK", + "BONE_BLOCK", + "STRUCTURE_VOID", + "OBSERVER", + "WHITE_SHULKER_BOX", + "ORANGE_SHULKER_BOX", + "MAGENTA_SHULKER_BOX", + "LIGHT_BLUE_SHULKER_BOX", + "YELLOW_SHULKER_BOX", + "LIME_SHULKER_BOX", + "PINK_SHULKER_BOX", + "GRAY_SHULKER_BOX", + "SILVER_SHULKER_BOX", + "CYAN_SHULKER_BOX", + "PURPLE_SHULKER_BOX", + "BLUE_SHULKER_BOX", + "BROWN_SHULKER_BOX", + "GREEN_SHULKER_BOX", + "RED_SHULKER_BOX", + "BLACK_SHULKER_BOX", + "WHITE_GLAZED_TERRACOTTA", + "ORANGE_GLAZED_TERRACOTTA", + "MAGENTA_GLAZED_TERRACOTTA", + "LIGHT_BLUE_GLAZED_TERRACOTTA", + "YELLOW_GLAZED_TERRACOTTA", + "LIME_GLAZED_TERRACOTTA", + "PINK_GLAZED_TERRACOTTA", + "GRAY_GLAZED_TERRACOTTA", + "SILVER_GLAZED_TERRACOTTA", + "CYAN_GLAZED_TERRACOTTA", + "PURPLE_GLAZED_TERRACOTTA", + "BLUE_GLAZED_TERRACOTTA", + "BROWN_GLAZED_TERRACOTTA", + "GREEN_GLAZED_TERRACOTTA", + "RED_GLAZED_TERRACOTTA", + "BLACK_GLAZED_TERRACOTTA", + "CONCRETE", + "CONCRETE_POWDER", + null, + null, + "STRUCTURE_BLOCK", + "IRON_SPADE", + "IRON_PICKAXE", + "IRON_AXE", + "FLINT_AND_STEEL", + "APPLE", + "BOW", + "ARROW", + "COAL", + "DIAMOND", + "IRON_INGOT", + "GOLD_INGOT", + "IRON_SWORD", + "WOOD_SWORD", + "WOOD_SPADE", + "WOOD_PICKAXE", + "WOOD_AXE", + "STONE_SWORD", + "STONE_SPADE", + "STONE_PICKAXE", + "STONE_AXE", + "DIAMOND_SWORD", + "DIAMOND_SPADE", + "DIAMOND_PICKAXE", + "DIAMOND_AXE", + "STICK", + "BOWL", + "MUSHROOM_SOUP", + "GOLD_SWORD", + "GOLD_SPADE", + "GOLD_PICKAXE", + "GOLD_AXE", + "STRING", + "FEATHER", + "SULPHUR", + "WOOD_HOE", + "STONE_HOE", + "IRON_HOE", + "DIAMOND_HOE", + "GOLD_HOE", + "SEEDS", + "WHEAT", + "BREAD", + "LEATHER_HELMET", + "LEATHER_CHESTPLATE", + "LEATHER_LEGGINGS", + "LEATHER_BOOTS", + "CHAINMAIL_HELMET", + "CHAINMAIL_CHESTPLATE", + "CHAINMAIL_LEGGINGS", + "CHAINMAIL_BOOTS", + "IRON_HELMET", + "IRON_CHESTPLATE", + "IRON_LEGGINGS", + "IRON_BOOTS", + "DIAMOND_HELMET", + "DIAMOND_CHESTPLATE", + "DIAMOND_LEGGINGS", + "DIAMOND_BOOTS", + "GOLD_HELMET", + "GOLD_CHESTPLATE", + "GOLD_LEGGINGS", + "GOLD_BOOTS", + "FLINT", + "PORK", + "GRILLED_PORK", + "PAINTING", + "GOLDEN_APPLE", + "SIGN", + "WOOD_DOOR", + "BUCKET", + "WATER_BUCKET", + "LAVA_BUCKET", + "MINECART", + "SADDLE", + "IRON_DOOR", + "REDSTONE", + "SNOW_BALL", + "BOAT", + "LEATHER", + "MILK_BUCKET", + "CLAY_BRICK", + "CLAY_BALL", + "SUGAR_CANE", + "PAPER", + "BOOK", + "SLIME_BALL", + "STORAGE_MINECART", + "POWERED_MINECART", + "EGG", + "COMPASS", + "FISHING_ROD", + "WATCH", + "GLOWSTONE_DUST", + "RAW_FISH", + "COOKED_FISH", + "INK_SACK", + "BONE", + "SUGAR", + "CAKE", + "BED", + "DIODE", + "COOKIE", + "MAP", + "SHEARS", + "MELON", + "PUMPKIN_SEEDS", + "MELON_SEEDS", + "RAW_BEEF", + "COOKED_BEEF", + "RAW_CHICKEN", + "COOKED_CHICKEN", + "ROTTEN_FLESH", + "ENDER_PEARL", + "BLAZE_ROD", + "GHAST_TEAR", + "GOLD_NUGGET", + "NETHER_STALK", + "POTION", + "GLASS_BOTTLE", + "SPIDER_EYE", + "FERMENTED_SPIDER_EYE", + "BLAZE_POWDER", + "MAGMA_CREAM", + "BREWING_STAND_ITEM", + "CAULDRON_ITEM", + "EYE_OF_ENDER", + "SPECKLED_MELON", + "MONSTER_EGG", + "EXP_BOTTLE", + "FIREBALL", + "BOOK_AND_QUILL", + "WRITTEN_BOOK", + "EMERALD", + "ITEM_FRAME", + "FLOWER_POT_ITEM", + "CARROT_ITEM", + "POTATO_ITEM", + "BAKED_POTATO", + "POISONOUS_POTATO", + "EMPTY_MAP", + "GOLDEN_CARROT", + "SKULL_ITEM", + "CARROT_STICK", + "NETHER_STAR", + "PUMPKIN_PIE", + "FIREWORK", + "FIREWORK_CHARGE", + "ENCHANTED_BOOK", + "REDSTONE_COMPARATOR", + "NETHER_BRICK_ITEM", + "QUARTZ", + "EXPLOSIVE_MINECART", + "HOPPER_MINECART", + "PRISMARINE_SHARD", + "PRISMARINE_CRYSTALS", + "RABBIT", + "COOKED_RABBIT", + "RABBIT_STEW", + "RABBIT_FOOT", + "RABBIT_HIDE", + "ARMOR_STAND", + "IRON_BARDING", + "GOLD_BARDING", + "DIAMOND_BARDING", + "LEASH", + "NAME_TAG", + "COMMAND_MINECART", + "MUTTON", + "COOKED_MUTTON", + "BANNER", + "END_CRYSTAL", + "SPRUCE_DOOR_ITEM", + "BIRCH_DOOR_ITEM", + "JUNGLE_DOOR_ITEM", + "ACACIA_DOOR_ITEM", + "DARK_OAK_DOOR_ITEM", + "CHORUS_FRUIT", + "CHORUS_FRUIT_POPPED", + "BEETROOT", + "BEETROOT_SEEDS", + "BEETROOT_SOUP", + "DRAGONS_BREATH", + "SPLASH_POTION", + "SPECTRAL_ARROW", + "TIPPED_ARROW", + "LINGERING_POTION", + "SHIELD", + "ELYTRA", + "BOAT_SPRUCE", + "BOAT_BIRCH", + "BOAT_JUNGLE", + "BOAT_ACACIA", + "BOAT_DARK_OAK", + "TOTEM", + "SHULKER_SHELL", + null, + "IRON_NUGGET", + "KNOWLEDGE_BOOK", + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + "GOLD_RECORD", + "GREEN_RECORD", + "RECORD_3", + "RECORD_4", + "RECORD_5", + "RECORD_6", + "RECORD_7", + "RECORD_8", + "RECORD_9", + "RECORD_10", + "RECORD_11", + "RECORD_12" +] diff --git a/packages/bukkit/src/item.ts b/packages/bukkit/src/item.ts new file mode 100644 index 00000000..d2b0c70e --- /dev/null +++ b/packages/bukkit/src/item.ts @@ -0,0 +1,126 @@ +import { item } from "@ccms/api" +import { provideSingleton } from "@ccms/container" + +import { itemIds } from './internal/item' + +const Material: typeof org.bukkit.Material = Java.type('org.bukkit.Material') +const ItemStack: typeof org.bukkit.inventory.ItemStack = Java.type('org.bukkit.inventory.ItemStack') + +@provideSingleton(item.Item) +export class BukkitItem extends item.Item { + private CraftItemStack: any + private NBTTagCompound: any + private nmsSaveNBTMethodName: any + private MojangsonParser: any + private mpParseMethodName: any + private nmsVersion: any + constructor() { + super() + this.reflect() + } + builder(): item.ItemBuilder { + return new BukkitItemBuilder() + } + toJson(item: any): string { + let nbt = new this.NBTTagCompound() + return this.CraftItemStack.asNMSCopy(item)[this.nmsSaveNBTMethodName](nbt).toString() + } + fromJSON(json: string) { + return this.CraftItemStack.asBukkitCopy(this.MojangsonParser[this.mpParseMethodName](json)) + } + private obcCls(name: string) { + return base.getClass(['org.bukkit.craftbukkit', this.nmsVersion, name].join('.')) + } + private nmsCls(name: string) { + return base.getClass(['net.minecraft.server', this.nmsVersion, name].join('.')) + } + private reflect() { + try { + let Bukkit: typeof org.bukkit.Bukkit = Java.type('org.bukkit.Bukkit') + // @ts-ignore + this.nmsVersion = Bukkit.getServer().class.name.split('.')[3] + let CraftItemStackClass = this.obcCls('inventory.CraftItemStack') + this.CraftItemStack = Java.type(CraftItemStackClass.getName()) + // @ts-ignore + let asNMSCopyMethod = CraftItemStackClass.getMethod('asNMSCopy', ItemStack.class) + let nmsItemStackClass = asNMSCopyMethod.getReturnType() + let nmsNBTTagCompoundClass = undefined + for (let method of Java.from(nmsItemStackClass.getMethods())) { + let rt = method.getReturnType() + if (method.getParameterTypes().length == 0 && "NBTTagCompound" == rt.getSimpleName()) { + nmsNBTTagCompoundClass = rt + } + } + this.NBTTagCompound = Java.type(nmsNBTTagCompoundClass.getName()) + for (let method of Java.from(nmsItemStackClass.getMethods())) { + let params = method.getParameterTypes() + let rt = method.getReturnType() + if (params.length == 1 && "NBTTagCompound" == params[0].getSimpleName() && "NBTTagCompound" == rt.getSimpleName()) { + this.nmsSaveNBTMethodName = method.getName() + break + } + } + try { + this.MojangsonParser = this.nmsCls('MojangsonParser') + } catch (error) { + this.MojangsonParser = Java.type('net.minecraft.nbt.MojangsonParser') + } + for (let method of Java.from(this.MojangsonParser.class.getMethods())) { + let params = method.getParameterTypes() + let rt = method.getReturnType() + if (params.length == 1 && "String" == params[0].getSimpleName() && "NBTTagCompound" == rt.getSimpleName()) { + this.mpParseMethodName = method.getName() + } + } + } catch (error: any) { + console.log('Bukkit 物品管理器初始化失败:', error) + if (global.debug) { + console.ex(error) + } + } + } +} + +export class BukkitItemBuilder implements item.ItemBuilder { + private itemStack: org.bukkit.inventory.ItemStack + private itemMeta: org.bukkit.inventory.meta.ItemMeta + + from(itemStack: any): item.ItemBuilder { + this.itemStack = itemStack + this.itemMeta = this.itemStack.getItemMeta() + return this + } + create(type: string | number): item.ItemBuilder { + let material = undefined + if (typeof type == 'number') { + type = itemIds[type] + } + material = Material[type] || Material[Material['LEGACY_PREFIX'] + type] + this.itemStack = new ItemStack(material) + this.itemMeta = this.itemStack.getItemMeta() + return this + } + name(name: string): item.ItemBuilder { + this.itemMeta.setDisplayName(name) + return this + } + lore(...lores: string[]): item.ItemBuilder { + this.itemMeta.setLore(lores) + return this + } + amount(amount: number): item.ItemBuilder { + this.itemStack.setAmount(amount) + return this + } + durability(durability: number): item.ItemBuilder { + this.itemStack.setDurability(durability) + return this + } + clone() { + return this.build().clone() + } + build() { + this.itemStack.setItemMeta(this.itemMeta) + return this.itemStack + } +} diff --git a/packages/bukkit/tsconfig.json b/packages/bukkit/tsconfig.json index 7aae5d2b..960ebcf5 100644 --- a/packages/bukkit/tsconfig.json +++ b/packages/bukkit/tsconfig.json @@ -2,6 +2,7 @@ "extends": "../../tsconfig.json", "compilerOptions": { "baseUrl": "src", - "outDir": "dist" + "outDir": "dist", + "resolveJsonModule": true } -} \ No newline at end of file +} diff --git a/packages/common/src/reflect.ts b/packages/common/src/reflect.ts index ba472a16..52d5905a 100644 --- a/packages/common/src/reflect.ts +++ b/packages/common/src/reflect.ts @@ -129,12 +129,14 @@ function declaredField(clazz: java.lang.Class, name: string | java.lang.Str let field = null // noinspection JSUnresolvedVariable while (target !== JavaObject.class) { + console.debug(`reflect field ${name} from ${target.getName()}`) try { field = target.getDeclaredField(name) if (field !== null) { break } } catch (error: any) { if (target === undefined) { break } target = target.getSuperclass() + console.debug(`切换到超类: ${target.getName()}`) } } if (field === null) { @@ -146,21 +148,32 @@ function declaredField(clazz: java.lang.Class, name: string | java.lang.Str function declaredMethod(clazz: java.lang.Class, nameOrIndex: string | number, ...clazzs: java.lang.Class[]): java.lang.reflect.Method { let key = clazz.getName() + '.' + nameOrIndex + ':' + (clazzs || []).map(c => c.getName()).join(':') if (methodCache.has(key)) { return methodCache.get(key) } + let target = clazz if (typeof nameOrIndex === "number") { methodCache.set(key, declaredMethods(clazz)[nameOrIndex]) } else { - try { - methodCache.set(key, clazz.getMethod(nameOrIndex, clazzs as any)) - } catch (ex: any) { + while (target !== JavaObject.class && !methodCache.has(key)) { try { - methodCache.set(key, clazz.getDeclaredMethod(nameOrIndex, clazzs as any)) - } catch (ex: any) { - for (const m of Java.from(declaredMethods(clazz))) { - if (m.getName() == nameOrIndex) { - methodCache.set(key, m) - break + console.debug(`reflect method ${typeof nameOrIndex == "number" ? 'index' : 'name'} ${nameOrIndex} from ${target.getName()}`) + try { + methodCache.set(key, target.getMethod(nameOrIndex, clazzs as any)) + } catch (ex: any) { + try { + methodCache.set(key, target.getDeclaredMethod(nameOrIndex, clazzs as any)) + } catch (ex: any) { + for (const m of Java.from(declaredMethods(target))) { + if (m.getName() == nameOrIndex) { + methodCache.set(key, m) + break + } + } + throw new Error(`method ${typeof nameOrIndex == "number" ? 'index' : 'name'} ${nameOrIndex} not found.`) } } + } catch (error) { + if (target === undefined) { break } + target = target.getSuperclass() + console.debug(`切换到超类: ${target.getName()}`) } } } diff --git a/packages/common/src/tellraw.ts b/packages/common/src/tellraw.ts index 729b4648..dc5eef1a 100644 --- a/packages/common/src/tellraw.ts +++ b/packages/common/src/tellraw.ts @@ -17,14 +17,14 @@ class ChatMessagePart { } convert() { - return this.internal; + return this.internal } } class Tellraw { static duplicateChar = '§卐' static create() { - return new Tellraw().then(Tellraw.duplicateChar); + return new Tellraw().then(Tellraw.duplicateChar) } private cache: string = ''; @@ -32,77 +32,81 @@ class Tellraw { then(part: ChatMessagePart | string) { if (typeof part === "string") { - var newPart = new ChatMessagePart(); + var newPart = new ChatMessagePart() newPart.text = part - this.then(newPart); - return this; + this.then(newPart) + return this } - var last = this.latest(); + var last = this.latest() if (!last.text) { - last.text = part.text; + last.text = part.text } else { - this.parts.push(part); + this.parts.push(part) } - this.cache = null; + this.cache = null } text(text: string) { - this.latest().text = text; - return this; + this.latest().text = text + return this } - tip(text: string) { - this.latest().hover("show_text", text); - return this; + tip(texts: string) { + return this.hover(texts) } - item(text: string) { - this.latest().hover("show_item", text); - return this; + hover(texts: string) { + this.latest().hover("show_text", texts) + return this + } + + item(item: string) { + this.latest().hover("show_item", item) + return this } command(command: string) { - this.latest().click("run_command", command); - return this; + this.latest().click("run_command", command) + return this } suggest(url: string) { - this.latest().click("suggest_command", url); - return this; + this.latest().click("suggest_command", url) + return this } file(path: string) { - this.latest().click("open_file", path); - return this; + this.latest().click("open_file", path) + return this } link(url: string) { - this.latest().click("open_url", url); - return this; + this.latest().click("open_url", url) + return this } latest() { - return this.parts[this.parts.length - 1]; + return this.parts[this.parts.length - 1] } json() { if (!this.cache) { - var temp = []; + var temp = [] this.parts.forEach(t => { - temp.push(t.convert()); - }); - this.cache = JSON.stringify(temp); - console.trace(this.cache); + temp.push(t.convert()) + }) + this.cache = JSON.stringify(temp) + console.trace(this.cache) } - return this.cache; + return this.cache } string() { - var temp = ''; + var temp = '' this.parts.forEach(t => { temp += t.text - }); - return temp; + }) + return temp } } diff --git a/packages/core/src/index.ts b/packages/core/src/index.ts index 4829d002..1500ef4a 100644 --- a/packages/core/src/index.ts +++ b/packages/core/src/index.ts @@ -1,6 +1,6 @@ let containerStartTime = Date.now() console.i18n("ms.core.ioc.initialize", { scope: global.scope }) -import { plugin, server, task, constants } from '@ccms/api' +import { plugin, server, task, constants, console as jsconsole } from '@ccms/api' import { DefaultContainer as container, provideSingleton, ContainerInstance, buildProviderModule, Autowired } from '@ccms/container' console.i18n("ms.core.ioc.completed", { scope: global.scope, time: (Date.now() - containerStartTime) / 1000 }) import * as yaml from 'js-yaml' @@ -161,19 +161,13 @@ function initialize() { loadCoreScript('initialize') try { let core = createCore() - if (VersionUtils.isGreaterOrEqual(base.version, '0.22.0')) { return core } - return core.enable() + return VersionUtils.isGreaterOrEqual(base.version, '0.22.0') ? core : core.enable() } catch (error: any) { - if (console.console) { - console.i18n("core.initialize.error", { error }) - console.ex(error) - } else { - error.printStackTrace() - } + let core = { enable: () => () => console.i18n('ms.core.engine.disable.abnormal') } + console.i18n("core.initialize.error", { error }) + jsconsole.getStackTrace(error, false).forEach(line => console.log(line)) process.emit('core.initialize.error') - return { - enable: () => console.i18n('ms.core.engine.disable.abnormal') - } + return VersionUtils.isGreaterOrEqual(base.version, '0.22.0') ? core : core.enable() } finally { process.emit('core.after.initialize') } diff --git a/packages/database/src/database.ts b/packages/database/src/database.ts index fbc9abec..8cb77158 100644 --- a/packages/database/src/database.ts +++ b/packages/database/src/database.ts @@ -1,6 +1,7 @@ import { database } from '@ccms/api' -import { JSClass, postConstruct } from '@ccms/container' +import { JSClass } from '@ccms/container' +const Thread = Java.type('java.lang.Thread') const JavaString = Java.type('java.lang.String') const Properties = Java.type('java.util.Properties') @@ -26,9 +27,21 @@ export class DataBase extends database.DataBase { private createDataSource(dbConfig: database.DataBaseConfig) { if (typeof dbConfig.url === "string") { + let originClassLoader = Thread.currentThread().getContextClassLoader() + Thread.currentThread().setContextClassLoader(base.getInstance().class.classLoader) let config = new this.HikariConfig() if (dbConfig.driverClassName) { config.setDriverClassName(dbConfig.driverClassName) + } else { + switch (dbConfig.type) { + case "h2": + config.setDriverClassName("org.h2.Driver") + break + case "sqlite": + config.setDriverClassName("org.sqlite.JDBC") + break + default: + } } if (dbConfig.username) { config.setUsername(dbConfig.username) @@ -44,7 +57,9 @@ export class DataBase extends database.DataBase { } config.setDataSourceProperties(properties) } + console.debug('createDataSource from config ' + JSON.stringify(dbConfig)) this.dataSource = new this.HikariDataSource(config) + Thread.currentThread().setContextClassLoader(originClassLoader) } else { this.dataSource = dbConfig.url } diff --git a/packages/database/src/decorators.ts b/packages/database/src/decorators.ts deleted file mode 100644 index 34d518a9..00000000 --- a/packages/database/src/decorators.ts +++ /dev/null @@ -1,7 +0,0 @@ -import 'reflect-metadata' - -export function id() { - return (target: Object, propertyKey: string | symbol) => void { - - } -} \ No newline at end of file diff --git a/packages/database/src/model.ts b/packages/database/src/model.ts deleted file mode 100644 index e659d13c..00000000 --- a/packages/database/src/model.ts +++ /dev/null @@ -1,11 +0,0 @@ -import { DataBase } from "./database" - -export class Model { - constructor(private database: DataBase) { - - } - queryForList(): Array { - - return [] - } -} \ No newline at end of file diff --git a/packages/i18n/languages/zh_cn.yml b/packages/i18n/languages/zh_cn.yml index 564391fb..7f4dec49 100644 --- a/packages/i18n/languages/zh_cn.yml +++ b/packages/i18n/languages/zh_cn.yml @@ -34,7 +34,7 @@ ms.api.command.tab.completer.slow: "§c注意! §6玩家 §a{player} §6执行 ms.api.command.tab.completer.error: "§6玩家 §a{player} §6执行 §b{plugin} §6插件 §d{command} {args} §6补全时发生异常 §4{ex}" ms.plugin.initialize: "初始化 MiaoScript 插件系统: 实例: {plugin} 加载器: {loader}..." -ms.plugin.event.map: "总计 {count} 个 {type} 事件 映射完成..." +ms.plugin.event.map: "映射 {type} 事件 成功 总计 {count} 个 {type} 事件..." ms.plugin.event.map.error: "映射 {type} 事件 异常 将无法使用事件简称. Error: {error}" ms.plugin.manager.scan: "扫描器 {scanner} 扫描 {folder} 中的插件..." ms.plugin.manager.scan.finish: "扫描器 {scanner} 在 {folder} 中 发现 {size} 个插件 开始编译..." diff --git a/packages/plugin/src/config.ts b/packages/plugin/src/config.ts index e888993c..42bc226c 100644 --- a/packages/plugin/src/config.ts +++ b/packages/plugin/src/config.ts @@ -83,7 +83,7 @@ export class PluginConfigManager { name: metadata.name, format: metadata.format }) - } else { + } else if (metadata.migrate) { configValue = configLoader.load(base.read(metadata.file)) || {} if (defaultValue && this.setDefaultValue(configValue, defaultValue, !!metadata.default)) { base.save(metadata.file, configLoader.dump(configValue)) diff --git a/packages/plugin/src/decorators.ts b/packages/plugin/src/decorators.ts index fc87e444..ac1b8e4e 100644 --- a/packages/plugin/src/decorators.ts +++ b/packages/plugin/src/decorators.ts @@ -76,6 +76,7 @@ export function config(metadata: interfaces.ConfigMetadata = {}) { metadata.variable = key metadata.version = metadata.version ?? 1 metadata.format = metadata.format ?? 'yml' + metadata.migrate = metadata.migrate ?? true metadata.autosave = metadata.autosave ?? false metadata.filename = metadata.filename ?? metadata.name + '.' + metadata.format let previousMetadata = getPluginConfigMetadata(target) diff --git a/packages/plugin/src/interfaces.ts b/packages/plugin/src/interfaces.ts index d7561b3a..1b3e14b3 100644 --- a/packages/plugin/src/interfaces.ts +++ b/packages/plugin/src/interfaces.ts @@ -91,6 +91,10 @@ export namespace interfaces { * 配置文件格式 默认 yml */ format?: string + /** + * 是否合并默认配置 + */ + migrate?: boolean /** * 自动保存 默认为 false */ diff --git a/packages/plugin/src/manager.ts b/packages/plugin/src/manager.ts index 37e22bfd..5acf5619 100644 --- a/packages/plugin/src/manager.ts +++ b/packages/plugin/src/manager.ts @@ -69,7 +69,7 @@ export class PluginManagerImpl implements plugin.PluginManager { try { console.i18n('ms.plugin.event.map', { count: this.eventManager.mapEventName(), type: this.serverType }) } catch (error) { - console.i18n('ms.plugin.event.map.error', { error }) + console.i18n('ms.plugin.event.map.error', { type: this.serverType, error }) } let pluginScanner = this.container.getAll(plugin.PluginScanner) pluginScanner.forEach((scanner) => { diff --git a/packages/polyfill/src/node-shim.ts b/packages/polyfill/src/node-shim.ts index 342c7d05..5fdfb82f 100644 --- a/packages/polyfill/src/node-shim.ts +++ b/packages/polyfill/src/node-shim.ts @@ -2,6 +2,7 @@ import { EventEmitter } from 'events' const System = Java.type('java.lang.System') const Thread = Java.type('java.lang.Thread') +const ManagementFactory = Java.type('java.lang.management.ManagementFactory') const InterruptedException = Java.type('java.lang.InterruptedException') const ThreadGroup = Java.type("java.lang.ThreadGroup") const AtomicInteger = Java.type("java.util.concurrent.atomic.AtomicInteger") @@ -18,16 +19,32 @@ const threadCount = new AtomicInteger(0) const threadGroup = new ThreadGroup("@ccms/micro-task") const microTaskPool = new ThreadPoolExecutor( 100, 200, 60, TimeUnit.SECONDS, - new LinkedBlockingQueue(300), + new LinkedBlockingQueue(1024), new ThreadFactory((run: any) => new Thread(threadGroup, run, "@ccms/micro-task-" + threadCount.incrementAndGet())) ) class Process extends EventEmitter { + readonly version = base.version + readonly versions = [] + readonly config = {} + readonly pid: number = parseInt(ManagementFactory.getRuntimeMXBean().getName().split('@')[0]) + readonly ppid: number + title: string + readonly arch: string = System.getProperty("os.arch") + readonly platform = System.getProperty("os.name") + env = { __noSuchProperty__: (prop) => { return System.getenv(prop) } } - platform = System.getProperty("os.name") + + stdout = System.out + stderr = System.err + stdin = System.in + + execArgv = '' + execPath = '' + constructor() { super() this.on('exit', () => { @@ -49,10 +66,10 @@ class Process extends EventEmitter { } }) } - nextTick(func: Function, ...args: any[]) { + nextTick(callback: Function, ...args: any[]): void { microTaskPool.execute(() => { try { - func(args) + callback(args) } catch (origin: any) { try { super.emit('error', origin) @@ -65,9 +82,44 @@ class Process extends EventEmitter { } exit(code: number) { console.log(`process exit by code ${code}!`) - this.emit('exit', code) + this.emit('exit', this.exitCode = code) + } + exitCode = 0 + openStdin() { + throw new Error('MiaoScript unsupport openStdin.') + } + chdir(directory: string): void { + console.error('MiaoScript unsupport chdir. lock at ' + root) + } + cwd() { + return root + } + getgid(): number { + throw new Error('MiaoScript unsupport getgid.') + } + setgid(id: number | string) { + throw new Error('MiaoScript unsupport setgid.') + } + getuid(): number { + throw new Error('MiaoScript unsupport getuid.') + } + setuid(id: number | string) { + throw new Error('MiaoScript unsupport setuid.') + } + setUncaughtExceptionCaptureCallback(cb: ((err: Error) => void) | null) { + if (cb == null) { + this.removeAllListeners('error') + } else { + this.on('error', cb) + } + } + hasUncaughtExceptionCaptureCallback() { + return this.listenerCount('error') > 0 + } + kill(pid: number, signal?: string | number): true { + throw new Error('MiaoScript unsupport kill.') + return true } - toString() { return "[object process]" } @@ -83,7 +135,7 @@ class EventLoop { this.taskExecuteTimeout = parseInt(process.env.MS_TASK_EXECUTE_TIMEOUT) || 3000 this.fixedThreadPool = new ThreadPoolExecutor( 1, 1, 0, TimeUnit.SECONDS, - new LinkedBlockingQueue(500), + new LinkedBlockingQueue(1024), new ThreadFactory((run: any) => { let thread = new Thread(run, "@ccms/event-loop") thread.setDaemon(true)