Minecraft-Console-Client/MinecraftClient/ChatBots/AutoCraft.cs

696 lines
26 KiB
C#
Raw Normal View History

2020-07-07 11:38:15 +08:00
using System;
using System.Collections.Generic;
using System.Linq;
2022-10-07 21:49:05 +08:00
using System.Text;
2020-07-07 11:38:15 +08:00
using MinecraftClient.Inventory;
2020-07-09 14:30:24 +08:00
using MinecraftClient.Mapping;
2022-10-05 15:02:30 +08:00
using Tomlet.Attributes;
2022-10-05 19:16:33 +08:00
using static MinecraftClient.ChatBots.AutoCraft.Configs;
2020-07-07 11:38:15 +08:00
namespace MinecraftClient.ChatBots
{
2022-10-05 15:02:30 +08:00
public class AutoCraft : ChatBot
2020-07-07 11:38:15 +08:00
{
2022-10-05 15:02:30 +08:00
public static Configs Config = new();
[TomlDoNotInlineObject]
public class Configs
{
[NonSerialized]
private const string BotName = "AutoCraft";
public bool Enabled = false;
2022-10-05 19:39:21 +08:00
[TomlInlineComment("$config.ChatBot.AutoCraft.CraftingTable$")]
public LocationConfig CraftingTable = new(123, 65, 456);
2022-10-05 19:16:33 +08:00
2022-10-05 19:39:21 +08:00
[TomlInlineComment("$config.ChatBot.AutoCraft.OnFailure$")]
public OnFailConfig OnFailure = OnFailConfig.abort;
2022-10-05 19:16:33 +08:00
[TomlPrecedingComment("$config.ChatBot.AutoCraft.Recipes$")]
public RecipeConfig[] Recipes = new RecipeConfig[]
{
new RecipeConfig(
2022-10-06 11:03:56 +08:00
Name: "Recipe-Name-1",
2022-10-05 19:16:33 +08:00
Type: CraftTypeConfig.player,
Result: ItemType.StoneBricks,
Slots: new ItemType[4] { ItemType.Stone, ItemType.Stone, ItemType.Stone, ItemType.Stone }
),
new RecipeConfig(
2022-10-06 11:03:56 +08:00
Name: "Recipe-Name-2",
2022-10-05 19:16:33 +08:00
Type: CraftTypeConfig.table,
Result: ItemType.StoneBricks,
Slots: new ItemType[9] {
ItemType.Stone, ItemType.Stone, ItemType.Null,
ItemType.Stone, ItemType.Stone, ItemType.Null,
ItemType.Null, ItemType.Null, ItemType.Null,
}
),
};
[NonSerialized]
public Location _Table_Location = Location.Zero;
2022-10-05 15:02:30 +08:00
2022-10-05 15:39:42 +08:00
public void OnSettingUpdate()
{
2022-10-05 19:39:21 +08:00
_Table_Location = new Location(CraftingTable.X, CraftingTable.Y, CraftingTable.Z).ToFloor();
2022-10-06 22:14:15 +08:00
List<string> nameList = new();
2022-10-05 19:16:33 +08:00
foreach (RecipeConfig recipe in Recipes)
{
2022-10-06 22:14:15 +08:00
if (string.IsNullOrWhiteSpace(recipe.Name))
{
recipe.Name = new Random().NextInt64().ToString();
LogToConsole(BotName, Translations.TryGet("bot.autoCraft.exception.name_miss"));
}
if (nameList.Contains(recipe.Name))
{
LogToConsole(BotName, Translations.TryGet("bot.autoCraft.exception.duplicate", recipe.Name));
do
{
recipe.Name = new Random().NextInt64().ToString();
} while (nameList.Contains(recipe.Name));
}
nameList.Add(recipe.Name);
2022-10-05 19:16:33 +08:00
int fixLength = -1;
if (recipe.Type == CraftTypeConfig.player && recipe.Slots.Length != 4)
fixLength = 4;
else if (recipe.Type == CraftTypeConfig.table && recipe.Slots.Length != 9)
fixLength = 9;
if (fixLength > 0)
{
ItemType[] Slots = new ItemType[fixLength];
for (int i = 0; i < fixLength; ++i)
Slots[i] = (i < recipe.Slots.Length) ? recipe.Slots[i] : ItemType.Null;
recipe.Slots = Slots;
LogToConsole(BotName, Translations.TryGet("bot.autocraft.invaild_slots"));
}
if (recipe.Result == ItemType.Air || recipe.Result == ItemType.Null)
{
LogToConsole(BotName, Translations.TryGet("bot.autocraft.invaild_result"));
}
}
2022-10-05 15:39:42 +08:00
}
2022-10-05 19:16:33 +08:00
public struct LocationConfig
{
public double X, Y, Z;
public LocationConfig(double X, double Y, double Z)
{
this.X = X;
this.Y = Y;
this.Z = Z;
}
}
public enum OnFailConfig { abort, wait }
public class RecipeConfig
{
public string Name = "Recipe Name";
public CraftTypeConfig Type = CraftTypeConfig.player;
public ItemType Result = ItemType.Air;
public ItemType[] Slots = new ItemType[9] {
ItemType.Null, ItemType.Null, ItemType.Null,
ItemType.Null, ItemType.Null, ItemType.Null,
ItemType.Null, ItemType.Null, ItemType.Null,
};
public RecipeConfig() { }
public RecipeConfig(string Name, CraftTypeConfig Type, ItemType Result, ItemType[] Slots)
{
this.Name = Name;
this.Type = Type;
this.Result = Result;
this.Slots = Slots;
}
}
public enum CraftTypeConfig { player, table }
2022-10-05 15:02:30 +08:00
}
2020-07-20 19:21:52 +08:00
private bool waitingForMaterials = false;
2020-07-09 22:21:39 +08:00
private bool waitingForUpdate = false;
2020-07-20 19:21:52 +08:00
private bool waitingForTable = false;
2020-07-19 21:27:01 +08:00
private bool craftingFailed = false;
2020-07-09 22:21:39 +08:00
private int inventoryInUse = -2;
private int index = 0;
private Recipe? recipeInUse;
private readonly List<ActionStep> actionSteps = new();
2020-07-09 22:21:39 +08:00
2020-07-20 19:21:52 +08:00
private int updateDebounceValue = 2;
2020-07-09 22:21:39 +08:00
private int updateDebounce = 0;
private readonly int updateTimeoutValue = 10;
2020-07-20 19:21:52 +08:00
private int updateTimeout = 0;
private string timeoutAction = "unspecified";
2020-07-09 22:21:39 +08:00
private void ResetVar()
2020-07-20 19:21:52 +08:00
{
craftingFailed = false;
waitingForTable = false;
waitingForUpdate = false;
waitingForMaterials = false;
inventoryInUse = -2;
index = 0;
recipeInUse = null;
actionSteps.Clear();
}
2020-07-09 14:30:24 +08:00
private enum ActionType
{
2020-07-09 22:21:39 +08:00
LeftClick,
ShiftClick,
2020-07-09 14:30:24 +08:00
WaitForUpdate,
2020-07-09 22:21:39 +08:00
ResetCraftArea,
Repeat,
CheckResult
2020-07-09 14:30:24 +08:00
}
2020-07-12 19:21:30 +08:00
/// <summary>
/// Represent a single action step of the whole crafting process
/// </summary>
2020-07-09 14:30:24 +08:00
private class ActionStep
{
2020-07-12 19:21:30 +08:00
/// <summary>
/// The action type of this action step
/// </summary>
2020-07-09 22:21:39 +08:00
public ActionType ActionType;
2020-07-12 19:21:30 +08:00
/// <summary>
/// For storing data needed for processing
/// </summary>
/// <remarks>-2 mean not used</remarks>
2020-07-09 22:21:39 +08:00
public int Slot = -2;
2020-07-12 19:21:30 +08:00
/// <summary>
/// For storing data needed for processing
/// </summary>
/// <remarks>-2 mean not used</remarks>
2020-07-09 22:21:39 +08:00
public int InventoryID = -2;
2020-07-12 19:21:30 +08:00
/// <summary>
/// For storing data needed for processing
/// </summary>
2020-07-09 22:21:39 +08:00
public ItemType ItemType;
public ActionStep(ActionType actionType)
{
ActionType = actionType;
}
public ActionStep(ActionType actionType, int inventoryID)
{
ActionType = actionType;
InventoryID = inventoryID;
}
public ActionStep(ActionType actionType, int inventoryID, int slot)
{
ActionType = actionType;
Slot = slot;
InventoryID = inventoryID;
}
public ActionStep(ActionType actionType, int inventoryID, ItemType itemType)
{
ActionType = actionType;
InventoryID = inventoryID;
ItemType = itemType;
}
}
2020-07-12 19:21:30 +08:00
/// <summary>
/// Represent a crafting recipe
/// </summary>
2020-07-09 22:21:39 +08:00
private class Recipe
{
2020-07-12 19:21:30 +08:00
/// <summary>
/// The results item of this recipe
/// </summary>
2020-07-09 22:21:39 +08:00
public ItemType ResultItem;
2020-07-12 19:21:30 +08:00
/// <summary>
/// Crafting table required for this recipe, playerInventory or Crafting
/// </summary>
2020-07-09 22:21:39 +08:00
public ContainerType CraftingAreaType;
2020-07-12 19:21:30 +08:00
/// <summary>
/// Materials needed and their position
/// </summary>
/// <remarks>position start with 1, from left to right, top to bottom</remarks>
public Dictionary<int, ItemType>? Materials;
2020-07-09 22:21:39 +08:00
2020-07-19 21:27:01 +08:00
public Recipe() { }
2020-07-09 22:21:39 +08:00
public Recipe(Dictionary<int, ItemType> materials, ItemType resultItem, ContainerType type)
{
Materials = materials;
ResultItem = resultItem;
CraftingAreaType = type;
}
2020-07-12 19:21:30 +08:00
/// <summary>
/// Convert the position of a defined recipe from playerInventory to Crafting
/// </summary>
/// <param name="recipe"></param>
/// <returns>Converted recipe</returns>
/// <remarks>so that it can be used in crafting table</remarks>
2020-07-09 22:21:39 +08:00
public static Recipe ConvertToCraftingTable(Recipe recipe)
{
if (recipe.CraftingAreaType == ContainerType.PlayerInventory && recipe.Materials != null)
2020-07-09 22:21:39 +08:00
{
if (recipe.Materials.ContainsKey(4))
{
recipe.Materials[5] = recipe.Materials[4];
recipe.Materials.Remove(4);
}
if (recipe.Materials.ContainsKey(3))
{
recipe.Materials[4] = recipe.Materials[3];
recipe.Materials.Remove(3);
}
}
return recipe;
}
2020-07-09 14:30:24 +08:00
}
2020-07-07 11:38:15 +08:00
public override void Initialize()
{
2020-07-20 19:33:57 +08:00
if (!GetInventoryEnabled())
{
LogToConsoleTranslated("extra.inventory_required");
LogToConsoleTranslated("general.bot_unload");
2020-07-20 19:33:57 +08:00
UnloadBot();
return;
2020-07-20 19:33:57 +08:00
}
RegisterChatBotCommand("autocraft", Translations.Get("bot.autoCraft.cmd"), GetHelp(), CommandHandler);
RegisterChatBotCommand("ac", Translations.Get("bot.autoCraft.alias"), GetHelp(), CommandHandler);
2020-07-19 21:27:01 +08:00
}
public string CommandHandler(string cmd, string[] args)
{
2020-07-20 19:21:52 +08:00
if (args.Length > 0)
2020-07-19 21:27:01 +08:00
{
2020-07-20 19:21:52 +08:00
switch (args[0])
{
case "list":
2022-10-07 21:49:05 +08:00
StringBuilder nameList = new();
foreach (RecipeConfig recipe in Config.Recipes)
nameList.Append(recipe.Name).Append(", ");
return Translations.Get("bot.autoCraft.cmd.list", Config.Recipes.Length, nameList.ToString());
2020-07-20 19:21:52 +08:00
case "start":
if (args.Length >= 2)
{
string name = args[1];
2022-10-05 19:16:33 +08:00
bool hasRecipe = false;
RecipeConfig? recipe = null;
foreach (RecipeConfig recipeConfig in Config.Recipes)
{
if (recipeConfig.Name == name)
{
hasRecipe = true;
recipe = recipeConfig;
break;
}
}
if (hasRecipe)
2020-07-20 19:21:52 +08:00
{
ResetVar();
2022-10-05 19:16:33 +08:00
PrepareCrafting(recipe!);
2020-07-20 19:21:52 +08:00
return "";
}
2022-10-05 19:16:33 +08:00
else
return Translations.Get("bot.autoCraft.recipe_not_exist");
2020-07-20 19:21:52 +08:00
}
2022-10-05 19:16:33 +08:00
else
return Translations.Get("bot.autoCraft.no_recipe_name");
2020-07-20 19:21:52 +08:00
case "stop":
StopCrafting();
return Translations.Get("bot.autoCraft.stop");
2020-07-20 19:21:52 +08:00
case "help":
return GetCommandHelp(args.Length >= 2 ? args[1] : "");
default:
return GetHelp();
}
2020-07-19 21:27:01 +08:00
}
2020-07-20 19:21:52 +08:00
else return GetHelp();
2020-07-19 21:27:01 +08:00
}
private static string GetHelp()
2020-07-19 21:27:01 +08:00
{
return Translations.Get("bot.autoCraft.available_cmd", "load, list, reload, resetcfg, start, stop, help");
2020-07-07 11:38:15 +08:00
}
2020-07-20 19:21:52 +08:00
private string GetCommandHelp(string cmd)
2020-07-07 11:38:15 +08:00
{
return cmd.ToLower() switch
2020-07-07 11:38:15 +08:00
{
#pragma warning disable format // @formatter:off
"load" => Translations.Get("bot.autocraft.help.load"),
"list" => Translations.Get("bot.autocraft.help.list"),
"reload" => Translations.Get("bot.autocraft.help.reload"),
"resetcfg" => Translations.Get("bot.autocraft.help.resetcfg"),
"start" => Translations.Get("bot.autocraft.help.start"),
"stop" => Translations.Get("bot.autocraft.help.stop"),
"help" => Translations.Get("bot.autocraft.help.help"),
_ => GetHelp(),
#pragma warning restore format // @formatter:on
};
2020-07-19 21:27:01 +08:00
}
#region Core part of auto-crafting
2020-07-07 11:38:15 +08:00
public override void OnInventoryUpdate(int inventoryId)
{
2020-07-20 19:21:52 +08:00
if ((waitingForUpdate && inventoryInUse == inventoryId) || (waitingForMaterials && inventoryInUse == inventoryId))
2020-07-07 11:38:15 +08:00
{
2020-07-12 19:21:30 +08:00
// Because server might send us a LOT of update at once, even there is only a single slot updated.
// Using this to make sure we don't do things before inventory update finish
2020-07-20 19:21:52 +08:00
updateDebounce = updateDebounceValue;
}
}
public override void OnInventoryOpen(int inventoryId)
{
if (waitingForTable)
{
if (GetInventories()[inventoryId].Type == ContainerType.Crafting)
{
waitingForTable = false;
ClearTimeout();
// After table opened, we need to wait for server to update table inventory items
waitingForUpdate = true;
inventoryInUse = inventoryId;
PrepareCrafting(recipeInUse!);
2020-07-20 19:21:52 +08:00
}
2020-07-09 22:21:39 +08:00
}
}
public override void Update()
{
if (updateDebounce > 0)
{
updateDebounce--;
if (updateDebounce <= 0)
InventoryUpdateFinished();
}
2020-07-20 19:21:52 +08:00
if (updateTimeout > 0)
{
updateTimeout--;
if (updateTimeout <= 0)
HandleUpdateTimeout();
}
2020-07-09 22:21:39 +08:00
}
private void InventoryUpdateFinished()
{
2020-07-20 19:21:52 +08:00
if (waitingForUpdate || waitingForMaterials)
{
if (waitingForUpdate)
waitingForUpdate = false;
if (waitingForMaterials)
{
waitingForMaterials = false;
craftingFailed = false;
}
HandleNextStep();
}
}
private void OpenTable(Location location)
{
SendPlaceBlock(location, Direction.Up);
}
2020-07-22 19:01:43 +08:00
/// <summary>
/// Prepare the crafting action steps by the given recipe name and start crafting
/// </summary>
/// <param name="recipe">Name of the recipe to craft</param>
2022-10-05 19:16:33 +08:00
private void PrepareCrafting(RecipeConfig recipeConfig)
2020-07-20 19:21:52 +08:00
{
2022-10-05 19:16:33 +08:00
Dictionary<int, ItemType> materials = new();
for (int i = 0; i < recipeConfig.Slots.Length; ++i)
if (recipeConfig.Slots[i] != ItemType.Null)
2022-10-07 21:49:05 +08:00
materials[i + 1] = recipeConfig.Slots[i];
2022-10-05 19:16:33 +08:00
ItemType ResultItem = recipeConfig.Result;
ContainerType CraftingAreaType =
(recipeConfig.Type == CraftTypeConfig.player) ? ContainerType.PlayerInventory : ContainerType.Crafting;
PrepareCrafting(new Recipe(materials, ResultItem, CraftingAreaType));
2020-07-20 19:21:52 +08:00
}
2020-07-22 19:01:43 +08:00
/// <summary>
/// Prepare the crafting action steps by the given recipe and start crafting
/// </summary>
/// <param name="recipe">Recipe to craft</param>
2020-07-20 19:21:52 +08:00
private void PrepareCrafting(Recipe recipe)
{
recipeInUse = recipe;
if (recipeInUse.CraftingAreaType == ContainerType.PlayerInventory)
inventoryInUse = 0;
else
{
var inventories = GetInventories();
foreach (var inventory in inventories)
if (inventory.Value.Type == ContainerType.Crafting)
inventoryInUse = inventory.Key;
if (inventoryInUse == -2)
{
// table required but not found. Try to open one
2022-10-05 19:16:33 +08:00
OpenTable(Config._Table_Location);
2020-07-20 19:21:52 +08:00
waitingForTable = true;
SetTimeout(Translations.Get("bot.autoCraft.table_not_found"));
2020-07-20 19:21:52 +08:00
return;
}
}
if (recipe.Materials != null)
2020-07-20 19:21:52 +08:00
{
foreach (KeyValuePair<int, ItemType> slot in recipe.Materials)
{
// Steps for moving items from inventory to crafting area
actionSteps.Add(new ActionStep(ActionType.LeftClick, inventoryInUse, slot.Value));
actionSteps.Add(new ActionStep(ActionType.LeftClick, inventoryInUse, slot.Key));
}
2020-07-20 19:21:52 +08:00
}
if (actionSteps.Count > 0)
{
// Wait for server to send us the crafting result
actionSteps.Add(new ActionStep(ActionType.WaitForUpdate, inventoryInUse, 0));
// Check the crafting result is the item we want
actionSteps.Add(new ActionStep(ActionType.CheckResult, inventoryInUse, recipe.ResultItem));
2020-07-20 19:21:52 +08:00
// Put item back to inventory. (Using shift-click can take all item at once)
actionSteps.Add(new ActionStep(ActionType.ShiftClick, inventoryInUse, 0));
// We need to wait for server to update us after taking item from crafting result
actionSteps.Add(new ActionStep(ActionType.WaitForUpdate, inventoryInUse));
// Repeat the whole process again
actionSteps.Add(new ActionStep(ActionType.Repeat));
// Start crafting
LogToConsoleTranslated("bot.autoCraft.start", recipe.ResultItem);
2020-07-20 19:21:52 +08:00
HandleNextStep();
}
else LogToConsoleTranslated("bot.autoCraft.start_fail", recipe.ResultItem);
2020-07-20 19:21:52 +08:00
}
2020-07-22 19:01:43 +08:00
/// <summary>
/// Stop the crafting process by clearing crafting action steps and close the inventory
/// </summary>
2020-07-20 19:21:52 +08:00
private void StopCrafting()
{
actionSteps.Clear();
// Put item back to inventory or they will be dropped
ClearCraftingArea(inventoryInUse);
2020-07-20 19:21:52 +08:00
// Closing inventory can make server to update our inventory
// Useful when
// - There are some items left in the crafting area
// - Resynchronize player inventory if using crafting table
if (GetInventories().ContainsKey(inventoryInUse))
{
CloseInventory(inventoryInUse);
LogToConsoleTranslated("bot.autoCraft.close_inventory", inventoryInUse);
2020-07-20 19:21:52 +08:00
}
2020-07-09 22:21:39 +08:00
}
2020-07-22 19:01:43 +08:00
/// <summary>
/// Handle next crafting action step
/// </summary>
2020-07-09 22:21:39 +08:00
private void HandleNextStep()
{
while (actionSteps.Count > 0)
{
2020-07-20 19:21:52 +08:00
if (waitingForUpdate || waitingForMaterials || craftingFailed) break;
2020-07-09 22:21:39 +08:00
ActionStep step = actionSteps[index];
index++;
switch (step.ActionType)
2020-07-07 11:38:15 +08:00
{
2020-07-09 22:21:39 +08:00
case ActionType.LeftClick:
if (step.Slot != -2)
{
WindowAction(step.InventoryID, step.Slot, WindowActionType.LeftClick);
}
else
{
int[] slots = GetInventories()[step.InventoryID].SearchItem(step.ItemType);
if (slots.Length > 0)
2020-07-09 22:21:39 +08:00
{
int ignoredSlot;
if (recipeInUse!.CraftingAreaType == ContainerType.PlayerInventory)
2020-07-09 22:21:39 +08:00
ignoredSlot = 9;
else
ignoredSlot = 10;
slots = slots.Where(slot => slot >= ignoredSlot).ToArray();
if (slots.Length > 0)
2020-07-09 22:21:39 +08:00
WindowAction(step.InventoryID, slots[0], WindowActionType.LeftClick);
else
craftingFailed = true;
}
else craftingFailed = true;
}
break;
case ActionType.ShiftClick:
if (step.Slot == 0)
{
WindowAction(step.InventoryID, step.Slot, WindowActionType.ShiftClick);
}
else craftingFailed = true;
break;
// Compare the crafting result with the recipe result
case ActionType.CheckResult:
if (GetInventories()[step.InventoryID].Items.ContainsKey(0)
&& GetInventories()[step.InventoryID].Items[0].Type == step.ItemType)
{
// OK
break;
}
else
{
// Bad, reset everything
ClearCraftingArea(step.InventoryID);
index = 0;
}
break;
2020-07-09 22:21:39 +08:00
case ActionType.WaitForUpdate:
if (step.InventoryID != -2)
{
waitingForUpdate = true;
}
else craftingFailed = true;
break;
case ActionType.ResetCraftArea:
if (step.InventoryID != -2)
ClearCraftingArea(step.InventoryID);
2020-07-09 22:21:39 +08:00
else
craftingFailed = true;
break;
case ActionType.Repeat:
index = 0;
break;
2020-07-07 11:38:15 +08:00
}
2020-07-09 22:21:39 +08:00
HandleError();
}
}
2020-07-22 19:01:43 +08:00
/// <summary>
/// Handle any crafting error after a step was processed
/// </summary>
2020-07-09 22:21:39 +08:00
private void HandleError()
{
if (craftingFailed)
{
if (actionSteps[index - 1].ActionType == ActionType.LeftClick && actionSteps[index - 1].ItemType != ItemType.Air)
{
// Inform user the missing meterial name
LogToConsoleTranslated("bot.autoCraft.missing_material", actionSteps[index - 1].ItemType.ToString());
}
2022-10-05 19:39:21 +08:00
if (Config.OnFailure == OnFailConfig.abort)
2020-07-20 19:21:52 +08:00
{
StopCrafting();
LogToConsoleTranslated("bot.autoCraft.aborted");
2020-07-20 19:21:52 +08:00
}
else
{
waitingForMaterials = true;
// Even though crafting failed, action step index will still increase
// we want to do that failed step again so decrease index by 1
index--;
LogToConsoleTranslated("bot.autoCraft.craft_fail");
2020-07-20 19:21:52 +08:00
}
2020-07-07 11:38:15 +08:00
}
}
2020-07-19 21:27:01 +08:00
/// <summary>
/// Put any item left in the crafting area back to inventory
/// </summary>
/// <param name="inventoryId">Inventory ID to operate with</param>
private void ClearCraftingArea(int inventoryId)
{
if (GetInventories().ContainsKey(inventoryId))
{
var inventory = GetInventories()[inventoryId];
int areaStart = 1;
int areaEnd = 4;
if (inventory.Type == ContainerType.Crafting)
{
areaEnd = 9;
}
List<int> emptySlots = inventory.GetEmpytSlots().Where(s => s > 9).ToList();
for (int i = areaStart; i <= areaEnd; i++)
{
if (inventory.Items.ContainsKey(i))
{
if (emptySlots.Count != 0)
{
WindowAction(inventoryId, i, WindowActionType.LeftClick);
WindowAction(inventoryId, emptySlots[0], WindowActionType.LeftClick);
emptySlots.RemoveAt(0);
}
else
{
WindowAction(inventoryId, i, WindowActionType.DropItemStack);
}
}
}
}
}
2020-07-20 19:21:52 +08:00
private void HandleUpdateTimeout()
{
LogToConsoleTranslated("bot.autoCraft.timeout", timeoutAction);
2020-07-20 19:21:52 +08:00
}
2020-07-22 19:01:43 +08:00
/// <summary>
/// Set the timeout. Used to detect the failure of open crafting table
/// </summary>
/// <param name="reason">The reason to display if timeout</param>
2020-07-20 19:21:52 +08:00
private void SetTimeout(string reason = "unspecified")
{
updateTimeout = updateTimeoutValue;
timeoutAction = reason;
}
2020-07-22 19:01:43 +08:00
/// <summary>
/// Clear the timeout
/// </summary>
2020-07-20 19:21:52 +08:00
private void ClearTimeout()
{
updateTimeout = 0;
}
2020-07-19 21:27:01 +08:00
#endregion
2020-07-07 11:38:15 +08:00
}
}