Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

1.21.3 cant usage #3562

Open
1 task
y3621555 opened this issue Jan 16, 2025 · 1 comment
Open
1 task

1.21.3 cant usage #3562

y3621555 opened this issue Jan 16, 2025 · 1 comment
Labels
possible bug Stage1 just created by someone new to the project, we don't know yet if it deserves an implementation / a f

Comments

@y3621555
Copy link

  • The FAQ doesn't contain a resolution to my issue

Versions

  • mineflayer: 1.21.3
  • server: vanilla/spigot/paper

Your current code

const mineflayer = require("mineflayer");
const readline = require("readline");
const fs = require("fs");
const moment = require('moment-timezone');
const socks = require('socks').SocksClient;
const toml = require('@iarna/toml');
const container = require('./lib/containerOperation');
const { sleep } = require("mineflayer/lib/promise_utils");
const Vec3 = require('vec3').Vec3;

// 讀取配置文件
const config = toml.parse(fs.readFileSync(`${process.cwd()}/config.toml`, 'utf8'));
//const craftConfig = JSON.parse(fs.readFileSync(`${process.cwd()}/craft_config.json`, 'utf8'));

let craftConfig;
let bot;

// 添加全局狀態追踪
let currentTask = {
    type: null,  // 'crafting' 或 null
    itemName: null,
    isIntermediateProduct: false
};

// 定義載入設定的函數
function loadConfig() {
    try {
        craftConfig = JSON.parse(fs.readFileSync(`${process.cwd()}/craft_config.json`, 'utf8'));
        console.log('[INFO] 已重新載入設定檔');
    } catch (e) {
        console.log('[ERROR] 載入設定檔失敗:', e);
        throw e;
    }
}

loadConfig();

// 物品處理類
class ItemHandler {
    constructor(bot) {
        this.bot = bot;
    }

    async withdrawItem(itemList, box, takeAmount = 576) {
        for (let itemName of itemList) {
            const boxPos = box[itemName];
            const currentCount = this.bot.inventory.count(this.bot.registry.itemsByName[itemName].id);
            if (currentCount === takeAmount) continue;

            const shulkerBox = await this.bot.blockAt(new Vec3(boxPos[0], boxPos[1], boxPos[2]));
            const window = await container.openBlock(this.bot, shulkerBox, "shulker_box");

            //await new Promise(resolve => setTimeout(resolve, 1000));

            // 檢查箱子內容並取出異物
            for (const slot of window.slots) {
                if (!slot || slot.slot >= 27) continue;
                if (slot.name !== itemName) {
                    console.log(`發現異物${slot.name} x${slot.count}`);
                    await container.withdraw(this.bot, window, slot.name, slot.count, true);
                    continue;
                }
            }

            // 提取所需物品
            const amountNeeded = takeAmount - currentCount;
            if (amountNeeded > 0) {
                await container.withdraw(this.bot, window, itemName, amountNeeded, false);
            }
            window.close();
        }
    }

    async depositItem(itemList, box) {
        for (let itemName of itemList) {
            const boxPos = box[itemName];
            const itemCount = this.bot.inventory.count(this.bot.registry.itemsByName[itemName].id);
            if (itemCount === 0) continue;

            const shulkerBox = await this.bot.blockAt(new Vec3(boxPos[0], boxPos[1], boxPos[2]));
            const window = await container.openBlock(this.bot, shulkerBox, "shulker_box");

            // 檢查是否有異物
            if (window.slots) {
                for (const slot of window.slots) {
                    if (!slot || slot.slot >= 27) continue;

                    if (slot.name !== itemName) {
                        console.log(`發現異物${slot.name} x${slot.count}`);
                        await container.withdraw(this.bot, window, slot.name, slot.count, true);
                        continue;
                    }
                }
            }

            // 存放物品
            await container.deposit(this.bot, window, itemName, -1, false);
            window.close();
        }
    }
}

// 自動合成系統
class AutoCraftingSystem {
    constructor(bot) {
        this.bot = bot;
        this.craftingHandler = new CraftingPacketHandler(bot);
        this.itemHandler = new ItemHandler(bot);
        this.isCrafting = false;  // 添加狀態標記
    }

