Skip to content

Commit

Permalink
improve many types, fix build!
Browse files Browse the repository at this point in the history
pickup up
  • Loading branch information
zardoy committed Jan 17, 2024
1 parent f320ef2 commit be9ce39
Show file tree
Hide file tree
Showing 11 changed files with 205 additions and 155 deletions.
9 changes: 7 additions & 2 deletions src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -65,7 +65,6 @@ class MCServer extends EventEmitter {
}
server.commands = new Command({})
server._server = createServer(options)
server.mcData = mcData

const promises: Promise<any>[] = []
for (const plugin of modules.builtinPlugins) {
Expand All @@ -92,10 +91,16 @@ class MCServer extends EventEmitter {

declare global {
interface Server {
mcData: IndexedData
commands: Command
pluginsReady: boolean
_server: ProtocolServer
supportFeature: (feature: string) => boolean
}
}

export * as Behavior from './lib/behavior'
export * as Command from './lib/command'
export { default as generations } from './lib/generations'
export * as experience from './lib/experience'
export * as UserError from './lib/user_error'
export { default as portal_detector } from './lib/portal_detector'
38 changes: 35 additions & 3 deletions src/lib/modules/chat.ts
Original file line number Diff line number Diff line change
Expand Up @@ -170,13 +170,45 @@ export const player = function (player: Player, serv: Server) {
declare global {
interface Server {
/** Broadcasts `message` to all the players with the optional `color`. */
"broadcast": (message: any, { whitelist, blacklist, system }?: { whitelist?: any; blacklist?: any[] | undefined; system?: boolean | undefined }) => void
"broadcast": (message: any, opt?: { whitelist?: any; blacklist?: any[]; system?: boolean }) => void
/** @internal */
"color": { black: string; dark_blue: string; dark_green: string; dark_cyan: string; dark_red: string; purple: string; dark_purple: string; gold: string; gray: string; grey: string; dark_gray: string; dark_grey: string; blue: string; green: string; aqua: string; cyan: string; red: string; pink: string; light_purple: string; yellow: string; white: string; random: string; obfuscated: string; bold: string; strikethrough: string; underlined: string; underline: string; italic: string; italics: string; reset: string }
"color": {
black: string;
dark_blue: string;
dark_green: string;
dark_cyan: string;
dark_red: string;
purple: string;
dark_purple: string;
gold: string;
gray: string;
grey: string;
dark_gray: string;
dark_grey: string;
blue: string;
green: string;
aqua: string;
cyan: string;
red: string;
pink: string;
light_purple: string;
yellow: string;
white: string;
random: string;
obfuscated: string;
bold: string;
strikethrough: string;
underlined: string;
underline: string;
italic: string;
italics: string;
reset: string
}
/** @internal */
"parseClassic": (message: any) => any
"parseClassic": (message: string) => any
}
interface Player {
// todo better type
/** sends `message` to the player */
"chat": (message: any) => void
/** @internal */
Expand Down
196 changes: 99 additions & 97 deletions src/lib/modules/chest.ts
Original file line number Diff line number Diff line change
@@ -1,109 +1,111 @@
import MinecraftData from 'minecraft-data'
import { Vec3 } from 'vec3'

export const player = function (player: Player, serv: Server, { version }: Options) {
// Importing necessary libraries
const mcData = MinecraftData(version)
// Getting ALL supported blocks
// Chests
const blockChest = mcData.blocksByName.chest
const blockEnderChest = mcData.blocksByName.ender_chest
// TODO: Large chest (NOT IMPLEMENTED)
// Shulker boxes
let blockShulkerBox = [] as any[]
if (serv.supportFeature('theShulkerBoxes')) {
blockShulkerBox = [mcData.blocksByName.shulker_box.id, mcData.blocksByName.red_shulker_box.id,
mcData.blocksByName.orange_shulker_box.id, mcData.blocksByName.yellow_shulker_box.id, mcData.blocksByName.lime_shulker_box.id,
mcData.blocksByName.green_shulker_box.id, mcData.blocksByName.light_blue_shulker_box.id, mcData.blocksByName.cyan_shulker_box.id,
mcData.blocksByName.blue_shulker_box.id, mcData.blocksByName.purple_shulker_box.id, mcData.blocksByName.magenta_shulker_box.id,
mcData.blocksByName.brown_shulker_box.id, mcData.blocksByName.gray_shulker_box.id, mcData.blocksByName.light_gray_shulker_box.id,
mcData.blocksByName.black_shulker_box.id, mcData.blocksByName.white_shulker_box.id, mcData.blocksByName.pink_shulker_box.id]
}
// Showing container GUI
const openContainerGUI = (block, player, guiType, title, sound) => {
// Default GUI shape and content
const guiContent = [
{ present: false }, { present: false }, { present: false }, { present: false }, { present: false }, { present: false }, { present: false }, { present: false }, { present: false },
{ present: false }, { present: false }, { present: false }, { present: false }, { present: false }, { present: false }, { present: false }, { present: false }, { present: false },
{ present: false }, { present: false }, { present: false }, { present: false }, { present: false }, { present: false }, { present: false }, { present: false }, { present: false }]
const invType = 2 // 3 rows, 9 slots
// Filling the GUI content and reshaping the GUI (if necessary)
switch (guiType) {
case 'chest':
// TODO: Gathering of content (NOT IMPLEMENTED)
break
case 'double_chest':
// TODO: Gathering of content (NOT IMPLEMENTED)
break
case 'ender_chest':
// TODO: Gathering of content (NOT IMPLEMENTED)
break
case 'shulker_box':
// TODO: Gathering of content (NOT IMPLEMENTED)
break
// TODO: Add more GUIs
export const server = (serv: Server, { version }: Options) => {
serv.once('asap', () => {
// Importing necessary libraries
const registry = require('prismarine-registry')(version)
// Getting ALL supported blocks
// Chests
const blockChest = registry.blocksByName.chest
const blockEnderChest = registry.blocksByName.ender_chest
// TODO: Large chest (NOT IMPLEMENTED)
// Shulker boxes
let blockShulkerBox: string[] = []
if (registry.supportFeature('theShulkerBoxes')) {
blockShulkerBox = [registry.blocksByName.shulker_box.id, registry.blocksByName.red_shulker_box.id,
registry.blocksByName.orange_shulker_box.id, registry.blocksByName.yellow_shulker_box.id, registry.blocksByName.lime_shulker_box.id,
registry.blocksByName.green_shulker_box.id, registry.blocksByName.light_blue_shulker_box.id, registry.blocksByName.cyan_shulker_box.id,
registry.blocksByName.blue_shulker_box.id, registry.blocksByName.purple_shulker_box.id, registry.blocksByName.magenta_shulker_box.id,
registry.blocksByName.brown_shulker_box.id, registry.blocksByName.gray_shulker_box.id, registry.blocksByName.light_gray_shulker_box.id,
registry.blocksByName.black_shulker_box.id, registry.blocksByName.white_shulker_box.id, registry.blocksByName.pink_shulker_box.id]
}
if (sound) {
// Playing provided sound
serv.playSound(sound, player.world, block.position, {})
}
// Opening container GUI window
player._client.write('open_window', {
windowId: player.windowId,
inventoryType: invType,
windowTitle: JSON.stringify(title)
})
// Sending container content
player._client.write('window_items', {
windowId: player.windowId,
stateId: 1,
items: guiContent,
carriedItem: { present: false }
})
}
// Interaction with a container block
const containerBlockInteractionHandler = async ({ block, player }) => {
if (player.crouching) return
try {
// Getting current block and block above it
const id = await player.world.getBlockType(block.position)
const blockAbove = await player.world.getBlockType(block.position.plus(new Vec3(0, 1, 0)))
// If there is any block directly above container then we can't open it
if (blockAbove) { return }
// Dynamic window ID feature
if (player.windowId === undefined) { player.windowId = 1 } else { player.windowId = player.windowId + 1 }
player.windowPos = block.position
if (id === blockChest.id) {
player.windowType = 'chest'
openContainerGUI(block, player, 'chest', { translate: 'container.chest' }, 'block.chest.open')
return true
// Showing container GUI
const openContainerGUI = (block, player, guiType, title, sound) => {
// Default GUI shape and content
const guiContent = [
{ present: false }, { present: false }, { present: false }, { present: false }, { present: false }, { present: false }, { present: false }, { present: false }, { present: false },
{ present: false }, { present: false }, { present: false }, { present: false }, { present: false }, { present: false }, { present: false }, { present: false }, { present: false },
{ present: false }, { present: false }, { present: false }, { present: false }, { present: false }, { present: false }, { present: false }, { present: false }, { present: false }]
const invType = 2 // 3 rows, 9 slots
// Filling the GUI content and reshaping the GUI (if necessary)
switch (guiType) {
case 'chest':
// TODO: Gathering of content (NOT IMPLEMENTED)
break
case 'double_chest':
// TODO: Gathering of content (NOT IMPLEMENTED)
break
case 'ender_chest':
// TODO: Gathering of content (NOT IMPLEMENTED)
break
case 'shulker_box':
// TODO: Gathering of content (NOT IMPLEMENTED)
break
// TODO: Add more GUIs
}
if (id === blockEnderChest.id) {
player.windowType = 'ender_chest'
openContainerGUI(block, player, 'ender_chest', { translate: 'container.enderchest' }, 'block.chest.open')
return true
if (sound) {
// Playing provided sound
serv.playSound(sound, player.world, block.position, {})
}
// TODO: Large chest (NOT IMPLEMENTED)
if (blockShulkerBox.includes(id)) {
player.windowType = 'shulker_box'
openContainerGUI(block, player, 'shulker_box', { translate: 'container.shulkerBox' }, 'block.chest.open')
return true
// Opening container GUI window
player._client.write('open_window', {
windowId: player.windowId,
inventoryType: invType,
windowTitle: JSON.stringify(title)
})
// Sending container content
player._client.write('window_items', {
windowId: player.windowId,
stateId: 1,
items: guiContent,
carriedItem: { present: false }
})
}
// Interaction with a container block
const containerBlockInteractionHandler = async ({ block, player }) => {
if (player.crouching) return
try {
// Getting current block and block above it
const id = await player.world.getBlockType(block.position)
const blockAbove = await player.world.getBlockType(block.position.plus(new Vec3(0, 1, 0)))
// If there is any block directly above container then we can't open it
if (blockAbove) { return }
// Dynamic window ID feature
if (player.windowId === undefined) { player.windowId = 1 } else { player.windowId = player.windowId + 1 }
player.windowPos = block.position
if (id === blockChest.id) {
player.windowType = 'chest'
openContainerGUI(block, player, 'chest', { translate: 'container.chest' }, 'block.chest.open')
return true
}
if (id === blockEnderChest.id) {
player.windowType = 'ender_chest'
openContainerGUI(block, player, 'ender_chest', { translate: 'container.enderchest' }, 'block.chest.open')
return true
}
// TODO: Large chest (NOT IMPLEMENTED)
if (blockShulkerBox.includes(id)) {
player.windowType = 'shulker_box'
openContainerGUI(block, player, 'shulker_box', { translate: 'container.shulkerBox' }, 'block.chest.open')
return true
}
} catch (err) {
setTimeout(() => { throw err }, 0)
}
} catch (err) {
setTimeout(() => { throw err }, 0)
}
}
// Registering block interaction handlers
// Chests
serv.onBlockInteraction(mcData.blocksByName.chest.name, containerBlockInteractionHandler)
serv.onBlockInteraction(mcData.blocksByName.ender_chest.name, containerBlockInteractionHandler)
// TODO: Large chest (NOT IMPLEMENTED)
// Shulker boxes
if (serv.supportFeature('theShulkerBoxes')) {
for (const currentShulkerBoxID of blockShulkerBox) {
serv.onBlockInteraction(mcData.blocks[currentShulkerBoxID].name, containerBlockInteractionHandler)
// Registering block interaction handlers
// Chests
serv.onBlockInteraction(registry.blocksByName.chest.name, containerBlockInteractionHandler)
serv.onBlockInteraction(registry.blocksByName.ender_chest.name, containerBlockInteractionHandler)
// TODO: Large chest (NOT IMPLEMENTED)
// Shulker boxes
if (registry.supportFeature('theShulkerBoxes')) {
for (const currentShulkerBoxID of blockShulkerBox) {
serv.onBlockInteraction(registry.blocks[currentShulkerBoxID].name, containerBlockInteractionHandler)
}
}
}
})
}
declare global {
}
40 changes: 22 additions & 18 deletions src/lib/modules/communication.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
import { Vec3 } from 'vec3'

export const server = function (serv: Server) {
serv._writeAll = (packetName, packetFields) =>
serv.players.forEach((player) => player._client.write(packetName, packetFields))
Expand Down Expand Up @@ -32,14 +34,13 @@ export const entity = function (entity: Entity, serv: Server) {

entity.getOtherPlayers = () => serv.players.filter((p) => p !== entity)

// warning: might be slow
entity.getOthers = () => Object.fromEntries(Object.entries(serv.entities).filter(([id]) => id !== entity.id))

entity.getNearbyPlayers = (radius = entity.viewDistance) => entity.getNearby()
.filter((e) => e.type === 'player')
.filter((e) => e.type === 'player' && entity.position.distanceTo(entity.position) <= radius) as Player[]

entity.nearbyPlayers = (radius = entity.viewDistance) => entity.nearbyEntities
.filter(e => e.type === 'player')
.filter(e => e.type === 'player' && entity.position.distanceTo(entity.position) <= radius) as Player[]

entity._writeOthers = (packetName, packetFields) =>
serv._writeArray(packetName, packetFields, entity.getOtherPlayers())
Expand All @@ -48,37 +49,40 @@ export const entity = function (entity: Entity, serv: Server) {
serv._writeArray(packetName, packetFields, entity.getNearbyPlayers())

entity._writeNearby = (packetName, packetFields) =>
serv._writeArray(packetName, packetFields, entity.getNearbyPlayers().concat(entity.type === 'player' ? [entity] : []))
serv._writeArray(packetName, packetFields, [...entity.getNearbyPlayers(), ...entity.type === 'player' ? [entity] : []])
}
declare global {
interface Server {
/** @internal */
"_writeAll": (packetName: any, packetFields: any) => any
"_writeAll": (packetName: any, packetFields: any) => void
/** @internal */
"_writeArray": (packetName: any, packetFields: any, players: any) => any
"_writeArray": (packetName: any, packetFields: any, players: any) => void
/** @internal */
"_writeNearby": (packetName: any, packetFields: any, loc: any) => any
"_writeNearby": (packetName: any, packetFields: any, loc: { world: Player['world']; position: Vec3; radius?: number }) => void
/** Returns array of players within loc. loc is a required paramater. The object contains:, * , * * world: World position is in, * * position: Center position, * * radius: Distance from position */
"getNearby": ({ world, position, radius }: { world: any; position: any; radius?: number | undefined }) => any
"getNearby": (params: { world: Player['world']; position: Vec3; radius?: number }) => Player[]
/** @internal */
"getNearbyEntities": ({ world, position, radius }: { world: any; position: any; radius?: number | undefined }) => any[]
"getNearbyEntities": (params: { world: Player['world']; position: Vec3; radius?: number }) => Entity[]
}
interface Entity {
/** Gets all entities nearby (within entity.viewDistance) */
"getNearby": () => any
"getNearby": () => Entity[]
/** Gets every player other than self (all players if entity is not a player) */
"getOtherPlayers": () => any
/** Get every other entity other than self */
"getOthers": () => any
"getOtherPlayers": () => Player[]
/**
* Get every other entity other than self
* Should not be used repeatedly as it is a slow operation
*/
"getOthers": () => Server['entities']
/** Gets all nearby players regardless of what client thinks */
"getNearbyPlayers": (radius?: any) => any
"getNearbyPlayers": (radius?: number) => Player[]
/** Gets all nearby players that client can see */
"nearbyPlayers": (radius?: any) => any
"nearbyPlayers": (radius?: number) => Player[]
/** @internal */
"_writeOthers": (packetName: any, packetFields: any) => any
"_writeOthers": (packetName: any, packetFields: any) => void
/** @internal */
"_writeOthersNearby": (packetName: any, packetFields: any) => any
"_writeOthersNearby": (packetName: any, packetFields: any) => void
/** @internal */
"_writeNearby": (packetName: any, packetFields: any) => any
"_writeNearby": (packetName: any, packetFields: any) => void
}
}
8 changes: 4 additions & 4 deletions src/lib/modules/effects.ts
Original file line number Diff line number Diff line change
Expand Up @@ -110,12 +110,12 @@ declare global {
/** @internal */
"effects": {}
/** @internal */
"sendEffect": (effectId: any, { amplifier, duration, particles, whitelist, blacklist }?: { amplifier?: number | undefined; duration?: number | undefined; particles?: boolean | undefined; whitelist?: any; blacklist?: any[] | undefined }) => void
"sendEffect": (effectId: any, opt?: { amplifier?: number; duration?: number; particles?: boolean; whitelist?: any; blacklist?: any[] }) => void
/** @internal */
"sendRemoveEffect": (effectId: any, { whitelist, blacklist }?: { whitelist?: any; blacklist?: any[] | undefined }) => void
"sendRemoveEffect": (effectId: any, opt?: { whitelist?: any; blacklist?: any[] | undefined }) => void
/** @internal */
"addEffect": (effectId: any, opt?: {}) => boolean
"addEffect": (effectId: any, opt?: { amplifier?: number; duration?: number; particles?: boolean; whitelist?: any; blacklist?: any[] }) => boolean
/** @internal */
removeEffect: (effectId: any, opt?: any) => void
removeEffect: (effectId: any, opt?: { amplifier?: number; duration?: number; particles?: boolean; whitelist?: any; blacklist?: any[] }) => void
}
}
4 changes: 2 additions & 2 deletions src/lib/modules/login.ts
Original file line number Diff line number Diff line change
Expand Up @@ -79,9 +79,9 @@ export const player = async function (player: Player, serv: Server, settings: Op
function updateInventory () {
playerData.inventory.forEach((item) => {
const itemValue: string | number = item.id.value
const itemName = typeof itemValue === 'string' ? skipMcPrefix(itemValue) : mcData.itemsArray.find(item => item.id === itemValue)?.name
const itemName = typeof itemValue === 'string' ? skipMcPrefix(itemValue) : registry.itemsArray.find(item => item.id === itemValue)?.name
// todo how it can be block?
const theItem = mcData.itemsByName[itemName] || mcData.blocksByName[itemName]
const theItem = registry.itemsByName[itemName] || registry.blocksByName[itemName]
// todo test with undefined values (need to preserve!)
if (!theItem) {
console.warn(`Unknown item ${itemName} (id in player ${player.username} inventory ${itemValue})`)
Expand Down
Loading

0 comments on commit be9ce39

Please sign in to comment.