Compare commits
13 Commits
5906f248f4
...
main
| Author | SHA1 | Date | |
|---|---|---|---|
| 170aebacdc | |||
| b58acff195 | |||
| c9ac4641a0 | |||
| 0fa7ba766b | |||
| a76a50f38e | |||
| 8a0347f1a1 | |||
| 75c93f0c43 | |||
| bf4d856608 | |||
| 4db51be374 | |||
| d620189e4e | |||
| 4a7494ff6b | |||
| 7d25f6d448 | |||
| e7ef3896b0 |
2
.gitmodules
vendored
2
.gitmodules
vendored
@@ -1,3 +1,3 @@
|
||||
[submodule "assets"]
|
||||
path = assets
|
||||
url = http://192.168.1.4:3000/Ronnie/FoodFactoryAssets
|
||||
url = https://gitea.superjrking.com/Ronnie/FoodFactoryAssets
|
||||
|
||||
4
.vscode/settings.json
vendored
4
.vscode/settings.json
vendored
@@ -27,7 +27,7 @@
|
||||
"dotnet.enableXamlTools": false,
|
||||
"dotnet.formatting.organizeImportsOnFormat": true,
|
||||
"dotnet.preferCSharpExtension": true,
|
||||
// "dotnet.server.useOmnisharp": true, // You decide
|
||||
"dotnet.server.useOmnisharp": false, // You decide
|
||||
"dotnetAcquisitionExtension.enableTelemetry": false,
|
||||
"editor.semanticHighlighting.enabled": true,
|
||||
// C# doc comment colorization gets lost with semantic highlighting, but we
|
||||
@@ -164,7 +164,7 @@
|
||||
"omnisharp.enableEditorConfigSupport": true,
|
||||
"omnisharp.enableMsBuildLoadProjectsOnDemand": false,
|
||||
"omnisharp.maxFindSymbolsItems": 3000,
|
||||
"omnisharp.useModernNet": true,
|
||||
"omnisharp.useModernNet": false,
|
||||
// Remove these if you're happy with your terminal profiles.
|
||||
"terminal.integrated.defaultProfile.windows": "Git Bash",
|
||||
"terminal.integrated.profiles.windows": {
|
||||
|
||||
@@ -10,6 +10,7 @@
|
||||
<!-- Required for some nuget packages to work -->
|
||||
<!-- godotengine/godot/issues/42271#issuecomment-751423827 -->
|
||||
<CopyLocalLockFileAssemblies>true</CopyLocalLockFileAssemblies>
|
||||
<AllowUnsafeBlocks>true</AllowUnsafeBlocks>
|
||||
<!-- To show generated files -->
|
||||
<!-- <EmitCompilerGeneratedFiles>true</EmitCompilerGeneratedFiles> -->
|
||||
<!--
|
||||
@@ -35,31 +36,45 @@
|
||||
|
||||
<ItemGroup>
|
||||
<!-- Production dependencies go here! -->
|
||||
<PackageReference Include="Chickensoft.GameTools" Version="3.1.6" />
|
||||
<PackageReference Include="SjkScripts" Version="1.0.1" />
|
||||
<PackageReference Include="System.IO.Abstractions" Version="22.1.0" />
|
||||
<PackageReference Include="Arch" Version="2.1.0" />
|
||||
<PackageReference Include="Arch.LowLevel" Version="1.1.5" />
|
||||
<PackageReference Include="Arch.System" Version="1.1.0" />
|
||||
<PackageReference Include="Chickensoft.GameTools" Version="3.1.27" />
|
||||
<PackageReference Include="GodotHelper" Version="0.0.2" />
|
||||
<!-- <PackageReference Include="LanguageExt.Core" Version="5.0.0-beta-77" /> -->
|
||||
<PackageReference Include="MessagePack" Version="3.1.4" />
|
||||
<PackageReference Include="NCalc.LambdaCompilation" Version="5.12.0" />
|
||||
<PackageReference Include="NCalcSync" Version="5.12.0" />
|
||||
<PackageReference Include="SharpYaml" Version="3.7.1" />
|
||||
<PackageReference Include="SjkScripts" Version="1.0.17" />
|
||||
<PackageReference Include="System.IO.Abstractions" Version="22.1.1" />
|
||||
<PackageReference Include="EnvironmentAbstractions" Version="5.0.0" />
|
||||
<PackageReference Include="GodotSharp.SourceGenerators" Version="2.6.0" PrivateAssets="all" OutputItemType="analyzer" />
|
||||
<PackageReference Include="Chickensoft.SaveFileBuilder" Version="1.3.54" />
|
||||
<PackageReference Include="Chickensoft.AutoInject" Version="2.9.18" PrivateAssets="all" />
|
||||
<PackageReference Include="GodotSharp.SourceGenerators" Version="2.7.0" PrivateAssets="all" OutputItemType="analyzer" />
|
||||
<PackageReference Include="Chickensoft.SaveFileBuilder" Version="2.0.1" />
|
||||
<PackageReference Include="Chickensoft.AutoInject" Version="2.13.15" PrivateAssets="all" />
|
||||
<PackageReference Include="Chickensoft.Collections" Version="3.1.4" />
|
||||
<PackageReference Include="Chickensoft.GodotNodeInterfaces" Version="2.4.57" />
|
||||
<PackageReference Include="Chickensoft.Introspection" Version="3.0.2" />
|
||||
<PackageReference Include="Chickensoft.Introspection.Generator" Version="3.0.2" PrivateAssets="all" OutputItemType="analyzer" />
|
||||
<PackageReference Include="Chickensoft.GodotNodeInterfaces" Version="3.0.20" />
|
||||
<PackageReference Include="Chickensoft.Introspection" Version="3.0.3" />
|
||||
<PackageReference Include="Chickensoft.Introspection.Generator" Version="3.0.3" PrivateAssets="all" OutputItemType="analyzer" />
|
||||
<PackageReference Include="Chickensoft.Serialization" Version="3.1.0" />
|
||||
<PackageReference Include="Chickensoft.Serialization.Godot" Version="0.8.46" />
|
||||
<PackageReference Include="Chickensoft.LogicBlocks" Version="5.20.0" />
|
||||
<PackageReference Include="Chickensoft.LogicBlocks.DiagramGenerator" Version="5.20.0" PrivateAssets="all" OutputItemType="analyzer" />
|
||||
<PackageReference Include="Chickensoft.UMLGenerator" Version="1.1.0" />
|
||||
<PackageReference Include="Chickensoft.Sync" Version="2.2.0" />
|
||||
<PackageReference Include="Chickensoft.Serialization.Godot" Version="0.9.1" />
|
||||
<PackageReference Include="Chickensoft.LogicBlocks" Version="6.1.1" />
|
||||
<PackageReference Include="Chickensoft.LogicBlocks.Auto" Version="6.1.1" />
|
||||
<PackageReference Include="Chickensoft.LogicBlocks.DiagramGenerator" Version="6.1.1" PrivateAssets="all" OutputItemType="analyzer" />
|
||||
<PackageReference Include="Chickensoft.UMLGenerator" Version="1.3.1" />
|
||||
<PackageReference Include="Chickensoft.Sync" Version="2.4.1" />
|
||||
<PackageReference Include="TrimKit.VirtualFileSystem" Version="1.8.1" />
|
||||
<PackageReference Include="Utf8Json" Version="1.3.7" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<Compile Remove="/mods" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup Condition="'$(RunTests)' == 'true'">
|
||||
<!-- Test dependencies go here! -->
|
||||
<!-- Dependencies added here will not be included in release builds. -->
|
||||
<PackageReference Include="Chickensoft.GoDotTest" Version="2.0.27" />
|
||||
<PackageReference Include="Chickensoft.GoDotTest" Version="2.0.39" />
|
||||
<!-- Used to drive test scenes when testing visual code -->
|
||||
<PackageReference Include="Chickensoft.GodotTestDriver" Version="3.1.56" />
|
||||
<PackageReference Include="Chickensoft.GodotTestDriver" Version="3.1.72" />
|
||||
<!-- Bring your own assertion library for tests! -->
|
||||
<!-- We're using Shouldly for this example, but you can use anything. -->
|
||||
<PackageReference Include="Shouldly" Version="4.3.0" />
|
||||
|
||||
8
GlobalSuppressions.cs
Normal file
8
GlobalSuppressions.cs
Normal file
@@ -0,0 +1,8 @@
|
||||
// This file is used by Code Analysis to maintain SuppressMessage
|
||||
// attributes that are applied to this project.
|
||||
// Project-level suppressions either have no target or are given
|
||||
// a specific target and scoped to a namespace, type, member, etc.
|
||||
|
||||
using System.Diagnostics.CodeAnalysis;
|
||||
|
||||
[assembly: SuppressMessage("Style", "IDE0065:Misplaced using directive", Justification = "<Pending>")]
|
||||
1
GlobalSuppressions.cs.uid
Normal file
1
GlobalSuppressions.cs.uid
Normal file
@@ -0,0 +1 @@
|
||||
uid://oaa3naabb0bi
|
||||
187
RTSCamera.cs
Executable file
187
RTSCamera.cs
Executable file
@@ -0,0 +1,187 @@
|
||||
using Godot;
|
||||
using System;
|
||||
using SJK.Math;
|
||||
using Chickensoft.SaveFileBuilder;
|
||||
using Chickensoft.Introspection;
|
||||
using Chickensoft.Serialization;
|
||||
|
||||
|
||||
[Meta, Id("save_data")]
|
||||
public partial class SaveData
|
||||
{
|
||||
[Save("player_transform")]
|
||||
public Transform3D Transform3D {get;set;}
|
||||
[Save("player_camera")]
|
||||
public Transform3D CameraTransform {get;set;}
|
||||
}
|
||||
public partial class RTSCamera : Node3D, ISaveable<SaveData>
|
||||
{
|
||||
[Export]
|
||||
public Camera3D camera;
|
||||
Vector3 positionStartPos;
|
||||
Vector3 positionOffsetPos;
|
||||
Vector3 lastTransalation;
|
||||
|
||||
Vector3 cameraTargetOffset;
|
||||
Action Update_CurrentFunc;
|
||||
|
||||
//Generic bookkeeping variables;
|
||||
Vector2 LastMousePostition;
|
||||
//Camera Dragging Bookkeeping variables
|
||||
Vector3 LastMouseGroundPlanePositon;
|
||||
//Camera Rotating Bookkeeping variables
|
||||
float CameraRotateSpeed = .5f;
|
||||
bool isDraging;
|
||||
public override void _Ready()
|
||||
{
|
||||
Update_CurrentFunc = Update_DetectModeStart;
|
||||
}
|
||||
void Update_DetectModeStart()
|
||||
{
|
||||
if (Input.IsActionJustPressed("move_mouse"))
|
||||
{
|
||||
//Mouse Left went Down
|
||||
//Does nothing basicly
|
||||
}
|
||||
else if (Input.IsActionPressed("move_mouse") && GetViewport().GetMousePosition() != LastMousePostition)
|
||||
{
|
||||
LastMouseGroundPlanePositon = camera.MouseToGroundPlane() ?? Vector3.Zero;
|
||||
Update_CurrentFunc = Update_CameraDrag;
|
||||
Update_CurrentFunc();
|
||||
// GD.Print("1");
|
||||
}
|
||||
else if (Input.IsActionPressed("camera_rotate") && GetViewport().GetMousePosition() != LastMousePostition)
|
||||
{
|
||||
LastMouseGroundPlanePositon = camera.MouseToGroundPlane() ?? Vector3.Zero;
|
||||
Update_CurrentFunc = Update_RotateCamera;
|
||||
Update_CurrentFunc();
|
||||
// GD.Print("2");
|
||||
}
|
||||
|
||||
}
|
||||
public override void _Process(double delta)
|
||||
{
|
||||
if (Input.IsActionJustPressed("ui_cancel"))
|
||||
{
|
||||
CancelUpdateFunc();
|
||||
}
|
||||
// GD.PrintS(positionStartPos,positionOffsetPos,lastTransalation,LastMouseGroundPlanePositon,LastMousePostition,cameraTargetOffset,scrollAmount);
|
||||
Update_CurrentFunc();
|
||||
Update_CameraScroll((float)delta);
|
||||
LastMousePostition = GetViewport().GetMousePosition();
|
||||
camera.Fov = Mathf.Lerp(camera.Fov, Mathf.Lerp(90, 50, (camera.Position.Y - 5) / (80 - 5)), (float)delta * 10);
|
||||
}
|
||||
void CancelUpdateFunc()
|
||||
{
|
||||
Update_CurrentFunc = Update_DetectModeStart;
|
||||
}
|
||||
void Update_CameraDrag()
|
||||
{
|
||||
if (Input.IsActionJustReleased("move_mouse"))
|
||||
{
|
||||
CancelUpdateFunc();
|
||||
}
|
||||
|
||||
var hitpos = camera.MouseToGroundPlane() ?? Vector3.Zero;
|
||||
|
||||
|
||||
var diff = LastMouseGroundPlanePositon - hitpos;
|
||||
|
||||
GlobalTranslate(diff);
|
||||
LastMouseGroundPlanePositon = hitpos = camera.MouseToGroundPlane() ?? Vector3.Zero;
|
||||
|
||||
}
|
||||
float scrollAmount;
|
||||
public override void _UnhandledInput(InputEvent @event)
|
||||
{
|
||||
scrollAmount += Input.IsActionJustReleased("mouse_scroll_up") ? 1 : 0;
|
||||
scrollAmount += Input.IsActionJustReleased("mouse_scroll_down") ? -1 : 0;
|
||||
if (@event is InputEventKey eventKey && eventKey.Keycode == Key.F)
|
||||
{
|
||||
GlobalPosition = Vector3.Zero;
|
||||
camera.Position = Vector3.Zero;
|
||||
CancelUpdateFunc();
|
||||
}
|
||||
}
|
||||
void Update_CameraScroll(float delta)
|
||||
{
|
||||
|
||||
// Zoom to scrollwheel
|
||||
// float scrollAmount = Input.IsActionJustReleased("mouse_scroll_up")?1:0;
|
||||
// scrollAmount += Input.IsActionJustReleased( "mouse_scroll_down")?-1:0;
|
||||
// GD.Print(scrollAmount);
|
||||
float minHeight = 2;
|
||||
float maxHeight = 30;
|
||||
// Move camera towards hitPos
|
||||
// var hitPos = camera.MouseToGroundPlane();
|
||||
// Vector3 dir = hitPos - camera.Translation;
|
||||
var dir = -camera.GlobalTransform.Basis.Z * .5f;
|
||||
|
||||
var p = camera.Position;
|
||||
|
||||
// Stop zooming out at a certain distance.
|
||||
// TODO: Maybe you should still slide around at 20 zoom?
|
||||
if (scrollAmount > 0 || p.Y < (maxHeight - 0.1f))
|
||||
{
|
||||
cameraTargetOffset += dir * scrollAmount;
|
||||
}
|
||||
// DebugDraw3D.DrawArrow(Vector3.Zero,cameraTargetOffset);
|
||||
Vector3 lastCameraPosition = camera.Position;
|
||||
camera.Position = camera.Position.Lerp(camera.Position + cameraTargetOffset, delta);
|
||||
cameraTargetOffset -= camera.Position - lastCameraPosition;
|
||||
|
||||
p = camera.Position;
|
||||
if (p.Y < minHeight)
|
||||
{
|
||||
p.Y = minHeight;
|
||||
}
|
||||
if (p.Y > maxHeight)
|
||||
{
|
||||
p.Y = maxHeight;
|
||||
}
|
||||
|
||||
camera.Position = p;
|
||||
// Change camera angle
|
||||
camera.RotationDegrees = new Vector3(
|
||||
Mathf.Lerp(-50, -75, camera.Position.Y / maxHeight),
|
||||
camera.RotationDegrees.Y,
|
||||
camera.RotationDegrees.Z
|
||||
);
|
||||
scrollAmount = 0;
|
||||
}
|
||||
void Update_RotateCamera()
|
||||
{
|
||||
if (!Input.IsActionPressed("move_mouse"))
|
||||
{
|
||||
CancelUpdateFunc();
|
||||
}
|
||||
Vector2 hitpos = GetViewport().GetMousePosition();
|
||||
|
||||
Vector2 diff = LastMousePostition - hitpos;
|
||||
camera.RotationDegrees = new Vector3(
|
||||
camera.RotationDegrees.X,
|
||||
camera.RotationDegrees.Y + (diff.X * CameraRotateSpeed),
|
||||
camera.RotationDegrees.Z
|
||||
);
|
||||
LastMousePostition = hitpos = GetViewport().GetMousePosition();
|
||||
}
|
||||
|
||||
public SaveData Save() => new(){Transform3D = Transform, CameraTransform = camera.Transform};
|
||||
public void Load(in SaveData data)
|
||||
{
|
||||
Transform = data.Transform3D;
|
||||
camera.Transform = data.CameraTransform;
|
||||
}
|
||||
}
|
||||
|
||||
public static partial class SJKMath
|
||||
{
|
||||
public static Vector3? MouseToPlane(this Plane plane, Camera3D viewCamera, Vector2? mousePos = null)
|
||||
{
|
||||
var from = viewCamera.ProjectRayOrigin( mousePos ?? viewCamera.GetViewport().GetMousePosition());
|
||||
var normal = viewCamera.ProjectRayNormal(mousePos ?? viewCamera.GetViewport().GetMousePosition());
|
||||
return plane.IntersectsRay(from, normal);
|
||||
}
|
||||
public static Vector3? MouseToGroundPlane(this Camera3D viewCamera, Vector2? mousePos = null, float offset = 0) =>
|
||||
(Plane.PlaneXZ with { D = offset }).MouseToPlane(viewCamera, mousePos);
|
||||
}
|
||||
1
RTSCamera.cs.uid
Normal file
1
RTSCamera.cs.uid
Normal file
@@ -0,0 +1 @@
|
||||
uid://bdvwvdqrbks7c
|
||||
14
addons.jsonc
14
addons.jsonc
@@ -20,7 +20,19 @@
|
||||
"url": "https://github.com/DmitriySalnikov/godot_debug_draw_3d/releases/download/1.7.3/debug-draw-3d_1.7.3.zip/",
|
||||
"source": "zip", // optional — this is the default
|
||||
// "checkout": "master", // optional — this is the default
|
||||
// "subfolder": "addons/debug_draw_3d" // optional — defaults to "/"
|
||||
// "subfolder": "" // optional — defaults to "/"
|
||||
},
|
||||
"controller_icons": {
|
||||
"url": "https://github.com/jembawls/controller_icons_csharp/releases/download/v3.1.6/controller_icons_csharp-3.1.6.zip",
|
||||
"source": "zip", // optional — this is the default
|
||||
// "checkout": "master", // optional — this is the default
|
||||
"subfolder": "controller_icons_csharp-3.1.6/addons/controller_icons/" // optional — defaults to "/"
|
||||
},
|
||||
// "editor_theme_explorer": {
|
||||
// "url": "https://github.com/YuriSizov/godot-editor-theme-explorer/releases/godot-editor-theme-explorer-2.1.1.zip",
|
||||
// "source": "zip", // optional — this is the default
|
||||
// // "checkout": "master", // optional — this is the default
|
||||
// // "subfolder": "controller_icons_csharp-3.1.6/addons/controller_icons/" // optional — defaults to "/"
|
||||
// },
|
||||
}
|
||||
}
|
||||
|
||||
2
assets
2
assets
Submodule assets updated: b5ad429c59...235643b054
49
export_presets.cfg
Normal file
49
export_presets.cfg
Normal file
@@ -0,0 +1,49 @@
|
||||
[preset.0]
|
||||
|
||||
name="Linux"
|
||||
platform="Linux"
|
||||
runnable=true
|
||||
dedicated_server=false
|
||||
custom_features=""
|
||||
export_filter="all_resources"
|
||||
include_filter=""
|
||||
exclude_filter="/mods"
|
||||
export_path="../../Builds/ChickenGameTest.x86_64"
|
||||
patches=PackedStringArray()
|
||||
patch_delta_encoding=false
|
||||
patch_delta_compression_level_zstd=19
|
||||
patch_delta_min_reduction=0.1
|
||||
patch_delta_include_filters="*"
|
||||
patch_delta_exclude_filters=""
|
||||
encryption_include_filters=""
|
||||
encryption_exclude_filters=""
|
||||
seed=0
|
||||
encrypt_pck=false
|
||||
encrypt_directory=false
|
||||
script_export_mode=2
|
||||
|
||||
[preset.0.options]
|
||||
|
||||
custom_template/debug=""
|
||||
custom_template/release=""
|
||||
debug/export_console_wrapper=1
|
||||
binary_format/embed_pck=false
|
||||
texture_format/s3tc_bptc=true
|
||||
texture_format/etc2_astc=false
|
||||
shader_baker/enabled=false
|
||||
binary_format/architecture="x86_64"
|
||||
ssh_remote_deploy/enabled=false
|
||||
ssh_remote_deploy/host="user@host_ip"
|
||||
ssh_remote_deploy/port="22"
|
||||
ssh_remote_deploy/extra_args_ssh=""
|
||||
ssh_remote_deploy/extra_args_scp=""
|
||||
ssh_remote_deploy/run_script="#!/usr/bin/env bash
|
||||
export DISPLAY=:0
|
||||
unzip -o -q \"{temp_dir}/{archive_name}\" -d \"{temp_dir}\"
|
||||
\"{temp_dir}/{exe_name}\" {cmd_args}"
|
||||
ssh_remote_deploy/cleanup_script="#!/usr/bin/env bash
|
||||
pkill -x -f \"{temp_dir}/{exe_name} {cmd_args}\"
|
||||
rm -rf \"{temp_dir}\""
|
||||
dotnet/include_scripts_content=false
|
||||
dotnet/include_debug_symbols=true
|
||||
dotnet/embed_build_outputs=false
|
||||
0
mods/.gdignore
Normal file
0
mods/.gdignore
Normal file
1
mods/modA/mod.json
Normal file
1
mods/modA/mod.json
Normal file
@@ -0,0 +1 @@
|
||||
"Hello"
|
||||
1
mods/modB/mod.json
Normal file
1
mods/modB/mod.json
Normal file
@@ -0,0 +1 @@
|
||||
"World"
|
||||
13
mods/raw_onion.yaml
Normal file
13
mods/raw_onion.yaml
Normal file
@@ -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
|
||||
13
mods/raw_potato.yaml
Normal file
13
mods/raw_potato.yaml
Normal file
@@ -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.
|
||||
15
mods/raw_red_onion.yaml
Normal file
15
mods/raw_red_onion.yaml
Normal file
@@ -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"]
|
||||
@@ -19,6 +19,10 @@ run/main_scene="res://src/Main.tscn"
|
||||
config/features=PackedStringArray("4.6", "C#", "Mobile")
|
||||
config/icon="res://icon.png"
|
||||
|
||||
[autoload]
|
||||
|
||||
ControllerIcons="*uid://cvqaxgmq6by3f"
|
||||
|
||||
[debug_draw_3d]
|
||||
|
||||
settings/addon_root_folder="res://addons/debug_draw_3d"
|
||||
@@ -41,7 +45,7 @@ naming/scene_name_casing=1
|
||||
|
||||
[editor_plugins]
|
||||
|
||||
enabled=PackedStringArray("res://addons/imrp/plugin.cfg")
|
||||
enabled=PackedStringArray("res://addons/controller_icons/plugin.cfg", "res://addons/explore-editor-theme/plugin.cfg", "res://addons/imrp/plugin.cfg")
|
||||
|
||||
[gui]
|
||||
|
||||
@@ -49,6 +53,75 @@ theme/default_font_multichannel_signed_distance_field=true
|
||||
theme/default_font_generate_mipmaps=true
|
||||
theme/default_theme_scale=2.0
|
||||
|
||||
[input]
|
||||
|
||||
move_forward={
|
||||
"deadzone": 0.2,
|
||||
"events": [Object(InputEventKey,"resource_local_to_scene":false,"resource_name":"","device":-1,"window_id":0,"alt_pressed":false,"shift_pressed":false,"ctrl_pressed":false,"meta_pressed":false,"pressed":false,"keycode":0,"physical_keycode":87,"key_label":0,"unicode":119,"location":0,"echo":false,"script":null)
|
||||
, Object(InputEventJoypadMotion,"resource_local_to_scene":false,"resource_name":"","device":-1,"axis":1,"axis_value":-1.0,"script":null)
|
||||
]
|
||||
}
|
||||
move_back={
|
||||
"deadzone": 0.2,
|
||||
"events": [Object(InputEventKey,"resource_local_to_scene":false,"resource_name":"","device":-1,"window_id":0,"alt_pressed":false,"shift_pressed":false,"ctrl_pressed":false,"meta_pressed":false,"pressed":false,"keycode":0,"physical_keycode":83,"key_label":0,"unicode":115,"location":0,"echo":false,"script":null)
|
||||
]
|
||||
}
|
||||
move_left={
|
||||
"deadzone": 0.2,
|
||||
"events": [Object(InputEventKey,"resource_local_to_scene":false,"resource_name":"","device":-1,"window_id":0,"alt_pressed":false,"shift_pressed":false,"ctrl_pressed":false,"meta_pressed":false,"pressed":false,"keycode":0,"physical_keycode":65,"key_label":0,"unicode":97,"location":0,"echo":false,"script":null)
|
||||
]
|
||||
}
|
||||
move_right={
|
||||
"deadzone": 0.2,
|
||||
"events": [Object(InputEventKey,"resource_local_to_scene":false,"resource_name":"","device":-1,"window_id":0,"alt_pressed":false,"shift_pressed":false,"ctrl_pressed":false,"meta_pressed":false,"pressed":false,"keycode":0,"physical_keycode":68,"key_label":0,"unicode":100,"location":0,"echo":false,"script":null)
|
||||
]
|
||||
}
|
||||
rotate_left={
|
||||
"deadzone": 0.2,
|
||||
"events": [Object(InputEventKey,"resource_local_to_scene":false,"resource_name":"","device":-1,"window_id":0,"alt_pressed":false,"shift_pressed":false,"ctrl_pressed":false,"meta_pressed":false,"pressed":false,"keycode":0,"physical_keycode":81,"key_label":0,"unicode":113,"location":0,"echo":false,"script":null)
|
||||
]
|
||||
}
|
||||
rotate_right={
|
||||
"deadzone": 0.2,
|
||||
"events": [Object(InputEventKey,"resource_local_to_scene":false,"resource_name":"","device":-1,"window_id":0,"alt_pressed":false,"shift_pressed":false,"ctrl_pressed":false,"meta_pressed":false,"pressed":false,"keycode":0,"physical_keycode":69,"key_label":0,"unicode":101,"location":0,"echo":false,"script":null)
|
||||
]
|
||||
}
|
||||
camera_up={
|
||||
"deadzone": 0.2,
|
||||
"events": [Object(InputEventKey,"resource_local_to_scene":false,"resource_name":"","device":-1,"window_id":0,"alt_pressed":false,"shift_pressed":false,"ctrl_pressed":false,"meta_pressed":false,"pressed":false,"keycode":0,"physical_keycode":32,"key_label":0,"unicode":32,"location":0,"echo":false,"script":null)
|
||||
]
|
||||
}
|
||||
camera_down={
|
||||
"deadzone": 0.2,
|
||||
"events": [Object(InputEventKey,"resource_local_to_scene":false,"resource_name":"","device":-1,"window_id":0,"alt_pressed":false,"shift_pressed":false,"ctrl_pressed":false,"meta_pressed":false,"pressed":false,"keycode":0,"physical_keycode":4194325,"key_label":0,"unicode":0,"location":0,"echo":false,"script":null)
|
||||
]
|
||||
}
|
||||
place={
|
||||
"deadzone": 0.2,
|
||||
"events": [Object(InputEventMouseButton,"resource_local_to_scene":false,"resource_name":"","device":-1,"window_id":0,"alt_pressed":false,"shift_pressed":false,"ctrl_pressed":false,"meta_pressed":false,"button_mask":1,"position":Vector2(97, 9),"global_position":Vector2(106, 57),"factor":1.0,"button_index":1,"canceled":false,"pressed":true,"double_click":false,"script":null)
|
||||
]
|
||||
}
|
||||
mouse_scroll_up={
|
||||
"deadzone": 0.2,
|
||||
"events": [Object(InputEventMouseButton,"resource_local_to_scene":false,"resource_name":"","device":-1,"window_id":0,"alt_pressed":false,"shift_pressed":false,"ctrl_pressed":false,"meta_pressed":false,"button_mask":0,"position":Vector2(0, 0),"global_position":Vector2(0, 0),"factor":1.0,"button_index":4,"canceled":false,"pressed":false,"double_click":false,"script":null)
|
||||
]
|
||||
}
|
||||
mouse_scroll_down={
|
||||
"deadzone": 0.2,
|
||||
"events": [Object(InputEventMouseButton,"resource_local_to_scene":false,"resource_name":"","device":-1,"window_id":0,"alt_pressed":false,"shift_pressed":false,"ctrl_pressed":false,"meta_pressed":false,"button_mask":0,"position":Vector2(0, 0),"global_position":Vector2(0, 0),"factor":1.0,"button_index":5,"canceled":false,"pressed":false,"double_click":false,"script":null)
|
||||
]
|
||||
}
|
||||
move_mouse={
|
||||
"deadzone": 0.2,
|
||||
"events": [Object(InputEventMouseButton,"resource_local_to_scene":false,"resource_name":"","device":-1,"window_id":0,"alt_pressed":false,"shift_pressed":false,"ctrl_pressed":false,"meta_pressed":false,"button_mask":1,"position":Vector2(88, 14),"global_position":Vector2(97, 62),"factor":1.0,"button_index":1,"canceled":false,"pressed":true,"double_click":false,"script":null)
|
||||
]
|
||||
}
|
||||
camera_rotate={
|
||||
"deadzone": 0.2,
|
||||
"events": [Object(InputEventMouseButton,"resource_local_to_scene":false,"resource_name":"","device":-1,"window_id":0,"alt_pressed":false,"shift_pressed":false,"ctrl_pressed":false,"meta_pressed":false,"button_mask":2,"position":Vector2(197, 20),"global_position":Vector2(206, 68),"factor":1.0,"button_index":2,"canceled":false,"pressed":true,"double_click":false,"script":null)
|
||||
]
|
||||
}
|
||||
|
||||
[rendering]
|
||||
|
||||
renderer/rendering_method="mobile"
|
||||
|
||||
51
src/Api/IFoodFactoryApi.cs
Normal file
51
src/Api/IFoodFactoryApi.cs
Normal file
@@ -0,0 +1,51 @@
|
||||
namespace FoodFactory;
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using FoodFactory.Blueprints;
|
||||
using FoodFactory.Conveyors;
|
||||
using FoodFactory.Recipes;
|
||||
using FoodFactory.Voxel;
|
||||
|
||||
public interface IFoodFactoryApi
|
||||
{
|
||||
T? GetApi<T>() where T : class;
|
||||
IRecipes Recipes { get; }
|
||||
IBlueprintManger BlueprintManger { get; }
|
||||
ITickManger TickManger { get; }
|
||||
IItemRenderer ItemRenderer { get; }
|
||||
IVoxelGridRegistry GridRegistry { get; }
|
||||
}
|
||||
public interface ITickManger
|
||||
{
|
||||
delegate void Tick(TickArgs args);
|
||||
event Tick GameTick;
|
||||
}
|
||||
public record struct TickArgs(int Tick, double Delta);
|
||||
public class TickManger : ITickManger
|
||||
{
|
||||
public event ITickManger.Tick? GameTick;
|
||||
public void BroadCast(TickArgs args) => GameTick?.Invoke(args);
|
||||
}
|
||||
public class FoodFactoryApi : IFoodFactoryApi
|
||||
{
|
||||
public required IRecipes Recipes { get; set; }
|
||||
|
||||
public required IBlueprintManger BlueprintManger { get; set; }
|
||||
|
||||
public required ITickManger TickManger { get; set; }
|
||||
|
||||
public required IItemRenderer ItemRenderer { get; set; }
|
||||
|
||||
public required IVoxelGridRegistry GridRegistry { get; set; }
|
||||
|
||||
private readonly Dictionary<Type, object> _otherApi = [];
|
||||
public T? GetApi<T>() where T : class
|
||||
{
|
||||
if (_otherApi.TryGetValue(typeof(T), out var api))
|
||||
{
|
||||
return api as T;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
}
|
||||
1
src/Api/IFoodFactoryApi.cs.uid
Normal file
1
src/Api/IFoodFactoryApi.cs.uid
Normal file
@@ -0,0 +1 @@
|
||||
uid://dbmq2c5nn8r1q
|
||||
477
src/App/App.cs
Normal file
477
src/App/App.cs
Normal file
@@ -0,0 +1,477 @@
|
||||
namespace FoodFactory;
|
||||
|
||||
using Godot;
|
||||
using Chickensoft.AutoInject;
|
||||
using Chickensoft.Introspection;
|
||||
using Chickensoft.GodotNodeInterfaces;
|
||||
using Chickensoft.LogicBlocks.Auto;
|
||||
using Chickensoft.LogicBlocks;
|
||||
using System;
|
||||
using System.Threading.Tasks;
|
||||
using Chickensoft.UMLGenerator;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using FoodFactory.Modding;
|
||||
using System.IO.Abstractions;
|
||||
using Chickensoft.Sync.Primitives;
|
||||
using Chickensoft.SaveFileBuilder;
|
||||
using System.IO;
|
||||
using System.Text.Json;
|
||||
using Chickensoft.Serialization;
|
||||
using Chickensoft.Collections;
|
||||
using Chickensoft.Serialization.Godot;
|
||||
using SJK.Functional;
|
||||
|
||||
// using Shouldly;
|
||||
|
||||
public interface IApp : ICanvasLayer, IProvide<IAppRepo>, IProvide<IOptionConfig>, IProvide<ISaveService>;
|
||||
[Meta(typeof(IAutoNode))]
|
||||
[ClassDiagram(UseVSCodePaths = true)]
|
||||
public partial class App : CanvasLayer, IApp
|
||||
{
|
||||
public const string GAME_PATH = "res://src/Game/Game.tscn";
|
||||
public override void _Notification(int what) => this.Notify(what);
|
||||
|
||||
[Node] public ISubViewport GameViewPort { get; private set; } = default!;
|
||||
[Node] public Menu.IMainMenu MainMenu {get;set;} = default!;
|
||||
[Node] public ICreateGameMenu CreateGameMenu {get;set;} = default!;
|
||||
[Node] public ILoadingGameMenu LoadingGameMenu {get;set;} = default!;
|
||||
[Node] public IOptionsMenu OptionsMenu {get;set;} = default!;
|
||||
[Node] public IModMenu ModsMenu {get;set;} = default!;
|
||||
|
||||
public IGame Game { get; set; } = default!;
|
||||
|
||||
public IAppRepo AppRepo { get; set; } = default!;
|
||||
public IOptionConfig OptionConfig { get; set; } = default!;
|
||||
public IAppLogic AppLogic { get; set; } = default!;
|
||||
public LogicBlock.Binding AppBinding { get; set; } = default!;
|
||||
IAppRepo IProvide<IAppRepo>.Value() => AppRepo;
|
||||
IOptionConfig IProvide<IOptionConfig>.Value() => OptionConfig;
|
||||
private AutoChannel.Binding _binding;
|
||||
private static JsonSerializerOptions _serializerOptions = new JsonSerializerOptions()
|
||||
{
|
||||
Converters = {
|
||||
new SerializableTypeConverter(new Blackboard())
|
||||
},
|
||||
TypeInfoResolver = new SerializableTypeResolver(),
|
||||
WriteIndented = true
|
||||
};
|
||||
public ISaveService SaveService { get; set; } = new SaveService(){
|
||||
FilePath =Path.Join(OS.GetUserDataDir(), "saves", "game.json.gz"),
|
||||
CurrentSave =
|
||||
Chickensoft.SaveFileBuilder.SaveFile.CreateGZipJsonFile(
|
||||
Path.Join(OS.GetUserDataDir(), "saves", "game.json.gz"),
|
||||
// Create a standard JsonSerializerOptions with our introspective type
|
||||
// resolver and the logic blocks converter.
|
||||
_serializerOptions
|
||||
)
|
||||
};
|
||||
private Dictionary<IOptionKey,Action<Variant>> _settingsBind = [];
|
||||
public void BindSetting<[MustBeVariant] T>(IOptionKey<T> option, Action<T> action) => _settingsBind.Add(option, variant => action(variant.As<T>()));
|
||||
|
||||
public void Initialize()
|
||||
{
|
||||
GodotSerialization.Setup();
|
||||
var fileSystem =new FileSystem();
|
||||
AppRepo = new AppRepo()
|
||||
{
|
||||
ModManger = new ModManger()
|
||||
{
|
||||
AllMods = new AllMods("/home/ronnie/Documents/Godot/Projects/ChickenGameTest/mods", fileSystem),
|
||||
Profiles = new ModProfiles()
|
||||
}
|
||||
};
|
||||
AppRepo.ModManger.Profiles.Add(("base", [.. AppRepo.ModManger.AllMods.GetMods()]));
|
||||
AppRepo.ModManger.Profiles.Add(("test", []));
|
||||
// GD.Print(string.Join(',', AppRepo.ModManger.AllMods.GetMods()));
|
||||
AppLogic = new AppLogic();
|
||||
AppLogic.Set(AppRepo);
|
||||
AppLogic.Set(new AppLogic.Data());
|
||||
|
||||
OptionConfig = new TestConfig();
|
||||
((TestConfig)OptionConfig).OnSettingChanged += AppRepo.OnSettingChanged;
|
||||
BindSetting(OptionConfig.SettingKeys.Video.RenderQuality, value => GameViewPort.Scaling3DScale = value);
|
||||
BindSetting(OptionConfig.SettingKeys.Video.FullScreen, value => DisplayServer.WindowSetMode(value ? DisplayServer.WindowMode.Fullscreen : DisplayServer.WindowMode.Windowed));
|
||||
_binding = AppRepo.AutoChannel.Bind();
|
||||
_binding.On<IAppRepo.GameSettingChanged>((in setting) =>
|
||||
{
|
||||
GD.Print(setting);
|
||||
if (_settingsBind.GetValue(setting.Setting).HasValue(out var action)){
|
||||
action(setting.Value);
|
||||
}
|
||||
return;
|
||||
if (setting.Setting is OptionKey<bool> key && key.Key == "FullScreen")
|
||||
{
|
||||
DisplayServer.WindowSetMode(setting.Value.As<bool>() ? DisplayServer.WindowMode.Fullscreen : DisplayServer.WindowMode.Windowed );
|
||||
}
|
||||
if (setting.Setting is OptionKey<float> key2 && key2.Key == "RenderQuality")
|
||||
{
|
||||
GameViewPort.Scaling3DScale = setting.Value.As<float>() / 100f;
|
||||
}
|
||||
});
|
||||
// _binding.On((in IAppRepo.GameExiting exiting) =>
|
||||
// {
|
||||
// AppLogic.Input(new AppLogicState.Input.EndGame(AppLogic.Get<AppLogic.Data>().ExitGameReason));
|
||||
// });
|
||||
MainMenu.NewGame += OnNewGame;
|
||||
MainMenu.LastGame += () =>
|
||||
{
|
||||
var path = fileSystem.File.ReadAllText(Path.Combine(OS.GetUserDataDir(), "lastGame.txt"));
|
||||
AppLogic.Input(new AppLogicState.Input.LoadGame());
|
||||
OnLoadSaveGame(path);
|
||||
};
|
||||
MainMenu.LoadGame += OnLoadGame;
|
||||
MainMenu.Options += OnOptions;
|
||||
MainMenu.Exit += () => GetTree().Quit();
|
||||
|
||||
MainMenu.Mods += OnMods;
|
||||
|
||||
// =>
|
||||
// {
|
||||
// var vbox = new VBoxContainer();
|
||||
// vbox.SetAnchorsPreset(Control.LayoutPreset.Center);
|
||||
// foreach (var item in AppRepo.ModManger.AllMods.GetMods())
|
||||
// {
|
||||
// var b = new Button(){Text = item.ModName};
|
||||
// b.Pressed += () =>
|
||||
// {
|
||||
// var p = AppRepo.ModManger.Profiles;
|
||||
// if (p.Current.Contains(item))
|
||||
// {
|
||||
// p.Current.Remove(item);
|
||||
// }
|
||||
// else
|
||||
// {
|
||||
// p.Current.Add(item);
|
||||
// }
|
||||
// b.Text += "a";
|
||||
// };
|
||||
// vbox.AddChild(b);
|
||||
// }
|
||||
// MainMenu.AddChild(vbox);
|
||||
// };
|
||||
OptionsMenu.OnMenuExit += OnMenuExit;
|
||||
CreateGameMenu.Exit += OnMenuExit;
|
||||
CreateGameMenu.NewGame += OnNewGame;
|
||||
CreateGameMenu.LevelSelected += (index) => GD.Print("selected ",index);
|
||||
|
||||
LoadingGameMenu.Exit += OnMenuExit;
|
||||
LoadingGameMenu.LoadGame += index => OnLoadSaveGame(GetSaves()[index]);
|
||||
|
||||
MainMenu.SetLastGameVisible(fileSystem.File.Exists(Path.Combine(OS.GetUserDataDir(), "lastGame.txt")));
|
||||
|
||||
ModsMenu.ExitModMenu += OnMenuExit;
|
||||
|
||||
this.Provide();
|
||||
}
|
||||
|
||||
private void OnLastGame()
|
||||
{
|
||||
AppLogic.Input(new AppLogicState.Input.LoadGame());
|
||||
|
||||
AppLogic.Input(new AppLogicState.Input.LoadSave());
|
||||
}
|
||||
|
||||
private void OnMenuExit() => AppLogic.Input(new AppLogicState.Input.ExitMenu());
|
||||
private void OnNewGame() => AppLogic.Input(new AppLogicState.Input.NewGame());
|
||||
private void OnLoadGame() => AppLogic.Input(new AppLogicState.Input.LoadGame());
|
||||
private void OnLoadSaveGame(string path)
|
||||
{
|
||||
SaveService.FilePath = path;
|
||||
SaveService.CurrentSave = Chickensoft.SaveFileBuilder.SaveFile.CreateGZipJsonFile(
|
||||
path,
|
||||
_serializerOptions);
|
||||
// Game.LoadExistingGame(GetSaves()[arg]);
|
||||
GD.Print("save ", path, " loaded.");
|
||||
AppLogic.Input(new AppLogicState.Input.LoadSave());
|
||||
}
|
||||
|
||||
private void OnOptions() => AppLogic.Input(new AppLogicState.Input.Options());
|
||||
private void OnMods() => AppLogic.Input(new AppLogicState.Input.Mods());
|
||||
|
||||
public void OnReady()
|
||||
{
|
||||
AppBinding = AppLogic.Bind();
|
||||
AppBinding
|
||||
.OnOutput((in AppLogicState.Output.ShowMainMenu _) =>
|
||||
{
|
||||
HideMenus();
|
||||
MainMenu.Show();
|
||||
//Fade In from black
|
||||
})
|
||||
.OnOutput((in AppLogicState.Output.StartLoadingSaveFile save) =>
|
||||
{
|
||||
Game.SaveFileLoaded += OnSaveFileLoaded;
|
||||
Game.LoadGame();
|
||||
})
|
||||
.OnOutput((in AppLogicState.Output.RemoveExistingGame _) =>
|
||||
{
|
||||
GameViewPort.RemoveChildEx(Game);
|
||||
Game.QueueFree();
|
||||
Game = default!;
|
||||
})
|
||||
.OnOutput((in AppLogicState.Output.SetUpGameScene _) =>
|
||||
{
|
||||
Game = GD.Load<PackedScene>(GAME_PATH).Instantiate<Game>();
|
||||
Game.Visible = false;
|
||||
GameViewPort.AddChildEx(Game);
|
||||
})
|
||||
.OnOutput((in AppLogicState.Output.ShowGameCreationWindow _) =>
|
||||
{
|
||||
HideMenus();
|
||||
CreateGameMenu.Initialize([.. GetLevels()]);
|
||||
CreateGameMenu.Show();
|
||||
})
|
||||
.OnOutput((in AppLogicState.Output.ShowGameLoadingWindow _) =>
|
||||
{
|
||||
HideMenus();
|
||||
LoadingGameMenu.Initialize(GetSaves());
|
||||
LoadingGameMenu.Show();
|
||||
})
|
||||
.OnOutput((in AppLogicState.Output.ShowGame _) =>
|
||||
{
|
||||
HideMenus();
|
||||
Game.Show();
|
||||
})
|
||||
.OnOutput((in AppLogicState.Output.QuitGame _) =>
|
||||
{
|
||||
GetTree().CallDeferred(SceneTree.MethodName.Quit);
|
||||
})
|
||||
.OnOutput((in AppLogicState.Output.ShowOptionMenu _) =>
|
||||
{
|
||||
HideMenus();
|
||||
OptionsMenu.Show();
|
||||
})
|
||||
.OnOutput((in AppLogicState.Output.ShowModsMenu _) =>
|
||||
{
|
||||
HideMenus();
|
||||
ModsMenu.Show();
|
||||
})
|
||||
|
||||
;
|
||||
AppLogic.Start<AppLogicState.MainMenu>();
|
||||
}
|
||||
|
||||
private IReadOnlyList<string> GetSaves()
|
||||
{
|
||||
var fileSystem = new FileSystem();
|
||||
var saves = fileSystem.Directory.EnumerateFiles(Path.Join(OS.GetUserDataDir(), "saves"));
|
||||
return [.. saves];
|
||||
|
||||
}
|
||||
|
||||
private void OnSaveFileLoaded()
|
||||
{
|
||||
Game.SaveFileLoaded -= OnSaveFileLoaded;
|
||||
AppLogic.Input(new AppLogicState.Input.SaveFileLoaded());
|
||||
}
|
||||
|
||||
public void Setup()
|
||||
{
|
||||
|
||||
}
|
||||
public void OnExitTree()
|
||||
{
|
||||
AppLogic.Stop();
|
||||
AppLogic.Dispose();
|
||||
AppRepo.Dispose();
|
||||
MainMenu.NewGame -= OnNewGame;
|
||||
MainMenu.LoadGame -= OnLoadGame;
|
||||
MainMenu.Options -= OnOptions;
|
||||
MainMenu.Mods -= OnMods;
|
||||
OptionsMenu.OnMenuExit -= OnMenuExit;
|
||||
}
|
||||
|
||||
|
||||
public void OnResolved()
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
public void HideMenus()
|
||||
{
|
||||
// Splash.Hide();
|
||||
MainMenu.Hide();
|
||||
CreateGameMenu.Hide();
|
||||
LoadingGameMenu.Hide();
|
||||
OptionsMenu.Hide();
|
||||
ModsMenu.Hide();
|
||||
}
|
||||
|
||||
|
||||
public IEnumerable<LevelEntry> GetLevels()
|
||||
{
|
||||
yield return new LevelEntry("Level1", "res://assets/kenney_conveyor-kit/Sample.png");
|
||||
yield return new LevelEntry("Level2", "res://assets/KayKit_Restaurant_Bits_1.0_FREE/sample.png");
|
||||
yield return new LevelEntry("Level3", "res://assets/KayKit_Restaurant_Bits_1.0_FREE/contents.png");
|
||||
}
|
||||
|
||||
public ISaveService Value() => SaveService;
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
// public override void _Ready()//=> new Test().TestECS();
|
||||
// {
|
||||
// // TestButton = GetNode<Button>("%TestButton");
|
||||
// GD.Print(GameViewPort);
|
||||
// }
|
||||
|
||||
// // public void OnTestButtonPressed() => ButtonPresses++;
|
||||
|
||||
|
||||
|
||||
|
||||
// IGameLogic GameLogic = default!;
|
||||
// LogicBlock.Binding GameLogicBinding = default!;
|
||||
// Task task = default!;
|
||||
// public void OnResolved()
|
||||
// {
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
// // return;
|
||||
// GD.Print("GLHF");
|
||||
// GameLogic = new GameRootLogic();
|
||||
// GameLogicBinding = GameLogic.Bind();
|
||||
// GameLogicBinding
|
||||
// .OnOutput((in GameRootLogicState.Output.QuitConfirmation _) => {
|
||||
|
||||
|
||||
// var popup = new ConfirmationDialog();
|
||||
// popup.Confirmed += () => GetTree().Quit();
|
||||
// popup.Canceled += popup.QueueFree;
|
||||
// popup.DialogText = "You sure you want to Quit";
|
||||
// popup.OkButtonText = "Quit";
|
||||
// popup.CancelButtonText = "Return to Menu";
|
||||
// AddChild(popup);
|
||||
// popup.PopupCentered();
|
||||
|
||||
|
||||
// })
|
||||
// .OnInput((in GameRootLogicState.Input.RequestQuit _) =>
|
||||
// {
|
||||
|
||||
// }
|
||||
// );
|
||||
// // TestButton.ButtonUp += () => GameLogic.Input(new GameRootLogicState.Input.SaveRequested());
|
||||
// GameLogic.Start<GameRootLogicState.MainMenu>();
|
||||
// // GameLogic.Input(new GameLogicState.Input.Start());
|
||||
|
||||
|
||||
|
||||
|
||||
// MainMenu.NewGame += () => GD.Print("New");
|
||||
// MainMenu.LoadGame += () => GD.Print("Load");
|
||||
// MainMenu.Options += () => GD.Print("Option");
|
||||
// MainMenu.Mods += () => GD.Print("Mods");
|
||||
// MainMenu.Exit += () => GameLogic.Input(new GameRootLogicState.Input.RequestQuit());
|
||||
// }
|
||||
|
||||
}
|
||||
|
||||
// public interface IGameLogic : ILogicBlock
|
||||
// {
|
||||
|
||||
// }
|
||||
// [Meta]
|
||||
// public partial class GameRootLogic : AutoBlock, IGameLogic
|
||||
// {
|
||||
// public GameRootLogic()
|
||||
// {
|
||||
// Preallocate<GameRootLogicState>();
|
||||
|
||||
// }
|
||||
// }
|
||||
|
||||
|
||||
// [Meta, StateDiagram]
|
||||
// public abstract partial record GameRootLogicState : LogicBlockState
|
||||
// {
|
||||
// public static class Input
|
||||
// {
|
||||
// public readonly record struct Start;
|
||||
// public readonly record struct Playing;
|
||||
// public readonly record struct RequestQuit;
|
||||
// // public readonly record struct SaveRequested;
|
||||
// // public readonly record struct SaveCompleted;
|
||||
// }
|
||||
// public static class Output
|
||||
// {
|
||||
// // public readonly record struct StartSaving;
|
||||
// public readonly record struct ShowMainMenu;
|
||||
// public readonly record struct HideMainMenu;
|
||||
// public readonly record struct QuitConfirmation;
|
||||
// public readonly record struct Quit;
|
||||
// }
|
||||
// // [Meta]
|
||||
// // public partial record Paused : GameRootLogicState, IGet<Input.SaveRequested>
|
||||
// // {
|
||||
// // public Paused()
|
||||
// // {
|
||||
// // this.OnEnter(() =>
|
||||
// // {
|
||||
// // GD.Print("enter paluse");
|
||||
// // Output(new Output.ShowPauseMenu());
|
||||
// // });
|
||||
// // this.OnExit(()=>Output(new Output.HidePauseMenu()));
|
||||
// // }
|
||||
// // public Type On(in Input.SaveRequested input) => To<Saving>();
|
||||
// // }
|
||||
|
||||
// // [Meta]
|
||||
// // public partial record Saving : Paused, IGet<Input.SaveCompleted>
|
||||
// // {
|
||||
// // public Type On(in Input.SaveCompleted input) => To<MainMenu>();
|
||||
// // public Saving()
|
||||
// // {
|
||||
// // this.OnEnter(
|
||||
// // ()=>
|
||||
// // {
|
||||
// // GD.Print("enter save");
|
||||
// // Output(new Output.StartSaving());
|
||||
// // });
|
||||
// // // this.OnExit(()=> Output(new Output.HidePauseMenu()));
|
||||
|
||||
// // }
|
||||
// // }
|
||||
|
||||
// [Meta]
|
||||
// public partial record MainMenu : GameRootLogicState, IGet<Input.RequestQuit>
|
||||
// {
|
||||
// public Type On(in Input.RequestQuit input) => To<ProcessQuit>();
|
||||
// public MainMenu()
|
||||
// {
|
||||
|
||||
// this.OnEnter(
|
||||
// ()=>
|
||||
// {
|
||||
// GD.Print("enter menu");
|
||||
// // Output(new Output.StartSaving());
|
||||
// });
|
||||
// // this.OnExit(()=> Output(new Output.HidePauseMenu()));
|
||||
// }
|
||||
|
||||
// }
|
||||
// [Meta]
|
||||
// public partial record ProcessQuit : GameRootLogicState
|
||||
// {
|
||||
// public ProcessQuit()
|
||||
// {
|
||||
|
||||
// this.OnEnter(
|
||||
// ()=>
|
||||
// {
|
||||
// GD.Print("enter menu");
|
||||
// Output(new Output.QuitConfirmation());
|
||||
// });
|
||||
// // this.OnExit(()=> Output(new Output.HidePauseMenu()));
|
||||
// }
|
||||
|
||||
// }
|
||||
// }
|
||||
213
src/App/App.g.puml
Normal file
213
src/App/App.g.puml
Normal file
@@ -0,0 +1,213 @@
|
||||
@startuml
|
||||
|
||||
package App-Type [[vscode://file//home/ronnie/Documents/Godot/Projects/ChickenGameTest/src/App/App.cs]] {
|
||||
class App {
|
||||
[[vscode://file//home/ronnie/Documents/Godot/Projects/ChickenGameTest/src/App/App.cs ScriptFile]]
|
||||
[Properties]
|
||||
[[vscode://file//home/ronnie/Documents/Godot/Projects/ChickenGameTest/src/App/App.cs:47 AppBinding]]
|
||||
[[vscode://file//home/ronnie/Documents/Godot/Projects/ChickenGameTest/src/App/App.cs:46 AppLogic]] - [[vscode://file//home/ronnie/Documents/Godot/Projects/ChickenGameTest/src/App/AppLogic.cs Script]]
|
||||
[[vscode://file//home/ronnie/Documents/Godot/Projects/ChickenGameTest/src/App/App.cs:44 AppRepo]]
|
||||
[[vscode://file//home/ronnie/Documents/Godot/Projects/ChickenGameTest/src/App/App.cs:37 CreateGameMenu]]
|
||||
[[vscode://file//home/ronnie/Documents/Godot/Projects/ChickenGameTest/src/App/App.cs:42 Game]] - [[vscode://file//home/ronnie/Documents/Godot/Projects/ChickenGameTest/src/Game/Game.cs Script]]
|
||||
[[vscode://file//home/ronnie/Documents/Godot/Projects/ChickenGameTest/src/App/App.cs:35 GameViewPort]]
|
||||
[[vscode://file//home/ronnie/Documents/Godot/Projects/ChickenGameTest/src/App/App.cs:38 LoadingGameMenu]] - [[vscode://file//home/ronnie/Documents/Godot/Projects/ChickenGameTest/src/TitleMenu/LoadingGameMenu.cs Script]]
|
||||
[[vscode://file//home/ronnie/Documents/Godot/Projects/ChickenGameTest/src/App/App.cs:36 MainMenu]] - [[vscode://file//home/ronnie/Documents/Godot/Projects/ChickenGameTest/src/TitleMenu/MainMenu.cs Script]]
|
||||
[[vscode://file//home/ronnie/Documents/Godot/Projects/ChickenGameTest/src/App/App.cs:40 ModsMenu]] - [[vscode://file//home/ronnie/Documents/Godot/Projects/ChickenGameTest/src/ModMenu/ModMenu.cs Script]]
|
||||
[[vscode://file//home/ronnie/Documents/Godot/Projects/ChickenGameTest/src/App/App.cs:45 OptionConfig]]
|
||||
[[vscode://file//home/ronnie/Documents/Godot/Projects/ChickenGameTest/src/App/App.cs:39 OptionsMenu]]
|
||||
[[vscode://file//home/ronnie/Documents/Godot/Projects/ChickenGameTest/src/App/App.cs:59 SaveService]]
|
||||
--
|
||||
[Methods]
|
||||
[[vscode://file//home/ronnie/Documents/Godot/Projects/ChickenGameTest/src/App/App.cs:33 _Notification()]]
|
||||
[[vscode://file//home/ronnie/Documents/Godot/Projects/ChickenGameTest/src/App/App.cs:70 BindSetting()]]
|
||||
[[vscode://file//home/ronnie/Documents/Godot/Projects/ChickenGameTest/src/App/App.cs:302 GetLevels()]]
|
||||
[[vscode://file//home/ronnie/Documents/Godot/Projects/ChickenGameTest/src/App/App.cs:255 GetSaves()]]
|
||||
[[vscode://file//home/ronnie/Documents/Godot/Projects/ChickenGameTest/src/App/App.cs:291 HideMenus()]]
|
||||
[[vscode://file//home/ronnie/Documents/Godot/Projects/ChickenGameTest/src/App/App.cs:72 Initialize()]]
|
||||
[[vscode://file//home/ronnie/Documents/Godot/Projects/ChickenGameTest/src/App/App.cs:273 OnExitTree()]]
|
||||
[[vscode://file//home/ronnie/Documents/Godot/Projects/ChickenGameTest/src/App/App.cs:168 OnLastGame()]]
|
||||
[[vscode://file//home/ronnie/Documents/Godot/Projects/ChickenGameTest/src/App/App.cs:177 OnLoadGame()]]
|
||||
[[vscode://file//home/ronnie/Documents/Godot/Projects/ChickenGameTest/src/App/App.cs:178 OnLoadSaveGame()]]
|
||||
[[vscode://file//home/ronnie/Documents/Godot/Projects/ChickenGameTest/src/App/App.cs:175 OnMenuExit()]]
|
||||
[[vscode://file//home/ronnie/Documents/Godot/Projects/ChickenGameTest/src/App/App.cs:190 OnMods()]]
|
||||
[[vscode://file//home/ronnie/Documents/Godot/Projects/ChickenGameTest/src/App/App.cs:176 OnNewGame()]]
|
||||
[[vscode://file//home/ronnie/Documents/Godot/Projects/ChickenGameTest/src/App/App.cs:189 OnOptions()]]
|
||||
[[vscode://file//home/ronnie/Documents/Godot/Projects/ChickenGameTest/src/App/App.cs:192 OnReady()]]
|
||||
[[vscode://file//home/ronnie/Documents/Godot/Projects/ChickenGameTest/src/App/App.cs:286 OnResolved()]]
|
||||
[[vscode://file//home/ronnie/Documents/Godot/Projects/ChickenGameTest/src/App/App.cs:263 OnSaveFileLoaded()]]
|
||||
[[vscode://file//home/ronnie/Documents/Godot/Projects/ChickenGameTest/src/App/App.cs:269 Setup()]]
|
||||
[[vscode://file//home/ronnie/Documents/Godot/Projects/ChickenGameTest/src/App/App.cs:309 Value()]]
|
||||
}
|
||||
class AppLogic {
|
||||
[[vscode://file//home/ronnie/Documents/Godot/Projects/ChickenGameTest/src/App/AppLogic.cs ScriptFile]]
|
||||
[Constructors]
|
||||
[[vscode://file//home/ronnie/Documents/Godot/Projects/ChickenGameTest/src/App/AppLogic.cs:14 AppLogic]]()
|
||||
--
|
||||
[Methods]
|
||||
[[vscode://file//home/ronnie/Documents/Godot/Projects/ChickenGameTest/src/App/AppLogic.cs:19 OnStartSubscriptions()]]
|
||||
}
|
||||
package Game-Type [[vscode://file//home/ronnie/Documents/Godot/Projects/ChickenGameTest/src/Game/Game.cs]] {
|
||||
class Game {
|
||||
[[vscode://file//home/ronnie/Documents/Godot/Projects/ChickenGameTest/src/Game/Game.cs ScriptFile]]
|
||||
[Properties]
|
||||
[[vscode://file//home/ronnie/Documents/Godot/Projects/ChickenGameTest/src/Game/Game.cs:40 Environment]]
|
||||
[[vscode://file//home/ronnie/Documents/Godot/Projects/ChickenGameTest/src/Game/Game.cs:43 EquipmentTable]]
|
||||
[[vscode://file//home/ronnie/Documents/Godot/Projects/ChickenGameTest/src/Game/Game.cs:39 FileSystem]]
|
||||
[[vscode://file//home/ronnie/Documents/Godot/Projects/ChickenGameTest/src/Game/Game.cs:54 GameBinding]]
|
||||
[[vscode://file//home/ronnie/Documents/Godot/Projects/ChickenGameTest/src/Game/Game.cs:53 GameLogic]] - [[vscode://file//home/ronnie/Documents/Godot/Projects/ChickenGameTest/src/Game/GameLogicState.cs Script]]
|
||||
[[vscode://file//home/ronnie/Documents/Godot/Projects/ChickenGameTest/src/Game/Game.cs:48 OptionsMenu]]
|
||||
[[vscode://file//home/ronnie/Documents/Godot/Projects/ChickenGameTest/src/Game/Game.cs:47 PauseContainer]]
|
||||
[[vscode://file//home/ronnie/Documents/Godot/Projects/ChickenGameTest/src/Game/Game.cs:46 PauseMenu]] - [[vscode://file//home/ronnie/Documents/Godot/Projects/ChickenGameTest/src/PauseMenu/PauseMenu.cs Script]]
|
||||
[[vscode://file//home/ronnie/Documents/Godot/Projects/ChickenGameTest/src/Game/Game.cs:49 PlayerCamera]]
|
||||
[[vscode://file//home/ronnie/Documents/Godot/Projects/ChickenGameTest/src/Game/Game.cs:38 SaveFile]]
|
||||
[[vscode://file//home/ronnie/Documents/Godot/Projects/ChickenGameTest/src/Game/Game.cs:41 SaveFilePath]]
|
||||
--
|
||||
[Methods]
|
||||
[[vscode://file//home/ronnie/Documents/Godot/Projects/ChickenGameTest/src/Game/Game.cs:33 _Notification()]]
|
||||
[[vscode://file//home/ronnie/Documents/Godot/Projects/ChickenGameTest/src/Game/Game.cs:218 _UnhandledInput()]]
|
||||
[[vscode://file//home/ronnie/Documents/Godot/Projects/ChickenGameTest/src/Game/Game.cs:132 HideMenus()]]
|
||||
[[vscode://file//home/ronnie/Documents/Godot/Projects/ChickenGameTest/src/Game/Game.cs:71 Initialize()]]
|
||||
[[vscode://file//home/ronnie/Documents/Godot/Projects/ChickenGameTest/src/Game/Game.cs:232 Load()]]
|
||||
[[vscode://file//home/ronnie/Documents/Godot/Projects/ChickenGameTest/src/Game/Game.cs:233 LoadExistingGame()]]
|
||||
[[vscode://file//home/ronnie/Documents/Godot/Projects/ChickenGameTest/src/Game/Game.cs:125 LoadGame()]]
|
||||
[[vscode://file//home/ronnie/Documents/Godot/Projects/ChickenGameTest/src/Game/Game.cs:162 OnExitTree()]]
|
||||
[[vscode://file//home/ronnie/Documents/Godot/Projects/ChickenGameTest/src/Game/Game.cs:102 OnReady()]]
|
||||
[[vscode://file//home/ronnie/Documents/Godot/Projects/ChickenGameTest/src/Game/Game.cs:165 OnResolved()]]
|
||||
[[vscode://file//home/ronnie/Documents/Godot/Projects/ChickenGameTest/src/Game/Game.cs:81 OnSaveButton()]]
|
||||
[[vscode://file//home/ronnie/Documents/Godot/Projects/ChickenGameTest/src/Game/Game.cs:96 OpenOptions()]]
|
||||
[[vscode://file//home/ronnie/Documents/Godot/Projects/ChickenGameTest/src/Game/Game.cs:84 PauseButtonPressed()]]
|
||||
[[vscode://file//home/ronnie/Documents/Godot/Projects/ChickenGameTest/src/Game/Game.cs:83 QuitToDesktop()]]
|
||||
[[vscode://file//home/ronnie/Documents/Godot/Projects/ChickenGameTest/src/Game/Game.cs:82 QuitToMainMenu()]]
|
||||
[[vscode://file//home/ronnie/Documents/Godot/Projects/ChickenGameTest/src/Game/Game.cs:228 Save()]]
|
||||
[[vscode://file//home/ronnie/Documents/Godot/Projects/ChickenGameTest/src/Game/Game.cs:106 SaveGame()]]
|
||||
[[vscode://file//home/ronnie/Documents/Godot/Projects/ChickenGameTest/src/Game/Game.cs:118 SaveGame2()]]
|
||||
[[vscode://file//home/ronnie/Documents/Godot/Projects/ChickenGameTest/src/Game/Game.cs:138 Setup()]]
|
||||
}
|
||||
class GameLogicState {
|
||||
[[vscode://file//home/ronnie/Documents/Godot/Projects/ChickenGameTest/src/Game/GameLogicState.cs ScriptFile]]
|
||||
[Constructors]
|
||||
[[vscode://file//home/ronnie/Documents/Godot/Projects/ChickenGameTest/src/Game/GameLogicState.cs:15 GameLogic]]()
|
||||
}
|
||||
class PauseMenu {
|
||||
[[vscode://file//home/ronnie/Documents/Godot/Projects/ChickenGameTest/src/PauseMenu/PauseMenu.cs ScriptFile]]
|
||||
[Properties]
|
||||
[[vscode://file//home/ronnie/Documents/Godot/Projects/ChickenGameTest/src/PauseMenu/PauseMenu.cs:29 BackToGameButton]]
|
||||
[[vscode://file//home/ronnie/Documents/Godot/Projects/ChickenGameTest/src/PauseMenu/PauseMenu.cs:30 OptionsButton]]
|
||||
[[vscode://file//home/ronnie/Documents/Godot/Projects/ChickenGameTest/src/PauseMenu/PauseMenu.cs:33 QuitToDesktopButton]]
|
||||
[[vscode://file//home/ronnie/Documents/Godot/Projects/ChickenGameTest/src/PauseMenu/PauseMenu.cs:32 QuitToMainMenuButton]]
|
||||
[[vscode://file//home/ronnie/Documents/Godot/Projects/ChickenGameTest/src/PauseMenu/PauseMenu.cs:31 SaveButton]]
|
||||
--
|
||||
[Methods]
|
||||
[[vscode://file//home/ronnie/Documents/Godot/Projects/ChickenGameTest/src/PauseMenu/PauseMenu.cs:27 _Notification()]]
|
||||
[[vscode://file//home/ronnie/Documents/Godot/Projects/ChickenGameTest/src/PauseMenu/PauseMenu.cs:44 Initialize()]]
|
||||
[[vscode://file//home/ronnie/Documents/Godot/Projects/ChickenGameTest/src/PauseMenu/PauseMenu.cs:79 OnExitTree()]]
|
||||
[[vscode://file//home/ronnie/Documents/Godot/Projects/ChickenGameTest/src/PauseMenu/PauseMenu.cs:55 OnReady()]]
|
||||
[[vscode://file//home/ronnie/Documents/Godot/Projects/ChickenGameTest/src/PauseMenu/PauseMenu.cs:84 OnResolved()]]
|
||||
[[vscode://file//home/ronnie/Documents/Godot/Projects/ChickenGameTest/src/PauseMenu/PauseMenu.cs:74 Setup()]]
|
||||
}
|
||||
Game::GameLogicState ---> GameLogicState
|
||||
Game::PauseMenu ---> PauseMenu
|
||||
}
|
||||
class LoadingGameMenu {
|
||||
[[vscode://file//home/ronnie/Documents/Godot/Projects/ChickenGameTest/src/TitleMenu/LoadingGameMenu.cs ScriptFile]]
|
||||
[Properties]
|
||||
[[vscode://file//home/ronnie/Documents/Godot/Projects/ChickenGameTest/src/TitleMenu/LoadingGameMenu.cs:26 ExitButton]]
|
||||
[[vscode://file//home/ronnie/Documents/Godot/Projects/ChickenGameTest/src/TitleMenu/LoadingGameMenu.cs:25 ItemList]]
|
||||
[[vscode://file//home/ronnie/Documents/Godot/Projects/ChickenGameTest/src/TitleMenu/LoadingGameMenu.cs:24 LoadGameButton]]
|
||||
--
|
||||
[Interface Methods]
|
||||
[[vscode://file//home/ronnie/Documents/Godot/Projects/ChickenGameTest/src/TitleMenu/LoadingGameMenu.cs:32 Initialize()]]
|
||||
[[vscode://file//home/ronnie/Documents/Godot/Projects/ChickenGameTest/src/TitleMenu/LoadingGameMenu.cs:36 Initialize()]]
|
||||
[Methods]
|
||||
[[vscode://file//home/ronnie/Documents/Godot/Projects/ChickenGameTest/src/TitleMenu/LoadingGameMenu.cs:22 _Notification()]]
|
||||
[[vscode://file//home/ronnie/Documents/Godot/Projects/ChickenGameTest/src/TitleMenu/LoadingGameMenu.cs:65 OnExitTree()]]
|
||||
[[vscode://file//home/ronnie/Documents/Godot/Projects/ChickenGameTest/src/TitleMenu/LoadingGameMenu.cs:56 OnGameLoad()]]
|
||||
[[vscode://file//home/ronnie/Documents/Godot/Projects/ChickenGameTest/src/TitleMenu/LoadingGameMenu.cs:63 OnItemSelected()]]
|
||||
[[vscode://file//home/ronnie/Documents/Godot/Projects/ChickenGameTest/src/TitleMenu/LoadingGameMenu.cs:49 OnReady()]]
|
||||
[[vscode://file//home/ronnie/Documents/Godot/Projects/ChickenGameTest/src/TitleMenu/LoadingGameMenu.cs:70 OnResolved()]]
|
||||
[[vscode://file//home/ronnie/Documents/Godot/Projects/ChickenGameTest/src/TitleMenu/LoadingGameMenu.cs:45 Setup()]]
|
||||
}
|
||||
class MainMenu {
|
||||
[[vscode://file//home/ronnie/Documents/Godot/Projects/ChickenGameTest/src/TitleMenu/MainMenu.cs ScriptFile]]
|
||||
[Properties]
|
||||
[[vscode://file//home/ronnie/Documents/Godot/Projects/ChickenGameTest/src/TitleMenu/MainMenu.cs:29 ExitButton]]
|
||||
[[vscode://file//home/ronnie/Documents/Godot/Projects/ChickenGameTest/src/TitleMenu/MainMenu.cs:24 LastGameButton]]
|
||||
[[vscode://file//home/ronnie/Documents/Godot/Projects/ChickenGameTest/src/TitleMenu/MainMenu.cs:26 LoadGameButton]]
|
||||
[[vscode://file//home/ronnie/Documents/Godot/Projects/ChickenGameTest/src/TitleMenu/MainMenu.cs:28 ModsButton]]
|
||||
[[vscode://file//home/ronnie/Documents/Godot/Projects/ChickenGameTest/src/TitleMenu/MainMenu.cs:25 NewGameButton]]
|
||||
[[vscode://file//home/ronnie/Documents/Godot/Projects/ChickenGameTest/src/TitleMenu/MainMenu.cs:27 OptionsButton]]
|
||||
--
|
||||
[Interface Methods]
|
||||
[[vscode://file//home/ronnie/Documents/Godot/Projects/ChickenGameTest/src/TitleMenu/MainMenu.cs:70 SetLastGameVisible()]]
|
||||
[Methods]
|
||||
[[vscode://file//home/ronnie/Documents/Godot/Projects/ChickenGameTest/src/TitleMenu/MainMenu.cs:23 _Notification()]]
|
||||
[[vscode://file//home/ronnie/Documents/Godot/Projects/ChickenGameTest/src/TitleMenu/MainMenu.cs:39 Initialize()]]
|
||||
[[vscode://file//home/ronnie/Documents/Godot/Projects/ChickenGameTest/src/TitleMenu/MainMenu.cs:56 OnExitTree()]]
|
||||
[[vscode://file//home/ronnie/Documents/Godot/Projects/ChickenGameTest/src/TitleMenu/MainMenu.cs:47 OnReady()]]
|
||||
[[vscode://file//home/ronnie/Documents/Godot/Projects/ChickenGameTest/src/TitleMenu/MainMenu.cs:65 OnResolved()]]
|
||||
[[vscode://file//home/ronnie/Documents/Godot/Projects/ChickenGameTest/src/TitleMenu/MainMenu.cs:43 Setup()]]
|
||||
}
|
||||
package ModMenu-Type [[vscode://file//home/ronnie/Documents/Godot/Projects/ChickenGameTest/src/ModMenu/ModMenu.cs]] {
|
||||
class ModMenu {
|
||||
[[vscode://file//home/ronnie/Documents/Godot/Projects/ChickenGameTest/src/ModMenu/ModMenu.cs ScriptFile]]
|
||||
[Properties]
|
||||
[[vscode://file//home/ronnie/Documents/Godot/Projects/ChickenGameTest/src/ModMenu/ModMenu.cs:27 BackButton]]
|
||||
[[vscode://file//home/ronnie/Documents/Godot/Projects/ChickenGameTest/src/ModMenu/ModMenu.cs:26 DisableAllButton]]
|
||||
[[vscode://file//home/ronnie/Documents/Godot/Projects/ChickenGameTest/src/ModMenu/ModMenu.cs:25 EnableAllButton]]
|
||||
[[vscode://file//home/ronnie/Documents/Godot/Projects/ChickenGameTest/src/ModMenu/ModMenu.cs:20 ModEntryScene]]
|
||||
[[vscode://file//home/ronnie/Documents/Godot/Projects/ChickenGameTest/src/ModMenu/ModMenu.cs:22 ModManger]]
|
||||
[[vscode://file//home/ronnie/Documents/Godot/Projects/ChickenGameTest/src/ModMenu/ModMenu.cs:23 ModsContainer]] - [[vscode://file//home/ronnie/Documents/Godot/Projects/ChickenGameTest/src/ReorderableContainer/ReOrderableContainer.cs Script]]
|
||||
[[vscode://file//home/ronnie/Documents/Godot/Projects/ChickenGameTest/src/ModMenu/ModMenu.cs:24 ProfilesButton]]
|
||||
--
|
||||
[Methods]
|
||||
[[vscode://file//home/ronnie/Documents/Godot/Projects/ChickenGameTest/src/ModMenu/ModMenu.cs:83 _ExitTree()]]
|
||||
[[vscode://file//home/ronnie/Documents/Godot/Projects/ChickenGameTest/src/ModMenu/ModMenu.cs:17 _Notification()]]
|
||||
[[vscode://file//home/ronnie/Documents/Godot/Projects/ChickenGameTest/src/ModMenu/ModMenu.cs:35 OnModProfileSelected()]]
|
||||
[[vscode://file//home/ronnie/Documents/Godot/Projects/ChickenGameTest/src/ModMenu/ModMenu.cs:28 OnResolved()]]
|
||||
[[vscode://file//home/ronnie/Documents/Godot/Projects/ChickenGameTest/src/ModMenu/ModMenu.cs:41 OnVisibilityChanged()]]
|
||||
[[vscode://file//home/ronnie/Documents/Godot/Projects/ChickenGameTest/src/ModMenu/ModMenu.cs:49 RefreshMods()]]
|
||||
}
|
||||
class ReOrderableContainer {
|
||||
[[vscode://file//home/ronnie/Documents/Godot/Projects/ChickenGameTest/src/ReorderableContainer/ReOrderableContainer.cs ScriptFile]]
|
||||
[Properties]
|
||||
[[vscode://file//home/ronnie/Documents/Godot/Projects/ChickenGameTest/src/ReorderableContainer/ReOrderableContainer.cs:62 AutoScrollRange]]
|
||||
[[vscode://file//home/ronnie/Documents/Godot/Projects/ChickenGameTest/src/ReorderableContainer/ReOrderableContainer.cs:61 AutoScrollSpeed]]
|
||||
[[vscode://file//home/ronnie/Documents/Godot/Projects/ChickenGameTest/src/ReorderableContainer/ReOrderableContainer.cs:23 HoldDuration]]
|
||||
[[vscode://file//home/ronnie/Documents/Godot/Projects/ChickenGameTest/src/ReorderableContainer/ReOrderableContainer.cs:64 IsDebugging]]
|
||||
[[vscode://file//home/ronnie/Documents/Godot/Projects/ChickenGameTest/src/ReorderableContainer/ReOrderableContainer.cs:38 IsVertical]]
|
||||
[[vscode://file//home/ronnie/Documents/Godot/Projects/ChickenGameTest/src/ReorderableContainer/ReOrderableContainer.cs:60 ScrollContainer]]
|
||||
[[vscode://file//home/ronnie/Documents/Godot/Projects/ChickenGameTest/src/ReorderableContainer/ReOrderableContainer.cs:63 ScrollThreshold]]
|
||||
[[vscode://file//home/ronnie/Documents/Godot/Projects/ChickenGameTest/src/ReorderableContainer/ReOrderableContainer.cs:25 Separation]]
|
||||
[[vscode://file//home/ronnie/Documents/Godot/Projects/ChickenGameTest/src/ReorderableContainer/ReOrderableContainer.cs:24 Speed]]
|
||||
--
|
||||
[Methods]
|
||||
[[vscode://file//home/ronnie/Documents/Godot/Projects/ChickenGameTest/src/ReorderableContainer/ReOrderableContainer.cs:148 _Draw()]]
|
||||
[[vscode://file//home/ronnie/Documents/Godot/Projects/ChickenGameTest/src/ReorderableContainer/ReOrderableContainer.cs:95 _GuiInput()]]
|
||||
[[vscode://file//home/ronnie/Documents/Godot/Projects/ChickenGameTest/src/ReorderableContainer/ReOrderableContainer.cs:20 _Notification()]]
|
||||
[[vscode://file//home/ronnie/Documents/Godot/Projects/ChickenGameTest/src/ReorderableContainer/ReOrderableContainer.cs:115 _Process()]]
|
||||
[[vscode://file//home/ronnie/Documents/Godot/Projects/ChickenGameTest/src/ReorderableContainer/ReOrderableContainer.cs:319 AdjustChildRect()]]
|
||||
[[vscode://file//home/ronnie/Documents/Godot/Projects/ChickenGameTest/src/ReorderableContainer/ReOrderableContainer.cs:364 AdjustDropZoneRect()]]
|
||||
[[vscode://file//home/ronnie/Documents/Godot/Projects/ChickenGameTest/src/ReorderableContainer/ReOrderableContainer.cs:294 AdjustExpectedChildRect()]]
|
||||
[[vscode://file//home/ronnie/Documents/Godot/Projects/ChickenGameTest/src/ReorderableContainer/ReOrderableContainer.cs:404 AsIReOrderableContainer()]]
|
||||
[[vscode://file//home/ronnie/Documents/Godot/Projects/ChickenGameTest/src/ReorderableContainer/ReOrderableContainer.cs:405 GetVisibleChildren()]]
|
||||
[[vscode://file//home/ronnie/Documents/Godot/Projects/ChickenGameTest/src/ReorderableContainer/ReOrderableContainer.cs:238 HandleAutoScroll()]]
|
||||
[[vscode://file//home/ronnie/Documents/Godot/Projects/ChickenGameTest/src/ReorderableContainer/ReOrderableContainer.cs:207 HandleDraggingChildPos()]]
|
||||
[[vscode://file//home/ronnie/Documents/Godot/Projects/ChickenGameTest/src/ReorderableContainer/ReOrderableContainer.cs:159 HandleInput()]]
|
||||
[[vscode://file//home/ronnie/Documents/Godot/Projects/ChickenGameTest/src/ReorderableContainer/ReOrderableContainer.cs:267 OnExitTree()]]
|
||||
[[vscode://file//home/ronnie/Documents/Godot/Projects/ChickenGameTest/src/ReorderableContainer/ReOrderableContainer.cs:276 OnNodeAdded()]]
|
||||
[[vscode://file//home/ronnie/Documents/Godot/Projects/ChickenGameTest/src/ReorderableContainer/ReOrderableContainer.cs:80 OnReady()]]
|
||||
[[vscode://file//home/ronnie/Documents/Godot/Projects/ChickenGameTest/src/ReorderableContainer/ReOrderableContainer.cs:284 OnSortChildren()]]
|
||||
[[vscode://file//home/ronnie/Documents/Godot/Projects/ChickenGameTest/src/ReorderableContainer/ReOrderableContainer.cs:283 OnSortChildrenWrapper()]]
|
||||
[[vscode://file//home/ronnie/Documents/Godot/Projects/ChickenGameTest/src/ReorderableContainer/ReOrderableContainer.cs:175 OnStartDragging()]]
|
||||
[[vscode://file//home/ronnie/Documents/Godot/Projects/ChickenGameTest/src/ReorderableContainer/ReOrderableContainer.cs:186 OnStopDragging()]]
|
||||
}
|
||||
ModMenu::ReOrderableContainer ---> ReOrderableContainer
|
||||
}
|
||||
App::AppLogic ---> AppLogic
|
||||
App::Game ---> Game
|
||||
App::LoadingGameMenu ---> LoadingGameMenu
|
||||
App::MainMenu ---> MainMenu
|
||||
App::ModMenu ---> ModMenu
|
||||
}
|
||||
|
||||
@enduml
|
||||
84
src/App/App.tscn
Normal file
84
src/App/App.tscn
Normal file
@@ -0,0 +1,84 @@
|
||||
[gd_scene format=3 uid="uid://cywpu6lxdjhuu"]
|
||||
|
||||
[ext_resource type="Script" uid="uid://bcadf3uhcfy2" path="res://src/App/App.cs" id="1_tn3dc"]
|
||||
[ext_resource type="PackedScene" uid="uid://nwt8o860iibl" path="res://src/TitleMenu/MainMenu.tscn" id="3_sb8ss"]
|
||||
[ext_resource type="PackedScene" uid="uid://dkgrn8n1on4ec" path="res://src/TitleMenu/CreateGameMenu.tscn" id="4_qlumy"]
|
||||
[ext_resource type="PackedScene" uid="uid://b7x2w61dwafxe" path="res://src/TitleMenu/LoadingGameMenu.tscn" id="5_sb8ss"]
|
||||
[ext_resource type="PackedScene" uid="uid://bf3a5w3h1evaf" path="res://src/OptionsMenu/OptionMenu.tscn" id="6_6skpx"]
|
||||
[ext_resource type="Script" uid="uid://c0qxjltjv53u7" path="res://addons/controller_icons/objects/ControllerIconTexture.cs" id="6_fj5p6"]
|
||||
[ext_resource type="PackedScene" uid="uid://cgp6gntheq4g6" path="res://src/ModMenu/ModMenu.tscn" id="7_6skpx"]
|
||||
|
||||
[sub_resource type="Texture2D" id="Texture2D_6skpx"]
|
||||
resource_local_to_scene = false
|
||||
resource_name = ""
|
||||
script = ExtResource("6_fj5p6")
|
||||
path = "move_forward"
|
||||
metadata/_custom_type_script = "uid://c0qxjltjv53u7"
|
||||
|
||||
[node name="App" type="CanvasLayer" unique_id=2068981625]
|
||||
process_mode = 3
|
||||
script = ExtResource("1_tn3dc")
|
||||
|
||||
[node name="GameSession" type="Control" parent="." unique_id=1185123368]
|
||||
layout_mode = 3
|
||||
anchors_preset = 15
|
||||
anchor_right = 1.0
|
||||
anchor_bottom = 1.0
|
||||
grow_horizontal = 2
|
||||
grow_vertical = 2
|
||||
|
||||
[node name="SubViewportContainer" type="SubViewportContainer" parent="GameSession" unique_id=2046367171]
|
||||
layout_mode = 1
|
||||
anchors_preset = 15
|
||||
anchor_right = 1.0
|
||||
anchor_bottom = 1.0
|
||||
grow_horizontal = 2
|
||||
grow_vertical = 2
|
||||
stretch = true
|
||||
|
||||
[node name="GameViewPort" type="SubViewport" parent="GameSession/SubViewportContainer" unique_id=1388492347]
|
||||
unique_name_in_owner = true
|
||||
own_world_3d = true
|
||||
handle_input_locally = false
|
||||
size = Vector2i(720, 720)
|
||||
render_target_update_mode = 4
|
||||
|
||||
[node name="CenterContainer" type="CenterContainer" parent="." unique_id=1905436581]
|
||||
visible = false
|
||||
anchors_preset = 15
|
||||
anchor_right = 1.0
|
||||
anchor_bottom = 1.0
|
||||
grow_horizontal = 2
|
||||
grow_vertical = 2
|
||||
|
||||
[node name="VBoxContainer" type="VBoxContainer" parent="CenterContainer" unique_id=1941107825]
|
||||
layout_mode = 2
|
||||
|
||||
[node name="TestButton" type="Button" parent="CenterContainer/VBoxContainer" unique_id=1060152075]
|
||||
unique_name_in_owner = true
|
||||
layout_mode = 2
|
||||
text = "Test Button"
|
||||
|
||||
[node name="OptionsMenu" parent="." unique_id=2143215493 instance=ExtResource("6_6skpx")]
|
||||
unique_name_in_owner = true
|
||||
visible = false
|
||||
|
||||
[node name="MainMenu" parent="." unique_id=869490384 instance=ExtResource("3_sb8ss")]
|
||||
unique_name_in_owner = true
|
||||
|
||||
[node name="CreateGameMenu" parent="." unique_id=1233498611 instance=ExtResource("4_qlumy")]
|
||||
unique_name_in_owner = true
|
||||
visible = false
|
||||
|
||||
[node name="LoadingGameMenu" parent="." unique_id=1791717647 instance=ExtResource("5_sb8ss")]
|
||||
unique_name_in_owner = true
|
||||
visible = false
|
||||
|
||||
[node name="Sprite2D" type="Sprite2D" parent="." unique_id=1847137886]
|
||||
texture = SubResource("Texture2D_6skpx")
|
||||
|
||||
[node name="ModsMenu" parent="." unique_id=1651472975 instance=ExtResource("7_6skpx")]
|
||||
unique_name_in_owner = true
|
||||
visible = false
|
||||
|
||||
[connection signal="pressed" from="CenterContainer/VBoxContainer/TestButton" to="." method="OnTestButtonPressed"]
|
||||
24
src/App/AppLogic.cs
Normal file
24
src/App/AppLogic.cs
Normal file
@@ -0,0 +1,24 @@
|
||||
namespace FoodFactory;
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using Chickensoft.Introspection;
|
||||
using Chickensoft.LogicBlocks;
|
||||
using Chickensoft.LogicBlocks.Auto;
|
||||
|
||||
public interface IAppLogic : ILogicBlock;
|
||||
[Meta]
|
||||
public partial class AppLogic : AutoBlock, IAppLogic
|
||||
{
|
||||
|
||||
public AppLogic()
|
||||
{
|
||||
Preallocate<AppLogicState>();
|
||||
|
||||
}
|
||||
public override IEnumerable<IDisposable> OnStartSubscriptions()
|
||||
{
|
||||
yield return Get<IAppRepo>().AutoChannel.Bind()
|
||||
.On((in IAppRepo.GameExiting message) => (State as AppLogicState.InGame)?.OnGameExited(message.Reason));
|
||||
}
|
||||
}
|
||||
1
src/App/AppLogic.cs.uid
Normal file
1
src/App/AppLogic.cs.uid
Normal file
@@ -0,0 +1 @@
|
||||
uid://dugahjx2qt4fq
|
||||
22
src/App/AppLogicState.Input.cs
Normal file
22
src/App/AppLogicState.Input.cs
Normal file
@@ -0,0 +1,22 @@
|
||||
namespace FoodFactory;
|
||||
|
||||
public abstract partial record AppLogicState
|
||||
{
|
||||
public static class Input
|
||||
{
|
||||
public readonly record struct ExitMenu;
|
||||
public readonly record struct NewGame;
|
||||
public readonly record struct LoadGame;
|
||||
public readonly record struct LoadSave;
|
||||
public readonly record struct Options;
|
||||
public readonly record struct Mods;
|
||||
public readonly record struct EndGame(ExitGameReason Reason = ExitGameReason.QuitToMenu);
|
||||
public readonly record struct SaveFileLoaded;
|
||||
|
||||
}
|
||||
}
|
||||
public enum ExitGameReason
|
||||
{
|
||||
QuitToDesktop,
|
||||
QuitToMenu,
|
||||
}
|
||||
1
src/App/AppLogicState.Input.cs.uid
Normal file
1
src/App/AppLogicState.Input.cs.uid
Normal file
@@ -0,0 +1 @@
|
||||
uid://cvvofc4v4vmr5
|
||||
25
src/App/AppLogicState.Output.cs
Normal file
25
src/App/AppLogicState.Output.cs
Normal file
@@ -0,0 +1,25 @@
|
||||
namespace FoodFactory;
|
||||
public abstract partial record AppLogicState
|
||||
{
|
||||
public static class Output
|
||||
{
|
||||
public readonly record struct StartLoadingSaveFile();
|
||||
// public readonly record struct StartLoadingSaveFile(LoadGameArgs Save);
|
||||
public readonly record struct SetUpGameScene;
|
||||
public readonly record struct ShowGameCreationWindow;
|
||||
// public readonly record struct HideGameCreationWindow;
|
||||
public readonly record struct ShowMainMenu;
|
||||
public readonly record struct ShowOptionMenu;
|
||||
public readonly record struct ShowModsMenu;
|
||||
// public readonly record struct ShowHideMenu;
|
||||
public readonly record struct PlayGame;
|
||||
public readonly record struct ShowGame;
|
||||
// public readonly record struct HideGame;
|
||||
public readonly record struct RemoveExistingGame;
|
||||
public readonly record struct ShowGameLoadingWindow;
|
||||
public readonly record struct QuitGame;
|
||||
|
||||
|
||||
// public readonly record struct HideGameLoadingWindow;
|
||||
}
|
||||
}
|
||||
1
src/App/AppLogicState.Output.cs.uid
Normal file
1
src/App/AppLogicState.Output.cs.uid
Normal file
@@ -0,0 +1 @@
|
||||
uid://chh4b4blwffef
|
||||
7
src/App/AppLogicState.cs
Normal file
7
src/App/AppLogicState.cs
Normal file
@@ -0,0 +1,7 @@
|
||||
namespace FoodFactory;
|
||||
|
||||
using Chickensoft.Introspection;
|
||||
using Chickensoft.LogicBlocks;
|
||||
|
||||
[Meta, StateDiagram]
|
||||
public abstract partial record AppLogicState : LogicBlockState;
|
||||
1
src/App/AppLogicState.cs.uid
Normal file
1
src/App/AppLogicState.cs.uid
Normal file
@@ -0,0 +1 @@
|
||||
uid://c1315dvv52mf0
|
||||
34
src/App/AppLogicState.g.puml
Normal file
34
src/App/AppLogicState.g.puml
Normal file
@@ -0,0 +1,34 @@
|
||||
@startuml AppLogicState
|
||||
state "AppLogicState" as FoodFactory_AppLogicState {
|
||||
state "CreatingGame" as FoodFactory_AppLogicState_CreatingGame
|
||||
state "InGame" as FoodFactory_AppLogicState_InGame
|
||||
state "LeavingGame" as FoodFactory_AppLogicState_LeavingGame
|
||||
state "LoadingGame" as FoodFactory_AppLogicState_LoadingGame
|
||||
state "MainMenu" as FoodFactory_AppLogicState_MainMenu
|
||||
state "ModsMenu" as FoodFactory_AppLogicState_ModsMenu
|
||||
state "OptionMenu" as FoodFactory_AppLogicState_OptionMenu
|
||||
}
|
||||
|
||||
FoodFactory_AppLogicState_CreatingGame --> FoodFactory_AppLogicState_InGame : NewGame
|
||||
FoodFactory_AppLogicState_CreatingGame --> FoodFactory_AppLogicState_MainMenu : ExitMenu
|
||||
FoodFactory_AppLogicState_InGame --> FoodFactory_AppLogicState_LeavingGame : EndGame
|
||||
FoodFactory_AppLogicState_LeavingGame --> FoodFactory_AppLogicState_MainMenu : EndGame
|
||||
FoodFactory_AppLogicState_LoadingGame --> FoodFactory_AppLogicState_InGame : LoadSave
|
||||
FoodFactory_AppLogicState_LoadingGame --> FoodFactory_AppLogicState_MainMenu : ExitMenu
|
||||
FoodFactory_AppLogicState_MainMenu --> FoodFactory_AppLogicState_CreatingGame : NewGame
|
||||
FoodFactory_AppLogicState_MainMenu --> FoodFactory_AppLogicState_LoadingGame : LoadGame
|
||||
FoodFactory_AppLogicState_MainMenu --> FoodFactory_AppLogicState_ModsMenu : Mods
|
||||
FoodFactory_AppLogicState_MainMenu --> FoodFactory_AppLogicState_OptionMenu : Options
|
||||
FoodFactory_AppLogicState_ModsMenu --> FoodFactory_AppLogicState_MainMenu : ExitMenu
|
||||
FoodFactory_AppLogicState_OptionMenu --> FoodFactory_AppLogicState_MainMenu : ExitMenu
|
||||
|
||||
FoodFactory_AppLogicState_CreatingGame : OnEnter → ShowGameCreationWindow
|
||||
FoodFactory_AppLogicState_CreatingGame : OnNewGame → SetUpGameScene
|
||||
FoodFactory_AppLogicState_InGame : OnEnter → ShowGame
|
||||
FoodFactory_AppLogicState_LeavingGame : OnEndGame → QuitGame, RemoveExistingGame
|
||||
FoodFactory_AppLogicState_LoadingGame : OnEnter → ShowGameLoadingWindow
|
||||
FoodFactory_AppLogicState_LoadingGame : OnLoadSave → SetUpGameScene, StartLoadingSaveFile
|
||||
FoodFactory_AppLogicState_MainMenu : OnEnter → ShowMainMenu
|
||||
FoodFactory_AppLogicState_ModsMenu : OnEnter → ShowModsMenu
|
||||
FoodFactory_AppLogicState_OptionMenu : OnEnter → ShowOptionMenu
|
||||
@enduml
|
||||
66
src/App/AppRepo.cs
Normal file
66
src/App/AppRepo.cs
Normal file
@@ -0,0 +1,66 @@
|
||||
|
||||
|
||||
namespace FoodFactory;
|
||||
|
||||
using System;
|
||||
using Chickensoft.Sync.Primitives;
|
||||
using FoodFactory.Modding;
|
||||
using Godot;
|
||||
using TrimKit.VirtualFileSystem;
|
||||
|
||||
public interface IAppRepo : IDisposable
|
||||
{
|
||||
readonly record struct GameEntering;
|
||||
readonly record struct GameExiting(ExitGameReason Reason);
|
||||
readonly record struct GameSettingChanged(IOptionKey Setting, Variant Value);
|
||||
void OnEnteringGame();
|
||||
void OnExitGame(ExitGameReason reason = ExitGameReason.QuitToMenu);
|
||||
void OnSettingChanged(IOptionKey setting, Variant value);
|
||||
IAutoChannel AutoChannel { get; }
|
||||
IModManger ModManger { get; }
|
||||
}
|
||||
|
||||
public class AppRepo : IAppRepo
|
||||
{
|
||||
private readonly AutoChannel _autoChannel = new();
|
||||
private bool _disposedValue;
|
||||
|
||||
public IAutoChannel AutoChannel => _autoChannel;
|
||||
// private readonly IModManger _vFSManager;
|
||||
public required IModManger ModManger {get;init;}
|
||||
|
||||
public void OnEnteringGame() => _autoChannel.Send(new IAppRepo.GameEntering());
|
||||
public void OnExitGame(ExitGameReason reason = ExitGameReason.QuitToMenu) => _autoChannel.Send(new IAppRepo.GameExiting(reason));
|
||||
public void OnSettingChanged(IOptionKey setting, Variant value) => _autoChannel.Send(new IAppRepo.GameSettingChanged(setting, value));
|
||||
|
||||
protected virtual void Dispose(bool disposing)
|
||||
{
|
||||
if (!_disposedValue)
|
||||
{
|
||||
if (disposing)
|
||||
{
|
||||
// TODO: dispose managed state (managed objects)
|
||||
_autoChannel.Dispose();
|
||||
ModManger.Dispose();
|
||||
}
|
||||
|
||||
// TODO: free unmanaged resources (unmanaged objects) and override finalizer
|
||||
// TODO: set large fields to null
|
||||
_disposedValue = true;
|
||||
}
|
||||
}
|
||||
|
||||
void IDisposable.Dispose()
|
||||
{
|
||||
// Do not change this code. Put cleanup code in 'Dispose(bool disposing)' method
|
||||
Dispose(disposing: true);
|
||||
GC.SuppressFinalize(this);
|
||||
}
|
||||
}
|
||||
public partial class AppLogic
|
||||
{
|
||||
public record Data
|
||||
{
|
||||
public ExitGameReason ExitGameReason { get; set; }
|
||||
}
|
||||
}
|
||||
1
src/App/AppRepo.cs.uid
Normal file
1
src/App/AppRepo.cs.uid
Normal file
@@ -0,0 +1 @@
|
||||
uid://cgp65d616yhl0
|
||||
53
src/App/States/CreatingGame.cs
Normal file
53
src/App/States/CreatingGame.cs
Normal file
@@ -0,0 +1,53 @@
|
||||
namespace FoodFactory;
|
||||
|
||||
using System;
|
||||
using Chickensoft.Introspection;
|
||||
using Chickensoft.LogicBlocks;
|
||||
|
||||
public partial record AppLogicState
|
||||
{
|
||||
[Meta]
|
||||
public partial record CreatingGame : AppLogicState, IGet<Input.ExitMenu>, IGet<Input.NewGame>
|
||||
{
|
||||
public CreatingGame()
|
||||
{
|
||||
this.OnEnter(() =>
|
||||
{
|
||||
Output(new Output.ShowGameCreationWindow());
|
||||
});
|
||||
}
|
||||
|
||||
public Type On(in Input.ExitMenu input) => To<MainMenu>();
|
||||
public Type On(in Input.NewGame input)
|
||||
{
|
||||
Output(new Output.SetUpGameScene());
|
||||
return To<InGame>();
|
||||
}
|
||||
}
|
||||
[Meta]
|
||||
public partial record OptionMenu : AppLogicState, IGet<Input.ExitMenu>
|
||||
{
|
||||
public OptionMenu()
|
||||
{
|
||||
this.OnEnter(() =>
|
||||
{
|
||||
Output(new Output.ShowOptionMenu());
|
||||
});
|
||||
}
|
||||
public Type On(in Input.ExitMenu input) => To<MainMenu>();
|
||||
|
||||
}
|
||||
[Meta]
|
||||
public partial record ModsMenu : AppLogicState, IGet<Input.ExitMenu>
|
||||
{
|
||||
public ModsMenu()
|
||||
{
|
||||
this.OnEnter(() =>
|
||||
{
|
||||
Output(new Output.ShowModsMenu());
|
||||
});
|
||||
}
|
||||
public Type On(in Input.ExitMenu input) => To<MainMenu>();
|
||||
|
||||
}
|
||||
}
|
||||
1
src/App/States/CreatingGame.cs.uid
Normal file
1
src/App/States/CreatingGame.cs.uid
Normal file
@@ -0,0 +1 @@
|
||||
uid://coo66h6424rhu
|
||||
30
src/App/States/InGame.cs
Normal file
30
src/App/States/InGame.cs
Normal file
@@ -0,0 +1,30 @@
|
||||
namespace FoodFactory;
|
||||
|
||||
using System;
|
||||
using Chickensoft.Introspection;
|
||||
using Chickensoft.LogicBlocks;
|
||||
|
||||
public partial record AppLogicState
|
||||
{
|
||||
[Meta]
|
||||
public partial record InGame : AppLogicState, IGet<Input.EndGame>
|
||||
{
|
||||
public InGame()
|
||||
{
|
||||
this.OnEnter(() =>
|
||||
{
|
||||
Get<IAppRepo>().OnEnteringGame();
|
||||
Output(new Output.ShowGame());
|
||||
});
|
||||
}
|
||||
public Type On(in Input.EndGame input)
|
||||
{
|
||||
// return To<LeavingGame>();
|
||||
Get<AppLogic.Data>().ExitGameReason = input.Reason;
|
||||
return To<LeavingGame>();
|
||||
}
|
||||
|
||||
public void OnGameExited(ExitGameReason reason) => Input(new Input.EndGame(reason));
|
||||
|
||||
}
|
||||
}
|
||||
1
src/App/States/InGame.cs.uid
Normal file
1
src/App/States/InGame.cs.uid
Normal file
@@ -0,0 +1 @@
|
||||
uid://cqaqghb5xytb8
|
||||
33
src/App/States/LeavingGame.cs
Normal file
33
src/App/States/LeavingGame.cs
Normal file
@@ -0,0 +1,33 @@
|
||||
namespace FoodFactory;
|
||||
|
||||
using System;
|
||||
using Chickensoft.Introspection;
|
||||
using Chickensoft.LogicBlocks;
|
||||
|
||||
public partial record AppLogicState
|
||||
{
|
||||
[Meta]
|
||||
public partial record LeavingGame : AppLogicState, IGet<Input.EndGame>
|
||||
{
|
||||
public LeavingGame()
|
||||
{
|
||||
this.OnEnter(() =>
|
||||
{
|
||||
Input(new Input.EndGame());//PlaceHolder
|
||||
// Output(new Output.ShowGame());
|
||||
});
|
||||
this.OnExit(()=> Get<AppLogic.Data>().ExitGameReason = ExitGameReason.QuitToMenu);
|
||||
}
|
||||
|
||||
public Type On(in Input.EndGame input){
|
||||
var reason = Get<AppLogic.Data>().ExitGameReason;
|
||||
Output(new Output.RemoveExistingGame());
|
||||
if (reason is ExitGameReason.QuitToDesktop)
|
||||
{
|
||||
Output(new Output.QuitGame());
|
||||
}
|
||||
return To<MainMenu>();
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
1
src/App/States/LeavingGame.cs.uid
Normal file
1
src/App/States/LeavingGame.cs.uid
Normal file
@@ -0,0 +1 @@
|
||||
uid://xumdrcvr86av
|
||||
29
src/App/States/LoadingGame.cs
Normal file
29
src/App/States/LoadingGame.cs
Normal file
@@ -0,0 +1,29 @@
|
||||
namespace FoodFactory;
|
||||
|
||||
using System;
|
||||
using Chickensoft.Introspection;
|
||||
using Chickensoft.LogicBlocks;
|
||||
|
||||
public partial record AppLogicState
|
||||
{
|
||||
[Meta]
|
||||
public partial record LoadingGame : AppLogicState, IGet<Input.ExitMenu>, IGet<Input.LoadSave>
|
||||
{
|
||||
public LoadingGame()
|
||||
{
|
||||
this.OnEnter(() =>
|
||||
{
|
||||
Output(new Output.ShowGameLoadingWindow());
|
||||
});
|
||||
}
|
||||
|
||||
public Type On(in Input.ExitMenu input) => To<MainMenu>();
|
||||
public Type On(in Input.LoadSave input)
|
||||
{
|
||||
Output(new Output.SetUpGameScene());
|
||||
Output(new Output.StartLoadingSaveFile());
|
||||
return To<InGame>();
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
1
src/App/States/LoadingGame.cs.uid
Normal file
1
src/App/States/LoadingGame.cs.uid
Normal file
@@ -0,0 +1 @@
|
||||
uid://bovjntefvngmh
|
||||
26
src/App/States/MainMenu.cs
Normal file
26
src/App/States/MainMenu.cs
Normal file
@@ -0,0 +1,26 @@
|
||||
namespace FoodFactory;
|
||||
|
||||
using System;
|
||||
using Chickensoft.Introspection;
|
||||
using Chickensoft.LogicBlocks;
|
||||
|
||||
public partial record AppLogicState
|
||||
{
|
||||
[Meta]
|
||||
public partial record MainMenu : AppLogicState,
|
||||
IGet<Input.NewGame>, IGet<Input.LoadGame>, IGet<Input.Options>, IGet<Input.Mods>
|
||||
{
|
||||
public MainMenu()
|
||||
{
|
||||
this.OnEnter(() =>
|
||||
{
|
||||
|
||||
Output(new Output.ShowMainMenu());
|
||||
});
|
||||
}
|
||||
public Type On(in Input.LoadGame input) => To<LoadingGame>();
|
||||
public Type On(in Input.NewGame input) => To<CreatingGame>();
|
||||
public Type On(in Input.Options input) => To<OptionMenu>();
|
||||
public Type On(in Input.Mods input) => To<ModsMenu>();
|
||||
}
|
||||
}
|
||||
1
src/App/States/MainMenu.cs.uid
Normal file
1
src/App/States/MainMenu.cs.uid
Normal file
@@ -0,0 +1 @@
|
||||
uid://c6qr7sej5f6vh
|
||||
497
src/Arch.Extended/Arch.Persistence/Binary.cs
Normal file
497
src/Arch.Extended/Arch.Persistence/Binary.cs
Normal file
@@ -0,0 +1,497 @@
|
||||
using Arch.Core;
|
||||
using Arch.Core.Extensions;
|
||||
using Arch.Core.Extensions.Dangerous;
|
||||
using Arch.Core.Utils;
|
||||
using Arch.LowLevel.Jagged;
|
||||
using MessagePack;
|
||||
using MessagePack.Formatters;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Runtime.CompilerServices;
|
||||
using Utf8Json;
|
||||
|
||||
namespace Arch.Persistence;
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// The <see cref="SingleEntityFormatter"/> class
|
||||
/// is a <see cref="IJsonFormatter"/> to (de)serialize a single <see cref="Entity"/>to or from json.
|
||||
/// </summary>
|
||||
public partial class SingleEntityFormatter : IMessagePackFormatter<Entity>
|
||||
{
|
||||
|
||||
/// <inheritdoc cref="IMessagePackFormatter{T}.Serialize"/>
|
||||
public void Serialize(ref MessagePackWriter writer, Entity value, MessagePackSerializerOptions options)
|
||||
{
|
||||
// Write id
|
||||
writer.WriteInt32(value.Id);
|
||||
|
||||
#if !PURE_ECS
|
||||
|
||||
// Write world
|
||||
writer.WriteInt32(value.WorldId);
|
||||
#endif
|
||||
|
||||
// Write size
|
||||
var componentTypes = value.GetComponentTypes();
|
||||
writer.WriteInt32(componentTypes.Count);
|
||||
|
||||
// Write components
|
||||
foreach (ref var type in componentTypes.Components)
|
||||
{
|
||||
// Write type
|
||||
MessagePackSerializer.Serialize(ref writer, type, options);
|
||||
|
||||
// Write component
|
||||
var cmp = value.Get(type);
|
||||
MessagePackSerializer.Serialize(ref writer, cmp, options);
|
||||
}
|
||||
}
|
||||
|
||||
/// <inheritdoc cref="IMessagePackFormatter{T}.Deserialize"/>
|
||||
public Entity Deserialize(ref MessagePackReader reader, MessagePackSerializerOptions options)
|
||||
{
|
||||
// Read id
|
||||
var entityId = reader.ReadInt32();
|
||||
|
||||
#if !PURE_ECS
|
||||
|
||||
// Read world id
|
||||
var worldId = reader.ReadInt32();
|
||||
#endif
|
||||
|
||||
// Read size
|
||||
var size = reader.ReadInt32();
|
||||
var components = new object[size];
|
||||
|
||||
// Read components
|
||||
for (var index = 0; index < size; index++)
|
||||
{
|
||||
// Read type
|
||||
var type = MessagePackSerializer.Deserialize<ComponentType>(ref reader, options);
|
||||
var cmp = MessagePackSerializer.Deserialize(type, ref reader, options);
|
||||
components[index] = cmp!;
|
||||
}
|
||||
|
||||
// Create the entity
|
||||
var entity = EntityWorld.Create();
|
||||
EntityWorld.AddRange(entity, components.AsSpan());
|
||||
return entity;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// The <see cref="EntityFormatter"/> class
|
||||
/// is a formatter that (de)serializes <see cref="Entity"/> structs.
|
||||
/// </summary>
|
||||
public partial class EntityFormatter : IMessagePackFormatter<Entity>
|
||||
{
|
||||
/// <inheritdoc cref="IMessagePackFormatter{T}.Serialize"/>
|
||||
public void Serialize(ref MessagePackWriter writer, Entity value, MessagePackSerializerOptions options)
|
||||
{
|
||||
writer.WriteInt32(value.Id);
|
||||
writer.WriteInt32(value.Version);
|
||||
}
|
||||
|
||||
/// <inheritdoc cref="IMessagePackFormatter{T}.Deserialize"/>
|
||||
public Entity Deserialize(ref MessagePackReader reader, MessagePackSerializerOptions options)
|
||||
{
|
||||
// Read id
|
||||
var id = reader.ReadInt32();
|
||||
var version = reader.ReadInt32();
|
||||
return DangerousEntityExtensions.CreateEntityStruct(id, WorldId, version);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// The <see cref="ArrayFormatter"/> class
|
||||
/// is a <see cref="IJsonFormatter{Array}"/> to (de)serialize <see cref="Array"/>s to or from json.
|
||||
/// </summary>
|
||||
public partial class ArrayFormatter : IMessagePackFormatter<Array>
|
||||
{
|
||||
/// <inheritdoc cref="IMessagePackFormatter{T}.Serialize"/>
|
||||
public void Serialize(ref MessagePackWriter writer, Array value, MessagePackSerializerOptions options)
|
||||
{
|
||||
var type = value.GetType().GetElementType();
|
||||
|
||||
// Write type and size
|
||||
MessagePackSerializer.Serialize(ref writer, type, options);
|
||||
writer.WriteUInt32((uint)value.Length);
|
||||
|
||||
// Write array
|
||||
for (var index = 0; index < value.Length; index++)
|
||||
{
|
||||
var obj = value.GetValue(index);
|
||||
MessagePackSerializer.Serialize(ref writer, obj, options);
|
||||
}
|
||||
}
|
||||
|
||||
/// <inheritdoc cref="IMessagePackFormatter{T}.Deserialize"/>
|
||||
public Array Deserialize(ref MessagePackReader reader, MessagePackSerializerOptions options)
|
||||
{
|
||||
// Write type and size
|
||||
var type = MessagePackSerializer.Deserialize<Type>(ref reader, options);
|
||||
var size = reader.ReadUInt32();
|
||||
|
||||
// Create array
|
||||
var array = Array.CreateInstance(type, size);
|
||||
|
||||
// Read array
|
||||
for (var index = 0; index < size; index++)
|
||||
{
|
||||
var obj = MessagePackSerializer.Deserialize(type, ref reader, options);
|
||||
array.SetValue(obj, index);
|
||||
}
|
||||
return array;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// The <see cref="JaggedArrayFormatter{T}"/> class
|
||||
/// (de)serializes a <see cref="JaggedArray{T}"/>.
|
||||
/// </summary>
|
||||
/// <typeparam name="T">The type stored in the <see cref="JaggedArray{T}"/>.</typeparam>
|
||||
public partial class JaggedArrayFormatter<T> : IMessagePackFormatter<JaggedArray<T>>
|
||||
{
|
||||
private const int CpuL1CacheSize = 16_384;
|
||||
private readonly T _filler;
|
||||
|
||||
/// <summary>
|
||||
/// Constructor.
|
||||
/// </summary>
|
||||
/// <param name="filler">Filler.</param>
|
||||
public JaggedArrayFormatter(T filler)
|
||||
{
|
||||
_filler = filler;
|
||||
}
|
||||
|
||||
/// <inheritdoc cref="IMessagePackFormatter{T}.Serialize"/>
|
||||
public void Serialize(ref MessagePackWriter writer, JaggedArray<T> value, MessagePackSerializerOptions options)
|
||||
{
|
||||
// Write length/capacity and items
|
||||
writer.WriteInt32(value.Capacity);
|
||||
for (var index = 0; index < value.Capacity; index++)
|
||||
{
|
||||
var item = value[index];
|
||||
MessagePackSerializer.Serialize(ref writer, item, options);
|
||||
}
|
||||
}
|
||||
|
||||
/// <inheritdoc cref="IMessagePackFormatter{T}.Deserialize"/>
|
||||
public JaggedArray<T> Deserialize(ref MessagePackReader reader, MessagePackSerializerOptions options)
|
||||
{
|
||||
var capacity = reader.ReadInt32();
|
||||
var jaggedArray = new JaggedArray<T>(CpuL1CacheSize / Unsafe.SizeOf<T>(), _filler, capacity);
|
||||
|
||||
for (var index = 0; index < capacity; index++)
|
||||
{
|
||||
var item = MessagePackSerializer.Deserialize<T>(ref reader, options);
|
||||
jaggedArray.Add(index, item);
|
||||
}
|
||||
|
||||
return jaggedArray;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// The <see cref="ComponentTypeFormatter"/> class
|
||||
/// is a <see cref="IJsonFormatter{ComponentType}"/> to (de)serialize <see cref="ComponentType"/>s to or from json.
|
||||
/// </summary>
|
||||
public partial class ComponentTypeFormatter : IMessagePackFormatter<ComponentType>
|
||||
{
|
||||
/// <inheritdoc cref="IMessagePackFormatter{T}.Serialize"/>
|
||||
public void Serialize(ref MessagePackWriter writer, ComponentType value, MessagePackSerializerOptions options)
|
||||
{
|
||||
// Write id
|
||||
writer.WriteUInt32((uint)value.Id);
|
||||
|
||||
// Write bytesize
|
||||
writer.WriteUInt32((uint)value.ByteSize);
|
||||
}
|
||||
|
||||
/// <inheritdoc cref="IMessagePackFormatter{T}.Deserialize"/>
|
||||
public ComponentType Deserialize(ref MessagePackReader reader, MessagePackSerializerOptions options)
|
||||
{
|
||||
var id = reader.ReadUInt32();
|
||||
var bytesize = reader.ReadUInt32();
|
||||
|
||||
return new ComponentType((int)id, (int)bytesize);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// The <see cref="ComponentTypeFormatter"/> class
|
||||
/// is a <see cref="IJsonFormatter{ComponentType}"/> to (de)serialize <see cref="Signature"/>s to or from json.
|
||||
/// </summary>
|
||||
public partial class SignatureFormatter : IMessagePackFormatter<Signature>
|
||||
{
|
||||
/// <inheritdoc cref="IMessagePackFormatter{T}.Serialize"/>
|
||||
public void Serialize(ref MessagePackWriter writer, Signature value, MessagePackSerializerOptions options)
|
||||
{
|
||||
var componentTypeFormatter = options.Resolver.GetFormatter<ComponentType>() as ComponentTypeFormatter;
|
||||
|
||||
// Write count and types
|
||||
writer.WriteUInt32((uint)value.Count);
|
||||
foreach (var type in value.Components)
|
||||
{
|
||||
componentTypeFormatter!.Serialize(ref writer, type, options);
|
||||
}
|
||||
}
|
||||
|
||||
/// <inheritdoc cref="IMessagePackFormatter{T}.Deserialize"/>
|
||||
public Signature Deserialize(ref MessagePackReader reader, MessagePackSerializerOptions options)
|
||||
{
|
||||
var componentTypeFormatter = options.Resolver.GetFormatter<ComponentType>() as ComponentTypeFormatter;
|
||||
|
||||
// Read count
|
||||
var count = reader.ReadUInt32();
|
||||
|
||||
// Read types
|
||||
var componentTypes = new ComponentType[count];
|
||||
for (var index = 0; index < count; index++)
|
||||
{
|
||||
var componentType = componentTypeFormatter!.Deserialize(ref reader, options);
|
||||
componentTypes[index] = componentType;
|
||||
}
|
||||
return new Signature(componentTypes);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// The <see cref="ComponentTypeFormatter"/> class
|
||||
/// is a <see cref="IJsonFormatter{ComponentType}"/> to (de)serialize <see cref="ComponentType"/>s to or from json.
|
||||
/// </summary>
|
||||
public partial class EntitySlotFormatter : IMessagePackFormatter<EntityData>
|
||||
{
|
||||
/// <inheritdoc cref="IMessagePackFormatter{T}.Serialize"/>
|
||||
public void Serialize(ref MessagePackWriter writer, EntityData value, MessagePackSerializerOptions options)
|
||||
{
|
||||
// Write chunk index
|
||||
writer.WriteUInt32((uint)value.Slot.ChunkIndex);
|
||||
|
||||
// Write entity index
|
||||
writer.WriteUInt32((uint)value.Slot.Index);
|
||||
}
|
||||
|
||||
/// <inheritdoc cref="IMessagePackFormatter{T}.Deserialize"/>
|
||||
public EntityData Deserialize(ref MessagePackReader reader, MessagePackSerializerOptions options)
|
||||
{
|
||||
|
||||
// Read chunk index and entity index
|
||||
var chunkIndex = reader.ReadUInt32();
|
||||
var entityIndex = reader.ReadUInt32();
|
||||
|
||||
return new EntityData(null!, new Slot((int)entityIndex, (int)chunkIndex), 0);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// The <see cref="WorldFormatter"/> class
|
||||
/// is a <see cref="IJsonFormatter{World}"/> to (de)serialize <see cref="World"/>s to or from json.
|
||||
/// </summary>
|
||||
public partial class WorldFormatter : IMessagePackFormatter<World>
|
||||
{
|
||||
/// <inheritdoc cref="IMessagePackFormatter{T}.Serialize"/>
|
||||
public void Serialize(ref MessagePackWriter writer, World value, MessagePackSerializerOptions options)
|
||||
{
|
||||
// Write important meta data
|
||||
writer.WriteUInt32((uint)value.BaseChunkSize);
|
||||
writer.WriteUInt32((uint)value.BaseChunkEntityCount);
|
||||
|
||||
// Write slots
|
||||
MessagePackSerializer.Serialize(ref writer, value.GetEntityDataArray(), options);
|
||||
|
||||
//Write recycled entity ids
|
||||
var recycledEntityIDs = value.GetRecycledEntityIds();
|
||||
MessagePackSerializer.Serialize(ref writer, recycledEntityIDs, options);
|
||||
|
||||
// Write archetypes
|
||||
writer.WriteUInt32((uint)value.Archetypes.Count);
|
||||
foreach (var archetype in value)
|
||||
{
|
||||
MessagePackSerializer.Serialize(ref writer, archetype, options);
|
||||
}
|
||||
}
|
||||
|
||||
/// <inheritdoc cref="IMessagePackFormatter{T}.Deserialize"/>
|
||||
public World Deserialize(ref MessagePackReader reader, MessagePackSerializerOptions options)
|
||||
{
|
||||
// Read important metadata
|
||||
var baseChunkSize = reader.ReadUInt32();
|
||||
var baseChunkEntityCount = reader.ReadUInt32();
|
||||
|
||||
// Create world and setup formatter
|
||||
var world = World.Create(chunkSizeInBytes: (int)baseChunkSize, minimumAmountOfEntitiesPerChunk: (int)baseChunkEntityCount);
|
||||
var archetypeFormatter = options.Resolver.GetFormatter<Archetype>() as ArchetypeFormatter;
|
||||
var entityFormatter = options.Resolver.GetFormatter<Entity>() as EntityFormatter;
|
||||
entityFormatter!.WorldId = world.Id;
|
||||
archetypeFormatter!.World = world;
|
||||
|
||||
// Read slots
|
||||
var slots = MessagePackSerializer.Deserialize<JaggedArray<EntityData>>(ref reader, options);
|
||||
|
||||
//Read recycled entity ids
|
||||
var recycledEntityIDs = MessagePackSerializer.Deserialize<List<(int, int)>>(ref reader, options);
|
||||
|
||||
// Forward values to the world
|
||||
world.SetRecycledEntityIds(recycledEntityIDs);
|
||||
world.SetEntityDataArray(slots);
|
||||
world.EnsureCapacity(slots.Capacity);
|
||||
|
||||
// Read archetypes
|
||||
var size = reader.ReadInt32();
|
||||
List<Archetype> archetypes = new();
|
||||
|
||||
for (var index = 0; index < size; index++)
|
||||
{
|
||||
var archetype = archetypeFormatter.Deserialize(ref reader, options);
|
||||
archetypes.Add(archetype);
|
||||
}
|
||||
|
||||
// Set archetypes
|
||||
world.SetArchetypes(archetypes);
|
||||
return world;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// The <see cref="ArchetypeFormatter"/> class
|
||||
/// is a <see cref="IJsonFormatter{Archetype}"/> to (de)serialize <see cref="Archetype"/>s to or from json.
|
||||
/// </summary>
|
||||
public partial class ArchetypeFormatter : IMessagePackFormatter<Archetype>
|
||||
{
|
||||
/// <inheritdoc cref="IMessagePackFormatter{T}.Serialize"/>
|
||||
public void Serialize(ref MessagePackWriter writer, Archetype value, MessagePackSerializerOptions options)
|
||||
{
|
||||
// Setup formatters
|
||||
var types = value.Signature;
|
||||
var chunks = value.Chunks;
|
||||
var chunkFormatter = options.Resolver.GetFormatter<Chunk>() as ChunkFormatter;
|
||||
chunkFormatter!.Signature = types;
|
||||
|
||||
// Write type array
|
||||
MessagePackSerializer.Serialize(ref writer, types, options);
|
||||
|
||||
// Write lookup array
|
||||
MessagePackSerializer.Serialize(ref writer, value.GetLookupArray(), options);
|
||||
|
||||
// Write chunk size
|
||||
writer.WriteUInt32((uint)value.ChunkCount);
|
||||
|
||||
// Write chunks
|
||||
for (var index = 0; index < value.ChunkCount; index++)
|
||||
{
|
||||
ref var chunk = ref chunks[index];
|
||||
chunkFormatter.Serialize(ref writer, chunk, options);
|
||||
}
|
||||
}
|
||||
|
||||
/// <inheritdoc cref="IMessagePackFormatter{T}.Deserialize"/>
|
||||
public Archetype Deserialize(ref MessagePackReader reader, MessagePackSerializerOptions options)
|
||||
{
|
||||
|
||||
var chunkFormatter = options.Resolver.GetFormatter<Chunk>() as ChunkFormatter;
|
||||
|
||||
// Types
|
||||
var types = MessagePackSerializer.Deserialize<Signature>(ref reader, options);
|
||||
|
||||
// Archetype lookup array
|
||||
var lookupArray = MessagePackSerializer.Deserialize<int[]>(ref reader, options);
|
||||
|
||||
// Archetype chunk size and list
|
||||
var chunkSize = reader.ReadUInt32();
|
||||
|
||||
// Create archetype
|
||||
var chunks = new List<Chunk>((int)chunkSize);
|
||||
var archetype = DangerousArchetypeExtensions.CreateArchetype(World.BaseChunkSize, World.BaseChunkEntityCount, types);
|
||||
archetype.Chunks.Clear(true);
|
||||
archetype.SetCount((int)chunkSize - 1);
|
||||
|
||||
// Pass types and lookup array to the chunk formatter for saving performance and memory
|
||||
chunkFormatter!.World = World;
|
||||
chunkFormatter.Archetype = archetype;
|
||||
chunkFormatter.Signature = types;
|
||||
chunkFormatter.LookupArray = lookupArray;
|
||||
|
||||
// Deserialise each chunk and put it into the archetype.
|
||||
var entities = 0;
|
||||
for (var index = 0; index < chunkSize; index++)
|
||||
{
|
||||
var chunk = chunkFormatter.Deserialize(ref reader, options);
|
||||
chunks.Add(chunk);
|
||||
entities += chunk.Count;
|
||||
}
|
||||
|
||||
archetype.SetChunks(chunks);
|
||||
archetype.SetEntities(entities);
|
||||
return archetype;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// The <see cref="ChunkFormatter"/> class
|
||||
/// is a <see cref="IJsonFormatter{Chunk}"/> to (de)serialize <see cref="Chunk"/>s to or from json.
|
||||
/// </summary>
|
||||
public partial class ChunkFormatter : IMessagePackFormatter<Chunk>
|
||||
{
|
||||
/// <inheritdoc cref="IMessagePackFormatter{T}.Serialize"/>
|
||||
public void Serialize(ref MessagePackWriter writer, Chunk value, MessagePackSerializerOptions options)
|
||||
{
|
||||
// Write size
|
||||
writer.WriteUInt32((uint)value.Count);
|
||||
|
||||
// Write capacity
|
||||
writer.WriteUInt32((uint)value.Capacity);
|
||||
|
||||
// Write entitys
|
||||
MessagePackSerializer.Serialize(ref writer, value.Entities, options);
|
||||
|
||||
// Persist arrays as an array...
|
||||
foreach (var type in Signature.Components)
|
||||
{
|
||||
// Write array itself
|
||||
var array = value.GetArray(type);
|
||||
MessagePackSerializer.Serialize(ref writer, array, options);
|
||||
}
|
||||
}
|
||||
|
||||
/// <inheritdoc cref="IMessagePackFormatter{T}.Deserialize"/>
|
||||
public Chunk Deserialize(ref MessagePackReader reader, MessagePackSerializerOptions options)
|
||||
{
|
||||
// Read chunk size
|
||||
var size = reader.ReadUInt32();
|
||||
|
||||
// Read chunk size
|
||||
var capacity = reader.ReadUInt32();
|
||||
|
||||
// Read entities
|
||||
var entities = MessagePackSerializer.Deserialize<Entity[]>(ref reader, options);
|
||||
|
||||
// Create chunk
|
||||
var chunk = DangerousChunkExtensions.CreateChunk((int)capacity, LookupArray, Signature);
|
||||
entities.CopyTo(chunk.Entities, 0);
|
||||
chunk.SetSize((int)size);
|
||||
|
||||
// Updating World.EntityInfoStorage to their new archetype
|
||||
for (var index = 0; index < size; index++)
|
||||
{
|
||||
ref var entity = ref chunk.Entity(index);
|
||||
entity = DangerousEntityExtensions.CreateEntityStruct(entity.Id, World.Id, entity.Version);
|
||||
World.SetArchetype(entity, Archetype);
|
||||
}
|
||||
|
||||
// Persist arrays as an array...
|
||||
foreach (var type in Signature.Components)
|
||||
{
|
||||
// Read array of the type
|
||||
var array = MessagePackSerializer.Deserialize<Array>(ref reader, options);
|
||||
var chunkArray = chunk.GetArray(array.GetType().GetElementType()!);
|
||||
Array.Copy(array, chunkArray, (int)size);
|
||||
}
|
||||
|
||||
return chunk;
|
||||
}
|
||||
}
|
||||
|
||||
1
src/Arch.Extended/Arch.Persistence/Binary.cs.uid
Normal file
1
src/Arch.Extended/Arch.Persistence/Binary.cs.uid
Normal file
@@ -0,0 +1 @@
|
||||
uid://b22vip5klbxnq
|
||||
822
src/Arch.Extended/Arch.Persistence/Json.cs
Normal file
822
src/Arch.Extended/Arch.Persistence/Json.cs
Normal file
@@ -0,0 +1,822 @@
|
||||
namespace Arch.Persistence;
|
||||
|
||||
using global::System;
|
||||
using global::System.Collections.Generic;
|
||||
using Arch.Core;
|
||||
using Arch.Core.Extensions;
|
||||
using Arch.Core.Extensions.Dangerous;
|
||||
using Arch.LowLevel.Jagged;
|
||||
using CommunityToolkit.HighPerformance;
|
||||
using global::System.Runtime.CompilerServices;
|
||||
using Utf8Json;
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// The <see cref="SingleEntityFormatter"/> class
|
||||
/// is a <see cref="IJsonFormatter{Entity}"/> to (de)serialize a single <see cref="Entity"/>to or from json.
|
||||
/// </summary>
|
||||
public partial class SingleEntityFormatter : IJsonFormatter<Entity>
|
||||
{
|
||||
|
||||
/// <summary>
|
||||
/// The <see cref="EntityWorld"/> the entity belongs to.
|
||||
/// </summary>
|
||||
internal World EntityWorld { get; set; } = null!;
|
||||
|
||||
/// <inheritdoc cref="IJsonFormatter{T}.Serialize"/>
|
||||
public void Serialize(ref JsonWriter writer, Entity value, IJsonFormatterResolver formatterResolver)
|
||||
{
|
||||
writer.WriteBeginObject();
|
||||
|
||||
// Write id
|
||||
writer.WritePropertyName("id");
|
||||
writer.WriteInt32(value.Id);
|
||||
writer.WriteValueSeparator();
|
||||
|
||||
#if !PURE_ECS
|
||||
|
||||
// Write world
|
||||
writer.WritePropertyName("worldId");
|
||||
writer.WriteInt32(value.WorldId);
|
||||
writer.WriteValueSeparator();
|
||||
|
||||
#endif
|
||||
|
||||
// Write size
|
||||
var componentTypes = value.GetComponentTypes();
|
||||
writer.WritePropertyName("size");
|
||||
writer.WriteInt32(componentTypes.Count);
|
||||
writer.WriteValueSeparator();
|
||||
|
||||
// Write components
|
||||
writer.WritePropertyName("components");
|
||||
writer.WriteBeginArray();
|
||||
foreach (ref var type in componentTypes.Components)
|
||||
{
|
||||
// Write type
|
||||
writer.WriteBeginObject();
|
||||
writer.WritePropertyName("type");
|
||||
JsonSerializer.Serialize(ref writer, type);
|
||||
writer.WriteValueSeparator();
|
||||
|
||||
// Write component
|
||||
writer.WritePropertyName("component");
|
||||
var cmp = value.Get(type);
|
||||
JsonSerializer.NonGeneric.Serialize(ref writer, cmp, formatterResolver);
|
||||
writer.WriteEndObject();
|
||||
writer.WriteValueSeparator();
|
||||
}
|
||||
writer.AdvanceOffset(-1);
|
||||
|
||||
writer.WriteEndArray();
|
||||
writer.WriteEndObject();
|
||||
}
|
||||
|
||||
/// <inheritdoc cref="IJsonFormatter{T}.Deserialize"/>
|
||||
public Entity Deserialize(ref JsonReader reader, IJsonFormatterResolver formatterResolver)
|
||||
{
|
||||
reader.ReadIsBeginObject();
|
||||
|
||||
// Read id
|
||||
reader.ReadPropertyName();
|
||||
var entityId = reader.ReadInt32();
|
||||
reader.ReadIsValueSeparator();
|
||||
|
||||
#if !PURE_ECS
|
||||
|
||||
// Read world id
|
||||
reader.ReadPropertyName();
|
||||
var worldId = reader.ReadInt32();
|
||||
reader.ReadIsValueSeparator();
|
||||
|
||||
#endif
|
||||
|
||||
// Read size
|
||||
reader.ReadPropertyName();
|
||||
var size = reader.ReadInt32();
|
||||
reader.ReadIsValueSeparator();
|
||||
|
||||
var components = new object[size];
|
||||
var count = 0;
|
||||
|
||||
// Read components
|
||||
reader.ReadPropertyName();
|
||||
reader.ReadIsBeginArray();
|
||||
while (!reader.ReadIsEndArrayWithSkipValueSeparator(ref count))
|
||||
{
|
||||
reader.ReadIsBeginObject();
|
||||
|
||||
// Read type
|
||||
reader.ReadPropertyName();
|
||||
var type = JsonSerializer.Deserialize<ComponentType>(ref reader);
|
||||
reader.ReadIsValueSeparator();
|
||||
|
||||
reader.ReadPropertyName();
|
||||
var cmp = JsonSerializer.NonGeneric.Deserialize(type.Type, ref reader, formatterResolver);
|
||||
components[count - 1] = cmp;
|
||||
reader.ReadIsEndObject();
|
||||
}
|
||||
|
||||
// Creat the entity
|
||||
var entity = EntityWorld.Create();
|
||||
EntityWorld.AddRange(entity, components.AsSpan());
|
||||
|
||||
reader.ReadIsEndObject();
|
||||
return entity;
|
||||
}
|
||||
}
|
||||
|
||||
public partial class EntityFormatter : IJsonFormatter<Entity>
|
||||
{
|
||||
|
||||
/// <summary>
|
||||
/// The <see cref="World.Id"/> all deserialized <see cref="Entity"/>s will belong to.
|
||||
/// <remarks>Due to the nature of deserialisation and changing world landscape we need to assign new WorldIds to the deserialized entities.</remarks>
|
||||
/// </summary>
|
||||
internal int WorldId { get; set; }
|
||||
|
||||
/// <inheritdoc cref="IJsonFormatter{T}.Serialize"/>
|
||||
public void Serialize(ref JsonWriter writer, Entity value, IJsonFormatterResolver formatterResolver)
|
||||
{
|
||||
writer.WriteInt32(value.Id);
|
||||
writer.WriteValueSeparator();
|
||||
writer.WriteInt32(value.Version);
|
||||
}
|
||||
|
||||
/// <inheritdoc cref="IJsonFormatter{T}.Deserialize"/>
|
||||
public Entity Deserialize(ref JsonReader reader, IJsonFormatterResolver formatterResolver)
|
||||
{
|
||||
// Read id
|
||||
var id = reader.ReadInt32();
|
||||
reader.ReadIsValueSeparator();
|
||||
var version = reader.ReadInt32();
|
||||
return DangerousEntityExtensions.CreateEntityStruct(id, WorldId, version);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// The <see cref="ArrayFormatter"/> class
|
||||
/// is a <see cref="IJsonFormatter{Array}"/> to (de)serialize <see cref="Array"/>s to or from json.
|
||||
/// </summary>
|
||||
public partial class ArrayFormatter : IJsonFormatter<Array>
|
||||
{
|
||||
/// <inheritdoc cref="IJsonFormatter{T}.Serialize"/>
|
||||
public void Serialize(ref JsonWriter writer, Array value, IJsonFormatterResolver formatterResolver)
|
||||
{
|
||||
var type = value.GetType().GetElementType();
|
||||
|
||||
// Write type and size
|
||||
writer.WriteBeginObject();
|
||||
writer.WritePropertyName("type");
|
||||
JsonSerializer.Serialize(ref writer, type, formatterResolver);
|
||||
writer.WriteValueSeparator();
|
||||
|
||||
writer.WritePropertyName("length");
|
||||
writer.WriteUInt32((uint)value.Length);
|
||||
writer.WriteValueSeparator();
|
||||
|
||||
// Write array
|
||||
writer.WritePropertyName("items");
|
||||
writer.WriteBeginArray();
|
||||
for (var index = 0; index < value.Length; index++)
|
||||
{
|
||||
var obj = value.GetValue(index);
|
||||
JsonSerializer.NonGeneric.Serialize(ref writer, obj, formatterResolver);
|
||||
writer.WriteValueSeparator();
|
||||
}
|
||||
writer.AdvanceOffset(-1);
|
||||
writer.WriteEndArray();
|
||||
writer.WriteEndObject();
|
||||
}
|
||||
|
||||
/// <inheritdoc cref="IJsonFormatter{T}.Deserialize"/>
|
||||
public Array Deserialize(ref JsonReader reader, IJsonFormatterResolver formatterResolver)
|
||||
{
|
||||
// Write type and size
|
||||
reader.ReadIsBeginObject();
|
||||
reader.ReadPropertyName();
|
||||
var type = JsonSerializer.Deserialize<Type>(ref reader, formatterResolver);
|
||||
reader.ReadIsValueSeparator();
|
||||
|
||||
reader.ReadPropertyName();
|
||||
var size = reader.ReadUInt32();
|
||||
reader.ReadIsValueSeparator();
|
||||
|
||||
// Create array
|
||||
var array = Array.CreateInstance(type, size);
|
||||
|
||||
// Read array
|
||||
reader.ReadPropertyName();
|
||||
reader.ReadIsBeginArray();
|
||||
for (var index = 0; index < size; index++)
|
||||
{
|
||||
var obj = JsonSerializer.NonGeneric.Deserialize(type, ref reader, formatterResolver);
|
||||
array.SetValue(obj, index);
|
||||
reader.ReadIsValueSeparator();
|
||||
}
|
||||
reader.ReadIsEndArray();
|
||||
reader.ReadIsEndObject();
|
||||
return array;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// The <see cref="JaggedArrayFormatter{T}"/> class
|
||||
/// (de)serializes a <see cref="JaggedArray{T}"/>.
|
||||
/// </summary>
|
||||
public partial class JaggedArrayFormatter<T> : IJsonFormatter<JaggedArray<T>>
|
||||
{
|
||||
/// <inheritdoc cref="IJsonFormatter{T}.Serialize"/>
|
||||
public void Serialize(ref JsonWriter writer, JaggedArray<T> value, IJsonFormatterResolver formatterResolver)
|
||||
{
|
||||
writer.WriteBeginObject();
|
||||
|
||||
// Write length/capacity and items
|
||||
writer.WritePropertyName("capacity");
|
||||
writer.WriteInt32(value.Capacity);
|
||||
writer.WriteValueSeparator();
|
||||
|
||||
// Write items
|
||||
writer.WritePropertyName("items");
|
||||
writer.WriteBeginArray();
|
||||
|
||||
for (var index = 0; index < value.Capacity; index++)
|
||||
{
|
||||
var item = value[index];
|
||||
JsonSerializer.Serialize(ref writer, item, formatterResolver);
|
||||
writer.WriteValueSeparator();
|
||||
}
|
||||
|
||||
// Cut last value seperator
|
||||
if (value.Capacity > 0)
|
||||
{
|
||||
writer.AdvanceOffset(-1);
|
||||
}
|
||||
writer.WriteEndArray();
|
||||
writer.WriteEndObject();
|
||||
}
|
||||
|
||||
/// <inheritdoc cref="IJsonFormatter{T}.Deserialize"/>
|
||||
public JaggedArray<T> Deserialize(ref JsonReader reader, IJsonFormatterResolver formatterResolver)
|
||||
{
|
||||
reader.ReadIsBeginObject();
|
||||
|
||||
// Read capacity;
|
||||
reader.ReadPropertyName();
|
||||
var capacity = reader.ReadInt32();
|
||||
reader.ReadIsValueSeparator();
|
||||
|
||||
// Read items
|
||||
var jaggedArray = new JaggedArray<T>(CpuL1CacheSize / Unsafe.SizeOf<T>(), _filler, capacity);
|
||||
reader.ReadPropertyName();
|
||||
reader.ReadIsBeginArray();
|
||||
for (var index = 0; index < capacity; index++)
|
||||
{
|
||||
var item = JsonSerializer.Deserialize<T>(ref reader, formatterResolver);
|
||||
jaggedArray.Add(index, item);
|
||||
reader.ReadIsValueSeparator();
|
||||
}
|
||||
reader.ReadIsEndArray();
|
||||
reader.ReadIsEndObject();
|
||||
|
||||
return jaggedArray;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// The <see cref="ComponentTypeFormatter"/> class
|
||||
/// is a <see cref="IJsonFormatter{ComponentType}"/> to (de)serialize <see cref="ComponentType"/>s to or from json.
|
||||
/// </summary>
|
||||
public partial class ComponentTypeFormatter : IJsonFormatter<ComponentType>
|
||||
{
|
||||
/// <inheritdoc cref="IJsonFormatter{T}.Serialize"/>
|
||||
public void Serialize(ref JsonWriter writer, ComponentType value, IJsonFormatterResolver formatterResolver)
|
||||
{
|
||||
writer.WriteBeginObject();
|
||||
|
||||
// Write id
|
||||
writer.WritePropertyName("id");
|
||||
writer.WriteUInt32((uint)value.Id);
|
||||
writer.WriteValueSeparator();
|
||||
|
||||
// Write bytesize
|
||||
writer.WritePropertyName("byteSize");
|
||||
writer.WriteUInt32((uint)value.ByteSize);
|
||||
|
||||
writer.WriteEndObject();
|
||||
}
|
||||
|
||||
/// <inheritdoc cref="IJsonFormatter{T}.Deserialize"/>
|
||||
public ComponentType Deserialize(ref JsonReader reader, IJsonFormatterResolver formatterResolver)
|
||||
{
|
||||
reader.ReadIsBeginObject();
|
||||
|
||||
reader.ReadPropertyName();
|
||||
var id = reader.ReadUInt32();
|
||||
reader.ReadIsValueSeparator();
|
||||
|
||||
reader.ReadPropertyName();
|
||||
var bytesize = reader.ReadUInt32();
|
||||
reader.ReadIsValueSeparator();
|
||||
|
||||
reader.ReadIsEndObject();
|
||||
|
||||
return new ComponentType((int)id, (int)bytesize);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// The <see cref="ComponentTypeFormatter"/> class
|
||||
/// is a <see cref="IJsonFormatter{ComponentType}"/> to (de)serialize <see cref="Signature"/>s to or from json.
|
||||
/// </summary>
|
||||
public partial class SignatureFormatter : IJsonFormatter<Signature>
|
||||
{
|
||||
/// <inheritdoc cref="IJsonFormatter{T}.Serialize"/>
|
||||
public void Serialize(ref JsonWriter writer, Signature value, IJsonFormatterResolver formatterResolver)
|
||||
{
|
||||
var componentTypeFormatter = formatterResolver.GetFormatter<ComponentType>() as ComponentTypeFormatter;
|
||||
|
||||
writer.WriteBeginObject();
|
||||
|
||||
// write Count
|
||||
writer.WritePropertyName("count");
|
||||
writer.WriteUInt32((uint)value.Count);
|
||||
writer.WriteValueSeparator();
|
||||
|
||||
// Write components
|
||||
writer.WritePropertyName("components");
|
||||
writer.WriteBeginArray();
|
||||
|
||||
foreach (var type in value.Components)
|
||||
{
|
||||
componentTypeFormatter!.Serialize(ref writer, type, formatterResolver);
|
||||
writer.WriteValueSeparator();
|
||||
}
|
||||
|
||||
// Cut last value seperator
|
||||
if (value.Count > 0)
|
||||
{
|
||||
writer.AdvanceOffset(-1);
|
||||
}
|
||||
|
||||
writer.WriteEndArray();
|
||||
writer.WriteEndObject();
|
||||
}
|
||||
|
||||
/// <inheritdoc cref="IJsonFormatter{T}.Deserialize"/>
|
||||
public Signature Deserialize(ref JsonReader reader, IJsonFormatterResolver formatterResolver)
|
||||
{
|
||||
var componentTypeFormatter = formatterResolver.GetFormatter<ComponentType>() as ComponentTypeFormatter;
|
||||
|
||||
reader.ReadIsBeginObject();
|
||||
reader.ReadPropertyName();
|
||||
|
||||
// Read count
|
||||
var count = (int)reader.ReadUInt32();
|
||||
reader.ReadIsValueSeparator();
|
||||
|
||||
// Read types
|
||||
reader.ReadPropertyName();
|
||||
reader.ReadIsBeginArray();
|
||||
|
||||
var componentTypes = new ComponentType[count];
|
||||
count = 0;
|
||||
while (!reader.ReadIsEndArrayWithSkipValueSeparator(ref count))
|
||||
{
|
||||
var archetype = componentTypeFormatter!.Deserialize(ref reader, formatterResolver);
|
||||
componentTypes[count - 1] = (archetype);
|
||||
}
|
||||
|
||||
// Set archetypes
|
||||
reader.ReadIsEndObject();
|
||||
return new Signature(componentTypes);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// The <see cref="ComponentTypeFormatter"/> class
|
||||
/// is a <see cref="IJsonFormatter{ComponentType}"/> to (de)serialize <see cref="ComponentType"/>s to or from json.
|
||||
/// </summary>
|
||||
public partial class EntitySlotFormatter : IJsonFormatter<EntityData>
|
||||
{
|
||||
/// <inheritdoc cref="IJsonFormatter{T}.Serialize"/>
|
||||
public void Serialize(ref JsonWriter writer, EntityData value, IJsonFormatterResolver options)
|
||||
{
|
||||
writer.WriteBeginObject();
|
||||
|
||||
// Write chunk index
|
||||
writer.WritePropertyName("chunkIndex");
|
||||
writer.WriteUInt32((uint)value.Slot.ChunkIndex);
|
||||
writer.WriteValueSeparator();
|
||||
|
||||
// Write entity index
|
||||
writer.WritePropertyName("index");
|
||||
writer.WriteUInt32((uint)value.Slot.Index);
|
||||
|
||||
writer.WriteEndObject();
|
||||
}
|
||||
|
||||
/// <inheritdoc cref="IJsonFormatter{T}.Deserialize"/>
|
||||
public EntityData Deserialize(ref JsonReader reader, IJsonFormatterResolver options)
|
||||
{
|
||||
reader.ReadIsBeginObject();
|
||||
|
||||
// Read chunk index
|
||||
reader.ReadPropertyName();
|
||||
var chunkIndex = reader.ReadUInt32();
|
||||
reader.ReadIsValueSeparator();
|
||||
|
||||
// Read entity index
|
||||
reader.ReadPropertyName();
|
||||
var entityIndex = reader.ReadUInt32();
|
||||
|
||||
reader.ReadIsEndObject();
|
||||
return new EntityData(null!, new Slot((int)entityIndex, (int)chunkIndex), 0);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// The <see cref="WorldFormatter"/> class
|
||||
/// is a <see cref="IJsonFormatter{World}"/> to (de)serialize <see cref="World"/>s to or from json.
|
||||
/// </summary>
|
||||
public partial class WorldFormatter : IJsonFormatter<World>
|
||||
{
|
||||
/// <inheritdoc cref="IJsonFormatter{T}.Serialize"/>
|
||||
public void Serialize(ref JsonWriter writer, World value, IJsonFormatterResolver formatterResolver)
|
||||
{
|
||||
//var archetypeFormatter = formatterResolver.GetFormatter<Archetype>();
|
||||
//var versionsFormatter = formatterResolver.GetFormatter<int[][]>();
|
||||
//var slotFormatter = formatterResolver.GetFormatter<(int,int)[][]>();
|
||||
|
||||
writer.WriteBeginObject();
|
||||
|
||||
// Write meta data
|
||||
writer.WritePropertyName("baseChunkSize");
|
||||
writer.WriteUInt32((uint)value.BaseChunkSize);
|
||||
writer.WriteValueSeparator();
|
||||
|
||||
writer.WritePropertyName("baseChunkEntityCount");
|
||||
writer.WriteUInt32((uint)value.BaseChunkEntityCount);
|
||||
writer.WriteValueSeparator();
|
||||
|
||||
// Write slots
|
||||
writer.WritePropertyName("slots");
|
||||
JsonSerializer.Serialize(ref writer, value.GetEntityDataArray(), formatterResolver);
|
||||
writer.WriteValueSeparator();
|
||||
|
||||
//Write recycled entity ids
|
||||
writer.WritePropertyName("recycledEntityIDs");
|
||||
writer.WriteBeginArray();
|
||||
var recycledEntityIDs = value.GetRecycledEntityIds();
|
||||
foreach (var recycledId in recycledEntityIDs)
|
||||
{
|
||||
writer.WriteBeginObject();
|
||||
writer.WritePropertyName("id");
|
||||
writer.WriteInt32(recycledId.Item1);
|
||||
writer.WriteValueSeparator();
|
||||
writer.WritePropertyName("version");
|
||||
writer.WriteInt32(recycledId.Item2);
|
||||
writer.WriteEndObject();
|
||||
|
||||
writer.WriteValueSeparator();
|
||||
}
|
||||
// Cut last value seperator
|
||||
if (recycledEntityIDs.Count > 0)
|
||||
{
|
||||
writer.AdvanceOffset(-1);
|
||||
}
|
||||
writer.WriteEndArray();
|
||||
|
||||
writer.WriteValueSeparator();
|
||||
|
||||
//Write archetypes
|
||||
writer.WritePropertyName("archetypes");
|
||||
writer.WriteBeginArray();
|
||||
foreach (var archetype in value)
|
||||
{
|
||||
JsonSerializer.Serialize(ref writer, archetype, formatterResolver);
|
||||
writer.WriteValueSeparator();
|
||||
}
|
||||
|
||||
// Cut last value seperator
|
||||
if (value.Archetypes.Count > 0)
|
||||
{
|
||||
writer.AdvanceOffset(-1);
|
||||
}
|
||||
writer.WriteEndArray();
|
||||
writer.WriteEndObject();
|
||||
}
|
||||
|
||||
/// <inheritdoc cref="IJsonFormatter{T}.Deserialize"/>
|
||||
public World Deserialize(ref JsonReader reader, IJsonFormatterResolver formatterResolver)
|
||||
{
|
||||
// Create world and setup formatter
|
||||
var archetypeFormatter = formatterResolver.GetFormatter<Archetype>() as ArchetypeFormatter;
|
||||
var entityFormatter = formatterResolver.GetFormatter<Entity>() as EntityFormatter;
|
||||
|
||||
reader.ReadIsBeginObject();
|
||||
|
||||
// Read meta data
|
||||
reader.ReadPropertyName();
|
||||
var baseChunkSize = reader.ReadUInt32();
|
||||
reader.ReadIsValueSeparator();
|
||||
|
||||
reader.ReadPropertyName();
|
||||
var baseChunkEntityCount = reader.ReadUInt32();
|
||||
reader.ReadIsValueSeparator();
|
||||
|
||||
// Construct world
|
||||
var world = World.Create(chunkSizeInBytes: (int)baseChunkSize, minimumAmountOfEntitiesPerChunk: (int)baseChunkEntityCount);
|
||||
entityFormatter!.WorldId = world.Id;
|
||||
archetypeFormatter!.World = world;
|
||||
|
||||
// Read slots
|
||||
reader.ReadPropertyName();
|
||||
var slots = JsonSerializer.Deserialize<JaggedArray<EntityData>>(ref reader, formatterResolver);
|
||||
reader.ReadIsValueSeparator();
|
||||
|
||||
// Read recycled ids
|
||||
var count = 0;
|
||||
List<(int, int)> recycledIds = new();
|
||||
|
||||
reader.ReadPropertyName();
|
||||
reader.ReadIsBeginArray();
|
||||
|
||||
while (!reader.ReadIsEndArrayWithSkipValueSeparator(ref count))
|
||||
{
|
||||
reader.ReadIsBeginObject();
|
||||
reader.ReadPropertyName();
|
||||
var id = reader.ReadInt32();
|
||||
reader.ReadIsValueSeparator();
|
||||
reader.ReadPropertyName();
|
||||
var value = reader.ReadInt32();
|
||||
reader.ReadIsEndObject();
|
||||
|
||||
(int, int) recycledId = new(id, value);
|
||||
|
||||
recycledIds.Add(recycledId);
|
||||
}
|
||||
|
||||
reader.ReadIsValueSeparator();
|
||||
|
||||
// Forward values to the world
|
||||
world.SetRecycledEntityIds(recycledIds);
|
||||
world.SetEntityDataArray(slots);
|
||||
world.EnsureCapacity(slots.Capacity);
|
||||
|
||||
// Read archetypes
|
||||
count = 0;
|
||||
List<Archetype> archetypes = new();
|
||||
reader.ReadPropertyName();
|
||||
reader.ReadIsBeginArray();
|
||||
while (!reader.ReadIsEndArrayWithSkipValueSeparator(ref count))
|
||||
{
|
||||
var archetype = archetypeFormatter.Deserialize(ref reader, formatterResolver);
|
||||
archetypes.Add(archetype);
|
||||
}
|
||||
|
||||
// Set archetypes
|
||||
world.SetArchetypes(archetypes);
|
||||
reader.ReadIsEndObject();
|
||||
return world;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// The <see cref="ArchetypeFormatter"/> class
|
||||
/// is a <see cref="IJsonFormatter{Archetype}"/> to (de)serialize <see cref="Archetype"/>s to or from json.
|
||||
/// </summary>
|
||||
public partial class ArchetypeFormatter : IJsonFormatter<Archetype>
|
||||
{
|
||||
|
||||
/// <summary>
|
||||
/// The <see cref="World"/> which is being used by this formatter during serialisation/deserialisation.
|
||||
/// </summary>
|
||||
internal World World { get; set; } = null!;
|
||||
|
||||
/// <inheritdoc cref="IJsonFormatter{T}.Serialize"/>
|
||||
public void Serialize(ref JsonWriter writer, Archetype value, IJsonFormatterResolver formatterResolver)
|
||||
{
|
||||
// Setup formatters
|
||||
var types = value.Signature;
|
||||
var chunks = value.Chunks;
|
||||
var chunkFormatter = formatterResolver.GetFormatter<Chunk>() as ChunkFormatter;
|
||||
chunkFormatter!.Signature = types;
|
||||
|
||||
writer.WriteBeginObject();
|
||||
|
||||
// Write type array
|
||||
writer.WritePropertyName("types");
|
||||
JsonSerializer.Serialize(ref writer, types, formatterResolver);
|
||||
writer.WriteValueSeparator();
|
||||
|
||||
// Write lookup array
|
||||
writer.WritePropertyName("lookup");
|
||||
JsonSerializer.Serialize(ref writer, value.GetLookupArray(), formatterResolver);
|
||||
writer.WriteValueSeparator();
|
||||
|
||||
// Write chunk size
|
||||
writer.WritePropertyName("chunkCount");
|
||||
writer.WriteUInt32((uint)value.ChunkCount);
|
||||
writer.WriteValueSeparator();
|
||||
|
||||
// Write chunks
|
||||
writer.WritePropertyName("chunks");
|
||||
writer.WriteBeginArray();
|
||||
for (var index = 0; index < value.ChunkCount; index++)
|
||||
{
|
||||
ref var chunk = ref chunks[index];
|
||||
chunkFormatter.Serialize(ref writer, chunk, formatterResolver);
|
||||
writer.WriteValueSeparator();
|
||||
}
|
||||
|
||||
// Trim last value separator
|
||||
if (value.ChunkCount > 0)
|
||||
{
|
||||
writer.AdvanceOffset(-1);
|
||||
}
|
||||
|
||||
writer.WriteEndArray();
|
||||
writer.WriteEndObject();
|
||||
}
|
||||
|
||||
/// <inheritdoc cref="IJsonFormatter{T}.Deserialize"/>
|
||||
public Archetype Deserialize(ref JsonReader reader, IJsonFormatterResolver formatterResolver)
|
||||
{
|
||||
var chunkFormatter = formatterResolver.GetFormatter<Chunk>() as ChunkFormatter;
|
||||
|
||||
reader.ReadIsBeginObject();
|
||||
|
||||
// Types
|
||||
reader.ReadPropertyName();
|
||||
var types = JsonSerializer.Deserialize<Signature>(ref reader, formatterResolver);
|
||||
reader.ReadIsValueSeparator();
|
||||
|
||||
// Archetype lookup array
|
||||
reader.ReadPropertyName();
|
||||
var lookupArray = JsonSerializer.Deserialize<int[]>(ref reader, formatterResolver);
|
||||
reader.ReadIsValueSeparator();
|
||||
|
||||
// Archetype chunk size and list
|
||||
reader.ReadPropertyName();
|
||||
var chunkCount = reader.ReadUInt32();
|
||||
reader.ReadIsValueSeparator();
|
||||
|
||||
// Create archetype
|
||||
var chunks = new List<Chunk>((int)chunkCount);
|
||||
var archetype = DangerousArchetypeExtensions.CreateArchetype(World.BaseChunkSize, World.BaseChunkEntityCount, types);
|
||||
archetype.Chunks.Clear(true);
|
||||
archetype.SetCount((int)chunkCount - 1);
|
||||
|
||||
// Pass types and lookup array to the chunk formatter for saving performance and memory
|
||||
chunkFormatter!.World = World;
|
||||
chunkFormatter.Archetype = archetype;
|
||||
chunkFormatter.Signature = types;
|
||||
chunkFormatter.LookupArray = lookupArray;
|
||||
|
||||
// Deserialise each chunk and put it into the archetype.
|
||||
reader.ReadPropertyName();
|
||||
reader.ReadIsBeginArray();
|
||||
|
||||
var entities = 0;
|
||||
for (var index = 0; index < chunkCount; index++)
|
||||
{
|
||||
var chunk = chunkFormatter.Deserialize(ref reader, formatterResolver);
|
||||
chunks.Add(chunk);
|
||||
entities += chunk.Count;
|
||||
reader.ReadIsValueSeparator();
|
||||
}
|
||||
|
||||
archetype.SetChunks(chunks);
|
||||
archetype.SetEntities(entities);
|
||||
|
||||
reader.ReadIsEndArray();
|
||||
reader.ReadIsEndObject();
|
||||
return archetype;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// The <see cref="ChunkFormatter"/> class
|
||||
/// is a <see cref="IJsonFormatter{Chunk}"/> to (de)serialize <see cref="Chunk"/>s to or from json.
|
||||
/// </summary>
|
||||
public partial class ChunkFormatter : IJsonFormatter<Chunk>
|
||||
{
|
||||
|
||||
/// <summary>
|
||||
/// The <see cref="Archetype"/> the current (de)serialized <see cref="Chunk"/> belongs to.
|
||||
/// Since chunks do not know this, we need to pass this information along it.
|
||||
/// </summary>
|
||||
internal World World { get; set; } = null!;
|
||||
|
||||
/// <summary>
|
||||
/// The <see cref="Archetype"/> the current (de)serialized <see cref="Chunk"/> belongs to.
|
||||
/// Since chunks do not know this, we need to pass this information along it.
|
||||
/// </summary>
|
||||
internal Archetype Archetype { get; set; } = null!;
|
||||
|
||||
/// <summary>
|
||||
/// The types used in the <see cref="Chunk"/> in each <see cref="Chunk"/> (de)serialized by this formatter.
|
||||
/// <remarks>Since <see cref="Chunk"/> does not have a reference to them and its controlled by its <see cref="Archetype"/>.</remarks>
|
||||
/// </summary>
|
||||
internal Signature Signature { get; set; } = Signature.Null;
|
||||
|
||||
/// <summary>
|
||||
/// The lookup array used by each <see cref="Chunk"/> (de)serialized by this formatter.
|
||||
/// <remarks>Since <see cref="Chunk"/> does not have a reference to them and its controlled by its <see cref="Archetype"/>.</remarks>
|
||||
/// </summary>
|
||||
internal int[] LookupArray { get; set; } = Array.Empty<int>();
|
||||
|
||||
/// <inheritdoc cref="IJsonFormatter{T}.Serialize"/>
|
||||
public void Serialize(ref JsonWriter writer, Chunk value, IJsonFormatterResolver formatterResolver)
|
||||
{
|
||||
writer.WriteBeginObject();
|
||||
|
||||
// Write size
|
||||
writer.WritePropertyName("count");
|
||||
writer.WriteUInt32((uint)value.Count);
|
||||
writer.WriteValueSeparator();
|
||||
|
||||
// Write capacity
|
||||
writer.WritePropertyName("capacity");
|
||||
writer.WriteUInt32((uint)value.Capacity);
|
||||
writer.WriteValueSeparator();
|
||||
|
||||
// Write entitys
|
||||
writer.WritePropertyName("entities");
|
||||
JsonSerializer.NonGeneric.Serialize(ref writer, value.Entities, formatterResolver);
|
||||
writer.WriteValueSeparator();
|
||||
|
||||
// Persist arrays as an array...
|
||||
writer.WritePropertyName("arrays");
|
||||
writer.WriteBeginArray();
|
||||
foreach (var type in Signature.Components)
|
||||
{
|
||||
// Write array itself
|
||||
var array = value.GetArray(type);
|
||||
JsonSerializer.Serialize(ref writer, array, formatterResolver);
|
||||
writer.WriteValueSeparator();
|
||||
}
|
||||
|
||||
// Remove trailing
|
||||
if (Signature.Count > 0)
|
||||
{
|
||||
writer.AdvanceOffset(-1);
|
||||
}
|
||||
|
||||
writer.WriteEndArray();
|
||||
writer.WriteEndObject();
|
||||
}
|
||||
|
||||
/// <inheritdoc cref="IJsonFormatter{T}.Deserialize"/>
|
||||
public Chunk Deserialize(ref JsonReader reader, IJsonFormatterResolver formatterResolver)
|
||||
{
|
||||
reader.ReadIsBeginObject();
|
||||
|
||||
// Read chunk size
|
||||
reader.ReadPropertyName();
|
||||
var size = reader.ReadUInt32();
|
||||
reader.ReadIsValueSeparator();
|
||||
|
||||
// Read chunk size
|
||||
reader.ReadPropertyName();
|
||||
var capacity = reader.ReadUInt32();
|
||||
reader.ReadIsValueSeparator();
|
||||
|
||||
// Read entities
|
||||
reader.ReadPropertyName();
|
||||
var entities = JsonSerializer.Deserialize<Entity[]>(ref reader, formatterResolver);
|
||||
reader.ReadIsValueSeparator();
|
||||
|
||||
// Create chunk
|
||||
var chunk = DangerousChunkExtensions.CreateChunk((int)capacity, LookupArray, Signature);
|
||||
entities.CopyTo(chunk.Entities, 0);
|
||||
chunk.SetSize((int)size);
|
||||
|
||||
// Updating World.EntityInfoStorage to their new archetype
|
||||
for (var index = 0; index < size; index++)
|
||||
{
|
||||
ref var entity = ref chunk.Entity(index);
|
||||
entity = DangerousEntityExtensions.CreateEntityStruct(entity.Id, World.Id, entity.Version);
|
||||
World.SetArchetype(entity, Archetype);
|
||||
}
|
||||
|
||||
// Persist arrays as an array...
|
||||
reader.ReadPropertyName();
|
||||
reader.ReadIsBeginArray();
|
||||
foreach (var type in Signature)
|
||||
{
|
||||
// Read array of the type
|
||||
var array = JsonSerializer.Deserialize<Array>(ref reader, formatterResolver);
|
||||
var chunkArray = chunk.GetArray(array.GetType().GetElementType()!);
|
||||
Array.Copy(array, chunkArray, (int)size);
|
||||
reader.ReadIsValueSeparator();
|
||||
}
|
||||
|
||||
reader.ReadIsEndArray();
|
||||
reader.ReadIsEndObject();
|
||||
return chunk;
|
||||
}
|
||||
}
|
||||
|
||||
1
src/Arch.Extended/Arch.Persistence/Json.cs.uid
Normal file
1
src/Arch.Extended/Arch.Persistence/Json.cs.uid
Normal file
@@ -0,0 +1 @@
|
||||
uid://ddnds4ritwn2f
|
||||
410
src/Arch.Extended/Arch.Persistence/Serializer.cs
Normal file
410
src/Arch.Extended/Arch.Persistence/Serializer.cs
Normal file
@@ -0,0 +1,410 @@
|
||||
namespace Arch.Persistence;
|
||||
|
||||
using Arch.Core;
|
||||
using MessagePack;
|
||||
using MessagePack.Formatters;
|
||||
using global::System;
|
||||
using global::System.Buffers;
|
||||
using global::System.IO;
|
||||
using Utf8Json;
|
||||
using Utf8Json.Resolvers;
|
||||
using DateTimeFormatter = Utf8Json.Formatters.DateTimeFormatter;
|
||||
using NullableDateTimeFormatter = Utf8Json.Formatters.NullableDateTimeFormatter;
|
||||
|
||||
/// <summary>
|
||||
/// The <see cref="IArchSerializer"/> interface
|
||||
/// represents an interface with shared methods to (de)serialize worlds and entities.
|
||||
/// <remarks>It might happen that the serialized object is too large to fit into a regular c# byte-array. In this case use the <see cref="IBufferWriter{T}"/>-API.</remarks>
|
||||
/// </summary>
|
||||
public interface IArchSerializer
|
||||
{
|
||||
/// <summary>
|
||||
/// Serializes an <see cref="Entity"/> to a <see cref="byte"/>-array.
|
||||
/// </summary>
|
||||
/// <param name="world">The <see cref="World"/>.</param>
|
||||
/// <param name="entity">The <see cref="Entity"/>.</param>
|
||||
byte[] Serialize(World world, Entity entity);
|
||||
|
||||
/// <summary>
|
||||
/// Serializes an <see cref="Entity"/> to a <see cref="Stream"/> e.g. a File or existing array.
|
||||
/// </summary>
|
||||
/// <param name="stream">The <see cref="Stream"/>.</param>
|
||||
/// <param name="world">The <see cref="World"/>.</param>
|
||||
/// <param name="entity">The <see cref="Entity"/>.</param>
|
||||
void Serialize(Stream stream, World world, Entity entity);
|
||||
|
||||
/// <summary>
|
||||
/// Serializes an <see cref="Entity"/> to a <see cref="IBufferWriter{T}"/> e.g. a File or existing array.
|
||||
/// </summary>
|
||||
/// <param name="writer">The <see cref="IBufferWriter{T}"/>.</param>
|
||||
/// <param name="world">The <see cref="World"/>.</param>
|
||||
/// <param name="entity">The <see cref="Entity"/>.</param>
|
||||
void Serialize(IBufferWriter<byte> writer, World world, Entity entity);
|
||||
|
||||
/// <summary>
|
||||
/// Deserializes an <see cref="Entity"/> from its bytes to an real <see cref="Entity"/> in a <see cref="World"/>.
|
||||
/// <remarks>The new <see cref="Entity.Id"/> and <see cref="Entity.WorldId"/> will differ.</remarks>
|
||||
/// </summary>
|
||||
/// <param name="world">The <see cref="World"/>.</param>
|
||||
/// <param name="entity">The <see cref="Entity"/>.</param>
|
||||
/// <returns></returns>
|
||||
Entity Deserialize(World world, byte[] entity);
|
||||
|
||||
/// <summary>
|
||||
/// Deserializes an <see cref="Entity"/> from its bytes to an real <see cref="Entity"/> in a <see cref="World"/>.
|
||||
/// <remarks>The new <see cref="Entity.Id"/> and <see cref="Entity.WorldId"/> will differ.</remarks>
|
||||
/// </summary>
|
||||
/// <param name="stream">The <see cref="Stream"/>.</param>
|
||||
/// <param name="world">The <see cref="World"/>.</param>
|
||||
/// <returns></returns>
|
||||
Entity Deserialize(Stream stream, World world);
|
||||
|
||||
/// <summary>
|
||||
/// Serializes a <see cref="World"/> to a <see cref="byte"/>-array.
|
||||
/// </summary>
|
||||
/// <param name="world">The <see cref="World"/>.</param>
|
||||
byte[] Serialize(World world);
|
||||
|
||||
/// <summary>
|
||||
/// Serializes a <see cref="World"/> to a <see cref="Stream"/>.
|
||||
/// </summary>
|
||||
/// <param name="stream">The <see cref="Stream"/>.</param>
|
||||
/// <param name="world">The <see cref="World"/>.</param>
|
||||
void Serialize(Stream stream, World world);
|
||||
|
||||
/// <summary>
|
||||
/// Serializes a <see cref="World"/> to a <see cref="IBufferWriter{T}"/>.
|
||||
/// </summary>
|
||||
/// <param name="writer">The <see cref="IBufferWriter{T}"/>.</param>
|
||||
/// <param name="world">The <see cref="World"/>.</param>
|
||||
void Serialize(IBufferWriter<byte> writer, World world);
|
||||
|
||||
/// <summary>
|
||||
/// Deserializes a byte-array into a <see cref="World"/>.
|
||||
/// </summary>
|
||||
/// <param name="world">The <see cref="World"/> as an byte-array.</param>
|
||||
/// <returns>The new <see cref="World"/>.</returns>
|
||||
World Deserialize(byte[] world);
|
||||
|
||||
/// <summary>
|
||||
/// Deserializes a byte-array into a <see cref="World"/>.
|
||||
/// </summary>
|
||||
/// <param name="stream">The <see cref="Stream"/>.</param>
|
||||
/// <returns>The new <see cref="World"/>.</returns>
|
||||
World Deserialize(Stream stream);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// The <see cref="ArchBinarySerializer"/> class
|
||||
/// represents a binary serializer for arch to (de)serialize single entities and whole worlds by binary.
|
||||
/// </summary>
|
||||
public class ArchBinarySerializer : IArchSerializer
|
||||
{
|
||||
/// <summary>
|
||||
/// The default formatters used to (de)serialize the <see cref="World"/>.
|
||||
/// </summary>
|
||||
private readonly IMessagePackFormatter[] _formatters =
|
||||
[
|
||||
new WorldFormatter(),
|
||||
new ArchetypeFormatter(),
|
||||
new ChunkFormatter(),
|
||||
new ArrayFormatter(),
|
||||
new ComponentTypeFormatter(),
|
||||
new SignatureFormatter(),
|
||||
new EntitySlotFormatter(),
|
||||
new EntityFormatter(),
|
||||
new JaggedArrayFormatter<int>(-1),
|
||||
new JaggedArrayFormatter<(int,int)>((-1,-1)),
|
||||
new JaggedArrayFormatter<EntityData>(new EntityData(null!, new Slot(-1,-1), -1))
|
||||
];
|
||||
|
||||
/// <summary>
|
||||
/// The default formatters used to (de)serialize a single <see cref="Entity"/>.
|
||||
/// </summary>
|
||||
private readonly IMessagePackFormatter[] _singleEntityFormatters =
|
||||
[
|
||||
new ComponentTypeFormatter(),
|
||||
new SignatureFormatter(),
|
||||
new SingleEntityFormatter()
|
||||
];
|
||||
|
||||
/// <summary>
|
||||
/// The standard <see cref="MessagePackSerializerOptions"/> for world (de)serialization.
|
||||
/// </summary>
|
||||
private readonly MessagePackSerializerOptions _options;
|
||||
|
||||
/// <summary>
|
||||
/// The standard <see cref="MessagePackSerializerOptions"/> for single entity (de)serialization.
|
||||
/// </summary>
|
||||
private readonly MessagePackSerializerOptions _singleEntityOptions;
|
||||
|
||||
/// <summary>
|
||||
/// The static constructor gets called during compile time to setup the serializer.
|
||||
/// </summary>
|
||||
public ArchBinarySerializer(params IMessagePackFormatter[] custFormatters)
|
||||
{
|
||||
// Register all important jsonformatters
|
||||
_options = MessagePackSerializerOptions.Standard.WithResolver(
|
||||
MessagePack.Resolvers.CompositeResolver.Create(
|
||||
[.. _formatters, .. custFormatters],
|
||||
[
|
||||
MessagePack.Resolvers.BuiltinResolver.Instance,
|
||||
MessagePack.Resolvers.ContractlessStandardResolverAllowPrivate.Instance
|
||||
]
|
||||
)
|
||||
);
|
||||
|
||||
_singleEntityOptions = MessagePackSerializerOptions.Standard.WithResolver(
|
||||
MessagePack.Resolvers.CompositeResolver.Create(
|
||||
[.. _singleEntityFormatters, .. custFormatters],
|
||||
[
|
||||
MessagePack.Resolvers.BuiltinResolver.Instance,
|
||||
MessagePack.Resolvers.ContractlessStandardResolverAllowPrivate.Instance
|
||||
]
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public byte[] Serialize(World world, Entity entity)
|
||||
{
|
||||
(_singleEntityFormatters[2] as SingleEntityFormatter)!.EntityWorld = world;
|
||||
return MessagePackSerializer.Serialize(entity, _singleEntityOptions);
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public void Serialize(Stream stream, World world, Entity entity)
|
||||
{
|
||||
(_singleEntityFormatters[2] as SingleEntityFormatter)!.EntityWorld = world;
|
||||
MessagePackSerializer.Serialize(stream, entity, _singleEntityOptions);
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public void Serialize(IBufferWriter<byte> writer, World world, Entity entity)
|
||||
{
|
||||
(_singleEntityFormatters[2] as SingleEntityFormatter)!.EntityWorld = world;
|
||||
MessagePackSerializer.Serialize(writer, entity, _singleEntityOptions);
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public Entity Deserialize(World world, byte[] entity)
|
||||
{
|
||||
(_singleEntityFormatters[2] as SingleEntityFormatter)!.EntityWorld = world;
|
||||
return MessagePackSerializer.Deserialize<Entity>(entity, _singleEntityOptions);
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public Entity Deserialize(Stream stream, World world)
|
||||
{
|
||||
(_singleEntityFormatters[2] as SingleEntityFormatter)!.EntityWorld = world;
|
||||
return MessagePackSerializer.Deserialize<Entity>(stream, _singleEntityOptions);
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public byte[] Serialize(World world)
|
||||
{
|
||||
return MessagePackSerializer.Serialize(world, _options);
|
||||
;
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public void Serialize(Stream stream, World world) => MessagePackSerializer.Serialize(stream, world, _options);
|
||||
|
||||
/// <inheritdoc/>
|
||||
public void Serialize(IBufferWriter<byte> writer, World world) => MessagePackSerializer.Serialize(writer, world, _options);
|
||||
|
||||
/// <inheritdoc/>
|
||||
public World Deserialize(byte[] world) => MessagePackSerializer.Deserialize<World>(world, _options);
|
||||
|
||||
/// <inheritdoc/>
|
||||
public World Deserialize(Stream stream) => MessagePackSerializer.Deserialize<World>(stream, _options);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// The <see cref="ArchJsonSerializer"/> class
|
||||
/// represents a json serializer for arch to (de)serialize single entities and whole worlds by binary.
|
||||
/// </summary>
|
||||
public class ArchJsonSerializer : IArchSerializer
|
||||
{
|
||||
|
||||
/// <summary>
|
||||
/// The default formatters used to (de)serialize the <see cref="World"/>.
|
||||
/// </summary>
|
||||
private readonly IJsonFormatter[] _formatters = [
|
||||
new WorldFormatter(),
|
||||
new ArchetypeFormatter(),
|
||||
new ChunkFormatter(),
|
||||
new ArrayFormatter(),
|
||||
new ComponentTypeFormatter(),
|
||||
new SignatureFormatter(),
|
||||
new EntitySlotFormatter(),
|
||||
new EntityFormatter(),
|
||||
new JaggedArrayFormatter<int>(-1),
|
||||
new JaggedArrayFormatter<(int,int)>((-1,-1)),
|
||||
new JaggedArrayFormatter<EntityData>(new EntityData(null!, new Slot(-1, -1), -1)),
|
||||
new DateTimeFormatter("yyyy-MM-dd HH:mm:ss"),
|
||||
new NullableDateTimeFormatter("yyyy-MM-dd HH:mm:ss")
|
||||
];
|
||||
|
||||
/// <summary>
|
||||
/// The default formatters used to (de)serialize a single <see cref="Entity"/>.
|
||||
/// </summary>
|
||||
private readonly IJsonFormatter[] _singleEntityFormatters =
|
||||
[
|
||||
new ComponentTypeFormatter(),
|
||||
new SignatureFormatter(),
|
||||
new SingleEntityFormatter(),
|
||||
new DateTimeFormatter("yyyy-MM-dd HH:mm:ss"),
|
||||
new NullableDateTimeFormatter("yyyy-MM-dd HH:mm:ss")
|
||||
];
|
||||
|
||||
// It can `not` garbage collect and create is slightly high cost.
|
||||
// so you should store to static field.
|
||||
private readonly IJsonFormatterResolver _formatterResolver;
|
||||
|
||||
// CompositeResolver.Create can create dynamic composite resolver.
|
||||
// It can `not` garbage collect and create is slightly high cost.
|
||||
// so you should store to static field.
|
||||
private readonly IJsonFormatterResolver _singleEntityFormatterResolver;
|
||||
|
||||
/// <summary>
|
||||
/// The static constructor gets called during compile time to setup the serializer.
|
||||
/// </summary>
|
||||
public ArchJsonSerializer(params IJsonFormatter[] custFormatters)
|
||||
{
|
||||
// Register all important jsonformatters
|
||||
_formatterResolver = CompositeResolver.Create(
|
||||
[.. _formatters, .. custFormatters],
|
||||
[
|
||||
EnumResolver.UnderlyingValue,
|
||||
StandardResolver.AllowPrivateExcludeNullSnakeCase,
|
||||
BuiltinResolver.Instance,
|
||||
DynamicGenericResolver.Instance,
|
||||
]
|
||||
);
|
||||
|
||||
_singleEntityFormatterResolver = CompositeResolver.Create(
|
||||
[.. _singleEntityFormatters, .. custFormatters],
|
||||
[
|
||||
EnumResolver.UnderlyingValue,
|
||||
StandardResolver.AllowPrivateExcludeNullSnakeCase,
|
||||
]
|
||||
);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// The static constructor gets called during compile time to setup the serializer.
|
||||
/// This variant allows custom resolvers to be passed in as well.
|
||||
/// </summary>
|
||||
public ArchJsonSerializer(IJsonFormatter[] custFormatters, IJsonFormatterResolver[] custResolvers)
|
||||
{
|
||||
// Register all important jsonformatters
|
||||
_formatterResolver = CompositeResolver.Create(
|
||||
[.. _formatters, .. custFormatters],
|
||||
[
|
||||
.. custResolvers,
|
||||
.. new[] {
|
||||
EnumResolver.UnderlyingValue,
|
||||
StandardResolver.AllowPrivateExcludeNullSnakeCase,
|
||||
BuiltinResolver.Instance,
|
||||
DynamicGenericResolver.Instance,
|
||||
},
|
||||
]);
|
||||
|
||||
_singleEntityFormatterResolver = CompositeResolver.Create(
|
||||
[.. _singleEntityFormatters, .. custFormatters],
|
||||
[
|
||||
.. custResolvers,
|
||||
.. new[] {
|
||||
EnumResolver.UnderlyingValue,
|
||||
StandardResolver.AllowPrivateExcludeNullSnakeCase,
|
||||
},
|
||||
]);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Serializes the given <see cref="World"/> to a json-string.
|
||||
/// </summary>
|
||||
/// <param name="world">The <see cref="World"/> to serialize.</param>
|
||||
/// <returns>Its json-string.</returns>
|
||||
public string ToJson(World world) => JsonSerializer.ToJsonString(world, _formatterResolver);
|
||||
|
||||
/// <summary>
|
||||
/// Serializes the given <see cref="Entity"/> to a json-string.
|
||||
/// </summary>
|
||||
/// <param name="world">The <see cref="World"/> the entity belongs to..</param>
|
||||
/// <param name="entity">The <see cref="Entity"/>.</param>
|
||||
/// <returns>Its json-string.</returns>
|
||||
/// <returns>A json-string of the entity with all its components.</returns>
|
||||
public string ToJson(World world, Entity entity)
|
||||
{
|
||||
(_singleEntityFormatters[2] as SingleEntityFormatter)!.EntityWorld = world;
|
||||
return JsonSerializer.ToJsonString(entity, _singleEntityFormatterResolver);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Deserializes the given json <see cref="string"/> to a <see cref="World"/>.
|
||||
/// </summary>
|
||||
/// <param name="jsonWorld">The json <see cref="string"/> to deserialize.</param>
|
||||
/// <returns>A new <see cref="World"/>.</returns>
|
||||
public World FromJson(string jsonWorld) => JsonSerializer.Deserialize<World>(jsonWorld, _formatterResolver);
|
||||
|
||||
/// <summary>
|
||||
/// Deserializes the given json <see cref="string"/> to a <see cref="World"/>.
|
||||
/// <remarks>The deserialized <see cref="Entity"/> will receive a new id and a new worldId.</remarks>
|
||||
/// </summary>
|
||||
/// <param name="world">The <see cref="World"/> to deserialize the entity into.</param>
|
||||
/// <param name="jsonEntity">The json <see cref="string"/> of the entity to deserialize.</param>
|
||||
/// <returns>A new <see cref="Entity"/>.</returns>
|
||||
public Entity FromJson(World world, string jsonEntity)
|
||||
{
|
||||
(_singleEntityFormatters[2] as SingleEntityFormatter)!.EntityWorld = world;
|
||||
return JsonSerializer.Deserialize<Entity>(jsonEntity, _singleEntityFormatterResolver);
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public byte[] Serialize(World world, Entity entity)
|
||||
{
|
||||
(_singleEntityFormatters[2] as SingleEntityFormatter)!.EntityWorld = world;
|
||||
return JsonSerializer.Serialize(entity, _singleEntityFormatterResolver);
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public void Serialize(Stream stream, World world, Entity entity)
|
||||
{
|
||||
(_singleEntityFormatters[1] as SingleEntityFormatter)!.EntityWorld = world;
|
||||
JsonSerializer.Serialize(stream, entity, _singleEntityFormatterResolver);
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public void Serialize(IBufferWriter<byte> writer, World world, Entity entity) => throw new NotImplementedException();
|
||||
|
||||
/// <inheritdoc/>
|
||||
public Entity Deserialize(World world, byte[] entity)
|
||||
{
|
||||
(_singleEntityFormatters[2] as SingleEntityFormatter)!.EntityWorld = world;
|
||||
return JsonSerializer.Deserialize<Entity>(entity, _singleEntityFormatterResolver);
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public Entity Deserialize(Stream stream, World world)
|
||||
{
|
||||
(_singleEntityFormatters[2] as SingleEntityFormatter)!.EntityWorld = world;
|
||||
return JsonSerializer.Deserialize<Entity>(stream, _singleEntityFormatterResolver);
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public byte[] Serialize(World world) => JsonSerializer.Serialize(world, _formatterResolver);
|
||||
|
||||
/// <inheritdoc/>
|
||||
public void Serialize(Stream stream, World world) => JsonSerializer.Serialize(stream, world, _formatterResolver);
|
||||
|
||||
/// <inheritdoc/>
|
||||
public void Serialize(IBufferWriter<byte> writer, World world) => throw new NotImplementedException();
|
||||
|
||||
/// <inheritdoc/>
|
||||
public World Deserialize(byte[] world) => JsonSerializer.Deserialize<World>(world, _formatterResolver);
|
||||
|
||||
/// <inheritdoc/>
|
||||
public World Deserialize(Stream stream) => JsonSerializer.Deserialize<World>(stream, _formatterResolver);
|
||||
}
|
||||
1
src/Arch.Extended/Arch.Persistence/Serializer.cs.uid
Normal file
1
src/Arch.Extended/Arch.Persistence/Serializer.cs.uid
Normal file
@@ -0,0 +1 @@
|
||||
uid://8hihhfy6hol
|
||||
139
src/Arch.Extended/Arch.Persistence/StreamBufferWriter.cs
Normal file
139
src/Arch.Extended/Arch.Persistence/StreamBufferWriter.cs
Normal file
@@ -0,0 +1,139 @@
|
||||
using System;
|
||||
using System.Buffers;
|
||||
using System.IO;
|
||||
|
||||
namespace Arch.Persistence;
|
||||
|
||||
/// <summary>
|
||||
/// The <see cref="StreamBufferWriter"/> class
|
||||
/// is a small wrapper around a <see cref="Stream"/> implementing a <see cref="IBufferWriter{T}"/>.
|
||||
/// It buffers incoming bytes in an internally stored array and flushes it regulary into the <see cref="_destination"/>-<see cref="Stream"/>.
|
||||
/// </summary>
|
||||
public sealed class StreamBufferWriter : IBufferWriter<byte>, IDisposable
|
||||
{
|
||||
/// <summary>
|
||||
/// The buffer.
|
||||
/// </summary>
|
||||
private byte[] _buffer;
|
||||
|
||||
/// <summary>
|
||||
/// The <see cref="Stream"/>.
|
||||
/// </summary>
|
||||
private readonly Stream _destination;
|
||||
|
||||
/// <summary>
|
||||
// / If this instance owns the <see cref="_destination"/> stream.
|
||||
/// </summary>
|
||||
private readonly bool _ownsStream;
|
||||
|
||||
/// <summary>
|
||||
/// The current position and the amount of total leased bytes.
|
||||
/// </summary>
|
||||
private int _position, _leased;
|
||||
|
||||
/// <summary>
|
||||
/// Creates a new <see cref="StreamBufferWriter"/> instance.
|
||||
/// </summary>
|
||||
/// <param name="destination">The <see cref="Stream"/>.</param>
|
||||
/// <param name="bufferSize">The buffer-size of the <see cref="_buffer"/>.</param>
|
||||
/// <param name="ownsStream">If it owns the stream.</param>
|
||||
public StreamBufferWriter(Stream destination, int bufferSize = 1024, bool ownsStream = true)
|
||||
{
|
||||
const int minBufferSize = 128;
|
||||
if (bufferSize < minBufferSize)
|
||||
{
|
||||
bufferSize = minBufferSize;
|
||||
}
|
||||
|
||||
_buffer = ArrayPool<byte>.Shared.Rent(bufferSize);
|
||||
_ownsStream = ownsStream;
|
||||
_destination = destination;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Leases an amount of bytes from the <see cref="_buffer"/>.
|
||||
/// </summary>
|
||||
/// <param name="sizeHint">The total amount.</param>
|
||||
/// <returns>The leased amount.</returns>
|
||||
private int Lease(int sizeHint)
|
||||
{
|
||||
var available = _buffer.Length - _position;
|
||||
if (available < sizeHint && _position != 0)
|
||||
{ // try to get more
|
||||
Flush();
|
||||
available = _buffer.Length - _position;
|
||||
}
|
||||
|
||||
_leased = available;
|
||||
return available;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Flushes the buffered bytes to the <see cref="_destination"/>.
|
||||
/// </summary>
|
||||
/// <param name="flushUnderlyingStream">If it also should flush the <see cref="Stream"/>.</param>
|
||||
public void Flush(bool flushUnderlyingStream = false)
|
||||
{
|
||||
if (_position != 0)
|
||||
{
|
||||
_destination.Write(_buffer, 0, _position);
|
||||
_position = 0;
|
||||
}
|
||||
if (flushUnderlyingStream)
|
||||
{
|
||||
_destination.Flush();
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Advances the buffer, notifies this instance that there was something new written into the <see cref="_buffer"/> memory.
|
||||
/// </summary>
|
||||
/// <param name="count">The amount of bytes written.</param>
|
||||
/// <exception cref="ArgumentOutOfRangeException">Throws if we are out of memory.</exception>
|
||||
void IBufferWriter<byte>.Advance(int count)
|
||||
{
|
||||
if (count > _leased || count < 0)
|
||||
throw new ArgumentOutOfRangeException(nameof(count));
|
||||
_position += count;
|
||||
_leased = 0;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns a partion of the <see cref="_buffer"/> as a <see cref="Memory{T}"/>.
|
||||
/// </summary>
|
||||
/// <param name="sizeHint">The total amount.</param>
|
||||
/// <returns>The new <see cref="Memory{T}"/> instance.</returns>
|
||||
Memory<byte> IBufferWriter<byte>.GetMemory(int sizeHint)
|
||||
{
|
||||
var actual = Lease(sizeHint);
|
||||
return new Memory<byte>(_buffer, _position, actual);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns a partion of the <see cref="_buffer"/> as a <see cref="Span{T}"/>.
|
||||
/// </summary>
|
||||
/// <param name="sizeHint">The total amount.</param>
|
||||
/// <returns>The new <see cref="Span{T}"/> instance.</returns>
|
||||
Span<byte> IBufferWriter<byte>.GetSpan(int sizeHint)
|
||||
{
|
||||
var actual = Lease(sizeHint);
|
||||
return new Span<byte>(_buffer, _position, actual);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Disposes this instance, flushes and releases all memory.
|
||||
/// </summary>
|
||||
public void Dispose()
|
||||
{
|
||||
Flush(true);
|
||||
|
||||
var tmp = _buffer;
|
||||
_buffer = null!;
|
||||
ArrayPool<byte>.Shared.Return(tmp);
|
||||
|
||||
if (_ownsStream)
|
||||
{
|
||||
_destination.Dispose();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1 @@
|
||||
uid://dbb3dlqyskwqs
|
||||
@@ -0,0 +1,125 @@
|
||||
namespace Arch.Relationships;
|
||||
|
||||
using global::System.Diagnostics.Contracts;
|
||||
using global::System.Runtime.CompilerServices;
|
||||
using Arch.Core;
|
||||
|
||||
#if !PURE_ECS
|
||||
|
||||
/// <summary>
|
||||
/// The <see cref="EntityRelationshipExtensions"/> class
|
||||
/// stores several methods to forward relationship methods from the <see cref="World"/> to the <see cref="Entity"/>.
|
||||
/// </summary>
|
||||
public static class EntityRelationshipExtensions
|
||||
{
|
||||
|
||||
/// <summary>
|
||||
/// Adds a new relationship to the <see cref="Entity"/>.
|
||||
/// </summary>
|
||||
/// <param name="source">The source <see cref="Entity"/> of the relationship.</param>
|
||||
/// <param name="target">The target <see cref="Entity"/> of the relationship.</param>
|
||||
// / <typeparam name="T">The relationship type.</typeparam>
|
||||
/// <param name="relationship">The relationship instance.</param>
|
||||
public static void AddRelationship<T>(this in Entity source, Entity target, T relationship = default!)
|
||||
{
|
||||
var world = World.Worlds[source.WorldId];
|
||||
world.AddRelationship(source, target, relationship);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Sets a relationship to the <see cref="Entity"/> by updating its relationship data.
|
||||
/// </summary>
|
||||
/// <param name="source">The source <see cref="Entity"/> of the relationship.</param>
|
||||
/// <param name="target">The target <see cref="Entity"/> of the relationship.</param>
|
||||
/// <typeparam name="T">The relationship type.</typeparam>
|
||||
/// <param name="relationship">The relationship instance.</param>
|
||||
public static void SetRelationship<T>(this in Entity source, Entity target, T relationship = default!)
|
||||
{
|
||||
var world = World.Worlds[source.WorldId];
|
||||
world.SetRelationship(source, target, relationship);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Checks if an <see cref="Entity"/> has a certain relationship.
|
||||
/// </summary>
|
||||
/// <typeparam name="T">The relationship type.</typeparam>
|
||||
/// <param name="source">The source <see cref="Entity"/> of the relationship.</param>
|
||||
/// <param name="target">The target <see cref="Entity"/> of the relationship.</param>
|
||||
/// <returns>True if it has the desired relationship, otherwise false.</returns>
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining), Pure]
|
||||
public static bool HasRelationship<T>(this in Entity source, Entity target)
|
||||
{
|
||||
var world = World.Worlds[source.WorldId];
|
||||
return world.HasRelationship<T>(source, target);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Checks if an <see cref="Entity"/> has a certain relationship.
|
||||
/// </summary>
|
||||
/// <typeparam name="T">The relationship type.</typeparam>
|
||||
/// <param name="source">The source <see cref="Entity"/> of the relationship.</param>
|
||||
/// <returns>True if it has the desired relationship, otherwise false.</returns>
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining), Pure]
|
||||
public static bool HasRelationship<T>(this in Entity source)
|
||||
{
|
||||
var world = World.Worlds[source.WorldId];
|
||||
return world.HasRelationship<T>(source);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns a relationship of an <see cref="Entity"/>.
|
||||
/// </summary>
|
||||
/// <typeparam name="T">The relationship type.</typeparam>
|
||||
/// <param name="source">The source <see cref="Entity"/> of the relationship.</param>
|
||||
/// <param name="target">The target <see cref="Entity"/> of the relationship.</param>
|
||||
/// <returns>The relationship.</returns>
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining), Pure]
|
||||
public static T GetRelationship<T>(this in Entity source, Entity target)
|
||||
{
|
||||
var world = World.Worlds[source.WorldId];
|
||||
return world.GetRelationship<T>(source, target);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns a relationship of an <see cref="Entity"/>.
|
||||
/// </summary>
|
||||
/// <typeparam name="T">The relationship type.</typeparam>
|
||||
/// <param name="source">The source <see cref="Entity"/> of the relationship.</param>
|
||||
/// <returns>The <see cref="Relationship{T}"/>.</returns>
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining), Pure]
|
||||
public static ref Relationship<T> GetRelationships<T>(this in Entity source)
|
||||
{
|
||||
var world = World.Worlds[source.WorldId];
|
||||
return ref world.GetRelationships<T>(source);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Tries to return an <see cref="Entity"/>s relationship of the specified type.
|
||||
/// Will copy the relationship if its a struct.
|
||||
/// </summary>
|
||||
/// <typeparam name="T">The relationship type.</typeparam>
|
||||
/// <param name="source">The source <see cref="Entity"/> of the relationship.</param>
|
||||
/// <param name="target">The target <see cref="Entity"/> of the relationship.</param>
|
||||
/// <param name="relationship">The found relationship.</param>
|
||||
/// <returns>True if it exists, otherwise false.</returns>
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining), Pure]
|
||||
public static bool TryGetRelationship<T>(this in Entity source, Entity target, out T relationship)
|
||||
{
|
||||
var world = World.Worlds[source.WorldId];
|
||||
return world.TryGetRelationship(source, target, out relationship);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Removes a relationship from an <see cref="Entity"/>.
|
||||
/// </summary>
|
||||
/// <typeparam name="T">The relationship type.</typeparam>
|
||||
/// <param name="source">The <see cref="Entity"/> to remove the relationship from.</param>
|
||||
/// <param name="target">The target <see cref="Entity"/> of the relationship.</param>
|
||||
public static void RemoveRelationship<T>(this in Entity source, Entity target)
|
||||
{
|
||||
var world = World.Worlds[source.WorldId];
|
||||
world.RemoveRelationship<T>(source, target);
|
||||
}
|
||||
}
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1 @@
|
||||
uid://574q5d6c3s6y
|
||||
58
src/Arch.Extended/Arch.Relationships/Enumerators.cs
Normal file
58
src/Arch.Extended/Arch.Relationships/Enumerators.cs
Normal file
@@ -0,0 +1,58 @@
|
||||
namespace Arch.Relationships;
|
||||
|
||||
using global::System;
|
||||
using global::System.Collections.Generic;
|
||||
using Arch.Core;
|
||||
|
||||
/// <summary>
|
||||
/// The <see cref="SortedListEnumerator{TValue}"/> struct
|
||||
/// is a enumerator to enumerate a passed <see cref="SortedList{TKey,TValue}"/> in an efficient way.
|
||||
/// </summary>
|
||||
/// <typeparam name="TValue"></typeparam>
|
||||
public struct SortedListEnumerator<TValue>
|
||||
{
|
||||
private readonly SortedList<Entity, TValue> _sortedList;
|
||||
private int _currentIndex;
|
||||
|
||||
/// <summary>
|
||||
/// Constructor.
|
||||
/// </summary>
|
||||
/// <param name="list">List.</param>
|
||||
public SortedListEnumerator(SortedList<Entity, TValue> list)
|
||||
{
|
||||
_sortedList = list;
|
||||
_currentIndex = -1;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Current.
|
||||
/// </summary>
|
||||
public readonly KeyValuePair<Entity, TValue> Current
|
||||
{
|
||||
get
|
||||
{
|
||||
if (_currentIndex == -1 || _currentIndex >= _sortedList.Count)
|
||||
{
|
||||
throw new InvalidOperationException();
|
||||
}
|
||||
|
||||
var key = _sortedList.Keys[_currentIndex];
|
||||
var value = _sortedList.Values[_currentIndex];
|
||||
return new KeyValuePair<Entity, TValue>(key, value);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Moves to the next element in the enumerator.
|
||||
/// </summary>
|
||||
public bool MoveNext()
|
||||
{
|
||||
_currentIndex++;
|
||||
return _currentIndex < _sortedList.Count;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Resets the enumerator to its initial position.
|
||||
/// </summary>
|
||||
public void Reset() => _currentIndex = -1;
|
||||
}
|
||||
1
src/Arch.Extended/Arch.Relationships/Enumerators.cs.uid
Normal file
1
src/Arch.Extended/Arch.Relationships/Enumerators.cs.uid
Normal file
@@ -0,0 +1 @@
|
||||
uid://br3hmo40b0kmx
|
||||
38
src/Arch.Extended/Arch.Relationships/InRelationship.cs
Normal file
38
src/Arch.Extended/Arch.Relationships/InRelationship.cs
Normal file
@@ -0,0 +1,38 @@
|
||||
namespace Arch.Relationships;
|
||||
|
||||
using Arch.Core;
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// The struct <see cref="InRelationship"/>
|
||||
/// represents a reference to a <see cref="Relationship{T}"/>.
|
||||
/// It sits on an <see cref="Entity"/> to indicate in which other <see cref="Relationship{T}"/>s it is involved in.
|
||||
/// </summary>
|
||||
internal readonly struct InRelationship
|
||||
{
|
||||
/// <summary>
|
||||
/// The id of the <see cref="Relationship{T}"/>-Component that this <see cref="InRelationship"/> points to.
|
||||
/// Basically the <see cref="Relationship{T}"/> the <see cref="Entity"/> is in.
|
||||
/// TODO: Uhmm... how the heck do we convert the Id back to the <see cref="ComponentType"/>?
|
||||
/// </summary>
|
||||
public readonly int ComponentTypeId;
|
||||
|
||||
/// <summary>
|
||||
/// Creates a new <see cref="InRelationship"/> instance.
|
||||
/// </summary>
|
||||
/// <param name="targetRelation">The <see cref="ComponentType"/> that represents the relation.</param>
|
||||
internal InRelationship(ComponentType targetRelation)
|
||||
{
|
||||
ComponentTypeId = targetRelation.Id;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Creates a new <see cref="InRelationship"/> instance.
|
||||
/// <remarks>Mostly for binary serialization.</remarks>
|
||||
/// </summary>
|
||||
/// <param name="componentTypeId">The <see cref="ComponentTypeId"/>.</param>
|
||||
internal InRelationship(int componentTypeId)
|
||||
{
|
||||
ComponentTypeId = componentTypeId;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1 @@
|
||||
uid://cu62xtv73hjq7
|
||||
146
src/Arch.Extended/Arch.Relationships/Relationship.cs
Normal file
146
src/Arch.Extended/Arch.Relationships/Relationship.cs
Normal file
@@ -0,0 +1,146 @@
|
||||
namespace Arch.Relationships;
|
||||
|
||||
using global::System.Collections.Generic;
|
||||
using global::System.Runtime.CompilerServices;
|
||||
using Arch.Core;
|
||||
|
||||
/// <summary>
|
||||
/// The <see cref="IRelationship"/> interface
|
||||
/// is an interface that provides all methods required to act as a relationship.
|
||||
/// </summary>
|
||||
internal interface IRelationship
|
||||
{
|
||||
/// <summary>
|
||||
/// The amount of relationships currently in the buffer.
|
||||
/// </summary>
|
||||
int Count
|
||||
{
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
get;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Removes the buffer as a component from the given world and entity.
|
||||
/// </summary>
|
||||
/// <param name="world"></param>
|
||||
/// <param name="source"></param>
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
internal void Destroy(World world, Entity source);
|
||||
|
||||
/// <summary>
|
||||
/// Removes the relationship targeting <paramref name="target"/> from this buffer.
|
||||
/// </summary>
|
||||
/// <param name="target">The <see cref="Entity"/> in the relationship to remove.</param>
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
void Remove(Entity target);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// A buffer storing relationships of <see cref="Entity"/> and <typeparamref name="T"/>.
|
||||
/// </summary>
|
||||
/// <typeparam name="T">The type of the second relationship element.</typeparam>
|
||||
public class Relationship<T> : IRelationship
|
||||
{
|
||||
|
||||
/// <summary>
|
||||
/// Its relations.
|
||||
/// </summary>
|
||||
internal readonly SortedList<Entity, T> _elements;
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of an <see cref="Relationship{T}"/>.
|
||||
/// </summary>
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
internal Relationship()
|
||||
{
|
||||
_elements = [];
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of an <see cref="Relationship{T}"/>.
|
||||
/// <remarks>Mostly for binary serialization.</remarks>
|
||||
/// </summary>
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
internal Relationship(SortedList<Entity, T> elements)
|
||||
{
|
||||
_elements = elements;
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
int IRelationship.Count
|
||||
{
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
get => _elements.Count;
|
||||
}
|
||||
|
||||
/// <inheritdoc cref="IRelationship.Count"/>
|
||||
internal int Count
|
||||
{
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
get => ((IRelationship)this).Count;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Adds a relationship to this buffer.
|
||||
/// </summary>
|
||||
/// <param name="relationship">The instance of the relationship.</param>
|
||||
/// <param name="target">The target of the relationship.</param>
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
internal void Add(in T relationship, Entity target) => _elements.Add(target, relationship);
|
||||
|
||||
/// <summary>
|
||||
/// Sets the stored <typeparamref name="T"/> for the given <see cref="Entity"/>.
|
||||
/// </summary>
|
||||
/// <param name="entity">The <see cref="Entity"/>.</param>
|
||||
/// <param name="data">The data to store.</param>
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public void Set(Entity entity, T data = default!) => _elements[entity] = data;
|
||||
|
||||
/// <summary>
|
||||
/// Determines whether the given <see cref="Relationship{T}"/> contains the passed <see cref="Entity"/> or not.
|
||||
/// </summary>
|
||||
/// <param name="entity">The <see cref="Entity"/>.</param>
|
||||
/// <returns>True or false.</returns>
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public bool Contains(Entity entity) => _elements.ContainsKey(entity);
|
||||
|
||||
/// <summary>
|
||||
/// Returns the stored <typeparamref name="T"/> for the given <see cref="Entity"/>.
|
||||
/// </summary>
|
||||
/// <param name="entity">The <see cref="Entity"/>.</param>
|
||||
/// <returns>The stored <typeparamref name="T"/>.</returns>
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public T Get(Entity entity) => _elements[entity];
|
||||
|
||||
/// <summary>
|
||||
/// Returns the stored <typeparamref name="T"/> for the given <see cref="Entity"/>.
|
||||
/// </summary>
|
||||
/// <param name="entity">The <see cref="Entity"/>.</param>
|
||||
/// <param name="value">The stored <typeparamref name="T"/>.</param>
|
||||
/// <returns>The stored <typeparamref name="T"/>.</returns>
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public bool TryGetValue(Entity entity, out T value) => _elements.TryGetValue(entity, out value!);
|
||||
|
||||
/// <inheritdoc/>
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
void IRelationship.Remove(Entity target) => _elements.Remove(target);
|
||||
|
||||
/// <inheritdoc cref="IRelationship.Remove(Entity)"/>
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
internal void Remove(Entity target) => ((IRelationship)this).Remove(target);
|
||||
|
||||
/// <inheritdoc/>
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
void IRelationship.Destroy(World world, Entity source) => world.Remove<Relationship<T>>(source);
|
||||
|
||||
/// <inheritdoc cref="IRelationship.Destroy(World, Entity)"/>
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
internal void Destroy(World world, Entity source) => ((IRelationship)this).Destroy(world, source);
|
||||
|
||||
/// <summary>
|
||||
/// Creates a new <see cref="SortedListEnumerator{TValue}"/>.
|
||||
/// </summary>
|
||||
/// <returns>The new <see cref="SortedListEnumerator{TValue}"/>.</returns>
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public SortedListEnumerator<T> GetEnumerator() => new(_elements);
|
||||
};
|
||||
1
src/Arch.Extended/Arch.Relationships/Relationship.cs.uid
Normal file
1
src/Arch.Extended/Arch.Relationships/Relationship.cs.uid
Normal file
@@ -0,0 +1 @@
|
||||
uid://d2oi4itpntwl3
|
||||
@@ -0,0 +1,272 @@
|
||||
// [assembly:InternalsVisibleTo("Arch.Relationships.Tests")]
|
||||
namespace Arch.Relationships;
|
||||
|
||||
using global::System.Diagnostics.Contracts;
|
||||
using global::System.Runtime.CompilerServices;
|
||||
using Arch.Core;
|
||||
|
||||
/// <summary>
|
||||
/// The <see cref="WorldRelationshipExtensions"/> class
|
||||
/// stores several extension methods for relationships handling.
|
||||
/// </summary>
|
||||
public static class WorldRelationshipExtensions
|
||||
{
|
||||
|
||||
#if EVENTS
|
||||
|
||||
/// <summary>
|
||||
/// Subscribes to entity destruction events to cleanup their relations.
|
||||
/// </summary>
|
||||
public static void HandleRelationshipCleanup(this World world)
|
||||
{
|
||||
world.SubscribeEntityDestroyed((in Entity entity) => CleanupRelationships(world, in entity));
|
||||
}
|
||||
|
||||
// TODO: Probably someone will kill me for the dark magic that happens down below.
|
||||
/// <summary>
|
||||
/// Cleans up all relations of the passed <see cref="Entity"/>.
|
||||
/// </summary>
|
||||
/// <param name="world"></param>
|
||||
/// <param name="entity"></param>
|
||||
public static void CleanupRelationships(this World world, in Entity entity)
|
||||
{
|
||||
ref var relationships = ref world.TryGetRefRelationships<InRelationship>(entity, out var exists);
|
||||
if (!exists)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
foreach (var (target, inRelationship) in relationships.Elements)
|
||||
{
|
||||
var id = inRelationship.ComponentTypeId;
|
||||
var componentType = new ComponentType(id, 0);
|
||||
|
||||
// Get slots, chunk and array to prevent entity.Get(type) object allocation
|
||||
ref readonly var chunk = ref world.GetChunk(target);
|
||||
var array = chunk.GetArray(componentType);
|
||||
var relationshipsArray = Unsafe.As<IRelationship[]>(array);
|
||||
|
||||
var slot = world.GetSlot(target);
|
||||
var relationship = relationshipsArray[slot.Index];
|
||||
relationship.Remove(entity);
|
||||
|
||||
if (relationship.Count == 0)
|
||||
{
|
||||
relationship.Destroy(world, target);
|
||||
}
|
||||
|
||||
ref var targetRelationships = ref world.TryGetRefRelationships<InRelationship>(target, out exists);
|
||||
if (!exists)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
targetRelationships.Remove(entity);
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
/// <summary>
|
||||
/// Adds a new relationship to the <see cref="Entity"/>.
|
||||
/// </summary>
|
||||
/// <param name="world">World.</param>
|
||||
/// <param name="source">The source <see cref="Entity"/> of the relationship.</param>
|
||||
/// <param name="target">The target <see cref="Entity"/> of the relationship.</param>
|
||||
/// <typeparam name="T">The relationship type.</typeparam>
|
||||
/// <param name="relationship">The relationship instance.</param>
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static void AddRelationship<T>(this World world, Entity source, Entity target, in T relationship = default!)
|
||||
{
|
||||
ref var buffer = ref world.AddOrGetRelationships<T>(source);
|
||||
buffer.Add(in relationship, target);
|
||||
|
||||
var targetComp = new InRelationship(Component<Relationship<T>>.ComponentType);
|
||||
ref var targetBuffer = ref world.AddOrGetRelationships<InRelationship>(target);
|
||||
targetBuffer.Add(in targetComp, source);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Ensures the existence of a relationship on an <see cref="Entity"/>.
|
||||
/// </summary>
|
||||
/// <typeparam name="T">The relationship type.</typeparam>
|
||||
/// <param name="world">World.</param>
|
||||
/// <param name="source">The source <see cref="Entity"/> of the relationship.</param>
|
||||
/// <param name="target">The target <see cref="Entity"/> of the relationship.</param>
|
||||
/// <param name="relationship">The relationship value used if its being added.</param>
|
||||
/// <returns>The relationship.</returns>
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static T AddOrGetRelationship<T>(this World world, Entity source, Entity target, in T relationship = default!)
|
||||
{
|
||||
ref var relationships = ref world.TryGetRefRelationships<T>(source, out var exists);
|
||||
if (exists)
|
||||
{
|
||||
return relationships.Get(target);
|
||||
}
|
||||
|
||||
world.AddRelationship(source, target, in relationship);
|
||||
return world.GetRelationship<T>(source, target);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Ensures the existence of a buffer of relationships on an <see cref="Entity"/>.
|
||||
/// </summary>
|
||||
/// <param name="world">World.</param>
|
||||
/// <param name="source">The source <see cref="Entity"/> of the relationships.</param>
|
||||
/// <typeparam name="T">The relationship type.</typeparam>
|
||||
/// <returns>The relationships.</returns>
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
internal static ref Relationship<T> AddOrGetRelationships<T>(this World world, Entity source)
|
||||
{
|
||||
ref var component = ref world.TryGetRef<Relationship<T>>(source, out var exists);
|
||||
if (exists)
|
||||
{
|
||||
return ref component!;
|
||||
}
|
||||
|
||||
world.Add(source, new Relationship<T>());
|
||||
return ref world.Get<Relationship<T>>(source);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Sets the existing relationship data.
|
||||
/// </summary>
|
||||
/// <typeparam name="T">The relationship type.</typeparam>
|
||||
/// <param name="world">World.</param>
|
||||
/// <param name="source">The source <see cref="Entity"/> of the relationship.</param>
|
||||
/// <param name="target">The target <see cref="Entity"/> of the relationship.</param>
|
||||
/// <param name="relationship">The new data.</param>
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static void SetRelationship<T>(this World world, Entity source, Entity target, in T relationship = default!)
|
||||
{
|
||||
ref var relationships = ref world.GetRelationships<T>(source);
|
||||
relationships.Set(target, relationship);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Checks if an <see cref="Entity"/> has a certain relationship.
|
||||
/// </summary>
|
||||
/// <typeparam name="T">The relationship type.</typeparam>
|
||||
/// <param name="world">World.</param>
|
||||
/// <param name="source">The source <see cref="Entity"/> of the relationship.</param>
|
||||
/// <param name="target">The target <see cref="Entity"/> of the relationship.</param>
|
||||
/// <returns>True if it has the desired relationship, otherwise false.</returns>
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining), Pure]
|
||||
public static bool HasRelationship<T>(this World world, Entity source, Entity target)
|
||||
{
|
||||
ref var relationships = ref world.TryGetRefRelationships<T>(source, out var exists);
|
||||
if (!exists)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
return relationships.Contains(target);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Checks if an <see cref="Entity"/> has a certain relationship.
|
||||
/// </summary>
|
||||
/// <typeparam name="T">The relationship type.</typeparam>
|
||||
/// <param name="world">World.</param>
|
||||
/// <param name="source">The source <see cref="Entity"/> of the relationship.</param>
|
||||
/// <returns>True if it has the desired relationship, otherwise false.</returns>
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining), Pure]
|
||||
public static bool HasRelationship<T>(this World world, Entity source) => world.Has<Relationship<T>>(source);
|
||||
|
||||
/// <summary>
|
||||
/// Returns a relationship of an <see cref="Entity"/>.
|
||||
/// </summary>
|
||||
/// <typeparam name="T">The relationship type.</typeparam>
|
||||
/// <param name="world">World.</param>
|
||||
/// <param name="source">The source <see cref="Entity"/> of the relationship.</param>
|
||||
/// <param name="target">The target <see cref="Entity"/> of the relationship.</param>
|
||||
/// <returns>The relationship.</returns>
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining), Pure]
|
||||
public static T GetRelationship<T>(this World world, Entity source, Entity target)
|
||||
{
|
||||
ref var relationships = ref world.GetRelationships<T>(source);
|
||||
return relationships.Get(target);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Tries to return an <see cref="Entity"/>s relationship of the specified type.
|
||||
/// Will copy the relationship if its a struct.
|
||||
/// </summary>
|
||||
/// <typeparam name="T">The relationship type.</typeparam>
|
||||
/// <param name="world">World.</param>
|
||||
/// <param name="source">The source <see cref="Entity"/> of the relationship.</param>
|
||||
/// <param name="target">The target <see cref="Entity"/> of the relationship.</param>
|
||||
/// <param name="relationship">The found relationship.</param>
|
||||
/// <returns>True if it exists, otherwise false.</returns>
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining), Pure]
|
||||
public static bool TryGetRelationship<T>(this World world, Entity source, Entity target, out T relationship)
|
||||
{
|
||||
ref var relationships = ref world.TryGetRefRelationships<T>(source, out var exists);
|
||||
if (!exists)
|
||||
{
|
||||
relationship = default!;
|
||||
return false;
|
||||
}
|
||||
|
||||
return relationships.TryGetValue(target, out relationship);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns all relationships of the given type of an <see cref="Entity"/>.
|
||||
/// </summary>
|
||||
/// <typeparam name="T">The relationship type.</typeparam>
|
||||
/// <param name="world">World.</param>
|
||||
/// <param name="source">The source <see cref="Entity"/> of the relationship.</param>
|
||||
/// <returns>A reference to the relationships.</returns>
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining), Pure]
|
||||
public static ref Relationship<T> GetRelationships<T>(this World world, Entity source) => ref world.Get<Relationship<T>>(source);
|
||||
|
||||
/// <summary>
|
||||
/// Tries to return an <see cref="Entity"/>s relationships of the specified type.
|
||||
/// </summary>
|
||||
/// <typeparam name="T">The relationship type.</typeparam>
|
||||
/// <param name="world">World.</param>
|
||||
/// <param name="source">The <see cref="Entity"/>.</param>
|
||||
/// <param name="relationships">The found relationships.</param>
|
||||
/// <returns>True if it exists, otherwise false.</returns>
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining), Pure]
|
||||
internal static bool TryGetRelationships<T>(this World world, Entity source, out Relationship<T> relationships) => world.TryGet(source, out relationships!);
|
||||
|
||||
/// <summary>
|
||||
/// Tries to return a reference to an <see cref="Entity"/>s relationships of the
|
||||
/// specified type.
|
||||
/// </summary>
|
||||
/// <typeparam name="T">The relationship type.</typeparam>
|
||||
/// <param name="world">World.</param>
|
||||
/// <param name="source">The <see cref="Entity"/>.</param>
|
||||
/// <param name="exists">True if it exists, otherwise false.</param>
|
||||
/// <returns>A reference to the relationships.</returns>
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining), Pure]
|
||||
internal static ref Relationship<T> TryGetRefRelationships<T>(this World world, Entity source, out bool exists) => ref world.TryGetRef<Relationship<T>>(source, out exists);
|
||||
|
||||
/// <summary>
|
||||
/// Removes a relationship from an <see cref="Entity"/>.
|
||||
/// </summary>
|
||||
/// <typeparam name="T">The relationship type.</typeparam>
|
||||
/// <param name="world">World.</param>
|
||||
/// <param name="source">The <see cref="Entity"/> to remove the relationship from.</param>
|
||||
/// <param name="target">The target <see cref="Entity"/> of the relationship.</param>
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static void RemoveRelationship<T>(this World world, Entity source, Entity target)
|
||||
{
|
||||
ref var buffer = ref world.GetRelationships<T>(source);
|
||||
buffer.Remove(target);
|
||||
|
||||
if (buffer.Count == 0)
|
||||
{
|
||||
world.Remove<Relationship<T>>(source);
|
||||
}
|
||||
|
||||
ref var targetBuffer = ref world.GetRelationships<InRelationship>(target);
|
||||
targetBuffer.Remove(source);
|
||||
|
||||
if (targetBuffer.Count == 0)
|
||||
{
|
||||
world.Remove<Relationship<InRelationship>>(target);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1 @@
|
||||
uid://dfnift6mppxam
|
||||
129
src/Blueprints/Blueprint.cs
Normal file
129
src/Blueprints/Blueprint.cs
Normal file
@@ -0,0 +1,129 @@
|
||||
// namespace FoodFactory.Blueprints;
|
||||
|
||||
// using System;
|
||||
// using System.Diagnostics;
|
||||
// using System.Diagnostics.CodeAnalysis;
|
||||
// using Arch.Core;
|
||||
// using SharpYaml.Model;
|
||||
// using SJK.Functional;
|
||||
// using Utf8Json;
|
||||
|
||||
// public record Blueprint(string Name, Func<BlueprintContext, Entity> Factory)
|
||||
// {
|
||||
|
||||
// }
|
||||
// public record Blueprint(string Name, Func<BlueprintContext, Entity> Factory)
|
||||
// {
|
||||
|
||||
// }
|
||||
// public class ComponentBinderRegistry
|
||||
// {
|
||||
// private readonly Dictionary<ComponentType, IComponentBinder> _binder = [];
|
||||
// public void Add<T>(ComponentBinder<T> 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<Entity> 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<string, object> Data)
|
||||
// {
|
||||
// public abstract DataNodeType NodeType { get; }
|
||||
// // public T Get<T>(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<T> As<T>() => Value is T o?Option<T>.Some(o):Option<T>.None;
|
||||
// }
|
||||
// public sealed class SequenceNode(IReadOnlyList<DataNode> values) : DataNode
|
||||
// {
|
||||
// public override DataNodeType NodeType => DataNodeType.Sequence;
|
||||
|
||||
// public IReadOnlyList<DataNode> Values { get; set; } = values;
|
||||
// }
|
||||
// public sealed class MappingNode(IReadOnlyDictionary<string, DataNode> values) : DataNode
|
||||
// {
|
||||
// public override DataNodeType NodeType => DataNodeType.Mapping;
|
||||
|
||||
// public IReadOnlyDictionary<string, DataNode> Values { get; set; } = values;
|
||||
|
||||
// public bool TryGet<T>(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<DataNode>, IJsonFormatter<DataNode>
|
||||
// {
|
||||
// private Dictionary<string,string> _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();
|
||||
// }
|
||||
// }
|
||||
1
src/Blueprints/Blueprint.cs.uid
Normal file
1
src/Blueprints/Blueprint.cs.uid
Normal file
@@ -0,0 +1 @@
|
||||
uid://cj088ogl8uqmt
|
||||
121
src/Blueprints/BlueprintContext.cs
Normal file
121
src/Blueprints/BlueprintContext.cs
Normal file
@@ -0,0 +1,121 @@
|
||||
namespace FoodFactory.Blueprints;
|
||||
|
||||
using Arch.Core;
|
||||
|
||||
public struct BlueprintContext
|
||||
{
|
||||
public World World;
|
||||
public BlueprintId BluePrintId;
|
||||
public object PlaceHolderApi;
|
||||
}
|
||||
// public class ComponentBinderRegistry
|
||||
// {
|
||||
// private readonly Dictionary<ComponentType, IComponentBinder> _binder = [];
|
||||
// public void Add<T>(ComponentBinder<T> 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<Entity> 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<string, object> Data)
|
||||
// {
|
||||
// public abstract DataNodeType NodeType { get; }
|
||||
// // public T Get<T>(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<T> As<T>() => Value is T o?Option<T>.Some(o):Option<T>.None;
|
||||
// }
|
||||
// public sealed class SequenceNode(IReadOnlyList<DataNode> values) : DataNode
|
||||
// {
|
||||
// public override DataNodeType NodeType => DataNodeType.Sequence;
|
||||
|
||||
// public IReadOnlyList<DataNode> Values { get; set; } = values;
|
||||
// }
|
||||
// public sealed class MappingNode(IReadOnlyDictionary<string, DataNode> values) : DataNode
|
||||
// {
|
||||
// public override DataNodeType NodeType => DataNodeType.Mapping;
|
||||
|
||||
// public IReadOnlyDictionary<string, DataNode> Values { get; set; } = values;
|
||||
|
||||
// public bool TryGet<T>(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<DataNode>, IJsonFormatter<DataNode>
|
||||
// {
|
||||
// private Dictionary<string,string> _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();
|
||||
// }
|
||||
// }
|
||||
1
src/Blueprints/BlueprintContext.cs.uid
Normal file
1
src/Blueprints/BlueprintContext.cs.uid
Normal file
@@ -0,0 +1 @@
|
||||
uid://cy4shmpf6ncqn
|
||||
14
src/Blueprints/BlueprintId.cs
Normal file
14
src/Blueprints/BlueprintId.cs
Normal file
@@ -0,0 +1,14 @@
|
||||
namespace FoodFactory.Blueprints;
|
||||
|
||||
using FoodFactory.Core;
|
||||
|
||||
public readonly record struct BlueprintId
|
||||
{
|
||||
public readonly int Id;
|
||||
public CompiledBlueprint Blueprint() => _registry.GetEntry(Id);
|
||||
private static readonly Registry<CompiledBlueprint> _registry = new();
|
||||
public BlueprintId(CompiledBlueprint blueprint)
|
||||
{
|
||||
Id = _registry.GetOrCreate(blueprint);
|
||||
}
|
||||
}
|
||||
1
src/Blueprints/BlueprintId.cs.uid
Normal file
1
src/Blueprints/BlueprintId.cs.uid
Normal file
@@ -0,0 +1 @@
|
||||
uid://b7kmhd6uamhg
|
||||
304
src/Blueprints/BlueprintManger.cs
Normal file
304
src/Blueprints/BlueprintManger.cs
Normal file
@@ -0,0 +1,304 @@
|
||||
namespace FoodFactory.Blueprints;
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Reflection;
|
||||
using FoodFactory.Blueprints.Providers;
|
||||
using FoodFactory.Blueprints.Providers.Converters;
|
||||
using FoodFactory.Items;
|
||||
using Godot;
|
||||
using SharpYaml;
|
||||
|
||||
public interface IBlueprintManger
|
||||
{
|
||||
CompiledBlueprint GetCompiledBlueprint(string name);
|
||||
// ComponentBinderRegistry BinderRegistry { get; }
|
||||
}
|
||||
public class BlueprintManger : IBlueprintManger
|
||||
{
|
||||
public readonly Dictionary<string, CompiledBlueprint> CompiledBlueprints = [];
|
||||
|
||||
// public ComponentBinderRegistry BinderRegistry { get; }
|
||||
|
||||
public BlueprintManger()
|
||||
{
|
||||
|
||||
|
||||
var root = "/home/ronnie/Documents/Godot/Projects/ChickenGameTest/mods";
|
||||
var dir = DirAccess.Open(root);
|
||||
var itemDatas = new List<ItemData>();
|
||||
|
||||
var options = new YamlSerializerOptions()
|
||||
{
|
||||
Converters = [
|
||||
// new ComponentsPatcherConverter(
|
||||
// new(){
|
||||
// [nameof(Temperature)] = typeof(IValueProvider<Temperature>),
|
||||
// [nameof(Tags)] = typeof(IValueProvider<Tags>),
|
||||
// [nameof(Name)] = typeof(IValueProvider<Name>),
|
||||
// [nameof(Mass)] = typeof(IValueProvider<Mass>),
|
||||
// [nameof(Color)] = typeof(IValueProvider<Color>),
|
||||
// [nameof(BurnableTemp)] = typeof(IValueProvider<BurnableTemp>),
|
||||
// }
|
||||
// ),
|
||||
new ComponentTemperatureConverter(),
|
||||
new ComponentTagsConverter(),
|
||||
new ComponentNameConverter(),
|
||||
|
||||
]
|
||||
};
|
||||
foreach (var item in dir.GetFiles())
|
||||
{
|
||||
if (item.EndsWith(".yaml"))
|
||||
{
|
||||
var file = FileAccess.Open(dir.GetCurrentDir() + "/" + item, FileAccess.ModeFlags.Read);
|
||||
var data = YamlSerializer.Deserialize<ItemData>(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);
|
||||
}
|
||||
}
|
||||
|
||||
//TODO Call a Patch method thingy to adjust exsting data before loading
|
||||
|
||||
CompiledBlueprints = LoadBluprints([.. itemDatas]);
|
||||
}
|
||||
private Dictionary<string, CompiledBlueprint> LoadBluprints(ItemData[] all, Dictionary<string, CompiledBlueprint>? existing = null)
|
||||
{
|
||||
var completed = existing is not null ? new(existing) : new Dictionary<string,CompiledBlueprint>();
|
||||
var queue = new Queue<ItemData>(all);
|
||||
while (queue.Any())
|
||||
{
|
||||
reset:
|
||||
var item = queue.Dequeue();
|
||||
var sources = new List<CompiledBlueprint>();
|
||||
foreach (var c in item.Extend)
|
||||
{
|
||||
if (!completed.ContainsKey(c))
|
||||
{
|
||||
queue.Enqueue(item);
|
||||
goto reset;
|
||||
}
|
||||
sources.Add(completed[c]);
|
||||
}
|
||||
var providers = new Dictionary<Type, List<IValueProvider>>();//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 = component.Value;//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)
|
||||
// {
|
||||
|
||||
// // throw new NotImplementedException();
|
||||
// if (!Blueprints.TryGetValue(name, out var blueprint))
|
||||
// {
|
||||
// //TODO this is where this should look through mods/base game
|
||||
// if (name == "Potato")
|
||||
// {
|
||||
// blueprint = new Blueprint("Potato", static ctx => ctx.World.Create(new Name("Potato_raw"),
|
||||
// new Tags("potato", "raw", "vegetable", "burnable"),
|
||||
// new Temperature(40, TemperatureUnit.Fahrenheit),
|
||||
// ctx.BluePrintId,
|
||||
// new Bacteria(BacterialSystem.Resources.Add([new BacteriaData("slamnala", 400)])),
|
||||
// new BurnableTemp(new Temperature(400, TemperatureUnit.Fahrenheit))));//TODO blueprintID should not be passed, but know when blueprint is created, meaning a rework of Blueprint id register
|
||||
// }
|
||||
// else if (name == "Onion")
|
||||
// {
|
||||
// blueprint = new Blueprint("Onion", static ctx => ctx.World.Create(new Name("Onion_raw"),
|
||||
// new Tags("onion", "raw", "vegetable", "burnable", "sliceable"),
|
||||
// new Temperature(71, TemperatureUnit.Fahrenheit),
|
||||
// ctx.BluePrintId,
|
||||
// new BurnableTemp(new Temperature(400, TemperatureUnit.Fahrenheit)),
|
||||
// new Mass(Weight.FromGrams(110)),
|
||||
// new Carbohydrates(Fiber: 2, Starch: 3, Sugars: new(Glucose: 0, Fructose: 1.2f, Sucrose: 1, Lactose: 0))
|
||||
// ));//TODO blueprintID should not be passed, but know when blueprint is created, meaning a rework of Blueprint id register
|
||||
// }
|
||||
// else
|
||||
// {
|
||||
// throw new Exception();
|
||||
// }
|
||||
// }
|
||||
// return blueprint;
|
||||
// }
|
||||
// }
|
||||
// public class ComponentBinderRegistry
|
||||
// {
|
||||
// private readonly Dictionary<ComponentType, IComponentBinder> _binder = [];
|
||||
// public void Add<T>(ComponentBinder<T> 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<Entity> 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<string, object> Data)
|
||||
// {
|
||||
// public abstract DataNodeType NodeType { get; }
|
||||
// // public T Get<T>(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<T> As<T>() => Value is T o?Option<T>.Some(o):Option<T>.None;
|
||||
// }
|
||||
// public sealed class SequenceNode(IReadOnlyList<DataNode> values) : DataNode
|
||||
// {
|
||||
// public override DataNodeType NodeType => DataNodeType.Sequence;
|
||||
|
||||
// public IReadOnlyList<DataNode> Values { get; set; } = values;
|
||||
// }
|
||||
// public sealed class MappingNode(IReadOnlyDictionary<string, DataNode> values) : DataNode
|
||||
// {
|
||||
// public override DataNodeType NodeType => DataNodeType.Mapping;
|
||||
|
||||
// public IReadOnlyDictionary<string, DataNode> Values { get; set; } = values;
|
||||
|
||||
// public bool TryGet<T>(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<DataNode>, IJsonFormatter<DataNode>
|
||||
// {
|
||||
// private Dictionary<string,string> _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();
|
||||
// }
|
||||
// }
|
||||
1
src/Blueprints/BlueprintManger.cs.uid
Normal file
1
src/Blueprints/BlueprintManger.cs.uid
Normal file
@@ -0,0 +1 @@
|
||||
uid://bvf8g3wbikopb
|
||||
48
src/Blueprints/CompiledBlueprint.cs
Normal file
48
src/Blueprints/CompiledBlueprint.cs
Normal file
@@ -0,0 +1,48 @@
|
||||
namespace FoodFactory.Blueprints;
|
||||
|
||||
using Arch.Core;
|
||||
using FoodFactory.Blueprints.Providers;
|
||||
using FoodFactory.Core.Components;
|
||||
|
||||
// public sealed class StaticReflectionCompiledNode<T> : IValueProvider<T> 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<string, object> _data;
|
||||
// private readonly T _template;
|
||||
|
||||
// public StaticReflectionCompiledNode(Dictionary<string, object> 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<T> : IValueProvider<T> 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<string, object> _data;
|
||||
// // private readonly T _template;
|
||||
|
||||
// public RuntimeReflectionCompiledNode(Dictionary<string, object> 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 required string Id { get; init; }
|
||||
public Tags Traits { get; set; }
|
||||
public IValueProvider[][] Providers = [];
|
||||
public Signature Signature;
|
||||
public Entity CreateEmptyItem(BlueprintContext blueprintContext) => blueprintContext.World.Create(Signature);
|
||||
|
||||
}
|
||||
1
src/Blueprints/CompiledBlueprint.cs.uid
Normal file
1
src/Blueprints/CompiledBlueprint.cs.uid
Normal file
@@ -0,0 +1 @@
|
||||
uid://7ncdfun3kqil
|
||||
162
src/Blueprints/Provider/Converters/ComponentTagsConverter.cs
Normal file
162
src/Blueprints/Provider/Converters/ComponentTagsConverter.cs
Normal file
@@ -0,0 +1,162 @@
|
||||
namespace FoodFactory.Blueprints.Providers.Converters;
|
||||
|
||||
using System;
|
||||
using FoodFactory.Core.Components;
|
||||
using SharpYaml.Serialization;
|
||||
public sealed class ComponentTagsConverter : ItemComponentsConverter<Tags>
|
||||
{
|
||||
public override IValueProvider<Tags> Read(YamlReader reader)
|
||||
{
|
||||
if (reader.TokenType == YamlTokenType.StartSequence)
|
||||
{
|
||||
if (reader.GetConverter(typeof(string[])) is not YamlConverter<string[]> converter)
|
||||
{
|
||||
throw new Exception();
|
||||
}
|
||||
return new StaticCompiledNode<Tags>(new Tags(converter.Read(reader)));
|
||||
}
|
||||
if (reader.TokenType == YamlTokenType.StartMapping)
|
||||
{
|
||||
string[] with = [];
|
||||
string[] exclude = [];
|
||||
reader.Read();
|
||||
while (reader.TokenType != YamlTokenType.EndMapping)
|
||||
{
|
||||
var key = reader.ScalarValue;
|
||||
reader.Read();
|
||||
if (key == "With")
|
||||
{
|
||||
if (reader.GetConverter(typeof(string[])) is not YamlConverter<string[]> converter)
|
||||
{
|
||||
throw new Exception();
|
||||
}
|
||||
with = converter.Read(reader);
|
||||
}
|
||||
if (key == "Except")
|
||||
{
|
||||
if (reader.GetConverter(typeof(string[])) is not YamlConverter<string[]> converter)
|
||||
{
|
||||
throw new Exception();
|
||||
}
|
||||
exclude = converter.Read(reader);
|
||||
}
|
||||
reader.Read();
|
||||
}
|
||||
return new FactoryCompiledNode<Tags>((in factory, ref tags) => tags.With(with).WithOut(exclude));
|
||||
}
|
||||
throw new NotSupportedException();
|
||||
}
|
||||
|
||||
public override void Write(YamlWriter writer, IValueProvider<Tags> value) => throw new NotImplementedException();
|
||||
}
|
||||
// public class ComponentBinderRegistry
|
||||
// {
|
||||
// private readonly Dictionary<ComponentType, IComponentBinder> _binder = [];
|
||||
// public void Add<T>(ComponentBinder<T> 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<Entity> 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<string, object> Data)
|
||||
// {
|
||||
// public abstract DataNodeType NodeType { get; }
|
||||
// // public T Get<T>(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<T> As<T>() => Value is T o?Option<T>.Some(o):Option<T>.None;
|
||||
// }
|
||||
// public sealed class SequenceNode(IReadOnlyList<DataNode> values) : DataNode
|
||||
// {
|
||||
// public override DataNodeType NodeType => DataNodeType.Sequence;
|
||||
|
||||
// public IReadOnlyList<DataNode> Values { get; set; } = values;
|
||||
// }
|
||||
// public sealed class MappingNode(IReadOnlyDictionary<string, DataNode> values) : DataNode
|
||||
// {
|
||||
// public override DataNodeType NodeType => DataNodeType.Mapping;
|
||||
|
||||
// public IReadOnlyDictionary<string, DataNode> Values { get; set; } = values;
|
||||
|
||||
// public bool TryGet<T>(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<DataNode>, IJsonFormatter<DataNode>
|
||||
// {
|
||||
// private Dictionary<string,string> _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();
|
||||
// }
|
||||
// }
|
||||
@@ -0,0 +1 @@
|
||||
uid://cxtk4cn40072m
|
||||
@@ -0,0 +1,263 @@
|
||||
namespace FoodFactory.Blueprints.Providers.Converters;
|
||||
|
||||
using System;
|
||||
using FoodFactory.Math;
|
||||
using SharpYaml.Serialization;
|
||||
|
||||
// public interface IComponentBinder
|
||||
// {
|
||||
// ComponentType ComponentType { get; }
|
||||
// void ApplyUntyped(
|
||||
// Entity existing,
|
||||
// IValueProvider data,
|
||||
// BlueprintContext context);
|
||||
// void ApplyUntyped(
|
||||
// Span<Entity> existing,
|
||||
// IValueProvider data,
|
||||
// BlueprintContext context);
|
||||
// }
|
||||
// public class ComponentBinder<T> : IComponentBinder where T : struct
|
||||
// {
|
||||
// public ComponentType ComponentType => Component<T>.ComponentType;
|
||||
|
||||
// public void ApplyUntyped(
|
||||
// Entity existing,
|
||||
// IValueProvider data,
|
||||
// BlueprintContext context)
|
||||
// {
|
||||
// if (data is IValueProvider<T> dataT)
|
||||
// {
|
||||
// ref var component = ref context.World.Get<T>(existing);
|
||||
// Apply(ref component, dataT, context);
|
||||
// return;
|
||||
// }
|
||||
// throw new System.Exception();
|
||||
// }
|
||||
// public void ApplyUntyped(Span<Entity> existing, IValueProvider data, BlueprintContext context)
|
||||
// {
|
||||
// if (data is not IValueProvider<T> dataT)
|
||||
// {
|
||||
// throw new System.Exception();
|
||||
// }
|
||||
// for (int i = 0; i < existing.Length; i++)
|
||||
// {
|
||||
// Apply(ref existing[i].Get<T>(), dataT, context);
|
||||
// }
|
||||
// }
|
||||
|
||||
// public void Apply(
|
||||
// ref T existing,
|
||||
// IValueProvider<T> data,
|
||||
// BlueprintContext context) => data.Resolve(context, ref existing);
|
||||
// }
|
||||
// public class DefaultBinder<T> : ComponentBinder<T> where T : struct
|
||||
// {
|
||||
// public override void Apply(ref T existing, IValueProvider<T> data, BlueprintContext context);
|
||||
|
||||
// }
|
||||
// public class TemperatureBinder : ComponentBinder<Temperature>
|
||||
// {
|
||||
// public override void Apply(ref Temperature existing, IValueProvider<Temperature> data, BlueprintContext context) => data.Resolve(context, ref existing);
|
||||
|
||||
// }
|
||||
public sealed class ComponentTemperatureConverter : ItemComponentsConverter<Temperature>//, IDataCompiler<Temperature>
|
||||
{
|
||||
// public IValueProvider<Temperature> Compile(object data)
|
||||
// {
|
||||
// if (data is IValueProvider<Temperature> t)
|
||||
// {
|
||||
// return t;
|
||||
// }
|
||||
// if (data is string says)
|
||||
// {
|
||||
// if (says == "ambient")
|
||||
// {
|
||||
// return new AmbientTemperatureProvider();
|
||||
// }
|
||||
// }
|
||||
// if (data is float f)
|
||||
// {
|
||||
// return new StaticCompiledNode<Temperature>(new Temperature(f));
|
||||
// }
|
||||
// if (data is double d)
|
||||
// {
|
||||
// return new StaticCompiledNode<Temperature>(new Temperature(d));
|
||||
// }
|
||||
// if (data is Dictionary<string, object> dict)
|
||||
// {
|
||||
// return new StaticReflectionCompiledNode<Temperature>(dict);
|
||||
|
||||
// }
|
||||
// throw new NotSupportedException();
|
||||
// }
|
||||
|
||||
public override IValueProvider<Temperature>? 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<Temperature>(new Temperature(f));
|
||||
}
|
||||
if (double.TryParse(value, out var d))
|
||||
{
|
||||
return new StaticCompiledNode<Temperature>(new Temperature(f));
|
||||
}
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
if (reader.TokenType == YamlTokenType.StartMapping)
|
||||
{
|
||||
reader.Read();
|
||||
IValueProvider<Temperature>? provider = null;
|
||||
while (reader.TokenType != YamlTokenType.EndMapping)
|
||||
{
|
||||
var key = reader.ScalarValue;
|
||||
reader.Read();
|
||||
if (key == nameof(Fahrenheit))
|
||||
{
|
||||
provider = new StaticCompiledNode<Temperature>(new Fahrenheit(float.Parse(reader.ScalarValue)));
|
||||
}
|
||||
if (key == nameof(Celsius))
|
||||
{
|
||||
provider = new StaticCompiledNode<Temperature>(new Celsius(float.Parse(reader.ScalarValue)));
|
||||
}
|
||||
if (key == nameof(Kelvin))
|
||||
{
|
||||
provider = new StaticCompiledNode<Temperature>(new Kelvin(float.Parse(reader.ScalarValue)));
|
||||
}
|
||||
reader.Read();
|
||||
}
|
||||
return provider??throw new NotSupportedException();
|
||||
}
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
|
||||
public override void Write(YamlWriter writer, IValueProvider<Temperature> value) => throw new NotImplementedException();
|
||||
|
||||
public sealed class AmbientTemperatureProvider : IValueProvider<Temperature>
|
||||
{
|
||||
public ProviderMode Mode => ProviderMode.Static;
|
||||
|
||||
public void Resolve(BlueprintContext context, ref Temperature value)
|
||||
{
|
||||
value.Kelvin = (float)context.PlaceHolderApi;
|
||||
}
|
||||
}
|
||||
}
|
||||
// public class ComponentBinderRegistry
|
||||
// {
|
||||
// private readonly Dictionary<ComponentType, IComponentBinder> _binder = [];
|
||||
// public void Add<T>(ComponentBinder<T> 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<Entity> 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<string, object> Data)
|
||||
// {
|
||||
// public abstract DataNodeType NodeType { get; }
|
||||
// // public T Get<T>(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<T> As<T>() => Value is T o?Option<T>.Some(o):Option<T>.None;
|
||||
// }
|
||||
// public sealed class SequenceNode(IReadOnlyList<DataNode> values) : DataNode
|
||||
// {
|
||||
// public override DataNodeType NodeType => DataNodeType.Sequence;
|
||||
|
||||
// public IReadOnlyList<DataNode> Values { get; set; } = values;
|
||||
// }
|
||||
// public sealed class MappingNode(IReadOnlyDictionary<string, DataNode> values) : DataNode
|
||||
// {
|
||||
// public override DataNodeType NodeType => DataNodeType.Mapping;
|
||||
|
||||
// public IReadOnlyDictionary<string, DataNode> Values { get; set; } = values;
|
||||
|
||||
// public bool TryGet<T>(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<DataNode>, IJsonFormatter<DataNode>
|
||||
// {
|
||||
// private Dictionary<string,string> _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();
|
||||
// }
|
||||
// }
|
||||
@@ -0,0 +1 @@
|
||||
uid://dpgrony8su3im
|
||||
131
src/Blueprints/Provider/Converters/ComponentsNameConverter.cs
Normal file
131
src/Blueprints/Provider/Converters/ComponentsNameConverter.cs
Normal file
@@ -0,0 +1,131 @@
|
||||
namespace FoodFactory.Blueprints.Providers.Converters;
|
||||
|
||||
using System;
|
||||
using SharpYaml.Serialization;
|
||||
|
||||
public sealed class ComponentNameConverter : ItemComponentsConverter<Name>
|
||||
{
|
||||
public override IValueProvider<Name> Read(YamlReader reader)
|
||||
{
|
||||
if (reader.TokenType == YamlTokenType.Scalar)
|
||||
{
|
||||
var key = reader.GetScalarValue();
|
||||
reader.Read();
|
||||
return new StaticCompiledNode<Name>(new(key));
|
||||
}
|
||||
return base.Read(reader);
|
||||
}
|
||||
|
||||
public override void Write(YamlWriter writer, IValueProvider<Name> value) => throw new NotImplementedException();
|
||||
}
|
||||
// public class ComponentBinderRegistry
|
||||
// {
|
||||
// private readonly Dictionary<ComponentType, IComponentBinder> _binder = [];
|
||||
// public void Add<T>(ComponentBinder<T> 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<Entity> 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<string, object> Data)
|
||||
// {
|
||||
// public abstract DataNodeType NodeType { get; }
|
||||
// // public T Get<T>(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<T> As<T>() => Value is T o?Option<T>.Some(o):Option<T>.None;
|
||||
// }
|
||||
// public sealed class SequenceNode(IReadOnlyList<DataNode> values) : DataNode
|
||||
// {
|
||||
// public override DataNodeType NodeType => DataNodeType.Sequence;
|
||||
|
||||
// public IReadOnlyList<DataNode> Values { get; set; } = values;
|
||||
// }
|
||||
// public sealed class MappingNode(IReadOnlyDictionary<string, DataNode> values) : DataNode
|
||||
// {
|
||||
// public override DataNodeType NodeType => DataNodeType.Mapping;
|
||||
|
||||
// public IReadOnlyDictionary<string, DataNode> Values { get; set; } = values;
|
||||
|
||||
// public bool TryGet<T>(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<DataNode>, IJsonFormatter<DataNode>
|
||||
// {
|
||||
// private Dictionary<string,string> _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();
|
||||
// }
|
||||
// }
|
||||
@@ -0,0 +1 @@
|
||||
uid://boar6ktwp2m1e
|
||||
133
src/Blueprints/Provider/Converters/ItemComponentsConverter.cs
Normal file
133
src/Blueprints/Provider/Converters/ItemComponentsConverter.cs
Normal file
@@ -0,0 +1,133 @@
|
||||
namespace FoodFactory.Blueprints.Providers.Converters;
|
||||
|
||||
using System;
|
||||
|
||||
using SharpYaml.Serialization;
|
||||
|
||||
public abstract class ItemComponentsConverter<T> : YamlConverter<IValueProvider<T>> where T : struct
|
||||
{
|
||||
public override IValueProvider<T> Read(YamlReader reader)
|
||||
{
|
||||
var converter = reader.GetConverter(typeof(T));
|
||||
var value = converter.Read(reader, typeof(T));
|
||||
if (value is not T t)
|
||||
{
|
||||
throw new Exception();
|
||||
}
|
||||
return new StaticCompiledNode<T>(t);
|
||||
}
|
||||
|
||||
|
||||
public override void Write(YamlWriter writer, IValueProvider<T> value) => throw new NotSupportedException();
|
||||
}
|
||||
// public class ComponentBinderRegistry
|
||||
// {
|
||||
// private readonly Dictionary<ComponentType, IComponentBinder> _binder = [];
|
||||
// public void Add<T>(ComponentBinder<T> 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<Entity> 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<string, object> Data)
|
||||
// {
|
||||
// public abstract DataNodeType NodeType { get; }
|
||||
// // public T Get<T>(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<T> As<T>() => Value is T o?Option<T>.Some(o):Option<T>.None;
|
||||
// }
|
||||
// public sealed class SequenceNode(IReadOnlyList<DataNode> values) : DataNode
|
||||
// {
|
||||
// public override DataNodeType NodeType => DataNodeType.Sequence;
|
||||
|
||||
// public IReadOnlyList<DataNode> Values { get; set; } = values;
|
||||
// }
|
||||
// public sealed class MappingNode(IReadOnlyDictionary<string, DataNode> values) : DataNode
|
||||
// {
|
||||
// public override DataNodeType NodeType => DataNodeType.Mapping;
|
||||
|
||||
// public IReadOnlyDictionary<string, DataNode> Values { get; set; } = values;
|
||||
|
||||
// public bool TryGet<T>(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<DataNode>, IJsonFormatter<DataNode>
|
||||
// {
|
||||
// private Dictionary<string,string> _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();
|
||||
// }
|
||||
// }
|
||||
@@ -0,0 +1 @@
|
||||
uid://cv0hsod03jmng
|
||||
21
src/Blueprints/Provider/DefaultValueProvider.cs
Normal file
21
src/Blueprints/Provider/DefaultValueProvider.cs
Normal file
@@ -0,0 +1,21 @@
|
||||
namespace FoodFactory.Blueprints.Providers;
|
||||
using System;
|
||||
using Arch.Core;
|
||||
using Arch.Core.Extensions;
|
||||
|
||||
public sealed class DefaultValueProvider : IValueProvider
|
||||
{
|
||||
private readonly object _object;
|
||||
|
||||
public DefaultValueProvider(Type type, object @object)
|
||||
{
|
||||
Type = type;
|
||||
_object = @object;
|
||||
}
|
||||
public ProviderMode Mode => ProviderMode.Static;
|
||||
|
||||
public Type Type { get; }
|
||||
|
||||
public Type GetProviderType() => Type;
|
||||
public void Resolve(BlueprintContext context, Entity entity) => entity.Set(_object);
|
||||
}
|
||||
1
src/Blueprints/Provider/DefaultValueProvider.cs.uid
Normal file
1
src/Blueprints/Provider/DefaultValueProvider.cs.uid
Normal file
@@ -0,0 +1 @@
|
||||
uid://croijjg1q3ui3
|
||||
16
src/Blueprints/Provider/FactoryCompiledNode.cs
Normal file
16
src/Blueprints/Provider/FactoryCompiledNode.cs
Normal file
@@ -0,0 +1,16 @@
|
||||
namespace FoodFactory.Blueprints.Providers;
|
||||
|
||||
public sealed class FactoryCompiledNode<T> : IValueProvider<T>
|
||||
{
|
||||
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);
|
||||
}
|
||||
1
src/Blueprints/Provider/FactoryCompiledNode.cs.uid
Normal file
1
src/Blueprints/Provider/FactoryCompiledNode.cs.uid
Normal file
@@ -0,0 +1 @@
|
||||
uid://ds3aflbt1maj5
|
||||
22
src/Blueprints/Provider/IValueProvider.cs
Normal file
22
src/Blueprints/Provider/IValueProvider.cs
Normal file
@@ -0,0 +1,22 @@
|
||||
namespace FoodFactory.Blueprints.Providers;
|
||||
|
||||
using System;
|
||||
using Arch.Core;
|
||||
using Arch.Core.Extensions;
|
||||
public interface IValueProvider
|
||||
{
|
||||
Type GetProviderType();
|
||||
void Resolve(BlueprintContext context, Entity entity);
|
||||
ProviderMode Mode { get; }
|
||||
}
|
||||
public interface IValueProvider<T> : 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<T>());
|
||||
}
|
||||
public enum ProviderMode
|
||||
{
|
||||
Static,
|
||||
Modify
|
||||
}
|
||||
1
src/Blueprints/Provider/IValueProvider.cs.uid
Normal file
1
src/Blueprints/Provider/IValueProvider.cs.uid
Normal file
@@ -0,0 +1 @@
|
||||
uid://bp0bhlpk25uh8
|
||||
14
src/Blueprints/Provider/StaticCompiledNode.cs
Normal file
14
src/Blueprints/Provider/StaticCompiledNode.cs
Normal file
@@ -0,0 +1,14 @@
|
||||
namespace FoodFactory.Blueprints.Providers;
|
||||
|
||||
public sealed class StaticCompiledNode<T> : IValueProvider<T>
|
||||
{
|
||||
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;
|
||||
}
|
||||
1
src/Blueprints/Provider/StaticCompiledNode.cs.uid
Normal file
1
src/Blueprints/Provider/StaticCompiledNode.cs.uid
Normal file
@@ -0,0 +1 @@
|
||||
uid://2srbqlc7hyo8
|
||||
@@ -1,71 +1,70 @@
|
||||
using ChickenGameTest;
|
||||
using Godot;
|
||||
using System;
|
||||
namespace FoodFactory.Conveyors;
|
||||
|
||||
public partial class ConveyorBeltStraight : Node3D
|
||||
using System.Diagnostics;
|
||||
using Chickensoft.AutoInject;
|
||||
using Chickensoft.Introspection;
|
||||
using FoodFactory.Items;
|
||||
using FoodFactory.Math;
|
||||
using FoodFactory.Voxel;
|
||||
using Godot;
|
||||
[Meta(typeof(IAutoNode))]
|
||||
public partial class ConveyorBeltStraight : Node3D, IProvide<IVoxelGridRegistry>
|
||||
{
|
||||
[Export] public Vector3I VoxelPos { get; set; } = default;
|
||||
public override void _Notification(int what) => this.Notify(what);
|
||||
[Export] public int Width { get; set; } = default;
|
||||
public IVoxelGridRegistry Value() => FoodFactoryApi.GridRegistry;
|
||||
[Dependency] protected IFoodFactoryApi FoodFactoryApi => this.DependOn<IFoodFactoryApi>();
|
||||
private VoxelGuid _guid = default;
|
||||
public GridTransform3D VoxelTransform
|
||||
{
|
||||
get => GridTransform3D.FromGodot(Transform);
|
||||
set => Transform = value.ToGodot();
|
||||
get => GridTransform3D.FromGodot(GlobalTransform);
|
||||
set => GlobalTransform = value.ToGodot();
|
||||
}
|
||||
[Node("ConveyorLogic")]
|
||||
public TestItemConveyor ConveyorLogic { get; set; } = default!;
|
||||
public override void _Ready()
|
||||
{
|
||||
var node = GetNode("Node") as TestItemConveyor;
|
||||
base._Ready();
|
||||
Debug.Assert(ConveyorLogic is not null, $"{nameof(ConveyorLogic)} is not set on {nameof(ConveyorBeltStraight)}, Path {SceneFilePath}");
|
||||
// GD.PrintS(VoxelTransform.Origin,VoxelTransform.Forward);
|
||||
node.StartPort = node.CreatePort(
|
||||
ConveyorLogic.StartPort = ConveyorLogic.CreatePort(
|
||||
new(VoxelTransform.TranslatedLocal(new(1 - Width, 0, 0)).RotateY(2),
|
||||
// Direction.Back,
|
||||
Width,
|
||||
PortAccess.BiDirectional),
|
||||
new ItemConveyor.BeltTEnd(ItemConveyor.ConveyorEnd.Start),
|
||||
new LaneSpan(0, 1));
|
||||
// GD.Print(VoxelTransform.Origin,GridTransform3D.FromGodot(Transform.TranslatedLocal(Vector3.Left)).Origin);
|
||||
node.EndPort = node.CreatePort(
|
||||
new BeltTEnd(ConveyorEnd.Start));
|
||||
ConveyorLogic.EndPort = ConveyorLogic.CreatePort(
|
||||
new(VoxelTransform, //new(GridTransform3D.FromGodot(Transform.TranslatedLocal(Vector3.Left)),
|
||||
// Direction.Front,
|
||||
Width,
|
||||
PortAccess.BiDirectional),
|
||||
new ItemConveyor.BeltTEnd(ItemConveyor.ConveyorEnd.End),
|
||||
new LaneSpan(0, 1));
|
||||
new BeltTEnd(ConveyorEnd.End));
|
||||
|
||||
// node.OtherPorts = [
|
||||
// node.CreatePort(
|
||||
// new(VoxelTransform.RotateY90CW(),
|
||||
// // Direction.Right,
|
||||
// 1,
|
||||
// PortAccess.In),
|
||||
// new ItemConveyor.BeltTOffset(node.Length/2),
|
||||
// LaneSpan.One),
|
||||
// node.CreatePort(
|
||||
// new(VoxelTransform.RotateY90CCW(),
|
||||
// // Direction.Left,
|
||||
// 1,
|
||||
// PortAccess.In),
|
||||
// new ItemConveyor.BeltTOffset(node.Length/2),
|
||||
// LaneSpan.One)
|
||||
// ];
|
||||
// new ConveyorPort(this,
|
||||
// new BeltPortProfile(VoxelPos, Direction.Back, 1, PortAccess.BiDirectional),
|
||||
// new ItemConveyor.BeltTEnd(ItemConveyor.ConveyorEnd.Start),
|
||||
// (item, offset) =>
|
||||
// {
|
||||
// var obstacle = node.GetDistanceToNextItem(ItemConveyor.BeltDirection.TowardEnd, 0, offset, LaneSpan.One);
|
||||
// return ItemConveyor.ITEMSIZE < obstacle.DistanceToCenter - (obstacle.IsItem ? ItemConveyor.ITEMSIZE : 0);
|
||||
// },
|
||||
// (item, offset) => { _items.Insert(0, new(item, offset)); return true;});
|
||||
// node.EndPort = new ConveyorPort(this,
|
||||
// new BeltPortProfile(VoxelPos, Direction.Front, 1, PortAccess.BiDirectional),
|
||||
// new ItemConveyor.BeltTEnd(ItemConveyor.ConveyorEnd.End),
|
||||
// (item, offset) =>
|
||||
// {
|
||||
// var obstacle = node.GetDistanceToNextItem(ItemConveyor.BeltDirection.TowardEnd, node.Length, offset, LaneSpan.One);
|
||||
// return ItemConveyor.ITEMSIZE < obstacle.DistanceToCenter - (obstacle.IsItem ? ItemConveyor.ITEMSIZE : 0);
|
||||
// // return ItemConveyor.ITEMSIZE < GetDistanceToNextItem(ItemConveyor.BeltDirection.TowardStart, Length, SpeedMagnitude, LaneSpan.One);
|
||||
// },
|
||||
// (item, offset) => { _items.Add(new(item, offset)); return true;});
|
||||
ConveyorLogic.OtherPorts = [
|
||||
ConveyorLogic.CreatePort(
|
||||
new(VoxelTransform.RotateY90CW(),
|
||||
// Direction.Right,
|
||||
1,
|
||||
PortAccess.In),
|
||||
new BeltTOffset(ConveyorLogic.Length/2)),
|
||||
ConveyorLogic.CreatePort(
|
||||
new(VoxelTransform.RotateY90CCW(),
|
||||
// Direction.Left,
|
||||
1,
|
||||
PortAccess.In),
|
||||
new BeltTOffset(ConveyorLogic.Length/2))
|
||||
];
|
||||
|
||||
}
|
||||
public void OnResolved()
|
||||
{
|
||||
_guid = FoodFactoryApi.GridRegistry.Register(this, VoxelTransform.Origin);
|
||||
this.Provide();
|
||||
|
||||
}
|
||||
public override void _ExitTree()
|
||||
{
|
||||
FoodFactoryApi.GridRegistry.UnRegister(_guid);
|
||||
base._ExitTree();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,10 +1,10 @@
|
||||
[gd_scene load_steps=7 format=3 uid="uid://c4h7mwnfrdesg"]
|
||||
[gd_scene format=3 uid="uid://c4h7mwnfrdesg"]
|
||||
|
||||
[ext_resource type="Script" uid="uid://dgyuh6un1qoj4" path="res://src/Conveyors/ConveyorBeltStraight/ConveyorBeltStraight.cs" id="1_mn8ro"]
|
||||
[ext_resource type="Script" uid="uid://j24fuotdwwx4" path="res://src/VoxelGrid/TestItemConveyor.cs" id="2_1vr1d"]
|
||||
[ext_resource type="Script" uid="uid://j24fuotdwwx4" path="res://src/Conveyors/TestItemConveyor.cs" id="2_1vr1d"]
|
||||
[ext_resource type="PackedScene" uid="uid://bapdy1c6rb4qr" path="res://assets/kenney_conveyor-kit/Models/GLB format/conveyor-sides.glb" id="2_mn8ro"]
|
||||
[ext_resource type="PackedScene" uid="uid://ccuhhy2oa8xvm" path="res://assets/kenney_conveyor-kit/Models/GLB format/arrow-basic.glb" id="3_1vr1d"]
|
||||
[ext_resource type="Script" uid="uid://bjuntmf2sjynp" path="res://src/VoxelGrid/ConveyorItemRender.cs" id="4_g7n1f"]
|
||||
[ext_resource type="Script" uid="uid://bjuntmf2sjynp" path="res://src/Conveyors/ConveyorItemRender.cs" id="4_g7n1f"]
|
||||
|
||||
[sub_resource type="Curve3D" id="Curve3D_x5qdr"]
|
||||
_data = {
|
||||
@@ -13,22 +13,23 @@ _data = {
|
||||
}
|
||||
point_count = 2
|
||||
|
||||
[node name="ConveyorBeltStraight" type="Node3D"]
|
||||
[node name="ConveyorBeltStraight" type="Node3D" unique_id=1032197109]
|
||||
script = ExtResource("1_mn8ro")
|
||||
Width = 1
|
||||
|
||||
[node name="Node" type="Node" parent="."]
|
||||
[node name="ConveyorLogic" type="Node" parent="." unique_id=1693852977]
|
||||
script = ExtResource("2_1vr1d")
|
||||
|
||||
[node name="conveyor-sides" parent="." instance=ExtResource("2_mn8ro")]
|
||||
[node name="conveyor-sides" parent="." unique_id=516239399 instance=ExtResource("2_mn8ro")]
|
||||
transform = Transform3D(-4.371139e-08, 0, -1, 0, 1, 0, 1, 0, -4.371139e-08, 0, 0, 0)
|
||||
|
||||
[node name="arrow-basic2" parent="." instance=ExtResource("3_1vr1d")]
|
||||
[node name="arrow-basic2" parent="." unique_id=1668302786 instance=ExtResource("3_1vr1d")]
|
||||
transform = Transform3D(-4.371139e-08, 0, -1, 0, 1, 0, 1, 0, -4.371139e-08, 0, 0.21879572, 0)
|
||||
|
||||
[node name="Path3D" type="Path3D" parent="."]
|
||||
[node name="Path3D" type="Path3D" parent="." unique_id=878303794]
|
||||
curve = SubResource("Curve3D_x5qdr")
|
||||
|
||||
[node name="Node2" type="Node" parent="." node_paths=PackedStringArray("Path3D", "ItemConveyor")]
|
||||
[node name="Node2" type="Node" parent="." unique_id=1875202216 node_paths=PackedStringArray("Path3D", "ItemConveyor")]
|
||||
script = ExtResource("4_g7n1f")
|
||||
Path3D = NodePath("../Path3D")
|
||||
ItemConveyor = NodePath("../Node")
|
||||
ItemConveyor = NodePath("../ConveyorLogic")
|
||||
|
||||
209
src/Conveyors/ConveyorItemRender.cs
Normal file
209
src/Conveyors/ConveyorItemRender.cs
Normal file
@@ -0,0 +1,209 @@
|
||||
namespace FoodFactory.Conveyors;
|
||||
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Runtime.CompilerServices;
|
||||
using Arch.Core;
|
||||
using Chickensoft.AutoInject;
|
||||
using Chickensoft.Introspection;
|
||||
using Chickensoft.Sync.Primitives;
|
||||
using CommunityToolkit.HighPerformance.Helpers;
|
||||
using FoodFactory.Items;
|
||||
using Godot;
|
||||
using SJK.Math;
|
||||
|
||||
[Meta(typeof(IAutoNode))]
|
||||
public partial class ConveyorItemRender : Node
|
||||
{
|
||||
public override void _Notification(int what) => this.Notify(what);
|
||||
[Export] protected Path3D Path3D { get; set; } = default!;
|
||||
[Export] protected TestItemConveyor ItemConveyor { get; set; } = default!;
|
||||
[Chickensoft.AutoInject.Dependency] protected IItemRenderer Items => this.DependOn<IItemRenderer>();
|
||||
[Chickensoft.AutoInject.Dependency] protected IFoodFactoryApi FoodFactoryApi => this.DependOn<IFoodFactoryApi>();
|
||||
// private Chickensoft.Sync.Primitives.AutoList<ConveyorSlice>.Binding _binding = default!;
|
||||
private AutoList<Ordered1DList<IBeltItem>.Entry>.Binding _binding = default!;
|
||||
public void OnResolved()
|
||||
{
|
||||
|
||||
_binding = ItemConveyor.Items.Values.Bind().OnUpdate((old, updated) =>
|
||||
{
|
||||
if (old.Position == updated.Position)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
Items.UpdateTransform(updated.Value, Path3D.GlobalTransform * Path3D.Curve.SampleBakedWithRotation(updated.Position), (float)delta);
|
||||
|
||||
});
|
||||
// FoodFactoryApi.TickManger.GameTick += _ =>Process(_.Delta);// offset = 0;
|
||||
FoodFactoryApi.TickManger.GameTick += _ => delta = _.Delta;// offset = 0;
|
||||
}
|
||||
// public override async void _Ready()
|
||||
// {
|
||||
// base._Ready();
|
||||
// if (!ItemConveyor.IsNodeReady())
|
||||
// {
|
||||
// await ToSignal(ItemConveyor, Node.SignalName.Ready);
|
||||
// }
|
||||
// await ToSignal(GetTree(), SceneTree.SignalName.ProcessFrame);
|
||||
// // ItemConveyor.GetChildren().OfType<Timer>().First().Timeout += () => offset = 0;
|
||||
// return;
|
||||
// // _binding = ItemConveyor.Items.Items.Bind();
|
||||
// // binding.OnRemove(callback =>
|
||||
// // {
|
||||
// // Items.Remove(callback.Item);
|
||||
// // // GD.PrintS(callback.Item, callback.BeltT);
|
||||
// // // if (itemsRenders.TryGetValue(callback.Item, out var node))
|
||||
// // // {
|
||||
// // // GD.PrintS(callback.Item, callback.BeltT,node);
|
||||
// // // itemsRenders.Remove(callback.Item);
|
||||
// // // node.QueueFree();
|
||||
// // // }
|
||||
// // });
|
||||
// // _binding.OnAdd((i, v) => Items.UpdateTransform(i.Item, Path3D.GlobalTransform * Path3D.Curve.SampleBakedWithRotation(i.BeltT)));
|
||||
// // _binding.OnUpdate((a, b) => Items.UpdateTransform(a.Item, Path3D.GlobalTransform * Path3D.Curve.SampleBakedWithRotation(b.BeltT).Translated(-Path3D.Curve.SampleBakedWithRotation(b.BeltT).Basis.X * b.LaneSpan.Start)));
|
||||
|
||||
// }
|
||||
float offset = 0;
|
||||
double delta = 0;
|
||||
|
||||
// public void Process(double delta)
|
||||
// {
|
||||
// offset += (float)delta;
|
||||
// base._Process(delta);
|
||||
// var e = ItemConveyor.EnumerateTowardStart();
|
||||
// while (e.MoveNext())
|
||||
// {
|
||||
// // Items.UpdateTransform(e.Current.Value, Path3D.GlobalTransform * Path3D.Curve.SampleBakedWithRotation(e.Current.Position + (ItemConveyor.SignedSpeed * offset)));
|
||||
// Items.UpdateTransform(e.Current.Value, Path3D.GlobalTransform * Path3D.Curve.SampleBakedWithRotation(e.Current.Position), (float)delta);
|
||||
// }
|
||||
// }
|
||||
|
||||
protected override void Dispose(bool disposing)
|
||||
{
|
||||
_binding.Dispose();
|
||||
base.Dispose(disposing);
|
||||
}
|
||||
}
|
||||
|
||||
public interface IItemRenderer
|
||||
{
|
||||
// Node3D GetVisualNode(IBeltItem beltItem);
|
||||
void UpdateTransform(IBeltItem beltItem, Transform3D newTransform);
|
||||
void UpdateTransform(IBeltItem beltItem, Transform3D newTransform, float time);
|
||||
void Remove(IBeltItem beltItem);
|
||||
}
|
||||
public partial class ItemRenderBuffered : Node3D, IItemRenderer
|
||||
{
|
||||
private record struct ItemEntry(IBeltItem Item, Transform3D PreviousTransform, Transform3D NewTransform, float Elapsed, float Duration);
|
||||
private List<ItemEntry> _current = [];
|
||||
private List<ItemEntry> _next = [];
|
||||
private Dictionary<IBeltItem, Node3D> _visuals = [];
|
||||
public void Remove(IBeltItem beltItem) => throw new System.NotImplementedException();
|
||||
public void UpdateTransform(IBeltItem beltItem, Transform3D newTransform) => throw new System.NotImplementedException();
|
||||
public void UpdateTransform(IBeltItem beltItem, Transform3D newTransform, float time)
|
||||
{
|
||||
var index = _current.FindIndex(f => f.Item == beltItem);
|
||||
if (index >= 0)
|
||||
{
|
||||
_current[index] = _current[index] with { PreviousTransform = _current[index].NewTransform, NewTransform = newTransform, Elapsed = 0, Duration = time };
|
||||
}
|
||||
else
|
||||
{
|
||||
if (!_visuals.TryGetValue(beltItem, out var node3D))
|
||||
{
|
||||
_visuals[beltItem] = node3D = beltItem.CreateItemVisual();
|
||||
node3D.Transform = newTransform;
|
||||
AddChild(node3D);
|
||||
beltItem.Disposed += _ => node3D.QueueFree();
|
||||
}
|
||||
// if (node3D.Transform.Origin.DistanceTo(newTransform.Origin) < .00001f)
|
||||
// {
|
||||
// return;
|
||||
// }
|
||||
_current.Add(new(beltItem, node3D.Transform, newTransform, 0, time));
|
||||
}
|
||||
}
|
||||
public override void _Process(double delta)
|
||||
{
|
||||
_next.Clear();
|
||||
GD.Print(_current.Count);
|
||||
for (int i = 0; i < _current.Count; i++)
|
||||
{
|
||||
var entry = _current[i];
|
||||
if (!IsInstanceValid(_visuals[entry.Item])){
|
||||
_visuals.Remove(entry.Item);
|
||||
continue;
|
||||
}
|
||||
entry.Elapsed = (float)(entry.Elapsed + delta);
|
||||
_visuals[entry.Item].Transform = entry.PreviousTransform.InterpolateWith(entry.NewTransform, entry.Elapsed / entry.Duration);
|
||||
if ((entry.Elapsed / entry.Duration) < 1f)
|
||||
{
|
||||
_next.Add(entry);
|
||||
}
|
||||
}
|
||||
(_current, _next) = (_next, _current);
|
||||
}
|
||||
}
|
||||
public partial class ItemRenderSimple : Node3D, IItemRenderer
|
||||
{
|
||||
private Dictionary<IBeltItem, Node3D> _items = [];
|
||||
public void Remove(IBeltItem beltItem)
|
||||
{
|
||||
if (_items.TryGetValue(beltItem, out var node))
|
||||
{
|
||||
node.QueueFree();
|
||||
}
|
||||
_items.Remove(beltItem);
|
||||
}
|
||||
|
||||
public void UpdateTransform(IBeltItem beltItem, Transform3D newTransform, float time)
|
||||
{
|
||||
if (!_items.TryGetValue(beltItem, out var node))
|
||||
{
|
||||
_items.Add(beltItem, node = beltItem.CreateItemVisual());
|
||||
AddChild(node);
|
||||
beltItem.Disposed += _ => { node.QueueFree(); _items.Remove(beltItem); };
|
||||
node.Transform = newTransform;
|
||||
return;
|
||||
}
|
||||
node.Transform = newTransform;
|
||||
}
|
||||
public void UpdateTransform(IBeltItem beltItem, Transform3D newTransform) => UpdateTransform(beltItem, newTransform, .25f);
|
||||
}
|
||||
public partial class TestItemRendered : Node3D, IItemRenderer
|
||||
{
|
||||
public Dictionary<IBeltItem, Node3D> _items = [];
|
||||
private ConditionalWeakTable<IBeltItem, Tween> _tweens = [];
|
||||
public void Remove(IBeltItem beltItem)
|
||||
{
|
||||
if (_items.TryGetValue(beltItem, out var node))
|
||||
{
|
||||
node.QueueFree();
|
||||
}
|
||||
_items.Remove(beltItem);
|
||||
}
|
||||
|
||||
public void UpdateTransform(IBeltItem beltItem, Transform3D newTransform, float time)
|
||||
{
|
||||
if (!_items.TryGetValue(beltItem, out var node))
|
||||
{
|
||||
_items.Add(beltItem, node = beltItem.CreateItemVisual());
|
||||
AddChild(node);
|
||||
beltItem.Disposed += _ => { node.QueueFree(); _items.Remove(beltItem); };
|
||||
node.Transform = newTransform;
|
||||
return;
|
||||
}
|
||||
if (_tweens.TryGetValue(beltItem, out var tween))
|
||||
{
|
||||
tween.Kill();
|
||||
_tweens.Remove(beltItem);
|
||||
}
|
||||
// GD.Print(newTransform);
|
||||
tween = GetTree().CreateTween().BindNode(node);
|
||||
tween.TweenProperty(node, "transform", newTransform, time);
|
||||
_tweens.Add(beltItem, tween);
|
||||
}
|
||||
|
||||
public void UpdateTransform(IBeltItem beltItem, Transform3D newTransform) => UpdateTransform(beltItem, newTransform, .25f);
|
||||
}
|
||||
312
src/Conveyors/ItemConveyor.cs
Normal file
312
src/Conveyors/ItemConveyor.cs
Normal file
@@ -0,0 +1,312 @@
|
||||
namespace FoodFactory.Conveyors;
|
||||
|
||||
using Godot;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using Chickensoft.Sync.Primitives;
|
||||
using SJK.Functional;
|
||||
using SJK.Math;
|
||||
using FoodFactory.Items;
|
||||
using FoodFactory.Voxel;
|
||||
|
||||
public interface IMovementConveyor
|
||||
{
|
||||
IBeltPort StartPort { get; }
|
||||
// IBeltSlotProfile StartPort { get; }
|
||||
IBeltPort EndPort { get; }
|
||||
// IBeltSlotProfile EndPort { get; }
|
||||
// IOption<IBeltSlotProfile> InputPort { get; }
|
||||
// IOption<IBeltSlotProfile> OutputPort { get; }
|
||||
// IEnumerable<IBeltSlotProfile> GetPorts();
|
||||
IEnumerable<IBeltPort> GetPorts();
|
||||
IAutoValue<float> SpeedValue { get; }
|
||||
float SpeedMagnitude { get; set; }
|
||||
float SignedSpeed { get; set; }
|
||||
bool IsReversed { get; set; }
|
||||
float Length { get; set; }
|
||||
bool HasItems();
|
||||
// float GetAvailableTravel(ItemConveyor.BeltDirection beltDirection, LaneSpanT itemSpan, float maxDistance);
|
||||
// IBeltSlotProfile GetPortFacingStart();
|
||||
// IBeltSlotProfile GetPortFacingEnd();
|
||||
BeltDirection GetBeltDirection();
|
||||
// Option<ConveyorSlice> GetItemTowardStart();
|
||||
// Option<ConveyorSlice> GetItemTowardEnd();
|
||||
// Option<ConveyorSlice> GetItemTowardInput();
|
||||
// Option<ConveyorSlice> GetItemTowardOutput();
|
||||
Ordered1DList<IBeltItem>.Enumerator EnumerateTowardEnd();
|
||||
Ordered1DList<IBeltItem>.Enumerator EnumerateTowardStart();
|
||||
IBeltMovement GetMovementPolicy();
|
||||
// IList<ConveyorSlice> Items { get; }
|
||||
IOption<IBeltPort> GetPortFacing(IBeltPort slot);
|
||||
BeltObstacle GetDistanceToNextItem(BeltDirection beltDirection, float itemBeltT, float maxDistToCheck, HashSet<IMovementConveyor>? visted = null);
|
||||
ConveyorPort CreatePort(BeltPortProfile profile, BeltT beltT);
|
||||
}
|
||||
public interface IBeltMovement
|
||||
{
|
||||
void AdvanceBelt(IMovementConveyor conveyor, float delta);
|
||||
|
||||
}
|
||||
public sealed class IndividualMovement : IBeltMovement
|
||||
{
|
||||
public void AdvanceBelt(IMovementConveyor conveyor, float delta)
|
||||
{
|
||||
// GD.Print(conveyor.Items);
|
||||
if (conveyor.GetBeltDirection() == BeltDirection.NotMoving)
|
||||
{
|
||||
return;
|
||||
}
|
||||
// GD.Print("hello");
|
||||
var towardStart = conveyor.GetBeltDirection() == BeltDirection.TowardStart;
|
||||
var enumerator = towardStart ? conveyor.EnumerateTowardStart() : conveyor.EnumerateTowardEnd();
|
||||
while (enumerator.MoveNext())
|
||||
{
|
||||
var itemRef = enumerator.Current;
|
||||
float maxMove = conveyor.SpeedMagnitude * delta;
|
||||
var space = conveyor.GetDistanceToNextItem(conveyor.GetBeltDirection(), itemRef.Position, maxMove);
|
||||
|
||||
|
||||
// space -= ITEMSIZE;//AcountForSPacing
|
||||
// if ()
|
||||
// {
|
||||
// continue;
|
||||
// }
|
||||
|
||||
if (space is ItemBeltObstacle itemOb && itemOb.Distance <= 0)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
float amountToMove = Mathf.Min(space.Distance, maxMove);
|
||||
|
||||
enumerator.Set(itemRef.Position + amountToMove * Mathf.Sign(conveyor.SignedSpeed));
|
||||
// itemRef.Replace(itemRef.Value with { BeltT = itemRef.Value.BeltT + amountToMove * Mathf.Sign(conveyor.SignedSpeed) });
|
||||
// GD.PrintS(conveyor.Items.Count,space,amountToMove," "+ itemRef.BeltT,itemRef.Item,conveyor.GetBeltDirection(),towardStart);
|
||||
if (towardStart ? itemRef.Position <= 0 : itemRef.Position >= conveyor.Length)
|
||||
{
|
||||
var facing = conveyor.GetPortFacing(towardStart ? conveyor.StartPort : conveyor.EndPort);
|
||||
// GD.Print(facing.HasValue(out var slot2),slot2);// , slot2.CanAccept(new TestItem(),LaneSpan.One,0));
|
||||
if (facing.HasValue(out var slot) && slot.TryInsert(itemRef.Value, 0))
|
||||
{
|
||||
// GD.Print(space is PortBeltObstacle port?port.LaneSpan:itemRef.Value.LaneSpan);
|
||||
// GD.PrintS(itemRef.Value.LaneSpan,itemRef.Value.LaneSpan);
|
||||
// GD.Print(itemRef.Item);
|
||||
enumerator.Remove();
|
||||
}
|
||||
}
|
||||
}
|
||||
// enumerator.SortAll();
|
||||
}
|
||||
}
|
||||
public sealed class StrictMovement : IBeltMovement
|
||||
{
|
||||
// // public event IBeltMovement.ItemMoved? OnItemMoved;
|
||||
|
||||
// public void AdvanceBelt(ItemConveyor conveyor, float delta)
|
||||
// {
|
||||
// if (!conveyor.AnyItems())
|
||||
// {
|
||||
// return;
|
||||
// }
|
||||
// var beltDirection = conveyor.GetBeltDirection();
|
||||
// if (beltDirection == BeltDirection.NotMoving)
|
||||
// {
|
||||
// return;
|
||||
// }
|
||||
// var towardStart = beltDirection == BeltDirection.TowardStart;
|
||||
// // GD.Print("Thing: " + Speed + " "+ conveyor.GetConveyorPortFacing(conveyor.GetOutputPort()));
|
||||
// // var moveItemsIntoNextBelt = false;
|
||||
// var next = conveyor.GetConveyorPortFacing(conveyor.GetOutputPort());
|
||||
// var possibleItemTravel = conveyor.GetAvailableTravelForFrontItem(next.HasValue);
|
||||
|
||||
// // return;
|
||||
// if (!possibleItemTravel.HasValue)
|
||||
// {
|
||||
// //There is no items, this should not be called
|
||||
// throw new NotSupportedException("This should not e possible if there is no items or the belt is not moving, it should have returend before");
|
||||
// }
|
||||
// // if (next.HasValue && next.Value.Port.BeltT is BeltTEnd beltTEnd && (next.Value.Port.Direction == PortAccess.In || next.Value.Port.Direction == PortAccess.BiDirectional))
|
||||
// // {
|
||||
// // possibleItemTravel += next.Value.Conveyor.DistanceFromEnd(beltTEnd.End);
|
||||
// // moveItemsIntoNextBelt = true;
|
||||
// // }
|
||||
|
||||
|
||||
// // var itemPair = conveyor.GetFirstItem();
|
||||
// // var distToEnd = conveyor.GetAvailableTravel(conveyor.ItemsCount - 1, beltDirection);//(Speed < 0 ? ConveyorEnd.Start : ConveyorEnd.End);
|
||||
// var maxSpeed = Mathf.Min(conveyor.SpeedMagnitude * delta, possibleItemTravel.Value) * Mathf.Sign(conveyor.SignedSpeed);//Or possibly (int)BeltDirection, givn that they stgore the sign as part of the enum
|
||||
// if (maxSpeed == 0)
|
||||
// {
|
||||
// // var fI = conveyor.GetAvailableTravelForFrontItem();
|
||||
// // if (fI.HasValue && fI.Value <= 0 && next.Value.Port.TryInsertItem(conveyor.GetFirstItem().Value.Item, LaneSpan.One))
|
||||
// // {
|
||||
// // GD.Print("Item Moved");
|
||||
// // conveyor._items.Remove(conveyor.GetFirstItem().Value);
|
||||
// // }
|
||||
|
||||
// return;
|
||||
// }
|
||||
// List<ConveyorSlice> toMove = [];
|
||||
// // Dictionary<IBeltItem, List<ItemMovementSegment>> moves = [];
|
||||
// for (int i = 0; i < conveyor.ItemsCount; i++)
|
||||
// {
|
||||
// // List<ItemMovementSegment> segments = [];
|
||||
// // var startT = conveyor._items[i].BeltT;
|
||||
// var endT = Mathf.Clamp(conveyor._items[i].BeltT + maxSpeed, 0, conveyor.Length);
|
||||
|
||||
// conveyor._items[i] = conveyor._items[i] with { BeltT = endT };
|
||||
// // segments.Add(new(conveyor, startT, towardStart? Mathf.Max(endT, 0) : Mathf.Min(endT, conveyor.Length),Mathf.Abs(maxSpeed)));
|
||||
// if (towardStart ? (endT <= 0) : endT >= conveyor.Length)
|
||||
// {
|
||||
// toMove.Add(conveyor._items[i]);
|
||||
// // segments.Add(new(next.Value.Conveyor, ));
|
||||
// }
|
||||
// // moves.Add(conveyor._items[i].Item, segments);
|
||||
|
||||
// GD.Print($"Item: {conveyor._items[i].Item}, Position:{endT}");
|
||||
// }
|
||||
// // conveyor._items.ForEach((item) => item = item = Mathf.Clamp(item.Position + maxSpeed, 0, Length));
|
||||
// // var list = _items.Where(item=> Speed>=0?item.BeltT >= Length:item.BeltT <= 0).ToList();
|
||||
// // GD.Print(list.Count);
|
||||
// if (next.HasValue)
|
||||
// {
|
||||
// foreach (var item in toMove)
|
||||
// {
|
||||
// GD.Print($"Item: {item.Item}, Position:{item.BeltT} -----------------");
|
||||
// //TEST
|
||||
// // var v = GetConveyorPortFacing(GetOutputPort());
|
||||
// // if (v.HasValue)//Should be true if items are > Length
|
||||
// // {
|
||||
// // GD.PrintS(v.Value.Port.LocalOffset,v.Value.Port.BeltT,v.Value.Port.Direction,v.Value.Port.PullPush,v.Value.Port.Face);
|
||||
// // var end = (next.Value.Port.BeltT as BeltTEnd).End;
|
||||
// // next.Value.Conveyor.InsertAtEndWithoutCheck(end, item.Item);
|
||||
// if (next.Value.Port.TryInsertItem(item.Item, item.LaneSpan))
|
||||
// {
|
||||
// GD.Print("Item Moved");
|
||||
// conveyor._items.Remove(item);
|
||||
// }
|
||||
|
||||
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
|
||||
public void AdvanceBelt(IMovementConveyor conveyor, float delta)
|
||||
{
|
||||
|
||||
// GD.Print(conveyor.Items);
|
||||
if (conveyor.GetBeltDirection() == BeltDirection.NotMoving)
|
||||
{
|
||||
return;
|
||||
}
|
||||
if (!conveyor.HasItems())
|
||||
{
|
||||
return;
|
||||
}
|
||||
// GD.Print("hello");
|
||||
var towardStart = conveyor.GetBeltDirection() == BeltDirection.TowardStart;
|
||||
float maxMove = conveyor.SpeedMagnitude * delta;
|
||||
var enumerator = !towardStart ? conveyor.EnumerateTowardStart() : conveyor.EnumerateTowardEnd();
|
||||
BeltObstacle freeSpace = default!;
|
||||
float lastPos = 0;
|
||||
if (enumerator.MoveNext())
|
||||
{
|
||||
lastPos = enumerator.Current.Position;
|
||||
freeSpace = conveyor.GetDistanceToNextItem(conveyor.GetBeltDirection(),lastPos,maxMove,[]);
|
||||
}
|
||||
if (lastPos > conveyor.Length || lastPos < 0)
|
||||
{
|
||||
enumerator = !towardStart ? conveyor.EnumerateTowardStart() : conveyor.EnumerateTowardEnd();
|
||||
while (enumerator.MoveNext() && (enumerator.Current.Position >= conveyor.Length || enumerator.Current.Position <= 0))
|
||||
{
|
||||
if (towardStart ? enumerator.Current.Position <= 0 : enumerator.Current.Position >= conveyor.Length)
|
||||
{
|
||||
var facing = conveyor.GetPortFacing(towardStart ? conveyor.StartPort : conveyor.EndPort);
|
||||
// GD.Print(facing.HasValue(out var slot2),slot2);// , slot2.CanAccept(new TestItem(),LaneSpan.One,0));
|
||||
if (facing.HasValue(out var slot) && slot.TryInsert(enumerator.Current.Value, 0))
|
||||
{
|
||||
// GD.Print(space is PortBeltObstacle port?port.LaneSpan:itemRef.Value.LaneSpan);
|
||||
// GD.PrintS(itemRef.Value.LaneSpan,itemRef.Value.LaneSpan);
|
||||
// GD.Print(itemRef.Item);
|
||||
enumerator.Remove();
|
||||
}
|
||||
}
|
||||
}
|
||||
return;
|
||||
}
|
||||
// var freeSpace = conveyor.GetDistanceToNextItem(conveyor.GetBeltDirection(),conveyor.GetLastItemPosition(),maxMove,LaneSpan.One,[]);
|
||||
if (freeSpace.Distance <= 0)
|
||||
{
|
||||
return;
|
||||
}
|
||||
enumerator = !towardStart ? conveyor.EnumerateTowardStart() : conveyor.EnumerateTowardEnd();
|
||||
if (enumerator.MoveNext())
|
||||
{
|
||||
enumerator.MoveAllBy(MathF.Min(maxMove, freeSpace.Distance));
|
||||
}
|
||||
enumerator = !towardStart ? conveyor.EnumerateTowardStart() : conveyor.EnumerateTowardEnd();
|
||||
while (enumerator.MoveNext() && (enumerator.Current.Position >= conveyor.Length || enumerator.Current.Position <= 0))
|
||||
{
|
||||
if (towardStart ? enumerator.Current.Position <= 0 : enumerator.Current.Position >= conveyor.Length)
|
||||
{
|
||||
var facing = conveyor.GetPortFacing(towardStart ? conveyor.StartPort : conveyor.EndPort);
|
||||
// GD.Print(facing.HasValue(out var slot2),slot2);// , slot2.CanAccept(new TestItem(),LaneSpan.One,0));
|
||||
if (facing.HasValue(out var slot) && slot.TryInsert(enumerator.Current.Value, 0))
|
||||
{
|
||||
// GD.Print(space is PortBeltObstacle port?port.LaneSpan:itemRef.Value.LaneSpan);
|
||||
// GD.PrintS(itemRef.Value.LaneSpan,itemRef.Value.LaneSpan);
|
||||
// GD.Print(itemRef.Item);
|
||||
enumerator.Remove();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
public struct ConveyorSlice(IBeltItem item, float beltT = 0)
|
||||
{
|
||||
public IBeltItem Item = item;
|
||||
public float BeltT = beltT;
|
||||
public override readonly string ToString() => $"{Item}, {BeltT}";
|
||||
}
|
||||
|
||||
public enum BeltDirection : sbyte
|
||||
{
|
||||
TowardStart = -1,
|
||||
NotMoving = 0,
|
||||
TowardEnd = 1
|
||||
}
|
||||
public enum ConveyorEnd { Start, End }
|
||||
|
||||
public abstract record BeltT();
|
||||
public record BeltTEnd(ConveyorEnd End) : BeltT();
|
||||
public record BeltTOffset(float T) : BeltT();
|
||||
|
||||
public static class ConveyorExtensions
|
||||
{
|
||||
public static BeltDirection Reverse(this BeltDirection beltDirection) => beltDirection switch
|
||||
{
|
||||
BeltDirection.TowardStart => BeltDirection.TowardEnd,
|
||||
BeltDirection.TowardEnd => BeltDirection.TowardStart,
|
||||
_ => throw new NotSupportedException(),
|
||||
};
|
||||
public static IOption<IBeltPort> GetPortFacing(this IBeltPort slot, IVoxelGridRegistry gridRegistry)
|
||||
{
|
||||
var toCheck = new List<Vector3I>();
|
||||
for (int i = 0; i < slot.Profile.Width; i++)
|
||||
{
|
||||
toCheck.Add(slot.Profile.LocalOffset.LocalToWorld(Vector3I.Forward));
|
||||
}
|
||||
return gridRegistry
|
||||
.Get<IBeltPort>([.. toCheck])
|
||||
.Where(port => port.Profile.LocalOffset.TransformDirection(Vector3I.Forward) == slot.Profile.LocalOffset.TransformDirection(Vector3I.Back))
|
||||
.FirstOrNone();
|
||||
}
|
||||
public static BeltDirection DirectionTo(this ConveyorEnd end) => end switch
|
||||
{
|
||||
ConveyorEnd.Start => BeltDirection.TowardStart,
|
||||
ConveyorEnd.End => BeltDirection.TowardEnd,
|
||||
_ => throw new NotImplementedException(),
|
||||
};
|
||||
public static ConveyorEnd Swap(this ConveyorEnd end) => end switch { ConveyorEnd.Start => ConveyorEnd.End, ConveyorEnd.End => ConveyorEnd.Start, _ => throw new NotSupportedException() };
|
||||
}
|
||||
360
src/Conveyors/TestItemConveyor.cs
Normal file
360
src/Conveyors/TestItemConveyor.cs
Normal file
@@ -0,0 +1,360 @@
|
||||
namespace FoodFactory.Conveyors;
|
||||
|
||||
using Chickensoft.Introspection;
|
||||
using Chickensoft.AutoInject;
|
||||
using Godot;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using Chickensoft.Sync.Primitives;
|
||||
using SJK.Functional;
|
||||
using SJK.Math;
|
||||
using FoodFactory.Items;
|
||||
using FoodFactory.Voxel;
|
||||
|
||||
[Meta(typeof(IAutoNode))]
|
||||
[Tool]
|
||||
public partial class TestItemConveyor : Node, IMovementConveyor
|
||||
{
|
||||
public override void _Notification(int what) => this.Notify(what);
|
||||
[Dependency] public IBeltMovement MovementSystem => this.DependOn<IBeltMovement>(() => new IndividualMovement());
|
||||
[Dependency] public IVoxelGridRegistry GridRegistry => this.DependOn<IVoxelGridRegistry>();
|
||||
[Dependency] public IFoodFactoryApi FoodFactoryApi => this.DependOn<IFoodFactoryApi>();
|
||||
// private readonly AutoList<ConveyorSlice> _items = [];
|
||||
// public IAutoList<ConveyorSlice> Items => _items;
|
||||
// public readonly Sorted1DList<ConveyorSlice> Items = new(pos => pos.BeltT);
|
||||
private readonly Ordered1DList<IBeltItem> _items = new();
|
||||
public Ordered1DList<IBeltItem> Items => _items;
|
||||
// private AutoList<ConveyorSlice>.Binding _itemsBinding;
|
||||
public IBeltPort StartPort { get; set; }
|
||||
// public IBeltSlotProfile StartPort => new ItemConveyor.ConveyorPort() {
|
||||
// Face = Direction.Back,
|
||||
// MovementConveyor = this,
|
||||
// LocalOffset = Position,
|
||||
// BeltT = new ItemConveyor.BeltTEnd(ItemConveyor.ConveyorEnd.Start),
|
||||
// PullPush = TransferMode.PushPull ,
|
||||
// Direction = PortAccess.BiDirectional,
|
||||
// AcceptItemFunc = (item,offset)=>{ _items.Insert(0,new ConveyorSlice(item,offset)); return true;},
|
||||
// CanAcceptItemFunc = (item, offset) =>
|
||||
// {
|
||||
// return .25f <= GetDistanceToNextItem(ItemConveyor.BeltDirection.TowardEnd, 0, 5f, LaneSpan.One);
|
||||
// }
|
||||
// };
|
||||
public IBeltPort EndPort { get; set; }
|
||||
// public IBeltSlotProfile EndPort => new ItemConveyor.ConveyorPort() {
|
||||
// Face = Direction.Front,
|
||||
// MovementConveyor = this,
|
||||
// Direction = PortAccess.BiDirectional,
|
||||
// LocalOffset = Position,
|
||||
// BeltT = new ItemConveyor.BeltTEnd(ItemConveyor.ConveyorEnd.End),
|
||||
// PullPush = TransferMode.PushPull,
|
||||
// AcceptItemFunc = (item,offset)=>{ _items.Add(new ConveyorSlice(item,Length-offset)); return true;},
|
||||
// CanAcceptItemFunc = (item,offset)=> .25f >= GetDistanceToNextItem(ItemConveyor.BeltDirection.TowardStart,Length,.5f,LaneSpan.One)
|
||||
// };
|
||||
public IList<IBeltPort> OtherPorts = [];
|
||||
// [Export] public Vector3I Position { get; set; } = default!;
|
||||
//Speed per unit time
|
||||
private readonly AutoValue<float> _speed = new(.5f);
|
||||
public IAutoValue<float> SpeedValue => _speed;
|
||||
|
||||
public float SignedSpeed
|
||||
{
|
||||
get => _speed.Value;
|
||||
set => _speed.Value = value;
|
||||
}
|
||||
|
||||
public float SpeedMagnitude
|
||||
{
|
||||
get => Mathf.Abs(_speed.Value);
|
||||
set => _speed.Value = Mathf.Abs(value) * Mathf.Sign(_speed.Value);
|
||||
}
|
||||
public bool IsReversed { get => _speed.Value < 0; set => _speed.Value = SpeedMagnitude * (value ? -1 : 1); }
|
||||
// public IList<ConveyorSlice> Items => _items;
|
||||
public float Length { get; set; } = 1;
|
||||
|
||||
// public IEnumerable<ConveyorSlice> EnumerateTowardEnd() => _items;
|
||||
|
||||
// public IEnumerable<ConveyorSlice> EnumerateTowardStart() => _items.Reverse();
|
||||
public BeltDirection GetBeltDirection() => IsReversed ? BeltDirection.TowardStart : BeltDirection.TowardEnd;
|
||||
public IBeltMovement GetMovementPolicy() => MovementSystem;
|
||||
public IEnumerable<IBeltPort> GetPorts() => [StartPort, EndPort, .. OtherPorts];
|
||||
public void OnResolved()
|
||||
{
|
||||
if (Engine.IsEditorHint())
|
||||
{
|
||||
return;
|
||||
}
|
||||
// var timer = new Timer() { WaitTime = .05f, Autostart = true };
|
||||
// AddChild(timer);
|
||||
FoodFactoryApi.TickManger.GameTick += args => MovementSystem.AdvanceBelt(this, (float)args.Delta);
|
||||
if (StartPort is null || EndPort is null)
|
||||
{
|
||||
throw new Exception();
|
||||
}
|
||||
foreach (var item in GetPorts())
|
||||
{
|
||||
var points = item.Points();
|
||||
var guid = GridRegistry.Register(item, [.. points]);
|
||||
TreeExiting += () => GridRegistry.UnRegister(guid);
|
||||
}
|
||||
}
|
||||
//ChatGPT Assisted
|
||||
public BeltObstacle GetDistanceToNextItem(
|
||||
BeltDirection beltDirection,
|
||||
float itemBeltT,
|
||||
float maxDistToCheck,
|
||||
HashSet<IMovementConveyor>? visited = null)
|
||||
{
|
||||
if (beltDirection == BeltDirection.NotMoving)
|
||||
{
|
||||
throw new NotSupportedException();
|
||||
}
|
||||
|
||||
visited ??= [];
|
||||
if (!visited.Add(this))
|
||||
{
|
||||
return new BeltObstacle(0);
|
||||
}
|
||||
|
||||
bool towardStart = beltDirection == BeltDirection.TowardStart;
|
||||
|
||||
// 1️⃣ Try to find next item on this conveyor
|
||||
var nextItem = FindNextLocalItem(towardStart, itemBeltT);
|
||||
// GD.Print(nextItem);
|
||||
if (nextItem.HasValue(out var item))
|
||||
{
|
||||
return new ItemBeltObstacle(MathF.Abs(itemBeltT - item.BeltT) - ITEMSIZE, item);
|
||||
// return MathF.Abs(itemBeltT - item.BeltT);//TODO UseLaneSpan inadditon
|
||||
}
|
||||
|
||||
// 2️⃣ No local item → try crossing into adjacent conveyor
|
||||
var recursiveDistance = GetDistanceAcrossPort(
|
||||
towardStart,
|
||||
itemBeltT,
|
||||
maxDistToCheck,
|
||||
visited
|
||||
);
|
||||
|
||||
return recursiveDistance;// with { Distance = recursiveDistance.Distance + Length };
|
||||
}
|
||||
//ChatGPT Assisted
|
||||
private IOption<ConveyorSlice> FindNextLocalItem(
|
||||
bool towardStart,
|
||||
float itemBeltT)
|
||||
{
|
||||
var sequence = towardStart
|
||||
? _items.EnumerateTowardStart(itemBeltT)
|
||||
: _items.EnumerateTowardEnd(itemBeltT);
|
||||
while (sequence.MoveNext())
|
||||
{
|
||||
var item = sequence.Current;
|
||||
if (towardStart ? item.Position < itemBeltT : item.Position > itemBeltT)
|
||||
{
|
||||
return Some<ConveyorSlice>.Of(new ConveyorSlice(item.Value, item.Position));
|
||||
}
|
||||
}
|
||||
return None<ConveyorSlice>.Of();
|
||||
// return sequence
|
||||
// .Where(i => towardStart ? i.Value.BeltT < itemBeltT : i.Value.BeltT > itemBeltT)
|
||||
// .Where(i => LaneSpan.OverLaps(i.Value.LaneSpan, laneSpan))
|
||||
// .Select(item => item.Value)
|
||||
// .FirstOrNone();
|
||||
}
|
||||
//ChatGPT Assisted
|
||||
private BeltObstacle GetDistanceAcrossPort(
|
||||
bool towardStart,
|
||||
float itemBeltT,
|
||||
float maxDistToCheck,
|
||||
HashSet<IMovementConveyor> visited)
|
||||
{
|
||||
var port = towardStart ? StartPort : EndPort;
|
||||
var boundaryT = towardStart ? 0f : Length;
|
||||
var distanceToBoundary = MathF.Abs(itemBeltT - boundaryT);
|
||||
|
||||
// 🚫 Boundary already exceeds budget
|
||||
// if (distanceToBoundary >= maxDistToCheck)
|
||||
// {
|
||||
// return maxDistToCheck;
|
||||
// }
|
||||
|
||||
|
||||
|
||||
|
||||
var neighborPort = FindFacingConveyorPort(port);
|
||||
if (!neighborPort.HasValue(out var conveyorPort))
|
||||
{
|
||||
var o = GetPortFacing(port);
|
||||
if (o.HasValue(out var otherport))
|
||||
{
|
||||
return new BoundaryBeltObstacle(distanceToBoundary);
|
||||
// return new BeltObstacle(ObstacleKind.Boundary,distanceToBoundary,laneSpan,this,null, null);//TODO THIS SHOULD GET SPACE IN PORT SO ITEMS KNOW IF THEY CAN FIT
|
||||
}
|
||||
return new BoundaryBeltObstacle(distanceToBoundary);
|
||||
}
|
||||
|
||||
if (conveyorPort.BeltT is not BeltTEnd end)
|
||||
{
|
||||
return new BoundaryBeltObstacle(distanceToBoundary);
|
||||
}
|
||||
|
||||
var nextDirection = DirectionAwayFromEnd(end.End);
|
||||
var startT = end.End == ConveyorEnd.Start
|
||||
? 0f
|
||||
: conveyorPort.Conveyor.Length;
|
||||
|
||||
// 🔻 Remaining budget after reaching boundary
|
||||
// var remainingDist = maxDistToCheck - distanceToBoundary;
|
||||
// if (remainingDist <= 0f)
|
||||
// {
|
||||
// return maxDistToCheck;
|
||||
// }
|
||||
|
||||
var recursiveDistance =
|
||||
conveyorPort.Conveyor.GetDistanceToNextItem(
|
||||
nextDirection,
|
||||
startT,
|
||||
maxDistToCheck,
|
||||
visited
|
||||
);
|
||||
return recursiveDistance with { Distance = recursiveDistance.Distance + distanceToBoundary };
|
||||
}
|
||||
private IOption<ConveyorPort> FindFacingConveyorPort(
|
||||
IBeltPort slot)
|
||||
{
|
||||
var targetpos = slot.Profile.LocalOffset.TransformDirection(Vector3I.Forward);
|
||||
// var targetPos = port.Profile.Position + port.Profile.Face.ToVector();
|
||||
return GridRegistry
|
||||
.Get<IBeltPort>(slot.Profile.LocalOffset.LocalToWorld(Vector3I.Forward))
|
||||
|
||||
.Where(port => port is ConveyorPort)
|
||||
.Where(port => port.Profile.LocalOffset.TransformDirection(Vector3I.Forward) == slot.Profile.LocalOffset.TransformDirection(Vector3I.Back))
|
||||
.FirstOrNone()
|
||||
.Bind(p => p is ConveyorPort cp
|
||||
? cp.ToOption()
|
||||
: None<ConveyorPort>.Of());
|
||||
}
|
||||
public IOption<IBeltPort> GetPortFacing(IBeltPort slot) => slot.GetPortFacing(GridRegistry);
|
||||
|
||||
private BeltDirection DirectionAwayFromEnd(ConveyorEnd conveyorEnd) => conveyorEnd switch { ConveyorEnd.Start => BeltDirection.TowardEnd, ConveyorEnd.End => BeltDirection.TowardStart, _ => throw new NotSupportedException() };
|
||||
// public IEnumerable<Sorted1DList<ConveyorSlice>.ItemHandle> EnumerateTowardEnd() => Items.EnumerateTowardEnd();
|
||||
// public IEnumerable<Sorted1DList<ConveyorSlice>.ItemHandle> EnumerateTowardStart() => Items.EnumerateTowardStart();
|
||||
private static readonly Vector3[] _square = [new(-.5f, .5f, -.5f), new(.5f, .5f, -.5f), new(.5f, -.5f, -.5f), new(-.5f, -.5f, -.5f), new(-.5f, .5f, -.5f)];
|
||||
public const float ITEMSIZE = .2f;
|
||||
private const float ITEMHALFSIZE = ITEMSIZE / 2f;
|
||||
|
||||
// public override void _Process(double delta)
|
||||
// {
|
||||
// if (StartPort is null || EndPort is null)
|
||||
// {
|
||||
// return;
|
||||
// }
|
||||
// foreach (var item in GetPorts())
|
||||
// {
|
||||
// for (int ii = 0; ii < item.Profile.Width; ii++)
|
||||
// {
|
||||
// DebugDraw3D.DrawLinePath(_square.Select(i => item.Profile.LocalOffset.ToGodot().TranslatedLocal(i).TranslatedLocal(new Vector3(ii, 0, 0)).Origin).ToArray(), (!Engine.IsEditorHint()) && GetPortFacing(item).HasValue() ? Colors.Green : Colors.Red);
|
||||
|
||||
// }
|
||||
// // DebugDraw3D.DrawArrow(item.Profile.LocalOffset.Origin, item.Profile.LocalOffset.LocalToWorld(item.Profile.Face.ToVector()),(!Engine.IsEditorHint())&&GetPortFacing(item).HasValue()?Colors.Green:Colors.Red);
|
||||
// }
|
||||
// }
|
||||
|
||||
public ConveyorPort CreatePort(BeltPortProfile profile, BeltT beltT)
|
||||
{
|
||||
// var towardStartEnd = beltT is ItemConveyor.BeltTEnd end && end.End == ItemConveyor.ConveyorEnd.End;
|
||||
bool accept(IBeltItem item, float offset)
|
||||
{
|
||||
if (beltT is BeltTEnd end)
|
||||
{
|
||||
var startBeltT = end.End == ConveyorEnd.Start ? 0 : Length;
|
||||
var obstacle = GetDistanceToNextItem(end.End.Swap().DirectionTo(), startBeltT, offset);
|
||||
var distanceAllowed = obstacle.Distance;
|
||||
return ITEMSIZE < distanceAllowed;
|
||||
}
|
||||
else if (beltT is BeltTOffset endOffset)
|
||||
{
|
||||
return HasClearance(endOffset.T);
|
||||
}
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
bool tryInsert(IBeltItem item, float offset)
|
||||
{
|
||||
if (beltT is BeltTEnd end)
|
||||
{
|
||||
if (end.End == ConveyorEnd.End)
|
||||
{
|
||||
_items.Insert(item, Length - offset);
|
||||
// Items.Insert(new(item, Length - offset) { LaneSpan = laneSpan });
|
||||
// _items.Add(new(item, Length-offset));
|
||||
}
|
||||
else
|
||||
{
|
||||
_items.Insert(item, offset);
|
||||
// Items.Insert(new(item, offset) { LaneSpan = laneSpan });
|
||||
// _items.Insert(0, new(item, offset));
|
||||
}
|
||||
return true;
|
||||
}
|
||||
else if (beltT is BeltTOffset endOffset)
|
||||
{
|
||||
|
||||
if (!HasClearance(endOffset.T))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
_items.Insert(item, endOffset.T);
|
||||
// Items.Insert(new(item, endOffset.T) { LaneSpan = laneSpan });
|
||||
// int insertIndex = FindInsertIndex(endOffset.T);
|
||||
|
||||
// _items.Insert(insertIndex, new(item, endOffset.T));
|
||||
|
||||
return true;
|
||||
}
|
||||
// _items.Insert(0,new ConveyorSlice(item,0));
|
||||
// return true;
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
|
||||
var port = new ConveyorPort(this,
|
||||
profile,
|
||||
beltT,
|
||||
accept,
|
||||
tryInsert);
|
||||
return port;
|
||||
}
|
||||
private bool HasClearance(float centerT)
|
||||
{
|
||||
var lower = GetDistanceToNextItem(
|
||||
BeltDirection.TowardStart,
|
||||
centerT,
|
||||
ITEMSIZE
|
||||
);
|
||||
|
||||
var upper = GetDistanceToNextItem(
|
||||
BeltDirection.TowardEnd,
|
||||
centerT,
|
||||
ITEMSIZE
|
||||
);
|
||||
|
||||
float lowerAllowed =
|
||||
lower.Distance;// -
|
||||
// (lower.IsItem ? ItemConveyor.ITEMSIZE : 0);
|
||||
|
||||
float upperAllowed =
|
||||
upper.Distance;// -
|
||||
// (upper.IsItem ? ItemConveyor.ITEMSIZE : 0);
|
||||
|
||||
return ITEMSIZE < lowerAllowed &&
|
||||
ITEMSIZE < upperAllowed;
|
||||
}
|
||||
|
||||
public Ordered1DList<IBeltItem>.Enumerator EnumerateTowardEnd() => _items.EnumerateTowardEnd(-1);
|
||||
public Ordered1DList<IBeltItem>.Enumerator EnumerateTowardStart() => _items.EnumerateTowardStart(_items.Count);
|
||||
public bool HasItems() => _items.Count > 0;
|
||||
}
|
||||
//TODO Should liklely account for max search distance where the conveyorm may be needed to know
|
||||
public record class BeltObstacle(float Distance);
|
||||
public record ItemBeltObstacle(float Distance, ConveyorSlice Item) : BeltObstacle(Distance);
|
||||
public record BoundaryBeltObstacle(float DistanceToBoundary) : BeltObstacle(DistanceToBoundary);
|
||||
public record PortBeltObstacle(float DistanceToBoundary, IBeltPort BeltPort) : BoundaryBeltObstacle(DistanceToBoundary);
|
||||
|
||||
15
src/Core/Components/Converters/TagConverter.cs
Normal file
15
src/Core/Components/Converters/TagConverter.cs
Normal file
@@ -0,0 +1,15 @@
|
||||
namespace FoodFactory.Core.Components.Converters;
|
||||
|
||||
using SharpYaml.Serialization;
|
||||
|
||||
public sealed class TagConverter : YamlConverter<Tag>
|
||||
{
|
||||
public override Tag Read(YamlReader reader)
|
||||
{
|
||||
var tag = reader.GetScalarValue();
|
||||
reader.Read();
|
||||
return Tag.Create(tag);
|
||||
}
|
||||
|
||||
public override void Write(YamlWriter writer, Tag value) => writer.WriteScalar(value.Name);
|
||||
}
|
||||
1
src/Core/Components/Converters/TagConverter.cs.uid
Normal file
1
src/Core/Components/Converters/TagConverter.cs.uid
Normal file
@@ -0,0 +1 @@
|
||||
uid://cgs4d1j3i5ua6
|
||||
34
src/Core/Components/Converters/TagsConverter.cs
Normal file
34
src/Core/Components/Converters/TagsConverter.cs
Normal file
@@ -0,0 +1,34 @@
|
||||
namespace FoodFactory.Core.Components.Converters;
|
||||
|
||||
using System.Collections.Generic;
|
||||
using SharpYaml.Serialization;
|
||||
|
||||
public sealed class TagsConverter : YamlConverter<Tags>
|
||||
{
|
||||
public override Tags Read(YamlReader reader)
|
||||
{
|
||||
if (reader.TokenType != YamlTokenType.StartSequence)
|
||||
{
|
||||
throw new System.Exception();
|
||||
}
|
||||
List<string> tags = [];
|
||||
reader.Read();
|
||||
while (reader.TokenType != YamlTokenType.EndSequence)
|
||||
{
|
||||
tags.Add(reader.GetScalarValue());
|
||||
reader.Read();
|
||||
}
|
||||
reader.Read();
|
||||
return new Tags(tags);
|
||||
}
|
||||
|
||||
public override void Write(YamlWriter writer, Tags value)
|
||||
{
|
||||
writer.WriteStartSequence();
|
||||
foreach (var item in value)
|
||||
{
|
||||
writer.WriteScalar(item.Name);
|
||||
}
|
||||
writer.WriteEndSequence();
|
||||
}
|
||||
}
|
||||
1
src/Core/Components/Converters/TagsConverter.cs.uid
Normal file
1
src/Core/Components/Converters/TagsConverter.cs.uid
Normal file
@@ -0,0 +1 @@
|
||||
uid://dqn40oryt7yyt
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user