    async startAutoCrafting(itemName,isIntermediateProduct = false) {
        this.isCrafting = true;  // 開始合成時設置標記

        // 更新當前任務狀態
        currentTask.type = 'crafting';
        currentTask.itemName = itemName;
        currentTask.isIntermediateProduct = isIntermediateProduct;


        const itemConfig = craftConfig.crafting[itemName];
        if (!itemConfig) {
            // 清除任務狀態
            currentTask.type = null;
            currentTask.itemName = null;
            currentTask.isIntermediateProduct = false;
            throw new Error(`未找到 ${itemName} 的配置`);
        }
    
    
        console.log(`[INFO] 開始自動合成 ${itemName}`);
        
        while (this.isCrafting) {
            try {
                // 檢查成品數量
                const finishedItemCount = this.bot.inventory.count(this.bot.registry.itemsByName[itemName].id);
                console.log(`[INFO] 當前成品數量: ${finishedItemCount}`);

                // 如果是中間產物且已經有足夠數量,就退出循環
                if (isIntermediateProduct && finishedItemCount >= itemConfig.recipe.stackSize) {
                    currentTask.type = null;
                    currentTask.itemName = null;
                    currentTask.isIntermediateProduct = false;
                    console.log(`[INFO] 中間產物 ${itemName} 已經足夠,返回上層合成`);
                    return;
                }
                
                // 存放成品
                if (finishedItemCount > 0&& !isIntermediateProduct) {
                    console.log(`[INFO] 存放成品到指定箱子 (${finishedItemCount}個)`);
                    const outputBox = { [itemName]: itemConfig.output_box };
                    await this.itemHandler.depositItem([itemName], outputBox);
                }
    
                // 檢查材料數量
                const materials = this.craftingHandler.checkMaterials(itemConfig.recipe);
                console.log(`[INFO] 當前材料狀況:`, materials);
                let needMoreMaterials = false;
    
                // 檢查每種材料
                /*for (const [material, required] of Object.entries(itemConfig.recipe.materials)) {
                    const currentCount = materials[material] || 0;
                    // 計算所需數量:考慮每個成品需要的數量
                    const neededAmount = Math.ceil((itemConfig.recipe.stackSize * required) / itemConfig.recipe.output_count);
                    console.log(`[INFO] 檢查材料 ${material}: 當前 ${currentCount},需要 ${neededAmount}`);
                    
                    if (currentCount < neededAmount) {
                        console.log(`[INFO] ${material} 數量不足,目前數量: ${currentCount}`);
                        const boxPos = itemConfig.materials_box[material];
                        
                        // 如果材料本身需要合成
                        if (craftConfig.crafting[material]) {
                            console.log(`[INFO] ${material} 需要先合成`);
                            await this.startAutoCrafting(material, true);  // 標記為中間產物
                            continue;
                        }
                        
                        console.log(`[INFO] 準備從箱子 ${boxPos} 提取 ${material}`);
                        await this.itemHandler.withdrawItem([material], { 
                            [material]: boxPos 
                        }, neededAmount);
                        needMoreMaterials = true;
                    }
                }*/

                // 檢查每種材料
                for (const [material, required] of Object.entries(itemConfig.recipe.materials)) {
                    const currentCount = materials[material] || 0;
                    const neededAmount = Math.ceil((itemConfig.recipe.stackSize * required) / itemConfig.recipe.output_count);
                    console.log(`[INFO] 檢查材料 ${material}: 當前 ${currentCount},需要 ${neededAmount}`);
                    
                    if (currentCount < neededAmount) {
                        console.log(`[INFO] ${material} 數量不足,目前數量: ${currentCount}`);
                        const boxPos = itemConfig.materials_box[material];
                        
                        // 添加使用中間產物的判斷
                        if (itemConfig.use_crafted_materials && craftConfig.crafting[material]) {
                            console.log(`[INFO] ${material} 需要先合成`);
                            await this.startAutoCrafting(material, true);
                            continue;
                        }
                        
                        console.log(`[INFO] 準備從箱子 ${boxPos} 提取 ${material}`);
                        await this.itemHandler.withdrawItem([material], { 
                            [material]: boxPos 
                        }, neededAmount);
                        needMoreMaterials = true;
                    }
                }
    
                // 計算可合成數量
                const currentMaterials = this.craftingHandler.checkMaterials(itemConfig.recipe);
                let maxCraft = Infinity;
                
                for (const [material, required] of Object.entries(itemConfig.recipe.materials)) {
                    const available = currentMaterials[material] || 0;
                    // 考慮輸出數量進行計算
                    maxCraft = Math.min(maxCraft, Math.floor((available * itemConfig.recipe.output_count) / required));
                    console.log(`[INFO] ${material}: 可合成 ${Math.floor((available * itemConfig.recipe.output_count) / required)} 個`);
                }
    
                // 限制每批次合成數量
                maxCraft = Math.min(maxCraft, itemConfig.recipe.stackSize);
                console.log(`[INFO] 最終可合成數量: ${maxCraft}`);
    
                if (maxCraft > 0) {
                    console.log(`[INFO] 開始合成 ${itemName},數量: ${maxCraft}`);
                    try {
                        // 記錄合成前的材料數量
                        const beforeMaterialCounts = {};
                        for (const material of Object.keys(itemConfig.recipe.materials)) {
                            beforeMaterialCounts[material] = this.bot.inventory.count(
                                this.bot.registry.itemsByName[material].id
                            );
                        }
                        
                        // 執行合成
                        const craftedAmount = await this.craftingHandler.craftAll(itemConfig.recipe, maxCraft);
                        
                        //await new Promise(resolve => setTimeout(resolve, 500));
                        
                        // 檢查合成後的材料和成品數量
                        const resultCount = this.bot.inventory.count(
                            this.bot.registry.itemsByName[itemName].id
                        );

                        
                        // 檢查材料是否正確消耗
                        let craftingFailed = false;
                        let materialsConsumed = true;

                        for (const [material, required] of Object.entries(itemConfig.recipe.materials)) {
                            const afterCount = this.bot.inventory.count(
                                this.bot.registry.itemsByName[material].id
                            );
                            const expectedConsumption = required * (resultCount / itemConfig.recipe.output_count);
                            const actualConsumption = beforeMaterialCounts[material] - afterCount;

                            console.log(Math.abs(actualConsumption - expectedConsumption))

                            if (Math.abs(actualConsumption - expectedConsumption) > 5) {
                                materialsConsumed = false;
                                break;
                            }
                        }

                        console.log("resultCount: " + resultCount + "materialsConsumed: " + materialsConsumed)

                        // 修改這部分,同時檢查結果數量和材料消耗
                        if (resultCount === 0 || !materialsConsumed) {
                            console.log('[WARN] 合成失敗: ' + (resultCount === 0 ? '沒有產出' : '材料消耗不正確'));
                            craftingFailed = true;
                        }

                        console.log("craftingFailed: " + craftingFailed)
                        
                        if (true) {
                            console.log(`[WARN] 檢測到合成異常,嘗試重置合成台狀態`);
                            
                            // 關閉合成台
                            await this.bot._client.write('close_window', {
                                windowId: this.craftingHandler.windowId
                            });
                            
                            // 存放材料回箱子
                            /* (const [material, boxPos] of Object.entries(itemConfig.materials_box)) {
                                const count = this.bot.inventory.count(
                                    this.bot.registry.itemsByName[material].id
                                );
                                if (count > 0) {
                                    await this.itemHandler.depositItem([material], {
                                        [material]: boxPos
                                    });
                                }
                            }*/
                           // 嘗試打開並關閉任意一個材料箱來刷新背包狀態
                            try {
                                const anyMaterialBox = Object.values(itemConfig.materials_box)[0]; // 取得第一個材料箱位置
                                const box = await this.bot.blockAt(new Vec3(anyMaterialBox[0], anyMaterialBox[1], anyMaterialBox[2]));
                                if (box) {
                                    const window = await container.openBlock(this.bot, box, "shulker_box");
                                    if (window) {
                                        // 等待一下確保背包狀態更新
                                        //await new Promise(resolve => setTimeout(resolve, 500));
                                        window.close();
                                    }
                                }
                            } catch (error) {
                                console.log(`[ERROR] 刷新背包狀態時出錯:`, error);
                            }
                            //await new Promise(resolve => setTimeout(resolve, 500));
                            
                            continue;
                        }
                        
                    } catch (error) {
                        console.log(`[ERROR] 合成過程出錯:`, error);
                    }
                    
                }
    
                await new Promise(resolve => setTimeout(resolve, 500));
    
            } catch (error) {
                console.log(`[ERROR] 自動合成出錯:`, error.stack);
                await new Promise(resolve => setTimeout(resolve, 100));
            }
        }
    }

