UNPKG

@brayjamin/mantle

Version:

Useful utilities for modern-JS Minecraft development via the Grakkit pipeline, forked from @harrix432/mantle.

631 lines (597 loc) 23 kB
import { type, core, event, server } from '@grakkit/stdlib-paper'; declare function require (name: string) const Data = require('@grakkit/stdlib-paper').data; import { obMaterial, obiInventory, obimItemMeta, obiItemStack, obuVector, obLocation, obePlayer, obuBoundingBox, obbBlock, obeEntity, obeItem, obcCommandSender, obOfflinePlayer, obpPersistentDataHolder, obeLivingEntity, obimBlockStateMeta } from '@grakkit/types-paper'; const ArrayList = type('java.util.ArrayList'); const Block = type('org.bukkit.block.Block'); const BlockStateMeta = type('org.bukkit.inventory.meta.BlockStateMeta'); const BoundingBox = type('org.bukkit.util.BoundingBox'); const CommandSender = type('org.bukkit.command.CommandSender'); const Entity = type('org.bukkit.entity.Entity'); const EntityType = type('org.bukkit.entity.EntityType'); const ItemFlag = type('org.bukkit.inventory.ItemFlag'); const ItemStack = type('org.bukkit.inventory.ItemStack'); const LivingEntity = type('org.bukkit.entity.LivingEntity'); const Location = type('org.bukkit.Location'); const Material = type('org.bukkit.Material'); const NamespacedKey = type('org.bukkit.NamespacedKey'); const OfflinePlayer = type('org.bukkit.OfflinePlayer'); const PersistentDataHolder = type('org.bukkit.persistence.PersistentDataHolder'); const PersistentDataType = type('org.bukkit.persistence.PersistentDataType'); const SpawnReason = type('org.bukkit.event.entity.CreatureSpawnEvent.SpawnReason'); const UUID = type('java.util.UUID'); const Vector = type('org.bukkit.util.Vector'); const NMS: string = (() => { const item = new ItemStack(Material.STONE).ensureServerConversions(); const meta = item.getItemMeta(); meta.setDisplayName('sus'); item.setItemMeta(meta); //@ts-expect-error return item.handle.getTag().getClass().getCanonicalName().split('.').slice(0, -1).join('.'); })(); //@ts-expect-error const NBTTagByte: NBTTag = type(`${NMS}.NBTTagByte`); //@ts-expect-error const NBTTagByteArray: NBTTag = type(`${NMS}.NBTTagByteArray`); //@ts-expect-error const NBTTagCompound: NBTTag = type(`${NMS}.NBTTagCompound`); //@ts-expect-error const NBTTagDouble: NBTTag = type(`${NMS}.NBTTagDouble`); //@ts-expect-error const NBTTagFloat: NBTTag = type(`${NMS}.NBTTagFloat`); //@ts-expect-error const NBTTagInt: NBTTag = type(`${NMS}.NBTTagInt`); //@ts-expect-error const NBTTagIntArray: NBTTag = type(`${NMS}.NBTTagIntArray`); //@ts-expect-error const NBTTagList: NBTTag = type(`${NMS}.NBTTagList`); //@ts-expect-error const NBTTagLong: NBTTag = type(`${NMS}.NBTTagLong`); //@ts-expect-error const NBTTagLongArray: NBTTag = type(`${NMS}.NBTTagLongArray`); //@ts-expect-error const NBTTagShort: NBTTag = type(`${NMS}.NBTTagShort`); //@ts-expect-error const NBTTagString: NBTTag = type(`${NMS}.NBTTagString`); type HasBoundingBox = obuBoundingBox | SerialBoundingBox | IsPhysical; type HasItemStack = obiItemStack | SerialItemStack | IsPhysical; type HasLocation = obLocation | SerialLocation | IsPhysical; type HasVector = obuVector | SerialVector | HasLocation; type InterfaceAction = string | Function | Interface | InterfaceOptions; type InterfaceItem = obiItemStack | ItemOptions | ((...args: any[]) => obiItemStack | ItemOptions); type InterfaceOptions = { rows: 1 | 2 | 3 | 4 | 5 | 6; title: string; items: { [x: number]: { item: InterfaceItem; action?: InterfaceAction } }; }; type IsContainer = obbBlock | IsEntity | obiItemStack | SerialEntity | SerialItemStack; type IsEntity = obeEntity | obeItem | obePlayer | obOfflinePlayer; type IsPhysical = obbBlock | IsEntity | SerialEntity; type ItemOptions = { type: obMaterial; amount?: number; name?: string; lore?: string[]; hide?: boolean; meta?: (meta: obimItemMeta) => any; data?: any; }; type MantleParse = { (object: SerialBoundingBox): obuBoundingBox; (object: SerialEntity): obeEntity; (object: SerialItemStack): obiItemStack; (object: SerialLocation): obLocation; (object: SerialVector): obuVector; <X>(object: X): X; }; type MantleSerialize = { (object: obuBoundingBox): SerialBoundingBox; (object: IsEntity): SerialEntity; (object: obiItemStack): SerialItemStack; (object: obLocation): SerialLocation; (object: obuVector): SerialVector; <X>(object: X): X; }; type NBTTag = { new (...args: any[]): any; a(value: any): any }; type SerialBoundingBox = { class: 'BoundingBox'; min: SerialVector; max: SerialVector; }; type SerialEntity = { class: 'Entity'; location: SerialLocation; nbt: SerialNBTTagCompound; type: string; uuid: string; }; type SerialItemStack = { amount: number; class: 'ItemStack'; nbt: SerialNBTTagCompound; type: string; }; type SerialLocation = { class: 'Location'; pitch: number; world: string; x: number; y: number; yaw: number; z: number; }; type SerialNBTTag = | { class: 'NBTTagByte'; value: number } | { class: 'NBTTagByteArray'; value: number[] } | { class: 'NBTTagCompound'; value: { [x: string]: SerialNBTTag } } | { class: 'NBTTagDouble'; value: number } | { class: 'NBTTagFloat'; value: number } | { class: 'NBTTagInt'; value: number } | { class: 'NBTTagIntArray'; value: number[] } | { class: 'NBTTagList'; value: SerialNBTTag[] } | { class: 'NBTTagLong'; value: number } | { class: 'NBTTagLongArray'; value: number[] } | { class: 'NBTTagShort'; value: number } | { class: 'NBTTagString'; value: string }; type SerialNBTTagCompound = { class: 'NBTTagCompound'; value: { [x: string]: SerialNBTTag }; }; type SerialVector = { class: 'Vector'; x: number; y: number; z: number; }; export function boundingBox (arg1: HasBoundingBox) { const thing = parse(arg1); if (thing instanceof Block) { const bounds = thing.getBoundingBox(); if (bounds.getVolume() === 0) { // don't use default zero-volume bounding box return BoundingBox.of(thing.getLocation(), 0, 0, 0); } else { return bounds; } } else if (thing instanceof BoundingBox) { return thing; } else if (thing instanceof Entity) { return thing.getBoundingBox(); } } export function data (arg1: IsContainer, arg2 = 'default') { const thing = parse(arg1); const store = `mantle/${arg2}`; if (typeof arg2 !== 'string') { throw new TypeError('Argument 2 (if specified) must be of type "string"'); } else if (thing instanceof Block) { return Data(`${store}/block/${thing.getWorld().getUID().toString()}/${thing.getBlockKey().toString(16)}`); } else if (thing instanceof OfflinePlayer) { return Data(`${store}/player/${thing.getUniqueId().toString()}`); } else if (thing instanceof Entity) { return Data(`${store}/entity/${thing.getUniqueId().toString()}`); } else if (thing instanceof ItemStack) { const meta = thing.getItemMeta(); if (meta instanceof PersistentDataHolder) { const key = new NamespacedKey(plugin, `mantle/data/${arg2}`); //@ts-expect-error const type = PersistentDataType.STRING; const container = meta.getPersistentDataContainer(); container.has(key, type) || (container.set(key, type, UUID.randomUUID().toString()), thing.setItemMeta(meta)); // retroactively add key to serialized item stack input arg1 && 'class' in arg1 && arg1.class === 'ItemStack' && Object.assign(arg1, serialize(thing)); return Data(`${store}/itemStack/${container.get(key, type)}`); } } } export function dist (arg1: HasVector, arg2: HasVector, arg3 = false) { const from = vector(arg1); const to = vector(arg2); if (from === void 0) { throw new TypeError('Argument 1 must be of type "HasVector"'); } else if (to === void 0) { throw new TypeError('Argument 2 must be of type "HasVector"'); } else if (typeof arg3 !== 'boolean') { throw new TypeError('Argument 3 (if specified) must be of type "boolean"'); } else { const fromLocation = location(arg1); const toLocation = location(arg2); if (fromLocation && toLocation && fromLocation.getWorld() !== toLocation.getWorld()) { return Infinity; } else { const delta = vector(from).subtract(to); arg3 && delta.setY(0); return Math.sqrt(Math.pow(delta.getX(), 2) + Math.pow(delta.getY(), 2) + Math.pow(delta.getZ(), 2)); } } } export function drop (arg1: HasLocation, arg2: HasItemStack, arg3 = false) { const position = location(arg1); const item = itemStack(arg2); if (position === void 0) { throw new TypeError('Argument 1 must be of type "HasLocation"'); } else if (item === void 0) { throw new TypeError('Argument 2 must be of type "HasItemStack"'); } else if (typeof arg3 !== 'boolean') { throw new TypeError('Argument 3 (if specified) must be of type "boolean"'); } else if (item) { position.getWorld()[arg3 ? 'dropItemNaturally' : 'dropItem'](position, item); } } export function item (options: ItemOptions) { const item = new ItemStack(options.type, options.amount || 1); meta(item, meta => { options.name && meta.setDisplayName(options.name); //@ts-expect-error options.lore && meta.setLore(options.lore); options.hide && //@ts-expect-error meta.addItemFlags([ ItemFlag.HIDE_ATTRIBUTES, ItemFlag.HIDE_DYE, ItemFlag.HIDE_ENCHANTS, ItemFlag.HIDE_POTION_EFFECTS ]); options.meta && options.meta(meta); }); options.data && Object.assign(data(item), options.data); return item; } export function itemStack (arg1: HasItemStack) { const stuff = parse(arg1); if (stuff instanceof Block) { const item = new ItemStack(stuff.getType()); const meta = item.getItemMeta(); meta instanceof BlockStateMeta && (meta.setBlockState(stuff.getState()), item.setItemMeta(meta)); return item; } else if (stuff instanceof Entity) { return stuff instanceof LivingEntity ? stuff.getEquipment().getItemInMainHand() : null; } else if (stuff instanceof ItemStack) { return stuff; } } export function location (arg1) { const stuff = parse(arg1); if (stuff instanceof Block || stuff instanceof Entity) { return stuff.getLocation(); } else if (stuff instanceof Location) { return stuff; } } export function meta<X> (item: obiItemStack, modifier: (meta: obimItemMeta) => X): X { const meta = item.getItemMeta(); if (meta) { const result = modifier(meta); item.setItemMeta(meta); return result; } } export const parse: MantleParse = ( object: SerialBoundingBox | SerialEntity | SerialItemStack | SerialLocation | SerialNBTTag | SerialVector ) => { if (object && typeof object.class === 'string') { switch (object.class) { case 'BoundingBox': return BoundingBox.of(parse(object.min), parse(object.max)); case 'Entity': try { let entity = select().filter(entity => object.uuid === entity.getUniqueId().toString())[0]; if (!entity) { const location = parse(object.location); entity = location.getWorld().spawnEntity(location, EntityType[object.type], SpawnReason.CUSTOM); } //@ts-expect-error entity.getHandle().load(parse(object.nbt)); return entity; } catch (error) { console.error('An error occured while attempting to parse a serialized entity!'); console.error(error.stack || error.message || error); return null; } case 'ItemStack': const item_stack = new ItemStack(Material[object.type], object.amount).ensureServerConversions(); //@ts-expect-error item_stack.getHandle().setTag(parse(object.nbt)); return item_stack; case 'Location': return new Location( //@ts-expect-error server.getWorld(UUID.fromString(object.world)), object.x, object.y, object.z, object.yaw, object.pitch ); case 'NBTTagByte': return NBTTagByte.a(object.value); case 'NBTTagByteArray': const nbt_tag_byte_array = new NBTTagByteArray(new ArrayList()); for (const value of object.value) nbt_tag_byte_array.add(NBTTagByte.a(value)); return nbt_tag_byte_array; case 'NBTTagCompound': const nbt_tag_compound = new NBTTagCompound(); for (const key in object.value) nbt_tag_compound.set(key, parse(object.value[key])); return nbt_tag_compound; case 'NBTTagDouble': return NBTTagDouble.a(object.value); case 'NBTTagFloat': return NBTTagFloat.a(object.value); case 'NBTTagInt': return NBTTagInt.a(object.value); case 'NBTTagIntArray': const nbt_tag_int_array = new NBTTagIntArray(new ArrayList()); for (const value of object.value) nbt_tag_int_array.add(NBTTagInt.a(value)); return nbt_tag_int_array; case 'NBTTagList': const nbt_tag_list = new NBTTagList(); for (const value of object.value) nbt_tag_list.add(parse(value)); return nbt_tag_list; case 'NBTTagLong': return NBTTagLong.a(object.value); case 'NBTTagLongArray': const nbt_tag_long_array = new NBTTagLongArray(new ArrayList()); for (const value of object.value) nbt_tag_long_array.add(NBTTagLong.a(value)); return nbt_tag_long_array; case 'NBTTagShort': return NBTTagShort.a(object.value); case 'NBTTagString': return NBTTagString.a(object.value); case 'Vector': return new Vector(object.x, object.y, object.z); } } return object; }; export function player (arg1: string) { if (typeof arg1 !== 'string') { throw new TypeError('Argument 1 must be of type "string"'); } else { const online = server.getPlayer(arg1); if (online) { return online; } else { for (const offline of server.getOfflinePlayers()) { if (offline.getName().toLowerCase().startsWith(arg1.toLowerCase())) { return offline; } } } } } export function select (arg1 = '@e', arg2: obcCommandSender = server.getConsoleSender()) { const context = parse(arg2); if (typeof arg1 !== 'string') { throw new TypeError('Argument 1 (if specified) must be of type "string"'); } else if (context instanceof CommandSender) { try { return [ ...server.selectEntities(context, arg1) ]; } catch (error) { throw new SyntaxError('The provided selector was invalid and could not be parsed.'); } } else { throw new TypeError('Argument 2 (if specified) must be of type "HasEntity" or "CommandSender"'); } } export const serialize: MantleSerialize = ( object: obuBoundingBox | IsEntity | obiItemStack | obLocation | obuVector ) => { if (object instanceof BoundingBox) { return { class: 'BoundingBox', min: serialize(object.getMin()), max: serialize(object.getMax()) }; } else if (object instanceof Entity) { //@ts-expect-error const nbt: SerialNBTTagCompound = serialize(object.getHandle().save(new NBTTagCompound())); return { class: 'Entity', location: serialize(object.getLocation()), nbt, type: object.getType().name(), uuid: object.getUniqueId().toString() }; } else if (object instanceof ItemStack) { //@ts-expect-error const nbt: SerialNBTTagCompound = serialize(object.ensureServerConversions().getHandle().getTag()); return { amount: object.getAmount(), class: 'ItemStack', nbt, type: object.getType().name() }; } else if (object instanceof Location) { return { class: 'Location', pitch: object.getPitch(), world: object.getWorld().getUID().toString(), x: object.getX(), y: object.getY(), yaw: object.getYaw(), z: object.getZ() }; } else if (object instanceof Vector) { return { class: 'Vector', x: object.getX(), y: object.getY(), z: object.getZ() }; } else { // force VS code to shut the fuck up const nbt: any = object; if (nbt instanceof NBTTagByte) { return { class: 'NBTTagByte', value: nbt.asByte() }; } else if (nbt instanceof NBTTagByteArray) { return { class: 'NBTTagByteArray', value: [ ...nbt ].map(value => value.asByte()) }; } else if (nbt instanceof NBTTagCompound) { return { class: 'NBTTagCompound', value: Object.fromEntries([ ...nbt.getKeys() ].map(key => [ key, serialize(nbt.get(key)) ])) }; } else if (nbt instanceof NBTTagDouble) { return { class: 'NBTTagDouble', value: nbt.asDouble() }; } else if (nbt instanceof NBTTagFloat) { return { class: 'NBTTagFloat', value: nbt.asFloat() }; } else if (nbt instanceof NBTTagInt) { return { class: 'NBTTagInt', value: nbt.asInt() }; } else if (nbt instanceof NBTTagIntArray) { return { class: 'NBTTagIntArray', value: [ ...nbt ].map(value => value.asInt()) }; } else if (nbt instanceof NBTTagList) { return { class: 'NBTTagList', value: [ ...nbt ].map(serialize) }; } else if (nbt instanceof NBTTagLong) { return { class: 'NBTTagLong', value: nbt.asLong() }; } else if (nbt instanceof NBTTagLongArray) { return { class: 'NBTTagLongArray', value: [ ...nbt ].map(value => value.asLong()) }; } else if (nbt instanceof NBTTagShort) { return { class: 'NBTTagShort', value: nbt.asShort() }; } else if (nbt instanceof NBTTagString) { return { class: 'NBTTagString', value: nbt.asString() }; } else { return nbt; } } }; export function vector (arg1: HasVector) { const stuff = parse(arg1); if (stuff instanceof Block || stuff instanceof Entity) { return stuff.getLocation().toVector(); } else if (stuff instanceof Location) { return stuff.toVector(); } else if (stuff instanceof Vector) { return stuff; } } const interfaces: Map<number, Interface> = new Map(); export class Interface { player: obePlayer; options: InterfaceOptions; inventory: obiInventory; parent?: Interface; constructor (player: obePlayer, options: InterfaceOptions, parent?: Interface) { this.player = player; this.options = options; this.parent = parent; } get rows () { return this.options.rows; } set rows (value) { this.options.rows = value; this.generate().refresh(); } get title () { return this.options.title; } set title (value) { this.options.title = value; this.generate().refresh(); } get items () { return this.options.items; } set items (value) { this.options.items = value; this.put(); } put (index?: number) { if (index === void 0) { for (const index in this.items) this.put(+index); } else { const value = this.items[index]; const stack = typeof value.item === 'function' ? value.item() : value.item; this.inventory.setItem(index, stack instanceof ItemStack ? stack : item(stack)); } return this; } generate () { //@ts-expect-error this.inventory && interfaces.delete(this.inventory.hashCode()); //@ts-expect-error this.inventory = server.createInventory(this.player, this.options.rows * 9, this.options.title); //@ts-expect-error interfaces.set(this.inventory.hashCode(), this); return this.put(); } show () { this.inventory || this.generate(); this.player.openInventory(this.inventory); return this; } refresh () { //@ts-expect-error this.inventory.getViewers().contains(this.player) && this.show(); } item (index: number, input?: InterfaceItem) { if (input) { this.options.items[index].item = input; return this.put(index); } else { return this.options.items[index]; } } action (index: number, input?: InterfaceAction) { if (input) { this.options.items[index].action = input; return this; } else { return this.options.items[index].action; } } child (options: InterfaceOptions) { return new Interface(this.player, options, this); } } event('org.bukkit.event.inventory.InventoryClickEvent', event => { const inventory = event.getWhoClicked().getOpenInventory().getTopInventory(); if (inventory) { //@ts-expect-error const code: number = inventory.hashCode(); if (interfaces.has(code)) { let cancel = true; const ui: Interface = interfaces.get(code); const index = event.getSlot(); const value = ui.items[index]; if (value) { handle(value.action); ui.put(index); function handle (action: InterfaceAction) { switch (typeof action) { case 'object': (action instanceof Interface ? action : new Interface(ui.player, action)).show(); break; case 'function': handle(action({ event, ui, item: value.item })); break; case 'string': switch (action) { case 'exit': ui.player.closeInventory(); break; case 'return': ui.parent ? ui.parent.show() : ui.player.closeInventory(); break; case 'take': cancel = false; break; } } } } cancel && event.setCancelled(true); } } });