diff --git a/ChickenGameTest.csproj b/ChickenGameTest.csproj index 88056f2..a27456b 100644 --- a/ChickenGameTest.csproj +++ b/ChickenGameTest.csproj @@ -43,6 +43,7 @@ + diff --git a/mods/raw_onion.yaml b/mods/raw_onion.yaml new file mode 100644 index 0000000..316fffa --- /dev/null +++ b/mods/raw_onion.yaml @@ -0,0 +1,13 @@ +Id: raw_onion + +# TypeMapping: +# Tags: FoodFactory.Items.Tags +Components: + Name: Raw Onion + Tags: ["onion", "raw", "vegetable", "burnable", "sliceable"] + Temperature: + Fahrenheit: 71. + # Bacteria: + # - salmonella: 400. + BurnableTemp: + Temperature: 477.594 diff --git a/mods/raw_potato.yaml b/mods/raw_potato.yaml new file mode 100644 index 0000000..3fcd09c --- /dev/null +++ b/mods/raw_potato.yaml @@ -0,0 +1,13 @@ +Id: raw_potato + +# TypeMapping: +# Tags: FoodFactory.Items.Tags +Components: + Name: Raw Potato + Tags: ["potato", "raw", "vegetable", "burnable"] + Temperature: + Fahrenheit: 71. + # Bacteria: + # - salmonella: 400. + BurnableTemp: + Temperature: 100. diff --git a/mods/raw_red_onion.yaml b/mods/raw_red_onion.yaml new file mode 100644 index 0000000..e377cfb --- /dev/null +++ b/mods/raw_red_onion.yaml @@ -0,0 +1,15 @@ +Id: raw_red_onion + +Extend: + - raw_onion +# TypeMapping: +# Tags: FoodFactory.Items.Tags +Components: + Name: Raw Red Onion + Color: + R: 1 + G: 0 + B: 0 + Tags: + With: ["red"] + Except: ["sliceable"] diff --git a/src/Equipment/ItemSpawner.cs b/src/Equipment/ItemSpawner.cs index 8e88301..801a83d 100644 --- a/src/Equipment/ItemSpawner.cs +++ b/src/Equipment/ItemSpawner.cs @@ -9,6 +9,8 @@ using FoodFactory.Voxel; using FoodFactory.Math; using FoodFactory.Items; using FoodFactory.Conveyors; +using System.Linq; +using Arch.Core.Extensions; [Meta(typeof(IAutoNode))] public partial class ItemSpawner : Node3D, IProvide @@ -52,12 +54,25 @@ public partial class ItemSpawner : Node3D, IProvide { continue; } - var itemBlueprint = ItemFactory.GetBlueprint(ItemName); + var itemBlueprint = ItemFactory.GetCompiledBlueprint(ItemName); var dummyItem = new TestItem(); if (beltPort.CanAccept(dummyItem, 0)) { - var ctx = new BlueprintContext() { BluePrintId = new BlueprintId(itemBlueprint), World = World }; - dummyItem.Item = itemBlueprint.Factory(ctx); + var ctx = new BlueprintContext() { /*BluePrintId = new BlueprintId(itemBlueprint),*/ World = World }; + + var newItem = itemBlueprint.CreateEmptyItem(ctx); + // ItemFactory.BinderRegistry.Apply(newItem, ctx.World, itemBlueprint); + foreach (var providers in itemBlueprint.Providers) + { + var type = providers[0].GetProviderType(); + for (int i = 0; i < providers.Length; i++) + { + ComponentBinder.Apply(type, newItem, providers[i], ctx); + + } + } + GD.Print(newItem.Get()); + dummyItem.Item = newItem; if (!beltPort.TryInsert(dummyItem, 0)) { World.Destroy(dummyItem.Item);//TODO this should not call, but not sure diff --git a/src/Equipment/OvenTest.cs b/src/Equipment/OvenTest.cs index 776cb59..1778c29 100644 --- a/src/Equipment/OvenTest.cs +++ b/src/Equipment/OvenTest.cs @@ -86,6 +86,10 @@ public partial class OvenTest : Node3D, IProvide, IProvide, IProvide port is BeltPort beltPort && beltPort.PortName == "OutPut").Bind(f => f.GetPortFacing(GridRegistry)); if (port.HasValue(out var v) && v.TryInsert(new TestItem() { Item = sliced[0] }, 0)) { - GD.Print(sliced[0].Get()); sliced.RemoveAt(0); } } diff --git a/src/Items/Components/Tags.cs b/src/Items/Components/Tags.cs index 481707f..246272c 100644 --- a/src/Items/Components/Tags.cs +++ b/src/Items/Components/Tags.cs @@ -39,6 +39,7 @@ public readonly struct Tags : IEquatable, IReadOnlyCollection var key = new TagArrayKey(tags); if (!_internedTags.TryGetValue(key, out var interned)) { + interned = _internedTags.Count; _tags.Add(tags); _internedTags[key] = _tags.Count - 1; } diff --git a/src/Items/IDataCompiler.cs b/src/Items/IDataCompiler.cs new file mode 100644 index 0000000..b1b1b92 --- /dev/null +++ b/src/Items/IDataCompiler.cs @@ -0,0 +1,108 @@ +namespace FoodFactory.Items; + +using System; +using System.Collections.Generic; +using System.Linq; +using System.Reflection; +using Arch.Core; +using Arch.Core.Extensions; +using FoodFactory.Recipes; +using Godot; + +public interface IDataCompiler +{ + IValueProvider CompileUnTyped(object data);//Use Dicnary and have preset values + Type GetCompilerType(); +} + +public interface IDataCompiler : IDataCompiler +{ + IValueProvider Compile(object data); + Type IDataCompiler.GetCompilerType() => typeof(T); + IValueProvider IDataCompiler.CompileUnTyped(object data) => Compile(data); +} +public interface IValueProvider +{ + Type GetProviderType(); + void Resolve(BlueprintContext context, Entity entity); + ProviderMode Mode { get; } +} +public enum ProviderMode +{ + Static, + Modify +} +public interface IValueProvider : IValueProvider +{ + Type IValueProvider.GetProviderType() => typeof(T); + void Resolve(BlueprintContext context, ref T value); + void IValueProvider.Resolve(BlueprintContext context, Entity entity) => Resolve(context, ref entity.Get()); +} +public sealed class FactoryCompiledNode : IValueProvider +{ + public delegate void ResolveFunc(in BlueprintContext context, ref T value); + private readonly ResolveFunc _factory; + + public ProviderMode Mode => ProviderMode.Modify; + + public FactoryCompiledNode(ResolveFunc factory) + { + _factory = factory; + } + + public void Resolve(BlueprintContext context, ref T value) => _factory(context, ref value); +} +public sealed class StaticCompiledNode : IValueProvider +{ + public StaticCompiledNode(T value) + { + Value = value; + } + + public T Value { get; } + + public void Resolve(BlueprintContext context, ref T value) => value = Value; + public ProviderMode Mode => ProviderMode.Static; +} +public sealed class StaticReflectionCompiledNode : IValueProvider where T : struct +{ + //TODO Make more performance by only having properties of each method, and share use a list using tuples instead. + // private readonly Dictionary _data; + private readonly T _template; + + public StaticReflectionCompiledNode(Dictionary itemData) + { + StructReflection.ApplyValues(ref _template, itemData); + + + + } + + public void Resolve(BlueprintContext context, ref T value) => value = _template; + public ProviderMode Mode => ProviderMode.Static; + +} +public sealed class RuntimeReflectionCompiledNode : IValueProvider where T : struct +{ + //TODO Make more performance by only having properties of each method, and share use a list using tuples instead. + private readonly Dictionary _data; + // private readonly T _template; + + public RuntimeReflectionCompiledNode(Dictionary itemData) + { + _data = itemData; + } + + public void Resolve(BlueprintContext context, ref T value) => StructReflection.ApplyValues(ref value, _data); + public ProviderMode Mode => ProviderMode.Modify; + +} +public class CompiledBlueprint +{ + public string Id { get; init; } + public Tags Traits { get; set; } + public IValueProvider[][] Providers = []; + public Signature Signature; + public Entity CreateEmptyItem(BlueprintContext blueprintContext) => blueprintContext.World.Create(Signature); + +} diff --git a/src/Items/IDataCompiler.cs.uid b/src/Items/IDataCompiler.cs.uid new file mode 100644 index 0000000..4ab86a5 --- /dev/null +++ b/src/Items/IDataCompiler.cs.uid @@ -0,0 +1 @@ +uid://caur7nt65ia2o diff --git a/src/Items/Item.cs b/src/Items/Item.cs index f1afeb7..2baff0a 100644 --- a/src/Items/Item.cs +++ b/src/Items/Item.cs @@ -36,6 +36,7 @@ public class TestItem() : IBeltItem, IBeltItemData public Node3D CreateItemVisual() { Node3D node; + var tags = Item.Get().ToArray(); if (Item.Get().Contains(TagRegistry.GetTag("potato"))) { node = PotatoScene.Instantiate(); @@ -55,7 +56,7 @@ public class TestItem() : IBeltItem, IBeltItemData node.GetChildren().OfType().ToList().ForEach(i => i.Scale *= new Vector3(.2f, .2f, .2f)); if (Item.TryGet(out var color)) { - node.GetChildren().OfType().FirstOrNone().IfSome(some => some.SetSurfaceOverrideMaterial(0, new StandardMaterial3D() { AlbedoColor = Item.TryGet(out var color) ? color : Colors.White })); + node.GetChildren().OfType().FirstOrNone().IfSome(some => some.SetSurfaceOverrideMaterial(0, new StandardMaterial3D() { AlbedoColor = color })); } return node; // return new MeshInstance3D() { Mesh = new BoxMesh() { Size = new(.1f, .1f, .1f) }, MaterialOverride = new StandardMaterial3D() { AlbedoColor = Item.TryGet(out var color) ? color : Colors.White } }; diff --git a/src/Items/ItemData.cs b/src/Items/ItemData.cs new file mode 100644 index 0000000..a0f48ab --- /dev/null +++ b/src/Items/ItemData.cs @@ -0,0 +1,79 @@ +namespace FoodFactory.Items; + +using System; +using System.Collections.Generic; +using FoodFactory.Math; +using FoodFactory.Recipes; +using SharpYaml; +using SharpYaml.Serialization; + +public class ItemData +{ + public string Id { get; set; } = ""; + public string[] Extend { get; set; } = []; + [YamlConverter(typeof(ComponentsPatcherConverter))] + public Dictionary Components { get; set; } = []; + public HashSet Remove { get; set; } = []; + // public DataNode GetComponent(Type type) + // { + // if (!Components.TryGetValue(type.Name, out var value)) + // { + // throw new System.Exception(); + // } + // if (value is Dictionary thing) + // { + // return new DataNode(thing); + // } + // return new DataNode(new() { ["Value"] = value }); + // } + // public DataNode GetComponent() where T : struct => GetComponent(typeof(T)); +} + +public class ComponentsPatcherConverter : YamlConverter> +{ + private readonly Dictionary _componentsTypes = new(){ + [nameof(Temperature)] = typeof(IValueProvider), + // [nameof(Tags)] = typeof(IValueProvider), + // [nameof(Name)] = typeof(IValueProvider), + // [nameof(Mass)] = typeof(IValueProvider), + // [nameof(Godot.Color)] = typeof(IValueProvider), + // [nameof(BurnableTemp)] = typeof(IValueProvider), + }; + + // public ComponentsPatcherConverter(Dictionary componentsTypes) + // { + // _componentsTypes = componentsTypes; + // } + + public override Dictionary? Read(YamlReader reader) + { + var dict = new Dictionary(); + if (reader.TokenType == YamlTokenType.StartMapping) + { + reader.Read(); + } + while (reader.TokenType != YamlTokenType.EndMapping) + { + var key = reader.ScalarValue; + reader.Read(); + var d = reader.ScalarValue; + + if (!_componentsTypes.TryGetValue(key, out var type)) + { + type = typeof(object); + } + if (!reader.TryGetCustomConverter(type, out var converter)){ + converter = reader.GetConverter(type); + } + object data; + data = converter.Read(reader, type); + + // reader.Read(); + dict[key] = data; + + } + return dict; + } + + public override void Write(YamlWriter writer, Dictionary value) => throw new NotImplementedException(); +} diff --git a/src/Items/ItemData.cs.uid b/src/Items/ItemData.cs.uid new file mode 100644 index 0000000..524dfb1 --- /dev/null +++ b/src/Items/ItemData.cs.uid @@ -0,0 +1 @@ +uid://ymjkr070i83c diff --git a/src/Items/ItemECSTest.cs b/src/Items/ItemECSTest.cs index 46e99f9..1979a40 100644 --- a/src/Items/ItemECSTest.cs +++ b/src/Items/ItemECSTest.cs @@ -1,7 +1,10 @@ namespace FoodFactory; +using System; using System.Collections.Generic; using System.IO; +using System.Linq; +using System.Reflection; using Arch.Core; using Arch.Core.Extensions; using Arch.Core.Utils; @@ -11,7 +14,10 @@ using Chickensoft.Introspection; using Chickensoft.SaveFileBuilder; using FoodFactory.Items; using FoodFactory.Math; +using FoodFactory.Recipes; using Godot; +using SharpYaml; +using SharpYaml.Serialization; using Utf8Json; public class Test @@ -63,7 +69,86 @@ public class Test var worldJson = serializer.ToJson(world); // GD.Print(worldJson); var otherWorld = serializer.FromJson(worldJson); + foreach (var item in ComponentRegistry.TypeToComponentType.Keys) + { + if (item is null) + { + continue; + } + GD.Print(item.Name); + GD.Print(item.Namespace); + GD.Print(item.FullName); + } + string yaml = @" + Id: potato_raw + # TypeMapping: + # Tags: FoodFactory.Items.Tags + Components: + Name: Raw Potato + Tags: + - potato + - vegetable + - raw + - burnable + Temperature: + - + $type: temperature + Kelvin : 500. + + Bacteria: + - salmonella: !!float 400. + BurnableTemp: !!float 400. + "; + var options = new YamlSerializerOptions() + { + // Converters = [new Recipes.DataNodeSerlicer()] + // PolymorphismOptions = new YamlPolymorphismOptions + // { + // DerivedTypeMappings = + // { + // [typeof(DataNode)] = + // [ + // new YamlDerivedType(typeof(SequenceNode), "sequence"){Tag = "!sequence"}, + // new YamlDerivedType(typeof(MappingNode), "mapping"){Tag = "!mapping"}, + // new YamlDerivedType(typeof(ValueNode), "value"){Tag = "!value"} + // ], + // [typeof(object)] = + // [ + // new YamlDerivedType(typeof(Temperature), "temperature") { Tag = "!temperature"} + // ] + + // }, + // }, + }; + // var test = YamlSerializer.Serialize(new ItemData() + // { + // Id = "potato_raw", + // Components = { [nameof(Name)] = new ValueNode("Raw Potato") , + // [nameof(Temperature)] = new ValueNode(100f), + // [nameof(Tags)] = new SequenceNode([new ValueNode("potato"), new ValueNode("vegetable"), new ValueNode("food"),new ValueNode("raw"),new ValueNode("burnable")])} + // }, options); + // GD.Print(test); + var result = YamlSerializer.Deserialize(yaml,options); + var names = result.Components.Keys.ToArray(); + var list3 = new List(); + foreach (var item in Assembly.GetAssembly(typeof(Name)).GetTypes()) + { + if (!names.Contains(item.Name)) + { + continue; + } + list3.Add(item); + } + // var types = names.Select(f => Type.GetType(f)).ToArray(); + // var names2 = new Type[]{typeof(Name), typeof(Temperature), typeof(Tags)}; +var componentTypes = list3.Select(Component.GetComponentType).ToArray(); + + Entity entite = world.Create(componentTypes); + // var reg = new ComponentBinderRegistry(); + // reg.Add(new TemperatureBinder()); + // reg.Apply(entite,world,result); + // GD.Print(string.Join(',',entite.GetAllComponents())); // ISaveChunk saveChunk =new SaveChunk( // chunk => // { diff --git a/src/Items/StructReflection.cs b/src/Items/StructReflection.cs new file mode 100644 index 0000000..06ee213 --- /dev/null +++ b/src/Items/StructReflection.cs @@ -0,0 +1,96 @@ +namespace FoodFactory.Items; + +using System; +using System.Collections.Generic; +using System.Reflection; + +public static class StructReflection +{ + public static void ApplyValues( + ref T target, + Dictionary values) + where T : struct + { + object boxed = target; + + Type type = typeof(T); + + foreach (var kvp in values) + { + string name = kvp.Key; + object value = kvp.Value; + + // Try property first + var prop = type.GetProperty( + name, + BindingFlags.Public | + BindingFlags.Instance); + + if (prop != null && prop.CanWrite) + { + object converted = ConvertValue(value, prop.PropertyType); + prop.SetValue(boxed, converted); + continue; + } + + // Then field + var field = type.GetField( + name, + BindingFlags.Public | + BindingFlags.Instance); + + if (field != null) + { + object converted = ConvertValue(value, field.FieldType); + field.SetValue(boxed, converted); + continue; + } + + throw new Exception( + $"Member '{name}' not found on {type.Name}"); + } + + target = (T)boxed; + } + + private static object ConvertValue(object value, Type targetType) + { + if (value == null) + return null!; + + Type valueType = value.GetType(); + + if (targetType.IsAssignableFrom(valueType)) + return value; + + return Convert.ChangeType(value, targetType); + } +} +/* +static RefSetter CreateFieldSetter(FieldInfo field) +{ + var target = Expression.Parameter(typeof(T).MakeByRefType(), "target"); + var value = Expression.Parameter(typeof(TValue), "value"); + + var fieldExp = Expression.Field(target, field); + + var assign = Expression.Assign(fieldExp, value); + + return Expression.Lambda>( + assign, + target, + value + ).Compile(); +} +static RefSetter CreatePropertySetter(PropertyInfo prop) +{ + var target = Expression.Parameter(typeof(T).MakeByRefType(), "target"); + var value = Expression.Parameter(typeof(TValue), "value"); + + var call = Expression.Call(target, prop.SetMethod!, value); + + return Expression + .Lambda>(call, target, value) + .Compile(); +} +*/ diff --git a/src/Items/StructReflection.cs.uid b/src/Items/StructReflection.cs.uid new file mode 100644 index 0000000..900f188 --- /dev/null +++ b/src/Items/StructReflection.cs.uid @@ -0,0 +1 @@ +uid://bpw4csgtk7qwf diff --git a/src/Math/Temperature.cs b/src/Math/Temperature.cs index 2679009..e2c7abf 100644 --- a/src/Math/Temperature.cs +++ b/src/Math/Temperature.cs @@ -32,7 +32,21 @@ public enum TemperatureUnit /// Fahrenheit } - +public record struct Fahrenheit(double Value) +{ + public static implicit operator Fahrenheit(double value) => new(value); + public static implicit operator Temperature(Fahrenheit value) => new(value); +} +public record struct Celsius(double Value) +{ + public static implicit operator Celsius(double value) => new(value); + public static implicit operator Temperature(Celsius value) => new(value); +} +public record struct Kelvin(double Value) +{ + public static implicit operator Kelvin(double value) => new(value); + public static implicit operator Temperature(Kelvin value) => new(value); +} /// /// A temperature value. /// @@ -53,7 +67,18 @@ public struct Temperature : IFormattable, IComparable, /// The value of the temperature. public Temperature(double kelvin) : this() { _kelvin = kelvin; } - + public Temperature(Fahrenheit fahrenheit) + { + _kelvin = FahrenheitToKelvin(fahrenheit.Value); + } + public Temperature(Celsius celsius) + { + _kelvin = CelsiusToKelvin(celsius.Value); + } + public Temperature(Kelvin kelvin) + { + _kelvin = kelvin.Value; + } /// /// Creates a new temperature with the specified value in the /// specified unit of measurement. @@ -90,6 +115,11 @@ public struct Temperature : IFormattable, IComparable, set => _kelvin = value; } + public Kelvin KelvinMeasurement + { + readonly get => _kelvin; + set => _kelvin = value.Value; + } /// /// Gets or sets the temperature value in Celsius. @@ -98,6 +128,11 @@ public struct Temperature : IFormattable, IComparable, { readonly get => KelvinToCelsius(_kelvin); set => _kelvin = CelsiusToKelvin(value); } + public Celsius CelsiusMeasurement + { + readonly get => KelvinToCelsius(_kelvin); + set => _kelvin = CelsiusToKelvin(value.Value); + } /// /// Gets or sets the temperature value in Fahrenheit. @@ -106,6 +141,11 @@ public struct Temperature : IFormattable, IComparable, { readonly get => KelvinToFahrenheit(_kelvin); set => _kelvin = FahrenheitToKelvin(value); } + public Fahrenheit FahrenheitMeasurement + { + readonly get => KelvinToFahrenheit(_kelvin); + set => _kelvin = FahrenheitToKelvin(value.Value); + } /// /// Gets the temperature value in the specified unit of measurement. diff --git a/src/Recipes/Blueprint.cs b/src/Recipes/Blueprint.cs index c096ec9..826fb81 100644 --- a/src/Recipes/Blueprint.cs +++ b/src/Recipes/Blueprint.cs @@ -2,21 +2,201 @@ namespace FoodFactory.Recipes; using System; using System.Collections.Generic; +using System.Diagnostics; +using System.Diagnostics.CodeAnalysis; +using System.Linq; +using System.Reflection; using Arch.Core; +using Arch.Core.Extensions; using FoodFactory.Items; using FoodFactory.Math; using FoodFactory.Voxel; +using Godot; +using SharpYaml; +using SharpYaml.Model; +using SharpYaml.Serialization; +using SJK.Functional; +using Utf8Json; public interface IBlueprintManger { Blueprint GetBlueprint(string name); + CompiledBlueprint GetCompiledBlueprint(string name); + // ComponentBinderRegistry BinderRegistry { get; } } public class BlueprintManger : IBlueprintManger { - private readonly Dictionary _bluePrints = []; + // private readonly Dictionary _bluePrints = []; + // public readonly ComponentBinderRegistry ComponentBinder = new(); + public readonly Dictionary Blueprints = []; + public readonly Dictionary CompiledBlueprints = []; + public readonly Dictionary Providers = []; + + // public ComponentBinderRegistry BinderRegistry { get; } + + public BlueprintManger() + { + // BinderRegistry = new ComponentBinderRegistry(); + + // BinderRegistry.Add(new ComponentBinder()); + // BinderRegistry.Add(new ComponentBinder()); + // BinderRegistry.Add(new ComponentBinder()); + // BinderRegistry.Add(new ComponentBinder()); + // BinderRegistry.Add(new ComponentBinder()); + // BinderRegistry.Add(new ComponentBinder()); + // BinderRegistry.Add(new ComponentBinder()); + + // Providers.Add(nameof(Temperature), new TemperatureCompiler()); + Providers.Add(nameof(Tags), new TagsCompiler()); + Providers.Add(nameof(Name), new NameCompiler()); + Providers.Add(nameof(Mass), new DefaultCompiler()); + Providers.Add(nameof(Color), new DefaultCompiler()); + Providers.Add(nameof(BurnableTemp), new DefaultConverterCompiler(){Converter = data => + { + if (data is Dictionary dict){ + if (dict.TryGetValue(nameof(BurnableTemp.Temperature), out var value)) + { + dict[nameof(BurnableTemp.Temperature)] = new Temperature((double)value); + } + return dict; + } + throw new NotImplementedException(); + } + }); + + + var root = "/home/ronnie/Documents/Godot/Projects/ChickenGameTest/mods"; + var dir = DirAccess.Open(root); + var cache = new Dictionary(); + var types = Assembly.GetAssembly(typeof(Name)).GetTypes();//Temperary mesure + var itemDatas = new List(); + + var options = new YamlSerializerOptions() + { + Converters = [ + // new ComponentsPatcherConverter( + // new(){ + // [nameof(Temperature)] = typeof(IValueProvider), + // [nameof(Tags)] = typeof(IValueProvider), + // [nameof(Name)] = typeof(IValueProvider), + // [nameof(Mass)] = typeof(IValueProvider), + // [nameof(Color)] = typeof(IValueProvider), + // [nameof(BurnableTemp)] = typeof(IValueProvider), + // } + // ), + new TemperatureCompiler(), + + ] + }; + foreach (var item in dir.GetFiles()) + { + if (item.EndsWith(".yaml")) + { + var file = FileAccess.Open(dir.GetCurrentDir() + "/" + item, FileAccess.ModeFlags.Read); + var data = YamlSerializer.Deserialize(file.GetAsText(),options); + itemDatas.Add(data); + // var compiledBlueprint = new CompiledBlueprint + // { + // // Id = data.Id, + // // Signature = new Signature([typeof(BlueprintId), .. types.Where(f => data.Components.ContainsKey(f.Name)).Select(Component.GetComponentType)]), + // // Providers = new IValueProvider[data.Components.Count] + // // }; + // // var i = 0; + // // foreach (var item2 in data.Components) + // // { + // // var compiler = Providers[item2.Key]; + // // var compiled = compiler.CompileUnTyped(item2.Value); + // // compiledBlueprint.Providers[i] = compiled; + // // i++; + // // } + // CompiledBlueprints.Add(compiledBlueprint.Id, compiledBlueprint); + } + } + CompiledBlueprints = LoadBluprints([.. itemDatas]); + } + private Dictionary LoadBluprints(ItemData[] all, Dictionary? existing = null) + { + var completed = existing is not null ? new(existing) : new Dictionary(); + var queue = new Queue(all); + while (queue.Any()) + { + reset: + var item = queue.Dequeue(); + var sources = new List(); + foreach (var c in item.Extend) + { + if (!completed.ContainsKey(c)) + { + queue.Enqueue(item); + goto reset; + } + sources.Add(completed[c]); + } + var providers = new Dictionary>();//TODO Make Blupritns able to hold muiple providers in a array for cases taht build off previouse providers + foreach (var blueprint in sources) + { + foreach (var provider in blueprint.Providers) + { + if (item.Remove.Contains(provider[0].GetProviderType().Name)) + { + continue; + } + if (!providers.TryGetValue(provider[0].GetProviderType(), out var providersList)) + { + providers[provider[0].GetProviderType()] = providersList = new(); + } + for (int i = 0; i < provider.Length; i++) + { + if (provider[i].Mode == ProviderMode.Static) + { + providersList.Clear(); + providersList.Add(provider[i]); + } + + } + } + } + foreach (var component in item.Components) + { + var compiler = Providers[component.Key]; + var compiledProvider = compiler.CompileUnTyped(component.Value); + if (!providers.TryGetValue(compiledProvider.GetProviderType(), out var providersList)) + { + providers[compiledProvider.GetProviderType()] = providersList = new(); + } + providersList.Add(compiledProvider); + } + var compiled = new CompiledBlueprint() + { + Id = item.Id, + // Traits = new Tags([.. item.Components.Keys]),//TODO need a way for recipies to look in each 'parent/extend' for getting recipes, either iterating each parent pool(recipe behing only in max shared, or each buckert container a copy for each parent) + Signature = [.. providers.Keys], + Providers = [.. providers.Values.Select(list => list.ToArray())], + }; + /* + ex Food-> onion -> red onion + if onion id, then onion and red onion bucket have a recipe + of for each trait, iterate each bucket, starting from spefic, to top. + */ + completed.Add(item.Id, compiled); + } + return completed; + } + public CompiledBlueprint? GetCompiledBlueprint(string id) + + { + if (CompiledBlueprints.TryGetValue(id, out var compiledBlueprint)) + { + return compiledBlueprint; + } + return null; + + } public Blueprint GetBlueprint(string name) { - if (!_bluePrints.TryGetValue(name, out var blueprint)) + + // throw new NotImplementedException(); + if (!Blueprints.TryGetValue(name, out var blueprint)) { //TODO this is where this should look through mods/base game if (name == "Potato") @@ -47,6 +227,10 @@ public class BlueprintManger : IBlueprintManger return blueprint; } } +// public record Blueprint(string Name, Func Factory) +// { + +// } public record Blueprint(string Name, Func Factory) { @@ -56,4 +240,306 @@ public struct BlueprintContext { public World World; public BlueprintId BluePrintId; + public object PlaceHolderApi; } +// public interface IComponentBinder +// { +// ComponentType ComponentType { get; } +// void ApplyUntyped( +// Entity existing, +// IValueProvider data, +// BlueprintContext context); +// void ApplyUntyped( +// Span existing, +// IValueProvider data, +// BlueprintContext context); +// } +// public class ComponentBinder : IComponentBinder where T : struct +// { +// public ComponentType ComponentType => Component.ComponentType; + +// public void ApplyUntyped( +// Entity existing, +// IValueProvider data, +// BlueprintContext context) +// { +// if (data is IValueProvider dataT) +// { +// ref var component = ref context.World.Get(existing); +// Apply(ref component, dataT, context); +// return; +// } +// throw new System.Exception(); +// } +// public void ApplyUntyped(Span existing, IValueProvider data, BlueprintContext context) +// { +// if (data is not IValueProvider dataT) +// { +// throw new System.Exception(); +// } +// for (int i = 0; i < existing.Length; i++) +// { +// Apply(ref existing[i].Get(), dataT, context); +// } +// } + +// public void Apply( +// ref T existing, +// IValueProvider data, +// BlueprintContext context) => data.Resolve(context, ref existing); +// } +// public class DefaultBinder : ComponentBinder where T : struct +// { +// public override void Apply(ref T existing, IValueProvider data, BlueprintContext context); + +// } +// public class TemperatureBinder : ComponentBinder +// { +// public override void Apply(ref Temperature existing, IValueProvider data, BlueprintContext context) => data.Resolve(context, ref existing); + +// } +public sealed class TemperatureCompiler : YamlConverter>//, IDataCompiler +{ + // public IValueProvider Compile(object data) + // { + // if (data is IValueProvider t) + // { + // return t; + // } + // if (data is string says) + // { + // if (says == "ambient") + // { + // return new AmbientTemperatureProvider(); + // } + // } + // if (data is float f) + // { + // return new StaticCompiledNode(new Temperature(f)); + // } + // if (data is double d) + // { + // return new StaticCompiledNode(new Temperature(d)); + // } + // if (data is Dictionary dict) + // { + // return new StaticReflectionCompiledNode(dict); + + // } + // throw new NotSupportedException(); + // } + + public override IValueProvider? Read(YamlReader reader) + { + if (reader.TokenType == YamlTokenType.Scalar) + { + var value = reader.ScalarValue; + reader.Read(); + if (value == "ambient") + { + return new AmbientTemperatureProvider(); + } + if (float.TryParse(value, out var f)) + { + return new StaticCompiledNode(new Temperature(f)); + } + if (double.TryParse(value, out var d)) + { + return new StaticCompiledNode(new Temperature(f)); + } + throw new NotImplementedException(); + } + if (reader.TokenType == YamlTokenType.StartMapping) + { + reader.Read(); + IValueProvider? provider = null; + while (reader.TokenType != YamlTokenType.EndMapping) + { + var key = reader.ScalarValue; + reader.Read(); + if (key == nameof(Fahrenheit)) + { + provider = new StaticCompiledNode(new Fahrenheit(float.Parse(reader.ScalarValue))); + } + reader.Read(); + } + return provider??throw new NotSupportedException(); + } + throw new NotImplementedException(); + } + + public override void Write(YamlWriter writer, IValueProvider value) => throw new NotImplementedException(); + + public sealed class AmbientTemperatureProvider : IValueProvider + { + public ProviderMode Mode => ProviderMode.Static; + + public void Resolve(BlueprintContext context, ref Temperature value) + { + value.Kelvin = (float)context.PlaceHolderApi; + } + } +} +public sealed class DefaultCompiler : IDataCompiler where T : struct +{ + + public IValueProvider Compile(object data) => new StaticReflectionCompiledNode(data as Dictionary); +} +public sealed class DefaultConverterCompiler : IDataCompiler where T : struct +{ + public required Func> Converter { get; set; } + public IValueProvider Compile(object data) => new StaticReflectionCompiledNode(Converter(data)); +} +public sealed class NameCompiler : IDataCompiler +{ + public IValueProvider Compile(object data) + { +if (data is string s) + { + return new StaticCompiledNode(new(s)); + } + throw new NotSupportedException(); + } +} +public sealed class TagsCompiler : IDataCompiler +{ + public IValueProvider Compile(object data) + { + if (data is List list) + { + var cast = list.Cast().ToArray(); + Tags tag = new(cast); + return new StaticCompiledNode(tag); + + } + if (data is Dictionary dict) + { + var tagsWith = new string[0]; + if (dict.TryGetValue("With", out var with)) + { + tagsWith = ((List)with).Cast().ToArray(); + } + var tagsWiothout = new string[0]; + if (dict.TryGetValue("Except", out var except)) + { + tagsWiothout = ((List)except).Cast().ToArray(); + } + return new FactoryCompiledNode((in context, ref tags) => + { + tags = tags.With(tagsWith).WithOut(tagsWiothout); + }); + } + throw new NotSupportedException(); + } +} +// public class ComponentBinderRegistry +// { +// private readonly Dictionary _binder = []; +// public void Add(ComponentBinder componentBinder) where T : struct +// { + +// _binder.Add(typeof(T), componentBinder); +// } +// public void Apply(Entity entity, World world, CompiledBlueprint itemData) +// { + +// var s = world.GetSignature(entity); +// for (int i = 0; i < s.Count; i++) +// { +// if (!_binder.ContainsKey(s.Components[i])) +// { +// continue; +// } +// var binder = _binder[s.Components[i]]; +// var type = s.Components[i].Type; +// var context = new BlueprintContext(){World = world}; +// binder.ApplyUntyped(entity, itemData.Providers[type], context); +// } +// } +// public void Apply(Span entities, World world, CompiledBlueprint itemData) +// { +// var signature = world.GetSignature(entities[0]); +// for (int i = 1; i < entities.Length; i++) +// { +// Debug.Assert(world.GetSignature(entities[i]) == signature); +// } + +// for (int i = 0; i < signature.Count; i++) +// { +// var binder = _binder[signature.Components[i]]; +// var type = signature.Components[i].Type; +// var context = new BlueprintContext(){World = world}; +// binder.ApplyUntyped(entities, itemData.Providers[type], context); +// } +// } + +// } +// public abstract class DataNode//(Dictionary Data) +// { +// public abstract DataNodeType NodeType { get; } +// // public T Get(string name) => (T)Data[name]; +// } +// public enum DataNodeType +// {hthous stomatitis, pharyngitis, adenitis) is a +// Value, +// Sequence, +// Mapping, +// Component +// } +// public sealed class ValueNode(object value) : DataNode +// { +// public override DataNodeType NodeType => DataNodeType.Value; + +// public object Value { get; set; } = value; + +// public Option As() => Value is T o?Option.Some(o):Option.None; +// } +// public sealed class SequenceNode(IReadOnlyList values) : DataNode +// { +// public override DataNodeType NodeType => DataNodeType.Sequence; + +// public IReadOnlyList Values { get; set; } = values; +// } +// public sealed class MappingNode(IReadOnlyDictionary values) : DataNode +// { +// public override DataNodeType NodeType => DataNodeType.Mapping; + +// public IReadOnlyDictionary Values { get; set; } = values; + +// public bool TryGet(string key, [NotNullWhen(true)] out T value) where T : DataNode +// { +// var result = Values.TryGetValue(key, out var node); +// value = (T)node!; +// return result; + +// } +// public bool TryGet(string key, [NotNullWhen(true)] out DataNode node) => Values.TryGetValue(key, out node!); +// } + +// public class DataNodeSerlicer : YamlConverter, IJsonFormatter +// { +// private Dictionary _data = new(); +// public DataNode Deserialize(ref JsonReader reader, IJsonFormatterResolver formatterResolver) +// { +// var i = 0; +// while (reader.ReadIsInObject(ref i)) +// { +// var name = reader.ReadPropertyName(); +// var value = reader.ReadString(); +// _data.Add(name, value); +// } +// throw new NotImplementedException(); +// } +// public void Serialize(ref JsonWriter writer, DataNode value, IJsonFormatterResolver formatterResolver) => throw new NotImplementedException(); + +// public override DataNode? Read(YamlReader reader) +// { +// // reader.pa +// throw new NotImplementedException(); +// } + +// public override void Write(YamlWriter writer, DataNode value) +// { +// throw new NotImplementedException(); +// } +// } diff --git a/src/Recipes/ComponetBinder.cs b/src/Recipes/ComponetBinder.cs new file mode 100644 index 0000000..4c50c99 --- /dev/null +++ b/src/Recipes/ComponetBinder.cs @@ -0,0 +1,81 @@ +using System; +using System.Collections.Generic; +using System.Reflection; +using Arch.Core; +using FoodFactory.Items; +using FoodFactory.Recipes; + +public static class ComponentBinder +{ + private static readonly Dictionary _cache = new(); + + private delegate void ApplyDelegate( + Entity entity, + IValueProvider provider, + BlueprintContext context); + + public static void Apply(Type componentType, Span existing, IValueProvider data, BlueprintContext context) + { + if (!_cache.TryGetValue(componentType, out var del)) + { + del = CreateDelegate(componentType); + _cache[componentType] = del; + } + for (int i = 0; i < existing.Length; i++) + { + Apply(componentType, existing[i], data, context); + } + } + public static void Apply( + ReadOnlySpan componentType, + Entity entity, + ReadOnlySpan provider, + BlueprintContext context) + { + for (int i = 0; i < componentType.Length; i++) + { + Apply(componentType[i], entity, provider[i], context); + } + } + public static void Apply( + Type componentType, + Entity entity, + IValueProvider provider, + BlueprintContext context) + { + if (!_cache.TryGetValue(componentType, out var del)) + { + del = CreateDelegate(componentType); + _cache[componentType] = del; + } + + del(entity, provider, context); + } + + private static ApplyDelegate CreateDelegate(Type t) + { + var method = typeof(ComponentBinder) + .GetMethod(nameof(ApplyGeneric), + BindingFlags.Static | BindingFlags.NonPublic)! + .MakeGenericMethod(t); + + return (ApplyDelegate)Delegate.CreateDelegate( + typeof(ApplyDelegate), + method); + } + + private static void ApplyGeneric( + Entity entity, + IValueProvider provider, + BlueprintContext context) + { + if (provider is not IValueProvider typedProvider) + { + throw new InvalidOperationException(); + } + + ref var component = ref context.World.Get(entity); + + typedProvider.Resolve(context, ref component); + } +} diff --git a/src/Recipes/ComponetBinder.cs.uid b/src/Recipes/ComponetBinder.cs.uid new file mode 100644 index 0000000..adf9622 --- /dev/null +++ b/src/Recipes/ComponetBinder.cs.uid @@ -0,0 +1 @@ +uid://cmes4oeo8jjov diff --git a/src/VoxelGrid/VoxelGridNode.tscn b/src/VoxelGrid/VoxelGridNode.tscn index 07a975d..fffae83 100644 --- a/src/VoxelGrid/VoxelGridNode.tscn +++ b/src/VoxelGrid/VoxelGridNode.tscn @@ -3,6 +3,7 @@ [ext_resource type="Script" uid="uid://dcrb286hmpli" path="res://src/VoxelGrid/VoxelGridNode.cs" id="1_tsdpe"] [ext_resource type="Script" uid="uid://cnkblltup5guy" path="res://src/Equipment/OvenTest.cs" id="3_r7dgx"] [ext_resource type="PackedScene" uid="uid://bktqs1lw6go4" path="res://src/VoxelGrid/ItemSpawner.tscn" id="5_mxaon"] +[ext_resource type="PackedScene" uid="uid://bkg733oidira6" path="res://assets/KayKit_Restaurant_Bits_1.0_FREE/Assets/gltf/oven.gltf" id="5_ujjjs"] [ext_resource type="PackedScene" uid="uid://h00mq2srsbfa" path="res://assets/kenney_conveyor-kit/Models/GLB format/door.glb" id="5_wk2t5"] [ext_resource type="Script" uid="uid://ee5aoxi8mjnw" path="res://src/Equipment/BeltPort.cs" id="6_2wkfx"] [ext_resource type="PackedScene" uid="uid://c4h7mwnfrdesg" path="res://src/Conveyors/ConveyorBeltStraight/ConveyorBeltStraight.tscn" id="6_mxaon"] @@ -10,6 +11,9 @@ [ext_resource type="Script" uid="uid://yec84plemjv1" path="res://src/Equipment/SlicerTest.cs" id="8_2lg7i"] [ext_resource type="Script" uid="uid://culjdbwllmsyk" path="res://src/Equipment/StackerTest.cs" id="8_e2skk"] +[sub_resource type="BoxMesh" id="BoxMesh_2wkfx"] +size = Vector3(1, 1, 2) + [sub_resource type="Curve3D" id="Curve3D_mxaon"] _data = { "points": PackedVector3Array(0, 0, 0, 0, 0, 0, 0, 0.5, -0.5, 0, 0, 0, 0, 0, 0, 0, 0.5, 0), @@ -17,9 +21,6 @@ _data = { } point_count = 2 -[sub_resource type="BoxMesh" id="BoxMesh_2wkfx"] -size = Vector3(1, 1, 2) - [node name="VoxelGridNode" type="Node3D" unique_id=825696340] script = ExtResource("1_tsdpe") @@ -59,6 +60,9 @@ transform = Transform3D(1, 0, 1.7484555e-07, 0, 1, 0, -1.7484555e-07, 0, 1, 4, 0 [node name="ConveyorBeltStraight9" parent="." unique_id=1449572266 instance=ExtResource("6_mxaon")] transform = Transform3D(1.3113416e-07, 0, -1, 0, 1, 0, 1, 0, 1.3113416e-07, 3, 0, 1) +[node name="ConveyorBeltStraight44" parent="." unique_id=27021356 instance=ExtResource("6_mxaon")] +transform = Transform3D(-1, 0, -8.742277e-08, 0, 1, 0, 8.742277e-08, 0, -1, 3, 0, 0) + [node name="ConveyorBeltStraight26" parent="." unique_id=181268712 instance=ExtResource("6_mxaon")] transform = Transform3D(1.3113416e-07, 0, -1, 0, 1, 0, 1, 0, 1.3113416e-07, 5, 0, 5) @@ -152,40 +156,28 @@ Width = 1 Access = 1 PortName = "Input" -[node name="Path3D" type="Path3D" parent="OvenTest/Node3D2" unique_id=1525648764] -curve = SubResource("Curve3D_mxaon") - -[node name="door3" parent="OvenTest/Node3D2" unique_id=351624885 instance=ExtResource("5_wk2t5")] -transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, -7.870017e-08, 0, -0.6001501) - [node name="Node3D3" type="Node3D" parent="OvenTest" unique_id=1033202746] -transform = Transform3D(-2.1855693e-07, 0, -1, 0, 1, 0, 1, 0, -2.1855693e-07, 0, 0, 1) +transform = Transform3D(-2.1855693e-07, 0, -1, 0, 1, 0, 1, 0, -2.1855693e-07, 0, 0, 0) script = ExtResource("6_2wkfx") Face = 4 Width = 1 Access = 2 PortName = "OutPut" -[node name="Path3D2" type="Path3D" parent="OvenTest/Node3D3" unique_id=739562310] -curve = SubResource("Curve3D_mxaon") - -[node name="door4" parent="OvenTest/Node3D3" unique_id=1602265662 instance=ExtResource("5_wk2t5")] -transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, -5.9604645e-08, 0, -0.3998499) - -[node name="door5" parent="OvenTest/Node3D3" unique_id=102838222 instance=ExtResource("5_wk2t5")] -transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, -5.9604645e-08, 0, -0.3998499) +[node name="oven" parent="OvenTest" unique_id=169939827 instance=ExtResource("5_ujjjs")] +transform = Transform3D(-2.1855694e-08, 0, -0.5, 0, 0.5, 0, 0.5, 0, -2.1855694e-08, 0, 0, 0) [node name="ItemSpawner" parent="." unique_id=966349707 instance=ExtResource("5_mxaon")] transform = Transform3D(-4.371139e-08, 0, -1, 0, 1, 0, 1, 0, -4.371139e-08, -1.0023941, 0, 0.0019463301) -ItemName = "Potato" +ItemName = "raw_potato" [node name="ItemSpawner2" parent="." unique_id=405829395 instance=ExtResource("5_mxaon")] transform = Transform3D(3.059797e-07, 0, -1, 0, 1, 0, 1, 0, 3.059797e-07, -1, 0, 1) -ItemName = "Onion" +ItemName = "raw_onion" [node name="ItemSpawner3" parent="." unique_id=422927887 instance=ExtResource("5_mxaon")] transform = Transform3D(3.059797e-07, 0, -1, 0, 1, 0, 1, 0, 3.059797e-07, -1, 0, 6) -ItemName = "Potato" +ItemName = "raw_red_onion" [node name="Balancer" type="Node3D" parent="." unique_id=619243985] transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 2, 0, 2)