    stopCrafting() {
        this.isCrafting = false;
        console.log('[INFO] 正在停止合成...');
        // 清除任務狀態
        currentTask.type = null;
        currentTask.itemName = null;
        currentTask.isIntermediateProduct = false;
    }
}



// 合成封包處理器
class CraftingPacketHandler {
    constructor(bot) {
        this.bot = bot;
        this.windowId = 0;
        this.lastCraftTime = 0;
        this.minCraftInterval = 50; // 最小合成間隔,可以根據伺服器的限制調整
    }

    async craftAll(recipe, maxTimes) {
        console.log(`[DEBUG] 進入craftAll方法,準備合成${maxTimes}個物品`);
    
        const craftingTable = this.bot.findBlock({
            matching: block => block.name === 'crafting_table'
        });
    
        if (!craftingTable) {
            throw new Error('找不到合成台');
        }
    
        // 只打開一次合成台
        await this.bot._client.write('block_place', {
            location: craftingTable.position,
            direction: 1,
            hand: 0,
            cursorX: 0.5,
            cursorY: 0.5,
            cursorZ: 0.5,
            sequence: 1,
            insideBlock: false
        });
    
        // 等待窗口打開
        await new Promise(resolve => {
            const timeout = setTimeout(() => resolve(), 1000); // 設置超時
            this.bot.once('windowOpen', (window) => {
                clearTimeout(timeout);
                this.windowId = window.id;
                resolve();
            });
        });

        // 優化批次處理邏輯
        const batchSize = recipe.batchSize || 64;
        const outputPerCraft = recipe.output_count || 1;
        const targetAmount = Math.min(maxTimes, recipe.stackSize || maxTimes);
        let remainingAmount = targetAmount;
        const totalBatches = Math.ceil(targetAmount / (batchSize * outputPerCraft));

        // 批次處理使用Promise.all進行並行處理
        const batchPromises = [];
        
        for (let batch = 0; batch < totalBatches && remainingAmount > 0; batch++) {
            // 確保合成間隔不會太小
            const now = Date.now();
            const timeSinceLastCraft = now - this.lastCraftTime;
            if (timeSinceLastCraft < this.minCraftInterval) {
                await new Promise(resolve => 
                    setTimeout(resolve, this.minCraftInterval - timeSinceLastCraft)
                );
            }
            
            batchPromises.push(this._processBatch(recipe, batchSize));
            this.lastCraftTime = Date.now();
            
            remainingAmount -= batchSize * outputPerCraft;
        }

        // 等待所有批次完成
        await Promise.all(batchPromises);
        
        // 關閉窗口
        await this.bot._client.write('close_window', {
            windowId: this.windowId
        });

        return maxTimes;
    }

