Compare commits
13 Commits
5906f248f4
...
170aebacdc
| 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"]
|
[submodule "assets"]
|
||||||
path = 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.enableXamlTools": false,
|
||||||
"dotnet.formatting.organizeImportsOnFormat": true,
|
"dotnet.formatting.organizeImportsOnFormat": true,
|
||||||
"dotnet.preferCSharpExtension": true,
|
"dotnet.preferCSharpExtension": true,
|
||||||
// "dotnet.server.useOmnisharp": true, // You decide
|
"dotnet.server.useOmnisharp": false, // You decide
|
||||||
"dotnetAcquisitionExtension.enableTelemetry": false,
|
"dotnetAcquisitionExtension.enableTelemetry": false,
|
||||||
"editor.semanticHighlighting.enabled": true,
|
"editor.semanticHighlighting.enabled": true,
|
||||||
// C# doc comment colorization gets lost with semantic highlighting, but we
|
// C# doc comment colorization gets lost with semantic highlighting, but we
|
||||||
@@ -164,7 +164,7 @@
|
|||||||
"omnisharp.enableEditorConfigSupport": true,
|
"omnisharp.enableEditorConfigSupport": true,
|
||||||
"omnisharp.enableMsBuildLoadProjectsOnDemand": false,
|
"omnisharp.enableMsBuildLoadProjectsOnDemand": false,
|
||||||
"omnisharp.maxFindSymbolsItems": 3000,
|
"omnisharp.maxFindSymbolsItems": 3000,
|
||||||
"omnisharp.useModernNet": true,
|
"omnisharp.useModernNet": false,
|
||||||
// Remove these if you're happy with your terminal profiles.
|
// Remove these if you're happy with your terminal profiles.
|
||||||
"terminal.integrated.defaultProfile.windows": "Git Bash",
|
"terminal.integrated.defaultProfile.windows": "Git Bash",
|
||||||
"terminal.integrated.profiles.windows": {
|
"terminal.integrated.profiles.windows": {
|
||||||
|
|||||||
@@ -10,6 +10,7 @@
|
|||||||
<!-- Required for some nuget packages to work -->
|
<!-- Required for some nuget packages to work -->
|
||||||
<!-- godotengine/godot/issues/42271#issuecomment-751423827 -->
|
<!-- godotengine/godot/issues/42271#issuecomment-751423827 -->
|
||||||
<CopyLocalLockFileAssemblies>true</CopyLocalLockFileAssemblies>
|
<CopyLocalLockFileAssemblies>true</CopyLocalLockFileAssemblies>
|
||||||
|
<AllowUnsafeBlocks>true</AllowUnsafeBlocks>
|
||||||
<!-- To show generated files -->
|
<!-- To show generated files -->
|
||||||
<!-- <EmitCompilerGeneratedFiles>true</EmitCompilerGeneratedFiles> -->
|
<!-- <EmitCompilerGeneratedFiles>true</EmitCompilerGeneratedFiles> -->
|
||||||
<!--
|
<!--
|
||||||
@@ -35,31 +36,45 @@
|
|||||||
|
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<!-- Production dependencies go here! -->
|
<!-- Production dependencies go here! -->
|
||||||
<PackageReference Include="Chickensoft.GameTools" Version="3.1.6" />
|
<PackageReference Include="Arch" Version="2.1.0" />
|
||||||
<PackageReference Include="SjkScripts" Version="1.0.1" />
|
<PackageReference Include="Arch.LowLevel" Version="1.1.5" />
|
||||||
<PackageReference Include="System.IO.Abstractions" Version="22.1.0" />
|
<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="EnvironmentAbstractions" Version="5.0.0" />
|
||||||
<PackageReference Include="GodotSharp.SourceGenerators" Version="2.6.0" PrivateAssets="all" OutputItemType="analyzer" />
|
<PackageReference Include="GodotSharp.SourceGenerators" Version="2.7.0" PrivateAssets="all" OutputItemType="analyzer" />
|
||||||
<PackageReference Include="Chickensoft.SaveFileBuilder" Version="1.3.54" />
|
<PackageReference Include="Chickensoft.SaveFileBuilder" Version="2.0.1" />
|
||||||
<PackageReference Include="Chickensoft.AutoInject" Version="2.9.18" PrivateAssets="all" />
|
<PackageReference Include="Chickensoft.AutoInject" Version="2.13.15" PrivateAssets="all" />
|
||||||
<PackageReference Include="Chickensoft.Collections" Version="3.1.4" />
|
<PackageReference Include="Chickensoft.Collections" Version="3.1.4" />
|
||||||
<PackageReference Include="Chickensoft.GodotNodeInterfaces" Version="2.4.57" />
|
<PackageReference Include="Chickensoft.GodotNodeInterfaces" Version="3.0.20" />
|
||||||
<PackageReference Include="Chickensoft.Introspection" Version="3.0.2" />
|
<PackageReference Include="Chickensoft.Introspection" Version="3.0.3" />
|
||||||
<PackageReference Include="Chickensoft.Introspection.Generator" Version="3.0.2" PrivateAssets="all" OutputItemType="analyzer" />
|
<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" Version="3.1.0" />
|
||||||
<PackageReference Include="Chickensoft.Serialization.Godot" Version="0.8.46" />
|
<PackageReference Include="Chickensoft.Serialization.Godot" Version="0.9.1" />
|
||||||
<PackageReference Include="Chickensoft.LogicBlocks" Version="5.20.0" />
|
<PackageReference Include="Chickensoft.LogicBlocks" Version="6.1.1" />
|
||||||
<PackageReference Include="Chickensoft.LogicBlocks.DiagramGenerator" Version="5.20.0" PrivateAssets="all" OutputItemType="analyzer" />
|
<PackageReference Include="Chickensoft.LogicBlocks.Auto" Version="6.1.1" />
|
||||||
<PackageReference Include="Chickensoft.UMLGenerator" Version="1.1.0" />
|
<PackageReference Include="Chickensoft.LogicBlocks.DiagramGenerator" Version="6.1.1" PrivateAssets="all" OutputItemType="analyzer" />
|
||||||
<PackageReference Include="Chickensoft.Sync" Version="2.2.0" />
|
<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>
|
||||||
|
|
||||||
<ItemGroup Condition="'$(RunTests)' == 'true'">
|
<ItemGroup Condition="'$(RunTests)' == 'true'">
|
||||||
<!-- Test dependencies go here! -->
|
<!-- Test dependencies go here! -->
|
||||||
<!-- Dependencies added here will not be included in release builds. -->
|
<!-- 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 -->
|
<!-- 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! -->
|
<!-- Bring your own assertion library for tests! -->
|
||||||
<!-- We're using Shouldly for this example, but you can use anything. -->
|
<!-- We're using Shouldly for this example, but you can use anything. -->
|
||||||
<PackageReference Include="Shouldly" Version="4.3.0" />
|
<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/",
|
"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
|
"source": "zip", // optional — this is the default
|
||||||
// "checkout": "master", // 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/features=PackedStringArray("4.6", "C#", "Mobile")
|
||||||
config/icon="res://icon.png"
|
config/icon="res://icon.png"
|
||||||
|
|
||||||
|
[autoload]
|
||||||
|
|
||||||
|
ControllerIcons="*uid://cvqaxgmq6by3f"
|
||||||
|
|
||||||
[debug_draw_3d]
|
[debug_draw_3d]
|
||||||
|
|
||||||
settings/addon_root_folder="res://addons/debug_draw_3d"
|
settings/addon_root_folder="res://addons/debug_draw_3d"
|
||||||
@@ -41,7 +45,7 @@ naming/scene_name_casing=1
|
|||||||
|
|
||||||
[editor_plugins]
|
[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]
|
[gui]
|
||||||
|
|
||||||
@@ -49,6 +53,75 @@ theme/default_font_multichannel_signed_distance_field=true
|
|||||||
theme/default_font_generate_mipmaps=true
|
theme/default_font_generate_mipmaps=true
|
||||||
theme/default_theme_scale=2.0
|
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]
|
[rendering]
|
||||||
|
|
||||||
renderer/rendering_method="mobile"
|
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;
|
namespace FoodFactory.Conveyors;
|
||||||
using Godot;
|
|
||||||
using System;
|
|
||||||
|
|
||||||
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;
|
[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
|
public GridTransform3D VoxelTransform
|
||||||
{
|
{
|
||||||
get => GridTransform3D.FromGodot(Transform);
|
get => GridTransform3D.FromGodot(GlobalTransform);
|
||||||
set => Transform = value.ToGodot();
|
set => GlobalTransform = value.ToGodot();
|
||||||
}
|
}
|
||||||
|
[Node("ConveyorLogic")]
|
||||||
|
public TestItemConveyor ConveyorLogic { get; set; } = default!;
|
||||||
public override void _Ready()
|
public override void _Ready()
|
||||||
{
|
{
|
||||||
var node = GetNode("Node") as TestItemConveyor;
|
|
||||||
base._Ready();
|
base._Ready();
|
||||||
|
Debug.Assert(ConveyorLogic is not null, $"{nameof(ConveyorLogic)} is not set on {nameof(ConveyorBeltStraight)}, Path {SceneFilePath}");
|
||||||
// GD.PrintS(VoxelTransform.Origin,VoxelTransform.Forward);
|
// GD.PrintS(VoxelTransform.Origin,VoxelTransform.Forward);
|
||||||
node.StartPort = node.CreatePort(
|
ConveyorLogic.StartPort = ConveyorLogic.CreatePort(
|
||||||
new(VoxelTransform.TranslatedLocal(new(1-Width,0,0)).RotateY(2),
|
new(VoxelTransform.TranslatedLocal(new(1 - Width, 0, 0)).RotateY(2),
|
||||||
// Direction.Back,
|
// Direction.Back,
|
||||||
Width,
|
Width,
|
||||||
PortAccess.BiDirectional),
|
PortAccess.BiDirectional),
|
||||||
new ItemConveyor.BeltTEnd(ItemConveyor.ConveyorEnd.Start),
|
new BeltTEnd(ConveyorEnd.Start));
|
||||||
new LaneSpan(0, 1));
|
ConveyorLogic.EndPort = ConveyorLogic.CreatePort(
|
||||||
// GD.Print(VoxelTransform.Origin,GridTransform3D.FromGodot(Transform.TranslatedLocal(Vector3.Left)).Origin);
|
|
||||||
node.EndPort = node.CreatePort(
|
|
||||||
new(VoxelTransform, //new(GridTransform3D.FromGodot(Transform.TranslatedLocal(Vector3.Left)),
|
new(VoxelTransform, //new(GridTransform3D.FromGodot(Transform.TranslatedLocal(Vector3.Left)),
|
||||||
// Direction.Front,
|
// Direction.Front,
|
||||||
Width,
|
Width,
|
||||||
PortAccess.BiDirectional),
|
PortAccess.BiDirectional),
|
||||||
new ItemConveyor.BeltTEnd(ItemConveyor.ConveyorEnd.End),
|
new BeltTEnd(ConveyorEnd.End));
|
||||||
new LaneSpan(0, 1));
|
|
||||||
|
|
||||||
// node.OtherPorts = [
|
ConveyorLogic.OtherPorts = [
|
||||||
// node.CreatePort(
|
ConveyorLogic.CreatePort(
|
||||||
// new(VoxelTransform.RotateY90CW(),
|
new(VoxelTransform.RotateY90CW(),
|
||||||
// // Direction.Right,
|
// Direction.Right,
|
||||||
// 1,
|
1,
|
||||||
// PortAccess.In),
|
PortAccess.In),
|
||||||
// new ItemConveyor.BeltTOffset(node.Length/2),
|
new BeltTOffset(ConveyorLogic.Length/2)),
|
||||||
// LaneSpan.One),
|
ConveyorLogic.CreatePort(
|
||||||
// node.CreatePort(
|
new(VoxelTransform.RotateY90CCW(),
|
||||||
// new(VoxelTransform.RotateY90CCW(),
|
// Direction.Left,
|
||||||
// // Direction.Left,
|
1,
|
||||||
// 1,
|
PortAccess.In),
|
||||||
// PortAccess.In),
|
new BeltTOffset(ConveyorLogic.Length/2))
|
||||||
// new ItemConveyor.BeltTOffset(node.Length/2),
|
];
|
||||||
// LaneSpan.One)
|
|
||||||
// ];
|
}
|
||||||
// new ConveyorPort(this,
|
public void OnResolved()
|
||||||
// new BeltPortProfile(VoxelPos, Direction.Back, 1, PortAccess.BiDirectional),
|
{
|
||||||
// new ItemConveyor.BeltTEnd(ItemConveyor.ConveyorEnd.Start),
|
_guid = FoodFactoryApi.GridRegistry.Register(this, VoxelTransform.Origin);
|
||||||
// (item, offset) =>
|
this.Provide();
|
||||||
// {
|
|
||||||
// var obstacle = node.GetDistanceToNextItem(ItemConveyor.BeltDirection.TowardEnd, 0, offset, LaneSpan.One);
|
}
|
||||||
// return ItemConveyor.ITEMSIZE < obstacle.DistanceToCenter - (obstacle.IsItem ? ItemConveyor.ITEMSIZE : 0);
|
public override void _ExitTree()
|
||||||
// },
|
{
|
||||||
// (item, offset) => { _items.Insert(0, new(item, offset)); return true;});
|
FoodFactoryApi.GridRegistry.UnRegister(_guid);
|
||||||
// node.EndPort = new ConveyorPort(this,
|
base._ExitTree();
|
||||||
// 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;});
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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://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://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="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"]
|
[sub_resource type="Curve3D" id="Curve3D_x5qdr"]
|
||||||
_data = {
|
_data = {
|
||||||
@@ -13,22 +13,23 @@ _data = {
|
|||||||
}
|
}
|
||||||
point_count = 2
|
point_count = 2
|
||||||
|
|
||||||
[node name="ConveyorBeltStraight" type="Node3D"]
|
[node name="ConveyorBeltStraight" type="Node3D" unique_id=1032197109]
|
||||||
script = ExtResource("1_mn8ro")
|
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")
|
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)
|
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)
|
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")
|
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")
|
script = ExtResource("4_g7n1f")
|
||||||
Path3D = NodePath("../Path3D")
|
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