From a8464077a195f4257fc8cf1b4eb6eb991c60a53a Mon Sep 17 00:00:00 2001 From: ReinforceZwei <39955851+ReinforceZwei@users.noreply.github.com> Date: Sun, 19 Jul 2020 21:27:01 +0800 Subject: [PATCH] Add config support --- MinecraftClient/ChatBots/AutoCarft.cs | 218 +++++++++++++++++++++++++- 1 file changed, 216 insertions(+), 2 deletions(-) diff --git a/MinecraftClient/ChatBots/AutoCarft.cs b/MinecraftClient/ChatBots/AutoCarft.cs index 59160526..895a8d2f 100644 --- a/MinecraftClient/ChatBots/AutoCarft.cs +++ b/MinecraftClient/ChatBots/AutoCarft.cs @@ -2,6 +2,7 @@ using System.Collections.Generic; using System.Linq; using System.Text; +using System.IO; using MinecraftClient.Inventory; using MinecraftClient.Mapping; @@ -10,15 +11,17 @@ namespace MinecraftClient.ChatBots class AutoCarft : ChatBot { private bool waitingForUpdate = false; + private bool craftingFailed = false; private int inventoryInUse = -2; private int index = 0; private Recipe recipeInUse; + private List actionSteps = new List(); private int updateDebounce = 0; - private bool craftingFailed = false; + private string configPath = @"autocraft\config.ini"; - private List actionSteps = new List(); + private Dictionary recipes = new Dictionary(); private enum ActionType { @@ -100,6 +103,7 @@ namespace MinecraftClient.ChatBots /// position start with 1, from left to right, top to bottom public Dictionary Materials; + public Recipe() { } public Recipe(Dictionary materials, ItemType resultItem, ContainerType type) { Materials = materials; @@ -135,6 +139,35 @@ namespace MinecraftClient.ChatBots public override void Initialize() { RegisterChatBotCommand("craft", "craft", CraftCommand); + RegisterChatBotCommand("open", "open", Open); + RegisterChatBotCommand("autocraft", "auto craft", CommandHandler); + } + + public string CommandHandler(string cmd, string[] args) + { + switch (args[0]) + { + case "load": + LoadConfig(); + return ""; + case "list": + string names = string.Join(", ", recipes.Keys.ToList()); + return String.Format("Total {0} recipes loaded: {1}", recipes.Count, names); + case "reload": + recipes.Clear(); + LoadConfig(); + return ""; + } + return ""; + } + + public string Open(string cmd, string[] args) + { + double x = Convert.ToDouble(args[0]); + double y = Convert.ToDouble(args[1]); + double z = Convert.ToDouble(args[2]); + SendPlaceBlock(new Location(x, y, z), .5f, .5f, .5f, Direction.Up); + return "ok"; } public string CraftCommand(string command, string[] args) @@ -178,6 +211,185 @@ namespace MinecraftClient.ChatBots else return "AutoCraft cannot be started. Check your available materials"; } + public string LoadConfigRunner(string command, string[] args) + { + LoadConfig(); + return "ok"; + } + + #region Config handling + + public void LoadConfig() + { + if (!File.Exists(configPath)) + { + if (!Directory.Exists(configPath)) + { + Directory.CreateDirectory(@"autocraft"); + } + WriteDefaultConfig(); + ConsoleIO.WriteLogLine("No config found. Writing a new one."); + } + try + { + ParseConfig(); + ConsoleIO.WriteLogLine("Successfully loaded"); + } + catch (Exception e) + { + ConsoleIO.WriteLogLine("Error while parsing config: \n" + e.Message); + } + } + + private void WriteDefaultConfig() + { + string[] content = + { + "[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", + "[recipe]", + "# name could be whatever you like. This must be in the first place", + "name=whatever", + "# crafting table type: player or table", + "type=player", + "# the resulting item", + "result=StoneButton", + "# define slots with their deserved item", + "# slot start with 1, count from top to bottom, left to right", + "slot1=Stone", + "# For the naming of the items, please see", + "# https://github.com/ORelio/Minecraft-Console-Client/blob/master/MinecraftClient/Inventory/ItemType.cs" + }; + File.WriteAllLines(configPath, content); + } + + private void ParseConfig() + { + string[] content = File.ReadAllLines(configPath); + if (content[0] != "[autocraft]") + { + throw new Exception("Cannot parse this config"); + } + + // local variable for use in parsing config + string session = ""; + Dictionary recipes = new Dictionary(); + string lastRecipe = ""; + + foreach (string l in content) + { + // ignore comment start with # + if (l.StartsWith("#")) continue; + string line = l.Split('#')[0].Trim(); + if (line.Length <= 0) continue; + + if (line[0] == '[' && line[line.Length - 1] == ']') + { + session = line.Substring(1, line.Length - 2).ToLower(); + continue; + } + + string key = line.Split('=')[0].ToLower(); + if (!(line.Length > (key.Length + 1))) continue; + string value = line.Substring(key.Length + 1); + switch (session) + { + case "recipe": + parseRecipe(key, value); + break; + } + } + + // check and save recipe + foreach(var pair in recipes) + { + if ((pair.Value.CraftingAreaType == ContainerType.PlayerInventory + || pair.Value.CraftingAreaType == ContainerType.Crafting) + && (pair.Value.Materials != null + && pair.Value.Materials.Count > 0) + && pair.Value.ResultItem != ItemType.Air) + { + // checking pass + this.recipes.Add(pair.Key, pair.Value); + } + else + { + throw new Exception("Missing item in recipe"); + } + } + + #region Local method for parsing different session of config + + void parseRecipe(string key, string value) + { + if (key.StartsWith("slot")) + { + int slot = Convert.ToInt32(key[key.Length - 1].ToString()); + if (slot > 0 && slot < 10) + { + if (recipes.ContainsKey(lastRecipe)) + { + if (Enum.TryParse(value, out ItemType itemType)) + { + if (recipes[lastRecipe].Materials != null && recipes[lastRecipe].Materials.Count > 0) + { + recipes[lastRecipe].Materials.Add(slot, itemType); + } + else + { + recipes[lastRecipe].Materials = new Dictionary() + { + { slot, itemType } + }; + } + return; + } + } + } + throw new Exception("Invalid config format"); + } + else + { + switch (key) + { + case "name": + if (!recipes.ContainsKey(value)) + { + recipes.Add(value, new Recipe()); + lastRecipe = value; + } + else + { + throw new Exception("Duplicate recipe name specified"); + } + break; + case "type": + if (recipes.ContainsKey(lastRecipe)) + { + recipes[lastRecipe].CraftingAreaType = value.ToLower() == "player" ? ContainerType.PlayerInventory : ContainerType.Crafting; + } + break; + case "result": + if (recipes.ContainsKey(lastRecipe)) + { + if (Enum.TryParse(value, out ItemType itemType)) + { + recipes[lastRecipe].ResultItem = itemType; + } + } + break; + } + } + } + #endregion + } + + #endregion + + #region Core part of auto-crafting + public override void OnInventoryUpdate(int inventoryId) { if (waitingForUpdate && inventoryInUse == inventoryId) @@ -283,5 +495,7 @@ namespace MinecraftClient.ChatBots ConsoleIO.WriteLogLine("Crafting aborted! Check your available materials."); } } + + #endregion } }