    async _processBatch(recipe, batchSize) {
        // 發送合成請求
        await this.bot._client.write('craft_recipe_request', {
            windowId: this.windowId,
            recipe: recipe.id,
            makeAll: true
        });

        // 優化後的結果獲取邏輯
        await this._getResult();
    }

    async _getResult() {
        const packet = {
            windowId: this.windowId,
            stateId: this.bot._client.state === 'play' ? this.bot._client.windowStateId++ : 0,
            slot: 0,
            mouseButton: 1,
            mode: 1,
            changedSlots: [],
            cursorItem: {
                present: false
            }
        };

        await this.bot._client.write('window_click', packet);
    }

    checkMaterials(recipe) {
        const inventory = this.bot.inventory.items();
        const materials = {};

        inventory.forEach(item => {
            if (!materials[item.name]) {
                materials[item.name] = 0;
            }
            materials[item.name] += item.count;
        });

        return materials;
    }
}



const botArgs = {
    host: config.bot_args.host,
    port: config.bot_args.port,
    username: config.bot_args.username,
    version: '1.21.3',
    auth: 'microsoft',
    skipValidation: true,
    ...(config.proxy ? {
        connect: (client) => {
            socks.createConnection({
                proxy: {
                    host: config.proxy.Host,
                    port: parseInt(config.proxy.Port),
                    type: 5,
                    userId: config.proxy.Username,
                    password: config.proxy.Password
                },
                command: 'connect',
                destination: {
                    host: config.bot_args.host,
                    port: parseInt(config.bot_args.port)
                }
            }, (err, info) => {
                if (err) {
                    console.log('[ERROR] 代理連線失敗:', err);
                    return;
                }
                
                try {
                    client.setSocket(info.socket);
                    client.emit("connect");
                    console.log('[INFO] 成功通過代理連線');
                } catch (e) {
                    console.log('[ERROR] 設置 socket 時發生錯誤:', e);
                }
            });
        }
    } : {})
};

