using System;
using System.Collections.Generic;
using System.Globalization;
using System.Linq;
using Newtonsoft.Json.Linq;
using Sandbox.Definitions;
using Sandbox.ModAPI.Ingame;
using VRage;
using VRage.Game;
using VRage.Game.ModAPI.Ingame;
using Terminal = Sandbox.ModAPI.Ingame.IMyTerminalBlock;
namespace XFE.SeAgent.Plugin.Game
{
public sealed partial class GameDebugApi
{
private JObject InventoryRoute(JObject args)
{
InventoryParameters(args, "sourceId", "targetId", "sourceInventoryIndex", "targetInventoryIndex", "itemType");
var sourceBlock = Block(InventoryEntityId(args, "sourceId"));
var targetBlock = Block(InventoryEntityId(args, "targetId"));
int sourceIndex = InventoryIndex(args, "sourceInventoryIndex");
int targetIndex = InventoryIndex(args, "targetInventoryIndex");
var source = InventoryAt(sourceBlock, sourceIndex);
var target = InventoryAt(targetBlock, targetIndex);
var definition = InventoryDefinition(args["itemType"] ?? new JValue("MyObjectBuilder_Ore/Stone"), false);
var itemType = MyItemType.Parse(definition.ToString());
return new JObject
{
["source"] = InventorySummary(sourceBlock, source, sourceIndex),
["target"] = InventorySummary(targetBlock, target, targetIndex),
["itemType"] = definition.ToString(), ["amountTested"] = 1,
["isConnectedTo"] = source.IsConnectedTo(target),
["canTransferItemTo"] = source.CanTransferItemTo(target, itemType),
["targetCanAddOne"] = target.CanItemsBeAdded((MyFixedPoint)1, itemType)
};
}
private JObject SetSorterFilters(JObject args)
{
InventoryParameters(args, "entityId", "mode", "items");
long entityId = InventoryEntityId(args, "entityId");
string modeText = Text(args, "mode");
MyConveyorSorterMode mode;
if (modeText == "Whitelist") mode = MyConveyorSorterMode.Whitelist;
else if (modeText == "Blacklist") mode = MyConveyorSorterMode.Blacklist;
else throw new ArgumentException("mode must be Whitelist or Blacklist.");
var items = args["items"] as JArray;
if (items == null || items.Count > 128) throw new ArgumentException("items must be an array of at most 128 filters; an empty array explicitly clears the filters.");
var filters = new List<MyInventoryItemFilter>();
var keys = new HashSet<string>(StringComparer.Ordinal);
foreach (var token in items)
{
var item = token as JObject ?? throw new ArgumentException("Each filter must be an object with itemId and optional allSubTypes.");
InventoryParameters(item, "itemId", "allSubTypes");
bool allSubTypes = false;
if (item["allSubTypes"] != null)
{
if (item["allSubTypes"].Type != JTokenType.Boolean) throw new ArgumentException("allSubTypes must be a JSON boolean.");
allSubTypes = (bool)item["allSubTypes"];
}
var definition = InventoryDefinition(item["itemId"], allSubTypes);
var filter = new MyInventoryItemFilter(definition, allSubTypes);
if (!keys.Add(FilterKey(filter))) throw new ArgumentException("Duplicate sorter filter.");
filters.Add(filter);
}
var sorter = Block(entityId) as IMyConveyorSorter ?? throw new ArgumentException("Entity is not a conveyor sorter.");
var previous = DescribeSorter(sorter);
// All inputs have been validated before this sole hardware mutation. SetFilter
// replaces only mode/list; DrainAll, Enabled, names and other sorters are untouched.
sorter.SetFilter(mode, filters);
var actual = new List<MyInventoryItemFilter>();
sorter.GetFilterList(actual);
bool matches = sorter.Mode == mode && actual.Count == filters.Count && actual.All(value => keys.Contains(FilterKey(value)));
_log("Replace sorter filters on " + Sid(entityId) + ": " + modeText + ", " + filters.Count + " entries; readback matches=" + matches);
return new JObject { ["entityId"] = Sid(entityId), ["submitted"] = true,
["readbackMatchesRequest"] = matches, ["previous"] = previous, ["sorter"] = DescribeSorter(sorter, actual) };
}
private static JObject DescribeSorter(IMyConveyorSorter sorter, List<MyInventoryItemFilter> filters = null)
{
if (filters == null) { filters = new List<MyInventoryItemFilter>(); sorter.GetFilterList(filters); }
return new JObject { ["mode"] = sorter.Mode.ToString(), ["drainAll"] = sorter.DrainAll,
["items"] = new JArray(filters.Take(128).Select(value => new JObject {
["itemId"] = value.ItemId.ToString(), ["allSubTypes"] = value.AllSubTypes })),
["total"] = filters.Count, ["truncated"] = filters.Count > 128 };
}
private static string FilterKey(MyInventoryItemFilter filter)
{
return filter.AllSubTypes ? filter.ItemId.TypeId.ToString() + "/*" : filter.ItemId.ToString();
}
private static MyDefinitionId InventoryDefinition(JToken token, bool allSubTypes)
{
if (token == null || token.Type != JTokenType.String) throw new ArgumentException("itemId/itemType must be a string.");
string text = (string)token;
if (text.Length == 0 || text.Length > 256 || text.Any(char.IsControl) || text != text.Trim())
throw new ArgumentException("itemId/itemType must contain 1..256 characters without surrounding whitespace or control characters.");
int slash = text.IndexOf('/');
if (slash < 0 && allSubTypes) { text += "/"; slash = text.Length - 1; }
if (slash <= 0 || text.IndexOf('/', slash + 1) >= 0 || !text.StartsWith("MyObjectBuilder_", StringComparison.Ordinal))
throw new ArgumentException("Use MyObjectBuilder_Type/Subtype; an allSubTypes filter may use MyObjectBuilder_Type alone.");
string subtype = text.Substring(slash + 1);
if (allSubTypes ? subtype.Length > 0 && subtype != "(null)" : subtype.Length == 0 || subtype == "(null)")
throw new ArgumentException("allSubTypes requires an empty subtype; a specific item requires a nonempty subtype.");
if (text.Substring(0, slash).Any(c => !(char.IsLetterOrDigit(c) || c == '_')) || subtype != subtype.Trim())
throw new ArgumentException("Invalid item definition syntax.");
MyDefinitionId definition;
if (!MyDefinitionId.TryParse(text, out definition) || !typeof(MyObjectBuilder_PhysicalObject).IsAssignableFrom((Type)definition.TypeId))
throw new ArgumentException("Item type is not a registered physical inventory item type.");
if (!allSubTypes && MyDefinitionManager.Static.TryGetPhysicalItemDefinition(definition) == null)
throw new ArgumentException("Item definition does not exist in the current world.");
return definition;
}
private static long InventoryEntityId(JObject args, string key)
{
var token = args[key];
if (token == null || (token.Type != JTokenType.String && token.Type != JTokenType.Integer))
throw new ArgumentException(key + " must be a nonzero decimal Int64 string or integer.");
string text = (string)token;
int firstDigit = text.StartsWith("-", StringComparison.Ordinal) ? 1 : 0;
if (text.Length <= firstDigit || text.Length > 20 || text.Skip(firstDigit).Any(c => c < '0' || c > '9'))
throw new ArgumentException(key + " must be a nonzero decimal Int64 string or integer.");
long id;
if (!long.TryParse(text, NumberStyles.AllowLeadingSign, CultureInfo.InvariantCulture, out id) || id == 0)
throw new ArgumentException(key + " must be a nonzero decimal Int64 string or integer.");
return id;
}
private static int InventoryIndex(JObject args, string key)
{
var token = args[key];
if (token == null) return 0;
int index;
if (token.Type != JTokenType.Integer || !int.TryParse((string)token, NumberStyles.None, CultureInfo.InvariantCulture, out index) || index < 0 || index > 15)
throw new ArgumentException(key + " must be a JSON integer from 0 to 15.");
return index;
}
private static IMyInventory InventoryAt(Terminal block, int index)
{
if (!block.HasInventory || index >= block.InventoryCount) throw new ArgumentException("Inventory index does not exist on block " + Sid(block.EntityId) + ".");
return block.GetInventory(index) ?? throw new ArgumentException("Block inventory is unavailable.");
}
private static JObject InventorySummary(Terminal block, IMyInventory inventory, int index)
{
return new JObject { ["entityId"] = Sid(block.EntityId), ["gridId"] = Sid(block.CubeGrid.EntityId), ["index"] = index,
["massKg"] = (double)inventory.CurrentMass, ["volumeM3"] = (double)inventory.CurrentVolume,
["maxVolumeM3"] = (double)inventory.MaxVolume, ["freeVolumeM3"] = Math.Max(0, (double)inventory.MaxVolume - (double)inventory.CurrentVolume),
["isFull"] = inventory.IsFull, ["canPutItems"] = inventory.CanPutItems, ["itemCount"] = inventory.ItemCount };
}
private static void InventoryParameters(JObject args, params string[] names)
{
foreach (var property in args.Properties())
if (!names.Contains(property.Name, StringComparer.Ordinal)) throw new ArgumentException("Unknown parameter: " + property.Name);
}
}
}
using System;
using System.Collections.Generic;
using System.Globalization;
using System.Linq;
using Newtonsoft.Json.Linq;
using Sandbox.Definitions;
using Sandbox.ModAPI.Ingame;
using VRage;
using VRage.Game;
using VRage.Game.ModAPI.Ingame;
using Terminal = Sandbox.ModAPI.Ingame.IMyTerminalBlock;
namespace XFE.SeAgent.Plugin.Game
{
public sealed partial class GameDebugApi
{
private JObject InventoryRoute(JObject args)
{
InventoryParameters(args, "sourceId", "targetId", "sourceInventoryIndex", "targetInventoryIndex", "itemType");
var sourceBlock = Block(InventoryEntityId(args, "sourceId"));
var targetBlock = Block(InventoryEntityId(args, "targetId"));
int sourceIndex = InventoryIndex(args, "sourceInventoryIndex");
int targetIndex = InventoryIndex(args, "targetInventoryIndex");
var source = InventoryAt(sourceBlock, sourceIndex);
var target = InventoryAt(targetBlock, targetIndex);
var definition = InventoryDefinition(args["itemType"] ?? new JValue("MyObjectBuilder_Ore/Stone"), false);
var itemType = MyItemType.Parse(definition.ToString());
return new JObject
{
["source"] = InventorySummary(sourceBlock, source, sourceIndex),
["target"] = InventorySummary(targetBlock, target, targetIndex),
["itemType"] = definition.ToString(), ["amountTested"] = 1,
["isConnectedTo"] = source.IsConnectedTo(target),
["canTransferItemTo"] = source.CanTransferItemTo(target, itemType),
["targetCanAddOne"] = target.CanItemsBeAdded((MyFixedPoint)1, itemType)
};
}
private JObject SetSorterFilters(JObject args)
{
InventoryParameters(args, "entityId", "mode", "items");
long entityId = InventoryEntityId(args, "entityId");
string modeText = Text(args, "mode");
MyConveyorSorterMode mode;
if (modeText == "Whitelist") mode = MyConveyorSorterMode.Whitelist;
else if (modeText == "Blacklist") mode = MyConveyorSorterMode.Blacklist;
else throw new ArgumentException("mode must be Whitelist or Blacklist.");
var items = args["items"] as JArray;
if (items == null || items.Count > 128) throw new ArgumentException("items must be an array of at most 128 filters; an empty array explicitly clears the filters.");
var filters = new List<MyInventoryItemFilter>();
var keys = new HashSet<string>(StringComparer.Ordinal);
foreach (var token in items)
{
var item = token as JObject ?? throw new ArgumentException("Each filter must be an object with itemId and optional allSubTypes.");
InventoryParameters(item, "itemId", "allSubTypes");
bool allSubTypes = false;
if (item["allSubTypes"] != null)
{
if (item["allSubTypes"].Type != JTokenType.Boolean) throw new ArgumentException("allSubTypes must be a JSON boolean.");
allSubTypes = (bool)item["allSubTypes"];
}
var definition = InventoryDefinition(item["itemId"], allSubTypes);
var filter = new MyInventoryItemFilter(definition, allSubTypes);
if (!keys.Add(FilterKey(filter))) throw new ArgumentException("Duplicate sorter filter.");
filters.Add(filter);
}
var sorter = Block(entityId) as IMyConveyorSorter ?? throw new ArgumentException("Entity is not a conveyor sorter.");
var previous = DescribeSorter(sorter);
// All inputs have been validated before this sole hardware mutation. SetFilter
// replaces only mode/list; DrainAll, Enabled, names and other sorters are untouched.
sorter.SetFilter(mode, filters);
var actual = new List<MyInventoryItemFilter>();
sorter.GetFilterList(actual);
bool matches = sorter.Mode == mode && actual.Count == filters.Count && actual.All(value => keys.Contains(FilterKey(value)));
_log("Replace sorter filters on " + Sid(entityId) + ": " + modeText + ", " + filters.Count + " entries; readback matches=" + matches);
return new JObject { ["entityId"] = Sid(entityId), ["submitted"] = true,
["readbackMatchesRequest"] = matches, ["previous"] = previous, ["sorter"] = DescribeSorter(sorter, actual) };
}
private static JObject DescribeSorter(IMyConveyorSorter sorter, List<MyInventoryItemFilter> filters = null)
{
if (filters == null) { filters = new List<MyInventoryItemFilter>(); sorter.GetFilterList(filters); }
return new JObject { ["mode"] = sorter.Mode.ToString(), ["drainAll"] = sorter.DrainAll,
["items"] = new JArray(filters.Take(128).Select(value => new JObject {
["itemId"] = value.ItemId.ToString(), ["allSubTypes"] = value.AllSubTypes })),
["total"] = filters.Count, ["truncated"] = filters.Count > 128 };
}
private static string FilterKey(MyInventoryItemFilter filter)
{
return filter.AllSubTypes ? filter.ItemId.TypeId.ToString() + "/*" : filter.ItemId.ToString();
}
private static MyDefinitionId InventoryDefinition(JToken token, bool allSubTypes)
{
if (token == null || token.Type != JTokenType.String) throw new ArgumentException("itemId/itemType must be a string.");
string text = (string)token;
if (text.Length == 0 || text.Length > 256 || text.Any(char.IsControl) || text != text.Trim())
throw new ArgumentException("itemId/itemType must contain 1..256 characters without surrounding whitespace or control characters.");
int slash = text.IndexOf('/');
if (slash < 0 && allSubTypes) { text += "/"; slash = text.Length - 1; }
if (slash <= 0 || text.IndexOf('/', slash + 1) >= 0 || !text.StartsWith("MyObjectBuilder_", StringComparison.Ordinal))
throw new ArgumentException("Use MyObjectBuilder_Type/Subtype; an allSubTypes filter may use MyObjectBuilder_Type alone.");
string subtype = text.Substring(slash + 1);
if (allSubTypes ? subtype.Length > 0 && subtype != "(null)" : subtype.Length == 0 || subtype == "(null)")
throw new ArgumentException("allSubTypes requires an empty subtype; a specific item requires a nonempty subtype.");
if (text.Substring(0, slash).Any(c => !(char.IsLetterOrDigit(c) || c == '_')) || subtype != subtype.Trim())
throw new ArgumentException("Invalid item definition syntax.");
MyDefinitionId definition;
if (!MyDefinitionId.TryParse(text, out definition) || !typeof(MyObjectBuilder_PhysicalObject).IsAssignableFrom((Type)definition.TypeId))
throw new ArgumentException("Item type is not a registered physical inventory item type.");
if (!allSubTypes && MyDefinitionManager.Static.TryGetPhysicalItemDefinition(definition) == null)
throw new ArgumentException("Item definition does not exist in the current world.");
return definition;
}
private static long InventoryEntityId(JObject args, string key)
{
var token = args[key];
if (token == null || (token.Type != JTokenType.String && token.Type != JTokenType.Integer))
throw new ArgumentException(key + " must be a nonzero decimal Int64 string or integer.");
string text = (string)token;
int firstDigit = text.StartsWith("-", StringComparison.Ordinal) ? 1 : 0;
if (text.Length <= firstDigit || text.Length > 20 || text.Skip(firstDigit).Any(c => c < '0' || c > '9'))
throw new ArgumentException(key + " must be a nonzero decimal Int64 string or integer.");
long id;
if (!long.TryParse(text, NumberStyles.AllowLeadingSign, CultureInfo.InvariantCulture, out id) || id == 0)
throw new ArgumentException(key + " must be a nonzero decimal Int64 string or integer.");
return id;
}
private static int InventoryIndex(JObject args, string key)
{
var token = args[key];
if (token == null) return 0;
int index;
if (token.Type != JTokenType.Integer || !int.TryParse((string)token, NumberStyles.None, CultureInfo.InvariantCulture, out index) || index < 0 || index > 15)
throw new ArgumentException(key + " must be a JSON integer from 0 to 15.");
return index;
}
private static IMyInventory InventoryAt(Terminal block, int index)
{
if (!block.HasInventory || index >= block.InventoryCount) throw new ArgumentException("Inventory index does not exist on block " + Sid(block.EntityId) + ".");
return block.GetInventory(index) ?? throw new ArgumentException("Block inventory is unavailable.");
}
private static JObject InventorySummary(Terminal block, IMyInventory inventory, int index)
{
return new JObject { ["entityId"] = Sid(block.EntityId), ["gridId"] = Sid(block.CubeGrid.EntityId), ["index"] = index,
["massKg"] = (double)inventory.CurrentMass, ["volumeM3"] = (double)inventory.CurrentVolume,
["maxVolumeM3"] = (double)inventory.MaxVolume, ["freeVolumeM3"] = Math.Max(0, (double)inventory.MaxVolume - (double)inventory.CurrentVolume),
["isFull"] = inventory.IsFull, ["canPutItems"] = inventory.CanPutItems, ["itemCount"] = inventory.ItemCount };
}
private static void InventoryParameters(JObject args, params string[] names)
{
foreach (var property in args.Properties())
if (!names.Contains(property.Name, StringComparer.Ordinal)) throw new ArgumentException("Unknown parameter: " + property.Name);
}
}
}