Code structure changes
See original GitHub issueJust a breakdown from what I understand of the system so far, and ideas on how to maybe improve it. Please give feedback.
Mineflayer code structure
Overview on the current structure of Mineflayer and some ideas for changes
Current Structure
The structure in use in the repo at the moment, to the best of my understanding
Libraries
Biome
Biome class containing color, name, height, rainfall, temperature
Block
Block class containing type, metadata, light, skyLight, add, biome, position
. Also contains name, hardness, displayName, boundingBox, diggable, material, havestTools
extracted from the block enum.
Chest
Chest reference class for interacting with a ChestWindow
Conversions
Utility class containing conversions for rotations and notchian data
Dispenser
Dispenser reference class for interacting with a DispenserWindow
Enchantment Table
Enchantment Table reference class for interacting with an Enchantment Table
Entity
Entity class containing id, type, position, velocity, yaw, pitch, onGround, height, potion effects, equipment, heldItem
Furnace
Furnace reference class for interacting with a FurnaceWindow
Item
Item class containing type, count, metadata, nbt
. Also name, displayName, stackSize (max)
from the appropriate block/item enum
Location
Location class for a position in the world containing floored, blockPoint, chunkCorner, blockIndex, biomeBlockIndex, chunkYIndex
Math
Utility class with clamp, sign, euclidianMod
functions
Painting
Painting class containing id, position, name, direction
Recipe
Recipe class storing recipes and information about crafting them. Properties are type, count, metadata, ingredients, requiresTable
, as well as some functions and calculated properties
Windows
Windows class providing ChestWindow
, CraftingTableWindow
, FurnaceWindow
, DispenerWindow
, EnchantmentTableWindow
and BrewingStandWindow
. It also exposes the parent class Window
that is inherited by the formentioned windows. Window
exposes functions that enable clicking and interacting with items on an inventory window, the subclasses provide functions that are window specific.
Plugins
bed
Bed plugin providing sleep
and wake
functions as well as tracking sleep state
block_actions
Block actions plugin tracking noteblocks, piston movement and chest lid state
blocks
Blocks plugin handles chunk data and block information, it also contains the unexposed Column
class for a chunk column
chat
Handles parsing of incomming messages and emitting a chat
or whisper
event. Also provides chat
and whisper
functions for sending chat
digging
Digging plugin provides dig
and stopDigging
functions to the bot, as well as functions to query if a block is indestructable (canDigBlock
) and how long the block will take to dig (digTime
)
entities
Provides entity tracking including animation information about the entities in the world. This includes tracking players that have joined or left the server. Provides attack
, mount
, dismount
and useOn
functions. Exposes players
and entities
properties
experience
Experience plugin supports experience levels and exposes level, points, progress
properties
game
Game plugin tracks level/dimension information and gameMode. Exposes the game
property which contains levelType, gameMode, hardcore, dimension, difficulty, maxPlayers
.
health
Health plugin tracks death and health packets, exposing health, food, foodSaturation, isAlive
.
inventory
Inventory is an overpopulated plugin that ties together the Windows classes, the TileEntity classes e.g. Chest, Furnace, Dispenser, Enchantment Table
and handles bot inventory, tracks the currently heldItem
and the selected hotbar slot quickBarSlot
. Provides functions to (un)equip armour and transfer items around the inventory and between the inventories of other blocks. Also handles windows opening and closing and relays them back and forth between the TileEntity classes and Windows class
kick
Provides kicked
event and the ability to nicely leave the server via quit
physics
Provides a simplified implementation of AABB physics, handles movement including jumping and sprinting. Is able to query the passability of a block to determine collisions. Is used by navigation plugins for routing a path
rain
Simply tracks if it is raining or not, exposes isRaining
settings
Handles client settings information from the server and allows changing of settings that are sent back to the server. exposes settings
and setSettings
. settings
has the properties chat, colorsEnabled, viewDistance, difficulty, showCape
spawn_point
Exposes spawnPoint
which is set each time the bot spawns
time
Exposes time
which has the properties day
and age
.
Enums
biomes.json
Dictionary of all the biomes keyed by network id, each biome exposes id, color, height, name, rainfall, temperature
blocks.json
Dictionary of all the blocks keyed by id, each block exposes id, displayName, name, hardness, stackSize, diggable, boundingBox, material, havestTools
. havestTools
is a dictionary of ids that work more efficiently at breaking the block
instruments.json
Dictionary of instrument sounds keyed by network id, exposes id, name
items.json
Dictionary of all the items, keyed by item id, exposes id, displayName, stackSize, name
materials.json
Dictionary of block materials, keyed by material name containing a dictionary of havestTools and efficency multiplier. Used for digTime most likely
recipes
Dictionary of craftable items, keyed by the type to be crafted. Value of each key is an array of possible recipes. Each recipe exposes type, count, metadata, ingredients, inShape
. Type, count and metadata is the result. ingredients is an array of required ingredients in the form of id, metadata
. ingredients
is used for a shapeless recipe (Example: planks from logs). inShape
is an array of arrays for the shaped crafting recipe.
Summary
For the most part the bot is very full-featured with a lot of information at it’s disposal. However some structure changes could be made to make the code slightly more managable. My current concerns are Windows, Inventory management, TileEntities, and Entity tracking. As well as splitting core plugins (The ones we provide) with user plugins. A seperate folder is a simple change, but I’m mentioning it anyway.
Windows/Inventories/TileEntities
Currently the structure of these objects is in no way modular, it is spanned across individual objects in lib/, as well as the inventory
plugin and the windows
object.
I believe the project would benefit from splitting the handling of each individual window in to a seperate file, and restructuring so that the inventory plugin would not decorate the objects in lib (Looking at you deposit
and withdraw
). But instead a class dedicated to the interaction of a block would be self-contained. inventory
would not override such functions and it would not go to windows
for the appropriate window.
I think a similar structure to how plugins are done (requireindex
) might be a cleaner alternative to the current windows
object.
Also the player inventory is a special case and is treated as such by the server, it should be the primary function of the inventory
plugin, as well as simply updating the slots in the currently displayed inventory. But it should not be used for opening and closing other windows. That should be handled imo elsewhere.
Entities
The way entities and players are tracked could be slightly improved, I think having some sort of proxy object that uses getters for information would help prevent references being held by the end-user. Currently if a plugin holds a reference to an entity or a player, and that entity/player is removed by the server, the reference still exists. This is something we can not help, but we can provide better means of dealing with this.
Emitting an event on the object is one idea, using an isValid flag and asserting that in all get/set operations (Keeping properties private from the object) is another idea. Forcing the user to use isValid or risk an error.
This would give a very early warning to the user that they are incorrectly holding on to a reference of an entity that is no longer in existance.
NOTE: player names and ids are going away in favour of a new UUID system. This will be very important because starting from MC 1.8, users will be able to change their names to any freely available name. UUIDs will be the only way to determine who a user is when online
All the helpers
I know that core code is something that is really needed to get anything to happen at all, but I don’t see much point in exposing any simple functions like click a slot or pick up an item. I’m aware that high level functions like transfer/deposit/withdraw/toss exist. I’m just putting this here as a point that we should focus strongly on helpers to keep the bot high level, and expose less low level functions.
If a third-party plugin needs access to a low level feature, we are not doing something correctly as a high level API.
More abstraction
I noticed that some high level functions still individually handle and send packets directly to the client. I think this should be avoided because it creates redundant code and increases the points of failure and bugs
Promises?
I, personally, am very fond of Promises (eg. Q). I would like to move a lot of the callback structure over to Promises. I feel that more structured and higher level code can be written by using an API like this. e.g.
bot.findBlock('wheat').then(function(location) {
return bot.harvestBlockAt(location);
}).then(function(success) {
if(success) bot.chat('I got some wheat');
else bot.chat('I couldn't havest the wheat');
}).catch(function(error) {
bot.chat('I hard failed for some reason, check console.');
console.log(error.stack);
});
This is just my opinion and I would enjoy feedback on this entire document.
Issue Analytics
- State:
- Created 9 years ago
- Comments:10 (10 by maintainers)
Top GitHub Comments
The promise idea (or something similar) would avoid doing something like https://github.com/andrewrk/mineflayer/blob/ce4b2606b8ff5eafd17b40c9478f73cd2d9935be/test/superflat.js#L105
I think we dealt with everything in that issue except for one thing that still isn’t done : combining tasks the right way™. I opened a new more specific issue about that point here #275.
So I think we can close that issue that contain too many things.