// Bot 初始化函數
const initBot = () => {
    try {
        bot = mineflayer.createBot(botArgs);
        let autoCraftingSystem;

        bot.once("login", () => {
            let botSocket = bot._client.socket;
            console.log(
                `[INFO] 已成功登入 ${botSocket.server ? botSocket.server : botSocket._host}`
            );
        });

        

        bot.once("spawn", async () => {
            console.log(`[INFO] 地圖已載入`);
            
            autoCraftingSystem = new AutoCraftingSystem(bot);

            // 檢查是否有未完成的任務需要恢復
            if (currentTask.type === 'crafting' && currentTask.itemName) {
                console.log(`[INFO] 檢測到未完成的合成任務: ${currentTask.itemName}`);
                // 給系統一些時間完全初始化
                await new Promise(resolve => setTimeout(resolve, 2000));
                autoCraftingSystem.startAutoCrafting(
                    currentTask.itemName,
                    currentTask.isIntermediateProduct
                ).catch(error => {
                    console.log('[ERROR] 恢復自動合成錯誤:', error);
                });
            }

            rl.on("line", function (line) {
                if (line.startsWith('craft ')) {
                    const itemName = line.split(' ')[1];
                    autoCraftingSystem.startAutoCrafting(itemName).catch(error => {
                        console.log('[ERROR] 自動合成錯誤:', error);
                    });
                } else if (line === 'stop') {
                    if (autoCraftingSystem) {
                        autoCraftingSystem.stopCrafting();
                    }
                } else if (line === 'reload') {  // 添加重載命令
                    try {
                        loadConfig();
                        console.log('[INFO] 設定已重新載入');
                    } catch (error) {
                        console.log('[ERROR] 重載設定失敗:', error);
                    }
                } else if (line === 'throwall') {
                    (async () => {
                        try {
                            console.log('[INFO] 開始清空背包...');
                            const items = bot.inventory.items();
                            for (const item of items) {
                                try {
                                    // 設置更長的超時時間,並添加延遲
                                    await bot.tossStack(item);
                                    await new Promise(resolve => setTimeout(resolve, 250)); // 添加延遲
                                } catch (itemError) {
                                    console.log(`[WARN] 丟棄物品 ${item.name} 失敗: ${itemError}`);
                                    continue; // 繼續處理下一個物品
                                }
                            }
                            console.log('[INFO] 背包已清空');
                        } catch (error) {
                            console.log('[ERROR] 清空背包時出錯:', error);
                        }
                    })();
                } else {
                    bot.chat(line);
                }
            });
        });

        bot.on("message", (jsonMsg) => {
            console.log(jsonMsg.toAnsi());
        });

        bot.on("end", () => {
            console.log(`機器人已斷線,將於 5 秒後重啟`);
            for (listener of rl.listeners("line")) {
                rl.removeListener("line", listener);
            }
            setTimeout(initBot, 5000);
        });

        bot.on("kicked", (reason) => {
            console.log(`機器人被伺服器踢出\n原因:${reason}`);
        });

        bot.on("error", (err) => {
            if (err.code === "ECONNREFUSED") {
                console.log(`連線到 ${err.address}:${err.port} 時失敗`);
            } else {
                console.log(`發生無法預期的錯誤: ${err}`);
            }
        });
    } catch (error) {
        console.log(`初始化機器人時發生錯誤:`, error);
        setTimeout(initBot, 5000);
    }
};

let rl = readline.createInterface({
    input: process.stdin,
    output: process.stdout,
});

// 初始化 Bot
initBot();

// 錯誤處理
process.on("unhandledRejection", async (error) => {
    console.log('未處理的 Promise 拒絕:', error);
});

