mirror of
https://github.com/MCCTeam/Minecraft-Console-Client
synced 2026-08-15 13:04:36 +00:00
Merge branch 'MCCTeam:master' into master
This commit is contained in:
commit
37f71d4494
639 changed files with 126385 additions and 17202 deletions
36
MinecraftClient/Achievement.cs
Normal file
36
MinecraftClient/Achievement.cs
Normal file
|
|
@ -0,0 +1,36 @@
|
|||
using System.Collections.Generic;
|
||||
|
||||
namespace MinecraftClient
|
||||
{
|
||||
/// <summary>
|
||||
/// The type of an achievement or advancement.
|
||||
/// </summary>
|
||||
public enum AchievementType
|
||||
{
|
||||
Task,
|
||||
Challenge,
|
||||
Goal,
|
||||
Legacy
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Represents a Minecraft achievement (pre-1.12) or advancement (1.12+).
|
||||
/// </summary>
|
||||
/// <param name="Id">Resource identifier, e.g. "minecraft:story/root" or "achievement.openInventory"</param>
|
||||
/// <param name="Title">Display title (null for legacy achievements without display info)</param>
|
||||
/// <param name="Description">Display description (null for legacy achievements without display info)</param>
|
||||
/// <param name="Type">The frame type / achievement category</param>
|
||||
/// <param name="IsHidden">Whether this advancement is hidden in the UI</param>
|
||||
/// <param name="IsCompleted">Whether all requirements have been met</param>
|
||||
/// <param name="Requirements">OR-groups of criterion names; all groups must be satisfied</param>
|
||||
/// <param name="CriteriaProgress">Per-criterion completion status</param>
|
||||
public record Achievement(
|
||||
string Id,
|
||||
string? Title,
|
||||
string? Description,
|
||||
AchievementType Type,
|
||||
bool IsHidden,
|
||||
bool IsCompleted,
|
||||
IReadOnlyList<IReadOnlyList<string>> Requirements,
|
||||
IReadOnlyDictionary<string, bool> CriteriaProgress);
|
||||
}
|
||||
|
|
@ -48,8 +48,9 @@ namespace MinecraftClient.ChatBots
|
|||
Delay.min = Math.Max(1.0, Delay.min);
|
||||
Delay.max = Math.Max(1.0, Delay.max);
|
||||
|
||||
Delay.min = Math.Min(int.MaxValue / 10, Delay.min);
|
||||
Delay.max = Math.Min(int.MaxValue / 10, Delay.max);
|
||||
double maxDelaySeconds = int.MaxValue / (double)Settings.ClientTicksPerSecond;
|
||||
Delay.min = Math.Min(maxDelaySeconds, Delay.min);
|
||||
Delay.max = Math.Min(maxDelaySeconds, Delay.max);
|
||||
|
||||
if (Delay.min > Delay.max)
|
||||
{
|
||||
|
|
@ -64,6 +65,12 @@ namespace MinecraftClient.ChatBots
|
|||
{
|
||||
public double min, max;
|
||||
|
||||
public Range()
|
||||
{
|
||||
min = 0;
|
||||
max = 0;
|
||||
}
|
||||
|
||||
public Range(int value)
|
||||
{
|
||||
min = max = value;
|
||||
|
|
@ -77,7 +84,7 @@ namespace MinecraftClient.ChatBots
|
|||
}
|
||||
}
|
||||
|
||||
private int count, nextrun = 50;
|
||||
private int count, nextrun = Settings.DoubleToTick(5.0);
|
||||
private bool previousSneakState = false;
|
||||
private readonly Random random = new();
|
||||
|
||||
|
|
@ -120,7 +127,7 @@ namespace MinecraftClient.ChatBots
|
|||
private void DoAntiAfkStuff()
|
||||
{
|
||||
var isMovementLocked = BotMovementLock.Instance;
|
||||
if (Config.Use_Terrain_Handling && GetTerrainEnabled() && isMovementLocked is {IsLocked: false})
|
||||
if (Config.Use_Terrain_Handling && GetTerrainEnabled() && isMovementLocked is { IsLocked: false })
|
||||
{
|
||||
var currentLocation = GetCurrentLocation();
|
||||
|
||||
|
|
@ -180,4 +187,4 @@ namespace MinecraftClient.ChatBots
|
|||
currentLocation.Z + random.Next(range * -1, range));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -28,7 +28,7 @@ namespace MinecraftClient.ChatBots
|
|||
public PriorityType Priority = PriorityType.distance;
|
||||
|
||||
[TomlInlineComment("$ChatBot.AutoAttack.Cooldown_Time$")]
|
||||
public CooldownConfig Cooldown_Time = new(false, 1.0);
|
||||
public CooldownConfig Cooldown_Time = new();
|
||||
|
||||
[TomlInlineComment("$ChatBot.AutoAttack.Interaction$")]
|
||||
public InteractType Interaction = InteractType.Attack;
|
||||
|
|
@ -50,10 +50,19 @@ namespace MinecraftClient.ChatBots
|
|||
|
||||
public void OnSettingUpdate()
|
||||
{
|
||||
if (Cooldown_Time.Custom && Cooldown_Time.value <= 0)
|
||||
if (Cooldown_Time.Custom)
|
||||
{
|
||||
LogToConsole(BotName, Translations.bot_autoAttack_invalidcooldown);
|
||||
Cooldown_Time.value = 1.0;
|
||||
if (Cooldown_Time.Min <= 0)
|
||||
Cooldown_Time.Min = 0.1;
|
||||
if (Cooldown_Time.Max <= 0)
|
||||
Cooldown_Time.Max = 0.1;
|
||||
|
||||
if (Cooldown_Time.Min > Cooldown_Time.Max)
|
||||
{
|
||||
double temp = Cooldown_Time.Min;
|
||||
Cooldown_Time.Min = Cooldown_Time.Max;
|
||||
Cooldown_Time.Max = temp;
|
||||
}
|
||||
}
|
||||
|
||||
if (Attack_Range < 1.0)
|
||||
|
|
@ -72,24 +81,16 @@ namespace MinecraftClient.ChatBots
|
|||
public struct CooldownConfig
|
||||
{
|
||||
public bool Custom;
|
||||
public double value;
|
||||
public bool RandomMode = false;
|
||||
public double Min = 1.5;
|
||||
public double Max = 2.5;
|
||||
|
||||
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;
|
||||
RandomMode = false;
|
||||
Min = 1.5;
|
||||
Max = 2.5;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -105,14 +106,15 @@ namespace MinecraftClient.ChatBots
|
|||
private float health = 100;
|
||||
private readonly bool attackHostile = true;
|
||||
private readonly bool attackPassive = false;
|
||||
private readonly Random _random = new();
|
||||
|
||||
public AutoAttack()
|
||||
{
|
||||
overrideAttackSpeed = Config.Cooldown_Time.Custom;
|
||||
if (Config.Cooldown_Time.Custom)
|
||||
{
|
||||
attackCooldownSeconds = Config.Cooldown_Time.value;
|
||||
attackCooldown = Convert.ToInt32(Math.Truncate(attackCooldownSeconds / 0.1) + 1);
|
||||
attackCooldownSeconds = Config.Cooldown_Time.Min;
|
||||
attackCooldown = SecondsToAttackCooldownTicks(attackCooldownSeconds);
|
||||
}
|
||||
|
||||
attackHostile = Config.Attack_Hostile;
|
||||
|
|
@ -137,6 +139,12 @@ namespace MinecraftClient.ChatBots
|
|||
|
||||
if (attackCooldownCounter == 0)
|
||||
{
|
||||
if (Config.Cooldown_Time.Custom && Config.Cooldown_Time.RandomMode)
|
||||
{
|
||||
double randomSeconds = _random.NextDouble() * (Config.Cooldown_Time.Max - Config.Cooldown_Time.Min) + Config.Cooldown_Time.Min;
|
||||
attackCooldown = SecondsToAttackCooldownTicks(randomSeconds);
|
||||
}
|
||||
|
||||
attackCooldownCounter = attackCooldown;
|
||||
if (entitiesToAttack.Count > 0)
|
||||
{
|
||||
|
|
@ -177,6 +185,8 @@ namespace MinecraftClient.ChatBots
|
|||
InteractEntity(priorityEntity, Config.Interaction); // hit the entity!
|
||||
SendAnimation(Inventory.Hand.MainHand); // Arm animation
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
}
|
||||
else
|
||||
|
|
@ -188,6 +198,7 @@ namespace MinecraftClient.ChatBots
|
|||
{
|
||||
InteractEntity(entity.Key, Config.Interaction); // hit the entity!
|
||||
}
|
||||
|
||||
}
|
||||
SendAnimation(Inventory.Hand.MainHand); // Arm animation
|
||||
}
|
||||
|
|
@ -274,7 +285,7 @@ namespace MinecraftClient.ChatBots
|
|||
serverTPS = GetServerTPS();
|
||||
attackSpeed = prop[attackSpeedKey];
|
||||
attackCooldownSeconds = 1 / attackSpeed * (serverTPS / 20.0); // server tps will affect the cooldown
|
||||
attackCooldown = Convert.ToInt32(Math.Truncate(attackCooldownSeconds / 0.1) + 1);
|
||||
attackCooldown = SecondsToAttackCooldownTicks(attackCooldownSeconds);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -288,7 +299,13 @@ namespace MinecraftClient.ChatBots
|
|||
serverTPS = tps;
|
||||
// re-calculate attack speed
|
||||
attackCooldownSeconds = 1 / attackSpeed * (serverTPS / 20.0); // server tps will affect the cooldown
|
||||
attackCooldown = Convert.ToInt32(Math.Truncate(attackCooldownSeconds / 0.1) + 1);
|
||||
attackCooldown = SecondsToAttackCooldownTicks(attackCooldownSeconds);
|
||||
}
|
||||
|
||||
private static int SecondsToAttackCooldownTicks(double seconds)
|
||||
{
|
||||
seconds = Math.Min(int.MaxValue / (double)Settings.ClientTicksPerSecond, seconds);
|
||||
return Math.Max(1, (int)Math.Truncate(seconds * Settings.ClientTicksPerSecond) + 1);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
|
|
|
|||
|
|
@ -106,6 +106,13 @@ namespace MinecraftClient.ChatBots
|
|||
{
|
||||
public double X, Y, Z;
|
||||
|
||||
public LocationConfig()
|
||||
{
|
||||
X = 0;
|
||||
Y = 0;
|
||||
Z = 0;
|
||||
}
|
||||
|
||||
public LocationConfig(double X, double Y, double Z)
|
||||
{
|
||||
this.X = X;
|
||||
|
|
@ -116,7 +123,7 @@ namespace MinecraftClient.ChatBots
|
|||
|
||||
public enum OnFailConfig { abort, wait }
|
||||
|
||||
public class RecipeConfig
|
||||
public record RecipeConfig
|
||||
{
|
||||
public string Name = "Recipe Name";
|
||||
|
||||
|
|
@ -153,9 +160,9 @@ namespace MinecraftClient.ChatBots
|
|||
private Recipe? recipeInUse;
|
||||
private readonly List<ActionStep> actionSteps = new();
|
||||
|
||||
private int updateDebounceValue = 2;
|
||||
private int updateDebounceValue = Settings.DoubleToTick(0.2);
|
||||
private int updateDebounce = 0;
|
||||
private readonly int updateTimeoutValue = 10;
|
||||
private readonly int updateTimeoutValue = Settings.ClientTicksPerSecond;
|
||||
private int updateTimeout = 0;
|
||||
private string timeoutAction = "unspecified";
|
||||
|
||||
|
|
@ -234,7 +241,7 @@ namespace MinecraftClient.ChatBots
|
|||
/// <summary>
|
||||
/// Represent a crafting recipe
|
||||
/// </summary>
|
||||
private class Recipe
|
||||
private record Recipe
|
||||
{
|
||||
/// <summary>
|
||||
/// The results item of this recipe
|
||||
|
|
@ -269,7 +276,7 @@ namespace MinecraftClient.ChatBots
|
|||
/// <remarks>so that it can be used in crafting table</remarks>
|
||||
public static Recipe ConvertToCraftingTable(Recipe recipe)
|
||||
{
|
||||
if (recipe.CraftingAreaType == ContainerType.PlayerInventory && recipe.Materials != null)
|
||||
if (recipe.CraftingAreaType == ContainerType.PlayerInventory && recipe.Materials is not null)
|
||||
{
|
||||
if (recipe.Materials.ContainsKey(4))
|
||||
{
|
||||
|
|
@ -493,7 +500,7 @@ namespace MinecraftClient.ChatBots
|
|||
}
|
||||
}
|
||||
|
||||
if (recipe.Materials != null)
|
||||
if (recipe.Materials is not null)
|
||||
{
|
||||
foreach (KeyValuePair<int, ItemType> slot in recipe.Materials)
|
||||
{
|
||||
|
|
|
|||
|
|
@ -1,10 +1,13 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Threading;
|
||||
using Brigadier.NET.Builder;
|
||||
using MinecraftClient.CommandHandler;
|
||||
using MinecraftClient.CommandHandler.Patch;
|
||||
using MinecraftClient.Inventory;
|
||||
using MinecraftClient.Mapping;
|
||||
using MinecraftClient.Protocol.Handlers.StructuredComponents.Components._1_20_6;
|
||||
using MinecraftClient.Scripting;
|
||||
using Tomlet.Attributes;
|
||||
|
||||
|
|
@ -24,15 +27,18 @@ namespace MinecraftClient.ChatBots
|
|||
|
||||
public bool Enabled = false;
|
||||
|
||||
[NonSerialized]
|
||||
[TomlInlineComment("$ChatBot.AutoDig.Auto_Tool_Switch$")]
|
||||
public bool Auto_Tool_Switch = false;
|
||||
|
||||
[NonSerialized]
|
||||
[TomlInlineComment("$ChatBot.AutoDig.Apply_Efficiency_Enchantments$")]
|
||||
public bool Apply_Efficiency_Enchantments = true;
|
||||
|
||||
[TomlInlineComment("$ChatBot.AutoDig.Apply_Haste_Effects$")]
|
||||
public bool Apply_Haste_Effects = true;
|
||||
|
||||
[TomlInlineComment("$ChatBot.AutoDig.Durability_Limit$")]
|
||||
public int Durability_Limit = 2;
|
||||
|
||||
[NonSerialized]
|
||||
[TomlInlineComment("$ChatBot.AutoDig.Drop_Low_Durability_Tools$")]
|
||||
public bool Drop_Low_Durability_Tools = false;
|
||||
|
||||
|
|
@ -64,6 +70,8 @@ namespace MinecraftClient.ChatBots
|
|||
|
||||
public void OnSettingUpdate()
|
||||
{
|
||||
Durability_Limit = Math.Max(0, Durability_Limit);
|
||||
|
||||
if (Auto_Start_Delay >= 0)
|
||||
Auto_Start_Delay = Math.Max(0.1, Auto_Start_Delay);
|
||||
|
||||
|
|
@ -85,6 +93,13 @@ namespace MinecraftClient.ChatBots
|
|||
{
|
||||
public double x, y, z;
|
||||
|
||||
public Coordination()
|
||||
{
|
||||
x = 0;
|
||||
y = 0;
|
||||
z = 0;
|
||||
}
|
||||
|
||||
public Coordination(double x, double y, double z)
|
||||
{
|
||||
this.x = x; this.y = y; this.z = z;
|
||||
|
|
@ -95,7 +110,7 @@ namespace MinecraftClient.ChatBots
|
|||
private bool inventoryEnabled;
|
||||
|
||||
private int counter = 0;
|
||||
private readonly object stateLock = new();
|
||||
private readonly Lock stateLock = new();
|
||||
private State state = State.WaitJoinGame;
|
||||
|
||||
bool AlreadyWaitting = false;
|
||||
|
|
@ -217,6 +232,111 @@ namespace MinecraftClient.ChatBots
|
|||
}
|
||||
}
|
||||
|
||||
private static int GetLegacyMaxDamage(ItemType itemType)
|
||||
{
|
||||
return itemType switch
|
||||
{
|
||||
ItemType.WoodenPickaxe or ItemType.WoodenAxe or ItemType.WoodenShovel or ItemType.WoodenSword or ItemType.WoodenHoe => 59,
|
||||
ItemType.StonePickaxe or ItemType.StoneAxe or ItemType.StoneShovel or ItemType.StoneSword or ItemType.StoneHoe => 131,
|
||||
ItemType.IronPickaxe or ItemType.IronAxe or ItemType.IronShovel or ItemType.IronSword or ItemType.IronHoe => 250,
|
||||
ItemType.GoldenPickaxe or ItemType.GoldenAxe or ItemType.GoldenShovel or ItemType.GoldenSword or ItemType.GoldenHoe => 32,
|
||||
ItemType.DiamondPickaxe or ItemType.DiamondAxe or ItemType.DiamondShovel or ItemType.DiamondSword or ItemType.DiamondHoe => 1561,
|
||||
ItemType.NetheritePickaxe or ItemType.NetheriteAxe or ItemType.NetheriteShovel or ItemType.NetheriteSword or ItemType.NetheriteHoe => 2031,
|
||||
ItemType.Shears => 238,
|
||||
_ => 0
|
||||
};
|
||||
}
|
||||
|
||||
private static int GetMaxDamage(Item item)
|
||||
{
|
||||
if (item.Components is not null)
|
||||
{
|
||||
var maxDamageComponent = item.Components.OfType<MaxDamageComponent>().FirstOrDefault();
|
||||
if (maxDamageComponent is not null)
|
||||
return maxDamageComponent.MaxDamage;
|
||||
}
|
||||
|
||||
return GetLegacyMaxDamage(item.Type);
|
||||
}
|
||||
|
||||
private static int GetRemainingDurability(Item item)
|
||||
{
|
||||
int maxDamage = GetMaxDamage(item);
|
||||
return maxDamage > 0 ? maxDamage - item.Damage : int.MaxValue;
|
||||
}
|
||||
|
||||
private bool HasEnoughDurability(Item item)
|
||||
{
|
||||
return Config.Durability_Limit <= 0 || GetRemainingDurability(item) >= Config.Durability_Limit;
|
||||
}
|
||||
|
||||
private bool IsBelowDurabilityLimit(Item? item)
|
||||
{
|
||||
return item is not null && Config.Durability_Limit > 0 && GetRemainingDurability(item) < Config.Durability_Limit;
|
||||
}
|
||||
|
||||
private static bool IsRecommendedTool(Item? item, ItemType[] recommendedTools)
|
||||
{
|
||||
return item is not null && recommendedTools.Contains(item.Type);
|
||||
}
|
||||
|
||||
private bool SwapToolIntoHand(int sourceSlot, int handSlot)
|
||||
{
|
||||
return WindowAction(0, sourceSlot, WindowActionType.LeftClick)
|
||||
&& WindowAction(0, handSlot, WindowActionType.LeftClick)
|
||||
&& WindowAction(0, sourceSlot, WindowActionType.LeftClick);
|
||||
}
|
||||
|
||||
private bool EnsureSuitableTool(Material blockType)
|
||||
{
|
||||
if (!inventoryEnabled || !Config.Auto_Tool_Switch)
|
||||
return true;
|
||||
|
||||
ItemType[] recommendedTools = Material2Tool.GetCorrectToolForBlock(blockType);
|
||||
if (recommendedTools.Length == 0)
|
||||
return true;
|
||||
|
||||
Container container = GetPlayerInventory();
|
||||
int handSlot = 36 + GetCurrentSlot();
|
||||
container.Items.TryGetValue(handSlot, out Item? currentTool);
|
||||
|
||||
if (currentTool is not null && IsRecommendedTool(currentTool, recommendedTools) && HasEnoughDurability(currentTool))
|
||||
return true;
|
||||
|
||||
foreach (ItemType recommendedTool in recommendedTools)
|
||||
{
|
||||
foreach ((int slot, Item item) in container.Items)
|
||||
{
|
||||
if (slot == handSlot || item.Type != recommendedTool || !HasEnoughDurability(item))
|
||||
continue;
|
||||
|
||||
if (!SwapToolIntoHand(slot, handSlot))
|
||||
return false;
|
||||
|
||||
LogToConsole(GetTimestamp() + ": " + string.Format(Translations.bot_autodig_switch, item.GetTypeString(), slot));
|
||||
|
||||
if (Config.Drop_Low_Durability_Tools && IsBelowDurabilityLimit(currentTool) &&
|
||||
WindowAction(0, slot, WindowActionType.DropItemStack))
|
||||
{
|
||||
LogToConsole(GetTimestamp() + ": " + string.Format(Translations.bot_autodig_drop_low_durability, currentTool!.GetTypeString(), slot));
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return !IsBelowDurabilityLimit(currentTool);
|
||||
}
|
||||
|
||||
private static MiningCalculator.MiningOptions GetMiningOptions()
|
||||
{
|
||||
return new MiningCalculator.MiningOptions
|
||||
{
|
||||
ApplyEfficiencyEnchantments = Config.Apply_Efficiency_Enchantments,
|
||||
ApplyHasteEffects = Config.Apply_Haste_Effects
|
||||
};
|
||||
}
|
||||
|
||||
public override void Update()
|
||||
{
|
||||
lock (stateLock)
|
||||
|
|
@ -285,7 +405,10 @@ namespace MinecraftClient.ChatBots
|
|||
if (Config.Mode == Configs.ModeType.lookat ||
|
||||
(Config.Mode == Configs.ModeType.both && Config._Locations.Contains(blockLoc)))
|
||||
{
|
||||
if (DigBlock(blockLoc, lookAtBlock: false))
|
||||
if (!EnsureSuitableTool(block.Type))
|
||||
return false;
|
||||
|
||||
if (DigBlock(blockLoc, Direction.Down, lookAtBlock: false, miningOptions: GetMiningOptions()))
|
||||
{
|
||||
currentDig = blockLoc;
|
||||
if (Config.Log_Block_Dig)
|
||||
|
|
@ -346,7 +469,10 @@ namespace MinecraftClient.ChatBots
|
|||
|
||||
if (minDistance <= 6.0)
|
||||
{
|
||||
if (DigBlock(target, lookAtBlock: true))
|
||||
if (!EnsureSuitableTool(targetBlock.Type))
|
||||
return false;
|
||||
|
||||
if (DigBlock(target, Direction.Down, lookAtBlock: true, miningOptions: GetMiningOptions()))
|
||||
{
|
||||
currentDig = target;
|
||||
if (Config.Log_Block_Dig)
|
||||
|
|
@ -380,7 +506,10 @@ namespace MinecraftClient.ChatBots
|
|||
((Config.List_Type == Configs.ListType.whitelist && Config.Blocks.Contains(block.Type)) ||
|
||||
(Config.List_Type == Configs.ListType.blacklist && !Config.Blocks.Contains(block.Type))))
|
||||
{
|
||||
if (DigBlock(blockLoc, lookAtBlock: true))
|
||||
if (!EnsureSuitableTool(block.Type))
|
||||
return false;
|
||||
|
||||
if (DigBlock(blockLoc, Direction.Down, lookAtBlock: true, miningOptions: GetMiningOptions()))
|
||||
{
|
||||
currentDig = blockLoc;
|
||||
if (Config.Log_Block_Dig)
|
||||
|
|
|
|||
|
|
@ -41,7 +41,7 @@ namespace MinecraftClient.ChatBots
|
|||
}
|
||||
|
||||
private int updateDebounce = 0;
|
||||
private readonly int updateDebounceValue = 2;
|
||||
private readonly int updateDebounceValue = Settings.DoubleToTick(0.2);
|
||||
private int inventoryUpdated = -1;
|
||||
|
||||
public override void Initialize()
|
||||
|
|
|
|||
|
|
@ -2,6 +2,7 @@
|
|||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading;
|
||||
using Brigadier.NET.Builder;
|
||||
using MinecraftClient.CommandHandler;
|
||||
using MinecraftClient.CommandHandler.Patch;
|
||||
|
|
@ -61,6 +62,21 @@ namespace MinecraftClient.ChatBots
|
|||
[TomlInlineComment("$ChatBot.AutoFishing.Hook_Threshold$")]
|
||||
public double Hook_Threshold = 0.2;
|
||||
|
||||
[TomlInlineComment("$ChatBot.AutoFishing.Enable_Velocity_Detection$")]
|
||||
public bool Enable_Velocity_Detection = true;
|
||||
|
||||
[TomlInlineComment("$ChatBot.AutoFishing.Velocity_Hook_Threshold$")]
|
||||
public double Velocity_Hook_Threshold = -0.2;
|
||||
|
||||
[TomlInlineComment("$ChatBot.AutoFishing.Enable_Sound_Detection$")]
|
||||
public bool Enable_Sound_Detection = true;
|
||||
|
||||
[TomlInlineComment("$ChatBot.AutoFishing.Sound_Distance$")]
|
||||
public double Sound_Distance = 5.0;
|
||||
|
||||
[TomlInlineComment("$ChatBot.AutoFishing.Detection_Warmup$")]
|
||||
public double Detection_Warmup = 1.0;
|
||||
|
||||
[TomlInlineComment("$ChatBot.AutoFishing.Log_Fish_Bobber$")]
|
||||
public bool Log_Fish_Bobber = false;
|
||||
|
||||
|
|
@ -96,6 +112,15 @@ namespace MinecraftClient.ChatBots
|
|||
|
||||
if (Hook_Threshold < 0)
|
||||
Hook_Threshold = -Hook_Threshold;
|
||||
|
||||
if (Velocity_Hook_Threshold > 0)
|
||||
Velocity_Hook_Threshold = -Velocity_Hook_Threshold;
|
||||
|
||||
if (Sound_Distance < 0)
|
||||
Sound_Distance = -Sound_Distance;
|
||||
|
||||
if (Detection_Warmup < 0)
|
||||
Detection_Warmup = 0;
|
||||
}
|
||||
|
||||
public struct LocationConfig
|
||||
|
|
@ -103,6 +128,12 @@ namespace MinecraftClient.ChatBots
|
|||
public Coordination? XYZ;
|
||||
public Facing? facing;
|
||||
|
||||
public LocationConfig()
|
||||
{
|
||||
XYZ = null;
|
||||
facing = null;
|
||||
}
|
||||
|
||||
public LocationConfig(double yaw, double pitch)
|
||||
{
|
||||
this.XYZ = null;
|
||||
|
|
@ -125,6 +156,13 @@ namespace MinecraftClient.ChatBots
|
|||
{
|
||||
public double x, y, z;
|
||||
|
||||
public Coordination()
|
||||
{
|
||||
x = 0;
|
||||
y = 0;
|
||||
z = 0;
|
||||
}
|
||||
|
||||
public Coordination(double x, double y, double z)
|
||||
{
|
||||
this.x = x; this.y = y; this.z = z;
|
||||
|
|
@ -135,6 +173,12 @@ namespace MinecraftClient.ChatBots
|
|||
{
|
||||
public double yaw, pitch;
|
||||
|
||||
public Facing()
|
||||
{
|
||||
yaw = 0;
|
||||
pitch = 0;
|
||||
}
|
||||
|
||||
public Facing(double yaw, double pitch)
|
||||
{
|
||||
this.yaw = yaw; this.pitch = pitch;
|
||||
|
|
@ -151,12 +195,13 @@ namespace MinecraftClient.ChatBots
|
|||
private Entity? fishingBobber;
|
||||
private Location LastPos = Location.Zero;
|
||||
private DateTime CaughtTime = DateTime.Now;
|
||||
private DateTime BobberSpawnTime = DateTime.MinValue;
|
||||
private int fishItemCounter = 15;
|
||||
private Dictionary<ItemType, uint> fishItemCnt = new();
|
||||
private Entity fishItem = new(-1, EntityType.Item, Location.Zero);
|
||||
|
||||
private int counter = 0;
|
||||
private readonly object stateLock = new();
|
||||
private readonly Lock stateLock = new();
|
||||
private FishingState state = FishingState.WaitJoinGame;
|
||||
|
||||
private int curLocationIdx = 0, moveDir = 1;
|
||||
|
|
@ -444,6 +489,7 @@ namespace MinecraftClient.ChatBots
|
|||
fishingBobber = entity;
|
||||
LastPos = entity.Location;
|
||||
isFishing = true;
|
||||
BobberSpawnTime = DateTime.Now;
|
||||
|
||||
castTimeout = 24;
|
||||
counter = 0;
|
||||
|
|
@ -454,7 +500,7 @@ namespace MinecraftClient.ChatBots
|
|||
|
||||
public override void OnEntityDespawn(Entity entity)
|
||||
{
|
||||
if (entity != null && fishingBobber != null && entity.Type == EntityType.FishingBobber && entity.ID == fishingBobber!.ID)
|
||||
if (entity is not null && fishingBobber is not null && entity.Type == EntityType.FishingBobber && entity.ID == fishingBobber!.ID)
|
||||
{
|
||||
if (Config.Log_Fish_Bobber)
|
||||
LogToConsole(string.Format("FishingBobber despawn at {0}", entity.Location));
|
||||
|
|
@ -479,8 +525,8 @@ namespace MinecraftClient.ChatBots
|
|||
|
||||
public override void OnEntityMove(Entity entity)
|
||||
{
|
||||
if (isFishing && entity != null && fishingBobber!.ID == entity.ID &&
|
||||
(state == FishingState.WaitingFishToBite || state == FishingState.WaitingFishingBobber))
|
||||
if (isFishing && entity is not null && fishingBobber!.ID == entity.ID &&
|
||||
state == FishingState.WaitingFishToBite)
|
||||
{
|
||||
Location Pos = entity.Location;
|
||||
double Dx = LastPos.X - Pos.X;
|
||||
|
|
@ -495,13 +541,7 @@ namespace MinecraftClient.ChatBots
|
|||
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)
|
||||
{
|
||||
isFishing = false;
|
||||
CaughtTime = DateTime.Now;
|
||||
OnCaughtFish();
|
||||
}
|
||||
TryCatchFish();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -520,6 +560,38 @@ namespace MinecraftClient.ChatBots
|
|||
}
|
||||
}
|
||||
|
||||
public override void OnEntityVelocity(Entity entity, double velocityX, double velocityY, double velocityZ)
|
||||
{
|
||||
if (!Config.Enable_Velocity_Detection || !CanUseAdvancedDetection())
|
||||
return;
|
||||
|
||||
if (fishingBobber is null || entity.ID != fishingBobber.ID)
|
||||
return;
|
||||
|
||||
if (velocityY <= Config.Velocity_Hook_Threshold)
|
||||
TryCatchFish();
|
||||
}
|
||||
|
||||
public override void OnSoundEffect(string? soundName, Location? location, int category, float volume, float pitch,
|
||||
Entity? sourceEntity)
|
||||
{
|
||||
if (!Config.Enable_Sound_Detection || !CanUseAdvancedDetection())
|
||||
return;
|
||||
|
||||
if (!IsFishingBobberSplashSound(soundName))
|
||||
return;
|
||||
|
||||
Location? soundLocation = location;
|
||||
if (soundLocation is null && sourceEntity is not null)
|
||||
soundLocation = sourceEntity.Location;
|
||||
|
||||
if (soundLocation is null || fishingBobber is null)
|
||||
return;
|
||||
|
||||
if (soundLocation.Value.Distance(fishingBobber.Location) <= Config.Sound_Distance)
|
||||
TryCatchFish();
|
||||
}
|
||||
|
||||
public override void AfterGameJoined()
|
||||
{
|
||||
StartFishing();
|
||||
|
|
@ -542,10 +614,42 @@ namespace MinecraftClient.ChatBots
|
|||
fishingBobber = null;
|
||||
LastPos = Location.Zero;
|
||||
CaughtTime = DateTime.Now;
|
||||
BobberSpawnTime = DateTime.MinValue;
|
||||
|
||||
return base.OnDisconnect(reason, message);
|
||||
}
|
||||
|
||||
private bool CanUseAdvancedDetection()
|
||||
{
|
||||
if (!isFishing || fishingBobber is null || state != FishingState.WaitingFishToBite)
|
||||
return false;
|
||||
|
||||
return (DateTime.Now - BobberSpawnTime).TotalSeconds >= Config.Detection_Warmup;
|
||||
}
|
||||
|
||||
private void TryCatchFish()
|
||||
{
|
||||
if (!CanUseAdvancedDetection())
|
||||
return;
|
||||
|
||||
// Prevent repeated catches from multiple packets of the same bite.
|
||||
if ((DateTime.Now - CaughtTime).TotalSeconds <= 1)
|
||||
return;
|
||||
|
||||
isFishing = false;
|
||||
CaughtTime = DateTime.Now;
|
||||
OnCaughtFish();
|
||||
}
|
||||
|
||||
private static bool IsFishingBobberSplashSound(string? soundName)
|
||||
{
|
||||
return string.Equals(soundName, "minecraft:entity.fishing_bobber.splash",
|
||||
StringComparison.OrdinalIgnoreCase)
|
||||
|| string.Equals(soundName, "entity.fishing_bobber.splash", StringComparison.OrdinalIgnoreCase)
|
||||
|| string.Equals(soundName, "minecraft:entity.bobber.splash", StringComparison.OrdinalIgnoreCase)
|
||||
|| string.Equals(soundName, "entity.bobber.splash", StringComparison.OrdinalIgnoreCase);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Called when detected a fish is caught
|
||||
/// </summary>
|
||||
|
|
@ -583,12 +687,12 @@ namespace MinecraftClient.ChatBots
|
|||
|
||||
LocationConfig curConfig = locationList[curLocationIdx];
|
||||
|
||||
if (curConfig.facing != null)
|
||||
if (curConfig.facing is not null)
|
||||
(nextYaw, nextPitch) = ((float)curConfig.facing.Value.yaw, (float)curConfig.facing.Value.pitch);
|
||||
else
|
||||
(nextYaw, nextPitch) = (GetYaw(), GetPitch());
|
||||
|
||||
if (curConfig.XYZ != null)
|
||||
if (curConfig.XYZ is not null)
|
||||
{
|
||||
Location current = GetCurrentLocation();
|
||||
Location goal = new(curConfig.XYZ.Value.x, curConfig.XYZ.Value.y, curConfig.XYZ.Value.z);
|
||||
|
|
|
|||
|
|
@ -1,4 +1,5 @@
|
|||
using System;
|
||||
using System;
|
||||
using System.Threading;
|
||||
using MinecraftClient.Scripting;
|
||||
using Tomlet.Attributes;
|
||||
|
||||
|
|
@ -39,8 +40,9 @@ namespace MinecraftClient.ChatBots
|
|||
Delay.min = Math.Max(0.1, Delay.min);
|
||||
Delay.max = Math.Max(0.1, Delay.max);
|
||||
|
||||
Delay.min = Math.Min(int.MaxValue / 10, Delay.min);
|
||||
Delay.max = Math.Min(int.MaxValue / 10, Delay.max);
|
||||
double maxDelaySeconds = int.MaxValue / (double)Settings.ClientTicksPerSecond;
|
||||
Delay.min = Math.Min(maxDelaySeconds, Delay.min);
|
||||
Delay.max = Math.Min(maxDelaySeconds, Delay.max);
|
||||
|
||||
if (Delay.min > Delay.max)
|
||||
(Delay.min, Delay.max) = (Delay.max, Delay.min);
|
||||
|
|
@ -57,6 +59,12 @@ namespace MinecraftClient.ChatBots
|
|||
{
|
||||
public double min, max;
|
||||
|
||||
public Range()
|
||||
{
|
||||
min = 0;
|
||||
max = 0;
|
||||
}
|
||||
|
||||
public Range(int value)
|
||||
{
|
||||
min = max = value;
|
||||
|
|
@ -70,7 +78,9 @@ namespace MinecraftClient.ChatBots
|
|||
}
|
||||
}
|
||||
|
||||
private static readonly Random random = new();
|
||||
private static readonly Lock s_reconnectStateLock = new();
|
||||
private static readonly TimeSpan s_stableJoinBeforeRetryReset = TimeSpan.FromSeconds(60);
|
||||
private static DateTime? s_lastJoinUtc;
|
||||
|
||||
/// <summary>
|
||||
/// This bot automatically re-join the server if kick message contains predefined string
|
||||
|
|
@ -88,6 +98,17 @@ namespace MinecraftClient.ChatBots
|
|||
_Initialize();
|
||||
}
|
||||
|
||||
public override void AfterGameJoined()
|
||||
{
|
||||
lock (s_reconnectStateLock)
|
||||
s_lastJoinUtc = DateTime.UtcNow;
|
||||
}
|
||||
|
||||
public override void Update()
|
||||
{
|
||||
ResetRetriesAfterStableJoin();
|
||||
}
|
||||
|
||||
private void _Initialize()
|
||||
{
|
||||
McClient.ReconnectionAttemptsLeft = Config.Retries;
|
||||
|
|
@ -103,7 +124,11 @@ namespace MinecraftClient.ChatBots
|
|||
{
|
||||
LogDebugToConsole(Translations.bot_autoRelog_ignore_user_logout);
|
||||
}
|
||||
else if (Config.Retries < 0 || Configs._BotRecoAttempts < Config.Retries)
|
||||
else if (Program.HasRestartPendingForAnotherThread)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
else if (CanReconnect())
|
||||
{
|
||||
message = GetVerbatim(message);
|
||||
string comp = message.ToLower();
|
||||
|
|
@ -112,18 +137,14 @@ namespace MinecraftClient.ChatBots
|
|||
|
||||
if (Config.Ignore_Kick_Message)
|
||||
{
|
||||
Configs._BotRecoAttempts++;
|
||||
LaunchDelayedReconnection(null);
|
||||
return true;
|
||||
return LaunchDelayedReconnection(null);
|
||||
}
|
||||
|
||||
foreach (string msg in Config.Kick_Messages)
|
||||
{
|
||||
if (comp.Contains(msg))
|
||||
{
|
||||
Configs._BotRecoAttempts++;
|
||||
LaunchDelayedReconnection(msg);
|
||||
return true;
|
||||
return LaunchDelayedReconnection(msg);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -133,14 +154,83 @@ namespace MinecraftClient.ChatBots
|
|||
return false;
|
||||
}
|
||||
|
||||
private void LaunchDelayedReconnection(string? msg)
|
||||
private static bool CanReconnect()
|
||||
{
|
||||
double delay = random.NextDouble() * (Config.Delay.max - Config.Delay.min) + Config.Delay.min;
|
||||
lock (s_reconnectStateLock)
|
||||
return Config.Retries < 0 || Configs._BotRecoAttempts < Config.Retries;
|
||||
}
|
||||
|
||||
private static void ResetRetriesAfterStableJoin()
|
||||
{
|
||||
lock (s_reconnectStateLock)
|
||||
{
|
||||
if (Configs._BotRecoAttempts <= 0 || s_lastJoinUtc is not DateTime lastJoinUtc)
|
||||
return;
|
||||
|
||||
if (DateTime.UtcNow - lastJoinUtc < s_stableJoinBeforeRetryReset)
|
||||
return;
|
||||
|
||||
Configs._BotRecoAttempts = 0;
|
||||
s_lastJoinUtc = null;
|
||||
McClient.ReconnectionAttemptsLeft = Config.Retries;
|
||||
}
|
||||
}
|
||||
|
||||
private static bool TryConsumeReconnectAttempt(out int retriesLeft)
|
||||
{
|
||||
lock (s_reconnectStateLock)
|
||||
{
|
||||
bool unlimitedRetries = HasUnlimitedRetries();
|
||||
if (!unlimitedRetries && Configs._BotRecoAttempts >= Config.Retries)
|
||||
{
|
||||
retriesLeft = 0;
|
||||
return false;
|
||||
}
|
||||
|
||||
Configs._BotRecoAttempts++;
|
||||
s_lastJoinUtc = null;
|
||||
retriesLeft = unlimitedRetries ? int.MaxValue : Config.Retries - Configs._BotRecoAttempts;
|
||||
if (retriesLeft < 0)
|
||||
retriesLeft = 0;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
private static bool HasUnlimitedRetries()
|
||||
{
|
||||
return Config.Retries < 0 || Config.Retries == int.MaxValue;
|
||||
}
|
||||
|
||||
private static void RollBackReconnectAttempt()
|
||||
{
|
||||
lock (s_reconnectStateLock)
|
||||
{
|
||||
if (Configs._BotRecoAttempts > 0)
|
||||
Configs._BotRecoAttempts--;
|
||||
}
|
||||
}
|
||||
|
||||
private bool LaunchDelayedReconnection(string? msg)
|
||||
{
|
||||
if (!TryConsumeReconnectAttempt(out int retriesLeft))
|
||||
return false;
|
||||
|
||||
double delay = Random.Shared.NextDouble() * (Config.Delay.max - Config.Delay.min) + Config.Delay.min;
|
||||
LogDebugToConsole(string.Format(string.IsNullOrEmpty(msg) ? Translations.bot_autoRelog_reconnect_always : Translations.bot_autoRelog_reconnect, msg));
|
||||
|
||||
// TODO: Change this translation string to add the retries left text
|
||||
LogToConsole(string.Format(Translations.bot_autoRelog_wait, delay) + $" ({Config.Retries - Configs._BotRecoAttempts} retries left)");
|
||||
ReconnectToTheServer(Config.Retries - Configs._BotRecoAttempts, (int)Math.Floor(delay), true);
|
||||
|
||||
string retriesDisplay = HasUnlimitedRetries()
|
||||
? Translations.bot_autoRelog_retries_unlimited
|
||||
: retriesLeft.ToString();
|
||||
|
||||
McClient.ReconnectionAttemptsLeft = retriesLeft;
|
||||
if (Program.TryRestart((int)Math.Floor(delay), true))
|
||||
{
|
||||
LogToConsole(string.Format(Translations.bot_autoRelog_wait_with_retries, delay, retriesDisplay));
|
||||
return true;
|
||||
}
|
||||
|
||||
RollBackReconnectAttempt();
|
||||
return true;
|
||||
}
|
||||
|
||||
public static bool OnDisconnectStatic(DisconnectReason reason, string message)
|
||||
|
|
|
|||
|
|
@ -132,7 +132,7 @@ namespace MinecraftClient.ChatBots
|
|||
if (String.IsNullOrEmpty(toSend))
|
||||
return null;
|
||||
|
||||
if (regex != null)
|
||||
if (regex is not null)
|
||||
{
|
||||
if (regex.IsMatch(message))
|
||||
{
|
||||
|
|
@ -261,15 +261,15 @@ namespace MinecraftClient.ChatBots
|
|||
/// <param name="cooldown">Minimal cooldown between two matches</param>
|
||||
private void CheckAddMatch(Regex? matchRegex, string? matchString, string? matchAction, string? matchActionPrivate, string? matchActionOther, bool ownersOnly, TimeSpan cooldown)
|
||||
{
|
||||
if (matchRegex != null || matchString != null || matchAction != null || matchActionPrivate != null || matchActionOther != null || ownersOnly || cooldown != TimeSpan.Zero)
|
||||
if (matchRegex is not null || matchString is not null || matchAction is not null || matchActionPrivate is not null || matchActionOther is not null || ownersOnly || cooldown != TimeSpan.Zero)
|
||||
{
|
||||
RespondRule rule = matchRegex != null
|
||||
RespondRule rule = matchRegex is not null
|
||||
? new RespondRule(matchRegex, matchAction, matchActionPrivate, matchActionOther, ownersOnly, cooldown)
|
||||
: new RespondRule(matchString, matchAction, matchActionPrivate, matchActionOther, ownersOnly, cooldown);
|
||||
|
||||
if (matchAction != null || matchActionPrivate != null || matchActionOther != null)
|
||||
if (matchAction is not null || matchActionPrivate is not null || matchActionOther is not null)
|
||||
{
|
||||
if (matchRegex != null || matchString != null)
|
||||
if (matchRegex is not null || matchString is not null)
|
||||
{
|
||||
respondRules!.Add(rule);
|
||||
LogDebugToConsole(string.Format(Translations.bot_autoRespond_loaded_match, rule));
|
||||
|
|
|
|||
|
|
@ -1,5 +1,6 @@
|
|||
using System;
|
||||
using System.IO;
|
||||
using System.Threading;
|
||||
using MinecraftClient.CommandHandler;
|
||||
using MinecraftClient.Scripting;
|
||||
using Tomlet.Attributes;
|
||||
|
|
@ -50,7 +51,7 @@ namespace MinecraftClient.ChatBots
|
|||
private bool saveChat = true;
|
||||
private bool savePrivate = true;
|
||||
private bool saveInternal = true;
|
||||
private readonly object logfileLock = new();
|
||||
private readonly Lock logfileLock = new();
|
||||
|
||||
/// <summary>
|
||||
/// This bot saves the messages received in the specified file, with some filters and date/time tagging.
|
||||
|
|
|
|||
|
|
@ -1,7 +1,11 @@
|
|||
using System;
|
||||
using System;
|
||||
using System.Collections.Concurrent;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Text.RegularExpressions;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using Brigadier.NET.Builder;
|
||||
using DSharpPlus;
|
||||
|
|
@ -33,6 +37,9 @@ namespace MinecraftClient.ChatBots
|
|||
private DiscordChannel? discordChannel;
|
||||
private BridgeDirection bridgeDirection = BridgeDirection.Both;
|
||||
|
||||
private readonly ConcurrentQueue<string> aggregationBuffer = new();
|
||||
private Timer? aggregationTimer;
|
||||
|
||||
public static Configs Config = new();
|
||||
|
||||
[TomlDoNotInlineObject]
|
||||
|
|
@ -58,6 +65,15 @@ namespace MinecraftClient.ChatBots
|
|||
[TomlInlineComment("$ChatBot.DiscordBridge.MessageSendTimeout$")]
|
||||
public int Message_Send_Timeout = 3;
|
||||
|
||||
[TomlInlineComment("$ChatBot.DiscordBridge.AllowOtherBotMessages$")]
|
||||
public bool Allow_Other_Bot_Messages = false;
|
||||
|
||||
[TomlInlineComment("$ChatBot.DiscordBridge.RelayAllMessages$")]
|
||||
public bool Relay_All_Messages = false;
|
||||
|
||||
[TomlInlineComment("$ChatBot.DiscordBridge.MessageAggregationInterval$")]
|
||||
public double Message_Aggregation_Interval = 3.0;
|
||||
|
||||
[TomlPrecedingComment("$ChatBot.DiscordBridge.Formats$")]
|
||||
public string PrivateMessageFormat = "**[Private Message]** {username}: {message}";
|
||||
public string PublicMessageFormat = "{username}: {message}";
|
||||
|
|
@ -66,6 +82,8 @@ namespace MinecraftClient.ChatBots
|
|||
public void OnSettingUpdate()
|
||||
{
|
||||
Message_Send_Timeout = Message_Send_Timeout <= 0 ? 3 : Message_Send_Timeout;
|
||||
if (Message_Aggregation_Interval < 0)
|
||||
Message_Aggregation_Interval = 0;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -96,6 +114,12 @@ namespace MinecraftClient.ChatBots
|
|||
.Redirect(McClient.dispatcher.GetRoot().GetChild("help").GetChild(CommandName)))
|
||||
);
|
||||
|
||||
if (Config.Message_Aggregation_Interval > 0)
|
||||
{
|
||||
var intervalMs = (int)(Config.Message_Aggregation_Interval * 1000);
|
||||
aggregationTimer = new Timer(_ => FlushAggregationBuffer(), null, intervalMs, intervalMs);
|
||||
}
|
||||
|
||||
Task.Run(async () => await MainAsync());
|
||||
}
|
||||
|
||||
|
|
@ -103,6 +127,7 @@ namespace MinecraftClient.ChatBots
|
|||
{
|
||||
McClient.dispatcher.Unregister(CommandName);
|
||||
McClient.dispatcher.GetRoot().GetChild("help").RemoveChild(CommandName);
|
||||
StopAggregation();
|
||||
Disconnect();
|
||||
}
|
||||
|
||||
|
|
@ -143,6 +168,40 @@ namespace MinecraftClient.ChatBots
|
|||
return r.SetAndReturn(CmdResult.Status.Done, string.Format(Translations.bot_DiscordBridge_direction, bridgeName));
|
||||
}
|
||||
|
||||
private void FlushAggregationBuffer()
|
||||
{
|
||||
if (aggregationBuffer.IsEmpty || !CanSendMessages())
|
||||
return;
|
||||
|
||||
var sb = new StringBuilder();
|
||||
while (aggregationBuffer.TryDequeue(out var line))
|
||||
{
|
||||
if (sb.Length + line.Length + 1 > 1900)
|
||||
{
|
||||
SendMessage(sb.ToString());
|
||||
sb.Clear();
|
||||
}
|
||||
|
||||
if (sb.Length > 0)
|
||||
sb.AppendLine();
|
||||
sb.Append(line);
|
||||
}
|
||||
|
||||
if (sb.Length > 0)
|
||||
SendMessage(sb.ToString());
|
||||
}
|
||||
|
||||
private void StopAggregation()
|
||||
{
|
||||
if (aggregationTimer is not null)
|
||||
{
|
||||
aggregationTimer.Dispose();
|
||||
aggregationTimer = null;
|
||||
}
|
||||
|
||||
FlushAggregationBuffer();
|
||||
}
|
||||
|
||||
~DiscordBridge()
|
||||
{
|
||||
Disconnect();
|
||||
|
|
@ -150,11 +209,11 @@ namespace MinecraftClient.ChatBots
|
|||
|
||||
private void Disconnect()
|
||||
{
|
||||
if (discordBotClient != null)
|
||||
if (discordBotClient is not null)
|
||||
{
|
||||
try
|
||||
{
|
||||
if (discordChannel != null)
|
||||
if (discordChannel is not null)
|
||||
discordBotClient.SendMessageAsync(discordChannel, new DiscordEmbedBuilder
|
||||
{
|
||||
Description = Translations.bot_DiscordBridge_disconnected,
|
||||
|
|
@ -184,7 +243,6 @@ namespace MinecraftClient.ChatBots
|
|||
|
||||
text = GetVerbatim(text).Trim();
|
||||
|
||||
// Stop the crash when an empty text is recived somehow
|
||||
if (string.IsNullOrEmpty(text))
|
||||
return;
|
||||
|
||||
|
|
@ -201,7 +259,10 @@ namespace MinecraftClient.ChatBots
|
|||
message = Config.TeleportRequestMessageFormat.Replace("{username}", username).Replace("{timestamp}", GetTimestamp()).Trim();
|
||||
teleportRequest = true;
|
||||
}
|
||||
else message = text;
|
||||
else if (Config.Relay_All_Messages)
|
||||
message = text;
|
||||
else
|
||||
return;
|
||||
|
||||
if (teleportRequest)
|
||||
{
|
||||
|
|
@ -219,7 +280,28 @@ namespace MinecraftClient.ChatBots
|
|||
SendMessage(messageBuilder);
|
||||
return;
|
||||
}
|
||||
else SendMessage(message);
|
||||
|
||||
string discordText = GetDiscordText(message);
|
||||
|
||||
if (Config.Message_Aggregation_Interval > 0)
|
||||
aggregationBuffer.Enqueue(discordText);
|
||||
else
|
||||
SendMessage(discordText);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Converts Minecraft § formatting codes to Discord Markdown equivalents
|
||||
/// and strips remaining § codes.
|
||||
/// Handles both properly closed formatting (§l...§r) and unclosed formatting (§l... end).
|
||||
/// </summary>
|
||||
private static string GetDiscordText(string text)
|
||||
{
|
||||
text = Regex.Replace(text, @"§l(.*?)(?:§r|$)", "**$1**");
|
||||
text = Regex.Replace(text, @"§m(.*?)(?:§r|$)", "~~$1~~");
|
||||
text = Regex.Replace(text, @"§n(.*?)(?:§r|$)", "__$1__");
|
||||
text = Regex.Replace(text, @"§o(.*?)(?:§r|$)", "*$1*");
|
||||
text = Regex.Replace(text, @"§.", "");
|
||||
return text;
|
||||
}
|
||||
|
||||
public void SendMessage(string message)
|
||||
|
|
@ -281,10 +363,10 @@ namespace MinecraftClient.ChatBots
|
|||
filePath = filePath[(filePath.IndexOf(Path.DirectorySeparatorChar) + 1)..];
|
||||
var messageBuilder = new DiscordMessageBuilder();
|
||||
|
||||
if (text != null)
|
||||
if (text is not null)
|
||||
messageBuilder.WithContent(text);
|
||||
|
||||
messageBuilder.WithFiles(new Dictionary<string, Stream>() { { $"attachment://{filePath}", fs } });
|
||||
messageBuilder.AddFiles(new Dictionary<string, Stream>() { { filePath, fs } });
|
||||
|
||||
discordBotClient!.SendMessageAsync(discordChannel, messageBuilder).Wait(Config.Message_Send_Timeout * 1000);
|
||||
}
|
||||
|
|
@ -301,12 +383,12 @@ namespace MinecraftClient.ChatBots
|
|||
if (!CanSendMessages())
|
||||
return;
|
||||
|
||||
SendMessage(new DiscordMessageBuilder().WithFile(fileStream));
|
||||
SendMessage(new DiscordMessageBuilder().AddFile(fileStream));
|
||||
}
|
||||
|
||||
private bool CanSendMessages()
|
||||
{
|
||||
return discordBotClient != null && discordChannel != null && bridgeDirection != BridgeDirection.Minecraft;
|
||||
return discordBotClient is not null && discordChannel is not null && bridgeDirection != BridgeDirection.Minecraft;
|
||||
}
|
||||
|
||||
async Task MainAsync()
|
||||
|
|
@ -372,12 +454,25 @@ namespace MinecraftClient.ChatBots
|
|||
if (e.Channel.Id != Config.ChannelId)
|
||||
return;
|
||||
|
||||
if (!Config.OwnersIds.Contains(e.Author.Id))
|
||||
// Always ignore own messages to prevent loops
|
||||
if (e.Author.Id == discordBotClient.CurrentUser.Id)
|
||||
return;
|
||||
|
||||
string message = e.Message.Content.Trim();
|
||||
|
||||
if (string.IsNullOrEmpty(message) || string.IsNullOrWhiteSpace(message))
|
||||
if (string.IsNullOrWhiteSpace(message))
|
||||
return;
|
||||
|
||||
// Relay messages from other bots when configured, but never process commands from them.
|
||||
// Skip relay when direction is Discord-only (Discord -> MC disabled).
|
||||
if (e.Author.IsBot)
|
||||
{
|
||||
if (Config.Allow_Other_Bot_Messages && bridgeDirection != BridgeDirection.Discord)
|
||||
SendText(message);
|
||||
return;
|
||||
}
|
||||
|
||||
if (!Config.OwnersIds.Contains(e.Author.Id))
|
||||
return;
|
||||
|
||||
if (bridgeDirection == BridgeDirection.Discord)
|
||||
|
|
|
|||
727
MinecraftClient/ChatBots/DiscordRpc.cs
Normal file
727
MinecraftClient/ChatBots/DiscordRpc.cs
Normal file
|
|
@ -0,0 +1,727 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.IO.Pipes;
|
||||
using System.Threading;
|
||||
using DiscordRPC;
|
||||
using DiscordRPC.IO;
|
||||
using DiscordRPC.Logging;
|
||||
using MinecraftClient.Mapping;
|
||||
using MinecraftClient.Scripting;
|
||||
using Tomlet.Attributes;
|
||||
|
||||
namespace MinecraftClient.ChatBots
|
||||
{
|
||||
/// <summary>
|
||||
/// Displays a Discord Rich Presence status showing the player's
|
||||
/// current Minecraft session information (server, health, dimension, etc.).
|
||||
/// Requires a Discord Application ID from https://discord.com/developers/applications
|
||||
/// </summary>
|
||||
public class DiscordRpc : ChatBot
|
||||
{
|
||||
public static Configs Config = new();
|
||||
|
||||
[TomlDoNotInlineObject]
|
||||
public class Configs
|
||||
{
|
||||
[NonSerialized]
|
||||
private const string BotName = "DiscordRpc";
|
||||
|
||||
public bool Enabled = false;
|
||||
|
||||
[TomlInlineComment("$ChatBot.DiscordRpc.ApplicationId$")]
|
||||
public string ApplicationId = string.Empty;
|
||||
|
||||
[TomlInlineComment("$ChatBot.DiscordRpc.PresenceDetails$")]
|
||||
public string PresenceDetails = "Playing on {server_host}:{server_port}";
|
||||
|
||||
[TomlInlineComment("$ChatBot.DiscordRpc.PresenceState$")]
|
||||
public string PresenceState = "{dimension} - HP: {health}/{max_health}";
|
||||
|
||||
[TomlInlineComment("$ChatBot.DiscordRpc.LargeImageKey$")]
|
||||
public string LargeImageKey = "mcc_icon";
|
||||
|
||||
[TomlInlineComment("$ChatBot.DiscordRpc.LargeImageText$")]
|
||||
public string LargeImageText = "Minecraft Console Client";
|
||||
|
||||
[TomlInlineComment("$ChatBot.DiscordRpc.SmallImageKey$")]
|
||||
public string SmallImageKey = string.Empty;
|
||||
|
||||
[TomlInlineComment("$ChatBot.DiscordRpc.SmallImageText$")]
|
||||
public string SmallImageText = string.Empty;
|
||||
|
||||
[TomlInlineComment("$ChatBot.DiscordRpc.ShowServerAddress$")]
|
||||
public bool ShowServerAddress = true;
|
||||
|
||||
[TomlInlineComment("$ChatBot.DiscordRpc.ShowCoordinates$")]
|
||||
public bool ShowCoordinates = true;
|
||||
|
||||
[TomlInlineComment("$ChatBot.DiscordRpc.ShowHealth$")]
|
||||
public bool ShowHealth = true;
|
||||
|
||||
[TomlInlineComment("$ChatBot.DiscordRpc.ShowDimension$")]
|
||||
public bool ShowDimension = true;
|
||||
|
||||
[TomlInlineComment("$ChatBot.DiscordRpc.ShowGamemode$")]
|
||||
public bool ShowGamemode = true;
|
||||
|
||||
[TomlInlineComment("$ChatBot.DiscordRpc.ShowElapsedTime$")]
|
||||
public bool ShowElapsedTime = true;
|
||||
|
||||
[TomlInlineComment("$ChatBot.DiscordRpc.ShowPlayerCount$")]
|
||||
public bool ShowPlayerCount = true;
|
||||
|
||||
[TomlInlineComment("$ChatBot.DiscordRpc.UpdateIntervalSeconds$")]
|
||||
public int UpdateIntervalSeconds = 10;
|
||||
|
||||
public void OnSettingUpdate()
|
||||
{
|
||||
ApplicationId ??= string.Empty;
|
||||
PresenceDetails ??= string.Empty;
|
||||
PresenceState ??= string.Empty;
|
||||
LargeImageKey ??= string.Empty;
|
||||
LargeImageText ??= string.Empty;
|
||||
SmallImageKey ??= string.Empty;
|
||||
SmallImageText ??= string.Empty;
|
||||
|
||||
if (UpdateIntervalSeconds < 1)
|
||||
{
|
||||
UpdateIntervalSeconds = 10;
|
||||
LogToConsole(BotName, Translations.bot_DiscordRpc_invalid_interval);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private DiscordRpcClient? _rpcClient;
|
||||
private int _tickCounter;
|
||||
private int _updateIntervalTicks;
|
||||
private Timestamps? _sessionTimestamps;
|
||||
private float _lastHealth;
|
||||
|
||||
public override void Initialize()
|
||||
{
|
||||
if (string.IsNullOrWhiteSpace(Config.ApplicationId))
|
||||
{
|
||||
LogToConsole(Translations.bot_DiscordRpc_missing_app_id);
|
||||
UnloadBot();
|
||||
return;
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
_rpcClient = OperatingSystem.IsLinux()
|
||||
? new DiscordRpcClient(Config.ApplicationId.Trim(), client: new DiscordRpcPipeClient())
|
||||
: new DiscordRpcClient(Config.ApplicationId.Trim());
|
||||
|
||||
_rpcClient.Logger = Settings.Config.Logging.DebugMessages
|
||||
? new ConsoleLogger(LogLevel.Trace)
|
||||
: new ConsoleLogger(LogLevel.None);
|
||||
|
||||
_rpcClient.OnReady += (_, e) =>
|
||||
{
|
||||
LogToConsole(string.Format(Translations.bot_DiscordRpc_connected, e.User.Username));
|
||||
};
|
||||
|
||||
_rpcClient.OnConnectionFailed += (_, e) =>
|
||||
{
|
||||
LogToConsole(string.Format(Translations.bot_DiscordRpc_connection_failed, e.FailedPipe));
|
||||
};
|
||||
|
||||
_rpcClient.Initialize();
|
||||
_updateIntervalTicks = Settings.DoubleToTick(Config.UpdateIntervalSeconds);
|
||||
|
||||
LogToConsole(Translations.bot_DiscordRpc_initialized);
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
LogToConsole(string.Format(Translations.bot_DiscordRpc_init_error, e.Message));
|
||||
LogDebugToConsole(e.StackTrace ?? string.Empty);
|
||||
UnloadBot();
|
||||
}
|
||||
}
|
||||
|
||||
public override void OnUnload()
|
||||
{
|
||||
if (_rpcClient is { IsDisposed: false })
|
||||
{
|
||||
_rpcClient.ClearPresence();
|
||||
_rpcClient.Dispose();
|
||||
}
|
||||
|
||||
_rpcClient = null;
|
||||
}
|
||||
|
||||
public override void AfterGameJoined()
|
||||
{
|
||||
if (Config.ShowElapsedTime)
|
||||
_sessionTimestamps = Timestamps.Now;
|
||||
|
||||
_lastHealth = Handler.GetHealth();
|
||||
_tickCounter = 0;
|
||||
SetPresence();
|
||||
}
|
||||
|
||||
public override void Update()
|
||||
{
|
||||
_tickCounter++;
|
||||
if (_tickCounter < _updateIntervalTicks)
|
||||
return;
|
||||
|
||||
_tickCounter = 0;
|
||||
SetPresence();
|
||||
}
|
||||
|
||||
public override void OnHealthUpdate(float health, int food)
|
||||
{
|
||||
_lastHealth = health;
|
||||
}
|
||||
|
||||
public override bool OnDisconnect(DisconnectReason reason, string message)
|
||||
{
|
||||
if (_rpcClient is { IsDisposed: false })
|
||||
_rpcClient.ClearPresence();
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
private void SetPresence()
|
||||
{
|
||||
if (_rpcClient is null or { IsDisposed: true })
|
||||
return;
|
||||
|
||||
try
|
||||
{
|
||||
string details = ReplacePlaceholders(Config.PresenceDetails);
|
||||
string state = ReplacePlaceholders(Config.PresenceState);
|
||||
|
||||
var presence = new RichPresence
|
||||
{
|
||||
Details = TruncateForDiscord(details, 128),
|
||||
State = TruncateForDiscord(state, 128)
|
||||
};
|
||||
|
||||
// Assets (images)
|
||||
var assets = new Assets();
|
||||
bool hasAssets = false;
|
||||
|
||||
if (!string.IsNullOrWhiteSpace(Config.LargeImageKey))
|
||||
{
|
||||
assets.LargeImageKey = Config.LargeImageKey.Trim();
|
||||
assets.LargeImageText = TruncateForDiscord(
|
||||
ReplacePlaceholders(Config.LargeImageText), 128);
|
||||
hasAssets = true;
|
||||
}
|
||||
|
||||
if (!string.IsNullOrWhiteSpace(Config.SmallImageKey))
|
||||
{
|
||||
assets.SmallImageKey = Config.SmallImageKey.Trim();
|
||||
assets.SmallImageText = TruncateForDiscord(
|
||||
ReplacePlaceholders(Config.SmallImageText), 128);
|
||||
hasAssets = true;
|
||||
}
|
||||
|
||||
if (hasAssets)
|
||||
presence.Assets = assets;
|
||||
|
||||
// Timestamps
|
||||
if (Config.ShowElapsedTime && _sessionTimestamps is not null)
|
||||
presence.Timestamps = _sessionTimestamps;
|
||||
|
||||
// Player count as party
|
||||
if (Config.ShowPlayerCount)
|
||||
{
|
||||
string[] onlinePlayers = GetOnlinePlayers();
|
||||
int playerCount = onlinePlayers.Length;
|
||||
if (playerCount > 0)
|
||||
{
|
||||
presence.Party = new Party
|
||||
{
|
||||
ID = $"mcc_{GetServerHost()}_{GetServerPort()}",
|
||||
Size = playerCount,
|
||||
Max = playerCount
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
_rpcClient.SetPresence(presence);
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
LogDebugToConsole(string.Format(Translations.bot_DiscordRpc_update_error, e.Message));
|
||||
}
|
||||
}
|
||||
|
||||
private string ReplacePlaceholders(string template)
|
||||
{
|
||||
if (string.IsNullOrEmpty(template))
|
||||
return string.Empty;
|
||||
|
||||
string serverHost = Config.ShowServerAddress ? GetServerHost() : "Hidden";
|
||||
int serverPort = GetServerPort();
|
||||
string serverPortStr = Config.ShowServerAddress ? serverPort.ToString() : "****";
|
||||
string username = GetUsername();
|
||||
float health = Handler.GetHealth();
|
||||
int foodLevel = Handler.GetSaturation();
|
||||
Location location = GetCurrentLocation();
|
||||
string[] onlinePlayers = GetOnlinePlayers();
|
||||
int gamemode = GetGamemode();
|
||||
int protocolVersion = GetProtocolVersion();
|
||||
|
||||
string healthStr = Config.ShowHealth ? ((int)Math.Ceiling(health)).ToString() : "?";
|
||||
string maxHealthStr = Config.ShowHealth ? "20" : "?";
|
||||
string foodStr = Config.ShowHealth ? foodLevel.ToString() : "?";
|
||||
string xStr = Config.ShowCoordinates ? ((int)location.X).ToString() : "?";
|
||||
string yStr = Config.ShowCoordinates ? ((int)location.Y).ToString() : "?";
|
||||
string zStr = Config.ShowCoordinates ? ((int)location.Z).ToString() : "?";
|
||||
|
||||
string dimensionName = Config.ShowDimension ? "Unknown" : "Hidden";
|
||||
if (Config.ShowDimension)
|
||||
{
|
||||
try
|
||||
{
|
||||
var dim = World.GetDimension();
|
||||
dimensionName = dim.Name ?? "Unknown";
|
||||
|
||||
// Clean up the dimension name for display
|
||||
if (dimensionName.StartsWith("minecraft:", StringComparison.Ordinal))
|
||||
dimensionName = dimensionName["minecraft:".Length..];
|
||||
|
||||
dimensionName = dimensionName switch
|
||||
{
|
||||
"overworld" => "Overworld",
|
||||
"the_nether" => "The Nether",
|
||||
"the_end" => "The End",
|
||||
_ => dimensionName
|
||||
};
|
||||
}
|
||||
catch
|
||||
{
|
||||
// World may not be available
|
||||
}
|
||||
}
|
||||
|
||||
string gamemodeStr = Config.ShowGamemode
|
||||
? gamemode switch
|
||||
{
|
||||
0 => "Survival",
|
||||
1 => "Creative",
|
||||
2 => "Adventure",
|
||||
3 => "Spectator",
|
||||
_ => "Unknown"
|
||||
}
|
||||
: "Hidden";
|
||||
|
||||
return template
|
||||
.Replace("{server_host}", serverHost)
|
||||
.Replace("{server_port}", serverPortStr)
|
||||
.Replace("{username}", username)
|
||||
.Replace("{health}", healthStr)
|
||||
.Replace("{max_health}", maxHealthStr)
|
||||
.Replace("{food}", foodStr)
|
||||
.Replace("{dimension}", dimensionName)
|
||||
.Replace("{gamemode}", gamemodeStr)
|
||||
.Replace("{x}", xStr)
|
||||
.Replace("{y}", yStr)
|
||||
.Replace("{z}", zStr)
|
||||
.Replace("{player_count}", onlinePlayers.Length.ToString())
|
||||
.Replace("{protocol}", protocolVersion.ToString());
|
||||
}
|
||||
|
||||
private static string TruncateForDiscord(string value, int maxLength)
|
||||
{
|
||||
if (string.IsNullOrEmpty(value))
|
||||
return string.Empty;
|
||||
|
||||
return value.Length <= maxLength ? value : value[..(maxLength - 3)] + "...";
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Flatpak Discord exposes the RPC socket under app/com.discordapp.Discord,
|
||||
/// but the currently published DiscordRichPresence package does not probe that path.
|
||||
/// </summary>
|
||||
private sealed class DiscordRpcPipeClient : INamedPipeClient
|
||||
{
|
||||
private const string DiscordPipePrefix = "discord-ipc-";
|
||||
private const int MaximumPipeVariations = 10;
|
||||
|
||||
private static readonly string[] s_unixPackageDirectories =
|
||||
[
|
||||
// Official Discord clients
|
||||
"app/com.discordapp.Discord",
|
||||
"snap.discord",
|
||||
|
||||
// Community desktop clients / wrappers
|
||||
"app/dev.vencord.Vesktop",
|
||||
".flatpak/dev.vencord.Vesktop/xdg-run",
|
||||
"app/org.equicord.equibop",
|
||||
"app/io.github.equicord.equibop",
|
||||
"app/xyz.armcord.ArmCord",
|
||||
"app/io.github.spacingbat3.webcord"
|
||||
];
|
||||
|
||||
private readonly byte[] _buffer = new byte[PipeFrame.MAX_SIZE];
|
||||
private readonly Queue<PipeFrame> _frameQueue = new();
|
||||
private readonly Lock _frameQueueLock = new();
|
||||
private readonly Lock _streamLock = new();
|
||||
|
||||
private int _connectedPipe;
|
||||
private NamedPipeClientStream? _stream;
|
||||
private volatile bool _isClosed = true;
|
||||
private volatile bool _isDisposed;
|
||||
|
||||
public ILogger Logger { get; set; } = new NullLogger();
|
||||
|
||||
public bool IsConnected
|
||||
{
|
||||
get
|
||||
{
|
||||
if (_isClosed)
|
||||
return false;
|
||||
|
||||
lock (_streamLock)
|
||||
return _stream is { IsConnected: true };
|
||||
}
|
||||
}
|
||||
|
||||
[Obsolete("The connected pipe is not neccessary information.")]
|
||||
public int ConnectedPipe => _connectedPipe;
|
||||
|
||||
public bool Connect(int pipe)
|
||||
{
|
||||
Logger.Trace("DiscordRpcPipeClient.Connect({0})", pipe);
|
||||
|
||||
if (_isDisposed)
|
||||
throw new ObjectDisposedException(nameof(DiscordRpcPipeClient));
|
||||
|
||||
if (pipe > 9)
|
||||
throw new ArgumentOutOfRangeException(nameof(pipe), "Argument cannot be greater than 9");
|
||||
|
||||
int startPipe = pipe >= 0 ? pipe : 0;
|
||||
|
||||
foreach (string pipeName in GetPipeCandidates(startPipe))
|
||||
{
|
||||
if (AttemptConnection(pipeName))
|
||||
{
|
||||
BeginReadStream();
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
public bool ReadFrame(out PipeFrame frame)
|
||||
{
|
||||
if (_isDisposed)
|
||||
throw new ObjectDisposedException(nameof(DiscordRpcPipeClient));
|
||||
|
||||
lock (_frameQueueLock)
|
||||
{
|
||||
if (_frameQueue.Count == 0)
|
||||
{
|
||||
frame = default;
|
||||
return false;
|
||||
}
|
||||
|
||||
frame = _frameQueue.Dequeue();
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
public bool WriteFrame(PipeFrame frame)
|
||||
{
|
||||
if (_isDisposed)
|
||||
throw new ObjectDisposedException(nameof(DiscordRpcPipeClient));
|
||||
|
||||
if (_isClosed || !IsConnected)
|
||||
{
|
||||
Logger.Error("Failed to write frame because the stream is closed");
|
||||
return false;
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
frame.WriteStream(_stream);
|
||||
return true;
|
||||
}
|
||||
catch (IOException io)
|
||||
{
|
||||
Logger.Error("Failed to write frame because of a IO Exception: {0}", io.Message);
|
||||
}
|
||||
catch (ObjectDisposedException)
|
||||
{
|
||||
Logger.Warning("Failed to write frame as the stream was already disposed");
|
||||
}
|
||||
catch (InvalidOperationException)
|
||||
{
|
||||
Logger.Warning("Failed to write frame because of a invalid operation");
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
public void Close()
|
||||
{
|
||||
if (_isClosed)
|
||||
{
|
||||
Logger.Warning("Tried to close a already closed pipe.");
|
||||
return;
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
lock (_streamLock)
|
||||
{
|
||||
if (_stream is not null)
|
||||
{
|
||||
try
|
||||
{
|
||||
_stream.Flush();
|
||||
_stream.Dispose();
|
||||
}
|
||||
catch
|
||||
{
|
||||
}
|
||||
|
||||
_stream = null;
|
||||
_isClosed = true;
|
||||
}
|
||||
else
|
||||
{
|
||||
Logger.Warning("Stream was closed, but no stream was available to begin with!");
|
||||
}
|
||||
}
|
||||
}
|
||||
catch (ObjectDisposedException)
|
||||
{
|
||||
Logger.Warning("Tried to dispose already disposed stream");
|
||||
}
|
||||
finally
|
||||
{
|
||||
_isClosed = true;
|
||||
}
|
||||
}
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
if (_isDisposed)
|
||||
return;
|
||||
|
||||
if (!_isClosed)
|
||||
Close();
|
||||
|
||||
lock (_streamLock)
|
||||
{
|
||||
_stream?.Dispose();
|
||||
_stream = null;
|
||||
}
|
||||
|
||||
_isDisposed = true;
|
||||
}
|
||||
|
||||
private bool AttemptConnection(string pipeName)
|
||||
{
|
||||
if (_isDisposed)
|
||||
throw new ObjectDisposedException(nameof(DiscordRpcPipeClient));
|
||||
|
||||
try
|
||||
{
|
||||
lock (_streamLock)
|
||||
{
|
||||
Logger.Info("Attempting to connect to {0}", pipeName);
|
||||
_stream = new NamedPipeClientStream(".", pipeName, PipeDirection.InOut, PipeOptions.Asynchronous);
|
||||
_stream.Connect(0);
|
||||
|
||||
Logger.Trace("Waiting for connection...");
|
||||
while (!_stream.IsConnected)
|
||||
Thread.Sleep(10);
|
||||
}
|
||||
|
||||
Logger.Info("Connected to {0}", pipeName);
|
||||
_connectedPipe = int.Parse(pipeName[(pipeName.LastIndexOf('-') + 1)..], System.Globalization.CultureInfo.InvariantCulture);
|
||||
_isClosed = false;
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
Logger.Error("Failed connection to {0}. {1}", pipeName, e.Message);
|
||||
Close();
|
||||
}
|
||||
|
||||
Logger.Trace("Done. Result: {0}", _isClosed);
|
||||
return !_isClosed;
|
||||
}
|
||||
|
||||
private void BeginReadStream()
|
||||
{
|
||||
if (_isClosed)
|
||||
return;
|
||||
|
||||
try
|
||||
{
|
||||
lock (_streamLock)
|
||||
{
|
||||
if (_stream is not { IsConnected: true })
|
||||
return;
|
||||
|
||||
Logger.Trace("Beginning Read of {0} bytes", _buffer.Length);
|
||||
_stream.BeginRead(_buffer, 0, _buffer.Length, EndReadStream, _stream.IsConnected);
|
||||
}
|
||||
}
|
||||
catch (ObjectDisposedException)
|
||||
{
|
||||
Logger.Warning("Attempted to start reading from a disposed pipe");
|
||||
}
|
||||
catch (InvalidOperationException)
|
||||
{
|
||||
Logger.Warning("Attempted to start reading from a closed pipe");
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
Logger.Error("An exception occurred while starting to read a stream: {0}", e.Message);
|
||||
Logger.Error(e.StackTrace);
|
||||
}
|
||||
}
|
||||
|
||||
private void EndReadStream(IAsyncResult callback)
|
||||
{
|
||||
Logger.Trace("Ending Read");
|
||||
int bytes;
|
||||
|
||||
try
|
||||
{
|
||||
lock (_streamLock)
|
||||
{
|
||||
if (_stream is not { IsConnected: true })
|
||||
return;
|
||||
|
||||
bytes = _stream.EndRead(callback);
|
||||
}
|
||||
}
|
||||
catch (IOException)
|
||||
{
|
||||
Logger.Warning("Attempted to end reading from a closed pipe");
|
||||
return;
|
||||
}
|
||||
catch (NullReferenceException)
|
||||
{
|
||||
Logger.Warning("Attempted to read from a null pipe");
|
||||
return;
|
||||
}
|
||||
catch (ObjectDisposedException)
|
||||
{
|
||||
Logger.Warning("Attempted to end reading from a disposed pipe");
|
||||
return;
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
Logger.Error("An exception occurred while ending a read of a stream: {0}", e.Message);
|
||||
Logger.Error(e.StackTrace);
|
||||
return;
|
||||
}
|
||||
|
||||
Logger.Trace("Read {0} bytes", bytes);
|
||||
|
||||
if (bytes > 0)
|
||||
{
|
||||
using MemoryStream memory = new(_buffer, 0, bytes);
|
||||
try
|
||||
{
|
||||
PipeFrame frame = new();
|
||||
if (frame.ReadStream(memory))
|
||||
{
|
||||
Logger.Trace("Read a frame: {0}", frame.Opcode);
|
||||
lock (_frameQueueLock)
|
||||
_frameQueue.Enqueue(frame);
|
||||
}
|
||||
else
|
||||
{
|
||||
Logger.Error("Pipe failed to read from the data received by the stream.");
|
||||
Close();
|
||||
}
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
Logger.Error("An exception has occurred while trying to parse the pipe data: {0}", e.Message);
|
||||
Close();
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
Logger.Error("Empty frame was read on {0}, aborting.", Environment.OSVersion);
|
||||
Close();
|
||||
}
|
||||
|
||||
if (!_isClosed && IsConnected)
|
||||
{
|
||||
Logger.Trace("Starting another read");
|
||||
BeginReadStream();
|
||||
}
|
||||
}
|
||||
|
||||
private static IEnumerable<string> GetPipeCandidates(int startPipe)
|
||||
{
|
||||
if (OperatingSystem.IsWindows())
|
||||
{
|
||||
for (int i = startPipe; i < MaximumPipeVariations; i++)
|
||||
yield return $"{DiscordPipePrefix}{i}";
|
||||
|
||||
yield break;
|
||||
}
|
||||
|
||||
foreach (string runtimeDir in GetUnixRuntimeDirectories())
|
||||
{
|
||||
for (int index = startPipe; index < MaximumPipeVariations; index++)
|
||||
{
|
||||
string pipeFileName = $"{DiscordPipePrefix}{index}";
|
||||
|
||||
foreach (string packageDirectory in s_unixPackageDirectories)
|
||||
{
|
||||
string packagePipe = Path.Combine(runtimeDir, packageDirectory, pipeFileName);
|
||||
if (File.Exists(packagePipe))
|
||||
yield return packagePipe;
|
||||
}
|
||||
|
||||
string defaultPipe = Path.Combine(runtimeDir, pipeFileName);
|
||||
if (File.Exists(defaultPipe))
|
||||
yield return defaultPipe;
|
||||
|
||||
foreach (string packageDirectory in s_unixPackageDirectories)
|
||||
{
|
||||
string packagePipe = Path.Combine(runtimeDir, packageDirectory, pipeFileName);
|
||||
if (!File.Exists(packagePipe))
|
||||
yield return packagePipe;
|
||||
}
|
||||
|
||||
if (!File.Exists(defaultPipe))
|
||||
yield return defaultPipe;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private static IEnumerable<string> GetUnixRuntimeDirectories()
|
||||
{
|
||||
HashSet<string> yielded = new(StringComparer.Ordinal);
|
||||
|
||||
string[] candidates =
|
||||
[
|
||||
Environment.GetEnvironmentVariable("XDG_RUNTIME_DIR") ?? string.Empty,
|
||||
Environment.GetEnvironmentVariable("TMPDIR") ?? string.Empty,
|
||||
Environment.GetEnvironmentVariable("TMP") ?? string.Empty,
|
||||
Environment.GetEnvironmentVariable("TEMP") ?? string.Empty,
|
||||
Path.GetTempPath(),
|
||||
"/tmp"
|
||||
];
|
||||
|
||||
foreach (string candidate in candidates)
|
||||
{
|
||||
if (string.IsNullOrWhiteSpace(candidate))
|
||||
continue;
|
||||
|
||||
string normalized = candidate.TrimEnd(Path.DirectorySeparatorChar, Path.AltDirectorySeparatorChar);
|
||||
if (yielded.Add(normalized))
|
||||
yield return normalized;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -73,7 +73,7 @@ namespace MinecraftClient.ChatBots
|
|||
|
||||
public override void Initialize()
|
||||
{
|
||||
if (GetProtocolVersion() < Protocol18Handler.MC_1_13_Version)
|
||||
if (GetProtocolVersion() < Protocol18Handler.MC_1_8_Version)
|
||||
{
|
||||
LogToConsole(Translations.bot_farmer_not_implemented);
|
||||
return;
|
||||
|
|
@ -149,6 +149,10 @@ namespace MinecraftClient.ChatBots
|
|||
if (running)
|
||||
return r.SetAndReturn(CmdResult.Status.Fail, Translations.bot_farmer_already_running);
|
||||
|
||||
if (!IsCropAvailableForProtocol(whatToFarm, GetProtocolVersion()))
|
||||
return r.SetAndReturn(CmdResult.Status.Fail,
|
||||
string.Format(Translations.bot_farmer_crop_unavailable, whatToFarm, "1.9"));
|
||||
|
||||
var movementLock = BotMovementLock.Instance;
|
||||
if (movementLock is { IsLocked: true })
|
||||
return r.SetAndReturn(CmdResult.Status.Fail,
|
||||
|
|
@ -369,11 +373,11 @@ namespace MinecraftClient.ChatBots
|
|||
break;
|
||||
}
|
||||
|
||||
var loc = new Location(Math.Floor(location.X), Math.Floor(location2.Y),
|
||||
var loc = new Location(Math.Floor(location.X), Math.Floor(location.Y),
|
||||
Math.Floor(location.Z));
|
||||
LogDebug("Sending placeblock to: " + loc);
|
||||
|
||||
SendPlaceBlock(loc, Direction.Up);
|
||||
SendPlaceBlock(loc, Direction.Up, lookAtBlock: true);
|
||||
Thread.Sleep(300);
|
||||
}
|
||||
else LogDebug("Can't move to: " + location2);
|
||||
|
|
@ -496,7 +500,7 @@ namespace MinecraftClient.ChatBots
|
|||
{
|
||||
// TODO: Do a check if the carrot/potato is on the first growth stage
|
||||
// if so, use: new Location(location.X, (double)(location.Y - 1) + (double)0.93750, location.Z)
|
||||
SendPlaceBlock(location2, Direction.Down);
|
||||
SendPlaceBlock(location2, Direction.Down, lookAtBlock: true);
|
||||
}
|
||||
|
||||
Thread.Sleep(100);
|
||||
|
|
@ -591,6 +595,15 @@ namespace MinecraftClient.ChatBots
|
|||
};
|
||||
}
|
||||
|
||||
private static bool IsCropAvailableForProtocol(CropType type, int protocolVersion)
|
||||
{
|
||||
return type switch
|
||||
{
|
||||
CropType.Beetroot => protocolVersion >= Protocol18Handler.MC_1_9_Version,
|
||||
_ => true
|
||||
};
|
||||
}
|
||||
|
||||
private List<Location> FindEmptyFarmland(int radius)
|
||||
{
|
||||
return GetWorld()
|
||||
|
|
@ -616,16 +629,19 @@ namespace MinecraftClient.ChatBots
|
|||
if (fullyGrown && material is Material.Melon or Material.Pumpkin)
|
||||
return true;
|
||||
|
||||
var isFullyGrown = IsCropFullyGrown(GetWorld().GetBlock(location), cropType);
|
||||
var isFullyGrown = IsCropFullyGrown(GetWorld().GetBlock(location), cropType, location);
|
||||
return fullyGrown ? isFullyGrown : !isFullyGrown;
|
||||
})
|
||||
.ToList();
|
||||
}
|
||||
|
||||
private bool IsCropFullyGrown(Block block, CropType cropType)
|
||||
private bool IsCropFullyGrown(Block block, CropType cropType, Location? location = null)
|
||||
{
|
||||
var protocolVersion = GetProtocolVersion();
|
||||
|
||||
if (protocolVersion < Protocol18Handler.MC_1_13_Version)
|
||||
return IsLegacyCropFullyGrown(block, cropType, location);
|
||||
|
||||
switch (cropType)
|
||||
{
|
||||
case CropType.Beetroot:
|
||||
|
|
@ -781,6 +797,44 @@ namespace MinecraftClient.ChatBots
|
|||
return false;
|
||||
}
|
||||
|
||||
private bool IsLegacyCropFullyGrown(Block block, CropType cropType, Location? location)
|
||||
{
|
||||
return cropType switch
|
||||
{
|
||||
CropType.Beetroot => block.BlockId == 207 && block.BlockMeta >= 3,
|
||||
CropType.Carrot => block.BlockId == 141 && block.BlockMeta >= 7,
|
||||
CropType.Melon => block.BlockId == 105
|
||||
&& (block.BlockMeta >= 7 || HasAdjacentBlock(location, Material.Melon)),
|
||||
CropType.NetherWart => block.BlockId == 115 && block.BlockMeta >= 3,
|
||||
CropType.Pumpkin => block.BlockId == 104
|
||||
&& (block.BlockMeta >= 7 || HasAdjacentBlock(location, Material.Pumpkin)),
|
||||
CropType.Potato => block.BlockId == 142 && block.BlockMeta >= 7,
|
||||
CropType.Wheat => block.BlockId == 59 && block.BlockMeta >= 7,
|
||||
_ => false
|
||||
};
|
||||
}
|
||||
|
||||
private bool HasAdjacentBlock(Location? location, Material material)
|
||||
{
|
||||
if (location is not Location stemLocation)
|
||||
return false;
|
||||
|
||||
var world = GetWorld();
|
||||
int x = (int)Math.Floor(stemLocation.X);
|
||||
int y = (int)Math.Floor(stemLocation.Y);
|
||||
int z = (int)Math.Floor(stemLocation.Z);
|
||||
|
||||
Location[] adjacentLocations =
|
||||
[
|
||||
new(x + 1, y, z),
|
||||
new(x - 1, y, z),
|
||||
new(x, y, z + 1),
|
||||
new(x, y, z - 1)
|
||||
];
|
||||
|
||||
return adjacentLocations.Any(adjacentLocation => world.GetBlock(adjacentLocation).Type == material);
|
||||
}
|
||||
|
||||
// Yoinked from ReinforceZwei's AutoTree and adapted to search the whole of inventory in additon to the hotbar
|
||||
private bool SwitchToItem(ItemType itemType)
|
||||
{
|
||||
|
|
@ -831,7 +885,7 @@ namespace MinecraftClient.ChatBots
|
|||
// Yoinked from Daenges's Sugarcane Farmer
|
||||
private bool WaitForDigBlock(Location block, int digTimeout = 1000)
|
||||
{
|
||||
if (!DigBlock(block.ToFloor())) return false;
|
||||
if (!DigBlock(block.ToFloor(), Direction.Down)) return false;
|
||||
short i = 0; // Maximum wait time of 10 sec.
|
||||
while (GetWorld().GetBlock(block).Type != Material.Air && i <= digTimeout)
|
||||
{
|
||||
|
|
@ -854,4 +908,4 @@ namespace MinecraftClient.ChatBots
|
|||
else LogDebugToConsole(text);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
105
MinecraftClient/ChatBots/FileInputBot.cs
Normal file
105
MinecraftClient/ChatBots/FileInputBot.cs
Normal file
|
|
@ -0,0 +1,105 @@
|
|||
using System;
|
||||
using System.IO;
|
||||
using System.Threading;
|
||||
using MinecraftClient.CommandHandler;
|
||||
using MinecraftClient.Scripting;
|
||||
|
||||
namespace MinecraftClient.ChatBots
|
||||
{
|
||||
/// <summary>
|
||||
/// Debug-only ChatBot that monitors a text file for commands.
|
||||
/// Write lines to the file from any external tool (e.g. Cursor Shell)
|
||||
/// and this bot will execute them as MCC internal commands.
|
||||
///
|
||||
/// Usage from Cursor Shell:
|
||||
/// Add-Content mcc_input.txt "inventory"
|
||||
/// Add-Content mcc_input.txt "send /give @s diamond_sword 1"
|
||||
///
|
||||
/// Lines starting with "/" are sent as server chat; others are treated
|
||||
/// as MCC internal commands (same as typing in the MCC console).
|
||||
/// </summary>
|
||||
public class FileInputBot : ChatBot
|
||||
{
|
||||
private const string BotName = "FileInput";
|
||||
private string _filePath = string.Empty;
|
||||
private long _lastPosition;
|
||||
private int _tickCounter;
|
||||
|
||||
public override void Initialize()
|
||||
{
|
||||
_filePath = Path.GetFullPath(
|
||||
Environment.GetEnvironmentVariable("MCC_INPUT_FILE") ?? "mcc_input.txt");
|
||||
|
||||
if (File.Exists(_filePath))
|
||||
_lastPosition = new FileInfo(_filePath).Length;
|
||||
else
|
||||
File.WriteAllText(_filePath, "");
|
||||
|
||||
LogToConsole(BotName, $"Watching: {_filePath}");
|
||||
LogToConsole(BotName, "Write commands to this file to execute them.");
|
||||
}
|
||||
|
||||
public override void Update()
|
||||
{
|
||||
// Poll every ~500ms while the MCC main loop runs at 20 TPS.
|
||||
if (++_tickCounter < Settings.DoubleToTick(0.5))
|
||||
return;
|
||||
_tickCounter = 0;
|
||||
|
||||
try
|
||||
{
|
||||
if (!File.Exists(_filePath))
|
||||
return;
|
||||
|
||||
var info = new FileInfo(_filePath);
|
||||
if (info.Length <= _lastPosition)
|
||||
return;
|
||||
|
||||
string newContent;
|
||||
using (var fs = new FileStream(_filePath, FileMode.Open, FileAccess.Read, FileShare.ReadWrite))
|
||||
{
|
||||
fs.Seek(_lastPosition, SeekOrigin.Begin);
|
||||
using var reader = new StreamReader(fs);
|
||||
newContent = reader.ReadToEnd();
|
||||
}
|
||||
_lastPosition = info.Length;
|
||||
|
||||
foreach (var rawLine in newContent.Split('\n'))
|
||||
{
|
||||
var line = rawLine.Trim();
|
||||
if (string.IsNullOrEmpty(line))
|
||||
continue;
|
||||
|
||||
LogToConsole(BotName, $"> {line}");
|
||||
|
||||
if (line.StartsWith("/"))
|
||||
{
|
||||
SendText(line);
|
||||
}
|
||||
else
|
||||
{
|
||||
CmdResult result = new();
|
||||
if (PerformInternalCommand(line, ref result))
|
||||
{
|
||||
if (!string.IsNullOrEmpty(result.ToString()))
|
||||
LogToConsole(BotName, result.ToString());
|
||||
}
|
||||
else
|
||||
{
|
||||
// Not an internal command — send as chat
|
||||
SendText(line);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
catch (IOException)
|
||||
{
|
||||
// File may be temporarily locked by the writer
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
LogToConsole(BotName, $"Error: {ex.Message}");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -110,13 +110,13 @@ namespace MinecraftClient.ChatBots
|
|||
&& !string.IsNullOrEmpty(entity.Name)
|
||||
&& entity.Name.Equals(name, StringComparison.OrdinalIgnoreCase));
|
||||
|
||||
if (player == null)
|
||||
if (player is null)
|
||||
return r.SetAndReturn(CmdResult.Status.Fail, Translations.cmd_follow_invalid_player);
|
||||
|
||||
if (!CanMoveThere(player.Location))
|
||||
return r.SetAndReturn(CmdResult.Status.Fail, Translations.cmd_follow_cant_reach_player);
|
||||
|
||||
if (_playerToFollow != null && _playerToFollow.Equals(name, StringComparison.OrdinalIgnoreCase))
|
||||
if (_playerToFollow is not null && _playerToFollow.Equals(name, StringComparison.OrdinalIgnoreCase))
|
||||
return r.SetAndReturn(CmdResult.Status.Fail,
|
||||
string.Format(Translations.cmd_follow_already_following, _playerToFollow));
|
||||
|
||||
|
|
@ -127,7 +127,7 @@ namespace MinecraftClient.ChatBots
|
|||
|
||||
var result =
|
||||
string.Format(
|
||||
_playerToFollow != null ? Translations.cmd_follow_switched : Translations.cmd_follow_started,
|
||||
_playerToFollow is not null ? Translations.cmd_follow_switched : Translations.cmd_follow_started,
|
||||
player.Name!);
|
||||
_playerToFollow = name.ToLower();
|
||||
|
||||
|
|
@ -152,7 +152,7 @@ namespace MinecraftClient.ChatBots
|
|||
|
||||
private int OnCommandStop(CmdResult r)
|
||||
{
|
||||
if (_playerToFollow == null)
|
||||
if (_playerToFollow is null)
|
||||
return r.SetAndReturn(CmdResult.Status.Fail, Translations.cmd_follow_already_stopped);
|
||||
|
||||
var movementLock = BotMovementLock.Instance;
|
||||
|
|
@ -172,7 +172,7 @@ namespace MinecraftClient.ChatBots
|
|||
if (entity.Type != EntityType.Player)
|
||||
return;
|
||||
|
||||
if (_playerToFollow == null || string.IsNullOrEmpty(entity.Name))
|
||||
if (_playerToFollow is null || string.IsNullOrEmpty(entity.Name))
|
||||
return;
|
||||
|
||||
if (_playerToFollow != entity.Name.ToLower())
|
||||
|
|
@ -200,7 +200,7 @@ namespace MinecraftClient.ChatBots
|
|||
if (entity.Type != EntityType.Player)
|
||||
return;
|
||||
|
||||
if (_playerToFollow != null && !string.IsNullOrEmpty(entity.Name) &&
|
||||
if (_playerToFollow is not null && !string.IsNullOrEmpty(entity.Name) &&
|
||||
_playerToFollow.Equals(entity.Name, StringComparison.OrdinalIgnoreCase))
|
||||
{
|
||||
LogToConsole(string.Format(Translations.cmd_follow_player_came_to_the_range, _playerToFollow));
|
||||
|
|
@ -213,7 +213,7 @@ namespace MinecraftClient.ChatBots
|
|||
if (entity.Type != EntityType.Player)
|
||||
return;
|
||||
|
||||
if (_playerToFollow != null && !string.IsNullOrEmpty(entity.Name) &&
|
||||
if (_playerToFollow is not null && !string.IsNullOrEmpty(entity.Name) &&
|
||||
_playerToFollow.Equals(entity.Name, StringComparison.OrdinalIgnoreCase))
|
||||
{
|
||||
LogToConsole(string.Format(Translations.cmd_follow_player_left_the_range, _playerToFollow));
|
||||
|
|
@ -223,7 +223,7 @@ namespace MinecraftClient.ChatBots
|
|||
|
||||
public override void OnPlayerLeave(Guid uuid, string? name)
|
||||
{
|
||||
if (_playerToFollow != null && !string.IsNullOrEmpty(name) &&
|
||||
if (_playerToFollow is not null && !string.IsNullOrEmpty(name) &&
|
||||
_playerToFollow.Equals(name, StringComparison.OrdinalIgnoreCase))
|
||||
{
|
||||
LogToConsole(string.Format(Translations.cmd_follow_player_left, _playerToFollow));
|
||||
|
|
@ -235,7 +235,7 @@ namespace MinecraftClient.ChatBots
|
|||
private bool CanMoveThere(Location location)
|
||||
{
|
||||
var chunkColumn = GetWorld().GetChunkColumn(location);
|
||||
return chunkColumn != null && chunkColumn.FullyLoaded != false;
|
||||
return chunkColumn is not null && chunkColumn.FullyLoaded != false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -3,6 +3,7 @@ using System.Collections.Generic;
|
|||
using System.Data;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Threading;
|
||||
using Brigadier.NET;
|
||||
using Brigadier.NET.Builder;
|
||||
using MinecraftClient.CommandHandler;
|
||||
|
|
@ -218,7 +219,7 @@ namespace MinecraftClient.ChatBots
|
|||
private IgnoreList ignoreList = new();
|
||||
private FileMonitor? mailDbFileMonitor;
|
||||
private FileMonitor? ignoreListFileMonitor;
|
||||
private readonly object readWriteLock = new();
|
||||
private readonly Lock readWriteLock = new();
|
||||
|
||||
/// <summary>
|
||||
/// Initialization of the Mailer bot
|
||||
|
|
@ -423,7 +424,7 @@ namespace MinecraftClient.ChatBots
|
|||
}
|
||||
|
||||
/// <summary>
|
||||
/// Called on each MCC tick, around 10 times per second
|
||||
/// Called on each MCC tick, around 20 times per second
|
||||
/// </summary>
|
||||
public override void Update()
|
||||
{
|
||||
|
|
|
|||
|
|
@ -1,9 +1,10 @@
|
|||
using System;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Diagnostics;
|
||||
using System.IO;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
using Avalonia.Threading;
|
||||
using Brigadier.NET;
|
||||
using Brigadier.NET.Builder;
|
||||
using ImageMagick;
|
||||
|
|
@ -11,6 +12,7 @@ using MinecraftClient.CommandHandler;
|
|||
using MinecraftClient.CommandHandler.Patch;
|
||||
using MinecraftClient.Mapping;
|
||||
using MinecraftClient.Scripting;
|
||||
using MinecraftClient.Tui;
|
||||
using Tomlet.Attributes;
|
||||
|
||||
namespace MinecraftClient.ChatBots
|
||||
|
|
@ -142,7 +144,12 @@ namespace MinecraftClient.ChatBots
|
|||
SaveToFile(map);
|
||||
|
||||
if (Config.Render_In_Console)
|
||||
RenderInConsole(map);
|
||||
{
|
||||
if (ConsoleIO.Backend is TuiConsoleBackend)
|
||||
RenderInTui(map);
|
||||
else
|
||||
RenderInConsole(map);
|
||||
}
|
||||
|
||||
return r.SetAndReturn(CmdResult.Status.Done);
|
||||
}
|
||||
|
|
@ -213,7 +220,12 @@ namespace MinecraftClient.ChatBots
|
|||
SaveToFile(map);
|
||||
|
||||
if (Config.Render_In_Console)
|
||||
RenderInConsole(map);
|
||||
{
|
||||
if (ConsoleIO.Backend is TuiConsoleBackend)
|
||||
RenderInTui(map);
|
||||
else
|
||||
RenderInConsole(map);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -259,7 +271,8 @@ namespace MinecraftClient.ChatBots
|
|||
{
|
||||
using (var image = new MagickImage(fileName))
|
||||
{
|
||||
var size = new MagickGeometry(Config.Resize_To, Config.Resize_To);
|
||||
uint resizeTo = (uint)Math.Max(Config.Resize_To, 1);
|
||||
var size = new MagickGeometry(resizeTo, resizeTo);
|
||||
size.IgnoreAspectRatio = true;
|
||||
|
||||
image.Resize(size);
|
||||
|
|
@ -283,13 +296,13 @@ namespace MinecraftClient.ChatBots
|
|||
|
||||
if (Config.Send_Rendered_To_Discord)
|
||||
{
|
||||
if (discordBridge == null || (discordBridge != null && !discordBridge.IsConnected))
|
||||
if (discordBridge is null || (discordBridge is not null && !discordBridge.IsConnected))
|
||||
return;
|
||||
}
|
||||
|
||||
if (Config.Send_Rendered_To_Telegram)
|
||||
{
|
||||
if (telegramBridge == null || (telegramBridge != null && !telegramBridge.IsConnected))
|
||||
if (telegramBridge is null || (telegramBridge is not null && !telegramBridge.IsConnected))
|
||||
return;
|
||||
}
|
||||
|
||||
|
|
@ -341,11 +354,32 @@ namespace MinecraftClient.ChatBots
|
|||
}
|
||||
}
|
||||
|
||||
private static void RenderInTui(McMap map)
|
||||
{
|
||||
var view = TuiConsoleBackend.Instance?.GetView();
|
||||
if (view is null)
|
||||
return;
|
||||
|
||||
Dispatcher.UIThread.Post(() =>
|
||||
{
|
||||
if (view.HasOverlay && view.OverlayContent is MapOverlay existing)
|
||||
{
|
||||
existing.UpdateMap(map);
|
||||
return;
|
||||
}
|
||||
|
||||
view.ShowOverlay(new MapOverlay(map));
|
||||
});
|
||||
}
|
||||
|
||||
private static void RenderInConsole(McMap map)
|
||||
{
|
||||
StringBuilder sb = new();
|
||||
int consoleWidth = Math.Max(Console.BufferWidth, Settings.Config.Main.Advanced.MinTerminalWidth) / 2;
|
||||
int consoleHeight = Math.Max(Console.BufferHeight, Settings.Config.Main.Advanced.MinTerminalHeight) - 1;
|
||||
int safeBufWidth, safeBufHeight;
|
||||
try { safeBufWidth = Console.BufferWidth; } catch { safeBufWidth = 120; }
|
||||
try { safeBufHeight = Console.BufferHeight; } catch { safeBufHeight = 50; }
|
||||
int consoleWidth = Math.Max(safeBufWidth, Settings.Config.Main.Advanced.MinTerminalWidth) / 2;
|
||||
int consoleHeight = Math.Max(safeBufHeight, Settings.Config.Main.Advanced.MinTerminalHeight) - 1;
|
||||
int scaleX = (map.Width + consoleWidth - 1) / consoleWidth;
|
||||
int scaleY = (map.Height + consoleHeight - 1) / consoleHeight;
|
||||
int scale = Math.Max(scaleX, scaleY);
|
||||
|
|
@ -443,109 +477,62 @@ namespace MinecraftClient.ChatBots
|
|||
public DateTime LastUpdated { get; set; }
|
||||
}
|
||||
|
||||
internal class MapColors
|
||||
/// <summary>
|
||||
/// Map packet base color palette. Colors are loaded from the embedded
|
||||
/// MinimapBlockColors.json resource (map_palette section) generated by
|
||||
/// tools/gen_block_color_map.py, which parses MapColor.java.
|
||||
/// </summary>
|
||||
internal static class MapColors
|
||||
{
|
||||
// When colors are updated in a new update, you can get them using the game code: net\minecraft\world\level\material\MaterialColor.java
|
||||
public static Dictionary<byte, byte[]> Colors = new()
|
||||
private static readonly Dictionary<byte, byte[]> Colors;
|
||||
|
||||
private static readonly byte[] ShadeMultipliers = [180, 220, 255, 135];
|
||||
|
||||
static MapColors()
|
||||
{
|
||||
//Color ID R G B
|
||||
{0, new byte[]{0, 0, 0}},
|
||||
{1, new byte[]{127, 178, 56}},
|
||||
{2, new byte[]{247, 233, 163}},
|
||||
{3, new byte[]{199, 199, 199}},
|
||||
{4, new byte[]{255, 0, 0}},
|
||||
{5, new byte[]{160, 160, 255}},
|
||||
{6, new byte[]{167, 167, 167}},
|
||||
{7, new byte[]{0, 124, 0}},
|
||||
{8, new byte[]{255, 255, 255}},
|
||||
{9, new byte[]{164, 168, 184}},
|
||||
{10, new byte[]{151, 109, 77}},
|
||||
{11, new byte[]{112, 112, 112}},
|
||||
{12, new byte[]{64, 64, 255}},
|
||||
{13, new byte[]{143, 119, 72}},
|
||||
{14, new byte[]{255, 252, 245}},
|
||||
{15, new byte[]{216, 127, 51}},
|
||||
{16, new byte[]{178, 76, 216}},
|
||||
{17, new byte[]{102, 153, 216}},
|
||||
{18, new byte[]{229, 229, 51}},
|
||||
{19, new byte[]{127, 204, 25}},
|
||||
{20, new byte[]{242, 127, 165}},
|
||||
{21, new byte[]{76, 76, 76}},
|
||||
{22, new byte[]{153, 153, 153}},
|
||||
{23, new byte[]{76, 127, 153}},
|
||||
{24, new byte[]{127, 63, 178}},
|
||||
{25, new byte[]{51, 76, 178}},
|
||||
{26, new byte[]{102, 76, 51}},
|
||||
{27, new byte[]{102, 127, 51}},
|
||||
{28, new byte[]{153, 51, 51}},
|
||||
{29, new byte[]{25, 25, 25}},
|
||||
{30, new byte[]{250, 238, 77}},
|
||||
{31, new byte[]{92, 219, 213}},
|
||||
{32, new byte[]{74, 128, 255}},
|
||||
{33, new byte[]{0, 217, 58}},
|
||||
{34, new byte[]{129, 86, 49}},
|
||||
{35, new byte[]{112, 2, 0}},
|
||||
{36, new byte[]{209, 177, 161}},
|
||||
{37, new byte[]{159, 82, 36}},
|
||||
{38, new byte[]{149, 87, 108}},
|
||||
{39, new byte[]{112, 108, 138}},
|
||||
{40, new byte[]{186, 133, 36}},
|
||||
{41, new byte[]{103, 117, 53}},
|
||||
{42, new byte[]{160, 77, 78}},
|
||||
{43, new byte[]{57, 41, 35}},
|
||||
{44, new byte[]{135, 107, 98}},
|
||||
{45, new byte[]{87, 92, 92}},
|
||||
{46, new byte[]{122, 73, 88}},
|
||||
{47, new byte[]{76, 62, 92}},
|
||||
{48, new byte[]{76, 50, 35}},
|
||||
{49, new byte[]{76, 82, 42}},
|
||||
{50, new byte[]{142, 60, 46}},
|
||||
{51, new byte[]{37, 22, 16}},
|
||||
{52, new byte[]{189, 48, 49}},
|
||||
{53, new byte[]{148, 63, 97}},
|
||||
{54, new byte[]{92, 25, 29}},
|
||||
{55, new byte[]{22, 126, 134}},
|
||||
{56, new byte[]{58, 142, 140}},
|
||||
{57, new byte[]{86, 44, 62}},
|
||||
{58, new byte[]{20, 180, 133}},
|
||||
{59, new byte[]{100, 100, 100}},
|
||||
{60, new byte[]{216, 175, 147}},
|
||||
{61, new byte[]{127, 167, 150}}
|
||||
};
|
||||
Colors = new Dictionary<byte, byte[]>();
|
||||
try
|
||||
{
|
||||
using var stream = System.Reflection.Assembly.GetExecutingAssembly()
|
||||
.GetManifestResourceStream("MinimapBlockColors.json");
|
||||
if (stream is not null)
|
||||
{
|
||||
using var doc = System.Text.Json.JsonDocument.Parse(stream);
|
||||
if (doc.RootElement.TryGetProperty("map_palette", out var palette))
|
||||
{
|
||||
foreach (var prop in palette.EnumerateObject())
|
||||
{
|
||||
if (!byte.TryParse(prop.Name, out byte id))
|
||||
continue;
|
||||
var arr = prop.Value;
|
||||
Colors[id] = [
|
||||
arr[0].GetByte(),
|
||||
arr[1].GetByte(),
|
||||
arr[2].GetByte()
|
||||
];
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
ConsoleIO.WriteLogLine($"[Map] Failed to load map palette: {ex.Message}");
|
||||
}
|
||||
}
|
||||
|
||||
public static ColorRGBA ColorByteToRGBA(byte receivedColorId)
|
||||
{
|
||||
// Divide received color id by 4 to get the base color id
|
||||
// Much thanks to DevBobcorn
|
||||
byte baseColorId = (byte)(receivedColorId >> 2);
|
||||
|
||||
// Any new colors that we haven't added will be purple like in the missing CS: Source Texture
|
||||
if (!Colors.ContainsKey(baseColorId))
|
||||
if (!Colors.TryGetValue(baseColorId, out byte[]? rgb))
|
||||
return new(248, 0, 248, 255, true);
|
||||
|
||||
byte shadeId = (byte)(receivedColorId % 4);
|
||||
byte shadeMultiplier = 255;
|
||||
|
||||
switch (shadeId)
|
||||
{
|
||||
case 0:
|
||||
shadeMultiplier = 180;
|
||||
break;
|
||||
|
||||
case 1:
|
||||
shadeMultiplier = 220;
|
||||
break;
|
||||
|
||||
case 3:
|
||||
// NOTE: If we ever add map support below 1.8, this needs to be 220 before 1.8
|
||||
shadeMultiplier = 135;
|
||||
break;
|
||||
}
|
||||
byte multiplier = ShadeMultipliers[receivedColorId & 3];
|
||||
|
||||
return new(
|
||||
r: (byte)((Colors[baseColorId][0] * shadeMultiplier) / 255),
|
||||
g: (byte)((Colors[baseColorId][1] * shadeMultiplier) / 255),
|
||||
b: (byte)((Colors[baseColorId][2] * shadeMultiplier) / 255),
|
||||
r: (byte)(rgb[0] * multiplier / 255),
|
||||
g: (byte)(rgb[1] * multiplier / 255),
|
||||
b: (byte)(rgb[2] * multiplier / 255),
|
||||
a: 255
|
||||
);
|
||||
}
|
||||
|
|
|
|||
272
MinecraftClient/ChatBots/McpServer.cs
Normal file
272
MinecraftClient/ChatBots/McpServer.cs
Normal file
|
|
@ -0,0 +1,272 @@
|
|||
using System;
|
||||
using MinecraftClient.Mapping;
|
||||
using MinecraftClient.Mcp;
|
||||
using MinecraftClient.Scripting;
|
||||
using Tomlet.Attributes;
|
||||
|
||||
namespace MinecraftClient.ChatBots
|
||||
{
|
||||
public class McpServer : ChatBot
|
||||
{
|
||||
public static Configs Config = new();
|
||||
|
||||
[TomlDoNotInlineObject]
|
||||
public class Configs
|
||||
{
|
||||
[NonSerialized]
|
||||
private const string BotName = "McpServer";
|
||||
|
||||
[TomlInlineComment("$ChatBot.McpServer.Enabled$")]
|
||||
public bool Enabled = false;
|
||||
|
||||
[TomlPrecedingComment("$ChatBot.McpServer.Transport$")]
|
||||
public MccMcpTransportConfig Transport = new();
|
||||
|
||||
[TomlPrecedingComment("$ChatBot.McpServer.Capabilities$")]
|
||||
public MccMcpCapabilityToggles Capabilities = new();
|
||||
|
||||
public void OnSettingUpdate()
|
||||
{
|
||||
Transport ??= new MccMcpTransportConfig();
|
||||
Capabilities ??= new MccMcpCapabilityToggles();
|
||||
|
||||
if (Transport.Port is < 1 or > 65535)
|
||||
Transport.Port = 33333;
|
||||
|
||||
if (string.IsNullOrWhiteSpace(Transport.BindHost))
|
||||
Transport.BindHost = "127.0.0.1";
|
||||
|
||||
if (string.IsNullOrWhiteSpace(Transport.Route))
|
||||
Transport.Route = "/mcp";
|
||||
|
||||
if (!Transport.Route.StartsWith('/'))
|
||||
Transport.Route = "/" + Transport.Route;
|
||||
|
||||
if (string.IsNullOrWhiteSpace(Transport.AuthTokenEnvVar))
|
||||
Transport.AuthTokenEnvVar = "MCC_MCP_AUTH_TOKEN";
|
||||
}
|
||||
}
|
||||
|
||||
private MccEmbeddedMcpHost? host;
|
||||
|
||||
public override void Initialize()
|
||||
{
|
||||
Config.OnSettingUpdate();
|
||||
}
|
||||
|
||||
public override void AfterGameJoined()
|
||||
{
|
||||
if (!Config.Enabled)
|
||||
return;
|
||||
|
||||
ClearStores();
|
||||
|
||||
MccMcpConfig mcpConfig = new()
|
||||
{
|
||||
Enabled = Config.Enabled,
|
||||
Transport = Config.Transport,
|
||||
Capabilities = Config.Capabilities
|
||||
};
|
||||
|
||||
host ??= new MccEmbeddedMcpHost(mcpConfig, new MccMcpCapabilities(() => Config.Capabilities));
|
||||
|
||||
if (host.IsRunning)
|
||||
return;
|
||||
|
||||
LogToConsole(Translations.bot_mcpserver_starting);
|
||||
if (!host.Start(out string? error))
|
||||
{
|
||||
if (error == "missing_auth_token")
|
||||
LogToConsole(string.Format(Translations.bot_mcpserver_missing_auth_token, Config.Transport.AuthTokenEnvVar));
|
||||
LogToConsole(string.Format(Translations.bot_mcpserver_start_failed, error ?? "unknown"));
|
||||
return;
|
||||
}
|
||||
|
||||
LogToConsole(string.Format(Translations.bot_mcpserver_started, host.Endpoint));
|
||||
}
|
||||
|
||||
public override bool OnDisconnect(DisconnectReason reason, string message)
|
||||
{
|
||||
MccObservedStateStore.AddRecentEvent("disconnect", new
|
||||
{
|
||||
reason = reason.ToString(),
|
||||
message
|
||||
});
|
||||
StopHost();
|
||||
ClearStores();
|
||||
return false;
|
||||
}
|
||||
|
||||
public override void OnUnload()
|
||||
{
|
||||
StopHost();
|
||||
ClearStores();
|
||||
}
|
||||
|
||||
public override void GetText(string text, string? json)
|
||||
{
|
||||
string clean = GetVerbatim(text);
|
||||
if (string.IsNullOrWhiteSpace(clean))
|
||||
return;
|
||||
|
||||
string kind = "system";
|
||||
string? sender = null;
|
||||
string? message = null;
|
||||
|
||||
string parsedMessage = string.Empty;
|
||||
string parsedSender = string.Empty;
|
||||
if (IsPrivateMessage(clean, ref parsedMessage, ref parsedSender))
|
||||
{
|
||||
kind = "private";
|
||||
sender = parsedSender;
|
||||
message = parsedMessage;
|
||||
}
|
||||
else if (IsChatMessage(clean, ref parsedMessage, ref parsedSender))
|
||||
{
|
||||
kind = "chat";
|
||||
sender = parsedSender;
|
||||
message = parsedMessage;
|
||||
}
|
||||
|
||||
MccObservedStateStore.AddChatHistoryEntry(new MccChatHistoryEntry
|
||||
{
|
||||
TimestampUtc = DateTimeOffset.UtcNow,
|
||||
Kind = kind,
|
||||
Text = clean,
|
||||
Sender = sender,
|
||||
Message = message,
|
||||
Json = json
|
||||
});
|
||||
}
|
||||
|
||||
public override void OnTimeUpdate(long WorldAge, long TimeOfDay)
|
||||
{
|
||||
MccObservedStateStore.SetTime(WorldAge, TimeOfDay);
|
||||
}
|
||||
|
||||
public override void OnRainLevelChange(float level)
|
||||
{
|
||||
MccObservedStateStore.SetRainLevel(level);
|
||||
MccObservedStateStore.AddRecentEvent("weather_rain", new { level });
|
||||
}
|
||||
|
||||
public override void OnThunderLevelChange(float level)
|
||||
{
|
||||
MccObservedStateStore.SetThunderLevel(level);
|
||||
MccObservedStateStore.AddRecentEvent("weather_thunder", new { level });
|
||||
}
|
||||
|
||||
public override void OnDeath()
|
||||
{
|
||||
MccObservedStateStore.AddRecentEvent("death");
|
||||
}
|
||||
|
||||
public override void OnRespawn()
|
||||
{
|
||||
MccObservedStateStore.AddRecentEvent("respawn");
|
||||
}
|
||||
|
||||
public override void OnPlayerJoin(Guid uuid, string name)
|
||||
{
|
||||
MccObservedStateStore.AddRecentEvent("player_join", new
|
||||
{
|
||||
uuid,
|
||||
name
|
||||
});
|
||||
}
|
||||
|
||||
public override void OnPlayerLeave(Guid uuid, string? name)
|
||||
{
|
||||
MccObservedStateStore.AddRecentEvent("player_leave", new
|
||||
{
|
||||
uuid,
|
||||
name
|
||||
});
|
||||
}
|
||||
|
||||
public override void OnInventoryOpen(int inventoryId)
|
||||
{
|
||||
MccObservedStateStore.AddRecentEvent("inventory_open", new { inventoryId });
|
||||
}
|
||||
|
||||
public override void OnInventoryClose(int inventoryId)
|
||||
{
|
||||
MccObservedStateStore.AddRecentEvent("inventory_close", new { inventoryId });
|
||||
}
|
||||
|
||||
public override void OnTitle(int action, string titletext, string subtitletext, string actionbartext, int fadein, int stay, int fadeout, string json)
|
||||
{
|
||||
if (action == 2)
|
||||
{
|
||||
MccObservedStateStore.AddRecentEvent("actionbar", new
|
||||
{
|
||||
action,
|
||||
text = actionbartext,
|
||||
fadein,
|
||||
stay,
|
||||
fadeout,
|
||||
json
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
||||
if (action is 0 or 1)
|
||||
{
|
||||
MccObservedStateStore.AddRecentEvent("title", new
|
||||
{
|
||||
action,
|
||||
titleText = titletext,
|
||||
subtitleText = subtitletext,
|
||||
fadein,
|
||||
stay,
|
||||
fadeout,
|
||||
json
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
public override void OnBlockBreakAnimation(Entity entity, Location location, byte stage)
|
||||
{
|
||||
MccObservedStateStore.AddRecentEvent("block_break_animation", new
|
||||
{
|
||||
entityId = entity.ID,
|
||||
entityType = entity.Type.ToString(),
|
||||
stage,
|
||||
location = new
|
||||
{
|
||||
x = location.X,
|
||||
y = location.Y,
|
||||
z = location.Z
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
public override void OnEntityAnimation(Entity entity, byte animation)
|
||||
{
|
||||
MccObservedStateStore.AddRecentEvent("entity_animation", new
|
||||
{
|
||||
entityId = entity.ID,
|
||||
entityType = entity.Type.ToString(),
|
||||
animation,
|
||||
name = entity.Name,
|
||||
customName = entity.CustomName
|
||||
});
|
||||
}
|
||||
|
||||
private void StopHost()
|
||||
{
|
||||
if (host is null || !host.IsRunning)
|
||||
return;
|
||||
|
||||
if (host.Stop(out string? error))
|
||||
LogToConsole(Translations.bot_mcpserver_stopped);
|
||||
else
|
||||
LogToConsole(string.Format(Translations.bot_mcpserver_stop_failed, error ?? "unknown"));
|
||||
}
|
||||
|
||||
private static void ClearStores()
|
||||
{
|
||||
MccObservedStateStore.ClearAll();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -1,5 +1,6 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using Brigadier.NET.Builder;
|
||||
using MinecraftClient.CommandHandler;
|
||||
using MinecraftClient.CommandHandler.Patch;
|
||||
|
|
@ -42,8 +43,7 @@ namespace MinecraftClient.ChatBots
|
|||
public override void Initialize()
|
||||
{
|
||||
SetNetworkPacketEventEnabled(true);
|
||||
replay = new ReplayHandler(GetProtocolVersion());
|
||||
replay.MetaData.serverName = GetServerHost() + GetServerPort();
|
||||
replay = new ReplayHandler(GetProtocolVersion(), $"{GetServerHost()}:{GetServerPort()}");
|
||||
backupCounter = Settings.DoubleToTick(Config.Backup_Interval);
|
||||
|
||||
McClient.dispatcher.Register(l => l.Literal("help")
|
||||
|
|
@ -67,6 +67,8 @@ namespace MinecraftClient.ChatBots
|
|||
{
|
||||
McClient.dispatcher.Unregister(CommandName);
|
||||
McClient.dispatcher.GetRoot().GetChild("help").RemoveChild(CommandName);
|
||||
replay?.Dispose();
|
||||
replay = null;
|
||||
}
|
||||
|
||||
private int OnCommandHelp(CmdResult r, string? cmd)
|
||||
|
|
@ -84,9 +86,9 @@ namespace MinecraftClient.ChatBots
|
|||
{
|
||||
try
|
||||
{
|
||||
if (replay!.RecordRunning)
|
||||
if (replay is { RecordRunning: true })
|
||||
{
|
||||
replay.CreateBackupReplay(@"replay_recordings\" + replay.GetReplayDefaultName());
|
||||
replay.CreateBackupReplay(Path.Combine(replay.ReplayFileDirectory, replay.GetReplayDefaultName()));
|
||||
return r.SetAndReturn(CmdResult.Status.Done, Translations.bot_replayCapture_created);
|
||||
}
|
||||
else
|
||||
|
|
@ -102,7 +104,7 @@ namespace MinecraftClient.ChatBots
|
|||
{
|
||||
try
|
||||
{
|
||||
if (replay!.RecordRunning)
|
||||
if (replay is { RecordRunning: true })
|
||||
{
|
||||
replay.OnShutDown();
|
||||
return r.SetAndReturn(CmdResult.Status.Done, Translations.bot_replayCapture_stopped);
|
||||
|
|
@ -118,16 +120,16 @@ namespace MinecraftClient.ChatBots
|
|||
|
||||
public override void OnNetworkPacket(int packetID, List<byte> packetData, bool isLogin, bool isInbound)
|
||||
{
|
||||
replay!.AddPacket(packetID, packetData, isLogin, isInbound);
|
||||
replay?.AddPacket(packetID, packetData, isLogin, isInbound);
|
||||
}
|
||||
|
||||
public override void Update()
|
||||
{
|
||||
if (Config.Backup_Interval > 0 && replay!.RecordRunning)
|
||||
if (Config.Backup_Interval > 0 && replay is { RecordRunning: true })
|
||||
{
|
||||
if (backupCounter <= 0)
|
||||
{
|
||||
replay.CreateBackupReplay(@"recording_cache\REPLAY_BACKUP.mcpr");
|
||||
replay.CreateBackupReplay(replay.GetBackupReplayPath());
|
||||
backupCounter = Settings.DoubleToTick(Config.Backup_Interval);
|
||||
}
|
||||
else backupCounter--;
|
||||
|
|
@ -136,7 +138,7 @@ namespace MinecraftClient.ChatBots
|
|||
|
||||
public override bool OnDisconnect(DisconnectReason reason, string message)
|
||||
{
|
||||
replay!.OnShutDown();
|
||||
replay?.OnShutDown();
|
||||
return base.OnDisconnect(reason, message);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -19,12 +19,13 @@ namespace MinecraftClient.ChatBots
|
|||
private string? file;
|
||||
private string[] lines = Array.Empty<string>();
|
||||
private string[] args = Array.Empty<string>();
|
||||
private int sleepticks = 10;
|
||||
private int sleepticks = Settings.ClientTicksPerSecond;
|
||||
private int nextline = 0;
|
||||
private readonly string? owner;
|
||||
private bool csharp;
|
||||
private Thread? thread;
|
||||
private readonly Dictionary<string, object>? localVars;
|
||||
private readonly string? scriptOwnerKey;
|
||||
|
||||
public Script(string filename)
|
||||
{
|
||||
|
|
@ -38,6 +39,13 @@ namespace MinecraftClient.ChatBots
|
|||
this.localVars = localVars;
|
||||
}
|
||||
|
||||
internal Script(string filename, string? ownername, Dictionary<string, object>? localVars, string? scriptOwnerKey)
|
||||
: this(filename, ownername, localVars)
|
||||
{
|
||||
this.scriptOwnerKey = scriptOwnerKey;
|
||||
SetScriptOwnerKey(scriptOwnerKey);
|
||||
}
|
||||
|
||||
private void ParseArguments(string argstr)
|
||||
{
|
||||
List<string> args = new();
|
||||
|
|
@ -86,7 +94,7 @@ namespace MinecraftClient.ChatBots
|
|||
public static bool LookForScript(ref string filename)
|
||||
{
|
||||
//Automatically look in subfolders and try to add ".txt" file extension
|
||||
char dir_slash = Path.DirectorySeparatorChar;
|
||||
char dir_slash = Path.DirectorySeparatorChar;
|
||||
string[] files = new string[]
|
||||
{
|
||||
filename,
|
||||
|
|
@ -149,24 +157,30 @@ namespace MinecraftClient.ChatBots
|
|||
}
|
||||
}
|
||||
|
||||
public override bool OnDisconnect(DisconnectReason reason, string message)
|
||||
{
|
||||
UnloadBot();
|
||||
return false;
|
||||
}
|
||||
|
||||
public override void Update()
|
||||
{
|
||||
if (csharp) //C# compiled script
|
||||
{
|
||||
//Initialize thread on first update
|
||||
if (thread == null)
|
||||
if (thread is null)
|
||||
{
|
||||
thread = new Thread(() =>
|
||||
{
|
||||
try
|
||||
{
|
||||
CSharpRunner.Run(this, lines, args, localVars, scriptName: file!);
|
||||
CSharpRunner.Run(this, lines, args, localVars, scriptName: file!, scriptOwnerKey: scriptOwnerKey);
|
||||
}
|
||||
catch (CSharpException e)
|
||||
{
|
||||
string errorMessage = string.Format(Translations.bot_script_fail, file, e.ExceptionType);
|
||||
LogToConsole(errorMessage);
|
||||
if (owner != null)
|
||||
if (owner is not null)
|
||||
SendPrivateMessage(owner, errorMessage);
|
||||
LogToConsole(e.InnerException);
|
||||
}
|
||||
|
|
@ -178,7 +192,7 @@ namespace MinecraftClient.ChatBots
|
|||
}
|
||||
|
||||
//Unload bot once the thread has finished running
|
||||
if (thread != null && !thread.IsAlive)
|
||||
if (thread is not null && !thread.IsAlive)
|
||||
{
|
||||
UnloadBot();
|
||||
}
|
||||
|
|
@ -202,7 +216,7 @@ namespace MinecraftClient.ChatBots
|
|||
switch (instruction_name.ToLower())
|
||||
{
|
||||
case "wait":
|
||||
int ticks = 10;
|
||||
int ticks = Settings.ClientTicksPerSecond;
|
||||
try
|
||||
{
|
||||
if (instruction_line[5..].Contains("to", StringComparison.OrdinalIgnoreCase) ||
|
||||
|
|
@ -213,7 +227,7 @@ namespace MinecraftClient.ChatBots
|
|||
.ToLower();
|
||||
processedLine = string.Join("", processedLine.Split(default(string[]), StringSplitOptions.RemoveEmptyEntries));
|
||||
var parts = processedLine.Contains("to") ? processedLine.Split("to") : processedLine.Split("-");
|
||||
|
||||
|
||||
if (parts.Length == 2)
|
||||
{
|
||||
var min = Convert.ToInt32(parts[0]);
|
||||
|
|
@ -224,10 +238,12 @@ namespace MinecraftClient.ChatBots
|
|||
(min, max) = (max, min);
|
||||
LogToConsole(Translations.cmd_wait_random_min_bigger);
|
||||
}
|
||||
|
||||
|
||||
ticks = new Random().Next(min, max);
|
||||
} else ticks = Convert.ToInt32(instruction_line[5..]);
|
||||
} else ticks = Convert.ToInt32(instruction_line[5..]);
|
||||
}
|
||||
else ticks = Convert.ToInt32(instruction_line[5..]);
|
||||
}
|
||||
else ticks = Convert.ToInt32(instruction_line[5..]);
|
||||
}
|
||||
catch { }
|
||||
sleepticks = ticks;
|
||||
|
|
|
|||
|
|
@ -116,6 +116,12 @@ namespace MinecraftClient.ChatBots
|
|||
public bool Enable = false;
|
||||
public TimeSpan[] Times;
|
||||
|
||||
public TriggerOnTimeConfig()
|
||||
{
|
||||
Enable = false;
|
||||
Times = Array.Empty<TimeSpan>();
|
||||
}
|
||||
|
||||
public TriggerOnTimeConfig(bool Enable, TimeSpan[] Time)
|
||||
{
|
||||
this.Enable = Enable;
|
||||
|
|
@ -134,6 +140,13 @@ namespace MinecraftClient.ChatBots
|
|||
public bool Enable = false;
|
||||
public double MinTime, MaxTime;
|
||||
|
||||
public TriggerOnIntervalConfig()
|
||||
{
|
||||
Enable = false;
|
||||
MinTime = 0;
|
||||
MaxTime = 0;
|
||||
}
|
||||
|
||||
public TriggerOnIntervalConfig(double value)
|
||||
{
|
||||
this.Enable = true;
|
||||
|
|
@ -167,67 +180,57 @@ namespace MinecraftClient.ChatBots
|
|||
private static bool firstlogin_done = false;
|
||||
|
||||
private bool serverlogin_done = false;
|
||||
private int verifytasks_timeleft = 10;
|
||||
private readonly int verifytasks_delay = 10;
|
||||
private int verifytasks_timeleft = Settings.ClientTicksPerSecond;
|
||||
private readonly int verifytasks_delay = Settings.ClientTicksPerSecond;
|
||||
|
||||
public override void AfterGameJoined()
|
||||
{
|
||||
if (serverlogin_done)
|
||||
return;
|
||||
|
||||
serverlogin_done = true;
|
||||
verifytasks_timeleft = verifytasks_delay;
|
||||
RunLoginTasks();
|
||||
}
|
||||
|
||||
public override void Update()
|
||||
{
|
||||
if (!serverlogin_done)
|
||||
return;
|
||||
|
||||
if (verifytasks_timeleft <= 0)
|
||||
{
|
||||
verifytasks_timeleft = verifytasks_delay;
|
||||
if (serverlogin_done)
|
||||
for (int taskIndex = 0; taskIndex < Config.TaskList.Length; taskIndex++)
|
||||
{
|
||||
foreach (TaskConfig task in Config.TaskList)
|
||||
TaskConfig task = Config.TaskList[taskIndex];
|
||||
if (task.Trigger_On_Times.Enable)
|
||||
{
|
||||
if (task.Trigger_On_Times.Enable)
|
||||
{
|
||||
bool matching_time_found = false;
|
||||
bool matching_time_found = false;
|
||||
|
||||
foreach (TimeSpan time in task.Trigger_On_Times.Times)
|
||||
foreach (TimeSpan time in task.Trigger_On_Times.Times)
|
||||
{
|
||||
if (time.Hours == DateTime.Now.Hour && time.Minutes == DateTime.Now.Minute)
|
||||
{
|
||||
if (time.Hours == DateTime.Now.Hour && time.Minutes == DateTime.Now.Minute)
|
||||
matching_time_found = true;
|
||||
if (!task.Trigger_On_Time_Already_Triggered)
|
||||
{
|
||||
matching_time_found = true;
|
||||
if (!task.Trigger_On_Time_Already_Triggered)
|
||||
{
|
||||
task.Trigger_On_Time_Already_Triggered = true;
|
||||
LogDebugToConsole(string.Format(Translations.bot_scriptScheduler_running_time, task.Action));
|
||||
CmdResult response = new();
|
||||
PerformInternalCommand(task.Action, ref response);
|
||||
if (response.status != CmdResult.Status.Done || !string.IsNullOrWhiteSpace(response.result))
|
||||
LogToConsole(response);
|
||||
}
|
||||
task.Trigger_On_Time_Already_Triggered = true;
|
||||
RunTaskAction(task, taskIndex, string.Format(Translations.bot_scriptScheduler_running_time, task.Action));
|
||||
}
|
||||
}
|
||||
|
||||
if (!matching_time_found)
|
||||
task.Trigger_On_Time_Already_Triggered = false;
|
||||
}
|
||||
|
||||
if (!matching_time_found)
|
||||
task.Trigger_On_Time_Already_Triggered = false;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
foreach (TaskConfig task in Config.TaskList)
|
||||
{
|
||||
if (task.Trigger_On_Login || (firstlogin_done == false && task.Trigger_On_First_Login))
|
||||
{
|
||||
LogDebugToConsole(string.Format(Translations.bot_scriptScheduler_running_login, task.Action));
|
||||
CmdResult response = new();
|
||||
PerformInternalCommand(task.Action, ref response);
|
||||
if (response.status != CmdResult.Status.Done || !string.IsNullOrWhiteSpace(response.result))
|
||||
LogToConsole(response);
|
||||
}
|
||||
}
|
||||
|
||||
firstlogin_done = true;
|
||||
serverlogin_done = true;
|
||||
}
|
||||
}
|
||||
else verifytasks_timeleft--;
|
||||
|
||||
foreach (TaskConfig task in Config.TaskList)
|
||||
for (int taskIndex = 0; taskIndex < Config.TaskList.Length; taskIndex++)
|
||||
{
|
||||
TaskConfig task = Config.TaskList[taskIndex];
|
||||
if (task.Trigger_On_Interval.Enable)
|
||||
{
|
||||
if (task.Trigger_On_Interval_Countdown == 0)
|
||||
|
|
@ -235,11 +238,7 @@ namespace MinecraftClient.ChatBots
|
|||
task.Trigger_On_Interval_Countdown = random.Next(
|
||||
Settings.DoubleToTick(task.Trigger_On_Interval.MinTime), Settings.DoubleToTick(task.Trigger_On_Interval.MaxTime)
|
||||
);
|
||||
LogDebugToConsole(string.Format(Translations.bot_scriptScheduler_running_inverval, task.Action));
|
||||
CmdResult response = new();
|
||||
PerformInternalCommand(task.Action, ref response);
|
||||
if (response.status != CmdResult.Status.Done || !string.IsNullOrWhiteSpace(response.result))
|
||||
LogToConsole(response);
|
||||
RunTaskAction(task, taskIndex, string.Format(Translations.bot_scriptScheduler_running_inverval, task.Action));
|
||||
}
|
||||
else task.Trigger_On_Interval_Countdown--;
|
||||
}
|
||||
|
|
@ -252,6 +251,58 @@ namespace MinecraftClient.ChatBots
|
|||
return false;
|
||||
}
|
||||
|
||||
private void RunLoginTasks()
|
||||
{
|
||||
bool isFirstLogin = !firstlogin_done;
|
||||
|
||||
for (int taskIndex = 0; taskIndex < Config.TaskList.Length; taskIndex++)
|
||||
{
|
||||
TaskConfig task = Config.TaskList[taskIndex];
|
||||
if (task.Trigger_On_Login || (isFirstLogin && task.Trigger_On_First_Login))
|
||||
RunTaskAction(task, taskIndex, string.Format(Translations.bot_scriptScheduler_running_login, task.Action));
|
||||
}
|
||||
|
||||
firstlogin_done = true;
|
||||
}
|
||||
|
||||
private void RunTaskAction(TaskConfig task, int taskIndex, string debugMessage)
|
||||
{
|
||||
LogDebugToConsole(debugMessage);
|
||||
|
||||
if (TryRunOwnedScript(task, taskIndex))
|
||||
return;
|
||||
|
||||
CmdResult response = new();
|
||||
PerformInternalCommand(task.Action, ref response);
|
||||
if (response.status != CmdResult.Status.Done || !string.IsNullOrWhiteSpace(response.result))
|
||||
LogToConsole(response);
|
||||
}
|
||||
|
||||
private bool TryRunOwnedScript(TaskConfig task, int taskIndex)
|
||||
{
|
||||
string action = task.Action.Trim();
|
||||
const string scriptCommand = "script";
|
||||
if (!action.StartsWith(scriptCommand, StringComparison.OrdinalIgnoreCase))
|
||||
return false;
|
||||
|
||||
if (action.Length == scriptCommand.Length || !char.IsWhiteSpace(action[scriptCommand.Length]))
|
||||
return false;
|
||||
|
||||
string scriptArgs = action[scriptCommand.Length..].Trim();
|
||||
if (string.IsNullOrWhiteSpace(scriptArgs))
|
||||
return false;
|
||||
|
||||
string scriptOwnerKey = BuildScriptOwnerKey(task, taskIndex);
|
||||
Handler.UnloadBotsByScriptOwnerKey(scriptOwnerKey);
|
||||
Handler.BotLoad(new Script(scriptArgs, null, null, scriptOwnerKey));
|
||||
return true;
|
||||
}
|
||||
|
||||
private static string BuildScriptOwnerKey(TaskConfig task, int taskIndex)
|
||||
{
|
||||
return $"{nameof(ScriptScheduler)}:{taskIndex}:{task.Task_Name}:{task.Action.Trim()}";
|
||||
}
|
||||
|
||||
private static string Task2String(TaskConfig task)
|
||||
{
|
||||
return string.Format(
|
||||
|
|
|
|||
|
|
@ -12,7 +12,6 @@ using Telegram.Bot.Exceptions;
|
|||
using Telegram.Bot.Polling;
|
||||
using Telegram.Bot.Types;
|
||||
using Telegram.Bot.Types.Enums;
|
||||
using Telegram.Bot.Types.InputFiles;
|
||||
using Tomlet.Attributes;
|
||||
using File = System.IO.File;
|
||||
|
||||
|
|
@ -149,7 +148,7 @@ namespace MinecraftClient.ChatBots
|
|||
|
||||
private void Disconnect()
|
||||
{
|
||||
if (botClient != null)
|
||||
if (botClient is not null)
|
||||
{
|
||||
try
|
||||
{
|
||||
|
|
@ -195,7 +194,7 @@ namespace MinecraftClient.ChatBots
|
|||
|
||||
else message = text;
|
||||
|
||||
SendMessage(message);
|
||||
SendRawMessage(message);
|
||||
}
|
||||
|
||||
public void SendMessage(string message)
|
||||
|
|
@ -205,7 +204,23 @@ namespace MinecraftClient.ChatBots
|
|||
|
||||
try
|
||||
{
|
||||
botClient!.SendTextMessageAsync(Config.ChannelId.Trim(), message, ParseMode.Markdown).Wait(Config.Message_Send_Timeout);
|
||||
botClient!.SendMessage(Config.ChannelId.Trim(), message, parseMode: ParseMode.Markdown).Wait(Config.Message_Send_Timeout);
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
LogToConsole("§§4§l§f" + Translations.bot_TelegramBridge_canceled_sending);
|
||||
LogDebugToConsole(e);
|
||||
}
|
||||
}
|
||||
|
||||
public void SendRawMessage(string message)
|
||||
{
|
||||
if (!CanSendMessages() || string.IsNullOrEmpty(message))
|
||||
return;
|
||||
|
||||
try
|
||||
{
|
||||
botClient!.SendMessage(Config.ChannelId.Trim(), message).Wait(Config.Message_Send_Timeout);
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
|
|
@ -224,9 +239,9 @@ namespace MinecraftClient.ChatBots
|
|||
string fileName = filePath[(filePath.IndexOf(Path.DirectorySeparatorChar) + 1)..];
|
||||
|
||||
Stream stream = File.OpenRead(filePath);
|
||||
botClient!.SendDocumentAsync(
|
||||
botClient!.SendDocument(
|
||||
Config.ChannelId.Trim(),
|
||||
document: new InputOnlineFile(content: stream, fileName),
|
||||
document: InputFile.FromStream(stream, fileName),
|
||||
caption: text,
|
||||
parseMode: ParseMode.Markdown).Wait(Config.Message_Send_Timeout * 1000);
|
||||
}
|
||||
|
|
@ -239,7 +254,7 @@ namespace MinecraftClient.ChatBots
|
|||
|
||||
private bool CanSendMessages()
|
||||
{
|
||||
return botClient != null && !string.IsNullOrEmpty(Config.ChannelId.Trim()) && bridgeDirection != BridgeDirection.Minecraft;
|
||||
return botClient is not null && !string.IsNullOrEmpty(Config.ChannelId.Trim()) && bridgeDirection != BridgeDirection.Minecraft;
|
||||
}
|
||||
|
||||
async Task MainAsync()
|
||||
|
|
@ -260,14 +275,14 @@ namespace MinecraftClient.ChatBots
|
|||
cancellationToken = new CancellationTokenSource();
|
||||
|
||||
botClient.StartReceiving(
|
||||
updateHandler: HandleUpdateAsync,
|
||||
pollingErrorHandler: HandlePollingErrorAsync,
|
||||
receiverOptions: new ReceiverOptions
|
||||
HandleUpdateAsync,
|
||||
HandlePollingErrorAsync,
|
||||
new ReceiverOptions
|
||||
{
|
||||
// receive all update types
|
||||
AllowedUpdates = Array.Empty<UpdateType>()
|
||||
},
|
||||
cancellationToken: cancellationToken.Token
|
||||
cancellationToken.Token
|
||||
);
|
||||
|
||||
IsConnected = true;
|
||||
|
|
@ -313,9 +328,9 @@ namespace MinecraftClient.ChatBots
|
|||
|
||||
if (text.ToLower().Contains(".chatid"))
|
||||
{
|
||||
await botClient.SendTextMessageAsync(chatId: chatId,
|
||||
replyToMessageId: message.MessageId,
|
||||
await botClient.SendMessage(chatId: chatId,
|
||||
text: $"Chat ID: {chatId}",
|
||||
replyParameters: message.MessageId,
|
||||
cancellationToken: _cancellationToken,
|
||||
parseMode: ParseMode.Markdown);
|
||||
return;
|
||||
|
|
@ -324,10 +339,10 @@ namespace MinecraftClient.ChatBots
|
|||
if (Config.Authorized_Chat_Ids.Length > 0 && !Config.Authorized_Chat_Ids.Contains(chatId))
|
||||
{
|
||||
LogDebugToConsole($"Unauthorized message '{messageText}' received in a chat with with an ID: {chatId} !");
|
||||
await botClient.SendTextMessageAsync(
|
||||
await botClient.SendMessage(
|
||||
chatId: chatId,
|
||||
replyToMessageId: message.MessageId,
|
||||
text: Translations.bot_TelegramBridge_unauthorized,
|
||||
replyParameters: message.MessageId,
|
||||
cancellationToken: _cancellationToken,
|
||||
parseMode: ParseMode.Markdown);
|
||||
return;
|
||||
|
|
@ -347,23 +362,22 @@ namespace MinecraftClient.ChatBots
|
|||
|
||||
if (command.ToLower().Contains("quit") || command.ToLower().Contains("exit"))
|
||||
{
|
||||
await botClient.SendTextMessageAsync(
|
||||
await botClient.SendMessage(
|
||||
chatId: chatId,
|
||||
replyToMessageId: message.MessageId,
|
||||
text: $"{Translations.bot_TelegramBridge_quit_disabled}",
|
||||
replyParameters: message.MessageId,
|
||||
cancellationToken: _cancellationToken,
|
||||
parseMode: ParseMode.Markdown);
|
||||
return;;
|
||||
return; ;
|
||||
}
|
||||
|
||||
CmdResult result = new();
|
||||
PerformInternalCommand(command, ref result);
|
||||
|
||||
await botClient.SendTextMessageAsync(
|
||||
await botClient.SendMessage(
|
||||
chatId: chatId,
|
||||
replyToMessageId:
|
||||
message.MessageId,
|
||||
text: $"{Translations.bot_TelegramBridge_command_executed}:\n\n{result}",
|
||||
replyParameters: message.MessageId,
|
||||
cancellationToken: _cancellationToken,
|
||||
parseMode: ParseMode.Markdown);
|
||||
}
|
||||
|
|
|
|||
File diff suppressed because it is too large
Load diff
235
MinecraftClient/ClassicConsoleBackend.cs
Normal file
235
MinecraftClient/ClassicConsoleBackend.cs
Normal file
|
|
@ -0,0 +1,235 @@
|
|||
using System;
|
||||
using System.Text.RegularExpressions;
|
||||
|
||||
namespace MinecraftClient
|
||||
{
|
||||
/// <summary>
|
||||
/// Console backend wrapping the ConsoleInteractive library (existing behavior).
|
||||
/// </summary>
|
||||
public partial class ClassicConsoleBackend : IConsoleBackend
|
||||
{
|
||||
private static readonly (byte R, byte G, byte B, char Code)[] McStandardColors =
|
||||
[
|
||||
(0, 0, 0, '0'), // black
|
||||
(0, 0, 170, '1'), // dark_blue
|
||||
(0, 170, 0, '2'), // dark_green
|
||||
(0, 170, 170, '3'), // dark_aqua
|
||||
(170, 0, 0, '4'), // dark_red
|
||||
(170, 0, 170, '5'), // dark_purple
|
||||
(255, 170, 0, '6'), // gold
|
||||
(170, 170, 170, '7'), // gray
|
||||
(85, 85, 85, '8'), // dark_gray
|
||||
(85, 85, 255, '9'), // blue
|
||||
(85, 255, 85, 'a'), // green
|
||||
(85, 255, 255, 'b'), // aqua
|
||||
(255, 85, 85, 'c'), // red
|
||||
(255, 85, 255, 'd'), // light_purple
|
||||
(255, 255, 85, 'e'), // yellow
|
||||
(255, 255, 255, 'f'), // white
|
||||
];
|
||||
|
||||
[GeneratedRegex("§#([0-9a-fA-F]{6})")]
|
||||
private static partial Regex HexColorRegex();
|
||||
|
||||
private static char NearestMcColor(byte r, byte g, byte b)
|
||||
{
|
||||
int bestIdx = 0;
|
||||
long bestDist = long.MaxValue;
|
||||
|
||||
for (int i = 0; i < McStandardColors.Length; i++)
|
||||
{
|
||||
var (sr, sg, sb, _) = McStandardColors[i];
|
||||
long dr = r - sr;
|
||||
long dg = g - sg;
|
||||
long db = b - sb;
|
||||
long dist = dr * dr + dg * dg + db * db;
|
||||
if (dist < bestDist)
|
||||
{
|
||||
bestDist = dist;
|
||||
bestIdx = i;
|
||||
}
|
||||
}
|
||||
|
||||
return McStandardColors[bestIdx].Code;
|
||||
}
|
||||
|
||||
private static string ResolveHexColors(string text)
|
||||
{
|
||||
if (string.IsNullOrEmpty(text) || !text.Contains("§#", StringComparison.Ordinal))
|
||||
return text;
|
||||
|
||||
return HexColorRegex().Replace(text, match =>
|
||||
{
|
||||
ReadOnlySpan<char> hex = match.Groups[1].ValueSpan;
|
||||
byte r = (byte)((HexVal(hex[0]) << 4) | HexVal(hex[1]));
|
||||
byte g = (byte)((HexVal(hex[2]) << 4) | HexVal(hex[3]));
|
||||
byte b = (byte)((HexVal(hex[4]) << 4) | HexVal(hex[5]));
|
||||
return ColorHelper.GetColorEscapeCode(r, g, b, foreground: true);
|
||||
});
|
||||
}
|
||||
|
||||
private static int HexVal(char c) => c switch
|
||||
{
|
||||
>= '0' and <= '9' => c - '0',
|
||||
>= 'a' and <= 'f' => c - 'a' + 10,
|
||||
>= 'A' and <= 'F' => c - 'A' + 10,
|
||||
_ => 0
|
||||
};
|
||||
|
||||
public event EventHandler<string>? MessageReceived;
|
||||
public event EventHandler<ConsoleInputBuffer>? OnInputChange;
|
||||
|
||||
public bool DisplayUserInput
|
||||
{
|
||||
get => ConsoleInteractive.ConsoleReader.DisplayUesrInput;
|
||||
set => ConsoleInteractive.ConsoleReader.DisplayUesrInput = value;
|
||||
}
|
||||
|
||||
public void Init()
|
||||
{
|
||||
ConsoleInteractive.ConsoleWriter.Init();
|
||||
}
|
||||
|
||||
public void WriteLine(string text)
|
||||
{
|
||||
ConsoleInteractive.ConsoleWriter.WriteLine(text);
|
||||
}
|
||||
|
||||
public void WriteLineFormatted(string text)
|
||||
{
|
||||
ConsoleInteractive.ConsoleWriter.WriteLineFormatted(ResolveHexColors(text));
|
||||
}
|
||||
|
||||
public void BeginReadThread()
|
||||
{
|
||||
ConsoleInteractive.ConsoleReader.MessageReceived += ForwardMessage;
|
||||
ConsoleInteractive.ConsoleReader.OnInputChange += ForwardInputChange;
|
||||
ConsoleInteractive.ConsoleReader.BeginReadThread();
|
||||
}
|
||||
|
||||
public void StopReadThread()
|
||||
{
|
||||
ConsoleInteractive.ConsoleReader.StopReadThread();
|
||||
ConsoleInteractive.ConsoleReader.MessageReceived -= ForwardMessage;
|
||||
ConsoleInteractive.ConsoleReader.OnInputChange -= ForwardInputChange;
|
||||
}
|
||||
|
||||
public string RequestImmediateInput()
|
||||
{
|
||||
return ConsoleInteractive.ConsoleReader.RequestImmediateInput();
|
||||
}
|
||||
|
||||
public string? ReadPassword()
|
||||
{
|
||||
ConsoleInteractive.ConsoleReader.SetInputVisible(false);
|
||||
var input = ConsoleInteractive.ConsoleReader.RequestImmediateInput();
|
||||
ConsoleInteractive.ConsoleReader.SetInputVisible(true);
|
||||
return input;
|
||||
}
|
||||
|
||||
public void ClearInputBuffer()
|
||||
{
|
||||
ConsoleInteractive.ConsoleReader.ClearBuffer();
|
||||
}
|
||||
|
||||
public void ClearScreen()
|
||||
{
|
||||
Console.Clear();
|
||||
ConsoleInteractive.ConsoleSuggestion.ClearSuggestions();
|
||||
}
|
||||
|
||||
public void SetInputVisible(bool visible)
|
||||
{
|
||||
ConsoleInteractive.ConsoleReader.SetInputVisible(visible);
|
||||
}
|
||||
|
||||
public void SetBackreadBufferLimit(int limit)
|
||||
{
|
||||
ConsoleInteractive.ConsoleBuffer.SetBackreadBufferLimit(limit);
|
||||
}
|
||||
|
||||
public void Shutdown()
|
||||
{
|
||||
ConsoleInteractive.ConsoleSuggestion.ClearSuggestions();
|
||||
}
|
||||
|
||||
#region Suggestion forwarding for classic mode
|
||||
|
||||
public void UpdateSuggestions(
|
||||
ConsoleInteractive.ConsoleSuggestion.Suggestion[] suggestions,
|
||||
Tuple<int, int> range)
|
||||
{
|
||||
ConsoleInteractive.ConsoleSuggestion.UpdateSuggestions(suggestions, range);
|
||||
}
|
||||
|
||||
public void ClearSuggestions()
|
||||
{
|
||||
ConsoleInteractive.ConsoleSuggestion.ClearSuggestions();
|
||||
}
|
||||
|
||||
public void SetSuggestionColors(
|
||||
string textColor, string textBgColor,
|
||||
string hlTextColor, string hlTextBgColor,
|
||||
string tooltipColor, string hlTooltipColor,
|
||||
string arrowColor)
|
||||
{
|
||||
ConsoleInteractive.ConsoleSuggestion.SetColors(
|
||||
textColor, textBgColor,
|
||||
hlTextColor, hlTextBgColor,
|
||||
tooltipColor, hlTooltipColor,
|
||||
arrowColor);
|
||||
}
|
||||
|
||||
public bool EnableSuggestionColor
|
||||
{
|
||||
get => ConsoleInteractive.ConsoleSuggestion.EnableColor;
|
||||
set => ConsoleInteractive.ConsoleSuggestion.EnableColor = value;
|
||||
}
|
||||
|
||||
public bool Enable24bitColor
|
||||
{
|
||||
get => ConsoleInteractive.ConsoleSuggestion.Enable24bitColor;
|
||||
set => ConsoleInteractive.ConsoleSuggestion.Enable24bitColor = value;
|
||||
}
|
||||
|
||||
public bool UseBasicArrow
|
||||
{
|
||||
get => ConsoleInteractive.ConsoleSuggestion.UseBasicArrow;
|
||||
set => ConsoleInteractive.ConsoleSuggestion.UseBasicArrow = value;
|
||||
}
|
||||
|
||||
public int SetMaxSuggestionLength(int length)
|
||||
{
|
||||
return ConsoleInteractive.ConsoleSuggestion.SetMaxSuggestionLength(length);
|
||||
}
|
||||
|
||||
public int SetMaxSuggestionCount(int count)
|
||||
{
|
||||
return ConsoleInteractive.ConsoleSuggestion.SetMaxSuggestionCount(count);
|
||||
}
|
||||
|
||||
public bool EnableWriterColor
|
||||
{
|
||||
get => ConsoleInteractive.ConsoleWriter.EnableColor;
|
||||
set => ConsoleInteractive.ConsoleWriter.EnableColor = value;
|
||||
}
|
||||
|
||||
public bool UseVT100ColorCode
|
||||
{
|
||||
get => ConsoleInteractive.ConsoleWriter.UseVT100ColorCode;
|
||||
set => ConsoleInteractive.ConsoleWriter.UseVT100ColorCode = value;
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
private void ForwardMessage(object? sender, string e)
|
||||
{
|
||||
MessageReceived?.Invoke(sender, e);
|
||||
}
|
||||
|
||||
private void ForwardInputChange(object? sender, ConsoleInteractive.ConsoleReader.Buffer buffer)
|
||||
{
|
||||
OnInputChange?.Invoke(sender, new ConsoleInputBuffer(buffer.Text, buffer.CursorPosition));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -1,4 +1,4 @@
|
|||
using System;
|
||||
using System;
|
||||
using static MinecraftClient.Settings.ConsoleConfigHealper.ConsoleConfig;
|
||||
|
||||
namespace MinecraftClient
|
||||
|
|
@ -100,9 +100,9 @@ namespace MinecraftClient
|
|||
}
|
||||
}
|
||||
if (foreground)
|
||||
return $"§{best_idx:X}";
|
||||
return $"§{best_idx:x}";
|
||||
else
|
||||
return $"§§{best_idx:X}";
|
||||
return $"§§{best_idx:x}";
|
||||
}
|
||||
|
||||
case ConsoleColorModeType.vt100_4bit:
|
||||
|
|
@ -163,7 +163,7 @@ namespace MinecraftClient
|
|||
}
|
||||
}
|
||||
|
||||
public class ColorRGBA
|
||||
public record struct ColorRGBA
|
||||
{
|
||||
public byte R { get; set; }
|
||||
public byte G { get; set; }
|
||||
|
|
|
|||
|
|
@ -17,7 +17,7 @@ namespace MinecraftClient.CommandHandler.ArgumentType
|
|||
public override Task<Suggestions> ListSuggestions<TSource>(CommandContext<TSource> context, SuggestionsBuilder builder)
|
||||
{
|
||||
McClient? client = CmdResult.currentHandler;
|
||||
if (client != null)
|
||||
if (client is not null)
|
||||
{
|
||||
var botList = client.GetLoadedChatBots();
|
||||
foreach (var bot in botList)
|
||||
|
|
|
|||
|
|
@ -18,10 +18,10 @@ namespace MinecraftClient.CommandHandler.ArgumentType
|
|||
public override Task<Suggestions> ListSuggestions<TSource>(CommandContext<TSource> context, SuggestionsBuilder builder)
|
||||
{
|
||||
McClient? client = CmdResult.currentHandler;
|
||||
if (client != null)
|
||||
if (client is not null)
|
||||
{
|
||||
Inventory.Container? inventory = client.GetInventory(0);
|
||||
if (inventory != null)
|
||||
if (inventory is not null)
|
||||
{
|
||||
for (int i = 1; i <= 9; ++i)
|
||||
{
|
||||
|
|
|
|||
|
|
@ -18,7 +18,7 @@ namespace MinecraftClient.CommandHandler.ArgumentType
|
|||
public override Task<Suggestions> ListSuggestions<TSource>(CommandContext<TSource> context, SuggestionsBuilder builder)
|
||||
{
|
||||
McClient? client = CmdResult.currentHandler;
|
||||
if (client != null)
|
||||
if (client is not null)
|
||||
{
|
||||
var invList = client.GetInventories();
|
||||
foreach (var inv in invList)
|
||||
|
|
|
|||
|
|
@ -19,7 +19,7 @@ namespace MinecraftClient.CommandHandler.ArgumentType
|
|||
public override Task<Suggestions> ListSuggestions<TSource>(CommandContext<TSource> context, SuggestionsBuilder builder)
|
||||
{
|
||||
McClient? client = CmdResult.currentHandler;
|
||||
if (client != null && context.Nodes.Count >= 2)
|
||||
if (client is not null && context.Nodes.Count >= 2)
|
||||
{
|
||||
string invName = context.Nodes[1].Range.Get(builder.Input);
|
||||
if (!int.TryParse(invName, out int invId))
|
||||
|
|
@ -33,11 +33,11 @@ namespace MinecraftClient.CommandHandler.ArgumentType
|
|||
};
|
||||
|
||||
Inventory.Container? inventory = client.GetInventory(invId);
|
||||
if (inventory != null)
|
||||
if (inventory is not null)
|
||||
{
|
||||
foreach ((int slot, Inventory.Item item) in inventory.Items)
|
||||
{
|
||||
if (item != null && item.Count > 0)
|
||||
if (item is not null && item.Count > 0)
|
||||
{
|
||||
string slotStr = slot.ToString();
|
||||
if (slotStr.StartsWith(builder.RemainingLowerCase, StringComparison.InvariantCultureIgnoreCase))
|
||||
|
|
|
|||
|
|
@ -51,7 +51,7 @@ namespace MinecraftClient.CommandHandler.ArgumentType
|
|||
string[] args = builder.Remaining.Split(' ', StringSplitOptions.TrimEntries);
|
||||
if (args.Length == 0 || (args.Length == 1 && string.IsNullOrWhiteSpace(args[0])))
|
||||
{
|
||||
if (client != null)
|
||||
if (client is not null)
|
||||
{
|
||||
Location current = client.GetCurrentLocation();
|
||||
builder.Suggest(string.Format("{0:0.00}", current.X));
|
||||
|
|
@ -68,7 +68,7 @@ namespace MinecraftClient.CommandHandler.ArgumentType
|
|||
else if (args.Length == 1 || (args.Length == 2 && string.IsNullOrWhiteSpace(args[1])))
|
||||
{
|
||||
string add = args.Length == 1 ? " " : string.Empty;
|
||||
if (client != null)
|
||||
if (client is not null)
|
||||
{
|
||||
Location current = client.GetCurrentLocation();
|
||||
builder.Suggest(string.Format("{0}{2}{1:0.00}", builder.Remaining, current.Y, add));
|
||||
|
|
@ -83,7 +83,7 @@ namespace MinecraftClient.CommandHandler.ArgumentType
|
|||
else if (args.Length == 2 || (args.Length == 3 && string.IsNullOrWhiteSpace(args[2])))
|
||||
{
|
||||
string add = args.Length == 2 ? " " : string.Empty;
|
||||
if (client != null)
|
||||
if (client is not null)
|
||||
{
|
||||
Location current = client.GetCurrentLocation();
|
||||
builder.Suggest(string.Format("{0}{2}{1:0.00}", builder.Remaining, current.Z, add));
|
||||
|
|
|
|||
|
|
@ -19,10 +19,10 @@ namespace MinecraftClient.CommandHandler.ArgumentType
|
|||
public override Task<Suggestions> ListSuggestions<TSource>(CommandContext<TSource> context, SuggestionsBuilder builder)
|
||||
{
|
||||
McClient? client = CmdResult.currentHandler;
|
||||
if (client != null)
|
||||
if (client is not null)
|
||||
{
|
||||
var bot = (Map?)client.GetLoadedChatBots().Find(bot => bot.GetType().Name == "Map");
|
||||
if (bot != null)
|
||||
if (bot is not null)
|
||||
{
|
||||
var mapList = bot.cachedMaps;
|
||||
foreach (var map in mapList)
|
||||
|
|
|
|||
|
|
@ -19,7 +19,7 @@ namespace MinecraftClient.CommandHandler.ArgumentType
|
|||
public override Task<Suggestions> ListSuggestions<TSource>(CommandContext<TSource> context, SuggestionsBuilder builder)
|
||||
{
|
||||
McClient? client = CmdResult.currentHandler;
|
||||
if (client != null)
|
||||
if (client is not null)
|
||||
{
|
||||
var entityList = client.GetEntities().Values.ToList();
|
||||
foreach (var entity in entityList)
|
||||
|
|
|
|||
|
|
@ -87,7 +87,7 @@ namespace MinecraftClient.CommandHandler
|
|||
|
||||
public override string ToString()
|
||||
{
|
||||
if (result != null)
|
||||
if (result is not null)
|
||||
return result;
|
||||
else
|
||||
return status.ToString();
|
||||
|
|
|
|||
|
|
@ -2,13 +2,8 @@
|
|||
|
||||
namespace MinecraftClient.CommandHandler
|
||||
{
|
||||
internal class SuggestionTooltip : IMessage
|
||||
internal class SuggestionTooltip(string tooltip) : IMessage
|
||||
{
|
||||
public SuggestionTooltip(string tooltip)
|
||||
{
|
||||
String = tooltip;
|
||||
}
|
||||
|
||||
public string String { get; set; }
|
||||
public string String { get; set; } = tooltip;
|
||||
}
|
||||
}
|
||||
|
|
|
|||
106
MinecraftClient/Commands/AchievementCommand.cs
Normal file
106
MinecraftClient/Commands/AchievementCommand.cs
Normal file
|
|
@ -0,0 +1,106 @@
|
|||
using System.Linq;
|
||||
using System.Text;
|
||||
using Brigadier.NET;
|
||||
using Brigadier.NET.Builder;
|
||||
using MinecraftClient.CommandHandler;
|
||||
|
||||
namespace MinecraftClient.Commands
|
||||
{
|
||||
public class AchievementCommand : Command
|
||||
{
|
||||
public override string CmdName => "achievement";
|
||||
public override string CmdUsage => "achievement <list|locked|unlocked>";
|
||||
public override string CmdDesc => Translations.cmd_achievement_desc;
|
||||
|
||||
public override void RegisterCommand(CommandDispatcher<CmdResult> dispatcher)
|
||||
{
|
||||
dispatcher.Register(l => l.Literal("help")
|
||||
.Then(l => l.Literal(CmdName)
|
||||
.Executes(r => GetUsage(r.Source, string.Empty))
|
||||
.Then(l => l.Literal("list")
|
||||
.Executes(r => GetUsage(r.Source, "list")))
|
||||
.Then(l => l.Literal("locked")
|
||||
.Executes(r => GetUsage(r.Source, "locked")))
|
||||
.Then(l => l.Literal("unlocked")
|
||||
.Executes(r => GetUsage(r.Source, "unlocked")))
|
||||
)
|
||||
);
|
||||
|
||||
dispatcher.Register(l => l.Literal(CmdName)
|
||||
.Executes(r => ListAchievements(r.Source, null))
|
||||
.Then(l => l.Literal("list")
|
||||
.Executes(r => ListAchievements(r.Source, null)))
|
||||
.Then(l => l.Literal("locked")
|
||||
.Executes(r => ListAchievements(r.Source, false)))
|
||||
.Then(l => l.Literal("unlocked")
|
||||
.Executes(r => ListAchievements(r.Source, true)))
|
||||
.Then(l => l.Literal("_help")
|
||||
.Executes(r => GetUsage(r.Source, string.Empty))
|
||||
.Redirect(dispatcher.GetRoot().GetChild("help").GetChild(CmdName)))
|
||||
);
|
||||
}
|
||||
|
||||
private int GetUsage(CmdResult r, string? cmd)
|
||||
{
|
||||
return r.SetAndReturn(cmd switch
|
||||
{
|
||||
#pragma warning disable format
|
||||
"list" => GetCmdDescTranslated(),
|
||||
"locked" => GetCmdDescTranslated(),
|
||||
"unlocked" => GetCmdDescTranslated(),
|
||||
_ => GetCmdDescTranslated(),
|
||||
#pragma warning restore format
|
||||
});
|
||||
}
|
||||
|
||||
/// <param name="completed">null = all, true = unlocked only, false = locked only</param>
|
||||
private static int ListAchievements(CmdResult r, bool? completed)
|
||||
{
|
||||
McClient handler = CmdResult.currentHandler!;
|
||||
|
||||
Achievement[] items = completed switch
|
||||
{
|
||||
true => handler.GetUnlockedAchievements(),
|
||||
false => handler.GetLockedAchievements(),
|
||||
null => handler.GetAchievements()
|
||||
};
|
||||
|
||||
if (items.Length == 0)
|
||||
{
|
||||
string msg = completed switch
|
||||
{
|
||||
true => Translations.cmd_achievement_none_unlocked,
|
||||
false => Translations.cmd_achievement_none_locked,
|
||||
_ => Translations.cmd_achievement_none
|
||||
};
|
||||
return r.SetAndReturn(CmdResult.Status.Done, msg);
|
||||
}
|
||||
|
||||
string header = completed switch
|
||||
{
|
||||
true => Translations.cmd_achievement_header_unlocked,
|
||||
false => Translations.cmd_achievement_header_locked,
|
||||
_ => Translations.cmd_achievement_header
|
||||
};
|
||||
|
||||
StringBuilder sb = new();
|
||||
sb.AppendLine(header);
|
||||
|
||||
foreach (Achievement a in items.OrderBy(static a => a.Id))
|
||||
{
|
||||
string status = a.IsCompleted
|
||||
? Translations.cmd_achievement_done
|
||||
: Translations.cmd_achievement_todo;
|
||||
|
||||
string display = a.Title is not null
|
||||
? string.Format(Translations.cmd_achievement_entry_titled, status, a.Title, a.Id, a.Type)
|
||||
: string.Format(Translations.cmd_achievement_entry, status, a.Id, a.Type);
|
||||
|
||||
sb.AppendLine(display);
|
||||
}
|
||||
|
||||
handler.Log.Info(sb.ToString().TrimEnd());
|
||||
return r.SetAndReturn(CmdResult.Status.Done);
|
||||
}
|
||||
}
|
||||
}
|
||||
300
MinecraftClient/Commands/Book.cs
Normal file
300
MinecraftClient/Commands/Book.cs
Normal file
|
|
@ -0,0 +1,300 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using Brigadier.NET;
|
||||
using Brigadier.NET.Builder;
|
||||
using MinecraftClient.CommandHandler;
|
||||
using MinecraftClient.Inventory;
|
||||
using MinecraftClient.Tui;
|
||||
|
||||
namespace MinecraftClient.Commands
|
||||
{
|
||||
public class Book : Command
|
||||
{
|
||||
private const char PageDelimiter = '\f';
|
||||
|
||||
public override string CmdName => "book";
|
||||
public override string CmdUsage => Translations.cmd_book_usage;
|
||||
public override string CmdDesc => Translations.cmd_book_desc;
|
||||
|
||||
public override void RegisterCommand(CommandDispatcher<CmdResult> dispatcher)
|
||||
{
|
||||
dispatcher.Register(l => l.Literal("help")
|
||||
.Then(l => l.Literal(CmdName)
|
||||
.Executes(r => GetUsage(r.Source, string.Empty))
|
||||
.Then(l => l.Literal("read").Executes(r => GetUsage(r.Source, "read")))
|
||||
.Then(l => l.Literal("write").Executes(r => GetUsage(r.Source, "write")))
|
||||
.Then(l => l.Literal("edit").Executes(r => GetUsage(r.Source, "edit")))
|
||||
.Then(l => l.Literal("sign").Executes(r => GetUsage(r.Source, "sign")))));
|
||||
|
||||
dispatcher.Register(l => l.Literal(CmdName)
|
||||
.Then(l => l.Literal("read")
|
||||
.Executes(r => ReadBook(r.Source, null))
|
||||
.Then(l => l.Argument("Page", Arguments.Integer(min: 1))
|
||||
.Executes(r => ReadBook(r.Source, Arguments.GetInteger(r, "Page")))))
|
||||
.Then(l => l.Literal("write")
|
||||
.Then(l => l.Literal("text")
|
||||
.Then(l => l.Argument("Text", Arguments.GreedyString())
|
||||
.Executes(r => WriteBook(r.Source, Arguments.GetString(r, "Text")))))
|
||||
.Then(l => l.Literal("file")
|
||||
.Then(l => l.Argument("Path", Arguments.GreedyString())
|
||||
.Executes(r => WriteBookFromFile(r.Source, Arguments.GetString(r, "Path"))))))
|
||||
.Then(l => l.Literal("edit")
|
||||
.Executes(r => OpenEditor(r.Source))
|
||||
.Then(l => l.Literal("page")
|
||||
.Then(l => l.Argument("Page", Arguments.Integer(min: 1))
|
||||
.Then(l => l.Argument("Text", Arguments.GreedyString())
|
||||
.Executes(r => EditPage(r.Source, Arguments.GetInteger(r, "Page"), Arguments.GetString(r, "Text"))))))
|
||||
.Then(l => l.Literal("insert")
|
||||
.Then(l => l.Argument("Page", Arguments.Integer(min: 1))
|
||||
.Then(l => l.Argument("Text", Arguments.GreedyString())
|
||||
.Executes(r => InsertPage(r.Source, Arguments.GetInteger(r, "Page"), Arguments.GetString(r, "Text"))))))
|
||||
.Then(l => l.Literal("delete")
|
||||
.Then(l => l.Argument("Page", Arguments.Integer(min: 1))
|
||||
.Executes(r => DeletePage(r.Source, Arguments.GetInteger(r, "Page"))))))
|
||||
.Then(l => l.Literal("sign")
|
||||
.Then(l => l.Argument("Title", Arguments.GreedyString())
|
||||
.Executes(r => SignBook(r.Source, Arguments.GetString(r, "Title")))))
|
||||
.Then(l => l.Literal("_help")
|
||||
.Executes(r => GetUsage(r.Source, string.Empty))
|
||||
.Redirect(dispatcher.GetRoot().GetChild("help").GetChild(CmdName))));
|
||||
}
|
||||
|
||||
private int GetUsage(CmdResult r, string? cmd)
|
||||
{
|
||||
return r.SetAndReturn(cmd switch
|
||||
{
|
||||
"read" => Translations.cmd_book_help_read,
|
||||
"write" => Translations.cmd_book_help_write,
|
||||
"edit" => Translations.cmd_book_help_edit,
|
||||
"sign" => Translations.cmd_book_help_sign,
|
||||
_ => GetCmdDescTranslated()
|
||||
});
|
||||
}
|
||||
|
||||
private int ReadBook(CmdResult r, int? page)
|
||||
{
|
||||
McClient handler = CmdResult.currentHandler!;
|
||||
if (!EnsureInventory(r, handler))
|
||||
return -1;
|
||||
|
||||
if (!handler.TryGetHeldBookContent(out BookContent content))
|
||||
return r.SetAndReturn(CmdResult.Status.Fail, Translations.cmd_book_not_holding_book);
|
||||
|
||||
if (page is null && BookTuiHost.TryOpen(handler, BookHand.Main, editable: false))
|
||||
return r.SetAndReturn(CmdResult.Status.Done, Translations.cmd_book_tui_opened);
|
||||
|
||||
handler.Log.Info(FormatBook(content, page));
|
||||
return r.SetAndReturn(CmdResult.Status.Done);
|
||||
}
|
||||
|
||||
private int OpenEditor(CmdResult r)
|
||||
{
|
||||
McClient handler = CmdResult.currentHandler!;
|
||||
if (!EnsureWritable(r, handler, out _, Translations.cmd_book_cannot_edit_signed))
|
||||
return -1;
|
||||
|
||||
return BookTuiHost.TryOpen(handler, BookHand.Main, editable: true)
|
||||
? r.SetAndReturn(CmdResult.Status.Done, Translations.cmd_book_tui_opened)
|
||||
: r.SetAndReturn(CmdResult.Status.Fail, Translations.cmd_book_tui_required);
|
||||
}
|
||||
|
||||
private int WriteBook(CmdResult r, string text)
|
||||
{
|
||||
McClient handler = CmdResult.currentHandler!;
|
||||
if (!EnsureWritable(r, handler, out _, Translations.cmd_book_cannot_edit_signed))
|
||||
return -1;
|
||||
|
||||
IReadOnlyList<string> pages = SplitPages(text);
|
||||
if (!Validate(r, handler, pages, title: null))
|
||||
return -1;
|
||||
|
||||
return handler.SendBookEdit(pages)
|
||||
? r.SetAndReturn(CmdResult.Status.Done, Translations.cmd_book_write_sent)
|
||||
: r.SetAndReturn(CmdResult.Status.Fail, Translations.cmd_book_write_failed);
|
||||
}
|
||||
|
||||
private int WriteBookFromFile(CmdResult r, string path)
|
||||
{
|
||||
if (!File.Exists(path))
|
||||
return r.SetAndReturn(CmdResult.Status.Fail, string.Format(Translations.cmd_book_file_not_found, path));
|
||||
|
||||
return WriteBook(r, File.ReadAllText(path, Encoding.UTF8));
|
||||
}
|
||||
|
||||
private int EditPage(CmdResult r, int page, string text)
|
||||
{
|
||||
McClient handler = CmdResult.currentHandler!;
|
||||
if (!EnsureWritable(r, handler, out BookContent content, Translations.cmd_book_cannot_edit_signed))
|
||||
return -1;
|
||||
|
||||
List<string> pages = content.Pages.ToList();
|
||||
if (page > pages.Count)
|
||||
return r.SetAndReturn(CmdResult.Status.Fail, string.Format(Translations.cmd_book_page_out_of_range, page, pages.Count));
|
||||
|
||||
pages[page - 1] = DecodeInlineText(text);
|
||||
if (!Validate(r, handler, pages, title: null))
|
||||
return -1;
|
||||
|
||||
return handler.SendBookEdit(pages)
|
||||
? r.SetAndReturn(CmdResult.Status.Done, Translations.cmd_book_edit_sent)
|
||||
: r.SetAndReturn(CmdResult.Status.Fail, Translations.cmd_book_write_failed);
|
||||
}
|
||||
|
||||
private int InsertPage(CmdResult r, int page, string text)
|
||||
{
|
||||
McClient handler = CmdResult.currentHandler!;
|
||||
if (!EnsureWritable(r, handler, out BookContent content, Translations.cmd_book_cannot_edit_signed))
|
||||
return -1;
|
||||
|
||||
List<string> pages = content.Pages.ToList();
|
||||
if (page > pages.Count + 1)
|
||||
return r.SetAndReturn(CmdResult.Status.Fail, string.Format(Translations.cmd_book_page_out_of_range, page, pages.Count));
|
||||
|
||||
pages.Insert(page - 1, DecodeInlineText(text));
|
||||
if (!Validate(r, handler, pages, title: null))
|
||||
return -1;
|
||||
|
||||
return handler.SendBookEdit(pages)
|
||||
? r.SetAndReturn(CmdResult.Status.Done, Translations.cmd_book_edit_sent)
|
||||
: r.SetAndReturn(CmdResult.Status.Fail, Translations.cmd_book_write_failed);
|
||||
}
|
||||
|
||||
private int DeletePage(CmdResult r, int page)
|
||||
{
|
||||
McClient handler = CmdResult.currentHandler!;
|
||||
if (!EnsureWritable(r, handler, out BookContent content, Translations.cmd_book_cannot_edit_signed))
|
||||
return -1;
|
||||
|
||||
List<string> pages = content.Pages.ToList();
|
||||
if (page > pages.Count)
|
||||
return r.SetAndReturn(CmdResult.Status.Fail, string.Format(Translations.cmd_book_page_out_of_range, page, pages.Count));
|
||||
|
||||
pages.RemoveAt(page - 1);
|
||||
if (pages.Count == 0)
|
||||
pages.Add(string.Empty);
|
||||
|
||||
if (!Validate(r, handler, pages, title: null))
|
||||
return -1;
|
||||
|
||||
return handler.SendBookEdit(pages)
|
||||
? r.SetAndReturn(CmdResult.Status.Done, Translations.cmd_book_edit_sent)
|
||||
: r.SetAndReturn(CmdResult.Status.Fail, Translations.cmd_book_write_failed);
|
||||
}
|
||||
|
||||
private int SignBook(CmdResult r, string title)
|
||||
{
|
||||
McClient handler = CmdResult.currentHandler!;
|
||||
if (!EnsureWritable(r, handler, out BookContent content, Translations.cmd_book_already_signed))
|
||||
return -1;
|
||||
|
||||
string normalizedTitle = title.Trim();
|
||||
if (!Validate(r, handler, content.Pages, normalizedTitle))
|
||||
return -1;
|
||||
|
||||
return handler.SendBookEdit(content.Pages, normalizedTitle)
|
||||
? r.SetAndReturn(CmdResult.Status.Done, Translations.cmd_book_sign_sent)
|
||||
: r.SetAndReturn(CmdResult.Status.Fail, Translations.cmd_book_write_failed);
|
||||
}
|
||||
|
||||
private static bool EnsureInventory(CmdResult r, McClient handler)
|
||||
{
|
||||
if (handler.GetInventoryEnabled())
|
||||
return true;
|
||||
|
||||
r.SetAndReturn(CmdResult.Status.FailNeedInventory);
|
||||
return false;
|
||||
}
|
||||
|
||||
private static bool EnsureWritable(CmdResult r, McClient handler, out BookContent content, string signedBookMessage)
|
||||
{
|
||||
content = BookContent.EmptyWritable;
|
||||
if (!EnsureInventory(r, handler))
|
||||
return false;
|
||||
|
||||
Item? item = handler.GetHeldBook();
|
||||
if (!BookContentHelper.IsWritableBook(item))
|
||||
{
|
||||
r.SetAndReturn(CmdResult.Status.Fail, GetWritableBookFailureMessage(item, signedBookMessage));
|
||||
return false;
|
||||
}
|
||||
|
||||
return BookContentHelper.TryRead(item, out content);
|
||||
}
|
||||
|
||||
private static string GetWritableBookFailureMessage(Item? item, string signedBookMessage)
|
||||
{
|
||||
return BookContentHelper.TryRead(item, out BookContent content) && content.IsSigned
|
||||
? signedBookMessage
|
||||
: Translations.cmd_book_not_holding_writable;
|
||||
}
|
||||
|
||||
private static IReadOnlyList<string> SplitPages(string text)
|
||||
{
|
||||
return BookContentHelper.NormalizePages(DecodeInlineText(text).Split(PageDelimiter));
|
||||
}
|
||||
|
||||
private static string DecodeInlineText(string text)
|
||||
{
|
||||
return text.Replace("\\f", PageDelimiter.ToString(), StringComparison.Ordinal)
|
||||
.Replace("\\n", "\n", StringComparison.Ordinal);
|
||||
}
|
||||
|
||||
private static bool Validate(CmdResult r, McClient handler, IReadOnlyList<string> pages, string? title)
|
||||
{
|
||||
BookLimits limits = BookLimits.ForProtocol(handler.GetProtocolVersion());
|
||||
|
||||
if (pages.Count > limits.MaxPages)
|
||||
{
|
||||
r.SetAndReturn(CmdResult.Status.Fail, string.Format(Translations.cmd_book_too_many_pages, pages.Count, limits.MaxPages));
|
||||
return false;
|
||||
}
|
||||
|
||||
for (int i = 0; i < pages.Count; i++)
|
||||
{
|
||||
if (pages[i].Length > limits.MaxPageLength)
|
||||
{
|
||||
r.SetAndReturn(CmdResult.Status.Fail, string.Format(Translations.cmd_book_page_too_long, i + 1, pages[i].Length, limits.MaxPageLength));
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
if (title is not null && (title.Length == 0 || title.Length > limits.MaxTitleLength))
|
||||
{
|
||||
r.SetAndReturn(CmdResult.Status.Fail, string.Format(Translations.cmd_book_title_invalid, limits.MaxTitleLength));
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
private static string FormatBook(BookContent content, int? page)
|
||||
{
|
||||
StringBuilder sb = new();
|
||||
sb.AppendLine(content.IsSigned
|
||||
? string.Format(Translations.cmd_book_header_signed, content.Title ?? string.Empty, content.Author ?? string.Empty)
|
||||
: Translations.cmd_book_header_writable);
|
||||
|
||||
if (page is not null)
|
||||
{
|
||||
int index = page.Value - 1;
|
||||
if (index < 0 || index >= content.Pages.Count)
|
||||
return string.Format(Translations.cmd_book_page_out_of_range, page.Value, content.Pages.Count);
|
||||
|
||||
sb.AppendLine(string.Format(Translations.cmd_book_page_header, page.Value, content.Pages.Count));
|
||||
sb.Append(content.Pages[index]);
|
||||
return sb.ToString();
|
||||
}
|
||||
|
||||
for (int i = 0; i < content.Pages.Count; i++)
|
||||
{
|
||||
sb.AppendLine(string.Format(Translations.cmd_book_page_header, i + 1, content.Pages.Count));
|
||||
sb.AppendLine(content.Pages[i]);
|
||||
}
|
||||
|
||||
return sb.ToString().TrimEnd();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -84,7 +84,7 @@ namespace MinecraftClient.Commands
|
|||
else
|
||||
{
|
||||
ChatBot? bot = handler.GetLoadedChatBots().Find(bot => bot.GetType().Name.ToLower() == botName.ToLower());
|
||||
if (bot == null)
|
||||
if (bot is null)
|
||||
return r.SetAndReturn(CmdResult.Status.Fail, string.Format(Translations.cmd_bots_notfound, botName));
|
||||
else
|
||||
{
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
using System;
|
||||
using System;
|
||||
using System.Text;
|
||||
using Brigadier.NET;
|
||||
using Brigadier.NET.Builder;
|
||||
|
|
@ -92,7 +92,7 @@ namespace MinecraftClient.Commands
|
|||
sb.Append('\n');
|
||||
|
||||
sb.AppendLine(string.Format(Translations.cmd_chunk_current, current, current.ChunkX, current.ChunkZ));
|
||||
if (markedChunkPos != null)
|
||||
if (markedChunkPos is not null)
|
||||
{
|
||||
sb.Append(Translations.cmd_chunk_marked);
|
||||
if (pos.HasValue)
|
||||
|
|
@ -100,11 +100,15 @@ namespace MinecraftClient.Commands
|
|||
sb.AppendLine(string.Format(Translations.cmd_chunk_chunk_pos, markChunkX, markChunkZ)); ;
|
||||
}
|
||||
|
||||
int consoleHeight = Math.Max(Math.Max(Console.BufferHeight, Settings.Config.Main.Advanced.MinTerminalHeight) - 2, 25);
|
||||
int safeHeight;
|
||||
int safeWidth;
|
||||
try { safeHeight = Console.BufferHeight; } catch { safeHeight = 50; }
|
||||
try { safeWidth = Console.BufferWidth; } catch { safeWidth = 120; }
|
||||
int consoleHeight = Math.Max(Math.Max(safeHeight, Settings.Config.Main.Advanced.MinTerminalHeight) - 2, 25);
|
||||
if (consoleHeight % 2 == 0)
|
||||
--consoleHeight;
|
||||
|
||||
int consoleWidth = Math.Max(Math.Max(Console.BufferWidth, Settings.Config.Main.Advanced.MinTerminalWidth) / 2, 17);
|
||||
int consoleWidth = Math.Max(Math.Max(safeWidth, Settings.Config.Main.Advanced.MinTerminalWidth) / 2, 17);
|
||||
if (consoleWidth % 2 == 0)
|
||||
--consoleWidth;
|
||||
|
||||
|
|
@ -116,7 +120,7 @@ namespace MinecraftClient.Commands
|
|||
{
|
||||
for (int x = startX; x <= endX; ++x)
|
||||
{
|
||||
if (world[x, z] != null)
|
||||
if (world[x, z] is not null)
|
||||
{
|
||||
leftMost = Math.Min(leftMost, x);
|
||||
rightMost = Math.Max(rightMost, x);
|
||||
|
|
@ -180,7 +184,7 @@ namespace MinecraftClient.Commands
|
|||
}
|
||||
|
||||
// Try to include the marker chunk
|
||||
if (markedChunkPos != null &&
|
||||
if (markedChunkPos is not null &&
|
||||
(((Math.Max(bottomMost, markChunkZ) - Math.Min(topMost, markChunkZ) + 1) > consoleHeight) ||
|
||||
((Math.Max(rightMost, markChunkX) - Math.Min(leftMost, markChunkX) + 1) > consoleWidth)))
|
||||
sb.AppendLine(Translations.cmd_chunk_outside);
|
||||
|
|
@ -208,7 +212,7 @@ namespace MinecraftClient.Commands
|
|||
sb.Append("§§4"); // Marked chunk: background red
|
||||
|
||||
ChunkColumn? chunkColumn = world[x, z];
|
||||
if (chunkColumn == null)
|
||||
if (chunkColumn is null)
|
||||
sb.Append(chunkStatusStr[0]);
|
||||
else if (chunkColumn.FullyLoaded)
|
||||
sb.Append(chunkStatusStr[2]);
|
||||
|
|
@ -238,10 +242,10 @@ namespace MinecraftClient.Commands
|
|||
handler.Log.Info(Translations.cmd_chunk_for_debug);
|
||||
(int chunkX, int chunkZ) = markedChunkPos ?? new(pos!.Value.ChunkX, pos!.Value.ChunkZ);
|
||||
ChunkColumn? chunkColumn = handler.GetWorld()[chunkX, chunkZ];
|
||||
if (chunkColumn != null)
|
||||
if (chunkColumn is not null)
|
||||
chunkColumn.FullyLoaded = false;
|
||||
|
||||
if (chunkColumn == null)
|
||||
if (chunkColumn is null)
|
||||
return r.SetAndReturn(Status.Fail, "Fail: chunk dosen't exist!");
|
||||
else
|
||||
return r.SetAndReturn(Status.Done, string.Format("Successfully marked chunk ({0}, {1}) as loading.", chunkX, chunkZ));
|
||||
|
|
@ -258,10 +262,10 @@ namespace MinecraftClient.Commands
|
|||
handler.Log.Info(Translations.cmd_chunk_for_debug);
|
||||
(int chunkX, int chunkZ) = markedChunkPos ?? new(pos!.Value.ChunkX, pos!.Value.ChunkZ);
|
||||
ChunkColumn? chunkColumn = handler.GetWorld()[chunkX, chunkZ];
|
||||
if (chunkColumn != null)
|
||||
if (chunkColumn is not null)
|
||||
chunkColumn.FullyLoaded = false;
|
||||
|
||||
if (chunkColumn == null)
|
||||
if (chunkColumn is null)
|
||||
return r.SetAndReturn(Status.Fail, "Fail: chunk dosen't exist!");
|
||||
else
|
||||
return r.SetAndReturn(Status.Done, string.Format("Successfully marked chunk ({0}, {1}) as loaded.", chunkX, chunkZ));
|
||||
|
|
|
|||
45
MinecraftClient/Commands/ClearConsole.cs
Normal file
45
MinecraftClient/Commands/ClearConsole.cs
Normal file
|
|
@ -0,0 +1,45 @@
|
|||
using Brigadier.NET;
|
||||
using Brigadier.NET.Builder;
|
||||
using MinecraftClient.CommandHandler;
|
||||
|
||||
namespace MinecraftClient.Commands
|
||||
{
|
||||
public class ClearConsole : Command
|
||||
{
|
||||
public override string CmdName => "clear-console";
|
||||
public override string CmdUsage => "clear-console";
|
||||
public override string CmdDesc => Translations.cmd_clear_console_desc;
|
||||
|
||||
public override void RegisterCommand(CommandDispatcher<CmdResult> dispatcher)
|
||||
{
|
||||
dispatcher.Register(l => l.Literal("help")
|
||||
.Then(l => l.Literal(CmdName)
|
||||
.Executes(r => GetUsage(r.Source))
|
||||
)
|
||||
);
|
||||
|
||||
var clearConsole = dispatcher.Register(l => l.Literal(CmdName)
|
||||
.Executes(r => Execute(r.Source))
|
||||
.Then(l => l.Literal("_help")
|
||||
.Executes(r => GetUsage(r.Source))
|
||||
.Redirect(dispatcher.GetRoot().GetChild("help").GetChild(CmdName)))
|
||||
);
|
||||
|
||||
dispatcher.Register(l => l.Literal("cc")
|
||||
.Executes(r => Execute(r.Source))
|
||||
.Redirect(clearConsole)
|
||||
);
|
||||
}
|
||||
|
||||
private int GetUsage(CmdResult result)
|
||||
{
|
||||
return result.SetAndReturn(GetCmdDescTranslated());
|
||||
}
|
||||
|
||||
private int Execute(CmdResult result)
|
||||
{
|
||||
ConsoleIO.ClearConsole();
|
||||
return result.SetAndReturn(CmdResult.Status.Done);
|
||||
}
|
||||
}
|
||||
}
|
||||
49
MinecraftClient/Commands/ConsoleChat.cs
Normal file
49
MinecraftClient/Commands/ConsoleChat.cs
Normal file
|
|
@ -0,0 +1,49 @@
|
|||
using Brigadier.NET;
|
||||
using Brigadier.NET.Builder;
|
||||
using MinecraftClient.CommandHandler;
|
||||
|
||||
namespace MinecraftClient.Commands
|
||||
{
|
||||
public class ConsoleChat : Command
|
||||
{
|
||||
public override string CmdName => "console-chat";
|
||||
public override string CmdUsage => "console-chat [on|off]";
|
||||
public override string CmdDesc => Translations.cmd_console_chat_desc;
|
||||
|
||||
public override void RegisterCommand(CommandDispatcher<CmdResult> dispatcher)
|
||||
{
|
||||
dispatcher.Register(l => l.Literal("help")
|
||||
.Then(l => l.Literal(CmdName)
|
||||
.Executes(r => GetUsage(r.Source))
|
||||
)
|
||||
);
|
||||
|
||||
dispatcher.Register(l => l.Literal(CmdName)
|
||||
.Executes(r => SetChatVisibility(r.Source, null))
|
||||
.Then(l => l.Literal("on")
|
||||
.Executes(r => SetChatVisibility(r.Source, true)))
|
||||
.Then(l => l.Literal("off")
|
||||
.Executes(r => SetChatVisibility(r.Source, false)))
|
||||
.Then(l => l.Literal("_help")
|
||||
.Executes(r => GetUsage(r.Source))
|
||||
.Redirect(dispatcher.GetRoot().GetChild("help").GetChild(CmdName)))
|
||||
);
|
||||
}
|
||||
|
||||
private int GetUsage(CmdResult result)
|
||||
{
|
||||
return result.SetAndReturn(GetCmdDescTranslated());
|
||||
}
|
||||
|
||||
private int SetChatVisibility(CmdResult result, bool? visible)
|
||||
{
|
||||
ConsoleIO.ChatVisible = visible ?? !ConsoleIO.ChatVisible;
|
||||
|
||||
return result.SetAndReturn(
|
||||
CmdResult.Status.Done,
|
||||
ConsoleIO.ChatVisible
|
||||
? Translations.cmd_console_chat_state_on
|
||||
: Translations.cmd_console_chat_state_off);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -1,13 +1,17 @@
|
|||
using Brigadier.NET;
|
||||
using System;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using Brigadier.NET;
|
||||
using Brigadier.NET.Builder;
|
||||
using MinecraftClient.CommandHandler;
|
||||
using MinecraftClient.Scripting;
|
||||
|
||||
namespace MinecraftClient.Commands
|
||||
{
|
||||
public class Debug : Command
|
||||
{
|
||||
public override string CmdName { get { return "debug"; } }
|
||||
public override string CmdUsage { get { return "debug [on|off]"; } }
|
||||
public override string CmdUsage { get { return "debug [on|off|state]"; } }
|
||||
public override string CmdDesc { get { return Translations.cmd_debug_desc; } }
|
||||
|
||||
public override void RegisterCommand(CommandDispatcher<CmdResult> dispatcher)
|
||||
|
|
@ -24,6 +28,8 @@ namespace MinecraftClient.Commands
|
|||
.Executes(r => SetDebugMode(r.Source, false, true)))
|
||||
.Then(l => l.Literal("off")
|
||||
.Executes(r => SetDebugMode(r.Source, false, false)))
|
||||
.Then(l => l.Literal("state")
|
||||
.Executes(r => ShowState(r.Source)))
|
||||
.Then(l => l.Literal("_help")
|
||||
.Executes(r => GetUsage(r.Source, string.Empty))
|
||||
.Redirect(dispatcher.GetRoot().GetChild("help").GetChild(CmdName)))
|
||||
|
|
@ -42,15 +48,57 @@ namespace MinecraftClient.Commands
|
|||
|
||||
private int SetDebugMode(CmdResult r, bool flip, bool mode = false)
|
||||
{
|
||||
McClient handler = CmdResult.currentHandler!;
|
||||
|
||||
if (flip)
|
||||
Settings.Config.Logging.DebugMessages = !Settings.Config.Logging.DebugMessages;
|
||||
else
|
||||
Settings.Config.Logging.DebugMessages = mode;
|
||||
|
||||
handler.Log.DebugEnabled = Settings.Config.Logging.DebugMessages;
|
||||
|
||||
if (Settings.Config.Logging.DebugMessages)
|
||||
return r.SetAndReturn(CmdResult.Status.Done, Translations.cmd_debug_state_on);
|
||||
else
|
||||
return r.SetAndReturn(CmdResult.Status.Done, Translations.cmd_debug_state_off);
|
||||
}
|
||||
|
||||
private int ShowState(CmdResult r)
|
||||
{
|
||||
McClient handler = CmdResult.currentHandler!;
|
||||
var sb = new StringBuilder();
|
||||
|
||||
sb.AppendLine($"§e=== {Translations.cmd_debug_state_header} ===");
|
||||
sb.AppendLine($"§7{Translations.cmd_debug_state_server,-10}§f{handler.GetServerHost()}:{handler.GetServerPort()}");
|
||||
sb.AppendLine($"§7{Translations.cmd_debug_state_username,-10}§f{handler.GetUsername()}");
|
||||
sb.AppendLine($"§7{Translations.cmd_debug_state_protocol,-10}§f{handler.GetProtocolVersion()}");
|
||||
sb.AppendLine($"§7{Translations.cmd_debug_state_gamemode,-10}§f{handler.GetGamemode()}");
|
||||
sb.AppendLine($"§7{Translations.cmd_debug_state_health,-10}§f{handler.GetHealth():F1}");
|
||||
sb.AppendLine($"§7{Translations.cmd_debug_state_food,-10}§f{handler.GetSaturation()}");
|
||||
|
||||
var loc = handler.GetCurrentLocation();
|
||||
sb.AppendLine($"§7{Translations.cmd_debug_state_location,-10}§f{loc.X:F2}, {loc.Y:F2}, {loc.Z:F2}");
|
||||
|
||||
sb.AppendLine($"§7{Translations.cmd_debug_state_tps,-10}§f{handler.GetServerTPS():F1}");
|
||||
|
||||
sb.AppendLine($"§7{Translations.cmd_debug_state_console,-10}§f{(ConsoleIO.Backend?.GetType().Name ?? "null")}");
|
||||
|
||||
var features = new StringBuilder();
|
||||
features.Append(handler.GetTerrainEnabled() ? "§aTerrain " : "§8Terrain ");
|
||||
features.Append(handler.GetInventoryEnabled() ? "§aInventory " : "§8Inventory ");
|
||||
features.Append(handler.GetEntityHandlingEnabled() ? "§aEntity " : "§8Entity ");
|
||||
sb.AppendLine($"§7{Translations.cmd_debug_state_features,-10}{features}");
|
||||
|
||||
sb.AppendLine($"§7{Translations.cmd_debug_state_debug,-10}§f{(Settings.Config.Logging.DebugMessages ? "§aON" : "§cOFF")}");
|
||||
|
||||
var bots = handler.GetLoadedChatBots();
|
||||
sb.AppendLine($"§7{Translations.cmd_debug_state_bots} ({bots.Count}): §f{string.Join(", ", bots.Select(b => b.GetType().Name))}");
|
||||
|
||||
var players = handler.GetOnlinePlayers();
|
||||
sb.AppendLine($"§7{Translations.cmd_debug_state_players,-10}§f{string.Format(Translations.cmd_debug_state_online, players.Length)}");
|
||||
|
||||
handler.Log.Info(sb.ToString());
|
||||
return r.SetAndReturn(CmdResult.Status.Done);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
108
MinecraftClient/Commands/Dialog.cs
Normal file
108
MinecraftClient/Commands/Dialog.cs
Normal file
|
|
@ -0,0 +1,108 @@
|
|||
using Brigadier.NET;
|
||||
using Brigadier.NET.Builder;
|
||||
using MinecraftClient.CommandHandler;
|
||||
using MinecraftClient.Dialogs;
|
||||
using MinecraftClient.Tui;
|
||||
|
||||
namespace MinecraftClient.Commands;
|
||||
|
||||
public class Dialog : Command
|
||||
{
|
||||
public override string CmdName => "dialog";
|
||||
public override string CmdUsage => Translations.cmd_dialog_usage;
|
||||
public override string CmdDesc => Translations.cmd_dialog_desc;
|
||||
|
||||
public override void RegisterCommand(CommandDispatcher<CmdResult> dispatcher)
|
||||
{
|
||||
dispatcher.Register(l => l.Literal("help")
|
||||
.Then(l => l.Literal(CmdName)
|
||||
.Executes(r => GetUsage(r.Source))));
|
||||
|
||||
dispatcher.Register(l => l.Literal(CmdName)
|
||||
.Executes(r => Show(r.Source))
|
||||
.Then(l => l.Literal("show")
|
||||
.Executes(r => Show(r.Source)))
|
||||
.Then(l => l.Literal("open")
|
||||
.Executes(r => Open(r.Source)))
|
||||
.Then(l => l.Literal("set")
|
||||
.Then(l => l.Argument("Input", Arguments.String())
|
||||
.Then(l => l.Argument("Value", Arguments.GreedyString())
|
||||
.Executes(r => SetInput(r.Source, Arguments.GetString(r, "Input"), Arguments.GetString(r, "Value"))))))
|
||||
.Then(l => l.Literal("input")
|
||||
.Then(l => l.Argument("Input", Arguments.String())
|
||||
.Then(l => l.Argument("Value", Arguments.GreedyString())
|
||||
.Executes(r => SetInput(r.Source, Arguments.GetString(r, "Input"), Arguments.GetString(r, "Value"))))))
|
||||
.Then(l => l.Literal("click")
|
||||
.Then(l => l.Argument("Index", Arguments.Integer(min: 1))
|
||||
.Executes(r => Click(r.Source, Arguments.GetInteger(r, "Index")))))
|
||||
.Then(l => l.Literal("click-label")
|
||||
.Then(l => l.Argument("Label", Arguments.GreedyString())
|
||||
.Executes(r => ClickLabel(r.Source, Arguments.GetString(r, "Label")))))
|
||||
.Then(l => l.Literal("cancel")
|
||||
.Executes(r => Cancel(r.Source)))
|
||||
.Then(l => l.Literal("dismiss")
|
||||
.Executes(r => Dismiss(r.Source)))
|
||||
.Then(l => l.Literal("_help")
|
||||
.Executes(r => GetUsage(r.Source))
|
||||
.Redirect(dispatcher.GetRoot().GetChild("help").GetChild(CmdName))));
|
||||
}
|
||||
|
||||
private int GetUsage(CmdResult r) => r.SetAndReturn(GetCmdDescTranslated());
|
||||
|
||||
private static int Show(CmdResult r)
|
||||
{
|
||||
var handler = CmdResult.currentHandler!;
|
||||
var current = handler.Dialogs.Current;
|
||||
if (current is null)
|
||||
return r.SetAndReturn(CmdResult.Status.Fail, Translations.dialog_none);
|
||||
|
||||
ConsoleIO.WriteLineFormatted(DialogFormatter.Render(current), acceptnewlines: true);
|
||||
return r.SetAndReturn(CmdResult.Status.Done);
|
||||
}
|
||||
|
||||
private static int Open(CmdResult r)
|
||||
{
|
||||
var handler = CmdResult.currentHandler!;
|
||||
var current = handler.Dialogs.Current;
|
||||
if (current is null)
|
||||
return r.SetAndReturn(CmdResult.Status.Fail, Translations.dialog_none);
|
||||
|
||||
if (ConsoleIO.Backend is not TuiConsoleBackend)
|
||||
return r.SetAndReturn(CmdResult.Status.Fail, Translations.dialog_tui_unavailable);
|
||||
|
||||
return DialogTuiHost.TryOpen(handler, current, force: true)
|
||||
? r.SetAndReturn(CmdResult.Status.Done, Translations.dialog_tui_opened)
|
||||
: r.SetAndReturn(CmdResult.Status.Fail, Translations.dialog_tui_unavailable);
|
||||
}
|
||||
|
||||
private static int SetInput(CmdResult r, string key, string value)
|
||||
{
|
||||
var result = CmdResult.currentHandler!.Dialogs.SetInput(key, value);
|
||||
return r.SetAndReturn(result.Success ? CmdResult.Status.Done : CmdResult.Status.Fail, result.Message);
|
||||
}
|
||||
|
||||
private static int Click(CmdResult r, int index)
|
||||
{
|
||||
var result = CmdResult.currentHandler!.Dialogs.Click(index);
|
||||
return r.SetAndReturn(result.Success ? CmdResult.Status.Done : CmdResult.Status.Fail, result.Message);
|
||||
}
|
||||
|
||||
private static int ClickLabel(CmdResult r, string label)
|
||||
{
|
||||
var result = CmdResult.currentHandler!.Dialogs.ClickLabel(label);
|
||||
return r.SetAndReturn(result.Success ? CmdResult.Status.Done : CmdResult.Status.Fail, result.Message);
|
||||
}
|
||||
|
||||
private static int Cancel(CmdResult r)
|
||||
{
|
||||
var result = CmdResult.currentHandler!.Dialogs.Cancel();
|
||||
return r.SetAndReturn(result.Success ? CmdResult.Status.Done : CmdResult.Status.Fail, result.Message);
|
||||
}
|
||||
|
||||
private static int Dismiss(CmdResult r)
|
||||
{
|
||||
var result = CmdResult.currentHandler!.Dialogs.Dismiss();
|
||||
DialogTuiHost.CloseCurrent();
|
||||
return r.SetAndReturn(result.Success ? CmdResult.Status.Done : CmdResult.Status.Fail, result.Message);
|
||||
}
|
||||
}
|
||||
|
|
@ -22,6 +22,7 @@ namespace MinecraftClient.Commands
|
|||
);
|
||||
|
||||
dispatcher.Register(l => l.Literal(CmdName)
|
||||
// TODO Get blockFace direction from arguments
|
||||
.Executes(r => DigLookAt(r.Source))
|
||||
.Then(l => l.Argument("Duration", Arguments.Double())
|
||||
.Executes(r => DigLookAt(r.Source, Arguments.GetDouble(r, "Duration"))))
|
||||
|
|
@ -58,7 +59,7 @@ namespace MinecraftClient.Commands
|
|||
Block block = handler.GetWorld().GetBlock(blockToBreak);
|
||||
if (block.Type == Material.Air)
|
||||
return r.SetAndReturn(Status.Fail, Translations.cmd_dig_no_block);
|
||||
else if (handler.DigBlock(blockToBreak, duration: duration))
|
||||
else if (handler.DigBlock(blockToBreak, Direction.Down, duration: duration))
|
||||
{
|
||||
blockToBreak = blockToBreak.ToCenter();
|
||||
return r.SetAndReturn(Status.Done, string.Format(Translations.cmd_dig_dig, blockToBreak.X, blockToBreak.Y, blockToBreak.Z, block.GetTypeString()));
|
||||
|
|
@ -78,7 +79,7 @@ namespace MinecraftClient.Commands
|
|||
return r.SetAndReturn(Status.Fail, Translations.cmd_dig_too_far);
|
||||
else if (block.Type == Material.Air)
|
||||
return r.SetAndReturn(Status.Fail, Translations.cmd_dig_no_block);
|
||||
else if (handler.DigBlock(blockLoc, lookAtBlock: false, duration: duration))
|
||||
else if (handler.DigBlock(blockLoc, Direction.Down, lookAtBlock: false, duration: duration))
|
||||
return r.SetAndReturn(Status.Done, string.Format(Translations.cmd_dig_dig, blockLoc.X, blockLoc.Y, blockLoc.Z, block.GetTypeString()));
|
||||
else
|
||||
return r.SetAndReturn(Status.Fail, Translations.cmd_dig_fail);
|
||||
|
|
|
|||
67
MinecraftClient/Commands/EffectsCommand.cs
Normal file
67
MinecraftClient/Commands/EffectsCommand.cs
Normal file
|
|
@ -0,0 +1,67 @@
|
|||
using System.Linq;
|
||||
using System.Text;
|
||||
using Brigadier.NET;
|
||||
using Brigadier.NET.Builder;
|
||||
using MinecraftClient.CommandHandler;
|
||||
|
||||
namespace MinecraftClient.Commands
|
||||
{
|
||||
public class EffectsCommand : Command
|
||||
{
|
||||
public override string CmdName { get { return "effects"; } }
|
||||
public override string CmdUsage { get { return "effects"; } }
|
||||
public override string CmdDesc { get { return Translations.cmd_effects_desc; } }
|
||||
|
||||
public override void RegisterCommand(CommandDispatcher<CmdResult> dispatcher)
|
||||
{
|
||||
dispatcher.Register(l => l.Literal("help")
|
||||
.Then(l => l.Literal(CmdName)
|
||||
.Executes(r => GetUsage(r.Source, string.Empty))
|
||||
)
|
||||
);
|
||||
|
||||
dispatcher.Register(l => l.Literal(CmdName)
|
||||
.Executes(r => ShowEffects(r.Source))
|
||||
.Then(l => l.Literal("_help")
|
||||
.Executes(r => GetUsage(r.Source, string.Empty))
|
||||
.Redirect(dispatcher.GetRoot().GetChild("help").GetChild(CmdName)))
|
||||
);
|
||||
}
|
||||
|
||||
private int GetUsage(CmdResult r, string? cmd)
|
||||
{
|
||||
return r.SetAndReturn(cmd switch
|
||||
{
|
||||
#pragma warning disable format // @formatter:off
|
||||
_ => GetCmdDescTranslated(),
|
||||
#pragma warning restore format // @formatter:on
|
||||
});
|
||||
}
|
||||
|
||||
private int ShowEffects(CmdResult r)
|
||||
{
|
||||
McClient handler = CmdResult.currentHandler!;
|
||||
if (!handler.GetEntityHandlingEnabled())
|
||||
return r.SetAndReturn(CmdResult.Status.FailNeedEntity);
|
||||
|
||||
var effects = handler.GetPlayerEffects()
|
||||
.Values
|
||||
.Where(effectData => !effectData.IsExpired)
|
||||
.OrderBy(effectData => effectData.Effect)
|
||||
.ToArray();
|
||||
|
||||
if (effects.Length == 0)
|
||||
return r.SetAndReturn(CmdResult.Status.Done, Translations.cmd_effects_none);
|
||||
|
||||
StringBuilder response = new();
|
||||
response.AppendLine(Translations.cmd_effects_header);
|
||||
foreach (var effectData in effects)
|
||||
{
|
||||
response.AppendLine(string.Format(Translations.cmd_effects_entry,
|
||||
effectData.GetDisplayName(), effectData.GetRemainingDurationText()));
|
||||
}
|
||||
|
||||
return r.SetAndReturn(CmdResult.Status.Done, response.ToString().TrimEnd());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -66,7 +66,7 @@ namespace MinecraftClient.Commands
|
|||
}
|
||||
}
|
||||
|
||||
if (enchantingTable == null)
|
||||
if (enchantingTable is null)
|
||||
return r.SetAndReturn(CmdResult.Status.Fail, Translations.cmd_enchant_enchanting_table_not_opened);
|
||||
|
||||
int[] emptySlots = enchantingTable.GetEmpytSlots();
|
||||
|
|
@ -84,7 +84,7 @@ namespace MinecraftClient.Commands
|
|||
|
||||
EnchantmentData? enchantment = handler.GetLastEnchantments();
|
||||
|
||||
if (enchantment == null)
|
||||
if (enchantment is null)
|
||||
return r.SetAndReturn(CmdResult.Status.Fail, Translations.cmd_enchant_no_enchantments);
|
||||
|
||||
short requiredLevel = slotId switch
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
using System;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Diagnostics.CodeAnalysis;
|
||||
using System.Text;
|
||||
|
|
@ -210,7 +210,7 @@ namespace MinecraftClient.Commands
|
|||
Item item = entity.Item;
|
||||
string location = $"X:{Math.Round(entity.Location.X, 2)}, Y:{Math.Round(entity.Location.Y, 2)}, Z:{Math.Round(entity.Location.Z, 2)}";
|
||||
|
||||
if (type == EntityType.Item || type == EntityType.ItemFrame || type == EntityType.EyeOfEnder || type == EntityType.Egg || type == EntityType.EnderPearl || type == EntityType.Potion || type == EntityType.Fireball || type == EntityType.FireworkRocket)
|
||||
if (type == EntityType.Item || type == EntityType.ItemFrame || type == EntityType.EyeOfEnder || type == EntityType.Egg || type == EntityType.EnderPearl || type == EntityType.Potion || type == EntityType.SplashPotion || type == EntityType.LingeringPotion || type == EntityType.Fireball || type == EntityType.FireworkRocket)
|
||||
return $" #{id}: {Translations.cmd_entityCmd_type}: {entity.GetTypeString()}, {Translations.cmd_entityCmd_item}: {item.GetTypeString()}, {Translations.cmd_entityCmd_location}: {location}";
|
||||
else if (type == EntityType.Player && !string.IsNullOrEmpty(nickname))
|
||||
return $" #{id}: {Translations.cmd_entityCmd_type}: {entity.GetTypeString()}, {Translations.cmd_entityCmd_nickname}: §8{nickname}§8, {Translations.cmd_entityCmd_latency}: {latency}, {Translations.cmd_entityCmd_health}: {health}, {Translations.cmd_entityCmd_pose}: {pose}, {Translations.cmd_entityCmd_location}: {location}";
|
||||
|
|
@ -251,7 +251,7 @@ namespace MinecraftClient.Commands
|
|||
{
|
||||
sb.Append($"\n [MCC] {Translations.cmd_entityCmd_latency}: {latency}");
|
||||
}
|
||||
else if (type == EntityType.Item || type == EntityType.ItemFrame || type == Mapping.EntityType.EyeOfEnder || type == Mapping.EntityType.Egg || type == Mapping.EntityType.EnderPearl || type == Mapping.EntityType.Potion || type == Mapping.EntityType.Fireball || type == Mapping.EntityType.FireworkRocket)
|
||||
else if (type == EntityType.Item || type == EntityType.ItemFrame || type == Mapping.EntityType.EyeOfEnder || type == Mapping.EntityType.Egg || type == Mapping.EntityType.EnderPearl || type == Mapping.EntityType.Potion || type == Mapping.EntityType.SplashPotion || type == Mapping.EntityType.LingeringPotion || type == Mapping.EntityType.Fireball || type == Mapping.EntityType.FireworkRocket)
|
||||
{
|
||||
string? displayName = item.DisplayName;
|
||||
if (string.IsNullOrEmpty(displayName))
|
||||
|
|
@ -260,20 +260,20 @@ namespace MinecraftClient.Commands
|
|||
sb.Append($"\n [MCC] {Translations.cmd_entityCmd_item}: {item.GetTypeString()} x{item.Count} - {displayName}§8");
|
||||
}
|
||||
|
||||
if (entity.Equipment.Count >= 1 && entity.Equipment != null)
|
||||
if (entity.Equipment is not null && entity.Equipment.Count >= 1)
|
||||
{
|
||||
sb.Append($"\n [MCC] {Translations.cmd_entityCmd_equipment}:");
|
||||
if (entity.Equipment.ContainsKey(0) && entity.Equipment[0] != null)
|
||||
if (entity.Equipment.ContainsKey(0) && entity.Equipment[0] is not null)
|
||||
sb.Append($"\n [MCC] {Translations.cmd_entityCmd_mainhand}: {entity.Equipment[0].GetTypeString()} x{entity.Equipment[0].Count}");
|
||||
if (entity.Equipment.ContainsKey(1) && entity.Equipment[1] != null)
|
||||
if (entity.Equipment.ContainsKey(1) && entity.Equipment[1] is not null)
|
||||
sb.Append($"\n [MCC] {Translations.cmd_entityCmd_offhand}: {entity.Equipment[1].GetTypeString()} x{entity.Equipment[1].Count}");
|
||||
if (entity.Equipment.ContainsKey(5) && entity.Equipment[5] != null)
|
||||
if (entity.Equipment.ContainsKey(5) && entity.Equipment[5] is not null)
|
||||
sb.Append($"\n [MCC] {Translations.cmd_entityCmd_helmet}: {entity.Equipment[5].GetTypeString()} x{entity.Equipment[5].Count}");
|
||||
if (entity.Equipment.ContainsKey(4) && entity.Equipment[4] != null)
|
||||
if (entity.Equipment.ContainsKey(4) && entity.Equipment[4] is not null)
|
||||
sb.Append($"\n [MCC] {Translations.cmd_entityCmd_chestplate}: {entity.Equipment[4].GetTypeString()} x{entity.Equipment[4].Count}");
|
||||
if (entity.Equipment.ContainsKey(3) && entity.Equipment[3] != null)
|
||||
if (entity.Equipment.ContainsKey(3) && entity.Equipment[3] is not null)
|
||||
sb.Append($"\n [MCC] {Translations.cmd_entityCmd_leggings}: {entity.Equipment[3].GetTypeString()} x{entity.Equipment[3].Count}");
|
||||
if (entity.Equipment.ContainsKey(2) && entity.Equipment[2] != null)
|
||||
if (entity.Equipment.ContainsKey(2) && entity.Equipment[2] is not null)
|
||||
sb.Append($"\n [MCC] {Translations.cmd_entityCmd_boots}: {entity.Equipment[2].GetTypeString()} x{entity.Equipment[2].Count}");
|
||||
}
|
||||
|
||||
|
|
@ -317,7 +317,7 @@ namespace MinecraftClient.Commands
|
|||
bool shouldInteractAt = entity.Type == EntityType.ArmorStand ||
|
||||
entity.Type == EntityType.ChestMinecart ||
|
||||
entity.Type == EntityType.ChestBoat;
|
||||
|
||||
|
||||
handler.InteractEntity(entity.ID, shouldInteractAt ? InteractType.InteractAt : InteractType.Interact);
|
||||
return Translations.cmd_entityCmd_used;
|
||||
case ActionType.List:
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
using System;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
|
|
@ -6,6 +6,7 @@ using Brigadier.NET;
|
|||
using Brigadier.NET.Builder;
|
||||
using MinecraftClient.CommandHandler;
|
||||
using MinecraftClient.Inventory;
|
||||
using MinecraftClient.Tui;
|
||||
|
||||
namespace MinecraftClient.Commands
|
||||
{
|
||||
|
|
@ -22,6 +23,8 @@ namespace MinecraftClient.Commands
|
|||
.Executes(r => GetUsage(r.Source, string.Empty))
|
||||
.Then(l => l.Literal("list")
|
||||
.Executes(r => GetUsage(r.Source, "list")))
|
||||
.Then(l => l.Literal("open")
|
||||
.Executes(r => GetUsage(r.Source, "open")))
|
||||
.Then(l => l.Literal("close")
|
||||
.Executes(r => GetUsage(r.Source, "close")))
|
||||
.Then(l => l.Literal("click")
|
||||
|
|
@ -59,6 +62,9 @@ namespace MinecraftClient.Commands
|
|||
.Then(l => l.Argument("Count", Arguments.Integer(0, 64))
|
||||
.Executes(r => SearchItem(r.Source, MccArguments.GetItemType(r, "ItemType"), Arguments.GetInteger(r, "Count"))))))
|
||||
.Then(l => l.Argument("InventoryId", MccArguments.InventoryId())
|
||||
.Executes(r => DoOpenOrList(r.Source, Arguments.GetInteger(r, "InventoryId")))
|
||||
.Then(l => l.Literal("open")
|
||||
.Executes(r => DoOpenTui(r.Source, Arguments.GetInteger(r, "InventoryId"))))
|
||||
.Then(l => l.Literal("close")
|
||||
.Executes(r => DoCloseAction(r.Source, Arguments.GetInteger(r, "InventoryId"))))
|
||||
.Then(l => l.Literal("list")
|
||||
|
|
@ -113,6 +119,7 @@ namespace MinecraftClient.Commands
|
|||
return r.SetAndReturn(cmd switch
|
||||
{
|
||||
#pragma warning disable format // @formatter:off
|
||||
"open" => Translations.cmd_inventory_help_open + usageStr + "/inventory <id> open",
|
||||
"list" => Translations.cmd_inventory_help_list + usageStr + "/inventory <player|container|<id>> list",
|
||||
"close" => Translations.cmd_inventory_help_close + usageStr + "/inventory <player|container|<id>> close",
|
||||
"click" => Translations.cmd_inventory_help_click + usageStr + "/inventory <player|container|<id>> click <slot> [left|right|middle|shift|shiftright]\nDefault is left click",
|
||||
|
|
@ -276,7 +283,7 @@ namespace MinecraftClient.Commands
|
|||
}
|
||||
|
||||
Container? inventory = handler.GetInventory(inventoryId.Value);
|
||||
if (inventory == null)
|
||||
if (inventory is null)
|
||||
return r.SetAndReturn(CmdResult.Status.Fail, string.Format(Translations.cmd_inventory_not_exist, inventoryId));
|
||||
|
||||
if (handler.CloseInventory(inventoryId.Value))
|
||||
|
|
@ -299,7 +306,7 @@ namespace MinecraftClient.Commands
|
|||
}
|
||||
|
||||
Container? inventory = handler.GetInventory(inventoryId.Value);
|
||||
if (inventory == null)
|
||||
if (inventory is null)
|
||||
return r.SetAndReturn(CmdResult.Status.Fail, string.Format(Translations.cmd_inventory_not_exist, inventoryId));
|
||||
|
||||
StringBuilder response = new();
|
||||
|
|
@ -307,7 +314,7 @@ namespace MinecraftClient.Commands
|
|||
response.AppendLine(String.Format(" #{0} - {1}§8", inventoryId, inventory.Title));
|
||||
|
||||
string? asciiArt = inventory.Type.GetAsciiArt();
|
||||
if (asciiArt != null && Settings.Config.Main.Advanced.ShowInventoryLayout)
|
||||
if (asciiArt is not null && Settings.Config.Main.Advanced.ShowInventoryLayout)
|
||||
response.AppendLine(asciiArt);
|
||||
|
||||
int selectedHotbar = handler.GetCurrentSlot() + 1;
|
||||
|
|
@ -342,7 +349,7 @@ namespace MinecraftClient.Commands
|
|||
}
|
||||
|
||||
Container? inventory = handler.GetInventory(inventoryId.Value);
|
||||
if (inventory == null)
|
||||
if (inventory is null)
|
||||
return r.SetAndReturn(CmdResult.Status.Fail, string.Format(Translations.cmd_inventory_not_exist, inventoryId));
|
||||
|
||||
string keyName = actionType switch
|
||||
|
|
@ -373,7 +380,7 @@ namespace MinecraftClient.Commands
|
|||
}
|
||||
|
||||
Container? inventory = handler.GetInventory(inventoryId.Value);
|
||||
if (inventory == null)
|
||||
if (inventory is null)
|
||||
return r.SetAndReturn(CmdResult.Status.Fail, string.Format(Translations.cmd_inventory_not_exist, inventoryId));
|
||||
|
||||
// check item exist
|
||||
|
|
@ -394,6 +401,61 @@ namespace MinecraftClient.Commands
|
|||
}
|
||||
|
||||
|
||||
private int DoOpenOrList(CmdResult r, int inventoryId)
|
||||
{
|
||||
if (ConsoleIO.Backend is TuiConsoleBackend)
|
||||
return DoOpenTui(r, inventoryId);
|
||||
return DoListAction(r, inventoryId);
|
||||
}
|
||||
|
||||
private int DoOpenTui(CmdResult r, int inventoryId)
|
||||
{
|
||||
McClient handler = CmdResult.currentHandler!;
|
||||
|
||||
if (!handler.GetInventoryEnabled())
|
||||
return r.SetAndReturn(CmdResult.Status.FailNeedInventory);
|
||||
|
||||
if (ConsoleIO.Backend is not TuiConsoleBackend)
|
||||
{
|
||||
handler.Log.Warn(Translations.cmd_inventory_tui_only);
|
||||
return r.SetAndReturn(CmdResult.Status.Fail);
|
||||
}
|
||||
|
||||
if (InventoryTuiHost.IsRunning)
|
||||
{
|
||||
handler.Log.Warn(Translations.cmd_inventory_tui_already_running);
|
||||
return r.SetAndReturn(CmdResult.Status.Fail);
|
||||
}
|
||||
|
||||
var container = handler.GetInventory(inventoryId);
|
||||
if (container == null)
|
||||
{
|
||||
string msg = string.Format(Translations.cmd_inventory_not_exist, inventoryId);
|
||||
handler.Log.Warn(msg);
|
||||
return r.SetAndReturn(CmdResult.Status.Fail, msg);
|
||||
}
|
||||
|
||||
if (!Tui.ContainerViewBase.HasTuiSupport(container.Type))
|
||||
{
|
||||
handler.Log.Warn(string.Format(Translations.cmd_inventory_tui_unsupported_container, inventoryId));
|
||||
return r.SetAndReturn(CmdResult.Status.Fail);
|
||||
}
|
||||
|
||||
handler.Log.Info(string.Format(Translations.cmd_inventory_tui_opening, inventoryId));
|
||||
|
||||
bool success = InventoryTuiHost.Launch(handler, inventoryId);
|
||||
if (success)
|
||||
{
|
||||
handler.Log.Info(Translations.cmd_inventory_tui_opened);
|
||||
return r.SetAndReturn(CmdResult.Status.Done);
|
||||
}
|
||||
else
|
||||
{
|
||||
handler.Log.Warn(Translations.cmd_inventory_tui_launch_failed);
|
||||
return r.SetAndReturn(CmdResult.Status.Fail);
|
||||
}
|
||||
}
|
||||
|
||||
#region Methods for commands help
|
||||
|
||||
private static string GetAvailableActions()
|
||||
|
|
|
|||
|
|
@ -92,6 +92,7 @@ namespace MinecraftClient.Commands
|
|||
return r.SetAndReturn(Status.FailNeedTerrain);
|
||||
|
||||
handler.UpdateLocation(handler.GetCurrentLocation(), direction);
|
||||
handler.SendLocationUpdate();
|
||||
return r.SetAndReturn(Status.Done, "Looking " + direction.ToString());
|
||||
}
|
||||
|
||||
|
|
@ -102,6 +103,7 @@ namespace MinecraftClient.Commands
|
|||
return r.SetAndReturn(Status.FailNeedTerrain);
|
||||
|
||||
handler.UpdateLocation(handler.GetCurrentLocation(), yaw, pitch);
|
||||
handler.SendLocationUpdate();
|
||||
return r.SetAndReturn(Status.Done, string.Format(Translations.cmd_look_at, yaw.ToString("0.00"), pitch.ToString("0.00")));
|
||||
}
|
||||
|
||||
|
|
@ -113,6 +115,7 @@ namespace MinecraftClient.Commands
|
|||
|
||||
Location current = handler.GetCurrentLocation();
|
||||
handler.UpdateLocation(current, location);
|
||||
handler.SendLocationUpdate();
|
||||
return r.SetAndReturn(Status.Done, string.Format(Translations.cmd_look_block, location));
|
||||
}
|
||||
}
|
||||
|
|
|
|||
284
MinecraftClient/Commands/Minimap.cs
Normal file
284
MinecraftClient/Commands/Minimap.cs
Normal file
|
|
@ -0,0 +1,284 @@
|
|||
using System;
|
||||
using Brigadier.NET;
|
||||
using Brigadier.NET.Builder;
|
||||
using MinecraftClient.CommandHandler;
|
||||
using MinecraftClient.Tui;
|
||||
using Avalonia.Threading;
|
||||
using static MinecraftClient.CommandHandler.CmdResult;
|
||||
|
||||
namespace MinecraftClient.Commands
|
||||
{
|
||||
class Minimap : Command
|
||||
{
|
||||
public override string CmdName => "minimap";
|
||||
public override string CmdUsage => "minimap [on|off] | minimap zoom [in|out|<1-16>] | minimap names [players|hostile|neutral|passive] [on|off] | minimap names [all_on|all_off] | minimap position [top_left|top_right|center|bottom_left|bottom_right] | minimap cave [auto|on|off]";
|
||||
public override string CmdDesc => Translations.cmd_minimap_desc;
|
||||
|
||||
public override void RegisterCommand(CommandDispatcher<CmdResult> dispatcher)
|
||||
{
|
||||
dispatcher.Register(l => l.Literal("help")
|
||||
.Then(l => l.Literal(CmdName)
|
||||
.Executes(r => GetUsage(r.Source, string.Empty))
|
||||
)
|
||||
);
|
||||
|
||||
dispatcher.Register(l => l.Literal(CmdName)
|
||||
.Executes(r => DoToggle(r.Source))
|
||||
.Then(l => l.Literal("on")
|
||||
.Executes(r => DoOn(r.Source)))
|
||||
.Then(l => l.Literal("off")
|
||||
.Executes(r => DoOff(r.Source)))
|
||||
.Then(l => l.Literal("zoom")
|
||||
.Executes(r => DoZoomInfo(r.Source))
|
||||
.Then(l => l.Literal("in")
|
||||
.Executes(r => DoZoomIn(r.Source)))
|
||||
.Then(l => l.Literal("out")
|
||||
.Executes(r => DoZoomOut(r.Source)))
|
||||
.Then(l => l.Argument("level", Arguments.Integer(MinimapControl.MinZoom, MinimapControl.MaxZoom))
|
||||
.Executes(r => DoZoomSet(r.Source, Arguments.GetInteger(r, "level")))))
|
||||
.Then(l => l.Literal("names")
|
||||
.Executes(r => DoNamesInfo(r.Source))
|
||||
.Then(l => l.Literal("all_on")
|
||||
.Executes(r => DoNamesAll(r.Source, true)))
|
||||
.Then(l => l.Literal("all_off")
|
||||
.Executes(r => DoNamesAll(r.Source, false)))
|
||||
.Then(l => l.Literal("players")
|
||||
.Executes(r => DoNamesCatInfo(r.Source, MobCategory.Player))
|
||||
.Then(l => l.Literal("on")
|
||||
.Executes(r => DoNamesCatSet(r.Source, MobCategory.Player, true)))
|
||||
.Then(l => l.Literal("off")
|
||||
.Executes(r => DoNamesCatSet(r.Source, MobCategory.Player, false))))
|
||||
.Then(l => l.Literal("hostile")
|
||||
.Executes(r => DoNamesCatInfo(r.Source, MobCategory.Hostile))
|
||||
.Then(l => l.Literal("on")
|
||||
.Executes(r => DoNamesCatSet(r.Source, MobCategory.Hostile, true)))
|
||||
.Then(l => l.Literal("off")
|
||||
.Executes(r => DoNamesCatSet(r.Source, MobCategory.Hostile, false))))
|
||||
.Then(l => l.Literal("neutral")
|
||||
.Executes(r => DoNamesCatInfo(r.Source, MobCategory.Neutral))
|
||||
.Then(l => l.Literal("on")
|
||||
.Executes(r => DoNamesCatSet(r.Source, MobCategory.Neutral, true)))
|
||||
.Then(l => l.Literal("off")
|
||||
.Executes(r => DoNamesCatSet(r.Source, MobCategory.Neutral, false))))
|
||||
.Then(l => l.Literal("passive")
|
||||
.Executes(r => DoNamesCatInfo(r.Source, MobCategory.Passive))
|
||||
.Then(l => l.Literal("on")
|
||||
.Executes(r => DoNamesCatSet(r.Source, MobCategory.Passive, true)))
|
||||
.Then(l => l.Literal("off")
|
||||
.Executes(r => DoNamesCatSet(r.Source, MobCategory.Passive, false)))))
|
||||
.Then(l => l.Literal("position")
|
||||
.Executes(r => DoPositionInfo(r.Source))
|
||||
.Then(l => l.Literal("top_left")
|
||||
.Executes(r => DoPositionSet(r.Source, MinimapPosition.top_left)))
|
||||
.Then(l => l.Literal("top_right")
|
||||
.Executes(r => DoPositionSet(r.Source, MinimapPosition.top_right)))
|
||||
.Then(l => l.Literal("center")
|
||||
.Executes(r => DoPositionSet(r.Source, MinimapPosition.center)))
|
||||
.Then(l => l.Literal("bottom_left")
|
||||
.Executes(r => DoPositionSet(r.Source, MinimapPosition.bottom_left)))
|
||||
.Then(l => l.Literal("bottom_right")
|
||||
.Executes(r => DoPositionSet(r.Source, MinimapPosition.bottom_right))))
|
||||
.Then(l => l.Literal("cave")
|
||||
.Executes(r => DoCaveInfo(r.Source))
|
||||
.Then(l => l.Literal("auto")
|
||||
.Executes(r => DoCaveSet(r.Source, CaveModeOption.auto)))
|
||||
.Then(l => l.Literal("on")
|
||||
.Executes(r => DoCaveSet(r.Source, CaveModeOption.on)))
|
||||
.Then(l => l.Literal("off")
|
||||
.Executes(r => DoCaveSet(r.Source, CaveModeOption.off))))
|
||||
.Then(l => l.Literal("_help")
|
||||
.Executes(r => GetUsage(r.Source, string.Empty))
|
||||
.Redirect(dispatcher.GetRoot().GetChild("help")?.GetChild(CmdName)))
|
||||
);
|
||||
}
|
||||
|
||||
private int GetUsage(CmdResult r, string _) =>
|
||||
r.SetAndReturn(GetCmdDescTranslated());
|
||||
|
||||
private static MainTuiView? GetTuiView(CmdResult r)
|
||||
{
|
||||
if (ConsoleIO.Backend is not TuiConsoleBackend)
|
||||
{
|
||||
r.SetAndReturn(Status.Fail, Translations.cmd_minimap_tui_only);
|
||||
return null;
|
||||
}
|
||||
return TuiConsoleBackend.Instance?.GetView();
|
||||
}
|
||||
|
||||
private static int DoToggle(CmdResult r)
|
||||
{
|
||||
var view = GetTuiView(r);
|
||||
if (view is null) return (int)r.status;
|
||||
|
||||
bool wasVisible = view.IsMinimapVisible;
|
||||
Dispatcher.UIThread.Post(() => view.ToggleMinimap());
|
||||
string msg = wasVisible
|
||||
? Translations.cmd_minimap_disabled
|
||||
: Translations.cmd_minimap_enabled;
|
||||
return r.SetAndReturn(Status.Done, msg);
|
||||
}
|
||||
|
||||
private static int DoOn(CmdResult r)
|
||||
{
|
||||
var view = GetTuiView(r);
|
||||
if (view is null) return (int)r.status;
|
||||
|
||||
Dispatcher.UIThread.Post(() => view.ShowMinimap());
|
||||
return r.SetAndReturn(Status.Done, Translations.cmd_minimap_enabled);
|
||||
}
|
||||
|
||||
private static int DoOff(CmdResult r)
|
||||
{
|
||||
var view = GetTuiView(r);
|
||||
if (view is null) return (int)r.status;
|
||||
|
||||
Dispatcher.UIThread.Post(() => view.HideMinimap());
|
||||
return r.SetAndReturn(Status.Done, Translations.cmd_minimap_disabled);
|
||||
}
|
||||
|
||||
private static int DoZoomInfo(CmdResult r)
|
||||
{
|
||||
var view = GetTuiView(r);
|
||||
if (view is null) return (int)r.status;
|
||||
|
||||
int current = view.GetMinimapZoom();
|
||||
return r.SetAndReturn(Status.Done,
|
||||
string.Format(Translations.cmd_minimap_zoom_current, current, MinimapControl.MaxZoom));
|
||||
}
|
||||
|
||||
private static int DoZoomIn(CmdResult r)
|
||||
{
|
||||
var view = GetTuiView(r);
|
||||
if (view is null) return (int)r.status;
|
||||
|
||||
int newLevel = Math.Max(view.GetMinimapZoom() - 1, MinimapControl.MinZoom);
|
||||
Dispatcher.UIThread.Post(() => view.SetMinimapZoom(newLevel));
|
||||
return r.SetAndReturn(Status.Done, string.Format(Translations.cmd_minimap_zoom_set, newLevel));
|
||||
}
|
||||
|
||||
private static int DoZoomOut(CmdResult r)
|
||||
{
|
||||
var view = GetTuiView(r);
|
||||
if (view is null) return (int)r.status;
|
||||
|
||||
int newLevel = Math.Min(view.GetMinimapZoom() + 1, MinimapControl.MaxZoom);
|
||||
Dispatcher.UIThread.Post(() => view.SetMinimapZoom(newLevel));
|
||||
return r.SetAndReturn(Status.Done, string.Format(Translations.cmd_minimap_zoom_set, newLevel));
|
||||
}
|
||||
|
||||
private static int DoZoomSet(CmdResult r, int level)
|
||||
{
|
||||
var view = GetTuiView(r);
|
||||
if (view is null) return (int)r.status;
|
||||
|
||||
Dispatcher.UIThread.Post(() => view.SetMinimapZoom(level));
|
||||
return r.SetAndReturn(Status.Done, string.Format(Translations.cmd_minimap_zoom_set, level));
|
||||
}
|
||||
|
||||
private static int DoNamesInfo(CmdResult r)
|
||||
{
|
||||
var view = GetTuiView(r);
|
||||
if (view is null) return (int)r.status;
|
||||
|
||||
var nc = view.GetMinimapNameConfig();
|
||||
string status = string.Format(Translations.cmd_minimap_names_status,
|
||||
BoolStr(nc.Players), BoolStr(nc.Hostile), BoolStr(nc.Neutral), BoolStr(nc.Passive));
|
||||
return r.SetAndReturn(Status.Done, status);
|
||||
}
|
||||
|
||||
private static int DoNamesAll(CmdResult r, bool on)
|
||||
{
|
||||
var view = GetTuiView(r);
|
||||
if (view is null) return (int)r.status;
|
||||
|
||||
Dispatcher.UIThread.Post(() =>
|
||||
{
|
||||
view.GetMinimapNameConfig().SetAll(on);
|
||||
view.SyncMinimapNameConfig();
|
||||
});
|
||||
string msg = on ? Translations.cmd_minimap_names_all_on : Translations.cmd_minimap_names_all_off;
|
||||
return r.SetAndReturn(Status.Done, msg);
|
||||
}
|
||||
|
||||
private static int DoNamesCatInfo(CmdResult r, MobCategory cat)
|
||||
{
|
||||
var view = GetTuiView(r);
|
||||
if (view is null) return (int)r.status;
|
||||
|
||||
var nc = view.GetMinimapNameConfig();
|
||||
bool val = cat switch
|
||||
{
|
||||
MobCategory.Player => nc.Players,
|
||||
MobCategory.Hostile => nc.Hostile,
|
||||
MobCategory.Neutral => nc.Neutral,
|
||||
MobCategory.Passive => nc.Passive,
|
||||
_ => false,
|
||||
};
|
||||
return r.SetAndReturn(Status.Done,
|
||||
string.Format(Translations.cmd_minimap_names_cat, cat, BoolStr(val)));
|
||||
}
|
||||
|
||||
private static int DoNamesCatSet(CmdResult r, MobCategory cat, bool on)
|
||||
{
|
||||
var view = GetTuiView(r);
|
||||
if (view is null) return (int)r.status;
|
||||
|
||||
Dispatcher.UIThread.Post(() =>
|
||||
{
|
||||
var nc = view.GetMinimapNameConfig();
|
||||
switch (cat)
|
||||
{
|
||||
case MobCategory.Player: nc.Players = on; break;
|
||||
case MobCategory.Hostile: nc.Hostile = on; break;
|
||||
case MobCategory.Neutral: nc.Neutral = on; break;
|
||||
case MobCategory.Passive: nc.Passive = on; break;
|
||||
}
|
||||
view.SyncMinimapNameConfig();
|
||||
});
|
||||
return r.SetAndReturn(Status.Done,
|
||||
string.Format(Translations.cmd_minimap_names_cat_set, cat, BoolStr(on)));
|
||||
}
|
||||
|
||||
private static int DoPositionInfo(CmdResult r)
|
||||
{
|
||||
var view = GetTuiView(r);
|
||||
if (view is null) return (int)r.status;
|
||||
|
||||
var pos = view.GetMinimapPosition();
|
||||
return r.SetAndReturn(Status.Done,
|
||||
string.Format(Translations.cmd_minimap_position_current, pos));
|
||||
}
|
||||
|
||||
private static int DoPositionSet(CmdResult r, MinimapPosition pos)
|
||||
{
|
||||
var view = GetTuiView(r);
|
||||
if (view is null) return (int)r.status;
|
||||
|
||||
Dispatcher.UIThread.Post(() => view.SetMinimapPosition(pos));
|
||||
return r.SetAndReturn(Status.Done,
|
||||
string.Format(Translations.cmd_minimap_position_set, pos));
|
||||
}
|
||||
|
||||
private static int DoCaveInfo(CmdResult r)
|
||||
{
|
||||
var view = GetTuiView(r);
|
||||
if (view is null) return (int)r.status;
|
||||
|
||||
var mode = view.GetMinimapCaveMode();
|
||||
return r.SetAndReturn(Status.Done,
|
||||
string.Format(Translations.cmd_minimap_cave_current, mode));
|
||||
}
|
||||
|
||||
private static int DoCaveSet(CmdResult r, CaveModeOption mode)
|
||||
{
|
||||
var view = GetTuiView(r);
|
||||
if (view is null) return (int)r.status;
|
||||
|
||||
Dispatcher.UIThread.Post(() => view.SetMinimapCaveMode(mode));
|
||||
return r.SetAndReturn(Status.Done,
|
||||
string.Format(Translations.cmd_minimap_cave_set, mode));
|
||||
}
|
||||
|
||||
private static string BoolStr(bool v) => v ? "ON" : "OFF";
|
||||
}
|
||||
}
|
||||
98
MinecraftClient/Commands/RecipeBook.cs
Normal file
98
MinecraftClient/Commands/RecipeBook.cs
Normal file
|
|
@ -0,0 +1,98 @@
|
|||
using System.Text;
|
||||
using Brigadier.NET;
|
||||
using Brigadier.NET.Builder;
|
||||
using MinecraftClient.CommandHandler;
|
||||
|
||||
namespace MinecraftClient.Commands
|
||||
{
|
||||
public class RecipeBook : Command
|
||||
{
|
||||
public override string CmdName => "recipebook";
|
||||
public override string CmdUsage => "recipebook <list|craft|craftall> [recipe id]";
|
||||
public override string CmdDesc => Translations.cmd_recipebook_desc;
|
||||
|
||||
public override void RegisterCommand(CommandDispatcher<CmdResult> dispatcher)
|
||||
{
|
||||
dispatcher.Register(l => l.Literal("help")
|
||||
.Then(l => l.Literal(CmdName)
|
||||
.Executes(r => GetUsage(r.Source, string.Empty))
|
||||
.Then(l => l.Literal("list")
|
||||
.Executes(r => GetUsage(r.Source, "list")))
|
||||
.Then(l => l.Literal("craft")
|
||||
.Executes(r => GetUsage(r.Source, "craft")))
|
||||
.Then(l => l.Literal("craftall")
|
||||
.Executes(r => GetUsage(r.Source, "craftall")))
|
||||
)
|
||||
);
|
||||
|
||||
dispatcher.Register(l => l.Literal(CmdName)
|
||||
.Then(l => l.Literal("list")
|
||||
.Executes(r => ListRecipes(r.Source)))
|
||||
.Then(l => l.Literal("craft")
|
||||
.Then(l => l.Argument("RecipeId", Arguments.String())
|
||||
.Executes(r => CraftRecipe(r.Source, Arguments.GetString(r, "RecipeId"), makeAll: false))))
|
||||
.Then(l => l.Literal("craftall")
|
||||
.Then(l => l.Argument("RecipeId", Arguments.String())
|
||||
.Executes(r => CraftRecipe(r.Source, Arguments.GetString(r, "RecipeId"), makeAll: true))))
|
||||
.Then(l => l.Literal("_help")
|
||||
.Executes(r => GetUsage(r.Source, string.Empty))
|
||||
.Redirect(dispatcher.GetRoot().GetChild("help").GetChild(CmdName)))
|
||||
);
|
||||
}
|
||||
|
||||
private int GetUsage(CmdResult r, string? cmd)
|
||||
{
|
||||
return r.SetAndReturn(cmd switch
|
||||
{
|
||||
#pragma warning disable format // @formatter:off
|
||||
"list" => GetCmdDescTranslated(),
|
||||
"craft" => GetCmdDescTranslated(),
|
||||
"craftall" => GetCmdDescTranslated(),
|
||||
_ => GetCmdDescTranslated(),
|
||||
#pragma warning restore format // @formatter:on
|
||||
});
|
||||
}
|
||||
|
||||
private int ListRecipes(CmdResult r)
|
||||
{
|
||||
McClient handler = CmdResult.currentHandler!;
|
||||
if (!handler.GetInventoryEnabled())
|
||||
return r.SetAndReturn(CmdResult.Status.FailNeedInventory);
|
||||
|
||||
RecipeBookRecipeEntry[] recipes = handler.GetUnlockedRecipes();
|
||||
if (recipes.Length == 0)
|
||||
return r.SetAndReturn(CmdResult.Status.Fail, Translations.cmd_recipebook_no_recipes);
|
||||
|
||||
StringBuilder response = new();
|
||||
response.AppendLine(Translations.cmd_recipebook_list);
|
||||
foreach (RecipeBookRecipeEntry recipe in recipes)
|
||||
response.AppendLine("- " + recipe.DisplayText);
|
||||
|
||||
handler.Log.Info(response.ToString().TrimEnd());
|
||||
return r.SetAndReturn(CmdResult.Status.Done);
|
||||
}
|
||||
|
||||
private int CraftRecipe(CmdResult r, string recipeId, bool makeAll)
|
||||
{
|
||||
McClient handler = CmdResult.currentHandler!;
|
||||
if (!handler.GetInventoryEnabled())
|
||||
return r.SetAndReturn(CmdResult.Status.FailNeedInventory);
|
||||
|
||||
if (string.IsNullOrWhiteSpace(recipeId))
|
||||
return r.SetAndReturn(CmdResult.Status.Fail, Translations.cmd_recipebook_recipe_id_empty);
|
||||
|
||||
if (handler.GetProtocolVersion() < Protocol.Handlers.Protocol18Handler.MC_1_13_Version)
|
||||
return r.SetAndReturn(CmdResult.Status.Fail, Translations.cmd_recipebook_unsupported);
|
||||
|
||||
if (handler.GetActiveRecipeBookInventory() is null)
|
||||
return r.SetAndReturn(CmdResult.Status.Fail, Translations.cmd_recipebook_no_active_inventory);
|
||||
|
||||
string normalizedRecipeId = McClient.NormalizeRecipeArgument(recipeId, handler.GetProtocolVersion());
|
||||
string successMessage = string.Format(makeAll ? Translations.cmd_recipebook_craftall_sent : Translations.cmd_recipebook_craft_sent, normalizedRecipeId);
|
||||
|
||||
return handler.SendPlaceRecipe(recipeId, makeAll)
|
||||
? r.SetAndReturn(CmdResult.Status.Done, successMessage)
|
||||
: r.SetAndReturn(CmdResult.Status.Fail, string.Format(Translations.cmd_recipebook_craft_failed, normalizedRecipeId));
|
||||
}
|
||||
}
|
||||
}
|
||||
50
MinecraftClient/Commands/Tab.cs
Normal file
50
MinecraftClient/Commands/Tab.cs
Normal file
|
|
@ -0,0 +1,50 @@
|
|||
using Avalonia.Threading;
|
||||
using Brigadier.NET;
|
||||
using Brigadier.NET.Builder;
|
||||
using MinecraftClient.CommandHandler;
|
||||
using MinecraftClient.Tui;
|
||||
|
||||
namespace MinecraftClient.Commands
|
||||
{
|
||||
public class Tab : Command
|
||||
{
|
||||
public override string CmdName => "tab";
|
||||
public override string CmdUsage => "tab";
|
||||
public override string CmdDesc => Translations.cmd_tab_desc;
|
||||
|
||||
public override void RegisterCommand(CommandDispatcher<CmdResult> dispatcher)
|
||||
{
|
||||
dispatcher.Register(l => l.Literal("help")
|
||||
.Then(l => l.Literal(CmdName)
|
||||
.Executes(r => GetUsage(r.Source)))
|
||||
);
|
||||
|
||||
dispatcher.Register(l => l.Literal(CmdName)
|
||||
.Executes(r => ShowTab(r.Source))
|
||||
.Then(l => l.Literal("_help")
|
||||
.Executes(r => GetUsage(r.Source))
|
||||
.Redirect(dispatcher.GetRoot().GetChild("help").GetChild(CmdName)))
|
||||
);
|
||||
}
|
||||
|
||||
private int GetUsage(CmdResult r) => r.SetAndReturn(GetCmdDescTranslated());
|
||||
|
||||
private static int ShowTab(CmdResult r)
|
||||
{
|
||||
McClient handler = CmdResult.currentHandler!;
|
||||
var snapshot = handler.GetTabListSnapshot();
|
||||
|
||||
if (ConsoleIO.Backend is TuiConsoleBackend)
|
||||
{
|
||||
var view = TuiConsoleBackend.Instance?.GetView();
|
||||
if (view is null)
|
||||
return r.SetAndReturn(CmdResult.Status.Fail, Translations.cmd_tab_tui_unavailable);
|
||||
|
||||
Dispatcher.UIThread.Post(() => view.ShowOverlay(new TabListOverlay(handler)));
|
||||
return r.SetAndReturn(CmdResult.Status.Done, Translations.cmd_tab_tui_opened);
|
||||
}
|
||||
|
||||
return r.SetAndReturn(CmdResult.Status.Done, TabListFormatter.Render(snapshot));
|
||||
}
|
||||
}
|
||||
}
|
||||
77
MinecraftClient/Commands/Teams.cs
Normal file
77
MinecraftClient/Commands/Teams.cs
Normal file
|
|
@ -0,0 +1,77 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using Brigadier.NET;
|
||||
using Brigadier.NET.Builder;
|
||||
using MinecraftClient.CommandHandler;
|
||||
using MinecraftClient.Mapping;
|
||||
|
||||
namespace MinecraftClient.Commands
|
||||
{
|
||||
public class Teams : Command
|
||||
{
|
||||
public override string CmdName => "teams";
|
||||
public override string CmdUsage => "teams";
|
||||
public override string CmdDesc => Translations.cmd_teams_desc;
|
||||
|
||||
public override void RegisterCommand(CommandDispatcher<CmdResult> dispatcher)
|
||||
{
|
||||
dispatcher.Register(l => l.Literal("help")
|
||||
.Then(l => l.Literal(CmdName)
|
||||
.Executes(r => GetUsage(r.Source, string.Empty))
|
||||
)
|
||||
);
|
||||
|
||||
dispatcher.Register(l => l.Literal(CmdName)
|
||||
.Executes(r => DoListTeams(r.Source))
|
||||
.Then(l => l.Literal("_help")
|
||||
.Executes(r => GetUsage(r.Source, string.Empty))
|
||||
.Redirect(dispatcher.GetRoot().GetChild("help").GetChild(CmdName)))
|
||||
);
|
||||
}
|
||||
|
||||
private int GetUsage(CmdResult r, string? cmd)
|
||||
{
|
||||
return r.SetAndReturn(cmd switch
|
||||
{
|
||||
#pragma warning disable format // @formatter:off
|
||||
_ => GetCmdDescTranslated(),
|
||||
#pragma warning restore format // @formatter:on
|
||||
});
|
||||
}
|
||||
|
||||
private static int DoListTeams(CmdResult r)
|
||||
{
|
||||
McClient handler = CmdResult.currentHandler!;
|
||||
Dictionary<string, PlayerTeam> snapshot = handler.GetTeams();
|
||||
|
||||
if (snapshot.Count == 0)
|
||||
return r.SetAndReturn(CmdResult.Status.Done, Translations.cmd_teams_no_teams);
|
||||
|
||||
var sb = new StringBuilder();
|
||||
foreach (var team in snapshot.Values.OrderBy(static t => t.Name, StringComparer.Ordinal))
|
||||
{
|
||||
sb.AppendLine(string.Format(Translations.cmd_teams_team_header,
|
||||
team.Name,
|
||||
team.DisplayName,
|
||||
team.Color,
|
||||
team.Prefix,
|
||||
team.Suffix,
|
||||
team.NameTagVisibility,
|
||||
team.CollisionRule,
|
||||
team.AllowFriendlyFire,
|
||||
team.SeeFriendlyInvisibles));
|
||||
|
||||
if (team.Members.Count == 0)
|
||||
sb.AppendLine(Translations.cmd_teams_team_no_members);
|
||||
else
|
||||
sb.AppendLine(string.Format(Translations.cmd_teams_team_members,
|
||||
team.Members.Count,
|
||||
string.Join(", ", team.Members.OrderBy(static m => m, StringComparer.OrdinalIgnoreCase))));
|
||||
}
|
||||
|
||||
return r.SetAndReturn(CmdResult.Status.Done, sb.ToString().TrimEnd());
|
||||
}
|
||||
}
|
||||
}
|
||||
63
MinecraftClient/Commands/Tryout.cs
Normal file
63
MinecraftClient/Commands/Tryout.cs
Normal file
|
|
@ -0,0 +1,63 @@
|
|||
using Brigadier.NET;
|
||||
using Brigadier.NET.Builder;
|
||||
using MinecraftClient.CommandHandler;
|
||||
using static MinecraftClient.Settings.ConsoleConfigHealper.ConsoleConfig;
|
||||
|
||||
namespace MinecraftClient.Commands
|
||||
{
|
||||
public class Tryout : Command
|
||||
{
|
||||
public override string CmdName => "tryout";
|
||||
public override string CmdUsage => "tryout [list|tui]";
|
||||
public override string CmdDesc => Translations.cmd_tryout_desc;
|
||||
|
||||
public override void RegisterCommand(CommandDispatcher<CmdResult> dispatcher)
|
||||
{
|
||||
dispatcher.Register(l => l.Literal("help")
|
||||
.Then(l => l.Literal(CmdName)
|
||||
.Executes(r => GetUsage(r.Source))
|
||||
)
|
||||
);
|
||||
|
||||
dispatcher.Register(l => l.Literal(CmdName)
|
||||
.Executes(r => ListTryouts(r.Source))
|
||||
.Then(l => l.Literal("list")
|
||||
.Executes(r => ListTryouts(r.Source)))
|
||||
.Then(l => l.Literal("tui")
|
||||
.Executes(r => EnableTuiMode(r.Source)))
|
||||
.Then(l => l.Literal("_help")
|
||||
.Executes(r => GetUsage(r.Source))
|
||||
.Redirect(dispatcher.GetRoot().GetChild("help").GetChild(CmdName)))
|
||||
);
|
||||
}
|
||||
|
||||
private int GetUsage(CmdResult r)
|
||||
{
|
||||
return r.SetAndReturn(GetCmdDescTranslated());
|
||||
}
|
||||
|
||||
private int ListTryouts(CmdResult r)
|
||||
{
|
||||
return r.SetAndReturn(string.Join('\n',
|
||||
GetCmdDescTranslated(),
|
||||
string.Empty,
|
||||
Translations.cmd_tryout_list_header,
|
||||
$" - {Translations.cmd_tryout_list_tui}"));
|
||||
}
|
||||
|
||||
private int EnableTuiMode(CmdResult r)
|
||||
{
|
||||
var previousMode = Settings.Config.Console.General.ConsoleMode;
|
||||
if (previousMode == ConsoleModeType.tui)
|
||||
{
|
||||
return r.SetAndReturn(CmdResult.Status.Done, Translations.cmd_tryout_tui_already_enabled);
|
||||
}
|
||||
|
||||
Settings.Config.Console.General.ConsoleMode = ConsoleModeType.tui;
|
||||
Program.WriteBackSettings();
|
||||
|
||||
return r.SetAndReturn(CmdResult.Status.Done,
|
||||
string.Format(Translations.cmd_tryout_tui_enabled, previousMode, ConsoleModeType.tui));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -1,6 +1,8 @@
|
|||
using Brigadier.NET;
|
||||
using Brigadier.NET.Builder;
|
||||
using MinecraftClient.CommandHandler;
|
||||
using MinecraftClient.Inventory;
|
||||
using MinecraftClient.Mapping;
|
||||
using static MinecraftClient.CommandHandler.CmdResult;
|
||||
|
||||
namespace MinecraftClient.Commands
|
||||
|
|
@ -8,7 +10,7 @@ namespace MinecraftClient.Commands
|
|||
class UseItem : Command
|
||||
{
|
||||
public override string CmdName { get { return "useitem"; } }
|
||||
public override string CmdUsage { get { return "useitem"; } }
|
||||
public override string CmdUsage { get { return "useitem [mainhand|offhand] | useitem [x] [y] [z] [mainhand|offhand]"; } }
|
||||
public override string CmdDesc { get { return Translations.cmd_useitem_desc; } }
|
||||
|
||||
public override void RegisterCommand(CommandDispatcher<CmdResult> dispatcher)
|
||||
|
|
@ -21,6 +23,16 @@ namespace MinecraftClient.Commands
|
|||
|
||||
dispatcher.Register(l => l.Literal(CmdName)
|
||||
.Executes(r => DoUseItem(r.Source))
|
||||
.Then(l => l.Literal("mainhand")
|
||||
.Executes(r => DoUseItem(r.Source, Hand.MainHand)))
|
||||
.Then(l => l.Literal("offhand")
|
||||
.Executes(r => DoUseItem(r.Source, Hand.OffHand)))
|
||||
.Then(l => l.Argument("Location", MccArguments.Location())
|
||||
.Executes(r => DoUseItemAtLocation(r.Source, MccArguments.GetLocation(r, "Location"), Hand.MainHand))
|
||||
.Then(l => l.Literal("mainhand")
|
||||
.Executes(r => DoUseItemAtLocation(r.Source, MccArguments.GetLocation(r, "Location"), Hand.MainHand)))
|
||||
.Then(l => l.Literal("offhand")
|
||||
.Executes(r => DoUseItemAtLocation(r.Source, MccArguments.GetLocation(r, "Location"), Hand.OffHand))))
|
||||
.Then(l => l.Literal("_help")
|
||||
.Executes(r => GetUsage(r.Source, string.Empty))
|
||||
.Redirect(dispatcher.GetRoot().GetChild("help").GetChild(CmdName)))
|
||||
|
|
@ -37,14 +49,64 @@ namespace MinecraftClient.Commands
|
|||
});
|
||||
}
|
||||
|
||||
private int DoUseItem(CmdResult r)
|
||||
private static bool ShouldUseOffhandFood(McClient handler)
|
||||
{
|
||||
Container? inventory = handler.GetInventory(0);
|
||||
if (inventory is null)
|
||||
return false;
|
||||
|
||||
if (!inventory.Items.TryGetValue(45, out Item? offhandItem)
|
||||
|| offhandItem.IsEmpty
|
||||
|| !offhandItem.Type.IsFood())
|
||||
return false;
|
||||
|
||||
int mainHandSlot = 36 + handler.GetCurrentSlot();
|
||||
return !inventory.Items.TryGetValue(mainHandSlot, out Item? mainHandItem)
|
||||
|| mainHandItem.IsEmpty
|
||||
|| !mainHandItem.Type.IsFood();
|
||||
}
|
||||
|
||||
private int DoUseItem(CmdResult r, Hand? requestedHand = null)
|
||||
{
|
||||
McClient handler = CmdResult.currentHandler!;
|
||||
if (!handler.GetInventoryEnabled())
|
||||
return r.SetAndReturn(Status.FailNeedInventory);
|
||||
|
||||
handler.UseItemOnHand();
|
||||
Hand hand = requestedHand ?? (ShouldUseOffhandFood(handler) ? Hand.OffHand : Hand.MainHand);
|
||||
bool useOffhandFood = !requestedHand.HasValue && hand == Hand.OffHand;
|
||||
|
||||
if (!useOffhandFood && handler.GetTerrainEnabled())
|
||||
{
|
||||
const double maxDistance = 4.5;
|
||||
var raycast = RaycastHelper.RaycastBlock(handler, maxDistance, false);
|
||||
if (raycast.Item1 && raycast.Item3.Type != Material.Air)
|
||||
{
|
||||
handler.PlaceBlock(raycast.Item2, Direction.Up, hand, lookAtBlock: true);
|
||||
handler.DoAnimation((int)hand);
|
||||
return r.SetAndReturn(Status.Done, Translations.cmd_useitem_use);
|
||||
}
|
||||
}
|
||||
|
||||
if (hand == Hand.OffHand)
|
||||
handler.UseItemOnLeftHand();
|
||||
else
|
||||
handler.UseItemOnHand();
|
||||
|
||||
return r.SetAndReturn(Status.Done, Translations.cmd_useitem_use);
|
||||
}
|
||||
|
||||
private int DoUseItemAtLocation(CmdResult r, Location block, Hand hand)
|
||||
{
|
||||
McClient handler = CmdResult.currentHandler!;
|
||||
if (!handler.GetTerrainEnabled())
|
||||
return r.SetAndReturn(Status.FailNeedTerrain);
|
||||
|
||||
Location current = handler.GetCurrentLocation();
|
||||
block = block.ToAbsolute(current).ToFloor();
|
||||
handler.PlaceBlock(block, Direction.Up, hand, lookAtBlock: true);
|
||||
handler.DoAnimation((int)hand);
|
||||
return r.SetAndReturn(Status.Done, Translations.cmd_useitem_use);
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,6 +1,8 @@
|
|||
using Brigadier.NET;
|
||||
using System;
|
||||
using Brigadier.NET;
|
||||
using Brigadier.NET.Builder;
|
||||
using MinecraftClient.CommandHandler;
|
||||
using MinecraftClient.Inventory;
|
||||
using MinecraftClient.Mapping;
|
||||
using static MinecraftClient.CommandHandler.CmdResult;
|
||||
|
||||
|
|
@ -9,7 +11,7 @@ namespace MinecraftClient.Commands
|
|||
class Useblock : Command
|
||||
{
|
||||
public override string CmdName { get { return "useblock"; } }
|
||||
public override string CmdUsage { get { return "useblock <x> <y> <z>"; } }
|
||||
public override string CmdUsage { get { return "useblock <x> <y> <z> [mainhand|offhand]"; } }
|
||||
public override string CmdDesc { get { return Translations.cmd_useblock_desc; } }
|
||||
|
||||
public override void RegisterCommand(CommandDispatcher<CmdResult> dispatcher)
|
||||
|
|
@ -22,7 +24,11 @@ namespace MinecraftClient.Commands
|
|||
|
||||
dispatcher.Register(l => l.Literal(CmdName)
|
||||
.Then(l => l.Argument("Location", MccArguments.Location())
|
||||
.Executes(r => UseBlockAtLocation(r.Source, MccArguments.GetLocation(r, "Location"))))
|
||||
.Executes(r => UseBlockAtLocation(r.Source, MccArguments.GetLocation(r, "Location"), Hand.MainHand))
|
||||
.Then(l => l.Literal("mainhand")
|
||||
.Executes(r => UseBlockAtLocation(r.Source, MccArguments.GetLocation(r, "Location"), Hand.MainHand)))
|
||||
.Then(l => l.Literal("offhand")
|
||||
.Executes(r => UseBlockAtLocation(r.Source, MccArguments.GetLocation(r, "Location"), Hand.OffHand))))
|
||||
.Then(l => l.Literal("_help")
|
||||
.Executes(r => GetUsage(r.Source, string.Empty))
|
||||
.Redirect(dispatcher.GetRoot().GetChild("help").GetChild(CmdName)))
|
||||
|
|
@ -39,7 +45,7 @@ namespace MinecraftClient.Commands
|
|||
});
|
||||
}
|
||||
|
||||
private int UseBlockAtLocation(CmdResult r, Location block)
|
||||
private int UseBlockAtLocation(CmdResult r, Location block, Hand hand)
|
||||
{
|
||||
McClient handler = CmdResult.currentHandler!;
|
||||
if (!handler.GetTerrainEnabled())
|
||||
|
|
@ -48,8 +54,27 @@ namespace MinecraftClient.Commands
|
|||
Location current = handler.GetCurrentLocation();
|
||||
block = block.ToAbsolute(current).ToFloor();
|
||||
Location blockCenter = block.ToCenter();
|
||||
bool res = handler.PlaceBlock(block, Direction.Down);
|
||||
bool res = handler.PlaceBlock(block, GetFaceNearestPlayer(current, blockCenter), hand, lookAtBlock: true);
|
||||
return r.SetAndReturn(string.Format(Translations.cmd_useblock_use, blockCenter.X, blockCenter.Y, blockCenter.Z, res ? "succeeded" : "failed"), res);
|
||||
}
|
||||
|
||||
private static Direction GetFaceNearestPlayer(Location playerLocation, Location blockCenter)
|
||||
{
|
||||
double dx = playerLocation.X - blockCenter.X;
|
||||
double dy = playerLocation.Y - blockCenter.Y;
|
||||
double dz = playerLocation.Z - blockCenter.Z;
|
||||
|
||||
double absX = Math.Abs(dx);
|
||||
double absY = Math.Abs(dy);
|
||||
double absZ = Math.Abs(dz);
|
||||
|
||||
if (absX >= absY && absX >= absZ)
|
||||
return dx >= 0 ? Direction.East : Direction.West;
|
||||
|
||||
if (absY >= absZ)
|
||||
return dy >= 0 ? Direction.Up : Direction.Down;
|
||||
|
||||
return dz >= 0 ? Direction.South : Direction.North;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,5 +1,6 @@
|
|||
using System;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading;
|
||||
|
|
@ -8,6 +9,7 @@ using Brigadier.NET;
|
|||
using FuzzySharp;
|
||||
using MinecraftClient.CommandHandler;
|
||||
using MinecraftClient.Scripting;
|
||||
using MinecraftClient.Tui;
|
||||
using static MinecraftClient.Settings;
|
||||
|
||||
namespace MinecraftClient
|
||||
|
|
@ -16,18 +18,23 @@ namespace MinecraftClient
|
|||
/// Allows simultaneous console input and output without breaking user input
|
||||
/// (Without having this annoying behaviour : User inp[Some Console output]ut)
|
||||
/// Provide some fancy features such as formatted output, text pasting and tab-completion.
|
||||
/// By ORelio - (c) 2012-2018 - Available under the CDDL-1.0 license
|
||||
/// By ORelio - (c) 2012-2018 - Available under the CDDL-1.0 License
|
||||
/// </summary>
|
||||
public static class ConsoleIO
|
||||
{
|
||||
private static IAutoComplete? autocomplete_engine;
|
||||
|
||||
/// <summary>
|
||||
/// The active console backend. Set once during startup.
|
||||
/// </summary>
|
||||
public static IConsoleBackend Backend { get; set; } = null!;
|
||||
|
||||
/// <summary>
|
||||
/// Reset the IO mechanism and clear all buffers
|
||||
/// </summary>
|
||||
public static void Reset()
|
||||
{
|
||||
ClearLineAndBuffer();
|
||||
Backend?.ClearInputBuffer();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
|
|
@ -40,9 +47,8 @@ namespace MinecraftClient
|
|||
}
|
||||
|
||||
/// <summary>
|
||||
/// Determines whether to use interactive IO or basic IO.
|
||||
/// Set to true to disable interactive command prompt and use the default Console.Read|Write() methods.
|
||||
/// Color codes are printed as is when BasicIO is enabled.
|
||||
/// Determines whether to use basic IO (legacy flag, kept for compatibility).
|
||||
/// In the new architecture this is true when Backend is BasicConsoleBackend.
|
||||
/// </summary>
|
||||
public static bool BasicIO = false;
|
||||
|
||||
|
|
@ -56,10 +62,15 @@ namespace MinecraftClient
|
|||
/// </summary>
|
||||
public static bool EnableTimestamps = false;
|
||||
|
||||
/// <summary>
|
||||
/// Determine whether chat lines should be displayed in the console.
|
||||
/// </summary>
|
||||
public static bool ChatVisible = true;
|
||||
|
||||
/// <summary>
|
||||
/// Specify a generic log line prefix for WriteLogLine()
|
||||
/// </summary>
|
||||
public static string LogPrefix = "§8[Log] ";
|
||||
public static string LogPrefix = "§8[MCC] ";
|
||||
|
||||
/// <summary>
|
||||
/// Read a password from the standard input
|
||||
|
|
@ -68,13 +79,7 @@ namespace MinecraftClient
|
|||
{
|
||||
if (BasicIO)
|
||||
return Console.ReadLine();
|
||||
else
|
||||
{
|
||||
ConsoleInteractive.ConsoleReader.SetInputVisible(false);
|
||||
var input = ConsoleInteractive.ConsoleReader.RequestImmediateInput();
|
||||
ConsoleInteractive.ConsoleReader.SetInputVisible(true);
|
||||
return input;
|
||||
}
|
||||
return Backend.ReadPassword();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
|
|
@ -84,8 +89,7 @@ namespace MinecraftClient
|
|||
{
|
||||
if (BasicIO)
|
||||
return Console.ReadLine() ?? String.Empty;
|
||||
else
|
||||
return ConsoleInteractive.ConsoleReader.RequestImmediateInput();
|
||||
return Backend.RequestImmediateInput();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
|
|
@ -106,10 +110,10 @@ namespace MinecraftClient
|
|||
/// </summary>
|
||||
public static void WriteLine(string line)
|
||||
{
|
||||
if (BasicIO)
|
||||
if (BasicIO || Backend is null)
|
||||
Console.WriteLine(line);
|
||||
else
|
||||
ConsoleInteractive.ConsoleWriter.WriteLine(line);
|
||||
Backend.WriteLine(line);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
|
|
@ -139,7 +143,7 @@ namespace MinecraftClient
|
|||
{
|
||||
str = str.Replace('\n', ' ');
|
||||
}
|
||||
if (BasicIO)
|
||||
if (BasicIO || Backend is null)
|
||||
{
|
||||
if (BasicIO_NoColor)
|
||||
{
|
||||
|
|
@ -153,10 +157,21 @@ namespace MinecraftClient
|
|||
return;
|
||||
}
|
||||
output.Append(str);
|
||||
ConsoleInteractive.ConsoleWriter.WriteLineFormatted(output.ToString());
|
||||
Backend.WriteLineFormatted(output.ToString());
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Write a formatted chat line to the console when chat output is enabled.
|
||||
/// </summary>
|
||||
public static void WriteChatLineIfVisible(string str, bool acceptnewlines = false, bool? displayTimestamp = null)
|
||||
{
|
||||
if (!ChatVisible)
|
||||
return;
|
||||
|
||||
WriteLineFormatted(str, acceptnewlines, displayTimestamp);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Write a prefixed log line. Prefix is set in LogPrefix.
|
||||
/// </summary>
|
||||
|
|
@ -177,9 +192,34 @@ namespace MinecraftClient
|
|||
private static void ClearLineAndBuffer()
|
||||
{
|
||||
if (BasicIO) return;
|
||||
ConsoleInteractive.ConsoleReader.ClearBuffer();
|
||||
Backend.ClearInputBuffer();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Clear the visible console output.
|
||||
/// </summary>
|
||||
public static void ClearConsole()
|
||||
{
|
||||
if (BasicIO || Backend is null)
|
||||
{
|
||||
try
|
||||
{
|
||||
Console.Clear();
|
||||
}
|
||||
catch (IOException ex)
|
||||
{
|
||||
System.Diagnostics.Debug.WriteLine(ex);
|
||||
}
|
||||
catch (PlatformNotSupportedException ex)
|
||||
{
|
||||
System.Diagnostics.Debug.WriteLine(ex);
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
Backend.ClearScreen();
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
|
|
@ -193,12 +233,37 @@ namespace MinecraftClient
|
|||
private static Task _latestTask = Task.CompletedTask;
|
||||
private static CancellationTokenSource? _cancellationTokenSource;
|
||||
|
||||
private static void MccAutocompleteHandler(ConsoleInteractive.ConsoleReader.Buffer buffer)
|
||||
private static void SendSuggestions(
|
||||
ConsoleInteractive.ConsoleSuggestion.Suggestion[] classicSugs,
|
||||
Tuple<int, int> range)
|
||||
{
|
||||
if (Backend is ClassicConsoleBackend classic)
|
||||
{
|
||||
classic.UpdateSuggestions(classicSugs, range);
|
||||
}
|
||||
else if (Backend is TuiConsoleBackend tui)
|
||||
{
|
||||
var tuiSugs = new CommandSuggestion[classicSugs.Length];
|
||||
for (int i = 0; i < classicSugs.Length; i++)
|
||||
tuiSugs[i] = new CommandSuggestion(classicSugs[i].Text, classicSugs[i].Tooltip);
|
||||
tui.UpdateSuggestions(tuiSugs, (range.Item1, range.Item2));
|
||||
}
|
||||
}
|
||||
|
||||
private static void DoClearSuggestions()
|
||||
{
|
||||
if (Backend is ClassicConsoleBackend classic)
|
||||
classic.ClearSuggestions();
|
||||
else if (Backend is TuiConsoleBackend tui)
|
||||
tui.ClearSuggestions();
|
||||
}
|
||||
|
||||
private static void MccAutocompleteHandler(ConsoleInputBuffer buffer)
|
||||
{
|
||||
string fullCommand = buffer.Text;
|
||||
if (string.IsNullOrEmpty(fullCommand))
|
||||
{
|
||||
ConsoleInteractive.ConsoleSuggestion.ClearSuggestions();
|
||||
DoClearSuggestions();
|
||||
return;
|
||||
}
|
||||
|
||||
|
|
@ -208,7 +273,7 @@ namespace MinecraftClient
|
|||
int offset = InternalCmdChar == MainConfigHelper.MainConfig.AdvancedConfig.InternalCmdCharType.none ? 0 : 1;
|
||||
if (buffer.CursorPosition - offset < 0)
|
||||
{
|
||||
ConsoleInteractive.ConsoleSuggestion.ClearSuggestions();
|
||||
DoClearSuggestions();
|
||||
return;
|
||||
}
|
||||
_cancellationTokenSource?.Cancel();
|
||||
|
|
@ -225,14 +290,14 @@ namespace MinecraftClient
|
|||
sugList.Add(new("/"));
|
||||
|
||||
var childs = McClient.dispatcher.GetRoot().Children;
|
||||
if (childs != null)
|
||||
if (childs is not null)
|
||||
foreach (var child in childs)
|
||||
sugList.Add(new(child.Name));
|
||||
|
||||
foreach (var cmd in Commands)
|
||||
sugList.Add(new(cmd));
|
||||
|
||||
ConsoleInteractive.ConsoleSuggestion.UpdateSuggestions(sugList.ToArray(), new(offset, offset));
|
||||
SendSuggestions(sugList.ToArray(), new(offset, offset));
|
||||
}
|
||||
else if (command.Length > 0 && command[0] == '/' && !command.Contains(' '))
|
||||
{
|
||||
|
|
@ -242,12 +307,12 @@ namespace MinecraftClient
|
|||
int index = 0;
|
||||
foreach (var sug in sorted)
|
||||
sugList[index++] = new(sug.Value);
|
||||
ConsoleInteractive.ConsoleSuggestion.UpdateSuggestions(sugList, new(offset, offset + command.Length));
|
||||
SendSuggestions(sugList, new(offset, offset + command.Length));
|
||||
}
|
||||
else
|
||||
{
|
||||
CommandDispatcher<CmdResult>? dispatcher = McClient.dispatcher;
|
||||
if (dispatcher == null)
|
||||
if (dispatcher is null)
|
||||
return;
|
||||
|
||||
ParseResults<CmdResult> parse = dispatcher.Parse(command, CmdResult.Empty);
|
||||
|
|
@ -257,7 +322,7 @@ namespace MinecraftClient
|
|||
int sugLen = suggestions.List.Count;
|
||||
if (sugLen == 0)
|
||||
{
|
||||
ConsoleInteractive.ConsoleSuggestion.ClearSuggestions();
|
||||
DoClearSuggestions();
|
||||
return;
|
||||
}
|
||||
|
||||
|
|
@ -278,7 +343,7 @@ namespace MinecraftClient
|
|||
foreach (var sug in sorted)
|
||||
sugList[index++] = new(sug.Value, dictionary[sug.Value] ?? string.Empty);
|
||||
|
||||
ConsoleInteractive.ConsoleSuggestion.UpdateSuggestions(sugList, range);
|
||||
SendSuggestions(sugList, range);
|
||||
}
|
||||
}, cts.Token);
|
||||
_latestTask = newTask;
|
||||
|
|
@ -287,22 +352,68 @@ namespace MinecraftClient
|
|||
}
|
||||
else
|
||||
{
|
||||
ConsoleInteractive.ConsoleSuggestion.ClearSuggestions();
|
||||
DoClearSuggestions();
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
public static void AutocompleteHandler(object? sender, ConsoleInteractive.ConsoleReader.Buffer buffer)
|
||||
public static void AutocompleteHandler(object? sender, ConsoleInputBuffer buffer)
|
||||
{
|
||||
if (Settings.Config.Console.CommandSuggestion.Enable)
|
||||
MccAutocompleteHandler(buffer);
|
||||
}
|
||||
|
||||
private static readonly string[] OfflineCommands = ["quit", "exit", "connect", "reco", "help"];
|
||||
|
||||
public static void OfflineAutocompleteHandler(object? sender, ConsoleInputBuffer buffer)
|
||||
{
|
||||
if (!Settings.Config.Console.CommandSuggestion.Enable)
|
||||
return;
|
||||
|
||||
string fullCommand = buffer.Text;
|
||||
if (string.IsNullOrEmpty(fullCommand))
|
||||
{
|
||||
DoClearSuggestions();
|
||||
return;
|
||||
}
|
||||
|
||||
var InternalCmdChar = Config.Main.Advanced.InternalCmdChar;
|
||||
int offset = 0;
|
||||
if (InternalCmdChar != MainConfigHelper.MainConfig.AdvancedConfig.InternalCmdCharType.none)
|
||||
{
|
||||
if (fullCommand[0] != InternalCmdChar.ToChar())
|
||||
{
|
||||
DoClearSuggestions();
|
||||
return;
|
||||
}
|
||||
offset = 1;
|
||||
}
|
||||
|
||||
string command = fullCommand[offset..];
|
||||
if (command.Contains(' '))
|
||||
{
|
||||
DoClearSuggestions();
|
||||
return;
|
||||
}
|
||||
|
||||
var sugList = new List<ConsoleInteractive.ConsoleSuggestion.Suggestion>();
|
||||
foreach (string cmd in OfflineCommands)
|
||||
{
|
||||
if (command.Length == 0 || cmd.StartsWith(command, StringComparison.OrdinalIgnoreCase))
|
||||
sugList.Add(new(cmd));
|
||||
}
|
||||
|
||||
if (sugList.Count > 0)
|
||||
SendSuggestions(sugList.ToArray(), new(offset, offset + command.Length));
|
||||
else
|
||||
DoClearSuggestions();
|
||||
}
|
||||
|
||||
public static void CancelAutocomplete()
|
||||
{
|
||||
_cancellationTokenSource?.Cancel();
|
||||
_latestTask = Task.CompletedTask;
|
||||
ConsoleInteractive.ConsoleSuggestion.ClearSuggestions();
|
||||
DoClearSuggestions();
|
||||
|
||||
AutoCompleteDone = false;
|
||||
AutoCompleteResult = Array.Empty<string>();
|
||||
|
|
|
|||
|
|
@ -89,7 +89,7 @@ namespace MinecraftClient.Crypto
|
|||
}
|
||||
|
||||
Span<byte> blockOutput = stackalloc byte[blockSize];
|
||||
if (FastAes != null)
|
||||
if (FastAes is not null)
|
||||
FastAes.EncryptEcb(ReadStreamIV, blockOutput);
|
||||
else
|
||||
Aes!.EncryptEcb(ReadStreamIV, blockOutput, PaddingMode.None);
|
||||
|
|
@ -122,7 +122,7 @@ namespace MinecraftClient.Crypto
|
|||
}
|
||||
|
||||
int processEnd = readed + curRead;
|
||||
if (FastAes != null)
|
||||
if (FastAes is not null)
|
||||
{
|
||||
for (int idx = readed; idx < processEnd; idx++)
|
||||
{
|
||||
|
|
@ -161,7 +161,7 @@ namespace MinecraftClient.Crypto
|
|||
{
|
||||
Span<byte> blockOutput = stackalloc byte[blockSize];
|
||||
|
||||
if (FastAes != null)
|
||||
if (FastAes is not null)
|
||||
FastAes.EncryptEcb(WriteStreamIV, blockOutput);
|
||||
else
|
||||
Aes!.EncryptEcb(WriteStreamIV, blockOutput, PaddingMode.None);
|
||||
|
|
@ -185,7 +185,7 @@ namespace MinecraftClient.Crypto
|
|||
for (int wirtten = 0; wirtten < required; ++wirtten)
|
||||
{
|
||||
ReadOnlySpan<byte> blockInput = new(outputBuf, wirtten, blockSize);
|
||||
if (FastAes != null)
|
||||
if (FastAes is not null)
|
||||
FastAes.EncryptEcb(blockInput, blockOutput);
|
||||
else
|
||||
Aes!.EncryptEcb(blockInput, blockOutput, PaddingMode.None);
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
using System;
|
||||
using System;
|
||||
using System.Runtime.CompilerServices;
|
||||
using System.Runtime.InteropServices;
|
||||
using System.Runtime.Intrinsics;
|
||||
|
|
@ -63,16 +63,16 @@ namespace MinecraftClient.Crypto
|
|||
|
||||
keys[0] = Unsafe.ReadUnaligned<Vector128<byte>>(ref key[0]);
|
||||
|
||||
MakeRoundKey(keys, 1, 0x01);
|
||||
MakeRoundKey(keys, 2, 0x02);
|
||||
MakeRoundKey(keys, 3, 0x04);
|
||||
MakeRoundKey(keys, 4, 0x08);
|
||||
MakeRoundKey(keys, 5, 0x10);
|
||||
MakeRoundKey(keys, 6, 0x20);
|
||||
MakeRoundKey(keys, 7, 0x40);
|
||||
MakeRoundKey(keys, 8, 0x80);
|
||||
MakeRoundKey(keys, 9, 0x1b);
|
||||
MakeRoundKey(keys, 10, 0x36);
|
||||
ExpandRound(keys, 1, Aes.KeygenAssist(keys[0], 0x01));
|
||||
ExpandRound(keys, 2, Aes.KeygenAssist(keys[1], 0x02));
|
||||
ExpandRound(keys, 3, Aes.KeygenAssist(keys[2], 0x04));
|
||||
ExpandRound(keys, 4, Aes.KeygenAssist(keys[3], 0x08));
|
||||
ExpandRound(keys, 5, Aes.KeygenAssist(keys[4], 0x10));
|
||||
ExpandRound(keys, 6, Aes.KeygenAssist(keys[5], 0x20));
|
||||
ExpandRound(keys, 7, Aes.KeygenAssist(keys[6], 0x40));
|
||||
ExpandRound(keys, 8, Aes.KeygenAssist(keys[7], 0x80));
|
||||
ExpandRound(keys, 9, Aes.KeygenAssist(keys[8], 0x1b));
|
||||
ExpandRound(keys, 10, Aes.KeygenAssist(keys[9], 0x36));
|
||||
|
||||
for (int i = 1; i < 10; i++)
|
||||
{
|
||||
|
|
@ -82,13 +82,11 @@ namespace MinecraftClient.Crypto
|
|||
return keys;
|
||||
}
|
||||
|
||||
private static void MakeRoundKey(Vector128<byte>[] keys, int i, byte rcon)
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
private static void ExpandRound(Vector128<byte>[] keys, int i, Vector128<byte> assist)
|
||||
{
|
||||
Vector128<byte> s = keys[i - 1];
|
||||
Vector128<byte> t = keys[i - 1];
|
||||
|
||||
t = Aes.KeygenAssist(t, rcon);
|
||||
t = Sse2.Shuffle(t.AsUInt32(), 0xFF).AsByte();
|
||||
Vector128<byte> t = Sse2.Shuffle(assist.AsUInt32(), 0xFF).AsByte();
|
||||
|
||||
s = Sse2.Xor(s, Sse2.ShiftLeftLogical128BitLane(s, 4));
|
||||
s = Sse2.Xor(s, Sse2.ShiftLeftLogical128BitLane(s, 8));
|
||||
|
|
|
|||
108
MinecraftClient/Dialogs/DialogFormatter.cs
Normal file
108
MinecraftClient/Dialogs/DialogFormatter.cs
Normal file
|
|
@ -0,0 +1,108 @@
|
|||
using System;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
|
||||
namespace MinecraftClient.Dialogs;
|
||||
|
||||
public static class DialogFormatter
|
||||
{
|
||||
public static string DisplayTitle(this DialogDefinition definition)
|
||||
{
|
||||
if (!string.IsNullOrWhiteSpace(definition.ExternalTitle))
|
||||
return definition.ExternalTitle!;
|
||||
|
||||
return string.IsNullOrWhiteSpace(definition.Title) ? DisplayType(definition.Type) : definition.Title;
|
||||
}
|
||||
|
||||
private const int BoxWidth = 50;
|
||||
|
||||
public static string Render(DialogInstance instance)
|
||||
{
|
||||
StringBuilder builder = new();
|
||||
string border = new('-', BoxWidth);
|
||||
builder.AppendLine(border);
|
||||
builder.AppendLine(" " + string.Format(Translations.dialog_render_header, instance.Revision, instance.Phase, instance.Definition.DisplayTitle()));
|
||||
builder.AppendLine(border);
|
||||
|
||||
foreach (var body in instance.Definition.Body.Where(static body => !string.IsNullOrWhiteSpace(body.Text)))
|
||||
builder.AppendLine(body.Text);
|
||||
|
||||
if (instance.Definition.Inputs.Count > 0)
|
||||
{
|
||||
builder.AppendLine(Translations.dialog_render_inputs);
|
||||
foreach (var input in instance.Definition.Inputs)
|
||||
{
|
||||
instance.Values.TryGetValue(input.Key, out var value);
|
||||
value ??= input.InitialValue;
|
||||
builder.AppendLine(string.Format(Translations.dialog_render_input, input.Key, DescribeKind(input.Kind), input.Label, value, DescribeInput(input)));
|
||||
}
|
||||
}
|
||||
|
||||
if (instance.Definition.Actions.Count > 0)
|
||||
{
|
||||
builder.AppendLine(Translations.dialog_render_actions);
|
||||
foreach (var action in instance.Definition.Actions)
|
||||
builder.AppendLine(string.Format(Translations.dialog_render_action, action.Index, action.Label, DescribeAction(action.Action)));
|
||||
}
|
||||
|
||||
builder.AppendLine();
|
||||
builder.AppendLine("§o" + Translations.dialog_render_help_hint + "§r");
|
||||
builder.Append(border);
|
||||
return builder.ToString();
|
||||
}
|
||||
|
||||
public static string DisplayType(string rawType)
|
||||
{
|
||||
return rawType switch
|
||||
{
|
||||
"minecraft:notice" => Translations.dialog_type_notice,
|
||||
"minecraft:confirmation" => Translations.dialog_type_confirmation,
|
||||
"minecraft:multi_action" => Translations.dialog_type_multi_action,
|
||||
"minecraft:dialog_list" => Translations.dialog_type_dialog_list,
|
||||
"minecraft:server_links" => Translations.dialog_type_server_links,
|
||||
_ => string.IsNullOrEmpty(rawType) ? Translations.dialog_type_unknown : rawType
|
||||
};
|
||||
}
|
||||
|
||||
private static string DescribeKind(DialogInputKind kind)
|
||||
{
|
||||
return kind switch
|
||||
{
|
||||
DialogInputKind.Text => Translations.dialog_input_kind_text,
|
||||
DialogInputKind.Boolean => Translations.dialog_input_kind_boolean,
|
||||
DialogInputKind.SingleOption => Translations.dialog_input_kind_options,
|
||||
DialogInputKind.NumberRange => Translations.dialog_input_kind_number,
|
||||
_ => Translations.dialog_input_kind_unknown
|
||||
};
|
||||
}
|
||||
|
||||
private static string DescribeInput(DialogInput input)
|
||||
{
|
||||
return input.Kind switch
|
||||
{
|
||||
DialogInputKind.Text => string.Format(Translations.dialog_input_desc_text, input.MaxLength),
|
||||
DialogInputKind.Boolean => string.Format(Translations.dialog_input_desc_boolean, input.OnTrue, input.OnFalse),
|
||||
DialogInputKind.SingleOption => string.Format(Translations.dialog_input_desc_options,
|
||||
string.Join(", ", input.Options?.Select(static option => option.Id) ?? [])),
|
||||
DialogInputKind.NumberRange => string.Format(Translations.dialog_input_desc_number, input.Start, input.End),
|
||||
_ => input.Type ?? Translations.dialog_input_desc_unknown
|
||||
};
|
||||
}
|
||||
|
||||
private static string DescribeAction(DialogActionDefinition? action)
|
||||
{
|
||||
if (action is null)
|
||||
return Translations.dialog_action_desc_close;
|
||||
|
||||
return action.Kind switch
|
||||
{
|
||||
DialogActionKind.RunCommand => Translations.dialog_action_desc_command,
|
||||
DialogActionKind.CustomClick => Translations.dialog_action_desc_custom,
|
||||
DialogActionKind.ShowDialog => Translations.dialog_action_desc_show_dialog,
|
||||
DialogActionKind.OpenUrl => Translations.dialog_action_desc_open_url,
|
||||
DialogActionKind.SuggestCommand => Translations.dialog_action_desc_suggest,
|
||||
DialogActionKind.CopyToClipboard => Translations.dialog_action_desc_copy,
|
||||
_ => action.Type ?? Translations.dialog_action_desc_unknown
|
||||
};
|
||||
}
|
||||
}
|
||||
445
MinecraftClient/Dialogs/DialogManager.cs
Normal file
445
MinecraftClient/Dialogs/DialogManager.cs
Normal file
|
|
@ -0,0 +1,445 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Globalization;
|
||||
using System.Linq;
|
||||
using System.Threading;
|
||||
|
||||
namespace MinecraftClient.Dialogs;
|
||||
|
||||
public sealed class DialogManager
|
||||
{
|
||||
private readonly McClient _client;
|
||||
private readonly Lock _lock = new();
|
||||
private readonly Dictionary<int, DialogDefinition> _registryById = new();
|
||||
private readonly Dictionary<string, DialogDefinition> _registryByName = new(StringComparer.Ordinal);
|
||||
private readonly List<DialogServerLink> _serverLinks = [];
|
||||
private DialogInstance? _current;
|
||||
private int _revision;
|
||||
|
||||
public DialogManager(McClient client)
|
||||
{
|
||||
ArgumentNullException.ThrowIfNull(client);
|
||||
_client = client;
|
||||
}
|
||||
|
||||
public event Action<DialogInstance>? DialogShown;
|
||||
public event Action<int>? DialogCleared;
|
||||
|
||||
public DialogInstance? Current
|
||||
{
|
||||
get
|
||||
{
|
||||
lock (_lock)
|
||||
return _current;
|
||||
}
|
||||
}
|
||||
|
||||
public void StoreRegistryDialog(int protocolId, string resourceId, DialogDefinition definition)
|
||||
{
|
||||
lock (_lock)
|
||||
{
|
||||
_registryById[protocolId] = definition;
|
||||
_registryByName[resourceId] = definition;
|
||||
}
|
||||
}
|
||||
|
||||
public void ClearRegistry()
|
||||
{
|
||||
lock (_lock)
|
||||
{
|
||||
_registryById.Clear();
|
||||
_registryByName.Clear();
|
||||
}
|
||||
}
|
||||
|
||||
public void SetServerLinks(IEnumerable<DialogServerLink> links)
|
||||
{
|
||||
lock (_lock)
|
||||
{
|
||||
_serverLinks.Clear();
|
||||
_serverLinks.AddRange(links);
|
||||
}
|
||||
}
|
||||
|
||||
public DialogInstance Show(DialogDefinition definition, DialogPhase phase)
|
||||
{
|
||||
DialogInstance instance;
|
||||
lock (_lock)
|
||||
{
|
||||
var expanded = ExpandServerLinks(definition);
|
||||
var values = expanded.Inputs.ToDictionary(static input => input.Key, static input => input.InitialValue, StringComparer.Ordinal);
|
||||
instance = new DialogInstance(++_revision, phase, expanded, values, DateTimeOffset.UtcNow);
|
||||
_current = instance;
|
||||
}
|
||||
|
||||
_client.Log.Info("§e" + string.Format(Translations.dialog_received, instance.Definition.DisplayTitle()));
|
||||
DialogShown?.Invoke(instance);
|
||||
return instance;
|
||||
}
|
||||
|
||||
public DialogInstance ShowRegistryReference(int protocolId, DialogPhase phase)
|
||||
{
|
||||
DialogDefinition? definition;
|
||||
lock (_lock)
|
||||
_registryById.TryGetValue(protocolId, out definition);
|
||||
|
||||
if (definition is not null)
|
||||
return Show(definition, phase);
|
||||
|
||||
var unresolved = new DialogDefinition(
|
||||
"minecraft:unresolved",
|
||||
string.Format(CultureInfo.InvariantCulture, Translations.dialog_unresolved_title, protocolId),
|
||||
null,
|
||||
CanCloseWithEscape: true,
|
||||
Pause: false,
|
||||
DialogAfterAction.Close,
|
||||
[new DialogBody(DialogBodyKind.Unknown, string.Format(CultureInfo.InvariantCulture, Translations.dialog_unresolved_body, protocolId))],
|
||||
[],
|
||||
[],
|
||||
null,
|
||||
IsResolved: false,
|
||||
UnresolvedReference: protocolId.ToString(CultureInfo.InvariantCulture));
|
||||
return Show(unresolved, phase);
|
||||
}
|
||||
|
||||
public void Clear()
|
||||
{
|
||||
int revision;
|
||||
lock (_lock)
|
||||
{
|
||||
revision = _current?.Revision ?? _revision;
|
||||
_current = null;
|
||||
}
|
||||
|
||||
_client.Log.Info(Translations.dialog_cleared);
|
||||
DialogCleared?.Invoke(revision);
|
||||
}
|
||||
|
||||
public DialogActionResult Dismiss()
|
||||
{
|
||||
int revision;
|
||||
lock (_lock)
|
||||
{
|
||||
if (_current is null)
|
||||
return new DialogActionResult(false, Translations.dialog_none);
|
||||
|
||||
revision = _current.Revision;
|
||||
_current = null;
|
||||
}
|
||||
|
||||
DialogCleared?.Invoke(revision);
|
||||
return new DialogActionResult(true, Translations.dialog_dismissed);
|
||||
}
|
||||
|
||||
public DialogActionResult SetInput(string key, string value)
|
||||
{
|
||||
lock (_lock)
|
||||
{
|
||||
if (_current is null)
|
||||
return new DialogActionResult(false, Translations.dialog_none);
|
||||
|
||||
var input = _current.Definition.Inputs.FirstOrDefault(input => input.Key.Equals(key, StringComparison.Ordinal));
|
||||
if (input is null)
|
||||
return new DialogActionResult(false, string.Format(Translations.dialog_input_unknown, key));
|
||||
|
||||
var normalized = NormalizeInputValue(input, value, out var error);
|
||||
if (error is not null)
|
||||
return new DialogActionResult(false, error);
|
||||
|
||||
var values = _current.Values.ToDictionary(static pair => pair.Key, static pair => pair.Value, StringComparer.Ordinal);
|
||||
values[key] = normalized;
|
||||
_current = _current with { Values = values };
|
||||
return new DialogActionResult(true, string.Format(Translations.dialog_input_set, key, normalized));
|
||||
}
|
||||
}
|
||||
|
||||
public DialogActionResult Click(int index)
|
||||
{
|
||||
DialogButton? button;
|
||||
DialogInstance? instance;
|
||||
lock (_lock)
|
||||
{
|
||||
instance = _current;
|
||||
button = instance?.Definition.Actions.FirstOrDefault(action => action.Index == index);
|
||||
}
|
||||
|
||||
if (instance is null)
|
||||
return new DialogActionResult(false, Translations.dialog_none);
|
||||
|
||||
if (button is null)
|
||||
return new DialogActionResult(false, string.Format(Translations.dialog_action_unknown, index));
|
||||
|
||||
return Execute(instance, button.Action, ShouldCloseAfterAction(instance.Definition.AfterAction));
|
||||
}
|
||||
|
||||
public DialogActionResult ClickLabel(string label)
|
||||
{
|
||||
DialogButton[] matches;
|
||||
DialogInstance? instance;
|
||||
lock (_lock)
|
||||
{
|
||||
instance = _current;
|
||||
matches = instance?.Definition.Actions
|
||||
.Where(action => action.Label.Equals(label, StringComparison.OrdinalIgnoreCase))
|
||||
.ToArray() ?? [];
|
||||
}
|
||||
|
||||
if (instance is null)
|
||||
return new DialogActionResult(false, Translations.dialog_none);
|
||||
|
||||
return matches.Length switch
|
||||
{
|
||||
0 => new DialogActionResult(false, string.Format(Translations.dialog_action_label_unknown, label)),
|
||||
> 1 => new DialogActionResult(false, string.Format(Translations.dialog_action_label_ambiguous, label)),
|
||||
_ => Execute(instance, matches[0].Action, ShouldCloseAfterAction(instance.Definition.AfterAction))
|
||||
};
|
||||
}
|
||||
|
||||
public DialogActionResult Cancel()
|
||||
{
|
||||
DialogInstance? instance;
|
||||
lock (_lock)
|
||||
instance = _current;
|
||||
|
||||
if (instance is null)
|
||||
return new DialogActionResult(false, Translations.dialog_none);
|
||||
|
||||
if (!instance.Definition.CanCloseWithEscape && instance.Definition.CancelAction is null)
|
||||
return new DialogActionResult(false, Translations.dialog_cannot_cancel);
|
||||
|
||||
return Execute(instance, instance.Definition.CancelAction, closeWhenDone: true);
|
||||
}
|
||||
|
||||
private DialogActionResult Execute(DialogInstance instance, DialogActionDefinition? action, bool closeWhenDone)
|
||||
{
|
||||
if (!instance.Definition.IsResolved)
|
||||
return new DialogActionResult(false, Translations.dialog_unresolved_action_disabled);
|
||||
|
||||
if (action is null || action.Kind == DialogActionKind.None)
|
||||
{
|
||||
if (closeWhenDone)
|
||||
_ = Dismiss();
|
||||
return new DialogActionResult(true, Translations.dialog_action_closed);
|
||||
}
|
||||
|
||||
var values = BuildActionValues(instance);
|
||||
switch (action.Kind)
|
||||
{
|
||||
case DialogActionKind.RunCommand:
|
||||
if (instance.Phase != DialogPhase.Play)
|
||||
return new DialogActionResult(false, Translations.dialog_action_command_not_in_play);
|
||||
|
||||
var command = ApplyTemplate(action.Value ?? string.Empty, values.TemplateValues);
|
||||
_client.SendText(command);
|
||||
if (closeWhenDone)
|
||||
_ = Dismiss();
|
||||
return new DialogActionResult(true, string.Format(Translations.dialog_action_command_sent, command));
|
||||
|
||||
case DialogActionKind.CustomClick:
|
||||
if (action.Id is null)
|
||||
return new DialogActionResult(false, Translations.dialog_action_invalid);
|
||||
|
||||
var payload = action.Type == "minecraft:custom" && action.Payload is null && values.TagValues.Count == 0
|
||||
? null
|
||||
: MergePayload(action.Payload, values.TagValues);
|
||||
if (!_client.SendCustomClickAction(action.Id, payload))
|
||||
return new DialogActionResult(false, Translations.dialog_action_custom_failed);
|
||||
|
||||
if (closeWhenDone)
|
||||
_ = Dismiss();
|
||||
return new DialogActionResult(true, string.Format(Translations.dialog_action_custom_sent, action.Id));
|
||||
|
||||
case DialogActionKind.ShowDialog:
|
||||
if (action.NestedDialog is not null)
|
||||
{
|
||||
Show(action.NestedDialog, instance.Phase);
|
||||
return new DialogActionResult(true, Translations.dialog_action_nested_opened);
|
||||
}
|
||||
|
||||
if (action.DialogReferenceId is int referenceId)
|
||||
{
|
||||
ShowRegistryReference(referenceId, instance.Phase);
|
||||
return new DialogActionResult(true, Translations.dialog_action_nested_opened);
|
||||
}
|
||||
|
||||
if (action.Value is not null)
|
||||
{
|
||||
DialogDefinition? referencedDialog;
|
||||
lock (_lock)
|
||||
_registryByName.TryGetValue(action.Value, out referencedDialog);
|
||||
|
||||
if (referencedDialog is not null)
|
||||
{
|
||||
Show(referencedDialog, instance.Phase);
|
||||
return new DialogActionResult(true, Translations.dialog_action_nested_opened);
|
||||
}
|
||||
}
|
||||
|
||||
return new DialogActionResult(false, Translations.dialog_action_invalid);
|
||||
|
||||
case DialogActionKind.OpenUrl:
|
||||
return new DialogActionResult(true, string.Format(Translations.dialog_action_open_url, action.Value ?? string.Empty));
|
||||
|
||||
case DialogActionKind.SuggestCommand:
|
||||
return new DialogActionResult(true, string.Format(Translations.dialog_action_suggest_command, action.Value ?? string.Empty));
|
||||
|
||||
case DialogActionKind.CopyToClipboard:
|
||||
return new DialogActionResult(true, string.Format(Translations.dialog_action_copy, action.Value ?? string.Empty));
|
||||
|
||||
default:
|
||||
return new DialogActionResult(false, string.Format(Translations.dialog_action_unsupported, action.Type ?? action.Kind.ToString()));
|
||||
}
|
||||
}
|
||||
|
||||
private static bool ShouldCloseAfterAction(DialogAfterAction afterAction)
|
||||
{
|
||||
return afterAction == DialogAfterAction.Close;
|
||||
}
|
||||
|
||||
private DialogDefinition ExpandServerLinks(DialogDefinition definition)
|
||||
{
|
||||
if (!definition.Type.Equals("minecraft:server_links", StringComparison.Ordinal))
|
||||
return definition;
|
||||
|
||||
var linkActions = _serverLinks
|
||||
.Select((link, index) => new DialogButton(
|
||||
index + 1,
|
||||
link.Label,
|
||||
new DialogActionDefinition(DialogActionKind.OpenUrl, Value: link.Url)))
|
||||
.ToList();
|
||||
|
||||
if (definition.Actions.Count > 0)
|
||||
linkActions.AddRange(definition.Actions.Select((button, i) => button with { Index = linkActions.Count + i + 1 }));
|
||||
|
||||
return definition with { Actions = linkActions };
|
||||
}
|
||||
|
||||
private static DialogActionValues BuildActionValues(DialogInstance instance)
|
||||
{
|
||||
Dictionary<string, string> templateValues = new(StringComparer.Ordinal);
|
||||
Dictionary<string, object> tagValues = new(StringComparer.Ordinal);
|
||||
|
||||
foreach (var input in instance.Definition.Inputs)
|
||||
{
|
||||
instance.Values.TryGetValue(input.Key, out var value);
|
||||
value ??= input.InitialValue;
|
||||
templateValues[input.Key] = ToTemplateValue(input, value);
|
||||
tagValues[input.Key] = ToNbtValue(input, value);
|
||||
}
|
||||
|
||||
return new DialogActionValues(templateValues, tagValues);
|
||||
}
|
||||
|
||||
private static Dictionary<string, object> MergePayload(Dictionary<string, object>? basePayload, Dictionary<string, object> inputTags)
|
||||
{
|
||||
Dictionary<string, object> payload = basePayload is null
|
||||
? new(StringComparer.Ordinal)
|
||||
: new(basePayload, StringComparer.Ordinal);
|
||||
|
||||
foreach (var (key, value) in inputTags)
|
||||
payload[key] = value;
|
||||
|
||||
return payload;
|
||||
}
|
||||
|
||||
private static string NormalizeInputValue(DialogInput input, string value, out string? error)
|
||||
{
|
||||
error = null;
|
||||
switch (input.Kind)
|
||||
{
|
||||
case DialogInputKind.Text:
|
||||
if (value.Length > input.MaxLength)
|
||||
{
|
||||
error = string.Format(Translations.dialog_input_too_long, input.Key, input.MaxLength);
|
||||
return input.InitialValue;
|
||||
}
|
||||
return value;
|
||||
|
||||
case DialogInputKind.Boolean:
|
||||
if (bool.TryParse(value, out var boolValue))
|
||||
return boolValue ? "true" : "false";
|
||||
|
||||
if (value.Equals(input.OnTrue, StringComparison.OrdinalIgnoreCase))
|
||||
return "true";
|
||||
|
||||
if (value.Equals(input.OnFalse, StringComparison.OrdinalIgnoreCase))
|
||||
return "false";
|
||||
|
||||
error = string.Format(Translations.dialog_input_boolean_invalid, input.Key);
|
||||
return input.InitialValue;
|
||||
|
||||
case DialogInputKind.SingleOption:
|
||||
if (input.Options?.Any(option => option.Id.Equals(value, StringComparison.Ordinal)) == true)
|
||||
return value;
|
||||
|
||||
error = string.Format(Translations.dialog_input_option_invalid, input.Key);
|
||||
return input.InitialValue;
|
||||
|
||||
case DialogInputKind.NumberRange:
|
||||
if (!float.TryParse(value, NumberStyles.Float, CultureInfo.InvariantCulture, out var number))
|
||||
{
|
||||
error = string.Format(Translations.dialog_input_number_invalid, input.Key);
|
||||
return input.InitialValue;
|
||||
}
|
||||
|
||||
var min = Math.Min(input.Start, input.End);
|
||||
var max = Math.Max(input.Start, input.End);
|
||||
if (number < min || number > max)
|
||||
{
|
||||
error = string.Format(CultureInfo.InvariantCulture, Translations.dialog_input_number_range_invalid, input.Key, min, max);
|
||||
return input.InitialValue;
|
||||
}
|
||||
|
||||
return NumberToString(number);
|
||||
|
||||
default:
|
||||
return value;
|
||||
}
|
||||
}
|
||||
|
||||
private static string ToTemplateValue(DialogInput input, string value)
|
||||
{
|
||||
return input.Kind switch
|
||||
{
|
||||
DialogInputKind.Boolean => value.Equals("true", StringComparison.OrdinalIgnoreCase) ? input.OnTrue : input.OnFalse,
|
||||
DialogInputKind.Text => EscapeStringTagWithoutQuotes(value),
|
||||
_ => value
|
||||
};
|
||||
}
|
||||
|
||||
private static object ToNbtValue(DialogInput input, string value)
|
||||
{
|
||||
return input.Kind switch
|
||||
{
|
||||
DialogInputKind.Boolean => (byte)(value.Equals("true", StringComparison.OrdinalIgnoreCase) ? 1 : 0),
|
||||
DialogInputKind.NumberRange when float.TryParse(value, NumberStyles.Float, CultureInfo.InvariantCulture, out var number) => number,
|
||||
_ => value
|
||||
};
|
||||
}
|
||||
|
||||
private static string ApplyTemplate(string template, IReadOnlyDictionary<string, string> values)
|
||||
{
|
||||
var result = template;
|
||||
foreach (var (key, value) in values)
|
||||
result = result.Replace("$(" + key + ")", value, StringComparison.Ordinal);
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
private static string EscapeStringTagWithoutQuotes(string value)
|
||||
{
|
||||
return value.Replace("\\", "\\\\", StringComparison.Ordinal).Replace("\"", "\\\"", StringComparison.Ordinal);
|
||||
}
|
||||
|
||||
private static string NumberToString(float value)
|
||||
{
|
||||
var integer = (int)value;
|
||||
return integer == value
|
||||
? integer.ToString(CultureInfo.InvariantCulture)
|
||||
: value.ToString(CultureInfo.InvariantCulture);
|
||||
}
|
||||
|
||||
private sealed record DialogActionValues(
|
||||
IReadOnlyDictionary<string, string> TemplateValues,
|
||||
Dictionary<string, object> TagValues);
|
||||
}
|
||||
110
MinecraftClient/Dialogs/DialogModels.cs
Normal file
110
MinecraftClient/Dialogs/DialogModels.cs
Normal file
|
|
@ -0,0 +1,110 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace MinecraftClient.Dialogs;
|
||||
|
||||
public enum DialogPhase
|
||||
{
|
||||
Configuration,
|
||||
Play
|
||||
}
|
||||
|
||||
public enum DialogAfterAction
|
||||
{
|
||||
Close,
|
||||
None,
|
||||
WaitForResponse
|
||||
}
|
||||
|
||||
public enum DialogBodyKind
|
||||
{
|
||||
PlainMessage,
|
||||
Item,
|
||||
Unknown
|
||||
}
|
||||
|
||||
public enum DialogInputKind
|
||||
{
|
||||
Text,
|
||||
Boolean,
|
||||
SingleOption,
|
||||
NumberRange,
|
||||
Unknown
|
||||
}
|
||||
|
||||
public enum DialogActionKind
|
||||
{
|
||||
None,
|
||||
RunCommand,
|
||||
CustomClick,
|
||||
ShowDialog,
|
||||
OpenUrl,
|
||||
SuggestCommand,
|
||||
CopyToClipboard,
|
||||
Unknown
|
||||
}
|
||||
|
||||
public sealed record DialogBody(DialogBodyKind Kind, string Text, string? Type = null);
|
||||
|
||||
public sealed record DialogOption(string Id, string Display, bool Initial)
|
||||
{
|
||||
public override string ToString()
|
||||
{
|
||||
return string.IsNullOrWhiteSpace(Display) ? Id : Display;
|
||||
}
|
||||
}
|
||||
|
||||
public sealed record DialogInput(
|
||||
string Key,
|
||||
DialogInputKind Kind,
|
||||
string Label,
|
||||
string InitialValue,
|
||||
int MaxLength = 32,
|
||||
bool LabelVisible = true,
|
||||
bool Multiline = false,
|
||||
IReadOnlyList<DialogOption>? Options = null,
|
||||
string OnTrue = "true",
|
||||
string OnFalse = "false",
|
||||
float Start = 0,
|
||||
float End = 1,
|
||||
float? InitialNumber = null,
|
||||
float? Step = null,
|
||||
string? Type = null);
|
||||
|
||||
public sealed record DialogActionDefinition(
|
||||
DialogActionKind Kind,
|
||||
string? Value = null,
|
||||
string? Id = null,
|
||||
Dictionary<string, object>? Payload = null,
|
||||
DialogDefinition? NestedDialog = null,
|
||||
int? DialogReferenceId = null,
|
||||
string? Type = null);
|
||||
|
||||
public sealed record DialogButton(int Index, string Label, DialogActionDefinition? Action, bool IsCancel = false);
|
||||
|
||||
public sealed record DialogServerLink(string Label, string Url);
|
||||
|
||||
public sealed record DialogDefinition(
|
||||
string Type,
|
||||
string Title,
|
||||
string? ExternalTitle,
|
||||
bool CanCloseWithEscape,
|
||||
bool Pause,
|
||||
DialogAfterAction AfterAction,
|
||||
IReadOnlyList<DialogBody> Body,
|
||||
IReadOnlyList<DialogInput> Inputs,
|
||||
IReadOnlyList<DialogButton> Actions,
|
||||
DialogActionDefinition? CancelAction,
|
||||
int Columns = 1,
|
||||
int ButtonWidth = 150,
|
||||
bool IsResolved = true,
|
||||
string? UnresolvedReference = null);
|
||||
|
||||
public sealed record DialogInstance(
|
||||
int Revision,
|
||||
DialogPhase Phase,
|
||||
DialogDefinition Definition,
|
||||
IReadOnlyDictionary<string, string> Values,
|
||||
DateTimeOffset ReceivedAt);
|
||||
|
||||
public sealed record DialogActionResult(bool Success, string Message);
|
||||
|
|
@ -59,9 +59,9 @@ namespace MinecraftClient
|
|||
/// </summary>
|
||||
public void Dispose()
|
||||
{
|
||||
if (monitor != null)
|
||||
if (monitor is not null)
|
||||
monitor.Item1.Dispose();
|
||||
if (polling != null)
|
||||
if (polling is not null)
|
||||
polling.Item2.Cancel();
|
||||
}
|
||||
|
||||
|
|
|
|||
73
MinecraftClient/IConsoleBackend.cs
Normal file
73
MinecraftClient/IConsoleBackend.cs
Normal file
|
|
@ -0,0 +1,73 @@
|
|||
using System;
|
||||
|
||||
namespace MinecraftClient
|
||||
{
|
||||
/// <summary>
|
||||
/// Input buffer state passed with OnInputChange events.
|
||||
/// </summary>
|
||||
public readonly struct ConsoleInputBuffer
|
||||
{
|
||||
public string Text { get; }
|
||||
public int CursorPosition { get; }
|
||||
|
||||
public ConsoleInputBuffer(string text, int cursorPosition)
|
||||
{
|
||||
Text = text;
|
||||
CursorPosition = cursorPosition;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Backend-independent suggestion item used by the TUI autocomplete popup.
|
||||
/// Mirrors the shape of ConsoleInteractive.ConsoleSuggestion.Suggestion
|
||||
/// without requiring a dependency on the ConsoleInteractive assembly.
|
||||
/// </summary>
|
||||
public readonly struct CommandSuggestion
|
||||
{
|
||||
public string Text { get; }
|
||||
public string Tooltip { get; }
|
||||
|
||||
public CommandSuggestion(string text, string tooltip = "")
|
||||
{
|
||||
Text = text;
|
||||
Tooltip = tooltip;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Abstraction over the console I/O backend.
|
||||
/// Implementations: ClassicConsoleBackend (ConsoleInteractive), TuiConsoleBackend (Avalonia/Consolonia), BasicConsoleBackend (stdio).
|
||||
/// </summary>
|
||||
public interface IConsoleBackend
|
||||
{
|
||||
void Init();
|
||||
|
||||
void WriteLine(string text);
|
||||
|
||||
void WriteLineFormatted(string text);
|
||||
|
||||
void BeginReadThread();
|
||||
|
||||
void StopReadThread();
|
||||
|
||||
event EventHandler<string>? MessageReceived;
|
||||
|
||||
event EventHandler<ConsoleInputBuffer>? OnInputChange;
|
||||
|
||||
string RequestImmediateInput();
|
||||
|
||||
string? ReadPassword();
|
||||
|
||||
void ClearInputBuffer();
|
||||
|
||||
void ClearScreen();
|
||||
|
||||
bool DisplayUserInput { get; set; }
|
||||
|
||||
void SetInputVisible(bool visible);
|
||||
|
||||
void SetBackreadBufferLimit(int limit);
|
||||
|
||||
void Shutdown();
|
||||
}
|
||||
}
|
||||
183
MinecraftClient/Inventory/BookContent.cs
Normal file
183
MinecraftClient/Inventory/BookContent.cs
Normal file
|
|
@ -0,0 +1,183 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text.Json;
|
||||
using MinecraftClient.Protocol.Handlers;
|
||||
using MinecraftClient.Protocol.Handlers.StructuredComponents.Components._1_20_6;
|
||||
using MinecraftClient.Protocol.Message;
|
||||
|
||||
namespace MinecraftClient.Inventory;
|
||||
|
||||
public enum BookHand
|
||||
{
|
||||
Main = 0,
|
||||
Off = 1
|
||||
}
|
||||
|
||||
public sealed record BookLimits(int MaxPages, int MaxPageLength, int MaxTitleLength)
|
||||
{
|
||||
public static BookLimits ForProtocol(int protocolVersion)
|
||||
{
|
||||
int maxPageLength = protocolVersion switch
|
||||
{
|
||||
>= Protocol18Handler.MC_1_21_2_Version => 1024,
|
||||
>= Protocol18Handler.MC_1_17_Version => 8192,
|
||||
_ => 32767
|
||||
};
|
||||
|
||||
int maxTitleLength = protocolVersion switch
|
||||
{
|
||||
>= Protocol18Handler.MC_1_21_2_Version => 32,
|
||||
>= Protocol18Handler.MC_1_17_Version => 128,
|
||||
_ => 16
|
||||
};
|
||||
|
||||
return new BookLimits(100, maxPageLength, maxTitleLength);
|
||||
}
|
||||
}
|
||||
|
||||
public sealed record BookContent(
|
||||
IReadOnlyList<string> Pages,
|
||||
string? Title,
|
||||
string? Author,
|
||||
int Generation,
|
||||
bool IsSigned)
|
||||
{
|
||||
public static BookContent EmptyWritable { get; } = new([string.Empty], null, null, 0, false);
|
||||
}
|
||||
|
||||
public static class BookContentHelper
|
||||
{
|
||||
public static bool IsBook(Item? item) => item?.Type is ItemType.WritableBook or ItemType.WrittenBook;
|
||||
|
||||
public static bool IsWritableBook(Item? item) => item?.Type == ItemType.WritableBook;
|
||||
|
||||
public static bool TryRead(Item? item, out BookContent content)
|
||||
{
|
||||
content = BookContent.EmptyWritable;
|
||||
|
||||
if (item is null || item.IsEmpty)
|
||||
return false;
|
||||
|
||||
return item.Type switch
|
||||
{
|
||||
ItemType.WritableBook => TryReadWritable(item, out content),
|
||||
ItemType.WrittenBook => TryReadWritten(item, out content),
|
||||
_ => false
|
||||
};
|
||||
}
|
||||
|
||||
public static Item CreateWritablePayload(Item currentBook, IReadOnlyList<string> pages)
|
||||
{
|
||||
return new Item(ItemType.WritableBook, 1, currentBook.Data, new Dictionary<string, object>
|
||||
{
|
||||
["pages"] = pages.Cast<object>().ToArray()
|
||||
});
|
||||
}
|
||||
|
||||
public static Item CreateWrittenPayload(Item currentBook, IReadOnlyList<string> pages, string title, string author, bool encodePagesAsJson)
|
||||
{
|
||||
object[] encodedPages = pages
|
||||
.Select(page => encodePagesAsJson ? ToJsonTextComponent(page) : page)
|
||||
.Cast<object>()
|
||||
.ToArray();
|
||||
|
||||
return new Item(ItemType.WrittenBook, 1, currentBook.Data, new Dictionary<string, object>
|
||||
{
|
||||
["author"] = author,
|
||||
["title"] = title,
|
||||
["pages"] = encodedPages
|
||||
});
|
||||
}
|
||||
|
||||
public static IReadOnlyList<string> NormalizePages(IEnumerable<string> pages)
|
||||
{
|
||||
string[] normalized = pages.Select(page => page ?? string.Empty).ToArray();
|
||||
return normalized.Length == 0 ? [string.Empty] : normalized;
|
||||
}
|
||||
|
||||
private static bool TryReadWritable(Item item, out BookContent content)
|
||||
{
|
||||
if (item.Components is not null)
|
||||
{
|
||||
var component = item.Components.OfType<WritableBookContentComponent>().FirstOrDefault();
|
||||
if (component is not null)
|
||||
{
|
||||
content = new BookContent(
|
||||
NormalizePages(component.Pages.Select(page => page.RawContent)),
|
||||
null,
|
||||
null,
|
||||
0,
|
||||
IsSigned: false);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
content = new BookContent(ReadStringList(item.NBT, "pages", parseJson: false), null, null, 0, IsSigned: false);
|
||||
return true;
|
||||
}
|
||||
|
||||
private static bool TryReadWritten(Item item, out BookContent content)
|
||||
{
|
||||
if (item.Components is not null)
|
||||
{
|
||||
var component = item.Components.OfType<WrittenBookContentComponent>().FirstOrDefault();
|
||||
if (component is not null)
|
||||
{
|
||||
content = new BookContent(
|
||||
NormalizePages(component.Pages.Select(page => page.RawContent)),
|
||||
component.RawTitle,
|
||||
component.Author,
|
||||
component.Generation,
|
||||
IsSigned: true);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
string? title = ReadString(item.NBT, "title");
|
||||
string? author = ReadString(item.NBT, "author");
|
||||
int generation = ReadInt(item.NBT, "generation");
|
||||
content = new BookContent(ReadStringList(item.NBT, "pages", parseJson: true), title, author, generation, IsSigned: true);
|
||||
return true;
|
||||
}
|
||||
|
||||
private static IReadOnlyList<string> ReadStringList(Dictionary<string, object>? nbt, string key, bool parseJson)
|
||||
{
|
||||
if (nbt is null || !nbt.TryGetValue(key, out object? value) || value is not object[] values)
|
||||
return [string.Empty];
|
||||
|
||||
string[] pages = values
|
||||
.Select(value => value?.ToString() ?? string.Empty)
|
||||
.Select(value => parseJson ? ChatParser.ParseText(value) : value)
|
||||
.ToArray();
|
||||
|
||||
return pages.Length == 0 ? [string.Empty] : pages;
|
||||
}
|
||||
|
||||
private static string? ReadString(Dictionary<string, object>? nbt, string key)
|
||||
{
|
||||
return nbt is not null && nbt.TryGetValue(key, out object? value)
|
||||
? value?.ToString()
|
||||
: null;
|
||||
}
|
||||
|
||||
private static int ReadInt(Dictionary<string, object>? nbt, string key)
|
||||
{
|
||||
if (nbt is null || !nbt.TryGetValue(key, out object? value) || value is null)
|
||||
return 0;
|
||||
|
||||
return value switch
|
||||
{
|
||||
int i => i,
|
||||
short s => s,
|
||||
byte b => b,
|
||||
_ when int.TryParse(value.ToString(), out int parsed) => parsed,
|
||||
_ => 0
|
||||
};
|
||||
}
|
||||
|
||||
private static string ToJsonTextComponent(string text)
|
||||
{
|
||||
return JsonSerializer.Serialize(new Dictionary<string, string> { ["text"] = text });
|
||||
}
|
||||
}
|
||||
10
MinecraftClient/Inventory/BookPage.cs
Normal file
10
MinecraftClient/Inventory/BookPage.cs
Normal file
|
|
@ -0,0 +1,10 @@
|
|||
using System.Collections.Generic;
|
||||
|
||||
namespace MinecraftClient.Inventory;
|
||||
|
||||
public record BookPage(
|
||||
string RawContent,
|
||||
bool HasFilteredContent,
|
||||
string? FilteredContent,
|
||||
Dictionary<string, object>? RawContentNbt = null,
|
||||
Dictionary<string, object>? FilteredContentNbt = null);
|
||||
|
|
@ -1,4 +1,4 @@
|
|||
using System.Collections.Generic;
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace MinecraftClient.Inventory
|
||||
{
|
||||
|
|
@ -50,8 +50,8 @@ namespace MinecraftClient.Inventory
|
|||
ID = id;
|
||||
Type = type;
|
||||
Title = title;
|
||||
Items = new Dictionary<int, Item>();
|
||||
Properties = new Dictionary<int, short>();
|
||||
Items = new();
|
||||
Properties = new();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
|
|
@ -67,7 +67,7 @@ namespace MinecraftClient.Inventory
|
|||
Type = type;
|
||||
Title = title;
|
||||
Items = items;
|
||||
Properties = new Dictionary<int, short>();
|
||||
Properties = new();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
|
|
@ -81,8 +81,8 @@ namespace MinecraftClient.Inventory
|
|||
ID = id;
|
||||
Title = title;
|
||||
Type = ConvertType.ToNew(type);
|
||||
Items = new Dictionary<int, Item>();
|
||||
Properties = new Dictionary<int, short>();
|
||||
Items = new();
|
||||
Properties = new();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
|
|
@ -91,13 +91,14 @@ namespace MinecraftClient.Inventory
|
|||
/// <param name="id">Container ID</param>
|
||||
/// <param name="typeID">Container Type</param>
|
||||
/// <param name="title">Container Title</param>
|
||||
public Container(int id, int typeID, string title)
|
||||
/// <param name="protocolVersion">Protocol version for version-specific mapping</param>
|
||||
public Container(int id, int typeID, string title, int protocolVersion = 0)
|
||||
{
|
||||
ID = id;
|
||||
Type = GetContainerType(typeID);
|
||||
Type = GetContainerType(typeID, protocolVersion);
|
||||
Title = title;
|
||||
Items = new Dictionary<int, Item>();
|
||||
Properties = new Dictionary<int, short>();
|
||||
Items = new();
|
||||
Properties = new();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
|
|
@ -109,8 +110,8 @@ namespace MinecraftClient.Inventory
|
|||
ID = -1;
|
||||
Type = type;
|
||||
Title = null;
|
||||
Items = new Dictionary<int, Item>();
|
||||
Properties = new Dictionary<int, short>();
|
||||
Items = new();
|
||||
Properties = new();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
|
|
@ -124,29 +125,69 @@ namespace MinecraftClient.Inventory
|
|||
Type = type;
|
||||
Title = null;
|
||||
Items = items;
|
||||
Properties = new Dictionary<int, short>();
|
||||
Properties = new();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Get container type from Type ID
|
||||
/// </summary>
|
||||
/// <param name="typeID">Container Type ID</param>
|
||||
/// <param name="protocolVersion">Protocol version (menu registry changed across versions)</param>
|
||||
/// <returns>Container Type</returns>
|
||||
public static ContainerType GetContainerType(int typeID)
|
||||
public static ContainerType GetContainerType(int typeID, int protocolVersion = 0)
|
||||
{
|
||||
// https://wiki.vg/Inventory didn't state the inventory ID, assume that list start with 0
|
||||
// MC 1.20.4 (protocol 765) added crafter_3x3 at index 7, shifting all subsequent IDs by +1.
|
||||
// Registry order from decompiled MenuType.java:
|
||||
// 1.14-1.20.2: generic_9x1..generic_3x3(6), anvil(7), beacon(8), ... stonecutter(22)
|
||||
// 1.20.4+: generic_9x1..generic_3x3(6), crafter_3x3(7), anvil(8), beacon(9), ... stonecutter(24)
|
||||
if (protocolVersion >= 765)
|
||||
{
|
||||
return typeID switch
|
||||
{
|
||||
#pragma warning disable format // @formatter:off
|
||||
0 => ContainerType.Generic_9x1,
|
||||
1 => ContainerType.Generic_9x2,
|
||||
2 => ContainerType.Generic_9x3,
|
||||
3 => ContainerType.Generic_9x4,
|
||||
4 => ContainerType.Generic_9x5,
|
||||
5 => ContainerType.Generic_9x6,
|
||||
6 => ContainerType.Generic_3x3,
|
||||
7 => ContainerType.Crafter,
|
||||
8 => ContainerType.Anvil,
|
||||
9 => ContainerType.Beacon,
|
||||
10 => ContainerType.BlastFurnace,
|
||||
11 => ContainerType.BrewingStand,
|
||||
12 => ContainerType.Crafting,
|
||||
13 => ContainerType.Enchantment,
|
||||
14 => ContainerType.Furnace,
|
||||
15 => ContainerType.Grindstone,
|
||||
16 => ContainerType.Hopper,
|
||||
17 => ContainerType.Lectern,
|
||||
18 => ContainerType.Loom,
|
||||
19 => ContainerType.Merchant,
|
||||
20 => ContainerType.ShulkerBox,
|
||||
21 => ContainerType.SmightingTable,
|
||||
22 => ContainerType.Smoker,
|
||||
23 => ContainerType.Cartography,
|
||||
24 => ContainerType.Stonecutter,
|
||||
_ => ContainerType.Unknown,
|
||||
#pragma warning restore format // @formatter:on
|
||||
};
|
||||
}
|
||||
|
||||
return typeID switch
|
||||
{
|
||||
0 => ContainerType.Generic_9x1,
|
||||
1 => ContainerType.Generic_9x2,
|
||||
2 => ContainerType.Generic_9x3,
|
||||
3 => ContainerType.Generic_9x4,
|
||||
4 => ContainerType.Generic_9x5,
|
||||
5 => ContainerType.Generic_9x6,
|
||||
6 => ContainerType.Generic_3x3,
|
||||
7 => ContainerType.Anvil,
|
||||
8 => ContainerType.Beacon,
|
||||
9 => ContainerType.BlastFurnace,
|
||||
#pragma warning disable format // @formatter:off
|
||||
0 => ContainerType.Generic_9x1,
|
||||
1 => ContainerType.Generic_9x2,
|
||||
2 => ContainerType.Generic_9x3,
|
||||
3 => ContainerType.Generic_9x4,
|
||||
4 => ContainerType.Generic_9x5,
|
||||
5 => ContainerType.Generic_9x6,
|
||||
6 => ContainerType.Generic_3x3,
|
||||
7 => ContainerType.Anvil,
|
||||
8 => ContainerType.Beacon,
|
||||
9 => ContainerType.BlastFurnace,
|
||||
10 => ContainerType.BrewingStand,
|
||||
11 => ContainerType.Crafting,
|
||||
12 => ContainerType.Enchantment,
|
||||
|
|
@ -160,7 +201,8 @@ namespace MinecraftClient.Inventory
|
|||
20 => ContainerType.Smoker,
|
||||
21 => ContainerType.Cartography,
|
||||
22 => ContainerType.Stonecutter,
|
||||
_ => ContainerType.Unknown,
|
||||
_ => ContainerType.Unknown,
|
||||
#pragma warning restore format // @formatter:on
|
||||
};
|
||||
}
|
||||
|
||||
|
|
@ -172,7 +214,7 @@ namespace MinecraftClient.Inventory
|
|||
public int[] SearchItem(ItemType itemType)
|
||||
{
|
||||
List<int> result = new();
|
||||
if (Items != null)
|
||||
if (Items is not null)
|
||||
{
|
||||
foreach (var item in Items)
|
||||
{
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
namespace MinecraftClient.Inventory
|
||||
namespace MinecraftClient.Inventory
|
||||
{
|
||||
// For MC 1.14 after ONLY
|
||||
public enum ContainerType
|
||||
|
|
@ -10,6 +10,7 @@
|
|||
Generic_9x5,
|
||||
Generic_9x6,
|
||||
Generic_3x3,
|
||||
Crafter,
|
||||
Anvil,
|
||||
Beacon,
|
||||
BlastFurnace,
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
namespace MinecraftClient.Inventory
|
||||
namespace MinecraftClient.Inventory
|
||||
{
|
||||
public static class ContainerTypeExtensions
|
||||
{
|
||||
|
|
@ -13,9 +13,14 @@
|
|||
{
|
||||
#pragma warning disable format // @formatter:off
|
||||
ContainerType.PlayerInventory => 46,
|
||||
ContainerType.Generic_9x1 => 45,
|
||||
ContainerType.Generic_9x2 => 54,
|
||||
ContainerType.Generic_9x3 => 63,
|
||||
ContainerType.Generic_9x4 => 72,
|
||||
ContainerType.Generic_9x5 => 81,
|
||||
ContainerType.Generic_9x6 => 90,
|
||||
ContainerType.Generic_3x3 => 45,
|
||||
ContainerType.Crafter => 45,
|
||||
ContainerType.Crafting => 46,
|
||||
ContainerType.BlastFurnace => 39,
|
||||
ContainerType.Furnace => 39,
|
||||
|
|
@ -27,6 +32,7 @@
|
|||
ContainerType.Anvil => 39,
|
||||
ContainerType.Hopper => 41,
|
||||
ContainerType.ShulkerBox => 63,
|
||||
ContainerType.SmightingTable => 39,
|
||||
ContainerType.Loom => 40,
|
||||
ContainerType.Stonecutter => 38,
|
||||
ContainerType.Lectern => 37,
|
||||
|
|
@ -52,6 +58,7 @@
|
|||
ContainerType.Generic_9x3 => AsciiArt.Container_Generic_9x3,
|
||||
ContainerType.Generic_9x6 => AsciiArt.Container_Generic_9x6,
|
||||
ContainerType.Generic_3x3 => AsciiArt.Container_Generic_3x3,
|
||||
ContainerType.Crafter => AsciiArt.Container_Generic_3x3,
|
||||
ContainerType.Crafting => AsciiArt.Container_Crafting,
|
||||
ContainerType.BlastFurnace => AsciiArt.Container_Furnace,
|
||||
ContainerType.Furnace => AsciiArt.Container_Furnace,
|
||||
|
|
|
|||
194
MinecraftClient/Inventory/EffectData.cs
Normal file
194
MinecraftClient/Inventory/EffectData.cs
Normal file
|
|
@ -0,0 +1,194 @@
|
|||
namespace MinecraftClient.Inventory;
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using MinecraftClient.Protocol;
|
||||
using MinecraftClient.Protocol.Message;
|
||||
|
||||
/// <summary>
|
||||
/// Represents an active status effect on an entity
|
||||
/// </summary>
|
||||
public class EffectData
|
||||
{
|
||||
/// <summary>
|
||||
/// The type of effect
|
||||
/// </summary>
|
||||
public Effects Effect { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Effect amplifier (level - 1, e.g., 0 = level I, 1 = level II)
|
||||
/// </summary>
|
||||
public int Amplifier { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Duration in ticks (20 ticks = 1 second). -1 for infinite.
|
||||
/// </summary>
|
||||
public int Duration { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Effect flags (ambient, show particles, show icon)
|
||||
/// </summary>
|
||||
public byte Flags { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Time when the effect was applied
|
||||
/// </summary>
|
||||
public DateTime StartTime { get; set; }
|
||||
|
||||
public EffectData(Effects effect, int amplifier, int duration, byte flags)
|
||||
{
|
||||
Effect = effect;
|
||||
Amplifier = amplifier;
|
||||
Duration = duration;
|
||||
Flags = flags;
|
||||
StartTime = DateTime.UtcNow;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Check if this is an infinite duration effect
|
||||
/// </summary>
|
||||
public bool IsInfinite => Duration == -1 || Duration == int.MaxValue;
|
||||
|
||||
/// <summary>
|
||||
/// Check if the effect has expired
|
||||
/// </summary>
|
||||
public bool IsExpired
|
||||
{
|
||||
get
|
||||
{
|
||||
if (IsInfinite) return false;
|
||||
return GetElapsedTicks() >= Duration;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Get remaining duration in ticks
|
||||
/// </summary>
|
||||
public int RemainingTicks
|
||||
{
|
||||
get
|
||||
{
|
||||
if (IsInfinite) return -1;
|
||||
return Math.Max(0, Duration - GetElapsedTicks());
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Get remaining duration in seconds
|
||||
/// </summary>
|
||||
public int RemainingSeconds
|
||||
{
|
||||
get
|
||||
{
|
||||
if (IsInfinite) return -1;
|
||||
return (RemainingTicks + 19) / 20;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Get the translated effect name from Minecraft translations
|
||||
/// </summary>
|
||||
public string GetTranslatedName()
|
||||
{
|
||||
var key = $"effect.minecraft.{Effect.ToString().ToUnderscoreCase()}";
|
||||
var translated = ChatParser.TranslateString(key);
|
||||
return string.IsNullOrEmpty(translated) ? Effect.ToString() : translated;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Get the translated effect name with level when applicable
|
||||
/// </summary>
|
||||
public string GetDisplayName()
|
||||
{
|
||||
string translatedName = GetTranslatedName();
|
||||
if (Amplifier <= 0)
|
||||
return translatedName;
|
||||
|
||||
return string.Format(Translations.effect_name_with_amplifier, translatedName,
|
||||
EnchantmentMapping.ConvertLevelToRomanNumbers(Amplifier + 1));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Get the translated effect name prefixed with the best-fit indefinite article
|
||||
/// </summary>
|
||||
public string GetDisplayNameWithArticle()
|
||||
{
|
||||
string displayName = GetDisplayName();
|
||||
char? firstLetter = displayName
|
||||
.TrimStart()
|
||||
.FirstOrDefault(char.IsLetter);
|
||||
|
||||
if (firstLetter is null)
|
||||
return displayName;
|
||||
|
||||
string article = "AEIOUaeiou".Contains(firstLetter.Value)
|
||||
? Translations.effect_article_an
|
||||
: Translations.effect_article_a;
|
||||
return $"{article} {displayName}";
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Get the configured short duration label for the remaining time
|
||||
/// </summary>
|
||||
public string GetRemainingDurationText()
|
||||
{
|
||||
return FormatShortDuration(RemainingSeconds);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Get the configured short duration label for the initial effect duration
|
||||
/// </summary>
|
||||
public string GetInitialDurationText()
|
||||
{
|
||||
if (IsInfinite)
|
||||
return Translations.effect_duration_unlimited;
|
||||
|
||||
int durationSeconds = (Duration + 19) / 20;
|
||||
return FormatShortDuration(durationSeconds);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Format a duration for compact UI output
|
||||
/// </summary>
|
||||
/// <param name="seconds">Duration in seconds, -1 for unlimited</param>
|
||||
public static string FormatShortDuration(int seconds)
|
||||
{
|
||||
if (seconds < 0)
|
||||
return Translations.effect_duration_short_unlimited;
|
||||
|
||||
if (seconds < 60)
|
||||
return string.Format(Translations.effect_duration_short_seconds, seconds);
|
||||
|
||||
int minutes = seconds / 60;
|
||||
int remainingSeconds = seconds % 60;
|
||||
if (seconds < 3600)
|
||||
{
|
||||
return remainingSeconds == 0
|
||||
? string.Format(Translations.effect_duration_short_minutes, minutes)
|
||||
: string.Format(Translations.effect_duration_short_minutes_seconds, minutes, remainingSeconds);
|
||||
}
|
||||
|
||||
int hours = seconds / 3600;
|
||||
int remainingMinutes = (seconds % 3600) / 60;
|
||||
return remainingMinutes == 0
|
||||
? string.Format(Translations.effect_duration_short_hours, hours)
|
||||
: string.Format(Translations.effect_duration_short_hours_minutes, hours, remainingMinutes);
|
||||
}
|
||||
|
||||
private int GetElapsedTicks()
|
||||
{
|
||||
return (int)((DateTime.UtcNow - StartTime).TotalMilliseconds / 50);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Extension method for converting PascalCase to snake_case
|
||||
/// </summary>
|
||||
public static class StringExtensions
|
||||
{
|
||||
public static string ToUnderscoreCase(this string str)
|
||||
{
|
||||
return string.Concat(str.Select((x, i) => i > 0 && char.IsUpper(x) ? "_" + x.ToString() : x.ToString())).ToLower();
|
||||
}
|
||||
}
|
||||
3
MinecraftClient/Inventory/Enchantment.cs
Normal file
3
MinecraftClient/Inventory/Enchantment.cs
Normal file
|
|
@ -0,0 +1,3 @@
|
|||
namespace MinecraftClient.Inventory;
|
||||
|
||||
public record Enchantment(Enchantments Type, int Level);
|
||||
|
|
@ -1,10 +1,10 @@
|
|||
namespace MinecraftClient.Inventory
|
||||
{
|
||||
public class EnchantmentData
|
||||
public record EnchantmentData
|
||||
{
|
||||
public Enchantment TopEnchantment { get; set; }
|
||||
public Enchantment MiddleEnchantment { get; set; }
|
||||
public Enchantment BottomEnchantment { get; set; }
|
||||
public Enchantments TopEnchantment { get; set; }
|
||||
public Enchantments MiddleEnchantment { get; set; }
|
||||
public Enchantments BottomEnchantment { get; set; }
|
||||
|
||||
// Seed for rendering Standard Galactic Language (symbols in the enchanting table) (Useful for poeple who use MCC for the protocol)
|
||||
public short Seed { get; set; }
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
using System;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using MinecraftClient.Protocol.Handlers;
|
||||
|
|
@ -10,168 +10,355 @@ namespace MinecraftClient.Inventory
|
|||
{
|
||||
#pragma warning disable format // @formatter:off
|
||||
// 1.14 - 1.15.2
|
||||
private static Dictionary<short, Enchantment> enchantmentMappings114 = new Dictionary<short, Enchantment>()
|
||||
private static Dictionary<short, Enchantments> enchantmentMappings114 = new()
|
||||
{
|
||||
//id type
|
||||
{ 0, Enchantment.Protection },
|
||||
{ 1, Enchantment.FireProtection },
|
||||
{ 2, Enchantment.FeatherFalling },
|
||||
{ 3, Enchantment.BlastProtection },
|
||||
{ 4, Enchantment.ProjectileProtection },
|
||||
{ 5, Enchantment.Respiration },
|
||||
{ 6, Enchantment.AquaAffinity },
|
||||
{ 7, Enchantment.Thorns },
|
||||
{ 8, Enchantment.DepthStrieder },
|
||||
{ 9, Enchantment.FrostWalker },
|
||||
{ 10, Enchantment.BindingCurse },
|
||||
{ 11, Enchantment.Sharpness },
|
||||
{ 12, Enchantment.Smite },
|
||||
{ 13, Enchantment.BaneOfArthropods },
|
||||
{ 14, Enchantment.Knockback },
|
||||
{ 15, Enchantment.FireAspect },
|
||||
{ 16, Enchantment.Looting },
|
||||
{ 17, Enchantment.Sweeping },
|
||||
{ 18, Enchantment.Efficency },
|
||||
{ 19, Enchantment.SilkTouch },
|
||||
{ 20, Enchantment.Unbreaking },
|
||||
{ 21, Enchantment.Fortune },
|
||||
{ 22, Enchantment.Power },
|
||||
{ 23, Enchantment.Punch },
|
||||
{ 24, Enchantment.Flame },
|
||||
{ 25, Enchantment.Infinity },
|
||||
{ 26, Enchantment.LuckOfTheSea },
|
||||
{ 27, Enchantment.Lure },
|
||||
{ 28, Enchantment.Loyality },
|
||||
{ 29, Enchantment.Impaling },
|
||||
{ 30, Enchantment.Riptide },
|
||||
{ 31, Enchantment.Channeling },
|
||||
{ 32, Enchantment.Mending },
|
||||
{ 33, Enchantment.VanishingCurse }
|
||||
{ 0, Enchantments.Protection },
|
||||
{ 1, Enchantments.FireProtection },
|
||||
{ 2, Enchantments.FeatherFalling },
|
||||
{ 3, Enchantments.BlastProtection },
|
||||
{ 4, Enchantments.ProjectileProtection },
|
||||
{ 5, Enchantments.Respiration },
|
||||
{ 6, Enchantments.AquaAffinity },
|
||||
{ 7, Enchantments.Thorns },
|
||||
{ 8, Enchantments.DepthStrider },
|
||||
{ 9, Enchantments.FrostWalker },
|
||||
{ 10, Enchantments.BindingCurse },
|
||||
{ 11, Enchantments.Sharpness },
|
||||
{ 12, Enchantments.Smite },
|
||||
{ 13, Enchantments.BaneOfArthropods },
|
||||
{ 14, Enchantments.Knockback },
|
||||
{ 15, Enchantments.FireAspect },
|
||||
{ 16, Enchantments.Looting },
|
||||
{ 17, Enchantments.Sweeping },
|
||||
{ 18, Enchantments.Efficiency },
|
||||
{ 19, Enchantments.SilkTouch },
|
||||
{ 20, Enchantments.Unbreaking },
|
||||
{ 21, Enchantments.Fortune },
|
||||
{ 22, Enchantments.Power },
|
||||
{ 23, Enchantments.Punch },
|
||||
{ 24, Enchantments.Flame },
|
||||
{ 25, Enchantments.Infinity },
|
||||
{ 26, Enchantments.LuckOfTheSea },
|
||||
{ 27, Enchantments.Lure },
|
||||
{ 28, Enchantments.Loyalty },
|
||||
{ 29, Enchantments.Impaling },
|
||||
{ 30, Enchantments.Riptide },
|
||||
{ 31, Enchantments.Channeling },
|
||||
{ 32, Enchantments.Mending },
|
||||
{ 33, Enchantments.VanishingCurse }
|
||||
};
|
||||
|
||||
// 1.16 - 1.18
|
||||
private static Dictionary<short, Enchantment> enchantmentMappings116 = new Dictionary<short, Enchantment>()
|
||||
private static Dictionary<short, Enchantments> enchantmentMappings116 = new()
|
||||
{
|
||||
//id type
|
||||
{ 0, Enchantment.Protection },
|
||||
{ 1, Enchantment.FireProtection },
|
||||
{ 2, Enchantment.FeatherFalling },
|
||||
{ 3, Enchantment.BlastProtection },
|
||||
{ 4, Enchantment.ProjectileProtection },
|
||||
{ 5, Enchantment.Respiration },
|
||||
{ 6, Enchantment.AquaAffinity },
|
||||
{ 7, Enchantment.Thorns },
|
||||
{ 8, Enchantment.DepthStrieder },
|
||||
{ 9, Enchantment.FrostWalker },
|
||||
{ 10, Enchantment.BindingCurse },
|
||||
{ 11, Enchantment.SoulSpeed },
|
||||
{ 12, Enchantment.Sharpness },
|
||||
{ 13, Enchantment.Smite },
|
||||
{ 14, Enchantment.BaneOfArthropods },
|
||||
{ 15, Enchantment.Knockback },
|
||||
{ 16, Enchantment.FireAspect },
|
||||
{ 17, Enchantment.Looting },
|
||||
{ 18, Enchantment.Sweeping },
|
||||
{ 19, Enchantment.Efficency },
|
||||
{ 20, Enchantment.SilkTouch },
|
||||
{ 21, Enchantment.Unbreaking },
|
||||
{ 22, Enchantment.Fortune },
|
||||
{ 23, Enchantment.Power },
|
||||
{ 24, Enchantment.Punch },
|
||||
{ 25, Enchantment.Flame },
|
||||
{ 26, Enchantment.Infinity },
|
||||
{ 27, Enchantment.LuckOfTheSea },
|
||||
{ 28, Enchantment.Lure },
|
||||
{ 29, Enchantment.Loyality },
|
||||
{ 30, Enchantment.Impaling },
|
||||
{ 31, Enchantment.Riptide },
|
||||
{ 32, Enchantment.Channeling },
|
||||
{ 33, Enchantment.Multishot },
|
||||
{ 34, Enchantment.QuickCharge },
|
||||
{ 35, Enchantment.Piercing },
|
||||
{ 36, Enchantment.Mending },
|
||||
{ 37, Enchantment.VanishingCurse }
|
||||
{ 0, Enchantments.Protection },
|
||||
{ 1, Enchantments.FireProtection },
|
||||
{ 2, Enchantments.FeatherFalling },
|
||||
{ 3, Enchantments.BlastProtection },
|
||||
{ 4, Enchantments.ProjectileProtection },
|
||||
{ 5, Enchantments.Respiration },
|
||||
{ 6, Enchantments.AquaAffinity },
|
||||
{ 7, Enchantments.Thorns },
|
||||
{ 8, Enchantments.DepthStrider },
|
||||
{ 9, Enchantments.FrostWalker },
|
||||
{ 10, Enchantments.BindingCurse },
|
||||
{ 11, Enchantments.SoulSpeed },
|
||||
{ 12, Enchantments.Sharpness },
|
||||
{ 13, Enchantments.Smite },
|
||||
{ 14, Enchantments.BaneOfArthropods },
|
||||
{ 15, Enchantments.Knockback },
|
||||
{ 16, Enchantments.FireAspect },
|
||||
{ 17, Enchantments.Looting },
|
||||
{ 18, Enchantments.Sweeping },
|
||||
{ 19, Enchantments.Efficiency },
|
||||
{ 20, Enchantments.SilkTouch },
|
||||
{ 21, Enchantments.Unbreaking },
|
||||
{ 22, Enchantments.Fortune },
|
||||
{ 23, Enchantments.Power },
|
||||
{ 24, Enchantments.Punch },
|
||||
{ 25, Enchantments.Flame },
|
||||
{ 26, Enchantments.Infinity },
|
||||
{ 27, Enchantments.LuckOfTheSea },
|
||||
{ 28, Enchantments.Lure },
|
||||
{ 29, Enchantments.Loyalty },
|
||||
{ 30, Enchantments.Impaling },
|
||||
{ 31, Enchantments.Riptide },
|
||||
{ 32, Enchantments.Channeling },
|
||||
{ 33, Enchantments.Multishot },
|
||||
{ 34, Enchantments.QuickCharge },
|
||||
{ 35, Enchantments.Piercing },
|
||||
{ 36, Enchantments.Mending },
|
||||
{ 37, Enchantments.VanishingCurse }
|
||||
};
|
||||
|
||||
// 1.19+
|
||||
private static Dictionary<short, Enchantment> enchantmentMappings = new Dictionary<short, Enchantment>()
|
||||
// 1.19 - 1.20.4
|
||||
private static Dictionary<short, Enchantments> enchantmentMappings119 = new()
|
||||
{
|
||||
//id type
|
||||
{ 0, Enchantment.Protection },
|
||||
{ 1, Enchantment.FireProtection },
|
||||
{ 2, Enchantment.FeatherFalling },
|
||||
{ 3, Enchantment.BlastProtection },
|
||||
{ 4, Enchantment.ProjectileProtection },
|
||||
{ 5, Enchantment.Respiration },
|
||||
{ 6, Enchantment.AquaAffinity },
|
||||
{ 7, Enchantment.Thorns },
|
||||
{ 8, Enchantment.DepthStrieder },
|
||||
{ 9, Enchantment.FrostWalker },
|
||||
{ 10, Enchantment.BindingCurse },
|
||||
{ 11, Enchantment.SoulSpeed },
|
||||
{ 12, Enchantment.SwiftSneak },
|
||||
{ 13, Enchantment.Sharpness },
|
||||
{ 14, Enchantment.Smite },
|
||||
{ 15, Enchantment.BaneOfArthropods },
|
||||
{ 16, Enchantment.Knockback },
|
||||
{ 17, Enchantment.FireAspect },
|
||||
{ 18, Enchantment.Looting },
|
||||
{ 19, Enchantment.Sweeping },
|
||||
{ 20, Enchantment.Efficency },
|
||||
{ 21, Enchantment.SilkTouch },
|
||||
{ 22, Enchantment.Unbreaking },
|
||||
{ 23, Enchantment.Fortune },
|
||||
{ 24, Enchantment.Power },
|
||||
{ 25, Enchantment.Punch },
|
||||
{ 26, Enchantment.Flame },
|
||||
{ 27, Enchantment.Infinity },
|
||||
{ 28, Enchantment.LuckOfTheSea },
|
||||
{ 29, Enchantment.Lure },
|
||||
{ 30, Enchantment.Loyality },
|
||||
{ 31, Enchantment.Impaling },
|
||||
{ 32, Enchantment.Riptide },
|
||||
{ 33, Enchantment.Channeling },
|
||||
{ 34, Enchantment.Multishot },
|
||||
{ 35, Enchantment.QuickCharge },
|
||||
{ 36, Enchantment.Piercing },
|
||||
{ 37, Enchantment.Mending },
|
||||
{ 38, Enchantment.VanishingCurse }
|
||||
{ 0, Enchantments.Protection },
|
||||
{ 1, Enchantments.FireProtection },
|
||||
{ 2, Enchantments.FeatherFalling },
|
||||
{ 3, Enchantments.BlastProtection },
|
||||
{ 4, Enchantments.ProjectileProtection },
|
||||
{ 5, Enchantments.Respiration },
|
||||
{ 6, Enchantments.AquaAffinity },
|
||||
{ 7, Enchantments.Thorns },
|
||||
{ 8, Enchantments.DepthStrider },
|
||||
{ 9, Enchantments.FrostWalker },
|
||||
{ 10, Enchantments.BindingCurse },
|
||||
{ 11, Enchantments.SoulSpeed },
|
||||
{ 12, Enchantments.SwiftSneak },
|
||||
{ 13, Enchantments.Sharpness },
|
||||
{ 14, Enchantments.Smite },
|
||||
{ 15, Enchantments.BaneOfArthropods },
|
||||
{ 16, Enchantments.Knockback },
|
||||
{ 17, Enchantments.FireAspect },
|
||||
{ 18, Enchantments.Looting },
|
||||
{ 19, Enchantments.Sweeping },
|
||||
{ 20, Enchantments.Efficiency },
|
||||
{ 21, Enchantments.SilkTouch },
|
||||
{ 22, Enchantments.Unbreaking },
|
||||
{ 23, Enchantments.Fortune },
|
||||
{ 24, Enchantments.Power },
|
||||
{ 25, Enchantments.Punch },
|
||||
{ 26, Enchantments.Flame },
|
||||
{ 27, Enchantments.Infinity },
|
||||
{ 28, Enchantments.LuckOfTheSea },
|
||||
{ 29, Enchantments.Lure },
|
||||
{ 30, Enchantments.Loyalty },
|
||||
{ 31, Enchantments.Impaling },
|
||||
{ 32, Enchantments.Riptide },
|
||||
{ 33, Enchantments.Channeling },
|
||||
{ 34, Enchantments.Multishot },
|
||||
{ 35, Enchantments.QuickCharge },
|
||||
{ 36, Enchantments.Piercing },
|
||||
{ 37, Enchantments.Mending },
|
||||
{ 38, Enchantments.VanishingCurse }
|
||||
};
|
||||
|
||||
// 1.20.6 - 1.21.10
|
||||
private static Dictionary<short, Enchantments> enchantmentMappings1206 = new()
|
||||
{
|
||||
//id type
|
||||
{ 0, Enchantments.Protection },
|
||||
{ 1, Enchantments.FireProtection },
|
||||
{ 2, Enchantments.FeatherFalling },
|
||||
{ 3, Enchantments.BlastProtection },
|
||||
{ 4, Enchantments.ProjectileProtection },
|
||||
{ 5, Enchantments.Respiration },
|
||||
{ 6, Enchantments.AquaAffinity },
|
||||
{ 7, Enchantments.Thorns },
|
||||
{ 8, Enchantments.DepthStrider },
|
||||
{ 9, Enchantments.FrostWalker },
|
||||
{ 10, Enchantments.BindingCurse },
|
||||
{ 11, Enchantments.SoulSpeed },
|
||||
{ 12, Enchantments.SwiftSneak },
|
||||
{ 13, Enchantments.Sharpness },
|
||||
{ 14, Enchantments.Smite },
|
||||
{ 15, Enchantments.BaneOfArthropods },
|
||||
{ 16, Enchantments.Knockback },
|
||||
{ 17, Enchantments.FireAspect },
|
||||
{ 18, Enchantments.Looting },
|
||||
{ 19, Enchantments.Sweeping },
|
||||
{ 20, Enchantments.Efficiency },
|
||||
{ 21, Enchantments.SilkTouch },
|
||||
{ 22, Enchantments.Unbreaking },
|
||||
{ 23, Enchantments.Fortune },
|
||||
{ 24, Enchantments.Power },
|
||||
{ 25, Enchantments.Punch },
|
||||
{ 26, Enchantments.Flame },
|
||||
{ 27, Enchantments.Infinity },
|
||||
{ 28, Enchantments.LuckOfTheSea },
|
||||
{ 29, Enchantments.Lure },
|
||||
{ 30, Enchantments.Loyalty },
|
||||
{ 31, Enchantments.Impaling },
|
||||
{ 32, Enchantments.Riptide },
|
||||
{ 33, Enchantments.Channeling },
|
||||
{ 34, Enchantments.Multishot },
|
||||
{ 35, Enchantments.QuickCharge },
|
||||
{ 36, Enchantments.Piercing },
|
||||
{ 37, Enchantments.Density },
|
||||
{ 38, Enchantments.Breach },
|
||||
{ 39, Enchantments.WindBurst },
|
||||
{ 40, Enchantments.Mending },
|
||||
{ 41, Enchantments.VanishingCurse }
|
||||
};
|
||||
|
||||
// 1.21.11+
|
||||
private static Dictionary<short, Enchantments> enchantmentMappings12111 = new()
|
||||
{
|
||||
//id type
|
||||
{ 0, Enchantments.Protection },
|
||||
{ 1, Enchantments.FireProtection },
|
||||
{ 2, Enchantments.FeatherFalling },
|
||||
{ 3, Enchantments.BlastProtection },
|
||||
{ 4, Enchantments.ProjectileProtection },
|
||||
{ 5, Enchantments.Respiration },
|
||||
{ 6, Enchantments.AquaAffinity },
|
||||
{ 7, Enchantments.Thorns },
|
||||
{ 8, Enchantments.DepthStrider },
|
||||
{ 9, Enchantments.FrostWalker },
|
||||
{ 10, Enchantments.BindingCurse },
|
||||
{ 11, Enchantments.SoulSpeed },
|
||||
{ 12, Enchantments.SwiftSneak },
|
||||
{ 13, Enchantments.Sharpness },
|
||||
{ 14, Enchantments.Smite },
|
||||
{ 15, Enchantments.BaneOfArthropods },
|
||||
{ 16, Enchantments.Knockback },
|
||||
{ 17, Enchantments.FireAspect },
|
||||
{ 18, Enchantments.Looting },
|
||||
{ 19, Enchantments.Sweeping },
|
||||
{ 20, Enchantments.Efficiency },
|
||||
{ 21, Enchantments.SilkTouch },
|
||||
{ 22, Enchantments.Unbreaking },
|
||||
{ 23, Enchantments.Fortune },
|
||||
{ 24, Enchantments.Power },
|
||||
{ 25, Enchantments.Punch },
|
||||
{ 26, Enchantments.Flame },
|
||||
{ 27, Enchantments.Infinity },
|
||||
{ 28, Enchantments.LuckOfTheSea },
|
||||
{ 29, Enchantments.Lure },
|
||||
{ 30, Enchantments.Loyalty },
|
||||
{ 31, Enchantments.Impaling },
|
||||
{ 32, Enchantments.Riptide },
|
||||
{ 33, Enchantments.Channeling },
|
||||
{ 34, Enchantments.Multishot },
|
||||
{ 35, Enchantments.QuickCharge },
|
||||
{ 36, Enchantments.Piercing },
|
||||
{ 37, Enchantments.Density },
|
||||
{ 38, Enchantments.Breach },
|
||||
{ 39, Enchantments.WindBurst },
|
||||
{ 40, Enchantments.Lunge },
|
||||
{ 41, Enchantments.Mending },
|
||||
{ 42, Enchantments.VanishingCurse }
|
||||
};
|
||||
#pragma warning restore format // @formatter:on
|
||||
|
||||
public static Enchantment GetEnchantmentById(int protocolVersion, short id)
|
||||
public static Enchantments GetEnchantmentById(int protocolVersion, short id)
|
||||
{
|
||||
if (protocolVersion < Protocol18Handler.MC_1_14_Version)
|
||||
throw new Exception("Enchantments mappings are not implemented bellow 1.14");
|
||||
throw new Exception("Enchantments mappings are not implemented below 1.14");
|
||||
|
||||
Dictionary<short, Enchantment> map = enchantmentMappings;
|
||||
var map = GetMapForProtocolVersion(protocolVersion);
|
||||
|
||||
if (protocolVersion >= Protocol18Handler.MC_1_14_Version && protocolVersion < Protocol18Handler.MC_1_16_Version)
|
||||
map = enchantmentMappings114;
|
||||
else if (protocolVersion >= Protocol18Handler.MC_1_16_Version && protocolVersion < Protocol18Handler.MC_1_19_Version)
|
||||
map = enchantmentMappings116;
|
||||
if (!map.TryGetValue(id, out var value))
|
||||
throw new Exception($"Got an Unknown Enchantment ID {id}, please update the Mappings!");
|
||||
|
||||
if (!map.ContainsKey(id))
|
||||
throw new Exception("Got an Unknown Enchantment ID '" + id + "', please update the Mappings!");
|
||||
|
||||
return map[id];
|
||||
return value;
|
||||
}
|
||||
|
||||
public static string GetEnchantmentName(Enchantment enchantment)
|
||||
private static Dictionary<Enchantments, short>? reverseDynamicEnchantmentMappings;
|
||||
private static readonly Dictionary<Enchantments, short> reverseEnchantmentMappings114 = CreateReverseMap(enchantmentMappings114);
|
||||
private static readonly Dictionary<Enchantments, short> reverseEnchantmentMappings116 = CreateReverseMap(enchantmentMappings116);
|
||||
private static readonly Dictionary<Enchantments, short> reverseEnchantmentMappings119 = CreateReverseMap(enchantmentMappings119);
|
||||
private static readonly Dictionary<Enchantments, short> reverseEnchantmentMappings1206 = CreateReverseMap(enchantmentMappings1206);
|
||||
private static readonly Dictionary<Enchantments, short> reverseEnchantmentMappings12111 = CreateReverseMap(enchantmentMappings12111);
|
||||
private static Dictionary<int, Enchantments>? dynamicEnchantmentIdMap;
|
||||
|
||||
private static readonly Dictionary<string, Enchantments> nameToEnchantment = new()
|
||||
{
|
||||
string? trans = ChatParser.TranslateString("enchantment.minecraft." + enchantment.ToString().ToUnderscoreCase());
|
||||
if (string.IsNullOrEmpty(trans))
|
||||
return "Unknown Enchantment with ID: " + ((short)enchantment) + " (Probably not named in the code yet)";
|
||||
else
|
||||
return trans;
|
||||
{ "protection", Enchantments.Protection },
|
||||
{ "fire_protection", Enchantments.FireProtection },
|
||||
{ "feather_falling", Enchantments.FeatherFalling },
|
||||
{ "blast_protection", Enchantments.BlastProtection },
|
||||
{ "projectile_protection", Enchantments.ProjectileProtection },
|
||||
{ "respiration", Enchantments.Respiration },
|
||||
{ "aqua_affinity", Enchantments.AquaAffinity },
|
||||
{ "thorns", Enchantments.Thorns },
|
||||
{ "depth_strider", Enchantments.DepthStrider },
|
||||
{ "frost_walker", Enchantments.FrostWalker },
|
||||
{ "binding_curse", Enchantments.BindingCurse },
|
||||
{ "soul_speed", Enchantments.SoulSpeed },
|
||||
{ "swift_sneak", Enchantments.SwiftSneak },
|
||||
{ "sharpness", Enchantments.Sharpness },
|
||||
{ "smite", Enchantments.Smite },
|
||||
{ "bane_of_arthropods", Enchantments.BaneOfArthropods },
|
||||
{ "knockback", Enchantments.Knockback },
|
||||
{ "fire_aspect", Enchantments.FireAspect },
|
||||
{ "looting", Enchantments.Looting },
|
||||
{ "sweeping_edge", Enchantments.Sweeping },
|
||||
{ "efficiency", Enchantments.Efficiency },
|
||||
{ "silk_touch", Enchantments.SilkTouch },
|
||||
{ "unbreaking", Enchantments.Unbreaking },
|
||||
{ "fortune", Enchantments.Fortune },
|
||||
{ "power", Enchantments.Power },
|
||||
{ "punch", Enchantments.Punch },
|
||||
{ "flame", Enchantments.Flame },
|
||||
{ "infinity", Enchantments.Infinity },
|
||||
{ "luck_of_the_sea", Enchantments.LuckOfTheSea },
|
||||
{ "lure", Enchantments.Lure },
|
||||
{ "loyalty", Enchantments.Loyalty },
|
||||
{ "lunge", Enchantments.Lunge },
|
||||
{ "impaling", Enchantments.Impaling },
|
||||
{ "riptide", Enchantments.Riptide },
|
||||
{ "channeling", Enchantments.Channeling },
|
||||
{ "multishot", Enchantments.Multishot },
|
||||
{ "quick_charge", Enchantments.QuickCharge },
|
||||
{ "piercing", Enchantments.Piercing },
|
||||
{ "density", Enchantments.Density },
|
||||
{ "breach", Enchantments.Breach },
|
||||
{ "wind_burst", Enchantments.WindBurst },
|
||||
{ "mending", Enchantments.Mending },
|
||||
{ "vanishing_curse", Enchantments.VanishingCurse },
|
||||
};
|
||||
|
||||
/// <summary>
|
||||
/// Set the dynamic enchantment ID map from server RegistryData.
|
||||
/// Called during configuration phase when receiving minecraft:enchantment registry.
|
||||
/// </summary>
|
||||
public static void SetDynamicEnchantmentIdMap(Dictionary<int, string> idMap)
|
||||
{
|
||||
dynamicEnchantmentIdMap = new();
|
||||
foreach (var kvp in idMap)
|
||||
{
|
||||
var name = kvp.Value.StartsWith("minecraft:") ? kvp.Value.Substring("minecraft:".Length) : kvp.Value;
|
||||
if (nameToEnchantment.TryGetValue(name, out var enchantment))
|
||||
dynamicEnchantmentIdMap[kvp.Key] = enchantment;
|
||||
}
|
||||
reverseDynamicEnchantmentMappings = null;
|
||||
}
|
||||
|
||||
public static Enchantments GetEnchantmentByRegistryId1206(int protocolVersion, int id)
|
||||
{
|
||||
if (dynamicEnchantmentIdMap is not null && dynamicEnchantmentIdMap.TryGetValue(id, out var dynValue))
|
||||
return dynValue;
|
||||
if (GetMapForProtocolVersion(protocolVersion).TryGetValue((short)id, out var value))
|
||||
return value;
|
||||
return (Enchantments)(-1);
|
||||
}
|
||||
|
||||
public static int GetRegistryId1206ByEnchantment(int protocolVersion, Enchantments enchantment)
|
||||
{
|
||||
if (dynamicEnchantmentIdMap is not null)
|
||||
{
|
||||
if (reverseDynamicEnchantmentMappings is null)
|
||||
{
|
||||
reverseDynamicEnchantmentMappings = new();
|
||||
foreach (var kvp in dynamicEnchantmentIdMap)
|
||||
reverseDynamicEnchantmentMappings[kvp.Value] = (short)kvp.Key;
|
||||
}
|
||||
|
||||
return reverseDynamicEnchantmentMappings.TryGetValue(enchantment, out var dynamicId) ? dynamicId : -1;
|
||||
}
|
||||
|
||||
var reverseMap = GetReverseMapForProtocolVersion(protocolVersion);
|
||||
return reverseMap.TryGetValue(enchantment, out var id) ? id : -1;
|
||||
}
|
||||
|
||||
public static string GetEnchantmentName(Enchantments enchantment)
|
||||
{
|
||||
var translation = ChatParser.TranslateString("enchantment.minecraft." + enchantment.ToString().ToUnderscoreCase());
|
||||
return string.IsNullOrEmpty(translation) ? $"Unknown Enchantment with ID: {(short)enchantment} (Probably not named in the code yet)" : translation;
|
||||
}
|
||||
|
||||
public static string ConvertLevelToRomanNumbers(int num)
|
||||
{
|
||||
string result = string.Empty;
|
||||
Dictionary<string, int> romanNumbers = new Dictionary<string, int>
|
||||
var result = string.Empty;
|
||||
var romanNumbers = new Dictionary<string, int>
|
||||
{
|
||||
{"M", 1000 },
|
||||
{"M", 1000},
|
||||
{"CM", 900},
|
||||
{"D", 500},
|
||||
{"CD", 400},
|
||||
|
|
@ -194,5 +381,40 @@ namespace MinecraftClient.Inventory
|
|||
|
||||
return result;
|
||||
}
|
||||
|
||||
private static Dictionary<short, Enchantments> GetMapForProtocolVersion(int protocolVersion)
|
||||
{
|
||||
return protocolVersion switch
|
||||
{
|
||||
>= Protocol18Handler.MC_1_14_Version and < Protocol18Handler.MC_1_16_Version => enchantmentMappings114,
|
||||
>= Protocol18Handler.MC_1_16_Version and < Protocol18Handler.MC_1_19_Version => enchantmentMappings116,
|
||||
>= Protocol18Handler.MC_1_19_Version and < Protocol18Handler.MC_1_20_6_Version => enchantmentMappings119,
|
||||
>= Protocol18Handler.MC_1_20_6_Version and < Protocol18Handler.MC_1_21_11_Version => enchantmentMappings1206,
|
||||
>= Protocol18Handler.MC_1_21_11_Version => enchantmentMappings12111,
|
||||
_ => throw new Exception("Enchantments mappings are not implemented below 1.14")
|
||||
};
|
||||
}
|
||||
|
||||
private static Dictionary<Enchantments, short> GetReverseMapForProtocolVersion(int protocolVersion)
|
||||
{
|
||||
return protocolVersion switch
|
||||
{
|
||||
>= Protocol18Handler.MC_1_14_Version and < Protocol18Handler.MC_1_16_Version => reverseEnchantmentMappings114,
|
||||
>= Protocol18Handler.MC_1_16_Version and < Protocol18Handler.MC_1_19_Version => reverseEnchantmentMappings116,
|
||||
>= Protocol18Handler.MC_1_19_Version and < Protocol18Handler.MC_1_20_6_Version => reverseEnchantmentMappings119,
|
||||
>= Protocol18Handler.MC_1_20_6_Version and < Protocol18Handler.MC_1_21_11_Version => reverseEnchantmentMappings1206,
|
||||
>= Protocol18Handler.MC_1_21_11_Version => reverseEnchantmentMappings12111,
|
||||
_ => throw new Exception("Enchantments mappings are not implemented below 1.14")
|
||||
};
|
||||
}
|
||||
|
||||
private static Dictionary<Enchantments, short> CreateReverseMap(Dictionary<short, Enchantments> map)
|
||||
{
|
||||
Dictionary<Enchantments, short> reverseMap = new();
|
||||
foreach (var kvp in map)
|
||||
reverseMap[kvp.Value] = kvp.Key;
|
||||
|
||||
return reverseMap;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,46 +1,50 @@
|
|||
namespace MinecraftClient.Inventory
|
||||
namespace MinecraftClient.Inventory
|
||||
{
|
||||
// Not implemented for 1.14
|
||||
public enum Enchantment : short
|
||||
public enum Enchantments : short
|
||||
{
|
||||
Protection = 0,
|
||||
FireProtection,
|
||||
FeatherFalling,
|
||||
BlastProtection,
|
||||
ProjectileProtection,
|
||||
Respiration,
|
||||
AquaAffinity,
|
||||
Thorns,
|
||||
DepthStrieder,
|
||||
FrostWalker,
|
||||
BindingCurse,
|
||||
SoulSpeed,
|
||||
SwiftSneak,
|
||||
Sharpness,
|
||||
Smite,
|
||||
AquaAffinity = 0,
|
||||
BaneOfArthropods,
|
||||
Knockback,
|
||||
FireAspect,
|
||||
Looting,
|
||||
Sweeping,
|
||||
Efficency,
|
||||
SilkTouch,
|
||||
Unbreaking,
|
||||
Fortune,
|
||||
Power,
|
||||
Punch,
|
||||
Flame,
|
||||
Infinity,
|
||||
LuckOfTheSea,
|
||||
Lure,
|
||||
Loyality,
|
||||
Impaling,
|
||||
Riptide,
|
||||
BindingCurse,
|
||||
BlastProtection,
|
||||
Breach,
|
||||
Channeling,
|
||||
Multishot,
|
||||
QuickCharge,
|
||||
Piercing,
|
||||
DepthStrider,
|
||||
Density,
|
||||
Efficiency,
|
||||
FeatherFalling,
|
||||
FireAspect,
|
||||
FireProtection,
|
||||
Flame,
|
||||
Fortune,
|
||||
FrostWalker,
|
||||
Impaling,
|
||||
Infinity,
|
||||
Knockback,
|
||||
Looting,
|
||||
LuckOfTheSea,
|
||||
Loyalty,
|
||||
Lunge,
|
||||
Lure,
|
||||
Mending,
|
||||
VanishingCurse
|
||||
Multishot,
|
||||
Piercing,
|
||||
Power,
|
||||
ProjectileProtection,
|
||||
Protection,
|
||||
Punch,
|
||||
QuickCharge,
|
||||
Respiration,
|
||||
Riptide,
|
||||
Sharpness,
|
||||
SilkTouch,
|
||||
Smite,
|
||||
SoulSpeed,
|
||||
Sweeping,
|
||||
SwiftSneak,
|
||||
Thorns,
|
||||
Unbreaking,
|
||||
VanishingCurse,
|
||||
WindBurst
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,8 +1,10 @@
|
|||
using System;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Globalization;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using MinecraftClient.Protocol.Handlers.StructuredComponents.Core;
|
||||
using MinecraftClient.Protocol.Handlers.StructuredComponents.Components._1_20_6;
|
||||
using MinecraftClient.Protocol.Message;
|
||||
|
||||
namespace MinecraftClient.Inventory
|
||||
|
|
@ -32,6 +34,11 @@ namespace MinecraftClient.Inventory
|
|||
/// </summary>
|
||||
public Dictionary<string, object>? NBT;
|
||||
|
||||
/// <summary>
|
||||
/// 1.20.6+ structured components (raw list for round-trip serialization)
|
||||
/// </summary>
|
||||
public List<StructuredComponent>? Components;
|
||||
|
||||
/// <summary>
|
||||
/// Create an item with ItemType, Count and Metadata
|
||||
/// </summary>
|
||||
|
|
@ -44,12 +51,20 @@ namespace MinecraftClient.Inventory
|
|||
Count = count;
|
||||
NBT = nbt;
|
||||
}
|
||||
|
||||
|
||||
public Item(ItemType itemType, int count, int data, Dictionary<string, object>? nbt) : this(itemType, count, nbt)
|
||||
{
|
||||
Data = data;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Create a shallow clone with a specific count (preserves NBT and Components).
|
||||
/// </summary>
|
||||
public Item CloneWithCount(int count)
|
||||
{
|
||||
return new Item(Type, count, Data, NBT) { Components = Components };
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Check if the item slot is empty
|
||||
/// </summary>
|
||||
|
|
@ -60,13 +75,27 @@ namespace MinecraftClient.Inventory
|
|||
}
|
||||
|
||||
/// <summary>
|
||||
/// Retrieve item display name from NBT properties. NULL if no display name is defined.
|
||||
/// Retrieve item display name. For 1.20.6+ reads from structured components
|
||||
/// (CustomNameComponent, then ItemNameComponent as fallback); for older versions reads from NBT.
|
||||
/// </summary>
|
||||
public string? DisplayName
|
||||
{
|
||||
get
|
||||
{
|
||||
if (NBT != null && NBT.ContainsKey("display"))
|
||||
if (Components is not null)
|
||||
{
|
||||
var customName = Components.OfType<CustomNameComponent>().FirstOrDefault();
|
||||
if (customName is not null && !string.IsNullOrEmpty(customName.CustomName))
|
||||
return customName.CustomName;
|
||||
|
||||
var itemName = Components.OfType<ItemNameComponent>().FirstOrDefault();
|
||||
if (itemName is not null && !string.IsNullOrEmpty(itemName.ItemName))
|
||||
return itemName.ItemName;
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
if (NBT is not null && NBT.ContainsKey("display"))
|
||||
{
|
||||
if (NBT["display"] is Dictionary<string, object> displayProperties &&
|
||||
displayProperties.ContainsKey("Name"))
|
||||
|
|
@ -82,22 +111,31 @@ namespace MinecraftClient.Inventory
|
|||
}
|
||||
|
||||
/// <summary>
|
||||
/// Retrieve item lores from NBT properties. Returns null if no lores is defined.
|
||||
/// Retrieve item lores. For 1.20.6+ reads from LoreNameComponent1206; for older versions reads from NBT.
|
||||
/// </summary>
|
||||
public string[]? Lores
|
||||
{
|
||||
get
|
||||
{
|
||||
if (Components is not null)
|
||||
{
|
||||
var loreComponent = Components.OfType<LoreNameComponent1206>().FirstOrDefault();
|
||||
if (loreComponent is not null && loreComponent.Lines.Count > 0)
|
||||
return loreComponent.Lines.ToArray();
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
List<string> lores = new();
|
||||
if (NBT != null && NBT.ContainsKey("display"))
|
||||
if (NBT is not null && NBT.ContainsKey("display"))
|
||||
{
|
||||
if (NBT["display"] is Dictionary<string, object> displayProperties &&
|
||||
displayProperties.ContainsKey("Lore"))
|
||||
{
|
||||
object[] displayName = (object[])displayProperties["Lore"];
|
||||
lores.AddRange(from string st in displayName
|
||||
let str = ChatParser.ParseText(st.ToString())
|
||||
select str);
|
||||
let str = ChatParser.ParseText(st.ToString())
|
||||
select str);
|
||||
return lores.ToArray();
|
||||
}
|
||||
}
|
||||
|
|
@ -107,16 +145,25 @@ namespace MinecraftClient.Inventory
|
|||
}
|
||||
|
||||
/// <summary>
|
||||
/// Retrieve item damage from NBT properties. Returns 0 if no damage is defined.
|
||||
/// Retrieve item damage. For 1.20.6+ reads from DamageComponent; for older versions reads from NBT.
|
||||
/// </summary>
|
||||
public int Damage
|
||||
{
|
||||
get
|
||||
{
|
||||
if (NBT != null && NBT.ContainsKey("Damage"))
|
||||
if (Components is not null)
|
||||
{
|
||||
var damageComponent = Components.OfType<DamageComponent>().FirstOrDefault();
|
||||
if (damageComponent is not null)
|
||||
return damageComponent.Damage;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (NBT is not null && NBT.ContainsKey("Damage"))
|
||||
{
|
||||
object damage = NBT["Damage"];
|
||||
if (damage != null)
|
||||
if (damage is not null)
|
||||
{
|
||||
return int.Parse(damage.ToString() ?? string.Empty, NumberStyles.Any,
|
||||
CultureInfo.CurrentCulture);
|
||||
|
|
@ -127,6 +174,26 @@ namespace MinecraftClient.Inventory
|
|||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Retrieve enchantments from structured components (1.20.6+). Returns null for older versions.
|
||||
/// Both normal enchantments (EnchantmentsComponent) and stored enchantments
|
||||
/// (StoredEnchantmentsComponent, e.g. enchanted books) are checked.
|
||||
/// </summary>
|
||||
public List<Enchantment>? EnchantmentList
|
||||
{
|
||||
get
|
||||
{
|
||||
if (Components is null)
|
||||
return null;
|
||||
|
||||
var enchComp = Components.OfType<EnchantmentsComponent>().FirstOrDefault();
|
||||
if (enchComp is not null && enchComp.Enchantments.Count > 0)
|
||||
return enchComp.Enchantments;
|
||||
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
public static string GetTypeString(ItemType type)
|
||||
{
|
||||
string type_str = type.ToString();
|
||||
|
|
@ -152,8 +219,18 @@ namespace MinecraftClient.Inventory
|
|||
|
||||
try
|
||||
{
|
||||
if (NBT != null && (NBT.TryGetValue("Enchantments", out object? enchantments) ||
|
||||
NBT.TryGetValue("StoredEnchantments", out enchantments)))
|
||||
var enchList = EnchantmentList;
|
||||
if (enchList is not null)
|
||||
{
|
||||
foreach (var ench in enchList)
|
||||
{
|
||||
string name = EnchantmentMapping.GetEnchantmentName(ench.Type);
|
||||
string level = EnchantmentMapping.ConvertLevelToRomanNumbers(ench.Level);
|
||||
sb.AppendFormat(" | {0} {1}", name, level);
|
||||
}
|
||||
}
|
||||
else if (NBT is not null && (NBT.TryGetValue("Enchantments", out object? enchantments) ||
|
||||
NBT.TryGetValue("StoredEnchantments", out enchantments)))
|
||||
{
|
||||
foreach (Dictionary<string, object> enchantment in (object[])enchantments)
|
||||
{
|
||||
|
|
@ -165,7 +242,7 @@ namespace MinecraftClient.Inventory
|
|||
}
|
||||
}
|
||||
|
||||
if (Lores != null && Lores.Length > 0)
|
||||
if (Lores is not null && Lores.Length > 0)
|
||||
{
|
||||
foreach (var lore in Lores)
|
||||
sb.AppendFormat(" | {0}", lore);
|
||||
|
|
@ -195,4 +272,4 @@ namespace MinecraftClient.Inventory
|
|||
return sb.ToString();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -7,24 +7,10 @@ namespace MinecraftClient.Inventory
|
|||
/// <summary>
|
||||
/// Class that contains useful methods to move item around in a container
|
||||
/// </summary>
|
||||
public class ItemMovingHelper
|
||||
public class ItemMovingHelper(Container c, McClient mc)
|
||||
{
|
||||
private readonly Container c;
|
||||
private readonly McClient mc;
|
||||
|
||||
/// <summary>
|
||||
/// Create a helper that contains useful methods to move item around in container
|
||||
/// </summary>
|
||||
/// <param name="c">Source container to use. All method will use this container for handling first slot parameter</param>
|
||||
/// <param name="mc">McClient handler. Needed for sending WindowAction packet to the server</param>
|
||||
/// <remarks>
|
||||
/// If you are using ChatBot API and cannot have direct access to McClient handler, use <see cref="ChatBot.WindowAction(int, int, WindowActionType)"/> as second parameter
|
||||
/// </remarks>
|
||||
public ItemMovingHelper(Container c, McClient mc)
|
||||
{
|
||||
this.c = c;
|
||||
this.mc = mc;
|
||||
}
|
||||
private readonly Container c = c;
|
||||
private readonly McClient mc = mc;
|
||||
|
||||
/// <summary>
|
||||
/// Move an item fron source to dest. Source should contain an item and dest slot should be empty
|
||||
|
|
@ -38,9 +24,9 @@ namespace MinecraftClient.Inventory
|
|||
// Condition: source has item and dest has no item
|
||||
if (ValidateSlots(source, dest, destContainer) &&
|
||||
HasItem(source) &&
|
||||
((destContainer != null && !HasItem(dest, destContainer)) || (destContainer == null && !HasItem(dest))))
|
||||
((destContainer is not null && !HasItem(dest, destContainer)) || (destContainer is null && !HasItem(dest))))
|
||||
return mc.DoWindowAction(c.ID, source, WindowActionType.LeftClick)
|
||||
&& mc.DoWindowAction(destContainer == null ? c.ID : destContainer.ID, dest, WindowActionType.LeftClick);
|
||||
&& mc.DoWindowAction(destContainer is null ? c.ID : destContainer.ID, dest, WindowActionType.LeftClick);
|
||||
else return false;
|
||||
}
|
||||
|
||||
|
|
@ -56,9 +42,9 @@ namespace MinecraftClient.Inventory
|
|||
// Condition: Both slot1 and slot2 has item
|
||||
if (ValidateSlots(slot1, slot2, destContainer) &&
|
||||
HasItem(slot1) &&
|
||||
(destContainer != null && HasItem(slot2, destContainer) || (destContainer == null && HasItem(slot2))))
|
||||
(destContainer is not null && HasItem(slot2, destContainer) || (destContainer is null && HasItem(slot2))))
|
||||
return mc.DoWindowAction(c.ID, slot1, WindowActionType.LeftClick)
|
||||
&& mc.DoWindowAction(destContainer == null ? c.ID : destContainer.ID, slot2, WindowActionType.LeftClick)
|
||||
&& mc.DoWindowAction(destContainer is null ? c.ID : destContainer.ID, slot2, WindowActionType.LeftClick)
|
||||
&& mc.DoWindowAction(c.ID, slot1, WindowActionType.LeftClick);
|
||||
else return false;
|
||||
}
|
||||
|
|
@ -126,7 +112,7 @@ namespace MinecraftClient.Inventory
|
|||
/// <returns>The compare result</returns>
|
||||
private bool ValidateSlots(int s1, int s2, Container? s2Container = null)
|
||||
{
|
||||
if (s2Container == null)
|
||||
if (s2Container is null)
|
||||
return (s1 != s2 && s1 < c.Type.SlotCount() && s2 < c.Type.SlotCount());
|
||||
else
|
||||
return (s1 < c.Type.SlotCount() && s2 < s2Container.Type.SlotCount());
|
||||
|
|
@ -153,7 +139,7 @@ namespace MinecraftClient.Inventory
|
|||
/// <returns>True if they are equal</returns>
|
||||
private bool ItemTypeEqual(int slot1, int slot2, Container? s2Container = null)
|
||||
{
|
||||
if (s2Container == null)
|
||||
if (s2Container is null)
|
||||
{
|
||||
if (HasItem(slot1) && HasItem(slot2))
|
||||
return c.Items[slot1].Type == c.Items[slot2].Type;
|
||||
|
|
|
|||
|
|
@ -14,7 +14,7 @@ namespace MinecraftClient.Inventory.ItemPalettes
|
|||
{
|
||||
if (DictReverse.ContainsKey(entry.Value))
|
||||
continue;
|
||||
|
||||
|
||||
DictReverse.Add(entry.Value, entry.Key);
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -71,7 +71,7 @@ namespace MinecraftClient.Inventory.ItemPalettes
|
|||
mappings[1835008] = ItemType.DetectorRail;
|
||||
mappings[1900544] = ItemType.StickyPiston;
|
||||
mappings[1966080] = ItemType.Cobweb;
|
||||
mappings[2031617] = ItemType.Grass;
|
||||
mappings[2031617] = ItemType.ShortGrass;
|
||||
mappings[2031618] = ItemType.Fern;
|
||||
mappings[2097152] = ItemType.DeadBush;
|
||||
mappings[2162688] = ItemType.Piston;
|
||||
|
|
|
|||
|
|
@ -71,7 +71,7 @@ namespace MinecraftClient.Inventory.ItemPalettes
|
|||
mappings[1835008] = ItemType.DetectorRail;
|
||||
mappings[1900544] = ItemType.StickyPiston;
|
||||
mappings[1966080] = ItemType.Cobweb;
|
||||
mappings[2031617] = ItemType.Grass;
|
||||
mappings[2031617] = ItemType.ShortGrass;
|
||||
mappings[2031618] = ItemType.Fern;
|
||||
mappings[2097152] = ItemType.DeadBush;
|
||||
mappings[2162688] = ItemType.Piston;
|
||||
|
|
|
|||
|
|
@ -62,7 +62,7 @@ namespace MinecraftClient.Inventory.ItemPalettes
|
|||
mappings[1769472] = ItemType.PoweredRail;
|
||||
mappings[1835008] = ItemType.DetectorRail;
|
||||
mappings[1900544] = ItemType.StickyPiston;
|
||||
mappings[2031617] = ItemType.Grass;
|
||||
mappings[2031617] = ItemType.ShortGrass;
|
||||
mappings[2031618] = ItemType.Fern;
|
||||
mappings[2097152] = ItemType.DeadBush;
|
||||
mappings[2162688] = ItemType.Piston;
|
||||
|
|
|
|||
803
MinecraftClient/Inventory/ItemPalettes/ItemPalette113.cs
Normal file
803
MinecraftClient/Inventory/ItemPalettes/ItemPalette113.cs
Normal file
|
|
@ -0,0 +1,803 @@
|
|||
using System.Collections.Generic;
|
||||
|
||||
namespace MinecraftClient.Inventory.ItemPalettes
|
||||
{
|
||||
public class ItemPalette113 : ItemPalette
|
||||
{
|
||||
private static readonly Dictionary<int, ItemType> mappings = new();
|
||||
|
||||
static ItemPalette113()
|
||||
{
|
||||
mappings[0] = ItemType.Air;
|
||||
mappings[1] = ItemType.Stone;
|
||||
mappings[2] = ItemType.Granite;
|
||||
mappings[3] = ItemType.PolishedGranite;
|
||||
mappings[4] = ItemType.Diorite;
|
||||
mappings[5] = ItemType.PolishedDiorite;
|
||||
mappings[6] = ItemType.Andesite;
|
||||
mappings[7] = ItemType.PolishedAndesite;
|
||||
mappings[8] = ItemType.GrassBlock;
|
||||
mappings[9] = ItemType.Dirt;
|
||||
mappings[10] = ItemType.CoarseDirt;
|
||||
mappings[11] = ItemType.Podzol;
|
||||
mappings[12] = ItemType.Cobblestone;
|
||||
mappings[13] = ItemType.OakPlanks;
|
||||
mappings[14] = ItemType.SprucePlanks;
|
||||
mappings[15] = ItemType.BirchPlanks;
|
||||
mappings[16] = ItemType.JunglePlanks;
|
||||
mappings[17] = ItemType.AcaciaPlanks;
|
||||
mappings[18] = ItemType.DarkOakPlanks;
|
||||
mappings[19] = ItemType.OakSapling;
|
||||
mappings[20] = ItemType.SpruceSapling;
|
||||
mappings[21] = ItemType.BirchSapling;
|
||||
mappings[22] = ItemType.JungleSapling;
|
||||
mappings[23] = ItemType.AcaciaSapling;
|
||||
mappings[24] = ItemType.DarkOakSapling;
|
||||
mappings[25] = ItemType.Bedrock;
|
||||
mappings[26] = ItemType.Sand;
|
||||
mappings[27] = ItemType.RedSand;
|
||||
mappings[28] = ItemType.Gravel;
|
||||
mappings[29] = ItemType.GoldOre;
|
||||
mappings[30] = ItemType.IronOre;
|
||||
mappings[31] = ItemType.CoalOre;
|
||||
mappings[32] = ItemType.OakLog;
|
||||
mappings[33] = ItemType.SpruceLog;
|
||||
mappings[34] = ItemType.BirchLog;
|
||||
mappings[35] = ItemType.JungleLog;
|
||||
mappings[36] = ItemType.AcaciaLog;
|
||||
mappings[37] = ItemType.DarkOakLog;
|
||||
mappings[38] = ItemType.StrippedOakLog;
|
||||
mappings[39] = ItemType.StrippedSpruceLog;
|
||||
mappings[40] = ItemType.StrippedBirchLog;
|
||||
mappings[41] = ItemType.StrippedJungleLog;
|
||||
mappings[42] = ItemType.StrippedAcaciaLog;
|
||||
mappings[43] = ItemType.StrippedDarkOakLog;
|
||||
mappings[44] = ItemType.StrippedOakWood;
|
||||
mappings[45] = ItemType.StrippedSpruceWood;
|
||||
mappings[46] = ItemType.StrippedBirchWood;
|
||||
mappings[47] = ItemType.StrippedJungleWood;
|
||||
mappings[48] = ItemType.StrippedAcaciaWood;
|
||||
mappings[49] = ItemType.StrippedDarkOakWood;
|
||||
mappings[50] = ItemType.OakWood;
|
||||
mappings[51] = ItemType.SpruceWood;
|
||||
mappings[52] = ItemType.BirchWood;
|
||||
mappings[53] = ItemType.JungleWood;
|
||||
mappings[54] = ItemType.AcaciaWood;
|
||||
mappings[55] = ItemType.DarkOakWood;
|
||||
mappings[56] = ItemType.OakLeaves;
|
||||
mappings[57] = ItemType.SpruceLeaves;
|
||||
mappings[58] = ItemType.BirchLeaves;
|
||||
mappings[59] = ItemType.JungleLeaves;
|
||||
mappings[60] = ItemType.AcaciaLeaves;
|
||||
mappings[61] = ItemType.DarkOakLeaves;
|
||||
mappings[62] = ItemType.Sponge;
|
||||
mappings[63] = ItemType.WetSponge;
|
||||
mappings[64] = ItemType.Glass;
|
||||
mappings[65] = ItemType.LapisOre;
|
||||
mappings[66] = ItemType.LapisBlock;
|
||||
mappings[67] = ItemType.Dispenser;
|
||||
mappings[68] = ItemType.Sandstone;
|
||||
mappings[69] = ItemType.ChiseledSandstone;
|
||||
mappings[70] = ItemType.CutSandstone;
|
||||
mappings[71] = ItemType.NoteBlock;
|
||||
mappings[72] = ItemType.PoweredRail;
|
||||
mappings[73] = ItemType.DetectorRail;
|
||||
mappings[74] = ItemType.StickyPiston;
|
||||
mappings[75] = ItemType.Cobweb;
|
||||
mappings[76] = ItemType.ShortGrass;
|
||||
mappings[77] = ItemType.Fern;
|
||||
mappings[78] = ItemType.DeadBush;
|
||||
mappings[79] = ItemType.Seagrass;
|
||||
mappings[80] = ItemType.SeaPickle;
|
||||
mappings[81] = ItemType.Piston;
|
||||
mappings[82] = ItemType.WhiteWool;
|
||||
mappings[83] = ItemType.OrangeWool;
|
||||
mappings[84] = ItemType.MagentaWool;
|
||||
mappings[85] = ItemType.LightBlueWool;
|
||||
mappings[86] = ItemType.YellowWool;
|
||||
mappings[87] = ItemType.LimeWool;
|
||||
mappings[88] = ItemType.PinkWool;
|
||||
mappings[89] = ItemType.GrayWool;
|
||||
mappings[90] = ItemType.LightGrayWool;
|
||||
mappings[91] = ItemType.CyanWool;
|
||||
mappings[92] = ItemType.PurpleWool;
|
||||
mappings[93] = ItemType.BlueWool;
|
||||
mappings[94] = ItemType.BrownWool;
|
||||
mappings[95] = ItemType.GreenWool;
|
||||
mappings[96] = ItemType.RedWool;
|
||||
mappings[97] = ItemType.BlackWool;
|
||||
mappings[98] = ItemType.Dandelion;
|
||||
mappings[99] = ItemType.Poppy;
|
||||
mappings[100] = ItemType.BlueOrchid;
|
||||
mappings[101] = ItemType.Allium;
|
||||
mappings[102] = ItemType.AzureBluet;
|
||||
mappings[103] = ItemType.RedTulip;
|
||||
mappings[104] = ItemType.OrangeTulip;
|
||||
mappings[105] = ItemType.WhiteTulip;
|
||||
mappings[106] = ItemType.PinkTulip;
|
||||
mappings[107] = ItemType.OxeyeDaisy;
|
||||
mappings[108] = ItemType.BrownMushroom;
|
||||
mappings[109] = ItemType.RedMushroom;
|
||||
mappings[110] = ItemType.GoldBlock;
|
||||
mappings[111] = ItemType.IronBlock;
|
||||
mappings[112] = ItemType.OakSlab;
|
||||
mappings[113] = ItemType.SpruceSlab;
|
||||
mappings[114] = ItemType.BirchSlab;
|
||||
mappings[115] = ItemType.JungleSlab;
|
||||
mappings[116] = ItemType.AcaciaSlab;
|
||||
mappings[117] = ItemType.DarkOakSlab;
|
||||
mappings[118] = ItemType.StoneSlab;
|
||||
mappings[119] = ItemType.SandstoneSlab;
|
||||
mappings[120] = ItemType.PetrifiedOakSlab;
|
||||
mappings[121] = ItemType.CobblestoneSlab;
|
||||
mappings[122] = ItemType.BrickSlab;
|
||||
mappings[123] = ItemType.StoneBrickSlab;
|
||||
mappings[124] = ItemType.NetherBrickSlab;
|
||||
mappings[125] = ItemType.QuartzSlab;
|
||||
mappings[126] = ItemType.RedSandstoneSlab;
|
||||
mappings[127] = ItemType.PurpurSlab;
|
||||
mappings[128] = ItemType.PrismarineSlab;
|
||||
mappings[129] = ItemType.PrismarineBrickSlab;
|
||||
mappings[130] = ItemType.DarkPrismarineSlab;
|
||||
mappings[131] = ItemType.SmoothQuartz;
|
||||
mappings[132] = ItemType.SmoothRedSandstone;
|
||||
mappings[133] = ItemType.SmoothSandstone;
|
||||
mappings[134] = ItemType.SmoothStone;
|
||||
mappings[135] = ItemType.Bricks;
|
||||
mappings[136] = ItemType.Tnt;
|
||||
mappings[137] = ItemType.Bookshelf;
|
||||
mappings[138] = ItemType.MossyCobblestone;
|
||||
mappings[139] = ItemType.Obsidian;
|
||||
mappings[140] = ItemType.Torch;
|
||||
mappings[141] = ItemType.EndRod;
|
||||
mappings[142] = ItemType.ChorusPlant;
|
||||
mappings[143] = ItemType.ChorusFlower;
|
||||
mappings[144] = ItemType.PurpurBlock;
|
||||
mappings[145] = ItemType.PurpurPillar;
|
||||
mappings[146] = ItemType.PurpurStairs;
|
||||
mappings[147] = ItemType.Spawner;
|
||||
mappings[148] = ItemType.OakStairs;
|
||||
mappings[149] = ItemType.Chest;
|
||||
mappings[150] = ItemType.DiamondOre;
|
||||
mappings[151] = ItemType.DiamondBlock;
|
||||
mappings[152] = ItemType.CraftingTable;
|
||||
mappings[153] = ItemType.Farmland;
|
||||
mappings[154] = ItemType.Furnace;
|
||||
mappings[155] = ItemType.Ladder;
|
||||
mappings[156] = ItemType.Rail;
|
||||
mappings[157] = ItemType.CobblestoneStairs;
|
||||
mappings[158] = ItemType.Lever;
|
||||
mappings[159] = ItemType.StonePressurePlate;
|
||||
mappings[160] = ItemType.OakPressurePlate;
|
||||
mappings[161] = ItemType.SprucePressurePlate;
|
||||
mappings[162] = ItemType.BirchPressurePlate;
|
||||
mappings[163] = ItemType.JunglePressurePlate;
|
||||
mappings[164] = ItemType.AcaciaPressurePlate;
|
||||
mappings[165] = ItemType.DarkOakPressurePlate;
|
||||
mappings[166] = ItemType.RedstoneOre;
|
||||
mappings[167] = ItemType.RedstoneTorch;
|
||||
mappings[168] = ItemType.StoneButton;
|
||||
mappings[169] = ItemType.Snow;
|
||||
mappings[170] = ItemType.Ice;
|
||||
mappings[171] = ItemType.SnowBlock;
|
||||
mappings[172] = ItemType.Cactus;
|
||||
mappings[173] = ItemType.Clay;
|
||||
mappings[174] = ItemType.Jukebox;
|
||||
mappings[175] = ItemType.OakFence;
|
||||
mappings[176] = ItemType.SpruceFence;
|
||||
mappings[177] = ItemType.BirchFence;
|
||||
mappings[178] = ItemType.JungleFence;
|
||||
mappings[179] = ItemType.AcaciaFence;
|
||||
mappings[180] = ItemType.DarkOakFence;
|
||||
mappings[181] = ItemType.Pumpkin;
|
||||
mappings[182] = ItemType.CarvedPumpkin;
|
||||
mappings[183] = ItemType.Netherrack;
|
||||
mappings[184] = ItemType.SoulSand;
|
||||
mappings[185] = ItemType.Glowstone;
|
||||
mappings[186] = ItemType.JackOLantern;
|
||||
mappings[187] = ItemType.OakTrapdoor;
|
||||
mappings[188] = ItemType.SpruceTrapdoor;
|
||||
mappings[189] = ItemType.BirchTrapdoor;
|
||||
mappings[190] = ItemType.JungleTrapdoor;
|
||||
mappings[191] = ItemType.AcaciaTrapdoor;
|
||||
mappings[192] = ItemType.DarkOakTrapdoor;
|
||||
mappings[193] = ItemType.InfestedStone;
|
||||
mappings[194] = ItemType.InfestedCobblestone;
|
||||
mappings[195] = ItemType.InfestedStoneBricks;
|
||||
mappings[196] = ItemType.InfestedMossyStoneBricks;
|
||||
mappings[197] = ItemType.InfestedCrackedStoneBricks;
|
||||
mappings[198] = ItemType.InfestedChiseledStoneBricks;
|
||||
mappings[199] = ItemType.StoneBricks;
|
||||
mappings[200] = ItemType.MossyStoneBricks;
|
||||
mappings[201] = ItemType.CrackedStoneBricks;
|
||||
mappings[202] = ItemType.ChiseledStoneBricks;
|
||||
mappings[203] = ItemType.BrownMushroomBlock;
|
||||
mappings[204] = ItemType.RedMushroomBlock;
|
||||
mappings[205] = ItemType.MushroomStem;
|
||||
mappings[206] = ItemType.IronBars;
|
||||
mappings[207] = ItemType.GlassPane;
|
||||
mappings[208] = ItemType.Melon;
|
||||
mappings[209] = ItemType.Vine;
|
||||
mappings[210] = ItemType.OakFenceGate;
|
||||
mappings[211] = ItemType.SpruceFenceGate;
|
||||
mappings[212] = ItemType.BirchFenceGate;
|
||||
mappings[213] = ItemType.JungleFenceGate;
|
||||
mappings[214] = ItemType.AcaciaFenceGate;
|
||||
mappings[215] = ItemType.DarkOakFenceGate;
|
||||
mappings[216] = ItemType.BrickStairs;
|
||||
mappings[217] = ItemType.StoneBrickStairs;
|
||||
mappings[218] = ItemType.Mycelium;
|
||||
mappings[219] = ItemType.LilyPad;
|
||||
mappings[220] = ItemType.NetherBricks;
|
||||
mappings[221] = ItemType.NetherBrickFence;
|
||||
mappings[222] = ItemType.NetherBrickStairs;
|
||||
mappings[223] = ItemType.EnchantingTable;
|
||||
mappings[224] = ItemType.EndPortalFrame;
|
||||
mappings[225] = ItemType.EndStone;
|
||||
mappings[226] = ItemType.EndStoneBricks;
|
||||
mappings[227] = ItemType.DragonEgg;
|
||||
mappings[228] = ItemType.RedstoneLamp;
|
||||
mappings[229] = ItemType.SandstoneStairs;
|
||||
mappings[230] = ItemType.EmeraldOre;
|
||||
mappings[231] = ItemType.EnderChest;
|
||||
mappings[232] = ItemType.TripwireHook;
|
||||
mappings[233] = ItemType.EmeraldBlock;
|
||||
mappings[234] = ItemType.SpruceStairs;
|
||||
mappings[235] = ItemType.BirchStairs;
|
||||
mappings[236] = ItemType.JungleStairs;
|
||||
mappings[237] = ItemType.CommandBlock;
|
||||
mappings[238] = ItemType.Beacon;
|
||||
mappings[239] = ItemType.CobblestoneWall;
|
||||
mappings[240] = ItemType.MossyCobblestoneWall;
|
||||
mappings[241] = ItemType.OakButton;
|
||||
mappings[242] = ItemType.SpruceButton;
|
||||
mappings[243] = ItemType.BirchButton;
|
||||
mappings[244] = ItemType.JungleButton;
|
||||
mappings[245] = ItemType.AcaciaButton;
|
||||
mappings[246] = ItemType.DarkOakButton;
|
||||
mappings[247] = ItemType.Anvil;
|
||||
mappings[248] = ItemType.ChippedAnvil;
|
||||
mappings[249] = ItemType.DamagedAnvil;
|
||||
mappings[250] = ItemType.TrappedChest;
|
||||
mappings[251] = ItemType.LightWeightedPressurePlate;
|
||||
mappings[252] = ItemType.HeavyWeightedPressurePlate;
|
||||
mappings[253] = ItemType.DaylightDetector;
|
||||
mappings[254] = ItemType.RedstoneBlock;
|
||||
mappings[255] = ItemType.NetherQuartzOre;
|
||||
mappings[256] = ItemType.Hopper;
|
||||
mappings[257] = ItemType.ChiseledQuartzBlock;
|
||||
mappings[258] = ItemType.QuartzBlock;
|
||||
mappings[259] = ItemType.QuartzPillar;
|
||||
mappings[260] = ItemType.QuartzStairs;
|
||||
mappings[261] = ItemType.ActivatorRail;
|
||||
mappings[262] = ItemType.Dropper;
|
||||
mappings[263] = ItemType.WhiteTerracotta;
|
||||
mappings[264] = ItemType.OrangeTerracotta;
|
||||
mappings[265] = ItemType.MagentaTerracotta;
|
||||
mappings[266] = ItemType.LightBlueTerracotta;
|
||||
mappings[267] = ItemType.YellowTerracotta;
|
||||
mappings[268] = ItemType.LimeTerracotta;
|
||||
mappings[269] = ItemType.PinkTerracotta;
|
||||
mappings[270] = ItemType.GrayTerracotta;
|
||||
mappings[271] = ItemType.LightGrayTerracotta;
|
||||
mappings[272] = ItemType.CyanTerracotta;
|
||||
mappings[273] = ItemType.PurpleTerracotta;
|
||||
mappings[274] = ItemType.BlueTerracotta;
|
||||
mappings[275] = ItemType.BrownTerracotta;
|
||||
mappings[276] = ItemType.GreenTerracotta;
|
||||
mappings[277] = ItemType.RedTerracotta;
|
||||
mappings[278] = ItemType.BlackTerracotta;
|
||||
mappings[279] = ItemType.Barrier;
|
||||
mappings[280] = ItemType.IronTrapdoor;
|
||||
mappings[281] = ItemType.HayBlock;
|
||||
mappings[282] = ItemType.WhiteCarpet;
|
||||
mappings[283] = ItemType.OrangeCarpet;
|
||||
mappings[284] = ItemType.MagentaCarpet;
|
||||
mappings[285] = ItemType.LightBlueCarpet;
|
||||
mappings[286] = ItemType.YellowCarpet;
|
||||
mappings[287] = ItemType.LimeCarpet;
|
||||
mappings[288] = ItemType.PinkCarpet;
|
||||
mappings[289] = ItemType.GrayCarpet;
|
||||
mappings[290] = ItemType.LightGrayCarpet;
|
||||
mappings[291] = ItemType.CyanCarpet;
|
||||
mappings[292] = ItemType.PurpleCarpet;
|
||||
mappings[293] = ItemType.BlueCarpet;
|
||||
mappings[294] = ItemType.BrownCarpet;
|
||||
mappings[295] = ItemType.GreenCarpet;
|
||||
mappings[296] = ItemType.RedCarpet;
|
||||
mappings[297] = ItemType.BlackCarpet;
|
||||
mappings[298] = ItemType.Terracotta;
|
||||
mappings[299] = ItemType.CoalBlock;
|
||||
mappings[300] = ItemType.PackedIce;
|
||||
mappings[301] = ItemType.AcaciaStairs;
|
||||
mappings[302] = ItemType.DarkOakStairs;
|
||||
mappings[303] = ItemType.SlimeBlock;
|
||||
mappings[304] = ItemType.DirtPath;
|
||||
mappings[305] = ItemType.Sunflower;
|
||||
mappings[306] = ItemType.Lilac;
|
||||
mappings[307] = ItemType.RoseBush;
|
||||
mappings[308] = ItemType.Peony;
|
||||
mappings[309] = ItemType.TallGrass;
|
||||
mappings[310] = ItemType.LargeFern;
|
||||
mappings[311] = ItemType.WhiteStainedGlass;
|
||||
mappings[312] = ItemType.OrangeStainedGlass;
|
||||
mappings[313] = ItemType.MagentaStainedGlass;
|
||||
mappings[314] = ItemType.LightBlueStainedGlass;
|
||||
mappings[315] = ItemType.YellowStainedGlass;
|
||||
mappings[316] = ItemType.LimeStainedGlass;
|
||||
mappings[317] = ItemType.PinkStainedGlass;
|
||||
mappings[318] = ItemType.GrayStainedGlass;
|
||||
mappings[319] = ItemType.LightGrayStainedGlass;
|
||||
mappings[320] = ItemType.CyanStainedGlass;
|
||||
mappings[321] = ItemType.PurpleStainedGlass;
|
||||
mappings[322] = ItemType.BlueStainedGlass;
|
||||
mappings[323] = ItemType.BrownStainedGlass;
|
||||
mappings[324] = ItemType.GreenStainedGlass;
|
||||
mappings[325] = ItemType.RedStainedGlass;
|
||||
mappings[326] = ItemType.BlackStainedGlass;
|
||||
mappings[327] = ItemType.WhiteStainedGlassPane;
|
||||
mappings[328] = ItemType.OrangeStainedGlassPane;
|
||||
mappings[329] = ItemType.MagentaStainedGlassPane;
|
||||
mappings[330] = ItemType.LightBlueStainedGlassPane;
|
||||
mappings[331] = ItemType.YellowStainedGlassPane;
|
||||
mappings[332] = ItemType.LimeStainedGlassPane;
|
||||
mappings[333] = ItemType.PinkStainedGlassPane;
|
||||
mappings[334] = ItemType.GrayStainedGlassPane;
|
||||
mappings[335] = ItemType.LightGrayStainedGlassPane;
|
||||
mappings[336] = ItemType.CyanStainedGlassPane;
|
||||
mappings[337] = ItemType.PurpleStainedGlassPane;
|
||||
mappings[338] = ItemType.BlueStainedGlassPane;
|
||||
mappings[339] = ItemType.BrownStainedGlassPane;
|
||||
mappings[340] = ItemType.GreenStainedGlassPane;
|
||||
mappings[341] = ItemType.RedStainedGlassPane;
|
||||
mappings[342] = ItemType.BlackStainedGlassPane;
|
||||
mappings[343] = ItemType.Prismarine;
|
||||
mappings[344] = ItemType.PrismarineBricks;
|
||||
mappings[345] = ItemType.DarkPrismarine;
|
||||
mappings[346] = ItemType.PrismarineStairs;
|
||||
mappings[347] = ItemType.PrismarineBrickStairs;
|
||||
mappings[348] = ItemType.DarkPrismarineStairs;
|
||||
mappings[349] = ItemType.SeaLantern;
|
||||
mappings[350] = ItemType.RedSandstone;
|
||||
mappings[351] = ItemType.ChiseledRedSandstone;
|
||||
mappings[352] = ItemType.CutRedSandstone;
|
||||
mappings[353] = ItemType.RedSandstoneStairs;
|
||||
mappings[354] = ItemType.RepeatingCommandBlock;
|
||||
mappings[355] = ItemType.ChainCommandBlock;
|
||||
mappings[356] = ItemType.MagmaBlock;
|
||||
mappings[357] = ItemType.NetherWartBlock;
|
||||
mappings[358] = ItemType.RedNetherBricks;
|
||||
mappings[359] = ItemType.BoneBlock;
|
||||
mappings[360] = ItemType.StructureVoid;
|
||||
mappings[361] = ItemType.Observer;
|
||||
mappings[362] = ItemType.ShulkerBox;
|
||||
mappings[363] = ItemType.WhiteShulkerBox;
|
||||
mappings[364] = ItemType.OrangeShulkerBox;
|
||||
mappings[365] = ItemType.MagentaShulkerBox;
|
||||
mappings[366] = ItemType.LightBlueShulkerBox;
|
||||
mappings[367] = ItemType.YellowShulkerBox;
|
||||
mappings[368] = ItemType.LimeShulkerBox;
|
||||
mappings[369] = ItemType.PinkShulkerBox;
|
||||
mappings[370] = ItemType.GrayShulkerBox;
|
||||
mappings[371] = ItemType.LightGrayShulkerBox;
|
||||
mappings[372] = ItemType.CyanShulkerBox;
|
||||
mappings[373] = ItemType.PurpleShulkerBox;
|
||||
mappings[374] = ItemType.BlueShulkerBox;
|
||||
mappings[375] = ItemType.BrownShulkerBox;
|
||||
mappings[376] = ItemType.GreenShulkerBox;
|
||||
mappings[377] = ItemType.RedShulkerBox;
|
||||
mappings[378] = ItemType.BlackShulkerBox;
|
||||
mappings[379] = ItemType.WhiteGlazedTerracotta;
|
||||
mappings[380] = ItemType.OrangeGlazedTerracotta;
|
||||
mappings[381] = ItemType.MagentaGlazedTerracotta;
|
||||
mappings[382] = ItemType.LightBlueGlazedTerracotta;
|
||||
mappings[383] = ItemType.YellowGlazedTerracotta;
|
||||
mappings[384] = ItemType.LimeGlazedTerracotta;
|
||||
mappings[385] = ItemType.PinkGlazedTerracotta;
|
||||
mappings[386] = ItemType.GrayGlazedTerracotta;
|
||||
mappings[387] = ItemType.LightGrayGlazedTerracotta;
|
||||
mappings[388] = ItemType.CyanGlazedTerracotta;
|
||||
mappings[389] = ItemType.PurpleGlazedTerracotta;
|
||||
mappings[390] = ItemType.BlueGlazedTerracotta;
|
||||
mappings[391] = ItemType.BrownGlazedTerracotta;
|
||||
mappings[392] = ItemType.GreenGlazedTerracotta;
|
||||
mappings[393] = ItemType.RedGlazedTerracotta;
|
||||
mappings[394] = ItemType.BlackGlazedTerracotta;
|
||||
mappings[395] = ItemType.WhiteConcrete;
|
||||
mappings[396] = ItemType.OrangeConcrete;
|
||||
mappings[397] = ItemType.MagentaConcrete;
|
||||
mappings[398] = ItemType.LightBlueConcrete;
|
||||
mappings[399] = ItemType.YellowConcrete;
|
||||
mappings[400] = ItemType.LimeConcrete;
|
||||
mappings[401] = ItemType.PinkConcrete;
|
||||
mappings[402] = ItemType.GrayConcrete;
|
||||
mappings[403] = ItemType.LightGrayConcrete;
|
||||
mappings[404] = ItemType.CyanConcrete;
|
||||
mappings[405] = ItemType.PurpleConcrete;
|
||||
mappings[406] = ItemType.BlueConcrete;
|
||||
mappings[407] = ItemType.BrownConcrete;
|
||||
mappings[408] = ItemType.GreenConcrete;
|
||||
mappings[409] = ItemType.RedConcrete;
|
||||
mappings[410] = ItemType.BlackConcrete;
|
||||
mappings[411] = ItemType.WhiteConcretePowder;
|
||||
mappings[412] = ItemType.OrangeConcretePowder;
|
||||
mappings[413] = ItemType.MagentaConcretePowder;
|
||||
mappings[414] = ItemType.LightBlueConcretePowder;
|
||||
mappings[415] = ItemType.YellowConcretePowder;
|
||||
mappings[416] = ItemType.LimeConcretePowder;
|
||||
mappings[417] = ItemType.PinkConcretePowder;
|
||||
mappings[418] = ItemType.GrayConcretePowder;
|
||||
mappings[419] = ItemType.LightGrayConcretePowder;
|
||||
mappings[420] = ItemType.CyanConcretePowder;
|
||||
mappings[421] = ItemType.PurpleConcretePowder;
|
||||
mappings[422] = ItemType.BlueConcretePowder;
|
||||
mappings[423] = ItemType.BrownConcretePowder;
|
||||
mappings[424] = ItemType.GreenConcretePowder;
|
||||
mappings[425] = ItemType.RedConcretePowder;
|
||||
mappings[426] = ItemType.BlackConcretePowder;
|
||||
mappings[427] = ItemType.TurtleEgg;
|
||||
mappings[428] = ItemType.DeadTubeCoralBlock;
|
||||
mappings[429] = ItemType.DeadBrainCoralBlock;
|
||||
mappings[430] = ItemType.DeadBubbleCoralBlock;
|
||||
mappings[431] = ItemType.DeadFireCoralBlock;
|
||||
mappings[432] = ItemType.DeadHornCoralBlock;
|
||||
mappings[433] = ItemType.TubeCoralBlock;
|
||||
mappings[434] = ItemType.BrainCoralBlock;
|
||||
mappings[435] = ItemType.BubbleCoralBlock;
|
||||
mappings[436] = ItemType.FireCoralBlock;
|
||||
mappings[437] = ItemType.HornCoralBlock;
|
||||
mappings[438] = ItemType.TubeCoral;
|
||||
mappings[439] = ItemType.BrainCoral;
|
||||
mappings[440] = ItemType.BubbleCoral;
|
||||
mappings[441] = ItemType.FireCoral;
|
||||
mappings[442] = ItemType.HornCoral;
|
||||
mappings[443] = ItemType.TubeCoralFan;
|
||||
mappings[444] = ItemType.BrainCoralFan;
|
||||
mappings[445] = ItemType.BubbleCoralFan;
|
||||
mappings[446] = ItemType.FireCoralFan;
|
||||
mappings[447] = ItemType.HornCoralFan;
|
||||
mappings[448] = ItemType.DeadTubeCoralFan;
|
||||
mappings[449] = ItemType.DeadBrainCoralFan;
|
||||
mappings[450] = ItemType.DeadBubbleCoralFan;
|
||||
mappings[451] = ItemType.DeadFireCoralFan;
|
||||
mappings[452] = ItemType.DeadHornCoralFan;
|
||||
mappings[453] = ItemType.BlueIce;
|
||||
mappings[454] = ItemType.Conduit;
|
||||
mappings[455] = ItemType.IronDoor;
|
||||
mappings[456] = ItemType.OakDoor;
|
||||
mappings[457] = ItemType.SpruceDoor;
|
||||
mappings[458] = ItemType.BirchDoor;
|
||||
mappings[459] = ItemType.JungleDoor;
|
||||
mappings[460] = ItemType.AcaciaDoor;
|
||||
mappings[461] = ItemType.DarkOakDoor;
|
||||
mappings[462] = ItemType.Repeater;
|
||||
mappings[463] = ItemType.Comparator;
|
||||
mappings[464] = ItemType.StructureBlock;
|
||||
mappings[465] = ItemType.TurtleHelmet;
|
||||
mappings[466] = ItemType.TurtleScute;
|
||||
mappings[467] = ItemType.IronShovel;
|
||||
mappings[468] = ItemType.IronPickaxe;
|
||||
mappings[469] = ItemType.IronAxe;
|
||||
mappings[470] = ItemType.FlintAndSteel;
|
||||
mappings[471] = ItemType.Apple;
|
||||
mappings[472] = ItemType.Bow;
|
||||
mappings[473] = ItemType.Arrow;
|
||||
mappings[474] = ItemType.Coal;
|
||||
mappings[475] = ItemType.Charcoal;
|
||||
mappings[476] = ItemType.Diamond;
|
||||
mappings[477] = ItemType.IronIngot;
|
||||
mappings[478] = ItemType.GoldIngot;
|
||||
mappings[479] = ItemType.IronSword;
|
||||
mappings[480] = ItemType.WoodenSword;
|
||||
mappings[481] = ItemType.WoodenShovel;
|
||||
mappings[482] = ItemType.WoodenPickaxe;
|
||||
mappings[483] = ItemType.WoodenAxe;
|
||||
mappings[484] = ItemType.StoneSword;
|
||||
mappings[485] = ItemType.StoneShovel;
|
||||
mappings[486] = ItemType.StonePickaxe;
|
||||
mappings[487] = ItemType.StoneAxe;
|
||||
mappings[488] = ItemType.DiamondSword;
|
||||
mappings[489] = ItemType.DiamondShovel;
|
||||
mappings[490] = ItemType.DiamondPickaxe;
|
||||
mappings[491] = ItemType.DiamondAxe;
|
||||
mappings[492] = ItemType.Stick;
|
||||
mappings[493] = ItemType.Bowl;
|
||||
mappings[494] = ItemType.MushroomStew;
|
||||
mappings[495] = ItemType.GoldenSword;
|
||||
mappings[496] = ItemType.GoldenShovel;
|
||||
mappings[497] = ItemType.GoldenPickaxe;
|
||||
mappings[498] = ItemType.GoldenAxe;
|
||||
mappings[499] = ItemType.String;
|
||||
mappings[500] = ItemType.Feather;
|
||||
mappings[501] = ItemType.Gunpowder;
|
||||
mappings[502] = ItemType.WoodenHoe;
|
||||
mappings[503] = ItemType.StoneHoe;
|
||||
mappings[504] = ItemType.IronHoe;
|
||||
mappings[505] = ItemType.DiamondHoe;
|
||||
mappings[506] = ItemType.GoldenHoe;
|
||||
mappings[507] = ItemType.WheatSeeds;
|
||||
mappings[508] = ItemType.Wheat;
|
||||
mappings[509] = ItemType.Bread;
|
||||
mappings[510] = ItemType.LeatherHelmet;
|
||||
mappings[511] = ItemType.LeatherChestplate;
|
||||
mappings[512] = ItemType.LeatherLeggings;
|
||||
mappings[513] = ItemType.LeatherBoots;
|
||||
mappings[514] = ItemType.ChainmailHelmet;
|
||||
mappings[515] = ItemType.ChainmailChestplate;
|
||||
mappings[516] = ItemType.ChainmailLeggings;
|
||||
mappings[517] = ItemType.ChainmailBoots;
|
||||
mappings[518] = ItemType.IronHelmet;
|
||||
mappings[519] = ItemType.IronChestplate;
|
||||
mappings[520] = ItemType.IronLeggings;
|
||||
mappings[521] = ItemType.IronBoots;
|
||||
mappings[522] = ItemType.DiamondHelmet;
|
||||
mappings[523] = ItemType.DiamondChestplate;
|
||||
mappings[524] = ItemType.DiamondLeggings;
|
||||
mappings[525] = ItemType.DiamondBoots;
|
||||
mappings[526] = ItemType.GoldenHelmet;
|
||||
mappings[527] = ItemType.GoldenChestplate;
|
||||
mappings[528] = ItemType.GoldenLeggings;
|
||||
mappings[529] = ItemType.GoldenBoots;
|
||||
mappings[530] = ItemType.Flint;
|
||||
mappings[531] = ItemType.Porkchop;
|
||||
mappings[532] = ItemType.CookedPorkchop;
|
||||
mappings[533] = ItemType.Painting;
|
||||
mappings[534] = ItemType.GoldenApple;
|
||||
mappings[535] = ItemType.EnchantedGoldenApple;
|
||||
mappings[536] = ItemType.OakSign;
|
||||
mappings[537] = ItemType.Bucket;
|
||||
mappings[538] = ItemType.WaterBucket;
|
||||
mappings[539] = ItemType.LavaBucket;
|
||||
mappings[540] = ItemType.Minecart;
|
||||
mappings[541] = ItemType.Saddle;
|
||||
mappings[542] = ItemType.Redstone;
|
||||
mappings[543] = ItemType.Snowball;
|
||||
mappings[544] = ItemType.OakBoat;
|
||||
mappings[545] = ItemType.Leather;
|
||||
mappings[546] = ItemType.MilkBucket;
|
||||
mappings[547] = ItemType.PufferfishBucket;
|
||||
mappings[548] = ItemType.SalmonBucket;
|
||||
mappings[549] = ItemType.CodBucket;
|
||||
mappings[550] = ItemType.TropicalFishBucket;
|
||||
mappings[551] = ItemType.Brick;
|
||||
mappings[552] = ItemType.ClayBall;
|
||||
mappings[553] = ItemType.SugarCane;
|
||||
mappings[554] = ItemType.Kelp;
|
||||
mappings[555] = ItemType.DriedKelpBlock;
|
||||
mappings[556] = ItemType.Paper;
|
||||
mappings[557] = ItemType.Book;
|
||||
mappings[558] = ItemType.SlimeBall;
|
||||
mappings[559] = ItemType.ChestMinecart;
|
||||
mappings[560] = ItemType.FurnaceMinecart;
|
||||
mappings[561] = ItemType.Egg;
|
||||
mappings[562] = ItemType.Compass;
|
||||
mappings[563] = ItemType.FishingRod;
|
||||
mappings[564] = ItemType.Clock;
|
||||
mappings[565] = ItemType.GlowstoneDust;
|
||||
mappings[566] = ItemType.Cod;
|
||||
mappings[567] = ItemType.Salmon;
|
||||
mappings[568] = ItemType.TropicalFish;
|
||||
mappings[569] = ItemType.Pufferfish;
|
||||
mappings[570] = ItemType.CookedCod;
|
||||
mappings[571] = ItemType.CookedSalmon;
|
||||
mappings[572] = ItemType.InkSac;
|
||||
mappings[573] = ItemType.RedDye;
|
||||
mappings[574] = ItemType.GreenDye;
|
||||
mappings[575] = ItemType.CocoaBeans;
|
||||
mappings[576] = ItemType.LapisLazuli;
|
||||
mappings[577] = ItemType.PurpleDye;
|
||||
mappings[578] = ItemType.CyanDye;
|
||||
mappings[579] = ItemType.LightGrayDye;
|
||||
mappings[580] = ItemType.GrayDye;
|
||||
mappings[581] = ItemType.PinkDye;
|
||||
mappings[582] = ItemType.LimeDye;
|
||||
mappings[583] = ItemType.YellowDye;
|
||||
mappings[584] = ItemType.LightBlueDye;
|
||||
mappings[585] = ItemType.MagentaDye;
|
||||
mappings[586] = ItemType.OrangeDye;
|
||||
mappings[587] = ItemType.BoneMeal;
|
||||
mappings[588] = ItemType.Bone;
|
||||
mappings[589] = ItemType.Sugar;
|
||||
mappings[590] = ItemType.Cake;
|
||||
mappings[591] = ItemType.WhiteBed;
|
||||
mappings[592] = ItemType.OrangeBed;
|
||||
mappings[593] = ItemType.MagentaBed;
|
||||
mappings[594] = ItemType.LightBlueBed;
|
||||
mappings[595] = ItemType.YellowBed;
|
||||
mappings[596] = ItemType.LimeBed;
|
||||
mappings[597] = ItemType.PinkBed;
|
||||
mappings[598] = ItemType.GrayBed;
|
||||
mappings[599] = ItemType.LightGrayBed;
|
||||
mappings[600] = ItemType.CyanBed;
|
||||
mappings[601] = ItemType.PurpleBed;
|
||||
mappings[602] = ItemType.BlueBed;
|
||||
mappings[603] = ItemType.BrownBed;
|
||||
mappings[604] = ItemType.GreenBed;
|
||||
mappings[605] = ItemType.RedBed;
|
||||
mappings[606] = ItemType.BlackBed;
|
||||
mappings[607] = ItemType.Cookie;
|
||||
mappings[608] = ItemType.FilledMap;
|
||||
mappings[609] = ItemType.Shears;
|
||||
mappings[610] = ItemType.MelonSlice;
|
||||
mappings[611] = ItemType.DriedKelp;
|
||||
mappings[612] = ItemType.PumpkinSeeds;
|
||||
mappings[613] = ItemType.MelonSeeds;
|
||||
mappings[614] = ItemType.Beef;
|
||||
mappings[615] = ItemType.CookedBeef;
|
||||
mappings[616] = ItemType.Chicken;
|
||||
mappings[617] = ItemType.CookedChicken;
|
||||
mappings[618] = ItemType.RottenFlesh;
|
||||
mappings[619] = ItemType.EnderPearl;
|
||||
mappings[620] = ItemType.BlazeRod;
|
||||
mappings[621] = ItemType.GhastTear;
|
||||
mappings[622] = ItemType.GoldNugget;
|
||||
mappings[623] = ItemType.NetherWart;
|
||||
mappings[624] = ItemType.Potion;
|
||||
mappings[625] = ItemType.GlassBottle;
|
||||
mappings[626] = ItemType.SpiderEye;
|
||||
mappings[627] = ItemType.FermentedSpiderEye;
|
||||
mappings[628] = ItemType.BlazePowder;
|
||||
mappings[629] = ItemType.MagmaCream;
|
||||
mappings[630] = ItemType.BrewingStand;
|
||||
mappings[631] = ItemType.Cauldron;
|
||||
mappings[632] = ItemType.EnderEye;
|
||||
mappings[633] = ItemType.GlisteringMelonSlice;
|
||||
mappings[634] = ItemType.BatSpawnEgg;
|
||||
mappings[635] = ItemType.BlazeSpawnEgg;
|
||||
mappings[636] = ItemType.CaveSpiderSpawnEgg;
|
||||
mappings[637] = ItemType.ChickenSpawnEgg;
|
||||
mappings[638] = ItemType.CodSpawnEgg;
|
||||
mappings[639] = ItemType.CowSpawnEgg;
|
||||
mappings[640] = ItemType.CreeperSpawnEgg;
|
||||
mappings[641] = ItemType.DolphinSpawnEgg;
|
||||
mappings[642] = ItemType.DonkeySpawnEgg;
|
||||
mappings[643] = ItemType.DrownedSpawnEgg;
|
||||
mappings[644] = ItemType.ElderGuardianSpawnEgg;
|
||||
mappings[645] = ItemType.EndermanSpawnEgg;
|
||||
mappings[646] = ItemType.EndermiteSpawnEgg;
|
||||
mappings[647] = ItemType.EvokerSpawnEgg;
|
||||
mappings[648] = ItemType.GhastSpawnEgg;
|
||||
mappings[649] = ItemType.GuardianSpawnEgg;
|
||||
mappings[650] = ItemType.HorseSpawnEgg;
|
||||
mappings[651] = ItemType.HuskSpawnEgg;
|
||||
mappings[652] = ItemType.LlamaSpawnEgg;
|
||||
mappings[653] = ItemType.MagmaCubeSpawnEgg;
|
||||
mappings[654] = ItemType.MooshroomSpawnEgg;
|
||||
mappings[655] = ItemType.MuleSpawnEgg;
|
||||
mappings[656] = ItemType.OcelotSpawnEgg;
|
||||
mappings[657] = ItemType.ParrotSpawnEgg;
|
||||
mappings[658] = ItemType.PhantomSpawnEgg;
|
||||
mappings[659] = ItemType.PigSpawnEgg;
|
||||
mappings[660] = ItemType.PolarBearSpawnEgg;
|
||||
mappings[661] = ItemType.PufferfishSpawnEgg;
|
||||
mappings[662] = ItemType.RabbitSpawnEgg;
|
||||
mappings[663] = ItemType.SalmonSpawnEgg;
|
||||
mappings[664] = ItemType.SheepSpawnEgg;
|
||||
mappings[665] = ItemType.ShulkerSpawnEgg;
|
||||
mappings[666] = ItemType.SilverfishSpawnEgg;
|
||||
mappings[667] = ItemType.SkeletonSpawnEgg;
|
||||
mappings[668] = ItemType.SkeletonHorseSpawnEgg;
|
||||
mappings[669] = ItemType.SlimeSpawnEgg;
|
||||
mappings[670] = ItemType.SpiderSpawnEgg;
|
||||
mappings[671] = ItemType.SquidSpawnEgg;
|
||||
mappings[672] = ItemType.StraySpawnEgg;
|
||||
mappings[673] = ItemType.TropicalFishSpawnEgg;
|
||||
mappings[674] = ItemType.TurtleSpawnEgg;
|
||||
mappings[675] = ItemType.VexSpawnEgg;
|
||||
mappings[676] = ItemType.VillagerSpawnEgg;
|
||||
mappings[677] = ItemType.VindicatorSpawnEgg;
|
||||
mappings[678] = ItemType.WitchSpawnEgg;
|
||||
mappings[679] = ItemType.WitherSkeletonSpawnEgg;
|
||||
mappings[680] = ItemType.WolfSpawnEgg;
|
||||
mappings[681] = ItemType.ZombieSpawnEgg;
|
||||
mappings[682] = ItemType.ZombieHorseSpawnEgg;
|
||||
mappings[683] = ItemType.ZombifiedPiglinSpawnEgg;
|
||||
mappings[684] = ItemType.ZombieVillagerSpawnEgg;
|
||||
mappings[685] = ItemType.ExperienceBottle;
|
||||
mappings[686] = ItemType.FireCharge;
|
||||
mappings[687] = ItemType.WritableBook;
|
||||
mappings[688] = ItemType.WrittenBook;
|
||||
mappings[689] = ItemType.Emerald;
|
||||
mappings[690] = ItemType.ItemFrame;
|
||||
mappings[691] = ItemType.FlowerPot;
|
||||
mappings[692] = ItemType.Carrot;
|
||||
mappings[693] = ItemType.Potato;
|
||||
mappings[694] = ItemType.BakedPotato;
|
||||
mappings[695] = ItemType.PoisonousPotato;
|
||||
mappings[696] = ItemType.Map;
|
||||
mappings[697] = ItemType.GoldenCarrot;
|
||||
mappings[698] = ItemType.SkeletonSkull;
|
||||
mappings[699] = ItemType.WitherSkeletonSkull;
|
||||
mappings[700] = ItemType.PlayerHead;
|
||||
mappings[701] = ItemType.ZombieHead;
|
||||
mappings[702] = ItemType.CreeperHead;
|
||||
mappings[703] = ItemType.DragonHead;
|
||||
mappings[704] = ItemType.CarrotOnAStick;
|
||||
mappings[705] = ItemType.NetherStar;
|
||||
mappings[706] = ItemType.PumpkinPie;
|
||||
mappings[707] = ItemType.FireworkRocket;
|
||||
mappings[708] = ItemType.FireworkStar;
|
||||
mappings[709] = ItemType.EnchantedBook;
|
||||
mappings[710] = ItemType.NetherBrick;
|
||||
mappings[711] = ItemType.Quartz;
|
||||
mappings[712] = ItemType.TntMinecart;
|
||||
mappings[713] = ItemType.HopperMinecart;
|
||||
mappings[714] = ItemType.PrismarineShard;
|
||||
mappings[715] = ItemType.PrismarineCrystals;
|
||||
mappings[716] = ItemType.Rabbit;
|
||||
mappings[717] = ItemType.CookedRabbit;
|
||||
mappings[718] = ItemType.RabbitStew;
|
||||
mappings[719] = ItemType.RabbitFoot;
|
||||
mappings[720] = ItemType.RabbitHide;
|
||||
mappings[721] = ItemType.ArmorStand;
|
||||
mappings[722] = ItemType.IronHorseArmor;
|
||||
mappings[723] = ItemType.GoldenHorseArmor;
|
||||
mappings[724] = ItemType.DiamondHorseArmor;
|
||||
mappings[725] = ItemType.Lead;
|
||||
mappings[726] = ItemType.NameTag;
|
||||
mappings[727] = ItemType.CommandBlockMinecart;
|
||||
mappings[728] = ItemType.Mutton;
|
||||
mappings[729] = ItemType.CookedMutton;
|
||||
mappings[730] = ItemType.WhiteBanner;
|
||||
mappings[731] = ItemType.OrangeBanner;
|
||||
mappings[732] = ItemType.MagentaBanner;
|
||||
mappings[733] = ItemType.LightBlueBanner;
|
||||
mappings[734] = ItemType.YellowBanner;
|
||||
mappings[735] = ItemType.LimeBanner;
|
||||
mappings[736] = ItemType.PinkBanner;
|
||||
mappings[737] = ItemType.GrayBanner;
|
||||
mappings[738] = ItemType.LightGrayBanner;
|
||||
mappings[739] = ItemType.CyanBanner;
|
||||
mappings[740] = ItemType.PurpleBanner;
|
||||
mappings[741] = ItemType.BlueBanner;
|
||||
mappings[742] = ItemType.BrownBanner;
|
||||
mappings[743] = ItemType.GreenBanner;
|
||||
mappings[744] = ItemType.RedBanner;
|
||||
mappings[745] = ItemType.BlackBanner;
|
||||
mappings[746] = ItemType.EndCrystal;
|
||||
mappings[747] = ItemType.ChorusFruit;
|
||||
mappings[748] = ItemType.PoppedChorusFruit;
|
||||
mappings[749] = ItemType.Beetroot;
|
||||
mappings[750] = ItemType.BeetrootSeeds;
|
||||
mappings[751] = ItemType.BeetrootSoup;
|
||||
mappings[752] = ItemType.DragonBreath;
|
||||
mappings[753] = ItemType.SplashPotion;
|
||||
mappings[754] = ItemType.SpectralArrow;
|
||||
mappings[755] = ItemType.TippedArrow;
|
||||
mappings[756] = ItemType.LingeringPotion;
|
||||
mappings[757] = ItemType.Shield;
|
||||
mappings[758] = ItemType.Elytra;
|
||||
mappings[759] = ItemType.SpruceBoat;
|
||||
mappings[760] = ItemType.BirchBoat;
|
||||
mappings[761] = ItemType.JungleBoat;
|
||||
mappings[762] = ItemType.AcaciaBoat;
|
||||
mappings[763] = ItemType.DarkOakBoat;
|
||||
mappings[764] = ItemType.TotemOfUndying;
|
||||
mappings[765] = ItemType.ShulkerShell;
|
||||
mappings[766] = ItemType.IronNugget;
|
||||
mappings[767] = ItemType.KnowledgeBook;
|
||||
mappings[768] = ItemType.DebugStick;
|
||||
mappings[769] = ItemType.MusicDisc13;
|
||||
mappings[770] = ItemType.MusicDiscCat;
|
||||
mappings[771] = ItemType.MusicDiscBlocks;
|
||||
mappings[772] = ItemType.MusicDiscChirp;
|
||||
mappings[773] = ItemType.MusicDiscFar;
|
||||
mappings[774] = ItemType.MusicDiscMall;
|
||||
mappings[775] = ItemType.MusicDiscMellohi;
|
||||
mappings[776] = ItemType.MusicDiscStal;
|
||||
mappings[777] = ItemType.MusicDiscStrad;
|
||||
mappings[778] = ItemType.MusicDiscWard;
|
||||
mappings[779] = ItemType.MusicDisc11;
|
||||
mappings[780] = ItemType.MusicDiscWait;
|
||||
mappings[781] = ItemType.Trident;
|
||||
mappings[782] = ItemType.PhantomMembrane;
|
||||
mappings[783] = ItemType.NautilusShell;
|
||||
mappings[784] = ItemType.HeartOfTheSea;
|
||||
}
|
||||
|
||||
protected override Dictionary<int, ItemType> GetDict()
|
||||
{
|
||||
return mappings;
|
||||
}
|
||||
}
|
||||
}
|
||||
808
MinecraftClient/Inventory/ItemPalettes/ItemPalette1132.cs
Normal file
808
MinecraftClient/Inventory/ItemPalettes/ItemPalette1132.cs
Normal file
|
|
@ -0,0 +1,808 @@
|
|||
using System.Collections.Generic;
|
||||
|
||||
namespace MinecraftClient.Inventory.ItemPalettes
|
||||
{
|
||||
public class ItemPalette1132 : ItemPalette
|
||||
{
|
||||
private static readonly Dictionary<int, ItemType> mappings = new();
|
||||
|
||||
static ItemPalette1132()
|
||||
{
|
||||
mappings[0] = ItemType.Air;
|
||||
mappings[1] = ItemType.Stone;
|
||||
mappings[2] = ItemType.Granite;
|
||||
mappings[3] = ItemType.PolishedGranite;
|
||||
mappings[4] = ItemType.Diorite;
|
||||
mappings[5] = ItemType.PolishedDiorite;
|
||||
mappings[6] = ItemType.Andesite;
|
||||
mappings[7] = ItemType.PolishedAndesite;
|
||||
mappings[8] = ItemType.GrassBlock;
|
||||
mappings[9] = ItemType.Dirt;
|
||||
mappings[10] = ItemType.CoarseDirt;
|
||||
mappings[11] = ItemType.Podzol;
|
||||
mappings[12] = ItemType.Cobblestone;
|
||||
mappings[13] = ItemType.OakPlanks;
|
||||
mappings[14] = ItemType.SprucePlanks;
|
||||
mappings[15] = ItemType.BirchPlanks;
|
||||
mappings[16] = ItemType.JunglePlanks;
|
||||
mappings[17] = ItemType.AcaciaPlanks;
|
||||
mappings[18] = ItemType.DarkOakPlanks;
|
||||
mappings[19] = ItemType.OakSapling;
|
||||
mappings[20] = ItemType.SpruceSapling;
|
||||
mappings[21] = ItemType.BirchSapling;
|
||||
mappings[22] = ItemType.JungleSapling;
|
||||
mappings[23] = ItemType.AcaciaSapling;
|
||||
mappings[24] = ItemType.DarkOakSapling;
|
||||
mappings[25] = ItemType.Bedrock;
|
||||
mappings[26] = ItemType.Sand;
|
||||
mappings[27] = ItemType.RedSand;
|
||||
mappings[28] = ItemType.Gravel;
|
||||
mappings[29] = ItemType.GoldOre;
|
||||
mappings[30] = ItemType.IronOre;
|
||||
mappings[31] = ItemType.CoalOre;
|
||||
mappings[32] = ItemType.OakLog;
|
||||
mappings[33] = ItemType.SpruceLog;
|
||||
mappings[34] = ItemType.BirchLog;
|
||||
mappings[35] = ItemType.JungleLog;
|
||||
mappings[36] = ItemType.AcaciaLog;
|
||||
mappings[37] = ItemType.DarkOakLog;
|
||||
mappings[38] = ItemType.StrippedOakLog;
|
||||
mappings[39] = ItemType.StrippedSpruceLog;
|
||||
mappings[40] = ItemType.StrippedBirchLog;
|
||||
mappings[41] = ItemType.StrippedJungleLog;
|
||||
mappings[42] = ItemType.StrippedAcaciaLog;
|
||||
mappings[43] = ItemType.StrippedDarkOakLog;
|
||||
mappings[44] = ItemType.StrippedOakWood;
|
||||
mappings[45] = ItemType.StrippedSpruceWood;
|
||||
mappings[46] = ItemType.StrippedBirchWood;
|
||||
mappings[47] = ItemType.StrippedJungleWood;
|
||||
mappings[48] = ItemType.StrippedAcaciaWood;
|
||||
mappings[49] = ItemType.StrippedDarkOakWood;
|
||||
mappings[50] = ItemType.OakWood;
|
||||
mappings[51] = ItemType.SpruceWood;
|
||||
mappings[52] = ItemType.BirchWood;
|
||||
mappings[53] = ItemType.JungleWood;
|
||||
mappings[54] = ItemType.AcaciaWood;
|
||||
mappings[55] = ItemType.DarkOakWood;
|
||||
mappings[56] = ItemType.OakLeaves;
|
||||
mappings[57] = ItemType.SpruceLeaves;
|
||||
mappings[58] = ItemType.BirchLeaves;
|
||||
mappings[59] = ItemType.JungleLeaves;
|
||||
mappings[60] = ItemType.AcaciaLeaves;
|
||||
mappings[61] = ItemType.DarkOakLeaves;
|
||||
mappings[62] = ItemType.Sponge;
|
||||
mappings[63] = ItemType.WetSponge;
|
||||
mappings[64] = ItemType.Glass;
|
||||
mappings[65] = ItemType.LapisOre;
|
||||
mappings[66] = ItemType.LapisBlock;
|
||||
mappings[67] = ItemType.Dispenser;
|
||||
mappings[68] = ItemType.Sandstone;
|
||||
mappings[69] = ItemType.ChiseledSandstone;
|
||||
mappings[70] = ItemType.CutSandstone;
|
||||
mappings[71] = ItemType.NoteBlock;
|
||||
mappings[72] = ItemType.PoweredRail;
|
||||
mappings[73] = ItemType.DetectorRail;
|
||||
mappings[74] = ItemType.StickyPiston;
|
||||
mappings[75] = ItemType.Cobweb;
|
||||
mappings[76] = ItemType.ShortGrass;
|
||||
mappings[77] = ItemType.Fern;
|
||||
mappings[78] = ItemType.DeadBush;
|
||||
mappings[79] = ItemType.Seagrass;
|
||||
mappings[80] = ItemType.SeaPickle;
|
||||
mappings[81] = ItemType.Piston;
|
||||
mappings[82] = ItemType.WhiteWool;
|
||||
mappings[83] = ItemType.OrangeWool;
|
||||
mappings[84] = ItemType.MagentaWool;
|
||||
mappings[85] = ItemType.LightBlueWool;
|
||||
mappings[86] = ItemType.YellowWool;
|
||||
mappings[87] = ItemType.LimeWool;
|
||||
mappings[88] = ItemType.PinkWool;
|
||||
mappings[89] = ItemType.GrayWool;
|
||||
mappings[90] = ItemType.LightGrayWool;
|
||||
mappings[91] = ItemType.CyanWool;
|
||||
mappings[92] = ItemType.PurpleWool;
|
||||
mappings[93] = ItemType.BlueWool;
|
||||
mappings[94] = ItemType.BrownWool;
|
||||
mappings[95] = ItemType.GreenWool;
|
||||
mappings[96] = ItemType.RedWool;
|
||||
mappings[97] = ItemType.BlackWool;
|
||||
mappings[98] = ItemType.Dandelion;
|
||||
mappings[99] = ItemType.Poppy;
|
||||
mappings[100] = ItemType.BlueOrchid;
|
||||
mappings[101] = ItemType.Allium;
|
||||
mappings[102] = ItemType.AzureBluet;
|
||||
mappings[103] = ItemType.RedTulip;
|
||||
mappings[104] = ItemType.OrangeTulip;
|
||||
mappings[105] = ItemType.WhiteTulip;
|
||||
mappings[106] = ItemType.PinkTulip;
|
||||
mappings[107] = ItemType.OxeyeDaisy;
|
||||
mappings[108] = ItemType.BrownMushroom;
|
||||
mappings[109] = ItemType.RedMushroom;
|
||||
mappings[110] = ItemType.GoldBlock;
|
||||
mappings[111] = ItemType.IronBlock;
|
||||
mappings[112] = ItemType.OakSlab;
|
||||
mappings[113] = ItemType.SpruceSlab;
|
||||
mappings[114] = ItemType.BirchSlab;
|
||||
mappings[115] = ItemType.JungleSlab;
|
||||
mappings[116] = ItemType.AcaciaSlab;
|
||||
mappings[117] = ItemType.DarkOakSlab;
|
||||
mappings[118] = ItemType.StoneSlab;
|
||||
mappings[119] = ItemType.SandstoneSlab;
|
||||
mappings[120] = ItemType.PetrifiedOakSlab;
|
||||
mappings[121] = ItemType.CobblestoneSlab;
|
||||
mappings[122] = ItemType.BrickSlab;
|
||||
mappings[123] = ItemType.StoneBrickSlab;
|
||||
mappings[124] = ItemType.NetherBrickSlab;
|
||||
mappings[125] = ItemType.QuartzSlab;
|
||||
mappings[126] = ItemType.RedSandstoneSlab;
|
||||
mappings[127] = ItemType.PurpurSlab;
|
||||
mappings[128] = ItemType.PrismarineSlab;
|
||||
mappings[129] = ItemType.PrismarineBrickSlab;
|
||||
mappings[130] = ItemType.DarkPrismarineSlab;
|
||||
mappings[131] = ItemType.SmoothQuartz;
|
||||
mappings[132] = ItemType.SmoothRedSandstone;
|
||||
mappings[133] = ItemType.SmoothSandstone;
|
||||
mappings[134] = ItemType.SmoothStone;
|
||||
mappings[135] = ItemType.Bricks;
|
||||
mappings[136] = ItemType.Tnt;
|
||||
mappings[137] = ItemType.Bookshelf;
|
||||
mappings[138] = ItemType.MossyCobblestone;
|
||||
mappings[139] = ItemType.Obsidian;
|
||||
mappings[140] = ItemType.Torch;
|
||||
mappings[141] = ItemType.EndRod;
|
||||
mappings[142] = ItemType.ChorusPlant;
|
||||
mappings[143] = ItemType.ChorusFlower;
|
||||
mappings[144] = ItemType.PurpurBlock;
|
||||
mappings[145] = ItemType.PurpurPillar;
|
||||
mappings[146] = ItemType.PurpurStairs;
|
||||
mappings[147] = ItemType.Spawner;
|
||||
mappings[148] = ItemType.OakStairs;
|
||||
mappings[149] = ItemType.Chest;
|
||||
mappings[150] = ItemType.DiamondOre;
|
||||
mappings[151] = ItemType.DiamondBlock;
|
||||
mappings[152] = ItemType.CraftingTable;
|
||||
mappings[153] = ItemType.Farmland;
|
||||
mappings[154] = ItemType.Furnace;
|
||||
mappings[155] = ItemType.Ladder;
|
||||
mappings[156] = ItemType.Rail;
|
||||
mappings[157] = ItemType.CobblestoneStairs;
|
||||
mappings[158] = ItemType.Lever;
|
||||
mappings[159] = ItemType.StonePressurePlate;
|
||||
mappings[160] = ItemType.OakPressurePlate;
|
||||
mappings[161] = ItemType.SprucePressurePlate;
|
||||
mappings[162] = ItemType.BirchPressurePlate;
|
||||
mappings[163] = ItemType.JunglePressurePlate;
|
||||
mappings[164] = ItemType.AcaciaPressurePlate;
|
||||
mappings[165] = ItemType.DarkOakPressurePlate;
|
||||
mappings[166] = ItemType.RedstoneOre;
|
||||
mappings[167] = ItemType.RedstoneTorch;
|
||||
mappings[168] = ItemType.StoneButton;
|
||||
mappings[169] = ItemType.Snow;
|
||||
mappings[170] = ItemType.Ice;
|
||||
mappings[171] = ItemType.SnowBlock;
|
||||
mappings[172] = ItemType.Cactus;
|
||||
mappings[173] = ItemType.Clay;
|
||||
mappings[174] = ItemType.Jukebox;
|
||||
mappings[175] = ItemType.OakFence;
|
||||
mappings[176] = ItemType.SpruceFence;
|
||||
mappings[177] = ItemType.BirchFence;
|
||||
mappings[178] = ItemType.JungleFence;
|
||||
mappings[179] = ItemType.AcaciaFence;
|
||||
mappings[180] = ItemType.DarkOakFence;
|
||||
mappings[181] = ItemType.Pumpkin;
|
||||
mappings[182] = ItemType.CarvedPumpkin;
|
||||
mappings[183] = ItemType.Netherrack;
|
||||
mappings[184] = ItemType.SoulSand;
|
||||
mappings[185] = ItemType.Glowstone;
|
||||
mappings[186] = ItemType.JackOLantern;
|
||||
mappings[187] = ItemType.OakTrapdoor;
|
||||
mappings[188] = ItemType.SpruceTrapdoor;
|
||||
mappings[189] = ItemType.BirchTrapdoor;
|
||||
mappings[190] = ItemType.JungleTrapdoor;
|
||||
mappings[191] = ItemType.AcaciaTrapdoor;
|
||||
mappings[192] = ItemType.DarkOakTrapdoor;
|
||||
mappings[193] = ItemType.InfestedStone;
|
||||
mappings[194] = ItemType.InfestedCobblestone;
|
||||
mappings[195] = ItemType.InfestedStoneBricks;
|
||||
mappings[196] = ItemType.InfestedMossyStoneBricks;
|
||||
mappings[197] = ItemType.InfestedCrackedStoneBricks;
|
||||
mappings[198] = ItemType.InfestedChiseledStoneBricks;
|
||||
mappings[199] = ItemType.StoneBricks;
|
||||
mappings[200] = ItemType.MossyStoneBricks;
|
||||
mappings[201] = ItemType.CrackedStoneBricks;
|
||||
mappings[202] = ItemType.ChiseledStoneBricks;
|
||||
mappings[203] = ItemType.BrownMushroomBlock;
|
||||
mappings[204] = ItemType.RedMushroomBlock;
|
||||
mappings[205] = ItemType.MushroomStem;
|
||||
mappings[206] = ItemType.IronBars;
|
||||
mappings[207] = ItemType.GlassPane;
|
||||
mappings[208] = ItemType.Melon;
|
||||
mappings[209] = ItemType.Vine;
|
||||
mappings[210] = ItemType.OakFenceGate;
|
||||
mappings[211] = ItemType.SpruceFenceGate;
|
||||
mappings[212] = ItemType.BirchFenceGate;
|
||||
mappings[213] = ItemType.JungleFenceGate;
|
||||
mappings[214] = ItemType.AcaciaFenceGate;
|
||||
mappings[215] = ItemType.DarkOakFenceGate;
|
||||
mappings[216] = ItemType.BrickStairs;
|
||||
mappings[217] = ItemType.StoneBrickStairs;
|
||||
mappings[218] = ItemType.Mycelium;
|
||||
mappings[219] = ItemType.LilyPad;
|
||||
mappings[220] = ItemType.NetherBricks;
|
||||
mappings[221] = ItemType.NetherBrickFence;
|
||||
mappings[222] = ItemType.NetherBrickStairs;
|
||||
mappings[223] = ItemType.EnchantingTable;
|
||||
mappings[224] = ItemType.EndPortalFrame;
|
||||
mappings[225] = ItemType.EndStone;
|
||||
mappings[226] = ItemType.EndStoneBricks;
|
||||
mappings[227] = ItemType.DragonEgg;
|
||||
mappings[228] = ItemType.RedstoneLamp;
|
||||
mappings[229] = ItemType.SandstoneStairs;
|
||||
mappings[230] = ItemType.EmeraldOre;
|
||||
mappings[231] = ItemType.EnderChest;
|
||||
mappings[232] = ItemType.TripwireHook;
|
||||
mappings[233] = ItemType.EmeraldBlock;
|
||||
mappings[234] = ItemType.SpruceStairs;
|
||||
mappings[235] = ItemType.BirchStairs;
|
||||
mappings[236] = ItemType.JungleStairs;
|
||||
mappings[237] = ItemType.CommandBlock;
|
||||
mappings[238] = ItemType.Beacon;
|
||||
mappings[239] = ItemType.CobblestoneWall;
|
||||
mappings[240] = ItemType.MossyCobblestoneWall;
|
||||
mappings[241] = ItemType.OakButton;
|
||||
mappings[242] = ItemType.SpruceButton;
|
||||
mappings[243] = ItemType.BirchButton;
|
||||
mappings[244] = ItemType.JungleButton;
|
||||
mappings[245] = ItemType.AcaciaButton;
|
||||
mappings[246] = ItemType.DarkOakButton;
|
||||
mappings[247] = ItemType.Anvil;
|
||||
mappings[248] = ItemType.ChippedAnvil;
|
||||
mappings[249] = ItemType.DamagedAnvil;
|
||||
mappings[250] = ItemType.TrappedChest;
|
||||
mappings[251] = ItemType.LightWeightedPressurePlate;
|
||||
mappings[252] = ItemType.HeavyWeightedPressurePlate;
|
||||
mappings[253] = ItemType.DaylightDetector;
|
||||
mappings[254] = ItemType.RedstoneBlock;
|
||||
mappings[255] = ItemType.NetherQuartzOre;
|
||||
mappings[256] = ItemType.Hopper;
|
||||
mappings[257] = ItemType.ChiseledQuartzBlock;
|
||||
mappings[258] = ItemType.QuartzBlock;
|
||||
mappings[259] = ItemType.QuartzPillar;
|
||||
mappings[260] = ItemType.QuartzStairs;
|
||||
mappings[261] = ItemType.ActivatorRail;
|
||||
mappings[262] = ItemType.Dropper;
|
||||
mappings[263] = ItemType.WhiteTerracotta;
|
||||
mappings[264] = ItemType.OrangeTerracotta;
|
||||
mappings[265] = ItemType.MagentaTerracotta;
|
||||
mappings[266] = ItemType.LightBlueTerracotta;
|
||||
mappings[267] = ItemType.YellowTerracotta;
|
||||
mappings[268] = ItemType.LimeTerracotta;
|
||||
mappings[269] = ItemType.PinkTerracotta;
|
||||
mappings[270] = ItemType.GrayTerracotta;
|
||||
mappings[271] = ItemType.LightGrayTerracotta;
|
||||
mappings[272] = ItemType.CyanTerracotta;
|
||||
mappings[273] = ItemType.PurpleTerracotta;
|
||||
mappings[274] = ItemType.BlueTerracotta;
|
||||
mappings[275] = ItemType.BrownTerracotta;
|
||||
mappings[276] = ItemType.GreenTerracotta;
|
||||
mappings[277] = ItemType.RedTerracotta;
|
||||
mappings[278] = ItemType.BlackTerracotta;
|
||||
mappings[279] = ItemType.Barrier;
|
||||
mappings[280] = ItemType.IronTrapdoor;
|
||||
mappings[281] = ItemType.HayBlock;
|
||||
mappings[282] = ItemType.WhiteCarpet;
|
||||
mappings[283] = ItemType.OrangeCarpet;
|
||||
mappings[284] = ItemType.MagentaCarpet;
|
||||
mappings[285] = ItemType.LightBlueCarpet;
|
||||
mappings[286] = ItemType.YellowCarpet;
|
||||
mappings[287] = ItemType.LimeCarpet;
|
||||
mappings[288] = ItemType.PinkCarpet;
|
||||
mappings[289] = ItemType.GrayCarpet;
|
||||
mappings[290] = ItemType.LightGrayCarpet;
|
||||
mappings[291] = ItemType.CyanCarpet;
|
||||
mappings[292] = ItemType.PurpleCarpet;
|
||||
mappings[293] = ItemType.BlueCarpet;
|
||||
mappings[294] = ItemType.BrownCarpet;
|
||||
mappings[295] = ItemType.GreenCarpet;
|
||||
mappings[296] = ItemType.RedCarpet;
|
||||
mappings[297] = ItemType.BlackCarpet;
|
||||
mappings[298] = ItemType.Terracotta;
|
||||
mappings[299] = ItemType.CoalBlock;
|
||||
mappings[300] = ItemType.PackedIce;
|
||||
mappings[301] = ItemType.AcaciaStairs;
|
||||
mappings[302] = ItemType.DarkOakStairs;
|
||||
mappings[303] = ItemType.SlimeBlock;
|
||||
mappings[304] = ItemType.DirtPath;
|
||||
mappings[305] = ItemType.Sunflower;
|
||||
mappings[306] = ItemType.Lilac;
|
||||
mappings[307] = ItemType.RoseBush;
|
||||
mappings[308] = ItemType.Peony;
|
||||
mappings[309] = ItemType.TallGrass;
|
||||
mappings[310] = ItemType.LargeFern;
|
||||
mappings[311] = ItemType.WhiteStainedGlass;
|
||||
mappings[312] = ItemType.OrangeStainedGlass;
|
||||
mappings[313] = ItemType.MagentaStainedGlass;
|
||||
mappings[314] = ItemType.LightBlueStainedGlass;
|
||||
mappings[315] = ItemType.YellowStainedGlass;
|
||||
mappings[316] = ItemType.LimeStainedGlass;
|
||||
mappings[317] = ItemType.PinkStainedGlass;
|
||||
mappings[318] = ItemType.GrayStainedGlass;
|
||||
mappings[319] = ItemType.LightGrayStainedGlass;
|
||||
mappings[320] = ItemType.CyanStainedGlass;
|
||||
mappings[321] = ItemType.PurpleStainedGlass;
|
||||
mappings[322] = ItemType.BlueStainedGlass;
|
||||
mappings[323] = ItemType.BrownStainedGlass;
|
||||
mappings[324] = ItemType.GreenStainedGlass;
|
||||
mappings[325] = ItemType.RedStainedGlass;
|
||||
mappings[326] = ItemType.BlackStainedGlass;
|
||||
mappings[327] = ItemType.WhiteStainedGlassPane;
|
||||
mappings[328] = ItemType.OrangeStainedGlassPane;
|
||||
mappings[329] = ItemType.MagentaStainedGlassPane;
|
||||
mappings[330] = ItemType.LightBlueStainedGlassPane;
|
||||
mappings[331] = ItemType.YellowStainedGlassPane;
|
||||
mappings[332] = ItemType.LimeStainedGlassPane;
|
||||
mappings[333] = ItemType.PinkStainedGlassPane;
|
||||
mappings[334] = ItemType.GrayStainedGlassPane;
|
||||
mappings[335] = ItemType.LightGrayStainedGlassPane;
|
||||
mappings[336] = ItemType.CyanStainedGlassPane;
|
||||
mappings[337] = ItemType.PurpleStainedGlassPane;
|
||||
mappings[338] = ItemType.BlueStainedGlassPane;
|
||||
mappings[339] = ItemType.BrownStainedGlassPane;
|
||||
mappings[340] = ItemType.GreenStainedGlassPane;
|
||||
mappings[341] = ItemType.RedStainedGlassPane;
|
||||
mappings[342] = ItemType.BlackStainedGlassPane;
|
||||
mappings[343] = ItemType.Prismarine;
|
||||
mappings[344] = ItemType.PrismarineBricks;
|
||||
mappings[345] = ItemType.DarkPrismarine;
|
||||
mappings[346] = ItemType.PrismarineStairs;
|
||||
mappings[347] = ItemType.PrismarineBrickStairs;
|
||||
mappings[348] = ItemType.DarkPrismarineStairs;
|
||||
mappings[349] = ItemType.SeaLantern;
|
||||
mappings[350] = ItemType.RedSandstone;
|
||||
mappings[351] = ItemType.ChiseledRedSandstone;
|
||||
mappings[352] = ItemType.CutRedSandstone;
|
||||
mappings[353] = ItemType.RedSandstoneStairs;
|
||||
mappings[354] = ItemType.RepeatingCommandBlock;
|
||||
mappings[355] = ItemType.ChainCommandBlock;
|
||||
mappings[356] = ItemType.MagmaBlock;
|
||||
mappings[357] = ItemType.NetherWartBlock;
|
||||
mappings[358] = ItemType.RedNetherBricks;
|
||||
mappings[359] = ItemType.BoneBlock;
|
||||
mappings[360] = ItemType.StructureVoid;
|
||||
mappings[361] = ItemType.Observer;
|
||||
mappings[362] = ItemType.ShulkerBox;
|
||||
mappings[363] = ItemType.WhiteShulkerBox;
|
||||
mappings[364] = ItemType.OrangeShulkerBox;
|
||||
mappings[365] = ItemType.MagentaShulkerBox;
|
||||
mappings[366] = ItemType.LightBlueShulkerBox;
|
||||
mappings[367] = ItemType.YellowShulkerBox;
|
||||
mappings[368] = ItemType.LimeShulkerBox;
|
||||
mappings[369] = ItemType.PinkShulkerBox;
|
||||
mappings[370] = ItemType.GrayShulkerBox;
|
||||
mappings[371] = ItemType.LightGrayShulkerBox;
|
||||
mappings[372] = ItemType.CyanShulkerBox;
|
||||
mappings[373] = ItemType.PurpleShulkerBox;
|
||||
mappings[374] = ItemType.BlueShulkerBox;
|
||||
mappings[375] = ItemType.BrownShulkerBox;
|
||||
mappings[376] = ItemType.GreenShulkerBox;
|
||||
mappings[377] = ItemType.RedShulkerBox;
|
||||
mappings[378] = ItemType.BlackShulkerBox;
|
||||
mappings[379] = ItemType.WhiteGlazedTerracotta;
|
||||
mappings[380] = ItemType.OrangeGlazedTerracotta;
|
||||
mappings[381] = ItemType.MagentaGlazedTerracotta;
|
||||
mappings[382] = ItemType.LightBlueGlazedTerracotta;
|
||||
mappings[383] = ItemType.YellowGlazedTerracotta;
|
||||
mappings[384] = ItemType.LimeGlazedTerracotta;
|
||||
mappings[385] = ItemType.PinkGlazedTerracotta;
|
||||
mappings[386] = ItemType.GrayGlazedTerracotta;
|
||||
mappings[387] = ItemType.LightGrayGlazedTerracotta;
|
||||
mappings[388] = ItemType.CyanGlazedTerracotta;
|
||||
mappings[389] = ItemType.PurpleGlazedTerracotta;
|
||||
mappings[390] = ItemType.BlueGlazedTerracotta;
|
||||
mappings[391] = ItemType.BrownGlazedTerracotta;
|
||||
mappings[392] = ItemType.GreenGlazedTerracotta;
|
||||
mappings[393] = ItemType.RedGlazedTerracotta;
|
||||
mappings[394] = ItemType.BlackGlazedTerracotta;
|
||||
mappings[395] = ItemType.WhiteConcrete;
|
||||
mappings[396] = ItemType.OrangeConcrete;
|
||||
mappings[397] = ItemType.MagentaConcrete;
|
||||
mappings[398] = ItemType.LightBlueConcrete;
|
||||
mappings[399] = ItemType.YellowConcrete;
|
||||
mappings[400] = ItemType.LimeConcrete;
|
||||
mappings[401] = ItemType.PinkConcrete;
|
||||
mappings[402] = ItemType.GrayConcrete;
|
||||
mappings[403] = ItemType.LightGrayConcrete;
|
||||
mappings[404] = ItemType.CyanConcrete;
|
||||
mappings[405] = ItemType.PurpleConcrete;
|
||||
mappings[406] = ItemType.BlueConcrete;
|
||||
mappings[407] = ItemType.BrownConcrete;
|
||||
mappings[408] = ItemType.GreenConcrete;
|
||||
mappings[409] = ItemType.RedConcrete;
|
||||
mappings[410] = ItemType.BlackConcrete;
|
||||
mappings[411] = ItemType.WhiteConcretePowder;
|
||||
mappings[412] = ItemType.OrangeConcretePowder;
|
||||
mappings[413] = ItemType.MagentaConcretePowder;
|
||||
mappings[414] = ItemType.LightBlueConcretePowder;
|
||||
mappings[415] = ItemType.YellowConcretePowder;
|
||||
mappings[416] = ItemType.LimeConcretePowder;
|
||||
mappings[417] = ItemType.PinkConcretePowder;
|
||||
mappings[418] = ItemType.GrayConcretePowder;
|
||||
mappings[419] = ItemType.LightGrayConcretePowder;
|
||||
mappings[420] = ItemType.CyanConcretePowder;
|
||||
mappings[421] = ItemType.PurpleConcretePowder;
|
||||
mappings[422] = ItemType.BlueConcretePowder;
|
||||
mappings[423] = ItemType.BrownConcretePowder;
|
||||
mappings[424] = ItemType.GreenConcretePowder;
|
||||
mappings[425] = ItemType.RedConcretePowder;
|
||||
mappings[426] = ItemType.BlackConcretePowder;
|
||||
mappings[427] = ItemType.TurtleEgg;
|
||||
mappings[428] = ItemType.DeadTubeCoralBlock;
|
||||
mappings[429] = ItemType.DeadBrainCoralBlock;
|
||||
mappings[430] = ItemType.DeadBubbleCoralBlock;
|
||||
mappings[431] = ItemType.DeadFireCoralBlock;
|
||||
mappings[432] = ItemType.DeadHornCoralBlock;
|
||||
mappings[433] = ItemType.TubeCoralBlock;
|
||||
mappings[434] = ItemType.BrainCoralBlock;
|
||||
mappings[435] = ItemType.BubbleCoralBlock;
|
||||
mappings[436] = ItemType.FireCoralBlock;
|
||||
mappings[437] = ItemType.HornCoralBlock;
|
||||
mappings[438] = ItemType.TubeCoral;
|
||||
mappings[439] = ItemType.BrainCoral;
|
||||
mappings[440] = ItemType.BubbleCoral;
|
||||
mappings[441] = ItemType.FireCoral;
|
||||
mappings[442] = ItemType.HornCoral;
|
||||
mappings[443] = ItemType.DeadBrainCoral;
|
||||
mappings[444] = ItemType.DeadBubbleCoral;
|
||||
mappings[445] = ItemType.DeadFireCoral;
|
||||
mappings[446] = ItemType.DeadHornCoral;
|
||||
mappings[447] = ItemType.DeadTubeCoral;
|
||||
mappings[448] = ItemType.TubeCoralFan;
|
||||
mappings[449] = ItemType.BrainCoralFan;
|
||||
mappings[450] = ItemType.BubbleCoralFan;
|
||||
mappings[451] = ItemType.FireCoralFan;
|
||||
mappings[452] = ItemType.HornCoralFan;
|
||||
mappings[453] = ItemType.DeadTubeCoralFan;
|
||||
mappings[454] = ItemType.DeadBrainCoralFan;
|
||||
mappings[455] = ItemType.DeadBubbleCoralFan;
|
||||
mappings[456] = ItemType.DeadFireCoralFan;
|
||||
mappings[457] = ItemType.DeadHornCoralFan;
|
||||
mappings[458] = ItemType.BlueIce;
|
||||
mappings[459] = ItemType.Conduit;
|
||||
mappings[460] = ItemType.IronDoor;
|
||||
mappings[461] = ItemType.OakDoor;
|
||||
mappings[462] = ItemType.SpruceDoor;
|
||||
mappings[463] = ItemType.BirchDoor;
|
||||
mappings[464] = ItemType.JungleDoor;
|
||||
mappings[465] = ItemType.AcaciaDoor;
|
||||
mappings[466] = ItemType.DarkOakDoor;
|
||||
mappings[467] = ItemType.Repeater;
|
||||
mappings[468] = ItemType.Comparator;
|
||||
mappings[469] = ItemType.StructureBlock;
|
||||
mappings[470] = ItemType.TurtleHelmet;
|
||||
mappings[471] = ItemType.TurtleScute;
|
||||
mappings[472] = ItemType.IronShovel;
|
||||
mappings[473] = ItemType.IronPickaxe;
|
||||
mappings[474] = ItemType.IronAxe;
|
||||
mappings[475] = ItemType.FlintAndSteel;
|
||||
mappings[476] = ItemType.Apple;
|
||||
mappings[477] = ItemType.Bow;
|
||||
mappings[478] = ItemType.Arrow;
|
||||
mappings[479] = ItemType.Coal;
|
||||
mappings[480] = ItemType.Charcoal;
|
||||
mappings[481] = ItemType.Diamond;
|
||||
mappings[482] = ItemType.IronIngot;
|
||||
mappings[483] = ItemType.GoldIngot;
|
||||
mappings[484] = ItemType.IronSword;
|
||||
mappings[485] = ItemType.WoodenSword;
|
||||
mappings[486] = ItemType.WoodenShovel;
|
||||
mappings[487] = ItemType.WoodenPickaxe;
|
||||
mappings[488] = ItemType.WoodenAxe;
|
||||
mappings[489] = ItemType.StoneSword;
|
||||
mappings[490] = ItemType.StoneShovel;
|
||||
mappings[491] = ItemType.StonePickaxe;
|
||||
mappings[492] = ItemType.StoneAxe;
|
||||
mappings[493] = ItemType.DiamondSword;
|
||||
mappings[494] = ItemType.DiamondShovel;
|
||||
mappings[495] = ItemType.DiamondPickaxe;
|
||||
mappings[496] = ItemType.DiamondAxe;
|
||||
mappings[497] = ItemType.Stick;
|
||||
mappings[498] = ItemType.Bowl;
|
||||
mappings[499] = ItemType.MushroomStew;
|
||||
mappings[500] = ItemType.GoldenSword;
|
||||
mappings[501] = ItemType.GoldenShovel;
|
||||
mappings[502] = ItemType.GoldenPickaxe;
|
||||
mappings[503] = ItemType.GoldenAxe;
|
||||
mappings[504] = ItemType.String;
|
||||
mappings[505] = ItemType.Feather;
|
||||
mappings[506] = ItemType.Gunpowder;
|
||||
mappings[507] = ItemType.WoodenHoe;
|
||||
mappings[508] = ItemType.StoneHoe;
|
||||
mappings[509] = ItemType.IronHoe;
|
||||
mappings[510] = ItemType.DiamondHoe;
|
||||
mappings[511] = ItemType.GoldenHoe;
|
||||
mappings[512] = ItemType.WheatSeeds;
|
||||
mappings[513] = ItemType.Wheat;
|
||||
mappings[514] = ItemType.Bread;
|
||||
mappings[515] = ItemType.LeatherHelmet;
|
||||
mappings[516] = ItemType.LeatherChestplate;
|
||||
mappings[517] = ItemType.LeatherLeggings;
|
||||
mappings[518] = ItemType.LeatherBoots;
|
||||
mappings[519] = ItemType.ChainmailHelmet;
|
||||
mappings[520] = ItemType.ChainmailChestplate;
|
||||
mappings[521] = ItemType.ChainmailLeggings;
|
||||
mappings[522] = ItemType.ChainmailBoots;
|
||||
mappings[523] = ItemType.IronHelmet;
|
||||
mappings[524] = ItemType.IronChestplate;
|
||||
mappings[525] = ItemType.IronLeggings;
|
||||
mappings[526] = ItemType.IronBoots;
|
||||
mappings[527] = ItemType.DiamondHelmet;
|
||||
mappings[528] = ItemType.DiamondChestplate;
|
||||
mappings[529] = ItemType.DiamondLeggings;
|
||||
mappings[530] = ItemType.DiamondBoots;
|
||||
mappings[531] = ItemType.GoldenHelmet;
|
||||
mappings[532] = ItemType.GoldenChestplate;
|
||||
mappings[533] = ItemType.GoldenLeggings;
|
||||
mappings[534] = ItemType.GoldenBoots;
|
||||
mappings[535] = ItemType.Flint;
|
||||
mappings[536] = ItemType.Porkchop;
|
||||
mappings[537] = ItemType.CookedPorkchop;
|
||||
mappings[538] = ItemType.Painting;
|
||||
mappings[539] = ItemType.GoldenApple;
|
||||
mappings[540] = ItemType.EnchantedGoldenApple;
|
||||
mappings[541] = ItemType.OakSign;
|
||||
mappings[542] = ItemType.Bucket;
|
||||
mappings[543] = ItemType.WaterBucket;
|
||||
mappings[544] = ItemType.LavaBucket;
|
||||
mappings[545] = ItemType.Minecart;
|
||||
mappings[546] = ItemType.Saddle;
|
||||
mappings[547] = ItemType.Redstone;
|
||||
mappings[548] = ItemType.Snowball;
|
||||
mappings[549] = ItemType.OakBoat;
|
||||
mappings[550] = ItemType.Leather;
|
||||
mappings[551] = ItemType.MilkBucket;
|
||||
mappings[552] = ItemType.PufferfishBucket;
|
||||
mappings[553] = ItemType.SalmonBucket;
|
||||
mappings[554] = ItemType.CodBucket;
|
||||
mappings[555] = ItemType.TropicalFishBucket;
|
||||
mappings[556] = ItemType.Brick;
|
||||
mappings[557] = ItemType.ClayBall;
|
||||
mappings[558] = ItemType.SugarCane;
|
||||
mappings[559] = ItemType.Kelp;
|
||||
mappings[560] = ItemType.DriedKelpBlock;
|
||||
mappings[561] = ItemType.Paper;
|
||||
mappings[562] = ItemType.Book;
|
||||
mappings[563] = ItemType.SlimeBall;
|
||||
mappings[564] = ItemType.ChestMinecart;
|
||||
mappings[565] = ItemType.FurnaceMinecart;
|
||||
mappings[566] = ItemType.Egg;
|
||||
mappings[567] = ItemType.Compass;
|
||||
mappings[568] = ItemType.FishingRod;
|
||||
mappings[569] = ItemType.Clock;
|
||||
mappings[570] = ItemType.GlowstoneDust;
|
||||
mappings[571] = ItemType.Cod;
|
||||
mappings[572] = ItemType.Salmon;
|
||||
mappings[573] = ItemType.TropicalFish;
|
||||
mappings[574] = ItemType.Pufferfish;
|
||||
mappings[575] = ItemType.CookedCod;
|
||||
mappings[576] = ItemType.CookedSalmon;
|
||||
mappings[577] = ItemType.InkSac;
|
||||
mappings[578] = ItemType.RedDye;
|
||||
mappings[579] = ItemType.GreenDye;
|
||||
mappings[580] = ItemType.CocoaBeans;
|
||||
mappings[581] = ItemType.LapisLazuli;
|
||||
mappings[582] = ItemType.PurpleDye;
|
||||
mappings[583] = ItemType.CyanDye;
|
||||
mappings[584] = ItemType.LightGrayDye;
|
||||
mappings[585] = ItemType.GrayDye;
|
||||
mappings[586] = ItemType.PinkDye;
|
||||
mappings[587] = ItemType.LimeDye;
|
||||
mappings[588] = ItemType.YellowDye;
|
||||
mappings[589] = ItemType.LightBlueDye;
|
||||
mappings[590] = ItemType.MagentaDye;
|
||||
mappings[591] = ItemType.OrangeDye;
|
||||
mappings[592] = ItemType.BoneMeal;
|
||||
mappings[593] = ItemType.Bone;
|
||||
mappings[594] = ItemType.Sugar;
|
||||
mappings[595] = ItemType.Cake;
|
||||
mappings[596] = ItemType.WhiteBed;
|
||||
mappings[597] = ItemType.OrangeBed;
|
||||
mappings[598] = ItemType.MagentaBed;
|
||||
mappings[599] = ItemType.LightBlueBed;
|
||||
mappings[600] = ItemType.YellowBed;
|
||||
mappings[601] = ItemType.LimeBed;
|
||||
mappings[602] = ItemType.PinkBed;
|
||||
mappings[603] = ItemType.GrayBed;
|
||||
mappings[604] = ItemType.LightGrayBed;
|
||||
mappings[605] = ItemType.CyanBed;
|
||||
mappings[606] = ItemType.PurpleBed;
|
||||
mappings[607] = ItemType.BlueBed;
|
||||
mappings[608] = ItemType.BrownBed;
|
||||
mappings[609] = ItemType.GreenBed;
|
||||
mappings[610] = ItemType.RedBed;
|
||||
mappings[611] = ItemType.BlackBed;
|
||||
mappings[612] = ItemType.Cookie;
|
||||
mappings[613] = ItemType.FilledMap;
|
||||
mappings[614] = ItemType.Shears;
|
||||
mappings[615] = ItemType.MelonSlice;
|
||||
mappings[616] = ItemType.DriedKelp;
|
||||
mappings[617] = ItemType.PumpkinSeeds;
|
||||
mappings[618] = ItemType.MelonSeeds;
|
||||
mappings[619] = ItemType.Beef;
|
||||
mappings[620] = ItemType.CookedBeef;
|
||||
mappings[621] = ItemType.Chicken;
|
||||
mappings[622] = ItemType.CookedChicken;
|
||||
mappings[623] = ItemType.RottenFlesh;
|
||||
mappings[624] = ItemType.EnderPearl;
|
||||
mappings[625] = ItemType.BlazeRod;
|
||||
mappings[626] = ItemType.GhastTear;
|
||||
mappings[627] = ItemType.GoldNugget;
|
||||
mappings[628] = ItemType.NetherWart;
|
||||
mappings[629] = ItemType.Potion;
|
||||
mappings[630] = ItemType.GlassBottle;
|
||||
mappings[631] = ItemType.SpiderEye;
|
||||
mappings[632] = ItemType.FermentedSpiderEye;
|
||||
mappings[633] = ItemType.BlazePowder;
|
||||
mappings[634] = ItemType.MagmaCream;
|
||||
mappings[635] = ItemType.BrewingStand;
|
||||
mappings[636] = ItemType.Cauldron;
|
||||
mappings[637] = ItemType.EnderEye;
|
||||
mappings[638] = ItemType.GlisteringMelonSlice;
|
||||
mappings[639] = ItemType.BatSpawnEgg;
|
||||
mappings[640] = ItemType.BlazeSpawnEgg;
|
||||
mappings[641] = ItemType.CaveSpiderSpawnEgg;
|
||||
mappings[642] = ItemType.ChickenSpawnEgg;
|
||||
mappings[643] = ItemType.CodSpawnEgg;
|
||||
mappings[644] = ItemType.CowSpawnEgg;
|
||||
mappings[645] = ItemType.CreeperSpawnEgg;
|
||||
mappings[646] = ItemType.DolphinSpawnEgg;
|
||||
mappings[647] = ItemType.DonkeySpawnEgg;
|
||||
mappings[648] = ItemType.DrownedSpawnEgg;
|
||||
mappings[649] = ItemType.ElderGuardianSpawnEgg;
|
||||
mappings[650] = ItemType.EndermanSpawnEgg;
|
||||
mappings[651] = ItemType.EndermiteSpawnEgg;
|
||||
mappings[652] = ItemType.EvokerSpawnEgg;
|
||||
mappings[653] = ItemType.GhastSpawnEgg;
|
||||
mappings[654] = ItemType.GuardianSpawnEgg;
|
||||
mappings[655] = ItemType.HorseSpawnEgg;
|
||||
mappings[656] = ItemType.HuskSpawnEgg;
|
||||
mappings[657] = ItemType.LlamaSpawnEgg;
|
||||
mappings[658] = ItemType.MagmaCubeSpawnEgg;
|
||||
mappings[659] = ItemType.MooshroomSpawnEgg;
|
||||
mappings[660] = ItemType.MuleSpawnEgg;
|
||||
mappings[661] = ItemType.OcelotSpawnEgg;
|
||||
mappings[662] = ItemType.ParrotSpawnEgg;
|
||||
mappings[663] = ItemType.PhantomSpawnEgg;
|
||||
mappings[664] = ItemType.PigSpawnEgg;
|
||||
mappings[665] = ItemType.PolarBearSpawnEgg;
|
||||
mappings[666] = ItemType.PufferfishSpawnEgg;
|
||||
mappings[667] = ItemType.RabbitSpawnEgg;
|
||||
mappings[668] = ItemType.SalmonSpawnEgg;
|
||||
mappings[669] = ItemType.SheepSpawnEgg;
|
||||
mappings[670] = ItemType.ShulkerSpawnEgg;
|
||||
mappings[671] = ItemType.SilverfishSpawnEgg;
|
||||
mappings[672] = ItemType.SkeletonSpawnEgg;
|
||||
mappings[673] = ItemType.SkeletonHorseSpawnEgg;
|
||||
mappings[674] = ItemType.SlimeSpawnEgg;
|
||||
mappings[675] = ItemType.SpiderSpawnEgg;
|
||||
mappings[676] = ItemType.SquidSpawnEgg;
|
||||
mappings[677] = ItemType.StraySpawnEgg;
|
||||
mappings[678] = ItemType.TropicalFishSpawnEgg;
|
||||
mappings[679] = ItemType.TurtleSpawnEgg;
|
||||
mappings[680] = ItemType.VexSpawnEgg;
|
||||
mappings[681] = ItemType.VillagerSpawnEgg;
|
||||
mappings[682] = ItemType.VindicatorSpawnEgg;
|
||||
mappings[683] = ItemType.WitchSpawnEgg;
|
||||
mappings[684] = ItemType.WitherSkeletonSpawnEgg;
|
||||
mappings[685] = ItemType.WolfSpawnEgg;
|
||||
mappings[686] = ItemType.ZombieSpawnEgg;
|
||||
mappings[687] = ItemType.ZombieHorseSpawnEgg;
|
||||
mappings[688] = ItemType.ZombifiedPiglinSpawnEgg;
|
||||
mappings[689] = ItemType.ZombieVillagerSpawnEgg;
|
||||
mappings[690] = ItemType.ExperienceBottle;
|
||||
mappings[691] = ItemType.FireCharge;
|
||||
mappings[692] = ItemType.WritableBook;
|
||||
mappings[693] = ItemType.WrittenBook;
|
||||
mappings[694] = ItemType.Emerald;
|
||||
mappings[695] = ItemType.ItemFrame;
|
||||
mappings[696] = ItemType.FlowerPot;
|
||||
mappings[697] = ItemType.Carrot;
|
||||
mappings[698] = ItemType.Potato;
|
||||
mappings[699] = ItemType.BakedPotato;
|
||||
mappings[700] = ItemType.PoisonousPotato;
|
||||
mappings[701] = ItemType.Map;
|
||||
mappings[702] = ItemType.GoldenCarrot;
|
||||
mappings[703] = ItemType.SkeletonSkull;
|
||||
mappings[704] = ItemType.WitherSkeletonSkull;
|
||||
mappings[705] = ItemType.PlayerHead;
|
||||
mappings[706] = ItemType.ZombieHead;
|
||||
mappings[707] = ItemType.CreeperHead;
|
||||
mappings[708] = ItemType.DragonHead;
|
||||
mappings[709] = ItemType.CarrotOnAStick;
|
||||
mappings[710] = ItemType.NetherStar;
|
||||
mappings[711] = ItemType.PumpkinPie;
|
||||
mappings[712] = ItemType.FireworkRocket;
|
||||
mappings[713] = ItemType.FireworkStar;
|
||||
mappings[714] = ItemType.EnchantedBook;
|
||||
mappings[715] = ItemType.NetherBrick;
|
||||
mappings[716] = ItemType.Quartz;
|
||||
mappings[717] = ItemType.TntMinecart;
|
||||
mappings[718] = ItemType.HopperMinecart;
|
||||
mappings[719] = ItemType.PrismarineShard;
|
||||
mappings[720] = ItemType.PrismarineCrystals;
|
||||
mappings[721] = ItemType.Rabbit;
|
||||
mappings[722] = ItemType.CookedRabbit;
|
||||
mappings[723] = ItemType.RabbitStew;
|
||||
mappings[724] = ItemType.RabbitFoot;
|
||||
mappings[725] = ItemType.RabbitHide;
|
||||
mappings[726] = ItemType.ArmorStand;
|
||||
mappings[727] = ItemType.IronHorseArmor;
|
||||
mappings[728] = ItemType.GoldenHorseArmor;
|
||||
mappings[729] = ItemType.DiamondHorseArmor;
|
||||
mappings[730] = ItemType.Lead;
|
||||
mappings[731] = ItemType.NameTag;
|
||||
mappings[732] = ItemType.CommandBlockMinecart;
|
||||
mappings[733] = ItemType.Mutton;
|
||||
mappings[734] = ItemType.CookedMutton;
|
||||
mappings[735] = ItemType.WhiteBanner;
|
||||
mappings[736] = ItemType.OrangeBanner;
|
||||
mappings[737] = ItemType.MagentaBanner;
|
||||
mappings[738] = ItemType.LightBlueBanner;
|
||||
mappings[739] = ItemType.YellowBanner;
|
||||
mappings[740] = ItemType.LimeBanner;
|
||||
mappings[741] = ItemType.PinkBanner;
|
||||
mappings[742] = ItemType.GrayBanner;
|
||||
mappings[743] = ItemType.LightGrayBanner;
|
||||
mappings[744] = ItemType.CyanBanner;
|
||||
mappings[745] = ItemType.PurpleBanner;
|
||||
mappings[746] = ItemType.BlueBanner;
|
||||
mappings[747] = ItemType.BrownBanner;
|
||||
mappings[748] = ItemType.GreenBanner;
|
||||
mappings[749] = ItemType.RedBanner;
|
||||
mappings[750] = ItemType.BlackBanner;
|
||||
mappings[751] = ItemType.EndCrystal;
|
||||
mappings[752] = ItemType.ChorusFruit;
|
||||
mappings[753] = ItemType.PoppedChorusFruit;
|
||||
mappings[754] = ItemType.Beetroot;
|
||||
mappings[755] = ItemType.BeetrootSeeds;
|
||||
mappings[756] = ItemType.BeetrootSoup;
|
||||
mappings[757] = ItemType.DragonBreath;
|
||||
mappings[758] = ItemType.SplashPotion;
|
||||
mappings[759] = ItemType.SpectralArrow;
|
||||
mappings[760] = ItemType.TippedArrow;
|
||||
mappings[761] = ItemType.LingeringPotion;
|
||||
mappings[762] = ItemType.Shield;
|
||||
mappings[763] = ItemType.Elytra;
|
||||
mappings[764] = ItemType.SpruceBoat;
|
||||
mappings[765] = ItemType.BirchBoat;
|
||||
mappings[766] = ItemType.JungleBoat;
|
||||
mappings[767] = ItemType.AcaciaBoat;
|
||||
mappings[768] = ItemType.DarkOakBoat;
|
||||
mappings[769] = ItemType.TotemOfUndying;
|
||||
mappings[770] = ItemType.ShulkerShell;
|
||||
mappings[771] = ItemType.IronNugget;
|
||||
mappings[772] = ItemType.KnowledgeBook;
|
||||
mappings[773] = ItemType.DebugStick;
|
||||
mappings[774] = ItemType.MusicDisc13;
|
||||
mappings[775] = ItemType.MusicDiscCat;
|
||||
mappings[776] = ItemType.MusicDiscBlocks;
|
||||
mappings[777] = ItemType.MusicDiscChirp;
|
||||
mappings[778] = ItemType.MusicDiscFar;
|
||||
mappings[779] = ItemType.MusicDiscMall;
|
||||
mappings[780] = ItemType.MusicDiscMellohi;
|
||||
mappings[781] = ItemType.MusicDiscStal;
|
||||
mappings[782] = ItemType.MusicDiscStrad;
|
||||
mappings[783] = ItemType.MusicDiscWard;
|
||||
mappings[784] = ItemType.MusicDisc11;
|
||||
mappings[785] = ItemType.MusicDiscWait;
|
||||
mappings[786] = ItemType.Trident;
|
||||
mappings[787] = ItemType.PhantomMembrane;
|
||||
mappings[788] = ItemType.NautilusShell;
|
||||
mappings[789] = ItemType.HeartOfTheSea;
|
||||
}
|
||||
|
||||
protected override Dictionary<int, ItemType> GetDict()
|
||||
{
|
||||
return mappings;
|
||||
}
|
||||
}
|
||||
}
|
||||
895
MinecraftClient/Inventory/ItemPalettes/ItemPalette114.cs
Normal file
895
MinecraftClient/Inventory/ItemPalettes/ItemPalette114.cs
Normal file
|
|
@ -0,0 +1,895 @@
|
|||
using System.Collections.Generic;
|
||||
|
||||
namespace MinecraftClient.Inventory.ItemPalettes
|
||||
{
|
||||
public class ItemPalette114 : ItemPalette
|
||||
{
|
||||
private static readonly Dictionary<int, ItemType> mappings = new();
|
||||
|
||||
static ItemPalette114()
|
||||
{
|
||||
mappings[0] = ItemType.Air;
|
||||
mappings[1] = ItemType.Stone;
|
||||
mappings[2] = ItemType.Granite;
|
||||
mappings[3] = ItemType.PolishedGranite;
|
||||
mappings[4] = ItemType.Diorite;
|
||||
mappings[5] = ItemType.PolishedDiorite;
|
||||
mappings[6] = ItemType.Andesite;
|
||||
mappings[7] = ItemType.PolishedAndesite;
|
||||
mappings[8] = ItemType.GrassBlock;
|
||||
mappings[9] = ItemType.Dirt;
|
||||
mappings[10] = ItemType.CoarseDirt;
|
||||
mappings[11] = ItemType.Podzol;
|
||||
mappings[12] = ItemType.Cobblestone;
|
||||
mappings[13] = ItemType.OakPlanks;
|
||||
mappings[14] = ItemType.SprucePlanks;
|
||||
mappings[15] = ItemType.BirchPlanks;
|
||||
mappings[16] = ItemType.JunglePlanks;
|
||||
mappings[17] = ItemType.AcaciaPlanks;
|
||||
mappings[18] = ItemType.DarkOakPlanks;
|
||||
mappings[19] = ItemType.OakSapling;
|
||||
mappings[20] = ItemType.SpruceSapling;
|
||||
mappings[21] = ItemType.BirchSapling;
|
||||
mappings[22] = ItemType.JungleSapling;
|
||||
mappings[23] = ItemType.AcaciaSapling;
|
||||
mappings[24] = ItemType.DarkOakSapling;
|
||||
mappings[25] = ItemType.Bedrock;
|
||||
mappings[26] = ItemType.Sand;
|
||||
mappings[27] = ItemType.RedSand;
|
||||
mappings[28] = ItemType.Gravel;
|
||||
mappings[29] = ItemType.GoldOre;
|
||||
mappings[30] = ItemType.IronOre;
|
||||
mappings[31] = ItemType.CoalOre;
|
||||
mappings[32] = ItemType.OakLog;
|
||||
mappings[33] = ItemType.SpruceLog;
|
||||
mappings[34] = ItemType.BirchLog;
|
||||
mappings[35] = ItemType.JungleLog;
|
||||
mappings[36] = ItemType.AcaciaLog;
|
||||
mappings[37] = ItemType.DarkOakLog;
|
||||
mappings[38] = ItemType.StrippedOakLog;
|
||||
mappings[39] = ItemType.StrippedSpruceLog;
|
||||
mappings[40] = ItemType.StrippedBirchLog;
|
||||
mappings[41] = ItemType.StrippedJungleLog;
|
||||
mappings[42] = ItemType.StrippedAcaciaLog;
|
||||
mappings[43] = ItemType.StrippedDarkOakLog;
|
||||
mappings[44] = ItemType.StrippedOakWood;
|
||||
mappings[45] = ItemType.StrippedSpruceWood;
|
||||
mappings[46] = ItemType.StrippedBirchWood;
|
||||
mappings[47] = ItemType.StrippedJungleWood;
|
||||
mappings[48] = ItemType.StrippedAcaciaWood;
|
||||
mappings[49] = ItemType.StrippedDarkOakWood;
|
||||
mappings[50] = ItemType.OakWood;
|
||||
mappings[51] = ItemType.SpruceWood;
|
||||
mappings[52] = ItemType.BirchWood;
|
||||
mappings[53] = ItemType.JungleWood;
|
||||
mappings[54] = ItemType.AcaciaWood;
|
||||
mappings[55] = ItemType.DarkOakWood;
|
||||
mappings[56] = ItemType.OakLeaves;
|
||||
mappings[57] = ItemType.SpruceLeaves;
|
||||
mappings[58] = ItemType.BirchLeaves;
|
||||
mappings[59] = ItemType.JungleLeaves;
|
||||
mappings[60] = ItemType.AcaciaLeaves;
|
||||
mappings[61] = ItemType.DarkOakLeaves;
|
||||
mappings[62] = ItemType.Sponge;
|
||||
mappings[63] = ItemType.WetSponge;
|
||||
mappings[64] = ItemType.Glass;
|
||||
mappings[65] = ItemType.LapisOre;
|
||||
mappings[66] = ItemType.LapisBlock;
|
||||
mappings[67] = ItemType.Dispenser;
|
||||
mappings[68] = ItemType.Sandstone;
|
||||
mappings[69] = ItemType.ChiseledSandstone;
|
||||
mappings[70] = ItemType.CutSandstone;
|
||||
mappings[71] = ItemType.NoteBlock;
|
||||
mappings[72] = ItemType.PoweredRail;
|
||||
mappings[73] = ItemType.DetectorRail;
|
||||
mappings[74] = ItemType.StickyPiston;
|
||||
mappings[75] = ItemType.Cobweb;
|
||||
mappings[76] = ItemType.ShortGrass;
|
||||
mappings[77] = ItemType.Fern;
|
||||
mappings[78] = ItemType.DeadBush;
|
||||
mappings[79] = ItemType.Seagrass;
|
||||
mappings[80] = ItemType.SeaPickle;
|
||||
mappings[81] = ItemType.Piston;
|
||||
mappings[82] = ItemType.WhiteWool;
|
||||
mappings[83] = ItemType.OrangeWool;
|
||||
mappings[84] = ItemType.MagentaWool;
|
||||
mappings[85] = ItemType.LightBlueWool;
|
||||
mappings[86] = ItemType.YellowWool;
|
||||
mappings[87] = ItemType.LimeWool;
|
||||
mappings[88] = ItemType.PinkWool;
|
||||
mappings[89] = ItemType.GrayWool;
|
||||
mappings[90] = ItemType.LightGrayWool;
|
||||
mappings[91] = ItemType.CyanWool;
|
||||
mappings[92] = ItemType.PurpleWool;
|
||||
mappings[93] = ItemType.BlueWool;
|
||||
mappings[94] = ItemType.BrownWool;
|
||||
mappings[95] = ItemType.GreenWool;
|
||||
mappings[96] = ItemType.RedWool;
|
||||
mappings[97] = ItemType.BlackWool;
|
||||
mappings[98] = ItemType.Dandelion;
|
||||
mappings[99] = ItemType.Poppy;
|
||||
mappings[100] = ItemType.BlueOrchid;
|
||||
mappings[101] = ItemType.Allium;
|
||||
mappings[102] = ItemType.AzureBluet;
|
||||
mappings[103] = ItemType.RedTulip;
|
||||
mappings[104] = ItemType.OrangeTulip;
|
||||
mappings[105] = ItemType.WhiteTulip;
|
||||
mappings[106] = ItemType.PinkTulip;
|
||||
mappings[107] = ItemType.OxeyeDaisy;
|
||||
mappings[108] = ItemType.Cornflower;
|
||||
mappings[109] = ItemType.LilyOfTheValley;
|
||||
mappings[110] = ItemType.WitherRose;
|
||||
mappings[111] = ItemType.BrownMushroom;
|
||||
mappings[112] = ItemType.RedMushroom;
|
||||
mappings[113] = ItemType.GoldBlock;
|
||||
mappings[114] = ItemType.IronBlock;
|
||||
mappings[115] = ItemType.OakSlab;
|
||||
mappings[116] = ItemType.SpruceSlab;
|
||||
mappings[117] = ItemType.BirchSlab;
|
||||
mappings[118] = ItemType.JungleSlab;
|
||||
mappings[119] = ItemType.AcaciaSlab;
|
||||
mappings[120] = ItemType.DarkOakSlab;
|
||||
mappings[121] = ItemType.StoneSlab;
|
||||
mappings[122] = ItemType.SmoothStoneSlab;
|
||||
mappings[123] = ItemType.SandstoneSlab;
|
||||
mappings[124] = ItemType.CutSandstoneSlab;
|
||||
mappings[125] = ItemType.PetrifiedOakSlab;
|
||||
mappings[126] = ItemType.CobblestoneSlab;
|
||||
mappings[127] = ItemType.BrickSlab;
|
||||
mappings[128] = ItemType.StoneBrickSlab;
|
||||
mappings[129] = ItemType.NetherBrickSlab;
|
||||
mappings[130] = ItemType.QuartzSlab;
|
||||
mappings[131] = ItemType.RedSandstoneSlab;
|
||||
mappings[132] = ItemType.CutRedSandstoneSlab;
|
||||
mappings[133] = ItemType.PurpurSlab;
|
||||
mappings[134] = ItemType.PrismarineSlab;
|
||||
mappings[135] = ItemType.PrismarineBrickSlab;
|
||||
mappings[136] = ItemType.DarkPrismarineSlab;
|
||||
mappings[137] = ItemType.SmoothQuartz;
|
||||
mappings[138] = ItemType.SmoothRedSandstone;
|
||||
mappings[139] = ItemType.SmoothSandstone;
|
||||
mappings[140] = ItemType.SmoothStone;
|
||||
mappings[141] = ItemType.Bricks;
|
||||
mappings[142] = ItemType.Tnt;
|
||||
mappings[143] = ItemType.Bookshelf;
|
||||
mappings[144] = ItemType.MossyCobblestone;
|
||||
mappings[145] = ItemType.Obsidian;
|
||||
mappings[146] = ItemType.Torch;
|
||||
mappings[147] = ItemType.EndRod;
|
||||
mappings[148] = ItemType.ChorusPlant;
|
||||
mappings[149] = ItemType.ChorusFlower;
|
||||
mappings[150] = ItemType.PurpurBlock;
|
||||
mappings[151] = ItemType.PurpurPillar;
|
||||
mappings[152] = ItemType.PurpurStairs;
|
||||
mappings[153] = ItemType.Spawner;
|
||||
mappings[154] = ItemType.OakStairs;
|
||||
mappings[155] = ItemType.Chest;
|
||||
mappings[156] = ItemType.DiamondOre;
|
||||
mappings[157] = ItemType.DiamondBlock;
|
||||
mappings[158] = ItemType.CraftingTable;
|
||||
mappings[159] = ItemType.Farmland;
|
||||
mappings[160] = ItemType.Furnace;
|
||||
mappings[161] = ItemType.Ladder;
|
||||
mappings[162] = ItemType.Rail;
|
||||
mappings[163] = ItemType.CobblestoneStairs;
|
||||
mappings[164] = ItemType.Lever;
|
||||
mappings[165] = ItemType.StonePressurePlate;
|
||||
mappings[166] = ItemType.OakPressurePlate;
|
||||
mappings[167] = ItemType.SprucePressurePlate;
|
||||
mappings[168] = ItemType.BirchPressurePlate;
|
||||
mappings[169] = ItemType.JunglePressurePlate;
|
||||
mappings[170] = ItemType.AcaciaPressurePlate;
|
||||
mappings[171] = ItemType.DarkOakPressurePlate;
|
||||
mappings[172] = ItemType.RedstoneOre;
|
||||
mappings[173] = ItemType.RedstoneTorch;
|
||||
mappings[174] = ItemType.StoneButton;
|
||||
mappings[175] = ItemType.Snow;
|
||||
mappings[176] = ItemType.Ice;
|
||||
mappings[177] = ItemType.SnowBlock;
|
||||
mappings[178] = ItemType.Cactus;
|
||||
mappings[179] = ItemType.Clay;
|
||||
mappings[180] = ItemType.Jukebox;
|
||||
mappings[181] = ItemType.OakFence;
|
||||
mappings[182] = ItemType.SpruceFence;
|
||||
mappings[183] = ItemType.BirchFence;
|
||||
mappings[184] = ItemType.JungleFence;
|
||||
mappings[185] = ItemType.AcaciaFence;
|
||||
mappings[186] = ItemType.DarkOakFence;
|
||||
mappings[187] = ItemType.Pumpkin;
|
||||
mappings[188] = ItemType.CarvedPumpkin;
|
||||
mappings[189] = ItemType.Netherrack;
|
||||
mappings[190] = ItemType.SoulSand;
|
||||
mappings[191] = ItemType.Glowstone;
|
||||
mappings[192] = ItemType.JackOLantern;
|
||||
mappings[193] = ItemType.OakTrapdoor;
|
||||
mappings[194] = ItemType.SpruceTrapdoor;
|
||||
mappings[195] = ItemType.BirchTrapdoor;
|
||||
mappings[196] = ItemType.JungleTrapdoor;
|
||||
mappings[197] = ItemType.AcaciaTrapdoor;
|
||||
mappings[198] = ItemType.DarkOakTrapdoor;
|
||||
mappings[199] = ItemType.InfestedStone;
|
||||
mappings[200] = ItemType.InfestedCobblestone;
|
||||
mappings[201] = ItemType.InfestedStoneBricks;
|
||||
mappings[202] = ItemType.InfestedMossyStoneBricks;
|
||||
mappings[203] = ItemType.InfestedCrackedStoneBricks;
|
||||
mappings[204] = ItemType.InfestedChiseledStoneBricks;
|
||||
mappings[205] = ItemType.StoneBricks;
|
||||
mappings[206] = ItemType.MossyStoneBricks;
|
||||
mappings[207] = ItemType.CrackedStoneBricks;
|
||||
mappings[208] = ItemType.ChiseledStoneBricks;
|
||||
mappings[209] = ItemType.BrownMushroomBlock;
|
||||
mappings[210] = ItemType.RedMushroomBlock;
|
||||
mappings[211] = ItemType.MushroomStem;
|
||||
mappings[212] = ItemType.IronBars;
|
||||
mappings[213] = ItemType.GlassPane;
|
||||
mappings[214] = ItemType.Melon;
|
||||
mappings[215] = ItemType.Vine;
|
||||
mappings[216] = ItemType.OakFenceGate;
|
||||
mappings[217] = ItemType.SpruceFenceGate;
|
||||
mappings[218] = ItemType.BirchFenceGate;
|
||||
mappings[219] = ItemType.JungleFenceGate;
|
||||
mappings[220] = ItemType.AcaciaFenceGate;
|
||||
mappings[221] = ItemType.DarkOakFenceGate;
|
||||
mappings[222] = ItemType.BrickStairs;
|
||||
mappings[223] = ItemType.StoneBrickStairs;
|
||||
mappings[224] = ItemType.Mycelium;
|
||||
mappings[225] = ItemType.LilyPad;
|
||||
mappings[226] = ItemType.NetherBricks;
|
||||
mappings[227] = ItemType.NetherBrickFence;
|
||||
mappings[228] = ItemType.NetherBrickStairs;
|
||||
mappings[229] = ItemType.EnchantingTable;
|
||||
mappings[230] = ItemType.EndPortalFrame;
|
||||
mappings[231] = ItemType.EndStone;
|
||||
mappings[232] = ItemType.EndStoneBricks;
|
||||
mappings[233] = ItemType.DragonEgg;
|
||||
mappings[234] = ItemType.RedstoneLamp;
|
||||
mappings[235] = ItemType.SandstoneStairs;
|
||||
mappings[236] = ItemType.EmeraldOre;
|
||||
mappings[237] = ItemType.EnderChest;
|
||||
mappings[238] = ItemType.TripwireHook;
|
||||
mappings[239] = ItemType.EmeraldBlock;
|
||||
mappings[240] = ItemType.SpruceStairs;
|
||||
mappings[241] = ItemType.BirchStairs;
|
||||
mappings[242] = ItemType.JungleStairs;
|
||||
mappings[243] = ItemType.CommandBlock;
|
||||
mappings[244] = ItemType.Beacon;
|
||||
mappings[245] = ItemType.CobblestoneWall;
|
||||
mappings[246] = ItemType.MossyCobblestoneWall;
|
||||
mappings[247] = ItemType.BrickWall;
|
||||
mappings[248] = ItemType.PrismarineWall;
|
||||
mappings[249] = ItemType.RedSandstoneWall;
|
||||
mappings[250] = ItemType.MossyStoneBrickWall;
|
||||
mappings[251] = ItemType.GraniteWall;
|
||||
mappings[252] = ItemType.StoneBrickWall;
|
||||
mappings[253] = ItemType.NetherBrickWall;
|
||||
mappings[254] = ItemType.AndesiteWall;
|
||||
mappings[255] = ItemType.RedNetherBrickWall;
|
||||
mappings[256] = ItemType.SandstoneWall;
|
||||
mappings[257] = ItemType.EndStoneBrickWall;
|
||||
mappings[258] = ItemType.DioriteWall;
|
||||
mappings[259] = ItemType.OakButton;
|
||||
mappings[260] = ItemType.SpruceButton;
|
||||
mappings[261] = ItemType.BirchButton;
|
||||
mappings[262] = ItemType.JungleButton;
|
||||
mappings[263] = ItemType.AcaciaButton;
|
||||
mappings[264] = ItemType.DarkOakButton;
|
||||
mappings[265] = ItemType.Anvil;
|
||||
mappings[266] = ItemType.ChippedAnvil;
|
||||
mappings[267] = ItemType.DamagedAnvil;
|
||||
mappings[268] = ItemType.TrappedChest;
|
||||
mappings[269] = ItemType.LightWeightedPressurePlate;
|
||||
mappings[270] = ItemType.HeavyWeightedPressurePlate;
|
||||
mappings[271] = ItemType.DaylightDetector;
|
||||
mappings[272] = ItemType.RedstoneBlock;
|
||||
mappings[273] = ItemType.NetherQuartzOre;
|
||||
mappings[274] = ItemType.Hopper;
|
||||
mappings[275] = ItemType.ChiseledQuartzBlock;
|
||||
mappings[276] = ItemType.QuartzBlock;
|
||||
mappings[277] = ItemType.QuartzPillar;
|
||||
mappings[278] = ItemType.QuartzStairs;
|
||||
mappings[279] = ItemType.ActivatorRail;
|
||||
mappings[280] = ItemType.Dropper;
|
||||
mappings[281] = ItemType.WhiteTerracotta;
|
||||
mappings[282] = ItemType.OrangeTerracotta;
|
||||
mappings[283] = ItemType.MagentaTerracotta;
|
||||
mappings[284] = ItemType.LightBlueTerracotta;
|
||||
mappings[285] = ItemType.YellowTerracotta;
|
||||
mappings[286] = ItemType.LimeTerracotta;
|
||||
mappings[287] = ItemType.PinkTerracotta;
|
||||
mappings[288] = ItemType.GrayTerracotta;
|
||||
mappings[289] = ItemType.LightGrayTerracotta;
|
||||
mappings[290] = ItemType.CyanTerracotta;
|
||||
mappings[291] = ItemType.PurpleTerracotta;
|
||||
mappings[292] = ItemType.BlueTerracotta;
|
||||
mappings[293] = ItemType.BrownTerracotta;
|
||||
mappings[294] = ItemType.GreenTerracotta;
|
||||
mappings[295] = ItemType.RedTerracotta;
|
||||
mappings[296] = ItemType.BlackTerracotta;
|
||||
mappings[297] = ItemType.Barrier;
|
||||
mappings[298] = ItemType.IronTrapdoor;
|
||||
mappings[299] = ItemType.HayBlock;
|
||||
mappings[300] = ItemType.WhiteCarpet;
|
||||
mappings[301] = ItemType.OrangeCarpet;
|
||||
mappings[302] = ItemType.MagentaCarpet;
|
||||
mappings[303] = ItemType.LightBlueCarpet;
|
||||
mappings[304] = ItemType.YellowCarpet;
|
||||
mappings[305] = ItemType.LimeCarpet;
|
||||
mappings[306] = ItemType.PinkCarpet;
|
||||
mappings[307] = ItemType.GrayCarpet;
|
||||
mappings[308] = ItemType.LightGrayCarpet;
|
||||
mappings[309] = ItemType.CyanCarpet;
|
||||
mappings[310] = ItemType.PurpleCarpet;
|
||||
mappings[311] = ItemType.BlueCarpet;
|
||||
mappings[312] = ItemType.BrownCarpet;
|
||||
mappings[313] = ItemType.GreenCarpet;
|
||||
mappings[314] = ItemType.RedCarpet;
|
||||
mappings[315] = ItemType.BlackCarpet;
|
||||
mappings[316] = ItemType.Terracotta;
|
||||
mappings[317] = ItemType.CoalBlock;
|
||||
mappings[318] = ItemType.PackedIce;
|
||||
mappings[319] = ItemType.AcaciaStairs;
|
||||
mappings[320] = ItemType.DarkOakStairs;
|
||||
mappings[321] = ItemType.SlimeBlock;
|
||||
mappings[322] = ItemType.DirtPath;
|
||||
mappings[323] = ItemType.Sunflower;
|
||||
mappings[324] = ItemType.Lilac;
|
||||
mappings[325] = ItemType.RoseBush;
|
||||
mappings[326] = ItemType.Peony;
|
||||
mappings[327] = ItemType.TallGrass;
|
||||
mappings[328] = ItemType.LargeFern;
|
||||
mappings[329] = ItemType.WhiteStainedGlass;
|
||||
mappings[330] = ItemType.OrangeStainedGlass;
|
||||
mappings[331] = ItemType.MagentaStainedGlass;
|
||||
mappings[332] = ItemType.LightBlueStainedGlass;
|
||||
mappings[333] = ItemType.YellowStainedGlass;
|
||||
mappings[334] = ItemType.LimeStainedGlass;
|
||||
mappings[335] = ItemType.PinkStainedGlass;
|
||||
mappings[336] = ItemType.GrayStainedGlass;
|
||||
mappings[337] = ItemType.LightGrayStainedGlass;
|
||||
mappings[338] = ItemType.CyanStainedGlass;
|
||||
mappings[339] = ItemType.PurpleStainedGlass;
|
||||
mappings[340] = ItemType.BlueStainedGlass;
|
||||
mappings[341] = ItemType.BrownStainedGlass;
|
||||
mappings[342] = ItemType.GreenStainedGlass;
|
||||
mappings[343] = ItemType.RedStainedGlass;
|
||||
mappings[344] = ItemType.BlackStainedGlass;
|
||||
mappings[345] = ItemType.WhiteStainedGlassPane;
|
||||
mappings[346] = ItemType.OrangeStainedGlassPane;
|
||||
mappings[347] = ItemType.MagentaStainedGlassPane;
|
||||
mappings[348] = ItemType.LightBlueStainedGlassPane;
|
||||
mappings[349] = ItemType.YellowStainedGlassPane;
|
||||
mappings[350] = ItemType.LimeStainedGlassPane;
|
||||
mappings[351] = ItemType.PinkStainedGlassPane;
|
||||
mappings[352] = ItemType.GrayStainedGlassPane;
|
||||
mappings[353] = ItemType.LightGrayStainedGlassPane;
|
||||
mappings[354] = ItemType.CyanStainedGlassPane;
|
||||
mappings[355] = ItemType.PurpleStainedGlassPane;
|
||||
mappings[356] = ItemType.BlueStainedGlassPane;
|
||||
mappings[357] = ItemType.BrownStainedGlassPane;
|
||||
mappings[358] = ItemType.GreenStainedGlassPane;
|
||||
mappings[359] = ItemType.RedStainedGlassPane;
|
||||
mappings[360] = ItemType.BlackStainedGlassPane;
|
||||
mappings[361] = ItemType.Prismarine;
|
||||
mappings[362] = ItemType.PrismarineBricks;
|
||||
mappings[363] = ItemType.DarkPrismarine;
|
||||
mappings[364] = ItemType.PrismarineStairs;
|
||||
mappings[365] = ItemType.PrismarineBrickStairs;
|
||||
mappings[366] = ItemType.DarkPrismarineStairs;
|
||||
mappings[367] = ItemType.SeaLantern;
|
||||
mappings[368] = ItemType.RedSandstone;
|
||||
mappings[369] = ItemType.ChiseledRedSandstone;
|
||||
mappings[370] = ItemType.CutRedSandstone;
|
||||
mappings[371] = ItemType.RedSandstoneStairs;
|
||||
mappings[372] = ItemType.RepeatingCommandBlock;
|
||||
mappings[373] = ItemType.ChainCommandBlock;
|
||||
mappings[374] = ItemType.MagmaBlock;
|
||||
mappings[375] = ItemType.NetherWartBlock;
|
||||
mappings[376] = ItemType.RedNetherBricks;
|
||||
mappings[377] = ItemType.BoneBlock;
|
||||
mappings[378] = ItemType.StructureVoid;
|
||||
mappings[379] = ItemType.Observer;
|
||||
mappings[380] = ItemType.ShulkerBox;
|
||||
mappings[381] = ItemType.WhiteShulkerBox;
|
||||
mappings[382] = ItemType.OrangeShulkerBox;
|
||||
mappings[383] = ItemType.MagentaShulkerBox;
|
||||
mappings[384] = ItemType.LightBlueShulkerBox;
|
||||
mappings[385] = ItemType.YellowShulkerBox;
|
||||
mappings[386] = ItemType.LimeShulkerBox;
|
||||
mappings[387] = ItemType.PinkShulkerBox;
|
||||
mappings[388] = ItemType.GrayShulkerBox;
|
||||
mappings[389] = ItemType.LightGrayShulkerBox;
|
||||
mappings[390] = ItemType.CyanShulkerBox;
|
||||
mappings[391] = ItemType.PurpleShulkerBox;
|
||||
mappings[392] = ItemType.BlueShulkerBox;
|
||||
mappings[393] = ItemType.BrownShulkerBox;
|
||||
mappings[394] = ItemType.GreenShulkerBox;
|
||||
mappings[395] = ItemType.RedShulkerBox;
|
||||
mappings[396] = ItemType.BlackShulkerBox;
|
||||
mappings[397] = ItemType.WhiteGlazedTerracotta;
|
||||
mappings[398] = ItemType.OrangeGlazedTerracotta;
|
||||
mappings[399] = ItemType.MagentaGlazedTerracotta;
|
||||
mappings[400] = ItemType.LightBlueGlazedTerracotta;
|
||||
mappings[401] = ItemType.YellowGlazedTerracotta;
|
||||
mappings[402] = ItemType.LimeGlazedTerracotta;
|
||||
mappings[403] = ItemType.PinkGlazedTerracotta;
|
||||
mappings[404] = ItemType.GrayGlazedTerracotta;
|
||||
mappings[405] = ItemType.LightGrayGlazedTerracotta;
|
||||
mappings[406] = ItemType.CyanGlazedTerracotta;
|
||||
mappings[407] = ItemType.PurpleGlazedTerracotta;
|
||||
mappings[408] = ItemType.BlueGlazedTerracotta;
|
||||
mappings[409] = ItemType.BrownGlazedTerracotta;
|
||||
mappings[410] = ItemType.GreenGlazedTerracotta;
|
||||
mappings[411] = ItemType.RedGlazedTerracotta;
|
||||
mappings[412] = ItemType.BlackGlazedTerracotta;
|
||||
mappings[413] = ItemType.WhiteConcrete;
|
||||
mappings[414] = ItemType.OrangeConcrete;
|
||||
mappings[415] = ItemType.MagentaConcrete;
|
||||
mappings[416] = ItemType.LightBlueConcrete;
|
||||
mappings[417] = ItemType.YellowConcrete;
|
||||
mappings[418] = ItemType.LimeConcrete;
|
||||
mappings[419] = ItemType.PinkConcrete;
|
||||
mappings[420] = ItemType.GrayConcrete;
|
||||
mappings[421] = ItemType.LightGrayConcrete;
|
||||
mappings[422] = ItemType.CyanConcrete;
|
||||
mappings[423] = ItemType.PurpleConcrete;
|
||||
mappings[424] = ItemType.BlueConcrete;
|
||||
mappings[425] = ItemType.BrownConcrete;
|
||||
mappings[426] = ItemType.GreenConcrete;
|
||||
mappings[427] = ItemType.RedConcrete;
|
||||
mappings[428] = ItemType.BlackConcrete;
|
||||
mappings[429] = ItemType.WhiteConcretePowder;
|
||||
mappings[430] = ItemType.OrangeConcretePowder;
|
||||
mappings[431] = ItemType.MagentaConcretePowder;
|
||||
mappings[432] = ItemType.LightBlueConcretePowder;
|
||||
mappings[433] = ItemType.YellowConcretePowder;
|
||||
mappings[434] = ItemType.LimeConcretePowder;
|
||||
mappings[435] = ItemType.PinkConcretePowder;
|
||||
mappings[436] = ItemType.GrayConcretePowder;
|
||||
mappings[437] = ItemType.LightGrayConcretePowder;
|
||||
mappings[438] = ItemType.CyanConcretePowder;
|
||||
mappings[439] = ItemType.PurpleConcretePowder;
|
||||
mappings[440] = ItemType.BlueConcretePowder;
|
||||
mappings[441] = ItemType.BrownConcretePowder;
|
||||
mappings[442] = ItemType.GreenConcretePowder;
|
||||
mappings[443] = ItemType.RedConcretePowder;
|
||||
mappings[444] = ItemType.BlackConcretePowder;
|
||||
mappings[445] = ItemType.TurtleEgg;
|
||||
mappings[446] = ItemType.DeadTubeCoralBlock;
|
||||
mappings[447] = ItemType.DeadBrainCoralBlock;
|
||||
mappings[448] = ItemType.DeadBubbleCoralBlock;
|
||||
mappings[449] = ItemType.DeadFireCoralBlock;
|
||||
mappings[450] = ItemType.DeadHornCoralBlock;
|
||||
mappings[451] = ItemType.TubeCoralBlock;
|
||||
mappings[452] = ItemType.BrainCoralBlock;
|
||||
mappings[453] = ItemType.BubbleCoralBlock;
|
||||
mappings[454] = ItemType.FireCoralBlock;
|
||||
mappings[455] = ItemType.HornCoralBlock;
|
||||
mappings[456] = ItemType.TubeCoral;
|
||||
mappings[457] = ItemType.BrainCoral;
|
||||
mappings[458] = ItemType.BubbleCoral;
|
||||
mappings[459] = ItemType.FireCoral;
|
||||
mappings[460] = ItemType.HornCoral;
|
||||
mappings[461] = ItemType.DeadBrainCoral;
|
||||
mappings[462] = ItemType.DeadBubbleCoral;
|
||||
mappings[463] = ItemType.DeadFireCoral;
|
||||
mappings[464] = ItemType.DeadHornCoral;
|
||||
mappings[465] = ItemType.DeadTubeCoral;
|
||||
mappings[466] = ItemType.TubeCoralFan;
|
||||
mappings[467] = ItemType.BrainCoralFan;
|
||||
mappings[468] = ItemType.BubbleCoralFan;
|
||||
mappings[469] = ItemType.FireCoralFan;
|
||||
mappings[470] = ItemType.HornCoralFan;
|
||||
mappings[471] = ItemType.DeadTubeCoralFan;
|
||||
mappings[472] = ItemType.DeadBrainCoralFan;
|
||||
mappings[473] = ItemType.DeadBubbleCoralFan;
|
||||
mappings[474] = ItemType.DeadFireCoralFan;
|
||||
mappings[475] = ItemType.DeadHornCoralFan;
|
||||
mappings[476] = ItemType.BlueIce;
|
||||
mappings[477] = ItemType.Conduit;
|
||||
mappings[478] = ItemType.PolishedGraniteStairs;
|
||||
mappings[479] = ItemType.SmoothRedSandstoneStairs;
|
||||
mappings[480] = ItemType.MossyStoneBrickStairs;
|
||||
mappings[481] = ItemType.PolishedDioriteStairs;
|
||||
mappings[482] = ItemType.MossyCobblestoneStairs;
|
||||
mappings[483] = ItemType.EndStoneBrickStairs;
|
||||
mappings[484] = ItemType.StoneStairs;
|
||||
mappings[485] = ItemType.SmoothSandstoneStairs;
|
||||
mappings[486] = ItemType.SmoothQuartzStairs;
|
||||
mappings[487] = ItemType.GraniteStairs;
|
||||
mappings[488] = ItemType.AndesiteStairs;
|
||||
mappings[489] = ItemType.RedNetherBrickStairs;
|
||||
mappings[490] = ItemType.PolishedAndesiteStairs;
|
||||
mappings[491] = ItemType.DioriteStairs;
|
||||
mappings[492] = ItemType.PolishedGraniteSlab;
|
||||
mappings[493] = ItemType.SmoothRedSandstoneSlab;
|
||||
mappings[494] = ItemType.MossyStoneBrickSlab;
|
||||
mappings[495] = ItemType.PolishedDioriteSlab;
|
||||
mappings[496] = ItemType.MossyCobblestoneSlab;
|
||||
mappings[497] = ItemType.EndStoneBrickSlab;
|
||||
mappings[498] = ItemType.SmoothSandstoneSlab;
|
||||
mappings[499] = ItemType.SmoothQuartzSlab;
|
||||
mappings[500] = ItemType.GraniteSlab;
|
||||
mappings[501] = ItemType.AndesiteSlab;
|
||||
mappings[502] = ItemType.RedNetherBrickSlab;
|
||||
mappings[503] = ItemType.PolishedAndesiteSlab;
|
||||
mappings[504] = ItemType.DioriteSlab;
|
||||
mappings[505] = ItemType.Scaffolding;
|
||||
mappings[506] = ItemType.IronDoor;
|
||||
mappings[507] = ItemType.OakDoor;
|
||||
mappings[508] = ItemType.SpruceDoor;
|
||||
mappings[509] = ItemType.BirchDoor;
|
||||
mappings[510] = ItemType.JungleDoor;
|
||||
mappings[511] = ItemType.AcaciaDoor;
|
||||
mappings[512] = ItemType.DarkOakDoor;
|
||||
mappings[513] = ItemType.Repeater;
|
||||
mappings[514] = ItemType.Comparator;
|
||||
mappings[515] = ItemType.StructureBlock;
|
||||
mappings[516] = ItemType.Jigsaw;
|
||||
mappings[517] = ItemType.Composter;
|
||||
mappings[518] = ItemType.TurtleHelmet;
|
||||
mappings[519] = ItemType.TurtleScute;
|
||||
mappings[520] = ItemType.IronShovel;
|
||||
mappings[521] = ItemType.IronPickaxe;
|
||||
mappings[522] = ItemType.IronAxe;
|
||||
mappings[523] = ItemType.FlintAndSteel;
|
||||
mappings[524] = ItemType.Apple;
|
||||
mappings[525] = ItemType.Bow;
|
||||
mappings[526] = ItemType.Arrow;
|
||||
mappings[527] = ItemType.Coal;
|
||||
mappings[528] = ItemType.Charcoal;
|
||||
mappings[529] = ItemType.Diamond;
|
||||
mappings[530] = ItemType.IronIngot;
|
||||
mappings[531] = ItemType.GoldIngot;
|
||||
mappings[532] = ItemType.IronSword;
|
||||
mappings[533] = ItemType.WoodenSword;
|
||||
mappings[534] = ItemType.WoodenShovel;
|
||||
mappings[535] = ItemType.WoodenPickaxe;
|
||||
mappings[536] = ItemType.WoodenAxe;
|
||||
mappings[537] = ItemType.StoneSword;
|
||||
mappings[538] = ItemType.StoneShovel;
|
||||
mappings[539] = ItemType.StonePickaxe;
|
||||
mappings[540] = ItemType.StoneAxe;
|
||||
mappings[541] = ItemType.DiamondSword;
|
||||
mappings[542] = ItemType.DiamondShovel;
|
||||
mappings[543] = ItemType.DiamondPickaxe;
|
||||
mappings[544] = ItemType.DiamondAxe;
|
||||
mappings[545] = ItemType.Stick;
|
||||
mappings[546] = ItemType.Bowl;
|
||||
mappings[547] = ItemType.MushroomStew;
|
||||
mappings[548] = ItemType.GoldenSword;
|
||||
mappings[549] = ItemType.GoldenShovel;
|
||||
mappings[550] = ItemType.GoldenPickaxe;
|
||||
mappings[551] = ItemType.GoldenAxe;
|
||||
mappings[552] = ItemType.String;
|
||||
mappings[553] = ItemType.Feather;
|
||||
mappings[554] = ItemType.Gunpowder;
|
||||
mappings[555] = ItemType.WoodenHoe;
|
||||
mappings[556] = ItemType.StoneHoe;
|
||||
mappings[557] = ItemType.IronHoe;
|
||||
mappings[558] = ItemType.DiamondHoe;
|
||||
mappings[559] = ItemType.GoldenHoe;
|
||||
mappings[560] = ItemType.WheatSeeds;
|
||||
mappings[561] = ItemType.Wheat;
|
||||
mappings[562] = ItemType.Bread;
|
||||
mappings[563] = ItemType.LeatherHelmet;
|
||||
mappings[564] = ItemType.LeatherChestplate;
|
||||
mappings[565] = ItemType.LeatherLeggings;
|
||||
mappings[566] = ItemType.LeatherBoots;
|
||||
mappings[567] = ItemType.ChainmailHelmet;
|
||||
mappings[568] = ItemType.ChainmailChestplate;
|
||||
mappings[569] = ItemType.ChainmailLeggings;
|
||||
mappings[570] = ItemType.ChainmailBoots;
|
||||
mappings[571] = ItemType.IronHelmet;
|
||||
mappings[572] = ItemType.IronChestplate;
|
||||
mappings[573] = ItemType.IronLeggings;
|
||||
mappings[574] = ItemType.IronBoots;
|
||||
mappings[575] = ItemType.DiamondHelmet;
|
||||
mappings[576] = ItemType.DiamondChestplate;
|
||||
mappings[577] = ItemType.DiamondLeggings;
|
||||
mappings[578] = ItemType.DiamondBoots;
|
||||
mappings[579] = ItemType.GoldenHelmet;
|
||||
mappings[580] = ItemType.GoldenChestplate;
|
||||
mappings[581] = ItemType.GoldenLeggings;
|
||||
mappings[582] = ItemType.GoldenBoots;
|
||||
mappings[583] = ItemType.Flint;
|
||||
mappings[584] = ItemType.Porkchop;
|
||||
mappings[585] = ItemType.CookedPorkchop;
|
||||
mappings[586] = ItemType.Painting;
|
||||
mappings[587] = ItemType.GoldenApple;
|
||||
mappings[588] = ItemType.EnchantedGoldenApple;
|
||||
mappings[589] = ItemType.OakSign;
|
||||
mappings[590] = ItemType.SpruceSign;
|
||||
mappings[591] = ItemType.BirchSign;
|
||||
mappings[592] = ItemType.JungleSign;
|
||||
mappings[593] = ItemType.AcaciaSign;
|
||||
mappings[594] = ItemType.DarkOakSign;
|
||||
mappings[595] = ItemType.Bucket;
|
||||
mappings[596] = ItemType.WaterBucket;
|
||||
mappings[597] = ItemType.LavaBucket;
|
||||
mappings[598] = ItemType.Minecart;
|
||||
mappings[599] = ItemType.Saddle;
|
||||
mappings[600] = ItemType.Redstone;
|
||||
mappings[601] = ItemType.Snowball;
|
||||
mappings[602] = ItemType.OakBoat;
|
||||
mappings[603] = ItemType.Leather;
|
||||
mappings[604] = ItemType.MilkBucket;
|
||||
mappings[605] = ItemType.PufferfishBucket;
|
||||
mappings[606] = ItemType.SalmonBucket;
|
||||
mappings[607] = ItemType.CodBucket;
|
||||
mappings[608] = ItemType.TropicalFishBucket;
|
||||
mappings[609] = ItemType.Brick;
|
||||
mappings[610] = ItemType.ClayBall;
|
||||
mappings[611] = ItemType.SugarCane;
|
||||
mappings[612] = ItemType.Kelp;
|
||||
mappings[613] = ItemType.DriedKelpBlock;
|
||||
mappings[614] = ItemType.Bamboo;
|
||||
mappings[615] = ItemType.Paper;
|
||||
mappings[616] = ItemType.Book;
|
||||
mappings[617] = ItemType.SlimeBall;
|
||||
mappings[618] = ItemType.ChestMinecart;
|
||||
mappings[619] = ItemType.FurnaceMinecart;
|
||||
mappings[620] = ItemType.Egg;
|
||||
mappings[621] = ItemType.Compass;
|
||||
mappings[622] = ItemType.FishingRod;
|
||||
mappings[623] = ItemType.Clock;
|
||||
mappings[624] = ItemType.GlowstoneDust;
|
||||
mappings[625] = ItemType.Cod;
|
||||
mappings[626] = ItemType.Salmon;
|
||||
mappings[627] = ItemType.TropicalFish;
|
||||
mappings[628] = ItemType.Pufferfish;
|
||||
mappings[629] = ItemType.CookedCod;
|
||||
mappings[630] = ItemType.CookedSalmon;
|
||||
mappings[631] = ItemType.InkSac;
|
||||
mappings[632] = ItemType.RedDye;
|
||||
mappings[633] = ItemType.GreenDye;
|
||||
mappings[634] = ItemType.CocoaBeans;
|
||||
mappings[635] = ItemType.LapisLazuli;
|
||||
mappings[636] = ItemType.PurpleDye;
|
||||
mappings[637] = ItemType.CyanDye;
|
||||
mappings[638] = ItemType.LightGrayDye;
|
||||
mappings[639] = ItemType.GrayDye;
|
||||
mappings[640] = ItemType.PinkDye;
|
||||
mappings[641] = ItemType.LimeDye;
|
||||
mappings[642] = ItemType.YellowDye;
|
||||
mappings[643] = ItemType.LightBlueDye;
|
||||
mappings[644] = ItemType.MagentaDye;
|
||||
mappings[645] = ItemType.OrangeDye;
|
||||
mappings[646] = ItemType.BoneMeal;
|
||||
mappings[647] = ItemType.BlueDye;
|
||||
mappings[648] = ItemType.BrownDye;
|
||||
mappings[649] = ItemType.BlackDye;
|
||||
mappings[650] = ItemType.WhiteDye;
|
||||
mappings[651] = ItemType.Bone;
|
||||
mappings[652] = ItemType.Sugar;
|
||||
mappings[653] = ItemType.Cake;
|
||||
mappings[654] = ItemType.WhiteBed;
|
||||
mappings[655] = ItemType.OrangeBed;
|
||||
mappings[656] = ItemType.MagentaBed;
|
||||
mappings[657] = ItemType.LightBlueBed;
|
||||
mappings[658] = ItemType.YellowBed;
|
||||
mappings[659] = ItemType.LimeBed;
|
||||
mappings[660] = ItemType.PinkBed;
|
||||
mappings[661] = ItemType.GrayBed;
|
||||
mappings[662] = ItemType.LightGrayBed;
|
||||
mappings[663] = ItemType.CyanBed;
|
||||
mappings[664] = ItemType.PurpleBed;
|
||||
mappings[665] = ItemType.BlueBed;
|
||||
mappings[666] = ItemType.BrownBed;
|
||||
mappings[667] = ItemType.GreenBed;
|
||||
mappings[668] = ItemType.RedBed;
|
||||
mappings[669] = ItemType.BlackBed;
|
||||
mappings[670] = ItemType.Cookie;
|
||||
mappings[671] = ItemType.FilledMap;
|
||||
mappings[672] = ItemType.Shears;
|
||||
mappings[673] = ItemType.MelonSlice;
|
||||
mappings[674] = ItemType.DriedKelp;
|
||||
mappings[675] = ItemType.PumpkinSeeds;
|
||||
mappings[676] = ItemType.MelonSeeds;
|
||||
mappings[677] = ItemType.Beef;
|
||||
mappings[678] = ItemType.CookedBeef;
|
||||
mappings[679] = ItemType.Chicken;
|
||||
mappings[680] = ItemType.CookedChicken;
|
||||
mappings[681] = ItemType.RottenFlesh;
|
||||
mappings[682] = ItemType.EnderPearl;
|
||||
mappings[683] = ItemType.BlazeRod;
|
||||
mappings[684] = ItemType.GhastTear;
|
||||
mappings[685] = ItemType.GoldNugget;
|
||||
mappings[686] = ItemType.NetherWart;
|
||||
mappings[687] = ItemType.Potion;
|
||||
mappings[688] = ItemType.GlassBottle;
|
||||
mappings[689] = ItemType.SpiderEye;
|
||||
mappings[690] = ItemType.FermentedSpiderEye;
|
||||
mappings[691] = ItemType.BlazePowder;
|
||||
mappings[692] = ItemType.MagmaCream;
|
||||
mappings[693] = ItemType.BrewingStand;
|
||||
mappings[694] = ItemType.Cauldron;
|
||||
mappings[695] = ItemType.EnderEye;
|
||||
mappings[696] = ItemType.GlisteringMelonSlice;
|
||||
mappings[697] = ItemType.BatSpawnEgg;
|
||||
mappings[698] = ItemType.BlazeSpawnEgg;
|
||||
mappings[699] = ItemType.CatSpawnEgg;
|
||||
mappings[700] = ItemType.CaveSpiderSpawnEgg;
|
||||
mappings[701] = ItemType.ChickenSpawnEgg;
|
||||
mappings[702] = ItemType.CodSpawnEgg;
|
||||
mappings[703] = ItemType.CowSpawnEgg;
|
||||
mappings[704] = ItemType.CreeperSpawnEgg;
|
||||
mappings[705] = ItemType.DolphinSpawnEgg;
|
||||
mappings[706] = ItemType.DonkeySpawnEgg;
|
||||
mappings[707] = ItemType.DrownedSpawnEgg;
|
||||
mappings[708] = ItemType.ElderGuardianSpawnEgg;
|
||||
mappings[709] = ItemType.EndermanSpawnEgg;
|
||||
mappings[710] = ItemType.EndermiteSpawnEgg;
|
||||
mappings[711] = ItemType.EvokerSpawnEgg;
|
||||
mappings[712] = ItemType.FoxSpawnEgg;
|
||||
mappings[713] = ItemType.GhastSpawnEgg;
|
||||
mappings[714] = ItemType.GuardianSpawnEgg;
|
||||
mappings[715] = ItemType.HorseSpawnEgg;
|
||||
mappings[716] = ItemType.HuskSpawnEgg;
|
||||
mappings[717] = ItemType.LlamaSpawnEgg;
|
||||
mappings[718] = ItemType.MagmaCubeSpawnEgg;
|
||||
mappings[719] = ItemType.MooshroomSpawnEgg;
|
||||
mappings[720] = ItemType.MuleSpawnEgg;
|
||||
mappings[721] = ItemType.OcelotSpawnEgg;
|
||||
mappings[722] = ItemType.PandaSpawnEgg;
|
||||
mappings[723] = ItemType.ParrotSpawnEgg;
|
||||
mappings[724] = ItemType.PhantomSpawnEgg;
|
||||
mappings[725] = ItemType.PigSpawnEgg;
|
||||
mappings[726] = ItemType.PillagerSpawnEgg;
|
||||
mappings[727] = ItemType.PolarBearSpawnEgg;
|
||||
mappings[728] = ItemType.PufferfishSpawnEgg;
|
||||
mappings[729] = ItemType.RabbitSpawnEgg;
|
||||
mappings[730] = ItemType.RavagerSpawnEgg;
|
||||
mappings[731] = ItemType.SalmonSpawnEgg;
|
||||
mappings[732] = ItemType.SheepSpawnEgg;
|
||||
mappings[733] = ItemType.ShulkerSpawnEgg;
|
||||
mappings[734] = ItemType.SilverfishSpawnEgg;
|
||||
mappings[735] = ItemType.SkeletonSpawnEgg;
|
||||
mappings[736] = ItemType.SkeletonHorseSpawnEgg;
|
||||
mappings[737] = ItemType.SlimeSpawnEgg;
|
||||
mappings[738] = ItemType.SpiderSpawnEgg;
|
||||
mappings[739] = ItemType.SquidSpawnEgg;
|
||||
mappings[740] = ItemType.StraySpawnEgg;
|
||||
mappings[741] = ItemType.TraderLlamaSpawnEgg;
|
||||
mappings[742] = ItemType.TropicalFishSpawnEgg;
|
||||
mappings[743] = ItemType.TurtleSpawnEgg;
|
||||
mappings[744] = ItemType.VexSpawnEgg;
|
||||
mappings[745] = ItemType.VillagerSpawnEgg;
|
||||
mappings[746] = ItemType.VindicatorSpawnEgg;
|
||||
mappings[747] = ItemType.WanderingTraderSpawnEgg;
|
||||
mappings[748] = ItemType.WitchSpawnEgg;
|
||||
mappings[749] = ItemType.WitherSkeletonSpawnEgg;
|
||||
mappings[750] = ItemType.WolfSpawnEgg;
|
||||
mappings[751] = ItemType.ZombieSpawnEgg;
|
||||
mappings[752] = ItemType.ZombieHorseSpawnEgg;
|
||||
mappings[753] = ItemType.ZombifiedPiglinSpawnEgg;
|
||||
mappings[754] = ItemType.ZombieVillagerSpawnEgg;
|
||||
mappings[755] = ItemType.ExperienceBottle;
|
||||
mappings[756] = ItemType.FireCharge;
|
||||
mappings[757] = ItemType.WritableBook;
|
||||
mappings[758] = ItemType.WrittenBook;
|
||||
mappings[759] = ItemType.Emerald;
|
||||
mappings[760] = ItemType.ItemFrame;
|
||||
mappings[761] = ItemType.FlowerPot;
|
||||
mappings[762] = ItemType.Carrot;
|
||||
mappings[763] = ItemType.Potato;
|
||||
mappings[764] = ItemType.BakedPotato;
|
||||
mappings[765] = ItemType.PoisonousPotato;
|
||||
mappings[766] = ItemType.Map;
|
||||
mappings[767] = ItemType.GoldenCarrot;
|
||||
mappings[768] = ItemType.SkeletonSkull;
|
||||
mappings[769] = ItemType.WitherSkeletonSkull;
|
||||
mappings[770] = ItemType.PlayerHead;
|
||||
mappings[771] = ItemType.ZombieHead;
|
||||
mappings[772] = ItemType.CreeperHead;
|
||||
mappings[773] = ItemType.DragonHead;
|
||||
mappings[774] = ItemType.CarrotOnAStick;
|
||||
mappings[775] = ItemType.NetherStar;
|
||||
mappings[776] = ItemType.PumpkinPie;
|
||||
mappings[777] = ItemType.FireworkRocket;
|
||||
mappings[778] = ItemType.FireworkStar;
|
||||
mappings[779] = ItemType.EnchantedBook;
|
||||
mappings[780] = ItemType.NetherBrick;
|
||||
mappings[781] = ItemType.Quartz;
|
||||
mappings[782] = ItemType.TntMinecart;
|
||||
mappings[783] = ItemType.HopperMinecart;
|
||||
mappings[784] = ItemType.PrismarineShard;
|
||||
mappings[785] = ItemType.PrismarineCrystals;
|
||||
mappings[786] = ItemType.Rabbit;
|
||||
mappings[787] = ItemType.CookedRabbit;
|
||||
mappings[788] = ItemType.RabbitStew;
|
||||
mappings[789] = ItemType.RabbitFoot;
|
||||
mappings[790] = ItemType.RabbitHide;
|
||||
mappings[791] = ItemType.ArmorStand;
|
||||
mappings[792] = ItemType.IronHorseArmor;
|
||||
mappings[793] = ItemType.GoldenHorseArmor;
|
||||
mappings[794] = ItemType.DiamondHorseArmor;
|
||||
mappings[795] = ItemType.LeatherHorseArmor;
|
||||
mappings[796] = ItemType.Lead;
|
||||
mappings[797] = ItemType.NameTag;
|
||||
mappings[798] = ItemType.CommandBlockMinecart;
|
||||
mappings[799] = ItemType.Mutton;
|
||||
mappings[800] = ItemType.CookedMutton;
|
||||
mappings[801] = ItemType.WhiteBanner;
|
||||
mappings[802] = ItemType.OrangeBanner;
|
||||
mappings[803] = ItemType.MagentaBanner;
|
||||
mappings[804] = ItemType.LightBlueBanner;
|
||||
mappings[805] = ItemType.YellowBanner;
|
||||
mappings[806] = ItemType.LimeBanner;
|
||||
mappings[807] = ItemType.PinkBanner;
|
||||
mappings[808] = ItemType.GrayBanner;
|
||||
mappings[809] = ItemType.LightGrayBanner;
|
||||
mappings[810] = ItemType.CyanBanner;
|
||||
mappings[811] = ItemType.PurpleBanner;
|
||||
mappings[812] = ItemType.BlueBanner;
|
||||
mappings[813] = ItemType.BrownBanner;
|
||||
mappings[814] = ItemType.GreenBanner;
|
||||
mappings[815] = ItemType.RedBanner;
|
||||
mappings[816] = ItemType.BlackBanner;
|
||||
mappings[817] = ItemType.EndCrystal;
|
||||
mappings[818] = ItemType.ChorusFruit;
|
||||
mappings[819] = ItemType.PoppedChorusFruit;
|
||||
mappings[820] = ItemType.Beetroot;
|
||||
mappings[821] = ItemType.BeetrootSeeds;
|
||||
mappings[822] = ItemType.BeetrootSoup;
|
||||
mappings[823] = ItemType.DragonBreath;
|
||||
mappings[824] = ItemType.SplashPotion;
|
||||
mappings[825] = ItemType.SpectralArrow;
|
||||
mappings[826] = ItemType.TippedArrow;
|
||||
mappings[827] = ItemType.LingeringPotion;
|
||||
mappings[828] = ItemType.Shield;
|
||||
mappings[829] = ItemType.Elytra;
|
||||
mappings[830] = ItemType.SpruceBoat;
|
||||
mappings[831] = ItemType.BirchBoat;
|
||||
mappings[832] = ItemType.JungleBoat;
|
||||
mappings[833] = ItemType.AcaciaBoat;
|
||||
mappings[834] = ItemType.DarkOakBoat;
|
||||
mappings[835] = ItemType.TotemOfUndying;
|
||||
mappings[836] = ItemType.ShulkerShell;
|
||||
mappings[837] = ItemType.IronNugget;
|
||||
mappings[838] = ItemType.KnowledgeBook;
|
||||
mappings[839] = ItemType.DebugStick;
|
||||
mappings[840] = ItemType.MusicDisc13;
|
||||
mappings[841] = ItemType.MusicDiscCat;
|
||||
mappings[842] = ItemType.MusicDiscBlocks;
|
||||
mappings[843] = ItemType.MusicDiscChirp;
|
||||
mappings[844] = ItemType.MusicDiscFar;
|
||||
mappings[845] = ItemType.MusicDiscMall;
|
||||
mappings[846] = ItemType.MusicDiscMellohi;
|
||||
mappings[847] = ItemType.MusicDiscStal;
|
||||
mappings[848] = ItemType.MusicDiscStrad;
|
||||
mappings[849] = ItemType.MusicDiscWard;
|
||||
mappings[850] = ItemType.MusicDisc11;
|
||||
mappings[851] = ItemType.MusicDiscWait;
|
||||
mappings[852] = ItemType.Trident;
|
||||
mappings[853] = ItemType.PhantomMembrane;
|
||||
mappings[854] = ItemType.NautilusShell;
|
||||
mappings[855] = ItemType.HeartOfTheSea;
|
||||
mappings[856] = ItemType.Crossbow;
|
||||
mappings[857] = ItemType.SuspiciousStew;
|
||||
mappings[858] = ItemType.Loom;
|
||||
mappings[859] = ItemType.FlowerBannerPattern;
|
||||
mappings[860] = ItemType.CreeperBannerPattern;
|
||||
mappings[861] = ItemType.SkullBannerPattern;
|
||||
mappings[862] = ItemType.MojangBannerPattern;
|
||||
mappings[863] = ItemType.GlobeBannerPattern;
|
||||
mappings[864] = ItemType.Barrel;
|
||||
mappings[865] = ItemType.Smoker;
|
||||
mappings[866] = ItemType.BlastFurnace;
|
||||
mappings[867] = ItemType.CartographyTable;
|
||||
mappings[868] = ItemType.FletchingTable;
|
||||
mappings[869] = ItemType.Grindstone;
|
||||
mappings[870] = ItemType.Lectern;
|
||||
mappings[871] = ItemType.SmithingTable;
|
||||
mappings[872] = ItemType.Stonecutter;
|
||||
mappings[873] = ItemType.Bell;
|
||||
mappings[874] = ItemType.Lantern;
|
||||
mappings[875] = ItemType.SweetBerries;
|
||||
mappings[876] = ItemType.Campfire;
|
||||
}
|
||||
|
||||
protected override Dictionary<int, ItemType> GetDict()
|
||||
{
|
||||
return mappings;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -88,7 +88,7 @@ namespace MinecraftClient.Inventory.ItemPalettes
|
|||
mappings[73] = ItemType.DetectorRail;
|
||||
mappings[74] = ItemType.StickyPiston;
|
||||
mappings[75] = ItemType.Cobweb;
|
||||
mappings[76] = ItemType.Grass;
|
||||
mappings[76] = ItemType.ShortGrass;
|
||||
mappings[77] = ItemType.Fern;
|
||||
mappings[78] = ItemType.DeadBush;
|
||||
mappings[79] = ItemType.Seagrass;
|
||||
|
|
@ -531,7 +531,7 @@ namespace MinecraftClient.Inventory.ItemPalettes
|
|||
mappings[516] = ItemType.Jigsaw;
|
||||
mappings[517] = ItemType.Composter;
|
||||
mappings[518] = ItemType.TurtleHelmet;
|
||||
mappings[519] = ItemType.Scute;
|
||||
mappings[519] = ItemType.TurtleScute;
|
||||
mappings[520] = ItemType.IronShovel;
|
||||
mappings[521] = ItemType.IronPickaxe;
|
||||
mappings[522] = ItemType.IronAxe;
|
||||
|
|
|
|||
|
|
@ -101,7 +101,7 @@ namespace MinecraftClient.Inventory.ItemPalettes
|
|||
mappings[86] = ItemType.DetectorRail;
|
||||
mappings[87] = ItemType.StickyPiston;
|
||||
mappings[88] = ItemType.Cobweb;
|
||||
mappings[89] = ItemType.Grass;
|
||||
mappings[89] = ItemType.ShortGrass;
|
||||
mappings[90] = ItemType.Fern;
|
||||
mappings[91] = ItemType.DeadBush;
|
||||
mappings[92] = ItemType.Seagrass;
|
||||
|
|
@ -583,7 +583,7 @@ namespace MinecraftClient.Inventory.ItemPalettes
|
|||
mappings[568] = ItemType.StructureBlock;
|
||||
mappings[569] = ItemType.Jigsaw;
|
||||
mappings[570] = ItemType.TurtleHelmet;
|
||||
mappings[571] = ItemType.Scute;
|
||||
mappings[571] = ItemType.TurtleScute;
|
||||
mappings[572] = ItemType.IronShovel;
|
||||
mappings[573] = ItemType.IronPickaxe;
|
||||
mappings[574] = ItemType.IronAxe;
|
||||
|
|
|
|||
|
|
@ -101,7 +101,7 @@ namespace MinecraftClient.Inventory.ItemPalettes
|
|||
mappings[86] = ItemType.DetectorRail;
|
||||
mappings[87] = ItemType.StickyPiston;
|
||||
mappings[88] = ItemType.Cobweb;
|
||||
mappings[89] = ItemType.Grass;
|
||||
mappings[89] = ItemType.ShortGrass;
|
||||
mappings[90] = ItemType.Fern;
|
||||
mappings[91] = ItemType.DeadBush;
|
||||
mappings[92] = ItemType.Seagrass;
|
||||
|
|
@ -583,7 +583,7 @@ namespace MinecraftClient.Inventory.ItemPalettes
|
|||
mappings[568] = ItemType.StructureBlock;
|
||||
mappings[569] = ItemType.Jigsaw;
|
||||
mappings[570] = ItemType.TurtleHelmet;
|
||||
mappings[571] = ItemType.Scute;
|
||||
mappings[571] = ItemType.TurtleScute;
|
||||
mappings[572] = ItemType.FlintAndSteel;
|
||||
mappings[573] = ItemType.Apple;
|
||||
mappings[574] = ItemType.Bow;
|
||||
|
|
|
|||
|
|
@ -158,7 +158,7 @@ namespace MinecraftClient.Inventory.ItemPalettes
|
|||
mappings[147] = ItemType.ChiseledSandstone;
|
||||
mappings[148] = ItemType.CutSandstone;
|
||||
mappings[149] = ItemType.Cobweb;
|
||||
mappings[150] = ItemType.Grass;
|
||||
mappings[150] = ItemType.ShortGrass;
|
||||
mappings[151] = ItemType.Fern;
|
||||
mappings[152] = ItemType.Azalea;
|
||||
mappings[153] = ItemType.FloweringAzalea;
|
||||
|
|
@ -687,7 +687,7 @@ namespace MinecraftClient.Inventory.ItemPalettes
|
|||
mappings[676] = ItemType.StructureBlock;
|
||||
mappings[677] = ItemType.Jigsaw;
|
||||
mappings[678] = ItemType.TurtleHelmet;
|
||||
mappings[679] = ItemType.Scute;
|
||||
mappings[679] = ItemType.TurtleScute;
|
||||
mappings[680] = ItemType.FlintAndSteel;
|
||||
mappings[681] = ItemType.Apple;
|
||||
mappings[682] = ItemType.Bow;
|
||||
|
|
|
|||
|
|
@ -158,7 +158,7 @@ namespace MinecraftClient.Inventory.ItemPalettes
|
|||
mappings[147] = ItemType.ChiseledSandstone;
|
||||
mappings[148] = ItemType.CutSandstone;
|
||||
mappings[149] = ItemType.Cobweb;
|
||||
mappings[150] = ItemType.Grass;
|
||||
mappings[150] = ItemType.ShortGrass;
|
||||
mappings[151] = ItemType.Fern;
|
||||
mappings[152] = ItemType.Azalea;
|
||||
mappings[153] = ItemType.FloweringAzalea;
|
||||
|
|
@ -687,7 +687,7 @@ namespace MinecraftClient.Inventory.ItemPalettes
|
|||
mappings[676] = ItemType.StructureBlock;
|
||||
mappings[677] = ItemType.Jigsaw;
|
||||
mappings[678] = ItemType.TurtleHelmet;
|
||||
mappings[679] = ItemType.Scute;
|
||||
mappings[679] = ItemType.TurtleScute;
|
||||
mappings[680] = ItemType.FlintAndSteel;
|
||||
mappings[681] = ItemType.Apple;
|
||||
mappings[682] = ItemType.Bow;
|
||||
|
|
|
|||
|
|
@ -449,7 +449,7 @@ namespace MinecraftClient.Inventory.ItemPalettes
|
|||
mappings[598] = ItemType.GraniteSlab;
|
||||
mappings[581] = ItemType.GraniteStairs;
|
||||
mappings[355] = ItemType.GraniteWall;
|
||||
mappings[160] = ItemType.Grass;
|
||||
mappings[160] = ItemType.ShortGrass;
|
||||
mappings[14] = ItemType.GrassBlock;
|
||||
mappings[42] = ItemType.Gravel;
|
||||
mappings[1032] = ItemType.GrayBanner;
|
||||
|
|
@ -927,7 +927,7 @@ namespace MinecraftClient.Inventory.ItemPalettes
|
|||
mappings[626] = ItemType.SculkSensor;
|
||||
mappings[329] = ItemType.SculkShrieker;
|
||||
mappings[327] = ItemType.SculkVein;
|
||||
mappings[715] = ItemType.Scute;
|
||||
mappings[715] = ItemType.TurtleScute;
|
||||
mappings[461] = ItemType.SeaLantern;
|
||||
mappings[166] = ItemType.SeaPickle;
|
||||
mappings[165] = ItemType.Seagrass;
|
||||
|
|
|
|||
|
|
@ -473,7 +473,7 @@ namespace MinecraftClient.Inventory.ItemPalettes
|
|||
mappings[608] = ItemType.GraniteSlab;
|
||||
mappings[591] = ItemType.GraniteStairs;
|
||||
mappings[365] = ItemType.GraniteWall;
|
||||
mappings[164] = ItemType.Grass;
|
||||
mappings[164] = ItemType.ShortGrass;
|
||||
mappings[14] = ItemType.GrassBlock;
|
||||
mappings[44] = ItemType.Gravel;
|
||||
mappings[1066] = ItemType.GrayBanner;
|
||||
|
|
@ -956,7 +956,7 @@ namespace MinecraftClient.Inventory.ItemPalettes
|
|||
mappings[636] = ItemType.SculkSensor;
|
||||
mappings[337] = ItemType.SculkShrieker;
|
||||
mappings[335] = ItemType.SculkVein;
|
||||
mappings[732] = ItemType.Scute;
|
||||
mappings[732] = ItemType.TurtleScute;
|
||||
mappings[471] = ItemType.SeaLantern;
|
||||
mappings[170] = ItemType.SeaPickle;
|
||||
mappings[169] = ItemType.Seagrass;
|
||||
|
|
|
|||
|
|
@ -495,7 +495,7 @@ namespace MinecraftClient.Inventory.ItemPalettes
|
|||
mappings[622] = ItemType.GraniteSlab;
|
||||
mappings[605] = ItemType.GraniteStairs;
|
||||
mappings[379] = ItemType.GraniteWall;
|
||||
mappings[172] = ItemType.Grass;
|
||||
mappings[172] = ItemType.ShortGrass;
|
||||
mappings[14] = ItemType.GrassBlock;
|
||||
mappings[47] = ItemType.Gravel;
|
||||
mappings[1090] = ItemType.GrayBanner;
|
||||
|
|
@ -985,7 +985,7 @@ namespace MinecraftClient.Inventory.ItemPalettes
|
|||
mappings[650] = ItemType.SculkSensor;
|
||||
mappings[350] = ItemType.SculkShrieker;
|
||||
mappings[348] = ItemType.SculkVein;
|
||||
mappings[753] = ItemType.Scute;
|
||||
mappings[753] = ItemType.TurtleScute;
|
||||
mappings[485] = ItemType.SeaLantern;
|
||||
mappings[178] = ItemType.SeaPickle;
|
||||
mappings[177] = ItemType.Seagrass;
|
||||
|
|
|
|||
|
|
@ -505,7 +505,7 @@ namespace MinecraftClient.Inventory.ItemPalettes
|
|||
mappings[625] = ItemType.GraniteSlab;
|
||||
mappings[608] = ItemType.GraniteStairs;
|
||||
mappings[381] = ItemType.GraniteWall;
|
||||
mappings[173] = ItemType.Grass;
|
||||
mappings[173] = ItemType.ShortGrass;
|
||||
mappings[14] = ItemType.GrassBlock;
|
||||
mappings[48] = ItemType.Gravel;
|
||||
mappings[1094] = ItemType.GrayBanner;
|
||||
|
|
@ -1003,7 +1003,7 @@ namespace MinecraftClient.Inventory.ItemPalettes
|
|||
mappings[653] = ItemType.SculkSensor;
|
||||
mappings[352] = ItemType.SculkShrieker;
|
||||
mappings[350] = ItemType.SculkVein;
|
||||
mappings[757] = ItemType.Scute;
|
||||
mappings[757] = ItemType.TurtleScute;
|
||||
mappings[487] = ItemType.SeaLantern;
|
||||
mappings[179] = ItemType.SeaPickle;
|
||||
mappings[178] = ItemType.Seagrass;
|
||||
|
|
|
|||
|
|
@ -1025,7 +1025,7 @@ namespace MinecraftClient.Inventory.ItemPalettes
|
|||
mappings[674] = ItemType.SculkSensor;
|
||||
mappings[373] = ItemType.SculkShrieker;
|
||||
mappings[371] = ItemType.SculkVein;
|
||||
mappings[794] = ItemType.Scute;
|
||||
mappings[794] = ItemType.TurtleScute;
|
||||
mappings[508] = ItemType.SeaLantern;
|
||||
mappings[200] = ItemType.SeaPickle;
|
||||
mappings[199] = ItemType.Seagrass;
|
||||
|
|
|
|||
1348
MinecraftClient/Inventory/ItemPalettes/ItemPalette1206.cs
Normal file
1348
MinecraftClient/Inventory/ItemPalettes/ItemPalette1206.cs
Normal file
File diff suppressed because it is too large
Load diff
1351
MinecraftClient/Inventory/ItemPalettes/ItemPalette121.cs
Normal file
1351
MinecraftClient/Inventory/ItemPalettes/ItemPalette121.cs
Normal file
File diff suppressed because it is too large
Load diff
1523
MinecraftClient/Inventory/ItemPalettes/ItemPalette12111.cs
Normal file
1523
MinecraftClient/Inventory/ItemPalettes/ItemPalette12111.cs
Normal file
File diff suppressed because it is too large
Load diff
1393
MinecraftClient/Inventory/ItemPalettes/ItemPalette1212.cs
Normal file
1393
MinecraftClient/Inventory/ItemPalettes/ItemPalette1212.cs
Normal file
File diff suppressed because it is too large
Load diff
1403
MinecraftClient/Inventory/ItemPalettes/ItemPalette1214.cs
Normal file
1403
MinecraftClient/Inventory/ItemPalettes/ItemPalette1214.cs
Normal file
File diff suppressed because it is too large
Load diff
1414
MinecraftClient/Inventory/ItemPalettes/ItemPalette1215.cs
Normal file
1414
MinecraftClient/Inventory/ItemPalettes/ItemPalette1215.cs
Normal file
File diff suppressed because it is too large
Load diff
1433
MinecraftClient/Inventory/ItemPalettes/ItemPalette1216.cs
Normal file
1433
MinecraftClient/Inventory/ItemPalettes/ItemPalette1216.cs
Normal file
File diff suppressed because it is too large
Load diff
1434
MinecraftClient/Inventory/ItemPalettes/ItemPalette1217.cs
Normal file
1434
MinecraftClient/Inventory/ItemPalettes/ItemPalette1217.cs
Normal file
File diff suppressed because it is too large
Load diff
Some files were not shown because too many files have changed in this diff Show more
Loading…
Add table
Add a link
Reference in a new issue