Refactoring Settings.cs

This commit is contained in:
BruceChen 2022-10-05 15:02:30 +08:00
parent f16b1c118b
commit 16c1d1fd77
59 changed files with 3425 additions and 2180 deletions

View file

@ -1,5 +1,6 @@
using System;
using System.Linq;
using Tomlet.Attributes;
namespace MinecraftClient.ChatBots
{
@ -8,9 +9,85 @@ namespace MinecraftClient.ChatBots
/// </summary>
public class Alerts : ChatBot
{
public static Configs Config = new();
[TomlDoNotInlineObject]
public class Configs
{
[NonSerialized]
private const string BotName = "Alerts";
public bool Enabled = false;
[TomlInlineComment("$config.ChatBot.Alerts.Beep_Enabled$")]
public bool Beep_Enabled = true;
[TomlInlineComment("$config.ChatBot.Alerts.Trigger_By_Words$")]
public bool Trigger_By_Words = false;
[TomlInlineComment("$config.ChatBot.Alerts.Trigger_By_Rain$")]
public bool Trigger_By_Rain = false;
[TomlInlineComment("$config.ChatBot.Alerts.Trigger_By_Thunderstorm$")]
public bool Trigger_By_Thunderstorm = false;
[TomlInlineComment("$config.ChatBot.Alerts.Matches_File$")]
public string Matches_File = @"alerts.txt";
[TomlInlineComment("$config.ChatBot.Alerts.Excludes_File$")]
public string Excludes_File = @"alerts-exclude.txt";
[TomlInlineComment("$config.ChatBot.Alerts.Log_To_File$")]
public bool Log_To_File = false;
[TomlInlineComment("$config.ChatBot.Alerts.Log_File$")]
public string Log_File = @"alerts-log.txt";
public void OnSettingUpdate()
{
if (!Enabled) return;
bool checkSuccessed = true;
if (Trigger_By_Words)
{
if (!System.IO.File.Exists(Matches_File))
{
checkSuccessed = false;
LogToConsole(BotName, "File not found: " + System.IO.Path.GetFullPath(Matches_File));
}
if (!System.IO.File.Exists(Excludes_File))
{
checkSuccessed = false;
LogToConsole(BotName, "File not found: " + System.IO.Path.GetFullPath(Excludes_File));
}
if (Log_To_File)
{
try
{
System.IO.File.AppendAllText(Log_File, string.Empty);
}
catch
{
checkSuccessed = false;
LogToConsole(BotName, "Can't write logs to " + System.IO.Path.GetFullPath(Excludes_File));
}
}
}
if (!checkSuccessed)
{
LogToConsole(BotName, Translations.TryGet("general.bot_unload"));
Enabled = false;
}
}
}
private string[] dictionary = Array.Empty<string>();
private string[] excludelist = Array.Empty<string>();
private bool logToFile = false;
float curRainLevel = 0;
float curThunderLevel = 0;
const float threshold = 0.2f;
@ -20,11 +97,10 @@ namespace MinecraftClient.ChatBots
/// </summary>
public override void Initialize()
{
if (Settings.Alerts_Trigger_By_Words)
if (Config.Trigger_By_Words)
{
dictionary = LoadDistinctEntriesFromFile(Settings.Alerts_MatchesFile);
excludelist = LoadDistinctEntriesFromFile(Settings.Alerts_ExcludesFile);
logToFile = Settings.Alerts_File_Logging;
dictionary = LoadDistinctEntriesFromFile(Config.Matches_File);
excludelist = LoadDistinctEntriesFromFile(Config.Excludes_File);
}
}
@ -34,7 +110,7 @@ namespace MinecraftClient.ChatBots
/// <param name="text">Received text</param>
public override void GetText(string text)
{
if (Settings.Alerts_Trigger_By_Words)
if (Config.Trigger_By_Words)
{
//Remove color codes and convert to lowercase
text = GetVerbatim(text).ToLower();
@ -45,16 +121,16 @@ namespace MinecraftClient.ChatBots
//Show an alert for each alert item found in text, if any
foreach (string alert in dictionary.Where(alert => text.Contains(alert)))
{
if (Settings.Alerts_Beep_Enabled)
if (Config.Beep_Enabled)
Console.Beep(); //Text found !
ConsoleIO.WriteLine(text.Replace(alert, "§c" + alert + "§r"));
if (logToFile && Settings.Alerts_LogFile.Length > 0)
if (Config.Log_To_File && Config.Log_File.Length > 0)
{
DateTime now = DateTime.Now;
string TimeStamp = "[" + now.Year + '/' + now.Month + '/' + now.Day + ' ' + now.Hour + ':' + now.Minute + ']';
System.IO.File.AppendAllText(Settings.Alerts_LogFile, TimeStamp + " " + GetVerbatim(text) + "\n");
System.IO.File.AppendAllText(Config.Log_File, TimeStamp + " " + GetVerbatim(text) + "\n");
}
}
}
@ -65,9 +141,9 @@ namespace MinecraftClient.ChatBots
{
if (curRainLevel < threshold && level >= threshold)
{
if (Settings.Alerts_Trigger_By_Rain)
if (Config.Trigger_By_Rain)
{
if (Settings.Alerts_Beep_Enabled)
if (Config.Beep_Enabled)
{
Console.Beep();
Console.Beep();
@ -77,9 +153,9 @@ namespace MinecraftClient.ChatBots
}
else if (curRainLevel >= threshold && level < threshold)
{
if (Settings.Alerts_Trigger_By_Rain)
if (Config.Trigger_By_Rain)
{
if (Settings.Alerts_Beep_Enabled)
if (Config.Beep_Enabled)
{
Console.Beep();
}
@ -93,9 +169,9 @@ namespace MinecraftClient.ChatBots
{
if (curThunderLevel < threshold && level >= threshold)
{
if (Settings.Alerts_Trigger_By_Thunderstorm)
if (Config.Trigger_By_Thunderstorm)
{
if (Settings.Alerts_Beep_Enabled)
if (Config.Beep_Enabled)
{
Console.Beep();
Console.Beep();
@ -105,9 +181,9 @@ namespace MinecraftClient.ChatBots
}
else if (curThunderLevel >= threshold && level < threshold)
{
if (Settings.Alerts_Trigger_By_Thunderstorm)
if (Config.Trigger_By_Thunderstorm)
{
if (Settings.Alerts_Beep_Enabled)
if (Config.Beep_Enabled)
{
Console.Beep();
}

View file

@ -1,6 +1,6 @@
using System;
using System.Globalization;
using MinecraftClient.Mapping;
using Tomlet.Attributes;
namespace MinecraftClient.ChatBots
{
@ -10,94 +10,94 @@ namespace MinecraftClient.ChatBots
public class AntiAFK : ChatBot
{
public static Configs Config = new();
[TomlDoNotInlineObject]
public class Configs
{
[NonSerialized]
private const string BotName = "AntiAFK";
public bool Enabled = false;
[TomlInlineComment("$config.ChatBot.AntiAfk.Delay$")]
public Range Delay = new(600);
[TomlInlineComment("$config.ChatBot.AntiAfk.Command$")]
public string Command = "/ping";
[TomlInlineComment("$config.ChatBot.AntiAfk.Use_Terrain_Handling$")]
public bool Use_Terrain_Handling = false;
[TomlInlineComment("$config.ChatBot.AntiAfk.Walk_Range$")]
public int Walk_Range = 5;
[TomlInlineComment("$config.ChatBot.AntiAfk.Walk_Retries$")]
public int Walk_Retries = 20;
public void OnSettingUpdate()
{
if (Walk_Range <= 0)
{
Walk_Range = 5;
LogToConsole(BotName, Translations.TryGet("bot.antiafk.invalid_walk_range"));
}
if (Delay.min > Delay.max)
{
(Delay.min, Delay.max) = (Delay.max, Delay.min);
LogToConsole(BotName, Translations.TryGet("bot.antiafk.swapping"));
}
}
public struct Range
{
public int min, max;
public Range(int value)
{
value = Math.Max(value, 10);
min = max = value;
}
public Range(int min, int max)
{
min = Math.Max(min, 10);
max = Math.Max(max, 10);
this.min = min;
this.max = max;
}
}
}
private int count;
private readonly string pingparam;
private int timeping = 600;
private int timepingMax = -1;
private bool useTerrainHandling = false;
private bool previousSneakState = false;
private int walkRange = 5;
private readonly int walkRetries = 10;
private readonly Random random = new();
/// <summary>
/// This bot sends a /ping command every X seconds in order to stay non-afk.
/// </summary>
/// <param name="pingparam">Time amount between each ping (10 = 1s, 600 = 1 minute, etc.) Can be a range of numbers eg. 10-600</param>
public AntiAFK(string pingparam, bool useTerrainHandling, int walkRange, int walkRetries)
public AntiAFK()
{
count = 0;
this.pingparam = pingparam;
this.useTerrainHandling = useTerrainHandling;
this.walkRange = walkRange;
this.walkRetries = walkRetries;
}
public override void Initialize()
{
if (useTerrainHandling)
if (Config.Use_Terrain_Handling)
{
if (!GetTerrainEnabled())
{
useTerrainHandling = false;
LogToConsole(Translations.TryGet("bot.antiafk.not_using_terrain_handling"));
}
else
{
if (walkRange <= 0)
{
walkRange = 5;
LogToConsole(Translations.TryGet("bot.antiafk.invalid_walk_range"));
}
}
}
if (string.IsNullOrEmpty(pingparam))
LogToConsole(Translations.TryGet("bot.antiafk.invalid_time"));
else
{
// Handle the random range
if (pingparam.Contains('-'))
{
string[] parts = pingparam.Split("-");
if (parts.Length == 2)
{
if (int.TryParse(parts[0].Trim(), NumberStyles.Any, CultureInfo.CurrentCulture, out int firstTime))
{
timeping = firstTime;
if (int.TryParse(parts[1].Trim(), NumberStyles.Any, CultureInfo.CurrentCulture, out int secondTime))
timepingMax = secondTime;
else LogToConsole(Translations.TryGet("bot.antiafk.invalid_range_partial", timeping));
}
else LogToConsole(Translations.TryGet("bot.antiafk.invalid_range"));
}
else LogToConsole(Translations.TryGet("bot.antiafk.invalid_range"));
}
else
{
if (int.TryParse(pingparam.Trim(), NumberStyles.Any, CultureInfo.CurrentCulture, out int value))
timeping = value;
else LogToConsole(Translations.TryGet("bot.antiafk.invalid_value"));
}
}
if (timepingMax != -1 && timeping > timepingMax)
{
(timeping, timepingMax) = (timepingMax, timeping);
LogToConsole(Translations.TryGet("bot.antiafk.swapping"));
}
if (timeping < 10) { timeping = 10; } //To avoid flooding
}
public override void Update()
{
count++;
if ((timepingMax != -1 && count == random.Next(timeping, timepingMax)) || count == timeping)
if (count == random.Next(Config.Delay.min, Config.Delay.max))
{
DoAntiAfkStuff();
count = 0;
@ -107,7 +107,7 @@ namespace MinecraftClient.ChatBots
private void DoAntiAfkStuff()
{
if (useTerrainHandling)
if (Config.Use_Terrain_Handling && GetTerrainEnabled())
{
Location currentLocation = GetCurrentLocation();
Location goal;
@ -118,19 +118,19 @@ namespace MinecraftClient.ChatBots
while (!moved)
{
if (triesCounter++ >= walkRetries)
if (triesCounter++ >= Config.Walk_Retries)
{
useAlternativeMethod = true;
break;
}
goal = GetRandomLocationWithinRangeXZ(currentLocation, walkRange);
goal = GetRandomLocationWithinRangeXZ(currentLocation, Config.Walk_Range);
// Prevent getting the same location
while ((currentLocation.X == goal.X) && (currentLocation.Y == goal.Y) && (currentLocation.Z == goal.Z))
{
LogToConsole("Same location!, generating new one");
goal = GetRandomLocationWithinRangeXZ(currentLocation, walkRange);
goal = GetRandomLocationWithinRangeXZ(currentLocation, Config.Walk_Range);
}
if (!Movement.CheckChunkLoading(GetWorld(), currentLocation, goal))
@ -151,7 +151,7 @@ namespace MinecraftClient.ChatBots
}
}
SendText(Settings.AntiAFK_Command);
SendText(Config.Command);
Sneak(previousSneakState);
previousSneakState = !previousSneakState;
count = 0;

View file

@ -2,14 +2,89 @@
using System.Collections.Generic;
using System.IO;
using MinecraftClient.Mapping;
using Tomlet.Attributes;
namespace MinecraftClient.ChatBots
{
/// <summary>
/// The AutoAttack bot will automatically attack any hostile mob close to the player
/// </summary>
class AutoAttack : ChatBot
public class AutoAttack : ChatBot
{
public static Configs Config = new();
[TomlDoNotInlineObject]
public class Configs
{
[NonSerialized]
private const string BotName = "AutoAttack";
public bool Enabled = false;
[TomlInlineComment("$config.ChatBot.AutoAttack.Mode$")]
public AttackMode Mode = AttackMode.single;
[TomlInlineComment("$config.ChatBot.AutoAttack.Priority$")]
public PriorityType Priority = PriorityType.distance;
[TomlInlineComment("$config.ChatBot.AutoAttack.Cooldown_Time$")]
public CooldownConfig Cooldown_Time = new(false, 1.0);
[TomlInlineComment("$config.ChatBot.AutoAttack.Interaction$")]
public InteractType Interaction = InteractType.Attack;
[TomlInlineComment("$config.ChatBot.AutoAttack.Attack_Hostile$")]
public bool Attack_Hostile = true;
[TomlInlineComment("$config.ChatBot.AutoAttack.Attack_Passive$")]
public bool Attack_Passive = false;
[TomlInlineComment("$config.ChatBot.AutoAttack.List_Mode$")]
public ListType List_Mode = ListType.blacklist;
[TomlInlineComment("$config.ChatBot.AutoAttack.Entites_List$")]
public List<EntityType> Entites_List = new() { EntityType.Zombie, EntityType.Cow };
public void OnSettingUpdate()
{
if (Cooldown_Time.Custom && Cooldown_Time.value <= 0)
{
LogToConsole(BotName, Translations.TryGet("bot.autoAttack.invalidcooldown"));
Cooldown_Time.value = 1.0;
}
}
public enum AttackMode { single, multi };
public enum PriorityType { distance, health };
public enum ListType { blacklist, whitelist };
public struct CooldownConfig
{
public bool Custom;
public double value;
public CooldownConfig()
{
Custom = false;
value = 0;
}
public CooldownConfig(double value)
{
Custom = true;
this.value = value;
}
public CooldownConfig(bool Override, double value)
{
this.Custom = Override;
this.value = value;
}
}
}
private readonly Dictionary<int, Entity> entitiesToAttack = new(); // mobs within attack range
private int attackCooldown = 6;
private int attackCooldownCounter = 6;
@ -19,73 +94,20 @@ namespace MinecraftClient.ChatBots
private readonly int attackRange = 4;
private Double serverTPS;
private float health = 100;
private readonly bool singleMode = true;
private readonly bool priorityDistance = true;
private readonly InteractType interactMode;
private readonly bool attackHostile = true;
private readonly bool attackPassive = false;
private readonly string listMode = "blacklist";
private readonly List<EntityType> listedEntites = new();
public AutoAttack(
string mode, string priority, bool overrideAttackSpeed = false, double cooldownSeconds = 1, InteractType interaction = InteractType.Attack)
public AutoAttack()
{
if (mode == "single")
singleMode = true;
else if (mode == "multi")
singleMode = false;
else LogToConsoleTranslated("bot.autoAttack.mode", mode);
if (priority == "distance")
priorityDistance = true;
else if (priority == "health")
priorityDistance = false;
else LogToConsoleTranslated("bot.autoAttack.priority", priority);
interactMode = interaction;
if (overrideAttackSpeed)
overrideAttackSpeed = Config.Cooldown_Time.Custom;
if (Config.Cooldown_Time.Custom)
{
if (cooldownSeconds <= 0)
{
LogToConsoleTranslated("bot.autoAttack.invalidcooldown");
}
else
{
this.overrideAttackSpeed = overrideAttackSpeed;
attackCooldownSeconds = cooldownSeconds;
attackCooldown = Convert.ToInt32(Math.Truncate(attackCooldownSeconds / 0.1) + 1);
}
attackCooldownSeconds = Config.Cooldown_Time.value;
attackCooldown = Convert.ToInt32(Math.Truncate(attackCooldownSeconds / 0.1) + 1);
}
attackHostile = Settings.AutoAttack_Attack_Hostile;
attackPassive = Settings.AutoAttack_Attack_Passive;
if (Settings.AutoAttack_ListMode.Length > 0)
{
listMode = Settings.AutoAttack_ListMode.ToLower();
if (!(listMode.Equals("whitelist", StringComparison.OrdinalIgnoreCase) || listMode.Equals("blacklist", StringComparison.OrdinalIgnoreCase)))
{
LogToConsole(Translations.TryGet("bot.autoAttack.invalidlist"));
listMode = "blacklist";
}
}
else LogToConsole(Translations.TryGet("bot.autoAttack.invalidlist"));
if (File.Exists(Settings.AutoAttack_ListFile))
{
string[] entityList = LoadDistinctEntriesFromFile(Settings.AutoAttack_ListFile);
if (entityList.Length > 0)
{
foreach (var item in entityList)
{
if (Enum.TryParse(item, true, out EntityType resultingType))
listedEntites.Add(resultingType);
}
}
}
attackHostile = Config.Attack_Hostile;
attackPassive = Config.Attack_Passive;
}
public override void Initialize()
@ -108,10 +130,10 @@ namespace MinecraftClient.ChatBots
attackCooldownCounter = attackCooldown;
if (entitiesToAttack.Count > 0)
{
if (singleMode)
if (Config.Mode == Configs.AttackMode.single)
{
int priorityEntity = 0;
if (priorityDistance) // closest distance priority
if (Config.Priority == Configs.PriorityType.distance) // closest distance priority
{
double distance = 5;
foreach (var entity in entitiesToAttack)
@ -142,7 +164,7 @@ namespace MinecraftClient.ChatBots
// check entity distance and health again
if (ShouldAttackEntity(entitiesToAttack[priorityEntity]))
{
InteractEntity(priorityEntity, interactMode); // hit the entity!
InteractEntity(priorityEntity, Config.Interaction); // hit the entity!
SendAnimation(Inventory.Hand.MainHand); // Arm animation
}
}
@ -154,7 +176,7 @@ namespace MinecraftClient.ChatBots
// check that we are in range once again.
if (ShouldAttackEntity(entity.Value))
{
InteractEntity(entity.Key, interactMode); // hit the entity!
InteractEntity(entity.Key, Config.Interaction); // hit the entity!
}
}
SendAnimation(Inventory.Hand.MainHand); // Arm animation
@ -206,10 +228,13 @@ namespace MinecraftClient.ChatBots
if (attackPassive && entity.Type.IsPassive())
result = true;
if (listedEntites.Count > 0)
if (Config.Entites_List.Count > 0)
{
bool inList = listedEntites.Contains(entity.Type);
result = listMode.Equals("blacklist") ? (!inList && result) : (inList);
bool inList = Config.Entites_List.Contains(entity.Type);
if (Config.List_Mode == Configs.ListType.blacklist)
result = !inList && result;
else
result = inList;
}
return result;

View file

@ -4,11 +4,27 @@ using System.IO;
using System.Linq;
using MinecraftClient.Inventory;
using MinecraftClient.Mapping;
using Tomlet.Attributes;
namespace MinecraftClient.ChatBots
{
class AutoCraft : ChatBot
public class AutoCraft : ChatBot
{
public static Configs Config = new();
[TomlDoNotInlineObject]
public class Configs
{
[NonSerialized]
private const string BotName = "AutoCraft";
public bool Enabled = false;
public string configFile = @"autocraft\config.ini";
public void OnSettingUpdate() { }
}
private bool waitingForMaterials = false;
private bool waitingForUpdate = false;
private bool waitingForTable = false;
@ -26,7 +42,6 @@ namespace MinecraftClient.ChatBots
private int updateTimeout = 0;
private string timeoutAction = "unspecified";
private readonly string configPath = @"autocraft\config.ini";
private string lastRecipe = ""; // Used in parsing recipe config
private readonly Dictionary<string, Recipe> recipes = new();
@ -158,11 +173,6 @@ namespace MinecraftClient.ChatBots
}
}
public AutoCraft(string configPath = @"autocraft\config.ini")
{
this.configPath = configPath;
}
public override void Initialize()
{
if (!GetInventoryEnabled())
@ -247,9 +257,9 @@ namespace MinecraftClient.ChatBots
public void LoadConfig()
{
if (!File.Exists(configPath))
if (!File.Exists(Config.configFile))
{
if (!Directory.Exists(configPath))
if (!Directory.Exists(Config.configFile))
{
Directory.CreateDirectory(@"autocraft");
}
@ -289,19 +299,19 @@ namespace MinecraftClient.ChatBots
"# For the naming of the items, please see",
"# https://github.com/MCCTeam/Minecraft-Console-Client/blob/master/MinecraftClient/Inventory/ItemType.cs"
};
File.WriteAllLines(configPath, content);
File.WriteAllLines(Config.configFile, content);
}
private void ParseConfig()
{
string[] content = File.ReadAllLines(configPath);
string[] content = File.ReadAllLines(Config.configFile);
if (content.Length <= 0)
{
throw new Exception(Translations.Get("bot.autoCraft.exception.empty", configPath));
throw new Exception(Translations.Get("bot.autoCraft.exception.empty", Config.configFile));
}
if (content[0].ToLower() != "[autocraft]")
{
throw new Exception(Translations.Get("bot.autoCraft.exception.invalid", configPath));
throw new Exception(Translations.Get("bot.autoCraft.exception.invalid", Config.configFile));
}
// local variable for use in parsing config

View file

@ -2,51 +2,43 @@
using System.Collections.Generic;
using System.Linq;
using MinecraftClient.Inventory;
using Tomlet.Attributes;
using static MinecraftClient.ChatBots.AutoDrop.Configs;
namespace MinecraftClient.ChatBots
{
class AutoDrop : ChatBot
public class AutoDrop : ChatBot
{
private enum Mode
public static Configs Config = new();
[TomlDoNotInlineObject]
public class Configs
{
Include, // Items in list will be dropped
Exclude, // Items in list will be kept
Everything // Everything will be dropped
[NonSerialized]
private const string BotName = "AutoDrop";
public bool Enabled = false;
[TomlInlineComment("$config.ChatBot.AutoDrop.Mode$")]
public DropMode Mode = DropMode.include;
[TomlInlineComment("$config.ChatBot.AutoDrop.Items$")]
public List<ItemType> Items = new() { ItemType.Cobblestone, ItemType.Dirt };
public void OnSettingUpdate() { }
public enum DropMode
{
include, // Items in list will be dropped
exclude, // Items in list will be kept
everything // Everything will be dropped
}
}
private Mode dropMode = Mode.Include;
private bool enable = true;
private int updateDebounce = 0;
private readonly int updateDebounceValue = 2;
private int inventoryUpdated = -1;
private readonly List<ItemType> itemList = new();
public AutoDrop(string mode, string itemList)
{
if (!Enum.TryParse(mode, true, out dropMode))
{
LogToConsoleTranslated("bot.autoDrop.no_mode");
}
if (dropMode != Mode.Everything)
this.itemList = ItemListParser(itemList).ToList();
}
/// <summary>
/// Convert an item type string to item type array
/// </summary>
/// <param name="itemList">String to convert</param>
/// <returns>Item type array</returns>
private ItemType[] ItemListParser(string itemList)
{
string trimed = new(itemList.Where(c => !char.IsWhiteSpace(c)).ToArray());
List<ItemType> result = new();
foreach (string t in trimed.Split(','))
if (Enum.TryParse(t, true, out ItemType item))
result.Add(item);
return result.ToArray();
}
public string CommandHandler(string cmd, string[] args)
{
if (args.Length > 0)
@ -54,19 +46,19 @@ namespace MinecraftClient.ChatBots
switch (args[0].ToLower())
{
case "on":
enable = true;
Config.Enabled = true;
inventoryUpdated = 0;
OnUpdateFinish();
return Translations.Get("bot.autoDrop.on");
case "off":
enable = false;
Config.Enabled = false;
return Translations.Get("bot.autoDrop.off");
case "add":
if (args.Length >= 2)
{
if (Enum.TryParse(args[1], true, out ItemType item))
{
itemList.Add(item);
Config.Items.Add(item);
return Translations.Get("bot.autoDrop.added", item.ToString());
}
else
@ -83,9 +75,9 @@ namespace MinecraftClient.ChatBots
{
if (Enum.TryParse(args[1], true, out ItemType item))
{
if (itemList.Contains(item))
if (Config.Items.Contains(item))
{
itemList.Remove(item);
Config.Items.Remove(item);
return Translations.Get("bot.autoDrop.removed", item.ToString());
}
else
@ -103,9 +95,9 @@ namespace MinecraftClient.ChatBots
return Translations.Get("cmd.inventory.help.usage") + ": remove <item name>";
}
case "list":
if (itemList.Count > 0)
if (Config.Items.Count > 0)
{
return Translations.Get("bot.autoDrop.list", itemList.Count, string.Join("\n", itemList));
return Translations.Get("bot.autoDrop.list", Config.Items.Count, string.Join("\n", Config.Items));
}
else
{
@ -117,20 +109,20 @@ namespace MinecraftClient.ChatBots
switch (args[1].ToLower())
{
case "include":
dropMode = Mode.Include;
Config.Mode = DropMode.include;
break;
case "exclude":
dropMode = Mode.Exclude;
Config.Mode = DropMode.exclude;
break;
case "everything":
dropMode = Mode.Everything;
Config.Mode = DropMode.everything;
break;
default:
return Translations.Get("bot.autoDrop.unknown_mode"); // Unknwon mode. Available modes: Include, Exclude, Everything
}
inventoryUpdated = 0;
OnUpdateFinish();
return Translations.Get("bot.autoDrop.switched", dropMode.ToString()); // Switched to {0} mode.
return Translations.Get("bot.autoDrop.switched", Config.Mode.ToString()); // Switched to {0} mode.
}
else
{
@ -178,7 +170,7 @@ namespace MinecraftClient.ChatBots
public override void OnInventoryUpdate(int inventoryId)
{
if (enable)
if (Config.Enabled)
{
updateDebounce = updateDebounceValue;
// Always interact container if available (larger ID) because they included player inventory (ID 0)
@ -199,28 +191,28 @@ namespace MinecraftClient.ChatBots
}
var inventory = GetInventories()[inventoryUpdated];
var items = inventory.Items.ToDictionary(entry => entry.Key, entry => entry.Value);
if (dropMode == Mode.Include)
if (Config.Mode == DropMode.include)
{
foreach (var item in items)
{
// Ingore crafting result slot
if (item.Key == 0)
continue;
if (itemList.Contains(item.Value.Type))
if (Config.Items.Contains(item.Value.Type))
{
// Drop it !!
WindowAction(inventoryUpdated, item.Key, WindowActionType.DropItemStack);
}
}
}
else if (dropMode == Mode.Exclude)
else if (Config.Mode == DropMode.exclude)
{
foreach (var item in items)
{
// Ingore crafting result slot
if (item.Key == 0)
continue;
if (!itemList.Contains(item.Value.Type))
if (!Config.Items.Contains(item.Value.Type))
{
// Drop it !!
WindowAction(inventoryUpdated, item.Key, WindowActionType.DropItemStack);

View file

@ -1,19 +1,36 @@
using MinecraftClient.Inventory;
using System;
using MinecraftClient.Inventory;
using Tomlet.Attributes;
namespace MinecraftClient.ChatBots
{
class AutoEat : ChatBot
public class AutoEat : ChatBot
{
public static Configs Config = new();
[TomlDoNotInlineObject]
public class Configs
{
[NonSerialized]
private const string BotName = "AutoEat";
public bool Enabled = false;
public int Threshold = 6;
public void OnSettingUpdate()
{
if (Threshold > 20)
Threshold = 20;
else if (Threshold < 0)
Threshold = 0;
}
}
byte LastSlot = 0;
public static bool Eating = false;
private readonly int HungerThreshold = 6;
private int DelayCounter = 0;
public AutoEat(int Threshold)
{
HungerThreshold = Threshold;
}
public override void Update()
{
if (DelayCounter > 0)
@ -31,7 +48,7 @@ namespace MinecraftClient.ChatBots
public override void OnHealthUpdate(float health, int food)
{
if (health <= 0) return; // player dead
if (((food <= HungerThreshold) || (food < 20 && health < 20)) && !Eating)
if (((food <= Config.Threshold) || (food < 20 && health < 20)) && !Eating)
{
Eating = FindFoodAndEat();
if (!Eating)

View file

@ -1,6 +1,9 @@
using System;
using MinecraftClient.Inventory;
using MinecraftClient.Mapping;
using Tomlet.Attributes;
using static MinecraftClient.ChatBots.AutoFishing.Configs;
using static MinecraftClient.ChatBots.AutoFishing.Configs.LocationConfig;
namespace MinecraftClient.ChatBots
{
@ -8,8 +11,122 @@ namespace MinecraftClient.ChatBots
/// The AutoFishing bot semi-automates fishing.
/// The player needs to have a fishing rod in hand, then manually send it using the UseItem command.
/// </summary>
class AutoFishing : ChatBot
public class AutoFishing : ChatBot
{
public static Configs Config = new();
[TomlDoNotInlineObject]
public class Configs
{
[NonSerialized]
private const string BotName = "AutoFishing";
public bool Enabled = false;
[TomlInlineComment("$config.ChatBot.AutoFishing.Antidespawn$")]
public bool Antidespawn = false;
[TomlInlineComment("$config.ChatBot.AutoFishing.Mainhand$")]
public bool Mainhand = true;
[TomlInlineComment("$config.ChatBot.AutoFishing.Auto_Start$")]
public bool Auto_Start = true;
[TomlInlineComment("$config.ChatBot.AutoFishing.Cast_Delay$")]
public double Cast_Delay = 0.4;
[TomlInlineComment("$config.ChatBot.AutoFishing.Fishing_Delay$")]
public double Fishing_Delay = 3.0;
[TomlInlineComment("$config.ChatBot.AutoFishing.Fishing_Timeout$")]
public double Fishing_Timeout = 300.0;
[TomlInlineComment("$config.ChatBot.AutoFishing.Durability_Limit$")]
public double Durability_Limit = 2;
[TomlInlineComment("$config.ChatBot.AutoFishing.Auto_Rod_Switch$")]
public bool Auto_Rod_Switch = true;
[TomlInlineComment("$config.ChatBot.AutoFishing.Stationary_Threshold$")]
public double Stationary_Threshold = 0.001;
[TomlInlineComment("$config.ChatBot.AutoFishing.Hook_Threshold$")]
public double Hook_Threshold = 0.2;
[TomlInlineComment("$config.ChatBot.AutoFishing.Log_Fish_Bobber$")]
public bool Log_Fish_Bobber = false;
[TomlInlineComment("$config.ChatBot.AutoFishing.Movements$")]
public LocationConfig[] Movements = Array.Empty<LocationConfig>();
public void OnSettingUpdate()
{
if (Cast_Delay < 0)
Cast_Delay = 0;
if (Fishing_Delay < 0)
Fishing_Delay = 0;
if (Fishing_Timeout < 0)
Fishing_Timeout = 0;
if (Durability_Limit < 0)
Durability_Limit = 0;
else if (Durability_Limit > 64)
Durability_Limit = 64;
if (Stationary_Threshold < 0)
Stationary_Threshold = -Stationary_Threshold;
if (Hook_Threshold < 0)
Hook_Threshold = -Hook_Threshold;
}
public struct LocationConfig
{
public Coordination? XYZ;
public Facing? facing;
public LocationConfig(float yaw, float pitch)
{
this.XYZ = null;
this.facing = new(yaw, pitch);
}
public LocationConfig(double x, double y, double z)
{
this.XYZ = new(x, y, z);
this.facing = null;
}
public LocationConfig(double x, double y, double z, float yaw, float pitch)
{
this.XYZ = new(x, y, z);
this.facing = new(yaw, pitch);
}
public struct Coordination
{
public double x, y, z;
public Coordination(double x, double y, double z)
{
this.x = x; this.y = y; this.z = z;
}
}
public struct Facing
{
public float yaw, pitch;
public Facing(float yaw, float pitch)
{
this.yaw = yaw; this.pitch = pitch;
}
}
}
}
private int fishCount = 0;
private bool inventoryEnabled;
private int castTimeout = 12;
@ -54,9 +171,9 @@ namespace MinecraftClient.ChatBots
private void StartFishing()
{
isFishing = false;
if (Settings.AutoFishing_AutoStart)
if (Config.Auto_Start)
{
double delay = Settings.AutoFishing_FishingDelay;
double delay = Config.Fishing_Delay;
LogToConsole(Translations.Get("bot.autoFish.start", delay));
lock (stateLock)
{
@ -84,7 +201,7 @@ namespace MinecraftClient.ChatBots
private void UseFishRod()
{
if (Settings.AutoFishing_Mainhand)
if (Config.Mainhand)
UseItemInHand();
else
UseItemInLeftHand();
@ -100,7 +217,7 @@ namespace MinecraftClient.ChatBots
break;
case FishingState.WaitingToCast:
if (AutoEat.Eating)
counter = (int)(Settings.AutoFishing_CastDelay * 10);
counter = (int)(Config.Cast_Delay * 10);
else if (--counter < 0)
state = FishingState.CastingRod;
break;
@ -116,28 +233,27 @@ namespace MinecraftClient.ChatBots
castTimeout *= 2; // Exponential backoff
LogToConsole(GetTimestamp() + ": " + Translations.Get("bot.autoFish.cast_timeout", castTimeout / 10.0));
counter = (int)(Settings.AutoFishing_CastDelay * 10);
counter = (int)(Config.Cast_Delay * 10);
state = FishingState.WaitingToCast;
}
break;
case FishingState.WaitingFishToBite:
if (++counter > (int)(Settings.AutoFishing_FishingTimeout * 10))
if (++counter > (int)(Config.Fishing_Timeout * 10))
{
LogToConsole(GetTimestamp() + ": " + Translations.Get("bot.autoFish.fishing_timeout"));
counter = (int)(Settings.AutoFishing_CastDelay * 10);
counter = (int)(Config.Cast_Delay * 10);
state = FishingState.WaitingToCast;
}
break;
case FishingState.StartMove:
if (--counter < 0)
{
double[,]? locationList = Settings.AutoFishing_Location;
if (locationList != null)
if (Config.Movements.Length > 0)
{
if (GetTerrainEnabled())
{
UpdateLocation(locationList);
UpdateLocation(Config.Movements);
state = FishingState.WaitingMovement;
}
else
@ -148,7 +264,7 @@ namespace MinecraftClient.ChatBots
}
else
{
counter = (int)(Settings.AutoFishing_CastDelay * 10);
counter = (int)(Config.Cast_Delay * 10);
state = FishingState.DurabilityCheck;
goto case FishingState.DurabilityCheck;
}
@ -167,7 +283,7 @@ namespace MinecraftClient.ChatBots
case FishingState.DurabilityCheck:
if (DurabilityCheck())
{
counter = (int)(Settings.AutoFishing_CastDelay * 10);
counter = (int)(Config.Cast_Delay * 10);
state = FishingState.WaitingToCast;
}
break;
@ -181,7 +297,7 @@ namespace MinecraftClient.ChatBots
{
if (entity.Type == EntityType.FishingBobber && entity.ObjectData == GetPlayerEntityID())
{
if (Settings.AutoFishing_LogFishingBobber)
if (Config.Log_Fish_Bobber)
LogToConsole(string.Format("FishingBobber spawn at {0}, distance = {1:0.00}", entity.Location, GetCurrentLocation().Distance(entity.Location)));
LogToConsole(GetTimestamp() + ": " + Translations.Get("bot.autoFish.throw"));
@ -202,20 +318,20 @@ namespace MinecraftClient.ChatBots
{
if (entity != null && entity.Type == EntityType.FishingBobber && entity.ID == fishingBobber!.ID)
{
if (Settings.AutoFishing_LogFishingBobber)
if (Config.Log_Fish_Bobber)
LogToConsole(string.Format("FishingBobber despawn at {0}", entity.Location));
if (isFishing)
{
isFishing = false;
if (Settings.AutoFishing_Antidespawn)
if (Config.Antidespawn)
{
LogToConsoleTranslated("bot.autoFish.despawn");
lock (stateLock)
{
counter = (int)(Settings.AutoFishing_CastDelay * 10);
counter = (int)(Config.Cast_Delay * 10);
state = FishingState.WaitingToCast;
}
}
@ -233,12 +349,12 @@ namespace MinecraftClient.ChatBots
double Dz = LastPos.Z - Pos.Z;
LastPos = Pos;
if (Settings.AutoFishing_LogFishingBobber)
if (Config.Log_Fish_Bobber)
LogToConsole(string.Format("FishingBobber {0} Dx={1:0.000000} Dy={2:0.000000} Dz={3:0.000000}", Pos, Dx, Math.Abs(Dy), Dz));
if (Math.Abs(Dx) < Math.Abs(Settings.AutoFishing_StationaryThreshold) &&
Math.Abs(Dz) < Math.Abs(Settings.AutoFishing_StationaryThreshold) &&
Math.Abs(Dy) > Math.Abs(Settings.AutoFishing_HookThreshold))
if (Math.Abs(Dx) < Math.Abs(Config.Stationary_Threshold) &&
Math.Abs(Dz) < Math.Abs(Config.Stationary_Threshold) &&
Math.Abs(Dy) > Math.Abs(Config.Hook_Threshold))
{
// prevent triggering multiple time
if ((DateTime.Now - CaughtTime).TotalSeconds > 1)
@ -283,7 +399,7 @@ namespace MinecraftClient.ChatBots
public void OnCaughtFish()
{
++fishCount;
if (Settings.AutoFishing_Location != null)
if (Config.Movements.Length > 0)
LogToConsole(GetTimestamp() + ": " + Translations.Get("bot.autoFish.caught_at",
fishingBobber!.Location.X, fishingBobber!.Location.Y, fishingBobber!.Location.Z, fishCount));
else
@ -298,47 +414,36 @@ namespace MinecraftClient.ChatBots
}
}
private void UpdateLocation(double[,] locationList)
private void UpdateLocation(LocationConfig[] locationList)
{
if (curLocationIdx >= locationList.GetLength(0))
if (curLocationIdx >= locationList.Length)
{
curLocationIdx = Math.Max(0, locationList.GetLength(0) - 2);
curLocationIdx = Math.Max(0, locationList.Length - 2);
moveDir = -1;
}
else if (curLocationIdx < 0)
{
curLocationIdx = Math.Min(locationList.GetLength(0) - 1, 1);
curLocationIdx = Math.Min(locationList.Length - 1, 1);
moveDir = 1;
}
int locationType = locationList.GetLength(1);
LocationConfig curConfig = locationList[curLocationIdx];
if (locationType == 2)
{
nextYaw = (float)locationList[curLocationIdx, 0];
nextPitch = (float)locationList[curLocationIdx, 1];
}
else if (locationType == 3)
{
nextYaw = GetYaw();
nextPitch = GetPitch();
}
else if (locationType == 5)
{
nextYaw = (float)locationList[curLocationIdx, 3];
nextPitch = (float)locationList[curLocationIdx, 4];
}
if (curConfig.facing != null)
(nextYaw, nextPitch) = (curConfig.facing.Value.yaw, curConfig.facing.Value.pitch);
else
(nextYaw, nextPitch) = (GetYaw(), GetPitch());
if (locationType == 3 || locationType == 5)
if (curConfig.XYZ != null)
{
Location current = GetCurrentLocation();
Location goal = new(locationList[curLocationIdx, 0], locationList[curLocationIdx, 1], locationList[curLocationIdx, 2]);
Location goal = new(curConfig.XYZ.Value.x, curConfig.XYZ.Value.y, curConfig.XYZ.Value.z);
bool isMoveSuccessed;
if (!Movement.CheckChunkLoading(GetWorld(), current, goal))
{
LogToConsole(Translations.Get("cmd.move.chunk_not_loaded", goal.X, goal.Y, goal.Z));
isMoveSuccessed = false;
LogToConsole(Translations.Get("cmd.move.chunk_not_loaded", goal.X, goal.Y, goal.Z));
}
else
{
@ -347,8 +452,7 @@ namespace MinecraftClient.ChatBots
if (!isMoveSuccessed)
{
nextYaw = GetYaw();
nextPitch = GetPitch();
(nextYaw, nextPitch) = (GetYaw(), GetPitch());
LogToConsole(Translations.Get("cmd.move.fail", goal));
}
else
@ -365,13 +469,13 @@ namespace MinecraftClient.ChatBots
if (!inventoryEnabled)
return true;
bool useMainHand = Settings.AutoFishing_Mainhand;
bool useMainHand = Config.Mainhand;
Container container = GetPlayerInventory();
int itemSolt = useMainHand ? GetCurrentSlot() + 36 : 45;
if (container.Items.TryGetValue(itemSolt, out Item? handItem) &&
handItem.Type == ItemType.FishingRod && (64 - handItem.Damage) >= Settings.AutoFishing_DurabilityLimit)
handItem.Type == ItemType.FishingRod && (64 - handItem.Damage) >= Config.Durability_Limit)
{
isWaitingRod = false;
return true;
@ -381,11 +485,11 @@ namespace MinecraftClient.ChatBots
if (!isWaitingRod)
LogToConsole(GetTimestamp() + ": " + Translations.Get("bot.autoFish.no_rod"));
if (Settings.AutoFishing_AutoRodSwitch)
if (Config.Auto_Rod_Switch)
{
foreach ((int slot, Item item) in container.Items)
{
if (item.Type == ItemType.FishingRod && (64 - item.Damage) >= Settings.AutoFishing_DurabilityLimit)
if (item.Type == ItemType.FishingRod && (64 - item.Damage) >= Config.Durability_Limit)
{
WindowAction(0, slot, WindowActionType.LeftClick);
WindowAction(0, itemSolt, WindowActionType.LeftClick);

View file

@ -1,5 +1,6 @@
using System;
using System.Text;
using Tomlet.Attributes;
namespace MinecraftClient.ChatBots
{
@ -8,11 +9,59 @@ namespace MinecraftClient.ChatBots
/// </summary>
public class AutoRelog : ChatBot
{
public static Configs Config = new();
[TomlDoNotInlineObject]
public class Configs
{
[NonSerialized]
private const string BotName = "AutoRelog";
public bool Enabled = false;
[TomlInlineComment("$config.ChatBot.AutoRelog.Delay$")]
public Range Delay = new(10);
[TomlInlineComment("$config.ChatBot.AutoRelog.Retries$")]
public int Retries = 3;
[TomlInlineComment("$config.ChatBot.AutoRelog.Ignore_Kick_Message$")]
public bool Ignore_Kick_Message = false;
[TomlInlineComment("$config.ChatBot.AutoRelog.Kick_Messages_File$")]
public string Kick_Messages_File = @"kickmessages.txt";
public void OnSettingUpdate()
{
if (Delay.min > Delay.max)
(Delay.min, Delay.max) = (Delay.max, Delay.min);
if (Retries == -1)
Retries = int.MaxValue;
}
public struct Range
{
public int min, max;
public Range(int value)
{
value = Math.Max(value, 1);
min = max = value;
}
public Range(int min, int max)
{
min = Math.Max(min, 1);
max = Math.Max(max, 1);
this.min = min;
this.max = max;
}
}
}
private static readonly Random random = new();
private string[] dictionary = Array.Empty<string>();
private readonly int attempts;
private readonly int delayMin;
private readonly int delayMax;
/// <summary>
/// This bot automatically re-join the server if kick message contains predefined string
@ -20,34 +69,25 @@ namespace MinecraftClient.ChatBots
/// <param name="DelayBeforeRelogMin">Minimum delay before re-joining the server (in seconds)</param>
/// <param name="DelayBeforeRelogMax">Maximum delay before re-joining the server (in seconds)</param>
/// <param name="retries">Number of retries if connection fails (-1 = infinite)</param>
public AutoRelog(int DelayBeforeRelogMin, int DelayBeforeRelogMax, int retries)
public AutoRelog()
{
attempts = retries;
if (attempts == -1) { attempts = int.MaxValue; }
McClient.ReconnectionAttemptsLeft = attempts;
delayMin = DelayBeforeRelogMin;
delayMax = DelayBeforeRelogMax;
if (delayMin < 1)
delayMin = 1;
if (delayMax < delayMin)
delayMax = delayMin;
LogDebugToConsoleTranslated("bot.autoRelog.launch", attempts);
LogDebugToConsoleTranslated("bot.autoRelog.launch", Config.Retries);
}
public override void Initialize()
{
McClient.ReconnectionAttemptsLeft = attempts;
if (Settings.AutoRelog_IgnoreKickMessage)
McClient.ReconnectionAttemptsLeft = Config.Retries;
if (Config.Ignore_Kick_Message)
{
LogDebugToConsoleTranslated("bot.autoRelog.no_kick_msg");
}
else
{
if (System.IO.File.Exists(Settings.AutoRelog_KickMessagesFile))
if (System.IO.File.Exists(Config.Kick_Messages_File))
{
LogDebugToConsoleTranslated("bot.autoRelog.loading", System.IO.Path.GetFullPath(Settings.AutoRelog_KickMessagesFile));
LogDebugToConsoleTranslated("bot.autoRelog.loading", System.IO.Path.GetFullPath(Config.Kick_Messages_File));
dictionary = System.IO.File.ReadAllLines(Settings.AutoRelog_KickMessagesFile, Encoding.UTF8);
dictionary = System.IO.File.ReadAllLines(Config.Kick_Messages_File, Encoding.UTF8);
for (int i = 0; i < dictionary.Length; i++)
{
@ -57,7 +97,7 @@ namespace MinecraftClient.ChatBots
}
else
{
LogToConsoleTranslated("bot.autoRelog.not_found", System.IO.Path.GetFullPath(Settings.AutoRelog_KickMessagesFile));
LogToConsoleTranslated("bot.autoRelog.not_found", System.IO.Path.GetFullPath(Config.Kick_Messages_File));
LogDebugToConsoleTranslated("bot.autoRelog.curr_dir", System.IO.Directory.GetCurrentDirectory());
}
@ -77,7 +117,7 @@ namespace MinecraftClient.ChatBots
LogDebugToConsoleTranslated("bot.autoRelog.disconnect_msg", message);
if (Settings.AutoRelog_IgnoreKickMessage)
if (Config.Ignore_Kick_Message)
{
LaunchDelayedReconnection(null);
return true;
@ -100,7 +140,7 @@ namespace MinecraftClient.ChatBots
private void LaunchDelayedReconnection(string? msg)
{
int delay = random.Next(delayMin, delayMax);
int delay = random.Next(Config.Delay.min, Config.Delay.max);
LogDebugToConsoleTranslated(String.IsNullOrEmpty(msg) ? "bot.autoRelog.reconnect_always" : "bot.autoRelog.reconnect", msg);
LogToConsoleTranslated("bot.autoRelog.wait", delay);
System.Threading.Thread.Sleep(delay * 1000);
@ -109,9 +149,9 @@ namespace MinecraftClient.ChatBots
public static bool OnDisconnectStatic(DisconnectReason reason, string message)
{
if (Settings.AutoRelog_Enabled)
if (Config.Enabled)
{
AutoRelog bot = new(Settings.AutoRelog_Delay_Min, Settings.AutoRelog_Delay_Max, Settings.AutoRelog_Retries);
AutoRelog bot = new();
bot.Initialize();
return bot.OnDisconnect(reason, message);
}

View file

@ -1,31 +1,50 @@
using System;
using System.Collections.Generic;
using System.Globalization;
using System.IO;
using System.Text;
using System.Text.RegularExpressions;
using Tomlet.Attributes;
using static MinecraftClient.Settings;
namespace MinecraftClient.ChatBots
{
/// <summary>
/// This bot automatically runs actions when a user sends a message matching a specified rule
/// </summary>
class AutoRespond : ChatBot
public class AutoRespond : ChatBot
{
private readonly string matchesFile;
private readonly bool matchColors;
public static Configs Config = new();
[TomlDoNotInlineObject]
public class Configs
{
[NonSerialized]
private const string BotName = "AutoRespond";
public bool Enabled = false;
public string Matches_File = @"matches.ini";
[TomlInlineComment("$config.ChatBot.AutoRespond.Match_Colors$")]
public bool Match_Colors = false;
public void OnSettingUpdate()
{
if (!Enabled) return;
if (!File.Exists(Matches_File))
{
LogToConsole(BotName, Translations.TryGet("bot.autoRespond.file_not_found", Path.GetFullPath(Matches_File)));
LogToConsole(BotName, Translations.TryGet("general.bot_unload"));
Enabled = false;
}
}
}
private List<RespondRule>? respondRules;
private enum MessageType { Public, Private, Other };
/// <summary>
/// Create a new AutoRespond bot
/// </summary>
/// <param name="matchesFile">INI File to load matches from</param>
public AutoRespond(string matchesFile, bool matchColors)
{
this.matchesFile = matchesFile;
this.matchColors = matchColors;
}
/// <summary>
/// Describe a respond rule based on a simple match or a regex
/// </summary>
@ -96,7 +115,7 @@ namespace MinecraftClient.ChatBots
string? toSend = null;
if (ownersOnly && (String.IsNullOrEmpty(username) || !Settings.Bots_Owners.Contains(username.ToLower())))
if (ownersOnly && (String.IsNullOrEmpty(username) || !Settings.Config.Main.Advanced.BotOwners.Contains(username.ToLower())))
return null;
switch (msgType)
@ -164,7 +183,7 @@ namespace MinecraftClient.ChatBots
/// </summary>
public override void Initialize()
{
if (File.Exists(matchesFile))
if (File.Exists(Config.Matches_File))
{
Regex? matchRegex = null;
string? matchString = null;
@ -175,9 +194,9 @@ namespace MinecraftClient.ChatBots
TimeSpan cooldown = TimeSpan.Zero;
respondRules = new List<RespondRule>();
LogDebugToConsoleTranslated("bot.autoRespond.loading", System.IO.Path.GetFullPath(matchesFile));
LogDebugToConsoleTranslated("bot.autoRespond.loading", System.IO.Path.GetFullPath(Config.Matches_File));
foreach (string lineRAW in File.ReadAllLines(matchesFile, Encoding.UTF8))
foreach (string lineRAW in File.ReadAllLines(Config.Matches_File, Encoding.UTF8))
{
string line = lineRAW.Split('#')[0].Trim();
if (line.Length > 0)
@ -211,8 +230,8 @@ namespace MinecraftClient.ChatBots
case "action": matchAction = argValue; break;
case "actionprivate": matchActionPrivate = argValue; break;
case "actionother": matchActionOther = argValue; break;
case "ownersonly": ownersOnly = Settings.str2bool(argValue); break;
case "cooldown": cooldown = TimeSpan.FromSeconds(Settings.str2int(argValue)); break;
case "ownersonly": ownersOnly = bool.Parse(argValue); break;
case "cooldown": cooldown = TimeSpan.FromSeconds(int.Parse(argValue, NumberStyles.Any, CultureInfo.CurrentCulture)); break;
}
}
}
@ -222,7 +241,7 @@ namespace MinecraftClient.ChatBots
}
else
{
LogToConsoleTranslated("bot.autoRespond.file_not_found", System.IO.Path.GetFullPath(matchesFile));
LogToConsoleTranslated("bot.autoRespond.file_not_found", System.IO.Path.GetFullPath(Config.Matches_File));
UnloadBot(); //No need to keep the bot active
}
}
@ -264,7 +283,7 @@ namespace MinecraftClient.ChatBots
public override void GetText(string text)
{
//Remove colour codes
if (!matchColors)
if (!Config.Match_Colors)
text = GetVerbatim(text);
//Get Message type
@ -277,7 +296,7 @@ namespace MinecraftClient.ChatBots
else message = text;
//Do not process messages sent by the bot itself
if (msgType == MessageType.Other || sender != Settings.Username)
if (msgType == MessageType.Other || sender != InternalConfig.Username)
{
foreach (RespondRule rule in respondRules!)
{

View file

@ -1,5 +1,7 @@
using System;
using System.IO;
using Tomlet.Attributes;
using static System.Net.WebRequestMethods;
namespace MinecraftClient.ChatBots
{
@ -9,13 +11,42 @@ namespace MinecraftClient.ChatBots
public class ChatLog : ChatBot
{
public enum MessageFilter { AllText, AllMessages, OnlyChat, OnlyWhispers, OnlyInternalCommands };
private readonly bool dateandtime;
private readonly bool saveOther = true;
private readonly bool saveChat = true;
private readonly bool savePrivate = true;
private readonly bool saveInternal = true;
private readonly string logfile;
public static Configs Config = new();
[TomlDoNotInlineObject]
public class Configs
{
[NonSerialized]
private const string BotName = "ChatLog";
public bool Enabled = false;
public bool Add_DateTime = true;
public string Log_File = @"chatlog-%username%-%serverip%.txt";
public MessageFilter Filter = MessageFilter.messages;
public void OnSettingUpdate()
{
if (!Enabled) return;
string Log_File_Full = Settings.Config.AppVar.ExpandVars(Log_File);
if (String.IsNullOrEmpty(Log_File_Full) || Log_File_Full.IndexOfAny(Path.GetInvalidPathChars()) >= 0)
{
LogToConsole(BotName, Translations.TryGet("bot.chatLog.invalid_file", Log_File_Full));
LogToConsole(BotName, Translations.TryGet("general.bot_unload"));
Enabled = false;
}
}
public enum MessageFilter { all, messages, chat, private_chat, internal_msg };
}
private bool saveOther = true;
private bool saveChat = true;
private bool savePrivate = true;
private bool saveInternal = true;
private readonly object logfileLock = new();
/// <summary>
@ -25,59 +56,42 @@ namespace MinecraftClient.ChatBots
/// <param name="filter">The kind of messages to save</param>
/// <param name="AddDateAndTime">Add a date and time before each message</param>
public ChatLog(string file, MessageFilter filter, bool AddDateAndTime)
public ChatLog()
{
UpdateFilter(Config.Filter);
}
public void UpdateFilter(Configs.MessageFilter filter)
{
dateandtime = AddDateAndTime;
logfile = file;
switch (filter)
{
case MessageFilter.AllText:
case Configs.MessageFilter.all:
saveOther = true;
savePrivate = true;
saveChat = true;
break;
case MessageFilter.AllMessages:
case Configs.MessageFilter.messages:
saveOther = false;
savePrivate = true;
saveChat = true;
break;
case MessageFilter.OnlyChat:
case Configs.MessageFilter.chat:
saveOther = false;
savePrivate = false;
saveChat = true;
break;
case MessageFilter.OnlyWhispers:
case Configs.MessageFilter.private_chat:
saveOther = false;
savePrivate = true;
saveChat = false;
break;
case MessageFilter.OnlyInternalCommands:
case Configs.MessageFilter.internal_msg:
saveOther = false;
savePrivate = false;
saveChat = false;
saveInternal = true;
break;
}
if (String.IsNullOrEmpty(file) || file.IndexOfAny(Path.GetInvalidPathChars()) >= 0)
{
LogToConsoleTranslated("bot.chatLog.invalid_file", file);
UnloadBot();
}
}
public static MessageFilter str2filter(string filtername)
{
return Settings.ToLowerIfNeed(filtername) switch
{
#pragma warning disable format // @formatter:off
"all" => MessageFilter.AllText,
"messages" => MessageFilter.AllMessages,
"chat" => MessageFilter.OnlyChat,
"private" => MessageFilter.OnlyWhispers,
"internal" => MessageFilter.OnlyInternalCommands,
_ => MessageFilter.AllText,
#pragma warning restore format // @formatter:on
};
}
public override void GetText(string text)
@ -110,14 +124,15 @@ namespace MinecraftClient.ChatBots
private void Save(string tosave)
{
if (dateandtime)
if (Config.Add_DateTime)
tosave = GetTimestamp() + ' ' + tosave;
string Log_File_Full = Settings.Config.AppVar.ExpandVars(Config.Log_File);
lock (logfileLock)
{
string? directory = Path.GetDirectoryName(logfile);
string? directory = Path.GetDirectoryName(Log_File_Full);
if (!String.IsNullOrEmpty(directory) && !Directory.Exists(directory))
Directory.CreateDirectory(directory);
FileStream stream = new(logfile, FileMode.OpenOrCreate);
FileStream stream = new(Log_File_Full, FileMode.OpenOrCreate);
StreamWriter writer = new(stream);
stream.Seek(0, SeekOrigin.End);
writer.WriteLine(tosave);

View file

@ -1,23 +1,42 @@
using System;
using System.Linq;
using MinecraftClient.Mapping;
using Tomlet.Attributes;
namespace MinecraftClient.ChatBots
{
public class FollowPlayer : ChatBot
{
public static Configs Config = new();
[TomlDoNotInlineObject]
public class Configs
{
[NonSerialized]
private const string BotName = "FollowPlayer";
public bool Enabled = false;
[TomlInlineComment("$config.ChatBot.FollowPlayer.Update_Limit$")]
public int Update_Limit = 10;
[TomlInlineComment("$config.ChatBot.FollowPlayer.Stop_At_Distance$")]
public double Stop_At_Distance = 3.0;
public void OnSettingUpdate()
{
if (Update_Limit < 0)
Update_Limit = 0;
if (Stop_At_Distance < 0)
Stop_At_Distance = 0;
}
}
private string? _playerToFollow = null;
private int _updateCounter = 0;
private readonly int _updateLimit;
private readonly int _stopAtDistance;
private bool _unsafeEnabled = false;
public FollowPlayer(int updateLimit = 15, int stopAtDistance = 3)
{
_updateLimit = updateLimit;
_stopAtDistance = stopAtDistance;
}
public override void Initialize()
{
if (!GetEntityHandlingEnabled())
@ -94,7 +113,7 @@ namespace MinecraftClient.ChatBots
public override void OnEntityMove(Entity entity)
{
if (_updateCounter < _updateLimit)
if (_updateCounter < Config.Update_Limit)
return;
_updateCounter = 0;
@ -114,7 +133,7 @@ namespace MinecraftClient.ChatBots
// Stop at specified distance from plater (prevents pushing player around)
double distance = entity.Location.Distance(GetCurrentLocation());
if (distance < _stopAtDistance)
if (distance < Config.Stop_At_Distance)
return;
MoveToLocation(entity.Location, _unsafeEnabled);

View file

@ -1,5 +1,6 @@
using System;
using System.Text;
using Tomlet.Attributes;
namespace MinecraftClient.ChatBots
{
@ -9,26 +10,33 @@ namespace MinecraftClient.ChatBots
public class HangmanGame : ChatBot
{
public static Configs Config = new();
[TomlDoNotInlineObject]
public class Configs
{
[NonSerialized]
private const string BotName = "HangmanGame";
public bool Enabled = false;
public bool English = true;
public string FileWords_EN = "hangman-en.txt";
public string FileWords_FR = "hangman-fr.txt";
public void OnSettingUpdate() { }
}
private int vie = 0;
private readonly int vie_param = 10;
private int compteur = 0;
private readonly int compteur_param = 3000; //5 minutes
private bool running = false;
private bool[] discovered;
private bool[] discovered = Array.Empty<bool>();
private string word = "";
private string letters = "";
private readonly bool English;
/// <summary>
/// Le jeu du Pendu / Hangman Game
/// </summary>
/// <param name="english">if true, the game will be in english. If false, the game will be in french.</param>
public HangmanGame(bool english)
{
English = english;
discovered = Array.Empty<bool>();
}
public override void Update()
{
@ -40,8 +48,8 @@ namespace MinecraftClient.ChatBots
}
else
{
SendText(English ? "You took too long to try a letter." : "Temps imparti écoulé !");
SendText(English ? "Game canceled." : "Partie annulée.");
SendText(Config.English ? "You took too long to try a letter." : "Temps imparti écoulé !");
SendText(Config.English ? "Game canceled." : "Partie annulée.");
running = false;
}
}
@ -55,7 +63,7 @@ namespace MinecraftClient.ChatBots
if (IsPrivateMessage(text, ref message, ref username))
{
if (Settings.Bots_Owners.Contains(username.ToLower()))
if (Settings.Config.Main.Advanced.BotOwners.Contains(username.ToLower()))
{
switch (message)
{
@ -81,7 +89,7 @@ namespace MinecraftClient.ChatBots
{
if (letters.Contains(letter))
{
SendText(English ? ("Letter " + letter + " has already been tried.") : ("Le " + letter + " a déjà été proposé."));
SendText(Config.English ? ("Letter " + letter + " has already been tried.") : ("Le " + letter + " a déjà été proposé."));
}
else
{
@ -91,29 +99,29 @@ namespace MinecraftClient.ChatBots
if (word.Contains(letter))
{
for (int i = 0; i < word.Length; i++) { if (word[i] == letter) { discovered[i] = true; } }
SendText(English ? ("Yes, the word contains a " + letter + '!') : ("Le " + letter + " figurait bien dans le mot :)"));
SendText(Config.English ? ("Yes, the word contains a " + letter + '!') : ("Le " + letter + " figurait bien dans le mot :)"));
}
else
{
vie--;
if (vie == 0)
{
SendText(English ? "Game Over! :]" : "Perdu ! Partie terminée :]");
SendText(English ? ("The word was: " + word) : ("Le mot était : " + word));
SendText(Config.English ? "Game Over! :]" : "Perdu ! Partie terminée :]");
SendText(Config.English ? ("The word was: " + word) : ("Le mot était : " + word));
running = false;
}
else SendText(English ? ("The " + letter + "? No.") : ("Le " + letter + " ? Non."));
else SendText(Config.English ? ("The " + letter + "? No.") : ("Le " + letter + " ? Non."));
}
if (running)
{
SendText(English ? ("Mysterious word: " + WordCached + " (lives : " + vie + ")")
SendText(Config.English ? ("Mysterious word: " + WordCached + " (lives : " + vie + ")")
: ("Mot mystère : " + WordCached + " (vie : " + vie + ")"));
}
if (Winner)
{
SendText(English ? ("Congrats, " + username + '!') : ("Félicitations, " + username + " !"));
SendText(Config.English ? ("Congrats, " + username + '!') : ("Félicitations, " + username + " !"));
running = false;
}
}
@ -132,23 +140,23 @@ namespace MinecraftClient.ChatBots
compteur = compteur_param;
discovered = new bool[word.Length];
SendText(English ? "Hangman v1.0 - By ORelio" : "Pendu v1.0 - Par ORelio");
SendText(English ? ("Mysterious word: " + WordCached + " (lives : " + vie + ")")
SendText(Config.English ? "Hangman v1.0 - By ORelio" : "Pendu v1.0 - Par ORelio");
SendText(Config.English ? ("Mysterious word: " + WordCached + " (lives : " + vie + ")")
: ("Mot mystère : " + WordCached + " (vie : " + vie + ")"));
SendText(English ? ("Try some letters ... :)") : ("Proposez une lettre ... :)"));
SendText(Config.English ? ("Try some letters ... :)") : ("Proposez une lettre ... :)"));
}
private string Chooseword()
{
if (System.IO.File.Exists(English ? Settings.Hangman_FileWords_EN : Settings.Hangman_FileWords_FR))
if (System.IO.File.Exists(Config.English ? Config.FileWords_EN : Config.FileWords_FR))
{
string[] dico = System.IO.File.ReadAllLines(English ? Settings.Hangman_FileWords_EN : Settings.Hangman_FileWords_FR, Encoding.UTF8);
string[] dico = System.IO.File.ReadAllLines(Config.English ? Config.FileWords_EN : Config.FileWords_FR, Encoding.UTF8);
return dico[new Random().Next(dico.Length)];
}
else
{
LogToConsole(English ? "File not found: " + Settings.Hangman_FileWords_EN : "Fichier introuvable : " + Settings.Hangman_FileWords_FR);
return English ? "WORDSAREMISSING" : "DICOMANQUANT";
LogToConsole(Config.English ? "File not found: " + Config.FileWords_EN : "Fichier introuvable : " + Config.FileWords_FR);
return Config.English ? "WORDSAREMISSING" : "DICOMANQUANT";
}
}

View file

@ -3,6 +3,7 @@ using System.Collections.Generic;
using System.Data;
using System.IO;
using System.Linq;
using Tomlet.Attributes;
namespace MinecraftClient.ChatBots
{
@ -11,6 +12,60 @@ namespace MinecraftClient.ChatBots
/// </summary>
public class Mailer : ChatBot
{
public static Configs Config = new();
[TomlDoNotInlineObject]
public class Configs
{
[NonSerialized]
private const string BotName = "Mailer";
public bool Enabled = false;
public string DatabaseFile = "MailerDatabase.ini";
public string IgnoreListFile = "MailerIgnoreList.ini";
public bool PublicInteractions = false;
public int MaxMailsPerPlayer = 10;
public int MaxDatabaseSize = 10000;
public int MailRetentionDays = 30;
public void OnSettingUpdate()
{
if (!Enabled) return;
bool checkSuccessed = true;
if (Config.MaxDatabaseSize <= 0)
{
LogToConsole(BotName, Translations.TryGet("bot.mailer.init_fail.db_size"));
checkSuccessed = false;
}
if (Config.MaxMailsPerPlayer <= 0)
{
LogToConsole(BotName, Translations.TryGet("bot.mailer.init_fail.max_mails"));
checkSuccessed = false;
}
if (Config.MailRetentionDays <= 0)
{
LogToConsole(BotName, Translations.TryGet("bot.mailer.init_fail.mail_retention"));
checkSuccessed = false;
}
if (!checkSuccessed)
{
LogToConsole(BotName, Translations.TryGet("general.bot_unload"));
Enabled = false;
}
}
}
/// <summary>
/// Holds the list of ignored players
/// </summary>
@ -161,58 +216,37 @@ namespace MinecraftClient.ChatBots
public override void Initialize()
{
LogDebugToConsoleTranslated("bot.mailer.init");
LogDebugToConsoleTranslated("bot.mailer.init.db" + Settings.Mailer_DatabaseFile);
LogDebugToConsoleTranslated("bot.mailer.init.ignore" + Settings.Mailer_IgnoreListFile);
LogDebugToConsoleTranslated("bot.mailer.init.public" + Settings.Mailer_PublicInteractions);
LogDebugToConsoleTranslated("bot.mailer.init.max_mails" + Settings.Mailer_MaxMailsPerPlayer);
LogDebugToConsoleTranslated("bot.mailer.init.db_size" + Settings.Mailer_MaxDatabaseSize);
LogDebugToConsoleTranslated("bot.mailer.init.mail_retention" + Settings.Mailer_MailRetentionDays + " days");
LogDebugToConsoleTranslated("bot.mailer.init.db" + Config.DatabaseFile);
LogDebugToConsoleTranslated("bot.mailer.init.ignore" + Config.IgnoreListFile);
LogDebugToConsoleTranslated("bot.mailer.init.public" + Config.PublicInteractions);
LogDebugToConsoleTranslated("bot.mailer.init.max_mails" + Config.MaxMailsPerPlayer);
LogDebugToConsoleTranslated("bot.mailer.init.db_size" + Config.MaxDatabaseSize);
LogDebugToConsoleTranslated("bot.mailer.init.mail_retention" + Config.MailRetentionDays + " days");
if (Settings.Mailer_MaxDatabaseSize <= 0)
if (!File.Exists(Config.DatabaseFile))
{
LogToConsoleTranslated("bot.mailer.init_fail.db_size");
UnloadBot();
return;
LogToConsoleTranslated("bot.mailer.create.db", Path.GetFullPath(Config.DatabaseFile));
new MailDatabase().SaveToFile(Config.DatabaseFile);
}
if (Settings.Mailer_MaxMailsPerPlayer <= 0)
if (!File.Exists(Config.IgnoreListFile))
{
LogToConsoleTranslated("bot.mailer.init_fail.max_mails");
UnloadBot();
return;
}
if (Settings.Mailer_MailRetentionDays <= 0)
{
LogToConsoleTranslated("bot.mailer.init_fail.mail_retention");
UnloadBot();
return;
}
if (!File.Exists(Settings.Mailer_DatabaseFile))
{
LogToConsoleTranslated("bot.mailer.create.db", Path.GetFullPath(Settings.Mailer_DatabaseFile));
new MailDatabase().SaveToFile(Settings.Mailer_DatabaseFile);
}
if (!File.Exists(Settings.Mailer_IgnoreListFile))
{
LogToConsoleTranslated("bot.mailer.create.ignore", Path.GetFullPath(Settings.Mailer_IgnoreListFile));
new IgnoreList().SaveToFile(Settings.Mailer_IgnoreListFile);
LogToConsoleTranslated("bot.mailer.create.ignore", Path.GetFullPath(Config.IgnoreListFile));
new IgnoreList().SaveToFile(Config.IgnoreListFile);
}
lock (readWriteLock)
{
LogDebugToConsoleTranslated("bot.mailer.load.db", Path.GetFullPath(Settings.Mailer_DatabaseFile));
mailDatabase = MailDatabase.FromFile(Settings.Mailer_DatabaseFile);
LogDebugToConsoleTranslated("bot.mailer.load.db", Path.GetFullPath(Config.DatabaseFile));
mailDatabase = MailDatabase.FromFile(Config.DatabaseFile);
LogDebugToConsoleTranslated("bot.mailer.load.ignore", Path.GetFullPath(Settings.Mailer_IgnoreListFile));
ignoreList = IgnoreList.FromFile(Settings.Mailer_IgnoreListFile);
LogDebugToConsoleTranslated("bot.mailer.load.ignore", Path.GetFullPath(Config.IgnoreListFile));
ignoreList = IgnoreList.FromFile(Config.IgnoreListFile);
}
//Initialize file monitors. In case the bot needs to unload for some reason in the future, do not forget to .Dispose() them
mailDbFileMonitor = new FileMonitor(Path.GetDirectoryName(Settings.Mailer_DatabaseFile)!, Path.GetFileName(Settings.Mailer_DatabaseFile), FileMonitorCallback);
ignoreListFileMonitor = new FileMonitor(Path.GetDirectoryName(Settings.Mailer_IgnoreListFile)!, Path.GetFileName(Settings.Mailer_IgnoreListFile), FileMonitorCallback);
mailDbFileMonitor = new FileMonitor(Path.GetDirectoryName(Config.DatabaseFile)!, Path.GetFileName(Config.DatabaseFile), FileMonitorCallback);
ignoreListFileMonitor = new FileMonitor(Path.GetDirectoryName(Config.IgnoreListFile)!, Path.GetFileName(Config.IgnoreListFile), FileMonitorCallback);
RegisterChatBotCommand("mailer", Translations.Get("bot.mailer.cmd"), "mailer <getmails|addignored|getignored|removeignored>", ProcessInternalCommand);
}
@ -224,7 +258,7 @@ namespace MinecraftClient.ChatBots
{
maxMessageLength = GetMaxChatMessageLength()
- 44 // Deduct length of "/ 16CharPlayerName 16CharPlayerName mailed: "
- Settings.PrivateMsgsCmdName.Length; // Deduct length of "tell" command
- Settings.Config.Main.Advanced.PrivateMsgsCmdName.Length; // Deduct length of "tell" command
}
/// <summary>
@ -236,7 +270,7 @@ namespace MinecraftClient.ChatBots
string username = "";
text = GetVerbatim(text);
if (IsPrivateMessage(text, ref message, ref username) || (Settings.Mailer_PublicInteractions && IsChatMessage(text, ref message, ref username)))
if (IsPrivateMessage(text, ref message, ref username) || (Config.PublicInteractions && IsChatMessage(text, ref message, ref username)))
{
string usernameLower = username.ToLower();
if (!ignoreList.Contains(usernameLower))
@ -247,8 +281,8 @@ namespace MinecraftClient.ChatBots
case "mail":
case "tellonym":
if (usernameLower != GetUsername().ToLower()
&& mailDatabase.Count < Settings.Mailer_MaxDatabaseSize
&& mailDatabase.Where(mail => mail.SenderLowercase == usernameLower).Count() < Settings.Mailer_MaxMailsPerPlayer)
&& mailDatabase.Count < Config.MaxDatabaseSize
&& mailDatabase.Where(mail => mail.SenderLowercase == usernameLower).Count() < Config.MaxMailsPerPlayer)
{
Queue<string> args = new(Command.GetArgs(message));
if (args.Count >= 2)
@ -266,7 +300,7 @@ namespace MinecraftClient.ChatBots
lock (readWriteLock)
{
mailDatabase.Add(mail);
mailDatabase.SaveToFile(Settings.Mailer_DatabaseFile);
mailDatabase.SaveToFile(Config.DatabaseFile);
}
SendPrivateMessage(username, "Message saved!");
}
@ -307,8 +341,8 @@ namespace MinecraftClient.ChatBots
lock (readWriteLock)
{
mailDatabase.RemoveAll(mail => mail.Delivered);
mailDatabase.RemoveAll(mail => mail.DateSent.AddDays(Settings.Mailer_MailRetentionDays) < DateTime.Now);
mailDatabase.SaveToFile(Settings.Mailer_DatabaseFile);
mailDatabase.RemoveAll(mail => mail.DateSent.AddDays(Config.MailRetentionDays) < DateTime.Now);
mailDatabase.SaveToFile(Config.DatabaseFile);
}
nextMailSend = dateNow.AddSeconds(10);
@ -324,8 +358,8 @@ namespace MinecraftClient.ChatBots
{
lock (readWriteLock)
{
mailDatabase = MailDatabase.FromFile(Settings.Mailer_DatabaseFile);
ignoreList = IgnoreList.FromFile(Settings.Mailer_IgnoreListFile);
mailDatabase = MailDatabase.FromFile(Config.DatabaseFile);
ignoreList = IgnoreList.FromFile(Config.IgnoreListFile);
}
}
@ -357,7 +391,7 @@ namespace MinecraftClient.ChatBots
if (!ignoreList.Contains(username))
{
ignoreList.Add(username);
ignoreList.SaveToFile(Settings.Mailer_IgnoreListFile);
ignoreList.SaveToFile(Config.IgnoreListFile);
}
}
return Translations.Get("bot.mailer.cmd.ignore.added", args[1]);
@ -369,7 +403,7 @@ namespace MinecraftClient.ChatBots
if (ignoreList.Contains(username))
{
ignoreList.Remove(username);
ignoreList.SaveToFile(Settings.Mailer_IgnoreListFile);
ignoreList.SaveToFile(Config.IgnoreListFile);
}
}
return Translations.Get("bot.mailer.cmd.ignore.removed", args[1]);

View file

@ -5,41 +5,59 @@ using System.Globalization;
using System.IO;
using MinecraftClient.Mapping;
using MinecraftClient.Protocol.Handlers;
using Tomlet.Attributes;
namespace MinecraftClient.ChatBots
{
class Map : ChatBot
public class Map : ChatBot
{
public static Configs Config = new();
[TomlDoNotInlineObject]
public class Configs
{
[NonSerialized]
private const string BotName = "Map";
public bool Enabled = false;
[TomlInlineComment("$config.ChatBot.Map.Should_Resize$")]
public bool Should_Resize = false;
[TomlInlineComment("$config.ChatBot.Map.Resize_To$")]
public int Resize_To = 256;
[TomlInlineComment("$config.ChatBot.Map.Auto_Render_On_Update$")]
public bool Auto_Render_On_Update = false;
[TomlInlineComment("$config.ChatBot.Map.Delete_All_On_Unload$")]
public bool Delete_All_On_Unload = true;
[TomlInlineComment("$config.ChatBot.Map.Notify_On_First_Update$")]
public bool Notify_On_First_Update = true;
public void OnSettingUpdate()
{
if (Resize_To < 128)
Resize_To = 128;
}
}
private readonly string baseDirectory = @"Rendered_Maps";
private readonly Dictionary<int, McMap> cachedMaps = new();
private bool shouldResize = true;
private int resizeTo = 256;
private bool autoRenderOnUpdate = true;
private bool deleteAllOnExit = true;
private bool notifyOnFirstUpdate = true;
public override void Initialize()
{
if (!Directory.Exists(baseDirectory))
Directory.CreateDirectory(baseDirectory);
shouldResize = Settings.Map_Should_Resize;
resizeTo = Settings.Map_Resize_To;
if (resizeTo < 128)
resizeTo = 128;
autoRenderOnUpdate = Settings.Map_Auto_Render_On_Update;
deleteAllOnExit = Settings.Map_Delete_All_On_Unload;
notifyOnFirstUpdate = Settings.Map_Notify_On_First_Update;
RegisterChatBotCommand("maps", "bot.map.cmd.desc", "maps list|render <id> or maps l|r <id>", OnMapCommand);
}
public override void OnUnload()
{
if (deleteAllOnExit)
if (Config.Delete_All_On_Unload)
{
DirectoryInfo di = new(baseDirectory);
FileInfo[] files = di.GetFiles();
@ -124,7 +142,7 @@ namespace MinecraftClient.ChatBots
{
cachedMaps.Add(mapid, map);
if (notifyOnFirstUpdate)
if (Config.Notify_On_First_Update)
LogToConsoleTranslated("bot.map.received_map", map.MapId);
}
else
@ -133,7 +151,7 @@ namespace MinecraftClient.ChatBots
cachedMaps.Add(mapid, map);
}
if (autoRenderOnUpdate)
if (Config.Auto_Render_On_Update)
GenerateMapImage(map);
}
@ -165,8 +183,8 @@ namespace MinecraftClient.ChatBots
// Resize, double the image
if (shouldResize)
image = ResizeBitmap(image, resizeTo, resizeTo);
if (Config.Should_Resize)
image = ResizeBitmap(image, Config.Resize_To, Config.Resize_To);
image.Save(fileName);
LogToConsole(Translations.TryGet("bot.map.rendered", map.MapId, fileName));

View file

@ -1,6 +1,7 @@
using System;
using System.Linq;
using System.Text;
using Tomlet.Attributes;
namespace MinecraftClient.ChatBots
{
@ -10,28 +11,34 @@ namespace MinecraftClient.ChatBots
public class PlayerListLogger : ChatBot
{
private int count;
private readonly int timeping;
private readonly string file;
public static Configs Config = new();
/// <summary>
/// This bot sends a /list command every X seconds and save the result.
/// </summary>
/// <param name="pingparam">Time amount between each list ping (10 = 1s, 600 = 1 minute, etc.)</param>
public PlayerListLogger(int pingparam, string filetosavein)
[TomlDoNotInlineObject]
public class Configs
{
count = 0;
file = filetosavein;
timeping = pingparam;
if (timeping < 10) { timeping = 10; } //To avoid flooding
[NonSerialized]
private const string BotName = "PlayerListLogger";
public bool Enabled = false;
public string File = "playerlog.txt";
[TomlInlineComment("$config.ChatBot.PlayerListLogger.Delay$")]
public int Delay = 600;
public void OnSettingUpdate()
{
if (Delay < 10)
Delay = 10;
}
}
private int count = 0;
public override void Update()
{
count++;
if (count == timeping)
if (count == Config.Delay)
{
DateTime now = DateTime.Now;
@ -40,7 +47,7 @@ namespace MinecraftClient.ChatBots
StringBuilder sb = new();
sb.AppendLine(string.Format("[{0}/{1}/{2} {3}:{4}]", now.Year, now.Month, now.Day, now.Hour, now.Minute));
sb.AppendLine(string.Join(", ", GetOnlinePlayers())).AppendLine();
System.IO.File.AppendAllText(file, sb.ToString());
System.IO.File.AppendAllText(Settings.Config.AppVar.ExpandVars(Config.File), sb.ToString());
count = 0;
}

View file

@ -1,4 +1,5 @@
using System;
using Tomlet.Attributes;
namespace MinecraftClient.ChatBots
{
@ -8,11 +9,28 @@ namespace MinecraftClient.ChatBots
public class RemoteControl : ChatBot
{
public static Configs Config = new();
[TomlDoNotInlineObject]
public class Configs
{
[NonSerialized]
private const string BotName = "RemoteControl";
public bool Enabled = false;
public bool AutoTpaccept = true;
public bool AutoTpaccept_Everyone = false;
public void OnSettingUpdate() { }
}
public override void GetText(string text)
{
text = GetVerbatim(text).Trim();
string command = "", sender = "";
if (IsPrivateMessage(text, ref command, ref sender) && Settings.Bots_Owners.Contains(sender.ToLower().Trim()))
if (IsPrivateMessage(text, ref command, ref sender) && Settings.Config.Main.Advanced.BotOwners.Contains(sender.ToLower().Trim()))
{
string? response = "";
PerformInternalCommand(command, ref response);
@ -26,9 +44,9 @@ namespace MinecraftClient.ChatBots
SendPrivateMessage(sender, response);
}
}
else if (Settings.RemoteCtrl_AutoTpaccept
else if (Config.AutoTpaccept
&& IsTeleportRequest(text, ref sender)
&& (Settings.RemoteCtrl_AutoTpaccept_Everyone || Settings.Bots_Owners.Contains(sender.ToLower().Trim())))
&& (Config.AutoTpaccept_Everyone || Settings.Config.Main.Advanced.BotOwners.Contains(sender.ToLower().Trim())))
{
SendText("/tpaccept");
}

View file

@ -1,6 +1,7 @@
using System;
using System.Collections.Generic;
using MinecraftClient.Protocol;
using Tomlet.Attributes;
namespace MinecraftClient.ChatBots
{
@ -9,24 +10,35 @@ namespace MinecraftClient.ChatBots
/// </summary>
public class ReplayCapture : ChatBot
{
private ReplayHandler? replay;
private readonly int backupInterval = 3000; // Unit: second * 10
private int backupCounter = -1;
public static Configs Config = new();
public ReplayCapture(int backupInterval)
[TomlDoNotInlineObject]
public class Configs
{
if (backupInterval != -1)
this.backupInterval = backupInterval * 10;
else
this.backupInterval = -1;
[NonSerialized]
private const string BotName = "ReplayCapture";
public bool Enabled = false;
[TomlInlineComment("$config.ChatBot.ReplayCapture.Backup_Interval$")]
public int Backup_Interval = 3000;
public void OnSettingUpdate()
{
if (Backup_Interval < -1)
Backup_Interval = -1;
}
}
private ReplayHandler? replay;
private int backupCounter = -1;
public override void Initialize()
{
SetNetworkPacketEventEnabled(true);
replay = new ReplayHandler(GetProtocolVersion());
replay.MetaData.serverName = GetServerHost() + GetServerPort();
backupCounter = backupInterval;
backupCounter = Config.Backup_Interval * 10;
RegisterChatBotCommand("replay", Translations.Get("bot.replayCapture.cmd"), "replay <save|stop>", Command);
}
@ -38,12 +50,12 @@ namespace MinecraftClient.ChatBots
public override void Update()
{
if (backupInterval > 0 && replay!.RecordRunning)
if (Config.Backup_Interval > 0 && replay!.RecordRunning)
{
if (backupCounter <= 0)
{
replay.CreateBackupReplay(@"recording_cache\REPLAY_BACKUP.mcpr");
backupCounter = backupInterval;
backupCounter = Config.Backup_Interval * 10;
}
else backupCounter--;
}

View file

@ -107,7 +107,7 @@ namespace MinecraftClient.ChatBots
}
}
if (Settings.DebugMessages)
if (Settings.Config.Logging.DebugMessages)
{
string caller = "Script";
try
@ -195,7 +195,7 @@ namespace MinecraftClient.ChatBots
{
if (instruction_line[0] != '#' && instruction_line[0] != '/' && instruction_line[1] != '/')
{
instruction_line = Settings.ExpandVars(instruction_line, localVars);
instruction_line = Settings.Config.AppVar.ExpandVars(instruction_line, localVars);
string instruction_name = instruction_line.Split(' ')[0];
switch (instruction_name.ToLower())
{

View file

@ -1,7 +1,9 @@
using System;
using System.Collections.Generic;
using System.Globalization;
using System.IO;
using System.Text;
using Tomlet.Attributes;
namespace MinecraftClient.ChatBots
{
@ -11,6 +13,32 @@ namespace MinecraftClient.ChatBots
public class ScriptScheduler : ChatBot
{
public static Configs Config = new();
[TomlDoNotInlineObject]
public class Configs
{
[NonSerialized]
private const string BotName = "ScriptScheduler";
public bool Enabled = false;
public string Tasks_File = @"tasks.ini";
public void OnSettingUpdate()
{
if (!Enabled) return;
string Tasks_File_Full = Settings.Config.AppVar.ExpandVars(Tasks_File);
if (!File.Exists(Tasks_File_Full))
{
LogToConsole(BotName, Translations.TryGet("bot.scriptScheduler.not_found", Path.GetFullPath(Tasks_File_Full)));
LogToConsole(BotName, Translations.TryGet("general.bot_unload"));
Enabled = false;
}
}
}
private class TaskDesc
{
public string? action = null;
@ -27,26 +55,20 @@ namespace MinecraftClient.ChatBots
private static bool firstlogin_done = false;
private readonly string tasksfile;
private bool serverlogin_done;
private bool serverlogin_done = false;
private readonly List<TaskDesc> tasks = new();
private int verifytasks_timeleft = 10;
private readonly int verifytasks_delay = 10;
public ScriptScheduler(string tasksfile)
{
this.tasksfile = tasksfile;
serverlogin_done = false;
}
public override void Initialize()
{
//Load the given file from the startup parameters
if (System.IO.File.Exists(tasksfile))
string Tasks_File_Full = Settings.Config.AppVar.ExpandVars(Config.Tasks_File);
if (File.Exists(Tasks_File_Full))
{
LogDebugToConsoleTranslated("bot.scriptScheduler.loading", System.IO.Path.GetFullPath(tasksfile));
LogDebugToConsoleTranslated("bot.scriptScheduler.loading", System.IO.Path.GetFullPath(Tasks_File_Full));
TaskDesc? current_task = null;
string[] lines = System.IO.File.ReadAllLines(tasksfile, Encoding.UTF8);
string[] lines = System.IO.File.ReadAllLines(Tasks_File_Full, Encoding.UTF8);
foreach (string lineRAW in lines)
{
string line = lineRAW.Split('#')[0].Trim();
@ -70,10 +92,10 @@ namespace MinecraftClient.ChatBots
string argValue = line[(argName.Length + 1)..];
switch (argName.ToLower())
{
case "triggeronfirstlogin": current_task.triggerOnFirstLogin = Settings.str2bool(argValue); break;
case "triggeronlogin": current_task.triggerOnLogin = Settings.str2bool(argValue); break;
case "triggerontime": current_task.triggerOnTime = Settings.str2bool(argValue); break;
case "triggeroninterval": current_task.triggerOnInterval = Settings.str2bool(argValue); break;
case "triggeronfirstlogin": current_task.triggerOnFirstLogin = bool.Parse(argValue); break;
case "triggeronlogin": current_task.triggerOnLogin = bool.Parse(argValue); break;
case "triggerontime": current_task.triggerOnTime = bool.Parse(argValue); break;
case "triggeroninterval": current_task.triggerOnInterval = bool.Parse(argValue); break;
case "timevalue": try { current_task.triggerOnTime_Times.Add(DateTime.ParseExact(argValue, "HH:mm", CultureInfo.InvariantCulture)); } catch { } break;
case "timeinterval":
int interval;
@ -112,7 +134,7 @@ namespace MinecraftClient.ChatBots
}
else
{
LogToConsoleTranslated("bot.scriptScheduler.not_found", System.IO.Path.GetFullPath(tasksfile));
LogToConsoleTranslated("bot.scriptScheduler.not_found", Path.GetFullPath(Tasks_File_Full));
UnloadBot(); //No need to keep the bot active
}
}

View file

@ -138,7 +138,7 @@ namespace MinecraftClient.Commands
// \ud83d\udd33: 🔳, \ud83d\udfe8: 🟨, \ud83d\udfe9: 🟩, \u25A1: □, \u25A3: ▣, \u25A0: ■
string[] chunkStatusStr = Settings.EnableEmoji ?
string[] chunkStatusStr = Settings.Config.Main.Advanced.EnableEmoji ?
new string[] { "\ud83d\udd33", "\ud83d\udfe8", "\ud83d\udfe9" } : new string[] { "\u25A1", "\u25A3", "\u25A0" };
// Output

View file

@ -15,13 +15,13 @@ namespace MinecraftClient.Commands
string[] args = GetArgs(command);
if (args.Length > 1)
{
if (!Settings.SetAccount(args[1]))
if (!Settings.Config.Main.Advanced.SetAccount(args[1]))
{
return Translations.Get("cmd.connect.unknown", args[1]);
}
}
if (Settings.SetServerIP(args[0]))
if (Settings.Config.Main.SetServerIP(new Settings.MainConfigHealper.MainConfig.ServerInfoConfig(args[0]), true))
{
Program.Restart();
return "";

View file

@ -11,11 +11,10 @@ namespace MinecraftClient.Commands
public override string Run(McClient handler, string command, Dictionary<string, object>? localVars)
{
if (HasArg(command))
{
Settings.DebugMessages = (GetArg(command).ToLower() == "on");
}
else Settings.DebugMessages = !Settings.DebugMessages;
return Translations.Get(Settings.DebugMessages ? "cmd.debug.state_on" : "cmd.debug.state_off");
Settings.Config.Logging.DebugMessages = (GetArg(command).ToLower() == "on");
else
Settings.Config.Logging.DebugMessages = !Settings.Config.Logging.DebugMessages;
return Translations.Get(Settings.Config.Logging.DebugMessages ? "cmd.debug.state_on" : "cmd.debug.state_off");
}
}
}

View file

@ -36,7 +36,7 @@ namespace MinecraftClient.Commands
var interpreter = new Interpreter();
interpreter.SetVariable("MCC", handler);
foreach (KeyValuePair<string, object> entry in Settings.GetVariables())
foreach (KeyValuePair<string, object> entry in Settings.Config.AppVar.GetVariables())
interpreter.SetVariable(entry.Key, entry.Value);
var result = interpreter.Eval<bool>(expressionText);

View file

@ -185,7 +185,7 @@ namespace MinecraftClient.Commands
response.AppendLine(String.Format(" #{0} - {1}§8", inventoryId, inventory.Title));
string? asciiArt = inventory.Type.GetAsciiArt();
if (asciiArt != null && Settings.DisplayInventoryLayout)
if (asciiArt != null && Settings.Config.Main.Advanced.ShowInventoryLayout)
response.AppendLine(asciiArt);
int selectedHotbar = handler.GetCurrentSlot() + 1;

View file

@ -45,8 +45,8 @@ namespace MinecraftClient.Commands
else if (args[0] == "gravity")
{
if (args.Count >= 2)
Settings.GravityEnabled = (args[1] == "on");
if (Settings.GravityEnabled)
Settings.InternalConfig.GravityEnabled = (args[1] == "on");
if (Settings.InternalConfig.GravityEnabled)
return Translations.Get("cmd.move.gravity.enabled");
else return Translations.Get("cmd.move.gravity.disabled");
}

View file

@ -13,7 +13,7 @@ namespace MinecraftClient.Commands
string[] args = GetArgs(command);
if (args.Length > 0)
{
if (!Settings.SetAccount(args[0]))
if (!Settings.Config.Main.Advanced.SetAccount(args[0]))
{
return Translations.Get("cmd.connect.unknown", args[0]);
}

View file

@ -15,15 +15,16 @@ namespace MinecraftClient.Commands
string[] temp = GetArg(command).Split('=');
if (temp.Length > 1)
{
if (Settings.SetVar(temp[0], GetArg(command).Substring(temp[0].Length + 1)))
{
if (Settings.Config.AppVar.SetVar(temp[0], GetArg(command).Substring(temp[0].Length + 1)))
return ""; //Success
}
else return Translations.Get("cmd.set.format");
else
return Translations.Get("cmd.set.format");
}
else return GetCmdDescTranslated();
else
return GetCmdDescTranslated();
}
else return GetCmdDescTranslated();
else
return GetCmdDescTranslated();
}
}
}

View file

@ -40,9 +40,9 @@ namespace MinecraftClient.Commands
(num2, num1) = (num1, num2);
// create a variable or set it to num1 <= varlue < num2
if (Settings.SetVar(args[0], rand.Next(num1, num2)))
if (Settings.Config.AppVar.SetVar(args[0], rand.Next(num1, num2)))
{
return string.Format("Set %{0}% to {1}.", args[0], Settings.GetVar(args[0])); //Success
return string.Format("Set %{0}% to {1}.", args[0], Settings.Config.AppVar.GetVar(args[0])); //Success
}
else return Translations.Get("cmd.setrndnum.format");
}
@ -55,9 +55,9 @@ namespace MinecraftClient.Commands
List<string> values = ParseCommandLine(argString);
// create a variable or set it to one of the values
if (values.Count > 0 && Settings.SetVar(args[0], values[rand.Next(0, values.Count)]))
if (values.Count > 0 && Settings.Config.AppVar.SetVar(args[0], values[rand.Next(0, values.Count)]))
{
return string.Format("Set %{0}% to {1}.", args[0], Settings.GetVar(args[0])); //Success
return string.Format("Set %{0}% to {1}.", args[0], Settings.Config.AppVar.GetVar(args[0])); //Success
}
else return Translations.Get("cmd.setrndstr.format");
}

View file

@ -8,8 +8,7 @@
// </auto-generated>
//------------------------------------------------------------------------------
namespace MinecraftClient
{
namespace MinecraftClient {
using System;
@ -20,31 +19,26 @@ namespace MinecraftClient
// class via a tool like ResGen or Visual Studio.
// To add or remove a member, edit your .ResX file then rerun ResGen
// with the /str option, or rebuild your VS project.
[global::System.CodeDom.Compiler.GeneratedCodeAttribute("System.Resources.Tools.StronglyTypedResourceBuilder", "4.0.0.0")]
[global::System.CodeDom.Compiler.GeneratedCodeAttribute("System.Resources.Tools.StronglyTypedResourceBuilder", "17.0.0.0")]
[global::System.Diagnostics.DebuggerNonUserCodeAttribute()]
[global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()]
public class DefaultConfigResource
{
public class DefaultConfigResource {
private static global::System.Resources.ResourceManager resourceMan;
private static global::System.Globalization.CultureInfo resourceCulture;
[global::System.Diagnostics.CodeAnalysis.SuppressMessageAttribute("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode")]
internal DefaultConfigResource()
{
internal DefaultConfigResource() {
}
/// <summary>
/// Returns the cached ResourceManager instance used by this class.
/// </summary>
[global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)]
public static global::System.Resources.ResourceManager ResourceManager
{
get
{
if (object.ReferenceEquals(resourceMan, null))
{
public static global::System.Resources.ResourceManager ResourceManager {
get {
if (object.ReferenceEquals(resourceMan, null)) {
global::System.Resources.ResourceManager temp = new global::System.Resources.ResourceManager("MinecraftClient.DefaultConfigResource", typeof(DefaultConfigResource).Assembly);
resourceMan = temp;
}
@ -57,14 +51,11 @@ namespace MinecraftClient
/// resource lookups using this strongly typed resource class.
/// </summary>
[global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)]
public static global::System.Globalization.CultureInfo Culture
{
get
{
public static global::System.Globalization.CultureInfo Culture {
get {
return resourceCulture;
}
set
{
set {
resourceCulture = value;
}
}
@ -82,12 +73,10 @@ namespace MinecraftClient
///║ Inventory ╚═══╝ ║
///║╔═══╦═══╦═══╦═══╦═══╦═══╦═══╦═══╦═══╗║
///║║ 5 ║ 6 ║ 7 ║ 8 ║ 9 ║10 ║11 ║12 ║13 ║║
///║╠═══╬═══╬═══╬═══╬═══╬═══╬═══╬══ [rest of string was truncated]&quot;;.
///║╠═══╬═══╬═══╬═══╬══ [rest of string was truncated]&quot;;.
/// </summary>
public static string ContainerType_BrewingStand
{
get
{
public static string ContainerType_BrewingStand {
get {
return ResourceManager.GetString("ContainerType_BrewingStand", resourceCulture);
}
}
@ -105,20 +94,31 @@ namespace MinecraftClient
///║ Inventory ║
///║╔═══╦═══╦═══╦═══╦═══╦═══╦═══╦═══╦═══╗║
///║║10 ║11 ║12 ║13 ║14 ║15 ║16 ║17 ║18 ║║
///║╠═══╬═══╬═══╬═══╬═══╬═══╬═══╬══ [rest of string was truncated]&quot;;.
///║╠═══╬═══╬═══╬═══╬══ [rest of string was truncated]&quot;;.
/// </summary>
public static string ContainerType_Crafting
{
get
{
public static string ContainerType_Crafting {
get {
return ResourceManager.GetString("ContainerType_Crafting", resourceCulture);
}
}
public static string ContainerType_Furnace
{
get
{
/// <summary>
/// Looks up a localized string similar to ╔═════════════════════════════════════╗
///║ Container ║
///║ ╔═══╗ ║
///║ ║ 0 ║ ║
///║ ╚═══╝ ╔═══╗ ║
///║ ⠂⡠⢂ ━━▶ ║ 2 ║ ║
///║ ⠂⡠⢂ ╚═══╝ ║
///║ ╔═══╗ ║
///║ ║ 1 ║ ║
///║ ╚═══╝ ║
///║ Inventory ║
///║╔═══╦═══╦═══╦═══╦═══╦═══╦═══╦═══╦═══╗║
///║║ 3 ║ 4 ║ 5 ║ 6 ║ 7 [rest of string was truncated]&quot;;.
/// </summary>
public static string ContainerType_Furnace {
get {
return ResourceManager.GetString("ContainerType_Furnace", resourceCulture);
}
}
@ -136,19 +136,17 @@ namespace MinecraftClient
///║ Inventory ║
///║╔═══╦═══╦═══╦═══╦═══╦═══╦═══╦═══╦═══╗║
///║║ 9 ║10 ║11 ║12 ║13 ║14 ║15 ║16 ║17 ║║
///║╠═══╬═══╬═══╬═══╬═══╬═══╬═══╬══ [rest of string was truncated]&quot;;.
///║╠═══╬═══╬═══╬═══╬══ [rest of string was truncated]&quot;;.
/// </summary>
public static string ContainerType_Generic_3x3
{
get
{
public static string ContainerType_Generic_3x3 {
get {
return ResourceManager.GetString("ContainerType_Generic_3x3", resourceCulture);
}
}
/// <summary>
/// Looks up a localized string similar to ╔═════════════════════════════════════╗
///║ Chest
///║ Container
///║╔═══╦═══╦═══╦═══╦═══╦═══╦═══╦═══╦═══╗║
///║║ 0 ║ 1 ║ 2 ║ 3 ║ 4 ║ 5 ║ 6 ║ 7 ║ 8 ║║
///║╠═══╬═══╬═══╬═══╬═══╬═══╬═══╬═══╬═══╣║
@ -159,19 +157,17 @@ namespace MinecraftClient
///║ Inventory ║
///║╔═══╦═══╦═══╦═══╦═══╦═══╦═══╦═══╦═══╗║
///║║27 ║28 ║29 ║30 ║31 ║32 ║33 ║34 ║35 ║║
///║╠═══╬═══╬═══╬═══╬═══╬═══╬═══╬══ [rest of string was truncated]&quot;;.
///║╠═══╬═══╬═══╬═══╬══ [rest of string was truncated]&quot;;.
/// </summary>
public static string ContainerType_Generic_9x3
{
get
{
public static string ContainerType_Generic_9x3 {
get {
return ResourceManager.GetString("ContainerType_Generic_9x3", resourceCulture);
}
}
/// <summary>
/// Looks up a localized string similar to ╔═════════════════════════════════════╗
///║ Large Container ║
///║ Container
///║╔═══╦═══╦═══╦═══╦═══╦═══╦═══╦═══╦═══╗║
///║║ 0 ║ 1 ║ 2 ║ 3 ║ 4 ║ 5 ║ 6 ║ 7 ║ 8 ║║
///║╠═══╬═══╬═══╬═══╬═══╬═══╬═══╬═══╬═══╣║
@ -182,28 +178,52 @@ namespace MinecraftClient
///║║27 ║28 ║29 ║30 ║31 ║32 ║33 ║34 ║35 ║║
///║╠═══╬═══╬═══╬═══╬═══╬═══╬═══╬═══╬═══╣║
///║║36 ║37 ║38 ║39 ║40 ║41 ║42 ║43 ║44 ║║
///║╠═══╬═══╬═══╬═══╬═══╬═══╬═══╬══ [rest of string was truncated]&quot;;.
///║╠═══╬═══╬═══╬═══╬══ [rest of string was truncated]&quot;;.
/// </summary>
public static string ContainerType_Generic_9x6
{
get
{
public static string ContainerType_Generic_9x6 {
get {
return ResourceManager.GetString("ContainerType_Generic_9x6", resourceCulture);
}
}
public static string ContainerType_Grindstone
{
get
{
/// <summary>
/// Looks up a localized string similar to ╔═════════════════════════════════════╗
///║ Repair &amp; Disenchant ║
///║ ╔═════════╗ ║
///║ ║ ╔═══╗ ║ ║
///║ ╔══║ ║ 0 ║ ║══╗ ║
///║ ║ ║ ╚═══╝ ║ ║ ║
///║ ║ ║ ╔═══╗ ║ ║ ╔═══╗ ║
///║ ║ ║ ║ 1 ║ ║ ║ ━━▶ ║ 2 ║ ║
///║ ║ ║ ╚═══╝ ║ ║ ╚═══╝ ║
///║ ║ ╠═════════╣ ║ ║
///║ ║ ║ ║ ║ ║
///║ ╚══╝ ╚══╝ ║
///║ [rest of string was truncated]&quot;;.
/// </summary>
public static string ContainerType_Grindstone {
get {
return ResourceManager.GetString("ContainerType_Grindstone", resourceCulture);
}
}
public static string ContainerType_Hopper
{
get
{
/// <summary>
/// Looks up a localized string similar to ╔═════════════════════════════════════╗
///║ Container ║
///║ ╔═══╦═══╦═══╦═══╦═══╗ ║
///║ ║ 0 ║ 1 ║ 2 ║ 3 ║ 4 ║ ║
///║ ╚═══╩═══╩═══╩═══╩═══╝ ║
///║ Inventory ║
///║╔═══╦═══╦═══╦═══╦═══╦═══╦═══╦═══╦═══╗║
///║║ 5 ║ 6 ║ 7 ║ 8 ║ 9 ║10 ║11 ║12 ║13 ║║
///║╠═══╬═══╬═══╬═══╬═══╬═══╬═══╬═══╬═══╣║
///║║14 ║15 ║16 ║17 ║18 ║19 ║20 ║21 ║22 ║║
///║╠═══╬═══╬═══╬═══╬═══╬═══╬═══╬═══╬═══╣║
///║║23 ║24 ║25 ║26 ║27 ║28 ║29 ║30 ║31 ║║
///║╚═══╩═══╩═══╩═══╩══ [rest of string was truncated]&quot;;.
/// </summary>
public static string ContainerType_Hopper {
get {
return ResourceManager.GetString("ContainerType_Hopper", resourceCulture);
}
}
@ -221,12 +241,10 @@ namespace MinecraftClient
///║╚═══╩═══════════╩═══╝ ║
///║╔═══╦═══╦═══╦═══╦═══╦═══╦═══╦═══╦═══╗║
///║║ 9 ║10 ║11 ║12 ║13 ║14 ║15 ║16 ║17 ║║
///║╠═══╬═══╬═══╬═══╬═══╬═══╬═══╬══ [rest of string was truncated]&quot;;.
///║╠═══╬═══╬═══╬═══╬══ [rest of string was truncated]&quot;;.
/// </summary>
public static string ContainerType_PlayerInventory
{
get
{
public static string ContainerType_PlayerInventory {
get {
return ResourceManager.GetString("ContainerType_PlayerInventory", resourceCulture);
}
}
@ -234,29 +252,19 @@ namespace MinecraftClient
/// <summary>
/// Looks up a localized string similar to # Startup Config File
///
///[Main]
///# New to Minecraft Console Client? See README and sample configuration files here:
///# https://github.com/MCCTeam/Minecraft-Console-Client/tree/master/MinecraftClient/config
///
///# Want to upgrade to a newer version? See https://github.com/MCCTeam/Minecraft-Console-Client/#download
///# Some settings missing here after an upgrade? Try to delete this file and relaunch MCC to generate a new one
///
///[Main]
///# General settings
///# Leave blank to prompt user on startup
///# Use &quot;-&quot; as password for offline mode
///
///login=
///password=
///serverip=
///type=mojang # Account type. mojang or microsoft
///method=mcc # Microsoft Account sign-in method. mcc OR browser
///
///# Advanced settings
///
///language=en_GB
///consoletitle=%username%@%serverip% - Minecraft Console Client
///internalcmdchar=slash # Use &apos;none&apos;, &apos;slash&apos; or &apos;backslash&apos;
///messagec [rest of string was truncated]&quot;;.
///# Use &quot;-&quot; as password fo [rest of string was truncated]&quot;;.
/// </summary>
public static string MinecraftClient
{
get
{
public static string MinecraftClient {
get {
return ResourceManager.GetString("MinecraftClient", resourceCulture);
}
}
@ -265,22 +273,124 @@ namespace MinecraftClient
/// Looks up a localized string similar to [mcc]
///# Messages from MCC itself
///mcc.login=Login :
///mcc.login_basic_io=Bitte gib einen Nutzernamen oder eine E-Mail deiner Wahl ein.
///mcc.password=Passwort :
///mcc.password_basic_io=Bitte gib das Passwort für {0} ein.
///mcc.password_hidden=Passwort : {0}
///mcc.offline=§8Das Programm läuft im Offline-Modus.
///mcc.session_invalid=§8Gespeicherte Session ungültig oder abgelaufen.
///mcc.session_valid=§8Gespeicherte Session gültig für {0}.
///mcc.connecting=Verbinde zu {0}...
///mcc.ip=Server-IP :
///mcc.use_version=§8B [rest of string was truncated]&quot;;.
/// </summary>
public static string Translation_de {
get {
return ResourceManager.GetString("Translation_de", resourceCulture);
}
}
/// <summary>
/// Looks up a localized string similar to [mcc]
///# Messages from MCC itself
///mcc.help_us_translate=Help us translate MCC: https://mccteam.github.io/guide/translation.html
///mcc.run_with_default_settings=\nMCC is running with default settings.
///mcc.settings_generated=§cSettings file MinecraftClient.ini has been generated.
///mcc.login=Login :
///mcc.login_basic_io=Please type the username or email of your choice.
///mcc.password=Password :
///mcc.password_basic_io=Please type the password for {0}.
///mcc.password_hidden=Password : {0}
///mcc.offline=§8You chose to run in offline mode.
///mcc.session_invalid=§8Cached session is invalid or expired.
///mcc.session_valid=§8Cached session is still valid for {0}.
///mcc.connecting=Connecting to {0}...
///mcc.ip=Server IP :
///mcc.use_version=§8Using Minecraft version [rest of string was truncated]&quot;;.
///mcc.offline=§8You chose [rest of string was truncated]&quot;;.
/// </summary>
public static string TranslationEnglish
{
get
{
return ResourceManager.GetString("TranslationEnglish", resourceCulture);
public static string Translation_en {
get {
return ResourceManager.GetString("Translation_en", resourceCulture);
}
}
/// <summary>
/// Looks up a localized string similar to [mcc]
///# Messages from MCC itself
///mcc.login=Connexion :
///mcc.login_basic_io=Veuillez saisir un nom d&apos;utilisateur ou une adresse email.
///mcc.password=Mot de passe :
///mcc.password_basic_io=Saisissez le mot de passe pour {0}.
///mcc.password_hidden=Mot de passe : {0}
///mcc.offline=§8Vous avez choisi d&apos;utiliser le mode hors ligne.
///mcc.session_invalid=§8Le cache de la session est invalide ou a expiré.
///mcc.session_valid=§8Le cache de la session est encore valable pendant {0}.
///mcc.connecting=Connexion à {0}...
/// [rest of string was truncated]&quot;;.
/// </summary>
public static string Translation_fr {
get {
return ResourceManager.GetString("Translation_fr", resourceCulture);
}
}
/// <summary>
/// Looks up a localized string similar to [mcc]
///# Messages from MCC itself
///mcc.login=Логин :
///mcc.login_basic_io=Пожалуйста, введите имя пользователя или email по вашему выбору.
///mcc.password=Пароль:
///mcc.password_basic_io=Пожалуйста, введите пароль для {0}.
///mcc.password_hidden=Пароль: {0}
///mcc.offline=§8Вы выбрали запуск в автономном режиме.
///mcc.session_invalid=§8Кэшированная сессия недействительна или истекла.
///mcc.session_valid=§8Кэшированная сессия все еще действительна для {0}.
///mcc.connecting=Подключение к {0}...
///mcc.ip=IP сервера:
///mc [rest of string was truncated]&quot;;.
/// </summary>
public static string Translation_ru {
get {
return ResourceManager.GetString("Translation_ru", resourceCulture);
}
}
/// <summary>
/// Looks up a localized string similar to [mcc]
///# Messages from MCC itself
///mcc.login=Đăng nhập:
///mcc.login_basic_io=Hãy nhập địa chỉ email hoặc tên tài khoản của bạn:
///mcc.password=Mật khẩu:
///mcc.password_basic_io=Hãy nhập mật khẩu cho {0}.
///mcc.password_hidden=Password : {0}
///mcc.offline=§8Bạn chọn sử dụng chế độ ngoại tuyến.
///mcc.session_invalid=§8Phiên không hợp lệ hoặc đã hết hạn.
///mcc.session_valid=§8Phiên vẫn còn hợp lệ cho {0}.
///mcc.connecting=Đang kết nối tới {0}...
///mcc.ip=Địa chỉ máy chủ:
///mcc.use_version=§8Sử dụng Minecraft phiên bản [rest of string was truncated]&quot;;.
/// </summary>
public static string Translation_vi {
get {
return ResourceManager.GetString("Translation_vi", resourceCulture);
}
}
/// <summary>
/// Looks up a localized string similar to [mcc]
///# Messages from MCC itself
///mcc.help_us_translate=帮助我们翻译MCChttps://mccteam.github.io/guide/translation.html
///mcc.run_with_default_settings=\nMCC正在使用默认配置运行。
///mcc.settings_generated=§c配置文件 MinecraftClient.ini 已经生成。
///mcc.login=登录:
///mcc.login_basic_io=请输入用户名或邮箱。
///mcc.password=密码:
///mcc.password_basic_io=请输入{0}的密码。
///mcc.password_hidden=密码:{0}
///mcc.offline=§8您正在使用离线模式。
///mcc.session_invalid=§8缓存无效或已过期。
///mcc.session_valid=§8{0}的缓存仍然有效。
///mcc.profile_key_invalid=§8缓存的聊天签名密钥需要刷新。
///mcc.profile_key_valid=§8{0}的聊天 [rest of string was truncated]&quot;;.
/// </summary>
public static string Translation_zh_Hans {
get {
return ResourceManager.GetString("Translation_zh_Hans", resourceCulture);
}
}
}

View file

@ -148,7 +148,22 @@
<data name="MinecraftClient" type="System.Resources.ResXFileRef, System.Windows.Forms">
<value>Resources\config\MinecraftClient.ini;System.String, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089;utf-8</value>
</data>
<data name="TranslationEnglish" type="System.Resources.ResXFileRef, System.Windows.Forms">
<data name="Translation_de" type="System.Resources.ResXFileRef, System.Windows.Forms">
<value>resources\lang\de.ini;System.String, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089;utf-8</value>
</data>
<data name="Translation_en" type="System.Resources.ResXFileRef, System.Windows.Forms">
<value>Resources\lang\en.ini;System.String, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089;utf-8</value>
</data>
<data name="Translation_fr" type="System.Resources.ResXFileRef, System.Windows.Forms">
<value>resources\lang\fr.ini;System.String, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089;utf-8</value>
</data>
<data name="Translation_ru" type="System.Resources.ResXFileRef, System.Windows.Forms">
<value>resources\lang\ru.ini;System.String, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089;utf-8</value>
</data>
<data name="Translation_vi" type="System.Resources.ResXFileRef, System.Windows.Forms">
<value>resources\lang\vi.ini;System.String, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089;utf-8</value>
</data>
<data name="Translation_zh_Hans" type="System.Resources.ResXFileRef, System.Windows.Forms">
<value>resources\lang\zh-hans.ini;System.String, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089;utf-8</value>
</data>
</root>

View file

@ -22,7 +22,7 @@ namespace MinecraftClient
/// <param name="handler">Callback for file changes</param>
public FileMonitor(string folder, string filename, FileSystemEventHandler handler)
{
if (Settings.DebugMessages)
if (Settings.Config.Logging.DebugMessages)
{
string callerClass = new System.Diagnostics.StackFrame(1).GetMethod()!.DeclaringType!.Name;
ConsoleIO.WriteLineFormatted(Translations.Get("filemonitor.init", callerClass, Path.Combine(folder, filename)));
@ -40,7 +40,7 @@ namespace MinecraftClient
}
catch
{
if (Settings.DebugMessages)
if (Settings.Config.Logging.DebugMessages)
{
string callerClass = new System.Diagnostics.StackFrame(1).GetMethod()!.DeclaringType!.Name;
ConsoleIO.WriteLineFormatted(Translations.Get("filemonitor.fail", callerClass));

View file

@ -26,7 +26,7 @@ namespace MinecraftClient.Logger
{
try
{
if (!Settings.SaveColorCodes)
if (!Settings.Config.Logging.SaveColorCodes)
msg = ChatBot.GetVerbatim(msg);
if (prependTimestamp)
msg = GetTimestamp() + ' ' + msg;

View file

@ -1,4 +1,5 @@
using System.Text.RegularExpressions;
using static MinecraftClient.Settings;
namespace MinecraftClient.Logger
{
@ -10,11 +11,23 @@ namespace MinecraftClient.Logger
{
Regex? regexToUse = null;
// Convert to bool for XOR later. Whitelist = 0, Blacklist = 1
bool filterMode = Settings.FilterMode == Settings.FilterModeEnum.Blacklist;
bool filterMode = Config.Logging.FilterMode == LoggingConfigHealper.LoggingConfig.FilterModeEnum.blacklist;
switch (channel)
{
case FilterChannel.Chat: regexToUse = Settings.ChatFilter; break;
case FilterChannel.Debug: regexToUse = Settings.DebugFilter; break;
case FilterChannel.Chat:
string chat = Config.Logging.ChatFilterRegex;
if (string.IsNullOrEmpty(chat))
regexToUse = null;
else
regexToUse = new(chat);
break;
case FilterChannel.Debug:
string debug = Config.Logging.DebugFilterRegex;
if (string.IsNullOrEmpty(debug))
regexToUse = null;
else
regexToUse = new(debug);
break;
}
if (regexToUse != null)
{

View file

@ -21,7 +21,7 @@ namespace MinecraftClient.Mapping
/// <returns>Updated location after applying gravity</returns>
public static Location HandleGravity(World world, Location location, ref double motionY)
{
if (Settings.GravityEnabled)
if (Settings.InternalConfig.GravityEnabled)
{
Location onFoots = new(location.X, Math.Floor(location.Y), location.Z);
Location belowFoots = Move(location, Direction.Down);

View file

@ -14,6 +14,7 @@ using MinecraftClient.Protocol.Keys;
using MinecraftClient.Protocol.Message;
using MinecraftClient.Protocol.Session;
using MinecraftClient.Proxy;
using static MinecraftClient.Settings;
namespace MinecraftClient
{
@ -142,12 +143,11 @@ namespace MinecraftClient
/// <param name="port">The server port to use</param>
/// <param name="protocolversion">Minecraft protocol version to use</param>
/// <param name="forgeInfo">ForgeInfo item stating that Forge is enabled</param>
/// <param name="command">The text or command to send. Will only be sent if singlecommand is set to true.</param>
public McClient(SessionToken session, PlayerKeyPair? playerKeyPair, string server_ip, ushort port, int protocolversion, ForgeInfo? forgeInfo, string? command)
public McClient(SessionToken session, PlayerKeyPair? playerKeyPair, string server_ip, ushort port, int protocolversion, ForgeInfo? forgeInfo)
{
terrainAndMovementsEnabled = Settings.TerrainAndMovements;
inventoryHandlingEnabled = Settings.InventoryHandling;
entityHandlingEnabled = Settings.EntityHandling;
terrainAndMovementsEnabled = Config.Main.Advanced.TerrainAndMovements;
inventoryHandlingEnabled = Config.Main.Advanced.InventoryHandling;
entityHandlingEnabled = Config.Main.Advanced.EntityHandling;
sessionid = session.ID;
if (!Guid.TryParse(session.PlayerID, out uuid))
@ -159,64 +159,47 @@ namespace MinecraftClient
this.protocolversion = protocolversion;
this.playerKeyPair = playerKeyPair;
Log = Settings.LogToFile
? new FileLogLogger(Settings.ExpandVars(Settings.LogFile), Settings.PrependTimestamp)
Log = Settings.Config.Logging.LogToFile
? new FileLogLogger(Config.AppVar.ExpandVars(Settings.Config.Logging.LogFile), Settings.Config.Logging.PrependTimestamp)
: new FilteredLogger();
Log.DebugEnabled = Settings.DebugMessages;
Log.InfoEnabled = Settings.InfoMessages;
Log.ChatEnabled = Settings.ChatMessages;
Log.WarnEnabled = Settings.WarningMessages;
Log.ErrorEnabled = Settings.ErrorMessages;
Log.DebugEnabled = Config.Logging.DebugMessages;
Log.InfoEnabled = Config.Logging.InfoMessages;
Log.ChatEnabled = Config.Logging.ChatMessages;
Log.WarnEnabled = Config.Logging.WarningMessages;
Log.ErrorEnabled = Config.Logging.ErrorMessages;
if (command == null)
{
/* Load commands from Commands namespace */
LoadCommands();
/* Load commands from Commands namespace */
LoadCommands();
if (botsOnHold.Count == 0)
RegisterBots();
}
if (botsOnHold.Count == 0)
RegisterBots();
try
{
client = ProxyHandler.NewTcpClient(host, port);
client.ReceiveBufferSize = 1024 * 1024;
client.ReceiveTimeout = Settings.Timeout * 1000; // Default: 30 seconds
client.ReceiveTimeout = Config.Main.Advanced.TcpTimeout * 1000; // Default: 30 seconds
handler = Protocol.ProtocolHandler.GetProtocolHandler(client, protocolversion, forgeInfo, this);
Log.Info(Translations.Get("mcc.version_supported"));
if (command == null)
{
timeoutdetector = new(new Thread(new ParameterizedThreadStart(TimeoutDetector)), new CancellationTokenSource());
timeoutdetector.Item1.Name = "MCC Connection timeout detector";
timeoutdetector.Item1.Start(timeoutdetector.Item2.Token);
}
timeoutdetector = new(new Thread(new ParameterizedThreadStart(TimeoutDetector)), new CancellationTokenSource());
timeoutdetector.Item1.Name = "MCC Connection timeout detector";
timeoutdetector.Item1.Start(timeoutdetector.Item2.Token);
try
{
if (handler.Login(this.playerKeyPair, session))
{
if (command != null)
{
handler.SendChatMessage(command, playerKeyPair);
Log.Info(Translations.Get("mcc.single_cmd", command));
Thread.Sleep(5000);
handler.Disconnect();
Thread.Sleep(1000);
}
else
{
foreach (ChatBot bot in botsOnHold)
BotLoad(bot, false);
botsOnHold.Clear();
foreach (ChatBot bot in botsOnHold)
BotLoad(bot, false);
botsOnHold.Clear();
Log.Info(Translations.Get("mcc.joined", (Settings.internalCmdChar == ' ' ? "" : "" + Settings.internalCmdChar)));
Log.Info(Translations.Get("mcc.joined", Config.Main.Advanced.InternalCmdChar.ToLogString()));
cmdprompt = new CancellationTokenSource();
ConsoleInteractive.ConsoleReader.BeginReadThread(cmdprompt);
ConsoleInteractive.ConsoleReader.MessageReceived += ConsoleReaderOnMessageReceived;
ConsoleInteractive.ConsoleReader.OnKeyInput += ConsoleIO.AutocompleteHandler;
}
cmdprompt = new CancellationTokenSource();
ConsoleInteractive.ConsoleReader.BeginReadThread(cmdprompt);
ConsoleInteractive.ConsoleReader.MessageReceived += ConsoleReaderOnMessageReceived;
ConsoleInteractive.ConsoleReader.OnKeyInput += ConsoleIO.AutocompleteHandler;
}
else
{
@ -253,7 +236,7 @@ namespace MinecraftClient
ReconnectionAttemptsLeft--;
Program.Restart();
}
else if (command == null && Settings.interactiveMode)
else if (InternalConfig.InteractiveMode)
{
ConsoleInteractive.ConsoleReader.StopReadThread();
ConsoleInteractive.ConsoleReader.MessageReceived -= ConsoleReaderOnMessageReceived;
@ -269,24 +252,24 @@ namespace MinecraftClient
/// </summary>
private void RegisterBots(bool reload = false)
{
if (Settings.AntiAFK_Enabled) { BotLoad(new AntiAFK(Settings.AntiAFK_Delay, Settings.AntiAFK_UseTerrain_Handling, Settings.AntiAFK_Walk_Range, Settings.AntiAFK_Walk_Retries)); }
if (Settings.Hangman_Enabled) { BotLoad(new HangmanGame(Settings.Hangman_English)); }
if (Settings.Alerts_Enabled) { BotLoad(new Alerts()); }
if (Settings.ChatLog_Enabled) { BotLoad(new ChatLog(Settings.ExpandVars(Settings.ChatLog_File), Settings.ChatLog_Filter, Settings.ChatLog_DateTime)); }
if (Settings.PlayerLog_Enabled) { BotLoad(new PlayerListLogger(Settings.PlayerLog_Delay, Settings.ExpandVars(Settings.PlayerLog_File))); }
if (Settings.AutoRelog_Enabled) { BotLoad(new AutoRelog(Settings.AutoRelog_Delay_Min, Settings.AutoRelog_Delay_Max, Settings.AutoRelog_Retries)); }
if (Settings.ScriptScheduler_Enabled) { BotLoad(new ScriptScheduler(Settings.ExpandVars(Settings.ScriptScheduler_TasksFile))); }
if (Settings.RemoteCtrl_Enabled) { BotLoad(new RemoteControl()); }
if (Settings.AutoRespond_Enabled) { BotLoad(new AutoRespond(Settings.AutoRespond_Matches, Settings.AutoRespond_MatchColors)); }
if (Settings.AutoAttack_Enabled) { BotLoad(new AutoAttack(Settings.AutoAttack_Mode, Settings.AutoAttack_Priority, Settings.AutoAttack_OverrideAttackSpeed, Settings.AutoAttack_CooldownSeconds, Settings.AutoAttack_Interaction)); }
if (Settings.AutoFishing_Enabled) { BotLoad(new AutoFishing()); }
if (Settings.AutoEat_Enabled) { BotLoad(new AutoEat(Settings.AutoEat_hungerThreshold)); }
if (Settings.Mailer_Enabled) { BotLoad(new Mailer()); }
if (Settings.AutoCraft_Enabled) { BotLoad(new AutoCraft(Settings.AutoCraft_configFile)); }
if (Settings.AutoDrop_Enabled) { BotLoad(new AutoDrop(Settings.AutoDrop_Mode, Settings.AutoDrop_items)); }
if (Settings.ReplayMod_Enabled && reload) { BotLoad(new ReplayCapture(Settings.ReplayMod_BackupInterval)); }
if (Settings.FollowPlayer_Enabled) { BotLoad(new FollowPlayer(Settings.FollowPlayer_UpdateLimit, Settings.FollowPlayer_UpdateLimit)); }
if (Settings.Map_Enabled) { BotLoad(new Map()); }
if (Config.ChatBot.AntiAFK.Enabled) { BotLoad(new AntiAFK()); }
if (Config.ChatBot.HangmanGame.Enabled) { BotLoad(new HangmanGame()); }
if (Config.ChatBot.Alerts.Enabled) { BotLoad(new Alerts()); }
if (Config.ChatBot.ChatLog.Enabled) { BotLoad(new ChatLog()); }
if (Config.ChatBot.PlayerListLogger.Enabled) { BotLoad(new PlayerListLogger()); }
if (Config.ChatBot.AutoRelog.Enabled) { BotLoad(new AutoRelog()); }
if (Config.ChatBot.ScriptScheduler.Enabled) { BotLoad(new ScriptScheduler()); }
if (Config.ChatBot.RemoteControl.Enabled) { BotLoad(new RemoteControl()); }
if (Config.ChatBot.AutoRespond.Enabled) { BotLoad(new AutoRespond()); }
if (Config.ChatBot.AutoAttack.Enabled) { BotLoad(new AutoAttack()); }
if (Config.ChatBot.AutoFishing.Enabled) { BotLoad(new AutoFishing()); }
if (Config.ChatBot.AutoEat.Enabled) { BotLoad(new AutoEat()); }
if (Config.ChatBot.Mailer.Enabled) { BotLoad(new Mailer()); }
if (Config.ChatBot.AutoCraft.Enabled) { BotLoad(new AutoCraft()); }
if (Config.ChatBot.AutoDrop.Enabled) { BotLoad(new AutoDrop()); }
if (Config.ChatBot.ReplayCapture.Enabled && reload) { BotLoad(new ReplayCapture()); }
if (Config.ChatBot.FollowPlayer.Enabled) { BotLoad(new FollowPlayer()); }
if (Config.ChatBot.Map.Enabled) { BotLoad(new Map()); }
//Add your ChatBot here by uncommenting and adapting
//BotLoad(new ChatBots.YourBot());
@ -302,7 +285,7 @@ namespace MinecraftClient
{
string text = chatQueue.Dequeue();
handler.SendChatMessage(text, playerKeyPair);
nextMessageSendTime = DateTime.Now + Settings.messageCooldown;
nextMessageSendTime = DateTime.Now + TimeSpan.FromSeconds(Config.Main.Advanced.MessageCooldown);
}
}
@ -336,7 +319,7 @@ namespace MinecraftClient
{
lock (locationLock)
{
for (int i = 0; i < Settings.MovementSpeed; i++) //Needs to run at 20 tps; MCC runs at 10 tps
for (int i = 0; i < Config.Main.Advanced.MovementSpeed; i++) //Needs to run at 20 tps; MCC runs at 10 tps
{
if (_yaw == null || _pitch == null)
{
@ -349,10 +332,8 @@ namespace MinecraftClient
Location next = path.Dequeue();
steps = Movement.Move2Steps(location, next, ref motionY);
if (Settings.MoveHeadWhileWalking) // Disable head movements to avoid anti-cheat triggers
{
if (Config.Main.Advanced.MoveHeadWhileWalking) // Disable head movements to avoid anti-cheat triggers
UpdateLocation(location, next + new Location(0, 1, 0)); // Update yaw and pitch to look at next step
}
}
else
{
@ -370,7 +351,7 @@ namespace MinecraftClient
}
}
if (Settings.AutoRespawn && respawnTicks > 0)
if (Config.Main.Advanced.AutoRespawn && respawnTicks > 0)
{
respawnTicks--;
if (respawnTicks == 0)
@ -404,7 +385,7 @@ namespace MinecraftClient
lock (lastKeepAliveLock)
{
if (lastKeepAlive.AddSeconds(Settings.Timeout) < DateTime.Now)
if (lastKeepAlive.AddSeconds(Config.Main.Advanced.TcpTimeout) < DateTime.Now)
{
if (((CancellationToken)o!).IsCancellationRequested)
return;
@ -572,11 +553,11 @@ namespace MinecraftClient
text = text.Trim();
if (text.Length > 0)
{
if (Settings.internalCmdChar == ' ' || text[0] == Settings.internalCmdChar)
if (Config.Main.Advanced.InternalCmdChar.ToChar() == ' ' || text[0] == Config.Main.Advanced.InternalCmdChar.ToChar())
{
string? response_msg = "";
string command = Settings.internalCmdChar == ' ' ? text : text[1..];
if (!PerformInternalCommand(Settings.ExpandVars(command), ref response_msg, Settings.GetVariables()) && Settings.internalCmdChar == '/')
string command = Config.Main.Advanced.InternalCmdChar.ToChar() == ' ' ? text : text[1..];
if (!PerformInternalCommand(Config.AppVar.ExpandVars(command), ref response_msg, Settings.Config.AppVar.GetVariables()) && Config.Main.Advanced.InternalCmdChar.ToChar() == '/')
{
SendText(text);
}
@ -659,7 +640,7 @@ namespace MinecraftClient
}
else response_msg = Translations.Get("icmd.unknown", command_name);
}
else response_msg = Translations.Get("icmd.list", String.Join(", ", cmd_names.ToArray()), Settings.internalCmdChar);
else response_msg = Translations.Get("icmd.list", String.Join(", ", cmd_names.ToArray()), Config.Main.Advanced.InternalCmdChar.ToChar());
}
else if (cmds.ContainsKey(command_name))
{
@ -845,7 +826,6 @@ namespace MinecraftClient
DispatchBotEvent(bot => bot.Initialize(), new ChatBot[] { b });
if (handler != null)
DispatchBotEvent(bot => bot.AfterGameJoined(), new ChatBot[] { b });
Settings.SingleCommand = "";
}
/// <summary>
@ -2395,18 +2375,19 @@ namespace MinecraftClient
/// </summary>
public void OnGameJoined()
{
if (!String.IsNullOrWhiteSpace(Settings.BrandInfo))
handler.SendBrandInfo(Settings.BrandInfo.Trim());
string? bandString = Config.Main.Advanced.BrandInfo.ToBrandString();
if (!String.IsNullOrWhiteSpace(bandString))
handler.SendBrandInfo(bandString.Trim());
if (Settings.MCSettings_Enabled)
if (Config.MCSettings.Enabled)
handler.SendClientSettings(
Settings.MCSettings_Locale,
Settings.MCSettings_RenderDistance,
Settings.MCSettings_Difficulty,
Settings.MCSettings_ChatMode,
Settings.MCSettings_ChatColors,
Settings.MCSettings_Skin_All,
Settings.MCSettings_MainHand);
Config.MCSettings.Locale,
Config.MCSettings.RenderDistance,
(byte)Config.MCSettings.Difficulty,
(byte)Config.MCSettings.ChatMode,
Config.MCSettings.ChatColors,
Config.MCSettings.Skin.GetByte(),
(byte)Config.MCSettings.MainHand);
if (inventoryHandlingRequested)
@ -2484,15 +2465,15 @@ namespace MinecraftClient
{
case MovementType.Sneak:
// https://minecraft.fandom.com/wiki/Sneaking#Effects - Sneaking 1.31m/s
Settings.MovementSpeed = 2;
Config.Main.Advanced.MovementSpeed = 2;
break;
case MovementType.Walk:
// https://minecraft.fandom.com/wiki/Walking#Usage - Walking 4.317 m/s
Settings.MovementSpeed = 4;
Config.Main.Advanced.MovementSpeed = 4;
break;
case MovementType.Sprint:
// https://minecraft.fandom.com/wiki/Sprinting#Usage - Sprinting 5.612 m/s
Settings.MovementSpeed = 5;
Config.Main.Advanced.MovementSpeed = 5;
break;
}
}
@ -2601,7 +2582,7 @@ namespace MinecraftClient
if (message.isSignedChat)
{
if (!Settings.ShowIllegalSignedChat && !message.isSystemChat && !(bool)message.isSignatureLegal!)
if (!Config.Signature.ShowIllegalSignedChat && !message.isSystemChat && !(bool)message.isSignatureLegal!)
return;
messageText = ChatParser.ParseSignedChat(message, links);
}
@ -2615,7 +2596,7 @@ namespace MinecraftClient
Log.Chat(messageText);
if (Settings.DisplayChatLinks)
if (Config.Main.Advanced.ShowChatLinks)
foreach (string link in links)
Log.Chat(Translations.Get("mcc.link", link));
@ -3024,14 +3005,14 @@ namespace MinecraftClient
if (health <= 0)
{
if (Settings.AutoRespawn)
if (Config.Main.Advanced.AutoRespawn)
{
Log.Info(Translations.Get("mcc.player_dead_respawn"));
respawnTicks = 10;
}
else
{
Log.Info(Translations.Get("mcc.player_dead", (Settings.internalCmdChar == ' ' ? "" : "" + Settings.internalCmdChar)));
Log.Info(Translations.Get("mcc.player_dead", Config.Main.Advanced.InternalCmdChar.ToLogString()));
}
DispatchBotEvent(bot => bot.OnDeath());
}

View file

@ -36,9 +36,10 @@
<PackageReference Include="DnsClient" Version="1.6.1" />
<PackageReference Include="DotNetZip" Version="1.16.0" />
<PackageReference Include="DynamicExpresso.Core" Version="2.13.0" />
<PackageReference Include="Microsoft.CodeAnalysis.CSharp" Version="4.3.0" />
<PackageReference Include="Microsoft.CodeAnalysis.CSharp" Version="4.3.1" />
<PackageReference Include="Microsoft.CSharp" Version="4.7.0" />
<PackageReference Include="Microsoft.Windows.Compatibility" Version="5.0.2" />
<PackageReference Include="Microsoft.Windows.Compatibility" Version="6.0.0" />
<PackageReference Include="Samboy063.Tomlet" Version="5.0.0" />
<PackageReference Include="SingleFileExtractor.Core" Version="1.0.1" />
<PackageReference Include="starksoft.aspen" Version="1.1.8">
<NoWarn>NU1701</NoWarn>
@ -72,4 +73,9 @@
<ItemGroup>
<ProjectReference Include="..\ConsoleInteractive\ConsoleInteractive\ConsoleInteractive\ConsoleInteractive.csproj" />
</ItemGroup>
<ItemGroup>
<EmbeddedResource Update="DefaultConfigResource.resx">
<Generator></Generator>
</EmbeddedResource>
</ItemGroup>
</Project>

View file

@ -6,6 +6,7 @@ using System.Linq;
using System.Reflection;
using System.Runtime.InteropServices;
using System.Text;
using System.Text.RegularExpressions;
using System.Threading;
using System.Threading.Tasks;
using MinecraftClient.Inventory.ItemPalettes;
@ -16,6 +17,12 @@ using MinecraftClient.Protocol.Handlers.Forge;
using MinecraftClient.Protocol.Keys;
using MinecraftClient.Protocol.Session;
using MinecraftClient.WinAPI;
using Tomlet;
using Tomlet.Models;
using static MinecraftClient.Settings;
using static MinecraftClient.Settings.MainConfigHealper.MainConfig.GeneralConfig;
using static MinecraftClient.Settings.MainConfigHealper.MainConfig.AdvancedConfig;
using static System.Net.Mime.MediaTypeNames;
namespace MinecraftClient
{
@ -104,12 +111,12 @@ namespace MinecraftClient
ConsoleIO.DebugReadInput();
}
settingsIniPath = "MinecraftClient.ini";
//Process ini configuration file
if (args.Length >= 1 && System.IO.File.Exists(args[0]) && Settings.ToLowerIfNeed(Path.GetExtension(args[0])) == ".ini")
bool needWriteDefaultSetting, newlyGenerated = false;
settingsIniPath = "MinecraftClient.ini";
if (args.Length >= 1 && File.Exists(args[0]) && Settings.ToLowerIfNeed(Path.GetExtension(args[0])) == ".ini")
{
Settings.LoadFile(args[0]);
needWriteDefaultSetting = Settings.LoadFromFile(args[0]);
settingsIniPath = args[0];
//remove ini configuration file from arguments array
@ -117,14 +124,34 @@ namespace MinecraftClient
args_tmp.RemoveAt(0);
args = args_tmp.ToArray();
}
else if (System.IO.File.Exists("MinecraftClient.ini"))
else if (File.Exists("MinecraftClient.ini"))
{
Settings.LoadFile("MinecraftClient.ini");
needWriteDefaultSetting = Settings.LoadFromFile("MinecraftClient.ini");
}
else
{
needWriteDefaultSetting = true;
newlyGenerated = true;
}
else Settings.WriteDefaultSettings("MinecraftClient.ini");
//Load external translation file. Should be called AFTER settings loaded
Translations.LoadExternalTranslationFile(Settings.Language);
if (needWriteDefaultSetting)
{
(string gameLanguage, string[] langList) = Translations.GetTranslationPriority();
Translations.LoadTranslationFile(langList);
Config.Main.Advanced.Language = gameLanguage;
Settings.WriteToFile("MinecraftClient.ini", false);
if (newlyGenerated)
ConsoleIO.WriteLineFormatted(Translations.TryGet("mcc.settings_generated"));
ConsoleIO.WriteLine(Translations.TryGet("mcc.run_with_default_settings"));
}
else
{
//Load external translation file. Should be called AFTER settings loaded
Translations.LoadTranslationFile(Translations.GetTranslationPriority(Config.Main.Advanced.Language));
if (!Config.Main.Advanced.Language.StartsWith("en"))
ConsoleIO.WriteLine(Translations.TryGet("mcc.help_us_translate"));
Settings.WriteToFile("MinecraftClient.ini", true); // format
}
//Other command-line arguments
if (args.Length >= 1)
@ -222,55 +249,56 @@ namespace MinecraftClient
}
catch (ArgumentException e)
{
Settings.interactiveMode = false;
InternalConfig.InteractiveMode = false;
HandleFailure(e.Message);
return;
}
}
if (Settings.ConsoleTitle != "")
if (Config.Main.Advanced.ConsoleTitle != "")
{
Settings.Username = "New Window";
Console.Title = Settings.ExpandVars(Settings.ConsoleTitle);
InternalConfig.Username = "New Window";
Console.Title = Config.AppVar.ExpandVars(Config.Main.Advanced.ConsoleTitle);
}
//Test line to troubleshoot invisible colors
if (Settings.DebugMessages)
if (Config.Logging.DebugMessages)
{
ConsoleIO.WriteLineFormatted(Translations.Get("debug.color_test", "[0123456789ABCDEF]: [§00§11§22§33§44§55§66§77§88§99§aA§bB§cC§dD§eE§fF§r]"));
}
//Load cached sessions from disk if necessary
if (Settings.SessionCaching == CacheType.Disk)
if (Config.Main.Advanced.SessionCache == CacheType.disk)
{
bool cacheLoaded = SessionCache.InitializeDiskCache();
if (Settings.DebugMessages)
if (Config.Logging.DebugMessages)
Translations.WriteLineFormatted(cacheLoaded ? "debug.session_cache_ok" : "debug.session_cache_fail");
}
// Setup exit cleaning code
ExitCleanUp.Add(() => { DoExit(0); });
//Asking the user to type in missing data such as Username and Password
bool useBrowser = Settings.AccountType == ProtocolHandler.AccountType.Microsoft && Settings.LoginMethod == "browser";
if (Settings.Login == "" && !useBrowser)
bool useBrowser = Config.Main.General.AccountType == LoginType.microsoft && Config.Main.General.Method == LoginMethod.browser;
if (string.IsNullOrEmpty(Config.Main.General.Account.Login) && !useBrowser)
{
ConsoleIO.WriteLine(ConsoleIO.BasicIO ? Translations.Get("mcc.login_basic_io") : Translations.Get("mcc.login"));
Settings.Login = ConsoleIO.ReadLine();
Config.Main.General.Account.Login = ConsoleIO.ReadLine().Trim();
if (string.IsNullOrEmpty(Config.Main.General.Account.Login))
{
HandleFailure(Translations.Get("error.login.blocked"), false, ChatBot.DisconnectReason.LoginRejected);
return;
}
}
if (Settings.Password == ""
&& (Settings.SessionCaching == CacheType.None || !SessionCache.Contains(Settings.ToLowerIfNeed(Settings.Login)))
&& !useBrowser)
if (string.IsNullOrEmpty(Config.Main.General.Account.Password) && !useBrowser &&
(Config.Main.Advanced.SessionCache == CacheType.none || !SessionCache.Contains(Settings.ToLowerIfNeed(Config.Main.General.Account.Login))))
{
RequestPassword();
}
// Setup exit cleaning code
ExitCleanUp.Add(delegate ()
else
{
// Do NOT use Program.Exit() as creating new Thread cause program to freeze
if (client != null) { client.Disconnect(); ConsoleIO.Reset(); }
if (offlinePrompt != null) { offlinePrompt.Item2.Cancel(); offlinePrompt = null; ConsoleIO.Reset(); }
if (Settings.playerHeadAsIcon) { ConsoleIcon.RevertToMCCIcon(); }
});
InternalConfig.Password = Config.Main.General.Account.Password;
}
startupargs = args;
InitializeClient();
@ -281,10 +309,10 @@ namespace MinecraftClient
/// </summary>
private static void RequestPassword()
{
ConsoleIO.WriteLine(ConsoleIO.BasicIO ? Translations.Get("mcc.password_basic_io", Settings.Login) + "\n" : Translations.Get("mcc.password"));
ConsoleIO.WriteLine(ConsoleIO.BasicIO ? Translations.Get("mcc.password_basic_io", Config.Main.General.Account.Login) + "\n" : Translations.Get("mcc.password"));
string? password = ConsoleIO.BasicIO ? Console.ReadLine() : ConsoleIO.ReadPassword();
if (password == null || password == string.Empty) { password = "-"; }
Settings.Password = password;
InternalConfig.Password = password;
}
/// <summary>
@ -292,23 +320,25 @@ namespace MinecraftClient
/// </summary>
private static void InitializeClient()
{
InternalConfig.MinecraftVersion = Config.Main.Advanced.MinecraftVersion;
SessionToken session = new();
PlayerKeyPair? playerKeyPair = null;
ProtocolHandler.LoginResult result = ProtocolHandler.LoginResult.LoginRequired;
string loginLower = Settings.ToLowerIfNeed(Settings.Login);
if (Settings.Password == "-")
string loginLower = Settings.ToLowerIfNeed(Config.Main.General.Account.Login);
if (InternalConfig.Password == "-")
{
Translations.WriteLineFormatted("mcc.offline");
result = ProtocolHandler.LoginResult.Success;
session.PlayerID = "0";
session.PlayerName = Settings.Login;
session.PlayerName = Config.Main.General.Account.Login;
}
else
{
// Validate cached session or login new session.
if (Settings.SessionCaching != CacheType.None && SessionCache.Contains(loginLower))
if (Config.Main.Advanced.SessionCache != CacheType.none && SessionCache.Contains(loginLower))
{
session = SessionCache.Get(loginLower);
result = ProtocolHandler.GetTokenValidation(session);
@ -330,8 +360,8 @@ namespace MinecraftClient
}
if (result != ProtocolHandler.LoginResult.Success
&& Settings.Password == ""
&& Settings.AccountType == ProtocolHandler.AccountType.Mojang)
&& InternalConfig.Password == ""
&& Config.Main.General.AccountType == LoginType.mojang)
RequestPassword();
}
else ConsoleIO.WriteLineFormatted(Translations.Get("mcc.session_valid", session.PlayerName));
@ -339,12 +369,12 @@ namespace MinecraftClient
if (result != ProtocolHandler.LoginResult.Success)
{
Translations.WriteLine("mcc.connecting", Settings.AccountType == ProtocolHandler.AccountType.Mojang ? "Minecraft.net" : "Microsoft");
result = ProtocolHandler.GetLogin(Settings.Login, Settings.Password, Settings.AccountType, out session);
Translations.WriteLine("mcc.connecting", Config.Main.General.AccountType == LoginType.mojang ? "Minecraft.net" : "Microsoft");
result = ProtocolHandler.GetLogin(Config.Main.General.Account.Login, InternalConfig.Password, Config.Main.General.AccountType, out session);
}
}
if (result == ProtocolHandler.LoginResult.Success && Settings.SessionCaching != CacheType.None)
if (result == ProtocolHandler.LoginResult.Success && Config.Main.Advanced.SessionCache != CacheType.none)
SessionCache.Store(loginLower, session);
if (result == ProtocolHandler.LoginResult.Success)
@ -352,17 +382,17 @@ namespace MinecraftClient
if (result == ProtocolHandler.LoginResult.Success)
{
if (Settings.AccountType == ProtocolHandler.AccountType.Microsoft && Settings.Password != "-" && Settings.LoginWithSecureProfile)
if (Config.Main.General.AccountType == LoginType.microsoft && InternalConfig.Password != "-" && Config.Signature.LoginWithSecureProfile)
{
// Load cached profile key from disk if necessary
if (Settings.ProfileKeyCaching == CacheType.Disk)
if (Config.Main.Advanced.ProfileKeyCache == CacheType.disk)
{
bool cacheKeyLoaded = KeysCache.InitializeDiskCache();
if (Settings.DebugMessages)
if (Config.Logging.DebugMessages)
Translations.WriteLineFormatted(cacheKeyLoaded ? "debug.keys_cache_ok" : "debug.keys_cache_fail");
}
if (Settings.ProfileKeyCaching != CacheType.None && KeysCache.Contains(loginLower))
if (Config.Main.Advanced.ProfileKeyCache != CacheType.none && KeysCache.Contains(loginLower))
{
playerKeyPair = KeysCache.Get(loginLower);
if (playerKeyPair.NeedRefresh())
@ -375,36 +405,36 @@ namespace MinecraftClient
{
Translations.WriteLineFormatted("mcc.fetching_key");
playerKeyPair = KeyUtils.GetNewProfileKeys(session.ID);
if (Settings.ProfileKeyCaching != CacheType.None && playerKeyPair != null)
if (Config.Main.Advanced.ProfileKeyCache != CacheType.none && playerKeyPair != null)
{
KeysCache.Store(loginLower, playerKeyPair);
}
}
}
Settings.Username = session.PlayerName;
InternalConfig.Username = session.PlayerName;
bool isRealms = false;
if (Settings.ConsoleTitle != "")
Console.Title = Settings.ExpandVars(Settings.ConsoleTitle);
if (Config.Main.Advanced.ConsoleTitle != "")
Console.Title = Config.AppVar.ExpandVars(Config.Main.Advanced.ConsoleTitle);
if (Settings.playerHeadAsIcon)
ConsoleIcon.SetPlayerIconAsync(Settings.Username);
if (Config.Main.Advanced.PlayerHeadAsIcon)
ConsoleIcon.SetPlayerIconAsync(InternalConfig.Username);
if (Settings.DebugMessages)
if (Config.Logging.DebugMessages)
Translations.WriteLine("debug.session_id", session.ID);
List<string> availableWorlds = new();
if (Settings.MinecraftRealmsEnabled && !String.IsNullOrEmpty(session.ID))
availableWorlds = ProtocolHandler.RealmsListWorlds(Settings.Username, session.PlayerID, session.ID);
if (Config.Main.Advanced.MinecraftRealms && !String.IsNullOrEmpty(session.ID))
availableWorlds = ProtocolHandler.RealmsListWorlds(InternalConfig.Username, session.PlayerID, session.ID);
if (Settings.ServerIP == "")
if (InternalConfig.ServerIP == string.Empty)
{
Translations.Write("mcc.ip");
string addressInput = ConsoleIO.ReadLine();
if (addressInput.StartsWith("realms:"))
{
if (Settings.MinecraftRealmsEnabled)
if (Config.Main.Advanced.MinecraftRealms)
{
if (availableWorlds.Count == 0)
{
@ -416,12 +446,12 @@ namespace MinecraftClient
worldId = availableWorlds[worldIndex];
if (availableWorlds.Contains(worldId))
{
string RealmsAddress = ProtocolHandler.GetRealmsWorldServerAddress(worldId, Settings.Username, session.PlayerID, session.ID);
string RealmsAddress = ProtocolHandler.GetRealmsWorldServerAddress(worldId, InternalConfig.Username, session.PlayerID, session.ID);
if (RealmsAddress != "")
{
addressInput = RealmsAddress;
isRealms = true;
Settings.ServerVersion = MCHighestVersion;
InternalConfig.MinecraftVersion = MCHighestVersion;
}
else
{
@ -441,37 +471,37 @@ namespace MinecraftClient
return;
}
}
Settings.SetServerIP(addressInput);
Config.Main.SetServerIP(new MainConfigHealper.MainConfig.ServerInfoConfig(addressInput), true);
}
//Get server version
int protocolversion = 0;
ForgeInfo? forgeInfo = null;
if (Settings.ServerVersion != "" && Settings.ToLowerIfNeed(Settings.ServerVersion) != "auto")
if (InternalConfig.MinecraftVersion != "" && Settings.ToLowerIfNeed(InternalConfig.MinecraftVersion) != "auto")
{
protocolversion = Protocol.ProtocolHandler.MCVer2ProtocolVersion(Settings.ServerVersion);
protocolversion = Protocol.ProtocolHandler.MCVer2ProtocolVersion(InternalConfig.MinecraftVersion);
if (protocolversion != 0)
{
ConsoleIO.WriteLineFormatted(Translations.Get("mcc.use_version", Settings.ServerVersion, protocolversion));
}
else ConsoleIO.WriteLineFormatted(Translations.Get("mcc.unknown_version", Settings.ServerVersion));
ConsoleIO.WriteLineFormatted(Translations.Get("mcc.use_version", InternalConfig.MinecraftVersion, protocolversion));
else
ConsoleIO.WriteLineFormatted(Translations.Get("mcc.unknown_version", InternalConfig.MinecraftVersion));
if (useMcVersionOnce)
{
useMcVersionOnce = false;
Settings.ServerVersion = "";
InternalConfig.MinecraftVersion = "";
}
}
//Retrieve server info if version is not manually set OR if need to retrieve Forge information
if (!isRealms && (protocolversion == 0 || Settings.ServerAutodetectForge || (Settings.ServerForceForge && !ProtocolHandler.ProtocolMayForceForge(protocolversion))))
if (!isRealms && (protocolversion == 0 || (Config.Main.Advanced.EnableForge == ForgeConfigType.auto) ||
((Config.Main.Advanced.EnableForge == ForgeConfigType.force) && !ProtocolHandler.ProtocolMayForceForge(protocolversion))))
{
if (protocolversion != 0)
Translations.WriteLine("mcc.forge");
else Translations.WriteLine("mcc.retrieve");
if (!ProtocolHandler.GetServerInfo(Settings.ServerIP, Settings.ServerPort, ref protocolversion, ref forgeInfo))
if (!ProtocolHandler.GetServerInfo(InternalConfig.ServerIP, InternalConfig.ServerPort, ref protocolversion, ref forgeInfo))
{
HandleFailure(Translations.Get("error.ping"), true, ChatBots.AutoRelog.DisconnectReason.ConnectionLost);
return;
@ -479,7 +509,7 @@ namespace MinecraftClient
}
//Force-enable Forge support?
if (!isRealms && Settings.ServerForceForge && forgeInfo == null)
if (!isRealms && (Config.Main.Advanced.EnableForge == ForgeConfigType.force) && forgeInfo == null)
{
if (ProtocolHandler.ProtocolMayForceForge(protocolversion))
{
@ -499,12 +529,11 @@ namespace MinecraftClient
try
{
//Start the main TCP client
string? command = String.IsNullOrEmpty(Settings.SingleCommand) ? null : Settings.SingleCommand;
client = new McClient(session, playerKeyPair, Settings.ServerIP, Settings.ServerPort, protocolversion, forgeInfo, command);
client = new McClient(session, playerKeyPair, InternalConfig.ServerIP, InternalConfig.ServerPort, protocolversion, forgeInfo);
//Update console title
if (Settings.ConsoleTitle != "")
Console.Title = Settings.ExpandVars(Settings.ConsoleTitle);
if (Config.Main.Advanced.ConsoleTitle != "")
Console.Title = Config.AppVar.ExpandVars(Config.Main.Advanced.ConsoleTitle);
}
catch (NotSupportedException)
{
@ -543,7 +572,8 @@ namespace MinecraftClient
public static void ReloadSettings()
{
if (settingsIniPath != null)
Settings.LoadFile(settingsIniPath);
if(Settings.LoadFromFile(settingsIniPath))
ConsoleIO.WriteLine(Translations.TryGet("config.loading", settingsIniPath));
}
/// <summary>
@ -563,22 +593,30 @@ namespace MinecraftClient
System.Threading.Thread.Sleep(delaySeconds * 1000);
}
Translations.WriteLine("mcc.restart");
ReloadSettings();
InitializeClient();
})).Start();
}
public static void DoExit(int exitcode = 0)
{
if (settingsIniPath != null)
{
Settings.WriteToFile(settingsIniPath, true);
ConsoleIO.WriteLineFormatted(Translations.TryGet("config.saving", settingsIniPath));
}
if (client != null) { client.Disconnect(); ConsoleIO.Reset(); }
if (offlinePrompt != null) { offlinePrompt.Item2.Cancel(); offlinePrompt.Item1.Join(); offlinePrompt = null; ConsoleIO.Reset(); }
if (Config.Main.Advanced.PlayerHeadAsIcon) { ConsoleIcon.RevertToMCCIcon(); }
Environment.Exit(exitcode);
}
/// <summary>
/// Disconnect the current client from the server and exit the app
/// </summary>
public static void Exit(int exitcode = 0)
{
new Thread(new ThreadStart(delegate
{
if (client != null) { client.Disconnect(); ConsoleIO.Reset(); }
if (offlinePrompt != null) { offlinePrompt.Item2.Cancel(); offlinePrompt.Item1.Join(); offlinePrompt = null; ConsoleIO.Reset(); }
if (Settings.playerHeadAsIcon) { ConsoleIcon.RevertToMCCIcon(); }
Environment.Exit(exitcode);
})).Start();
new Thread(new ThreadStart(() => { DoExit(exitcode); })).Start();
}
/// <summary>
@ -595,7 +633,7 @@ namespace MinecraftClient
ConsoleIO.Reset();
while (Console.KeyAvailable)
Console.ReadKey(true);
Console.WriteLine(errorMessage);
ConsoleIO.WriteLine(errorMessage);
if (disconnectReason.HasValue)
{
@ -604,13 +642,13 @@ namespace MinecraftClient
}
}
if (Settings.interactiveMode)
if (InternalConfig.InteractiveMode)
{
if (versionError)
{
Translations.Write("mcc.server_version");
Settings.ServerVersion = ConsoleInteractive.ConsoleReader.RequestImmediateInput();
if (Settings.ServerVersion != "")
InternalConfig.MinecraftVersion = ConsoleInteractive.ConsoleReader.RequestImmediateInput();
if (InternalConfig.MinecraftVersion != "")
{
useMcVersionOnce = true;
Restart();
@ -627,7 +665,8 @@ namespace MinecraftClient
{
bool exitThread = false;
string command = " ";
ConsoleIO.WriteLineFormatted(Translations.Get("mcc.disconnected", (Settings.internalCmdChar == ' ' ? "" : "" + Settings.internalCmdChar)));
ConsoleIO.WriteLine(string.Empty);
ConsoleIO.WriteLineFormatted(Translations.Get("mcc.disconnected", Config.Main.Advanced.InternalCmdChar.ToLogString()));
Translations.WriteLineFormatted("mcc.press_exit");
while (!cancellationTokenSource.IsCancellationRequested)
@ -645,13 +684,13 @@ namespace MinecraftClient
{
string message = "";
if (Settings.internalCmdChar != ' '
&& command[0] == Settings.internalCmdChar)
if (Config.Main.Advanced.InternalCmdChar.ToChar() != ' '
&& command[0] == Config.Main.Advanced.InternalCmdChar.ToChar())
command = command[1..];
if (command.StartsWith("reco"))
{
message = new Commands.Reco().Run(null, Settings.ExpandVars(command), null);
message = new Commands.Reco().Run(null, Config.AppVar.ExpandVars(command), null);
if (message == "")
{
exitThread = true;
@ -660,7 +699,7 @@ namespace MinecraftClient
}
else if (command.StartsWith("connect"))
{
message = new Commands.Connect().Run(null, Settings.ExpandVars(command), null);
message = new Commands.Connect().Run(null, Config.AppVar.ExpandVars(command), null);
if (message == "")
{
exitThread = true;
@ -669,31 +708,26 @@ namespace MinecraftClient
}
else if (command.StartsWith("exit") || command.StartsWith("quit"))
{
message = new Commands.Exit().Run(null, Settings.ExpandVars(command), null);
message = new Commands.Exit().Run(null, Config.AppVar.ExpandVars(command), null);
}
else if (command.StartsWith("help"))
{
ConsoleIO.WriteLineFormatted("§8MCC: " +
(Settings.internalCmdChar == ' '
? ""
: "" + Settings.internalCmdChar) +
Config.Main.Advanced.InternalCmdChar.ToLogString() +
new Commands.Reco().GetCmdDescTranslated());
ConsoleIO.WriteLineFormatted("§8MCC: " +
(Settings.internalCmdChar == ' '
? ""
: "" + Settings.internalCmdChar) +
Config.Main.Advanced.InternalCmdChar.ToLogString() +
new Commands.Connect().GetCmdDescTranslated());
}
else
ConsoleIO.WriteLineFormatted(Translations.Get("icmd.unknown",
command.Split(' ')[0]));
ConsoleIO.WriteLineFormatted(Translations.Get("icmd.unknown", command.Split(' ')[0]));
if (message != "")
ConsoleIO.WriteLineFormatted("§8MCC: " + message);
}
else
{
_ = new Commands.Exit().Run(null, Settings.ExpandVars(command), null);
_ = new Commands.Exit().Run(null, Config.AppVar.ExpandVars(command), null);
}
}

View file

@ -61,7 +61,7 @@ namespace MinecraftClient.Protocol.Handlers.PacketPalettes
}
else if (forgeEnabled)
{
if (Settings.DebugMessages)
if (Settings.Config.Logging.DebugMessages)
ConsoleIO.WriteLogLine("Ignoring unknown packet ID of 0x" + packetId.ToString("X2"));
return PacketTypesIn.Unknown;
}
@ -92,7 +92,7 @@ namespace MinecraftClient.Protocol.Handlers.PacketPalettes
}
else if (forgeEnabled)
{
if (Settings.DebugMessages)
if (Settings.Config.Logging.DebugMessages)
ConsoleIO.WriteLogLine("Ignoring unknown packet ID of 0x" + packetId.ToString("X2"));
return PacketTypesOut.Unknown;
}

View file

@ -13,6 +13,7 @@ using MinecraftClient.Protocol.Keys;
using MinecraftClient.Protocol.Message;
using MinecraftClient.Protocol.Session;
using MinecraftClient.Proxy;
using static MinecraftClient.Settings;
namespace MinecraftClient.Protocol.Handlers
{
@ -494,7 +495,7 @@ namespace MinecraftClient.Protocol.Handlers
if (serverID == "-")
Translations.WriteLineFormatted("mcc.server_offline");
else if (Settings.DebugMessages)
else if (Settings.Config.Logging.DebugMessages)
ConsoleIO.WriteLineFormatted(Translations.Get("mcc.handshake", serverID));
return StartEncryption(uuid, username, sessionID, token, serverID, PublicServerkey, session);
@ -511,7 +512,7 @@ namespace MinecraftClient.Protocol.Handlers
RSACryptoServiceProvider RSAService = CryptoHandler.DecodeRSAPublicKey(serverPublicKey)!;
byte[] secretKey = CryptoHandler.ClientAESPrivateKey ?? CryptoHandler.GenerateAESPrivateKey();
if (Settings.DebugMessages)
if (Settings.Config.Logging.DebugMessages)
Translations.WriteLineFormatted("debug.crypto");
if (serverIDhash != "-")
@ -534,7 +535,7 @@ namespace MinecraftClient.Protocol.Handlers
{
session.ServerIDhash = serverIDhash;
session.ServerPublicKey = serverPublicKey;
SessionCache.Store(Settings.Login.ToLower(), session);
SessionCache.Store(Config.Main.General.Account.Login.ToLower(), session);
}
else
{
@ -862,7 +863,7 @@ namespace MinecraftClient.Protocol.Handlers
Protocol16Handler ComTmp = new(tcp);
string result = ComTmp.ReadNextString();
if (Settings.DebugMessages)
if (Settings.Config.Logging.DebugMessages)
{
// May contain formatting codes, cannot use WriteLineFormatted
Console.ForegroundColor = ConsoleColor.DarkGray;

View file

@ -22,6 +22,7 @@ using MinecraftClient.Protocol.Keys;
using MinecraftClient.Protocol.Message;
using MinecraftClient.Protocol.Session;
using MinecraftClient.Proxy;
using static MinecraftClient.Settings;
namespace MinecraftClient.Protocol.Handlers
{
@ -461,8 +462,8 @@ namespace MinecraftClient.Protocol.Handlers
{
//Hide system messages or xp bar messages?
messageType = dataTypes.ReadNextByte(packetData);
if ((messageType == 1 && !Settings.DisplaySystemMessages)
|| (messageType == 2 && !Settings.DisplayXPBarMessages))
if ((messageType == 1 && !Config.Main.Advanced.ShowSystemMessages)
|| (messageType == 2 && !Config.Main.Advanced.ShowSystemMessages))
break;
if (protocolVersion >= MC_1_16_5_Version)
@ -482,8 +483,8 @@ namespace MinecraftClient.Protocol.Handlers
string? unsignedChatContent = hasUnsignedChatContent ? dataTypes.ReadNextString(packetData) : null;
messageType = dataTypes.ReadNextVarInt(packetData);
if ((messageType == 1 && !Settings.DisplaySystemMessages)
|| (messageType == 2 && !Settings.DisplayXPBarMessages))
if ((messageType == 1 && !Config.Main.Advanced.ShowSystemMessages)
|| (messageType == 2 && !Config.Main.Advanced.ShowXPBarMessages))
break;
Guid senderUUID = dataTypes.ReadNextUUID(packetData);
@ -1603,7 +1604,7 @@ namespace MinecraftClient.Protocol.Handlers
case PacketTypesIn.SystemChat:
string systemMessage = dataTypes.ReadNextString(packetData);
int msgType = dataTypes.ReadNextVarInt(packetData);
if ((msgType == 1 && !Settings.DisplaySystemMessages))
if ((msgType == 1 && !Config.Main.Advanced.ShowSystemMessages))
break;
handler.OnTextReceived(new(systemMessage, true, msgType, Guid.Empty, true));
break;
@ -1925,7 +1926,7 @@ namespace MinecraftClient.Protocol.Handlers
{
session.ServerIDhash = serverIDhash;
session.ServerPublicKey = serverPublicKey;
SessionCache.Store(Settings.Login.ToLower(), session);
SessionCache.Store(Config.Main.General.Account.Login.ToLower(), session);
}
else
{
@ -2119,7 +2120,7 @@ namespace MinecraftClient.Protocol.Handlers
{
string result = dataTypes.ReadNextString(packetData); //Get the Json data
if (Settings.DebugMessages)
if (Config.Logging.DebugMessages)
{
// May contain formatting codes, cannot use WriteLineFormatted
Console.ForegroundColor = ConsoleColor.DarkGray;
@ -2230,7 +2231,7 @@ namespace MinecraftClient.Protocol.Handlers
/// <returns> List< Argument Name, Argument Value > </returns>
private List<Tuple<string, string>>? CollectCommandArguments(string command)
{
if (!isOnlineMode || !Settings.SignMessageInCommand)
if (!isOnlineMode || !Config.Signature.SignMessageInCommand)
return null;
List<Tuple<string, string>> needSigned = new();
@ -2371,7 +2372,7 @@ namespace MinecraftClient.Protocol.Handlers
DateTimeOffset timeNow = DateTimeOffset.UtcNow;
fields.AddRange(dataTypes.GetLong(timeNow.ToUnixTimeMilliseconds()));
if (!isOnlineMode || playerKeyPair == null || !Settings.SignChat)
if (!isOnlineMode || playerKeyPair == null || !Config.Signature.SignChat)
{
fields.AddRange(dataTypes.GetLong(0)); // Salt: Long
fields.AddRange(dataTypes.GetVarInt(0)); // Signature Length: VarInt

View file

@ -130,7 +130,7 @@ namespace MinecraftClient.Protocol.Handlers
byte fmlProtocolVersion = dataTypes.ReadNextByte(packetData);
if (Settings.DebugMessages)
if (Settings.Config.Logging.DebugMessages)
ConsoleIO.WriteLineFormatted(Translations.Get("forge.version", fmlProtocolVersion));
if (fmlProtocolVersion >= 1)
@ -140,7 +140,7 @@ namespace MinecraftClient.Protocol.Handlers
SendForgeHandshakePacket(FMLHandshakeDiscriminator.ClientHello, new byte[] { fmlProtocolVersion });
// Then tell the server that we're running the same mods.
if (Settings.DebugMessages)
if (Settings.Config.Logging.DebugMessages)
Translations.WriteLineFormatted("forge.send_mod");
byte[][] mods = new byte[forgeInfo.Mods.Count][];
for (int i = 0; i < forgeInfo.Mods.Count; i++)
@ -160,7 +160,7 @@ namespace MinecraftClient.Protocol.Handlers
Thread.Sleep(2000);
if (Settings.DebugMessages)
if (Settings.Config.Logging.DebugMessages)
Translations.WriteLineFormatted("forge.accept");
// Tell the server that yes, we are OK with the mods it has
// even though we don't actually care what mods it has.
@ -182,7 +182,7 @@ namespace MinecraftClient.Protocol.Handlers
// with blocks and items.
int registrySize = dataTypes.ReadNextVarInt(packetData);
if (Settings.DebugMessages)
if (Settings.Config.Logging.DebugMessages)
ConsoleIO.WriteLineFormatted(Translations.Get("forge.registry", registrySize));
fmlHandshakeState = FMLHandshakeClientState.PENDINGCOMPLETE;
@ -194,7 +194,7 @@ namespace MinecraftClient.Protocol.Handlers
bool hasNextRegistry = dataTypes.ReadNextBool(packetData);
string registryName = dataTypes.ReadNextString(packetData);
int registrySize = dataTypes.ReadNextVarInt(packetData);
if (Settings.DebugMessages)
if (Settings.Config.Logging.DebugMessages)
ConsoleIO.WriteLineFormatted(Translations.Get("forge.registry_2", registryName, registrySize));
if (!hasNextRegistry)
fmlHandshakeState = FMLHandshakeClientState.PENDINGCOMPLETE;
@ -206,7 +206,7 @@ namespace MinecraftClient.Protocol.Handlers
// Just say yes.
if (discriminator != FMLHandshakeDiscriminator.HandshakeAck)
return false;
if (Settings.DebugMessages)
if (Settings.Config.Logging.DebugMessages)
Translations.WriteLineFormatted("forge.accept_registry");
SendForgeHandshakePacket(FMLHandshakeDiscriminator.HandshakeAck,
new byte[] { (byte)FMLHandshakeClientState.PENDINGCOMPLETE });
@ -220,7 +220,7 @@ namespace MinecraftClient.Protocol.Handlers
SendForgeHandshakePacket(FMLHandshakeDiscriminator.HandshakeAck,
new byte[] { (byte)FMLHandshakeClientState.COMPLETE });
if (Settings.DebugMessages)
if (Settings.Config.Logging.DebugMessages)
Translations.WriteLine("forge.complete");
fmlHandshakeState = FMLHandshakeClientState.DONE;
return true;
@ -300,7 +300,7 @@ namespace MinecraftClient.Protocol.Handlers
//
// [1]: Version is usually set to "FML2" for FML stuff and "1" for mods
if (Settings.DebugMessages)
if (Settings.Config.Logging.DebugMessages)
Translations.WriteLineFormatted("forge.fml2.mod");
List<string> mods = new();
@ -332,7 +332,7 @@ namespace MinecraftClient.Protocol.Handlers
// We are supposed to validate server info against our set of installed mods, then reply with our list
// In MCC, we just want to send a valid response so we'll reply back with data collected from the server.
if (Settings.DebugMessages)
if (Settings.Config.Logging.DebugMessages)
Translations.WriteLineFormatted("forge.fml2.mod_send");
// Packet ID 2: Client to Server Mod List
@ -368,7 +368,7 @@ namespace MinecraftClient.Protocol.Handlers
// Registry Snapshot: ForgeRegistry.java > Snapshot > read(PacketBuffer)
// Not documented yet. We're ignoring this packet in MCC
if (Settings.DebugMessages)
if (Settings.Config.Logging.DebugMessages)
{
string registryName = dataTypes.ReadNextString(packetData);
ConsoleIO.WriteLineFormatted(Translations.Get("forge.fml2.registry", registryName));
@ -387,7 +387,7 @@ namespace MinecraftClient.Protocol.Handlers
// [1] Config data may containt a standard Minecraft string readable with dataTypes.readNextString()
// We're ignoring this packet in MCC
if (Settings.DebugMessages)
if (Settings.Config.Logging.DebugMessages)
{
string configName = dataTypes.ReadNextString(packetData);
ConsoleIO.WriteLineFormatted(Translations.Get("forge.fml2.config", configName));
@ -398,7 +398,7 @@ namespace MinecraftClient.Protocol.Handlers
break;
default:
if (Settings.DebugMessages)
if (Settings.Config.Logging.DebugMessages)
ConsoleIO.WriteLineFormatted(Translations.Get("forge.fml2.unknown", packetID));
break;
}
@ -413,7 +413,7 @@ namespace MinecraftClient.Protocol.Handlers
return true;
}
}
else if (Settings.DebugMessages)
else if (Settings.Config.Logging.DebugMessages)
{
ConsoleIO.WriteLineFormatted(Translations.Get("forge.fml2.unknown_channel", fmlChannel));
}
@ -505,7 +505,7 @@ namespace MinecraftClient.Protocol.Handlers
if (forgeInfo.Mods.Any())
{
ConsoleIO.WriteLineFormatted(Translations.Get("forge.with_mod", forgeInfo.Mods.Count));
if (Settings.DebugMessages)
if (Settings.Config.Logging.DebugMessages)
{
Translations.WriteLineFormatted("forge.mod_list");
foreach (ForgeInfo.ForgeMod mod in forgeInfo.Mods)

View file

@ -5,13 +5,13 @@ using System.Net.Http;
using System.Text;
using System.Threading.Tasks;
using MinecraftClient.Protocol.Message;
using static MinecraftClient.Settings;
namespace MinecraftClient.Protocol
{
/// <summary>
/// This class parses JSON chat data from MC 1.6+ and returns the appropriate string to be printed.
/// </summary>
static class ChatParser
{
public enum MessageType
@ -47,7 +47,7 @@ namespace MinecraftClient.Protocol
/// <returns>Returns the translated text</returns>
public static string ParseSignedChat(ChatMessage message, List<string>? links = null)
{
string chatContent = Settings.ShowModifiedChat && message.unsignedContent != null ? message.unsignedContent : message.content;
string chatContent = Config.Signature.ShowModifiedChat && message.unsignedContent != null ? message.unsignedContent : message.content;
string content = message.isJson ? ParseText(chatContent, links) : chatContent;
string sender = message.displayName!;
@ -107,27 +107,27 @@ namespace MinecraftClient.Protocol
string color = string.Empty;
if (message.isSystemChat)
{
if (Settings.MarkSystemMessage)
if (Config.Signature.MarkSystemMessage)
color = "§z §r "; // Custom color code §z : Background Gray
}
else
{
if ((bool)message.isSignatureLegal!)
{
if (Settings.ShowModifiedChat && message.unsignedContent != null)
if (Config.Signature.ShowModifiedChat && message.unsignedContent != null)
{
if (Settings.MarkModifiedMsg)
if (Config.Signature.MarkModifiedMsg)
color = "§x §r "; // Custom color code §x : Background Yellow
}
else
{
if (Settings.MarkLegallySignedMsg)
if (Config.Signature.MarkLegallySignedMsg)
color = "§y §r "; // Custom color code §y : Background Green
}
}
else
{
if (Settings.MarkIllegallySignedMsg)
if (Config.Signature.MarkIllegallySignedMsg)
color = "§w §r "; // Custom color code §w : Background Red
}
}
@ -203,12 +203,12 @@ namespace MinecraftClient.Protocol
if (!Directory.Exists("lang"))
Directory.CreateDirectory("lang");
string Language_File = "lang" + Path.DirectorySeparatorChar + Settings.Language + ".lang";
string Language_File = "lang" + Path.DirectorySeparatorChar + Config.Main.Advanced.Language + ".lang";
//File not found? Try downloading language file from Mojang's servers?
if (!File.Exists(Language_File))
{
ConsoleIO.WriteLineFormatted(Translations.Get("chat.download", Settings.Language));
ConsoleIO.WriteLineFormatted(Translations.Get("chat.download", Config.Main.Advanced.Language));
HttpClient httpClient = new();
try
{
@ -217,11 +217,11 @@ namespace MinecraftClient.Protocol
string assets_index = fetch_index.Result;
fetch_index.Dispose();
string[] tmp = assets_index.Split(new string[] { "minecraft/lang/" + Settings.Language.ToLower() + ".json" }, StringSplitOptions.None);
string[] tmp = assets_index.Split(new string[] { "minecraft/lang/" + Config.Main.Advanced.Language.ToLower() + ".json" }, StringSplitOptions.None);
tmp = tmp[1].Split(new string[] { "hash\": \"" }, StringSplitOptions.None);
string hash = tmp[1].Split('"')[0]; //Translations file identifier on Mojang's servers
string translation_file_location = Settings.TranslationsFile_Website_Download + '/' + hash[..2] + '/' + hash;
if (Settings.DebugMessages)
if (Settings.Config.Logging.DebugMessages)
ConsoleIO.WriteLineFormatted(Translations.Get("chat.request", translation_file_location));
Task<string> fetch_file = httpClient.GetStringAsync(translation_file_location);
@ -266,7 +266,7 @@ namespace MinecraftClient.Protocol
}
}
if (Settings.DebugMessages)
if (Settings.Config.Logging.DebugMessages)
Translations.WriteLineFormatted("chat.loaded");
}
else //No external dictionnary found.

View file

@ -6,6 +6,8 @@ using System.Globalization;
using System.Linq;
using System.Runtime.InteropServices;
using System.Text.RegularExpressions;
using static MinecraftClient.Settings;
using static MinecraftClient.Settings.MainConfigHealper.MainConfig.GeneralConfig;
namespace MinecraftClient.Protocol
{
@ -204,7 +206,7 @@ namespace MinecraftClient.Protocol
var response = request.Post("application/x-www-form-urlencoded", postData);
if (Settings.DebugMessages)
if (Settings.Config.Logging.DebugMessages)
{
ConsoleIO.WriteLine(response.ToString());
}
@ -271,7 +273,7 @@ namespace MinecraftClient.Protocol
request.Headers.Add("x-xbl-contract-version", "0");
var accessToken = loginResponse.AccessToken;
if (Settings.LoginMethod == "browser")
if (Config.Main.General.Method == LoginMethod.browser)
{
// Our own client ID must have d= in front of the token or HTTP status 400
// "Stolen" client ID must not have d= in front of the token or HTTP status 400
@ -288,7 +290,7 @@ namespace MinecraftClient.Protocol
+ "\"TokenType\": \"JWT\""
+ "}";
var response = request.Post("application/json", payload);
if (Settings.DebugMessages)
if (Settings.Config.Logging.DebugMessages)
{
ConsoleIO.WriteLine(response.ToString());
}
@ -338,7 +340,7 @@ namespace MinecraftClient.Protocol
+ "\"TokenType\": \"JWT\""
+ "}";
var response = request.Post("application/json", payload);
if (Settings.DebugMessages)
if (Settings.Config.Logging.DebugMessages)
{
ConsoleIO.WriteLine(response.ToString());
}
@ -418,7 +420,7 @@ namespace MinecraftClient.Protocol
string payload = "{\"identityToken\": \"XBL3.0 x=" + userHash + ";" + xstsToken + "\"}";
var response = request.Post("application/json", payload);
if (Settings.DebugMessages)
if (Settings.Config.Logging.DebugMessages)
{
ConsoleIO.WriteLine(response.ToString());
}
@ -440,7 +442,7 @@ namespace MinecraftClient.Protocol
request.Headers.Add("Authorization", string.Format("Bearer {0}", accessToken));
var response = request.Get();
if (Settings.DebugMessages)
if (Settings.Config.Logging.DebugMessages)
{
ConsoleIO.WriteLine(response.ToString());
}
@ -456,7 +458,7 @@ namespace MinecraftClient.Protocol
request.Headers.Add("Authorization", string.Format("Bearer {0}", accessToken));
var response = request.Get();
if (Settings.DebugMessages)
if (Settings.Config.Logging.DebugMessages)
{
ConsoleIO.WriteLine(response.ToString());
}

View file

@ -25,7 +25,7 @@ namespace MinecraftClient.Protocol.Keys
response = request.Post("application/json", "");
if (Settings.DebugMessages)
if (Settings.Config.Logging.DebugMessages)
{
ConsoleIO.WriteLine(response.Body.ToString());
}
@ -47,7 +47,7 @@ namespace MinecraftClient.Protocol.Keys
{
int code = (response == null) ? 0 : response.StatusCode;
ConsoleIO.WriteLineFormatted("§cFetch profile key failed: HttpCode = " + code + ", Error = " + e.Message);
if (Settings.DebugMessages)
if (Settings.Config.Logging.DebugMessages)
{
ConsoleIO.WriteLineFormatted("§c" + e.StackTrace);
}

View file

@ -4,6 +4,8 @@ using System.IO;
using System.Runtime.Serialization.Formatters.Binary;
using System.Timers;
using MinecraftClient.Protocol.Session;
using static MinecraftClient.Settings;
using static MinecraftClient.Settings.MainConfigHealper.MainConfig.AdvancedConfig;
namespace MinecraftClient.Protocol.Keys
{
@ -46,11 +48,11 @@ namespace MinecraftClient.Protocol.Keys
keys.Add(login, playerKeyPair);
}
if (Settings.ProfileKeyCaching == CacheType.Disk && updatetimer.Enabled == true)
if (Config.Main.Advanced.ProfileKeyCache == CacheType.disk && updatetimer.Enabled == true)
{
pendingadds.Add(new KeyValuePair<string, PlayerKeyPair>(login, playerKeyPair));
}
else if (Settings.ProfileKeyCaching == CacheType.Disk)
else if (Config.Main.Advanced.ProfileKeyCache == CacheType.disk)
{
SaveToDisk();
}
@ -114,7 +116,7 @@ namespace MinecraftClient.Protocol.Keys
//User-editable keys cache file in text format
if (File.Exists(KeysCacheFilePlaintext))
{
if (Settings.DebugMessages)
if (Settings.Config.Logging.DebugMessages)
ConsoleIO.WriteLineFormatted(Translations.Get("cache.loading_keys", KeysCacheFilePlaintext));
try
@ -133,27 +135,27 @@ namespace MinecraftClient.Protocol.Keys
{
PlayerKeyPair playerKeyPair = PlayerKeyPair.FromString(value);
keys[login] = playerKeyPair;
if (Settings.DebugMessages)
if (Settings.Config.Logging.DebugMessages)
ConsoleIO.WriteLineFormatted(Translations.Get("cache.loaded_keys", playerKeyPair.ExpiresAt.ToString()));
}
catch (InvalidDataException e)
{
if (Settings.DebugMessages)
if (Settings.Config.Logging.DebugMessages)
ConsoleIO.WriteLineFormatted(Translations.Get("cache.ignore_string_keys", value, e.Message));
}
catch (FormatException e)
{
if (Settings.DebugMessages)
if (Settings.Config.Logging.DebugMessages)
ConsoleIO.WriteLineFormatted(Translations.Get("cache.ignore_string_keys", value, e.Message));
}
catch (ArgumentNullException e)
{
if (Settings.DebugMessages)
if (Settings.Config.Logging.DebugMessages)
ConsoleIO.WriteLineFormatted(Translations.Get("cache.ignore_string_keys", value, e.Message));
}
}
else if (Settings.DebugMessages)
else if (Settings.Config.Logging.DebugMessages)
{
ConsoleIO.WriteLineFormatted(Translations.Get("cache.ignore_line_keys", line));
}
@ -174,7 +176,7 @@ namespace MinecraftClient.Protocol.Keys
/// </summary>
private static void SaveToDisk()
{
if (Settings.DebugMessages)
if (Config.Logging.DebugMessages)
Translations.WriteLineFormatted("cache.saving_keys");
List<string> KeysCacheLines = new()

View file

@ -11,6 +11,8 @@ using MinecraftClient.Protocol.Handlers;
using MinecraftClient.Protocol.Handlers.Forge;
using MinecraftClient.Protocol.Session;
using MinecraftClient.Proxy;
using static MinecraftClient.Settings;
using static MinecraftClient.Settings.MainConfigHealper.MainConfig.GeneralConfig;
namespace MinecraftClient.Protocol
{
@ -64,7 +66,7 @@ namespace MinecraftClient.Protocol
{
ConsoleIO.WriteLineFormatted(Translations.Get("mcc.not_found", domainVal, e.GetType().FullName, e.Message));
}
}, TimeSpan.FromSeconds(Settings.ResolveSrvRecordsShortTimeout ? 10 : 30));
}, TimeSpan.FromSeconds(Config.Main.Advanced.ResolveSrvRecords == MainConfigHealper.MainConfig.AdvancedConfig.ResolveSrvRecordType.fast ? 10 : 30));
}
domain = domainVal;
@ -99,7 +101,7 @@ namespace MinecraftClient.Protocol
{
ConsoleIO.WriteLineFormatted(String.Format("§8{0}: {1}", e.GetType().FullName, e.Message));
}
}, TimeSpan.FromSeconds(Settings.ResolveSrvRecordsShortTimeout ? 10 : 30)))
}, TimeSpan.FromSeconds(Config.Main.Advanced.ResolveSrvRecords == MainConfigHealper.MainConfig.AdvancedConfig.ResolveSrvRecordType.fast ? 10 : 30)))
{
if (protocolversion != 0 && protocolversion != protocolversionTmp)
Translations.WriteLineFormatted("error.version_different");
@ -420,16 +422,16 @@ namespace MinecraftClient.Protocol
/// <param name="pass">Password</param>
/// <param name="session">In case of successful login, will contain session information for multiplayer</param>
/// <returns>Returns the status of the login (Success, Failure, etc.)</returns>
public static LoginResult GetLogin(string user, string pass, AccountType type, out SessionToken session)
public static LoginResult GetLogin(string user, string pass, LoginType type, out SessionToken session)
{
if (type == AccountType.Microsoft)
if (type == LoginType.microsoft)
{
if (Settings.LoginMethod == "mcc")
if (Config.Main.General.Method == LoginMethod.mcc)
return MicrosoftMCCLogin(user, pass, out session);
else
return MicrosoftBrowserLogin(out session, user);
}
else if (type == AccountType.Mojang)
else if (type == LoginType.mojang)
{
return MojangLogin(user, pass, out session);
}
@ -494,7 +496,7 @@ namespace MinecraftClient.Protocol
}
catch (System.Security.Authentication.AuthenticationException e)
{
if (Settings.DebugMessages)
if (Settings.Config.Logging.DebugMessages)
{
ConsoleIO.WriteLineFormatted("§8" + e.ToString());
}
@ -502,7 +504,7 @@ namespace MinecraftClient.Protocol
}
catch (System.IO.IOException e)
{
if (Settings.DebugMessages)
if (Settings.Config.Logging.DebugMessages)
{
ConsoleIO.WriteLineFormatted("§8" + e.ToString());
}
@ -514,7 +516,7 @@ namespace MinecraftClient.Protocol
}
catch (Exception e)
{
if (Settings.DebugMessages)
if (Settings.Config.Logging.DebugMessages)
{
ConsoleIO.WriteLineFormatted("§8" + e.ToString());
}
@ -543,7 +545,7 @@ namespace MinecraftClient.Protocol
{
session = new SessionToken() { ClientID = Guid.NewGuid().ToString().Replace("-", "") };
ConsoleIO.WriteLineFormatted("§cMicrosoft authenticate failed: " + e.Message);
if (Settings.DebugMessages)
if (Settings.Config.Logging.DebugMessages)
{
ConsoleIO.WriteLineFormatted("§c" + e.StackTrace);
}
@ -601,7 +603,7 @@ namespace MinecraftClient.Protocol
session.PlayerID = profile.UUID;
session.ID = accessToken;
session.RefreshToken = msaResponse.RefreshToken;
Settings.Login = msaResponse.Email;
Config.Main.General.Account.Login = msaResponse.Email;
return LoginResult.Success;
}
else
@ -612,7 +614,7 @@ namespace MinecraftClient.Protocol
catch (Exception e)
{
ConsoleIO.WriteLineFormatted("§cMicrosoft authenticate failed: " + e.Message);
if (Settings.DebugMessages)
if (Settings.Config.Logging.DebugMessages)
{
ConsoleIO.WriteLineFormatted("§c" + e.StackTrace);
}
@ -632,7 +634,7 @@ namespace MinecraftClient.Protocol
var expTimestamp = long.Parse(json.Properties["exp"].StringValue, NumberStyles.Any, CultureInfo.CurrentCulture);
var now = DateTime.Now;
var tokenExp = UnixTimeStampToDateTime(expTimestamp);
if (Settings.DebugMessages)
if (Settings.Config.Logging.DebugMessages)
{
ConsoleIO.WriteLine("Access token expiration time is " + tokenExp.ToString());
}
@ -772,7 +774,7 @@ namespace MinecraftClient.Protocol
catch (Exception e)
{
ConsoleIO.WriteLineFormatted("§8" + e.GetType().ToString() + ": " + e.Message);
if (Settings.DebugMessages)
if (Settings.Config.Logging.DebugMessages)
{
ConsoleIO.WriteLineFormatted("§8" + e.StackTrace);
}
@ -815,7 +817,7 @@ namespace MinecraftClient.Protocol
catch (Exception e)
{
ConsoleIO.WriteLineFormatted("§8" + e.GetType().ToString() + ": " + e.Message);
if (Settings.DebugMessages)
if (Settings.Config.Logging.DebugMessages)
{
ConsoleIO.WriteLineFormatted("§8" + e.StackTrace);
}
@ -890,14 +892,14 @@ namespace MinecraftClient.Protocol
{
try
{
if (Settings.DebugMessages)
if (Settings.Config.Logging.DebugMessages)
ConsoleIO.WriteLineFormatted(Translations.Get("debug.request", host));
TcpClient client = ProxyHandler.NewTcpClient(host, 443, true);
SslStream stream = new(client.GetStream());
stream.AuthenticateAsClient(host, null, SslProtocols.Tls12, true); // Enable TLS 1.2. Hotfix for #1780
if (Settings.DebugMessages)
if (Settings.Config.Logging.DebugMessages)
foreach (string line in headers)
ConsoleIO.WriteLineFormatted("§8> " + line);
@ -905,7 +907,7 @@ namespace MinecraftClient.Protocol
System.IO.StreamReader sr = new(stream);
string raw_result = sr.ReadToEnd();
if (Settings.DebugMessages)
if (Settings.Config.Logging.DebugMessages)
{
ConsoleIO.WriteLine("");
foreach (string line in raw_result.Split('\n'))
@ -915,7 +917,7 @@ namespace MinecraftClient.Protocol
if (raw_result.StartsWith("HTTP/1.1"))
{
postResult = raw_result[(raw_result.IndexOf("\r\n\r\n") + 4)..];
statusCode = Settings.str2int(raw_result.Split(' ')[1]);
statusCode = int.Parse(raw_result.Split(' ')[1], NumberStyles.Any, CultureInfo.CurrentCulture);
}
else statusCode = 520; //Web server is returning an unknown error
}

View file

@ -109,7 +109,7 @@ namespace MinecraftClient.Protocol
requestMessage.Add(body);
}
else requestMessage.Add(""); // <CR><LF>
if (Settings.DebugMessages)
if (Settings.Config.Logging.DebugMessages)
{
foreach (string l in requestMessage)
{

View file

@ -398,7 +398,7 @@ namespace MinecraftClient.Protocol
private static void WriteDebugLog(string t)
{
if (Settings.DebugMessages && logOutput)
if (Settings.Config.Logging.DebugMessages && logOutput)
WriteLog(t);
}

View file

@ -1,20 +0,0 @@
namespace MinecraftClient.Protocol.Session
{
public enum CacheType
{
/// <summary>
/// Do not perform any session caching, always perform login requests from scratch.
/// </summary>
None,
/// <summary>
/// Cache session information in memory to reuse session tokens across server joins.
/// </summary>
Memory,
/// <summary>
/// Cache session information in a SessionCache file to share session tokens between different MCC instances.
/// </summary>
Disk
};
}

View file

@ -4,6 +4,8 @@ using System.IO;
using System.Runtime.Serialization;
using System.Runtime.Serialization.Formatters.Binary;
using System.Timers;
using static MinecraftClient.Settings;
using static MinecraftClient.Settings.MainConfigHealper.MainConfig.AdvancedConfig;
namespace MinecraftClient.Protocol.Session
{
@ -54,11 +56,11 @@ namespace MinecraftClient.Protocol.Session
sessions.Add(login, session);
}
if (Settings.SessionCaching == CacheType.Disk && updatetimer.Enabled == true)
if (Config.Main.Advanced.SessionCache == CacheType.disk && updatetimer.Enabled == true)
{
pendingadds.Add(new KeyValuePair<string, SessionToken>(login, session));
}
else if (Settings.SessionCaching == CacheType.Disk)
else if (Config.Main.Advanced.SessionCache == CacheType.disk)
{
SaveToDisk();
}
@ -122,7 +124,7 @@ namespace MinecraftClient.Protocol.Session
//Grab sessions in the Minecraft directory
if (File.Exists(SessionCacheFileMinecraft))
{
if (Settings.DebugMessages)
if (Config.Logging.DebugMessages)
ConsoleIO.WriteLineFormatted(Translations.Get("cache.loading", Path.GetFileName(SessionCacheFileMinecraft)));
Json.JSONData mcSession = new(Json.JSONData.DataType.String);
try
@ -155,7 +157,7 @@ namespace MinecraftClient.Protocol.Session
sessionItem["uuid"].StringValue.Replace("-", ""),
clientID
));
if (Settings.DebugMessages)
if (Config.Logging.DebugMessages)
ConsoleIO.WriteLineFormatted(Translations.Get("cache.loaded", login, session.ID));
sessions[login] = session;
}
@ -169,7 +171,7 @@ namespace MinecraftClient.Protocol.Session
//Serialized session cache file in binary format
if (File.Exists(SessionCacheFileSerialized))
{
if (Settings.DebugMessages)
if (Config.Logging.DebugMessages)
ConsoleIO.WriteLineFormatted(Translations.Get("cache.converting", SessionCacheFileSerialized));
try
@ -181,7 +183,7 @@ namespace MinecraftClient.Protocol.Session
#pragma warning restore SYSLIB0011 // BinaryFormatter.Deserialize() is obsolete
foreach (KeyValuePair<string, SessionToken> item in sessionsTemp)
{
if (Settings.DebugMessages)
if (Config.Logging.DebugMessages)
ConsoleIO.WriteLineFormatted(Translations.Get("cache.loaded", item.Key, item.Value.ID));
sessions[item.Key] = item.Value;
}
@ -199,7 +201,7 @@ namespace MinecraftClient.Protocol.Session
//User-editable session cache file in text format
if (File.Exists(SessionCacheFilePlaintext))
{
if (Settings.DebugMessages)
if (Config.Logging.DebugMessages)
ConsoleIO.WriteLineFormatted(Translations.Get("cache.loading_session", SessionCacheFilePlaintext));
try
@ -215,17 +217,17 @@ namespace MinecraftClient.Protocol.Session
{
string login = Settings.ToLowerIfNeed(keyValue[0]);
SessionToken session = SessionToken.FromString(keyValue[1]);
if (Settings.DebugMessages)
if (Config.Logging.DebugMessages)
ConsoleIO.WriteLineFormatted(Translations.Get("cache.loaded", login, session.ID));
sessions[login] = session;
}
catch (InvalidDataException e)
{
if (Settings.DebugMessages)
if (Config.Logging.DebugMessages)
ConsoleIO.WriteLineFormatted(Translations.Get("cache.ignore_string", keyValue[1], e.Message));
}
}
else if (Settings.DebugMessages)
else if (Config.Logging.DebugMessages)
{
ConsoleIO.WriteLineFormatted(Translations.Get("cache.ignore_line", line));
}
@ -246,7 +248,7 @@ namespace MinecraftClient.Protocol.Session
/// </summary>
private static void SaveToDisk()
{
if (Settings.DebugMessages)
if (Config.Logging.DebugMessages)
Translations.WriteLineFormatted("cache.saving");
List<string> sessionCacheLines = new()

View file

@ -1,5 +1,7 @@
using System.Net.Sockets;
using System;
using System.Net.Sockets;
using Starksoft.Aspen.Proxy;
using Tomlet.Attributes;
namespace MinecraftClient.Proxy
{
@ -11,7 +13,45 @@ namespace MinecraftClient.Proxy
public static class ProxyHandler
{
public enum Type { HTTP, SOCKS4, SOCKS4a, SOCKS5 };
public static Configs Config = new();
[TomlDoNotInlineObject]
public class Configs
{
[TomlInlineComment("$Config.Proxy.Enabled_Login$")]
public bool Enabled_Login = false;
[TomlInlineComment("$Config.Proxy.Enabled_Ingame$")]
public bool Enabled_Ingame = false;
[TomlInlineComment("$Config.Proxy.Server$")]
public ProxyInfoConfig Server = new("0.0.0.0", 8080);
[TomlInlineComment("$Config.Proxy.Proxy_Type$")]
public ProxyType Proxy_Type = ProxyType.HTTP;
[TomlInlineComment("$Config.Proxy.Username$")]
public string Username = "";
[TomlInlineComment("$Config.Proxy.Password$")]
public string Password = "";
public void OnSettingUpdate() { }
public struct ProxyInfoConfig
{
public string Host;
public ushort Port;
public ProxyInfoConfig(string host, ushort port)
{
Host = host;
Port = port;
}
}
public enum ProxyType { HTTP, SOCKS4, SOCKS4a, SOCKS5 };
}
private static readonly ProxyClientFactory factory = new();
private static IProxyClient? proxy;
@ -28,27 +68,26 @@ namespace MinecraftClient.Proxy
{
try
{
if (login ? Settings.ProxyEnabledLogin : Settings.ProxyEnabledIngame)
if (login ? Config.Enabled_Login : Config.Enabled_Ingame)
{
ProxyType innerProxytype = ProxyType.Http;
switch (Settings.proxyType)
switch (Config.Proxy_Type)
{
case Type.HTTP: innerProxytype = ProxyType.Http; break;
case Type.SOCKS4: innerProxytype = ProxyType.Socks4; break;
case Type.SOCKS4a: innerProxytype = ProxyType.Socks4a; break;
case Type.SOCKS5: innerProxytype = ProxyType.Socks5; break;
case Configs.ProxyType.HTTP: innerProxytype = ProxyType.Http; break;
case Configs.ProxyType.SOCKS4: innerProxytype = ProxyType.Socks4; break;
case Configs.ProxyType.SOCKS4a: innerProxytype = ProxyType.Socks4a; break;
case Configs.ProxyType.SOCKS5: innerProxytype = ProxyType.Socks5; break;
}
if (Settings.ProxyUsername != "" && Settings.ProxyPassword != "")
{
proxy = factory.CreateProxyClient(innerProxytype, Settings.ProxyHost, Settings.ProxyPort, Settings.ProxyUsername, Settings.ProxyPassword);
}
else proxy = factory.CreateProxyClient(innerProxytype, Settings.ProxyHost, Settings.ProxyPort);
if (Config.Username != "" && Config.Password != "")
proxy = factory.CreateProxyClient(innerProxytype, Config.Server.Host, Config.Server.Port, Config.Username, Config.Password);
else
proxy = factory.CreateProxyClient(innerProxytype, Config.Server.Host, Config.Server.Port);
if (!proxy_ok)
{
ConsoleIO.WriteLineFormatted(Translations.Get("proxy.connected", Settings.ProxyHost, Settings.ProxyPort));
ConsoleIO.WriteLineFormatted(Translations.Get("proxy.connected", Config.Server.Host, Config.Server.Port));
proxy_ok = true;
}

View file

@ -1,5 +1,8 @@
[mcc]
# Messages from MCC itself
mcc.help_us_translate=Help us translate MCC: https://mccteam.github.io/guide/translation.html
mcc.run_with_default_settings=\nMCC is running with default settings.
mcc.settings_generated=§cSettings file MinecraftClient.ini has been generated.
mcc.login=Login :
mcc.login_basic_io=Please type the username or email of your choice.
mcc.password=Password :
@ -67,7 +70,7 @@ error.forgeforce=Cannot force Forge support for this Minecraft version!
error.login=Login failed :
error.login.migrated=Account migrated, use e-mail as username.
error.login.server=Login servers are unavailable. Please try again later.
error.login.blocked=Incorrect password, blacklisted IP or too many logins.
error.login.blocked=Incorrect username/password, blacklisted IP or too many logins.
error.login.response=Invalid server response.
error.login.premium=User not premium.
error.login.network=Network error.
@ -643,3 +646,215 @@ bot.scriptScheduler.task=triggeronfirstlogin: {0}\n triggeronlogin: {1}\n trigge
# TestBot
bot.testBot.told=Bot: {0} told me : {1}
bot.testBot.said=Bot: {0} said : {1}
[config]
config.load=Settings have been loaded from {0}
config.load.fail=§cFailed to load settings:§r
config.saving=§aThe current setting is saved as {0}
# Head
config.Head=Startup Config File\n\n# New to Minecraft Console Client? See README and sample configuration files here:\n# https://github.com/MCCTeam/Minecraft-Console-Client/tree/master/MinecraftClient/config\n\n# Want to upgrade to a newer version? See https://github.com/MCCTeam/Minecraft-Console-Client/#download
# Main.General
config.Main.General.account=Login=Email or Name. Use "-" as password for offline mode. Leave blank to prompt user on startup.
config.Main.General.login=Server address and port. Host can be filled in with domain name or IP.
config.Main.General.server_info=Account type: mojang OR microsoft. Also affects interactive login in console.
config.Main.General.method=Microsoft Account sign-in method: mcc OR browser.
# Main.Advanced
config.Main.Advanced=Make sure you understand what each setting does before changing anything!
config.Main.Advanced.language=Fill in with in-game locale code, https://minecraft.fandom.com/wiki/Language
config.Main.Advanced.console_title=
config.Main.Advanced.internal_cmd_char=Use 'none', 'slash' or 'backslash'
config.Main.Advanced.message_cooldown=Minimum delay in seconds between messages to avoid being kicked for spam.
config.Main.Advanced.bot_owners=Name list or myfile.txt, one name per line. /!\ Server admins can impersonate owners!
config.Main.Advanced.mc_version=Use 'auto' or '1.X.X' values. Allows to skip server info retrieval.
config.Main.Advanced.mc_forge=Use 'auto', 'no' or 'force'. Force-enabling only works for MC 1.13+.
config.Main.Advanced.brand_info=Use 'mcc', 'vanilla', or 'none'. This is how MCC identifies itself to the server.
config.Main.Advanced.chatbot_log_file=Leave empty for no logfile
config.Main.Advanced.private_msgs_cmd_name=Used by RemoteControl bot
config.Main.Advanced.show_system_messages=System messages for server ops
config.Main.Advanced.show_xpbar_messages=Messages displayed above xp bar, set this to false in case of xp bar spam
config.Main.Advanced.show_chat_links=Decode links embedded in chat messages and show them in console
config.Main.Advanced.show_inventory_layout=Show inventory layout as ASCII art in inventory command
config.Main.Advanced.terrain_and_movements=Uses more ram, cpu, bandwidth but allows you to move around
config.Main.Advanced.inventory_handling=Toggle inventory handling (beta)
config.Main.Advanced.entity_handling=Toggle entity handling (beta)
config.Main.Advanced.session_cache=How to retain session tokens. Use 'none', 'memory' or 'disk'
config.Main.Advanced.profilekey_cache=How to retain profile key. Use 'none', 'memory' or 'disk'
config.Main.Advanced.resolve_srv_records=Use 'no', 'fast' (5s timeout), or 'yes'. Required for joining some servers.
config.Main.Advanced.account_list=See README > 'Servers and Accounts file' for more info about this
config.Main.Advanced.server_list=See README > 'Servers and Accounts file' for more info about this
config.Main.Advanced.player_head_icon=Only works on Windows XP-8 or Windows 10 with old console
config.Main.Advanced.exit_on_failure=Disable pauses on error, for using MCC in non-interactive scripts
config.Main.Advanced.script_cache=Cache compiled scripts for faster load on low-end devices
config.Main.Advanced.timestamps=Prepend timestamps to chat messages
config.Main.Advanced.auto_respawn=Toggle auto respawn if client player was dead (make sure your spawn point is safe)
config.Main.Advanced.minecraft_realms=Enable support for joining Minecraft Realms worlds
config.Main.Advanced.move_head_while_walking=Enable head movement while walking to avoid anti-cheat triggers
config.Main.Advanced.timeout=Set a custom timeout in seconds (Default: 30). Use only if you know what you're doing.
config.Main.Advanced.enable_emoji=If turned off, the emoji will be replaced with a simpler character (for /chunk status)
config.Main.Advanced.movement_speed=A movement speed higher than 2 may be considered cheating.
config.Main.Advanced.language.invaild=The language code is invalid.
# Signature
config.Signature=Chat settings (affects minecraft 1.19+)
config.Signature.LoginWithSecureProfile=Microsoft accounts only. If disabled, will not be able to sign chat and join servers configured with "enforce-secure-profile=true"
config.Signature.SignChat=Whether to sign the chat send from MCC
config.Signature.SignMessageInCommand=Whether to sign the messages contained in the commands sent by MCC. For example, the message in "/msg" and "/me"
config.Signature.MarkLegallySignedMsg=Use green color block to mark chat with legitimate signatures
config.Signature.MarkModifiedMsg=Use yellow color block to mark chat that have been modified by the server.
config.Signature.MarkIllegallySignedMsg=Use red color block to mark chat without legitimate signature
config.Signature.MarkSystemMessage=Use gray color block to mark system message (always without signature)
config.Signature.ShowModifiedChat=Set to true to display messages modified by the server, false to display the original signed messages
config.Signature.ShowIllegalSignedChat=Whether to display chat and messages in commands without legal signatures
# Logging
config.Logging=Only affect the messages on console.
config.Logging.DebugMessages=Please enable this before submitting bug reports. Thanks!
config.Logging.ChatMessages=Show server chat messages
config.Logging.InfoMessages=Informative messages (i.e Most of the message from MCC)
config.Logging.WarningMessages=Show warning messages
config.Logging.ErrorMessages=Show error messages
config.Logging.ChatFilter=Regex for filtering chat message
config.Logging.DebugFilter=Regex for filtering debug message
config.Logging.FilterMode=blacklist OR whitelist. Blacklist hide message match regex. Whitelist show message match regex
config.Logging.LogToFile=Write log messages to file
config.Logging.LogFile=Log file name
config.Logging.PrependTimestamp=Prepend timestamp to messages in log file
config.Logging.SaveColorCodes=Keep color codes in the saved text (§b)
# AppVars
config.AppVars.Variables=can be used in some other fields as %yourvar%\n# %username% and %serverip% are reserved variables.
# Proxy
config.Proxy=Connect to a server via a proxy instead of connecting directly\n# If Mojang session services are blocked on your network, set enabled=login to login using proxy but connect directly to the server\n# If connecting to port 25565 (Minecraft) is blocked on your network, set enabled=true to login & connect using the proxy\n# /!\ Make sure your server rules allow Proxies or VPNs before setting enabled=true, or you may face consequences!
config.Proxy.Enabled_Login=Whether to connect to the login server through a proxy.
config.Proxy.Enabled_Ingame=Whether to connect to the game server through a proxy.
config.Proxy.Server=Proxy server must allow HTTPS for login, and non-443 ports for playing
config.Proxy.Proxy_Type=Supported types: HTTP, SOCKS4, SOCKS4a, SOCKS5
config.Proxy.Username=Only required for password-protected proxies
config.Proxy.Password=Only required for password-protected proxies
# ChatFormat
config.ChatFormat=MCC does it best to detect chat messages, but some server have unusual chat formats\n# When this happens, you'll need to configure chat format below, see README > 'Detecting chat messages'\n# Do not forget to uncomment (remove '#') these settings if modifying them
config.ChatFormat.Builtins=MCC support for common message formats. Set "false" to avoid conflicts with custom formats.
# MCSettings
config.MCSettings=Settings below are sent to the server and only affect server-side things like your skin
config.MCSettings.Enabled=If disabled, settings below are not sent to the server
config.MCSettings.Locale=Use any language implemented in Minecraft
config.MCSettings.RenderDistance=Value range: [0 - 255]
config.MCSettings.Difficulty=MC 1.7- difficulty. peaceful, easy, normal, difficult
config.MCSettings.ChatMode=Use 'enabled', 'commands', or 'disabled'. Allows to mute yourself...
config.MCSettings.ChatColors=Allows disabling chat colors server-side
config.MCSettings.MainHand=MC 1.9+ main hand. left or right
# ChatBot
config.ChatBot================================ #\n# Minecraft Console Client Bots #\n# =============================== #
# ChatBot.Alerts
config.ChatBot.Alerts=Get alerted when specified words are detected in chat\n# Useful for moderating your server or detecting when someone is talking to you
config.ChatBot.Alerts.Beep_Enabled=Play a beep sound when a word is detected in addition to highlighting.
config.ChatBot.Alerts.Trigger_By_Words=Triggers an alert after receiving a specified keyword.
config.ChatBot.Alerts.Trigger_By_Rain=Trigger alerts when it rains and when it stops.
config.ChatBot.Alerts.Trigger_By_Thunderstorm=Triggers alerts at the beginning and end of thunderstorms.
config.ChatBot.Alerts.Matches_File=List of words/strings to alert you on, e.g. "Yourname"
config.ChatBot.Alerts.Excludes_File=List of words/strings to NOT alert you on, e.g. "<Yourname>"
config.ChatBot.Alerts.Log_To_File=Log alerts info a file.
config.ChatBot.Alerts.Log_File=The name of a file where alers logs will be written.
# ChatBot.AntiAFK
config.ChatBot.AntiAfk=Send a command on a regular or random basis or make the bot walk around randomly to avoid automatic AFK disconnection\n# /!\ Make sure your server rules do not forbid anti-AFK mechanisms!\n# /!\ Make sure you keep the bot in an enclosure to prevent it wandering off if you're using terrain handling! (Recommended size 5x5x5)
config.ChatBot.AntiAfk.Delay=10 = 1s (Can also be a random number between 2 numbers, example: 50-600) (Default: 600)
config.ChatBot.AntiAfk.Command=Command to send to the server
config.ChatBot.AntiAfk.Use_Terrain_Handling=Use terrain handling to enable the bot to move around
config.ChatBot.AntiAfk.Walk_Range=The range the bot can move around randomly (Note: the bigger the range, the slower the bot will be)
config.ChatBot.AntiAfk.Walk_Retries=How many timec can the bot fail trying to move before using the command method
# ChatBot.AutoAttack
config.ChatBot.AutoAttack=Automatically attack hostile mobs around you\n# You need to enable Entity Handling to use this bot\n# /!\ Make sure server rules allow your planned use of AutoAttack\n# /!\ SERVER PLUGINS may consider AutoAttack to be a CHEAT MOD and TAKE ACTION AGAINST YOUR ACCOUNT so DOUBLE CHECK WITH SERVER RULES!
config.ChatBot.AutoAttack.Mode=single or multi. single target one mob per attack. multi target all mobs in range per attack
config.ChatBot.AutoAttack.Priority=health or distance. Only needed when using single mode
config.ChatBot.AutoAttack.Cooldown_Time=How long to wait between each attack. Use auto to let MCC calculate it
config.ChatBot.AutoAttack.Interaction=Possible values: Interact, Attack (default), InteractAt (Interact and Attack)
config.ChatBot.AutoAttack.Attack_Hostile=Allow attacking hostile mobs
config.ChatBot.AutoAttack.Attack_Passive=Allow attacking passive mobs
config.ChatBot.AutoAttack.List_Mode=Wether to treat the entities list as a whitelist or as a blacklist
config.ChatBot.AutoAttack.Entites_List=All entity types can be found here: https://bit.ly/3Rg68lp
# ChatBot.AutoCraft
config.ChatBot.AutoCraft=Automatically craft items in your inventory\n# See README > 'Using the AutoCraft bot' for how to use\n# You need to enable Inventory Handling to use this bot\n# You should also enable Terrain and Movements if you need to use a crafting table
# ChatBot.AutoDrop
config.ChatBot.AutoDrop=Automatically drop items in inventory\n# You need to enable Inventory Handling to use this bot\n# See this file for an up-to-date list of item types you can use with this bot:\n# https://github.com/MCCTeam/Minecraft-Console-Client/blob/master/MinecraftClient/Inventory/ItemType.cs
config.ChatBot.AutoDrop.Mode=include, exclude or everything. Include: drop item IN the list. Exclude: drop item NOT IN the list
config.ChatBot.AutoDrop.Items=separate each item name with a comma ',': "ItemOne", "ItemTwo"
# ChatBot.AutoEat
config.ChatBot.AutoEat=Automatically eat food when your Hunger value is low\n# You need to enable Inventory Handling to use this bot
# ChatBot.AutoFishing
config.ChatBot.AutoFishing=Automatically catch fish using a fishing rod\n# Guide: https://mccteam.github.io/guide/chat-bots.html#auto-fishing\n# /!\ Make sure server rules allow automated farming before using this bot
config.ChatBot.AutoFishing.Antidespawn=Keep it as false if you have not changed it before.
config.ChatBot.AutoFishing.Mainhand=Use the main hand or the off hand to hold the rod.
config.ChatBot.AutoFishing.Auto_Start=Whether to start fishing automatically after entering a world.
config.ChatBot.AutoFishing.Cast_Delay=How soon to re-cast after successful fishing.
config.ChatBot.AutoFishing.Fishing_Delay=How long after entering the game to start fishing (seconds).
config.ChatBot.AutoFishing.Fishing_Timeout=Fishing timeout (seconds). Timeout will trigger a re-cast.
config.ChatBot.AutoFishing.Durability_Limit=Will not use rods with less durability than this (full durability is 64). Set to zero to disable this feature.
config.ChatBot.AutoFishing.Auto_Rod_Switch=Switch to a new rod from inventory after the current rod is unavailable.
config.ChatBot.AutoFishing.Stationary_Threshold=Hooks moving in the X and Z axes below this threshold will be considered stationary.
config.ChatBot.AutoFishing.Hook_Threshold=A stationary hook moving on the Y-axis above this threshold will be considered to have caught a fish.
config.ChatBot.AutoFishing.Log_Fish_Bobber=For debugging purposes, you can use this log to adjust the two thresholds mentioned above.
config.ChatBot.AutoFishing.Movements=Some plugins do not allow the player to fish in one place. This allows the player to change position/angle after each fish caught. Usage can be found in the Guide.
# ChatBot.AutoRelog
config.ChatBot.AutoRelog=Automatically relog when disconnected by server, for example because the server is restating\n# Put keywords/strings such as "Server is restarting" in kick messages file to relog when the message is seen\n# /!\ Use ignorekickmessage=true at own risk! Server staff might not appreciate if you auto-relog on manual kicks
config.ChatBot.AutoRelog.Delay=use 10 for 10 seconds, 10-60 for a random delay between 10 and 60 seconds
config.ChatBot.AutoRelog.Retries=retries when failing to relog to the server. use -1 for unlimited retries
config.ChatBot.AutoRelog.Ignore_Kick_Message=when set to true, autorelog will reconnect regardless of kick messages
config.ChatBot.AutoRelog.Kick_Messages_File=file with list of matches in kick messages that will trigger autorelog
# ChatBot.AutoRespond
config.ChatBot.AutoRespond=Run commands or send messages automatically when a specified pattern is detected in chat\n# /!\ Server admins can spoof chat messages (/nick, /tellraw) so keep this in mind when implementing AutoRespond rules\n# /!\ This bot may get spammy depending on your rules, although the global messagecooldown setting can help you avoiding accidental spam
config.ChatBot.AutoRespond.Match_Colors=Do not remove colors from text (Note: Your matches will have to include color codes (ones using the § character) in order to work)
# ChatBot.ChatLog
config.ChatBot.ChatLog=Logs chat messages in a file on disk.
# ChatBot.FollowPlayer
config.ChatBot.FollowPlayer=Enabled you to make the bot follow you\n# NOTE: This is an experimental feature, the bot can be slow at times, you need to walk with a normal speed and to sometimes stop for it to be able to keep up with you\n# It's similar to making animals follow you when you're holding food in your hand.\n# This is due to a slow pathfinding algorithm, we're working on getting a better one\n# You can tweak the update limit and find what works best for you. (NOTE: Do not but a very low one, because you might achieve the opposite,\n# this might clog the thread for terain handling) and thus slow the bot even more.\n# /!\ Make sure server rules allow an option like this in the rules of the server before using this bot
config.ChatBot.FollowPlayer.Update_Limit=The rate at which the bot does calculations (10 = 1s) (Default: 5) (You can tweak this if you feel the bot is too slow)
config.ChatBot.FollowPlayer.Stop_At_Distance=Do not follow the player if he is in the range of 3 blocks (prevents the bot from pushing a player in an infinite loop)
# ChatBot.HangmanGame
config.ChatBot.HangmanGame=A small game to demonstrate chat interactions. Players can guess mystery words one letter at a time.\n# You need to have ChatFormat working correctly and add yourself in botowners to start the game with /tell <bot username> start\n# /!\ This bot may get a bit spammy if many players are interacting with it
# ChatBot.Mailer
config.ChatBot.Mailer=Relay messages between players and servers, like a mail plugin\n# This bot can store messages when the recipients are offline, and send them when they join the server\n# /!\ Server admins can spoof PMs (/tellraw, /nick) so enable this bot only if you trust server admins
# ChatBot.Map
config.ChatBot.Map=Allows you to render maps into .jpg images\n# This is useful for solving captchas which use maps\n# The maps are rendered into Rendered_Maps folder.\n# NOTE:\n# This feature is currently only useful for solving captchas which use maps.\n# If some servers have a very short time for solving captchas, enabe auto_render_on_update and prepare to open the file quickly.\n# On linux you can use FTP to access generated files.\n# In the future it might will be possible to display maps directly in the console with a separate command.\n# /!\ Make sure server rules allow bots to be used on the server, or you risk being punished.
config.ChatBot.Map.Should_Resize=Should the map be resized? (Default one is small 128x128)
config.ChatBot.Map.Resize_To=The size to resize the map to (Note: the bigger it is, the lower the quallity is)
config.ChatBot.Map.Auto_Render_On_Update=Automatically render the map once it's received or updated from/by the server
config.ChatBot.Map.Delete_All_On_Unload=Delete all rendered maps on unload/reload (Does not delete the images if you exit the client)
config.ChatBot.Map.Notify_On_First_Update=Get a notification when you have gotten a map from the server for the first time
# ChatBot.PlayerListLogger
config.ChatBot.PlayerListLogger=Log the list of players periodically into a textual file
config.ChatBot.PlayerListLogger.Delay=10 = 1s
# ChatBot.RemoteControl
config.ChatBot.RemoteControl=Send MCC console commands to your bot through server PMs (/tell)\n# You need to have ChatFormat working correctly and add yourself in botowners to use the bot\n# /!\ Server admins can spoof PMs (/tellraw, /nick) so enable RemoteControl only if you trust server admins
# ChatBot.ReplayCapture
config.ChatBot.ReplayCapture=Enable recording of the game (/replay start) and replay it later using the Replay Mod (https://www.replaymod.com/)\n# Please note that due to technical limitations, the client player (you) will not be shown in the replay file\n# /!\ You SHOULD use /replay stop or exit the program gracefully with /quit OR THE REPLAY FILE MAY GET CORRUPT!
config.ChatBot.ReplayCapture.Backup_Interval=How long should replay file be auto-saved, in seconds. Use -1 to disable
# ChatBot.ScriptScheduler
config.ChatBot.ScriptScheduler=Schedule commands and scripts to launch on various events such as server join, date/time or time interval\n# See README > 'Using the Script Scheduler' for more info

View file

@ -1,5 +1,8 @@
[mcc]
# Messages from MCC itself
mcc.help_us_translate=帮助我们翻译MCChttps://mccteam.github.io/guide/translation.html
mcc.run_with_default_settings=\nMCC正在使用默认配置运行。
mcc.settings_generated=§c配置文件 MinecraftClient.ini 已经生成。
mcc.login=登录:
mcc.login_basic_io=请输入用户名或邮箱。
mcc.password=密码:
@ -30,7 +33,7 @@ mcc.single_cmd=§7已发送命令§8 {0}
mcc.joined=已成功加入服务器。\n输入 '{0}quit' 离开服务器。
mcc.reconnect=等待5秒 (剩余{0}次尝试)...
mcc.disconnect.lost=失去连接。
mcc.disconnect.server=从服务器断开
mcc.disconnect.server=从服务器断开连接
mcc.disconnect.login=连接失败:
mcc.link=链接:{0}
mcc.player_dead_respawn=你死了1秒后自动重生。
@ -65,7 +68,7 @@ error.forgeforce=无法为此Minecraft版本强制启动Forge支持
error.login=登录失败:
error.login.migrated=帐户已迁移,使用电子邮件作为用户名。
error.login.server=登录服务器不可用。请稍后再试。
error.login.blocked=密码错误、IP被禁用或登录次数过多。
error.login.blocked=用户名/密码错误、IP被禁用或登录次数过多。
error.login.response=服务器响应无效。
error.login.premium=不是Premium用户。
error.login.network=网络错误。
@ -528,3 +531,18 @@ bot.scriptScheduler.task=triggeronfirstlogin: {0}\n triggeronlogin: {1}\n trigge
# TestBot
bot.testBot.told=Bot{0}对我说:{1}
bot.testBot.said=Bot{0}说:{1}
[config]
config.load=已从 {0} 加载设置。
config.load.fail=§c加载设置时出错§r
config.saving=§a当前设置已保存至 {0}
# Head
config.Head=启动配置文件\n\n# 对 MCCMinecraft 命令行客户端)不熟悉?请看 README 和示例配置文件:\n# https://github.com/MCCTeam/Minecraft-Console-Client/tree/master/MinecraftClient/config\n\n# 想升级到较新的版本吗?请访问 https://github.com/MCCTeam/Minecraft-Console-Client/#download
# Main.General
config.Main.General.account=Login请填写邮箱或玩家名称。若要以离线模式登录请使用"-"作为密码。若留空则使用交互式登录。
config.Main.General.login=游戏服务器的地址和端口可填入域名或IP地址。可删除端口字段会解析SRV记录
config.Main.General.server_info=帐户类型mojang 或是 microsoft。此项设置也会影响交互式登录。
config.Main.General.method=微软账户的登录方式mcc 或是 browser手动在网页上登录

View file

@ -5,6 +5,7 @@ using System.IO;
using System.Linq;
using System.Text;
using DynamicRun.Builder;
using static MinecraftClient.Settings;
namespace MinecraftClient
{
@ -43,7 +44,7 @@ namespace MinecraftClient
lock (CompileCache)
{
///Process and compile script only if not already compiled
if (!Settings.CacheScripts || !CompileCache.ContainsKey(scriptHash))
if (!Config.Main.Advanced.CacheScript || !CompileCache.ContainsKey(scriptHash))
{
//Process different sections of the script file
bool scriptMain = true;
@ -111,10 +112,10 @@ namespace MinecraftClient
//Retrieve compiled assembly
assembly = result.Assembly;
if (Settings.CacheScripts)
if (Config.Main.Advanced.CacheScript)
CompileCache[scriptHash] = assembly!;
}
else if (Settings.CacheScripts)
else if (Config.Main.Advanced.CacheScript)
assembly = CompileCache[scriptHash];
}
@ -293,7 +294,7 @@ namespace MinecraftClient
if (localVars != null && localVars.ContainsKey(varName))
return localVars[varName];
else
return Settings.GetVar(varName);
return Settings.Config.AppVar.GetVar(varName);
}
/// <summary>
@ -305,7 +306,7 @@ namespace MinecraftClient
{
if (localVars != null && localVars.ContainsKey(varName))
localVars.Remove(varName);
return Settings.SetVar(varName, varValue);
return Settings.Config.AppVar.SetVar(varName, varValue);
}
/// <summary>
@ -347,7 +348,7 @@ namespace MinecraftClient
/// <returns>True if the account was found and loaded</returns>
public bool SetAccount(string accountAlias, bool andReconnect = false)
{
bool result = Settings.SetAccount(accountAlias);
bool result = Settings.Config.Main.Advanced.SetAccount(accountAlias);
if (result && andReconnect)
ReconnectToTheServer();
return result;
@ -360,7 +361,7 @@ namespace MinecraftClient
/// <returns>True if the server IP was valid and loaded, false otherwise</returns>
public bool SetServer(string server, bool andReconnect = false)
{
bool result = Settings.SetServerIP(server);
bool result = Settings.Config.Main.SetServerIP(new MainConfigHealper.MainConfig.ServerInfoConfig(server), true);
if (result && andReconnect)
ReconnectToTheServer();
return result;

View file

@ -6,6 +6,7 @@ using System.Text;
using System.Text.RegularExpressions;
using MinecraftClient.Inventory;
using MinecraftClient.Mapping;
using static MinecraftClient.Settings;
namespace MinecraftClient
{
@ -521,9 +522,9 @@ namespace MinecraftClient
text = GetVerbatim(text);
//User-defined regex for private chat messages
if (Settings.ChatFormat_Private != null)
if (Config.ChatFormat.Private != null)
{
Match regexMatch = Settings.ChatFormat_Private.Match(text);
Match regexMatch = new Regex(Config.ChatFormat.Private).Match(text);
if (regexMatch.Success && regexMatch.Groups.Count >= 3)
{
sender = regexMatch.Groups[1].Value;
@ -533,7 +534,7 @@ namespace MinecraftClient
}
//Built-in detection routine for private messages
if (Settings.ChatFormat_Builtins)
if (Config.ChatFormat.Builtins)
{
string[] tmp = text.Split(' ');
try
@ -632,9 +633,9 @@ namespace MinecraftClient
text = GetVerbatim(text);
//User-defined regex for public chat messages
if (Settings.ChatFormat_Public != null)
if (Config.ChatFormat.Public != null)
{
Match regexMatch = Settings.ChatFormat_Public.Match(text);
Match regexMatch = new Regex(Config.ChatFormat.Public).Match(text);
if (regexMatch.Success && regexMatch.Groups.Count >= 3)
{
sender = regexMatch.Groups[1].Value;
@ -644,7 +645,7 @@ namespace MinecraftClient
}
//Built-in detection routine for public messages
if (Settings.ChatFormat_Builtins)
if (Config.ChatFormat.Builtins)
{
string[] tmp = text.Split(' ');
@ -735,9 +736,9 @@ namespace MinecraftClient
text = GetVerbatim(text);
//User-defined regex for teleport requests
if (Settings.ChatFormat_TeleportRequest != null)
if (Config.ChatFormat.TeleportRequest != null)
{
Match regexMatch = Settings.ChatFormat_TeleportRequest.Match(text);
Match regexMatch = new Regex(Config.ChatFormat.TeleportRequest).Match(text);
if (regexMatch.Success && regexMatch.Groups.Count >= 2)
{
sender = regexMatch.Groups[1].Value;
@ -746,7 +747,7 @@ namespace MinecraftClient
}
//Built-in detection routine for teleport requests
if (Settings.ChatFormat_Builtins)
if (Config.ChatFormat.Builtins)
{
string[] tmp = text.Split(' ');
@ -786,7 +787,26 @@ namespace MinecraftClient
ConsoleIO.WriteLogLine(String.Format("[{0}] {1}", GetType().Name, text));
else
Handler.Log.Info(String.Format("[{0}] {1}", GetType().Name, text));
string logfile = Settings.ExpandVars(Settings.chatbotLogFile);
string logfile = Settings.Config.AppVar.ExpandVars(Config.Main.Advanced.ChatbotLogFile);
if (!String.IsNullOrEmpty(logfile))
{
if (!File.Exists(logfile))
{
try { Directory.CreateDirectory(Path.GetDirectoryName(logfile)!); }
catch { return; /* Invalid path or access denied */ }
try { File.WriteAllText(logfile, ""); }
catch { return; /* Invalid file name or access denied */ }
}
File.AppendAllLines(logfile, new string[] { GetTimestamp() + ' ' + text });
}
}
protected static void LogToConsole(string botName, object? text)
{
ConsoleIO.WriteLogLine(String.Format("[{0}] {1}", botName, text));
string logfile = Settings.Config.AppVar.ExpandVars(Config.Main.Advanced.ChatbotLogFile);
if (!String.IsNullOrEmpty(logfile))
{
@ -808,7 +828,7 @@ namespace MinecraftClient
/// <param name="text">Debug log text to write</param>
protected void LogDebugToConsole(object text)
{
if (Settings.DebugMessages)
if (Settings.Config.Logging.DebugMessages)
LogToConsole(text);
}
@ -840,7 +860,7 @@ namespace MinecraftClient
/// <param name="delaySeconds">Optional delay, in seconds, before restarting</param>
protected void ReconnectToTheServer(int ExtraAttempts = 3, int delaySeconds = 0)
{
if (Settings.DebugMessages)
if (Settings.Config.Logging.DebugMessages)
ConsoleIO.WriteLogLine(Translations.Get("chatbot.reconnect", GetType().Name));
McClient.ReconnectionAttemptsLeft = ExtraAttempts;
Program.Restart(delaySeconds);
@ -873,7 +893,7 @@ namespace MinecraftClient
/// <param name="message">Message</param>
protected void SendPrivateMessage(string player, string message)
{
SendText(String.Format("/{0} {1} {2}", Settings.PrivateMsgsCmdName, player, message));
SendText(String.Format("/{0} {1} {2}", Config.Main.Advanced.PrivateMsgsCmdName, player, message));
}
/// <summary>

File diff suppressed because it is too large Load diff

View file

@ -1,5 +1,6 @@
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Globalization;
using System.IO;
using System.Text;
@ -15,7 +16,7 @@ namespace MinecraftClient
/// </remarks>
public static class Translations
{
private static readonly Dictionary<string, string> translations;
private static readonly Dictionary<string, string> translations = new();
private static readonly string translationFilePath = "lang" + Path.DirectorySeparatorChar + "mcc";
private static readonly string defaultTranslation = "en.ini";
private static readonly Regex translationKeyRegex = new(@"\(\[(.*?)\]\)", RegexOptions.Compiled); // Extract string inside ([ ])
@ -52,6 +53,21 @@ namespace MinecraftClient
return msgName;
}
/// <summary>
/// Return a tranlation for the requested text. Support string formatting. If not found, return the original text
/// </summary>
/// <param name="msgName">text identifier</param>
/// <param name="args"></param>
/// <returns>Translated text or original text if not found</returns>
/// <remarks>Useful when not sure msgName is a translation mapping key or a normal text</remarks>
public static string? GetOrNull(string msgName, params object?[] args)
{
if (translations.ContainsKey(msgName))
return Get(msgName, args);
else
return null;
}
/// <summary>
/// Replace the translation key inside a sentence to translated text. Wrap the key in ([translation.key])
/// </summary>
@ -75,32 +91,495 @@ namespace MinecraftClient
return Get(m.Groups[1].Value);
}
/// <summary>
/// Initialize translations depending on system language.
/// English is the default for all unknown system languages.
/// </summary>
static Translations()
public static Tuple<string, string[]> GetTranslationPriority()
{
translations = new Dictionary<string, string>();
LoadDefaultTranslationsFile();
string gameLanguage = "en_gb";
List<string> name = new();
string systemLanguage = string.IsNullOrWhiteSpace(CultureInfo.CurrentCulture.Name)
? CultureInfo.CurrentCulture.Parent.Name
: CultureInfo.CurrentCulture.Name;
switch (systemLanguage)
{
case "af":
case "af-ZA":
gameLanguage = "af_za";
break;
case "ar":
case "ar-AE":
case "ar-BH":
case "ar-DZ":
case "ar-EG":
case "ar-IQ":
case "ar-JO":
case "ar-KW":
case "ar-LB":
case "ar-LY":
case "ar-MA":
case "ar-OM":
case "ar-QA":
case "ar-SA":
case "ar-SY":
case "ar-TN":
case "ar-YE":
gameLanguage = "ar_sa";
break;
case "az":
case "az-Cyrl-AZ":
case "az-Latn-AZ":
gameLanguage = "az_az";
break;
case "be":
case "be-BY":
gameLanguage = "be_by";
break;
case "bg":
case "bg-BG":
gameLanguage = "bg_bg";
break;
case "bs-Latn-BA":
gameLanguage = "bs_ba";
break;
case "ca":
case "ca-ES":
gameLanguage = "ca_es";
break;
case "cs":
case "cs-CZ":
gameLanguage = "cs_cz";
break;
case "cy-GB":
gameLanguage = "cy_gb";
break;
case "da":
case "da-DK":
gameLanguage = "da_dk";
break;
case "de":
case "de-DE":
case "de-LI":
case "de-LU":
gameLanguage = "de_de";
name.Add("de");
break;
case "de-AT":
gameLanguage = "de_at";
name.Add("de");
break;
case "de-CH":
gameLanguage = "de_ch";
name.Add("de");
break;
case "dv":
case "dv-MV":
break;
case "el":
case "el-GR":
gameLanguage = "el_gr";
break;
case "en":
case "en-029":
case "en-BZ":
case "en-IE":
case "en-JM":
case "en-PH":
case "en-TT":
case "en-ZA":
case "en-ZW":
case "en-GB":
gameLanguage = "en_gb";
break;
case "en-AU":
gameLanguage = "en_au";
break;
case "en-CA":
gameLanguage = "en_ca";
break;
case "en-US":
gameLanguage = "en_us";
break;
case "en-NZ":
gameLanguage = "en_nz";
break;
case "es":
case "es-BO":
case "es-CO":
case "es-CR":
case "es-DO":
case "es-ES":
case "es-GT":
case "es-HN":
case "es-NI":
case "es-PA":
case "es-PE":
case "es-PR":
case "es-PY":
case "es-SV":
gameLanguage = "es_es";
break;
case "es-AR":
gameLanguage = "es_ar";
break;
case "es-CL":
gameLanguage = "es_cl";
break;
case "es-EC":
gameLanguage = "es_ec";
break;
case "es-MX":
gameLanguage = "es_mx";
break;
case "es-UY":
gameLanguage = "es_uy";
break;
case "es-VE":
gameLanguage = "es_ve";
break;
case "et":
case "et-EE":
gameLanguage = "et_ee";
break;
case "eu":
case "eu-ES":
gameLanguage = "eu_es";
break;
case "fa":
case "fa-IR":
gameLanguage = "fa_ir";
break;
case "fi":
case "fi-FI":
gameLanguage = "fi_fi";
break;
case "fo":
case "fo-FO":
gameLanguage = "fo_fo";
break;
case "fr":
case "fr-BE":
case "fr-FR":
case "fr-CH":
case "fr-LU":
case "fr-MC":
gameLanguage = "fr_fr";
name.Add("fr");
break;
case "fr-CA":
gameLanguage = "fr_ca";
name.Add("fr");
break;
case "gl":
case "gl-ES":
gameLanguage = "gl_es";
break;
case "gu":
case "gu-IN":
break;
case "he":
case "he-IL":
gameLanguage = "he_il";
break;
case "hi":
case "hi-IN":
gameLanguage = "hi_in";
break;
case "hr":
case "hr-BA":
case "hr-HR":
gameLanguage = "hr_hr";
break;
case "hu":
case "hu-HU":
gameLanguage = "hu_hu";
break;
case "hy":
case "hy-AM":
gameLanguage = "hy_am";
break;
case "id":
case "id-ID":
gameLanguage = "id_id";
break;
case "is":
case "is-IS":
gameLanguage = "is_is";
break;
case "it":
case "it-CH":
case "it-IT":
gameLanguage = "it_it";
break;
case "ja":
case "ja-JP":
gameLanguage = "ja_jp";
break;
case "ka":
case "ka-GE":
gameLanguage = "ka_ge";
break;
case "kk":
case "kk-KZ":
gameLanguage = "kk_kz";
break;
case "kn":
case "kn-IN":
gameLanguage = "kn_in";
break;
case "kok":
case "kok-IN":
break;
case "ko":
case "ko-KR":
gameLanguage = "ko_kr";
break;
case "ky":
case "ky-KG":
break;
case "lt":
case "lt-LT":
gameLanguage = "lt_lt";
break;
case "lv":
case "lv-LV":
gameLanguage = "lv_lv";
break;
case "mi-NZ":
break;
case "mk":
case "mk-MK":
gameLanguage = "mk_mk";
break;
case "mn":
case "mn-MN":
gameLanguage = "mn_mn";
break;
case "mr":
case "mr-IN":
break;
case "ms":
case "ms-BN":
case "ms-MY":
gameLanguage = "ms_my";
break;
case "mt-MT":
gameLanguage = "mt_mt";
break;
case "nb-NO":
break;
case "nl":
case "nl-NL":
gameLanguage = "nl_nl";
break;
case "nl-BE":
gameLanguage = "nl_be";
break;
case "nn-NO":
gameLanguage = "nn_no";
break;
case "no":
gameLanguage = "no_no";
break;
case "ns-ZA":
break;
case "pa":
case "pa-IN":
break;
case "pl":
case "pl-PL":
gameLanguage = "pl_pl";
break;
case "pt":
case "pt-PT":
gameLanguage = "pt_pt";
break;
case "pt-BR":
gameLanguage = "pt_br";
break;
case "quz-BO":
break;
case "quz-EC":
break;
case "quz-PE":
break;
case "ro":
case "ro-RO":
gameLanguage = "ro_ro";
break;
case "ru":
case "ru-RU":
gameLanguage = "ru_ru";
name.Add("ru");
break;
case "sa":
case "sa-IN":
break;
case "se-FI":
case "se-NO":
case "se-SE":
gameLanguage = "se_no";
break;
case "sk":
case "sk-SK":
gameLanguage = "sk_sk";
break;
case "sl":
case "sl-SI":
gameLanguage = "sl_si";
break;
case "sma-NO":
break;
case "sma-SE":
break;
case "smj-NO":
break;
case "smj-SE":
break;
case "smn-FI":
break;
case "sms-FI":
break;
case "sq":
case "sq-AL":
gameLanguage = "sq_al";
break;
case "sr":
case "sr-Cyrl-BA":
case "sr-Cyrl-CS":
case "sr-Latn-BA":
case "sr-Latn-CS":
gameLanguage = "sr_sp";
break;
case "sv":
case "sv-FI":
case "sv-SE":
gameLanguage = "sv_se";
break;
case "sw":
case "sw-KE":
break;
case "syr":
case "syr-SY":
break;
case "ta":
case "ta-IN":
gameLanguage = "ta_in";
break;
case "te":
case "te-IN":
break;
case "th":
case "th-TH":
gameLanguage = "th_th";
break;
case "tn-ZA":
break;
case "tr":
case "tr-TR":
gameLanguage = "tr_tr";
break;
case "tt":
case "tt-RU":
gameLanguage = "tt_ru";
break;
case "uk":
case "uk-UA":
gameLanguage = "uk_ua";
break;
case "ur":
case "ur-PK":
break;
case "uz":
case "uz-Cyrl-UZ":
case "uz-Latn-UZ":
break;
case "vi":
case "vi-VN":
gameLanguage = "vi_vn";
name.Add("vi");
break;
case "xh-ZA":
break;
case "zh-Hans": /* CurrentCulture.Parent.Name */
case "zh":
case "zh-CN":
case "zh-CHS":
case "zh-SG":
gameLanguage = "zh_cn";
name.Add("zh_Hans");
name.Add("zh_Hant");
break;
case "zh-Hant": /* CurrentCulture.Parent.Name */
case "zh-HK":
case "zh-CHT":
case "zh-MO":
gameLanguage = "zh_hk";
name.Add("zh_Hant");
name.Add("zh_Hans");
break;
case "zh-TW":
gameLanguage = "zh_tw";
name.Add("zh_Hant");
name.Add("zh_Hans");
break;
case "zu-ZA":
break;
}
name.Add("en");
return new(gameLanguage, name.ToArray());
}
public static string[] GetTranslationPriority(string gameLanguage)
{
List<string> name = new();
switch (gameLanguage)
{
case "de_at":
case "de_ch":
case "de_de":
name.Add("de");
break;
case "en_au":
case "en_ca":
case "en_gb":
case "en_nz":
case "en_pt":
case "en_ud":
case "en_us":
break;
case "fr_ca":
case "fr_fr":
name.Add("fr");
break;
case "ru_ru":
name.Add("ru");
break;
case "vi_vn":
name.Add("vi");
break;
case "zh_cn":
name.Add("zh_Hans");
name.Add("zh_Hant");
break;
case "zh_hk":
case "zh_tw":
name.Add("zh_Hant");
name.Add("zh_Hans");
break;
}
name.Add("en");
return name.ToArray();
}
/// <summary>
/// Load default translation file (English)
/// Load translation files
/// </summary>
/// <remarks>
/// This will be loaded during program start up.
/// </remarks>
private static void LoadDefaultTranslationsFile()
{
string[] engLang = DefaultConfigResource.TranslationEnglish.Split(new[] { "\r\n", "\r", "\n" }, StringSplitOptions.None); // use embedded translations
ParseTranslationContent(engLang);
}
/// <summary>
/// Load translation file depends on system language or by giving a file path. Default to English if translation file does not exist
/// </summary>
public static void LoadExternalTranslationFile(string language)
public static void LoadTranslationFile(string[] languageList)
{
/*
* External translation files
@ -108,44 +587,39 @@ namespace MinecraftClient
* Lang/abc.ini, e.g. Lang/eng.ini which is the default language file
* Useful for adding new translations of fixing typos without recompiling
*/
// Try to convert Minecraft language file name to two letters language name
if (language == "zh_cn")
language = "zh-CHS";
else if (language == "zh_tw")
language = "zh-CHT";
else
language = language.Split('_')[0];
string systemLanguage = string.IsNullOrEmpty(CultureInfo.CurrentCulture.Parent.Name) // Parent.Name might be empty
? CultureInfo.CurrentCulture.Name
: CultureInfo.CurrentCulture.Parent.Name;
string baseDir = AppDomain.CurrentDomain.BaseDirectory;
string langDir = baseDir + ((baseDir.EndsWith(Path.DirectorySeparatorChar) ? String.Empty : Path.DirectorySeparatorChar) +
translationFilePath + Path.DirectorySeparatorChar);
string langFileSystemLanguage = langDir + systemLanguage + ".ini";
string langFileConfigLanguage = langDir + language + ".ini";
if (File.Exists(langFileConfigLanguage))
{// Language set in ini config
ParseTranslationContent(File.ReadAllLines(langFileConfigLanguage));
return;
}
else
foreach (string lang in languageList)
{
if (Settings.DebugMessages)
ConsoleIO.WriteLogLine("[Translations] No translation file found for " + language + ". (Looked '" + langFileConfigLanguage + "'");
}
bool fileLoaded = false;
string langFileName = string.Format("{0}{1}.ini", langDir, lang);
if (File.Exists(langFileSystemLanguage))
{// Fallback to system language
ParseTranslationContent(File.ReadAllLines(langFileSystemLanguage));
return;
}
else
{
if (Settings.DebugMessages)
ConsoleIO.WriteLogLine("[Translations] No translation file found for system language (" + systemLanguage + "). (Looked '" + langFileSystemLanguage + "'");
if (File.Exists(langFileName)) // Language set in ini config
{
fileLoaded = true;
Dictionary<string, string> trans = ParseTranslationContent(File.ReadAllLines(langFileName));
foreach ((string key, string value) in trans)
if (!string.IsNullOrWhiteSpace(value) && !translations.ContainsKey(key))
translations.Add(key, value);
}
string? resourseLangFile = DefaultConfigResource.ResourceManager.GetString("Translation_" + lang);
if (resourseLangFile != null)
{
fileLoaded = true;
Dictionary<string, string> trans = ParseTranslationContent(resourseLangFile.Split(new[] { "\r\n", "\r", "\n" }, StringSplitOptions.None));
foreach ((string key, string value) in trans)
if (!string.IsNullOrWhiteSpace(value) && !translations.ContainsKey(key))
translations.Add(key, value);
}
if (!fileLoaded)
{
if (Settings.Config.Logging.DebugMessages)
ConsoleIO.WriteLogLine("[Translations] No translation file found for " + lang + ". (Looked '" + langFileName + "'");
}
}
}
@ -153,8 +627,9 @@ namespace MinecraftClient
/// Parse the given array to translation map
/// </summary>
/// <param name="content">Content of the translation file (in ini format)</param>
private static void ParseTranslationContent(string[] content)
private static Dictionary<string, string> ParseTranslationContent(string[] content)
{
Dictionary<string, string> translations = new();
foreach (string lineRaw in content)
{
string line = lineRaw.Trim();
@ -172,6 +647,7 @@ namespace MinecraftClient
translations[translationName] = translationValue;
}
}
return translations;
}
/// <summary>
@ -185,7 +661,7 @@ namespace MinecraftClient
{
Directory.CreateDirectory(translationFilePath);
}
File.WriteAllText(defaultPath, DefaultConfigResource.TranslationEnglish, Encoding.UTF8);
File.WriteAllText(defaultPath, DefaultConfigResource.Translation_en, Encoding.UTF8);
}
#region Console writing method wrapper

View file

@ -67,6 +67,16 @@ namespace MinecraftClient.WinAPI
imageStream.Dispose();
httpWebRequest.Dispose();
}
catch (AggregateException ae)
{
foreach (var ex in ae.InnerExceptions)
{
if (ex is HttpRequestException) //Skin not found? Reset to default icon
RevertToMCCIcon();
else
throw ex;
}
}
catch (HttpRequestException) //Skin not found? Reset to default icon
{
RevertToMCCIcon();

View file

@ -1,6 +1,7 @@
using System;
using System.Collections.Generic;
using System.Runtime.InteropServices;
using System.Threading;
namespace MinecraftClient.WinAPI
{
@ -79,9 +80,10 @@ namespace MinecraftClient.WinAPI
}
[DllImport("Kernel32")]
private static extern bool SetConsoleCtrlHandler(ConsoleCtrlHandler handler, bool add);
private delegate bool ConsoleCtrlHandler(CtrlType sig);
private static readonly ConsoleCtrlHandler? _handler;
private static extern bool SetConsoleCtrlHandler(EventHandler handler, bool add);
private delegate bool EventHandler(CtrlType sig);
static EventHandler? _handler;
enum CtrlType
{