process.on("uncaughtException", async (error) => {
    console.log('未捕獲的異常:', error);
});

process.on("uncaughtExceptionMonitor", async (error) => {
    console.log('未捕獲的異常 (監控):', error);
});

PartialReadError: Read error for undefined : varint is too big: 70
at new ExtendableError (D:\NodeJs\craft\node_modules\protodef\src\utils.js:63:13)
at new PartialReadError (D:\NodeJs\craft\node_modules\protodef\src\utils.js:70:5)
at Object.readVarInt [as varint] (D:\NodeJs\craft\node_modules\protodef\src\datatypes\varint.js:27:27)
at Object.IDSet (eval at compile (D:\NodeJs\craft\node_modules\protodef\src\compiler.js:262:12), :143:39)
at eval (eval at compile (D:\NodeJs\craft\node_modules\protodef\src\compiler.js:262:12), :3140:43)
at eval (eval at compile (D:\NodeJs\craft\node_modules\protodef\src\compiler.js:262:12), :3145:17)
at eval (eval at compile (D:\NodeJs\craft\node_modules\protodef\src\compiler.js:262:12), :3149:15)
at eval (eval at compile (D:\NodeJs\craft\node_modules\protodef\src\compiler.js:262:12), :3151:13)
at eval (eval at compile (D:\NodeJs\craft\node_modules\protodef\src\compiler.js:262:12), :3162:11)
at Object.packet_recipe_book_add (eval at compile (D:\NodeJs\craft\node_modules\protodef\src\compiler.js:262:12), :3167:9)
Chunk size is 80 but only 21 was read ; partial packet : {"name":"player_info","params":{"action":{"_value":32,"add_player":false,"initialize_chat":false,"update_game_mode":false,"update_listed":false,"update_latency":false,"update_display_name":true,"update_priority":false},"data":[{"uuid":"3b5dec80-c2c9-4da3-b78a-7b0dad698875","displayName":{"type":"byte","value":10}}]}}; buffer :4020013b5dec80c2c94da3b78a7b0dad698875010a080004746578740031c2a730c2a730c2a739c2a739c2a766e7949fe5ad98e88085c2a772c2a7382dc2a77273616e626f6e7a616b757261c2a73800
Chunk size is 107 but only 21 was read ; partial packet : {"name":"player_info","params":{"action":{"_value":32,"add_player":false,"initialize_chat":false,"update_game_mode":false,"update_listed":false,"update_latency":false,"update_display_name":true,"update_priority":false},"data":[{"uuid":"a57ff05b-abb8-4d0d-bee1-c04497f6d246","displayName":{"type":"byte","value":10}}]}}; buffer :402001a57ff05babb84d0dbee1c04497f6d246010a08000474657874004cc2a733e5bba2e59c9fe7a59ee8a9b1c2a772c2a7382dc2a772416e5f416e6479c2a7382dc2a772c2a7375bc2a764e296a1c2a7375dc2a772c2a772c2a7375bc2a738e296a0c2a7375dc2a77200
Chunk size is 79 but only 21 was read ; partial packet : {"name":"player_info","params":{"action":{"_value":32,"add_player":false,"initialize_chat":false,"update_game_mode":false,"update_listed":false,"update_latency":false,"update_display_name":true,"update_priority":false},"data":[{"uuid":"42c4b208-1aa2-4e4c-9166-dbf42b2f7b67","displayName":{"type":"byte","value":10}}]}}; buffer :40200142c4b2081aa24e4c9166dbf42b2f7b67010a080004746578740030c2a730c2a730c2a739c2a739c2a766e7949fe5ad98e88085c2a772c2a7382dc2a77253776966744172726f7732c2a73800
Chunk size is 1213 but only 1154 was read ; partial packet : {"name":"player_info","params":{"action":{"_value":127,"add_player":true,"initialize_chat":true,"update_game_mode":true,"update_listed":true,"update_latency":true,"update_display_name":true,"update_priority":true},"data":[{"uuid":"3b5dec80-c2c9-4da3-b78a-7b0dad698875","player":{"name":"sanbonzakura","properties":[{"key":"textures","value":"ewogICJ0aW1lc3RhbXAiIDogMTczNzA0MDU3OTQyMiwKICAicHJvZmlsZUlkIiA6ICIzYjVkZWM4MGMyYzk0ZGEzYjc4YTdiMGRhZDY5ODg3NSIsCiAgInByb2ZpbGVOYW1lIiA6ICJzYW5ib256YWt1cmEiLAogICJzaWduYXR1cmVSZXF1aXJlZCIgOiB0cnVlLAogICJ0ZXh0dXJlcyIgOiB7CiAgICAiU0tJTiIgOiB7CiAgICAgICJ1cmwiIDogImh0dHA6Ly90ZXh0dXJlcy5taW5lY3JhZnQubmV0L3RleHR1cmUvYmQxOTYyZmYyOWMwMzVhYzAwYjAzOWVhMGRjMGE2YzI4OTk5NDdmYzQwNGY4YTVmMDNkMzQyMDNkOTg1YThjYyIKICAgIH0KICB9Cn0=","signature":"cnkEgffpg2WQ6QMuWkXqYF7Iwduq5DEPO9iX3m7Dbqae4xL81hEWJHJBrQl6YsIFzVkj/qgEIaE3xVPsHOmyRt8FBIhWnWOp4YhqVKbI+D5haU/OVudv3vZW333Bb3QW6ZaGANB53eeo3C+Zb8fsCA9VXE4Ifgb+fMhZTiZA3NEbqGLT/uPV5WW4+JosG98DRJE0KOQx3YvRcMjmzbRuKJnAQtpLVNnJ8tNztGmLypvbZBAvLrGSVW/vi85zrAcpB9dLhP6ESe7c/OK26NdwFjX9ETS3k5i9A2YLNU/+9dq6F+N7Yo3ejFIKYGEnLv5V+Q8vmEAFwPP0SXG6TDbrp8zNxD+CdB5kfIXoqmb7WaQbfB+o6ZY4xwV4DhVN33aggQ1LFv/T7gJLrGqAAjU9i0UOENWG2/6hPZmjnqeq5ID7lC2wBtuAAldOw2bQpUIuZ9+UVKGkjWMaGirx1f9dMgM7fufZA77hf8jKO2z+M40YrUpzVxb2aRquE6FyTrgwIFMVFr4T9fWmju3ZH1hh3mbFAjXDV11vaVSkWX/Iwu77K90kOOTvdjq5jPLbqmhSvhyEvGeiqhEkPZgFMT+vqAVBxwKxHiAXCS5O6zxHDDQQ3CGIZJoIlPNVVttqNeEX4VIWjS/ZkJ9gydc1rxeior6c0xCGFetc4swR9+wGoRE="}]},"gamemode":0,"listed":1,"latency":0,"displayName":{"type":"byte","value":10},"listPriority":8}]}}; buffer :407f013b5dec80c2c94da3b78a7b0dad6988750c73616e626f6e7a616b75726101087465787475726573a00365776f6749434a306157316c633352686258416949446f674d54637a4e7a41304d4455334f5451794d69774b4943416963484a765a6d6c735a556c6b496941364943497a596a566b5a574d344d474d79597a6b305a47457a596a6334595464694d4752685a4459354f4467334e53497343694167496e427962325a706247564f5957316c4969413649434a7a595735696232353659577431636d45694c416f6749434a7a6157647559585231636d56535a58463161584a6c5a4349674f694230636e566c4c416f6749434a305a58683064584a6c637949674f69423743694167494341695530744a546949674f694237436941674943416749434a31636d776949446f67496d6830644841364c7939305a58683064584a6c637935746157356c59334a685a6e5175626d56304c33526c65485231636d5576596d51784f5459795a6d59794f574d774d7a5668597a4177596a417a4f5756684d47526a4d474532597a49344f546b354e44646d597a51774e4759345954566d4d444e6b4d7a51794d444e6b4f5467315954686a5979494b494341674948304b49434239436e303d01ac05636e6b45676666706732575136514d75576b58715946374977647571354445504f396958336d37446271616534784c38316845574a484a4272516c36597349467a566b6a2f7167454961453378565073484f6d7952743846424968576e574f7034596871564b62492b44356861552f4f5675647633765a573333334262335157365a6147414e42353365656f33432b5a6238667343413956584534496667622b664d685a54695a41334e456271474c542f755056355757342b4a6f7347393844524a45304b4f517833597652634d6a6d7a6252754b4a6e415174704c564e6e4a38744e7a74476d4c797076625a4241764c72475356572f766938357a724163704239644c68503645536537632f4f4b32364e6477466a5839455453336b3569394132594c4e552f2b39647136462b4e37596f33656a46494b5947456e4c7635562b5138766d45414677505030535847365444627270387a4e78442b436442356b6649586f716d62375761516266422b6f365a5934787756344468564e333361676751314c46762f5437674a4c72477141416a55396930554f454e5747322f3668505a6d6a6e716571354944376c43327742747541416c644f77326251705549755a392b55564b476b6a574d6147697278316639644d674d376675665a4137376866386a4b4f327a2b4d3430597255707a5678623261527175453646795472677749464d56467234543966576d6a75335a48316868336d6246416a5844563131766156536b57582f49777537374b39306b4f4f5476646a71356a504c62716d685376687945764765697168456b505a67464d542b767141564278774b78486941584353354f367a784844445151334347495a4a6f496c504e56567474714e654558345649576a532f5a6b4a396779646331727865696f723663307843474665746334737752392b77476f52453d00000100010a080004746578740031c2a730c2a730c2a739c2a739c2a766e7949fe5ad98e88085c2a772c2a7382dc2a77273616e626f6e7a616b757261c2a7380000
RangeError [ERR_OUT_OF_RANGE]: Read error for undefined : The value of "offset" is out of range. It must be >= 0 and <= 3458. Received -1973594525
at boundsError (node:internal/buffer:88:9)
at Buffer.readUInt8 (node:internal/buffer:254:5)
at Object.readVarInt [as varint] (D:\NodeJs\craft\node_modules\protodef\src\datatypes\varint.js:20:25)
at Object.string (eval at compile (D:\NodeJs\craft\node_modules\protodef\src\compiler.js:262:12), :103:61)
at eval (eval at compile (D:\NodeJs\craft\node_modules\protodef\src\compiler.js:262:12), :1572:61)
at eval (eval at compile (D:\NodeJs\craft\node_modules\protodef\src\compiler.js:262:12), :1583:11)
at Object.game_profile (eval at compile (D:\NodeJs\craft\node_modules\protodef\src\compiler.js:262:12), :1588:9)
at eval (eval at compile (D:\NodeJs\craft\node_modules\protodef\src\compiler.js:262:12), :3019:51)
at eval (eval at compile (D:\NodeJs\craft\node_modules\protodef\src\compiler.js:262:12), :3022:13)
at eval (eval at compile (D:\NodeJs\craft\node_modules\protodef\src\compiler.js:262:12), :3060:11) {
code: 'ERR_OUT_OF_RANGE',
field: 'play.toClient'
}
發生無法預期的錯誤: RangeError [ERR_OUT_OF_RANGE]: Deserialization error for play.toClient : Read error for undefined : The value of "offset" is out of range. It must be >= 0 and <= 3458. Received -1973594525

