mirror of
https://github.com/MCCTeam/Minecraft-Console-Client
synced 2025-10-14 21:22:49 +00:00
Complete AutoCraft
This commit is contained in:
parent
a8464077a1
commit
d71fb88d93
1 changed files with 263 additions and 92 deletions
|
|
@ -10,19 +10,39 @@ namespace MinecraftClient.ChatBots
|
||||||
{
|
{
|
||||||
class AutoCarft : ChatBot
|
class AutoCarft : ChatBot
|
||||||
{
|
{
|
||||||
|
private bool waitingForMaterials = false;
|
||||||
private bool waitingForUpdate = false;
|
private bool waitingForUpdate = false;
|
||||||
|
private bool waitingForTable = false;
|
||||||
private bool craftingFailed = false;
|
private bool craftingFailed = false;
|
||||||
private int inventoryInUse = -2;
|
private int inventoryInUse = -2;
|
||||||
private int index = 0;
|
private int index = 0;
|
||||||
private Recipe recipeInUse;
|
private Recipe recipeInUse;
|
||||||
private List<ActionStep> actionSteps = new List<ActionStep>();
|
private List<ActionStep> actionSteps = new List<ActionStep>();
|
||||||
|
|
||||||
|
private Location tableLocation = new Location();
|
||||||
|
private bool abortOnFailure = true;
|
||||||
|
private int updateDebounceValue = 2;
|
||||||
private int updateDebounce = 0;
|
private int updateDebounce = 0;
|
||||||
|
private int updateTimeoutValue = 10;
|
||||||
|
private int updateTimeout = 0;
|
||||||
|
private string timeoutAction = "unspecified";
|
||||||
|
|
||||||
private string configPath = @"autocraft\config.ini";
|
private string configPath = @"autocraft\config.ini";
|
||||||
|
|
||||||
private Dictionary<string, Recipe> recipes = new Dictionary<string, Recipe>();
|
private Dictionary<string, Recipe> recipes = new Dictionary<string, Recipe>();
|
||||||
|
|
||||||
|
private void resetVar()
|
||||||
|
{
|
||||||
|
craftingFailed = false;
|
||||||
|
waitingForTable = false;
|
||||||
|
waitingForUpdate = false;
|
||||||
|
waitingForMaterials = false;
|
||||||
|
inventoryInUse = -2;
|
||||||
|
index = 0;
|
||||||
|
recipeInUse = null;
|
||||||
|
actionSteps.Clear();
|
||||||
|
}
|
||||||
|
|
||||||
private enum ActionType
|
private enum ActionType
|
||||||
{
|
{
|
||||||
LeftClick,
|
LeftClick,
|
||||||
|
|
@ -138,12 +158,12 @@ namespace MinecraftClient.ChatBots
|
||||||
|
|
||||||
public override void Initialize()
|
public override void Initialize()
|
||||||
{
|
{
|
||||||
RegisterChatBotCommand("craft", "craft", CraftCommand);
|
|
||||||
RegisterChatBotCommand("open", "open", Open);
|
|
||||||
RegisterChatBotCommand("autocraft", "auto craft", CommandHandler);
|
RegisterChatBotCommand("autocraft", "auto craft", CommandHandler);
|
||||||
}
|
}
|
||||||
|
|
||||||
public string CommandHandler(string cmd, string[] args)
|
public string CommandHandler(string cmd, string[] args)
|
||||||
|
{
|
||||||
|
if (args.Length > 0)
|
||||||
{
|
{
|
||||||
switch (args[0])
|
switch (args[0])
|
||||||
{
|
{
|
||||||
|
|
@ -157,64 +177,60 @@ namespace MinecraftClient.ChatBots
|
||||||
recipes.Clear();
|
recipes.Clear();
|
||||||
LoadConfig();
|
LoadConfig();
|
||||||
return "";
|
return "";
|
||||||
}
|
case "resetcfg":
|
||||||
|
WriteDefaultConfig();
|
||||||
|
return "Resetting your config to the default";
|
||||||
|
case "start":
|
||||||
|
if (args.Length >= 2)
|
||||||
|
{
|
||||||
|
string name = args[1];
|
||||||
|
if (recipes.ContainsKey(name))
|
||||||
|
{
|
||||||
|
resetVar();
|
||||||
|
PrepareCrafting(recipes[name]);
|
||||||
return "";
|
return "";
|
||||||
}
|
}
|
||||||
|
else return "Specified recipe name do not exist. Check your config file.";
|
||||||
public string Open(string cmd, string[] args)
|
}
|
||||||
{
|
else return "Please specify the recipe name you want to carft.";
|
||||||
double x = Convert.ToDouble(args[0]);
|
case "stop":
|
||||||
double y = Convert.ToDouble(args[1]);
|
StopCrafting();
|
||||||
double z = Convert.ToDouble(args[2]);
|
return "AutoCraft stopped";
|
||||||
SendPlaceBlock(new Location(x, y, z), .5f, .5f, .5f, Direction.Up);
|
case "help":
|
||||||
return "ok";
|
return GetCommandHelp(args.Length >= 2 ? args[1] : "");
|
||||||
|
default:
|
||||||
|
return GetHelp();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else return GetHelp();
|
||||||
}
|
}
|
||||||
|
|
||||||
public string CraftCommand(string command, string[] args)
|
private string GetHelp()
|
||||||
{
|
{
|
||||||
/* Define crafting recipe */
|
return "Auto-crafting bot. Available commands: load, list, reload, resetcfg, start, stop, help. Use /autocraft help <cmd name> for more information";
|
||||||
// TODO: make a dedicated config file for user to set their own recipe
|
|
||||||
Dictionary<int, ItemType> materials = new Dictionary<int, ItemType>
|
|
||||||
{
|
|
||||||
{ 1, ItemType.OakPlanks }, { 2, ItemType.OakPlanks }, { 3, ItemType.OakPlanks },
|
|
||||||
{ 4, ItemType.Cobblestone }, { 5, ItemType.IronIngot }, { 6, ItemType.Cobblestone },
|
|
||||||
{ 7, ItemType.Cobblestone }, { 8, ItemType.Redstone }, { 9, ItemType.Cobblestone }
|
|
||||||
};
|
|
||||||
Recipe recipe = new Recipe(materials, ItemType.StoneButton, ContainerType.Crafting);
|
|
||||||
inventoryInUse = 1;
|
|
||||||
|
|
||||||
recipeInUse = recipe;
|
|
||||||
craftingFailed = false;
|
|
||||||
waitingForUpdate = false;
|
|
||||||
index = 0;
|
|
||||||
|
|
||||||
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));
|
|
||||||
}
|
|
||||||
if (actionSteps.Count > 0)
|
|
||||||
{
|
|
||||||
// Wait for server to send us the crafting result
|
|
||||||
actionSteps.Add(new ActionStep(ActionType.WaitForUpdate, inventoryInUse, 0));
|
|
||||||
// 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
|
|
||||||
HandleNextStep();
|
|
||||||
return "AutoCraft start!";
|
|
||||||
}
|
|
||||||
else return "AutoCraft cannot be started. Check your available materials";
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public string LoadConfigRunner(string command, string[] args)
|
private string GetCommandHelp(string cmd)
|
||||||
{
|
{
|
||||||
LoadConfig();
|
switch (cmd.ToLower())
|
||||||
return "ok";
|
{
|
||||||
|
case "load":
|
||||||
|
return "Load the config from default location.";
|
||||||
|
case "list":
|
||||||
|
return "List loaded recipes name.";
|
||||||
|
case "reload":
|
||||||
|
return "Reload the config from default location.";
|
||||||
|
case "resetcfg":
|
||||||
|
return "Write the default example config to default location.";
|
||||||
|
case "start":
|
||||||
|
return "Start the crafting. Usage: /autocraft start <recipe name>";
|
||||||
|
case "stop":
|
||||||
|
return "Stop the current running crafting process";
|
||||||
|
case "help":
|
||||||
|
return "Get the command description. Usage: /autocraft help <command name>";
|
||||||
|
default:
|
||||||
|
return GetHelp();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#region Config handling
|
#region Config handling
|
||||||
|
|
@ -247,18 +263,20 @@ namespace MinecraftClient.ChatBots
|
||||||
{
|
{
|
||||||
"[autocraft]",
|
"[autocraft]",
|
||||||
"# A vaild autocraft config must begin with [autocraft]",
|
"# A vaild autocraft config must begin with [autocraft]",
|
||||||
"# You can define multiple recipe in the config file",
|
"",
|
||||||
"# This is a example of how to define a recipe",
|
"tablelocation=0,65,0 # Location of the crafting table if you intended to use it. Terrain and movements must be enabled. Format: x,y,z",
|
||||||
|
"onfailure=abort # What to do on crafting failure, abort or wait",
|
||||||
|
"updatedebounce=2 # DO NOT change this unless you know what you are doing. Value must be larger than 0, usually between 1-3",
|
||||||
|
"",
|
||||||
|
"# You can define multiple recipe in a single config file",
|
||||||
|
"# This is an example of how to define a recipe",
|
||||||
"[recipe]",
|
"[recipe]",
|
||||||
"# name could be whatever you like. This must be in the first place",
|
"name=whatever # name could be whatever you like. This must be in the first place",
|
||||||
"name=whatever",
|
"type=player # crafting table type: player or table",
|
||||||
"# crafting table type: player or table",
|
"result=StoneButton # the resulting item",
|
||||||
"type=player",
|
"",
|
||||||
"# the resulting item",
|
|
||||||
"result=StoneButton",
|
|
||||||
"# define slots with their deserved item",
|
"# define slots with their deserved item",
|
||||||
"# slot start with 1, count from top to bottom, left to right",
|
"slot1=Stone # slot start with 1, count from left to right, top to bottom",
|
||||||
"slot1=Stone",
|
|
||||||
"# For the naming of the items, please see",
|
"# For the naming of the items, please see",
|
||||||
"# https://github.com/ORelio/Minecraft-Console-Client/blob/master/MinecraftClient/Inventory/ItemType.cs"
|
"# https://github.com/ORelio/Minecraft-Console-Client/blob/master/MinecraftClient/Inventory/ItemType.cs"
|
||||||
};
|
};
|
||||||
|
|
@ -296,9 +314,8 @@ namespace MinecraftClient.ChatBots
|
||||||
string value = line.Substring(key.Length + 1);
|
string value = line.Substring(key.Length + 1);
|
||||||
switch (session)
|
switch (session)
|
||||||
{
|
{
|
||||||
case "recipe":
|
case "recipe": parseRecipe(key, value); break;
|
||||||
parseRecipe(key, value);
|
case "autocraft": parseMain(key, value); break;
|
||||||
break;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -322,6 +339,29 @@ namespace MinecraftClient.ChatBots
|
||||||
|
|
||||||
#region Local method for parsing different session of config
|
#region Local method for parsing different session of config
|
||||||
|
|
||||||
|
void parseMain(string key, string value)
|
||||||
|
{
|
||||||
|
switch (key)
|
||||||
|
{
|
||||||
|
case "tablelocation":
|
||||||
|
string[] values = value.Split(',');
|
||||||
|
if (values.Length == 3)
|
||||||
|
{
|
||||||
|
tableLocation.X = Convert.ToInt32(values[0]);
|
||||||
|
tableLocation.Y = Convert.ToInt32(values[1]);
|
||||||
|
tableLocation.Z = Convert.ToInt32(values[2]);
|
||||||
|
}
|
||||||
|
else throw new Exception("Invaild config format");
|
||||||
|
break;
|
||||||
|
case "onfailure":
|
||||||
|
abortOnFailure = value.ToLower() == "abort" ? true : false;
|
||||||
|
break;
|
||||||
|
case "updatedebounce":
|
||||||
|
updateDebounceValue = Convert.ToInt32(value);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
void parseRecipe(string key, string value)
|
void parseRecipe(string key, string value)
|
||||||
{
|
{
|
||||||
if (key.StartsWith("slot"))
|
if (key.StartsWith("slot"))
|
||||||
|
|
@ -392,11 +432,27 @@ namespace MinecraftClient.ChatBots
|
||||||
|
|
||||||
public override void OnInventoryUpdate(int inventoryId)
|
public override void OnInventoryUpdate(int inventoryId)
|
||||||
{
|
{
|
||||||
if (waitingForUpdate && inventoryInUse == inventoryId)
|
if ((waitingForUpdate && inventoryInUse == inventoryId) || (waitingForMaterials && inventoryInUse == inventoryId))
|
||||||
{
|
{
|
||||||
// Because server might send us a LOT of update at once, even there is only a single slot updated.
|
// 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
|
// Using this to make sure we don't do things before inventory update finish
|
||||||
updateDebounce = 2;
|
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);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -408,19 +464,112 @@ namespace MinecraftClient.ChatBots
|
||||||
if (updateDebounce <= 0)
|
if (updateDebounce <= 0)
|
||||||
InventoryUpdateFinished();
|
InventoryUpdateFinished();
|
||||||
}
|
}
|
||||||
|
if (updateTimeout > 0)
|
||||||
|
{
|
||||||
|
updateTimeout--;
|
||||||
|
if (updateTimeout <= 0)
|
||||||
|
HandleUpdateTimeout();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private void InventoryUpdateFinished()
|
private void InventoryUpdateFinished()
|
||||||
{
|
{
|
||||||
|
if (waitingForUpdate || waitingForMaterials)
|
||||||
|
{
|
||||||
|
if (waitingForUpdate)
|
||||||
waitingForUpdate = false;
|
waitingForUpdate = false;
|
||||||
|
if (waitingForMaterials)
|
||||||
|
{
|
||||||
|
waitingForMaterials = false;
|
||||||
|
craftingFailed = false;
|
||||||
|
}
|
||||||
HandleNextStep();
|
HandleNextStep();
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void OpenTable(Location location)
|
||||||
|
{
|
||||||
|
SendPlaceBlock(location, Direction.Up);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void PrepareCrafting(string name)
|
||||||
|
{
|
||||||
|
PrepareCrafting(recipes[name]);
|
||||||
|
}
|
||||||
|
private void PrepareCrafting(Recipe recipe)
|
||||||
|
{
|
||||||
|
/* Define crafting recipe */
|
||||||
|
// TODO: make a dedicated config file for user to set their own recipe
|
||||||
|
//Dictionary<int, ItemType> materials = new Dictionary<int, ItemType>
|
||||||
|
//{
|
||||||
|
// { 1, ItemType.OakPlanks }, { 2, ItemType.OakPlanks }, { 3, ItemType.OakPlanks },
|
||||||
|
// { 4, ItemType.Cobblestone }, { 5, ItemType.IronIngot }, { 6, ItemType.Cobblestone },
|
||||||
|
// { 7, ItemType.Cobblestone }, { 8, ItemType.Redstone }, { 9, ItemType.Cobblestone }
|
||||||
|
//};
|
||||||
|
//Recipe recipe = new Recipe(materials, ItemType.StoneButton, ContainerType.Crafting);
|
||||||
|
//inventoryInUse = 1;
|
||||||
|
|
||||||
|
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
|
||||||
|
OpenTable(tableLocation);
|
||||||
|
waitingForTable = true;
|
||||||
|
SetTimeout("table not found");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
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));
|
||||||
|
}
|
||||||
|
if (actionSteps.Count > 0)
|
||||||
|
{
|
||||||
|
// Wait for server to send us the crafting result
|
||||||
|
actionSteps.Add(new ActionStep(ActionType.WaitForUpdate, inventoryInUse, 0));
|
||||||
|
// 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
|
||||||
|
ConsoleIO.WriteLogLine("AutoCraft start!");
|
||||||
|
HandleNextStep();
|
||||||
|
}
|
||||||
|
else ConsoleIO.WriteLogLine("AutoCraft cannot be started. Check your available materials");
|
||||||
|
}
|
||||||
|
|
||||||
|
private void StopCrafting()
|
||||||
|
{
|
||||||
|
actionSteps.Clear();
|
||||||
|
// 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);
|
||||||
|
ConsoleIO.WriteLogLine("Inventory #" + inventoryInUse + " was closed by AutoCraft");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private void HandleNextStep()
|
private void HandleNextStep()
|
||||||
{
|
{
|
||||||
while (actionSteps.Count > 0)
|
while (actionSteps.Count > 0)
|
||||||
{
|
{
|
||||||
if (waitingForUpdate) break;
|
if (waitingForUpdate || waitingForMaterials || craftingFailed) break;
|
||||||
ActionStep step = actionSteps[index];
|
ActionStep step = actionSteps[index];
|
||||||
index++;
|
index++;
|
||||||
switch (step.ActionType)
|
switch (step.ActionType)
|
||||||
|
|
@ -486,14 +635,36 @@ namespace MinecraftClient.ChatBots
|
||||||
{
|
{
|
||||||
if (craftingFailed)
|
if (craftingFailed)
|
||||||
{
|
{
|
||||||
actionSteps.Clear();
|
if (abortOnFailure)
|
||||||
// Closing inventory can make server to update our inventory
|
{
|
||||||
// Useful when
|
StopCrafting();
|
||||||
// - There are some items left in the crafting area
|
|
||||||
// - Resynchronize player inventory if using crafting table
|
|
||||||
CloseInventory(inventoryInUse);
|
|
||||||
ConsoleIO.WriteLogLine("Crafting aborted! Check your available materials.");
|
ConsoleIO.WriteLogLine("Crafting aborted! Check your available materials.");
|
||||||
}
|
}
|
||||||
|
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--;
|
||||||
|
ConsoleIO.WriteLogLine("Crafting failed! Waiting for more materials");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void HandleUpdateTimeout()
|
||||||
|
{
|
||||||
|
ConsoleIO.WriteLogLine("Action timeout! Reason: " + timeoutAction);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void SetTimeout(string reason = "unspecified")
|
||||||
|
{
|
||||||
|
updateTimeout = updateTimeoutValue;
|
||||||
|
timeoutAction = reason;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void ClearTimeout()
|
||||||
|
{
|
||||||
|
updateTimeout = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
#endregion
|
#endregion
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue