plugins
This commit is contained in:
		| @ -54,6 +54,7 @@ class CJbot{ | ||||
| 		this.autoReConnect = 'autoConnect' in args ? args.autoReConnect : true; | ||||
| 		this.autoConnect = 'autoConnect' in args  ? args.autoConnect : true; | ||||
|  | ||||
|  | ||||
| 		// If we want the be always connected, kick off the function to auto | ||||
| 		// reconnect | ||||
| 		if(this.autoReConnect) this.__autoReConnect() | ||||
| @ -74,8 +75,8 @@ class CJbot{ | ||||
| 				// If an error happens before the login event, toss an error back to | ||||
| 				// the caller of the function | ||||
| 				let onError = this.bot.on('error', (m)=>{ | ||||
| 					console.log(this.name, m.toString()) | ||||
| 					reject(m) | ||||
| 					console.log('ERROR CJbot.connect on error:', this.name, m.toString()); | ||||
| 					reject(m); | ||||
| 				}) | ||||
|  | ||||
| 				// If the connection ends before the login event, toss an error back | ||||
| @ -87,9 +88,10 @@ class CJbot{ | ||||
| 				}); | ||||
|  | ||||
| 				// When the bot is ready, return to the caller success | ||||
| 				this.bot.on('login', ()=>{ | ||||
| 					this.__onReady() | ||||
| 					resolve() | ||||
| 				this.bot.on('spawn', async()=>{ | ||||
| 					console.log('CJbot.connect on spawn') | ||||
| 					this.__onReady(); | ||||
| 					resolve(); | ||||
| 				}); | ||||
|  | ||||
| 				// Set a timer to try to connect again in 30 seconds if the bot is | ||||
| @ -123,20 +125,26 @@ class CJbot{ | ||||
|  | ||||
| 		// Add the listeners to the bot. We do this so if the bot loses | ||||
| 		// connection, the mineflayer instance will also be lost.  | ||||
| 		this.isReady = true; | ||||
|  | ||||
| 		this.__error(); | ||||
| 		this.__startListeners(); | ||||
|  | ||||
| 		// Call the internal listeners when the bot is ready | ||||
| 		for(let callback of this.listeners.onReady || []){ | ||||
| 			console.log('calling listener', callback); | ||||
| 			await callback.call(this); | ||||
| 			callback.call(this); | ||||
| 		} | ||||
|  | ||||
| 		this.isReady = true; | ||||
| 		this.__error(); | ||||
| 		console.log('Bot is ready', this.bot.entity.username, this.username); | ||||
| 		 | ||||
| 		// Start chat listeners | ||||
| 		this.__listen(); | ||||
|  | ||||
| 		// this.bot.on('path_update', (...args)=>{ console.log('EVENT path_update', args) }) | ||||
| 		// this.bot.on('goal_updated', (...args)=>{ console.log('EVENT goal_updated', args) }) | ||||
| 		// this.bot.on('path_reset', (...args)=>{ console.log('EVENT path_reset', args) }) | ||||
| 		// this.bot.on('path_stop', (...args)=>{ console.log('EVENT path_stop', args) }) | ||||
|  | ||||
| 	 | ||||
| 	}catch(error){ | ||||
| 		console.error('minecraft.js | __onReady | ', this.name, ' ', error); | ||||
| @ -144,6 +152,7 @@ class CJbot{ | ||||
|  | ||||
| 	__startListeners(){ | ||||
| 		for(let event in this.listeners){ | ||||
| 			console.log('__adding listeners', event) | ||||
| 			for(let callback of this.listeners[event]){ | ||||
| 				this.bot.on(event, callback); | ||||
| 			} | ||||
| @ -163,10 +172,26 @@ class CJbot{ | ||||
| 			else this.bot.on(event, callback); | ||||
| 		} | ||||
|  | ||||
| 		return event === 'onReady' ? true : ()=> this.bot.off(listener, callback); | ||||
| 		return ()=> this.off(event, callback); | ||||
| 	} | ||||
|  | ||||
| 	// todo; add .off wrapper | ||||
| 	// Remove listener for events | ||||
| 	off(event, callback) { | ||||
| 		console.log('off', event, callback) | ||||
| 		if (!this.listeners[event]) return false; | ||||
|  | ||||
| 		const index = this.listeners[event].indexOf(callback); | ||||
| 		if (index === -1) return false; | ||||
|  | ||||
| 		this.listeners[event].splice(index, 1); | ||||
|  | ||||
| 		// If bot is ready, also remove from the Mineflayer bot | ||||
| 		if (this.isReady) { | ||||
| 			this.bot.off(event, callback); | ||||
| 		} | ||||
|  | ||||
| 		return true; | ||||
| 	} | ||||
|  | ||||
| 	// Listen for ending events and call connect again | ||||
| 	__autoReConnect(){ | ||||
| @ -207,6 +232,33 @@ class CJbot{ | ||||
| 		} | ||||
| 	} | ||||
|  | ||||
| 	/* Plugins */ | ||||
|  | ||||
| 	static plungins = {}; | ||||
|  | ||||
| 	static pluginAdd(cls){ | ||||
| 		this.plungins[cls.name] = cls | ||||
| 	} | ||||
|  | ||||
| 	plunginsLoaded = {}; | ||||
|  | ||||
| 	async pluginLoad(pluginName, opts){ | ||||
| 		console.log('CJbot.pluginLoad', pluginName) | ||||
| 		let plugin = new this.constructor.plungins[pluginName]({...opts, bot:this}) | ||||
| 		await plugin.init(); | ||||
| 		this.plunginsLoaded[pluginName] = plugin; | ||||
| 	} | ||||
|  | ||||
| 	async pluginUnload(name){ | ||||
| 		console.log('CJbot.pluginUnload', name) | ||||
| 		if(this.plunginsLoaded[name]){ | ||||
| 			await this.plunginsLoaded[name].unload(); | ||||
| 			delete this.plunginsLoaded[name]; | ||||
| 			console.log('CJbot.pluginUnload', name, 'done'); | ||||
| 			return true; | ||||
| 		} | ||||
| 	} | ||||
|  | ||||
| 	/* Chat and messaging*/ | ||||
|  | ||||
| 	__listen(){ | ||||
| @ -277,10 +329,9 @@ class CJbot{ | ||||
|  | ||||
| 	async say(...messages){ | ||||
| 		for(let message of messages){ | ||||
| 			console.log('next chat time:', this.nextChatTime > Date.now(), Date.now()+1, this.nextChatTime-Date.now()+1); | ||||
| 			// console.log('next chat time:', this.nextChatTime > Date.now(), Date.now()+1, this.nextChatTime-Date.now()+1); | ||||
| 			(async (message)=>{ | ||||
| 				if(this.nextChatTime > Date.now()){ | ||||
| 					console.log('am sleeping'); | ||||
| 					await sleep(this.nextChatTime-Date.now()+1) | ||||
| 				} | ||||
| 				this.bot.chat(message); | ||||
| @ -357,6 +408,8 @@ class CJbot{ | ||||
| 		return this.bot.players; | ||||
| 	} | ||||
|  | ||||
| 	/* Actions */ | ||||
|  | ||||
| 	__blockOrVec(thing){ | ||||
| 		if(thing instanceof Vec3.Vec3) return this.bot.blockAt(thing); | ||||
| 		if(thing.constructor && thing.constructor.name === 'Block') return thing; | ||||
| @ -364,31 +417,83 @@ class CJbot{ | ||||
| 		throw new Error('Not supported block identifier'); | ||||
| 	} | ||||
|  | ||||
| 	async _goTo(block, range=2){ | ||||
| 		block = this.__blockOrVec(block); | ||||
|  | ||||
| 		return await this.bot.pathfinder.goto(new GoalNear(...block.position.toArray(), range)); | ||||
| 	} | ||||
|  | ||||
| 	goTo(options){ | ||||
| 		return new Promise(async(resolve, reject)=>{ | ||||
|  | ||||
| 			let range = options.range || 2; | ||||
|  | ||||
| 			try{ | ||||
| 				await this._goTo(options.where, range) | ||||
| 				return resolve(); | ||||
| 			}catch(error){ | ||||
| 				if(options.reTry) return reject('Action can not move to where') | ||||
| 				await this._goTo(options, true); | ||||
| 	findBlockBySign(text){ | ||||
| 		return this.bot.findBlock({ | ||||
| 			useExtraInfo: true, | ||||
| 			maxDistance: 64, | ||||
| 			matching: (block)=> { | ||||
| 				if(block.name.includes('sign') && block.signText.includes(text)){ | ||||
| 					return true; | ||||
| 				} | ||||
| 			} | ||||
| 		}); | ||||
| 	} | ||||
|  | ||||
| 	findChestBySign(text){ | ||||
| 		return this.bot.findBlock({ | ||||
| 			point: this.findBlockBySign(text).position, | ||||
| 			// maxDistance: 1, | ||||
| 			useExtraInfo: true, | ||||
| 			matching: block => block.name === 'chest' | ||||
| 		}); | ||||
| 	} | ||||
|  | ||||
| 	isWithinRange(target, range=2) { | ||||
| 		const botPos = this.bot.entity.position; | ||||
| 		const distance = botPos.distanceTo(target); | ||||
|  | ||||
| 		return distance <= range+.9; | ||||
| 	} | ||||
|  | ||||
| 	async _goTo(block, range=2){ | ||||
| 		try{ | ||||
| 		}catch(error){ | ||||
| 			if(error.message === 'Not supported block identifier'){ | ||||
| 				console.log('found block error') | ||||
| 				await sleep(1000); | ||||
| 				block = this.__blockOrVec(block); | ||||
| 			} | ||||
| 			console.log('other', error) | ||||
| 		} | ||||
|  | ||||
| 		try{ | ||||
| 		}catch(error){ | ||||
| 			if(error.name === 'GoalChanged') return await this._goTo(block, range); | ||||
| 			console.log('error in _goTo', error.name, '|', error.message); | ||||
| 		} | ||||
| 	} | ||||
|  | ||||
| 	async goTo(options) { | ||||
| 		let range = options.range || 2; | ||||
| 		let block = this.__blockOrVec(options.where); | ||||
| 		 | ||||
| 		while(!this.isWithinRange(this.__blockOrVec(options.where).position, range)){ | ||||
| 			try{ | ||||
| 				if(this.bot.pathfinder.isMoving()){ | ||||
| 					await sleep(500); | ||||
| 					console.log('the bot is moving...') | ||||
| 					continue; | ||||
| 				} | ||||
| 				await this.bot.pathfinder.goto( | ||||
| 					new GoalNear(...block.position.toArray(), range) | ||||
| 				); | ||||
| 			}catch(error){ | ||||
| 				await sleep(500); | ||||
| 				console.log('CJbot.goTo while loop error:', error) | ||||
| 				// await this.bot.pathfinder.setGoal(null); | ||||
| 				await this.bot.pathfinder.stop(); | ||||
| 				await sleep(500); | ||||
| 			} | ||||
| 		} | ||||
|  | ||||
| 		return true; | ||||
| 	} | ||||
|  | ||||
| 	async goToReturn(options){ | ||||
| 		let here = this.bot.entity.position; | ||||
| 		let hereYaw = this.bot.entity.yaw | ||||
| 		await this.goTo(options); | ||||
| 		 | ||||
| 		return async () =>{ | ||||
| 			await this.goTo({where: here, range: 0}, true); | ||||
| 			await sleep(500); | ||||
| @ -396,7 +501,6 @@ class CJbot{ | ||||
| 		} | ||||
| 	} | ||||
|  | ||||
|  | ||||
| 	async __nextContainerSlot(window, item) { | ||||
| 		let firstEmptySlot = false; | ||||
|  | ||||
| @ -418,27 +522,124 @@ class CJbot{ | ||||
| 	async openContainer(block){ | ||||
| 		let count = 0; | ||||
| 		block = this.__blockOrVec(block); | ||||
| 		let window; | ||||
|  | ||||
| 		while(!this.bot.currentWindow){ | ||||
| 			let window = this.bot.openContainer(block); | ||||
| 			await sleep(1500); | ||||
| 			try{ | ||||
| 				window = await this.bot.openContainer(block); | ||||
| 			}catch(error){ | ||||
| 				if(!error.message.includes('Event windowOpen did not fire within timeout')) throw error; | ||||
| 			} | ||||
| 			if(this.bot.currentWindow?.title){ | ||||
| 				break; | ||||
| 			} | ||||
|             this.bot.removeAllListeners('windowOpen'); | ||||
| 			this.bot.removeAllListeners('windowOpen'); | ||||
| 			await sleep(1500); | ||||
|  | ||||
| 			if(count++ == 3) throw 'Block wont open'; | ||||
| 		} | ||||
|  | ||||
| 		return this.bot.currentWindow; | ||||
| 	} | ||||
|  | ||||
| 	async openCraftingTable(block){ | ||||
| 		let count = 0; | ||||
| 		block = this.__blockOrVec(block); | ||||
| 		this.bot.activateBlock(block); | ||||
| 		let window = await this.once('windowOpen'); | ||||
|  | ||||
| 		// while(!this.bot.currentWindow){ | ||||
| 		// 	try{ | ||||
| 		// 		if(this.bot.currentWindow?.title){ | ||||
| 		// 			break; | ||||
| 		// 		} | ||||
| 		// 		this.bot.removeAllListeners('windowOpen'); | ||||
|  | ||||
| 		// 		if(count++ == 3) throw 'Block wont open'; | ||||
|  | ||||
| 		// 	}catch(error){ | ||||
| 		// 		console.error('ERROR in CJbot.openCraftingTable:', error) | ||||
| 		// 	} | ||||
| 		// } | ||||
|  | ||||
| 		return window; | ||||
| 	} | ||||
|  | ||||
|  | ||||
| 	async checkItemsFromContainer(containerBlock, itemName, count){ | ||||
| 		let currentSlot = 0; | ||||
| 		let foundCount = 0; | ||||
| 		let window = await this.openContainer(containerBlock); | ||||
|  | ||||
| 		for(let slot of window.slots){ | ||||
| 			if(currentSlot++ === window.inventoryStart) break;currentSlot | ||||
| 			if(!slot) continue; | ||||
| 			if(slot.name === itemName) foundCount += slot.count; | ||||
| 		} | ||||
|  | ||||
| 		await this.bot.closeWindow(window); | ||||
| 		if(foundCount >= count) return true; | ||||
| 	} | ||||
|  | ||||
| 	async getItemsFromChest(containerBlock, itemName, count){ | ||||
| 		let window = await this.openContainer(containerBlock); | ||||
| 		await sleep(500); | ||||
| 		// console.log('item id', this.mcData.itemsByName[itemName], this.mcData) | ||||
| 		await window.withdraw(this.mcData.itemsByName[itemName].id, null, count); | ||||
| 		await this.bot.closeWindow(window); | ||||
| 	} | ||||
|  | ||||
| 	async getFullShulkersFromChest(chestBlock, item) { | ||||
| 		const fullShulkers = []; | ||||
|  | ||||
| 		let window = await this.openContainer(chestBlock); | ||||
|  | ||||
|  | ||||
| 		let currentSlot = 0; | ||||
| 		for(let slot of window.slots){ | ||||
| 			if(currentSlot++ === window.inventoryStart) break; | ||||
| 			if(!slot) continue; | ||||
|  | ||||
| 			// if(!slot || slot.name !== 'shulker_box') continue; | ||||
| 			console.log('slot:', slot) | ||||
| 			if(slot.nbt){ | ||||
| 				// console.log(slot.nbt) | ||||
| 				console.log('BlockEntityTag:', slot.nbt.value.BlockEntityTag.value.Items.value.value) | ||||
| 			} | ||||
| 		} | ||||
|  | ||||
|  | ||||
| /*	  // Get the inventory of the chest block | ||||
| 	  const chestInventory = chestBlock.getInventory(); | ||||
|  | ||||
| 	  // Iterate through the chest's inventory | ||||
| 	  chestInventory.forEach((slot, index) => { | ||||
| 	    // Check if the slot contains a shulker box | ||||
| 	    if (slot && slot.type === 'shulker_box') { | ||||
| 	      // Retrieve the shulker's inventory | ||||
| 	      const shulkerInventory = slot.getInventory(); | ||||
|  | ||||
| 	      // Check if the shulker is full of the specified item | ||||
| 	      const isFull = shulkerInventory.every(shulkerSlot => { | ||||
| 	      	console.log('shulkerSlot', shulkerSlot) | ||||
| 	        return shulkerSlot && shulkerSlot.id === item.id && shulkerSlot.count === 64; // Assuming max stack size is 64 | ||||
| 	      }); | ||||
|  | ||||
| 	      // If full, add the shulker box to the list | ||||
| 	      if (isFull) { | ||||
| 	        fullShulkers.push(slot); | ||||
| 	      } | ||||
| 	    } | ||||
| 	  }); | ||||
|  | ||||
| 	  return fullShulkers;*/ | ||||
| 	} | ||||
|  | ||||
| 	async dumpToChest(block, blockName, amount) { | ||||
|  | ||||
| 		let window = await this.openContainer(block); | ||||
|  | ||||
| 		let items =  window.slots.slice(window.inventoryStart).filter(function(item){ | ||||
| 		let items = window.slots.slice(window.inventoryStart).filter(function(item){ | ||||
| 			if(!item) return false; | ||||
| 			if(blockName && blockName !== item.name) return false; | ||||
| 			return true; | ||||
|  | ||||
		Reference in New Issue
	
	Block a user