@y3621555 y3621555 added possible bug Stage1 just created by someone new to the project, we don't know yet if it deserves an implementation / a f labels Jan 16, 2025
@y3621555
Copy link
Author

Title: Recipe ID format change in Minecraft 1.21.3 breaks crafting functionality

Description:
I've identified a critical issue with recipe handling in Minecraft 1.21.3. Starting from this version, Minecraft has changed recipe IDs from strings to dynamic numeric IDs that require server synchronization.

Current Behavior:

  • Bot crashes with error: "PartialReadError: Read error for undefined : varint is too big: 70" when handling recipe_book_add packets
  • Error occurs because the code expects string IDs but receives numeric IDs

Expected Behavior:

  • Bot should first receive and store the recipe registry mapping from server
  • Use numeric recipe IDs for crafting requests instead of string IDs

Technical Details:

  1. The server now sends a registry synchronization packet containing recipe ID mappings
  2. Recipe IDs changed from string format (e.g. "minecraft:crafting_table") to dynamic numeric IDs
  3. The crafting system needs to:
    • Handle the registry sync packet
    • Store the recipe ID mappings
    • Use numeric IDs when sending craft_recipe_request packets

Error Log:

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
possible bug Stage1 just created by someone new to the project, we don't know yet if it deserves an implementation / a f
Projects
None yet
Development

No branches or pull requests

1 participant