mirror of
https://github.com/stride3d/stride.git
synced 2026-10-12 02:23:23 +00:00
Merge branch 'master' into feature/stride-sdk
This commit is contained in:
@@ -20,5 +20,13 @@ Stride is a game engine project that requires careful code reviews to maintain q
|
||||
- If you find a bug or performance issue, suggest a concrete fix in the PR.
|
||||
- For large PRs (20+ C#/*.cs files), do not attempt a full review, only highlight critical or blocking issues.
|
||||
- Always consider the context and established patterns in the Stride codebase before making suggestions.
|
||||
- **Always check for missing, incomplete, or incorrect XML documentation comments** on public types, methods, properties, and fields.
|
||||
- **Provide XML comment suggestions as individual, separate items** so they can be reviewed and approved independently.
|
||||
- **Do not rewrite existing XML comments unless they contain errors** such as incorrect information, poor grammar, typos, or lack clarity. Preserve well-written documentation.
|
||||
- When suggesting XML comments, ensure they are:
|
||||
- Accurate and describe the actual functionality
|
||||
- Consistent with existing documentation style in the codebase
|
||||
- Include `<summary>`, `<param>`, `<returns>`, `<exception>` and `<remarks>` tags where appropriate
|
||||
- Clear and helpful for API consumers
|
||||
|
||||
The goal is to minimize noise and maximize helpful, actionable feedback.
|
||||
|
||||
@@ -1,3 +1,3 @@
|
||||
version https://git-lfs.github.com/spec/v1
|
||||
oid sha256:c76d63dbc0c7c64e52f8ac2c5f8d15645956737befdcccac5ca09c5b86240ed3
|
||||
size 150528
|
||||
oid sha256:63c65b2d24cd6ae487ea6c9d957343dba83c7992a0a2831d20f0f4da5b78dadc
|
||||
size 147456
|
||||
|
||||
@@ -1 +1 @@
|
||||
C76D63DBC0C7C64E52F8AC2C5F8D15645956737BEFDCCCAC5CA09C5B86240ED3
|
||||
63C65B2D24CD6AE487EA6C9D957343DBA83C7992A0A2831D20F0F4DA5B78DADC
|
||||
@@ -1,3 +1,3 @@
|
||||
version https://git-lfs.github.com/spec/v1
|
||||
oid sha256:0d24c0c9a848e934363ba72dd325057f3f0f89737b7e0403896abaca860e1981
|
||||
size 411136
|
||||
oid sha256:c21b7719e470059842e4525453a248a3dcf033f75c4cc61a50382684c478a33f
|
||||
size 407040
|
||||
|
||||
@@ -1,3 +1,3 @@
|
||||
version https://git-lfs.github.com/spec/v1
|
||||
oid sha256:dd9b63a4a434e57ede5727114221868fe77d252fff4c14c3342ba4e74ad3905d
|
||||
size 158208
|
||||
oid sha256:6c5bc9da7e5a867c953c362c3419ad3c9554df115baa61baec10ed2c3856b752
|
||||
size 155136
|
||||
|
||||
+1
-1
@@ -1 +1 @@
|
||||
DD9B63A4A434E57EDE5727114221868FE77D252FFF4C14C3342BA4E74AD3905D
|
||||
6C5BC9DA7E5A867C953C362C3419AD3C9554DF115BAA61BAEC10ED2C3856B752
|
||||
@@ -1,3 +1,3 @@
|
||||
version https://git-lfs.github.com/spec/v1
|
||||
oid sha256:bee19bb3a32f1397b17d0e587ec0c65d88cd9dc5ba09253220df43802a93e806
|
||||
oid sha256:27df1b2f1b7d6beb16f173e0039a4d0810171d87a4f160c7d3756912dbb4c290
|
||||
size 550400
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
Microsoft Visual Studio Solution File, Format Version 12.00
|
||||
Microsoft Visual Studio Solution File, Format Version 12.00
|
||||
# Visual Studio Version 17
|
||||
VisualStudioVersion = 17.13.35919.96
|
||||
MinimumVisualStudioVersion = 14.0.23107.0
|
||||
@@ -164,6 +164,12 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "BepuSample.Game", "Physics\
|
||||
EndProject
|
||||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "BepuSample.Windows", "Physics\BepuSample\BepuSample.Windows\BepuSample.Windows.csproj", "{0ED8F265-28F1-A98A-189F-4731E49FAB30}"
|
||||
EndProject
|
||||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Platformer2D.Windows", "Templates\Platformer2D\Platformer2D\Platformer2D.Windows\Platformer2D.Windows.csproj", "{2CEDF0C2-9927-46DF-A5F2-BEF58BDE3B71}"
|
||||
EndProject
|
||||
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Platformer2D", "Platformer2D", "{E67FC5AC-4BC9-41A5-9C9B-B979A734C175}"
|
||||
EndProject
|
||||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Platformer2D.Game", "Templates\Platformer2D\Platformer2D\Platformer2D.Game\Platformer2D.csproj", "{6681EADC-8842-414C-88C8-A83BB7EDBFEB}"
|
||||
EndProject
|
||||
Global
|
||||
GlobalSection(SolutionConfigurationPlatforms) = preSolution
|
||||
AppStore|Any CPU = AppStore|Any CPU
|
||||
@@ -1464,6 +1470,54 @@ Global
|
||||
{0ED8F265-28F1-A98A-189F-4731E49FAB30}.Testing|Mixed Platforms.Build.0 = Debug|Any CPU
|
||||
{0ED8F265-28F1-A98A-189F-4731E49FAB30}.Testing|Windows.ActiveCfg = Debug|Any CPU
|
||||
{0ED8F265-28F1-A98A-189F-4731E49FAB30}.Testing|Windows.Build.0 = Debug|Any CPU
|
||||
{2CEDF0C2-9927-46DF-A5F2-BEF58BDE3B71}.AppStore|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{2CEDF0C2-9927-46DF-A5F2-BEF58BDE3B71}.AppStore|Any CPU.Build.0 = Debug|Any CPU
|
||||
{2CEDF0C2-9927-46DF-A5F2-BEF58BDE3B71}.AppStore|Mixed Platforms.ActiveCfg = Debug|Any CPU
|
||||
{2CEDF0C2-9927-46DF-A5F2-BEF58BDE3B71}.AppStore|Mixed Platforms.Build.0 = Debug|Any CPU
|
||||
{2CEDF0C2-9927-46DF-A5F2-BEF58BDE3B71}.AppStore|Windows.ActiveCfg = Debug|Any CPU
|
||||
{2CEDF0C2-9927-46DF-A5F2-BEF58BDE3B71}.AppStore|Windows.Build.0 = Debug|Any CPU
|
||||
{2CEDF0C2-9927-46DF-A5F2-BEF58BDE3B71}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{2CEDF0C2-9927-46DF-A5F2-BEF58BDE3B71}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{2CEDF0C2-9927-46DF-A5F2-BEF58BDE3B71}.Debug|Mixed Platforms.ActiveCfg = Debug|Any CPU
|
||||
{2CEDF0C2-9927-46DF-A5F2-BEF58BDE3B71}.Debug|Mixed Platforms.Build.0 = Debug|Any CPU
|
||||
{2CEDF0C2-9927-46DF-A5F2-BEF58BDE3B71}.Debug|Windows.ActiveCfg = Debug|Any CPU
|
||||
{2CEDF0C2-9927-46DF-A5F2-BEF58BDE3B71}.Debug|Windows.Build.0 = Debug|Any CPU
|
||||
{2CEDF0C2-9927-46DF-A5F2-BEF58BDE3B71}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{2CEDF0C2-9927-46DF-A5F2-BEF58BDE3B71}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
{2CEDF0C2-9927-46DF-A5F2-BEF58BDE3B71}.Release|Mixed Platforms.ActiveCfg = Release|Any CPU
|
||||
{2CEDF0C2-9927-46DF-A5F2-BEF58BDE3B71}.Release|Mixed Platforms.Build.0 = Release|Any CPU
|
||||
{2CEDF0C2-9927-46DF-A5F2-BEF58BDE3B71}.Release|Windows.ActiveCfg = Release|Any CPU
|
||||
{2CEDF0C2-9927-46DF-A5F2-BEF58BDE3B71}.Release|Windows.Build.0 = Release|Any CPU
|
||||
{2CEDF0C2-9927-46DF-A5F2-BEF58BDE3B71}.Testing|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{2CEDF0C2-9927-46DF-A5F2-BEF58BDE3B71}.Testing|Any CPU.Build.0 = Debug|Any CPU
|
||||
{2CEDF0C2-9927-46DF-A5F2-BEF58BDE3B71}.Testing|Mixed Platforms.ActiveCfg = Debug|Any CPU
|
||||
{2CEDF0C2-9927-46DF-A5F2-BEF58BDE3B71}.Testing|Mixed Platforms.Build.0 = Debug|Any CPU
|
||||
{2CEDF0C2-9927-46DF-A5F2-BEF58BDE3B71}.Testing|Windows.ActiveCfg = Debug|Any CPU
|
||||
{2CEDF0C2-9927-46DF-A5F2-BEF58BDE3B71}.Testing|Windows.Build.0 = Debug|Any CPU
|
||||
{6681EADC-8842-414C-88C8-A83BB7EDBFEB}.AppStore|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{6681EADC-8842-414C-88C8-A83BB7EDBFEB}.AppStore|Any CPU.Build.0 = Debug|Any CPU
|
||||
{6681EADC-8842-414C-88C8-A83BB7EDBFEB}.AppStore|Mixed Platforms.ActiveCfg = Debug|Any CPU
|
||||
{6681EADC-8842-414C-88C8-A83BB7EDBFEB}.AppStore|Mixed Platforms.Build.0 = Debug|Any CPU
|
||||
{6681EADC-8842-414C-88C8-A83BB7EDBFEB}.AppStore|Windows.ActiveCfg = Debug|Any CPU
|
||||
{6681EADC-8842-414C-88C8-A83BB7EDBFEB}.AppStore|Windows.Build.0 = Debug|Any CPU
|
||||
{6681EADC-8842-414C-88C8-A83BB7EDBFEB}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{6681EADC-8842-414C-88C8-A83BB7EDBFEB}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{6681EADC-8842-414C-88C8-A83BB7EDBFEB}.Debug|Mixed Platforms.ActiveCfg = Debug|Any CPU
|
||||
{6681EADC-8842-414C-88C8-A83BB7EDBFEB}.Debug|Mixed Platforms.Build.0 = Debug|Any CPU
|
||||
{6681EADC-8842-414C-88C8-A83BB7EDBFEB}.Debug|Windows.ActiveCfg = Debug|Any CPU
|
||||
{6681EADC-8842-414C-88C8-A83BB7EDBFEB}.Debug|Windows.Build.0 = Debug|Any CPU
|
||||
{6681EADC-8842-414C-88C8-A83BB7EDBFEB}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{6681EADC-8842-414C-88C8-A83BB7EDBFEB}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
{6681EADC-8842-414C-88C8-A83BB7EDBFEB}.Release|Mixed Platforms.ActiveCfg = Release|Any CPU
|
||||
{6681EADC-8842-414C-88C8-A83BB7EDBFEB}.Release|Mixed Platforms.Build.0 = Release|Any CPU
|
||||
{6681EADC-8842-414C-88C8-A83BB7EDBFEB}.Release|Windows.ActiveCfg = Release|Any CPU
|
||||
{6681EADC-8842-414C-88C8-A83BB7EDBFEB}.Release|Windows.Build.0 = Release|Any CPU
|
||||
{6681EADC-8842-414C-88C8-A83BB7EDBFEB}.Testing|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{6681EADC-8842-414C-88C8-A83BB7EDBFEB}.Testing|Any CPU.Build.0 = Debug|Any CPU
|
||||
{6681EADC-8842-414C-88C8-A83BB7EDBFEB}.Testing|Mixed Platforms.ActiveCfg = Debug|Any CPU
|
||||
{6681EADC-8842-414C-88C8-A83BB7EDBFEB}.Testing|Mixed Platforms.Build.0 = Debug|Any CPU
|
||||
{6681EADC-8842-414C-88C8-A83BB7EDBFEB}.Testing|Windows.ActiveCfg = Debug|Any CPU
|
||||
{6681EADC-8842-414C-88C8-A83BB7EDBFEB}.Testing|Windows.Build.0 = Debug|Any CPU
|
||||
EndGlobalSection
|
||||
GlobalSection(SolutionProperties) = preSolution
|
||||
HideSolutionNode = FALSE
|
||||
@@ -1541,6 +1595,9 @@ Global
|
||||
{02EA681E-C7D8-13C7-8484-4AC65E1B71E8} = {ED0924FF-2147-4C47-9DE6-37A549629A9D}
|
||||
{1A49E176-B829-4F72-80B2-9BED777872B7} = {02EA681E-C7D8-13C7-8484-4AC65E1B71E8}
|
||||
{0ED8F265-28F1-A98A-189F-4731E49FAB30} = {02EA681E-C7D8-13C7-8484-4AC65E1B71E8}
|
||||
{E67FC5AC-4BC9-41A5-9C9B-B979A734C175} = {1167BFE9-9460-4E2D-9FDB-A084B8961385}
|
||||
{2CEDF0C2-9927-46DF-A5F2-BEF58BDE3B71} = {E67FC5AC-4BC9-41A5-9C9B-B979A734C175}
|
||||
{6681EADC-8842-414C-88C8-A83BB7EDBFEB} = {E67FC5AC-4BC9-41A5-9C9B-B979A734C175}
|
||||
EndGlobalSection
|
||||
GlobalSection(ExtensibilityGlobals) = postSolution
|
||||
SolutionGuid = {6F8B04D3-3886-4523-9B40-74FAA1948673}
|
||||
|
||||
@@ -0,0 +1,3 @@
|
||||
version https://git-lfs.github.com/spec/v1
|
||||
oid sha256:db0bf70a7efcdf8abcd62315ca7ad510607ca526ec82e97bab3f2b8c38477fc8
|
||||
size 5870
|
||||
@@ -0,0 +1,11 @@
|
||||
|
||||
Microsoft Visual Studio Solution File, Format Version 12.00
|
||||
# Visual Studio 16
|
||||
VisualStudioVersion = 16.0.0.0
|
||||
MinimumVisualStudioVersion = 16.0.0.0
|
||||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Platformer2D.Windows", "Platformer2D\Platformer2D.Windows\Platformer2D.Windows.csproj", "{2304F791-B508-4281-94E8-37B0F9CF9839}"
|
||||
EndProject
|
||||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Platformer2D.Game", "Platformer2D\Platformer2D.Game\Platformer2D.csproj", "{5836D368-DBA7-4DC6-BBD0-040AF9C52765}"
|
||||
EndProject
|
||||
Global
|
||||
EndGlobal
|
||||
@@ -0,0 +1,3 @@
|
||||
version https://git-lfs.github.com/spec/v1
|
||||
oid sha256:e44554ec626707e71e2a08a5f0d0a4f28ad2667f4d34289aaa301a9e4facee98
|
||||
size 21982
|
||||
@@ -0,0 +1,3 @@
|
||||
version https://git-lfs.github.com/spec/v1
|
||||
oid sha256:36484a21efdb6dc1a75bf8e516ad63780497232a3c23ca4424f17c90fc483d67
|
||||
size 7256
|
||||
@@ -0,0 +1,8 @@
|
||||
!Sound
|
||||
Id: ae74b3ba-e6ab-4b7d-aefa-e5c13aeee712
|
||||
SerializedVersion: {Stride: 2.0.0.0}
|
||||
Tags: []
|
||||
Source: !file ../../Resources/coin_get.wav
|
||||
CompressionRatio: 15
|
||||
StreamFromDisk: false
|
||||
Spatialized: false
|
||||
@@ -0,0 +1,46 @@
|
||||
!PrefabAsset
|
||||
Id: e84eac4c-033e-490b-bf5b-3b0466b56bac
|
||||
SerializedVersion: {Stride: 3.1.0.1}
|
||||
Tags: []
|
||||
Hierarchy:
|
||||
RootParts:
|
||||
- ref!! 73a4d210-3371-45d9-ae64-8309c29cd682
|
||||
Parts:
|
||||
- Folder: Coins
|
||||
Entity:
|
||||
Id: 73a4d210-3371-45d9-ae64-8309c29cd682
|
||||
Name: Coin
|
||||
Components:
|
||||
bd6c862663f431c20e4e71d1008ede5e: !TransformComponent
|
||||
Id: 4c14e52c-7a5c-4a9c-a660-a49c5b25fef7
|
||||
Position: {X: 6.0, Y: -3.0, Z: 0.0}
|
||||
Rotation: {X: 0.0, Y: 0.0, Z: 0.0, W: 1.0}
|
||||
Scale: {X: 1.0, Y: 1.0, Z: 1.0}
|
||||
Children: {}
|
||||
d5890e1a6f58646b097a8eb852dce712: !SpriteComponent
|
||||
Id: 4ec5ec49-f029-41ca-9402-c6802b9bca82
|
||||
SpriteProvider: !SpriteFromSheet
|
||||
Sheet: c9c540bb-2671-42de-bf06-b935954cd604:Sprites/coin_sheet
|
||||
Color: {R: 1.0, G: 1.0, B: 1.0, A: 1.0}
|
||||
Sampler: PointClamp
|
||||
227a9894ca38cdbfaba3ceaabddf708f: !Platformer2D.Gameplay.CoinRotation,Platformer2D
|
||||
Id: efe1a55c-8b48-4e39-891e-e9ee9d93fd06
|
||||
Sprite: ref!! 4ec5ec49-f029-41ca-9402-c6802b9bca82
|
||||
00333083b1a4a66664cf165b07be04fd: !Platformer2D.Gameplay.CollectCoin,Platformer2D
|
||||
Id: 710fb168-8788-47e4-a553-fa3cc1a6db0a
|
||||
StaticColliderComponent: ref!! 4e5cc1af-9342-4dc7-8d7a-cd17e59dad7a
|
||||
SoundEffect: ae74b3ba-e6ab-4b7d-aefa-e5c13aeee712:Audio/coin_get
|
||||
1860df22024defbf44e056d1d0e1464d: !StaticColliderComponent
|
||||
Id: 4e5cc1af-9342-4dc7-8d7a-cd17e59dad7a
|
||||
CanSleep: false
|
||||
Restitution: 0.0
|
||||
Friction: 0.5
|
||||
RollingFriction: 0.0
|
||||
CcdMotionThreshold: 0.0
|
||||
CcdSweptSphereRadius: 0.0
|
||||
IsTrigger: true
|
||||
AlwaysUpdateNaviMeshCache: false
|
||||
ColliderShapes:
|
||||
f187d37aff16130ec23d3e708f3632c8: !SphereColliderShapeDesc
|
||||
Is2D: true
|
||||
LocalOffset: {X: 0.0, Y: 0.0, Z: 0.0}
|
||||
@@ -0,0 +1,37 @@
|
||||
!GameSettingsAsset
|
||||
Id: 9f0046b5-44b6-4761-871f-4a97d011015a
|
||||
SerializedVersion: {Stride: 3.1.0.1}
|
||||
Tags: []
|
||||
DefaultScene: b97c1868-2681-4e4d-b8b9-a19960649142:MainScene
|
||||
GraphicsCompositor: 26e6e1f6-f549-49f2-a395-ca58f7e17b89:GraphicsCompositor
|
||||
Defaults:
|
||||
- !Stride.Audio.AudioEngineSettings,Stride.Audio
|
||||
HrtfSupport: false
|
||||
- !Stride.Assets.EditorSettings,Stride.Assets
|
||||
RenderingMode: LDR
|
||||
- !Stride.Graphics.RenderingSettings,Stride.Graphics
|
||||
DefaultBackBufferWidth: 1280
|
||||
DefaultBackBufferHeight: 720
|
||||
AdaptBackBufferToScreen: false
|
||||
DefaultGraphicsProfile: Level_10_0
|
||||
ColorSpace: Linear
|
||||
DisplayOrientation: LandscapeRight
|
||||
- !Stride.Streaming.StreamingSettings,Stride.Rendering
|
||||
ManagerUpdatesInterval: 0:00:00:00.0330000
|
||||
ResourceLiveTimeout: 0:00:00:08.0000000
|
||||
- !Stride.Assets.Textures.TextureSettings,Stride.Assets
|
||||
TextureQuality: Fast
|
||||
Overrides: []
|
||||
PlatformFilters:
|
||||
- PowerVR SGX 54[0-9]
|
||||
- Adreno \(TM\) 2[0-9][0-9]
|
||||
- Adreno (TM) 320
|
||||
- Adreno (TM) 330
|
||||
- Adreno \(TM\) 4[0-9][0-9]
|
||||
- NVIDIA Tegra
|
||||
- Intel(R) HD Graphics
|
||||
- ^Mali\-4
|
||||
- ^Mali\-T6
|
||||
- ^Mali\-T7
|
||||
SplashScreenTexture: d26edb11-10bd-403c-b3c2-9c7fcccf25e5:StrideDefaultSplashScreen
|
||||
SplashScreenColor: {R: 0, G: 0, B: 0, A: 255}
|
||||
@@ -0,0 +1,222 @@
|
||||
!GraphicsCompositorAsset
|
||||
Id: 26e6e1f6-f549-49f2-a395-ca58f7e17b89
|
||||
SerializedVersion: {Stride: 3.1.0.1}
|
||||
Tags: []
|
||||
Archetype: 823a81bf-bac0-4552-9267-aeed499c40df:DefaultGraphicsCompositorLevel10
|
||||
Cameras:
|
||||
de2e75c3b2b23e54162686363f3f138e:
|
||||
Id: 291722d5-fb04-46ed-b58d-7f4455266d8e
|
||||
Name: Main
|
||||
RenderStages:
|
||||
47116750c1a5d449b4ad3625f71439b3:
|
||||
Id: 8d4a292b-24b3-4633-8906-dfec11b7c9b8
|
||||
Name: Opaque
|
||||
EffectSlotName: Main
|
||||
SortMode: !SortModeStateChange {}
|
||||
9105a30fee026d4893472b6aee83d035:
|
||||
Id: 7e91ee8c-255a-4bc0-90fb-feebfd461e2f
|
||||
Name: Transparent
|
||||
EffectSlotName: Main
|
||||
SortMode: !BackToFrontSortMode {}
|
||||
554e52c061404d4684dd7c4c70f70e0e:
|
||||
Id: f0962b6c-387c-454e-9759-721acf05bb80
|
||||
Name: ShadowMapCaster
|
||||
EffectSlotName: ShadowMapCaster
|
||||
SortMode: !FrontToBackSortMode {}
|
||||
5a50638f5c514dc490c8c4f57cc88b57:
|
||||
Id: 9d878307-f1a6-4ff9-bc10-2729c6e2cf40
|
||||
Name: ShadowMapCasterParaboloid
|
||||
EffectSlotName: ShadowMapCasterParaboloid
|
||||
SortMode: !FrontToBackSortMode {}
|
||||
bc1a77d2ab254a6e920f86cff65cd75e:
|
||||
Id: 65947d6a-5d89-456c-8774-830c2a2da459
|
||||
Name: ShadowMapCasterCubeMap
|
||||
EffectSlotName: ShadowMapCasterCubeMap
|
||||
SortMode: !FrontToBackSortMode {}
|
||||
33d9d311a1a65601da9ef56775477f95:
|
||||
Id: a1e7e6ad-7626-4eb6-a125-13275a9531c2
|
||||
Name: GBuffer
|
||||
EffectSlotName: GBuffer
|
||||
SortMode: !FrontToBackSortMode {}
|
||||
RenderFeatures:
|
||||
d8fb80b0e7995140a46bca8dc36ee8a2: !Stride.Rendering.MeshRenderFeature,Stride.Rendering
|
||||
RenderStageSelectors:
|
||||
44cf4a95ef82544e9ce3c6507d5569a9: !Stride.Rendering.MeshTransparentRenderStageSelector,Stride.Rendering
|
||||
OpaqueRenderStage: ref!! 8d4a292b-24b3-4633-8906-dfec11b7c9b8
|
||||
TransparentRenderStage: ref!! 7e91ee8c-255a-4bc0-90fb-feebfd461e2f
|
||||
EffectName: StrideForwardShadingEffect
|
||||
6f7224048750e7260ea87c444f74b32c: !Stride.Rendering.Shadows.ShadowMapRenderStageSelector,Stride.Rendering
|
||||
ShadowMapRenderStage: ref!! f0962b6c-387c-454e-9759-721acf05bb80
|
||||
EffectName: StrideForwardShadingEffect.ShadowMapCaster
|
||||
b60663d7cb46417a94341a39c3bc1a12: !Stride.Rendering.Shadows.ShadowMapRenderStageSelector,Stride.Rendering
|
||||
ShadowMapRenderStage: ref!! 9d878307-f1a6-4ff9-bc10-2729c6e2cf40
|
||||
EffectName: StrideForwardShadingEffect.ShadowMapCasterParaboloid
|
||||
f5533b1249b942df8a8aba311cd79532: !Stride.Rendering.Shadows.ShadowMapRenderStageSelector,Stride.Rendering
|
||||
ShadowMapRenderStage: ref!! 65947d6a-5d89-456c-8774-830c2a2da459
|
||||
EffectName: StrideForwardShadingEffect.ShadowMapCasterCubeMap
|
||||
106341b76db9fcda6a033dad16aa708b: !Stride.Rendering.MeshTransparentRenderStageSelector,Stride.Rendering
|
||||
OpaqueRenderStage: ref!! a1e7e6ad-7626-4eb6-a125-13275a9531c2
|
||||
EffectName: StrideForwardShadingEffect.ShadowMapCaster
|
||||
PipelineProcessors:
|
||||
d70f5aee0616e4ab25081ceaf643290c: !Stride.Rendering.MeshPipelineProcessor,Stride.Rendering
|
||||
TransparentRenderStage: ref!! 7e91ee8c-255a-4bc0-90fb-feebfd461e2f
|
||||
26c899b17f88c21ab13bf60a7220ccd1: !Stride.Rendering.ShadowMeshPipelineProcessor,Stride.Rendering
|
||||
ShadowMapRenderStage: ref!! f0962b6c-387c-454e-9759-721acf05bb80
|
||||
ff51170a7d1a4761b73ef6a5c9f0cba2: !Stride.Rendering.ShadowMeshPipelineProcessor,Stride.Rendering
|
||||
ShadowMapRenderStage: ref!! 9d878307-f1a6-4ff9-bc10-2729c6e2cf40
|
||||
DepthClipping: true
|
||||
ae4336b0a9514e8488e8e0ccbcef25f4: !Stride.Rendering.ShadowMeshPipelineProcessor,Stride.Rendering
|
||||
ShadowMapRenderStage: ref!! 65947d6a-5d89-456c-8774-830c2a2da459
|
||||
DepthClipping: true
|
||||
RenderFeatures:
|
||||
86b959cbdf51a1438d4973177c77c627: !Stride.Rendering.TransformRenderFeature,Stride.Rendering {}
|
||||
8e0351fee9883922648a11016224b195: !Stride.Rendering.SkinningRenderFeature,Stride.Rendering {}
|
||||
f5a2017030ba4b28784e804807ce7628: !Stride.Rendering.Materials.MaterialRenderFeature,Stride.Rendering {}
|
||||
83fea7526ebe4893a5bad953d0502bfd: !Stride.Rendering.Shadows.ShadowCasterRenderFeature,Stride.Rendering {}
|
||||
65743b4380f4cc43b2b4bdc23cd0c07c: !Stride.Rendering.Lights.ForwardLightingRenderFeature,Stride.Rendering
|
||||
LightRenderers:
|
||||
7ac2775468f53c4399b2f3f6357c85c9: !Stride.Rendering.Lights.LightAmbientRenderer,Stride.Rendering {}
|
||||
7b68f9cd17404a4ba9e5f7df72e3b48d: !Stride.Rendering.Lights.LightDirectionalGroupRenderer,Stride.Rendering {}
|
||||
411fdcfb9fc388449a0443173dfa3f27: !Stride.Rendering.Lights.LightSkyboxRenderer,Stride.Rendering {}
|
||||
facdcd5b543cf1c6bdf2138aab6cc473: !Stride.Rendering.Lights.LightClusteredPointSpotGroupRenderer,Stride.Rendering {}
|
||||
79582329a9cf466e960f8920f579de9b: !Stride.Rendering.Lights.LightPointGroupRenderer,Stride.Rendering {}
|
||||
cf0c6bd4198b4cc4aaaab5b54870bdfd: !Stride.Rendering.Lights.LightSpotGroupRenderer,Stride.Rendering {}
|
||||
451af18f3f5c4187cf3fe5f33feb46b1: !Stride.Rendering.LightProbes.LightProbeRenderer,Stride.Rendering {}
|
||||
ShadowMapRenderer: !Stride.Rendering.Shadows.ShadowMapRenderer,Stride.Rendering
|
||||
Renderers:
|
||||
7c3d3d4c86834c3551bacde2527b3836: !Stride.Rendering.Shadows.LightDirectionalShadowMapRenderer,Stride.Rendering
|
||||
ShadowCasterRenderStage: ref!! f0962b6c-387c-454e-9759-721acf05bb80
|
||||
1c204b09435636256a3fcfd6f9ddb347: !Stride.Rendering.Shadows.LightSpotShadowMapRenderer,Stride.Rendering
|
||||
ShadowCasterRenderStage: ref!! f0962b6c-387c-454e-9759-721acf05bb80
|
||||
7c8c69ce27034b4c8bbcab0bcdfe954b: !Stride.Rendering.Shadows.LightPointShadowMapRendererParaboloid,Stride.Rendering
|
||||
ShadowCasterRenderStage: ref!! 9d878307-f1a6-4ff9-bc10-2729c6e2cf40
|
||||
d59ef45dd99e49d3af3887763d153aa7: !Stride.Rendering.Shadows.LightPointShadowMapRendererCubeMap,Stride.Rendering
|
||||
ShadowCasterRenderStage: ref!! 65947d6a-5d89-456c-8774-830c2a2da459
|
||||
42be209952db58294c9f06c58809b8ca: !Stride.Rendering.InstancingRenderFeature,Stride.Rendering {}
|
||||
28e9bf54a5adbe063f59fb17acb2723e: !Stride.Rendering.Sprites.SpriteRenderFeature,Stride.Rendering
|
||||
RenderStageSelectors:
|
||||
d74665cff080638a2439c4422e542d85: !Stride.Rendering.Sprites.SpriteTransparentRenderStageSelector,Stride.Rendering
|
||||
OpaqueRenderStage: ref!! 8d4a292b-24b3-4633-8906-dfec11b7c9b8
|
||||
TransparentRenderStage: ref!! 7e91ee8c-255a-4bc0-90fb-feebfd461e2f
|
||||
EffectName: Test
|
||||
60780391e205770513fdd53e07279a01: !Stride.Rendering.Background.BackgroundRenderFeature,Stride.Rendering
|
||||
RenderStageSelectors:
|
||||
11c8b8ccb522e3cd1dd6688016062a6d: !Stride.Rendering.SimpleGroupToRenderStageSelector,Stride.Rendering
|
||||
RenderStage: ref!! 8d4a292b-24b3-4633-8906-dfec11b7c9b8
|
||||
EffectName: Test
|
||||
93933ad00d0c357d4915ad462cbfd04c: !Stride.Rendering.UI.UIRenderFeature,Stride.UI
|
||||
RenderStageSelectors:
|
||||
14a071694411235038a102ac3794bb4d: !Stride.Rendering.SimpleGroupToRenderStageSelector,Stride.Rendering
|
||||
RenderStage: ref!! 7e91ee8c-255a-4bc0-90fb-feebfd461e2f
|
||||
EffectName: Test
|
||||
9013eab3ea0ef6c98bf133b86c173d45: !Stride.Particles.Rendering.ParticleEmitterRenderFeature,Stride.Particles
|
||||
RenderStageSelectors:
|
||||
af1bd241305893ef8ff7952184e1cb0b: !Stride.Particles.Rendering.ParticleEmitterTransparentRenderStageSelector,Stride.Particles
|
||||
OpaqueRenderStage: ref!! 8d4a292b-24b3-4633-8906-dfec11b7c9b8
|
||||
TransparentRenderStage: ref!! 7e91ee8c-255a-4bc0-90fb-feebfd461e2f
|
||||
EffectName: null
|
||||
PipelineProcessors: {}
|
||||
SharedRenderers:
|
||||
60459475d3a3adaf2d1ba5d99913ca75: !Stride.Rendering.Compositing.ForwardRenderer,Stride.Engine
|
||||
Id: 34c516f8-bdd4-4cd0-9cb9-6d49181207c2
|
||||
Clear:
|
||||
Id: 4bc4b2ca-027e-4e4a-94cb-2912709bef5f
|
||||
Color: {R: 0.40491876, G: 0.41189542, B: 0.43775, A: 1.0}
|
||||
LightProbes: true
|
||||
OpaqueRenderStage: ref!! 8d4a292b-24b3-4633-8906-dfec11b7c9b8
|
||||
TransparentRenderStage: ref!! 7e91ee8c-255a-4bc0-90fb-feebfd461e2f
|
||||
ShadowMapRenderStages:
|
||||
fc4d1e0de5c2b0bbc27bcf96e9a848fd: ref!! f0962b6c-387c-454e-9759-721acf05bb80
|
||||
87ff1d9cdd52418daf76385176a0e316: ref!! 9d878307-f1a6-4ff9-bc10-2729c6e2cf40
|
||||
5e059d4cc2db4ee8a1f28a40f4ac3ae8: ref!! 65947d6a-5d89-456c-8774-830c2a2da459
|
||||
GBufferRenderStage: ref!! a1e7e6ad-7626-4eb6-a125-13275a9531c2
|
||||
PostEffects: !PostProcessingEffects ref!! 1aafafbc-ebfa-40a4-bfff-b1153b651a9f
|
||||
LightShafts: null
|
||||
VRSettings:
|
||||
Enabled: false
|
||||
RequiredApis: {}
|
||||
Overlays: {}
|
||||
RequestPassthrough: false
|
||||
SubsurfaceScatteringBlurEffect: null
|
||||
MSAALevel: None
|
||||
MSAAResolver: {}
|
||||
d5b2e71c088247e21556decdce138d96: !Stride.Rendering.Compositing.ForwardRenderer,Stride.Engine
|
||||
Id: 8f3f7aca-0a1d-435c-b142-85a5bf7700a2
|
||||
Clear:
|
||||
Id: 66a42307-1985-4316-871a-768449238c11
|
||||
Color: {R: 0.40491876, G: 0.41189542, B: 0.43775, A: 1.0}
|
||||
LightProbes: true
|
||||
OpaqueRenderStage: ref!! 8d4a292b-24b3-4633-8906-dfec11b7c9b8
|
||||
TransparentRenderStage: ref!! 7e91ee8c-255a-4bc0-90fb-feebfd461e2f
|
||||
ShadowMapRenderStages:
|
||||
2323a99a8a983e182f318e55604659b0: ref!! f0962b6c-387c-454e-9759-721acf05bb80
|
||||
61b557b74893400898c007d5ece5e87f: ref!! 9d878307-f1a6-4ff9-bc10-2729c6e2cf40
|
||||
5c382f66ee524c498d26d9aaf19fc9c3: ref!! 65947d6a-5d89-456c-8774-830c2a2da459
|
||||
GBufferRenderStage: ref!! a1e7e6ad-7626-4eb6-a125-13275a9531c2
|
||||
PostEffects: null
|
||||
LightShafts: null
|
||||
VRSettings:
|
||||
Enabled: false
|
||||
RequiredApis: {}
|
||||
Overlays: {}
|
||||
RequestPassthrough: false
|
||||
SubsurfaceScatteringBlurEffect: null
|
||||
MSAALevel: None
|
||||
MSAAResolver: {}
|
||||
34ecb9b2633eacfc439ba8744fe05102: !PostProcessingEffects
|
||||
Id: 1aafafbc-ebfa-40a4-bfff-b1153b651a9f
|
||||
Outline:
|
||||
Enabled: false
|
||||
NormalWeight: 2.0
|
||||
DepthWeight: 0.2
|
||||
NormalNearCutoff: 0.1
|
||||
Fog:
|
||||
Enabled: false
|
||||
Density: 0.1
|
||||
Color: {R: 1.0, G: 1.0, B: 1.0}
|
||||
FogStart: 10.0
|
||||
SkipBackground: false
|
||||
AmbientOcclusion:
|
||||
Enabled: false
|
||||
LocalReflections:
|
||||
Enabled: false
|
||||
ResolvePassResolution: Full
|
||||
DepthResolution: Half
|
||||
DepthOfField:
|
||||
Enabled: false
|
||||
DOFAreas: {X: 0.5, Y: 6.0, Z: 50.0, W: 200.0}
|
||||
BrightFilter:
|
||||
Color: {R: 1.0, G: 1.0, B: 1.0}
|
||||
Bloom:
|
||||
Distortion: {X: 1.0, Y: 1.0}
|
||||
Afterimage:
|
||||
Enabled: false
|
||||
LightStreak:
|
||||
Attenuation: 0.7
|
||||
LensFlare: {}
|
||||
ColorTransforms:
|
||||
Transforms:
|
||||
1e06f805f8b2e949a06c30d45fe413ef: !ToneMap
|
||||
Enabled*: false
|
||||
Operator: !ToneMapHejl2Operator {}
|
||||
c57351444609d14ea258b3f511ec8a74: !FilmGrain
|
||||
Enabled: false
|
||||
e86e22e9a5d65545b8b55fca26e0afee: !Vignetting
|
||||
Enabled: false
|
||||
Color: {R: 0.0, G: 0.0, B: 0.0}
|
||||
Antialiasing: !FXAAEffect {}
|
||||
ee80a20a9bd99f2d70711114e15fe7ca: !Stride.Rendering.Compositing.DebugRenderer,Stride.Rendering
|
||||
Id: 30560a55-2756-4ed5-8bb0-523ec2b44081
|
||||
DebugRenderStages: {}
|
||||
Game: !Stride.Rendering.Compositing.SceneCameraRenderer,Stride.Engine
|
||||
Id: 76fe87cf-f574-4ad6-85b8-e9a9586be0e2
|
||||
Camera: ref!! 291722d5-fb04-46ed-b58d-7f4455266d8e
|
||||
Child: !Stride.Rendering.Compositing.SceneRendererCollection,Stride.Rendering
|
||||
Id: 82568e46-92e7-421a-8dca-114a74e0cd69
|
||||
Children:
|
||||
d39c5ddbf8b7d5ca02bafb6496b1cc3c: !Stride.Rendering.Compositing.ForwardRenderer,Stride.Engine ref!! 34c516f8-bdd4-4cd0-9cb9-6d49181207c2
|
||||
01d338078e9b21121ead0868932613dd: !Stride.Rendering.Compositing.DebugRenderer,Stride.Rendering ref!! 30560a55-2756-4ed5-8bb0-523ec2b44081
|
||||
RenderMask: All
|
||||
SingleView: !Stride.Rendering.Compositing.ForwardRenderer,Stride.Engine ref!! 8f3f7aca-0a1d-435c-b142-85a5bf7700a2
|
||||
Editor: !Stride.Rendering.Compositing.ForwardRenderer,Stride.Engine ref!! 34c516f8-bdd4-4cd0-9cb9-6d49181207c2
|
||||
BlockPositions: {}
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,104 @@
|
||||
!SpriteSheet
|
||||
Id: c9c540bb-2671-42de-bf06-b935954cd604
|
||||
SerializedVersion: {Stride: 2.0.0.0}
|
||||
Tags: []
|
||||
Type: Sprite2D
|
||||
ColorKeyColor: {R: 255, G: 0, B: 255, A: 255}
|
||||
Packing: {}
|
||||
Sprites:
|
||||
11c7232b6b95611a617109444fd2eb30:
|
||||
Source: !file ../../Resources/coin.png
|
||||
Name: coin_0
|
||||
TextureRegion: {X: 0, Y: 0, Width: 16, Height: 16}
|
||||
PixelsPerUnit: 16.0
|
||||
Center: {X: 0.0, Y: 0.0}
|
||||
Borders: {X: 0.0, Y: 0.0, Z: 0.0, W: 0.0}
|
||||
BorderColor: {R: 0, G: 0, B: 0, A: 0}
|
||||
8982b889347b53d50b778103a3359113:
|
||||
Source: !file ../../Resources/coin.png
|
||||
Name: coin_1
|
||||
TextureRegion: {X: 16, Y: 0, Width: 16, Height: 16}
|
||||
PixelsPerUnit: 16.0
|
||||
Center: {X: 0.0, Y: 0.0}
|
||||
Borders: {X: 0.0, Y: 0.0, Z: 0.0, W: 0.0}
|
||||
BorderColor: {R: 0, G: 0, B: 0, A: 0}
|
||||
ea92186c4227d545f91e18983a339cc6:
|
||||
Source: !file ../../Resources/coin.png
|
||||
Name: coin_2
|
||||
TextureRegion: {X: 32, Y: 0, Width: 16, Height: 16}
|
||||
PixelsPerUnit: 16.0
|
||||
Center: {X: 0.0, Y: 0.0}
|
||||
Borders: {X: 0.0, Y: 0.0, Z: 0.0, W: 0.0}
|
||||
BorderColor: {R: 0, G: 0, B: 0, A: 0}
|
||||
049fa447233fea6f9418765b266d17b6:
|
||||
Source: !file ../../Resources/coin.png
|
||||
Name: coin_3
|
||||
TextureRegion: {X: 48, Y: 0, Width: 16, Height: 16}
|
||||
PixelsPerUnit: 16.0
|
||||
Center: {X: 0.0, Y: 0.0}
|
||||
Borders: {X: 0.0, Y: 0.0, Z: 0.0, W: 0.0}
|
||||
BorderColor: {R: 0, G: 0, B: 0, A: 0}
|
||||
0d51d74e8852bda127ac5515853080d7:
|
||||
Source: !file ../../Resources/coin.png
|
||||
Name: coin_4
|
||||
TextureRegion: {X: 64, Y: 0, Width: 16, Height: 16}
|
||||
PixelsPerUnit: 16.0
|
||||
Center: {X: 0.0, Y: 0.0}
|
||||
Borders: {X: 0.0, Y: 0.0, Z: 0.0, W: 0.0}
|
||||
BorderColor: {R: 0, G: 0, B: 0, A: 0}
|
||||
beeefe95329af5c68f8a56d87ce97a3c:
|
||||
Source: !file ../../Resources/coin.png
|
||||
Name: coin_5
|
||||
TextureRegion: {X: 80, Y: 0, Width: 16, Height: 16}
|
||||
PixelsPerUnit: 16.0
|
||||
Center: {X: 0.0, Y: 0.0}
|
||||
Borders: {X: 0.0, Y: 0.0, Z: 0.0, W: 0.0}
|
||||
BorderColor: {R: 0, G: 0, B: 0, A: 0}
|
||||
3a5ba65f13cf86400ceaab226b77ed2b:
|
||||
Source: !file ../../Resources/coin.png
|
||||
Name: coin_6
|
||||
TextureRegion: {X: 96, Y: 0, Width: 16, Height: 16}
|
||||
PixelsPerUnit: 16.0
|
||||
Center: {X: 0.0, Y: 0.0}
|
||||
Borders: {X: 0.0, Y: 0.0, Z: 0.0, W: 0.0}
|
||||
BorderColor: {R: 0, G: 0, B: 0, A: 0}
|
||||
595d05f6fcff29c84db1423286bcca2e:
|
||||
Source: !file ../../Resources/coin.png
|
||||
Name: coin_7
|
||||
TextureRegion: {X: 112, Y: 0, Width: 16, Height: 16}
|
||||
PixelsPerUnit: 16.0
|
||||
Center: {X: 0.0, Y: 0.0}
|
||||
Borders: {X: 0.0, Y: 0.0, Z: 0.0, W: 0.0}
|
||||
BorderColor: {R: 0, G: 0, B: 0, A: 0}
|
||||
ed078696aa11e4b2e4e4a9bf7c47ea09:
|
||||
Source: !file ../../Resources/coin.png
|
||||
Name: coin_8
|
||||
TextureRegion: {X: 128, Y: 0, Width: 16, Height: 16}
|
||||
PixelsPerUnit: 16.0
|
||||
Center: {X: 0.0, Y: 0.0}
|
||||
Borders: {X: 0.0, Y: 0.0, Z: 0.0, W: 0.0}
|
||||
BorderColor: {R: 0, G: 0, B: 0, A: 0}
|
||||
050188a4c71e78165ae1404b4d158dbe:
|
||||
Source: !file ../../Resources/coin.png
|
||||
Name: coin_9
|
||||
TextureRegion: {X: 144, Y: 0, Width: 16, Height: 16}
|
||||
PixelsPerUnit: 16.0
|
||||
Center: {X: 0.0, Y: 0.0}
|
||||
Borders: {X: 0.0, Y: 0.0, Z: 0.0, W: 0.0}
|
||||
BorderColor: {R: 0, G: 0, B: 0, A: 0}
|
||||
82bd686c511a8452b0f2099e21a0e10a:
|
||||
Source: !file ../../Resources/coin.png
|
||||
Name: coin_10
|
||||
TextureRegion: {X: 160, Y: 0, Width: 16, Height: 16}
|
||||
PixelsPerUnit: 16.0
|
||||
Center: {X: 0.0, Y: 0.0}
|
||||
Borders: {X: 0.0, Y: 0.0, Z: 0.0, W: 0.0}
|
||||
BorderColor: {R: 0, G: 0, B: 0, A: 0}
|
||||
ebe87ba1f73954173b812cf66ed48777:
|
||||
Source: !file ../../Resources/coin.png
|
||||
Name: coin_11
|
||||
TextureRegion: {X: 176, Y: 0, Width: 16, Height: 16}
|
||||
PixelsPerUnit: 16.0
|
||||
Center: {X: 0.0, Y: 0.0}
|
||||
Borders: {X: 0.0, Y: 0.0, Z: 0.0, W: 0.0}
|
||||
BorderColor: {R: 0, G: 0, B: 0, A: 0}
|
||||
@@ -0,0 +1,48 @@
|
||||
!SpriteSheet
|
||||
Id: 58a69aad-99db-4bb0-a3b8-951270816915
|
||||
SerializedVersion: {Stride: 2.0.0.0}
|
||||
Tags: []
|
||||
Type: Sprite2D
|
||||
ColorKeyColor: {R: 255, G: 0, B: 255, A: 255}
|
||||
Packing: {}
|
||||
Sprites:
|
||||
d3fd346f77408a63902fe39142b67375:
|
||||
Source: !file ../../Resources/spring.png
|
||||
Name: platform_thin_long
|
||||
TextureRegion: {X: 0, Y: 7, Width: 32, Height: 9}
|
||||
PixelsPerUnit: 16.0
|
||||
Center: {X: 0.0, Y: 0.0}
|
||||
Borders: {X: 0.0, Y: 0.0, Z: 0.0, W: 0.0}
|
||||
BorderColor: {R: 0, G: 0, B: 0, A: 0}
|
||||
0f233344a899bb5a19a36a258bc2bcb9:
|
||||
Source: !file ../../Resources/spring.png
|
||||
Name: platform_thin_short
|
||||
TextureRegion: {X: 32, Y: 7, Width: 16, Height: 9}
|
||||
PixelsPerUnit: 16.0
|
||||
Center: {X: 0.0, Y: 0.0}
|
||||
Borders: {X: 0.0, Y: 0.0, Z: 0.0, W: 0.0}
|
||||
BorderColor: {R: 0, G: 0, B: 0, A: 0}
|
||||
715628101d20f7b9d1f93c0f3ae5a90b:
|
||||
Source: !file ../../Resources/spring.png
|
||||
Name: platform_thick_long
|
||||
TextureRegion: {X: 0, Y: 32, Width: 32, Height: 16}
|
||||
PixelsPerUnit: 16.0
|
||||
Center: {X: 0.0, Y: 0.0}
|
||||
Borders: {X: 0.0, Y: 0.0, Z: 0.0, W: 0.0}
|
||||
BorderColor: {R: 0, G: 0, B: 0, A: 0}
|
||||
39238150bcbf695d39cdf3bff75fc3a5:
|
||||
Source: !file ../../Resources/spring.png
|
||||
Name: platform_thick_short
|
||||
TextureRegion: {X: 32, Y: 32, Width: 16, Height: 16}
|
||||
PixelsPerUnit: 16.0
|
||||
Center: {X: 0.0, Y: 0.0}
|
||||
Borders: {X: 0.0, Y: 0.0, Z: 0.0, W: 0.0}
|
||||
BorderColor: {R: 0, G: 0, B: 0, A: 0}
|
||||
cb058a356cba2a81e57968b56e2e4465:
|
||||
Source: !file ../../Resources/spring.png
|
||||
Name: wall
|
||||
TextureRegion: {X: 128, Y: 48, Width: 16, Height: 32}
|
||||
PixelsPerUnit: 16.0
|
||||
Center: {X: 0.0, Y: 0.0}
|
||||
Borders: {X: 0.0, Y: 0.0, Z: 0.0, W: 0.0}
|
||||
BorderColor: {R: 0, G: 0, B: 0, A: 0}
|
||||
@@ -0,0 +1,232 @@
|
||||
!SpriteSheet
|
||||
Id: c805102c-bb90-4d52-9429-dc7e873a001c
|
||||
SerializedVersion: {Stride: 2.0.0.0}
|
||||
Tags: []
|
||||
Type: Sprite2D
|
||||
ColorKeyColor: {R: 255, G: 0, B: 255, A: 255}
|
||||
Packing: {}
|
||||
Sprites:
|
||||
6a5e893e188fd89658070cf34e33b76c:
|
||||
Source: !file ../../Resources/lancelot.png
|
||||
Name: knight_idle_0
|
||||
TextureRegion: {X: 8, Y: 8, Width: 16, Height: 20}
|
||||
PixelsPerUnit: 16.0
|
||||
Center: {X: 0.0, Y: 0.0}
|
||||
Borders: {X: 0.0, Y: 0.0, Z: 0.0, W: 0.0}
|
||||
BorderColor: {R: 0, G: 0, B: 0, A: 0}
|
||||
872f5652f9a821afff5f4239ec440d66:
|
||||
Source: !file ../../Resources/lancelot.png
|
||||
Name: knight_idle_1
|
||||
TextureRegion: {X: 40, Y: 8, Width: 16, Height: 20}
|
||||
PixelsPerUnit: 16.0
|
||||
Center: {X: 0.0, Y: 0.0}
|
||||
Borders: {X: 0.0, Y: 0.0, Z: 0.0, W: 0.0}
|
||||
BorderColor: {R: 0, G: 0, B: 0, A: 0}
|
||||
d7d3d367a2c91f9ba6dfd6a227979f34:
|
||||
Source: !file ../../Resources/lancelot.png
|
||||
Name: knight_idle_2
|
||||
TextureRegion: {X: 72, Y: 8, Width: 16, Height: 20}
|
||||
PixelsPerUnit: 16.0
|
||||
Center: {X: 0.0, Y: 0.0}
|
||||
Borders: {X: 0.0, Y: 0.0, Z: 0.0, W: 0.0}
|
||||
BorderColor: {R: 0, G: 0, B: 0, A: 0}
|
||||
06128f1c92205be7f6837889971ea15c:
|
||||
Source: !file ../../Resources/lancelot.png
|
||||
Name: knight_idle_3
|
||||
TextureRegion: {X: 104, Y: 8, Width: 16, Height: 20}
|
||||
PixelsPerUnit: 16.0
|
||||
Center: {X: 0.0, Y: 0.0}
|
||||
Borders: {X: 0.0, Y: 0.0, Z: 0.0, W: 0.0}
|
||||
BorderColor: {R: 0, G: 0, B: 0, A: 0}
|
||||
e4dd778f1e42cd7286e6831738633da3:
|
||||
Source: !file ../../Resources/lancelot.png
|
||||
Name: knight_run_0
|
||||
TextureRegion: {X: 8, Y: 40, Width: 16, Height: 20}
|
||||
PixelsPerUnit: 16.0
|
||||
Center: {X: 0.0, Y: 0.0}
|
||||
Borders: {X: 0.0, Y: 0.0, Z: 0.0, W: 0.0}
|
||||
BorderColor: {R: 0, G: 0, B: 0, A: 0}
|
||||
5051b9981963e7ffd497ff48489a7d66:
|
||||
Source: !file ../../Resources/lancelot.png
|
||||
Name: knight_run_1
|
||||
TextureRegion: {X: 40, Y: 40, Width: 16, Height: 20}
|
||||
PixelsPerUnit: 16.0
|
||||
Center: {X: 0.0, Y: 0.0}
|
||||
Borders: {X: 0.0, Y: 0.0, Z: 0.0, W: 0.0}
|
||||
BorderColor: {R: 0, G: 0, B: 0, A: 0}
|
||||
24539a6ed06bb723efa07dd2729cd3c0:
|
||||
Source: !file ../../Resources/lancelot.png
|
||||
Name: knight_run_2
|
||||
TextureRegion: {X: 72, Y: 40, Width: 16, Height: 20}
|
||||
PixelsPerUnit: 16.0
|
||||
Center: {X: 0.0, Y: 0.0}
|
||||
Borders: {X: 0.0, Y: 0.0, Z: 0.0, W: 0.0}
|
||||
BorderColor: {R: 0, G: 0, B: 0, A: 0}
|
||||
0429a09865a03df44f09cf9a89285cbe:
|
||||
Source: !file ../../Resources/lancelot.png
|
||||
Name: knight_run_3
|
||||
TextureRegion: {X: 104, Y: 40, Width: 16, Height: 20}
|
||||
PixelsPerUnit: 16.0
|
||||
Center: {X: 0.0, Y: 0.0}
|
||||
Borders: {X: 0.0, Y: 0.0, Z: 0.0, W: 0.0}
|
||||
BorderColor: {R: 0, G: 0, B: 0, A: 0}
|
||||
88d5c93df11446bc2563cda9b26e6cb0:
|
||||
Source: !file ../../Resources/lancelot.png
|
||||
Name: knight_run_4
|
||||
TextureRegion: {X: 8, Y: 72, Width: 16, Height: 20}
|
||||
PixelsPerUnit: 16.0
|
||||
Center: {X: 0.0, Y: 0.0}
|
||||
Borders: {X: 0.0, Y: 0.0, Z: 0.0, W: 0.0}
|
||||
BorderColor: {R: 0, G: 0, B: 0, A: 0}
|
||||
d66bda5d30f0c9bb576bc9c9a1fd7015:
|
||||
Source: !file ../../Resources/lancelot.png
|
||||
Name: knight_run_5
|
||||
TextureRegion: {X: 40, Y: 72, Width: 16, Height: 20}
|
||||
PixelsPerUnit: 16.0
|
||||
Center: {X: 0.0, Y: 0.0}
|
||||
Borders: {X: 0.0, Y: 0.0, Z: 0.0, W: 0.0}
|
||||
BorderColor: {R: 0, G: 0, B: 0, A: 0}
|
||||
bd52d666201e8bf6b4f05f6463d887db:
|
||||
Source: !file ../../Resources/lancelot.png
|
||||
Name: knight_run_6
|
||||
TextureRegion: {X: 72, Y: 72, Width: 16, Height: 20}
|
||||
PixelsPerUnit: 16.0
|
||||
Center: {X: 0.0, Y: 0.0}
|
||||
Borders: {X: 0.0, Y: 0.0, Z: 0.0, W: 0.0}
|
||||
BorderColor: {R: 0, G: 0, B: 0, A: 0}
|
||||
2e038e01b3671391419ddfb346c8147b:
|
||||
Source: !file ../../Resources/lancelot.png
|
||||
Name: knight_run_7
|
||||
TextureRegion: {X: 104, Y: 72, Width: 16, Height: 20}
|
||||
PixelsPerUnit: 16.0
|
||||
Center: {X: 0.0, Y: 0.0}
|
||||
Borders: {X: 0.0, Y: 0.0, Z: 0.0, W: 0.0}
|
||||
BorderColor: {R: 0, G: 0, B: 0, A: 0}
|
||||
c58dc014a2573861b7a6acf4439fe9b5:
|
||||
Source: !file ../../Resources/lancelot.png
|
||||
Name: knight_run_8
|
||||
TextureRegion: {X: 8, Y: 104, Width: 16, Height: 20}
|
||||
PixelsPerUnit: 16.0
|
||||
Center: {X: 0.0, Y: 0.0}
|
||||
Borders: {X: 0.0, Y: 0.0, Z: 0.0, W: 0.0}
|
||||
BorderColor: {R: 0, G: 0, B: 0, A: 0}
|
||||
e5dcfbdc9f563aee81e62de833e1df5c:
|
||||
Source: !file ../../Resources/lancelot.png
|
||||
Name: knight_run_9
|
||||
TextureRegion: {X: 40, Y: 104, Width: 16, Height: 20}
|
||||
PixelsPerUnit: 16.0
|
||||
Center: {X: 0.0, Y: 0.0}
|
||||
Borders: {X: 0.0, Y: 0.0, Z: 0.0, W: 0.0}
|
||||
BorderColor: {R: 0, G: 0, B: 0, A: 0}
|
||||
adf918ccb83acdaa7c2a8c7d1c80c0b1:
|
||||
Source: !file ../../Resources/lancelot.png
|
||||
Name: knight_run_10
|
||||
TextureRegion: {X: 72, Y: 104, Width: 16, Height: 20}
|
||||
PixelsPerUnit: 16.0
|
||||
Center: {X: 0.0, Y: 0.0}
|
||||
Borders: {X: 0.0, Y: 0.0, Z: 0.0, W: 0.0}
|
||||
BorderColor: {R: 0, G: 0, B: 0, A: 0}
|
||||
256350f8e893e22e4bc65a548955f7c5:
|
||||
Source: !file ../../Resources/lancelot.png
|
||||
Name: knight_run_11
|
||||
TextureRegion: {X: 104, Y: 104, Width: 16, Height: 20}
|
||||
PixelsPerUnit: 16.0
|
||||
Center: {X: 0.0, Y: 0.0}
|
||||
Borders: {X: 0.0, Y: 0.0, Z: 0.0, W: 0.0}
|
||||
BorderColor: {R: 0, G: 0, B: 0, A: 0}
|
||||
05e80bb8f4e3f173708bacac478ab190:
|
||||
Source: !file ../../Resources/lancelot.png
|
||||
Name: knight_run_12
|
||||
TextureRegion: {X: 8, Y: 136, Width: 16, Height: 20}
|
||||
PixelsPerUnit: 16.0
|
||||
Center: {X: 0.0, Y: 0.0}
|
||||
Borders: {X: 0.0, Y: 0.0, Z: 0.0, W: 0.0}
|
||||
BorderColor: {R: 0, G: 0, B: 0, A: 0}
|
||||
0161bd6b447e3ad1d74a5281378430f7:
|
||||
Source: !file ../../Resources/lancelot.png
|
||||
Name: knight_run_13
|
||||
TextureRegion: {X: 40, Y: 136, Width: 16, Height: 20}
|
||||
PixelsPerUnit: 16.0
|
||||
Center: {X: 0.0, Y: 0.0}
|
||||
Borders: {X: 0.0, Y: 0.0, Z: 0.0, W: 0.0}
|
||||
BorderColor: {R: 0, G: 0, B: 0, A: 0}
|
||||
3c6965cce17c4561a3c0844ae0bd75d7:
|
||||
Source: !file ../../Resources/lancelot.png
|
||||
Name: knight_run_14
|
||||
TextureRegion: {X: 72, Y: 136, Width: 16, Height: 20}
|
||||
PixelsPerUnit: 16.0
|
||||
Center: {X: 0.0, Y: 0.0}
|
||||
Borders: {X: 0.0, Y: 0.0, Z: 0.0, W: 0.0}
|
||||
BorderColor: {R: 0, G: 0, B: 0, A: 0}
|
||||
5197a99fbe55a1327fb3d0281c7e5678:
|
||||
Source: !file ../../Resources/lancelot.png
|
||||
Name: knight_run_15
|
||||
TextureRegion: {X: 104, Y: 136, Width: 16, Height: 20}
|
||||
PixelsPerUnit: 16.0
|
||||
Center: {X: 0.0, Y: 0.0}
|
||||
Borders: {X: 0.0, Y: 0.0, Z: 0.0, W: 0.0}
|
||||
BorderColor: {R: 0, G: 0, B: 0, A: 0}
|
||||
13ee02648e7104acb1d26d0fc476476e:
|
||||
Source: !file ../../Resources/lancelot.png
|
||||
Name: knight_jump_0
|
||||
TextureRegion: {X: 8, Y: 168, Width: 16, Height: 20}
|
||||
PixelsPerUnit: 16.0
|
||||
Center: {X: 0.0, Y: 0.0}
|
||||
Borders: {X: 0.0, Y: 0.0, Z: 0.0, W: 0.0}
|
||||
BorderColor: {R: 0, G: 0, B: 0, A: 0}
|
||||
0294d2cde6a0e8326764f148b90aa2a6:
|
||||
Source: !file ../../Resources/lancelot.png
|
||||
Name: knight_jump_1
|
||||
TextureRegion: {X: 40, Y: 168, Width: 16, Height: 20}
|
||||
PixelsPerUnit: 16.0
|
||||
Center: {X: 0.0, Y: 0.0}
|
||||
Borders: {X: 0.0, Y: 0.0, Z: 0.0, W: 0.0}
|
||||
BorderColor: {R: 0, G: 0, B: 0, A: 0}
|
||||
df37eb68c7f59295b540d8c69ddeb1d4:
|
||||
Source: !file ../../Resources/lancelot.png
|
||||
Name: knight_jump_2
|
||||
TextureRegion: {X: 72, Y: 168, Width: 16, Height: 20}
|
||||
PixelsPerUnit: 16.0
|
||||
Center: {X: 0.0, Y: 0.0}
|
||||
Borders: {X: 0.0, Y: 0.0, Z: 0.0, W: 0.0}
|
||||
BorderColor: {R: 0, G: 0, B: 0, A: 0}
|
||||
9ae668af7084cc063b89afb67fcf46fb:
|
||||
Source: !file ../../Resources/lancelot.png
|
||||
Name: knight_jump_3
|
||||
TextureRegion: {X: 105, Y: 169, Width: 16, Height: 20}
|
||||
PixelsPerUnit: 16.0
|
||||
Center: {X: 0.0, Y: 0.0}
|
||||
Borders: {X: 0.0, Y: 0.0, Z: 0.0, W: 0.0}
|
||||
BorderColor: {R: 0, G: 0, B: 0, A: 0}
|
||||
02de2ab116eb08289673451a90a6c282:
|
||||
Source: !file ../../Resources/lancelot.png
|
||||
Name: knight_jump_4
|
||||
TextureRegion: {X: 8, Y: 201, Width: 16, Height: 20}
|
||||
PixelsPerUnit: 16.0
|
||||
Center: {X: 0.0, Y: 0.0}
|
||||
Borders: {X: 0.0, Y: 0.0, Z: 0.0, W: 0.0}
|
||||
BorderColor: {R: 0, G: 0, B: 0, A: 0}
|
||||
c5ec8e40515be423a38564e6c583609e:
|
||||
Source: !file ../../Resources/lancelot.png
|
||||
Name: knight_jump_5
|
||||
TextureRegion: {X: 40, Y: 202, Width: 16, Height: 20}
|
||||
PixelsPerUnit: 16.0
|
||||
Center: {X: 0.0, Y: 0.0}
|
||||
Borders: {X: 0.0, Y: 0.0, Z: 0.0, W: 0.0}
|
||||
BorderColor: {R: 0, G: 0, B: 0, A: 0}
|
||||
4e11af4756909c70a857f1f622a9c4bb:
|
||||
Source: !file ../../Resources/lancelot.png
|
||||
Name: knight_jump_6
|
||||
TextureRegion: {X: 72, Y: 200, Width: 16, Height: 20}
|
||||
PixelsPerUnit: 16.0
|
||||
Center: {X: 0.0, Y: 0.0}
|
||||
Borders: {X: 0.0, Y: 0.0, Z: 0.0, W: 0.0}
|
||||
BorderColor: {R: 0, G: 0, B: 0, A: 0}
|
||||
b16c2e3a9db1208ce9789d58c1f1a0ec:
|
||||
Source: !file ../../Resources/lancelot.png
|
||||
Name: knight_jump_7
|
||||
TextureRegion: {X: 104, Y: 200, Width: 16, Height: 20}
|
||||
PixelsPerUnit: 16.0
|
||||
Center: {X: 0.0, Y: 0.0}
|
||||
Borders: {X: 0.0, Y: 0.0, Z: 0.0, W: 0.0}
|
||||
BorderColor: {R: 0, G: 0, B: 0, A: 0}
|
||||
@@ -0,0 +1,9 @@
|
||||
!Texture
|
||||
Id: 50562287-8d9a-4613-a14a-ecb46f748419
|
||||
SerializedVersion: {Stride: 2.0.0.0}
|
||||
Tags: []
|
||||
Source: !file ../../Resources/sky.png
|
||||
IsCompressed: false
|
||||
Type: !ColorTextureType
|
||||
ColorKeyColor: {R: 255, G: 0, B: 255, A: 255}
|
||||
IsStreamable: false
|
||||
+42
@@ -0,0 +1,42 @@
|
||||
!PrefabAsset
|
||||
Id: d8b2fb42-429d-4448-b6cf-5e8d25260d7a
|
||||
SerializedVersion: {Stride: 3.1.0.1}
|
||||
Tags: []
|
||||
Hierarchy:
|
||||
RootParts:
|
||||
- ref!! 998cd5e2-baf0-43c8-a300-9df5130ed98b
|
||||
Parts:
|
||||
- Folder: Map
|
||||
Entity:
|
||||
Id: 998cd5e2-baf0-43c8-a300-9df5130ed98b
|
||||
Name: Platform_Thick_long
|
||||
Components:
|
||||
20f9276efc76d56da73398d0710656f4: !TransformComponent
|
||||
Id: cf7988cd-4953-4c5d-a7b5-58c56e1ee9a5
|
||||
Position: {X: 0.0, Y: -8.0, Z: 0.0}
|
||||
Rotation: {X: 0.0, Y: 0.0, Z: 0.0, W: 1.0}
|
||||
Scale: {X: 1.0, Y: 1.0, Z: 1.0}
|
||||
Children: {}
|
||||
a21979cf00278ebc58064c6f81abb412: !SpriteComponent
|
||||
Id: fcab2d14-3a8a-41cb-b147-198ea50a6cfa
|
||||
SpriteProvider: !SpriteFromSheet
|
||||
Sheet: 58a69aad-99db-4bb0-a3b8-951270816915:Sprites/ground_sheet
|
||||
CurrentFrame: 2
|
||||
Color: {R: 1.0, G: 1.0, B: 1.0, A: 1.0}
|
||||
Sampler: PointClamp
|
||||
2d8f25f180e3182173f8aaa66cf6cd8a: !StaticColliderComponent
|
||||
Id: 15124571-bfd2-4310-bd9f-09dead973575
|
||||
CanSleep: false
|
||||
Restitution: 0.0
|
||||
Friction: 0.5
|
||||
RollingFriction: 0.0
|
||||
CcdMotionThreshold: 0.0
|
||||
CcdSweptSphereRadius: 0.0
|
||||
IsTrigger: false
|
||||
AlwaysUpdateNaviMeshCache: false
|
||||
ColliderShapes:
|
||||
871b0d7c5a000160d4688735144c4309: !BoxColliderShapeDesc
|
||||
Is2D: true
|
||||
Size: {X: 2.1, Y: 1.0, Z: 1.0}
|
||||
LocalOffset: {X: 0.0, Y: 0.0, Z: 0.0}
|
||||
LocalRotation: {X: 0.0, Y: 0.0, Z: 0.0, W: 1.0}
|
||||
+41
@@ -0,0 +1,41 @@
|
||||
!PrefabAsset
|
||||
Id: a954e461-1843-4050-96cd-6e43e6b82b47
|
||||
SerializedVersion: {Stride: 3.1.0.1}
|
||||
Tags: []
|
||||
Hierarchy:
|
||||
RootParts:
|
||||
- ref!! ab043dcf-3513-4a3c-be2d-87125a0bf4fb
|
||||
Parts:
|
||||
- Folder: Map/Plattforms
|
||||
Entity:
|
||||
Id: ab043dcf-3513-4a3c-be2d-87125a0bf4fb
|
||||
Name: Platform_Thin_long
|
||||
Components:
|
||||
434ef69de74e6501fc61289ef3259ebc: !TransformComponent
|
||||
Id: 04b94243-5955-4207-a4cd-1930534e527d
|
||||
Position: {X: 5.0, Y: -5.0, Z: 0.0}
|
||||
Rotation: {X: 0.0, Y: 0.0, Z: 0.0, W: 1.0}
|
||||
Scale: {X: 1.0, Y: 1.0, Z: 1.0}
|
||||
Children: {}
|
||||
5b0a5872e9545d1a89edc9001b489567: !SpriteComponent
|
||||
Id: 42b0d164-010b-4cfc-a003-c5a215e164ff
|
||||
SpriteProvider: !SpriteFromSheet
|
||||
Sheet: 58a69aad-99db-4bb0-a3b8-951270816915:Sprites/ground_sheet
|
||||
Color: {R: 1.0, G: 1.0, B: 1.0, A: 1.0}
|
||||
Sampler: PointClamp
|
||||
10073a8d05bb76a838b430a04a8ed5d7: !StaticColliderComponent
|
||||
Id: 0b6ee4c5-2730-42d9-a630-61e308948b9f
|
||||
CanSleep: false
|
||||
Restitution: 0.0
|
||||
Friction: 0.5
|
||||
RollingFriction: 0.0
|
||||
CcdMotionThreshold: 0.0
|
||||
CcdSweptSphereRadius: 0.0
|
||||
IsTrigger: false
|
||||
AlwaysUpdateNaviMeshCache: false
|
||||
ColliderShapes:
|
||||
e1f5063c512e0cee9eda177cf7f75895: !BoxColliderShapeDesc
|
||||
Is2D: true
|
||||
Size: {X: 2.01, Y: 0.5, Z: 1.0}
|
||||
LocalOffset: {X: 0.0, Y: 0.0, Z: 0.0}
|
||||
LocalRotation: {X: 0.0, Y: 0.0, Z: 0.0, W: 1.0}
|
||||
+42
@@ -0,0 +1,42 @@
|
||||
!PrefabAsset
|
||||
Id: b4f4dad7-71d0-41ed-96e8-4c44deb4e618
|
||||
SerializedVersion: {Stride: 3.1.0.1}
|
||||
Tags: []
|
||||
Hierarchy:
|
||||
RootParts:
|
||||
- ref!! d5871d1f-0974-4d34-8dd7-9955583ffe62
|
||||
Parts:
|
||||
- Folder: Map/Plattforms
|
||||
Entity:
|
||||
Id: d5871d1f-0974-4d34-8dd7-9955583ffe62
|
||||
Name: Platform_Thin_short
|
||||
Components:
|
||||
89fda52fd0dcd43889322555fec8d1da: !TransformComponent
|
||||
Id: 812d76b9-5f12-49b9-a46a-1307882a992c
|
||||
Position: {X: 0.0, Y: 0.0, Z: 0.0}
|
||||
Rotation: {X: 0.0, Y: 0.0, Z: 0.0, W: 1.0}
|
||||
Scale: {X: 1.0, Y: 1.0, Z: 1.0}
|
||||
Children: {}
|
||||
38f388c3d5cc14129696b9b4940105b6: !SpriteComponent
|
||||
Id: 38a9f3eb-8397-45f9-a56c-e53400c54156
|
||||
SpriteProvider: !SpriteFromSheet
|
||||
Sheet: 58a69aad-99db-4bb0-a3b8-951270816915:Sprites/ground_sheet
|
||||
CurrentFrame: 1
|
||||
Color: {R: 1.0, G: 1.0, B: 1.0, A: 1.0}
|
||||
Sampler: PointClamp
|
||||
fdbc84f119d35b74720fafb625a63045: !StaticColliderComponent
|
||||
Id: 909b2408-9760-48f5-b976-41c3c0607b5b
|
||||
CanSleep: false
|
||||
Restitution: 0.0
|
||||
Friction: 0.5
|
||||
RollingFriction: 0.0
|
||||
CcdMotionThreshold: 0.0
|
||||
CcdSweptSphereRadius: 0.0
|
||||
IsTrigger: false
|
||||
AlwaysUpdateNaviMeshCache: false
|
||||
ColliderShapes:
|
||||
a3e271dc2945c92633e135fb0746b73b: !BoxColliderShapeDesc
|
||||
Is2D: true
|
||||
Size: {X: 1.0, Y: 0.5, Z: 1.0}
|
||||
LocalOffset: {X: 0.0, Y: 0.0, Z: 0.0}
|
||||
LocalRotation: {X: 0.0, Y: 0.0, Z: 0.0, W: 1.0}
|
||||
@@ -0,0 +1,42 @@
|
||||
!PrefabAsset
|
||||
Id: 6ae6f190-d593-4f6d-b6cf-6bb146502ecb
|
||||
SerializedVersion: {Stride: 3.1.0.1}
|
||||
Tags: []
|
||||
Hierarchy:
|
||||
RootParts:
|
||||
- ref!! 67c8a1fc-cdbb-4798-941f-aed2d085334c
|
||||
Parts:
|
||||
- Folder: Map/Wall
|
||||
Entity:
|
||||
Id: 67c8a1fc-cdbb-4798-941f-aed2d085334c
|
||||
Name: Wall
|
||||
Components:
|
||||
6b042eb4e809616483fdaf58b8c948eb: !TransformComponent
|
||||
Id: 3c47f86a-2715-448d-9b02-0caa734cb31a
|
||||
Position: {X: 0.0, Y: 0.0, Z: 0.0}
|
||||
Rotation: {X: 0.0, Y: 0.0, Z: 0.0, W: 1.0}
|
||||
Scale: {X: 1.0, Y: 1.0, Z: 1.0}
|
||||
Children: {}
|
||||
e9a80b3e9215ef8e6674eb86dae7c874: !SpriteComponent
|
||||
Id: c0b665c4-ce75-49ae-a2e1-020ff3ddd197
|
||||
SpriteProvider: !SpriteFromSheet
|
||||
Sheet: 58a69aad-99db-4bb0-a3b8-951270816915:Sprites/ground_sheet
|
||||
CurrentFrame: 4
|
||||
Color: {R: 1.0, G: 1.0, B: 1.0, A: 1.0}
|
||||
Sampler: PointClamp
|
||||
a4ebe4bf882a3966e7cf2dcdc873274d: !StaticColliderComponent
|
||||
Id: 7fe1158b-5b77-4c8a-bd59-222df956ad23
|
||||
CanSleep: false
|
||||
Restitution: 0.0
|
||||
Friction: 0.5
|
||||
RollingFriction: 0.0
|
||||
CcdMotionThreshold: 0.0
|
||||
CcdSweptSphereRadius: 0.0
|
||||
IsTrigger: false
|
||||
AlwaysUpdateNaviMeshCache: false
|
||||
ColliderShapes:
|
||||
35efa962fb0e5d173f33a90520ed0753: !BoxColliderShapeDesc
|
||||
Is2D: true
|
||||
Size: {X: 1.0, Y: 2.1, Z: 1.0}
|
||||
LocalOffset: {X: 0.0, Y: 0.0, Z: 0.0}
|
||||
LocalRotation: {X: 0.0, Y: 0.0, Z: 0.0, W: 1.0}
|
||||
+30
@@ -0,0 +1,30 @@
|
||||
using System;
|
||||
using Stride.Core;
|
||||
using Stride.Engine;
|
||||
using Stride.Graphics;
|
||||
using Stride.Rendering.Sprites;
|
||||
|
||||
namespace Platformer2D.Gameplay;
|
||||
|
||||
public class CoinRotation : SyncScript
|
||||
{
|
||||
public required SpriteComponent Sprite { get; init; }
|
||||
|
||||
private float animationTimer = 0f;
|
||||
// Animation Spped: Every 1/12 passed seconds the next frame will be played. Closer to 1.0 is a slower Animation.
|
||||
private const float animationInterval = 1f / 12f;
|
||||
private const int COIN_FRAMES_END = 12;
|
||||
|
||||
private SpriteFromSheet SpriteSheet => (SpriteFromSheet)Sprite.SpriteProvider;
|
||||
|
||||
public override void Update()
|
||||
{
|
||||
animationTimer += (float) Game.UpdateTime.Elapsed.TotalSeconds;
|
||||
|
||||
if (animationTimer >= animationInterval)
|
||||
{
|
||||
animationTimer -= animationInterval;
|
||||
SpriteSheet.CurrentFrame = (SpriteSheet.CurrentFrame + 1) % COIN_FRAMES_END;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,42 @@
|
||||
using System;
|
||||
using Stride.Core;
|
||||
using System.Threading.Tasks;
|
||||
using Stride.Engine;
|
||||
using Stride.Audio;
|
||||
using Stride.Physics;
|
||||
using Platformer2D.Player;
|
||||
|
||||
namespace Platformer2D.Gameplay;
|
||||
|
||||
public class CollectCoin : AsyncScript
|
||||
{
|
||||
public required StaticColliderComponent StaticColliderComponent { get; init; }
|
||||
|
||||
[Display("Collect Coin")]
|
||||
public Sound? SoundEffect { get; set; }
|
||||
private SoundInstance? sfxInstance;
|
||||
|
||||
public override async Task Execute()
|
||||
{
|
||||
while(Game.IsRunning)
|
||||
{
|
||||
// Waits for a Collision with the Character
|
||||
var collision = await StaticColliderComponent.NewCollision();
|
||||
var otherCollider = StaticColliderComponent == collision.ColliderA ? collision.ColliderB : collision.ColliderA;
|
||||
|
||||
if (otherCollider.Entity.Get<PlayerController>() != null)
|
||||
{
|
||||
// Disable Sprite
|
||||
Entity.Get<SpriteComponent>().Enabled = false;
|
||||
sfxInstance = SoundEffect?.CreateInstance();
|
||||
|
||||
// Play Sound
|
||||
sfxInstance?.Play();
|
||||
|
||||
// Disable Coin Object
|
||||
await Task.Delay(TimeSpan.FromSeconds(3));
|
||||
Entity.Scene = null;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,19 @@
|
||||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
|
||||
<PropertyGroup>
|
||||
<TargetFrameworks>net10.0</TargetFrameworks>
|
||||
<Nullable>enable</Nullable>
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="Stride.Engine" Version="4.3.0.2507" />
|
||||
|
||||
<PackageReference Include="Stride.Video" Version="4.3.0.2507" />
|
||||
<PackageReference Include="Stride.Physics" Version="4.3.0.2507" />
|
||||
<PackageReference Include="Stride.Navigation" Version="4.3.0.2507" />
|
||||
<PackageReference Include="Stride.Particles" Version="4.3.0.2507" />
|
||||
<PackageReference Include="Stride.UI" Version="4.3.0.2507" />
|
||||
|
||||
<PackageReference Include="Stride.Core.Assets.CompilerApp" Version="4.3.0.2507" IncludeAssets="build;buildTransitive" />
|
||||
</ItemGroup>
|
||||
</Project>
|
||||
@@ -0,0 +1,18 @@
|
||||
!Package
|
||||
SerializedVersion: {Assets: 3.1.0.0}
|
||||
Meta:
|
||||
Name: Platformer2D
|
||||
Version: 1.0.0
|
||||
Authors: []
|
||||
Owners: []
|
||||
Dependencies: null
|
||||
AssetFolders:
|
||||
- Path: !dir ../Assets
|
||||
- Path: !dir Effects
|
||||
ResourceFolders:
|
||||
- !dir Resources
|
||||
OutputGroupDirectories: {}
|
||||
ExplicitFolders: []
|
||||
Bundles: []
|
||||
TemplateFolders: []
|
||||
RootAssets: []
|
||||
@@ -0,0 +1,169 @@
|
||||
using System;
|
||||
using Stride.Core.Mathematics;
|
||||
using Stride.Input;
|
||||
using Stride.Engine;
|
||||
using Stride.Core;
|
||||
using Stride.Physics;
|
||||
using System.Windows.Input;
|
||||
using Stride.Graphics;
|
||||
using Stride.Rendering.Sprites;
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace Platformer2D.Player;
|
||||
|
||||
public class PlayerController : SyncScript
|
||||
{
|
||||
public required CharacterComponent CharacterComponent { get; init; }
|
||||
|
||||
public required SpriteComponent Sprite { get; init; }
|
||||
|
||||
private float animationTimer = 0f;
|
||||
// Animation Spped: Every 1/10 passed seconds the next frame will be played. Closer to 1.0 is a slower Animation.
|
||||
private const float animationInterval = 1f / 5f;
|
||||
private int idleFrame = 0;
|
||||
private int runFrame = 0;
|
||||
private int jumpFrame = 0;
|
||||
|
||||
private const int IDLE_FRAME_END = 4;
|
||||
private const int RUN_FRAME_END = 16;
|
||||
private const int JUMP_FRAME_END = 21;
|
||||
private const int JUMP_FRAME_COUNT = 6;
|
||||
|
||||
// units per second (is multiplied with Vector.UnitX which is 1)
|
||||
private const float MOVE_SPEED = 5f;
|
||||
|
||||
private bool isFacingRight = true;
|
||||
|
||||
private SpriteFromSheet SpriteSheet => (SpriteFromSheet)Sprite.SpriteProvider;
|
||||
|
||||
public override void Update()
|
||||
{
|
||||
// HandleAnimation uses bool isMoving from the users Input
|
||||
HandleAnimation(HandleInput());
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Handles both the Input of the Player via Keyboard (WAD- & Arrow-Keys) and the resulting Animation at the end according to input
|
||||
/// </summary>
|
||||
private bool HandleInput()
|
||||
{
|
||||
bool isMoving = false;
|
||||
if (Input.HasKeyboard)
|
||||
{
|
||||
var moveDirection = Vector3.Zero;
|
||||
|
||||
CharacterComponent.SetVelocity(Vector3.Zero);
|
||||
|
||||
// Left
|
||||
if (Input.IsKeyDown(Keys.A) || Input.IsKeyDown(Keys.Left))
|
||||
{
|
||||
isMoving = true;
|
||||
if (isFacingRight)
|
||||
{
|
||||
isFacingRight = false;
|
||||
CharacterComponent.Orientation = Quaternion.RotationY(MathUtil.DegreesToRadians(180));
|
||||
}
|
||||
moveDirection = -Vector3.UnitX * MOVE_SPEED;
|
||||
|
||||
}
|
||||
|
||||
// Right
|
||||
if (Input.IsKeyDown(Keys.D) || Input.IsKeyDown(Keys.Right))
|
||||
{
|
||||
isMoving = true;
|
||||
|
||||
if (!isFacingRight)
|
||||
{
|
||||
isFacingRight = true;
|
||||
CharacterComponent.Orientation = Quaternion.RotationY(MathUtil.DegreesToRadians(0));
|
||||
}
|
||||
moveDirection = Vector3.UnitX * MOVE_SPEED;
|
||||
}
|
||||
|
||||
// Up (Jump)
|
||||
if ((Input.IsKeyPressed(Keys.W) || Input.IsKeyPressed(Keys.Up)) && CharacterComponent.IsGrounded)
|
||||
{
|
||||
CharacterComponent.Jump();
|
||||
}
|
||||
CharacterComponent.SetVelocity(moveDirection);
|
||||
HandleAnimation(isMoving);
|
||||
}
|
||||
return isMoving;
|
||||
}
|
||||
|
||||
private void HandleAnimation(bool isMoving)
|
||||
{
|
||||
animationTimer += (float) Game.UpdateTime.Elapsed.TotalSeconds;
|
||||
|
||||
if (CharacterComponent.IsGrounded && !isMoving)
|
||||
{
|
||||
PlayIdleAnimation();
|
||||
}
|
||||
|
||||
if (CharacterComponent.IsGrounded && isMoving)
|
||||
{
|
||||
PlayRunAnimation();
|
||||
}
|
||||
|
||||
if (!CharacterComponent.IsGrounded)
|
||||
{
|
||||
PlayJumpAnimation();
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// When there is no input, play idle-Animation. Which loops frames 0-3.
|
||||
/// </summary>
|
||||
private void PlayIdleAnimation()
|
||||
{
|
||||
if (animationTimer >= animationInterval)
|
||||
{
|
||||
animationTimer -= animationInterval;
|
||||
SpriteSheet.CurrentFrame = idleFrame;
|
||||
idleFrame = (idleFrame + 1) % IDLE_FRAME_END;
|
||||
}
|
||||
ResetFrameCounts(ref runFrame, ref jumpFrame);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// When there is movement to either side, play run-animation. Which loops frames 4-19
|
||||
/// </summary>
|
||||
private void PlayRunAnimation()
|
||||
{
|
||||
if (animationTimer >= animationInterval)
|
||||
{
|
||||
animationTimer -= animationInterval;
|
||||
SpriteSheet.CurrentFrame = IDLE_FRAME_END + runFrame;
|
||||
runFrame = (runFrame + 1) % RUN_FRAME_END;
|
||||
}
|
||||
ResetFrameCounts(ref idleFrame, ref jumpFrame);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// When the character is not grounded, play jump-animation. Which only plays frames 20-27 once and then resets when the character is grounded.
|
||||
/// </summary>
|
||||
private void PlayJumpAnimation()
|
||||
{
|
||||
if (animationTimer >= animationInterval)
|
||||
{
|
||||
animationTimer -= animationInterval;
|
||||
SpriteSheet.CurrentFrame = JUMP_FRAME_END + jumpFrame;
|
||||
|
||||
if (jumpFrame < JUMP_FRAME_COUNT)
|
||||
{
|
||||
jumpFrame++;
|
||||
}
|
||||
}
|
||||
ResetFrameCounts(ref idleFrame, ref runFrame);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Resets frame counters to 0.
|
||||
/// Example: When playing the run animation, this is called to reset the idle and jump animation counter, to ensure they start at their first frame.
|
||||
/// </summary>
|
||||
private void ResetFrameCounts(ref int counter, ref int counter2)
|
||||
{
|
||||
counter = 0;
|
||||
counter2 = 0;
|
||||
}
|
||||
}
|
||||
+20
@@ -0,0 +1,20 @@
|
||||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
|
||||
<PropertyGroup>
|
||||
<TargetFramework>net10.0-windows</TargetFramework>
|
||||
<ApplicationIcon>Resources\Icon.ico</ApplicationIcon>
|
||||
<OutputType>WinExe</OutputType>
|
||||
<RootNamespace>Platformer2D</RootNamespace>
|
||||
|
||||
<OutputPath>..\Bin\Windows\$(Configuration)\</OutputPath>
|
||||
<AppendTargetFrameworkToOutputPath>false</AppendTargetFrameworkToOutputPath>
|
||||
|
||||
<!-- Force msbuild to check to rebuild this assembly instead of letting VS IDE guess -->
|
||||
<DisableFastUpToDateCheck>true</DisableFastUpToDateCheck>
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="..\Platformer2D.Game\Platformer2D.csproj" />
|
||||
</ItemGroup>
|
||||
|
||||
</Project>
|
||||
@@ -0,0 +1,5 @@
|
||||
using Stride.Engine;
|
||||
|
||||
using var game = new Game();
|
||||
game.Run();
|
||||
|
||||
Binary file not shown.
|
After Width: | Height: | Size: 55 KiB |
@@ -0,0 +1,11 @@
|
||||
!TemplateSample
|
||||
Id: 7b8f32f7-80ce-41bb-8671-93493d93d1ec
|
||||
Name: "Template: 2D Platformer"
|
||||
Scope: Session
|
||||
Description: A 2D Platformer template
|
||||
FullDescription: This template implements a static camera, Trigger, basic 2D Character movement and sprite animations via script.
|
||||
Group: Templates
|
||||
Icon: ../../.sdtpl/IconPlatformer2D.png
|
||||
DefaultOutputName: Platformer2D
|
||||
Screenshots:
|
||||
- .sdtpl/screenshot_small.jpg
|
||||
@@ -0,0 +1,3 @@
|
||||
version https://git-lfs.github.com/spec/v1
|
||||
oid sha256:5ff17de8ff447372cac869dce119e8bd587def80e62099e987a8cf721868adb7
|
||||
size 654
|
||||
@@ -0,0 +1,3 @@
|
||||
version https://git-lfs.github.com/spec/v1
|
||||
oid sha256:020ec2bd2564aa049f0db23c54b36c3fc49d116fe0a8fe1d69d24e64f8b9f0ce
|
||||
size 92514
|
||||
@@ -0,0 +1,3 @@
|
||||
version https://git-lfs.github.com/spec/v1
|
||||
oid sha256:7ec3ebb39d588b4e9988e36e011b427c46b284016cc66efbf3cbfc7d5a6d68f9
|
||||
size 10275
|
||||
@@ -0,0 +1,3 @@
|
||||
version https://git-lfs.github.com/spec/v1
|
||||
oid sha256:18a4cea497c30cd3764b97e98e694633b4d954d145ea2dd77c636eb8879b97a3
|
||||
size 161
|
||||
@@ -0,0 +1,3 @@
|
||||
version https://git-lfs.github.com/spec/v1
|
||||
oid sha256:69432f7ca3f4e4f3a50e3271c5bdf08e2aef426eba7e23a6fbb9144bc484946f
|
||||
size 13197
|
||||
@@ -4,7 +4,7 @@
|
||||
</PropertyGroup>
|
||||
<!-- Runtime dependencies -->
|
||||
<ItemGroup>
|
||||
<PackageVersion Include="BepuPhysics" Version="2.5.0-beta.25" />
|
||||
<PackageVersion Include="BepuPhysics" Version="2.5.0-beta.27" />
|
||||
<PackageVersion Include="DotRecast.Core" Version="2025.2.1" />
|
||||
<PackageVersion Include="DotRecast.Detour" Version="2025.2.1" />
|
||||
<PackageVersion Include="DotRecast.Recast" Version="2025.2.1" />
|
||||
|
||||
@@ -84,7 +84,7 @@ namespace Stride.Core.Assets.CompilerApp
|
||||
{ "log", "Enable file logging", v => options.EnableFileLogging = v != null },
|
||||
{ "disable-auto-compile", "Disable auto-compile of projects", v => options.DisableAutoCompileProjects = v != null},
|
||||
{ "project-configuration=", "Project configuration", v => options.ProjectConfiguration = v },
|
||||
{ "platform=", "Platform name", v => options.Platform = (PlatformType)Enum.Parse(typeof(PlatformType), v) },
|
||||
{ "platform=", "Platform name", v => options.Platform = Enum.Parse<PlatformType>(v) },
|
||||
{ "solution-file=", "Solution File Name", v => options.SolutionFile = v },
|
||||
{ "package-id=", "Package Id from the solution file", v => options.PackageId = Guid.Parse(v) },
|
||||
{ "package-file=", "Input Package File Name", v => options.PackageFile = v },
|
||||
|
||||
@@ -12,7 +12,7 @@ public class DefaultAssetFactory<T> : AssetFactory<T> where T : Asset
|
||||
{
|
||||
public static T Create()
|
||||
{
|
||||
return (T)Activator.CreateInstance(typeof(T))!;
|
||||
return Activator.CreateInstance<T>()!;
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
|
||||
@@ -1601,6 +1601,11 @@ public sealed partial class PackageSession : IDisposable, IAssetFinder
|
||||
return Equals(obj as PendingPackageUpgrade);
|
||||
}
|
||||
|
||||
public override int GetHashCode()
|
||||
{
|
||||
return HashCode.Combine(PackageUpgrader, Dependency, DependencyPackage, DependencyVersionBeforeUpgrade);
|
||||
}
|
||||
|
||||
public PendingPackageUpgrade Clone()
|
||||
{
|
||||
return new PendingPackageUpgrade(PackageUpgrader, Dependency.Clone(), DependencyPackage);
|
||||
|
||||
@@ -55,7 +55,7 @@ public static class VSProjectHelper
|
||||
{
|
||||
return null;
|
||||
}
|
||||
return (T)Enum.Parse(typeof(T), value);
|
||||
return Enum.Parse<T>(value);
|
||||
}
|
||||
|
||||
public static string GetOrCompileProjectAssembly(string fullProjectLocation, ILogger logger, string targets, bool autoCompileProject, string configuration, string platform = "AnyCPU", Dictionary<string, string>? extraProperties = null, bool onlyErrors = false, BuildRequestDataFlags flags = BuildRequestDataFlags.None)
|
||||
|
||||
@@ -10,7 +10,7 @@ public class StepCounter
|
||||
|
||||
public StepCounter()
|
||||
{
|
||||
stepResults = new int[Enum.GetValues(typeof(ResultStatus)).Length];
|
||||
stepResults = new int[Enum.GetValues<ResultStatus>().Length];
|
||||
}
|
||||
|
||||
public void AddStepResult(ResultStatus result)
|
||||
@@ -35,7 +35,7 @@ public class StepCounter
|
||||
lock (stepResults)
|
||||
{
|
||||
Total = 0;
|
||||
foreach (var value in Enum.GetValues(typeof(ResultStatus)))
|
||||
foreach (var value in Enum.GetValues<ResultStatus>())
|
||||
stepResults[(int)value] = 0;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,29 +0,0 @@
|
||||
// Copyright (c) .NET Foundation and Contributors (https://dotnetfoundation.org/ & https://stride3d.net) and Silicon Studio Corp. (https://www.siliconstudio.co.jp)
|
||||
// Distributed under the MIT license. See the LICENSE.md file in the project root for more information.
|
||||
|
||||
using Mono.Cecil;
|
||||
|
||||
namespace Stride.Core.AssemblyProcessor;
|
||||
|
||||
/// <summary>
|
||||
/// Generates array serializer type from a given array type.
|
||||
/// </summary>
|
||||
public class CecilArraySerializerFactory : ICecilSerializerFactory
|
||||
{
|
||||
private readonly TypeReference genericArraySerializerType;
|
||||
|
||||
public CecilArraySerializerFactory(TypeReference genericArraySerializerType)
|
||||
{
|
||||
this.genericArraySerializerType = genericArraySerializerType;
|
||||
}
|
||||
|
||||
public TypeReference? GetSerializer(TypeReference objectType)
|
||||
{
|
||||
if (objectType.IsArray)
|
||||
{
|
||||
return genericArraySerializerType.MakeGenericType(((ArrayType)objectType).ElementType);
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
}
|
||||
@@ -1,30 +0,0 @@
|
||||
// Copyright (c) .NET Foundation and Contributors (https://dotnetfoundation.org/ & https://stride3d.net) and Silicon Studio Corp. (https://www.siliconstudio.co.jp)
|
||||
// Distributed under the MIT license. See the LICENSE.md file in the project root for more information.
|
||||
|
||||
using Mono.Cecil;
|
||||
|
||||
namespace Stride.Core.AssemblyProcessor;
|
||||
|
||||
/// <summary>
|
||||
/// Generates enum serializer type from a given enum type.
|
||||
/// </summary>
|
||||
public class CecilEnumSerializerFactory : ICecilSerializerFactory
|
||||
{
|
||||
private readonly TypeReference genericEnumSerializerType;
|
||||
|
||||
public CecilEnumSerializerFactory(TypeReference genericEnumSerializerType)
|
||||
{
|
||||
this.genericEnumSerializerType = genericEnumSerializerType;
|
||||
}
|
||||
|
||||
public TypeReference? GetSerializer(TypeReference objectType)
|
||||
{
|
||||
var resolvedObjectType = objectType.Resolve();
|
||||
if (resolvedObjectType?.IsEnum == true)
|
||||
{
|
||||
return genericEnumSerializerType.MakeGenericType(objectType);
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
}
|
||||
@@ -1,53 +0,0 @@
|
||||
// Copyright (c) .NET Foundation and Contributors (https://dotnetfoundation.org/ & https://stride3d.net) and Silicon Studio Corp. (https://www.siliconstudio.co.jp)
|
||||
// Distributed under the MIT license. See the LICENSE.md file in the project root for more information.
|
||||
|
||||
using Mono.Cecil;
|
||||
|
||||
namespace Stride.Core.AssemblyProcessor;
|
||||
|
||||
/// <summary>
|
||||
/// Gives the specified serializer type constructed with generic arguments of the serialized type.
|
||||
/// As an example, this instance with <see cref="ListSerializer{}"/> will give <see cref="ListSerializer{int}"/> from <see cref="int"/>.
|
||||
/// </summary>
|
||||
public class CecilGenericSerializerFactory : ICecilSerializerFactory
|
||||
{
|
||||
private readonly Type genericSerializableType;
|
||||
private readonly bool checkInterfaces;
|
||||
|
||||
protected Func<TypeReference, TypeReference> CreateSerializer { get; set; }
|
||||
|
||||
private CecilGenericSerializerFactory(Type genericSerializableType, Func<TypeReference, TypeReference> createSerializer)
|
||||
{
|
||||
this.genericSerializableType = genericSerializableType;
|
||||
CreateSerializer = createSerializer;
|
||||
checkInterfaces = genericSerializableType.IsInterface;
|
||||
}
|
||||
|
||||
public CecilGenericSerializerFactory(Type genericSerializableType, TypeReference genericSerializerType)
|
||||
: this(genericSerializableType, type => genericSerializerType.MakeGenericType([.. ((GenericInstanceType)type).GenericArguments]))
|
||||
{
|
||||
if (genericSerializerType == null)
|
||||
throw new ArgumentNullException(nameof(genericSerializerType));
|
||||
if (genericSerializableType == null)
|
||||
throw new ArgumentNullException(nameof(genericSerializableType));
|
||||
}
|
||||
|
||||
#region IDataSerializerFactory Members
|
||||
|
||||
public virtual TypeReference? GetSerializer(TypeReference objectType)
|
||||
{
|
||||
// Check if objectType matches genericSerializableType.
|
||||
// Note: Not perfectly valid but hopefully it should be fast enough.
|
||||
if (objectType.IsGenericInstance && checkInterfaces)
|
||||
{
|
||||
if (objectType.GetElementType().Resolve().Interfaces.Any(x => x.InterfaceType.IsGenericInstance && x.InterfaceType.GetElementType().FullName == genericSerializableType.FullName))
|
||||
return CreateSerializer(objectType);
|
||||
}
|
||||
if (objectType.IsGenericInstance && objectType.GetElementType().FullName == genericSerializableType.FullName)
|
||||
return CreateSerializer(objectType);
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
#endregion
|
||||
}
|
||||
@@ -1,57 +0,0 @@
|
||||
// Copyright (c) .NET Foundation and Contributors (https://dotnetfoundation.org/ & https://stride3d.net) and Silicon Studio Corp. (https://www.siliconstudio.co.jp)
|
||||
// Distributed under the MIT license. See the LICENSE.md file in the project root for more information.
|
||||
|
||||
using Mono.Cecil;
|
||||
|
||||
namespace Stride.Core.AssemblyProcessor;
|
||||
|
||||
/// <summary>
|
||||
/// Enumerates required subtypes the given serializer will use internally.
|
||||
/// </summary>
|
||||
public class CecilSerializerDependency : ICecilSerializerDependency
|
||||
{
|
||||
readonly string genericSerializerTypeFullName;
|
||||
readonly TypeReference? genericSerializableType;
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="CecilSerializerDependency" /> class.
|
||||
/// It will enumerates T1, T2 from genericSerializerType{T1, T2}.
|
||||
/// </summary>
|
||||
/// <param name="genericSerializerTypeFullName">Type of the generic serializer.</param>
|
||||
public CecilSerializerDependency(string genericSerializerTypeFullName)
|
||||
{
|
||||
this.genericSerializerTypeFullName = genericSerializerTypeFullName ?? throw new ArgumentNullException(nameof(genericSerializerTypeFullName));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="CecilSerializerDependency" /> class.
|
||||
/// It will enumerates genericSerializableType{T1, T2} from genericSerializerType{T1, T2}.
|
||||
/// </summary>
|
||||
/// <param name="genericSerializerTypeFullName">Type of the generic serializer.</param>
|
||||
/// <param name="genericSerializableType">Type of the generic serializable.</param>
|
||||
public CecilSerializerDependency(string genericSerializerTypeFullName, TypeReference genericSerializableType)
|
||||
{
|
||||
this.genericSerializerTypeFullName = genericSerializerTypeFullName ?? throw new ArgumentNullException(nameof(genericSerializerTypeFullName));
|
||||
this.genericSerializableType = genericSerializableType ?? throw new ArgumentNullException(nameof(genericSerializableType));
|
||||
}
|
||||
|
||||
public IEnumerable<TypeReference> EnumerateSubTypesFromSerializer(TypeReference serializerType)
|
||||
{
|
||||
// Check if serializer type name matches
|
||||
if (serializerType.IsGenericInstance && serializerType.GetElementType().FullName == genericSerializerTypeFullName)
|
||||
{
|
||||
if (genericSerializableType != null)
|
||||
{
|
||||
// Transforms genericSerializerType{T1, T2} into genericSerializableType{T1, T2}
|
||||
return Enumerable.Repeat(genericSerializableType.MakeGenericType([.. ((GenericInstanceType)serializerType).GenericArguments]), 1);
|
||||
}
|
||||
else
|
||||
{
|
||||
// Transforms genericSerializerType{T1, T2} into T1, T2
|
||||
return ((GenericInstanceType)serializerType).GenericArguments;
|
||||
}
|
||||
}
|
||||
|
||||
return [];
|
||||
}
|
||||
}
|
||||
@@ -1,20 +0,0 @@
|
||||
// Copyright (c) .NET Foundation and Contributors (https://dotnetfoundation.org/ & https://stride3d.net) and Silicon Studio Corp. (https://www.siliconstudio.co.jp)
|
||||
// Distributed under the MIT license. See the LICENSE.md file in the project root for more information.
|
||||
|
||||
using Mono.Cecil;
|
||||
|
||||
namespace Stride.Core.AssemblyProcessor;
|
||||
|
||||
/// <summary>
|
||||
/// Enumerates required subtypes the given serializer will use internally.
|
||||
/// This is useful for generation of serialization assembly, when AOT is performed (all generic serializers must be available).
|
||||
/// </summary>
|
||||
public interface ICecilSerializerDependency
|
||||
{
|
||||
/// <summary>
|
||||
/// Enumerates the types this serializer requires.
|
||||
/// </summary>
|
||||
/// <param name="serializerType">Type of the serializer.</param>
|
||||
/// <returns></returns>
|
||||
IEnumerable<TypeReference> EnumerateSubTypesFromSerializer(TypeReference serializerType);
|
||||
}
|
||||
@@ -1,20 +0,0 @@
|
||||
// Copyright (c) .NET Foundation and Contributors (https://dotnetfoundation.org/ & https://stride3d.net) and Silicon Studio Corp. (https://www.siliconstudio.co.jp)
|
||||
// Distributed under the MIT license. See the LICENSE.md file in the project root for more information.
|
||||
|
||||
using Mono.Cecil;
|
||||
|
||||
namespace Stride.Core.AssemblyProcessor;
|
||||
|
||||
/// <summary>
|
||||
/// Gives the required generic serializer for a given type.
|
||||
/// This is useful for generation of serialization assembly, when AOT is performed (all generic serializers must be available).
|
||||
/// </summary>
|
||||
public interface ICecilSerializerFactory
|
||||
{
|
||||
/// <summary>
|
||||
/// Gets the serializer type from a given object type.
|
||||
/// </summary>
|
||||
/// <param name="objectType">Type of the object to serialize.</param>
|
||||
/// <returns></returns>
|
||||
TypeReference? GetSerializer(TypeReference objectType);
|
||||
}
|
||||
@@ -0,0 +1,206 @@
|
||||
// Copyright (c) .NET Foundation and Contributors (https://dotnetfoundation.org/ & https://stride3d.net)
|
||||
// Distributed under the MIT license. See the LICENSE.md file in the project root for more information.
|
||||
|
||||
using Mono.Cecil;
|
||||
using Mono.Cecil.Cil;
|
||||
|
||||
namespace Stride.Core.AssemblyProcessor;
|
||||
|
||||
/// <summary>
|
||||
/// A thin fluent wrapper over Cecil's IL emission that reduces verbosity.
|
||||
/// Provides automatic <see cref="ModuleDefinition.ImportReference(TypeReference)"/> and composite patterns
|
||||
/// like <c>typeof(T)</c> emission.
|
||||
/// </summary>
|
||||
internal class ILBuilder
|
||||
{
|
||||
private readonly MethodBody body;
|
||||
private readonly ModuleDefinition module;
|
||||
|
||||
public ILBuilder(MethodBody body, ModuleDefinition module)
|
||||
{
|
||||
this.body = body;
|
||||
this.module = module;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// The underlying method body, for direct access when needed (e.g. adding variables).
|
||||
/// </summary>
|
||||
public MethodBody Body => body;
|
||||
|
||||
/// <summary>
|
||||
/// The module used for importing references.
|
||||
/// </summary>
|
||||
public ModuleDefinition Module => module;
|
||||
|
||||
// ── Core Emit overloads ──────────────────────────────────────────
|
||||
|
||||
public ILBuilder Emit(OpCode opcode)
|
||||
{
|
||||
body.Instructions.Add(Instruction.Create(opcode));
|
||||
return this;
|
||||
}
|
||||
|
||||
public ILBuilder Emit(OpCode opcode, int value)
|
||||
{
|
||||
body.Instructions.Add(Instruction.Create(opcode, value));
|
||||
return this;
|
||||
}
|
||||
|
||||
public ILBuilder Emit(OpCode opcode, byte value)
|
||||
{
|
||||
body.Instructions.Add(Instruction.Create(opcode, value));
|
||||
return this;
|
||||
}
|
||||
|
||||
public ILBuilder Emit(OpCode opcode, string value)
|
||||
{
|
||||
body.Instructions.Add(Instruction.Create(opcode, value));
|
||||
return this;
|
||||
}
|
||||
|
||||
public ILBuilder Emit(OpCode opcode, TypeReference type)
|
||||
{
|
||||
body.Instructions.Add(Instruction.Create(opcode, type));
|
||||
return this;
|
||||
}
|
||||
|
||||
public ILBuilder Emit(OpCode opcode, MethodReference method)
|
||||
{
|
||||
body.Instructions.Add(Instruction.Create(opcode, method));
|
||||
return this;
|
||||
}
|
||||
|
||||
public ILBuilder Emit(OpCode opcode, FieldReference field)
|
||||
{
|
||||
body.Instructions.Add(Instruction.Create(opcode, field));
|
||||
return this;
|
||||
}
|
||||
|
||||
public ILBuilder Emit(OpCode opcode, VariableDefinition variable)
|
||||
{
|
||||
body.Instructions.Add(Instruction.Create(opcode, variable));
|
||||
return this;
|
||||
}
|
||||
|
||||
public ILBuilder Emit(OpCode opcode, ParameterDefinition parameter)
|
||||
{
|
||||
body.Instructions.Add(Instruction.Create(opcode, parameter));
|
||||
return this;
|
||||
}
|
||||
|
||||
public ILBuilder Emit(OpCode opcode, Instruction target)
|
||||
{
|
||||
body.Instructions.Add(Instruction.Create(opcode, target));
|
||||
return this;
|
||||
}
|
||||
|
||||
public ILBuilder Emit(OpCode opcode, CallSite callSite)
|
||||
{
|
||||
body.Instructions.Add(Instruction.Create(opcode, callSite));
|
||||
return this;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Appends a pre-created instruction to the method body.
|
||||
/// </summary>
|
||||
public ILBuilder Append(Instruction instruction)
|
||||
{
|
||||
body.Instructions.Add(instruction);
|
||||
return this;
|
||||
}
|
||||
|
||||
// ── Import helpers ───────────────────────────────────────────────
|
||||
|
||||
/// <summary>
|
||||
/// Imports a <see cref="TypeReference"/> into the current module.
|
||||
/// Shorthand for <c>module.ImportReference(type)</c>.
|
||||
/// </summary>
|
||||
public TypeReference Import(TypeReference type) => module.ImportReference(type);
|
||||
|
||||
/// <summary>
|
||||
/// Imports a <see cref="MethodReference"/> into the current module.
|
||||
/// Shorthand for <c>module.ImportReference(method)</c>.
|
||||
/// </summary>
|
||||
public MethodReference Import(MethodReference method) => module.ImportReference(method);
|
||||
|
||||
/// <summary>
|
||||
/// Imports a <see cref="FieldReference"/> into the current module.
|
||||
/// Shorthand for <c>module.ImportReference(field)</c>.
|
||||
/// </summary>
|
||||
public FieldReference Import(FieldReference field) => module.ImportReference(field);
|
||||
|
||||
// ── Composite patterns ───────────────────────────────────────────
|
||||
|
||||
/// <summary>
|
||||
/// Emits <c>typeof(T)</c>: <c>ldtoken type; call GetTypeFromHandle</c>.
|
||||
/// </summary>
|
||||
public ILBuilder EmitTypeof(TypeReference type, MethodReference getTypeFromHandle)
|
||||
{
|
||||
Emit(OpCodes.Ldtoken, type);
|
||||
Emit(OpCodes.Call, getTypeFromHandle);
|
||||
return this;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Emits <c>typeof(T).GetTypeInfo().Assembly</c>.
|
||||
/// </summary>
|
||||
public ILBuilder EmitTypeofAssembly(TypeReference type, MethodReference getTypeFromHandle, MethodReference getTypeInfo, MethodReference getAssembly)
|
||||
{
|
||||
EmitTypeof(type, getTypeFromHandle);
|
||||
Emit(OpCodes.Call, getTypeInfo);
|
||||
Emit(OpCodes.Callvirt, getAssembly);
|
||||
return this;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Emits <c>typeof(T).GetTypeInfo().Module</c>.
|
||||
/// </summary>
|
||||
public ILBuilder EmitTypeofModule(TypeReference type, MethodReference getTypeFromHandle, MethodReference getTypeInfo, MethodReference getModule)
|
||||
{
|
||||
EmitTypeof(type, getTypeFromHandle);
|
||||
Emit(OpCodes.Call, getTypeInfo);
|
||||
Emit(OpCodes.Callvirt, getModule);
|
||||
return this;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Emits <c>typeof(T).TypeHandle</c>: <c>ldtoken type; call GetTypeFromHandle; callvirt get_TypeHandle</c>.
|
||||
/// </summary>
|
||||
public ILBuilder EmitTypeHandle(TypeReference type, MethodReference getTypeFromHandle, MethodReference getTypeHandle)
|
||||
{
|
||||
EmitTypeof(type, getTypeFromHandle);
|
||||
Emit(OpCodes.Callvirt, getTypeHandle);
|
||||
return this;
|
||||
}
|
||||
|
||||
// ── Variable helpers ─────────────────────────────────────────────
|
||||
|
||||
/// <summary>
|
||||
/// Adds a local variable to the method body and returns it.
|
||||
/// Sets <see cref="MethodBody.InitLocals"/> to <see langword="true"/>.
|
||||
/// </summary>
|
||||
public VariableDefinition AddLocal(TypeReference type)
|
||||
{
|
||||
var variable = new VariableDefinition(type);
|
||||
body.Variables.Add(variable);
|
||||
body.InitLocals = true;
|
||||
return variable;
|
||||
}
|
||||
|
||||
// ── Label support ────────────────────────────────────────────────
|
||||
|
||||
/// <summary>
|
||||
/// Creates a <see cref="Instruction.Create(OpCodes.Nop)"/> that can be used as a branch target,
|
||||
/// without appending it to the body. Call <see cref="MarkLabel"/> to place it.
|
||||
/// </summary>
|
||||
public static Instruction DefineLabel() => Instruction.Create(OpCodes.Nop);
|
||||
|
||||
/// <summary>
|
||||
/// Appends a previously defined label instruction to the body, marking the current position.
|
||||
/// </summary>
|
||||
public ILBuilder MarkLabel(Instruction label)
|
||||
{
|
||||
body.Instructions.Add(label);
|
||||
return this;
|
||||
}
|
||||
}
|
||||
@@ -2,17 +2,17 @@
|
||||
// Distributed under the MIT license. See the LICENSE.md file in the project root for more information.
|
||||
//
|
||||
// Copyright (c) 2010-2012 SharpDX - Alexandre Mutel
|
||||
//
|
||||
//
|
||||
// Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
// of this software and associated documentation files (the "Software"), to deal
|
||||
// in the Software without restriction, including without limitation the rights
|
||||
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
// copies of the Software, and to permit persons to whom the Software is
|
||||
// furnished to do so, subject to the following conditions:
|
||||
//
|
||||
//
|
||||
// The above copyright notice and this permission notice shall be included in
|
||||
// all copies or substantial portions of the Software.
|
||||
//
|
||||
//
|
||||
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
@@ -103,7 +103,7 @@ internal class InteropProcessor : IAssemblyDefinitionProcessor
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Creates the write method with the following signature:
|
||||
/// Creates the write method with the following signature:
|
||||
/// <code>
|
||||
/// public static unsafe void* Write<T>(void* pDest, ref T data) where T : struct
|
||||
/// </code>
|
||||
@@ -114,7 +114,7 @@ internal class InteropProcessor : IAssemblyDefinitionProcessor
|
||||
method.Body.Instructions.Clear();
|
||||
method.Body.InitLocals = true;
|
||||
|
||||
var gen = method.Body.GetILProcessor();
|
||||
var il = new ILBuilder(method.Body, assembly.MainModule);
|
||||
var paramT = method.GenericParameters[0];
|
||||
// Preparing locals
|
||||
// local(0) int
|
||||
@@ -123,34 +123,28 @@ internal class InteropProcessor : IAssemblyDefinitionProcessor
|
||||
method.Body.Variables.Add(new VariableDefinition(new PinnedType(new ByReferenceType(paramT))));
|
||||
|
||||
// Push (0) pDest for memcpy
|
||||
gen.Emit(OpCodes.Ldarg_0);
|
||||
|
||||
// fixed (void* pinnedData = &data[offset])
|
||||
gen.Emit(OpCodes.Ldarg_1);
|
||||
gen.Emit(OpCodes.Stloc_1);
|
||||
|
||||
// Push (1) pinnedData for memcpy
|
||||
gen.Emit(OpCodes.Ldloc_1);
|
||||
|
||||
// totalSize = sizeof(T)
|
||||
gen.Emit(OpCodes.Sizeof, paramT);
|
||||
gen.Emit(OpCodes.Conv_I4);
|
||||
gen.Emit(OpCodes.Stloc_0);
|
||||
|
||||
// Push (2) totalSize
|
||||
gen.Emit(OpCodes.Ldloc_0);
|
||||
il.Emit(OpCodes.Ldarg_0)
|
||||
// fixed (void* pinnedData = &data[offset])
|
||||
.Emit(OpCodes.Ldarg_1)
|
||||
.Emit(OpCodes.Stloc_1)
|
||||
// Push (1) pinnedData for memcpy
|
||||
.Emit(OpCodes.Ldloc_1)
|
||||
// totalSize = sizeof(T)
|
||||
.Emit(OpCodes.Sizeof, paramT)
|
||||
.Emit(OpCodes.Conv_I4)
|
||||
.Emit(OpCodes.Stloc_0)
|
||||
// Push (2) totalSize
|
||||
.Emit(OpCodes.Ldloc_0);
|
||||
|
||||
// Emit cpblk
|
||||
EmitCpblk(method, gen);
|
||||
EmitCpblk(il);
|
||||
|
||||
// Return pDest + totalSize
|
||||
gen.Emit(OpCodes.Ldloc_0);
|
||||
gen.Emit(OpCodes.Conv_I);
|
||||
gen.Emit(OpCodes.Ldarg_0);
|
||||
gen.Emit(OpCodes.Add);
|
||||
|
||||
// Ret
|
||||
gen.Emit(OpCodes.Ret);
|
||||
il.Emit(OpCodes.Ldloc_0)
|
||||
.Emit(OpCodes.Conv_I)
|
||||
.Emit(OpCodes.Ldarg_0)
|
||||
.Emit(OpCodes.Add)
|
||||
.Emit(OpCodes.Ret);
|
||||
}
|
||||
|
||||
private void ReplacePinStatement(MethodDefinition method, ILProcessor ilProcessor, Instruction fixedtoPatch)
|
||||
@@ -348,12 +342,9 @@ internal class InteropProcessor : IAssemblyDefinitionProcessor
|
||||
method.Body.Instructions.Clear();
|
||||
method.Body.InitLocals = true;
|
||||
|
||||
var gen = method.Body.GetILProcessor();
|
||||
|
||||
gen.Emit(OpCodes.Ldarg_0);
|
||||
|
||||
// Ret
|
||||
gen.Emit(OpCodes.Ret);
|
||||
var il = new ILBuilder(method.Body, assembly.MainModule);
|
||||
il.Emit(OpCodes.Ldarg_0)
|
||||
.Emit(OpCodes.Ret);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -368,12 +359,9 @@ internal class InteropProcessor : IAssemblyDefinitionProcessor
|
||||
method.Body.Instructions.Clear();
|
||||
method.Body.InitLocals = true;
|
||||
|
||||
var gen = method.Body.GetILProcessor();
|
||||
|
||||
gen.Emit(OpCodes.Ldarg_0);
|
||||
|
||||
// Ret
|
||||
gen.Emit(OpCodes.Ret);
|
||||
var il = new ILBuilder(method.Body, assembly.MainModule);
|
||||
il.Emit(OpCodes.Ldarg_0)
|
||||
.Emit(OpCodes.Ret);
|
||||
}
|
||||
|
||||
private void ReplaceFixedArrayStatement(MethodDefinition method, ILProcessor ilProcessor, Instruction fixedtoPatch)
|
||||
@@ -431,7 +419,7 @@ internal class InteropProcessor : IAssemblyDefinitionProcessor
|
||||
method.Body.Instructions.Clear();
|
||||
method.Body.InitLocals = true;
|
||||
|
||||
var gen = method.Body.GetILProcessor();
|
||||
var il = new ILBuilder(method.Body, assembly.MainModule);
|
||||
var paramT = method.GenericParameters[0];
|
||||
// Preparing locals
|
||||
// local(0) int
|
||||
@@ -440,38 +428,32 @@ internal class InteropProcessor : IAssemblyDefinitionProcessor
|
||||
method.Body.Variables.Add(new VariableDefinition(new PinnedType(new ByReferenceType(paramT))));
|
||||
|
||||
// Push (0) pDest for memcpy
|
||||
gen.Emit(OpCodes.Ldarg_0);
|
||||
|
||||
// fixed (void* pinnedData = &data[offset])
|
||||
gen.Emit(OpCodes.Ldarg_1);
|
||||
gen.Emit(OpCodes.Ldarg_2);
|
||||
gen.Emit(OpCodes.Ldelema, paramT);
|
||||
gen.Emit(OpCodes.Stloc_1);
|
||||
|
||||
// Push (1) pinnedData for memcpy
|
||||
gen.Emit(OpCodes.Ldloc_1);
|
||||
|
||||
// totalSize = sizeof(T) * count
|
||||
gen.Emit(OpCodes.Sizeof, paramT);
|
||||
gen.Emit(OpCodes.Conv_I4);
|
||||
gen.Emit(OpCodes.Ldarg_3);
|
||||
gen.Emit(OpCodes.Mul);
|
||||
gen.Emit(OpCodes.Stloc_0);
|
||||
|
||||
// Push (2) totalSize
|
||||
gen.Emit(OpCodes.Ldloc_0);
|
||||
il.Emit(OpCodes.Ldarg_0)
|
||||
// fixed (void* pinnedData = &data[offset])
|
||||
.Emit(OpCodes.Ldarg_1)
|
||||
.Emit(OpCodes.Ldarg_2)
|
||||
.Emit(OpCodes.Ldelema, paramT)
|
||||
.Emit(OpCodes.Stloc_1)
|
||||
// Push (1) pinnedData for memcpy
|
||||
.Emit(OpCodes.Ldloc_1)
|
||||
// totalSize = sizeof(T) * count
|
||||
.Emit(OpCodes.Sizeof, paramT)
|
||||
.Emit(OpCodes.Conv_I4)
|
||||
.Emit(OpCodes.Ldarg_3)
|
||||
.Emit(OpCodes.Mul)
|
||||
.Emit(OpCodes.Stloc_0)
|
||||
// Push (2) totalSize
|
||||
.Emit(OpCodes.Ldloc_0);
|
||||
|
||||
// Emit cpblk
|
||||
EmitCpblk(method, gen);
|
||||
EmitCpblk(il);
|
||||
|
||||
// Return pDest + totalSize
|
||||
gen.Emit(OpCodes.Ldloc_0);
|
||||
gen.Emit(OpCodes.Conv_I);
|
||||
gen.Emit(OpCodes.Ldarg_0);
|
||||
gen.Emit(OpCodes.Add);
|
||||
|
||||
// Ret
|
||||
gen.Emit(OpCodes.Ret);
|
||||
il.Emit(OpCodes.Ldloc_0)
|
||||
.Emit(OpCodes.Conv_I)
|
||||
.Emit(OpCodes.Ldarg_0)
|
||||
.Emit(OpCodes.Add)
|
||||
.Emit(OpCodes.Ret);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -486,45 +468,38 @@ internal class InteropProcessor : IAssemblyDefinitionProcessor
|
||||
method.Body.Instructions.Clear();
|
||||
method.Body.InitLocals = true;
|
||||
|
||||
var gen = method.Body.GetILProcessor();
|
||||
var il = new ILBuilder(method.Body, assembly.MainModule);
|
||||
var paramT = method.GenericParameters[0];
|
||||
|
||||
// Preparing locals
|
||||
// local(0) int
|
||||
method.Body.Variables.Add(new VariableDefinition(intType));
|
||||
// local(1) T*
|
||||
|
||||
method.Body.Variables.Add(new VariableDefinition(new PinnedType(new ByReferenceType(paramT))));
|
||||
|
||||
// fixed (void* pinnedData = &data[offset])
|
||||
gen.Emit(OpCodes.Ldarg_1);
|
||||
gen.Emit(OpCodes.Stloc_1);
|
||||
|
||||
// Push (0) pinnedData for memcpy
|
||||
gen.Emit(OpCodes.Ldloc_1);
|
||||
|
||||
// Push (1) pSrc for memcpy
|
||||
gen.Emit(OpCodes.Ldarg_0);
|
||||
|
||||
// totalSize = sizeof(T)
|
||||
gen.Emit(OpCodes.Sizeof, paramT);
|
||||
gen.Emit(OpCodes.Conv_I4);
|
||||
gen.Emit(OpCodes.Stloc_0);
|
||||
|
||||
// Push (2) totalSize
|
||||
gen.Emit(OpCodes.Ldloc_0);
|
||||
il.Emit(OpCodes.Ldarg_1)
|
||||
.Emit(OpCodes.Stloc_1)
|
||||
// Push (0) pinnedData for memcpy
|
||||
.Emit(OpCodes.Ldloc_1)
|
||||
// Push (1) pSrc for memcpy
|
||||
.Emit(OpCodes.Ldarg_0)
|
||||
// totalSize = sizeof(T)
|
||||
.Emit(OpCodes.Sizeof, paramT)
|
||||
.Emit(OpCodes.Conv_I4)
|
||||
.Emit(OpCodes.Stloc_0)
|
||||
// Push (2) totalSize
|
||||
.Emit(OpCodes.Ldloc_0);
|
||||
|
||||
// Emit cpblk
|
||||
EmitCpblk(method, gen);
|
||||
EmitCpblk(il);
|
||||
|
||||
// Return pDest + totalSize
|
||||
gen.Emit(OpCodes.Ldloc_0);
|
||||
gen.Emit(OpCodes.Conv_I);
|
||||
gen.Emit(OpCodes.Ldarg_0);
|
||||
gen.Emit(OpCodes.Add);
|
||||
|
||||
// Ret
|
||||
gen.Emit(OpCodes.Ret);
|
||||
il.Emit(OpCodes.Ldloc_0)
|
||||
.Emit(OpCodes.Conv_I)
|
||||
.Emit(OpCodes.Ldarg_0)
|
||||
.Emit(OpCodes.Add)
|
||||
.Emit(OpCodes.Ret);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -539,12 +514,10 @@ internal class InteropProcessor : IAssemblyDefinitionProcessor
|
||||
method.Body.Instructions.Clear();
|
||||
method.Body.InitLocals = true;
|
||||
|
||||
var gen = method.Body.GetILProcessor();
|
||||
var paramT = method.GenericParameters[0];
|
||||
var il = new ILBuilder(method.Body, assembly.MainModule);
|
||||
|
||||
// Push (1) pSrc for memcpy
|
||||
gen.Emit(OpCodes.Cpobj);
|
||||
|
||||
il.Emit(OpCodes.Cpobj);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -559,7 +532,7 @@ internal class InteropProcessor : IAssemblyDefinitionProcessor
|
||||
method.Body.Instructions.Clear();
|
||||
method.Body.InitLocals = true;
|
||||
|
||||
var gen = method.Body.GetILProcessor();
|
||||
var il = new ILBuilder(method.Body, assembly.MainModule);
|
||||
var paramT = method.GenericParameters[0];
|
||||
// Preparing locals
|
||||
// local(0) int
|
||||
@@ -568,39 +541,33 @@ internal class InteropProcessor : IAssemblyDefinitionProcessor
|
||||
method.Body.Variables.Add(new VariableDefinition(new PinnedType(new ByReferenceType(paramT))));
|
||||
|
||||
// fixed (void* pinnedData = &data[offset])
|
||||
gen.Emit(OpCodes.Ldarg_1);
|
||||
gen.Emit(OpCodes.Ldarg_2);
|
||||
gen.Emit(OpCodes.Ldelema, paramT);
|
||||
gen.Emit(OpCodes.Stloc_1);
|
||||
|
||||
// Push (0) pinnedData for memcpy
|
||||
gen.Emit(OpCodes.Ldloc_1);
|
||||
|
||||
// Push (1) pDest for memcpy
|
||||
gen.Emit(OpCodes.Ldarg_0);
|
||||
|
||||
// totalSize = sizeof(T) * count
|
||||
gen.Emit(OpCodes.Sizeof, paramT);
|
||||
gen.Emit(OpCodes.Conv_I4);
|
||||
gen.Emit(OpCodes.Ldarg_3);
|
||||
gen.Emit(OpCodes.Conv_I4);
|
||||
gen.Emit(OpCodes.Mul);
|
||||
gen.Emit(OpCodes.Stloc_0);
|
||||
|
||||
// Push (2) totalSize
|
||||
gen.Emit(OpCodes.Ldloc_0);
|
||||
il.Emit(OpCodes.Ldarg_1)
|
||||
.Emit(OpCodes.Ldarg_2)
|
||||
.Emit(OpCodes.Ldelema, paramT)
|
||||
.Emit(OpCodes.Stloc_1)
|
||||
// Push (0) pinnedData for memcpy
|
||||
.Emit(OpCodes.Ldloc_1)
|
||||
// Push (1) pDest for memcpy
|
||||
.Emit(OpCodes.Ldarg_0)
|
||||
// totalSize = sizeof(T) * count
|
||||
.Emit(OpCodes.Sizeof, paramT)
|
||||
.Emit(OpCodes.Conv_I4)
|
||||
.Emit(OpCodes.Ldarg_3)
|
||||
.Emit(OpCodes.Conv_I4)
|
||||
.Emit(OpCodes.Mul)
|
||||
.Emit(OpCodes.Stloc_0)
|
||||
// Push (2) totalSize
|
||||
.Emit(OpCodes.Ldloc_0);
|
||||
|
||||
// Emit cpblk
|
||||
EmitCpblk(method, gen);
|
||||
EmitCpblk(il);
|
||||
|
||||
// Return pDest + totalSize
|
||||
gen.Emit(OpCodes.Ldloc_0);
|
||||
gen.Emit(OpCodes.Conv_I);
|
||||
gen.Emit(OpCodes.Ldarg_0);
|
||||
gen.Emit(OpCodes.Add);
|
||||
|
||||
// Ret
|
||||
gen.Emit(OpCodes.Ret);
|
||||
il.Emit(OpCodes.Ldloc_0)
|
||||
.Emit(OpCodes.Conv_I)
|
||||
.Emit(OpCodes.Ldarg_0)
|
||||
.Emit(OpCodes.Add)
|
||||
.Emit(OpCodes.Ret);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -614,16 +581,13 @@ internal class InteropProcessor : IAssemblyDefinitionProcessor
|
||||
{
|
||||
methodCopyStruct.Body.Instructions.Clear();
|
||||
|
||||
var gen = methodCopyStruct.Body.GetILProcessor();
|
||||
|
||||
gen.Emit(OpCodes.Ldarg_0);
|
||||
gen.Emit(OpCodes.Ldarg_1);
|
||||
gen.Emit(OpCodes.Ldarg_2);
|
||||
gen.Emit(OpCodes.Unaligned, (byte)1); // unaligned to 1
|
||||
gen.Emit(OpCodes.Cpblk);
|
||||
|
||||
// Ret
|
||||
gen.Emit(OpCodes.Ret);
|
||||
var il = new ILBuilder(methodCopyStruct.Body, assembly.MainModule);
|
||||
il.Emit(OpCodes.Ldarg_0)
|
||||
.Emit(OpCodes.Ldarg_1)
|
||||
.Emit(OpCodes.Ldarg_2)
|
||||
.Emit(OpCodes.Unaligned, (byte)1) // unaligned to 1
|
||||
.Emit(OpCodes.Cpblk)
|
||||
.Emit(OpCodes.Ret);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -637,32 +601,27 @@ internal class InteropProcessor : IAssemblyDefinitionProcessor
|
||||
{
|
||||
methodSetStruct.Body.Instructions.Clear();
|
||||
|
||||
var gen = methodSetStruct.Body.GetILProcessor();
|
||||
|
||||
gen.Emit(OpCodes.Ldarg_0);
|
||||
gen.Emit(OpCodes.Ldarg_1);
|
||||
gen.Emit(OpCodes.Ldarg_2);
|
||||
gen.Emit(OpCodes.Unaligned, (byte)1); // unaligned to 1
|
||||
gen.Emit(OpCodes.Initblk);
|
||||
|
||||
// Ret
|
||||
gen.Emit(OpCodes.Ret);
|
||||
var il = new ILBuilder(methodSetStruct.Body, assembly.MainModule);
|
||||
il.Emit(OpCodes.Ldarg_0)
|
||||
.Emit(OpCodes.Ldarg_1)
|
||||
.Emit(OpCodes.Ldarg_2)
|
||||
.Emit(OpCodes.Unaligned, (byte)1) // unaligned to 1
|
||||
.Emit(OpCodes.Initblk)
|
||||
.Emit(OpCodes.Ret);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Emits the cpblk method, supporting x86 and x64 platform.
|
||||
/// </summary>
|
||||
/// <param name="method">The method.</param>
|
||||
/// <param name="gen">The gen.</param>
|
||||
private void EmitCpblk(MethodDefinition method, ILProcessor gen)
|
||||
/// <param name="il">The IL builder.</param>
|
||||
private static void EmitCpblk(ILBuilder il)
|
||||
{
|
||||
var cpblk = gen.Create(OpCodes.Cpblk);
|
||||
//gen.Emit(OpCodes.Sizeof, voidPointerType);
|
||||
//gen.Emit(OpCodes.Ldc_I4_8);
|
||||
//gen.Emit(OpCodes.Bne_Un_S, cpblk);
|
||||
gen.Emit(OpCodes.Unaligned, (byte)1); // unaligned to 1
|
||||
gen.Append(cpblk);
|
||||
|
||||
var cpblk = Instruction.Create(OpCodes.Cpblk);
|
||||
//il.Emit(OpCodes.Sizeof, voidPointerType);
|
||||
//il.Emit(OpCodes.Ldc_I4_8);
|
||||
//il.Emit(OpCodes.Bne_Un_S, cpblk);
|
||||
il.Emit(OpCodes.Unaligned, (byte)1) // unaligned to 1
|
||||
.Append(cpblk);
|
||||
}
|
||||
|
||||
private List<string> GetSharpDXAttributes(MethodDefinition method)
|
||||
|
||||
@@ -22,7 +22,7 @@ internal class ProfileSerializerProcessor : ICecilSerializerProcessor
|
||||
// For each profile, try to instantiate all types existing in default profile
|
||||
foreach (var type in defaultProfile.SerializableTypes)
|
||||
{
|
||||
context.GenerateSerializer(type.Key, false, profile.Key);
|
||||
context.ResolveSerializer(type.Key, false, profile.Key);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -65,6 +65,21 @@ class ResolveGenericsVisitor : CecilTypeReferenceVisitor
|
||||
return result;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Creates a visitor that maps generic parameters from <paramref name="source"/> to <paramref name="target"/>.
|
||||
/// Returns null if the source has no generic parameters.
|
||||
/// </summary>
|
||||
public static ResolveGenericsVisitor? FromMapping(IGenericParameterProvider source, IGenericParameterProvider target)
|
||||
{
|
||||
if (source.GenericParameters.Count == 0)
|
||||
return null;
|
||||
|
||||
var mapping = new Dictionary<TypeReference, TypeReference>();
|
||||
for (int i = 0; i < source.GenericParameters.Count; i++)
|
||||
mapping[source.GenericParameters[i]] = target.GenericParameters[i];
|
||||
return new ResolveGenericsVisitor(mapping);
|
||||
}
|
||||
|
||||
public override TypeReference Visit(GenericParameter type)
|
||||
{
|
||||
TypeReference typeParent = type;
|
||||
|
||||
+37
-222
@@ -1,144 +1,38 @@
|
||||
// Copyright (c) .NET Foundation and Contributors (https://dotnetfoundation.org/ & https://stride3d.net) and Silicon Studio Corp. (https://www.siliconstudio.co.jp)
|
||||
// Distributed under the MIT license. See the LICENSE.md file in the project root for more information.
|
||||
|
||||
using System.Runtime.Versioning;
|
||||
using Stride.Core.AssemblyProcessor.Serializers;
|
||||
using System.Runtime.CompilerServices;
|
||||
using System.Text;
|
||||
using Mono.Cecil;
|
||||
|
||||
namespace Stride.Core.AssemblyProcessor;
|
||||
|
||||
internal class ComplexSerializerRegistry
|
||||
/// <summary>
|
||||
/// Static utility methods for analyzing serializable types and generating names.
|
||||
/// </summary>
|
||||
internal static class SerializationHelpers
|
||||
{
|
||||
private static readonly HashSet<string> forbiddenKeywords;
|
||||
private static readonly HashSet<IMemberDefinition> ignoredMembers;
|
||||
|
||||
static ComplexSerializerRegistry()
|
||||
{
|
||||
ignoredMembers = [];
|
||||
|
||||
forbiddenKeywords =
|
||||
[ "obj", "stream", "mode",
|
||||
"abstract", "event", "new", "struct",
|
||||
"as", "explicit", "null", "switch",
|
||||
"base", "extern", "object", "this",
|
||||
"bool", "false", "operator", "throw",
|
||||
"break", "finally", "out", "true",
|
||||
"byte", "fixed", "override", "try",
|
||||
"case", "float", "params", "typeof",
|
||||
"catch", "for", "private", "uint",
|
||||
"char", "foreach", "protected", "ulong",
|
||||
"checked", "goto", "public", "unchecked",
|
||||
"class", "if", "readonly", "unsafe",
|
||||
"const", "implicit", "ref", "ushort",
|
||||
"continue", "in", "return", "using",
|
||||
"decimal", "int", "sbyte", "virtual",
|
||||
"default", "interface", "sealed", "volatile",
|
||||
"delegate", "internal", "short", "void",
|
||||
"do", "is", "sizeof", "while",
|
||||
"double", "lock", "stackalloc",
|
||||
"else", "long", "static",
|
||||
"enum", "namespace", "string" ];
|
||||
}
|
||||
|
||||
public List<TypeReference> ReferencedAssemblySerializerFactoryTypes { get; } = [];
|
||||
|
||||
public CecilSerializerContext Context { get; }
|
||||
|
||||
//private List<IDataSerializerFactory> serializerFactories = new List<IDataSerializerFactory>();
|
||||
|
||||
public string? TargetFramework { get; }
|
||||
|
||||
public string ClassName { get; }
|
||||
|
||||
public AssemblyDefinition Assembly { get; }
|
||||
|
||||
public List<ICecilSerializerDependency> SerializerDependencies { get; } = [];
|
||||
|
||||
public List<ICecilSerializerFactory> SerializerFactories { get; } = [];
|
||||
|
||||
public ComplexSerializerRegistry(PlatformType platform, AssemblyDefinition assembly, TextWriter log)
|
||||
{
|
||||
Assembly = assembly;
|
||||
ClassName = Utilities.BuildValidClassName(assembly.Name.Name) + "SerializerFactory";
|
||||
|
||||
// Register referenced assemblies serializer factory, so that we can call them recursively
|
||||
foreach (var referencedAssemblyName in assembly.MainModule.AssemblyReferences)
|
||||
{
|
||||
try
|
||||
{
|
||||
var referencedAssembly = assembly.MainModule.AssemblyResolver.Resolve(referencedAssemblyName);
|
||||
|
||||
var assemblySerializerFactoryType = GetSerializerFactoryType(referencedAssembly);
|
||||
if (assemblySerializerFactoryType != null)
|
||||
ReferencedAssemblySerializerFactoryTypes.Add(assemblySerializerFactoryType);
|
||||
}
|
||||
catch (AssemblyResolutionException)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
// Find target framework and replicate it for serializer assembly.
|
||||
var targetFrameworkAttribute = assembly.CustomAttributes
|
||||
.FirstOrDefault(x => x.AttributeType.FullName == typeof(TargetFrameworkAttribute).FullName);
|
||||
if (targetFrameworkAttribute != null)
|
||||
{
|
||||
TargetFramework = "\"" + (string)targetFrameworkAttribute.ConstructorArguments[0].Value + "\"";
|
||||
var frameworkDisplayNameField = targetFrameworkAttribute.Properties.FirstOrDefault(x => x.Name == "FrameworkDisplayName");
|
||||
if (frameworkDisplayNameField.Name != null)
|
||||
{
|
||||
TargetFramework += ", FrameworkDisplayName=\"" + (string)frameworkDisplayNameField.Argument.Value + "\"";
|
||||
}
|
||||
}
|
||||
|
||||
// Prepare serializer processors
|
||||
Context = new CecilSerializerContext(platform, assembly, log);
|
||||
var processors = new List<ICecilSerializerProcessor>
|
||||
{
|
||||
// Import list of serializer registered by referenced assemblies
|
||||
new ReferencedAssemblySerializerProcessor(),
|
||||
|
||||
// Generate serializers for types tagged as serializable
|
||||
new CecilComplexClassSerializerProcessor(),
|
||||
|
||||
// Generate serializers for PropertyKey and ParameterKey
|
||||
new PropertyKeySerializerProcessor(),
|
||||
|
||||
// Update Engine (with AnimationData<T>)
|
||||
new UpdateEngineProcessor(),
|
||||
|
||||
// Profile serializers
|
||||
new ProfileSerializerProcessor(),
|
||||
|
||||
// Data contract aliases
|
||||
new DataContractAliasProcessor()
|
||||
};
|
||||
|
||||
// Apply each processor
|
||||
foreach (var processor in processors)
|
||||
processor.ProcessSerializers(Context);
|
||||
}
|
||||
|
||||
private static TypeDefinition? GetSerializerFactoryType(AssemblyDefinition referencedAssembly)
|
||||
{
|
||||
var assemblySerializerFactoryAttribute =
|
||||
referencedAssembly.CustomAttributes.FirstOrDefault(
|
||||
x =>
|
||||
x.AttributeType.FullName ==
|
||||
"Stride.Core.Serialization.AssemblySerializerFactoryAttribute");
|
||||
|
||||
// No serializer factory?
|
||||
if (assemblySerializerFactoryAttribute == null)
|
||||
return null;
|
||||
|
||||
var typeReference = (TypeReference)assemblySerializerFactoryAttribute.Fields.Single(x => x.Name == "Type").Argument.Value;
|
||||
if (typeReference == null)
|
||||
return null;
|
||||
|
||||
return typeReference.Resolve();
|
||||
}
|
||||
private static readonly HashSet<string> forbiddenKeywords =
|
||||
[ "obj", "stream", "mode",
|
||||
"abstract", "event", "new", "struct",
|
||||
"as", "explicit", "null", "switch",
|
||||
"base", "extern", "object", "this",
|
||||
"bool", "false", "operator", "throw",
|
||||
"break", "finally", "out", "true",
|
||||
"byte", "fixed", "override", "try",
|
||||
"case", "float", "params", "typeof",
|
||||
"catch", "for", "private", "uint",
|
||||
"char", "foreach", "protected", "ulong",
|
||||
"checked", "goto", "public", "unchecked",
|
||||
"class", "if", "readonly", "unsafe",
|
||||
"const", "implicit", "ref", "ushort",
|
||||
"continue", "in", "return", "using",
|
||||
"decimal", "int", "sbyte", "virtual",
|
||||
"default", "interface", "sealed", "volatile",
|
||||
"delegate", "internal", "short", "void",
|
||||
"do", "is", "sizeof", "while",
|
||||
"double", "lock", "stackalloc",
|
||||
"else", "long", "static",
|
||||
"enum", "namespace", "string" ];
|
||||
|
||||
private static string TypeNameWithoutGenericEnding(TypeReference type)
|
||||
{
|
||||
@@ -175,88 +69,17 @@ internal class ComplexSerializerRegistry
|
||||
return typeName;
|
||||
}
|
||||
|
||||
public static string GetSerializerInstantiateMethodName(TypeReference serializerType, bool appendGenerics)
|
||||
public static string CreateMemberVariableName(IMemberDefinition memberInfo)
|
||||
{
|
||||
return "Instantiate_" + SerializerTypeName(serializerType, appendGenerics, false);
|
||||
var memberVariableName = char.ToLowerInvariant(memberInfo.Name[0]) + memberInfo.Name[1..];
|
||||
if (forbiddenKeywords.Contains(memberVariableName))
|
||||
memberVariableName += "_";
|
||||
return memberVariableName;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Generates the generic constraints in a code form.
|
||||
/// </summary>
|
||||
/// <param name="type">The type.</param>
|
||||
/// <returns></returns>
|
||||
public static string GenerateGenericConstraints(TypeReference type)
|
||||
public static IEnumerable<SerializableItem> GetSerializableItems(TypeReference type, bool serializeFields, ComplexTypeSerializerFlags? flagsOverride = null, HashSet<IMemberDefinition>? ignoredMembers = null)
|
||||
{
|
||||
if (!type.HasGenericParameters)
|
||||
return string.Empty;
|
||||
|
||||
var result = new StringBuilder();
|
||||
foreach (var genericParameter in type.GenericParameters)
|
||||
{
|
||||
// If no constraints, skip it
|
||||
var hasContraints = genericParameter.HasReferenceTypeConstraint || genericParameter.HasNotNullableValueTypeConstraint || genericParameter.Constraints.Count > 0 || genericParameter.HasDefaultConstructorConstraint;
|
||||
if (!hasContraints)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
bool hasFirstContraint = false;
|
||||
|
||||
result.AppendFormat(" where {0}: ", genericParameter.Name);
|
||||
|
||||
// Where class/struct constraint must be before any other constraint
|
||||
if (genericParameter.HasReferenceTypeConstraint)
|
||||
{
|
||||
result.AppendFormat("class");
|
||||
hasFirstContraint = true;
|
||||
}
|
||||
else if (genericParameter.HasNotNullableValueTypeConstraint)
|
||||
{
|
||||
result.AppendFormat("struct");
|
||||
hasFirstContraint = true;
|
||||
}
|
||||
|
||||
foreach (var genericParameterConstraint in genericParameter.Constraints)
|
||||
{
|
||||
// Skip value type constraint
|
||||
if (genericParameterConstraint.ConstraintType.FullName != typeof(ValueType).FullName)
|
||||
{
|
||||
if (hasFirstContraint)
|
||||
{
|
||||
result.Append(", ");
|
||||
}
|
||||
|
||||
result.AppendFormat("{0}", genericParameterConstraint.ConstraintType.ConvertCSharp());
|
||||
result.AppendLine();
|
||||
|
||||
hasFirstContraint = true;
|
||||
}
|
||||
}
|
||||
|
||||
// New constraint must be last
|
||||
if (!genericParameter.HasNotNullableValueTypeConstraint && genericParameter.HasDefaultConstructorConstraint)
|
||||
{
|
||||
if (hasFirstContraint)
|
||||
{
|
||||
result.Append(", ");
|
||||
}
|
||||
|
||||
result.AppendFormat("new()");
|
||||
result.AppendLine();
|
||||
}
|
||||
}
|
||||
|
||||
return result.ToString();
|
||||
}
|
||||
|
||||
public static void IgnoreMember(IMemberDefinition memberInfo)
|
||||
{
|
||||
ignoredMembers.Add(memberInfo);
|
||||
}
|
||||
|
||||
public static IEnumerable<SerializableItem> GetSerializableItems(TypeReference type, bool serializeFields, ComplexTypeSerializerFlags? flagsOverride = null)
|
||||
{
|
||||
foreach (var serializableItemOriginal in GetSerializableItems(type.Resolve(), serializeFields, flagsOverride))
|
||||
foreach (var serializableItemOriginal in GetSerializableItems(type.Resolve(), serializeFields, flagsOverride, ignoredMembers))
|
||||
{
|
||||
var serializableItem = serializableItemOriginal;
|
||||
|
||||
@@ -270,7 +93,7 @@ internal class ComplexSerializerRegistry
|
||||
}
|
||||
}
|
||||
|
||||
public static IEnumerable<SerializableItem> GetSerializableItems(TypeDefinition type, bool serializeFields, ComplexTypeSerializerFlags? flagsOverride = null)
|
||||
public static IEnumerable<SerializableItem> GetSerializableItems(TypeDefinition type, bool serializeFields, ComplexTypeSerializerFlags? flagsOverride = null, HashSet<IMemberDefinition>? ignoredMembers = null)
|
||||
{
|
||||
ComplexTypeSerializerFlags flags;
|
||||
|
||||
@@ -282,7 +105,7 @@ internal class ComplexSerializerRegistry
|
||||
if (field.IsStatic)
|
||||
continue;
|
||||
|
||||
if (ignoredMembers.Contains(field))
|
||||
if (ignoredMembers?.Contains(field) == true)
|
||||
continue;
|
||||
|
||||
if (field.IsPublic || (field.IsAssembly && field.CustomAttributes.Any(a => a.AttributeType.FullName == "Stride.Core.DataMemberAttribute")))
|
||||
@@ -315,7 +138,7 @@ internal class ComplexSerializerRegistry
|
||||
}
|
||||
|
||||
// Ignore blacklisted properties
|
||||
if (ignoredMembers.Contains(property))
|
||||
if (ignoredMembers?.Contains(property) == true)
|
||||
continue;
|
||||
|
||||
properties.Add(property);
|
||||
@@ -385,7 +208,7 @@ internal class ComplexSerializerRegistry
|
||||
}
|
||||
}
|
||||
|
||||
static bool IsAccessibleThroughAccessModifiers(PropertyDefinition property)
|
||||
private static bool IsAccessibleThroughAccessModifiers(PropertyDefinition property)
|
||||
{
|
||||
var get = property.GetMethod;
|
||||
var set = property.SetMethod;
|
||||
@@ -445,14 +268,6 @@ internal class ComplexSerializerRegistry
|
||||
&& !((type.MetadataType == MetadataType.ValueType || type.MetadataType == MetadataType.Class) && type.Resolve().IsValueType);
|
||||
}
|
||||
|
||||
public static string CreateMemberVariableName(IMemberDefinition memberInfo)
|
||||
{
|
||||
var memberVariableName = char.ToLowerInvariant(memberInfo.Name[0]) + memberInfo.Name[1..];
|
||||
if (forbiddenKeywords.Contains(memberVariableName))
|
||||
memberVariableName += "_";
|
||||
return memberVariableName;
|
||||
}
|
||||
|
||||
public struct SerializableItem
|
||||
{
|
||||
public bool HasFixedAttribute;
|
||||
File diff suppressed because it is too large
Load Diff
+2
-3
@@ -6,14 +6,13 @@ using Mono.Cecil.Rocks;
|
||||
|
||||
namespace Stride.Core.AssemblyProcessor.Serializers;
|
||||
|
||||
class CecilComplexClassSerializerProcessor : ICecilSerializerProcessor
|
||||
class CecilDataContractSerializerProcessor : ICecilSerializerProcessor
|
||||
{
|
||||
public void ProcessSerializers(CecilSerializerContext context)
|
||||
{
|
||||
foreach (var type in context.Assembly.MainModule.GetAllTypes().ToArray())
|
||||
{
|
||||
// Force generation of serializers (complex types, etc...)
|
||||
// Check complex type definitions
|
||||
// Discover [DataContract] types and resolve their serializers
|
||||
ProcessType(context, type);
|
||||
}
|
||||
}
|
||||
+312
-333
@@ -18,10 +18,39 @@ internal class CecilSerializerContext
|
||||
SerializableTypesProfiles = [];
|
||||
SerializableTypes = new ProfileInfo();
|
||||
SerializableTypesProfiles.Add("Default", SerializableTypes);
|
||||
ComplexTypes = [];
|
||||
PendingSerializers = [];
|
||||
IgnoredMembers = [];
|
||||
this.log = log;
|
||||
|
||||
StrideCoreModule = assembly.GetStrideCoreModule();
|
||||
|
||||
// Discover referenced assemblies' serializer factories
|
||||
foreach (var referencedAssemblyName in assembly.MainModule.AssemblyReferences)
|
||||
{
|
||||
try
|
||||
{
|
||||
var referencedAssembly = assembly.MainModule.AssemblyResolver.Resolve(referencedAssemblyName);
|
||||
var factoryType = GetSerializerFactoryType(referencedAssembly);
|
||||
if (factoryType != null)
|
||||
ReferencedAssemblySerializerFactoryTypes.Add(factoryType);
|
||||
}
|
||||
catch (AssemblyResolutionException)
|
||||
{
|
||||
}
|
||||
}
|
||||
|
||||
// Run the processor pipeline
|
||||
ICecilSerializerProcessor[] processors =
|
||||
[
|
||||
new ReferencedAssemblySerializerProcessor(),
|
||||
new CecilDataContractSerializerProcessor(),
|
||||
new PropertyKeySerializerProcessor(),
|
||||
new UpdateEngineProcessor(),
|
||||
new ProfileSerializerProcessor(),
|
||||
new DataContractAliasProcessor(),
|
||||
];
|
||||
foreach (var processor in processors)
|
||||
processor.ProcessSerializers(this);
|
||||
}
|
||||
|
||||
public PlatformType Platform { get; }
|
||||
@@ -29,21 +58,17 @@ internal class CecilSerializerContext
|
||||
/// <summary>
|
||||
/// Gets the assembly being processed.
|
||||
/// </summary>
|
||||
/// <value>
|
||||
/// The assembly being processed.
|
||||
/// </value>
|
||||
public AssemblyDefinition Assembly { get; }
|
||||
|
||||
public ModuleDefinition StrideCoreModule { get; }
|
||||
|
||||
public List<TypeReference> ReferencedAssemblySerializerFactoryTypes { get; } = [];
|
||||
|
||||
public List<Tuple<string, TypeDefinition, bool>> DataContractAliases { get; } = [];
|
||||
|
||||
/// <summary>
|
||||
/// Gets the list of serializable type grouped by profile.
|
||||
/// </summary>
|
||||
/// <value>
|
||||
/// The serializable types profiles.
|
||||
/// </value>
|
||||
public Dictionary<string, ProfileInfo> SerializableTypesProfiles { get; }
|
||||
|
||||
/// <summary>
|
||||
@@ -52,158 +77,154 @@ internal class CecilSerializerContext
|
||||
public ProfileInfo SerializableTypes { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the list of complex serializers to generate.
|
||||
/// Gets the list of serializers pending code generation.
|
||||
/// Populated during collection, consumed by the code generation phase.
|
||||
/// </summary>
|
||||
/// <value>
|
||||
/// The list of complex serializers to generate.
|
||||
/// </value>
|
||||
public Dictionary<TypeDefinition, SerializableTypeInfo> ComplexTypes { get; }
|
||||
public List<SerializerDescriptor> PendingSerializers { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Members that should be excluded from serialization (e.g. members without valid serializers).
|
||||
/// </summary>
|
||||
public HashSet<IMemberDefinition> IgnoredMembers { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Ensure the following type can be serialized. If not, try to register appropriate serializer.
|
||||
/// This method can be recursive.
|
||||
/// </summary>
|
||||
/// <param name="type">The type.</param>
|
||||
public SerializableTypeInfo GenerateSerializer(TypeReference type, bool force = true, string profile = "Default", bool generic = false)
|
||||
public SerializableTypeInfo ResolveSerializer(TypeReference type, bool force = true, string profile = "Default", bool generic = false)
|
||||
{
|
||||
var serializableTypes = GetSerializableTypes(profile);
|
||||
|
||||
// Already handled?
|
||||
if (serializableTypes.TryGetSerializableTypeInfo(type, generic, out var serializableTypeInfo))
|
||||
return serializableTypeInfo;
|
||||
|
||||
// Try to get one without generic
|
||||
if (generic && serializableTypes.TryGetSerializableTypeInfo(type, false, out serializableTypeInfo))
|
||||
return serializableTypeInfo;
|
||||
|
||||
// TDOO: Array, List, Generic types, etc... (equivalent of previous serializer factories)
|
||||
// Arrays
|
||||
if (type is ArrayType arrayType)
|
||||
{
|
||||
// Only proceed if element type is serializable (and in Default profile, otherwise ElementType is enough)
|
||||
if (GenerateSerializer(arrayType.ElementType, force, profile) != null)
|
||||
{
|
||||
if (profile == "Default")
|
||||
{
|
||||
var arraySerializerType = StrideCoreModule.GetTypeResolved("Stride.Core.Serialization.Serializers.ArraySerializer`1");
|
||||
var serializerType = new GenericInstanceType(arraySerializerType);
|
||||
serializerType.GenericArguments.Add(arrayType.ElementType);
|
||||
AddSerializableType(type, serializableTypeInfo = new SerializableTypeInfo(serializerType, true), profile);
|
||||
return serializableTypeInfo;
|
||||
}
|
||||
else
|
||||
{
|
||||
// Fallback to default
|
||||
return GenerateSerializer(type, force, "Default");
|
||||
}
|
||||
}
|
||||
return ResolveArraySerializer(arrayType, force, profile);
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
// Try to match with existing generic serializer (for List, Dictionary, etc...)
|
||||
// Generic instances (List<T>, Dictionary<K,V>, etc.)
|
||||
if (type is GenericInstanceType genericInstanceType)
|
||||
{
|
||||
var elementType = genericInstanceType.ElementType;
|
||||
SerializableTypeInfo elementSerializableTypeInfo;
|
||||
if ((elementSerializableTypeInfo = GenerateSerializer(elementType, false, profile, true)) != null)
|
||||
{
|
||||
switch (elementSerializableTypeInfo.Mode)
|
||||
{
|
||||
case DataSerializerGenericMode.Type:
|
||||
{
|
||||
var serializerType = new GenericInstanceType(elementSerializableTypeInfo.SerializerType);
|
||||
serializerType.GenericArguments.Add(type);
|
||||
|
||||
AddSerializableType(type, serializableTypeInfo = new SerializableTypeInfo(serializerType, true) { ComplexSerializer = elementSerializableTypeInfo.ComplexSerializer }, profile);
|
||||
break;
|
||||
}
|
||||
case DataSerializerGenericMode.TypeAndGenericArguments:
|
||||
{
|
||||
var serializerType = new GenericInstanceType(elementSerializableTypeInfo.SerializerType);
|
||||
serializerType.GenericArguments.Add(type);
|
||||
foreach (var genericArgument in genericInstanceType.GenericArguments)
|
||||
{
|
||||
// Generate serializer for each generic argument
|
||||
//GenerateSerializer(genericArgument);
|
||||
|
||||
serializerType.GenericArguments.Add(genericArgument);
|
||||
}
|
||||
|
||||
AddSerializableType(type, serializableTypeInfo = new SerializableTypeInfo(serializerType, true) { ComplexSerializer = elementSerializableTypeInfo.ComplexSerializer }, profile);
|
||||
break;
|
||||
}
|
||||
case DataSerializerGenericMode.GenericArguments:
|
||||
{
|
||||
var serializerType = new GenericInstanceType(elementSerializableTypeInfo.SerializerType);
|
||||
foreach (var genericArgument in genericInstanceType.GenericArguments)
|
||||
{
|
||||
// Generate serializer for each generic argument
|
||||
//GenerateSerializer(genericArgument);
|
||||
|
||||
serializerType.GenericArguments.Add(genericArgument);
|
||||
}
|
||||
|
||||
AddSerializableType(type, serializableTypeInfo = new SerializableTypeInfo(serializerType, true) { ComplexSerializer = elementSerializableTypeInfo.ComplexSerializer }, profile);
|
||||
break;
|
||||
}
|
||||
default:
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
|
||||
if (elementSerializableTypeInfo.ComplexSerializer)
|
||||
{
|
||||
ProcessComplexSerializerMembers(type, serializableTypeInfo);
|
||||
}
|
||||
serializableTypeInfo = ResolveGenericSerializer(genericInstanceType, profile);
|
||||
if (serializableTypeInfo != null)
|
||||
return serializableTypeInfo;
|
||||
}
|
||||
}
|
||||
|
||||
// Check complex type definitions
|
||||
if (profile == "Default" && (serializableTypeInfo = FindSerializerInfo(type, generic)) != null)
|
||||
// Check type definitions for serializer info (only in Default profile)
|
||||
if (profile == "Default")
|
||||
{
|
||||
return serializableTypeInfo;
|
||||
serializableTypeInfo = FindSerializerInfo(type, generic);
|
||||
if (serializableTypeInfo != null)
|
||||
return serializableTypeInfo;
|
||||
}
|
||||
|
||||
// Fallback to default
|
||||
// Non-Default profiles fall back to Default
|
||||
if (profile != "Default")
|
||||
return GenerateSerializer(type, force, "Default", generic);
|
||||
return ResolveSerializer(type, force, "Default", generic);
|
||||
|
||||
// Part after that is only if a serializer is absolutely necessary. This is skipped when scanning normal assemblies type that might have nothing to do with serialization.
|
||||
// Past this point, only proceed if a serializer is absolutely necessary.
|
||||
// This is skipped when scanning normal assembly types that might have nothing to do with serialization.
|
||||
if (!force)
|
||||
return null;
|
||||
|
||||
// Non instantiable type? (object, interfaces, abstract classes)
|
||||
// Non-instantiable types (object, interfaces, abstract classes)
|
||||
// Serializer can be null since they will be inherited anyway (handled through MemberSerializer)
|
||||
var resolvedType = type.Resolve();
|
||||
if (resolvedType.IsAbstract || resolvedType.IsInterface || resolvedType.FullName == typeof(object).FullName)
|
||||
{
|
||||
AddSerializableType(type, serializableTypeInfo = new SerializableTypeInfo(null, true), profile);
|
||||
serializableTypeInfo = new SerializableTypeInfo(null, true);
|
||||
AddSerializableType(type, serializableTypeInfo, profile);
|
||||
return serializableTypeInfo;
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
private void ProcessComplexSerializerMembers(TypeReference type, SerializableTypeInfo serializableTypeInfo, string profile = "Default")
|
||||
private SerializableTypeInfo ResolveArraySerializer(ArrayType arrayType, bool force, string profile)
|
||||
{
|
||||
// Process base type (for complex serializers)
|
||||
// Check if we have any serializable closed base type and collect it to pass it over to GenerateSerializerCode later,
|
||||
// who'll ensure the generated serializer for this type calls into its base types' serializer
|
||||
// Only proceed if element type is serializable
|
||||
if (ResolveSerializer(arrayType.ElementType, force, profile) == null)
|
||||
return null;
|
||||
|
||||
// Non-Default profiles fall back to Default for array serializer registration
|
||||
if (profile != "Default")
|
||||
return ResolveSerializer(arrayType, force, "Default");
|
||||
|
||||
var arraySerializerType = StrideCoreModule.GetTypeResolved("Stride.Core.Serialization.Serializers.ArraySerializer`1");
|
||||
var serializerType = new GenericInstanceType(arraySerializerType);
|
||||
serializerType.GenericArguments.Add(arrayType.ElementType);
|
||||
|
||||
var info = new SerializableTypeInfo(serializerType, true);
|
||||
AddSerializableType(arrayType, info, profile);
|
||||
return info;
|
||||
}
|
||||
|
||||
private SerializableTypeInfo ResolveGenericSerializer(GenericInstanceType type, string profile)
|
||||
{
|
||||
// Try to match with existing generic serializer (for List, Dictionary, etc.)
|
||||
var elementInfo = ResolveSerializer(type.ElementType, false, profile, true);
|
||||
if (elementInfo == null)
|
||||
return null;
|
||||
|
||||
var serializerType = InstantiateSerializerType(elementInfo.SerializerType, elementInfo.Mode, type, type.GenericArguments);
|
||||
|
||||
var info = new SerializableTypeInfo(serializerType, true) { IsGeneratedSerializer = elementInfo.IsGeneratedSerializer };
|
||||
AddSerializableType(type, info, profile);
|
||||
|
||||
if (elementInfo.IsGeneratedSerializer)
|
||||
CollectSerializerDependencies(type, info);
|
||||
|
||||
return info;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Constructs a concrete serializer type from an open generic serializer definition
|
||||
/// by adding type arguments according to the <see cref="DataSerializerGenericMode"/>.
|
||||
/// </summary>
|
||||
private static GenericInstanceType InstantiateSerializerType(
|
||||
TypeReference openSerializerType,
|
||||
DataSerializerGenericMode mode,
|
||||
TypeReference dataType,
|
||||
IEnumerable<TypeReference> genericArguments)
|
||||
{
|
||||
var serializerType = new GenericInstanceType(openSerializerType);
|
||||
|
||||
// Add the data type itself as first arg for Type/TypeAndGenericArguments modes
|
||||
if (mode is DataSerializerGenericMode.Type or DataSerializerGenericMode.TypeAndGenericArguments)
|
||||
serializerType.GenericArguments.Add(dataType);
|
||||
|
||||
// Add the data type's generic arguments for GenericArguments/TypeAndGenericArguments modes
|
||||
if (mode is DataSerializerGenericMode.GenericArguments or DataSerializerGenericMode.TypeAndGenericArguments)
|
||||
{
|
||||
foreach (var arg in genericArguments)
|
||||
serializerType.GenericArguments.Add(arg);
|
||||
}
|
||||
|
||||
return serializerType;
|
||||
}
|
||||
|
||||
private void CollectSerializerDependencies(TypeReference type, SerializableTypeInfo serializableTypeInfo, string profile = "Default", SerializerDescriptor? descriptor = null)
|
||||
{
|
||||
// Find the nearest serializable base type so the generated serializer can chain to it
|
||||
for (var baseType = type; (baseType = ResolveGenericsVisitor.Process(baseType, baseType.Resolve().BaseType)) != null;)
|
||||
{
|
||||
if (baseType.ContainsGenericParameter())
|
||||
continue; // ResolveGenericsVisitor failed, the type it returned is not closed, we can't serialize it
|
||||
|
||||
var parentSerializableTypeInfo = GenerateSerializer(baseType, false, profile);
|
||||
if (parentSerializableTypeInfo?.SerializerType != null)
|
||||
var parentSerializableTypeInfo = ResolveSerializer(baseType, false, profile);
|
||||
if (parentSerializableTypeInfo?.SerializerType is not null)
|
||||
{
|
||||
serializableTypeInfo.ComplexSerializerProcessParentType = baseType;
|
||||
if (descriptor is not null)
|
||||
descriptor.SerializedParentType = baseType;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// Process members
|
||||
foreach (var serializableItem in ComplexSerializerRegistry.GetSerializableItems(type, true))
|
||||
// Resolve serializers for all members, ignoring those without valid serializers
|
||||
foreach (var serializableItem in SerializationHelpers.GetSerializableItems(type, true, ignoredMembers: IgnoredMembers))
|
||||
{
|
||||
// Check that all closed types have a proper serializer
|
||||
if (serializableItem.Attributes.Any(x => x.AttributeType.FullName == "Stride.Core.DataMemberCustomSerializerAttribute")
|
||||
@@ -217,9 +238,9 @@ internal class CecilSerializerContext
|
||||
|
||||
try
|
||||
{
|
||||
if (GenerateSerializer(serializableItem.Type, profile: profile) == null)
|
||||
if (ResolveSerializer(serializableItem.Type, profile: profile) == null)
|
||||
{
|
||||
ComplexSerializerRegistry.IgnoreMember(serializableItem.MemberInfo);
|
||||
IgnoredMembers.Add(serializableItem.MemberInfo);
|
||||
if (!isInterface)
|
||||
{
|
||||
log.Write(
|
||||
@@ -232,15 +253,15 @@ internal class CecilSerializerContext
|
||||
throw new InvalidOperationException($"Could not process serialization for member {serializableItem.MemberInfo}", e);
|
||||
}
|
||||
}
|
||||
|
||||
// Cache final serializable items (after ignored members have been updated)
|
||||
if (descriptor is not null)
|
||||
descriptor.SerializableItems = SerializationHelpers.GetSerializableItems(type, true, ignoredMembers: IgnoredMembers).ToArray();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Finds the serializer information.
|
||||
/// Finds the serializer information by inspecting the type's attributes and inheritance chain.
|
||||
/// </summary>
|
||||
/// <param name="type">The type.</param>
|
||||
/// <param name="generic">If set to true, when using <see cref="DataSerializerGenericMode.Type"/>, it will returns the generic version instead of actual type one.</param>
|
||||
/// <returns></returns>
|
||||
/// <exception cref="System.InvalidOperationException">Not sure how to process this inherited serializer</exception>
|
||||
internal SerializableTypeInfo FindSerializerInfo(TypeReference type, bool generic)
|
||||
{
|
||||
if (type == null || type.FullName == typeof(object).FullName || type.FullName == typeof(ValueType).FullName || type.IsGenericParameter)
|
||||
@@ -248,199 +269,138 @@ internal class CecilSerializerContext
|
||||
|
||||
var resolvedType = type.Resolve();
|
||||
|
||||
// Nested type
|
||||
if (resolvedType.IsNested)
|
||||
{
|
||||
// Check public/private flags
|
||||
if (!resolvedType.IsNestedPublic && !resolvedType.IsNestedAssembly)
|
||||
return null;
|
||||
}
|
||||
// Nested type — must be publicly accessible
|
||||
if (resolvedType.IsNested && !resolvedType.IsNestedPublic && !resolvedType.IsNestedAssembly)
|
||||
return null;
|
||||
|
||||
// Enums
|
||||
if (resolvedType.IsEnum)
|
||||
{
|
||||
// Enum
|
||||
// Let's generate a EnumSerializer
|
||||
var enumSerializerType = StrideCoreModule.GetTypeResolved("Stride.Core.Serialization.Serializers.EnumSerializer`1");
|
||||
var serializerType = new GenericInstanceType(enumSerializerType);
|
||||
serializerType.GenericArguments.Add(type);
|
||||
|
||||
var serializableTypeInfo = new SerializableTypeInfo(serializerType, true, DataSerializerGenericMode.None);
|
||||
AddSerializableType(type, serializableTypeInfo);
|
||||
return serializableTypeInfo;
|
||||
var info = new SerializableTypeInfo(serializerType, true, DataSerializerGenericMode.None);
|
||||
AddSerializableType(type, info);
|
||||
return info;
|
||||
}
|
||||
|
||||
// 1. Check if there is a Serializable attribute
|
||||
// Note: Not anymore since we don't want all system types to have unknown complex serializers.
|
||||
//if (((resolvedType.Attributes & TypeAttributes.Serializable) == TypeAttributes.Serializable) || resolvedType.CustomAttributes.Any(x => x.AttributeType.FullName == typeof(SerializableAttribute).FullName))
|
||||
//{
|
||||
// serializerInfo.Serializable = true;
|
||||
// serializerInfo.ComplexSerializer = true;
|
||||
// serializerInfo.ComplexSerializerName = SerializerTypeName(resolvedType);
|
||||
// return serializerInfo;
|
||||
//}
|
||||
|
||||
// 2.1. Check if there is DataSerializerAttribute on this type (if yes, it is serializable, but not a "complex type")
|
||||
var dataSerializerAttribute =
|
||||
resolvedType.CustomAttributes.FirstOrDefault(
|
||||
x => x.AttributeType.FullName == "Stride.Core.Serialization.DataSerializerAttribute");
|
||||
// [DataSerializer] attribute — explicit serializer assignment
|
||||
var dataSerializerAttribute = resolvedType.CustomAttributes
|
||||
.FirstOrDefault(x => x.AttributeType.FullName == "Stride.Core.Serialization.DataSerializerAttribute");
|
||||
if (dataSerializerAttribute != null)
|
||||
return ProcessDataSerializerAttribute(type, dataSerializerAttribute, generic);
|
||||
|
||||
// [DataContract] attribute — collect generated serializer
|
||||
var dataContractAttribute = resolvedType.CustomAttributes
|
||||
.FirstOrDefault(x => x.AttributeType.FullName == "Stride.Core.DataContractAttribute");
|
||||
if (dataContractAttribute != null)
|
||||
{
|
||||
var modeField = dataSerializerAttribute.Fields.FirstOrDefault(x => x.Name == "Mode");
|
||||
var mode = (modeField.Name != null) ? (DataSerializerGenericMode)modeField.Argument.Value : DataSerializerGenericMode.None;
|
||||
|
||||
var dataSerializerType = ((TypeReference)dataSerializerAttribute.ConstructorArguments[0].Value);
|
||||
|
||||
// Reading from custom arguments doesn't have its ValueType properly set
|
||||
dataSerializerType = dataSerializerType.FixupValueType();
|
||||
|
||||
if (mode == DataSerializerGenericMode.Type || (mode == DataSerializerGenericMode.TypeAndGenericArguments && type is GenericInstanceType))
|
||||
{
|
||||
var genericSerializableTypeInfo = new SerializableTypeInfo(dataSerializerType, true, mode) { Inherited = true };
|
||||
AddSerializableType(type, genericSerializableTypeInfo);
|
||||
|
||||
var actualSerializerType = new GenericInstanceType(dataSerializerType);
|
||||
|
||||
// Add Type as generic arguments
|
||||
actualSerializerType.GenericArguments.Add(type);
|
||||
|
||||
// If necessary, add generic arguments too
|
||||
if (mode == DataSerializerGenericMode.TypeAndGenericArguments)
|
||||
{
|
||||
foreach (var genericArgument in ((GenericInstanceType)type).GenericArguments)
|
||||
{
|
||||
actualSerializerType.GenericArguments.Add(genericArgument);
|
||||
}
|
||||
}
|
||||
|
||||
// Special case for GenericMode == DataSerializerGenericMode.Type: we store actual serializer instantiation in SerializerType (alongside the generic version in GenericSerializerType).
|
||||
var serializableTypeInfo = new SerializableTypeInfo(actualSerializerType, true);
|
||||
AddSerializableType(type, serializableTypeInfo);
|
||||
|
||||
if (!generic)
|
||||
return serializableTypeInfo;
|
||||
|
||||
return genericSerializableTypeInfo;
|
||||
}
|
||||
else
|
||||
{
|
||||
var serializableTypeInfo = new SerializableTypeInfo(dataSerializerType, true, mode) { Inherited = false };
|
||||
AddSerializableType(type, serializableTypeInfo);
|
||||
return serializableTypeInfo;
|
||||
}
|
||||
}
|
||||
|
||||
// 2.2. Check if SerializableExtendedAttribute is set on this class, or any of its base class with ApplyHierarchy
|
||||
var serializableExtendedAttribute =
|
||||
resolvedType.CustomAttributes.FirstOrDefault(
|
||||
x => x.AttributeType.FullName == "Stride.Core.DataContractAttribute");
|
||||
if (dataSerializerAttribute == null && serializableExtendedAttribute != null)
|
||||
{
|
||||
// CHeck if ApplyHierarchy is active, otherwise it needs to be the exact type.
|
||||
var inherited = serializableExtendedAttribute.Properties.Where(x => x.Name == "Inherited")
|
||||
var inherited = dataContractAttribute.Properties
|
||||
.Where(x => x.Name == "Inherited")
|
||||
.Select(x => (bool)x.Argument.Value)
|
||||
.FirstOrDefault();
|
||||
|
||||
var serializableTypeInfo = CreateComplexSerializer(type);
|
||||
serializableTypeInfo.Inherited = inherited;
|
||||
|
||||
// Process members
|
||||
ProcessComplexSerializerMembers(type, serializableTypeInfo);
|
||||
|
||||
return serializableTypeInfo;
|
||||
var (info, descriptor) = CollectSerializer(type);
|
||||
info.Inherited = inherited;
|
||||
CollectSerializerDependencies(type, info, descriptor: descriptor);
|
||||
return info;
|
||||
}
|
||||
|
||||
// Check if parent type contains Inherited attribute
|
||||
var parentType = ResolveGenericsVisitor.Process(type, type.Resolve().BaseType);
|
||||
if (parentType != null)
|
||||
{
|
||||
// Generate serializer for parent type
|
||||
var parentSerializableInfoType = GenerateSerializer(parentType.Resolve(), false, generic: true);
|
||||
|
||||
// If Inherited flag is on, we also generate a serializer for this type
|
||||
if (parentSerializableInfoType?.Inherited == true)
|
||||
{
|
||||
if (parentSerializableInfoType.ComplexSerializer)
|
||||
{
|
||||
var serializableTypeInfo = CreateComplexSerializer(type);
|
||||
serializableTypeInfo.Inherited = true;
|
||||
|
||||
// Process members
|
||||
ProcessComplexSerializerMembers(type, serializableTypeInfo);
|
||||
|
||||
return serializableTypeInfo;
|
||||
}
|
||||
else if (parentSerializableInfoType.Mode == DataSerializerGenericMode.Type || parentSerializableInfoType.Mode == DataSerializerGenericMode.TypeAndGenericArguments)
|
||||
{
|
||||
// Register generic version
|
||||
var genericSerializableTypeInfo = new SerializableTypeInfo(parentSerializableInfoType.SerializerType, true, parentSerializableInfoType.Mode);
|
||||
AddSerializableType(type, genericSerializableTypeInfo);
|
||||
|
||||
if (!type.HasGenericParameters)
|
||||
{
|
||||
var actualSerializerType = new GenericInstanceType(parentSerializableInfoType.SerializerType);
|
||||
|
||||
// Add Type as generic arguments
|
||||
actualSerializerType.GenericArguments.Add(type);
|
||||
|
||||
// If necessary, add generic arguments too
|
||||
if (parentSerializableInfoType.Mode == DataSerializerGenericMode.TypeAndGenericArguments)
|
||||
{
|
||||
foreach (var genericArgument in ((GenericInstanceType)parentType).GenericArguments)
|
||||
{
|
||||
actualSerializerType.GenericArguments.Add(genericArgument);
|
||||
}
|
||||
}
|
||||
|
||||
// Register actual type
|
||||
var serializableTypeInfo = new SerializableTypeInfo(actualSerializerType, true);
|
||||
AddSerializableType(type, serializableTypeInfo);
|
||||
|
||||
if (!generic)
|
||||
return serializableTypeInfo;
|
||||
}
|
||||
|
||||
return genericSerializableTypeInfo;
|
||||
}
|
||||
else
|
||||
{
|
||||
throw new InvalidOperationException("Not sure how to process this inherited serializer");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
// Check if parent type has Inherited attribute
|
||||
return FindInheritedSerializerInfo(type, generic);
|
||||
}
|
||||
|
||||
private SerializableTypeInfo CreateComplexSerializer(TypeReference type)
|
||||
private SerializableTypeInfo ProcessDataSerializerAttribute(TypeReference type, CustomAttribute attribute, bool generic)
|
||||
{
|
||||
var modeField = attribute.Fields.FirstOrDefault(x => x.Name == "Mode");
|
||||
var mode = (modeField.Name != null) ? (DataSerializerGenericMode)modeField.Argument.Value : DataSerializerGenericMode.None;
|
||||
var dataSerializerType = ((TypeReference)attribute.ConstructorArguments[0].Value).FixupValueType();
|
||||
|
||||
if (mode is not (DataSerializerGenericMode.Type or DataSerializerGenericMode.TypeAndGenericArguments)
|
||||
|| (mode == DataSerializerGenericMode.TypeAndGenericArguments && type is not GenericInstanceType))
|
||||
{
|
||||
// Simple non-generic or GenericArguments mode
|
||||
var info = new SerializableTypeInfo(dataSerializerType, true, mode) { Inherited = false };
|
||||
AddSerializableType(type, info);
|
||||
return info;
|
||||
}
|
||||
|
||||
// Type or TypeAndGenericArguments mode — register both generic and concrete versions
|
||||
var genericInfo = new SerializableTypeInfo(dataSerializerType, true, mode) { Inherited = true };
|
||||
AddSerializableType(type, genericInfo);
|
||||
|
||||
var genericArguments = type is GenericInstanceType git ? git.GenericArguments : [];
|
||||
var actualSerializerType = InstantiateSerializerType(dataSerializerType, mode, type, genericArguments);
|
||||
|
||||
var concreteInfo = new SerializableTypeInfo(actualSerializerType, true);
|
||||
AddSerializableType(type, concreteInfo);
|
||||
|
||||
return generic ? genericInfo : concreteInfo;
|
||||
}
|
||||
|
||||
private SerializableTypeInfo FindInheritedSerializerInfo(TypeReference type, bool generic)
|
||||
{
|
||||
var parentType = ResolveGenericsVisitor.Process(type, type.Resolve().BaseType);
|
||||
if (parentType == null)
|
||||
return null;
|
||||
|
||||
// Generate serializer for parent type
|
||||
var parentInfo = ResolveSerializer(parentType.Resolve(), false, generic: true);
|
||||
if (parentInfo?.Inherited != true)
|
||||
return null;
|
||||
|
||||
// Parent has a generated serializer — collect one for this type too
|
||||
if (parentInfo.IsGeneratedSerializer)
|
||||
{
|
||||
var (info, descriptor) = CollectSerializer(type);
|
||||
info.Inherited = true;
|
||||
CollectSerializerDependencies(type, info, descriptor: descriptor);
|
||||
return info;
|
||||
}
|
||||
|
||||
// Parent has a Type/TypeAndGenericArguments mode serializer — inherit it
|
||||
if (parentInfo.Mode is DataSerializerGenericMode.Type or DataSerializerGenericMode.TypeAndGenericArguments)
|
||||
{
|
||||
// Register generic version
|
||||
var genericInfo = new SerializableTypeInfo(parentInfo.SerializerType, true, parentInfo.Mode);
|
||||
AddSerializableType(type, genericInfo);
|
||||
|
||||
if (!type.HasGenericParameters)
|
||||
{
|
||||
var genericArguments = parentType is GenericInstanceType git ? git.GenericArguments : [];
|
||||
var actualSerializerType = InstantiateSerializerType(parentInfo.SerializerType, parentInfo.Mode, type, genericArguments);
|
||||
|
||||
var concreteInfo = new SerializableTypeInfo(actualSerializerType, true);
|
||||
AddSerializableType(type, concreteInfo);
|
||||
|
||||
if (!generic)
|
||||
return concreteInfo;
|
||||
}
|
||||
|
||||
return genericInfo;
|
||||
}
|
||||
|
||||
throw new InvalidOperationException("Not sure how to process this inherited serializer");
|
||||
}
|
||||
|
||||
private (SerializableTypeInfo Info, SerializerDescriptor? Descriptor) CollectSerializer(TypeReference type)
|
||||
{
|
||||
var isLocal = type.Resolve().Module.Assembly == Assembly;
|
||||
|
||||
// Create a fake TypeReference (even though it doesn't really exist yet, but at least ConvertCSharp to get its name will work).
|
||||
TypeReference dataSerializerType;
|
||||
var className = ComplexSerializerRegistry.SerializerTypeName(type, false, true);
|
||||
// Create a forward TypeReference for the serializer (the actual TypeDefinition is created later during code generation).
|
||||
var className = SerializationHelpers.SerializerTypeName(type, false, true);
|
||||
if (type.HasGenericParameters)
|
||||
className += "`" + type.GenericParameters.Count;
|
||||
if (isLocal && type is TypeDefinition)
|
||||
{
|
||||
dataSerializerType = new TypeDefinition("Stride.Core.DataSerializers", className,
|
||||
TypeAttributes.AnsiClass | TypeAttributes.AutoClass | TypeAttributes.Sealed |
|
||||
TypeAttributes.BeforeFieldInit |
|
||||
(type.HasGenericParameters ? TypeAttributes.Public : TypeAttributes.NotPublic));
|
||||
|
||||
// TODO: Only if not using Roslyn
|
||||
Assembly.MainModule.Types.Add((TypeDefinition)dataSerializerType);
|
||||
}
|
||||
else
|
||||
{
|
||||
dataSerializerType = new TypeReference("Stride.Core.DataSerializers", className, type.Module, type.Scope);
|
||||
}
|
||||
var dataSerializerType = new TypeReference("Stride.Core.DataSerializers", className, type.Module, isLocal ? Assembly.MainModule : type.Scope);
|
||||
|
||||
var mode = DataSerializerGenericMode.None;
|
||||
if (type.HasGenericParameters)
|
||||
{
|
||||
mode = DataSerializerGenericMode.GenericArguments;
|
||||
|
||||
// Clone generic parameters
|
||||
// Clone generic parameters onto the forward reference
|
||||
foreach (var genericParameter in type.GenericParameters)
|
||||
{
|
||||
var newGenericParameter = new GenericParameter(genericParameter.Name, dataSerializerType)
|
||||
@@ -448,7 +408,6 @@ internal class CecilSerializerContext
|
||||
Attributes = genericParameter.Attributes
|
||||
};
|
||||
|
||||
// Clone type constraints (others will be in Attributes)
|
||||
foreach (var constraint in genericParameter.Constraints)
|
||||
newGenericParameter.Constraints.Add(constraint);
|
||||
|
||||
@@ -456,31 +415,32 @@ internal class CecilSerializerContext
|
||||
}
|
||||
}
|
||||
|
||||
if (dataSerializerType is TypeDefinition dataSerializerTypeDefinition)
|
||||
{
|
||||
// Setup base class
|
||||
var resolvedType = type.Resolve();
|
||||
var useClassDataSerializer = resolvedType.IsClass && !resolvedType.IsValueType && !resolvedType.IsAbstract && !resolvedType.IsInterface && resolvedType.GetEmptyConstructor() != null;
|
||||
var classDataSerializerType = Assembly.GetStrideCoreModule().GetType(useClassDataSerializer ? "Stride.Core.Serialization.ClassDataSerializer`1" : "Stride.Core.Serialization.DataSerializer`1");
|
||||
var parentType = Assembly.MainModule.ImportReference(classDataSerializerType).MakeGenericType(type.MakeGenericType(dataSerializerType.GenericParameters.ToArray<TypeReference>()));
|
||||
//parentType = ResolveGenericsVisitor.Process(serializerType, type.BaseType);
|
||||
dataSerializerTypeDefinition.BaseType = parentType;
|
||||
}
|
||||
|
||||
var serializableTypeInfo = new SerializableTypeInfo(dataSerializerType, true, mode)
|
||||
{
|
||||
Local = type.Resolve().Module.Assembly == Assembly
|
||||
Local = isLocal
|
||||
};
|
||||
AddSerializableType(type, serializableTypeInfo);
|
||||
|
||||
SerializerDescriptor? descriptor = null;
|
||||
if (isLocal && type is TypeDefinition definition)
|
||||
{
|
||||
ComplexTypes.Add(definition, serializableTypeInfo);
|
||||
var resolvedType = type.Resolve();
|
||||
var useClassDataSerializer = resolvedType.IsClass && !resolvedType.IsValueType && !resolvedType.IsAbstract && !resolvedType.IsInterface && resolvedType.GetEmptyConstructor() != null;
|
||||
|
||||
descriptor = new SerializerDescriptor
|
||||
{
|
||||
DataType = definition,
|
||||
SerializerClassName = className,
|
||||
IsPublic = type.HasGenericParameters,
|
||||
UseClassDataSerializer = useClassDataSerializer,
|
||||
SerializableTypeInfo = serializableTypeInfo,
|
||||
};
|
||||
PendingSerializers.Add(descriptor);
|
||||
}
|
||||
|
||||
serializableTypeInfo.ComplexSerializer = true;
|
||||
serializableTypeInfo.IsGeneratedSerializer = true;
|
||||
|
||||
return serializableTypeInfo;
|
||||
return (serializableTypeInfo, descriptor);
|
||||
}
|
||||
|
||||
public void AddSerializableType(TypeReference dataType, SerializableTypeInfo serializableTypeInfo, string profile = "Default")
|
||||
@@ -498,8 +458,7 @@ internal class CecilSerializerContext
|
||||
if (profileInfo.TryGetSerializableTypeInfo(dataType, serializableTypeInfo.Mode != DataSerializerGenericMode.None, out var currentValue))
|
||||
{
|
||||
// TODO: Doesn't work in some generic case
|
||||
if (//currentValue.SerializerType.ConvertCSharp() != serializableTypeInfo.SerializerType.ConvertCSharp() ||
|
||||
currentValue.Mode != serializableTypeInfo.Mode)
|
||||
if (currentValue.Mode != serializableTypeInfo.Mode)
|
||||
throw new InvalidOperationException(string.Format("Incompatible serializer found for same type in different assemblies for {0}", dataType.ConvertCSharp()));
|
||||
return;
|
||||
}
|
||||
@@ -516,37 +475,46 @@ internal class CecilSerializerContext
|
||||
|
||||
profileInfo.AddSerializableTypeInfo(dataType, serializableTypeInfo);
|
||||
|
||||
// Scan and add dependencies (stored in EnumerateGenericInstantiations() functions).
|
||||
if (serializableTypeInfo.Local && serializableTypeInfo.SerializerType != null)
|
||||
{
|
||||
var resolvedSerializerType = serializableTypeInfo.SerializerType.Resolve();
|
||||
if (resolvedSerializerType != null)
|
||||
{
|
||||
var enumerateGenericInstantiationsMethod = resolvedSerializerType.Methods.FirstOrDefault(x => x.Name == "EnumerateGenericInstantiations");
|
||||
if (enumerateGenericInstantiationsMethod != null)
|
||||
{
|
||||
// Detect all ldtoken (attributes would have been better, but unfortunately C# doesn't allow generics in attributes)
|
||||
foreach (var inst in enumerateGenericInstantiationsMethod.Body.Instructions)
|
||||
{
|
||||
if (inst.OpCode.Code == Code.Ldtoken)
|
||||
{
|
||||
var type = (TypeReference)inst.Operand;
|
||||
// Scan and add dependencies (stored in EnumerateGenericInstantiations() methods)
|
||||
ScanSerializerDependencies(dataType, serializableTypeInfo);
|
||||
}
|
||||
|
||||
// Try to "close" generics type with serializer type as a context
|
||||
var dependentType = ResolveGenericsVisitor.Process(serializableTypeInfo.SerializerType, type);
|
||||
if (!dependentType.ContainsGenericParameter())
|
||||
{
|
||||
// Import type so that it becomes local to the assembly
|
||||
// (otherwise SerializableTypeInfo.Local will be false and it won't be instantiated)
|
||||
var importedType = Assembly.MainModule.ImportReference(dependentType);
|
||||
if (GenerateSerializer(importedType) == null)
|
||||
{
|
||||
throw new InvalidOperationException(string.Format("Could not find serializer for generic dependent type {0} when processing {1}", dependentType, dataType));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
/// <summary>
|
||||
/// Scans a serializer type's EnumerateGenericInstantiations method for ldtoken instructions
|
||||
/// to discover dependent types that also need serializers.
|
||||
/// </summary>
|
||||
private void ScanSerializerDependencies(TypeReference dataType, SerializableTypeInfo serializableTypeInfo)
|
||||
{
|
||||
if (!serializableTypeInfo.Local || serializableTypeInfo.SerializerType == null)
|
||||
return;
|
||||
|
||||
var resolvedSerializerType = serializableTypeInfo.SerializerType.Resolve();
|
||||
if (resolvedSerializerType == null)
|
||||
return;
|
||||
|
||||
var enumerateMethod = resolvedSerializerType.Methods.FirstOrDefault(x => x.Name == "EnumerateGenericInstantiations");
|
||||
if (enumerateMethod == null)
|
||||
return;
|
||||
|
||||
// Detect all ldtoken (attributes would have been better, but unfortunately C# doesn't allow generics in attributes)
|
||||
foreach (var inst in enumerateMethod.Body.Instructions)
|
||||
{
|
||||
if (inst.OpCode.Code != Code.Ldtoken)
|
||||
continue;
|
||||
|
||||
var type = (TypeReference)inst.Operand;
|
||||
|
||||
// Try to "close" generics type with serializer type as a context
|
||||
var dependentType = ResolveGenericsVisitor.Process(serializableTypeInfo.SerializerType, type);
|
||||
if (dependentType.ContainsGenericParameter())
|
||||
continue;
|
||||
|
||||
// Import type so that it becomes local to the assembly
|
||||
// (otherwise SerializableTypeInfo.Local will be false and it won't be instantiated)
|
||||
var importedType = Assembly.MainModule.ImportReference(dependentType);
|
||||
if (ResolveSerializer(importedType) == null)
|
||||
{
|
||||
throw new InvalidOperationException(string.Format("Could not find serializer for generic dependent type {0} when processing {1}", dependentType, dataType));
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -578,14 +546,9 @@ internal class CecilSerializerContext
|
||||
public bool Inherited;
|
||||
|
||||
/// <summary>
|
||||
/// True if it's a complex serializer.
|
||||
/// True if the serializer is auto-generated (for [DataContract] types).
|
||||
/// </summary>
|
||||
public bool ComplexSerializer;
|
||||
|
||||
/// <summary>
|
||||
/// Not null if it's a complex serializer and its base class should be serialized too.
|
||||
/// </summary>
|
||||
public TypeReference? ComplexSerializerProcessParentType;
|
||||
public bool IsGeneratedSerializer;
|
||||
|
||||
public SerializableTypeInfo(TypeReference serializerType, bool local, DataSerializerGenericMode mode = DataSerializerGenericMode.None)
|
||||
{
|
||||
@@ -595,6 +558,22 @@ internal class CecilSerializerContext
|
||||
}
|
||||
}
|
||||
|
||||
private static TypeDefinition? GetSerializerFactoryType(AssemblyDefinition referencedAssembly)
|
||||
{
|
||||
var assemblySerializerFactoryAttribute =
|
||||
referencedAssembly.CustomAttributes.FirstOrDefault(
|
||||
x => x.AttributeType.FullName == "Stride.Core.Serialization.AssemblySerializerFactoryAttribute");
|
||||
|
||||
if (assemblySerializerFactoryAttribute == null)
|
||||
return null;
|
||||
|
||||
var typeReference = (TypeReference)assemblySerializerFactoryAttribute.Fields.Single(x => x.Name == "Type").Argument.Value;
|
||||
if (typeReference == null)
|
||||
return null;
|
||||
|
||||
return typeReference.Resolve();
|
||||
}
|
||||
|
||||
public class ProfileInfo
|
||||
{
|
||||
/// <summary>
|
||||
|
||||
+3
-3
@@ -18,7 +18,7 @@ class PropertyKeySerializerProcessor : ICecilSerializerProcessor
|
||||
if (!member.IsStatic || member.IsPrivate)
|
||||
continue;
|
||||
|
||||
if (ComplexSerializerRegistry.IsMemberIgnored(member.CustomAttributes, ComplexTypeSerializerFlags.SerializePublicFields, DataMemberMode.Default))
|
||||
if (SerializationHelpers.IsMemberIgnored(member.CustomAttributes, ComplexTypeSerializerFlags.SerializePublicFields, DataMemberMode.Default))
|
||||
continue;
|
||||
|
||||
if (member.FieldType.Name == "PropertyKey`1"
|
||||
@@ -27,12 +27,12 @@ class PropertyKeySerializerProcessor : ICecilSerializerProcessor
|
||||
|| member.FieldType.Name == "ObjectParameterKey`1"
|
||||
|| member.FieldType.Name == "PermutationParameterKey`1")
|
||||
{
|
||||
context.GenerateSerializer(member.FieldType);
|
||||
context.ResolveSerializer(member.FieldType);
|
||||
|
||||
var genericType = (GenericInstanceType)member.FieldType;
|
||||
|
||||
// Also generate serializer for embedded type
|
||||
context.GenerateSerializer(genericType.GenericArguments[0]);
|
||||
context.ResolveSerializer(genericType.GenericArguments[0]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+4
-4
@@ -25,7 +25,7 @@ class ReferencedAssemblySerializerProcessor : ICecilSerializerProcessor
|
||||
if (!processedAssemblies.Add(assembly))
|
||||
return;
|
||||
|
||||
// TODO: Add a flag for ComplexSerializer and transmit it properly (it needs different kind of analysis)
|
||||
// TODO: Add a flag for generated serializers and transmit it properly (it needs different kind of analysis)
|
||||
|
||||
// Let's recurse over referenced assemblies
|
||||
foreach (var referencedAssemblyName in assembly.MainModule.AssemblyReferences.ToArray())
|
||||
@@ -80,8 +80,8 @@ class ReferencedAssemblySerializerProcessor : ICecilSerializerProcessor
|
||||
|
||||
if (dataSerializerType == null)
|
||||
{
|
||||
// TODO: We should avoid calling GenerateSerializer now just to have the dataSerializerType (we should do so only in a second step)
|
||||
serializableTypeInfo = context.GenerateSerializer(dataType, profile: profile);
|
||||
// TODO: We should avoid calling ResolveSerializer now just to have the dataSerializerType (we should do so only in a second step)
|
||||
serializableTypeInfo = context.ResolveSerializer(dataType, profile: profile);
|
||||
if (serializableTypeInfo == null)
|
||||
throw new InvalidOperationException(string.Format("Can't find serializer for type {0}", dataType));
|
||||
serializableTypeInfo.Local = local;
|
||||
@@ -91,7 +91,7 @@ class ReferencedAssemblySerializerProcessor : ICecilSerializerProcessor
|
||||
else
|
||||
{
|
||||
// Add it to list of serializable types
|
||||
serializableTypeInfo = new CecilSerializerContext.SerializableTypeInfo(dataSerializerType, local, mode) { ExistingLocal = local, Inherited = inherited, ComplexSerializer = complexSerializer };
|
||||
serializableTypeInfo = new CecilSerializerContext.SerializableTypeInfo(dataSerializerType, local, mode) { ExistingLocal = local, Inherited = inherited, IsGeneratedSerializer = complexSerializer };
|
||||
context.AddSerializableType(dataType, serializableTypeInfo, profile);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,51 @@
|
||||
// Copyright (c) .NET Foundation and Contributors (https://dotnetfoundation.org/ & https://stride3d.net) and Silicon Studio Corp. (https://www.siliconstudio.co.jp)
|
||||
// Distributed under the MIT license. See the LICENSE.md file in the project root for more information.
|
||||
|
||||
using Mono.Cecil;
|
||||
|
||||
namespace Stride.Core.AssemblyProcessor.Serializers;
|
||||
|
||||
/// <summary>
|
||||
/// Describes a serializer to be generated. Collected during the discovery phase
|
||||
/// and consumed by the code generation phase to create the actual <see cref="TypeDefinition"/>.
|
||||
/// </summary>
|
||||
internal class SerializerDescriptor
|
||||
{
|
||||
/// <summary>
|
||||
/// The data type being serialized.
|
||||
/// </summary>
|
||||
public required TypeDefinition DataType { get; init; }
|
||||
|
||||
/// <summary>
|
||||
/// The class name for the generated serializer (e.g. "MyTypeSerializer").
|
||||
/// </summary>
|
||||
public required string SerializerClassName { get; init; }
|
||||
|
||||
/// <summary>
|
||||
/// Whether the serializer type should be public (true for generic types).
|
||||
/// </summary>
|
||||
public required bool IsPublic { get; init; }
|
||||
|
||||
/// <summary>
|
||||
/// Whether to use ClassDataSerializer{T} (true) or DataSerializer{T} (false) as the base class.
|
||||
/// </summary>
|
||||
public required bool UseClassDataSerializer { get; init; }
|
||||
|
||||
/// <summary>
|
||||
/// The serializable type info created during collection, which holds the serializer type reference
|
||||
/// and metadata used by the registration/factory phase.
|
||||
/// </summary>
|
||||
public required CecilSerializerContext.SerializableTypeInfo SerializableTypeInfo { get; init; }
|
||||
|
||||
/// <summary>
|
||||
/// Base type whose serializer should be called first (for inheritance chains).
|
||||
/// Set during <see cref="CecilSerializerContext.CollectSerializerDependencies"/>.
|
||||
/// </summary>
|
||||
public TypeReference? SerializedParentType { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// The serializable fields/properties of <see cref="DataType"/>.
|
||||
/// Computed once during collection and reused during code generation.
|
||||
/// </summary>
|
||||
public SerializationHelpers.SerializableItem[] SerializableItems { get; set; }
|
||||
}
|
||||
@@ -72,21 +72,6 @@
|
||||
<Compile Include="..\Stride.Core\Storage\ObjectIdBuilder.cs">
|
||||
<Link>Core\ObjectIdBuilder.cs</Link>
|
||||
</Compile>
|
||||
<Compile Update="AssemblyScanCodeGenerator.cs">
|
||||
<AutoGen>True</AutoGen>
|
||||
<DesignTime>True</DesignTime>
|
||||
<DependentUpon>AssemblyScanCodeGenerator.tt</DependentUpon>
|
||||
</Compile>
|
||||
<Compile Update="ComplexSerializerCodeGenerator.cs">
|
||||
<DependentUpon>ComplexSerializerCodeGenerator.tt</DependentUpon>
|
||||
<AutoGen>True</AutoGen>
|
||||
<DesignTime>True</DesignTime>
|
||||
</Compile>
|
||||
<Compile Update="ComplexClassSerializerGenerator.cs">
|
||||
<DependentUpon>ComplexClassSerializerGenerator.tt</DependentUpon>
|
||||
<AutoGen>True</AutoGen>
|
||||
<DesignTime>True</DesignTime>
|
||||
</Compile>
|
||||
</ItemGroup>
|
||||
|
||||
<!-- Items that have no role in the build process -->
|
||||
|
||||
@@ -14,73 +14,45 @@ internal partial class UpdateEngineProcessor
|
||||
var assembly = context.Assembly;
|
||||
|
||||
// Check "#if IL" directly in the source to easily see what is generated
|
||||
GenerateUpdateEngineHelperCode(assembly);
|
||||
GenerateUpdatableFieldCode(assembly);
|
||||
new UpdatablePropertyCodeGenerator(assembly).GenerateUpdatablePropertyCode();
|
||||
new UpdatableListCodeGenerator(assembly).GenerateUpdatablePropertyCode();
|
||||
}
|
||||
|
||||
private static void GenerateUpdateEngineHelperCode(AssemblyDefinition assembly)
|
||||
{
|
||||
var updateEngineHelperType = assembly.MainModule.GetType("Stride.Updater.UpdateEngineHelper");
|
||||
|
||||
// UpdateEngineHelper.ObjectToPtr
|
||||
var objectToPtr = RewriteBody(updateEngineHelperType.Methods.First(x => x.Name == "ObjectToPtr"));
|
||||
objectToPtr.Emit(OpCodes.Ldarg, objectToPtr.Body.Method.Parameters[0]);
|
||||
objectToPtr.Emit(OpCodes.Conv_I);
|
||||
objectToPtr.Emit(OpCodes.Ret);
|
||||
|
||||
// UpdateEngineHelper.PtrToObject
|
||||
// Simpler "ldarg.0 + ret" doesn't work with Xamarin: https://bugzilla.xamarin.com/show_bug.cgi?id=40608
|
||||
var ptrToObject = RewriteBody(updateEngineHelperType.Methods.First(x => x.Name == "PtrToObject"));
|
||||
ptrToObject.Body.Variables.Add(new VariableDefinition(assembly.MainModule.TypeSystem.Object));
|
||||
ptrToObject.Emit(OpCodes.Ldloca_S, (byte)0);
|
||||
ptrToObject.Emit(OpCodes.Ldarg, ptrToObject.Body.Method.Parameters[0]);
|
||||
|
||||
// Somehow Xamarin forces us to do a roundtrip to an object
|
||||
ptrToObject.Emit(OpCodes.Stind_I);
|
||||
ptrToObject.Emit(OpCodes.Ldloc_0);
|
||||
|
||||
ptrToObject.Emit(OpCodes.Ret);
|
||||
|
||||
// UpdateEngineHelper.Unbox
|
||||
var unbox = RewriteBody(updateEngineHelperType.Methods.First(x => x.Name == "Unbox"));
|
||||
unbox.Emit(OpCodes.Ldarg, unbox.Body.Method.Parameters[0]);
|
||||
unbox.Emit(OpCodes.Unbox, unbox.Body.Method.GenericParameters[0]);
|
||||
unbox.Emit(OpCodes.Ret);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Rewrites UpdatableField methods with raw pointer-based field access.
|
||||
/// </summary>
|
||||
private static void GenerateUpdatableFieldCode(AssemblyDefinition assembly)
|
||||
{
|
||||
var updatableFieldType = assembly.MainModule.GetType("Stride.Updater.UpdatableField");
|
||||
var updatableFieldGenericType = assembly.MainModule.GetType("Stride.Updater.UpdatableField`1");
|
||||
|
||||
// UpdatableField.GetObject
|
||||
var getObject = RewriteBody(updatableFieldType.Methods.First(x => x.Name == "GetObject"));
|
||||
getObject.Emit(OpCodes.Ldarg, getObject.Body.Method.Parameters[0]);
|
||||
getObject.Emit(OpCodes.Ldind_Ref);
|
||||
getObject.Emit(OpCodes.Ret);
|
||||
// Generates: object GetObject(IntPtr ptr) => *(object*)ptr;
|
||||
var getObject = RewriteBody(updatableFieldType.Methods.First(x => x.Name == "GetObject"), assembly);
|
||||
getObject.Emit(OpCodes.Ldarg, getObject.Body.Method.Parameters[0])
|
||||
.Emit(OpCodes.Ldind_Ref)
|
||||
.Emit(OpCodes.Ret);
|
||||
|
||||
// UpdatableField.SetObject
|
||||
var setObject = RewriteBody(updatableFieldType.Methods.First(x => x.Name == "SetObject"));
|
||||
setObject.Emit(OpCodes.Ldarg, setObject.Body.Method.Parameters[0]);
|
||||
setObject.Emit(OpCodes.Ldarg, setObject.Body.Method.Parameters[1]);
|
||||
setObject.Emit(OpCodes.Stind_Ref);
|
||||
setObject.Emit(OpCodes.Ret);
|
||||
// Generates: void SetObject(IntPtr ptr, object value) { *(object*)ptr = value; }
|
||||
var setObject = RewriteBody(updatableFieldType.Methods.First(x => x.Name == "SetObject"), assembly);
|
||||
setObject.Emit(OpCodes.Ldarg, setObject.Body.Method.Parameters[0])
|
||||
.Emit(OpCodes.Ldarg, setObject.Body.Method.Parameters[1])
|
||||
.Emit(OpCodes.Stind_Ref)
|
||||
.Emit(OpCodes.Ret);
|
||||
|
||||
// UpdatableField<T>.SetStruct
|
||||
var setStruct = RewriteBody(updatableFieldGenericType.Methods.First(x => x.Name == "SetStruct"));
|
||||
setStruct.Emit(OpCodes.Ldarg, setStruct.Body.Method.Parameters[0]);
|
||||
setStruct.Emit(OpCodes.Ldarg, setStruct.Body.Method.Parameters[1]);
|
||||
setStruct.Emit(OpCodes.Unbox, updatableFieldGenericType.GenericParameters[0]);
|
||||
setStruct.Emit(OpCodes.Cpobj, updatableFieldGenericType.GenericParameters[0]);
|
||||
setStruct.Emit(OpCodes.Ret);
|
||||
// Generates: void SetStruct(IntPtr ptr, object value) { *(T*)ptr = (T)value; }
|
||||
var setStruct = RewriteBody(updatableFieldGenericType.Methods.First(x => x.Name == "SetStruct"), assembly);
|
||||
setStruct.Emit(OpCodes.Ldarg, setStruct.Body.Method.Parameters[0])
|
||||
.Emit(OpCodes.Ldarg, setStruct.Body.Method.Parameters[1])
|
||||
.Emit(OpCodes.Unbox, updatableFieldGenericType.GenericParameters[0])
|
||||
.Emit(OpCodes.Cpobj, updatableFieldGenericType.GenericParameters[0])
|
||||
.Emit(OpCodes.Ret);
|
||||
}
|
||||
|
||||
private static ILProcessor RewriteBody(MethodDefinition method)
|
||||
private static ILBuilder RewriteBody(MethodDefinition method, AssemblyDefinition assembly)
|
||||
{
|
||||
method.Body = new MethodBody(method);
|
||||
return method.Body.GetILProcessor();
|
||||
return new ILBuilder(method.Body, assembly.MainModule);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -96,50 +68,49 @@ internal partial class UpdateEngineProcessor
|
||||
this.assembly = assembly;
|
||||
}
|
||||
|
||||
public abstract void EmitGetCode(ILProcessor il, TypeReference type);
|
||||
public abstract void EmitGetCode(ILBuilder il, TypeReference type);
|
||||
|
||||
public virtual void EmitSetCodeBeforeValue(ILProcessor il, TypeReference type)
|
||||
public virtual void EmitSetCodeBeforeValue(ILBuilder il, TypeReference type)
|
||||
{
|
||||
}
|
||||
|
||||
public abstract void EmitSetCodeAfterValue(ILProcessor il, TypeReference type);
|
||||
public abstract void EmitSetCodeAfterValue(ILBuilder il, TypeReference type);
|
||||
|
||||
public virtual void GenerateUpdatablePropertyCode()
|
||||
{
|
||||
// UpdatableProperty.GetStructAndUnbox
|
||||
var getStructAndUnbox = RewriteBody(declaringType.Methods.First(x => x.Name == "GetStructAndUnbox"));
|
||||
getStructAndUnbox.Emit(OpCodes.Ldarg, getStructAndUnbox.Body.Method.Parameters[1]);
|
||||
//getStructAndUnbox.Emit(OpCodes.Call, assembly.MainModule.ImportReference(unbox).MakeGenericMethod(declaringType.GenericParameters[0]));
|
||||
getStructAndUnbox.Emit(OpCodes.Unbox, declaringType.GenericParameters[0]);
|
||||
getStructAndUnbox.Emit(OpCodes.Dup);
|
||||
getStructAndUnbox.Emit(OpCodes.Ldarg, getStructAndUnbox.Body.Method.Parameters[0]);
|
||||
// Generates: void GetStructAndUnbox(IntPtr ptr, object obj) { *(T*)Unbox(obj) = Get(ptr); }
|
||||
var getStructAndUnbox = RewriteBody(declaringType.Methods.First(x => x.Name == "GetStructAndUnbox"), assembly);
|
||||
getStructAndUnbox.Emit(OpCodes.Ldarg, getStructAndUnbox.Body.Method.Parameters[1])
|
||||
.Emit(OpCodes.Unbox, declaringType.GenericParameters[0])
|
||||
.Emit(OpCodes.Dup)
|
||||
.Emit(OpCodes.Ldarg, getStructAndUnbox.Body.Method.Parameters[0]);
|
||||
EmitGetCode(getStructAndUnbox, declaringType.GenericParameters[0]);
|
||||
getStructAndUnbox.Emit(OpCodes.Stobj, declaringType.GenericParameters[0]);
|
||||
getStructAndUnbox.Emit(OpCodes.Ret);
|
||||
getStructAndUnbox.Emit(OpCodes.Stobj, declaringType.GenericParameters[0])
|
||||
.Emit(OpCodes.Ret);
|
||||
|
||||
// UpdatableProperty.GetBlittable
|
||||
var getBlittable = RewriteBody(declaringType.Methods.First(x => x.Name == "GetBlittable"));
|
||||
getBlittable.Emit(OpCodes.Ldarg, getBlittable.Body.Method.Parameters[1]);
|
||||
getBlittable.Emit(OpCodes.Ldarg, getBlittable.Body.Method.Parameters[0]);
|
||||
// Generates: void GetBlittable(IntPtr ptr, IntPtr dest) { *(T*)dest = Get(ptr); }
|
||||
var getBlittable = RewriteBody(declaringType.Methods.First(x => x.Name == "GetBlittable"), assembly);
|
||||
getBlittable.Emit(OpCodes.Ldarg, getBlittable.Body.Method.Parameters[1])
|
||||
.Emit(OpCodes.Ldarg, getBlittable.Body.Method.Parameters[0]);
|
||||
EmitGetCode(getBlittable, declaringType.GenericParameters[0]);
|
||||
getBlittable.Emit(OpCodes.Stobj, declaringType.GenericParameters[0]);
|
||||
getBlittable.Emit(OpCodes.Ret);
|
||||
getBlittable.Emit(OpCodes.Stobj, declaringType.GenericParameters[0])
|
||||
.Emit(OpCodes.Ret);
|
||||
|
||||
// UpdatableProperty.SetStruct
|
||||
var setStruct = RewriteBody(declaringType.Methods.First(x => x.Name == "SetStruct"));
|
||||
// Generates: void SetStruct(IntPtr ptr, object value) { Set(ptr, (T)value); }
|
||||
var setStruct = RewriteBody(declaringType.Methods.First(x => x.Name == "SetStruct"), assembly);
|
||||
setStruct.Emit(OpCodes.Ldarg, setStruct.Body.Method.Parameters[0]);
|
||||
EmitSetCodeBeforeValue(setStruct, declaringType.GenericParameters[0]);
|
||||
setStruct.Emit(OpCodes.Ldarg, setStruct.Body.Method.Parameters[1]);
|
||||
setStruct.Emit(OpCodes.Unbox_Any, declaringType.GenericParameters[0]);
|
||||
setStruct.Emit(OpCodes.Ldarg, setStruct.Body.Method.Parameters[1])
|
||||
.Emit(OpCodes.Unbox_Any, declaringType.GenericParameters[0]);
|
||||
EmitSetCodeAfterValue(setStruct, declaringType.GenericParameters[0]);
|
||||
setStruct.Emit(OpCodes.Ret);
|
||||
|
||||
// UpdatableProperty.SetBlittable
|
||||
var setBlittable = RewriteBody(declaringType.Methods.First(x => x.Name == "SetBlittable"));
|
||||
// Generates: void SetBlittable(IntPtr ptr, IntPtr src) { Set(ptr, *(T*)src); }
|
||||
var setBlittable = RewriteBody(declaringType.Methods.First(x => x.Name == "SetBlittable"), assembly);
|
||||
setBlittable.Emit(OpCodes.Ldarg, setBlittable.Body.Method.Parameters[0]);
|
||||
EmitSetCodeBeforeValue(setBlittable, declaringType.GenericParameters[0]);
|
||||
setBlittable.Emit(OpCodes.Ldarg, setBlittable.Body.Method.Parameters[1]);
|
||||
setBlittable.Emit(OpCodes.Ldobj, declaringType.GenericParameters[0]);
|
||||
setBlittable.Emit(OpCodes.Ldarg, setBlittable.Body.Method.Parameters[1])
|
||||
.Emit(OpCodes.Ldobj, declaringType.GenericParameters[0]);
|
||||
EmitSetCodeAfterValue(setBlittable, declaringType.GenericParameters[0]);
|
||||
setBlittable.Emit(OpCodes.Ret);
|
||||
}
|
||||
@@ -167,16 +138,14 @@ internal partial class UpdateEngineProcessor
|
||||
|
||||
public override void GenerateUpdatablePropertyCode()
|
||||
{
|
||||
// For UpdatableProperty, GetObject/SetObject are declared on another type
|
||||
|
||||
// UpdatableProperty.GetObject
|
||||
var getObject = RewriteBody(declaringTypeForObjectMethods.Methods.First(x => x.Name == "GetObject"));
|
||||
// Generates: object GetObject(IntPtr ptr) => Getter(ptr); // via calli
|
||||
var getObject = RewriteBody(declaringTypeForObjectMethods.Methods.First(x => x.Name == "GetObject"), assembly);
|
||||
getObject.Emit(OpCodes.Ldarg, getObject.Body.Method.Parameters[0]);
|
||||
EmitGetCode(getObject, assembly.MainModule.TypeSystem.Object);
|
||||
getObject.Emit(OpCodes.Ret);
|
||||
|
||||
// UpdatableProperty.SetObject
|
||||
var setObject = RewriteBody(declaringTypeForObjectMethods.Methods.First(x => x.Name == "SetObject"));
|
||||
// Generates: void SetObject(IntPtr ptr, object value) { Setter(ptr, value); } // via calli
|
||||
var setObject = RewriteBody(declaringTypeForObjectMethods.Methods.First(x => x.Name == "SetObject"), assembly);
|
||||
setObject.Emit(OpCodes.Ldarg, setObject.Body.Method.Parameters[0]);
|
||||
EmitSetCodeBeforeValue(setObject, assembly.MainModule.TypeSystem.Object);
|
||||
setObject.Emit(OpCodes.Ldarg, setObject.Body.Method.Parameters[1]);
|
||||
@@ -186,48 +155,60 @@ internal partial class UpdateEngineProcessor
|
||||
base.GenerateUpdatablePropertyCode();
|
||||
}
|
||||
|
||||
public override void EmitGetCode(ILProcessor il, TypeReference type)
|
||||
/// <summary>
|
||||
/// Emits getter call via function pointer (calli). Branches on VirtualDispatchGetter:
|
||||
/// <code>
|
||||
/// if (VirtualDispatchGetter)
|
||||
/// return calli(Getter, (IntPtr)obj); // static dispatcher that does ldvirtftn internally
|
||||
/// else
|
||||
/// return calli(Getter, obj); // direct instance call via fn pointer
|
||||
/// </code>
|
||||
/// </summary>
|
||||
public override void EmitGetCode(ILBuilder il, TypeReference type)
|
||||
{
|
||||
var calliInstance = Instruction.Create(OpCodes.Calli, new CallSite(type) { HasThis = true });
|
||||
// Note: .NET 6 doesn't like IntPtr => object implicit conversion so we pretend the method expect a IntPtr rather than object
|
||||
// Note: .NET 6 doesn't like IntPtr => object implicit conversion so we pretend the method expects IntPtr
|
||||
// (another option would be to use "castclass object" after pushing the IntPtr on the stack)
|
||||
var calliVirtualDispatch = Instruction.Create(OpCodes.Calli, new CallSite(type) { HasThis = false, Parameters = { new ParameterDefinition(assembly.MainModule.TypeSystem.IntPtr) } });
|
||||
var postCalli = Instruction.Create(OpCodes.Nop);
|
||||
var postCalli = ILBuilder.DefineLabel();
|
||||
|
||||
il.Emit(OpCodes.Ldarg_0);
|
||||
il.Emit(OpCodes.Ldfld, updatablePropertyGetter);
|
||||
il.Emit(OpCodes.Ldarg_0);
|
||||
// For normal calls, we use ldftn and an instance calls
|
||||
// For virtual and interface calls, we generate a dispatch function that calls ldvirtftn on the actual object, then call the method on the object
|
||||
// this dispatcher method is static, so the calli has a different signature
|
||||
// Note: we could later optimize the bool check by having two variant of Get/SetObject
|
||||
// and two different implementations of both UpdatableProperty<T> and UpdatablePropertyObject<T>
|
||||
// (not sure if worth it)
|
||||
il.Emit(OpCodes.Ldfld, updatablePropertyVirtualDispatchGetter);
|
||||
il.Emit(OpCodes.Brfalse, calliInstance);
|
||||
il.Append(calliVirtualDispatch);
|
||||
il.Emit(OpCodes.Br, postCalli);
|
||||
il.Append(calliInstance);
|
||||
il.Append(postCalli);
|
||||
il.Emit(OpCodes.Ldarg_0)
|
||||
.Emit(OpCodes.Ldfld, updatablePropertyGetter)
|
||||
.Emit(OpCodes.Ldarg_0)
|
||||
// For normal calls, we use ldftn and an instance calli.
|
||||
// For virtual/interface calls, we generate a static dispatch function (via <see cref="CreateDispatcher"/>)
|
||||
// that calls ldvirtftn on the actual object — the calli has a different signature since the dispatcher is static.
|
||||
/// Note: we could later optimize the bool check by having two variants of Get/SetObject
|
||||
// and two different implementations of both UpdatableProperty<T> and UpdatablePropertyObject<T>
|
||||
// (not sure if worth it).
|
||||
.Emit(OpCodes.Ldfld, updatablePropertyVirtualDispatchGetter)
|
||||
.Emit(OpCodes.Brfalse, calliInstance)
|
||||
.Append(calliVirtualDispatch)
|
||||
.Emit(OpCodes.Br, postCalli)
|
||||
.Append(calliInstance)
|
||||
.MarkLabel(postCalli);
|
||||
}
|
||||
|
||||
public override void EmitSetCodeAfterValue(ILProcessor il, TypeReference type)
|
||||
/// <summary>
|
||||
/// Emits setter call via function pointer (calli), same branching pattern as getter.
|
||||
/// </summary>
|
||||
public override void EmitSetCodeAfterValue(ILBuilder il, TypeReference type)
|
||||
{
|
||||
var calliInstance = Instruction.Create(OpCodes.Calli, new CallSite(assembly.MainModule.TypeSystem.Void) { HasThis = true, Parameters = { new ParameterDefinition(type) } });
|
||||
// Note: .NET 6 doesn't like IntPtr => object implicit conversion so we pretend the method expect a IntPtr rather than object
|
||||
// Note: .NET 6 doesn't like IntPtr => object implicit conversion so we pretend the method expects IntPtr
|
||||
// (another option would be to use "castclass object" after pushing the IntPtr on the stack)
|
||||
var calliVirtualDispatch = Instruction.Create(OpCodes.Calli, new CallSite(assembly.MainModule.TypeSystem.Void) { HasThis = false, Parameters = { new ParameterDefinition(assembly.MainModule.TypeSystem.IntPtr), new ParameterDefinition(type) } });
|
||||
var postCalli = Instruction.Create(OpCodes.Nop);
|
||||
var postCalli = ILBuilder.DefineLabel();
|
||||
|
||||
il.Emit(OpCodes.Ldarg_0);
|
||||
il.Emit(OpCodes.Ldfld, updatablePropertySetter);
|
||||
il.Emit(OpCodes.Ldarg_0);
|
||||
il.Emit(OpCodes.Ldfld, updatablePropertyVirtualDispatchSetter);
|
||||
il.Emit(OpCodes.Brfalse, calliInstance);
|
||||
il.Append(calliVirtualDispatch);
|
||||
il.Emit(OpCodes.Br, postCalli);
|
||||
il.Append(calliInstance);
|
||||
il.Append(postCalli);
|
||||
il.Emit(OpCodes.Ldarg_0)
|
||||
.Emit(OpCodes.Ldfld, updatablePropertySetter)
|
||||
.Emit(OpCodes.Ldarg_0)
|
||||
.Emit(OpCodes.Ldfld, updatablePropertyVirtualDispatchSetter)
|
||||
.Emit(OpCodes.Brfalse, calliInstance)
|
||||
.Append(calliVirtualDispatch)
|
||||
.Emit(OpCodes.Br, postCalli)
|
||||
.Append(calliInstance)
|
||||
.MarkLabel(postCalli);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -239,21 +220,19 @@ internal partial class UpdateEngineProcessor
|
||||
|
||||
public override void GenerateUpdatablePropertyCode()
|
||||
{
|
||||
// For UpdatableCustomAccessor, GetObject/SetObject are declared in the generic type
|
||||
|
||||
// UpdatableProperty.GetObject
|
||||
var getObject = RewriteBody(declaringType.Methods.First(x => x.Name == "GetObject"));
|
||||
// Generates: object GetObject(IntPtr ptr) => (object)Get(ptr);
|
||||
var getObject = RewriteBody(declaringType.Methods.First(x => x.Name == "GetObject"), assembly);
|
||||
getObject.Emit(OpCodes.Ldarg, getObject.Body.Method.Parameters[0]);
|
||||
EmitGetCode(getObject, declaringType.GenericParameters[0]);
|
||||
getObject.Emit(OpCodes.Box, declaringType.GenericParameters[0]); // Required for Windows 10 AOT
|
||||
getObject.Emit(OpCodes.Ret);
|
||||
getObject.Emit(OpCodes.Box, declaringType.GenericParameters[0]) // Required for Windows 10 AOT
|
||||
.Emit(OpCodes.Ret);
|
||||
|
||||
// UpdatableProperty.SetObject
|
||||
var setObject = RewriteBody(declaringType.Methods.First(x => x.Name == "SetObject"));
|
||||
// Generates: void SetObject(IntPtr ptr, object value) { Set(ptr, (T)value); }
|
||||
var setObject = RewriteBody(declaringType.Methods.First(x => x.Name == "SetObject"), assembly);
|
||||
setObject.Emit(OpCodes.Ldarg, setObject.Body.Method.Parameters[0]);
|
||||
EmitSetCodeBeforeValue(setObject, declaringType.GenericParameters[0]);
|
||||
setObject.Emit(OpCodes.Ldarg, setObject.Body.Method.Parameters[1]);
|
||||
setObject.Emit(OpCodes.Unbox_Any, declaringType.GenericParameters[0]); // Required for Windows 10 AOT
|
||||
setObject.Emit(OpCodes.Ldarg, setObject.Body.Method.Parameters[1])
|
||||
.Emit(OpCodes.Unbox_Any, declaringType.GenericParameters[0]); // Required for Windows 10 AOT
|
||||
EmitSetCodeAfterValue(setObject, declaringType.GenericParameters[0]);
|
||||
setObject.Emit(OpCodes.Ret);
|
||||
|
||||
@@ -261,6 +240,11 @@ internal partial class UpdateEngineProcessor
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Generates list accessor methods that delegate to <c>IList<T>[Index]</c>.
|
||||
/// Get: <c>return ((IList<T>)obj)[this.Index];</c>
|
||||
/// Set: <c>((IList<T>)obj)[this.Index] = value;</c>
|
||||
/// </summary>
|
||||
class UpdatableListCodeGenerator : UpdatableCustomPropertyCodeGenerator
|
||||
{
|
||||
private readonly FieldDefinition indexField;
|
||||
@@ -282,20 +266,20 @@ internal partial class UpdateEngineProcessor
|
||||
ilistSetItem = assembly.MainModule.ImportReference(ilistItem.SetMethod).MakeGeneric(declaringType.GenericParameters[0]);
|
||||
}
|
||||
|
||||
public override void EmitGetCode(ILProcessor il, TypeReference type)
|
||||
public override void EmitGetCode(ILBuilder il, TypeReference type)
|
||||
{
|
||||
il.Emit(OpCodes.Ldarg_0);
|
||||
il.Emit(OpCodes.Ldfld, indexField);
|
||||
il.Emit(OpCodes.Callvirt, ilistGetItem);
|
||||
il.Emit(OpCodes.Ldarg_0)
|
||||
.Emit(OpCodes.Ldfld, indexField)
|
||||
.Emit(OpCodes.Callvirt, ilistGetItem);
|
||||
}
|
||||
|
||||
public override void EmitSetCodeBeforeValue(ILProcessor il, TypeReference type)
|
||||
public override void EmitSetCodeBeforeValue(ILBuilder il, TypeReference type)
|
||||
{
|
||||
il.Emit(OpCodes.Ldarg_0);
|
||||
il.Emit(OpCodes.Ldfld, indexField);
|
||||
il.Emit(OpCodes.Ldarg_0)
|
||||
.Emit(OpCodes.Ldfld, indexField);
|
||||
}
|
||||
|
||||
public override void EmitSetCodeAfterValue(ILProcessor il, TypeReference type)
|
||||
public override void EmitSetCodeAfterValue(ILBuilder il, TypeReference type)
|
||||
{
|
||||
il.Emit(OpCodes.Callvirt, ilistSetItem);
|
||||
}
|
||||
|
||||
@@ -41,22 +41,37 @@ internal partial class UpdateEngineProcessor : ICecilSerializerProcessor
|
||||
// Obtain the static constructor of <Module> and the return instruction
|
||||
var moduleConstructor = assembly.OpenModuleConstructor(out var returnInstruction);
|
||||
|
||||
// Get the IL processor of the module constructor
|
||||
var il = moduleConstructor.Body.GetILProcessor();
|
||||
// Get the IL processor of the module constructor (used only for InsertBefore)
|
||||
var moduleCtorIL = moduleConstructor.Body.GetILProcessor();
|
||||
|
||||
// Create the call to Initialize method
|
||||
var initializeMethodReference = assembly.MainModule.ImportReference(mainPrepareMethod);
|
||||
var callInitializeInstruction = il.Create(OpCodes.Call, initializeMethodReference);
|
||||
il.InsertBefore(moduleConstructor.Body.Instructions.Last(), callInitializeInstruction);
|
||||
var callInitializeInstruction = moduleCtorIL.Create(OpCodes.Call, initializeMethodReference);
|
||||
moduleCtorIL.InsertBefore(moduleConstructor.Body.Instructions.Last(), callInitializeInstruction);
|
||||
|
||||
return mainPrepareMethod;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Creates a static dispatcher method for virtual/interface property access via <c>ldvirtftn</c>/<c>calli</c>.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// Generates code equivalent to:
|
||||
/// <code>
|
||||
/// static TReturn Dispatcher_get_Property(object @this /*, params... */)
|
||||
/// {
|
||||
/// return @this./* virtual call via ldvirtftn+calli */get_Property(/* params... */);
|
||||
/// }
|
||||
/// </code>
|
||||
/// This is needed because <c>ldftn</c> of a virtual method gives the base slot, not the override.
|
||||
/// The dispatcher uses <c>ldvirtftn</c> to resolve the actual vtable entry at runtime.
|
||||
/// </remarks>
|
||||
private MethodReference CreateDispatcher(AssemblyDefinition assembly, MethodReference method)
|
||||
{
|
||||
var updateEngineType = GetOrCreateUpdateType(assembly, true);
|
||||
var module = assembly.MainModule;
|
||||
|
||||
var dispatcherMethod = new MethodDefinition($"Dispatcher_{method.Name}", MethodAttributes.HideBySig | MethodAttributes.Assembly | MethodAttributes.Static, assembly.MainModule.ImportReference(method.ReturnType));
|
||||
var dispatcherMethod = new MethodDefinition($"Dispatcher_{method.Name}", MethodAttributes.HideBySig | MethodAttributes.Assembly | MethodAttributes.Static, module.ImportReference(method.ReturnType));
|
||||
updateEngineType.Methods.Add(dispatcherMethod);
|
||||
|
||||
dispatcherMethod.Parameters.Add(new ParameterDefinition("this", ParameterAttributes.None, method.DeclaringType));
|
||||
@@ -65,19 +80,22 @@ internal partial class UpdateEngineProcessor : ICecilSerializerProcessor
|
||||
dispatcherMethod.Parameters.Add(new ParameterDefinition(param.Name, param.Attributes, param.ParameterType));
|
||||
}
|
||||
|
||||
var il = dispatcherMethod.Body.GetILProcessor();
|
||||
// Emit: load all args, then resolve virtual method and call via calli
|
||||
var il = new ILBuilder(dispatcherMethod.Body, module);
|
||||
il.Emit(OpCodes.Ldarg_0);
|
||||
foreach (var param in dispatcherMethod.Parameters.Skip(1)) // first parameter is "this"
|
||||
// note: first parameter is "this"
|
||||
foreach (var param in dispatcherMethod.Parameters.Skip(1))
|
||||
{
|
||||
il.Emit(OpCodes.Ldarg, param);
|
||||
}
|
||||
il.Emit(OpCodes.Ldarg_0);
|
||||
il.Emit(OpCodes.Ldvirtftn, method);
|
||||
var callsite = new Mono.Cecil.CallSite(method.ReturnType) { HasThis = true };
|
||||
foreach (var param in method.Parameters)
|
||||
callsite.Parameters.Add(param);
|
||||
il.Emit(OpCodes.Calli, callsite);
|
||||
il.Emit(OpCodes.Ret);
|
||||
// Emit: @this.ldvirtftn(method) then calli — resolves the actual override at runtime
|
||||
il.Emit(OpCodes.Ldarg_0)
|
||||
.Emit(OpCodes.Ldvirtftn, method)
|
||||
.Emit(OpCodes.Calli, callsite)
|
||||
.Emit(OpCodes.Ret);
|
||||
|
||||
return dispatcherMethod;
|
||||
}
|
||||
@@ -88,6 +106,7 @@ internal partial class UpdateEngineProcessor : ICecilSerializerProcessor
|
||||
EnumerateReferences(references, context.Assembly);
|
||||
|
||||
var coreAssembly = CecilExtensions.FindCorlibAssembly(context.Assembly);
|
||||
var module = context.Assembly.MainModule;
|
||||
|
||||
// Only process assemblies depending on Stride.Engine
|
||||
if (!references.Any(x => x.Name.Name == "Stride.Engine"))
|
||||
@@ -97,12 +116,12 @@ internal partial class UpdateEngineProcessor : ICecilSerializerProcessor
|
||||
var serializationAssemblyName = "Stride.Engine.Serializers";
|
||||
|
||||
// Add [InteralsVisibleTo] attribute
|
||||
var internalsVisibleToAttributeCtor = context.Assembly.MainModule.ImportReference(internalsVisibleToAttribute.GetConstructors().Single());
|
||||
var internalsVisibleToAttributeCtor = module.ImportReference(internalsVisibleToAttribute.GetConstructors().Single());
|
||||
var internalsVisibleAttribute = new CustomAttribute(internalsVisibleToAttributeCtor)
|
||||
{
|
||||
ConstructorArguments =
|
||||
{
|
||||
new CustomAttributeArgument(context.Assembly.MainModule.ImportReference(context.Assembly.MainModule.TypeSystem.String), serializationAssemblyName)
|
||||
new CustomAttributeArgument(module.ImportReference(module.TypeSystem.String), serializationAssemblyName)
|
||||
}
|
||||
};
|
||||
context.Assembly.CustomAttributes.Add(internalsVisibleAttribute);
|
||||
@@ -112,7 +131,7 @@ internal partial class UpdateEngineProcessor : ICecilSerializerProcessor
|
||||
|
||||
var strideEngineAssembly = context.Assembly.Name.Name == "Stride.Engine"
|
||||
? context.Assembly
|
||||
: context.Assembly.MainModule.AssemblyResolver.Resolve(new AssemblyNameReference("Stride.Engine", null));
|
||||
: module.AssemblyResolver.Resolve(new AssemblyNameReference("Stride.Engine", null));
|
||||
var strideEngineModule = strideEngineAssembly.MainModule;
|
||||
|
||||
// Generate IL for Stride.Core
|
||||
@@ -140,14 +159,14 @@ internal partial class UpdateEngineProcessor : ICecilSerializerProcessor
|
||||
parameterCollectionResolverInstantiateValueAccessor = parameterCollectionResolver.Methods.First(x => x.Name == "InstantiateValueAccessor");
|
||||
|
||||
var registerMemberMethod = strideEngineModule.GetType("Stride.Updater.UpdateEngine").Methods.First(x => x.Name == "RegisterMember");
|
||||
updateEngineRegisterMemberMethod = context.Assembly.MainModule.ImportReference(registerMemberMethod);
|
||||
updateEngineRegisterMemberMethod = module.ImportReference(registerMemberMethod);
|
||||
|
||||
var registerMemberResolverMethod = strideEngineModule.GetType("Stride.Updater.UpdateEngine").Methods.First(x => x.Name == "RegisterMemberResolver");
|
||||
//pclVisitor.VisitMethod(registerMemberResolverMethod);
|
||||
updateEngineRegisterMemberResolverMethod = context.Assembly.MainModule.ImportReference(registerMemberResolverMethod);
|
||||
updateEngineRegisterMemberResolverMethod = module.ImportReference(registerMemberResolverMethod);
|
||||
|
||||
var typeType = coreAssembly.MainModule.GetTypeResolved(typeof(Type).FullName);
|
||||
getTypeFromHandleMethod = context.Assembly.MainModule.ImportReference(typeType.Methods.First(x => x.Name == "GetTypeFromHandle"));
|
||||
getTypeFromHandleMethod = module.ImportReference(typeType.Methods.First(x => x.Name == "GetTypeFromHandle"));
|
||||
|
||||
var mainPrepareMethod = CreateUpdateMethod(context.Assembly);
|
||||
|
||||
@@ -168,7 +187,7 @@ internal partial class UpdateEngineProcessor : ICecilSerializerProcessor
|
||||
|
||||
try
|
||||
{
|
||||
ProcessType(context, context.Assembly.MainModule.ImportReference(typeDefinition), mainPrepareMethod);
|
||||
ProcessType(context, module.ImportReference(typeDefinition), mainPrepareMethod);
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
@@ -176,15 +195,20 @@ internal partial class UpdateEngineProcessor : ICecilSerializerProcessor
|
||||
}
|
||||
}
|
||||
|
||||
// Force generic instantiations
|
||||
var il = mainPrepareMethod.Body.GetILProcessor();
|
||||
// Force generic instantiations — register resolvers for lists, arrays, and trigger generic update methods
|
||||
// Generates calls like:
|
||||
// UpdateEngine.RegisterMemberResolver(new ListUpdateResolver<ElementType>());
|
||||
// UpdateEngine.RegisterMemberResolver(new ArrayUpdateResolver<ElementType>());
|
||||
// ParameterCollectionResolver.InstantiateValueAccessor<KeyType>(); // iOS AOT only
|
||||
// UpdateGeneric_TypeName<T1, T2>(); // for closed generic types
|
||||
var il = new ILBuilder(mainPrepareMethod.Body, module);
|
||||
foreach (var serializableType in context.SerializableTypesProfiles.SelectMany(x => x.Value.SerializableTypes).ToArray())
|
||||
{
|
||||
// Special case: when processing Stride.Engine assembly, we automatically add dependent assemblies types too
|
||||
if (!serializableType.Value.Local && strideEngineAssembly != context.Assembly)
|
||||
continue;
|
||||
|
||||
// Try to find if original method definition was generated
|
||||
// Try to find if original method definition was generated
|
||||
var typeDefinition = serializableType.Key.Resolve();
|
||||
|
||||
// If using List<T>, register this type in UpdateEngine
|
||||
@@ -196,8 +220,8 @@ internal partial class UpdateEngineProcessor : ICecilSerializerProcessor
|
||||
{
|
||||
//call Updater.UpdateEngine.RegisterMemberResolver(new Updater.ListUpdateResolver<T>());
|
||||
var elementType = ResolveGenericsVisitor.Process(serializableType.Key, listInterfaceType.GenericArguments[0]);
|
||||
il.Emit(OpCodes.Newobj, context.Assembly.MainModule.ImportReference(updatableListUpdateResolverGenericCtor).MakeGeneric(context.Assembly.MainModule.ImportReference(elementType)));
|
||||
il.Emit(OpCodes.Call, updateEngineRegisterMemberResolverMethod);
|
||||
il.Emit(OpCodes.Newobj, il.Import(updatableListUpdateResolverGenericCtor).MakeGeneric(il.Import(elementType)))
|
||||
.Emit(OpCodes.Call, updateEngineRegisterMemberResolverMethod);
|
||||
}
|
||||
|
||||
parentTypeDefinition = parentTypeDefinition.BaseType?.Resolve();
|
||||
@@ -208,15 +232,15 @@ internal partial class UpdateEngineProcessor : ICecilSerializerProcessor
|
||||
{
|
||||
//call Updater.UpdateEngine.RegisterMemberResolver(new Updater.ArrayUpdateResolver<T>());
|
||||
var elementType = ResolveGenericsVisitor.Process(serializableType.Key, arrayType.ElementType);
|
||||
il.Emit(OpCodes.Newobj, context.Assembly.MainModule.ImportReference(updatableArrayUpdateResolverGenericCtor).MakeGeneric(context.Assembly.MainModule.ImportReference(elementType)));
|
||||
il.Emit(OpCodes.Call, updateEngineRegisterMemberResolverMethod);
|
||||
il.Emit(OpCodes.Newobj, il.Import(updatableArrayUpdateResolverGenericCtor).MakeGeneric(il.Import(elementType)))
|
||||
.Emit(OpCodes.Call, updateEngineRegisterMemberResolverMethod);
|
||||
}
|
||||
|
||||
// Generic instantiation for AOT platforms
|
||||
if (context.Platform == Core.PlatformType.iOS && serializableType.Key.Name == "ValueParameterKey`1")
|
||||
{
|
||||
var keyType = ((GenericInstanceType)serializableType.Key).GenericArguments[0];
|
||||
il.Emit(OpCodes.Call, context.Assembly.MainModule.ImportReference(parameterCollectionResolverInstantiateValueAccessor).MakeGenericMethod(context.Assembly.MainModule.ImportReference(keyType)));
|
||||
il.Emit(OpCodes.Call, il.Import(parameterCollectionResolverInstantiateValueAccessor).MakeGenericMethod(il.Import(keyType)));
|
||||
}
|
||||
|
||||
if (serializableType.Key is GenericInstanceType genericInstanceType)
|
||||
@@ -233,9 +257,9 @@ internal partial class UpdateEngineProcessor : ICecilSerializerProcessor
|
||||
if (updateMethod != null)
|
||||
{
|
||||
// Emit call to update engine setup method with generic arguments of current type
|
||||
il.Emit(OpCodes.Call, context.Assembly.MainModule.ImportReference(updateMethod)
|
||||
il.Emit(OpCodes.Call, il.Import(updateMethod)
|
||||
.MakeGenericMethod(genericInstanceType.GenericArguments
|
||||
.Select(context.Assembly.MainModule.ImportReference)
|
||||
.Select(x => module.ImportReference(x))
|
||||
.ToArray()));
|
||||
}
|
||||
}
|
||||
@@ -244,6 +268,11 @@ internal partial class UpdateEngineProcessor : ICecilSerializerProcessor
|
||||
il.Emit(OpCodes.Ret);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Registers all updatable fields and properties of a type with the update engine.
|
||||
/// For generic types, creates a separate <c>UpdateGeneric_TypeName<T>()</c> method
|
||||
/// that can be instantiated per closed generic type.
|
||||
/// </summary>
|
||||
public void ProcessType(CecilSerializerContext context, TypeReference type, MethodDefinition updateMainMethod)
|
||||
{
|
||||
var typeDefinition = type.Resolve();
|
||||
@@ -252,14 +281,14 @@ internal partial class UpdateEngineProcessor : ICecilSerializerProcessor
|
||||
if (typeDefinition.IsEnum)
|
||||
return;
|
||||
|
||||
var module = context.Assembly.MainModule;
|
||||
var updateCurrentMethod = updateMainMethod;
|
||||
ResolveGenericsVisitor replaceGenericsVisitor = null;
|
||||
|
||||
if (typeDefinition.HasGenericParameters)
|
||||
{
|
||||
// Make a prepare method for just this object since it might need multiple instantiation
|
||||
updateCurrentMethod = new MethodDefinition(ComputeUpdateMethodName(typeDefinition), MethodAttributes.HideBySig | MethodAttributes.Public | MethodAttributes.Static, context.Assembly.MainModule.TypeSystem.Void);
|
||||
var genericsMapping = new Dictionary<TypeReference, TypeReference>();
|
||||
updateCurrentMethod = new MethodDefinition(ComputeUpdateMethodName(typeDefinition), MethodAttributes.HideBySig | MethodAttributes.Public | MethodAttributes.Static, module.TypeSystem.Void);
|
||||
foreach (var genericParameter in typeDefinition.GenericParameters)
|
||||
{
|
||||
var genericParameterCopy = new GenericParameter(genericParameter.Name, updateCurrentMethod)
|
||||
@@ -267,125 +296,28 @@ internal partial class UpdateEngineProcessor : ICecilSerializerProcessor
|
||||
Attributes = genericParameter.Attributes,
|
||||
};
|
||||
foreach (var constraint in genericParameter.Constraints)
|
||||
genericParameterCopy.Constraints.Add(new GenericParameterConstraint(context.Assembly.MainModule.ImportReference(constraint.ConstraintType)));
|
||||
genericParameterCopy.Constraints.Add(new GenericParameterConstraint(module.ImportReference(constraint.ConstraintType)));
|
||||
updateCurrentMethod.GenericParameters.Add(genericParameterCopy);
|
||||
|
||||
genericsMapping[genericParameter] = genericParameterCopy;
|
||||
}
|
||||
|
||||
replaceGenericsVisitor = new ResolveGenericsVisitor(genericsMapping);
|
||||
replaceGenericsVisitor = ResolveGenericsVisitor.FromMapping(typeDefinition, updateCurrentMethod);
|
||||
|
||||
updateMainMethod.DeclaringType.Methods.Add(updateCurrentMethod);
|
||||
}
|
||||
|
||||
var il = updateCurrentMethod.Body.GetILProcessor();
|
||||
var il = new ILBuilder(updateCurrentMethod.Body, module);
|
||||
var typeIsValueType = type.IsResolvedValueType();
|
||||
var emptyObjectField = typeIsValueType ? null : updateMainMethod.DeclaringType.Fields.FirstOrDefault(x => x.Name == "emptyObject");
|
||||
VariableDefinition emptyStruct = null;
|
||||
|
||||
// Note: forcing fields and properties to be processed in all cases
|
||||
foreach (var serializableItem in ComplexSerializerRegistry.GetSerializableItems(type, true, ComplexTypeSerializerFlags.SerializePublicFields | ComplexTypeSerializerFlags.SerializePublicProperties | ComplexTypeSerializerFlags.Updatable))
|
||||
foreach (var serializableItem in SerializationHelpers.GetSerializableItems(type, true, ComplexTypeSerializerFlags.SerializePublicFields | ComplexTypeSerializerFlags.SerializePublicProperties | ComplexTypeSerializerFlags.Updatable, context.IgnoredMembers))
|
||||
{
|
||||
if (serializableItem.MemberInfo is FieldReference fieldReference)
|
||||
{
|
||||
var field = fieldReference.Resolve();
|
||||
|
||||
// First time it is needed, let's create empty object in the class (var emptyObject = new object()) or empty local struct in the method
|
||||
if (typeIsValueType)
|
||||
{
|
||||
if (emptyStruct == null)
|
||||
{
|
||||
emptyStruct = new VariableDefinition(type);
|
||||
updateMainMethod.Body.Variables.Add(emptyStruct);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if (emptyObjectField == null)
|
||||
{
|
||||
emptyObjectField = new FieldDefinition("emptyObject", FieldAttributes.Static | FieldAttributes.Private, context.Assembly.MainModule.TypeSystem.Object);
|
||||
|
||||
// Create static ctor that will initialize this object
|
||||
var staticConstructor = new MethodDefinition(".cctor",
|
||||
MethodAttributes.Private | MethodAttributes.HideBySig | MethodAttributes.Static | MethodAttributes.SpecialName | MethodAttributes.RTSpecialName,
|
||||
context.Assembly.MainModule.TypeSystem.Void);
|
||||
var staticConstructorIL = staticConstructor.Body.GetILProcessor();
|
||||
staticConstructorIL.Emit(OpCodes.Newobj, context.Assembly.MainModule.ImportReference(emptyObjectField.FieldType.Resolve().GetConstructors().Single(x => !x.IsStatic && !x.HasParameters)));
|
||||
staticConstructorIL.Emit(OpCodes.Stsfld, emptyObjectField);
|
||||
staticConstructorIL.Emit(OpCodes.Ret);
|
||||
|
||||
updateMainMethod.DeclaringType.Fields.Add(emptyObjectField);
|
||||
updateMainMethod.DeclaringType.Methods.Add(staticConstructor);
|
||||
}
|
||||
}
|
||||
|
||||
il.Emit(OpCodes.Ldtoken, type);
|
||||
il.Emit(OpCodes.Call, getTypeFromHandleMethod);
|
||||
il.Emit(OpCodes.Ldstr, field.Name);
|
||||
|
||||
if (typeIsValueType)
|
||||
il.Emit(OpCodes.Ldloca, emptyStruct);
|
||||
else
|
||||
il.Emit(OpCodes.Ldsfld, emptyObjectField);
|
||||
il.Emit(OpCodes.Ldflda, context.Assembly.MainModule.ImportReference(fieldReference));
|
||||
il.Emit(OpCodes.Conv_I);
|
||||
if (typeIsValueType)
|
||||
il.Emit(OpCodes.Ldloca, emptyStruct);
|
||||
else
|
||||
il.Emit(OpCodes.Ldsfld, emptyObjectField);
|
||||
il.Emit(OpCodes.Conv_I);
|
||||
il.Emit(OpCodes.Sub);
|
||||
il.Emit(OpCodes.Conv_I4);
|
||||
|
||||
var fieldType = context.Assembly.MainModule.ImportReference(replaceGenericsVisitor != null ? replaceGenericsVisitor.VisitDynamic(field.FieldType) : field.FieldType);
|
||||
il.Emit(OpCodes.Newobj, context.Assembly.MainModule.ImportReference(updatableFieldGenericCtor).MakeGeneric(fieldType));
|
||||
il.Emit(OpCodes.Call, updateEngineRegisterMemberMethod);
|
||||
}
|
||||
EmitFieldRegistration(il, fieldReference, type, typeIsValueType, replaceGenericsVisitor, module, updateMainMethod, ref emptyObjectField, ref emptyStruct);
|
||||
|
||||
if (serializableItem.MemberInfo is PropertyReference propertyReference)
|
||||
{
|
||||
var property = propertyReference.Resolve();
|
||||
|
||||
var propertyGetMethod = context.Assembly.MainModule.ImportReference(property.GetMethod).MakeGeneric(updateCurrentMethod.GenericParameters.ToArray());
|
||||
|
||||
il.Emit(OpCodes.Ldtoken, type);
|
||||
il.Emit(OpCodes.Call, getTypeFromHandleMethod);
|
||||
il.Emit(OpCodes.Ldstr, property.Name);
|
||||
|
||||
// If it's a virtual or interface call, we need to create a dispatcher using ldvirtftn
|
||||
if (property.GetMethod.IsVirtual)
|
||||
propertyGetMethod = CreateDispatcher(context.Assembly, propertyGetMethod);
|
||||
|
||||
il.Emit(OpCodes.Ldftn, propertyGetMethod);
|
||||
|
||||
// Set whether getter method uses a VirtualDispatch (static call) or instance call
|
||||
il.Emit(property.GetMethod.IsVirtual ? OpCodes.Ldc_I4_1 : OpCodes.Ldc_I4_0);
|
||||
|
||||
// Only uses setter if it exists and it's public
|
||||
if (property.SetMethod?.IsPublic == true)
|
||||
{
|
||||
var propertySetMethod = context.Assembly.MainModule.ImportReference(property.SetMethod).MakeGeneric(updateCurrentMethod.GenericParameters.ToArray());
|
||||
if (property.SetMethod.IsVirtual)
|
||||
propertySetMethod = CreateDispatcher(context.Assembly, propertySetMethod);
|
||||
il.Emit(OpCodes.Ldftn, propertySetMethod);
|
||||
}
|
||||
else
|
||||
{
|
||||
// 0 (native int)
|
||||
il.Emit(OpCodes.Ldc_I4_0);
|
||||
il.Emit(OpCodes.Conv_I);
|
||||
}
|
||||
|
||||
// Set whether setter method uses a VirtualDispatch (static call) or instance call
|
||||
il.Emit((property.SetMethod?.IsVirtual ?? false) ? OpCodes.Ldc_I4_1 : OpCodes.Ldc_I4_0);
|
||||
|
||||
var propertyType = context.Assembly.MainModule.ImportReference(replaceGenericsVisitor != null ? replaceGenericsVisitor.VisitDynamic(property.PropertyType) : property.PropertyType);
|
||||
|
||||
var updatablePropertyInflatedCtor = GetOrCreateUpdatablePropertyCtor(context.Assembly, propertyType);
|
||||
|
||||
il.Emit(OpCodes.Newobj, updatablePropertyInflatedCtor);
|
||||
il.Emit(OpCodes.Call, updateEngineRegisterMemberMethod);
|
||||
}
|
||||
EmitPropertyRegistration(il, propertyReference, type, replaceGenericsVisitor, context.Assembly, updateCurrentMethod);
|
||||
}
|
||||
|
||||
if (updateCurrentMethod != updateMainMethod)
|
||||
@@ -396,12 +328,150 @@ internal partial class UpdateEngineProcessor : ICecilSerializerProcessor
|
||||
// Also call it from main method if it was a closed generic instantiation
|
||||
if (type is GenericInstanceType genericInstanceType)
|
||||
{
|
||||
il = updateMainMethod.Body.GetILProcessor();
|
||||
il.Emit(OpCodes.Call, updateCurrentMethod.MakeGeneric(genericInstanceType.GenericArguments.Select(context.Assembly.MainModule.ImportReference).ToArray()));
|
||||
var mainIL = new ILBuilder(updateMainMethod.Body, module);
|
||||
mainIL.Emit(OpCodes.Call, updateCurrentMethod.MakeGeneric(genericInstanceType.GenericArguments.Select(module.ImportReference).ToArray()));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Emits field registration with the update engine using pointer offset computation.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// Generates code equivalent to:
|
||||
/// <code>
|
||||
/// // For reference types:
|
||||
/// UpdateEngine.RegisterMember(typeof(T), "fieldName",
|
||||
/// new UpdatableField<FieldType>(&emptyObject.field - &emptyObject));
|
||||
/// // For value types:
|
||||
/// UpdateEngine.RegisterMember(typeof(T), "fieldName",
|
||||
/// new UpdatableField<FieldType>(&emptyStruct.field - &emptyStruct));
|
||||
/// </code>
|
||||
/// </remarks>
|
||||
private void EmitFieldRegistration(
|
||||
ILBuilder il, FieldReference fieldReference, TypeReference type, bool typeIsValueType,
|
||||
ResolveGenericsVisitor replaceGenericsVisitor, ModuleDefinition module,
|
||||
MethodDefinition updateMainMethod, ref FieldDefinition emptyObjectField, ref VariableDefinition emptyStruct)
|
||||
{
|
||||
var field = fieldReference.Resolve();
|
||||
|
||||
// We need a dummy instance to compute field offsets via pointer math: &empty.field - &empty
|
||||
// For reference types: a static field initialized in .cctor (var emptyObject = new object())
|
||||
// For value types: a local variable (T emptyStruct)
|
||||
if (typeIsValueType)
|
||||
{
|
||||
if (emptyStruct == null)
|
||||
{
|
||||
emptyStruct = new VariableDefinition(type);
|
||||
updateMainMethod.Body.Variables.Add(emptyStruct);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if (emptyObjectField == null)
|
||||
{
|
||||
emptyObjectField = new FieldDefinition("emptyObject", FieldAttributes.Static | FieldAttributes.Private, module.TypeSystem.Object);
|
||||
|
||||
// Create static ctor that will initialize this object
|
||||
var staticConstructor = new MethodDefinition(".cctor",
|
||||
MethodAttributes.Private | MethodAttributes.HideBySig | MethodAttributes.Static | MethodAttributes.SpecialName | MethodAttributes.RTSpecialName,
|
||||
module.TypeSystem.Void);
|
||||
var staticCtorIL = new ILBuilder(staticConstructor.Body, module);
|
||||
staticCtorIL.Emit(OpCodes.Newobj, module.ImportReference(emptyObjectField.FieldType.Resolve().GetConstructors().Single(x => !x.IsStatic && !x.HasParameters)))
|
||||
.Emit(OpCodes.Stsfld, emptyObjectField)
|
||||
.Emit(OpCodes.Ret);
|
||||
|
||||
updateMainMethod.DeclaringType.Fields.Add(emptyObjectField);
|
||||
updateMainMethod.DeclaringType.Methods.Add(staticConstructor);
|
||||
}
|
||||
}
|
||||
|
||||
// Emit: typeof(T), "fieldName"
|
||||
il.EmitTypeof(type, getTypeFromHandleMethod)
|
||||
.Emit(OpCodes.Ldstr, field.Name);
|
||||
|
||||
// Emit: (int)(&empty.field - &empty) — computes field byte offset
|
||||
if (typeIsValueType)
|
||||
il.Emit(OpCodes.Ldloca, emptyStruct);
|
||||
else
|
||||
il.Emit(OpCodes.Ldsfld, emptyObjectField);
|
||||
il.Emit(OpCodes.Ldflda, il.Import(fieldReference))
|
||||
.Emit(OpCodes.Conv_I);
|
||||
if (typeIsValueType)
|
||||
il.Emit(OpCodes.Ldloca, emptyStruct);
|
||||
else
|
||||
il.Emit(OpCodes.Ldsfld, emptyObjectField);
|
||||
il.Emit(OpCodes.Conv_I)
|
||||
.Emit(OpCodes.Sub)
|
||||
.Emit(OpCodes.Conv_I4);
|
||||
|
||||
// Emit: new UpdatableField<FieldType>(offset)
|
||||
var fieldType = il.Import(replaceGenericsVisitor != null ? replaceGenericsVisitor.VisitDynamic(field.FieldType) : field.FieldType);
|
||||
il.Emit(OpCodes.Newobj, il.Import(updatableFieldGenericCtor).MakeGeneric(fieldType))
|
||||
// Emit: UpdateEngine.RegisterMember(typeof(T), "fieldName", updatableField);
|
||||
.Emit(OpCodes.Call, updateEngineRegisterMemberMethod);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Emits property registration with the update engine using function pointers.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// Generates code equivalent to:
|
||||
/// <code>
|
||||
/// UpdateEngine.RegisterMember(typeof(T), "PropertyName",
|
||||
/// new UpdatableProperty<PropType>(
|
||||
/// ldftn(get_Property), // or ldftn(Dispatcher_get_Property) for virtual
|
||||
/// isGetterVirtual,
|
||||
/// ldftn(set_Property), // or IntPtr.Zero if no public setter
|
||||
/// isSetterVirtual));
|
||||
/// </code>
|
||||
/// Virtual/interface properties use a dispatcher trampoline (see <see cref="CreateDispatcher"/>).
|
||||
/// </remarks>
|
||||
private void EmitPropertyRegistration(
|
||||
ILBuilder il, PropertyReference propertyReference, TypeReference type,
|
||||
ResolveGenericsVisitor replaceGenericsVisitor, AssemblyDefinition assembly,
|
||||
MethodDefinition updateCurrentMethod)
|
||||
{
|
||||
var property = propertyReference.Resolve();
|
||||
|
||||
var propertyGetMethod = il.Import(property.GetMethod).MakeGeneric(updateCurrentMethod.GenericParameters.ToArray());
|
||||
|
||||
// Emit: typeof(T), "PropertyName"
|
||||
il.EmitTypeof(type, getTypeFromHandleMethod)
|
||||
.Emit(OpCodes.Ldstr, property.Name);
|
||||
|
||||
// Emit: ldftn(get_Property) — for virtual, wrap in a dispatcher that uses ldvirtftn+calli
|
||||
if (property.GetMethod.IsVirtual)
|
||||
propertyGetMethod = CreateDispatcher(assembly, propertyGetMethod);
|
||||
il.Emit(OpCodes.Ldftn, propertyGetMethod)
|
||||
// Set whether setter method uses a VirtualDispatch (static call) or instance call
|
||||
.Emit(property.GetMethod.IsVirtual ? OpCodes.Ldc_I4_1 : OpCodes.Ldc_I4_0);
|
||||
|
||||
// Emit: ldftn(set_Property) or IntPtr.Zero if no public setter
|
||||
if (property.SetMethod?.IsPublic == true)
|
||||
{
|
||||
var propertySetMethod = il.Import(property.SetMethod).MakeGeneric(updateCurrentMethod.GenericParameters.ToArray());
|
||||
if (property.SetMethod.IsVirtual)
|
||||
propertySetMethod = CreateDispatcher(assembly, propertySetMethod);
|
||||
il.Emit(OpCodes.Ldftn, propertySetMethod);
|
||||
}
|
||||
else
|
||||
{
|
||||
il.Emit(OpCodes.Ldc_I4_0)
|
||||
.Emit(OpCodes.Conv_I);
|
||||
}
|
||||
|
||||
// Set whether setter method uses a VirtualDispatch (static call) or instance call
|
||||
il.Emit((property.SetMethod?.IsVirtual ?? false) ? OpCodes.Ldc_I4_1 : OpCodes.Ldc_I4_0);
|
||||
|
||||
// Emit: new UpdatableProperty<PropType>(getter, isGetterVirtual, setter, isSetterVirtual)
|
||||
var propertyType = il.Import(replaceGenericsVisitor != null ? replaceGenericsVisitor.VisitDynamic(property.PropertyType) : property.PropertyType);
|
||||
var updatablePropertyInflatedCtor = GetOrCreateUpdatablePropertyCtor(assembly, propertyType);
|
||||
il.Emit(OpCodes.Newobj, updatablePropertyInflatedCtor)
|
||||
// Emit: UpdateEngine.RegisterMember(typeof(T), "PropertyName", updatableProperty);
|
||||
.Emit(OpCodes.Call, updateEngineRegisterMemberMethod);
|
||||
}
|
||||
|
||||
private static string ComputeUpdateMethodName(TypeDefinition typeDefinition)
|
||||
{
|
||||
var typeName = ComputeTypeName(typeDefinition);
|
||||
|
||||
@@ -58,6 +58,7 @@ public class ModuleInitializerGenerator : IIncrementalGenerator
|
||||
}
|
||||
}
|
||||
var outputClass = @$"
|
||||
// <auto-generated/>
|
||||
namespace Stride.Core.CompilerServices.AutoGenerated.ModuleInitializer
|
||||
{{
|
||||
file static class ModuleInitializer
|
||||
|
||||
@@ -102,7 +102,7 @@ public class TestHybridDictionary
|
||||
var result = dictionary.Remove("key1");
|
||||
|
||||
Assert.True(result);
|
||||
Assert.Equal(1, dictionary.Count);
|
||||
Assert.Single(dictionary);
|
||||
Assert.False(dictionary.ContainsKey("key1"));
|
||||
}
|
||||
|
||||
@@ -115,7 +115,7 @@ public class TestHybridDictionary
|
||||
var result = dictionary.Remove("key2");
|
||||
|
||||
Assert.False(result);
|
||||
Assert.Equal(1, dictionary.Count);
|
||||
Assert.Single(dictionary);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
@@ -202,7 +202,7 @@ public class TestHybridDictionary
|
||||
|
||||
dictionary.Clear();
|
||||
|
||||
Assert.Equal(0, dictionary.Count);
|
||||
Assert.Empty(dictionary);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
@@ -249,7 +249,7 @@ public class TestHybridDictionary
|
||||
foreach (var kvp in dictionary)
|
||||
{
|
||||
count++;
|
||||
Assert.True(kvp.Key.StartsWith("key"));
|
||||
Assert.StartsWith("key", kvp.Key);
|
||||
}
|
||||
|
||||
Assert.Equal(3, count);
|
||||
|
||||
@@ -86,8 +86,8 @@ public class TestBoundingSphere
|
||||
BoundingSphere.Merge(ref sphere1, ref sphere2, out var merged);
|
||||
|
||||
// Merged sphere should contain both spheres
|
||||
Assert.True(merged.Contains(ref sphere1) != ContainmentType.Disjoint);
|
||||
Assert.True(merged.Contains(ref sphere2) != ContainmentType.Disjoint);
|
||||
Assert.NotEqual(ContainmentType.Disjoint, merged.Contains(ref sphere1));
|
||||
Assert.NotEqual(ContainmentType.Disjoint, merged.Contains(ref sphere2));
|
||||
|
||||
// Test non-ref version
|
||||
var merged2 = BoundingSphere.Merge(sphere1, sphere2);
|
||||
|
||||
@@ -113,9 +113,9 @@ public class TestSphericalHarmonics
|
||||
var result = sh.Evaluate(direction);
|
||||
|
||||
// Verify result is valid (SH can produce negative or positive values)
|
||||
Assert.True(!float.IsNaN(result.R));
|
||||
Assert.True(!float.IsNaN(result.G));
|
||||
Assert.True(!float.IsNaN(result.B));
|
||||
Assert.False(float.IsNaN(result.R));
|
||||
Assert.False(float.IsNaN(result.G));
|
||||
Assert.False(float.IsNaN(result.B));
|
||||
}
|
||||
|
||||
[Fact]
|
||||
|
||||
@@ -38,12 +38,7 @@ public struct Rectangle : IEquatable<Rectangle>, ISpanFormattable
|
||||
/// <summary>
|
||||
/// An empty rectangle.
|
||||
/// </summary>
|
||||
public static readonly Rectangle Empty;
|
||||
|
||||
static Rectangle()
|
||||
{
|
||||
Empty = new Rectangle();
|
||||
}
|
||||
public static readonly Rectangle Empty = new();
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="Rectangle"/> struct.
|
||||
|
||||
@@ -38,12 +38,7 @@ public struct RectangleF : IEquatable<RectangleF>, ISpanFormattable
|
||||
/// <summary>
|
||||
/// An empty rectangle
|
||||
/// </summary>
|
||||
public static readonly RectangleF Empty;
|
||||
|
||||
static RectangleF()
|
||||
{
|
||||
Empty = new RectangleF();
|
||||
}
|
||||
public static readonly RectangleF Empty = new();
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="RectangleF"/> struct.
|
||||
|
||||
@@ -55,7 +55,7 @@ public sealed class DatabaseFileProvider : VirtualFileProviderBase
|
||||
if (streamFlags == StreamFlags.Seekable && !result.CanSeek)
|
||||
{
|
||||
var buffer = new byte[result.Length - result.Position];
|
||||
result.Read(buffer, 0, buffer.Length);
|
||||
result.ReadExactly(buffer, 0, buffer.Length);
|
||||
return new DatabaseReadFileStream(objectId, new MemoryStream(buffer), 0);
|
||||
}
|
||||
|
||||
|
||||
@@ -323,7 +323,7 @@ public abstract class Store<T> : IDisposable where T : new()
|
||||
return;
|
||||
|
||||
var bufferToRead = new byte[length];
|
||||
stream.Read(bufferToRead, 0, length);
|
||||
stream.ReadExactly(bufferToRead, 0, length);
|
||||
var memoryStream = new MemoryStream(bufferToRead);
|
||||
|
||||
try
|
||||
|
||||
@@ -32,7 +32,7 @@ public class Blob : ReferenceBase
|
||||
{
|
||||
Size = (int)stream.Length;
|
||||
Content = Marshal.AllocHGlobal(Size);
|
||||
stream.Read(new Span<byte>((void*)Content, Size));
|
||||
stream.ReadExactly(new Span<byte>((void*)Content, Size));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
|
||||
@@ -5,6 +5,8 @@
|
||||
|
||||
namespace Stride.Core.Diagnostics;
|
||||
|
||||
#nullable enable
|
||||
|
||||
/// <summary>
|
||||
/// Extensions for <see cref="ILogger"/>.
|
||||
/// </summary>
|
||||
@@ -19,11 +21,7 @@ public static class LoggerExtensions
|
||||
/// <param name="callerInfo">Information about the caller. Default is null, otherwise use <see cref="CallerInfo.Get"/>.</param>
|
||||
public static void Verbose(this ILogger logger, string message, Exception? exception, CallerInfo? callerInfo = null)
|
||||
{
|
||||
#if NET7_0_OR_GREATER
|
||||
ArgumentNullException.ThrowIfNull(logger);
|
||||
#else
|
||||
if (logger is null) throw new ArgumentNullException(nameof(logger));
|
||||
#endif // NET7_0_OR_GREATER
|
||||
logger.Log(new LogMessage(logger.Module, LogMessageType.Verbose, message, exception, callerInfo));
|
||||
}
|
||||
|
||||
@@ -46,11 +44,7 @@ public static class LoggerExtensions
|
||||
/// <param name="callerInfo">Information about the caller. Default is null, otherwise use <see cref="CallerInfo.Get"/>.</param>
|
||||
public static void Debug(this ILogger logger, string message, Exception? exception, CallerInfo? callerInfo = null)
|
||||
{
|
||||
#if NET7_0_OR_GREATER
|
||||
ArgumentNullException.ThrowIfNull(logger);
|
||||
#else
|
||||
if (logger is null) throw new ArgumentNullException(nameof(logger));
|
||||
#endif // NET7_0_OR_GREATER
|
||||
logger.Log(new LogMessage(logger.Module, LogMessageType.Debug, message, exception, callerInfo));
|
||||
}
|
||||
|
||||
@@ -73,11 +67,7 @@ public static class LoggerExtensions
|
||||
/// <param name="callerInfo">Information about the caller. Default is null, otherwise use <see cref="CallerInfo.Get"/>.</param>
|
||||
public static void Info(this ILogger logger, string message, Exception? exception, CallerInfo? callerInfo = null)
|
||||
{
|
||||
#if NET7_0_OR_GREATER
|
||||
ArgumentNullException.ThrowIfNull(logger);
|
||||
#else
|
||||
if (logger is null) throw new ArgumentNullException(nameof(logger));
|
||||
#endif // NET7_0_OR_GREATER
|
||||
logger.Log(new LogMessage(logger.Module, LogMessageType.Info, message, exception, callerInfo));
|
||||
}
|
||||
|
||||
@@ -100,11 +90,7 @@ public static class LoggerExtensions
|
||||
/// <param name="callerInfo">Information about the caller. Default is null, otherwise use <see cref="CallerInfo.Get"/>.</param>
|
||||
public static void Warning(this ILogger logger, string message, Exception? exception, CallerInfo? callerInfo = null)
|
||||
{
|
||||
#if NET7_0_OR_GREATER
|
||||
ArgumentNullException.ThrowIfNull(logger);
|
||||
#else
|
||||
if (logger is null) throw new ArgumentNullException(nameof(logger));
|
||||
#endif // NET7_0_OR_GREATER
|
||||
logger.Log(new LogMessage(logger.Module, LogMessageType.Warning, message, exception, callerInfo));
|
||||
}
|
||||
|
||||
@@ -127,11 +113,7 @@ public static class LoggerExtensions
|
||||
/// <param name="callerInfo">Information about the caller. Default is null, otherwise use <see cref="CallerInfo.Get"/>.</param>
|
||||
public static void Error(this ILogger logger, string message, Exception? exception, CallerInfo? callerInfo = null)
|
||||
{
|
||||
#if NET7_0_OR_GREATER
|
||||
ArgumentNullException.ThrowIfNull(logger);
|
||||
#else
|
||||
if (logger is null) throw new ArgumentNullException(nameof(logger));
|
||||
#endif // NET7_0_OR_GREATER
|
||||
logger.Log(new LogMessage(logger.Module, LogMessageType.Error, message, exception, callerInfo));
|
||||
}
|
||||
|
||||
@@ -154,11 +136,7 @@ public static class LoggerExtensions
|
||||
/// <param name="callerInfo">Information about the caller. Default is null, otherwise use <see cref="CallerInfo.Get"/>.</param>
|
||||
public static void Fatal(this ILogger logger, string message, Exception? exception, CallerInfo? callerInfo = null)
|
||||
{
|
||||
#if NET7_0_OR_GREATER
|
||||
ArgumentNullException.ThrowIfNull(logger);
|
||||
#else
|
||||
if (logger is null) throw new ArgumentNullException(nameof(logger));
|
||||
#endif // NET7_0_OR_GREATER
|
||||
logger.Log(new LogMessage(logger.Module, LogMessageType.Fatal, message, exception, callerInfo));
|
||||
}
|
||||
|
||||
|
||||
@@ -11,6 +11,8 @@
|
||||
|
||||
namespace Stride.Core.Diagnostics;
|
||||
|
||||
#nullable enable
|
||||
|
||||
/// <summary>
|
||||
/// Extensions for <see cref="ILogger"/>.
|
||||
/// </summary>
|
||||
@@ -26,11 +28,7 @@ public static class LoggerExtensions
|
||||
/// <param name="callerInfo">Information about the caller. Default is null, otherwise use <see cref="CallerInfo.Get"/>.</param>
|
||||
public static void <#= name #>(this ILogger logger, string message, Exception? exception, CallerInfo? callerInfo = null)
|
||||
{
|
||||
#if NET7_0_OR_GREATER
|
||||
ArgumentNullException.ThrowIfNull(logger);
|
||||
#else
|
||||
if (logger is null) throw new ArgumentNullException(nameof(logger));
|
||||
#endif // NET7_0_OR_GREATER
|
||||
logger.Log(new LogMessage(logger.Module, LogMessageType.<#= name #>, message, exception, callerInfo));
|
||||
}
|
||||
|
||||
|
||||
@@ -323,7 +323,7 @@ public class BlittableArraySerializer<T> : ArraySerializer<T> where T : unmanage
|
||||
{
|
||||
var span = MemoryMarshal.Cast<T, byte>(array.AsSpan());
|
||||
if (mode == ArchiveMode.Deserialize)
|
||||
stream.UnderlyingStream.Read(span);
|
||||
stream.UnderlyingStream.ReadExactly(span);
|
||||
else if (mode == ArchiveMode.Serialize)
|
||||
stream.UnderlyingStream.Write(span);
|
||||
}
|
||||
|
||||
+1
-1
@@ -247,7 +247,7 @@ namespace Stride.Assets.Presentation.AssetEditors.EntityHierarchyEditor.Game
|
||||
if (game.Input.IsKeyPressed(SceneEditorSettings.SwitchGizmo.GetValue()))
|
||||
{
|
||||
var current = activeTransformation;
|
||||
var next = (int)(current + 1) % Enum.GetValues(typeof(Transformation)).Length;
|
||||
var next = (int)(current + 1) % Enum.GetValues<Transformation>().Length;
|
||||
await editor.Dispatcher.InvokeAsync(() => editor.Transform.ActiveTransformation = (Transformation)next);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -290,7 +290,7 @@ namespace Stride.Assets.Presentation.AssetEditors
|
||||
{
|
||||
using (var streamReader = new StreamReader(File.Open(changedFile, FileMode.Open, FileAccess.Read, FileShare.ReadWrite), Encoding.UTF8, true))
|
||||
{
|
||||
source = streamReader.ReadToEnd();
|
||||
source = await streamReader.ReadToEndAsync();
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
+1
-1
@@ -110,7 +110,7 @@ namespace Stride.Assets.Presentation.AssetEditors.UIEditor.Views
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
protected override Thickness? UpateValueFromFloat(float value)
|
||||
protected override Thickness? UpdateValueFromFloat(float value)
|
||||
{
|
||||
return Thickness.UniformCuboid(value);
|
||||
}
|
||||
|
||||
+2
-2
@@ -79,7 +79,7 @@ namespace Stride.Assets.Presentation.AssetEditors.VisualScriptEditor
|
||||
SemanticModel = compilation.GetSemanticModel(syntaxTree);
|
||||
|
||||
// Compute list of overridable methods
|
||||
var type = SemanticModel.GetDeclaredSymbol(syntaxTree.GetRoot().DescendantNodes().OfType<ClassDeclarationSyntax>().FirstOrDefault());
|
||||
var type = SemanticModel.GetDeclaredSymbol((await syntaxTree.GetRootAsync()).DescendantNodes().OfType<ClassDeclarationSyntax>().FirstOrDefault());
|
||||
|
||||
// Note: Logic taken from Roslyn AbstractOverrideCompletionProvider
|
||||
var overridableMethodsCurrentType = new List<ISymbol>();
|
||||
@@ -123,7 +123,7 @@ namespace Stride.Assets.Presentation.AssetEditors.VisualScriptEditor
|
||||
|
||||
// Find the syntax node that generated this diagnostic
|
||||
var sourceSpan = diagnostic.Location.SourceSpan;
|
||||
var node = compileResult.SyntaxTree.GetRoot().FindNode(sourceSpan);
|
||||
var node = (await compileResult.SyntaxTree.GetRootAsync()).FindNode(sourceSpan);
|
||||
|
||||
// Find which block or link generated it
|
||||
// Go through parent recursively until we find one
|
||||
|
||||
+4
-4
@@ -455,7 +455,7 @@ namespace Stride.Assets.Presentation.AssetEditors.VisualScriptEditor
|
||||
foreach (var assembly in GetAssemblies(latestCompilation, cancellationToken)
|
||||
.OrderBy(assembly =>
|
||||
{
|
||||
if (assembly == latestCompilation.Assembly)
|
||||
if (SymbolEqualityComparer.Default.Equals(assembly, latestCompilation.Assembly))
|
||||
return 0;
|
||||
|
||||
if (assembly.Name.Contains("Stride"))
|
||||
@@ -471,11 +471,11 @@ namespace Stride.Assets.Presentation.AssetEditors.VisualScriptEditor
|
||||
|
||||
// List types that are either public or part of current assembly
|
||||
foreach (var type in GetAllTypes(assembly, cancellationToken)
|
||||
.Where(type => type.DeclaredAccessibility != RoslynAccessibility.Private || type.ContainingAssembly == latestCompilation.Assembly))
|
||||
.Where(type => type.DeclaredAccessibility != RoslynAccessibility.Private || SymbolEqualityComparer.Default.Equals(type.ContainingAssembly, latestCompilation.Assembly)))
|
||||
{
|
||||
// List methods
|
||||
foreach (var method in type.GetMembers().OfType<IMethodSymbol>()
|
||||
.Where(member => member.DeclaredAccessibility != RoslynAccessibility.Private || type.ContainingAssembly == latestCompilation.Assembly))
|
||||
.Where(member => member.DeclaredAccessibility != RoslynAccessibility.Private || SymbolEqualityComparer.Default.Equals(type.ContainingAssembly, latestCompilation.Assembly)))
|
||||
{
|
||||
// Ignore .ctor, property getter/setter, events, etc...
|
||||
if (method.MethodKind != MethodKind.Ordinary
|
||||
@@ -522,7 +522,7 @@ namespace Stride.Assets.Presentation.AssetEditors.VisualScriptEditor
|
||||
CancellationToken cancellationToken)
|
||||
{
|
||||
var stack = new Stack<IAssemblySymbol>();
|
||||
var processedAssemblies = new HashSet<IAssemblySymbol>();
|
||||
var processedAssemblies = new HashSet<IAssemblySymbol>(SymbolEqualityComparer.Default);
|
||||
|
||||
processedAssemblies.Add(compilation.Assembly);
|
||||
stack.Push(compilation.Assembly);
|
||||
|
||||
@@ -144,7 +144,7 @@ namespace Stride.Assets.Presentation.Templates
|
||||
scriptContent = scriptContent.Replace("##Namespace##", parameters.Namespace);
|
||||
scriptContent = scriptContent.Replace("##Scriptname##", location.GetFileNameWithoutExtension());
|
||||
|
||||
var asset = (ScriptSourceFileAsset)ObjectFactoryRegistry.NewInstance(typeof(ScriptSourceFileAsset));
|
||||
var asset = ObjectFactoryRegistry.NewInstance<ScriptSourceFileAsset>();
|
||||
asset.Id = SourceCodeAsset.GenerateIdFromLocation(parameters.Package.Meta.Name, location);
|
||||
asset.Text = scriptContent;
|
||||
yield return new AssetItem(location, asset);
|
||||
|
||||
@@ -1,13 +1,21 @@
|
||||
<ResourceDictionary xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
|
||||
xmlns:s="clr-namespace:System;assembly=mscorlib"
|
||||
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
||||
xmlns:ui="clr-namespace:Stride.UI;assembly=Stride.UI"
|
||||
xmlns:views="clr-namespace:Stride.Assets.Presentation.AssetEditors.UIEditor.Views"
|
||||
xmlns:tp="clr-namespace:Stride.Core.Assets.Editor.View.TemplateProviders;assembly=Stride.Core.Assets.Editor"
|
||||
xmlns:sd="http://schemas.stride3d.net/xaml/presentation">
|
||||
<ResourceDictionary.MergedDictionaries>
|
||||
<ResourceDictionary Source="/Stride.Core.Assets.Editor;component/View/DefaultPropertyTemplateProviders.xaml"/>
|
||||
<ResourceDictionary Source="ImageDictionary.xaml"/>
|
||||
</ResourceDictionary.MergedDictionaries>
|
||||
|
||||
<tp:TypeAndPropertyNameMatchTemplateProvider x:Key="RotationTemplateProvider" Type="{x:Type s:Single}" PropertyName="Rotation" sd:PropertyViewHelper.TemplateCategory="PropertyEditor">
|
||||
<DataTemplate>
|
||||
<sd:AngleEditor Value="{Binding NodeValue}" />
|
||||
</DataTemplate>
|
||||
</tp:TypeAndPropertyNameMatchTemplateProvider>
|
||||
|
||||
<sd:TypeMatchTemplateProvider x:Key="StripDefinitionTemplateProvider" Type="{x:Type ui:StripDefinition}" sd:PropertyViewHelper.TemplateCategory="PropertyEditor">
|
||||
<DataTemplate>
|
||||
<UniformGrid Rows="1">
|
||||
@@ -83,8 +91,4 @@
|
||||
<views:ThicknessEditor Value="{Binding NodeValue}" DecimalPlaces="3" />
|
||||
</DataTemplate>
|
||||
</sd:TypeMatchTemplateProvider>
|
||||
|
||||
</ResourceDictionary>
|
||||
|
||||
|
||||
|
||||
|
||||
+1
-1
@@ -148,7 +148,7 @@ namespace Stride.Assets.Presentation.ViewModel
|
||||
document = workspace.GetDocument(DocumentId.Result);
|
||||
|
||||
// Set new text
|
||||
TextDocument.Text = document.GetTextAsync().Result.ToString();
|
||||
TextDocument.Text = (await document.GetTextAsync()).ToString();
|
||||
|
||||
// Update dirty state
|
||||
hasExternalChanges = false;
|
||||
|
||||
@@ -881,7 +881,7 @@ namespace Stride.Core.Assets.Editor.Tests
|
||||
var result = service.DeserializeCopiedData(copiedText, asset, expectedType);
|
||||
Assert.NotNull(result);
|
||||
Assert.NotNull(result.Items);
|
||||
Assert.Equal(1, result.Items.Count);
|
||||
Assert.Single(result.Items);
|
||||
|
||||
var item = result.Items[0];
|
||||
Assert.NotNull(item);
|
||||
|
||||
@@ -72,7 +72,7 @@ namespace Stride.Core.Assets.Editor.Components.Status
|
||||
if (CurrentJob == job)
|
||||
{
|
||||
job = null;
|
||||
foreach (JobPriority priority in Enum.GetValues(typeof(JobPriority)).Cast<JobPriority>().Reverse())
|
||||
foreach (JobPriority priority in Enum.GetValues<JobPriority>().Cast<JobPriority>().Reverse())
|
||||
{
|
||||
var nextJob = jobList.LastOrDefault(x => x.Value.Priority == priority).Value;
|
||||
if (nextJob != null)
|
||||
|
||||
+25
@@ -0,0 +1,25 @@
|
||||
// Copyright (c) .NET Foundation and Contributors (https://dotnetfoundation.org/ & https://stride3d.net)
|
||||
// Distributed under the MIT license. See the LICENSE.md file in the project root for more information.
|
||||
using Stride.Core.Presentation.Quantum.ViewModels;
|
||||
|
||||
namespace Stride.Core.Assets.Editor.View.TemplateProviders
|
||||
{
|
||||
/// <summary>
|
||||
/// A template provider that matches nodes based on both their type and property name.
|
||||
/// </summary>
|
||||
public class TypeAndPropertyNameMatchTemplateProvider : TypeMatchTemplateProvider
|
||||
{
|
||||
/// <inheritdoc/>
|
||||
public override string Name => $"{base.Name}_{PropertyName}";
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the name of the property to match.
|
||||
/// </summary>
|
||||
public string PropertyName { get; set; }
|
||||
|
||||
public override bool MatchNode(NodeViewModel node)
|
||||
{
|
||||
return base.MatchNode(node) && node.Name == PropertyName;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -166,7 +166,7 @@ namespace Stride.Core.Assets.Editor.ViewModel
|
||||
// Storing out primitive data for filters to save between instances of editor
|
||||
private List<AssetFilterViewModelData> StoredListData = InternalSettings.ViewFilters.GetValue();
|
||||
|
||||
public static readonly IEnumerable<FilterCategory> AllFilterCategories = Enum.GetValues(typeof(FilterCategory)).Cast<FilterCategory>();
|
||||
public static readonly IEnumerable<FilterCategory> AllFilterCategories = Enum.GetValues<FilterCategory>();
|
||||
|
||||
private readonly ObservableSet<AssetViewModel> assets = new ObservableSet<AssetViewModel>();
|
||||
|
||||
|
||||
@@ -64,7 +64,7 @@ namespace Stride.GameStudio.Tests
|
||||
var result = service.DeserializeCopiedData(copiedText, asset, expectedType);
|
||||
Assert.NotNull(result);
|
||||
Assert.NotNull(result.Items);
|
||||
Assert.Equal(1, result.Items.Count);
|
||||
Assert.Single(result.Items);
|
||||
|
||||
var item = result.Items[0];
|
||||
Assert.NotNull(item);
|
||||
|
||||
@@ -137,7 +137,7 @@ namespace Stride.GameStudio.Tests
|
||||
var data = service.DeserializeCopiedData(text, assetGraph.Asset, typeof(Entity));
|
||||
Assert.NotNull(data);
|
||||
Assert.NotNull(data.Items);
|
||||
Assert.Equal(1, data.Items.Count);
|
||||
Assert.Single(data.Items);
|
||||
|
||||
var item = data.Items[0];
|
||||
Assert.NotNull(item);
|
||||
|
||||
@@ -23,7 +23,7 @@ namespace Stride.GameStudio.Debugging
|
||||
{
|
||||
public partial class AssemblyRecompiler
|
||||
{
|
||||
private SourceGroup[] previousSortedConnectedGroups = new SourceGroup[0];
|
||||
private SourceGroup[] previousSortedConnectedGroups = [];
|
||||
private IMutableBidirectionalGraph<SourceGroup, CondensedEdge<SyntaxTree, SEdge<SyntaxTree>, SourceGroup>> previousStronglyConnected = new BidirectionalGraph<SourceGroup, CondensedEdge<SyntaxTree, SEdge<SyntaxTree>, SourceGroup>>();
|
||||
private ImmutableHashSet<SourceGroup> previousConnectedGroups;
|
||||
private int assemblyCounter;
|
||||
@@ -53,7 +53,7 @@ namespace Stride.GameStudio.Debugging
|
||||
|
||||
foreach (var syntaxTree in gameProjectCompilation.SyntaxTrees)
|
||||
{
|
||||
var syntaxRoot = syntaxTree.GetRoot();
|
||||
var syntaxRoot = await syntaxTree.GetRootAsync();
|
||||
var semanticModel = gameProjectCompilation.GetSemanticModel(syntaxTree);
|
||||
|
||||
var dependencies = new SourceDependencySyntaxVisitor(new HashSet<SyntaxTree>(gameProjectCompilation.SyntaxTrees), semanticModel).DefaultVisit(syntaxRoot);
|
||||
@@ -106,7 +106,7 @@ namespace Stride.GameStudio.Debugging
|
||||
for (int i = 0; i < 1000; i++)
|
||||
internalsVisibleToBuilder.AppendFormat(@"[assembly: InternalsVisibleTo(""{0}.Part{1}"")]", gameProject.AssemblyName, i);
|
||||
|
||||
var internalsVisibleToSource = CSharpSyntaxTree.ParseText(internalsVisibleToBuilder.ToString(), null, "", Encoding.UTF8).GetText();
|
||||
var internalsVisibleToSource = await CSharpSyntaxTree.ParseText(internalsVisibleToBuilder.ToString(), null, "", Encoding.UTF8).GetTextAsync();
|
||||
|
||||
// 2. Compile assemblies
|
||||
foreach (var sourceGroup in sortedConnectedGroups.Reverse())
|
||||
@@ -125,7 +125,7 @@ namespace Stride.GameStudio.Debugging
|
||||
// Add sources
|
||||
foreach (var syntaxTree in sourceGroup.Vertices)
|
||||
{
|
||||
project = project.AddDocument(syntaxTree.FilePath, syntaxTree.GetText()).Project;
|
||||
project = project.AddDocument(syntaxTree.FilePath, await syntaxTree.GetTextAsync()).Project;
|
||||
}
|
||||
|
||||
// Add references to other sources
|
||||
|
||||
@@ -25,5 +25,6 @@ TemplateFolders:
|
||||
- !file ../../../samples/Templates/ThirdPersonPlatformer/ThirdPersonPlatformer/ThirdPersonPlatformer.sdtpl
|
||||
- !file ../../../samples/Templates/TopDownRPG/TopDownRPG/TopDownRPG.sdtpl
|
||||
- !file ../../../samples/Templates/VRSandbox/VRSandbox/VRSandbox.sdtpl
|
||||
- !file ../../../samples/Templates/Platformer2D/Platformer2D/Platformer2D.sdtpl
|
||||
- !file ../../../samples/Tutorials/CSharpBeginner/CSharpBeginner/CSharpBeginner.sdtpl
|
||||
- !file ../../../samples/Tutorials/CSharpIntermediate/CSharpIntermediate/CSharpIntermediate.sdtpl
|
||||
|
||||
@@ -33,7 +33,7 @@ namespace Stride.Assets.Models
|
||||
if (!importParameters.InputParameters.TryGet(DeduplicateMaterialsKey, out var deduplicateMaterials))
|
||||
deduplicateMaterials = true; // Dedupe is the default value
|
||||
|
||||
var entityInfo = meshConverter.ExtractEntity(localPath.FullPath, null, importParameters.IsTypeSelectedForOutput(typeof(TextureAsset)), deduplicateMaterials);
|
||||
var entityInfo = meshConverter.ExtractEntity(localPath.FullPath, null, importParameters.IsTypeSelectedForOutput<TextureAsset>(), deduplicateMaterials);
|
||||
return entityInfo;
|
||||
}
|
||||
|
||||
|
||||
@@ -34,7 +34,7 @@ namespace Stride.Assets
|
||||
// If we have a command line override, use it first
|
||||
string graphicsApi;
|
||||
if (context.OptionProperties.TryGetValue("StrideGraphicsApi", out graphicsApi))
|
||||
return (GraphicsPlatform)Enum.Parse(typeof(GraphicsPlatform), graphicsApi);
|
||||
return Enum.Parse<GraphicsPlatform>(graphicsApi);
|
||||
|
||||
// Ohterwise, use default as fallback
|
||||
return context.Platform.GetDefaultGraphicsPlatform();
|
||||
|
||||
@@ -331,7 +331,7 @@ namespace Stride.Assets.Navigation
|
||||
object loadedHeightfieldInitialData;
|
||||
if (!loadedHeightfieldInitialDatas.TryGetValue(assetReference.Url, out loadedHeightfieldInitialData))
|
||||
{
|
||||
loadedHeightfieldInitialData = contentManager.Load(typeof(Heightmap), assetReference.Url);
|
||||
loadedHeightfieldInitialData = contentManager.Load<Heightmap>(assetReference.Url);
|
||||
loadedHeightfieldInitialDatas.Add(assetReference.Url, loadedHeightfieldInitialData);
|
||||
}
|
||||
heightmapSource.Heightmap = loadedHeightfieldInitialData as Heightmap;
|
||||
|
||||
@@ -88,7 +88,7 @@ namespace Stride.Assets.Textures.Packing
|
||||
|
||||
results[bestAlgorithm] = new List<AtlasTextureLayout>(atlasTextureLayouts);
|
||||
|
||||
foreach (var heuristicMethod in (TexturePackingMethod[])Enum.GetValues(typeof(TexturePackingMethod)))
|
||||
foreach (var heuristicMethod in Enum.GetValues<TexturePackingMethod>())
|
||||
{
|
||||
if (heuristicMethod == TexturePackingMethod.Best || heuristicMethod == TexturePackingMethod.BestShortSideFit)
|
||||
continue;
|
||||
|
||||
@@ -260,7 +260,7 @@ namespace Stride.Audio
|
||||
|
||||
//read one packet, size first, then data
|
||||
var len = reader.ReadInt16();
|
||||
compressedSoundStream.Read(compressedBuffer, 0, len);
|
||||
compressedSoundStream.ReadExactly(compressedBuffer, 0, len);
|
||||
currentPacketIndex++;
|
||||
|
||||
var writePtr = bufferPtr + offset;
|
||||
|
||||
+14
-12
@@ -36,7 +36,7 @@ public class DebugRenderProcessor : EntityProcessor<DebugRenderComponent>
|
||||
get => _visible;
|
||||
set
|
||||
{
|
||||
if (_sceneSystem.SceneInstance.GetProcessor<CollidableProcessor>() is { } proc)
|
||||
if (_sceneSystem.SceneInstance.GetProcessor<CollidableProcessor>() is { } proc && _visibilityGroup is not null)
|
||||
{
|
||||
if (_visible == value)
|
||||
return;
|
||||
@@ -66,7 +66,7 @@ public class DebugRenderProcessor : EntityProcessor<DebugRenderComponent>
|
||||
protected override void OnEntityComponentAdding(Entity entity, DebugRenderComponent component, DebugRenderComponent data)
|
||||
{
|
||||
base.OnEntityComponentAdding(entity, component, data);
|
||||
if (_sceneSystem.SceneInstance.GetProcessor<CollidableProcessor>() is not null)
|
||||
if (_sceneSystem?.SceneInstance?.GetProcessor<CollidableProcessor>() is not null && _visibilityGroup is not null)
|
||||
Visible = component.Visible;
|
||||
else if (component.Visible)
|
||||
_latent = true;
|
||||
@@ -76,19 +76,9 @@ public class DebugRenderProcessor : EntityProcessor<DebugRenderComponent>
|
||||
|
||||
protected override void OnSystemAdd()
|
||||
{
|
||||
SinglePassWireframeRenderFeature wireframeRenderFeature;
|
||||
|
||||
_shapeCacheSystem = Services.GetOrCreate<ShapeCacheSystem>();
|
||||
_game = Services.GetSafeServiceAs<IGame>();
|
||||
_sceneSystem = Services.GetSafeServiceAs<SceneSystem>();
|
||||
|
||||
if (_sceneSystem.GraphicsCompositor.RenderFeatures.OfType<SinglePassWireframeRenderFeature>().FirstOrDefault() is null)
|
||||
{
|
||||
wireframeRenderFeature = new();
|
||||
_sceneSystem.GraphicsCompositor.RenderFeatures.Add(wireframeRenderFeature);
|
||||
}
|
||||
|
||||
_visibilityGroup = _sceneSystem.SceneInstance.VisibilityGroups.First();
|
||||
}
|
||||
|
||||
protected override void OnSystemRemove()
|
||||
@@ -98,6 +88,18 @@ public class DebugRenderProcessor : EntityProcessor<DebugRenderComponent>
|
||||
|
||||
public override void Draw(RenderContext context)
|
||||
{
|
||||
if (_visibilityGroup is null)
|
||||
{
|
||||
if (_sceneSystem.SceneInstance.VisibilityGroups.Count == 0)
|
||||
return;
|
||||
|
||||
_visibilityGroup = _sceneSystem.SceneInstance.VisibilityGroups.First();
|
||||
if (_sceneSystem.GraphicsCompositor.RenderFeatures.OfType<SinglePassWireframeRenderFeature>().FirstOrDefault() is null)
|
||||
{
|
||||
_sceneSystem.GraphicsCompositor.RenderFeatures.Add(new SinglePassWireframeRenderFeature());
|
||||
}
|
||||
}
|
||||
|
||||
if (_latent)
|
||||
{
|
||||
Visible = true;
|
||||
|
||||
@@ -15,6 +15,8 @@ using Stride.Core.Mathematics;
|
||||
using Xunit;
|
||||
using Stride.Engine;
|
||||
using Stride.Graphics.Regression;
|
||||
using System.Threading.Tasks;
|
||||
using System.Threading;
|
||||
|
||||
namespace Stride.BepuPhysics.Tests
|
||||
{
|
||||
@@ -125,6 +127,56 @@ namespace Stride.BepuPhysics.Tests
|
||||
RunGameTest(game);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public static void ThreadContextTest()
|
||||
{
|
||||
var game = new GameTest();
|
||||
game.Script.AddTask(async () =>
|
||||
{
|
||||
var thread = Thread.CurrentThread;
|
||||
|
||||
game.ScreenShotAutomationEnabled = false;
|
||||
|
||||
var e1 = new BodyComponent { Collider = new CompoundCollider { Colliders = { new BoxCollider() } } };
|
||||
var e2 = new BodyComponent { Collider = new CompoundCollider { Colliders = { new BoxCollider() } } };
|
||||
var e3 = new BodyComponent { Collider = new CompoundCollider { Colliders = { new BoxCollider() } } };
|
||||
|
||||
game.SceneSystem.SceneInstance.RootScene.Entities.AddRange(new EntityComponent[] { e1, e2, e3 }.Select(x => new Entity { x }));
|
||||
|
||||
await Task.Run(() =>
|
||||
{
|
||||
Assert.NotEqual(Thread.CurrentThread, thread);
|
||||
});
|
||||
|
||||
Assert.Equal(Thread.CurrentThread, thread);
|
||||
|
||||
await e1.Simulation!.AfterUpdate();
|
||||
|
||||
Assert.Equal(Thread.CurrentThread, thread);
|
||||
|
||||
await Task.Run(() =>
|
||||
{
|
||||
Assert.NotEqual(Thread.CurrentThread, thread);
|
||||
});
|
||||
|
||||
Assert.Equal(Thread.CurrentThread, thread);
|
||||
|
||||
await e1.Simulation!.NextUpdate();
|
||||
|
||||
Assert.Equal(Thread.CurrentThread, thread);
|
||||
|
||||
await Task.Run(() =>
|
||||
{
|
||||
Assert.NotEqual(Thread.CurrentThread, thread);
|
||||
});
|
||||
|
||||
Assert.Equal(Thread.CurrentThread, thread);
|
||||
|
||||
game.Exit();
|
||||
});
|
||||
RunGameTest(game);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public static void ConstraintsForceTest()
|
||||
{
|
||||
@@ -293,7 +345,7 @@ namespace Stride.BepuPhysics.Tests
|
||||
while (contactE.Exit == false)
|
||||
await simulation.AfterUpdate();
|
||||
|
||||
Assert.NotEmpty(contactE.ImpactForces.Where(x => x.Length() > 100));
|
||||
Assert.Contains(contactE.ImpactForces, x => x.Length() > 100);
|
||||
|
||||
game.Exit();
|
||||
});
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user