Description:
Initial version of encompass addition (not fully working). (grafted from 22b0a67e9ee54a2f6d58f957f394be390407a2c7)
Commit status:
[Not Reviewed]
References:
Comments:
0 Commit comments 0 Inline Comments
Unresolved TODOs:
There are no unresolved TODOs
Add another comment

r169:0ac347404ea4 -

@@ -0,0 +1,27
1 {
2 // Use IntelliSense to find out which attributes exist for C# debugging
3 // Use hover for the description of the existing attributes
4 // For further information visit https://github.com/OmniSharp/omnisharp-vscode/blob/master/debugger-launchjson.md
5 "version": "0.2.0",
6 "configurations": [
7 {
8 "name": ".NET Core Launch (console)",
9 "type": "coreclr",
10 "request": "launch",
11 "preLaunchTask": "build",
12 // If you have changed target frameworks, make sure to update the program path.
13 "program": "${workspaceFolder}/test/bin/Debug/netcoreapp2.2/test.dll",
14 "args": [],
15 "cwd": "${workspaceFolder}/test",
16 // For more information about the 'console' field, see https://aka.ms/VSCode-CS-LaunchJson-Console
17 "console": "internalConsole",
18 "stopAtEntry": false
19 },
20 {
21 "name": ".NET Core Attach",
22 "type": "coreclr",
23 "request": "attach",
24 "processId": "${command:pickProcess}"
25 }
26 ]
27 } No newline at end of file
@@ -0,0 +1,4
1 {
2 // "dotnet-test-explorer.testProjectPath": "test"
3 // "omnisharp.path": "latest"
4 }
@@ -0,0 +1,36
1 {
2 "version": "2.0.0",
3 "tasks": [
4 {
5 "label": "build",
6 "command": "dotnet",
7 "type": "process",
8 "args": [
9 "build",
10 "${workspaceFolder}/test/test.csproj"
11 ],
12 "problemMatcher": "$tsc"
13 },
14 {
15 "label": "publish",
16 "command": "dotnet",
17 "type": "process",
18 "args": [
19 "publish",
20 "${workspaceFolder}/test/test.csproj"
21 ],
22 "problemMatcher": "$tsc"
23 },
24 {
25 "label": "watch",
26 "command": "dotnet",
27 "type": "process",
28 "args": [
29 "watch",
30 "run",
31 "${workspaceFolder}/test/test.csproj"
32 ],
33 "problemMatcher": "$tsc"
34 }
35 ]
36 } No newline at end of file
@@ -0,0 +1,51
1 
2 Microsoft Visual Studio Solution File, Format Version 12.00
3 # Visual Studio 15
4 VisualStudioVersion = 15.0.26124.0
5 MinimumVisualStudioVersion = 15.0.26124.0
6 Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "encompass-cs", "encompass-cs\encompass-cs.csproj", "{B862FC25-0740-4CEA-BC53-3C5F43DCD985}"
7 EndProject
8 Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "test", "test\test.csproj", "{23D6C29C-A310-4D3E-8455-8D0B8CA1FC06}"
9 EndProject
10 Global
11 GlobalSection(SolutionConfigurationPlatforms) = preSolution
12 Debug|Any CPU = Debug|Any CPU
13 Debug|x64 = Debug|x64
14 Debug|x86 = Debug|x86
15 Release|Any CPU = Release|Any CPU
16 Release|x64 = Release|x64
17 Release|x86 = Release|x86
18 EndGlobalSection
19 GlobalSection(ProjectConfigurationPlatforms) = postSolution
20 {B862FC25-0740-4CEA-BC53-3C5F43DCD985}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
21 {B862FC25-0740-4CEA-BC53-3C5F43DCD985}.Debug|Any CPU.Build.0 = Debug|Any CPU
22 {B862FC25-0740-4CEA-BC53-3C5F43DCD985}.Debug|x64.ActiveCfg = Debug|Any CPU
23 {B862FC25-0740-4CEA-BC53-3C5F43DCD985}.Debug|x64.Build.0 = Debug|Any CPU
24 {B862FC25-0740-4CEA-BC53-3C5F43DCD985}.Debug|x86.ActiveCfg = Debug|Any CPU
25 {B862FC25-0740-4CEA-BC53-3C5F43DCD985}.Debug|x86.Build.0 = Debug|Any CPU
26 {B862FC25-0740-4CEA-BC53-3C5F43DCD985}.Release|Any CPU.ActiveCfg = Release|Any CPU
27 {B862FC25-0740-4CEA-BC53-3C5F43DCD985}.Release|Any CPU.Build.0 = Release|Any CPU
28 {B862FC25-0740-4CEA-BC53-3C5F43DCD985}.Release|x64.ActiveCfg = Release|Any CPU
29 {B862FC25-0740-4CEA-BC53-3C5F43DCD985}.Release|x64.Build.0 = Release|Any CPU
30 {B862FC25-0740-4CEA-BC53-3C5F43DCD985}.Release|x86.ActiveCfg = Release|Any CPU
31 {B862FC25-0740-4CEA-BC53-3C5F43DCD985}.Release|x86.Build.0 = Release|Any CPU
32 {23D6C29C-A310-4D3E-8455-8D0B8CA1FC06}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
33 {23D6C29C-A310-4D3E-8455-8D0B8CA1FC06}.Debug|Any CPU.Build.0 = Debug|Any CPU
34 {23D6C29C-A310-4D3E-8455-8D0B8CA1FC06}.Debug|x64.ActiveCfg = Debug|Any CPU
35 {23D6C29C-A310-4D3E-8455-8D0B8CA1FC06}.Debug|x64.Build.0 = Debug|Any CPU
36 {23D6C29C-A310-4D3E-8455-8D0B8CA1FC06}.Debug|x86.ActiveCfg = Debug|Any CPU
37 {23D6C29C-A310-4D3E-8455-8D0B8CA1FC06}.Debug|x86.Build.0 = Debug|Any CPU
38 {23D6C29C-A310-4D3E-8455-8D0B8CA1FC06}.Release|Any CPU.ActiveCfg = Release|Any CPU
39 {23D6C29C-A310-4D3E-8455-8D0B8CA1FC06}.Release|Any CPU.Build.0 = Release|Any CPU
40 {23D6C29C-A310-4D3E-8455-8D0B8CA1FC06}.Release|x64.ActiveCfg = Release|Any CPU
41 {23D6C29C-A310-4D3E-8455-8D0B8CA1FC06}.Release|x64.Build.0 = Release|Any CPU
42 {23D6C29C-A310-4D3E-8455-8D0B8CA1FC06}.Release|x86.ActiveCfg = Release|Any CPU
43 {23D6C29C-A310-4D3E-8455-8D0B8CA1FC06}.Release|x86.Build.0 = Release|Any CPU
44 EndGlobalSection
45 GlobalSection(SolutionProperties) = preSolution
46 HideSolutionNode = FALSE
47 EndGlobalSection
48 GlobalSection(ExtensibilityGlobals) = postSolution
49 SolutionGuid = {245F6C1B-676C-424E-80D9-518029E7B594}
50 EndGlobalSection
51 EndGlobal
@@ -0,0 +1,225
1 # The following command works for downloading when using Git for Windows:
2 # curl -LOf http://gist.githubusercontent.com/kmorcinek/2710267/raw/.gitignore
3 #
4 # Download this file using PowerShell v3 under Windows with the following comand:
5 # Invoke-WebRequest https://gist.githubusercontent.com/kmorcinek/2710267/raw/ -OutFile .gitignore
6 #
7 # or wget:
8 # wget --no-check-certificate http://gist.githubusercontent.com/kmorcinek/2710267/raw/.gitignore
9
10 # User-specific files
11 *.suo
12 *.user
13 *.sln.docstates
14
15 # Build results
16 [Dd]ebug/
17 [Rr]elease/
18 x64/
19 [Bb]in/
20 [Oo]bj/
21 # build folder is nowadays used for build scripts and should not be ignored
22 #build/
23
24 # NuGet Packages
25 *.nupkg
26 # The packages folder can be ignored because of Package Restore
27 **/packages/*
28 # except build/, which is used as an MSBuild target.
29 !**/packages/build/
30 # Uncomment if necessary however generally it will be regenerated when needed
31 #!**/packages/repositories.config
32
33 # MSTest test Results
34 [Tt]est[Rr]esult*/
35 [Bb]uild[Ll]og.*
36
37 *_i.c
38 *_p.c
39 *.ilk
40 *.meta
41 *.obj
42 *.pch
43 *.pdb
44 *.pgc
45 *.pgd
46 *.rsp
47 *.sbr
48 *.tlb
49 *.tli
50 *.tlh
51 *.tmp
52 *.tmp_proj
53 *.log
54 *.vspscc
55 *.vssscc
56 .builds
57 *.pidb
58 *.log
59 *.scc
60
61 # OS generated files #
62 .DS_Store*
63 Icon?
64
65 # Visual C++ cache files
66 ipch/
67 *.aps
68 *.ncb
69 *.opensdf
70 *.sdf
71 *.cachefile
72
73 # Visual Studio profiler
74 *.psess
75 *.vsp
76 *.vspx
77
78 # Guidance Automation Toolkit
79 *.gpState
80
81 # ReSharper is a .NET coding add-in
82 _ReSharper*/
83 *.[Rr]e[Ss]harper
84
85 # TeamCity is a build add-in
86 _TeamCity*
87
88 # DotCover is a Code Coverage Tool
89 *.dotCover
90
91 # NCrunch
92 *.ncrunch*
93 .*crunch*.local.xml
94
95 # Installshield output folder
96 [Ee]xpress/
97
98 # DocProject is a documentation generator add-in
99 DocProject/buildhelp/
100 DocProject/Help/*.HxT
101 DocProject/Help/*.HxC
102 DocProject/Help/*.hhc
103 DocProject/Help/*.hhk
104 DocProject/Help/*.hhp
105 DocProject/Help/Html2
106 DocProject/Help/html
107
108 # Click-Once directory
109 publish/
110
111 # Publish Web Output
112 *.Publish.xml
113
114 # Windows Azure Build Output
115 csx
116 *.build.csdef
117
118 # Windows Store app package directory
119 AppPackages/
120
121 # Others
122 *.Cache
123 ClientBin/
124 [Ss]tyle[Cc]op.*
125 ~$*
126 *~
127 *.dbmdl
128 *.[Pp]ublish.xml
129 *.pfx
130 *.publishsettings
131 modulesbin/
132 tempbin/
133
134 # EPiServer Site file (VPP)
135 AppData/
136
137 # RIA/Silverlight projects
138 Generated_Code/
139
140 # Backup & report files from converting an old project file to a newer
141 # Visual Studio version. Backup files are not needed, because we have git ;-)
142 _UpgradeReport_Files/
143 Backup*/
144 UpgradeLog*.XML
145 UpgradeLog*.htm
146
147 # vim
148 *.txt~
149 *.swp
150 *.swo
151
152 # Temp files when opening LibreOffice on ubuntu
153 .~lock.*
154
155 # svn
156 .svn
157
158 # CVS - Source Control
159 **/CVS/
160
161 # Remainings from resolving conflicts in Source Control
162 *.orig
163
164 # SQL Server files
165 **/App_Data/*.mdf
166 **/App_Data/*.ldf
167 **/App_Data/*.sdf
168
169
170 #LightSwitch generated files
171 GeneratedArtifacts/
172 _Pvt_Extensions/
173 ModelManifest.xml
174
175 # =========================
176 # Windows detritus
177 # =========================
178
179 # Windows image file caches
180 Thumbs.db
181 ehthumbs.db
182
183 # Folder config file
184 Desktop.ini
185
186 # Recycle Bin used on file shares
187 $RECYCLE.BIN/
188
189 # Mac desktop service store files
190 .DS_Store
191
192 # SASS Compiler cache
193 .sass-cache
194
195 # Visual Studio 2014 CTP
196 **/*.sln.ide
197
198 # Visual Studio temp something
199 .vs/
200
201 # dotnet stuff
202 project.lock.json
203
204 # VS 2015+
205 *.vc.vc.opendb
206 *.vc.db
207
208 # Rider
209 .idea/
210
211 # Visual Studio Code
212 .vscode/
213
214 # Output folder used by Webpack or other FE stuff
215 **/node_modules/*
216 **/wwwroot/*
217
218 # SpecFlow specific
219 *.feature.cs
220 *.feature.xlsx.*
221 *.Specs_*.html
222
223 #####
224 # End of core ignore list, below put you custom 'per project' settings (patterns or path)
225 #####
@@ -0,0 +1,15
1 using System;
2
3 namespace Encompass
4 {
5 [AttributeUsage(AttributeTargets.Class)]
6 public class DefaultWritePriority : Attribute
7 {
8 public int WritePriority { get; }
9
10 public DefaultWritePriority(int writePriority)
11 {
12 WritePriority = writePriority;
13 }
14 }
15 }
@@ -0,0 +1,7
1 using System;
2
3 namespace Encompass
4 {
5 [AttributeUsage(AttributeTargets.Class)]
6 public class IgnoresTimeDilation : Attribute { }
7 }
@@ -0,0 +1,20
1 using Encompass.Exceptions;
2 using System;
3 using System.Collections.Generic;
4
5 namespace Encompass
6 {
7 [AttributeUsage(AttributeTargets.Class)]
8 public class QueryWith : Attribute
9 {
10 public readonly HashSet<Type> QueryWithTypes = new HashSet<Type>();
11
12 public QueryWith(params Type[] queryWithTypes)
13 {
14 foreach (var queryWithType in queryWithTypes)
15 {
16 QueryWithTypes.Add(queryWithType);
17 }
18 }
19 }
20 }
@@ -0,0 +1,19
1 using System;
2 using System.Collections.Generic;
3
4 namespace Encompass
5 {
6 [AttributeUsage(AttributeTargets.Class)]
7 public class QueryWithout : Attribute
8 {
9 public readonly HashSet<Type> QueryWithoutTypes = new HashSet<Type>();
10
11 public QueryWithout(params Type[] queryWithoutTypes)
12 {
13 foreach (var type in queryWithoutTypes)
14 {
15 QueryWithoutTypes.Add(type);
16 }
17 }
18 }
19 }
@@ -0,0 +1,19
1 using System;
2 using System.Collections.Generic;
3
4 namespace Encompass
5 {
6 [AttributeUsage(AttributeTargets.Class)]
7 public class Reads : Attribute
8 {
9 public readonly HashSet<Type> ReadTypes = new HashSet<Type>();
10
11 public Reads(params Type[] readTypes)
12 {
13 foreach (var readType in readTypes)
14 {
15 ReadTypes.Add(readType);
16 }
17 }
18 }
19 }
@@ -0,0 +1,19
1 using System;
2 using System.Collections.Generic;
3
4 namespace Encompass
5 {
6 [AttributeUsage(AttributeTargets.Class)]
7 public class ReadsImmediate : Attribute
8 {
9 public readonly HashSet<Type> ReadImmediateTypes = new HashSet<Type>();
10
11 public ReadsImmediate(params Type[] readImmediateTypes)
12 {
13 foreach (var readImmediateType in readImmediateTypes)
14 {
15 ReadImmediateTypes.Add(readImmediateType);
16 }
17 }
18 }
19 }
@@ -0,0 +1,27
1 using System;
2 using System.Collections.Generic;
3 using System.Linq;
4 using Encompass.Exceptions;
5
6 namespace Encompass
7 {
8 [AttributeUsage(AttributeTargets.Class)]
9 public class Receives : Attribute
10 {
11 public readonly HashSet<Type> ReceiveTypes;
12
13 public Receives(params Type[] receiveTypes)
14 {
15 foreach (var receiveType in receiveTypes)
16 {
17 var isMessage = receiveType.GetInterfaces().Contains(typeof(IMessage));
18 if (!isMessage)
19 {
20 throw new IllegalSendTypeException("{0} must be a Message", receiveType.Name);
21 }
22 }
23
24 ReceiveTypes = new HashSet<Type>(receiveTypes);
25 }
26 }
27 }
@@ -0,0 +1,27
1 using System;
2 using System.Collections.Generic;
3 using System.Linq;
4 using Encompass.Exceptions;
5
6 namespace Encompass
7 {
8 [AttributeUsage(AttributeTargets.Class)]
9 public class Sends : Attribute
10 {
11 public readonly HashSet<Type> SendTypes;
12
13 public Sends(params Type[] sendTypes)
14 {
15 foreach (var sendType in sendTypes)
16 {
17 var isMessage = sendType.GetInterfaces().Contains(typeof(IMessage));
18 if (!isMessage)
19 {
20 throw new IllegalSendTypeException("{0} must be a Message", sendType.Name);
21 }
22 }
23
24 this.SendTypes = new HashSet<Type>(sendTypes);
25 }
26 }
27 }
@@ -0,0 +1,26
1 using System;
2 using System.Collections.Generic;
3
4 namespace Encompass
5 {
6 [AttributeUsage(AttributeTargets.Class, AllowMultiple = true)]
7 public class Writes : Attribute
8 {
9 public readonly HashSet<Type> WriteTypes = new HashSet<Type>();
10 public readonly Dictionary<Type, int> Priorities = new Dictionary<Type, int>();
11
12 public Writes(params Type[] writeTypes)
13 {
14 foreach (var writeType in writeTypes)
15 {
16 WriteTypes.Add(writeType);
17 }
18 }
19
20 public Writes(Type writeType, int priority)
21 {
22 WriteTypes.Add(writeType);
23 Priorities.Add(writeType, priority);
24 }
25 }
26 }
@@ -0,0 +1,19
1 using System;
2 using System.Collections.Generic;
3
4 namespace Encompass
5 {
6 [AttributeUsage(AttributeTargets.Class)]
7 public class WritesImmediate : Attribute
8 {
9 public readonly HashSet<Type> WriteImmediateTypes = new HashSet<Type>();
10
11 public WritesImmediate(params Type[] writeImmediateTypes)
12 {
13 foreach (var writeImmediateType in writeImmediateTypes)
14 {
15 WriteImmediateTypes.Add(writeImmediateType);
16 }
17 }
18 }
19 }
@@ -0,0 +1,54
1 using MoonTools.FastCollections;
2 using System;
3 using System.Collections.Generic;
4
5 namespace Encompass
6 {
7 internal class ComponentBitSet
8 {
9 private readonly Dictionary<int, BitSet512> _entities = new Dictionary<int, BitSet512>();
10 private readonly Dictionary<Type, int> _typeToIndex;
11
12 public ComponentBitSet(Dictionary<Type, int> typeToIndex)
13 {
14 _typeToIndex = typeToIndex;
15 }
16
17 public void Clear()
18 {
19 _entities.Clear();
20 }
21
22 public void AddEntity(int entityID)
23 {
24 _entities.Add(entityID, BitSet512.Zero);
25 }
26
27 public void Set<TComponent>(int entityID) where TComponent : struct
28 {
29 if (!_entities.ContainsKey(entityID)) { AddEntity(entityID); }
30 _entities[entityID] = _entities[entityID].Set(_typeToIndex[typeof(TComponent)]);
31 }
32
33 public void RemoveComponent<TComponent>(int entityID) where TComponent : struct
34 {
35 if (_entities.ContainsKey(entityID))
36 {
37 _entities[entityID] = _entities[entityID].UnSet(_typeToIndex[typeof(TComponent)]);
38 }
39 }
40
41 public void RemoveEntity(int entityID)
42 {
43 if (_entities.ContainsKey(entityID))
44 {
45 _entities.Remove(entityID);
46 }
47 }
48
49 public BitSet512 EntityBitArray(int entityID)
50 {
51 return _entities.ContainsKey(entityID) ? _entities[entityID] : BitSet512.Zero;
52 }
53 }
54 }
@@ -0,0 +1,92
1 using System;
2 using System.Collections.Generic;
3
4 namespace Encompass
5 {
6 internal class ComponentDeltaStore
7 {
8 private readonly ComponentStore _store;
9 private readonly Dictionary<Type, Replayer> _replayers = new Dictionary<Type, Replayer>();
10 private readonly HashSet<Replayer> _currentReplayers = new HashSet<Replayer>();
11
12 public IEnumerable<Replayer> CurrentReplayers { get { return _currentReplayers; } }
13
14 public ComponentDeltaStore(Dictionary<Type, int> typeToIndex)
15 {
16 _store = new ComponentStore(typeToIndex);
17 }
18
19 public void RegisterComponentType<TComponent>() where TComponent : struct
20 {
21 _store.RegisterComponentType<TComponent>();
22 if (!_replayers.ContainsKey(typeof(TComponent)))
23 {
24 _replayers.Add(typeof(TComponent), new Replayer<TComponent>(this));
25 }
26 }
27
28 public ref readonly TComponent GetComponent<TComponent>(int entityID) where TComponent : struct
29 {
30 return ref _store.Get<TComponent>(entityID);
31 }
32
33 public void Set<TComponent>(int entityID, in TComponent component) where TComponent : struct
34 {
35 _store.Set(entityID, component);
36 RegisterComponentType<TComponent>();
37 var replayer = _replayers[typeof(TComponent)];
38 _currentReplayers.Add(replayer);
39 replayer.UnMarkRemoval(entityID);
40 }
41
42 public bool Set<TComponent>(int entityID, TComponent component, int priority) where TComponent : struct
43 {
44 var result = _store.Set(entityID, component, priority);
45 if (result)
46 {
47 RegisterComponentType<TComponent>();
48 var replayer = _replayers[typeof(TComponent)];
49 _currentReplayers.Add(replayer);
50 replayer.UnMarkRemoval(entityID);
51 }
52 return result;
53 }
54
55 public bool Remove<TComponent>(int entityID, int priority) where TComponent : struct
56 {
57 var result = _store.Remove<TComponent>(entityID, priority);
58 if (result)
59 {
60 RegisterComponentType<TComponent>();
61 var replayer = _replayers[typeof(TComponent)];
62 _currentReplayers.Add(replayer);
63 replayer.MarkRemoval(entityID);
64 }
65 return result;
66 }
67
68 public void Remove(int entityID)
69 {
70 _store.Remove(entityID);
71 foreach (var replayer in CurrentReplayers)
72 {
73 replayer.MarkRemoval(entityID);
74 }
75 }
76
77 public Span<Entity> AllEntities<TComponent>() where TComponent : struct
78 {
79 return _store.AllEntities<TComponent>();
80 }
81
82 public void ClearAll()
83 {
84 _store.ClearAll();
85 foreach (var replayer in _currentReplayers)
86 {
87 replayer.Clear();
88 }
89 _currentReplayers.Clear();
90 }
91 }
92 }
@@ -0,0 +1,162
1 using Encompass.Exceptions;
2 using MoonTools.FastCollections;
3 using System;
4 using System.Collections.Generic;
5
6 namespace Encompass
7 {
8 internal sealed class ComponentStore
9 {
10 private Dictionary<Type, TypedComponentStore> _stores = new Dictionary<Type, TypedComponentStore>(512);
11 public ComponentBitSet ComponentBitSet { get; private set; }
12 private readonly Dictionary<Type, int> _typeToIndex;
13
14 public ComponentStore(Dictionary<Type, int> typeToIndex)
15 {
16 _typeToIndex = typeToIndex;
17 ComponentBitSet = new ComponentBitSet(typeToIndex);
18 }
19
20 public void RegisterComponentType<TComponent>() where TComponent : struct
21 {
22 if (!_stores.ContainsKey(typeof(TComponent)))
23 {
24 var store = new TypedComponentStore<TComponent>();
25 _stores.Add(typeof(TComponent), store);
26 }
27
28 if (!_typeToIndex.ContainsKey(typeof(TComponent))) { _typeToIndex.Add(typeof(TComponent), _typeToIndex.Count); }
29 }
30
31 private TypedComponentStore<TComponent> Lookup<TComponent>() where TComponent : struct
32 {
33 return _stores[typeof(TComponent)] as TypedComponentStore<TComponent>;
34 }
35
36 public bool Has<TComponent>(int entityID) where TComponent : struct
37 {
38 return Lookup<TComponent>().Has(entityID);
39 }
40
41 public bool Has(Type type, int entityID)
42 {
43 return _stores[type].Has(entityID);
44 }
45
46 public BitSet512 EntityBitArray(int entityID)
47 {
48 return ComponentBitSet.EntityBitArray(entityID);
49 }
50
51 public ref TComponent Get<TComponent>(int entityID) where TComponent : struct
52 {
53 return ref Lookup<TComponent>().Get(entityID);
54 }
55
56 public ref readonly TComponent Singular<TComponent>() where TComponent : struct
57 {
58 if (!Any<TComponent>()) { throw new NoComponentOfTypeException("No component of type {0} exists", typeof(TComponent).Name); }
59
60 return ref Lookup<TComponent>().Singular();
61 }
62
63 public ref readonly Entity SingularEntity<TComponent>() where TComponent : struct
64 {
65 if (!Any<TComponent>()) { throw new NoComponentOfTypeException("No component of type {0} exists", typeof(TComponent).Name); }
66
67 return ref Lookup<TComponent>().SingularEntity();
68 }
69
70 public void Set<TComponent>(int entityID, in TComponent component) where TComponent : struct
71 {
72 Lookup<TComponent>().Set(entityID, component);
73 ComponentBitSet.Set<TComponent>(entityID);
74 }
75
76 public bool Set<TComponent>(int entityID, in TComponent component, int priority) where TComponent : struct
77 {
78 if (Lookup<TComponent>().Set(entityID, component, priority))
79 {
80 ComponentBitSet.Set<TComponent>(entityID);
81 return true;
82 }
83 return false;
84 }
85
86 public bool Remove<TComponent>(int entityID, int priority) where TComponent : struct
87 {
88 if (Lookup<TComponent>().Remove(entityID, priority))
89 {
90 ComponentBitSet.RemoveComponent<TComponent>(entityID);
91 return true;
92 }
93 return false;
94 }
95
96 public void ForceRemove<TComponent>(int entityID) where TComponent : struct
97 {
98 Lookup<TComponent>().ForceRemove(entityID);
99 ComponentBitSet.RemoveComponent<TComponent>(entityID);
100 }
101
102 public void Remove(int entityID)
103 {
104 foreach (var entry in _stores.Values)
105 {
106 entry.ForceRemove(entityID);
107 }
108 ComponentBitSet.RemoveEntity(entityID);
109 }
110
111 public bool Any<TComponent>() where TComponent : struct
112 {
113 return Lookup<TComponent>().Count > 0;
114 }
115
116 public Span<TComponent> All<TComponent>() where TComponent : struct
117 {
118 return Lookup<TComponent>().AllComponents();
119 }
120
121 public Span<Entity> AllEntities<TComponent>() where TComponent : struct
122 {
123 return Lookup<TComponent>().AllEntities();
124 }
125
126 public void Clear<TComponent>() where TComponent : struct
127 {
128 Lookup<TComponent>().Clear();
129 }
130
131 public void ClearAllPriorities()
132 {
133 foreach (var store in _stores.Values)
134 {
135 store.ClearPriorities();
136 }
137 }
138
139 public void ClearAll()
140 {
141 ComponentBitSet.Clear();
142 foreach (var store in _stores.Values)
143 {
144 store.Clear();
145 }
146 }
147
148 public void SwapWith(ComponentStore other)
149 {
150 (_stores, other._stores) = (other._stores, _stores);
151 (ComponentBitSet, other.ComponentBitSet) = (other.ComponentBitSet, ComponentBitSet);
152 }
153
154 public void UpdateUsing(ComponentDeltaStore delta)
155 {
156 foreach (var replayer in delta.CurrentReplayers)
157 {
158 replayer.Replay(this);
159 }
160 }
161 }
162 }
@@ -0,0 +1,85
1 using System;
2 using System.Collections.Generic;
3 using Encompass.Exceptions;
4
5 namespace Encompass
6 {
7 internal class MessageStore
8 {
9 private readonly Dictionary<Type, TypedMessageStore> _stores = new Dictionary<Type, TypedMessageStore>(512);
10
11 private void RegisterMessageType<TMessage>() where TMessage : struct, IMessage
12 {
13 _stores.Add(typeof(TMessage), new TypedMessageStore<TMessage>());
14 }
15
16 private TypedMessageStore<TMessage> Lookup<TMessage>() where TMessage : struct, IMessage
17 {
18 if (!_stores.ContainsKey(typeof(TMessage))) { RegisterMessageType<TMessage>(); }
19 return _stores[typeof(TMessage)] as TypedMessageStore<TMessage>;
20 }
21
22 public void AddMessage<TMessage>(in TMessage message) where TMessage : struct, IMessage
23 {
24 Lookup<TMessage>().Add(message);
25 }
26
27 public void AddMessage<TMessage>(in TMessage message, double time) where TMessage : struct, IMessage
28 {
29 Lookup<TMessage>().Add(message, time);
30 }
31
32 public void AddMessageIgnoringTimeDilation<TMessage>(in TMessage message, double time) where TMessage : struct, IMessage
33 {
34 Lookup<TMessage>().AddIgnoringTimeDilation(message, time);
35 }
36
37 public ref readonly TMessage First<TMessage>() where TMessage : struct, IMessage
38 {
39 if (!Any<TMessage>()) { throw new NoMessageOfTypeException("No Message of type {0} exists", typeof(TMessage).Name); }
40
41 return ref Lookup<TMessage>().First();
42 }
43
44 public Span<TMessage> All<TMessage>() where TMessage : struct, IMessage
45 {
46 return Lookup<TMessage>().All();
47 }
48
49 public bool Any<TMessage>() where TMessage : struct, IMessage
50 {
51 return Lookup<TMessage>().Any();
52 }
53
54 public IEnumerable<TMessage> WithEntity<TMessage>(int entityID) where TMessage : struct, IMessage, IHasEntity
55 {
56 return Lookup<TMessage>().WithEntity(entityID);
57 }
58
59 public ref readonly TMessage FirstWithEntity<TMessage>(int entityID) where TMessage : struct, IMessage
60 {
61 return ref Lookup<TMessage>().FirstWithEntity(entityID);
62 }
63
64 public bool SomeWithEntity<TMessage>(int entityID) where TMessage : struct, IMessage, IHasEntity
65 {
66 return Lookup<TMessage>().SomeWithEntity(entityID);
67 }
68
69 public void ProcessDelayedMessages(double dilatedDelta, double realtimeDelta)
70 {
71 foreach (var store in _stores.Values)
72 {
73 store.ProcessDelayedMessages(dilatedDelta, realtimeDelta);
74 }
75 }
76
77 public void ClearAll()
78 {
79 foreach (var store in _stores.Values)
80 {
81 store.Clear();
82 }
83 }
84 }
85 }
@@ -0,0 +1,52
1 using System.Collections.Generic;
2
3 namespace Encompass
4 {
5 internal abstract class Replayer
6 {
7 public abstract void Replay(ComponentStore store);
8 public abstract void MarkRemoval(int entityID);
9 public abstract void UnMarkRemoval(int entityID);
10 public abstract void Clear();
11 }
12
13 internal class Replayer<TComponent> : Replayer where TComponent : struct
14 {
15 private readonly ComponentDeltaStore _deltaStore;
16 private readonly HashSet<int> _removals = new HashSet<int>();
17
18 public Replayer(ComponentDeltaStore componentStore)
19 {
20 _deltaStore = componentStore;
21 }
22
23 public override void Replay(ComponentStore store)
24 {
25 foreach (ref readonly var entity in _deltaStore.AllEntities<TComponent>())
26 {
27 ref readonly var component = ref _deltaStore.GetComponent<TComponent>(entity.ID);
28 store.Set(entity.ID, component);
29 }
30
31 foreach (var entityID in _removals)
32 {
33 store.ForceRemove<TComponent>(entityID);
34 }
35 }
36
37 public override void Clear()
38 {
39 _removals.Clear();
40 }
41
42 public override void MarkRemoval(int entityID)
43 {
44 _removals.Add(entityID);
45 }
46
47 public override void UnMarkRemoval(int entityID)
48 {
49 _removals.Remove(entityID);
50 }
51 }
52 }
@@ -0,0 +1,140
1 using System;
2 using System.Collections.Generic;
3 using System.Runtime.CompilerServices;
4
5 namespace Encompass
6 {
7 internal abstract class TypedComponentStore
8 {
9 public abstract int Count { get; }
10 public abstract bool Has(int entity);
11 public abstract bool Remove(int entity, int priority);
12 public abstract void ForceRemove(int entity);
13 public abstract void Clear();
14 public abstract void ClearPriorities();
15 }
16
17 internal class TypedComponentStore<TComponent> : TypedComponentStore where TComponent : struct
18 {
19 private int _nextID = 0;
20 private readonly Dictionary<int, int> _entityIDToStorageIndex = new Dictionary<int, int>(128);
21 private readonly Dictionary<int, int> _priorities = new Dictionary<int, int>(128);
22 private Entity[] _storageIndexToEntities = new Entity[128];
23 private TComponent[] _components = new TComponent[128];
24
25 public override int Count { get => _entityIDToStorageIndex.Count; }
26
27 public ref TComponent Get(int entityID)
28 {
29 if (!_entityIDToStorageIndex.ContainsKey(entityID)) { throw new Exceptions.NoComponentOfTypeOnEntityException("No component of type {0} exists on Entity with ID {1}", typeof(TComponent), entityID); }
30 return ref _components[_entityIDToStorageIndex[entityID]];
31 }
32
33 public ref TComponent Singular()
34 {
35 return ref _components[0];
36 }
37
38 public ref Entity SingularEntity()
39 {
40 return ref _storageIndexToEntities[0];
41 }
42
43 public void Set(int entityID, in TComponent component)
44 {
45 InternalSet(entityID, component);
46 }
47
48 public bool Set(int entityID, in TComponent component, int priority)
49 {
50 if (!_priorities.ContainsKey(entityID) || priority <= _priorities[entityID]) // if priorities are equal that means it's the same engine
51 {
52 InternalSet(entityID, component);
53 _priorities[entityID] = priority;
54 return true;
55 }
56
57 return false;
58 }
59
60 private void InternalSet(int entityID, in TComponent component)
61 {
62 if (!_entityIDToStorageIndex.ContainsKey(entityID))
63 {
64 var index = _nextID++;
65 if (index >= _components.Length)
66 {
67 System.Array.Resize(ref _components, _components.Length * 2);
68 System.Array.Resize(ref _storageIndexToEntities, _storageIndexToEntities.Length * 2);
69 }
70 _entityIDToStorageIndex[entityID] = index;
71 _storageIndexToEntities[index] = new Entity(entityID);
72 }
73
74 _components[_entityIDToStorageIndex[entityID]] = component;
75 }
76
77 public override bool Remove(int entityID, int priority)
78 {
79 if (!_priorities.ContainsKey(entityID) || priority <= _priorities[entityID]) // if priorities are equal that means it's the same engine
80 {
81 _priorities[entityID] = priority;
82 ForceRemove(entityID);
83 return true;
84 }
85
86 return false;
87 }
88
89 public override void ForceRemove(int entityID)
90 {
91 if (_entityIDToStorageIndex.ContainsKey(entityID))
92 {
93 var storageIndex = _entityIDToStorageIndex[entityID];
94 _entityIDToStorageIndex.Remove(entityID);
95 _priorities.Remove(entityID);
96
97 // move a component into the hole to maintain contiguous memory
98 if (_nextID > 1 && storageIndex != _nextID - 1)
99 {
100 var lastStorageIndex = _nextID - 1;
101 ref readonly var lastEntity = ref _storageIndexToEntities[lastStorageIndex];
102
103 _entityIDToStorageIndex[lastEntity.ID] = storageIndex;
104 _storageIndexToEntities[storageIndex] = lastEntity;
105 _components[storageIndex] = _components[lastStorageIndex];
106
107 }
108
109 _nextID--;
110 }
111 }
112
113 public override bool Has(int entityID)
114 {
115 return _entityIDToStorageIndex.ContainsKey(entityID);
116 }
117
118 public override void Clear()
119 {
120 _nextID = 0;
121 _entityIDToStorageIndex.Clear();
122 _priorities.Clear();
123 }
124
125 public override void ClearPriorities()
126 {
127 _priorities.Clear();
128 }
129
130 public Span<Entity> AllEntities()
131 {
132 return new Span<Entity>(_storageIndexToEntities, 0, _nextID);
133 }
134
135 public Span<TComponent> AllComponents()
136 {
137 return new Span<TComponent>(_components, 0, _nextID);
138 }
139 }
140 }
@@ -0,0 +1,130
1 using System;
2 using System.Collections.Generic;
3
4 namespace Encompass
5 {
6 internal abstract class TypedMessageStore
7 {
8 public abstract void ProcessDelayedMessages(double dilatedDelta, double realtimeDelta);
9 public abstract void Clear();
10 }
11
12 internal class TypedMessageStore<TMessage> : TypedMessageStore where TMessage : struct, IMessage
13 {
14 // messages are placed in a contiguous region
15 // so we can return the collection as a Span
16 private int _nextIndex = 0;
17 private TMessage[] _store = new TMessage[128];
18 private readonly List<(TMessage, double)> _delayedStore = new List<(TMessage, double)>(128);
19 private readonly List<(TMessage, double)> _delayedStoreIgnoringTimeDilation = new List<(TMessage, double)>(128);
20 private readonly Dictionary<int, List<int>> _entityToIndices = new Dictionary<int, List<int>>();
21
22 public override void ProcessDelayedMessages(double dilatedDelta, double realtimeDelta)
23 {
24 for (var i = _delayedStore.Count - 1; i >= 0; i--)
25 {
26 var (message, time) = _delayedStore[i];
27
28 var updatedTime = time - dilatedDelta;
29
30 if (updatedTime <= 0)
31 {
32 Add(message);
33 _delayedStore.RemoveAt(i);
34 }
35 else
36 {
37 _delayedStore[i] = (message, updatedTime);
38 }
39 }
40
41 for (var i = _delayedStoreIgnoringTimeDilation.Count - 1; i >= 0; i--)
42 {
43 var (message, time) = _delayedStoreIgnoringTimeDilation[i];
44
45 var updatedTime = time - realtimeDelta;
46
47 if (updatedTime <= 0)
48 {
49 Add(message);
50 _delayedStoreIgnoringTimeDilation.RemoveAt(i);
51 }
52 else
53 {
54 _delayedStoreIgnoringTimeDilation[i] = (message, updatedTime);
55 }
56 }
57 }
58
59 public void Add(in TMessage message)
60 {
61 var index = _nextIndex++;
62 if (index >= _store.Length)
63 {
64 Array.Resize(ref _store, _store.Length * 2);
65 }
66 _store[index] = message;
67 if (message is IHasEntity entityMessage)
68 {
69 var entityID = entityMessage.Entity.ID;
70 if (!_entityToIndices.ContainsKey(entityID)) { _entityToIndices.Add(entityID, new List<int>()); }
71 _entityToIndices[entityID].Add(index);
72 }
73 }
74
75 public void Add(in TMessage message, double time)
76 {
77 _delayedStore.Add((message, time));
78 }
79
80 public void AddIgnoringTimeDilation(in TMessage message, double time)
81 {
82 _delayedStoreIgnoringTimeDilation.Add((message, time));
83 }
84
85 public ref readonly TMessage First()
86 {
87 return ref _store[0];
88 }
89
90 public bool Any()
91 {
92 return _nextIndex != 0;
93 }
94
95 public Span<TMessage> All()
96 {
97 return new Span<TMessage>(_store, 0, _nextIndex);
98 }
99
100 public IEnumerable<TMessage> WithEntity(int entityID)
101 {
102 if (_entityToIndices.ContainsKey(entityID))
103 {
104 foreach (var index in _entityToIndices[entityID])
105 {
106 yield return _store[index];
107 }
108 }
109 }
110
111 public ref readonly TMessage FirstWithEntity(int entityID)
112 {
113 return ref _store[_entityToIndices[entityID][0]];
114 }
115
116 public bool SomeWithEntity(int entityID)
117 {
118 return _entityToIndices.ContainsKey(entityID) && _entityToIndices[entityID].Count > 0;
119 }
120
121 public override void Clear()
122 {
123 _nextIndex = 0;
124 foreach (var set in _entityToIndices.Values)
125 {
126 set.Clear();
127 }
128 }
129 }
130 }
@@ -0,0 +1,289
1 using System;
2 using System.Collections.Generic;
3
4 namespace Encompass
5 {
6 internal class ComponentManager
7 {
8 private readonly DrawLayerManager _drawLayerManager;
9
10 private readonly ComponentStore _existingComponentStore;
11 private readonly ComponentStore _immediateComponentStore;
12 private readonly ComponentDeltaStore _replayStore;
13 private readonly ComponentStore _upToDateComponentStore;
14
15 public Dictionary<Type, int> TypeToIndex { get; }
16
17 private readonly HashSet<int> _entitiesMarkedForRemoval = new HashSet<int>();
18
19 internal ComponentBitSet ImmediateBits { get { return _immediateComponentStore.ComponentBitSet; } }
20 internal ComponentBitSet ExistingBits { get { return _existingComponentStore.ComponentBitSet; } }
21
22 public ComponentManager(DrawLayerManager drawLayerManager, Dictionary<Type, int> typeToIndex)
23 {
24 this._drawLayerManager = drawLayerManager;
25 _existingComponentStore = new ComponentStore(typeToIndex);
26 _immediateComponentStore = new ComponentStore(typeToIndex);
27 _replayStore = new ComponentDeltaStore(typeToIndex);
28 _upToDateComponentStore = new ComponentStore(typeToIndex);
29 TypeToIndex = typeToIndex;
30 }
31
32 public void RegisterComponentType<TComponent>() where TComponent : struct
33 {
34 _existingComponentStore.RegisterComponentType<TComponent>();
35 _immediateComponentStore.RegisterComponentType<TComponent>();
36 _replayStore.RegisterComponentType<TComponent>();
37 _upToDateComponentStore.RegisterComponentType<TComponent>();
38 }
39
40 internal void SetExistingComponentStore(ComponentStore componentStore)
41 {
42 _existingComponentStore.SwapWith(componentStore);
43 }
44
45 internal void SetUpToDateComponentStore(ComponentStore componentStore)
46 {
47 _upToDateComponentStore.SwapWith(componentStore);
48 }
49
50 internal void RegisterDrawableComponent<TComponent>(int entityID, int layer) where TComponent : struct
51 {
52 _drawLayerManager.RegisterComponentWithLayer<TComponent>(entityID, layer);
53 }
54
55 internal void WriteComponents()
56 {
57 _existingComponentStore.UpdateUsing(_replayStore);
58 _existingComponentStore.ClearAllPriorities();
59 _upToDateComponentStore.ClearAllPriorities();
60 _immediateComponentStore.ClearAll();
61 _replayStore.ClearAll();
62 }
63
64 internal bool AddImmediateComponent<TComponent>(int entityID, in TComponent component, int priority) where TComponent : struct
65 {
66 if (_immediateComponentStore.Set(entityID, component, priority))
67 {
68 _replayStore.Set(entityID, component);
69 _upToDateComponentStore.Set(entityID, component);
70 return true;
71 }
72
73 return false;
74 }
75
76 internal void AddImmediateComponent<TComponent>(int entityID, in TComponent component) where TComponent : struct
77 {
78 _immediateComponentStore.Set(entityID, component);
79 _replayStore.Set(entityID, component);
80 _upToDateComponentStore.Set(entityID, component);
81 }
82
83 internal bool UpdateComponent<TComponent>(int entityID, in TComponent component, int priority) where TComponent : struct
84 {
85 var result = _upToDateComponentStore.Set(entityID, component, priority);
86 if (result)
87 {
88 _replayStore.Set(entityID, component);
89 }
90 return result;
91 }
92
93 internal void AddComponent<TComponent>(int entityID, in TComponent component) where TComponent : struct
94 {
95 _upToDateComponentStore.Set(entityID, component);
96 _replayStore.Set(entityID, component);
97 }
98
99 // existing or immediate reads
100
101 internal Span<TComponent> ReadExistingAndImmediateComponentsByType<TComponent>() where TComponent : struct
102 {
103 return _upToDateComponentStore.All<TComponent>();
104 }
105
106 internal ref readonly TComponent ExistingOrImmediateSingular<TComponent>() where TComponent : struct
107 {
108 return ref _upToDateComponentStore.Singular<TComponent>();
109 }
110
111 internal Span<Entity> GetExistingAndImmediateEntities<TComponent>() where TComponent : struct
112 {
113 return _upToDateComponentStore.AllEntities<TComponent>();
114 }
115
116 internal ref readonly Entity ExistingOrImmediateSingularEntity<TComponent>() where TComponent : struct
117 {
118 return ref _upToDateComponentStore.SingularEntity<TComponent>();
119 }
120
121 internal bool SomeExistingOrImmediateComponent<TComponent>() where TComponent : struct
122 {
123 return _upToDateComponentStore.Any<TComponent>();
124 }
125
126 // existing reads
127
128 internal Span<TComponent> GetExistingComponents<TComponent>() where TComponent : struct
129 {
130 return _existingComponentStore.All<TComponent>();
131 }
132
133 internal ref readonly TComponent ExistingSingular<TComponent>() where TComponent : struct
134 {
135 return ref _existingComponentStore.Singular<TComponent>();
136 }
137
138 internal Span<Entity> GetExistingEntities<TComponent>() where TComponent : struct
139 {
140 return _existingComponentStore.AllEntities<TComponent>();
141 }
142
143 internal ref readonly Entity ExistingSingularEntity<TComponent>() where TComponent : struct
144 {
145 return ref _existingComponentStore.SingularEntity<TComponent>();
146 }
147
148 internal bool SomeExistingComponent<TComponent>() where TComponent : struct
149 {
150 return _existingComponentStore.Any<TComponent>();
151 }
152
153 // immediate reads
154
155 internal Span<TComponent> ReadImmediateComponentsByType<TComponent>() where TComponent : struct
156 {
157 return _immediateComponentStore.All<TComponent>();
158 }
159
160 internal ref readonly TComponent ImmediateSingular<TComponent>() where TComponent : struct
161 {
162 return ref _immediateComponentStore.Singular<TComponent>();
163 }
164
165 internal Span<Entity> GetImmediateEntities<TComponent>() where TComponent : struct
166 {
167 return _immediateComponentStore.AllEntities<TComponent>();
168 }
169
170 internal ref readonly Entity ImmediateSingularEntity<TComponent>() where TComponent : struct
171 {
172 return ref _immediateComponentStore.SingularEntity<TComponent>();
173 }
174
175 internal bool SomeImmediateComponent<TComponent>() where TComponent : struct
176 {
177 return _immediateComponentStore.Any<TComponent>();
178 }
179
180 // component getters
181
182 internal ref TComponent ReadImmediateOrExistingComponentByEntityAndType<TComponent>(int entityID) where TComponent : struct
183 {
184 return ref _upToDateComponentStore.Get<TComponent>(entityID);
185 }
186
187 internal ref TComponent ReadExistingComponentByEntityAndType<TComponent>(int entityID) where TComponent : struct
188 {
189 return ref _existingComponentStore.Get<TComponent>(entityID);
190 }
191
192 internal ref TComponent ReadImmediateComponentByEntityAndType<TComponent>(int entityID) where TComponent : struct
193 {
194 return ref _immediateComponentStore.Get<TComponent>(entityID);
195 }
196
197 // has checkers
198
199 internal bool HasExistingOrImmediateComponent<TComponent>(int entityID) where TComponent : struct
200 {
201 return _upToDateComponentStore.Has<TComponent>(entityID);
202 }
203
204 internal bool HasExistingOrImmediateComponent(int entityID, Type type)
205 {
206 return _upToDateComponentStore.Has(type, entityID);
207 }
208
209 internal bool HasExistingComponent<TComponent>(int entityID) where TComponent : struct
210 {
211 return _existingComponentStore.Has<TComponent>(entityID);
212 }
213
214 internal bool HasExistingComponent(int entityID, Type type)
215 {
216 return _existingComponentStore.Has(type, entityID);
217 }
218
219 internal bool HasImmediateComponent<TComponent>(int entityID) where TComponent : struct
220 {
221 return _immediateComponentStore.Has<TComponent>(entityID);
222 }
223
224 internal bool HasImmediateComponent(int entityID, Type type)
225 {
226 return _immediateComponentStore.Has(type, entityID);
227 }
228
229 internal Span<TComponent> GetComponentsByType<TComponent>() where TComponent : struct
230 {
231 return _existingComponentStore.All<TComponent>();
232 }
233
234 internal ref readonly TComponent GetComponentByEntityAndType<TComponent>(int entityID) where TComponent : struct
235 {
236 return ref _existingComponentStore.Get<TComponent>(entityID);
237 }
238
239 internal bool EntityHasComponentOfType<TComponent>(int entityID) where TComponent : struct
240 {
241 return _existingComponentStore.Has<TComponent>(entityID);
242 }
243
244 internal void MarkAllComponentsOnEntityForRemoval(int entityID)
245 {
246 _entitiesMarkedForRemoval.Add(entityID);
247 }
248
249 internal void RemoveMarkedComponents()
250 {
251 foreach (var entityID in _entitiesMarkedForRemoval)
252 {
253 _existingComponentStore.Remove(entityID);
254 _immediateComponentStore.Remove(entityID);
255 _replayStore.Remove(entityID);
256 _upToDateComponentStore.Remove(entityID);
257 _drawLayerManager.UnRegisterEntityWithLayer(entityID);
258 }
259
260 _entitiesMarkedForRemoval.Clear();
261 }
262
263 public bool RemoveImmediate<TComponent>(int entityID, int priority) where TComponent : struct
264 {
265 if (_immediateComponentStore.Remove<TComponent>(entityID, priority))
266 {
267 _replayStore.Remove<TComponent>(entityID, priority);
268 _upToDateComponentStore.Remove<TComponent>(entityID, priority);
269 _drawLayerManager.UnRegisterComponentWithLayer<TComponent>(entityID);
270 return true;
271 }
272 return false;
273 }
274
275 public void Remove<TComponent>(int entityID, int priority) where TComponent : struct
276 {
277 if (_upToDateComponentStore.Remove<TComponent>(entityID, priority))
278 {
279 _replayStore.Remove<TComponent>(entityID, priority);
280 _drawLayerManager.UnRegisterComponentWithLayer<TComponent>(entityID);
281 }
282 }
283
284 public bool UpToDateEntityIsEmpty(int entityID)
285 {
286 return _upToDateComponentStore.EntityBitArray(entityID).AllFalse();
287 }
288 }
289 }
@@ -0,0 +1,132
1 using System;
2 using System.Collections.Generic;
3 using System.Linq;
4 using Encompass.Exceptions;
5
6 namespace Encompass
7 {
8 internal class DrawLayerManager
9 {
10 private readonly SortedList<int, int> _layerOrder = new SortedList<int, int>();
11
12 private readonly Dictionary<int, Dictionary<Type, HashSet<int>>> _layerIndexToTypeToID = new Dictionary<int, Dictionary<Type, HashSet<int>>>();
13 private readonly Dictionary<int, HashSet<GeneralRenderer>> _layerIndexToGeneralRenderers = new Dictionary<int, HashSet<GeneralRenderer>>(512);
14
15 private readonly Dictionary<Type, Dictionary<int, int>> _typeToEntityToLayer = new Dictionary<Type, Dictionary<int, int>>(512);
16 public IEnumerable<int> LayerOrder { get { return _layerOrder.Values; } }
17
18 public DrawLayerManager()
19 {
20 RegisterDrawLayer(0);
21 }
22
23 public void RegisterDrawLayer(int layer)
24 {
25 if (!_layerIndexToTypeToID.ContainsKey(layer))
26 {
27 _layerOrder.Add(layer, layer);
28 _layerIndexToGeneralRenderers.Add(layer, new HashSet<GeneralRenderer>());
29 _layerIndexToTypeToID.Add(layer, new Dictionary<Type, HashSet<int>>());
30 }
31 }
32
33 public void RegisterOrderedDrawable<TComponent>() where TComponent : struct
34 {
35 if (!_typeToEntityToLayer.ContainsKey(typeof(TComponent)))
36 {
37 _typeToEntityToLayer.Add(typeof(TComponent), new Dictionary<int, int>(128));
38 }
39
40 foreach (var pair in _layerIndexToTypeToID)
41 {
42 if (!pair.Value.ContainsKey(typeof(TComponent)))
43 {
44 pair.Value.Add(typeof(TComponent), new HashSet<int>());
45 }
46 }
47 }
48
49 public void RegisterGeneralRendererWithLayer(GeneralRenderer renderer, int layer)
50 {
51 RegisterDrawLayer(layer);
52 var set = _layerIndexToGeneralRenderers[layer];
53 set.Add(renderer);
54 }
55
56 public void RegisterComponentWithLayer<TComponent>(int entityID, int layer) where TComponent : struct
57 {
58 if (!_layerIndexToTypeToID.ContainsKey(layer))
59 {
60 throw new UndefinedLayerException("Layer {0} is not defined. Use WorldBuilder.RegisterDrawLayer to register the layer.", layer);
61 }
62
63 if (_typeToEntityToLayer[typeof(TComponent)].ContainsKey(entityID))
64 {
65 if (_typeToEntityToLayer[typeof(TComponent)][entityID] != layer)
66 {
67 UnRegisterComponentWithLayer<TComponent>(entityID);
68
69 var set = _layerIndexToTypeToID[layer][typeof(TComponent)];
70 set.Add(entityID);
71
72 _typeToEntityToLayer[typeof(TComponent)].Add(entityID, layer);
73 }
74 }
75 else
76 {
77 var set = _layerIndexToTypeToID[layer][typeof(TComponent)];
78 set.Add(entityID);
79
80 _typeToEntityToLayer[typeof(TComponent)].Add(entityID, layer);
81 }
82 }
83
84 public void UnRegisterComponentWithLayer<TComponent>(int entityID) where TComponent : struct
85 {
86 if (!_typeToEntityToLayer.ContainsKey(typeof(TComponent))) { return; }
87
88 if (_typeToEntityToLayer[typeof(TComponent)].ContainsKey(entityID))
89 {
90 var layer = _typeToEntityToLayer[typeof(TComponent)][entityID];
91 _layerIndexToTypeToID[layer][typeof(TComponent)].Remove(entityID);
92 }
93 _typeToEntityToLayer[typeof(TComponent)].Remove(entityID);
94 }
95
96 public void UnRegisterEntityWithLayer(int entityID)
97 {
98 foreach (var store in _layerIndexToTypeToID.Values)
99 {
100 foreach (var typeToSet in store)
101 {
102 var type = typeToSet.Key;
103 var set = typeToSet.Value;
104
105 if (set.Contains(entityID))
106 {
107 _typeToEntityToLayer[type].Remove(entityID);
108 set.Remove(entityID);
109 }
110 }
111 }
112 }
113
114 public IEnumerable<GeneralRenderer> GeneralRenderersByLayer(int layer)
115 {
116 return _layerIndexToGeneralRenderers.ContainsKey(layer) ?
117 _layerIndexToGeneralRenderers[layer] :
118 Enumerable.Empty<GeneralRenderer>();
119 }
120
121 public IEnumerable<(int, Type)> AllInLayer(int layer)
122 {
123 foreach (var kvp in _layerIndexToTypeToID[layer])
124 {
125 foreach (var id in kvp.Value)
126 {
127 yield return (id, kvp.Key);
128 }
129 }
130 }
131 }
132 }
This diff has been collapsed as it changes many lines, (807 lines changed) Show them Hide them
@@ -0,0 +1,807
1 using System;
2 using System.Reflection;
3 using System.Collections.Generic;
4 using System.Linq;
5 using Encompass.Exceptions;
6 using MoonTools.FastCollections;
7
8 namespace Encompass
9 {
10 /// <summary>
11 /// Engines are the Encompass notion of an ECS System.
12 /// They are responsible for reading the World state, reading messages, emitting messages, and creating or mutating Entities and Components.
13 /// Engines run once per World Update.
14 /// </summary>
15 public abstract class Engine : IEquatable<Engine>
16 {
17 internal Guid _id;
18
19 internal readonly HashSet<Type> ReadTypes = new HashSet<Type>();
20 internal readonly HashSet<Type> ReadImmediateTypes = new HashSet<Type>();
21 internal readonly HashSet<Type> SendTypes = new HashSet<Type>();
22 internal readonly HashSet<Type> ReceiveTypes = new HashSet<Type>();
23 internal readonly HashSet<Type> WriteTypes = new HashSet<Type>();
24 internal readonly HashSet<Type> WriteImmediateTypes = new HashSet<Type>();
25 internal readonly HashSet<Type> QueryWithTypes = new HashSet<Type>();
26 internal readonly HashSet<Type> QueryWithoutTypes = new HashSet<Type>();
27 internal readonly Dictionary<Type, int> WritePriorities = new Dictionary<Type, int>();
28 internal readonly int DefaultWritePriority = 0;
29
30 /// <summary>
31 /// If false, the Engine will ignore time dilation.
32 /// </summary>
33 internal bool _usesTimeDilation = true;
34 public bool TimeDilationActive { get => _usesTimeDilation && _timeManager.TimeDilationActive; }
35
36 private EntityManager _entityManager;
37 private MessageManager _messageManager;
38 private ComponentManager _componentManager;
39 private TimeManager _timeManager;
40 private TrackingManager _trackingManager;
41
42 private EntitySetQuery _entityQuery;
43
44 private readonly HashSet<int> _trackedEntities = new HashSet<int>();
45 protected IEnumerable<Entity> TrackedEntities
46 {
47 get
48 {
49 foreach (var entityID in _trackedEntities)
50 {
51 yield return _entityManager.GetEntity(entityID);
52 }
53 }
54 }
55
56 private readonly HashSet<int> _newlyCreatedEntities = new HashSet<int>();
57
58 protected Engine()
59 {
60 _id = Guid.NewGuid();
61
62 var sendsAttribute = GetType().GetCustomAttribute<Sends>(false);
63 if (sendsAttribute != null)
64 {
65 SendTypes = sendsAttribute.SendTypes;
66 }
67
68 var activatesAttribute = GetType().GetCustomAttribute<WritesImmediate>(false);
69 if (activatesAttribute != null)
70 {
71 WriteImmediateTypes = activatesAttribute.WriteImmediateTypes;
72 }
73
74 var defaultWritePriorityAttribute = GetType().GetCustomAttribute<DefaultWritePriority>(false);
75
76 if (defaultWritePriorityAttribute != null)
77 {
78 DefaultWritePriority = defaultWritePriorityAttribute.WritePriority;
79 }
80
81 foreach (var writesAttribute in GetType().GetCustomAttributes<Writes>(false))
82 {
83 WriteTypes.UnionWith(writesAttribute.WriteTypes);
84 WritePriorities = new Dictionary<Type, int>[2] { WritePriorities, writesAttribute.Priorities }.SelectMany(dict => dict).ToDictionary(pair => pair.Key, pair => pair.Value);
85 }
86
87 var receivesAttribute = GetType().GetCustomAttribute<Receives>(false);
88 if (receivesAttribute != null)
89 {
90 ReceiveTypes = receivesAttribute.ReceiveTypes;
91 }
92
93 var readsAttribute = GetType().GetCustomAttribute<Reads>(false);
94 if (readsAttribute != null)
95 {
96 ReadTypes = readsAttribute.ReadTypes;
97 }
98
99 var readsImmediateAttribute = GetType().GetCustomAttribute<ReadsImmediate>(false);
100 if (readsImmediateAttribute != null)
101 {
102 ReadImmediateTypes = readsImmediateAttribute.ReadImmediateTypes;
103 }
104
105 var queryWithAttribute = GetType().GetCustomAttribute<QueryWith>(false);
106 if (queryWithAttribute != null)
107 {
108 QueryWithTypes = queryWithAttribute.QueryWithTypes;
109 }
110
111 foreach (var queryType in QueryWithTypes)
112 {
113 ReadTypes.Add(queryType);
114 }
115
116 var queryWithoutAttribute = GetType().GetCustomAttribute<QueryWithout>(false);
117 if (queryWithoutAttribute != null)
118 {
119 QueryWithoutTypes = queryWithoutAttribute.QueryWithoutTypes;
120 }
121
122 foreach (var queryType in QueryWithoutTypes)
123 {
124 ReadTypes.Add(queryType);
125 }
126 }
127
128 public override bool Equals(object obj)
129 {
130 if (obj is Engine engine)
131 {
132 return Equals(engine);
133 }
134
135 return false;
136 }
137
138 public bool Equals(Engine other)
139 {
140 return other._id == _id;
141 }
142
143 public override int GetHashCode()
144 {
145 return HashCode.Combine(_id);
146 }
147
148 internal void AssignEntityManager(EntityManager entityManager)
149 {
150 _entityManager = entityManager;
151 }
152
153 internal void AssignComponentManager(ComponentManager componentManager)
154 {
155 _componentManager = componentManager;
156 }
157
158 internal void AssignMessageManager(MessageManager messageManager)
159 {
160 _messageManager = messageManager;
161 }
162
163 internal void AssignTimeManager(TimeManager timeManager)
164 {
165 _timeManager = timeManager;
166 }
167
168 internal void AssignTrackingManager(TrackingManager trackingManager)
169 {
170 _trackingManager = trackingManager;
171 }
172
173 internal void CheckMessageRead<TMessage>() where TMessage : struct, IMessage
174 {
175 if (!ReceiveTypes.Contains(typeof(TMessage)))
176 {
177 throw new IllegalReadException("Engine {0} tried to read undeclared Message {1}", this.GetType().Name, typeof(TMessage).Name);
178 }
179 }
180
181 private bool EntityCreatedThisFrame(int entityID)
182 {
183 return _newlyCreatedEntities.Contains(entityID);
184 }
185
186 internal void ClearNewlyCreatedEntities()
187 {
188 _newlyCreatedEntities.Clear();
189 }
190
191 /// <summary>
192 /// Runs once per World update with the calculated delta-time.
193 /// </summary>
194 /// <param name="dt">The time in seconds that has elapsed since the previous frame.</param>
195 public abstract void Update(double dt);
196
197 /// <summary>
198 /// Creates and returns a new empty Entity.
199 /// </summary>
200 protected Entity CreateEntity()
201 {
202 var entity = _entityManager.CreateEntity();
203 _newlyCreatedEntities.Add(entity.ID);
204 return entity;
205 }
206
207 /// <summary>
208 /// Returns true if an Entity with the specified ID exists.
209 /// </summary>
210 protected bool EntityExists(in Entity entity)
211 {
212 return _entityManager.EntityExists(entity.ID);
213 }
214
215 /// <summary>
216 /// Returns true if an Entity with the specified ID exists.
217 /// </summary>
218 protected bool EntityExists(int entityID)
219 {
220 return _entityManager.EntityExists(entityID);
221 }
222
223 /// <summary>
224 /// Returns all Entities containing the specified Component type.
225 /// </summary>
226 protected Span<Entity> ReadEntities<TComponent>() where TComponent : struct, IComponent
227 {
228 var immediateRead = ReadImmediateTypes.Contains(typeof(TComponent));
229 var existingRead = ReadTypes.Contains(typeof(TComponent));
230 if (existingRead && immediateRead)
231 {
232 return _componentManager.GetExistingAndImmediateEntities<TComponent>();
233 }
234 else if (existingRead)
235 {
236 return _componentManager.GetExistingEntities<TComponent>();
237 }
238 else if (immediateRead)
239 {
240 return _componentManager.GetImmediateEntities<TComponent>();
241 }
242 else
243 {
244 throw new IllegalReadException("Engine {0} tried to read undeclared Component {1}", GetType().Name, typeof(TComponent).Name);
245 }
246 }
247
248 /// <summary>
249 /// Returns an Entity containing the specified Component type.
250 /// </summary>
251 protected ref readonly Entity ReadEntity<TComponent>() where TComponent : struct, IComponent
252 {
253 var immediateRead = ReadImmediateTypes.Contains(typeof(TComponent));
254 var existingRead = ReadTypes.Contains(typeof(TComponent));
255 if (existingRead && immediateRead)
256 {
257 return ref _componentManager.ExistingOrImmediateSingularEntity<TComponent>();
258 }
259 else if (existingRead)
260 {
261 return ref _componentManager.ExistingSingularEntity<TComponent>();
262 }
263 else if (immediateRead)
264 {
265 return ref _componentManager.ImmediateSingularEntity<TComponent>();
266 }
267 else
268 {
269 throw new IllegalReadException("Engine {0} tried to read undeclared Component {1}", GetType().Name, typeof(TComponent).Name);
270 }
271 }
272
273 /// <summary>
274 /// Returns all of the Components with the specified Component Type.
275 /// </summary>
276 protected Span<TComponent> ReadComponents<TComponent>() where TComponent : struct, IComponent
277 {
278 var immediateRead = ReadImmediateTypes.Contains(typeof(TComponent));
279 var existingRead = ReadTypes.Contains(typeof(TComponent));
280 if (existingRead && immediateRead)
281 {
282 return _componentManager.ReadExistingAndImmediateComponentsByType<TComponent>();
283 }
284 else if (existingRead)
285 {
286 return _componentManager.GetExistingComponents<TComponent>();
287 }
288 else if (immediateRead)
289 {
290 return _componentManager.ReadImmediateComponentsByType<TComponent>();
291 }
292 else
293 {
294 throw new IllegalReadException("Engine {0} tried to read undeclared Component {1}", GetType().Name, typeof(TComponent).Name);
295 }
296 }
297
298 /// <summary>
299 /// Returns a Component with the specified Component Type. If multiples exist, an arbitrary Component is returned.
300 /// </summary>
301 protected ref readonly TComponent ReadComponent<TComponent>() where TComponent : struct, IComponent
302 {
303 var immediateRead = ReadImmediateTypes.Contains(typeof(TComponent));
304 var existingRead = ReadTypes.Contains(typeof(TComponent));
305 if (existingRead && immediateRead)
306 {
307 return ref _componentManager.ExistingOrImmediateSingular<TComponent>();
308 }
309 else if (existingRead)
310 {
311 return ref _componentManager.ExistingSingular<TComponent>();
312 }
313 else if (immediateRead)
314 {
315 return ref _componentManager.ImmediateSingular<TComponent>();
316 }
317 else
318 {
319 throw new IllegalReadException("Engine {0} tried to read undeclared Component {1}", GetType().Name, typeof(TComponent).Name);
320 }
321 }
322
323 /// <summary>
324 /// Returns true if any Component with the specified Component Type exists.
325 /// </summary>
326 protected bool SomeComponent<TComponent>() where TComponent : struct, IComponent
327 {
328 var immediateRead = ReadImmediateTypes.Contains(typeof(TComponent));
329 var existingRead = ReadTypes.Contains(typeof(TComponent));
330 if (existingRead && immediateRead)
331 {
332 return _componentManager.SomeExistingOrImmediateComponent<TComponent>();
333 }
334 else if (existingRead)
335 {
336 return _componentManager.SomeExistingComponent<TComponent>();
337 }
338 else if (immediateRead)
339 {
340 return _componentManager.SomeImmediateComponent<TComponent>();
341 }
342 else
343 {
344 throw new IllegalReadException("Engine {0} tried to read undeclared Component {1}", GetType().Name, typeof(TComponent).Name);
345 }
346 }
347
348 private ref TComponent GetComponentHelper<TComponent>(int entityID) where TComponent : struct, IComponent
349 {
350 var immediateRead = ReadImmediateTypes.Contains(typeof(TComponent));
351 var existingRead = ReadTypes.Contains(typeof(TComponent));
352 if (existingRead && immediateRead)
353 {
354 return ref _componentManager.ReadImmediateOrExistingComponentByEntityAndType<TComponent>(entityID);
355 }
356 else if (existingRead)
357 {
358 return ref _componentManager.ReadExistingComponentByEntityAndType<TComponent>(entityID);
359 }
360 else if (immediateRead)
361 {
362 return ref _componentManager.ReadImmediateComponentByEntityAndType<TComponent>(entityID);
363 }
364 else
365 {
366 throw new IllegalReadException("Engine {0} tried to read undeclared Component {1}", GetType().Name, typeof(TComponent).Name);
367 }
368 }
369
370 /// <summary>
371 /// Returns a Component with the specified Type that exists on the Entity.
372 /// </summary>
373 /// <exception cref="Encompass.Exceptions.NoComponentOfTypeOnEntityException">
374 /// Thrown when the Entity does not have a Component of the specified Type
375 /// </exception>
376 /// <exception cref="Encompass.Exceptions.IllegalReadException">
377 /// Thrown when the Engine does not declare that it reads the given Component Type.
378 /// </exception>
379 protected ref readonly TComponent GetComponent<TComponent>(in Entity entity) where TComponent : struct, IComponent
380 {
381 return ref GetComponentHelper<TComponent>(entity.ID);
382 }
383
384 /// <summary>
385 /// Returns true if the Entity has a Component of the given Type.
386 /// </summary>
387 /// <exception cref="Encompass.Exceptions.IllegalReadException">
388 /// Thrown when the Engine does not declare that is Reads the given Component Type.
389 /// </exception>
390 protected bool HasComponent<TComponent>(in Entity entity) where TComponent : struct, IComponent
391 {
392 var immediateRead = ReadImmediateTypes.Contains(typeof(TComponent));
393 var existingRead = ReadTypes.Contains(typeof(TComponent));
394
395 if (immediateRead && existingRead)
396 {
397 return _componentManager.HasExistingOrImmediateComponent<TComponent>(entity.ID);
398 }
399 else if (existingRead)
400 {
401 return _componentManager.HasExistingComponent<TComponent>(entity.ID);
402 }
403 else if (immediateRead)
404 {
405 return _componentManager.HasImmediateComponent<TComponent>(entity.ID);
406 }
407 else
408 {
409 throw new IllegalReadException("Engine {0} tried to read undeclared Component {1}", GetType().Name, typeof(TComponent).Name);
410 }
411 }
412
413 /// <summary>
414 /// Returns true if the Entity has a Component of the given Type.
415 /// </summary>
416 /// <exception cref="Encompass.Exceptions.IllegalReadException">
417 /// Thrown when the Engine does not declare that is Reads the given Component Type.
418 /// </exception>
419 protected bool HasComponent(in Entity entity, Type type)
420 {
421 var immediateRead = ReadImmediateTypes.Contains(type);
422 var existingRead = ReadTypes.Contains(type);
423
424 if (immediateRead && existingRead)
425 {
426 return _componentManager.HasExistingOrImmediateComponent(entity.ID, type);
427 }
428 else if (existingRead)
429 {
430 return _componentManager.HasExistingComponent(entity.ID, type);
431 }
432 else if (immediateRead)
433 {
434 return _componentManager.HasImmediateComponent(entity.ID, type);
435 }
436 else
437 {
438 throw new IllegalReadException("Engine {0} tried to read undeclared Component {1}", GetType().Name, type.Name);
439 }
440 }
441
442 /// <summary>
443 /// Sets Component data for the specified Component Type on the specified Entity. If Component data for this Type already existed on the Entity, the component data is overwritten.
444 /// </summary>
445 /// <exception cref="Encompass.Exceptions.IllegalWriteException">
446 /// Thrown when the Engine does not declare that it Writes the given Component Type.
447 /// </exception>
448 protected void SetComponent<TComponent>(in Entity entity, in TComponent component) where TComponent : struct, IComponent
449 {
450 var priority = WritePriorities.ContainsKey(typeof(TComponent)) ? WritePriorities[typeof(TComponent)] : DefaultWritePriority;
451
452 if (!WriteTypes.Contains(typeof(TComponent)))
453 {
454 throw new IllegalWriteException("Engine {0} tried to update undeclared Component {1}", GetType().Name, typeof(TComponent).Name);
455 }
456
457 bool written;
458 if (WriteImmediateTypes.Contains(typeof(TComponent)))
459 {
460 written = _componentManager.AddImmediateComponent(entity.ID, component, priority);
461 if (written)
462 {
463 _trackingManager.ImmediateUpdateTracking(entity.ID, typeof(TComponent));
464 }
465 }
466 else
467 {
468 written = _componentManager.UpdateComponent(entity.ID, component, priority);
469 }
470
471 if (!_componentManager.HasExistingComponent<TComponent>(entity.ID))
472 {
473 _trackingManager.RegisterAddition(entity.ID, typeof(TComponent));
474 }
475
476 if (written && component is IDrawableComponent drawableComponent)
477 {
478 _componentManager.RegisterDrawableComponent<TComponent>(entity.ID, drawableComponent.Layer);
479 }
480 }
481
482 /// <summary>
483 /// An alternative to SetComponent that can be used for new Entities and does not require setting write priority.
484 /// </summary>
485 /// <exception cref="Encompass.Exceptions.IllegalWriteException">
486 /// Thrown when the Engine does not declare that it Writes the given Component Type.
487 /// </exception>
488 protected void AddComponent<TComponent>(in Entity entity, in TComponent component) where TComponent : struct, IComponent
489 {
490 if (!EntityCreatedThisFrame(entity.ID))
491 {
492 throw new IllegalWriteException("AddComponent used on Entity that was not created in this context. Use SetComponent instead.");
493 }
494
495 if (WriteImmediateTypes.Contains(typeof(TComponent)))
496 {
497 _componentManager.AddImmediateComponent(entity.ID, component);
498 _trackingManager.ImmediateUpdateTracking(entity.ID, typeof(TComponent));
499 }
500 else
501 {
502 _componentManager.AddComponent(entity.ID, component);
503 }
504
505 _trackingManager.RegisterAddition(entity.ID, typeof(TComponent));
506
507 if (component is IDrawableComponent drawableComponent)
508 {
509 _componentManager.RegisterDrawableComponent<TComponent>(entity.ID, drawableComponent.Layer);
510 }
511 }
512
513 /// <summary>
514 /// Sends a Message.
515 /// </summary>
516 /// <exception cref="Encompass.Exceptions.IllegalSendException">
517 /// Thrown when the Engine does not declare that it Sends the Message Type.
518 /// </exception>
519 protected void SendMessage<TMessage>(in TMessage message) where TMessage : struct, IMessage
520 {
521 if (!SendTypes.Contains(typeof(TMessage)))
522 {
523 throw new IllegalSendException("Engine {0} tried to send undeclared Message {1}", GetType().Name, typeof(TMessage).Name);
524 }
525
526 _messageManager.AddMessage(message);
527 }
528
529 /// <summary>
530 /// Sends a message after the specified number of seconds, respecting time dilation.
531 /// </summary>
532 /// <param name="time">The time in seconds that will elapse before the message is sent.</param>
533 protected void SendMessage<TMessage>(in TMessage message, double time) where TMessage : struct, IMessage
534 {
535 _messageManager.AddMessage(message, time);
536 }
537
538 /// <summary>
539 /// Sends a message after the specified number of seconds, ignoring time dilation.
540 /// </summary>
541 /// <param name="time">The time in seconds that will elapse before the message is sent.</param>
542 protected void SendMessageIgnoringTimeDilation<TMessage>(in TMessage message, double time) where TMessage : struct, IMessage
543 {
544 _messageManager.AddMessageIgnoringTimeDilation(message, time);
545 }
546
547 /// <summary>
548 /// Reads all messages of the specified Type.
549 /// </summary>
550 /// <exception cref="Encompass.Exceptions.IllegalReadException">
551 /// Thrown when the Engine does not declare that it Receives the specified Message Type.
552 /// </exception>
553 protected Span<TMessage> ReadMessages<TMessage>() where TMessage : struct, IMessage
554 {
555 CheckMessageRead<TMessage>();
556 return _messageManager.GetMessagesByType<TMessage>();
557 }
558
559 /// <summary>
560 /// Reads an arbitrary message of the specified Type.
561 /// </summary>
562 /// <exception cref="Encompass.Exceptions.IllegalReadException">
563 /// Thrown when the Engine does not declare that it Receives the specified Message Type.
564 /// </exception>
565 protected ref readonly TMessage ReadMessage<TMessage>() where TMessage : struct, IMessage
566 {
567 CheckMessageRead<TMessage>();
568 return ref _messageManager.First<TMessage>();
569 }
570
571 /// <summary>
572 /// Returns true if a Message of the specified Type has been sent this frame.
573 /// </summary>
574 /// <exception cref="Encompass.Exceptions.IllegalReadException">
575 /// Thrown when the Engine does not declare that it Receives the specified Message Type.
576 /// </exception>
577 protected bool SomeMessage<TMessage>() where TMessage : struct, IMessage
578 {
579 CheckMessageRead<TMessage>();
580 return _messageManager.Any<TMessage>();
581 }
582
583 /// <summary>
584 /// Destroys the specified Entity. This also removes all of the Components associated with the Entity.
585 /// Entity destruction takes place after all the Engines have been processed by World Update.
586 /// </summary>
587 protected void Destroy(in Entity entity)
588 {
589 _entityManager.MarkForDestroy(entity.ID);
590 }
591
592 /// <summary>
593 /// Destroys an arbitrary Entity containing a Component of the specified Type.
594 /// Entity destruction takes place after all the Engines have been processed by World Update.
595 /// </summary>
596 protected void DestroyWith<TComponent>() where TComponent : struct, IComponent
597 {
598 Destroy(ReadEntity<TComponent>());
599 }
600
601 /// <summary>
602 /// Destroys all Entities containing a Component of the specified Type.
603 /// Entity destruction takes place after all the Engines have been processed by World Update.
604 /// </summary>
605 protected void DestroyAllWith<TComponent>() where TComponent : struct, IComponent
606 {
607 foreach (var entity in ReadEntities<TComponent>())
608 {
609 Destroy(entity);
610 }
611 }
612
613 /// <summary>
614 /// Removes a Component with the specified type from the given Entity.
615 /// Note that the Engine must Read the Component type that is being removed.
616 /// If a Component with the specified type does not exist on the Entity, returns false and does not mutate the Entity.
617 /// </summary>
618 protected void RemoveComponent<TComponent>(in Entity entity) where TComponent : struct, IComponent
619 {
620 var priority = WritePriorities.ContainsKey(typeof(TComponent)) ? WritePriorities[typeof(TComponent)] : DefaultWritePriority;
621
622 if (!WriteTypes.Contains(typeof(TComponent)))
623 {
624 throw new IllegalWriteException("Engine {0} tried to remove undeclared Component {1}. Declare with Writes attribute.", GetType().Name, typeof(TComponent).Name);
625 }
626
627 if (WriteImmediateTypes.Contains(typeof(TComponent)))
628 {
629 if (_componentManager.RemoveImmediate<TComponent>(entity.ID, priority))
630 {
631 _trackingManager.ImmediateUpdateTracking(entity.ID, typeof(TComponent));
632 }
633 }
634 else
635 {
636 _componentManager.Remove<TComponent>(entity.ID, priority);
637 }
638
639 if (_componentManager.HasExistingComponent<TComponent>(entity.ID))
640 {
641 _trackingManager.RegisterRemoval(entity.ID, typeof(TComponent));
642 }
643 }
644
645 /// <summary>
646 /// Activates the Encompass time dilation system.
647 /// Engines that have the IgnoresTimeDilation property will ignore all time dilation.
648 /// If multiple time dilations are active they will be averaged.
649 /// </summary>
650 /// <param name="factor">The time dilation factor, which is multiplied by real delta time.</param>
651 /// <param name="easeInTime">The time that will elapse before time is fully dilated, in real time.</param>
652 /// <param name="activeTime">The length of real time that time will be fully dilated.</param>
653 /// <param name="easeOutTime">The time that will elapse before time is fully undilated.</param>
654 protected void ActivateTimeDilation(double factor, double easeInTime, double activeTime, double easeOutTime)
655 {
656 _timeManager.ActivateTimeDilation(factor, easeInTime, activeTime, easeOutTime);
657 }
658
659 /// <summary>
660 /// Activates the Encompass time dilation system.
661 /// Engines that have the IgnoresTimeDilation property will ignore all time dilation.
662 /// If multiple time dilations are active they will be averaged.
663 /// </summary>
664 /// <param name="factor">The time dilation factor, which is multiplied by real delta time.</param>
665 /// <param name="easeInTime">The time that will elapse before time is fully dilated, in real time.</param>
666 /// <param name="easeInFunction">An easing function for the easing in of time dilation.</param>
667 /// <param name="activeTime">The length of real time that time will be fully dilated.</param>
668 /// <param name="easeOutTime">The time that will elapse before time is fully undilated.</param>
669 protected void ActivateTimeDilation(double factor, double easeInTime, System.Func<double, double, double, double, double> easeInFunction, double activeTime, double easeOutTime)
670 {
671 _timeManager.ActivateTimeDilation(factor, easeInTime, easeInFunction, activeTime, easeOutTime);
672 }
673
674 /// <summary>
675 /// Activates the Encompass time dilation system.
676 /// Engines that have the IgnoresTimeDilation property will ignore all time dilation.
677 /// If multiple time dilations are active they will be averaged.
678 /// </summary>
679 /// <param name="factor">The time dilation factor, which is multiplied by real delta time.</param>
680 /// <param name="easeInTime">The time that will elapse before time is fully dilated, in real time.</param>
681 /// <param name="activeTime">The length of real time that time will be fully dilated.</param>
682 /// <param name="easeOutTime">The time that will elapse before time is fully undilated.</param>
683 /// <param name="easeOutFunction">An easing function for the easing out of time dilation.</param>
684 protected void ActivateTimeDilation(double factor, double easeInTime, double activeTime, double easeOutTime, System.Func<double, double, double, double, double> easeOutFunction)
685 {
686 _timeManager.ActivateTimeDilation(factor, easeInTime, activeTime, easeOutTime, easeOutFunction);
687 }
688
689 /// <summary>
690 /// Activates the Encompass time dilation system.
691 /// Engines that have the IgnoresTimeDilation property will ignore all time dilation.
692 /// If multiple time dilations are active they will be averaged.
693 /// </summary>
694 /// <param name="factor">The time dilation factor, which is multiplied by real delta time.</param>
695 /// <param name="easeInTime">The time that will elapse before time is fully dilated, in real time.</param>
696 /// <param name="easeInFunction">An easing function for the easing in of time dilation.</param>
697 /// <param name="activeTime">The length of real time that time will be fully dilated.</param>
698 /// <param name="easeOutTime">The time that will elapse before time is fully undilated.</param>
699 /// <param name="easeOutFunction">An easing function for the easing out of time dilation.</param>
700 protected void ActivateTimeDilation(double factor, double easeInTime, System.Func<double, double, double, double, double> easeInFunction, double activeTime, double easeOutTime, System.Func<double, double, double, double, double> easeOutFunction)
701 {
702 _timeManager.ActivateTimeDilation(factor, easeInTime, easeInFunction, activeTime, easeOutTime, easeOutFunction);
703 }
704
705 /// <summary>
706 /// Efficiently reads Messages of a given type that all reference the given Entity.
707 /// </summary>
708 /// <typeparam name="TMessage">The Message subtype.</typeparam>
709 /// <param name="entity">The entity that all messages in the IEnumerable refer to.</param>
710 /// <returns></returns>
711 protected IEnumerable<TMessage> ReadMessagesWithEntity<TMessage>(in Entity entity) where TMessage : struct, IMessage, IHasEntity
712 {
713 CheckMessageRead<TMessage>();
714 return _messageManager.WithEntity<TMessage>(entity.ID);
715 }
716
717 /// <summary>
718 /// Efficiently reads a single Message of a given type that references a given Entity.
719 /// It is recommended to use this method in conjunction with SomeMessageWithEntity to prevent errors.
720 /// </summary>
721 protected ref readonly TMessage ReadMessageWithEntity<TMessage>(in Entity entity) where TMessage : struct, IMessage, IHasEntity
722 {
723 CheckMessageRead<TMessage>();
724 return ref _messageManager.WithEntitySingular<TMessage>(entity.ID);
725 }
726
727 /// <summary>
728 /// Efficiently checks if any Message of a given type referencing a given Entity exists.
729 /// </summary>
730 protected bool SomeMessageWithEntity<TMessage>(in Entity entity) where TMessage : struct, IMessage, IHasEntity
731 {
732 CheckMessageRead<TMessage>();
733 return _messageManager.SomeWithEntity<TMessage>(entity.ID);
734 }
735
736 internal void CheckAndUpdateTracking(int entityID)
737 {
738 if (_trackedEntities.Contains(entityID) && !_entityQuery.CheckEntity(entityID, _componentManager.ExistingBits))
739 {
740 _trackedEntities.Remove(entityID);
741 }
742 else if (!_trackedEntities.Contains(entityID) && _entityQuery.CheckEntity(entityID, _componentManager.ExistingBits))
743 {
744 _trackedEntities.Add(entityID);
745 }
746 }
747
748 internal void ImmediateCheckAndUpdateTracking(int entityID)
749 {
750 if (_trackedEntities.Contains(entityID) && !_entityQuery.ImmediateCheckEntity(entityID, _componentManager.ImmediateBits, _componentManager.ExistingBits))
751 {
752 _trackedEntities.Remove(entityID);
753 }
754 else if (!_trackedEntities.Contains(entityID) && _entityQuery.ImmediateCheckEntity(entityID, _componentManager.ImmediateBits, _componentManager.ExistingBits))
755 {
756 _trackedEntities.Add(entityID);
757 }
758 }
759
760 /// <summary>
761 /// Returns an empty EntitySetQuery. Can be modified and iterated over to obtain Entities that fit the given criteria.
762 /// </summary>
763 internal void BuildEntityQuery()
764 {
765 var withMask = BitSet512.Zero;
766 foreach (var type in QueryWithTypes)
767 {
768 if (!_componentManager.TypeToIndex.ContainsKey(type)) { _componentManager.TypeToIndex.Add(type, _componentManager.TypeToIndex.Count); }
769 withMask = withMask.Set(_componentManager.TypeToIndex[type]);
770 }
771
772 var withoutMask = BitSet512.Zero;
773 foreach (var type in QueryWithoutTypes)
774 {
775 if (!_componentManager.TypeToIndex.ContainsKey(type)) { _componentManager.TypeToIndex.Add(type, _componentManager.TypeToIndex.Count); }
776 withoutMask = withoutMask.Set(_componentManager.TypeToIndex[type]);
777 }
778
779 var immediateMask = BitSet512.Zero;
780 foreach (var type in ReadImmediateTypes)
781 {
782 if (!_componentManager.TypeToIndex.ContainsKey(type)) { _componentManager.TypeToIndex.Add(type, _componentManager.TypeToIndex.Count); }
783 immediateMask = immediateMask.Set(_componentManager.TypeToIndex[type]);
784 }
785
786 var existingMask = BitSet512.Zero;
787 foreach (var type in ReadTypes)
788 {
789 if (!_componentManager.TypeToIndex.ContainsKey(type)) { _componentManager.TypeToIndex.Add(type, _componentManager.TypeToIndex.Count); }
790 existingMask = existingMask.Set(_componentManager.TypeToIndex[type]);
791 }
792
793 _entityQuery = new EntitySetQuery(
794 withMask & immediateMask,
795 withMask & existingMask,
796 withoutMask & immediateMask,
797 withoutMask & existingMask,
798 ~withMask
799 );
800 }
801
802 internal void RegisterDestroyedEntity(int entityID)
803 {
804 _trackedEntities.Remove(entityID);
805 }
806 }
807 }
@@ -0,0 +1,32
1 using System.Reflection;
2
3 namespace Encompass
4 {
5 /// <summary>
6 /// A Spawner is a special type of Engine that runs a Spawn method in response to each Message it receives.
7 /// Spawners are useful for organizing the building of new Entities in your game.
8 /// </summary>
9 public abstract class Spawner<TMessage> : Engine where TMessage : struct, IMessage
10 {
11 protected Spawner() : base()
12 {
13 var readsAttribute = GetType().GetCustomAttribute<Reads>(false);
14 if (readsAttribute != null)
15 {
16 readsAttribute.ReadTypes.Add(typeof(TMessage));
17 }
18
19 ReceiveTypes.Add(typeof(TMessage));
20 }
21
22 public override void Update(double dt)
23 {
24 foreach (ref readonly var message in ReadMessages<TMessage>())
25 {
26 Spawn(message);
27 }
28 }
29
30 protected abstract void Spawn(in TMessage message);
31 }
32 }
@@ -0,0 +1,43
1 using System;
2
3 namespace Encompass
4 {
5 /// <summary>
6 /// An Entity is a structure composed of a unique ID and a collection of Components.
7 /// An Entity may only have a single Component of any particular Type.
8 /// </summary>
9 public struct Entity : IEquatable<Entity>
10 {
11 public readonly int ID;
12
13 internal Entity(int id)
14 {
15 ID = id;
16 }
17
18 public override bool Equals(object obj)
19 {
20 return obj is Entity entity && Equals(entity);
21 }
22
23 public bool Equals(Entity other)
24 {
25 return other.ID == ID;
26 }
27
28 public static bool operator ==(Entity one, Entity two)
29 {
30 return one.Equals(two);
31 }
32
33 public static bool operator !=(Entity one, Entity two)
34 {
35 return !one.Equals(two);
36 }
37
38 public override int GetHashCode()
39 {
40 return HashCode.Combine(ID);
41 }
42 }
43 }
@@ -0,0 +1,92
1 using System.Collections.Generic;
2 using Encompass.Exceptions;
3
4 namespace Encompass
5 {
6 internal class EntityManager
7 {
8 private readonly int _entityCapacity;
9 private readonly IDManager _idManager = new IDManager();
10 private readonly HashSet<int> _ids = new HashSet<int>();
11
12 private readonly HashSet<int> _entitiesMarkedForDestroy = new HashSet<int>();
13
14 private readonly ComponentManager _componentManager;
15
16 public IEnumerable<int> EntityIDs
17 {
18 get { return _ids; }
19 }
20
21 public EntityManager(ComponentManager componentManager, int entityCapacity)
22 {
23 _componentManager = componentManager;
24 _entityCapacity = entityCapacity;
25 }
26
27 private int NextID()
28 {
29 return _idManager.NextID();
30 }
31
32 public Entity CreateEntity()
33 {
34 if (_ids.Count < _entityCapacity)
35 {
36 var id = NextID();
37 var entity = new Entity(id);
38 _ids.Add(id);
39 return entity;
40 }
41 else
42 {
43 throw new EntityOverflowException("The number of entities has exceeded the entity capacity of {0}", _entityCapacity);
44 }
45 }
46
47 public bool EntityExists(int id)
48 {
49 return _ids.Contains(id);
50 }
51
52 public Entity GetEntity(int id)
53 {
54 if (!EntityExists(id))
55 {
56 throw new Encompass.Exceptions.EntityNotFoundException("Entity with id {0} does not exist.", id);
57 }
58
59 return new Entity(id);
60 }
61
62 public void MarkForDestroy(int entityID)
63 {
64 _entitiesMarkedForDestroy.Add(entityID);
65 }
66
67 public void DestroyMarkedEntities(IEnumerable<Engine> engines)
68 {
69 foreach (var entityID in _entitiesMarkedForDestroy)
70 {
71 foreach (var engine in engines) { engine.RegisterDestroyedEntity(entityID); }
72 _componentManager.MarkAllComponentsOnEntityForRemoval(entityID);
73 _ids.Remove(entityID);
74 _idManager.Free(entityID);
75 }
76
77 _entitiesMarkedForDestroy.Clear();
78 }
79
80 // NOTE: this is very suboptimal
81 public void PruneEmptyEntities()
82 {
83 foreach (var id in EntityIDs)
84 {
85 if (_componentManager.UpToDateEntityIsEmpty(id))
86 {
87 MarkForDestroy(id);
88 }
89 }
90 }
91 }
92 }
@@ -0,0 +1,48
1 using MoonTools.FastCollections;
2
3 namespace Encompass
4 {
5 internal struct EntitySetQuery
6 {
7 private BitSet512 WithImmediateMask { get; }
8 private BitSet512 WithExistingMask { get; }
9 private BitSet512 WithoutImmediateMask { get; }
10 private BitSet512 WithoutExistingMask { get; }
11 private BitSet512 NotWithMask { get; }
12
13 internal EntitySetQuery(BitSet512 withImmediateMask, BitSet512 withExistingMask, BitSet512 withoutImmediateMask, BitSet512 withoutExistingMask, BitSet512 notWithMask)
14 {
15 WithImmediateMask = withImmediateMask;
16 WithExistingMask = withExistingMask;
17 WithoutImmediateMask = withoutImmediateMask;
18 WithoutExistingMask = withoutExistingMask;
19 NotWithMask = notWithMask;
20 }
21
22 public bool CheckEntity(int entityID, ComponentBitSet componentBitSet)
23 {
24 var existingBits = componentBitSet.EntityBitArray(entityID);
25 var existing = (WithExistingMask & existingBits) | NotWithMask;
26
27 var existingForbidden = ~(WithoutExistingMask & existingBits);
28
29 return (existing & existingForbidden).AllTrue();
30 }
31
32 public bool ImmediateCheckEntity(int entityID, ComponentBitSet immediateBitLookup, ComponentBitSet existingBitLookup)
33 {
34 var immediateBits = immediateBitLookup.EntityBitArray(entityID);
35 var existingBits = existingBitLookup.EntityBitArray(entityID);
36
37 var immediate = WithImmediateMask & immediateBits;
38 var existing = WithExistingMask & existingBits;
39 var withCheck = immediate | existing | NotWithMask;
40
41 var immediateForbidden = ~(WithoutImmediateMask & immediateBits);
42 var existingForbidden = ~(WithoutExistingMask & existingBits);
43 var withoutCheck = immediateForbidden & existingForbidden;
44
45 return (withCheck & withoutCheck).AllTrue();
46 }
47 }
48 }
@@ -0,0 +1,13
1
2 using System;
3
4 namespace Encompass.Exceptions
5 {
6 public class EngineCycleException : Exception
7 {
8 public EngineCycleException(
9 string format,
10 params object[] args
11 ) : base(string.Format(format, args)) { }
12 }
13 }
@@ -0,0 +1,12
1 using System;
2
3 namespace Encompass.Exceptions
4 {
5 public class EngineSelfCycleException : Exception
6 {
7 public EngineSelfCycleException(
8 string format,
9 params object[] args
10 ) : base(string.Format(format, args)) { }
11 }
12 }
@@ -0,0 +1,12
1 using System;
2
3 namespace Encompass.Exceptions
4 {
5 public class EngineWriteConflictException : Exception
6 {
7 public EngineWriteConflictException(
8 string format,
9 params object[] args
10 ) : base(string.Format(format, args)) { }
11 }
12 }
@@ -0,0 +1,12
1 using System;
2
3 namespace Encompass.Exceptions
4 {
5 public class EntityNotFoundException : Exception
6 {
7 public EntityNotFoundException(
8 string format,
9 params object[] args
10 ) : base(string.Format(format, args)) { }
11 }
12 }
@@ -0,0 +1,12
1 using System;
2
3 namespace Encompass.Exceptions
4 {
5 public class EntityOverflowException : Exception
6 {
7 public EntityOverflowException(
8 string format,
9 params object[] args
10 ) : base(string.Format(format, args)) { }
11 }
12 }
@@ -0,0 +1,13
1
2 using System;
3
4 namespace Encompass.Exceptions
5 {
6 public class IllegalDrawLayerException : Exception
7 {
8 public IllegalDrawLayerException(
9 string format,
10 params object[] args
11 ) : base(string.Format(format, args)) { }
12 }
13 }
@@ -0,0 +1,13
1
2 using System;
3
4 namespace Encompass.Exceptions
5 {
6 public class IllegalEngineAttributesException : Exception
7 {
8 public IllegalEngineAttributesException(
9 string format,
10 params object[] args
11 ) : base(string.Format(format, args)) { }
12 }
13 }
@@ -0,0 +1,12
1 using System;
2
3 namespace Encompass.Exceptions
4 {
5 public class IllegalReadException : Exception
6 {
7 public IllegalReadException(
8 string format,
9 params object[] args
10 ) : base(string.Format(format, args)) { }
11 }
12 }
@@ -0,0 +1,12
1 using System;
2
3 namespace Encompass.Exceptions
4 {
5 public class IllegalSendException : Exception
6 {
7 public IllegalSendException(
8 string format,
9 params object[] args
10 ) : base(string.Format(format, args)) { }
11 }
12 }
@@ -0,0 +1,12
1 using System;
2
3 namespace Encompass.Exceptions
4 {
5 public class IllegalSendTypeException : Exception
6 {
7 public IllegalSendTypeException(
8 string format,
9 params object[] args
10 ) : base(string.Format(format, args)) { }
11 }
12 }
@@ -0,0 +1,12
1 using System;
2
3 namespace Encompass.Exceptions
4 {
5 public class IllegalWriteException : Exception
6 {
7 public IllegalWriteException(
8 string format,
9 params object[] args
10 ) : base(string.Format(format, args)) { }
11 }
12 }
@@ -0,0 +1,12
1 using System;
2
3 namespace Encompass.Exceptions
4 {
5 public class IllegalWriteImmediateException : Exception
6 {
7 public IllegalWriteImmediateException(
8 string format,
9 params object[] args
10 ) : base(string.Format(format, args)) { }
11 }
12 }
@@ -0,0 +1,12
1 using System;
2
3 namespace Encompass.Exceptions
4 {
5 public class IllegalWriteImmediateTypeException : Exception
6 {
7 public IllegalWriteImmediateTypeException(
8 string format,
9 params object[] args
10 ) : base(string.Format(format, args)) { }
11 }
12 }
@@ -0,0 +1,12
1 using System;
2
3 namespace Encompass.Exceptions
4 {
5 public class IllegalWriteTypeException : Exception
6 {
7 public IllegalWriteTypeException(
8 string format,
9 params object[] args
10 ) : base(string.Format(format, args)) { }
11 }
12 }
@@ -0,0 +1,12
1 using System;
2
3 namespace Encompass.Exceptions
4 {
5 public class MultipleComponentOfSameTypeException : Exception
6 {
7 public MultipleComponentOfSameTypeException(
8 string format,
9 params object[] args
10 ) : base(string.Format(format, args)) { }
11 }
12 }
@@ -0,0 +1,12
1 using System;
2
3 namespace Encompass.Exceptions
4 {
5 public class NoComponentOfTypeException : Exception
6 {
7 public NoComponentOfTypeException(
8 string format,
9 params object[] args
10 ) : base(string.Format(format, args)) { }
11 }
12 }
@@ -0,0 +1,12
1 using System;
2
3 namespace Encompass.Exceptions
4 {
5 public class NoComponentOfTypeOnEntityException : Exception
6 {
7 public NoComponentOfTypeOnEntityException(
8 string format,
9 params object[] args
10 ) : base(string.Format(format, args)) { }
11 }
12 }
@@ -0,0 +1,12
1 using System;
2
3 namespace Encompass.Exceptions
4 {
5 public class NoMessageOfTypeException : Exception
6 {
7 public NoMessageOfTypeException(
8 string format,
9 params object[] args
10 ) : base(string.Format(format, args)) { }
11 }
12 }
@@ -0,0 +1,12
1 using System;
2
3 namespace Encompass.Exceptions
4 {
5 public class UndefinedLayerException : Exception
6 {
7 public UndefinedLayerException(
8 string format,
9 params object[] args
10 ) : base(string.Format(format, args)) { }
11 }
12 }
@@ -0,0 +1,28
1 using System.Collections.Generic;
2
3 namespace Encompass
4 {
5 internal class IDManager
6 {
7 int _nextID = 0;
8
9 private readonly Stack<int> _availableIDs = new Stack<int>();
10
11 public int NextID()
12 {
13 if (_availableIDs.Count > 0)
14 {
15 return _availableIDs.Pop();
16 }
17 else
18 {
19 return _nextID++;
20 }
21 }
22
23 public void Free(int id)
24 {
25 _availableIDs.Push(id);
26 }
27 }
28 }
@@ -0,0 +1,7
1 namespace Encompass
2 {
3 /// <summary>
4 /// Structs that implement IMessage are considered to be Messages.
5 /// </summary>
6 public interface IMessage { }
7 }
@@ -0,0 +1,4
1 namespace Encompass
2 {
3 public interface IComponent { }
4 }
@@ -0,0 +1,7
1 namespace Encompass
2 {
3 public interface IDrawableComponent
4 {
5 int Layer { get; }
6 }
7 }
@@ -0,0 +1,7
1 namespace Encompass
2 {
3 public interface IHasEntity
4 {
5 Entity Entity { get; }
6 }
7 }
@@ -0,0 +1,71
1 using System;
2 using System.Collections.Generic;
3
4 namespace Encompass
5 {
6 internal class MessageManager
7 {
8 private readonly TimeManager _timeManager;
9 private readonly MessageStore _messageStore = new MessageStore();
10
11 public MessageManager(TimeManager timeManager)
12 {
13 _timeManager = timeManager;
14 }
15
16 internal void AddMessage<TMessage>(in TMessage message) where TMessage : struct, IMessage
17 {
18 _messageStore.AddMessage(message);
19 }
20
21 internal void AddMessage<TMessage>(in TMessage message, double time) where TMessage : struct, IMessage
22 {
23 _messageStore.AddMessage(message, time);
24 }
25
26 internal void AddMessageIgnoringTimeDilation<TMessage>(in TMessage message, double time) where TMessage : struct, IMessage
27 {
28 _messageStore.AddMessageIgnoringTimeDilation(message, time);
29 }
30
31 internal void ClearMessages()
32 {
33 _messageStore.ClearAll();
34 }
35
36 internal void ProcessDelayedMessages(double dt)
37 {
38 _messageStore.ProcessDelayedMessages(dt * _timeManager.TimeDilationFactor, dt);
39 }
40
41 internal Span<TMessage> GetMessagesByType<TMessage>() where TMessage : struct, IMessage
42 {
43 return _messageStore.All<TMessage>();
44 }
45
46 internal bool Any<TMessage>() where TMessage : struct, IMessage
47 {
48 return _messageStore.Any<TMessage>();
49 }
50
51 internal ref readonly TMessage First<TMessage>() where TMessage : struct, IMessage
52 {
53 return ref _messageStore.First<TMessage>();
54 }
55
56 internal IEnumerable<TMessage> WithEntity<TMessage>(int entityID) where TMessage : struct, IMessage, IHasEntity
57 {
58 return _messageStore.WithEntity<TMessage>(entityID);
59 }
60
61 internal ref readonly TMessage WithEntitySingular<TMessage>(int entityID) where TMessage : struct, IMessage, IHasEntity
62 {
63 return ref _messageStore.FirstWithEntity<TMessage>(entityID);
64 }
65
66 internal bool SomeWithEntity<TMessage>(int entityID) where TMessage : struct, IMessage, IHasEntity
67 {
68 return _messageStore.SomeWithEntity<TMessage>(entityID);
69 }
70 }
71 }
@@ -0,0 +1,52
1 using System;
2 using System.Collections.Generic;
3
4 namespace Encompass
5 {
6 internal class RenderManager
7 {
8 private readonly EntityManager _entityManager;
9 private readonly DrawLayerManager _drawLayerManager;
10
11 private readonly Dictionary<Type, Action<Entity>> _drawComponentTypeToOrderedRenderer = new Dictionary<Type, Action<Entity>>(256);
12
13 public RenderManager(EntityManager entityManager, DrawLayerManager drawLayerManager)
14 {
15 _entityManager = entityManager;
16 _drawLayerManager = drawLayerManager;
17 }
18
19 public void RegisterOrderedRenderer<TComponent>(Action<Entity> renderAction) where TComponent : struct
20 {
21 _drawComponentTypeToOrderedRenderer.Add(typeof(TComponent), renderAction);
22 _drawLayerManager.RegisterOrderedDrawable<TComponent>();
23 }
24
25 public void RegisterGeneralRendererWithLayer(GeneralRenderer renderer, int layer)
26 {
27 _drawLayerManager.RegisterGeneralRendererWithLayer(renderer, layer);
28 }
29
30 public void Draw()
31 {
32 foreach (var layer in _drawLayerManager.LayerOrder)
33 {
34 var generalRendererSet = _drawLayerManager.GeneralRenderersByLayer(layer);
35
36 foreach (var (entityID, componentType) in _drawLayerManager.AllInLayer(layer))
37 {
38 if (_drawComponentTypeToOrderedRenderer.ContainsKey(componentType))
39 {
40 var internalRenderAction = _drawComponentTypeToOrderedRenderer[componentType];
41 internalRenderAction(_entityManager.GetEntity(entityID));
42 }
43 }
44
45 foreach (var generalRenderer in generalRendererSet)
46 {
47 generalRenderer.Render();
48 }
49 }
50 }
51 }
52 }
@@ -0,0 +1,56
1 using System;
2 using System.Collections.Generic;
3
4 namespace Encompass
5 {
6 public abstract class Renderer
7 {
8 internal EntityManager _entityManager;
9 internal ComponentManager _componentManager;
10
11 internal void AssignEntityManager(EntityManager entityManager)
12 {
13 _entityManager = entityManager;
14 }
15
16 internal void AssignComponentManager(ComponentManager componentManager)
17 {
18 _componentManager = componentManager;
19 }
20
21 protected Span<Entity> ReadEntities<TComponent>() where TComponent : struct, IComponent
22 {
23 return _componentManager.GetExistingEntities<TComponent>();
24 }
25
26 protected ref readonly Entity ReadEntity<TComponent>() where TComponent : struct, IComponent
27 {
28 return ref _componentManager.ExistingSingularEntity<TComponent>();
29 }
30
31 protected Span<TComponent> ReadComponents<TComponent>() where TComponent : struct, IComponent
32 {
33 return _componentManager.GetComponentsByType<TComponent>();
34 }
35
36 protected ref readonly TComponent ReadComponent<TComponent>() where TComponent : struct, IComponent
37 {
38 return ref _componentManager.ExistingSingular<TComponent>();
39 }
40
41 protected ref readonly TComponent GetComponent<TComponent>(Entity entity) where TComponent : struct, IComponent
42 {
43 return ref _componentManager.GetComponentByEntityAndType<TComponent>(entity.ID);
44 }
45
46 protected bool HasComponent<TComponent>(Entity entity) where TComponent : struct, IComponent
47 {
48 return _componentManager.EntityHasComponentOfType<TComponent>(entity.ID);
49 }
50
51 protected bool SomeComponent<TComponent>() where TComponent : struct, IComponent
52 {
53 return _componentManager.SomeExistingComponent<TComponent>();
54 }
55 }
56 }
@@ -0,0 +1,11
1 namespace Encompass
2 {
3 /// <summary>
4 /// GeneralRenderer is a Renderer which generically reads the game state in order to draw elements to the screen.
5 /// GeneralRenderers have a layer specified when they are added to the World.
6 /// </summary>
7 public abstract class GeneralRenderer : Renderer
8 {
9 public abstract void Render();
10 }
11 }
@@ -0,0 +1,18
1 using System;
2
3 namespace Encompass
4 {
5 /// <summary>
6 /// OrdereredRenderer provides a structure for the common pattern of wishing to draw a specific DrawComponent at a specific layer.
7 /// </summary>
8 public abstract class OrderedRenderer<TComponent> : Renderer where TComponent : struct, IComponent, IDrawableComponent
9 {
10 public abstract void Render(Entity entity, in TComponent drawComponent);
11
12 internal void InternalRender(Entity entity)
13 {
14 ref readonly var component = ref GetComponent<TComponent>(entity);
15 Render(entity, component);
16 }
17 }
18 }
@@ -0,0 +1,56
1 using System;
2
3 namespace Encompass
4 {
5 internal struct TimeDilationData
6 {
7 private readonly double _factor;
8
9 public double Factor
10 {
11 get
12 {
13 if (ElapsedTime < EaseInTime)
14 {
15 return EaseInFunction(ElapsedTime, 1, _factor - 1, EaseInTime);
16 }
17 else if (ElapsedTime < EaseInTime + ActiveTime)
18 {
19 return _factor;
20 }
21 else if (ElapsedTime < EaseInTime + ActiveTime + EaseOutTime)
22 {
23 var elapsedOutTime = ElapsedTime - EaseInTime - ActiveTime;
24 return EaseOutFunction(elapsedOutTime, _factor, 1 - _factor, EaseOutTime);
25 }
26
27 return 1;
28 }
29 }
30
31 public double ElapsedTime { get; set; }
32 public double EaseInTime { get; }
33 public Func<double, double, double, double, double> EaseInFunction { get; }
34 public double ActiveTime { get; }
35 public double EaseOutTime { get; }
36 public Func<double, double, double, double, double> EaseOutFunction { get; }
37
38 public TimeDilationData(
39 double factor,
40 double easeInTime,
41 Func<double, double, double, double, double> easeInfunction,
42 double activeTime,
43 double easeOutTime,
44 Func<double, double, double, double, double> easeOutFunction
45 )
46 {
47 _factor = factor;
48 EaseInTime = easeInTime;
49 EaseInFunction = easeInfunction;
50 ActiveTime = activeTime;
51 EaseOutTime = easeOutTime;
52 EaseOutFunction = easeOutFunction;
53 ElapsedTime = 0;
54 }
55 }
56 }
@@ -0,0 +1,80
1 using System.Collections.Generic;
2
3 namespace Encompass
4 {
5 internal class TimeManager
6 {
7 private readonly List<TimeDilationData> _timeDilationDatas = new List<TimeDilationData>(32);
8
9 private double Linear(double t, double b, double c, double d)
10 {
11 return (c * t / d) + b;
12 }
13
14 public double TimeDilationFactor
15 {
16 get
17 {
18 if (_timeDilationDatas.Count == 0) { return 1; }
19 var average = 0.0;
20 foreach (var data in _timeDilationDatas)
21 {
22 average += data.Factor;
23 }
24 return average / _timeDilationDatas.Count;
25 }
26 }
27
28 public bool TimeDilationActive
29 {
30 get => _timeDilationDatas.Count != 0;
31 }
32
33 public void Update(double dt)
34 {
35 for (var i = _timeDilationDatas.Count - 1; i >= 0; i--)
36 {
37 var data = _timeDilationDatas[i];
38
39 data.ElapsedTime += dt;
40
41 if (data.ElapsedTime > data.EaseInTime + data.ActiveTime + data.EaseOutTime)
42 {
43 _timeDilationDatas.RemoveAt(i);
44 }
45 else
46 {
47 _timeDilationDatas[i] = data;
48 }
49 }
50 }
51
52 public void ActivateTimeDilation(double factor, double easeInTime, double activeTime, double easeOutTime)
53 {
54 ActivateTimeDilation(factor, easeInTime, Linear, activeTime, easeOutTime, Linear);
55 }
56
57 public void ActivateTimeDilation(double factor, double easeInTime, System.Func<double, double, double, double, double> easeInFunction, double activeTime, double easeOutTime)
58 {
59 ActivateTimeDilation(factor, easeInTime, easeInFunction, activeTime, easeOutTime, Linear);
60 }
61
62 public void ActivateTimeDilation(double factor, double easeInTime, double activeTime, double easeOutTime, System.Func<double, double, double, double, double> easeOutFunction)
63 {
64 ActivateTimeDilation(factor, easeInTime, Linear, activeTime, easeOutTime, easeOutFunction);
65 }
66
67 public void ActivateTimeDilation(double factor, double easeInTime, System.Func<double, double, double, double, double> easeInFunction, double activeTime, double easeOutTime, System.Func<double, double, double, double, double> easeOutFunction)
68 {
69 _timeDilationDatas.Add(new TimeDilationData
70 (
71 factor,
72 easeInTime,
73 easeInFunction,
74 activeTime,
75 easeOutTime,
76 easeOutFunction
77 ));
78 }
79 }
80 }
@@ -0,0 +1,97
1 using System;
2 using System.Collections.Generic;
3
4 namespace Encompass
5 {
6 internal class TrackingManager
7 {
8 private readonly Dictionary<Type, HashSet<Engine>> _immediateComponentTypesToEngines = new Dictionary<Type, HashSet<Engine>>();
9 private readonly Dictionary<Type, HashSet<Engine>> _componentTypesToEngines = new Dictionary<Type, HashSet<Engine>>();
10
11 private readonly HashSet<(int, Type)> _additions = new HashSet<(int, Type)>();
12 private readonly HashSet<(int, Type)> _removals = new HashSet<(int, Type)>();
13
14 private readonly HashSet<(int, Engine)> _pairsToCheck = new HashSet<(int, Engine)>();
15
16 public void RegisterComponentTypeToEngine(Type type, Engine engine)
17 {
18 if (!_componentTypesToEngines.ContainsKey(type)) { _componentTypesToEngines.Add(type, new HashSet<Engine>()); }
19 _componentTypesToEngines[type].Add(engine);
20 }
21
22 public void RegisterImmediateComponentTypeToEngine(Type type, Engine engine)
23 {
24 if (!_immediateComponentTypesToEngines.ContainsKey(type)) { _immediateComponentTypesToEngines.Add(type, new HashSet<Engine>()); }
25 _immediateComponentTypesToEngines[type].Add(engine);
26 }
27
28 public void RegisterAddition(int entityID, Type type)
29 {
30 _additions.Add((entityID, type));
31 }
32
33 public void RegisterRemoval(int entityID, Type type)
34 {
35 _removals.Add((entityID, type));
36 }
37
38 public void InitializeTracking(IEnumerable<int> entityIDs)
39 {
40 foreach (var entityID in entityIDs)
41 {
42 foreach (var engineSet in _componentTypesToEngines.Values)
43 {
44 foreach (var engine in engineSet)
45 {
46 engine.CheckAndUpdateTracking(entityID);
47 }
48 }
49 }
50 }
51
52 public void ImmediateUpdateTracking(int entityID, Type componentType)
53 {
54 if (_immediateComponentTypesToEngines.ContainsKey(componentType))
55 {
56 foreach (var engine in _componentTypesToEngines[componentType])
57 {
58 engine.ImmediateCheckAndUpdateTracking(entityID);
59 }
60 }
61 }
62
63 public void UpdateTracking()
64 {
65 // TODO: optimize so we only check each entity/engine pair once
66 foreach (var (entity, componentType) in _additions)
67 {
68 if (_componentTypesToEngines.ContainsKey(componentType))
69 {
70 foreach (var engine in _componentTypesToEngines[componentType])
71 {
72 _pairsToCheck.Add((entity, engine));
73 }
74 }
75 }
76 _additions.Clear();
77
78 foreach (var (entity, componentType) in _removals)
79 {
80 if (_componentTypesToEngines.ContainsKey(componentType))
81 {
82 foreach (var engine in _componentTypesToEngines[componentType])
83 {
84 _pairsToCheck.Add((entity, engine));
85 }
86 }
87 }
88 _removals.Clear();
89
90 foreach (var (entity, engine) in _pairsToCheck)
91 {
92 engine.CheckAndUpdateTracking(entity);
93 }
94 _pairsToCheck.Clear();
95 }
96 }
97 }
@@ -0,0 +1,96
1 using System;
2 using System.Collections.Generic;
3 using System.Reflection;
4
5 namespace Encompass
6 {
7 internal class UberEngine : Engine
8 {
9 private readonly IEnumerable<Type> _componentTypes;
10 private readonly IEnumerable<Type> _messageTypes;
11 public Entity Entity { get; private set; }
12
13 public UberEngine(IEnumerable<Type> componentTypes, IEnumerable<Type> messageTypes)
14 {
15 _componentTypes = componentTypes;
16 _messageTypes = messageTypes;
17 ReadTypes.UnionWith(componentTypes);
18 WriteTypes.UnionWith(componentTypes);
19 SendTypes.UnionWith(messageTypes);
20 ReceiveTypes.UnionWith(messageTypes);
21 }
22
23 public void Write()
24 {
25 Entity = CreateEntity();
26
27 foreach (var type in _componentTypes)
28 {
29 var instance = Activator.CreateInstance(type);
30 var instanceParam = new object[] { Entity, instance };
31 var setComponentMethod = typeof(Engine).GetMethod("SetComponent", BindingFlags.NonPublic | BindingFlags.Instance);
32 var genericSetComponentMethod = setComponentMethod.MakeGenericMethod(type);
33 genericSetComponentMethod.Invoke(this, instanceParam);
34 }
35 }
36
37 public override void Update(double dt)
38 {
39 foreach (var type in _componentTypes)
40 {
41 CallGenericWrappedMethod(type, "CallAllComponentMethods", null);
42 }
43
44 foreach (var type in _messageTypes)
45 {
46 CallGenericWrappedMethod(type, "CallAllMessageMethods", null);
47
48 if (typeof(IHasEntity).IsAssignableFrom(type))
49 {
50 CallGenericWrappedMethod(type, "CallAllEntityMessageMethods", null);
51 }
52 }
53 }
54
55 // we can't reflect invoke on byref returns or Span returns right now... so we have non-return wrapper methods
56
57 protected void CallAllComponentMethods<TComponent>() where TComponent : struct, IComponent
58 {
59 ReadComponent<TComponent>();
60 ReadComponents<TComponent>();
61 ReadEntity<TComponent>();
62 ReadEntities<TComponent>();
63 GetComponent<TComponent>(Entity);
64 HasComponent<TComponent>(Entity);
65 SomeComponent<TComponent>();
66 DestroyWith<TComponent>();
67 DestroyAllWith<TComponent>();
68 RemoveComponent<TComponent>(Entity);
69 AddComponent<TComponent>(Entity, default);
70 }
71
72 protected void CallAllMessageMethods<TMessage>() where TMessage : struct, IMessage
73 {
74 SendMessageIgnoringTimeDilation<TMessage>(default, 0.1);
75 SendMessage<TMessage>(default);
76 SendMessage<TMessage>(default, 0.1);
77 ReadMessage<TMessage>();
78 ReadMessages<TMessage>();
79 SomeMessage<TMessage>();
80 }
81
82 protected void CallAllEntityMessageMethods<TMessage>() where TMessage : struct, IMessage, IHasEntity
83 {
84 ReadMessagesWithEntity<TMessage>(Entity);
85 ReadMessageWithEntity<TMessage>(Entity);
86 SomeMessageWithEntity<TMessage>(Entity);
87 }
88
89 private void CallGenericWrappedMethod(Type type, string methodName, object[] parameters)
90 {
91 var readComponentMethod = typeof(UberEngine).GetMethod(methodName, BindingFlags.NonPublic | BindingFlags.Instance);
92 var genericReadComponentMethod = readComponentMethod.MakeGenericMethod(type);
93 genericReadComponentMethod.Invoke(this, parameters);
94 }
95 }
96 }
@@ -0,0 +1,49
1 using System;
2 using System.Collections.Generic;
3 using System.Reflection;
4
5 namespace Encompass
6 {
7 class UberRenderer : Renderer
8 {
9 private readonly IEnumerable<Type> _componentTypes;
10 private Entity _entity;
11
12 public UberRenderer(IEnumerable<Type> componentTypes)
13 {
14 _componentTypes = componentTypes;
15 }
16
17 public void SetEntity(Entity entity)
18 {
19 _entity = entity;
20 }
21
22 // can't reflect invoke on Span returns...
23 public void Render()
24 {
25 foreach (var type in _componentTypes)
26 {
27 CallGenericWrappedMethod(type, "CallAllComponentMethods", null);
28 }
29 }
30
31 protected void CallAllComponentMethods<TComponent>() where TComponent : struct, IComponent
32 {
33 ReadEntity<TComponent>();
34 ReadEntities<TComponent>();
35 ReadComponent<TComponent>();
36 ReadComponents<TComponent>();
37 GetComponent<TComponent>(_entity);
38 HasComponent<TComponent>(_entity);
39 SomeComponent<TComponent>();
40 }
41
42 private void CallGenericWrappedMethod(Type type, string methodName, object[] parameters)
43 {
44 var readComponentMethod = typeof(UberRenderer).GetMethod(methodName, BindingFlags.NonPublic | BindingFlags.Instance);
45 var genericReadComponentMethod = readComponentMethod.MakeGenericMethod(type);
46 genericReadComponentMethod.Invoke(this, parameters);
47 }
48 }
49 }
@@ -0,0 +1,77
1 using System.Collections.Generic;
2
3 namespace Encompass
4 {
5 /// <summary>
6 /// The World is a collection of Engines, Renderers, Entities, Components, and Messages that compose the simulation.
7 /// </summary>
8 public class World
9 {
10 private readonly List<Engine> _enginesInOrder;
11 private readonly EntityManager _entityManager;
12 private readonly ComponentManager _componentManager;
13 private readonly TrackingManager _trackingManager;
14 private readonly MessageManager _messageManager;
15 private readonly TimeManager _timeManager;
16 private readonly RenderManager _renderManager;
17
18 internal World(
19 List<Engine> enginesInOrder,
20 EntityManager entityManager,
21 ComponentManager componentManager,
22 TrackingManager trackingManager,
23 MessageManager messageManager,
24 TimeManager timeManager,
25 RenderManager renderManager
26 )
27 {
28 _enginesInOrder = enginesInOrder;
29 _entityManager = entityManager;
30 _componentManager = componentManager;
31 _trackingManager = trackingManager;
32 _messageManager = messageManager;
33 _timeManager = timeManager;
34 _renderManager = renderManager;
35 }
36
37 /// <summary>
38 /// Drives the simulation. Should be called from your game engine's update loop.
39 /// </summary>
40 /// <param name="dt">The time in seconds that has passed since the previous frame.</param>
41 public void Update(double dt)
42 {
43 _trackingManager.UpdateTracking();
44 _messageManager.ProcessDelayedMessages(dt);
45 _timeManager.Update(dt);
46
47 foreach (var engine in _enginesInOrder)
48 {
49 if (engine._usesTimeDilation)
50 {
51 engine.Update(dt * _timeManager.TimeDilationFactor);
52 }
53 else
54 {
55 engine.Update(dt);
56 }
57
58 engine.ClearNewlyCreatedEntities();
59 }
60
61 _messageManager.ClearMessages();
62 _entityManager.PruneEmptyEntities();
63 _entityManager.DestroyMarkedEntities(_enginesInOrder);
64
65 _componentManager.RemoveMarkedComponents();
66 _componentManager.WriteComponents();
67 }
68
69 /// <summary>
70 /// Causes the Renderers to draw.
71 /// </summary>
72 public void Draw()
73 {
74 _renderManager.Draw();
75 }
76 }
77 }
@@ -0,0 +1,471
1 using System;
2 using System.Collections.Generic;
3 using System.Reflection;
4 using System.Linq;
5 using Encompass.Exceptions;
6 using MoonTools.Core.Graph;
7 using MoonTools.Core.Graph.Extensions;
8
9 namespace Encompass
10 {
11 /// <summary>
12 /// WorldBuilder is used to construct a World from Engines, Renderers, and an initial state of Entities, Components, and Messages.
13 /// </summary>
14 /// <remarks>
15 /// WorldBuilder enforces certain rules about Engine structure. It is forbidden to have messages create cycles between Engines,
16 /// and no Component may be written by more than one Engine.
17 /// The WorldBuilder uses Engines and their Message read/emit information to determine a valid ordering of the Engines, which is given to the World.
18 /// </remarks>
19 public class WorldBuilder
20 {
21 private readonly int _entityCapacity;
22 private readonly List<Engine> _engines = new List<Engine>();
23 private readonly DirectedGraph<Engine, Unit> _engineGraph = GraphBuilder.DirectedGraph<Engine>();
24 private readonly ComponentStore _startingExistingComponentStore;
25 private readonly ComponentStore _startingUpToDateComponentStore;
26
27 private readonly ComponentManager _componentManager;
28 private readonly EntityManager _entityManager;
29 private readonly MessageManager _messageManager;
30 private readonly TimeManager _timeManager;
31 private readonly DrawLayerManager _drawLayerManager;
32 private readonly RenderManager _renderManager;
33 private readonly TrackingManager _trackingManager;
34
35 private readonly Dictionary<Type, HashSet<Engine>> _typeToReaders = new Dictionary<Type, HashSet<Engine>>();
36 private readonly HashSet<Engine> _senders = new HashSet<Engine>();
37 private readonly HashSet<Type> _componentTypesToPreload = new HashSet<Type>();
38 private readonly HashSet<Type> _messageTypes = new HashSet<Type>();
39 private readonly Dictionary<Type, int> _typeToIndex = new Dictionary<Type, int>();
40
41 private bool _rendererRegistered = false;
42
43 public WorldBuilder(int entityCapacity = 32768)
44 {
45 _entityCapacity = entityCapacity;
46 _drawLayerManager = new DrawLayerManager();
47 _timeManager = new TimeManager();
48 _trackingManager = new TrackingManager();
49 _componentManager = new ComponentManager(_drawLayerManager, _typeToIndex);
50 _messageManager = new MessageManager(_timeManager);
51 _entityManager = new EntityManager(_componentManager, entityCapacity);
52 _renderManager = new RenderManager(_entityManager, _drawLayerManager);
53
54 _startingExistingComponentStore = new ComponentStore(_typeToIndex);
55 _startingUpToDateComponentStore = new ComponentStore(_typeToIndex);
56 }
57
58 /// <summary>
59 /// Creates and returns a new empty Entity.
60 /// </summary>
61 public Entity CreateEntity()
62 {
63 return _entityManager.CreateEntity();
64 }
65
66 /// <summary>
67 /// Specifies that the given Message should be sent immediately on the first World Update.
68 /// </summary>
69 public void SendMessage<TMessage>(in TMessage message) where TMessage : struct, IMessage
70 {
71 _messageManager.AddMessage(message);
72 }
73
74 /// <summary>
75 /// Specifies that the given Message should be sent after the specified number of seconds after the first World Update.
76 /// </summary>
77 public void SendMessage<TMessage>(in TMessage message, double time) where TMessage : struct, IMessage
78 {
79 _messageManager.AddMessage<TMessage>(message, time);
80 }
81
82 /// <summary>
83 /// Sets Component data for the specified Component Type on the specified Entity.
84 /// </summary>
85 public void SetComponent<TComponent>(Entity entity, in TComponent component) where TComponent : struct
86 {
87 RegisterComponentType<TComponent>();
88 _startingExistingComponentStore.Set(entity.ID, component);
89 _startingUpToDateComponentStore.Set(entity.ID, component);
90
91 if (component is IDrawableComponent drawableComponent)
92 {
93 _componentManager.RegisterDrawableComponent<TComponent>(entity.ID, drawableComponent.Layer);
94 _drawLayerManager.RegisterOrderedDrawable<TComponent>();
95 }
96 }
97
98 internal void RegisterComponentType<TComponent>() where TComponent : struct
99 {
100 if (!_typeToIndex.ContainsKey(typeof(TComponent)))
101 {
102 _typeToIndex.Add(typeof(TComponent), _typeToIndex.Count);
103 _componentTypesToPreload.Add(typeof(TComponent));
104 _componentManager.RegisterComponentType<TComponent>();
105 _startingExistingComponentStore.RegisterComponentType<TComponent>();
106 _startingUpToDateComponentStore.RegisterComponentType<TComponent>();
107 }
108 }
109
110 internal void RegisterMessageTypes(IEnumerable<Type> types)
111 {
112 _messageTypes.UnionWith(types);
113 }
114
115 /// <summary>
116 /// Adds the specified Engine to the World.
117 /// </summary>
118 /// <param name="engine">An instance of an Engine.</param>
119 public Engine AddEngine<TEngine>(TEngine engine) where TEngine : Engine
120 {
121 engine.AssignEntityManager(_entityManager);
122 engine.AssignComponentManager(_componentManager);
123 engine.AssignMessageManager(_messageManager);
124 engine.AssignTimeManager(_timeManager);
125 engine.AssignTrackingManager(_trackingManager);
126
127 _engines.Add(engine);
128 _engineGraph.AddNode(engine);
129
130 var messageReceiveTypes = engine.ReceiveTypes;
131 var messageSendTypes = engine.SendTypes;
132
133 RegisterMessageTypes(engine.ReceiveTypes.Union(engine.SendTypes));
134
135 foreach (var writeImmediateType in engine.WriteImmediateTypes.Intersect(engine.ReadImmediateTypes))
136 {
137 throw new EngineSelfCycleException("Engine {0} both writes and reads immediate Component {1}", engine.GetType().Name, writeImmediateType.Name);
138 }
139
140 foreach (var messageType in messageReceiveTypes.Intersect(messageSendTypes))
141 {
142 throw new EngineSelfCycleException("Engine {0} both receives and sends Message {1}", engine.GetType().Name, messageType.Name);
143 }
144
145 if (messageSendTypes.Count > 0 || engine.WriteImmediateTypes.Count > 0)
146 {
147 _senders.Add(engine);
148 }
149
150 foreach (var componentType in engine.QueryWithTypes.Union(engine.QueryWithoutTypes))
151 {
152 _trackingManager.RegisterComponentTypeToEngine(componentType, engine);
153 if (engine.ReadImmediateTypes.Contains(componentType))
154 {
155 _trackingManager.RegisterImmediateComponentTypeToEngine(componentType, engine);
156 }
157 }
158
159 foreach (var receiveType in engine.ReceiveTypes.Union(engine.ReadImmediateTypes))
160 {
161 if (!_typeToReaders.ContainsKey(receiveType))
162 {
163 _typeToReaders.Add(receiveType, new HashSet<Engine>());
164 }
165
166 _typeToReaders[receiveType].Add(engine);
167 }
168
169 return engine;
170 }
171
172 /// <summary>
173 /// Registers a draw layer. This must be done before any Renderers are registered.
174 /// </summary>
175 /// <param name="layer">The draw layer to register.</param>
176 public void RegisterDrawLayer(int layer)
177 {
178 if (_rendererRegistered)
179 {
180 throw new IllegalDrawLayerException("Cannot register a draw layer after a Renderer has been registered.");
181 }
182 _drawLayerManager.RegisterDrawLayer(layer);
183 }
184
185 /// <summary>
186 /// Adds the specified OrderedRenderer to the World.
187 /// </summary>
188 public OrderedRenderer<TComponent> AddOrderedRenderer<TComponent>(OrderedRenderer<TComponent> renderer) where TComponent : struct, IComponent, IDrawableComponent
189 {
190 RegisterComponentType<TComponent>();
191 renderer.AssignEntityManager(_entityManager);
192 renderer.AssignComponentManager(_componentManager);
193 _renderManager.RegisterOrderedRenderer<TComponent>(renderer.InternalRender);
194 _rendererRegistered = true;
195 return renderer;
196 }
197
198 /// <summary>
199 /// Adds the specified GeneralRenderer to the World at the specified layer.
200 /// Higher layer numbers draw on top of lower layer numbers.
201 /// </summary>
202 /// <param name="renderer">An instance of a GeneralRenderer.</param>
203 /// <param name="layer">The layer at which the GeneralRenderer should render. Higher numbers draw over lower numbers.</param>
204 public TRenderer AddGeneralRenderer<TRenderer>(TRenderer renderer, int layer) where TRenderer : GeneralRenderer
205 {
206 renderer.AssignEntityManager(_entityManager);
207 renderer.AssignComponentManager(_componentManager);
208 _renderManager.RegisterGeneralRendererWithLayer(renderer, layer);
209 _rendererRegistered = true;
210 return renderer;
211 }
212
213 private void BuildEngineGraph()
214 {
215 foreach (var senderEngine in _senders)
216 {
217 foreach (var messageType in senderEngine.SendTypes.Union(senderEngine.WriteImmediateTypes))
218 {
219 if (_typeToReaders.ContainsKey(messageType))
220 {
221 foreach (var readerEngine in _typeToReaders[messageType])
222 {
223 if (senderEngine != readerEngine)
224 {
225 if (!_engineGraph.Exists(senderEngine, readerEngine))
226 {
227 _engineGraph.AddEdge(senderEngine, readerEngine);
228 }
229 }
230 }
231 }
232 }
233 }
234 }
235
236 /// <summary>
237 /// Builds the World out of the state specified on the WorldBuilder.
238 /// Validates and constructs an ordering of the given Engines.
239 /// </summary>
240 /// <returns>An instance of World.</returns>
241 public World Build()
242 {
243 BuildEngineGraph();
244
245 if (_engineGraph.Cyclic())
246 {
247 var cycles = _engineGraph.SimpleCycles();
248 var errorString = "Cycle(s) found in Engines: ";
249 foreach (var cycle in cycles)
250 {
251 var reversed = cycle.Reverse();
252 errorString += "\n" +
253 string.Join(" -> ", reversed.Select((engine) => engine.GetType().Name)) +
254 " -> " +
255 reversed.First().GetType().Name;
256 }
257 throw new EngineCycleException(errorString);
258 }
259
260 var writtenComponentTypesWithoutPriority = new HashSet<Type>();
261 var writtenComponentTypesWithPriority = new HashSet<Type>();
262 var duplicateWritesWithoutPriority = new List<Type>();
263 var duplicateWritesWithSamePriority = new List<Type>();
264 var writePriorities = new Dictionary<Type, HashSet<int>>();
265 var writeMessageToEngines = new Dictionary<Type, List<Engine>>();
266
267 foreach (var engine in _engines)
268 {
269 if (engine.GetType().GetCustomAttribute<IgnoresTimeDilation>() != null)
270 {
271 engine._usesTimeDilation = false;
272 }
273
274 var defaultWritePriorityAttribute = engine.GetType().GetCustomAttribute<DefaultWritePriority>(false);
275
276 foreach (var writeType in engine.WriteTypes)
277 {
278 int? priority = null;
279 if (engine.WritePriorities.ContainsKey(writeType))
280 {
281 priority = engine.WritePriorities[writeType];
282 }
283 else if (defaultWritePriorityAttribute != null)
284 {
285 priority = defaultWritePriorityAttribute.WritePriority;
286 }
287
288 if (priority.HasValue)
289 {
290 writtenComponentTypesWithPriority.Add(writeType);
291
292 if (!writePriorities.ContainsKey(writeType))
293 {
294 writePriorities[writeType] = new HashSet<int>();
295 }
296
297 if (writePriorities[writeType].Contains(priority.Value))
298 {
299 duplicateWritesWithSamePriority.Add(writeType);
300 }
301 else if (writtenComponentTypesWithoutPriority.Contains(writeType))
302 {
303 duplicateWritesWithoutPriority.Add(writeType);
304 }
305 else
306 {
307 writePriorities[writeType].Add(priority.Value);
308 }
309 }
310 else
311 {
312 if (writtenComponentTypesWithoutPriority.Contains(writeType) || writtenComponentTypesWithPriority.Contains(writeType))
313 {
314 duplicateWritesWithoutPriority.Add(writeType);
315 }
316 else
317 {
318 writtenComponentTypesWithoutPriority.Add(writeType);
319 }
320 }
321
322 if (!writeMessageToEngines.ContainsKey(writeType))
323 {
324 writeMessageToEngines[writeType] = new List<Engine>();
325 }
326
327 writeMessageToEngines[writeType].Add(engine);
328 }
329 }
330
331 if (duplicateWritesWithoutPriority.Count > 0)
332 {
333 var errorString = "Multiple Engines write the same Component without declaring priority: ";
334 foreach (var componentType in duplicateWritesWithoutPriority)
335 {
336 errorString += "\n" +
337 componentType.Name + " written by: " +
338 string.Join(", ", writeMessageToEngines[componentType].Select((engine) => engine.GetType().Name));
339 }
340 errorString += "\nTo resolve the conflict, add priority arguments to the Writes declarations or use a DefaultWritePriority attribute.";
341
342 throw new EngineWriteConflictException(errorString);
343 }
344
345 if (duplicateWritesWithSamePriority.Count > 0)
346 {
347 var errorString = "Multiple Engines write the same Component with the same priority: ";
348 foreach (var componentType in duplicateWritesWithSamePriority)
349 {
350 errorString += "\n" +
351 componentType.Name + " written by: " +
352 string.Join(", ", writeMessageToEngines[componentType].Select(engine => engine.GetType().Name));
353 }
354 errorString += "\nTo resolve the conflict, add priority arguments to the Writes declarations or use a DefaultWritePriority attribute.";
355
356 throw new EngineWriteConflictException(errorString);
357 }
358
359 PreloadJIT(_messageTypes);
360
361 var engineOrder = new List<Engine>();
362
363 foreach (var engine in _engineGraph.TopologicalSort())
364 {
365 engineOrder.Add(engine);
366 engine.BuildEntityQuery();
367 }
368
369 var world = new World(
370 engineOrder,
371 _entityManager,
372 _componentManager,
373 _trackingManager,
374 _messageManager,
375 _timeManager,
376 _renderManager
377 );
378
379 _componentManager.SetExistingComponentStore(_startingExistingComponentStore);
380 _componentManager.SetUpToDateComponentStore(_startingUpToDateComponentStore);
381
382 _trackingManager.InitializeTracking(_entityManager.EntityIDs);
383
384 return world;
385 }
386
387 /// <summary>
388 /// This is necessary because Encompass heavily uses generic methods with value types,
389 /// so the first time any code path runs the JIT gets smashed. This method warms up the runtime.
390 /// It does so by grabbing all component and message types known to the WorldBuilder and
391 /// executing every possible generic method that could be executed with those types.
392 /// </summary>
393 private void PreloadJIT(IEnumerable<Type> messageTypes)
394 {
395 var dummyTimeManager = new TimeManager();
396 var dummyMessageManager = new MessageManager(dummyTimeManager);
397 var dummyDrawLayerManager = new DrawLayerManager();
398 var dummyTrackingManager = new TrackingManager();
399 var dummyComponentManager = new ComponentManager(dummyDrawLayerManager, _typeToIndex);
400 var dummyEntityManager = new EntityManager(dummyComponentManager, _entityCapacity);
401 var dummyRenderManager = new RenderManager(dummyEntityManager, dummyDrawLayerManager);
402
403 // doing reflection to grab all component types, because not all writes need to be declared
404 foreach (var assembly in AppDomain.CurrentDomain.GetAssemblies())
405 {
406 foreach (var componentType in assembly.GetTypes())
407 {
408 if (typeof(IComponent).IsAssignableFrom(componentType) && componentType.IsValueType && !componentType.IsEnum && !componentType.IsPrimitive)
409 {
410 var method = typeof(WorldBuilder).GetMethod("RegisterComponentType", BindingFlags.NonPublic | BindingFlags.Instance);
411 var generic = method.MakeGenericMethod(componentType);
412 generic.Invoke(this, null);
413
414 var dummyRegisterMethod = typeof(ComponentManager).GetMethod("RegisterComponentType", BindingFlags.Public | BindingFlags.Instance);
415 var dummyGeneric = dummyRegisterMethod.MakeGenericMethod(componentType);
416 dummyGeneric.Invoke(dummyComponentManager, null);
417 }
418
419 if (componentType.GetInterface("IDrawableComponent") != null)
420 {
421 // register draw layer using property value
422 var instance = Activator.CreateInstance(componentType);
423 var layerPropertyInfo = componentType.GetProperty("Layer");
424 dummyDrawLayerManager.RegisterDrawLayer((int)layerPropertyInfo.GetValue(instance));
425
426 var drawLayerManagerRegisterMethod = typeof(DrawLayerManager).GetMethod("RegisterOrderedDrawable");
427 var drawLayerManagerRegisterGenericMethod = drawLayerManagerRegisterMethod.MakeGenericMethod(componentType);
428 drawLayerManagerRegisterGenericMethod.Invoke(dummyDrawLayerManager, null);
429 }
430 }
431 }
432
433 var prepEngineOrder = new List<Engine>();
434
435 var uberEngine = new UberEngine(_componentTypesToPreload, messageTypes);
436
437 uberEngine.AssignEntityManager(dummyEntityManager);
438 uberEngine.AssignComponentManager(dummyComponentManager);
439 uberEngine.AssignMessageManager(dummyMessageManager);
440 uberEngine.AssignTimeManager(dummyTimeManager);
441 uberEngine.AssignTrackingManager(dummyTrackingManager);
442
443 var uberRenderer = new UberRenderer(_componentTypesToPreload);
444 uberRenderer.AssignComponentManager(dummyComponentManager);
445 uberRenderer.AssignEntityManager(dummyEntityManager);
446
447 prepEngineOrder.Add(uberEngine);
448
449 var dummyWorld = new World(
450 prepEngineOrder,
451 dummyEntityManager,
452 dummyComponentManager,
453 dummyTrackingManager,
454 dummyMessageManager,
455 dummyTimeManager,
456 dummyRenderManager
457 );
458
459 uberEngine.Write();
460 dummyComponentManager.WriteComponents();
461
462 dummyWorld.Update(1);
463
464 uberEngine.Write();
465 dummyComponentManager.WriteComponents();
466
467 uberRenderer.SetEntity(uberEngine.Entity);
468 uberRenderer.Render();
469 }
470 }
471 }
@@ -0,0 +1,33
1 <Project Sdk="Microsoft.NET.Sdk">
2 <PropertyGroup>
3 <TargetFramework>netstandard2.0</TargetFramework>
4 <LangVersion>8.0</LangVersion>
5 <AllowUnsafeBlocks>true</AllowUnsafeBlocks>
6 <RootNamespace>Encompass</RootNamespace>
7 <PackageId>EncompassECS.Framework</PackageId>
8 <Version>0.22.0</Version>
9 <Authors>Evan Hemsley</Authors>
10 <PackageRequireLicenseAcceptance>true</PackageRequireLicenseAcceptance>
11 <Company>Moonside Games</Company>
12 <Product>Encompass ECS</Product>
13 <PackageProjectUrl>https://github.com/encompass-ecs</PackageProjectUrl>
14 <PackageLicenseUrl />
15 <Copyright>Evan Hemsley 2020</Copyright>
16 <Description>Encompass is an engine-agnostic MECS framework to help you code games, or other kinds of simulations.</Description>
17 <GeneratePackageOnBuild>true</GeneratePackageOnBuild>
18 <AssemblyName>EncompassECS.Framework</AssemblyName>
19 <PackageLicenseFile>LICENSE</PackageLicenseFile>
20 </PropertyGroup>
21 <ItemGroup>
22 <None Include="..\LICENSE">
23 <Pack>True</Pack>
24 <PackagePath />
25 </None>
26 </ItemGroup>
27 <ItemGroup>
28 <PackageReference Include="MoonTools.Core.Graph" Version="1.0.0" />
29 <PackageReference Include="MoonTools.FastCollections" Version="1.0.0" />
30 <PackageReference Include="System.Collections.Immutable" Version="1.7.0" />
31 <PackageReference Include="Microsoft.Bcl.HashCode" Version="1.1.0" />
32 </ItemGroup>
33 </Project>
@@ -0,0 +1,25
1 //------------------------------------------------------------------------------
2 // <auto-generated>
3 // This code was generated by a tool.
4 //
5 // Changes to this file may cause incorrect behavior and will be lost if
6 // the code is regenerated.
7 // </auto-generated>
8 //------------------------------------------------------------------------------
9
10 using System;
11 using System.Reflection;
12
13 [assembly: System.Reflection.AssemblyCompanyAttribute("Moonside Games")]
14 [assembly: System.Reflection.AssemblyConfigurationAttribute("Debug")]
15 [assembly: System.Reflection.AssemblyCopyrightAttribute("Evan Hemsley 2020")]
16 [assembly: System.Reflection.AssemblyDescriptionAttribute("Encompass is an engine-agnostic MECS framework to help you code games, or other k" +
17 "inds of simulations.")]
18 [assembly: System.Reflection.AssemblyFileVersionAttribute("0.22.0.0")]
19 [assembly: System.Reflection.AssemblyInformationalVersionAttribute("0.22.0")]
20 [assembly: System.Reflection.AssemblyProductAttribute("Encompass ECS")]
21 [assembly: System.Reflection.AssemblyTitleAttribute("EncompassECS.Framework")]
22 [assembly: System.Reflection.AssemblyVersionAttribute("0.22.0.0")]
23
24 // Von der MSBuild WriteCodeFragment-Klasse generiert.
25
@@ -0,0 +1,225
1 # The following command works for downloading when using Git for Windows:
2 # curl -LOf http://gist.githubusercontent.com/kmorcinek/2710267/raw/.gitignore
3 #
4 # Download this file using PowerShell v3 under Windows with the following comand:
5 # Invoke-WebRequest https://gist.githubusercontent.com/kmorcinek/2710267/raw/ -OutFile .gitignore
6 #
7 # or wget:
8 # wget --no-check-certificate http://gist.githubusercontent.com/kmorcinek/2710267/raw/.gitignore
9
10 # User-specific files
11 *.suo
12 *.user
13 *.sln.docstates
14
15 # Build results
16 [Dd]ebug/
17 [Rr]elease/
18 x64/
19 [Bb]in/
20 [Oo]bj/
21 # build folder is nowadays used for build scripts and should not be ignored
22 #build/
23
24 # NuGet Packages
25 *.nupkg
26 # The packages folder can be ignored because of Package Restore
27 **/packages/*
28 # except build/, which is used as an MSBuild target.
29 !**/packages/build/
30 # Uncomment if necessary however generally it will be regenerated when needed
31 #!**/packages/repositories.config
32
33 # MSTest test Results
34 [Tt]est[Rr]esult*/
35 [Bb]uild[Ll]og.*
36
37 *_i.c
38 *_p.c
39 *.ilk
40 *.meta
41 *.obj
42 *.pch
43 *.pdb
44 *.pgc
45 *.pgd
46 *.rsp
47 *.sbr
48 *.tlb
49 *.tli
50 *.tlh
51 *.tmp
52 *.tmp_proj
53 *.log
54 *.vspscc
55 *.vssscc
56 .builds
57 *.pidb
58 *.log
59 *.scc
60
61 # OS generated files #
62 .DS_Store*
63 Icon?
64
65 # Visual C++ cache files
66 ipch/
67 *.aps
68 *.ncb
69 *.opensdf
70 *.sdf
71 *.cachefile
72
73 # Visual Studio profiler
74 *.psess
75 *.vsp
76 *.vspx
77
78 # Guidance Automation Toolkit
79 *.gpState
80
81 # ReSharper is a .NET coding add-in
82 _ReSharper*/
83 *.[Rr]e[Ss]harper
84
85 # TeamCity is a build add-in
86 _TeamCity*
87
88 # DotCover is a Code Coverage Tool
89 *.dotCover
90
91 # NCrunch
92 *.ncrunch*
93 .*crunch*.local.xml
94
95 # Installshield output folder
96 [Ee]xpress/
97
98 # DocProject is a documentation generator add-in
99 DocProject/buildhelp/
100 DocProject/Help/*.HxT
101 DocProject/Help/*.HxC
102 DocProject/Help/*.hhc
103 DocProject/Help/*.hhk
104 DocProject/Help/*.hhp
105 DocProject/Help/Html2
106 DocProject/Help/html
107
108 # Click-Once directory
109 publish/
110
111 # Publish Web Output
112 *.Publish.xml
113
114 # Windows Azure Build Output
115 csx
116 *.build.csdef
117
118 # Windows Store app package directory
119 AppPackages/
120
121 # Others
122 *.Cache
123 ClientBin/
124 [Ss]tyle[Cc]op.*
125 ~$*
126 *~
127 *.dbmdl
128 *.[Pp]ublish.xml
129 *.pfx
130 *.publishsettings
131 modulesbin/
132 tempbin/
133
134 # EPiServer Site file (VPP)
135 AppData/
136
137 # RIA/Silverlight projects
138 Generated_Code/
139
140 # Backup & report files from converting an old project file to a newer
141 # Visual Studio version. Backup files are not needed, because we have git ;-)
142 _UpgradeReport_Files/
143 Backup*/
144 UpgradeLog*.XML
145 UpgradeLog*.htm
146
147 # vim
148 *.txt~
149 *.swp
150 *.swo
151
152 # Temp files when opening LibreOffice on ubuntu
153 .~lock.*
154
155 # svn
156 .svn
157
158 # CVS - Source Control
159 **/CVS/
160
161 # Remainings from resolving conflicts in Source Control
162 *.orig
163
164 # SQL Server files
165 **/App_Data/*.mdf
166 **/App_Data/*.ldf
167 **/App_Data/*.sdf
168
169
170 #LightSwitch generated files
171 GeneratedArtifacts/
172 _Pvt_Extensions/
173 ModelManifest.xml
174
175 # =========================
176 # Windows detritus
177 # =========================
178
179 # Windows image file caches
180 Thumbs.db
181 ehthumbs.db
182
183 # Folder config file
184 Desktop.ini
185
186 # Recycle Bin used on file shares
187 $RECYCLE.BIN/
188
189 # Mac desktop service store files
190 .DS_Store
191
192 # SASS Compiler cache
193 .sass-cache
194
195 # Visual Studio 2014 CTP
196 **/*.sln.ide
197
198 # Visual Studio temp something
199 .vs/
200
201 # dotnet stuff
202 project.lock.json
203
204 # VS 2015+
205 *.vc.vc.opendb
206 *.vc.db
207
208 # Rider
209 .idea/
210
211 # Visual Studio Code
212 .vscode/
213
214 # Output folder used by Webpack or other FE stuff
215 **/node_modules/*
216 **/wwwroot/*
217
218 # SpecFlow specific
219 *.feature.cs
220 *.feature.xlsx.*
221 *.Specs_*.html
222
223 #####
224 # End of core ignore list, below put you custom 'per project' settings (patterns or path)
225 #####
@@ -0,0 +1,485
1 using NUnit.Framework;
2 using FluentAssertions;
3
4 using Encompass;
5 using System.Runtime.CompilerServices;
6
7 namespace Tests
8 {
9 public class ComponentTests
10 {
11 struct MockComponent : IComponent
12 {
13 public int myInt;
14 }
15
16 struct EntityMessage : IMessage
17 {
18 public Entity entity;
19 }
20
21 static MockComponent gottenMockComponent;
22
23 [Receives(typeof(EntityMessage))]
24 [Reads(typeof(MockComponent))]
25 class GetMockComponentEngine : Engine
26 {
27 public override void Update(double dt)
28 {
29 foreach (ref readonly var entityMessage in ReadMessages<EntityMessage>())
30 {
31 gottenMockComponent = GetComponent<MockComponent>(entityMessage.entity);
32 }
33 }
34 }
35
36 struct AddComponentTestMessage : IMessage
37 {
38 public Entity entity;
39 public MockComponent mockComponent;
40 }
41
42 [Receives(typeof(AddComponentTestMessage))]
43 [Reads(typeof(MockComponent))]
44 class AddComponentTestEngine : Engine
45 {
46 public override void Update(double dt)
47 {
48 foreach (ref readonly var addComponentTestMessage in ReadMessages<AddComponentTestMessage>())
49 {
50 Assert.IsTrue(HasComponent<MockComponent>(addComponentTestMessage.entity));
51 ref readonly var gottenComponent = ref GetComponent<MockComponent>(addComponentTestMessage.entity);
52 gottenComponent.Should().BeEquivalentTo(addComponentTestMessage.mockComponent);
53 }
54 }
55 }
56
57 [Test]
58 public unsafe void AddComponent()
59 {
60 var worldBuilder = new WorldBuilder();
61 worldBuilder.AddEngine(new AddComponentTestEngine());
62
63 var entity = worldBuilder.CreateEntity();
64
65 const string MyString = "hello";
66
67 MockComponent mockComponent;
68 mockComponent.myInt = 3;
69
70 worldBuilder.SetComponent(entity, mockComponent);
71
72 AddComponentTestMessage addComponentTestMessage;
73 addComponentTestMessage.entity = entity;
74 addComponentTestMessage.mockComponent = mockComponent;
75 worldBuilder.SendMessage(addComponentTestMessage);
76
77 var world = worldBuilder.Build();
78
79 world.Update(0.01);
80 world.Update(0.01);
81 }
82
83 [Test]
84 public void SetMultipleComponentOfSameTypeOnEntity()
85 {
86 var worldBuilder = new WorldBuilder();
87 worldBuilder.AddEngine(new ReadMockComponentEngine());
88
89 var entity = worldBuilder.CreateEntity();
90 worldBuilder.SetComponent(entity, new MockComponent { myInt = 20 });
91 worldBuilder.SetComponent(entity, new MockComponent { myInt = 50 });
92 worldBuilder.SetComponent(entity, new MockComponent { myInt = 40 });
93
94 var world = worldBuilder.Build();
95
96 world.Update(0.01);
97
98 Assert.That(gottenMockComponent.myInt, Is.EqualTo(40));
99 }
100
101 [Reads(typeof(MockComponent))]
102 [Writes(typeof(MockComponent))]
103 class OverwriteEngine : Engine
104 {
105 public override void Update(double dt)
106 {
107 foreach (ref readonly var entity in ReadEntities<MockComponent>())
108 {
109 ref readonly var mockComponent = ref GetComponent<MockComponent>(entity);
110 SetComponent(entity, new MockComponent { myInt = mockComponent.myInt + 1 });
111 }
112 }
113 }
114
115 [Reads(typeof(MockComponent))]
116 class ReadMockComponentEngine : Engine
117 {
118 public override void Update(double dt)
119 {
120 gottenMockComponent = ReadComponent<MockComponent>();
121 }
122 }
123
124 [Test]
125 public void EngineOverwriteComponent()
126 {
127 var worldBuilder = new WorldBuilder();
128 worldBuilder.AddEngine(new OverwriteEngine());
129 worldBuilder.AddEngine(new ReadMockComponentEngine());
130
131 var entity = worldBuilder.CreateEntity();
132 worldBuilder.SetComponent(entity, new MockComponent { myInt = 420 });
133
134 var world = worldBuilder.Build();
135 world.Update(0.01);
136
137 Assert.That(gottenMockComponent.myInt, Is.EqualTo(420));
138
139 world.Update(0.01);
140
141 Assert.That(gottenMockComponent.myInt, Is.EqualTo(421));
142
143 world.Update(0.01);
144
145 Assert.That(gottenMockComponent.myInt, Is.EqualTo(422));
146 }
147
148 [Reads(typeof(MockComponent))]
149 [Writes(typeof(MockComponent))]
150 class AddAndRemoveComponentEngine : Engine
151 {
152 public override void Update(double dt)
153 {
154 foreach (ref readonly var entity in ReadEntities<MockComponent>())
155 {
156 ref readonly var mockComponent = ref GetComponent<MockComponent>(entity);
157 SetComponent(entity, mockComponent);
158 RemoveComponent<MockComponent>(entity);
159 }
160 }
161 }
162
163 [Test]
164 public void AddMultipleComponentSameFrameAsRemove()
165 {
166 var worldBuilder = new WorldBuilder();
167 worldBuilder.AddEngine(new AddAndRemoveComponentEngine());
168
169 var entity = worldBuilder.CreateEntity();
170 worldBuilder.SetComponent(entity, new MockComponent());
171
172 var world = worldBuilder.Build();
173
174 Assert.DoesNotThrow(() => world.Update(0.01));
175 }
176
177 struct AddMockComponentMessage : IMessage
178 {
179 public Entity entity;
180 public MockComponent mockComponent;
181 }
182
183 [Sends(typeof(AddMockComponentMessage))]
184 class EmitMockComponentMessageEngine : Engine
185 {
186 private Entity entity;
187
188 public EmitMockComponentMessageEngine(Entity entity)
189 {
190 this.entity = entity;
191 }
192
193 public override void Update(double dt)
194 {
195 MockComponent mockComponent;
196 mockComponent.myInt = 10;
197
198 AddMockComponentMessage addMockComponentMessage;
199 addMockComponentMessage.entity = entity;
200 addMockComponentMessage.mockComponent = mockComponent;
201 SendMessage(addMockComponentMessage);
202 }
203 }
204
205 [WritesImmediate(typeof(MockComponent))]
206 [Receives(typeof(AddMockComponentMessage))]
207 [Writes(typeof(MockComponent))]
208 class AddMockComponentEngine : Engine
209 {
210 public override void Update(double dt)
211 {
212 foreach (ref readonly var message in ReadMessages<AddMockComponentMessage>())
213 {
214 SetComponent(message.entity, message.mockComponent);
215 }
216 }
217 }
218
219 [ReadsImmediate(typeof(MockComponent))]
220 class HasMockComponentEngine : Engine
221 {
222 private Entity entity;
223
224 public HasMockComponentEngine(Entity entity)
225 {
226 this.entity = entity;
227 }
228
229 public override void Update(double dt)
230 {
231 Assert.IsTrue(HasComponent<MockComponent>(entity));
232 }
233 }
234
235 [Test]
236 public void AddComponentAndReadSameFrame()
237 {
238 var worldBuilder = new WorldBuilder();
239 var entity = worldBuilder.CreateEntity();
240
241 worldBuilder.AddEngine(new EmitMockComponentMessageEngine(entity));
242 worldBuilder.AddEngine(new AddMockComponentEngine());
243 worldBuilder.AddEngine(new HasMockComponentEngine(entity));
244
245 var world = worldBuilder.Build();
246
247 world.Update(0.01);
248 }
249
250 [Test]
251 public void GetComponent()
252 {
253 var worldBuilder = new WorldBuilder();
254 worldBuilder.AddEngine(new GetMockComponentEngine());
255
256 var entity = worldBuilder.CreateEntity();
257
258 MockComponent mockComponent;
259 mockComponent.myInt = 3;
260
261 worldBuilder.SetComponent(entity, mockComponent);
262
263 EntityMessage entityMessage;
264 entityMessage.entity = entity;
265 worldBuilder.SendMessage(entityMessage);
266
267 var world = worldBuilder.Build();
268
269 world.Update(0.01);
270 world.Update(0.01);
271
272 Assert.AreEqual(mockComponent, gottenMockComponent);
273 }
274 struct HasComponentTestMessage : IMessage
275 {
276 public Entity entity;
277 }
278
279 [Receives(typeof(HasComponentTestMessage))]
280 [Reads(typeof(MockComponent))]
281 class HasComponentTestEngine : Engine
282 {
283 public override void Update(double dt)
284 {
285 foreach (ref readonly var hasComponentTestEngine in ReadMessages<HasComponentTestMessage>())
286 {
287 Assert.IsTrue(HasComponent<MockComponent>(hasComponentTestEngine.entity));
288 }
289 }
290 }
291
292 [Test]
293 public void HasComponent()
294 {
295 var worldBuilder = new WorldBuilder();
296 worldBuilder.AddEngine(new HasComponentTestEngine());
297
298 var entity = worldBuilder.CreateEntity();
299
300 MockComponent mockComponent;
301 mockComponent.myInt = 3;
302
303 worldBuilder.SetComponent(entity, mockComponent);
304
305 HasComponentTestMessage hasComponentTestMessage;
306 hasComponentTestMessage.entity = entity;
307 worldBuilder.SendMessage(hasComponentTestMessage);
308
309 var world = worldBuilder.Build();
310
311 world.Update(0.01);
312 }
313
314 static bool hasComponentRuntimeTypeResult;
315
316 [Receives(typeof(HasComponentTestMessage))]
317 [Reads(typeof(MockComponent))]
318 class HasComponentWithRuntimeTypeEngine : Engine
319 {
320 public override void Update(double dt)
321 {
322 foreach (ref readonly var hasComponentTestEngine in ReadMessages<HasComponentTestMessage>())
323 {
324 hasComponentRuntimeTypeResult = HasComponent(hasComponentTestEngine.entity, typeof(MockComponent));
325 }
326 }
327 }
328
329 [Test]
330 public void HasComponentWithRuntimeType()
331 {
332 var worldBuilder = new WorldBuilder();
333 worldBuilder.AddEngine(new HasComponentWithRuntimeTypeEngine());
334
335 var entity = worldBuilder.CreateEntity();
336
337 MockComponent mockComponent;
338 mockComponent.myInt = 3;
339
340 worldBuilder.SetComponent(entity, mockComponent);
341
342 HasComponentTestMessage hasComponentTestMessage;
343 hasComponentTestMessage.entity = entity;
344 worldBuilder.SendMessage(hasComponentTestMessage);
345
346 var world = worldBuilder.Build();
347
348 world.Update(0.01);
349
350 Assert.IsTrue(hasComponentRuntimeTypeResult);
351 }
352
353 [Test]
354 public void HasComponentWithRuntimeTypeFalseWhenNoneHaveBeenCreated()
355 {
356 var worldBuilder = new WorldBuilder();
357 worldBuilder.AddEngine(new HasComponentWithRuntimeTypeEngine());
358
359 var entity = worldBuilder.CreateEntity();
360
361 HasComponentTestMessage hasComponentTestMessage;
362 hasComponentTestMessage.entity = entity;
363 worldBuilder.SendMessage(hasComponentTestMessage);
364
365 var world = worldBuilder.Build();
366
367 world.Update(0.01);
368
369 Assert.IsFalse(hasComponentRuntimeTypeResult);
370 }
371
372 struct RemoveComponentTestMessage : IMessage
373 {
374 public Entity entity;
375 }
376
377 [Reads(typeof(MockComponent))]
378 [Receives(typeof(RemoveComponentTestMessage))]
379 [Writes(typeof(MockComponent))]
380 class RemoveComponentTestEngine : Engine
381 {
382 public override void Update(double dt)
383 {
384 foreach (ref readonly var removeComponentMessage in ReadMessages<RemoveComponentTestMessage>())
385 {
386 RemoveComponent<MockComponent>(removeComponentMessage.entity);
387 }
388 }
389 }
390
391 [Receives(typeof(RemoveComponentTestMessage))]
392 [Sends(typeof(CheckHasMockComponentMessage))]
393 class DoRemoveCheckEngine : Engine
394 {
395 private Entity entity;
396
397 public DoRemoveCheckEngine(Entity entity)
398 {
399 this.entity = entity;
400 }
401
402 public override void Update(double dt)
403 {
404 if (SomeMessage<RemoveComponentTestMessage>())
405 {
406 CheckHasMockComponentMessage checkHasMockComponentMessage;
407 checkHasMockComponentMessage.entity = entity;
408 checkHasMockComponentMessage.shouldHaveComponent = true;
409 SendMessage(checkHasMockComponentMessage);
410 }
411 else
412 {
413 CheckHasMockComponentMessage checkHasMockComponentMessage;
414 checkHasMockComponentMessage.entity = entity;
415 checkHasMockComponentMessage.shouldHaveComponent = false;
416 SendMessage(checkHasMockComponentMessage);
417 }
418 }
419 }
420
421 static bool hasComponentResult;
422
423 [Receives(typeof(CheckHasMockComponentMessage))]
424 [Reads(typeof(MockComponent))]
425 class CheckHasMockComponentEngine : Engine
426 {
427 public override void Update(double dt)
428 {
429 foreach (ref readonly var checkHasMockComponentMessage in ReadMessages<CheckHasMockComponentMessage>())
430 {
431 hasComponentResult = HasComponent<MockComponent>(checkHasMockComponentMessage.entity);
432 }
433 }
434 }
435
436 [Test]
437 public void RemovedComponentShouldStillExistOnSameFrame()
438 {
439 var worldBuilder = new WorldBuilder();
440 var entity = worldBuilder.CreateEntity();
441
442 worldBuilder.AddEngine(new RemoveComponentTestEngine());
443 worldBuilder.AddEngine(new CheckHasMockComponentEngine());
444 worldBuilder.AddEngine(new DoRemoveCheckEngine(entity));
445
446 MockComponent mockComponent;
447 mockComponent.myInt = 3;
448
449 worldBuilder.SetComponent(entity, mockComponent);
450
451 RemoveComponentTestMessage removeComponentMessage;
452 removeComponentMessage.entity = entity;
453 worldBuilder.SendMessage(removeComponentMessage);
454
455 var world = worldBuilder.Build();
456
457 world.Update(0.01f);
458
459 hasComponentResult.Should().BeTrue();
460
461 world.Update(0.01f);
462
463 hasComponentResult.Should().BeFalse();
464 }
465
466 struct CheckHasMockComponentMessage : IMessage
467 {
468 public Entity entity;
469 public bool shouldHaveComponent;
470 }
471
472 [Receives(typeof(CheckHasMockComponentMessage))]
473 [ReadsImmediate(typeof(MockComponent))]
474 class CheckHasImmediateMockComponentEngine : Engine
475 {
476 public override void Update(double dt)
477 {
478 foreach (ref readonly var checkHasMockComponentMessage in ReadMessages<CheckHasMockComponentMessage>())
479 {
480 Assert.IsTrue(HasComponent<MockComponent>(checkHasMockComponentMessage.entity));
481 }
482 }
483 }
484 }
485 }
This diff has been collapsed as it changes many lines, (1976 lines changed) Show them Hide them
@@ -0,0 +1,1976
1 using NUnit.Framework;
2 using FluentAssertions;
3
4 using Encompass;
5
6 using System;
7 using System.Linq;
8 using System.Collections.Generic;
9 using Encompass.Exceptions;
10
11 namespace Tests
12 {
13 struct MockComponent : IComponent
14 {
15 public int myInt;
16 }
17
18 public class EngineTest
19 {
20 static MockComponent resultComponent;
21 static MockComponent[] resultComponents = new MockComponent[1];
22
23 static MockMessage resultMessage;
24 static MockMessage[] resultMessages = new MockMessage[1];
25
26 [Reads(typeof(MockComponent))]
27 public class ReadComponentsTestEngine : Engine
28 {
29 public override void Update(double dt)
30 {
31 resultComponents = ReadComponents<MockComponent>().ToArray();
32 }
33 }
34
35 static List<(MockComponent, Entity)> resultComponentsIncludingEntity = new List<(MockComponent, Entity)>();
36 static (MockComponent, Entity) resultComponentIncludingEntity;
37
38 [Reads(typeof(MockComponent))]
39 public class ReadComponentsIncludingEntityEngine : Engine
40 {
41 public override void Update(double dt)
42 {
43 foreach (ref readonly var entity in ReadEntities<MockComponent>())
44 {
45 ref readonly var mockComponent = ref GetComponent<MockComponent>(entity);
46 resultComponentsIncludingEntity.Add((mockComponent, entity));
47 }
48 }
49 }
50
51 [Reads(typeof(MockComponent))]
52 public class ReadComponentTestEngine : Engine
53 {
54 public override void Update(double dt)
55 {
56 resultComponent = ReadComponent<MockComponent>();
57 }
58 }
59
60 [Reads(typeof(MockComponent))]
61 public class ReadComponentIncludingEntityEngine : Engine
62 {
63 public override void Update(double dt)
64 {
65 ref readonly var entity = ref ReadEntity<MockComponent>();
66 ref readonly var mockComponent = ref GetComponent<MockComponent>(entity);
67 resultComponentIncludingEntity = (mockComponent, entity);
68 }
69 }
70
71 [Test]
72 public void ReadComponents()
73 {
74 var worldBuilder = new WorldBuilder();
75 worldBuilder.AddEngine(new ReadComponentsTestEngine());
76
77 var entity = worldBuilder.CreateEntity();
78 var entityB = worldBuilder.CreateEntity();
79
80 MockComponent mockComponent;
81 mockComponent.myInt = 2;
82
83 MockComponent mockComponentB;
84 mockComponentB.myInt = 1;
85
86 worldBuilder.SetComponent(entity, mockComponent);
87 worldBuilder.SetComponent(entityB, mockComponentB);
88
89 var world = worldBuilder.Build();
90
91 world.Update(0.01f);
92
93 resultComponents.Should().Contain(mockComponent);
94 resultComponents.Should().Contain(mockComponentB);
95 }
96
97 [Test]
98 public void ReadComponentsIncludingEntity()
99 {
100 resultComponentsIncludingEntity.Clear();
101
102 var worldBuilder = new WorldBuilder();
103 worldBuilder.AddEngine(new ReadComponentsIncludingEntityEngine());
104
105 var entity = worldBuilder.CreateEntity();
106 var entityB = worldBuilder.CreateEntity();
107
108 MockComponent mockComponent;
109 mockComponent.myInt = 2;
110
111 MockComponent mockComponentB;
112 mockComponentB.myInt = 1;
113
114 worldBuilder.SetComponent(entity, mockComponent);
115 worldBuilder.SetComponent(entityB, mockComponentB);
116
117 var world = worldBuilder.Build();
118
119 world.Update(0.01f);
120
121 var resultComponentValues = resultComponentsIncludingEntity.Select((kv) => kv.Item1);
122 resultComponentValues.Should().Contain(mockComponent);
123 resultComponentValues.Should().Contain(mockComponentB);
124
125 var resultEntities = resultComponentsIncludingEntity.Select((kv) => kv.Item2);
126 resultEntities.Should().Contain(entity);
127 resultEntities.Should().Contain(entityB);
128
129 resultComponentsIncludingEntity.Should().Contain((mockComponent, entity));
130 resultComponentsIncludingEntity.Should().Contain((mockComponentB, entityB));
131 }
132
133 [Test]
134 public void ReadComponent()
135 {
136 var worldBuilder = new WorldBuilder();
137 worldBuilder.AddEngine(new ReadComponentTestEngine());
138
139 var entity = worldBuilder.CreateEntity();
140
141 MockComponent mockComponent;
142 mockComponent.myInt = 3;
143
144 worldBuilder.SetComponent(entity, mockComponent);
145
146 var world = worldBuilder.Build();
147
148 world.Update(0.01f);
149
150 Assert.AreEqual(mockComponent, resultComponent);
151 }
152
153 [Test]
154 public void ReadComponentThrowsWhenNoneExist()
155 {
156 var worldBuilder = new WorldBuilder();
157 worldBuilder.AddEngine(new ReadComponentTestEngine());
158
159 var world = worldBuilder.Build();
160
161 Assert.Throws<NoComponentOfTypeException>(() => world.Update(0.01f), "No component of type MockComponent exists");
162 }
163
164 [Test]
165 public void ReadComponentWhenMultipleComponents()
166 {
167 var worldBuilder = new WorldBuilder();
168 worldBuilder.AddEngine(new ReadComponentTestEngine());
169
170 var entity = worldBuilder.CreateEntity();
171 var entityB = worldBuilder.CreateEntity();
172
173 MockComponent mockComponent;
174 mockComponent.myInt = 2;
175
176 MockComponent mockComponentB;
177 mockComponentB.myInt = 1;
178
179 worldBuilder.SetComponent(entity, mockComponent);
180 worldBuilder.SetComponent(entityB, mockComponentB);
181
182 var world = worldBuilder.Build();
183
184 world.Update(0.01);
185
186 Assert.That(resultComponent, Is.EqualTo(mockComponent).Or.EqualTo(mockComponentB));
187 }
188
189 [Test]
190 public void ReadComponentWithEntity()
191 {
192 var worldBuilder = new WorldBuilder();
193 worldBuilder.AddEngine(new ReadComponentIncludingEntityEngine());
194
195 var entity = worldBuilder.CreateEntity();
196
197 MockComponent mockComponent;
198 mockComponent.myInt = 2;
199
200 worldBuilder.SetComponent(entity, mockComponent);
201
202 var world = worldBuilder.Build();
203
204 world.Update(0.01f);
205
206 (mockComponent, entity).Should().BeEquivalentTo(resultComponentIncludingEntity);
207 }
208
209 [Reads(typeof(MockComponent))]
210 [Writes(typeof(MockComponent))]
211 public class UpdateComponentTestEngine : Engine
212 {
213 public override void Update(double dt)
214 {
215 ref readonly var entity = ref ReadEntity<MockComponent>();
216 SetComponent(entity, new MockComponent { myInt = 420 });
217 }
218 }
219
220 // this test needs to be improved...
221
222 [Test]
223 public void UpdateComponent()
224 {
225 var worldBuilder = new WorldBuilder();
226
227 worldBuilder.AddEngine(new UpdateComponentTestEngine());
228 worldBuilder.AddEngine(new ReadComponentTestEngine());
229
230 var entity = worldBuilder.CreateEntity();
231
232 MockComponent mockComponent;
233 mockComponent.myInt = 3;
234
235 worldBuilder.SetComponent(entity, mockComponent);
236
237 var world = worldBuilder.Build();
238
239 world.Update(0.01);
240 world.Update(0.01);
241
242 Assert.AreEqual(420, resultComponent.myInt);
243 }
244
245 [Reads(typeof(MockComponent))]
246 public class UndeclaredUpdateComponentTestEngine : Engine
247 {
248 public override void Update(double dt)
249 {
250 ref readonly var entity = ref ReadEntity<MockComponent>();
251 SetComponent(entity, new MockComponent { myInt = 420 });
252 }
253 }
254
255 [Test]
256 public void UpdateUndeclaredComponent()
257 {
258 var worldBuilder = new WorldBuilder();
259 worldBuilder.AddEngine(new UndeclaredUpdateComponentTestEngine());
260
261 var entity = worldBuilder.CreateEntity();
262
263 MockComponent mockComponent;
264 mockComponent.myInt = 3;
265
266 worldBuilder.SetComponent(entity, mockComponent);
267
268 var world = worldBuilder.Build();
269
270 var ex = Assert.Throws<IllegalWriteException>(() => world.Update(0.01f));
271 Assert.That(ex.Message, Is.EqualTo("Engine UndeclaredUpdateComponentTestEngine tried to update undeclared Component MockComponent"));
272 }
273
274 struct MockMessage : IMessage
275 {
276 public string myString;
277 }
278
279 [Sends(typeof(MockMessage))]
280 public class MessageEmitEngine : Engine
281 {
282 public override void Update(double dt)
283 {
284 MockMessage message;
285 message.myString = "howdy";
286
287 this.SendMessage(message);
288 }
289 }
290
291 [Receives(typeof(MockMessage))]
292 public class ReadMessagesEngine : Engine
293 {
294 public override void Update(double dt)
295 {
296 resultMessages = ReadMessages<MockMessage>().ToArray();
297 }
298 }
299
300 [Receives(typeof(MockMessage))]
301 public class ReadMessageEngine : Engine
302 {
303 public override void Update(double dt)
304 {
305 resultMessage = ReadMessage<MockMessage>();
306 }
307 }
308
309 [Test]
310 public void EmitAndReadMessage()
311 {
312 var worldBuilder = new WorldBuilder();
313 worldBuilder.AddEngine(new MessageEmitEngine());
314 worldBuilder.AddEngine(new ReadMessageEngine());
315
316 var world = worldBuilder.Build();
317
318 world.Update(0.01f);
319
320 Assert.AreEqual(resultMessage.myString, "howdy");
321 }
322
323 [Test]
324 public void ReadMessageThrowsWhenNoneOfTypeExist()
325 {
326 var worldBuilder = new WorldBuilder();
327 worldBuilder.AddEngine(new ReadMessageEngine());
328
329 var world = worldBuilder.Build();
330
331 Assert.Throws<NoMessageOfTypeException>(() => world.Update(0.01), "No Message of type MockMessage exists");
332 }
333
334 public class UndeclaredMessageEmitEngine : Engine
335 {
336 public override void Update(double dt)
337 {
338 MockMessage message;
339 message.myString = "howdy";
340
341 this.SendMessage(message);
342 }
343 }
344
345 static MockMessage[] emptyReadMessagesResult;
346
347 [Receives(typeof(MockMessage))]
348 class ReadMessagesWhenNoneExistEngine : Engine
349 {
350 public override void Update(double dt)
351 {
352 emptyReadMessagesResult = ReadMessages<MockMessage>().ToArray();
353 }
354 }
355
356 [Test]
357 public void ReadMessagesWhenNoneHaveBeenEmitted()
358 {
359 var worldBuilder = new WorldBuilder();
360 worldBuilder.AddEngine(new ReadMessagesWhenNoneExistEngine());
361
362 var world = worldBuilder.Build();
363
364 world.Update(0.01f);
365
366 emptyReadMessagesResult.Should().BeEmpty();
367 }
368
369 [Test]
370 public void EmitUndeclaredMessage()
371 {
372 var worldBuilder = new WorldBuilder();
373 worldBuilder.AddEngine(new UndeclaredMessageEmitEngine());
374
375 var world = worldBuilder.Build();
376
377 var ex = Assert.Throws<IllegalSendException>(() => world.Update(0.01f));
378 Assert.That(ex.Message, Is.EqualTo("Engine UndeclaredMessageEmitEngine tried to send undeclared Message MockMessage"));
379 }
380
381 static bool someTest;
382
383 [Sends(typeof(MockMessage))]
384 class EmitMockMessageEngine : Engine
385 {
386 public override void Update(double dt)
387 {
388 MockMessage message;
389 message.myString = "howdy";
390
391 this.SendMessage(message);
392 }
393 }
394
395 [Receives(typeof(MockMessage))]
396 class SomeMessageTestEngine : Engine
397 {
398 public override void Update(double dt)
399 {
400 someTest = this.SomeMessage<MockMessage>();
401 }
402 }
403
404 [Test]
405 public void SomeMessage()
406 {
407 var worldBuilder = new WorldBuilder();
408 worldBuilder.AddEngine(new EmitMockMessageEngine());
409 worldBuilder.AddEngine(new SomeMessageTestEngine());
410
411 var world = worldBuilder.Build();
412
413 world.Update(0.01f);
414
415 Assert.That(someTest, Is.True);
416 }
417
418 class UndeclaredSomeMessageEngine : Engine
419 {
420 public override void Update(double dt)
421 {
422 someTest = this.SomeMessage<MockMessage>();
423 }
424 }
425
426 [Test]
427 public void UndeclaredSomeMessage()
428 {
429 var worldBuilder = new WorldBuilder();
430 worldBuilder.AddEngine(new EmitMockMessageEngine());
431 worldBuilder.AddEngine(new UndeclaredSomeMessageEngine());
432
433 var world = worldBuilder.Build();
434
435 Assert.Throws<IllegalReadException>(() => world.Update(0.01f));
436 }
437
438 struct EntityMessage : IMessage, IHasEntity
439 {
440 public EntityMessage(Entity entity, int myInt)
441 {
442 Entity = entity;
443 MyInt = myInt;
444 }
445
446 public Entity Entity { get; }
447 public int MyInt { get; }
448 }
449
450 [Sends(typeof(EntityMessage), typeof(MockMessage))]
451 class EntityMessageEmitterEngine : Engine
452 {
453 private Entity _entity;
454
455 public EntityMessageEmitterEngine(Entity entity)
456 {
457 _entity = entity;
458 }
459
460 public override void Update(double dt)
461 {
462 SendMessage(new EntityMessage(_entity, 2));
463 SendMessage(new EntityMessage(_entity, 4));
464 SendMessage(new EntityMessage(_entity, 5));
465 SendMessage(new MockMessage());
466 }
467 }
468
469 static List<EntityMessage> entityMessageResults;
470
471 [Receives(typeof(EntityMessage))]
472 class EntityMessageReceiverEngine : Engine
473 {
474 private Entity _entity;
475
476 public EntityMessageReceiverEngine(Entity entity)
477 {
478 _entity = entity;
479 }
480
481 public override void Update(double dt)
482 {
483 entityMessageResults = ReadMessagesWithEntity<EntityMessage>(_entity).ToList();
484 }
485 }
486
487 [Test]
488 public void MessagesWithEntity()
489 {
490 var worldBuilder = new WorldBuilder();
491
492 var entity = worldBuilder.CreateEntity();
493 worldBuilder.AddEngine(new EntityMessageEmitterEngine(entity));
494 worldBuilder.AddEngine(new EntityMessageReceiverEngine(entity));
495
496 var world = worldBuilder.Build();
497
498 world.Update(0.01);
499
500 entityMessageResults.Should().HaveCount(3);
501 entityMessageResults.Should().ContainEquivalentOf(new EntityMessage(entity, 2));
502 entityMessageResults.Should().ContainEquivalentOf(new EntityMessage(entity, 4));
503 entityMessageResults.Should().ContainEquivalentOf(new EntityMessage(entity, 5));
504 }
505
506 [Test]
507 public void NoMessagesWithEntity()
508 {
509 var worldBuilder = new WorldBuilder();
510
511 var entity = worldBuilder.CreateEntity();
512 worldBuilder.AddEngine(new EntityMessageReceiverEngine(entity));
513
514 var world = worldBuilder.Build();
515 world.Update(0.01);
516
517 entityMessageResults.Should().BeEmpty();
518 }
519
520 [Sends(typeof(EntityMessage), typeof(MockMessage))]
521 class EntityMessageSingularEmitterEngine : Engine
522 {
523 private Entity _entity;
524
525 public EntityMessageSingularEmitterEngine(Entity entity)
526 {
527 _entity = entity;
528 }
529
530 public override void Update(double dt)
531 {
532 SendMessage(new EntityMessage(_entity, 2));
533 SendMessage(new MockMessage());
534 }
535 }
536
537 static EntityMessage entityMessageResult;
538
539 [Receives(typeof(EntityMessage))]
540 class SingularMessageWithEntityEngine : Engine
541 {
542 private Entity _entity;
543
544 public SingularMessageWithEntityEngine(Entity entity)
545 {
546 _entity = entity;
547 }
548
549 public override void Update(double dt)
550 {
551 entityMessageResult = ReadMessageWithEntity<EntityMessage>(_entity);
552 }
553 }
554
555 [Test]
556 public void MessageWithEntity()
557 {
558 var worldBuilder = new WorldBuilder();
559
560 var entity = worldBuilder.CreateEntity();
561
562 worldBuilder.AddEngine(new EntityMessageSingularEmitterEngine(entity));
563 worldBuilder.AddEngine(new SingularMessageWithEntityEngine(entity));
564
565 var world = worldBuilder.Build();
566
567 world.Update(0.01);
568
569 entityMessageResult.Should().Be(new EntityMessage(entity, 2));
570 }
571
572 class SomeComponentTestEngine : Engine
573 {
574 public override void Update(double dt)
575 {
576 Assert.IsTrue(SomeComponent<MockComponent>());
577 }
578 }
579
580 [Test]
581 public void SomeComponent()
582 {
583 var worldBuilder = new WorldBuilder();
584
585 var entity = worldBuilder.CreateEntity();
586 worldBuilder.SetComponent(entity, new MockComponent());
587
588 var world = worldBuilder.Build();
589
590 world.Update(0.01);
591 }
592
593 static (MockComponent, Entity) pairA;
594 static (MockComponent, Entity) pairB;
595
596 [Reads(typeof(MockComponent))]
597 class SameValueComponentReadEngine : Engine
598 {
599 public override void Update(double dt)
600 {
601 var entities = ReadEntities<MockComponent>();
602
603 pairA = (GetComponent<MockComponent>(entities[0]), entities[0]);
604 pairB = (GetComponent<MockComponent>(entities[1]), entities[1]);
605 }
606 }
607
608 // Tests that components with identical values should be distinguishable by their entities
609 [Test]
610 public void SameValueComponents()
611 {
612 var worldBuilder = new WorldBuilder();
613 worldBuilder.AddEngine(new SameValueComponentReadEngine());
614
615 MockComponent componentA;
616 componentA.myInt = 20;
617
618 MockComponent componentB;
619 componentB.myInt = 20;
620
621 var entity = worldBuilder.CreateEntity();
622 worldBuilder.SetComponent(entity, componentA);
623
624 var entityB = worldBuilder.CreateEntity();
625 worldBuilder.SetComponent(entityB, componentB);
626
627 var world = worldBuilder.Build();
628 world.Update(0.01f);
629
630 Assert.That(EngineTest.pairA, Is.Not.EqualTo(EngineTest.pairB));
631 Assert.That(EngineTest.pairA.Item1, Is.EqualTo(EngineTest.pairB.Item1));
632 }
633
634 [Reads(typeof(MockComponent))]
635 class ReadEmptyMockComponentsEngine : Engine
636 {
637 public override void Update(double dt)
638 {
639 ReadEntities<MockComponent>().ToArray().Should().BeEmpty();
640 }
641 }
642
643 [Test]
644 public void ReadComponentsOfTypeWhereNoneExist()
645 {
646 var worldBuilder = new WorldBuilder();
647 worldBuilder.AddEngine(new ReadEmptyMockComponentsEngine());
648
649 var world = worldBuilder.Build();
650 world.Update(0.01f);
651 }
652
653 struct DestroyerComponent : IComponent { }
654
655 [Reads(typeof(DestroyerComponent))]
656 class DestroyerEngine : Engine
657 {
658 public override void Update(double dt)
659 {
660 foreach (ref readonly var entity in ReadEntities<DestroyerComponent>())
661 {
662 Destroy(entity);
663 }
664 }
665 }
666
667 static List<(MockComponent, Entity)> results = new List<(MockComponent, Entity)>();
668
669 [Reads(typeof(MockComponent))]
670 class ReaderEngine : Engine
671 {
672 public override void Update(double dt)
673 {
674 results.Clear();
675
676 foreach (ref readonly var entity in ReadEntities<MockComponent>())
677 {
678 ref readonly var mockComponent = ref GetComponent<MockComponent>(entity);
679 results.Add((mockComponent, entity));
680 }
681 }
682 }
683
684 [Test]
685 public void DestroyEntity()
686 {
687 var worldBuilder = new WorldBuilder();
688 worldBuilder.AddEngine(new DestroyerEngine());
689 worldBuilder.AddEngine(new ReaderEngine());
690
691 var entity = worldBuilder.CreateEntity();
692 var entityB = worldBuilder.CreateEntity();
693 var entityC = worldBuilder.CreateEntity();
694
695 DestroyerComponent destroyerComponent;
696 MockComponent mockComponent;
697 mockComponent.myInt = 2;
698
699 worldBuilder.SetComponent(entity, destroyerComponent);
700 worldBuilder.SetComponent(entity, mockComponent);
701
702 worldBuilder.SetComponent(entityB, destroyerComponent);
703 worldBuilder.SetComponent(entityB, mockComponent);
704
705 worldBuilder.SetComponent(entityC, mockComponent);
706
707 var world = worldBuilder.Build();
708
709 world.Update(0.01);
710 world.Update(0.01);
711
712 Assert.That(results, Does.Not.Contain((mockComponent, entity)));
713 Assert.That(results, Does.Not.Contain((mockComponent, entityB)));
714 Assert.That(results, Does.Contain((mockComponent, entityC)));
715 }
716
717 [Receives(typeof(DestroyComponentMessage))]
718 class DestroyEntityEngine : Engine
719 {
720 public override void Update(double dt)
721 {
722 foreach (ref readonly var message in ReadMessages<DestroyComponentMessage>())
723 {
724 Destroy(message.entity);
725 }
726 }
727 }
728
729 [Test]
730 public void DestroyEntityWithoutID()
731 {
732 results.Clear();
733
734 var worldBuilder = new WorldBuilder();
735 worldBuilder.AddEngine(new AddComponentEngine());
736 worldBuilder.AddEngine(new DestroyEntityEngine());
737 worldBuilder.AddEngine(new ReaderEngine());
738
739 var mockComponent = new MockComponent { };
740 var entity = worldBuilder.CreateEntity();
741 worldBuilder.SetComponent(entity, mockComponent);
742 worldBuilder.SendMessage(new DestroyComponentMessage { entity = entity });
743
744 var world = worldBuilder.Build();
745 world.Update(0.01);
746
747 Assert.DoesNotThrow(() => world.Update(0.01));
748 Assert.That(results, Does.Not.Contain((mockComponent, entity)));
749 }
750
751 [Reads(typeof(DestroyerComponent))]
752 [Writes(typeof(MockComponent))]
753 class DestroyAndAddComponentEngine : Engine
754 {
755 public override void Update(double dt)
756 {
757 foreach (ref readonly var entity in ReadEntities<DestroyerComponent>())
758 {
759 RemoveComponent<MockComponent>(entity);
760 Destroy(entity);
761 }
762 }
763 }
764
765 [Test]
766 public void DestroyEntityWhileRemovingComponent()
767 {
768 var worldBuilder = new WorldBuilder();
769 worldBuilder.AddEngine(new DestroyAndAddComponentEngine());
770 worldBuilder.AddEngine(new ReaderEngine());
771
772 var entity = worldBuilder.CreateEntity();
773
774 worldBuilder.SetComponent(entity, new DestroyerComponent());
775 worldBuilder.SetComponent(entity, new MockComponent());
776
777 var world = worldBuilder.Build();
778
779 Assert.DoesNotThrow(() => world.Update(0.01));
780 }
781
782 [Reads(typeof(MockComponent))]
783 [WritesImmediate(typeof(MockComponent))]
784 [Writes(typeof(MockComponent))]
785 class AddAndRemoveMockComponentEngine : Engine
786 {
787 public override void Update(double dt)
788 {
789 foreach (ref readonly var entity in ReadEntities<MockComponent>())
790 {
791 RemoveComponent<MockComponent>(entity);
792 SetComponent(entity, new MockComponent());
793 }
794 }
795 }
796
797 static Entity entityResult;
798
799 [ReadsImmediate(typeof(MockComponent))]
800 class GetEntityFromImmediateReadComponents : Engine
801 {
802 public override void Update(double dt)
803 {
804 ref readonly var entity = ref ReadEntity<MockComponent>();
805 }
806 }
807
808 [Test]
809 public void GetEntityFromImmediateComponentID()
810 {
811 var worldBuilder = new WorldBuilder();
812 worldBuilder.AddEngine(new AddAndRemoveMockComponentEngine());
813 worldBuilder.AddEngine(new GetEntityFromImmediateReadComponents());
814
815 var entity = worldBuilder.CreateEntity();
816 worldBuilder.SetComponent(entity, new MockComponent());
817
818 var world = worldBuilder.Build();
819
820 Assert.DoesNotThrow(() => world.Update(0.01));
821 }
822
823 [Reads(typeof(MockComponent))]
824 [Writes(typeof(MockComponent))]
825 class DelayedMessageEngine : Engine
826 {
827 public override void Update(double dt)
828 {
829 foreach (ref readonly var entity in ReadEntities<MockComponent>())
830 {
831 RemoveComponent<MockComponent>(entity);
832 SendMessage(new MockMessage { }, 1);
833 }
834 }
835 }
836
837 [Test]
838 public void EngineSendMessageDelayed()
839 {
840 Array.Clear(resultMessages, 0, resultMessages.Length);
841
842 var worldBuilder = new WorldBuilder();
843 worldBuilder.AddEngine(new ActivateTimeDilationEngine());
844 worldBuilder.AddEngine(new DelayedMessageEngine());
845 worldBuilder.AddEngine(new ReadMessagesEngine());
846
847 var entity = worldBuilder.CreateEntity();
848 worldBuilder.SetComponent(entity, new MockComponent { });
849
850 var world = worldBuilder.Build();
851
852 world.Update(0.01);
853
854 resultMessages.Should().BeEmpty();
855
856 world.Update(0.5);
857
858 resultMessages.Should().BeEmpty();
859
860 world.Update(0.5);
861
862 resultMessages.Should().BeEmpty();
863
864 world.Update(2);
865
866 resultMessages.Should().NotBeEmpty();
867 resultMessages.First().Should().BeOfType<MockMessage>();
868 }
869
870 [Reads(typeof(MockComponent))]
871 [Writes(typeof(MockComponent))]
872 class DelayedMessageIgnoringTimeDilationEngine : Engine
873 {
874 public override void Update(double dt)
875 {
876 foreach (ref readonly var entity in ReadEntities<MockComponent>())
877 {
878 RemoveComponent<MockComponent>(entity);
879 SendMessageIgnoringTimeDilation(new MockMessage { }, 1);
880 }
881 }
882 }
883
884 [Test]
885 public void EngineSendMessageDelayedIgnoringTimeDilation()
886 {
887 Array.Clear(resultMessages, 0, resultMessages.Length);
888
889 var worldBuilder = new WorldBuilder();
890 worldBuilder.AddEngine(new ActivateTimeDilationEngine());
891 worldBuilder.AddEngine(new DelayedMessageIgnoringTimeDilationEngine());
892 worldBuilder.AddEngine(new ReadMessagesEngine());
893
894 var entity = worldBuilder.CreateEntity();
895 worldBuilder.SetComponent(entity, new MockComponent { });
896
897 var world = worldBuilder.Build();
898
899 world.Update(0.01);
900
901 resultMessages.Should().BeEmpty();
902
903 world.Update(0.5);
904
905 resultMessages.Should().BeEmpty();
906
907 world.Update(0.5);
908
909 resultMessages.Should().NotBeEmpty();
910 resultMessages.First().Should().BeOfType<MockMessage>();
911 }
912
913 [Receives(typeof(MockMessage))]
914 [WritesImmediate(typeof(MockComponent))]
915 [Writes(typeof(MockComponent), 1)]
916 class ActivateComponentEngine : Engine
917 {
918 public override void Update(double dt)
919 {
920 foreach (ref readonly var message in ReadMessages<MockMessage>())
921 {
922 var entity = CreateEntity();
923 SetComponent(entity, new MockComponent { });
924 }
925 }
926 }
927
928 [ReadsImmediate(typeof(MockComponent))]
929 [Writes(typeof(MockComponent), 0)]
930 class RemoveComponentEngine : Engine
931 {
932 public override void Update(double dt)
933 {
934 foreach (var entity in ReadEntities<MockComponent>())
935 {
936 RemoveComponent<MockComponent>(entity);
937 }
938 }
939 }
940
941 [Test]
942 public void EngineAddAndRemoveComponentSameFrameWithRemovePriority()
943 {
944 var worldBuilder = new WorldBuilder();
945 worldBuilder.AddEngine(new ActivateComponentEngine());
946 worldBuilder.AddEngine(new RemoveComponentEngine());
947 worldBuilder.AddEngine(new ReadComponentsTestEngine());
948
949 worldBuilder.SendMessage(new MockMessage { });
950
951 var world = worldBuilder.Build();
952
953 Assert.DoesNotThrow(() => world.Update(0.01));
954 world.Update(0.01); // update again for the read
955 resultComponents.Should().BeEmpty();
956 }
957
958 struct DestroyComponentMessage : IMessage { public Entity entity; }
959
960 [Reads(typeof(MockComponent))]
961 [Writes(typeof(MockComponent))]
962 class AddComponentEngine : Engine
963 {
964 public override void Update(double dt)
965 {
966 foreach (var entity in ReadEntities<MockComponent>())
967 {
968 SetComponent(entity, new MockComponent { });
969 }
970 }
971 }
972
973 static Entity readEntity;
974
975 [Reads(typeof(MockComponent))]
976 class ReadEntityByComponentTypeEngine : Engine
977 {
978 public override void Update(double dt)
979 {
980 readEntity = ReadEntity<MockComponent>();
981 }
982 }
983
984 [Test]
985 public void ReadEntity()
986 {
987 var worldBuilder = new WorldBuilder();
988 worldBuilder.AddEngine(new ReadEntityByComponentTypeEngine());
989
990 var entity = worldBuilder.CreateEntity();
991 worldBuilder.SetComponent(entity, new MockComponent());
992
993 var world = worldBuilder.Build();
994 world.Update(0.01);
995
996 entity.Should().BeEquivalentTo(readEntity);
997 }
998
999 [Test]
1000 public void ReadEntityThrowsWhenNoComponentOfTypeExists()
1001 {
1002 var worldBuilder = new WorldBuilder();
1003 worldBuilder.AddEngine(new ReadEntityByComponentTypeEngine());
1004
1005 var world = worldBuilder.Build();
1006
1007 Assert.Throws<NoComponentOfTypeException>(() => world.Update(0.01), "No component of type MockComponent exists");
1008 }
1009
1010 struct MockComponentB : IComponent
1011 {
1012 private int value;
1013
1014 public MockComponentB(int value)
1015 {
1016 this.value = value;
1017 }
1018 }
1019
1020 static MockComponentB getComponentResult;
1021
1022 [Reads(typeof(MockComponent), typeof(MockComponentB))]
1023 class GetComponentEngine : Engine
1024 {
1025 public override void Update(double dt)
1026 {
1027 getComponentResult = GetComponent<MockComponentB>(ReadEntity<MockComponent>());
1028 }
1029 }
1030
1031 [Test]
1032 public void GetComponent()
1033 {
1034 var worldBuilder = new WorldBuilder();
1035
1036 var entity = worldBuilder.CreateEntity();
1037
1038 worldBuilder.SetComponent(entity, new MockComponent());
1039 worldBuilder.SetComponent(entity, new MockComponentB(3));
1040 worldBuilder.AddEngine(new GetComponentEngine());
1041
1042 var world = worldBuilder.Build();
1043
1044 world.Update(0.01);
1045
1046 getComponentResult.Should().BeEquivalentTo(new MockComponentB(3));
1047 }
1048
1049 [Reads(typeof(MockComponent), typeof(MockComponentB))]
1050 class GetComponentExceptionEngine : Engine
1051 {
1052 public override void Update(double dt)
1053 {
1054 foreach (var entity in ReadEntities<MockComponent>())
1055 {
1056 GetComponent<MockComponentB>(entity);
1057 }
1058 }
1059 }
1060
1061 [Test]
1062 public void GetComponentWhenComponentIsNotOnEntity()
1063 {
1064 var worldBuilder = new WorldBuilder();
1065 worldBuilder.AddEngine(new GetComponentExceptionEngine());
1066
1067 var entity = worldBuilder.CreateEntity();
1068 worldBuilder.SetComponent(entity, new MockComponent());
1069
1070 var world = worldBuilder.Build();
1071 Assert.Throws<Encompass.Exceptions.NoComponentOfTypeOnEntityException>(() => world.Update(0.01));
1072 }
1073
1074 static Entity[] readEntities;
1075
1076 [Reads(typeof(MockComponent))]
1077 class ReadEntitiesWithComponentTypeEngine : Engine
1078 {
1079 public override void Update(double dt)
1080 {
1081 readEntities = ReadEntities<MockComponent>().ToArray();
1082 }
1083 }
1084
1085 [Test]
1086 public void ReadEntities()
1087 {
1088 var worldBuilder = new WorldBuilder();
1089 worldBuilder.AddEngine(new ReadEntitiesWithComponentTypeEngine());
1090 worldBuilder.AddEngine(new DestroyAllWithEngine());
1091
1092 var entity = worldBuilder.CreateEntity();
1093 worldBuilder.SetComponent(entity, new MockComponent { });
1094
1095 var entityB = worldBuilder.CreateEntity();
1096 worldBuilder.SetComponent(entityB, new MockComponent { });
1097
1098 var world = worldBuilder.Build();
1099 world.Update(0.01);
1100
1101 readEntities.Should().Contain(entity);
1102 readEntities.Should().Contain(entityB);
1103 }
1104
1105 [Reads(typeof(MockComponent))]
1106 class DestroyWithEngine : Engine
1107 {
1108 public override void Update(double dt)
1109 {
1110 if (SomeComponent<MockComponent>())
1111 {
1112 DestroyWith<MockComponent>();
1113 }
1114 }
1115 }
1116
1117 [Test]
1118 public void DestroyWith()
1119 {
1120 var worldBuilder = new WorldBuilder();
1121 worldBuilder.AddEngine(new ReadEntitiesWithComponentTypeEngine());
1122 worldBuilder.AddEngine(new DestroyWithEngine());
1123
1124 var entity = worldBuilder.CreateEntity();
1125 worldBuilder.SetComponent(entity, new MockComponent { });
1126
1127 var world = worldBuilder.Build();
1128 world.Update(0.01);
1129 world.Update(0.01); // update twice so the read happens after destroy
1130
1131 readEntities.Should().BeEmpty();
1132 }
1133
1134 [Reads(typeof(MockComponent))]
1135 class DestroyAllWithEngine : Engine
1136 {
1137 public override void Update(double dt)
1138 {
1139 DestroyAllWith<MockComponent>();
1140 }
1141 }
1142
1143 [Test]
1144 public void DestroyAllWith()
1145 {
1146 var worldBuilder = new WorldBuilder();
1147 worldBuilder.AddEngine(new ReadEntitiesWithComponentTypeEngine());
1148 worldBuilder.AddEngine(new DestroyAllWithEngine());
1149
1150 var entity = worldBuilder.CreateEntity();
1151 worldBuilder.SetComponent(entity, new MockComponent { });
1152
1153 var entityB = worldBuilder.CreateEntity();
1154 worldBuilder.SetComponent(entityB, new MockComponent { });
1155
1156 var world = worldBuilder.Build();
1157 world.Update(0.01);
1158 world.Update(0.01); // update twice so the read happens after destroy
1159
1160 readEntities.Should().BeEmpty();
1161 }
1162
1163 [Reads(typeof(MockComponent))]
1164 [Writes(typeof(MockComponent))]
1165 class RemoveComponentByTypeEngine : Engine
1166 {
1167 public override void Update(double dt)
1168 {
1169 foreach (ref readonly var entity in ReadEntities<MockComponent>())
1170 {
1171 RemoveComponent<MockComponent>(entity);
1172 }
1173 }
1174 }
1175
1176 [Test]
1177 public void RemoveComponentByType()
1178 {
1179 var worldBuilder = new WorldBuilder();
1180 worldBuilder.AddEngine(new ReadComponentsTestEngine());
1181 worldBuilder.AddEngine(new RemoveComponentByTypeEngine());
1182
1183 var entity = worldBuilder.CreateEntity();
1184 worldBuilder.SetComponent(entity, new MockComponent { });
1185
1186 var entityB = worldBuilder.CreateEntity();
1187 worldBuilder.SetComponent(entity, new MockComponent { });
1188
1189 var world = worldBuilder.Build();
1190
1191 world.Update(0.01);
1192 world.Update(0.01);
1193
1194 resultComponents.Should().BeEmpty();
1195 }
1196
1197 static double dilatedDeltaTime;
1198
1199 class ActivateTimeDilationEngine : Engine
1200 {
1201 public override void Update(double dt)
1202 {
1203 if (!TimeDilationActive)
1204 {
1205 ActivateTimeDilation(0.2, 1, 1, 1);
1206 }
1207 else
1208 {
1209 dilatedDeltaTime = dt;
1210 }
1211 }
1212 }
1213
1214 [Test]
1215 public void ActivateTimeDilation()
1216 {
1217 var worldBuilder = new WorldBuilder();
1218 worldBuilder.AddEngine(new ActivateTimeDilationEngine());
1219
1220 var world = worldBuilder.Build();
1221
1222 world.Update(0.01); // activate time dilation
1223
1224 world.Update(0.5);
1225
1226 dilatedDeltaTime.Should().BeApproximately(0.3, 0.01);
1227
1228 world.Update(0.5);
1229
1230 dilatedDeltaTime.Should().BeApproximately(0.1, 0.01);
1231
1232 world.Update(1);
1233
1234 world.Update(0.5);
1235
1236 dilatedDeltaTime.Should().BeApproximately(0.3, 0.01);
1237 }
1238
1239 class ActivateTimeDilationLowerFactorEngine : Engine
1240 {
1241 private bool activated = false;
1242
1243 public override void Update(double dt)
1244 {
1245 if (!activated)
1246 {
1247 ActivateTimeDilation(0.2, 1, 1, 1);
1248 activated = true;
1249 }
1250 }
1251 }
1252
1253 class ActivateTimeDilationHigherFactorEngine : Engine
1254 {
1255 private bool activated = false;
1256
1257 public override void Update(double dt)
1258 {
1259 if (!activated)
1260 {
1261 ActivateTimeDilation(0.5, 1, 1, 1);
1262 activated = true;
1263 }
1264 }
1265 }
1266
1267 static bool timeDilationActive;
1268 class ReadDilatedDeltaTimeEngine : Engine
1269 {
1270 public override void Update(double dt)
1271 {
1272 dilatedDeltaTime = dt;
1273 timeDilationActive = TimeDilationActive;
1274 }
1275 }
1276
1277 [Test]
1278 public void MultipleActivateTimeDilation()
1279 {
1280 var worldBuilder = new WorldBuilder();
1281 worldBuilder.AddEngine(new ReadDilatedDeltaTimeEngine());
1282 worldBuilder.AddEngine(new ActivateTimeDilationLowerFactorEngine());
1283 worldBuilder.AddEngine(new ActivateTimeDilationHigherFactorEngine());
1284
1285 var world = worldBuilder.Build();
1286
1287 world.Update(0.01); // activate time dilation
1288
1289 world.Update(0.5); // 0.3 and 0.375
1290
1291 dilatedDeltaTime.Should().BeApproximately(0.3375, 0.01);
1292 timeDilationActive.Should().BeTrue();
1293
1294 world.Update(5);
1295
1296 dilatedDeltaTime.Should().BeApproximately(5, 0.01);
1297 timeDilationActive.Should().BeFalse();
1298 }
1299
1300 static double undilatedDeltaTime;
1301
1302 [IgnoresTimeDilation]
1303 class IgnoresTimeDilationEngine : Engine
1304 {
1305 public override void Update(double dt)
1306 {
1307 undilatedDeltaTime = dt;
1308 }
1309 }
1310
1311 [Test]
1312 public void IgnoresTimeDilation()
1313 {
1314 var worldBuilder = new WorldBuilder();
1315 worldBuilder.AddEngine(new ActivateTimeDilationEngine());
1316 worldBuilder.AddEngine(new IgnoresTimeDilationEngine());
1317
1318 var world = worldBuilder.Build();
1319
1320 world.Update(0.01); // activate time dilation
1321
1322 world.Update(0.5);
1323
1324 undilatedDeltaTime.Should().Be(0.5);
1325 }
1326
1327 class AddComponentWithoutPriorityEngine : Engine
1328 {
1329 public override void Update(double dt)
1330 {
1331 var entity = CreateEntity();
1332 AddComponent(entity, new MockComponent());
1333
1334 var entityB = CreateEntity();
1335 AddComponent(entityB, new MockComponent());
1336 }
1337 }
1338
1339 [Test]
1340 public void AddComponent()
1341 {
1342 var worldBuilder = new WorldBuilder();
1343
1344 worldBuilder.AddEngine(new AddComponentWithoutPriorityEngine());
1345 worldBuilder.AddEngine(new ReadComponentsTestEngine());
1346
1347 var world = worldBuilder.Build();
1348
1349 world.Update(0.01);
1350 world.Update(0.01);
1351
1352 resultComponents.Should().HaveCount(2);
1353
1354 world.Update(0.01);
1355
1356 resultComponents.Should().HaveCount(4);
1357 }
1358
1359 [Reads(typeof(MockComponent))]
1360 class AddComponentToPreviouslyExistingEntityEngine : Engine
1361 {
1362 public override void Update(double dt)
1363 {
1364 ref readonly var entity = ref ReadEntity<MockComponent>();
1365 AddComponent(entity, new MockComponent());
1366 }
1367 }
1368
1369 [Test]
1370 public void AddComponentToPreviouslyExistingEntityTest()
1371 {
1372 var worldBuilder = new WorldBuilder();
1373 worldBuilder.AddEngine(new AddComponentToPreviouslyExistingEntityEngine());
1374
1375 var entity = worldBuilder.CreateEntity();
1376 worldBuilder.SetComponent(entity, new MockComponent());
1377
1378 var world = worldBuilder.Build();
1379
1380 Assert.Throws<IllegalWriteException>(() => world.Update(0.01));
1381 }
1382
1383 [WritesImmediate(typeof(MockComponentB))]
1384 class AddImmediateComponentEngine : Engine
1385 {
1386 public override void Update(double dt)
1387 {
1388 var entity = CreateEntity();
1389 AddComponent(entity, new MockComponentB(5));
1390 }
1391 }
1392
1393 [ReadsImmediate(typeof(MockComponentB))]
1394 class ReadImmediateComponentEngine : Engine
1395 {
1396 public override void Update(double dt)
1397 {
1398 ref readonly var component = ref ReadComponent<MockComponentB>();
1399 getComponentResult = component;
1400 }
1401 }
1402
1403 [Test]
1404 public void AddImmediateComponentTest()
1405 {
1406 getComponentResult = default(MockComponentB);
1407
1408 var worldBuilder = new WorldBuilder();
1409
1410 worldBuilder.AddEngine(new AddImmediateComponentEngine());
1411 worldBuilder.AddEngine(new ReadImmediateComponentEngine());
1412
1413 var world = worldBuilder.Build();
1414
1415 world.Update(0.01);
1416
1417 getComponentResult.Should().Be(new MockComponentB(5));
1418 }
1419
1420 static bool entityExistsResult;
1421
1422 class EntityExistsEngine : Engine
1423 {
1424 private int _id;
1425
1426 public EntityExistsEngine(int id)
1427 {
1428 _id = id;
1429 }
1430
1431 public override void Update(double dt)
1432 {
1433 entityExistsResult = EntityExists(_id);
1434 }
1435 }
1436
1437 [Test]
1438 public void PruneEmptyEntities()
1439 {
1440 var worldBuilder = new WorldBuilder();
1441 var entity = worldBuilder.CreateEntity();
1442 var id = entity.ID;
1443
1444 var world = worldBuilder.Build();
1445
1446 world.Update(0.01);
1447
1448 entityExistsResult.Should().BeFalse();
1449 }
1450
1451 public class QueryTests
1452 {
1453 struct MockComponentB : IComponent { }
1454 struct MockComponentC : IComponent { }
1455 struct MockComponentD : IComponent { }
1456
1457 [Writes(typeof(MockComponentB))]
1458 [QueryWith(typeof(MockComponent), typeof(MockComponentB))]
1459 class EntityQueryWithComponentsEngine : Engine
1460 {
1461 private List<Entity> entities;
1462
1463 public EntityQueryWithComponentsEngine(List<Entity> entities)
1464 {
1465 this.entities = entities;
1466 }
1467
1468 public override void Update(double dt)
1469 {
1470 entities.Clear();
1471 foreach (var entity in TrackedEntities)
1472 {
1473 entities.Add(entity);
1474 RemoveComponent<MockComponentB>(entity);
1475 }
1476 }
1477 }
1478
1479 [Test]
1480 public void EntitiesWithComponents()
1481 {
1482 var worldBuilder = new WorldBuilder();
1483
1484 var entity = worldBuilder.CreateEntity();
1485 var entityB = worldBuilder.CreateEntity();
1486 var entityC = worldBuilder.CreateEntity();
1487
1488 worldBuilder.SetComponent(entity, new MockComponent());
1489 worldBuilder.SetComponent(entity, new MockComponentB());
1490
1491 worldBuilder.SetComponent(entityB, new MockComponent());
1492 worldBuilder.SetComponent(entityB, new MockComponentB());
1493
1494 worldBuilder.SetComponent(entityC, new MockComponentB());
1495
1496 var queriedEntities = new List<Entity>();
1497 worldBuilder.AddEngine(new EntityQueryWithComponentsEngine(queriedEntities));
1498
1499 var world = worldBuilder.Build();
1500
1501 world.Update(0.01);
1502
1503 queriedEntities.Should().BeEquivalentTo(new Entity[] { entity, entityB });
1504
1505 world.Update(0.01);
1506
1507 queriedEntities.Should().BeEmpty();
1508 }
1509
1510 [Writes(typeof(MockComponent))]
1511 [QueryWithout(typeof(MockComponent))]
1512 class EntityQueryWithoutComponentsEngine : Engine
1513 {
1514 private List<Entity> entities;
1515
1516 public EntityQueryWithoutComponentsEngine(List<Entity> entities)
1517 {
1518 this.entities = entities;
1519 }
1520
1521 public override void Update(double dt)
1522 {
1523 entities.Clear();
1524 foreach (var entity in TrackedEntities)
1525 {
1526 entities.Add(entity);
1527 SetComponent(entity, new MockComponent());
1528 }
1529 }
1530 }
1531
1532 [Test]
1533 public void EntitiesWithoutComponents()
1534 {
1535 var worldBuilder = new WorldBuilder();
1536
1537 var entity = worldBuilder.CreateEntity();
1538 var entityB = worldBuilder.CreateEntity();
1539 var entityC = worldBuilder.CreateEntity();
1540
1541 worldBuilder.SetComponent(entity, new MockComponent());
1542 worldBuilder.SetComponent(entity, new MockComponentB());
1543
1544 worldBuilder.SetComponent(entityB, new MockComponent());
1545 worldBuilder.SetComponent(entityB, new MockComponentB());
1546
1547 worldBuilder.SetComponent(entityC, new MockComponentB());
1548
1549 var queriedEntities = new List<Entity>();
1550 worldBuilder.AddEngine(new EntityQueryWithoutComponentsEngine(queriedEntities));
1551
1552 var world = worldBuilder.Build();
1553
1554 world.Update(0.01);
1555
1556 queriedEntities.ToArray().Should().BeEquivalentTo(new Entity[] { entityC });
1557
1558 world.Update(0.01);
1559
1560 queriedEntities.Should().BeEmpty();
1561 }
1562
1563 [QueryWith(typeof(MockComponent), typeof(MockComponentB))]
1564 [QueryWithout(typeof(MockComponentD))]
1565 class EntityQueryWithandWithoutComponentsEngine : Engine
1566 {
1567 private List<Entity> entities;
1568
1569 public EntityQueryWithandWithoutComponentsEngine(List<Entity> entities)
1570 {
1571 this.entities = entities;
1572 }
1573
1574 public override void Update(double dt)
1575 {
1576 entities.Clear();
1577
1578 entities.AddRange(TrackedEntities);
1579 }
1580 }
1581
1582 [Test]
1583 public void EntitiesWithAndWithoutComponents()
1584 {
1585 var worldBuilder = new WorldBuilder();
1586
1587 var entity = worldBuilder.CreateEntity();
1588 var entityB = worldBuilder.CreateEntity();
1589 var entityC = worldBuilder.CreateEntity();
1590 var entityD = worldBuilder.CreateEntity();
1591
1592 worldBuilder.SetComponent(entity, new MockComponent());
1593 worldBuilder.SetComponent(entity, new MockComponentB());
1594 worldBuilder.SetComponent(entity, new MockComponentD());
1595
1596 worldBuilder.SetComponent(entityB, new MockComponent());
1597
1598 worldBuilder.SetComponent(entityC, new MockComponent());
1599 worldBuilder.SetComponent(entityC, new MockComponentB());
1600 worldBuilder.SetComponent(entityC, new MockComponentC());
1601 worldBuilder.SetComponent(entityC, new MockComponentD());
1602
1603 worldBuilder.SetComponent(entityD, new MockComponent());
1604 worldBuilder.SetComponent(entityD, new MockComponentB());
1605 worldBuilder.SetComponent(entityD, new MockComponentC());
1606
1607 var queriedEntities = new List<Entity>();
1608 worldBuilder.AddEngine(new EntityQueryWithandWithoutComponentsEngine(queriedEntities));
1609
1610 var world = worldBuilder.Build();
1611
1612 world.Update(0.01);
1613
1614 queriedEntities.ToArray().Should().BeEquivalentTo(new Entity[] { entityD });
1615 }
1616
1617 [Reads(typeof(MockComponent))]
1618 [WritesImmediate(typeof(MockComponentB))]
1619 [Writes(typeof(MockComponentB), 0)]
1620 class AddImmediateComponentEngine : Engine
1621 {
1622 public override void Update(double dt)
1623 {
1624 foreach (var entity in ReadEntities<MockComponent>())
1625 {
1626 SetComponent(entity, new MockComponentB());
1627 }
1628 }
1629 }
1630
1631 [ReadsImmediate(typeof(MockComponentB))]
1632 [QueryWith(typeof(MockComponentB))]
1633 class EntityQueryWithImmediateComponentsEngine : Engine
1634 {
1635 private List<Entity> entities;
1636
1637 public EntityQueryWithImmediateComponentsEngine(List<Entity> entities)
1638 {
1639 this.entities = entities;
1640 }
1641
1642 public override void Update(double dt)
1643 {
1644 entities.Clear();
1645 entities.AddRange(TrackedEntities);
1646 }
1647 }
1648
1649 [Test]
1650 public void EntitiesWithImmediateComponents()
1651 {
1652 var worldBuilder = new WorldBuilder();
1653
1654 var entity = worldBuilder.CreateEntity();
1655 var entityB = worldBuilder.CreateEntity();
1656
1657 worldBuilder.SetComponent(entity, new MockComponent());
1658
1659 var queriedEntities = new List<Entity>();
1660 worldBuilder.AddEngine(new AddImmediateComponentEngine());
1661 worldBuilder.AddEngine(new EntityQueryWithImmediateComponentsEngine(queriedEntities));
1662
1663 var world = worldBuilder.Build();
1664
1665 world.Update(0.01);
1666
1667 queriedEntities.ToArray().Should().BeEquivalentTo(new Entity[] { entity });
1668 }
1669
1670 [ReadsImmediate(typeof(MockComponentB))]
1671 [QueryWithout(typeof(MockComponentB))]
1672 class EntityQueryWithoutImmediateComponentsEngine : Engine
1673 {
1674 private List<Entity> entities;
1675
1676 public EntityQueryWithoutImmediateComponentsEngine(List<Entity> entities)
1677 {
1678 this.entities = entities;
1679 }
1680
1681 public override void Update(double dt)
1682 {
1683 entities.Clear();
1684 entities.AddRange(TrackedEntities);
1685 }
1686 }
1687
1688 [Test]
1689 public void EntitiesWithoutImmediateComponents()
1690 {
1691 var worldBuilder = new WorldBuilder();
1692
1693 var entity = worldBuilder.CreateEntity();
1694 var entityB = worldBuilder.CreateEntity();
1695
1696 worldBuilder.SetComponent(entity, new MockComponent());
1697
1698 var queriedEntities = new List<Entity>();
1699 worldBuilder.AddEngine(new AddImmediateComponentEngine());
1700 worldBuilder.AddEngine(new EntityQueryWithoutImmediateComponentsEngine(queriedEntities));
1701
1702 var world = worldBuilder.Build();
1703
1704 world.Update(0.01);
1705
1706 queriedEntities.ToArray().Should().BeEquivalentTo(new Entity[] { entityB });
1707 }
1708
1709 [Reads(typeof(MockComponentC), typeof(MockComponentD))]
1710 [WritesImmediate(typeof(MockComponent), typeof(MockComponentB))]
1711 [Writes(typeof(MockComponent), 0)]
1712 [Writes(typeof(MockComponentB), 0)]
1713 class ConditionallyAddImmediateComponentsEngine : Engine
1714 {
1715 public override void Update(double dt)
1716 {
1717 foreach (var entity in ReadEntities<MockComponentC>())
1718 {
1719 SetComponent(entity, new MockComponent());
1720 }
1721
1722 foreach (var entity in ReadEntities<MockComponentD>())
1723 {
1724 SetComponent(entity, new MockComponent());
1725 SetComponent(entity, new MockComponentB());
1726 }
1727 }
1728 }
1729
1730 [ReadsImmediate(typeof(MockComponent), typeof(MockComponentB))]
1731 [QueryWith(typeof(MockComponent))]
1732 [QueryWithout(typeof(MockComponentB))]
1733 class EntityQueryWithAndWithoutImmediateComponentsEngine : Engine
1734 {
1735 private List<Entity> entities;
1736
1737 public EntityQueryWithAndWithoutImmediateComponentsEngine(List<Entity> entities)
1738 {
1739 this.entities = entities;
1740 }
1741
1742 public override void Update(double dt)
1743 {
1744 entities.Clear();
1745
1746 entities.AddRange(TrackedEntities);
1747 }
1748 }
1749
1750 [Test]
1751 public void EntitiesWithAndWithoutImmediateComponents()
1752 {
1753 var worldBuilder = new WorldBuilder();
1754
1755 var entity = worldBuilder.CreateEntity();
1756 var entityB = worldBuilder.CreateEntity();
1757 var entityC = worldBuilder.CreateEntity();
1758
1759 worldBuilder.SetComponent(entityB, new MockComponentC());
1760 worldBuilder.SetComponent(entityC, new MockComponentD());
1761
1762 var queriedEntities = new List<Entity>();
1763 worldBuilder.AddEngine(new ConditionallyAddImmediateComponentsEngine());
1764 worldBuilder.AddEngine(new EntityQueryWithAndWithoutImmediateComponentsEngine(queriedEntities));
1765
1766 var world = worldBuilder.Build();
1767
1768 world.Update(0.01);
1769
1770 queriedEntities.ToArray().Should().BeEquivalentTo(new Entity[] { entityB });
1771 }
1772
1773 [Reads(typeof(MockComponentC))]
1774 [WritesImmediate(typeof(MockComponentB))]
1775 [Writes(typeof(MockComponentB), 0)]
1776 class ConditionallyAddImmediateComponentEngine : Engine
1777 {
1778 public override void Update(double dt)
1779 {
1780 foreach (var entity in ReadEntities<MockComponentC>())
1781 {
1782 SetComponent(entity, new MockComponentB());
1783 }
1784 }
1785 }
1786
1787 [ReadsImmediate(typeof(MockComponentB))]
1788 [QueryWith(typeof(MockComponent), typeof(MockComponentB))]
1789 class EntityQueryWithImmediateAndNonImmediateComponents : Engine
1790 {
1791 private List<Entity> entities;
1792
1793 public EntityQueryWithImmediateAndNonImmediateComponents(List<Entity> entities)
1794 {
1795 this.entities = entities;
1796 }
1797
1798 public override void Update(double dt)
1799 {
1800 entities.Clear();
1801 entities.AddRange(TrackedEntities);
1802 }
1803 }
1804
1805 [Test]
1806 public void EntitiesWithImmediateAndNonImmediateComponents()
1807 {
1808 var worldBuilder = new WorldBuilder();
1809
1810 var entity = worldBuilder.CreateEntity();
1811 var entityB = worldBuilder.CreateEntity();
1812 var entityC = worldBuilder.CreateEntity();
1813
1814 worldBuilder.SetComponent(entityB, new MockComponent());
1815 worldBuilder.SetComponent(entityB, new MockComponentC());
1816 worldBuilder.SetComponent(entityC, new MockComponentD());
1817
1818 var queriedEntities = new List<Entity>();
1819 worldBuilder.AddEngine(new ConditionallyAddImmediateComponentEngine());
1820 worldBuilder.AddEngine(new EntityQueryWithImmediateAndNonImmediateComponents(queriedEntities));
1821
1822 var world = worldBuilder.Build();
1823
1824 world.Update(0.01);
1825
1826 queriedEntities.ToArray().Should().BeEquivalentTo(new Entity[] { entityB });
1827 }
1828
1829 [ReadsImmediate(typeof(MockComponentB))]
1830 class ReadImmediateComponentsEngine : Engine
1831 {
1832 private List<MockComponentB> _components;
1833
1834 public ReadImmediateComponentsEngine(List<MockComponentB> components)
1835 {
1836 _components = components;
1837 }
1838 public override void Update(double dt)
1839 {
1840 _components.AddRange(ReadComponents<MockComponentB>().ToArray());
1841 }
1842 }
1843
1844 [Test]
1845 public void ReadImmediateComponents()
1846 {
1847 var worldBuilder = new WorldBuilder();
1848
1849 var _components = new List<MockComponentB>();
1850
1851 var entity = worldBuilder.CreateEntity();
1852 worldBuilder.SetComponent(entity, new MockComponent());
1853
1854 worldBuilder.AddEngine(new AddImmediateComponentEngine());
1855 worldBuilder.AddEngine(new ReadImmediateComponentsEngine(_components));
1856
1857 var world = worldBuilder.Build();
1858 world.Update(0.01);
1859
1860 _components.Should().NotBeEmpty();
1861 }
1862
1863 [ReadsImmediate(typeof(MockComponentB))]
1864 [Reads(typeof(MockComponent))]
1865 class HasAndGetImmediateComponentEngine : Engine
1866 {
1867 private List<MockComponentB> _components;
1868
1869 public HasAndGetImmediateComponentEngine(List<MockComponentB> components)
1870 {
1871 _components = components;
1872 }
1873
1874 public override void Update(double dt)
1875 {
1876 foreach (ref readonly var entity in ReadEntities<MockComponent>())
1877 {
1878 if (HasComponent<MockComponentB>(entity))
1879 {
1880 _components.Add(GetComponent<MockComponentB>(entity));
1881 }
1882 }
1883 }
1884 }
1885
1886 [Test]
1887 public void HasAndGetImmediateComponent()
1888 {
1889 var worldBuilder = new WorldBuilder();
1890
1891 var _components = new List<MockComponentB>();
1892
1893 var entity = worldBuilder.CreateEntity();
1894 worldBuilder.SetComponent(entity, new MockComponent());
1895
1896 worldBuilder.AddEngine(new AddImmediateComponentEngine());
1897 worldBuilder.AddEngine(new HasAndGetImmediateComponentEngine(_components));
1898
1899 var world = worldBuilder.Build();
1900 world.Update(0.01);
1901
1902 _components.Should().NotBeEmpty();
1903 }
1904
1905 struct MockTimerComponent : IComponent
1906 {
1907 public double Timer { get; }
1908
1909 public MockTimerComponent(double time)
1910 {
1911 Timer = time;
1912 }
1913 }
1914
1915 [Reads(typeof(MockTimerComponent))]
1916 [Writes(typeof(MockTimerComponent))]
1917 class ReadWhileRemovingComponentsEngine : Engine
1918 {
1919 public override void Update(double dt)
1920 {
1921 foreach (ref readonly var entity in ReadEntities<MockTimerComponent>())
1922 {
1923 ref readonly var component = ref GetComponent<MockTimerComponent>(entity);
1924
1925 if (component.Timer - dt <= 0)
1926 {
1927 RemoveComponent<MockTimerComponent>(entity);
1928
1929 }
1930 else
1931 {
1932 SetComponent<MockTimerComponent>(entity, new MockTimerComponent(component.Timer - dt));
1933 }
1934 }
1935 }
1936 }
1937
1938 [Test]
1939 public void ReadWhileRemovingComponents()
1940 {
1941 var worldBuilder = new WorldBuilder();
1942
1943 var entity = worldBuilder.CreateEntity();
1944 worldBuilder.SetComponent(entity, new MockTimerComponent(0.5));
1945
1946 var entityB = worldBuilder.CreateEntity();
1947 worldBuilder.SetComponent(entityB, new MockTimerComponent(0.4));
1948
1949 worldBuilder.AddEngine(new ReadWhileRemovingComponentsEngine());
1950
1951 var world = worldBuilder.Build();
1952 Assert.DoesNotThrow(() => world.Update(0.2));
1953 Assert.DoesNotThrow(() => world.Update(0.25));
1954 }
1955
1956 [Test]
1957 public void DestroyedEntitiesAreRemovedFromTracking()
1958 {
1959 var worldBuilder = new WorldBuilder();
1960
1961 var entity = worldBuilder.CreateEntity();
1962 worldBuilder.SetComponent(entity, new MockComponent());
1963
1964 worldBuilder.AddEngine(new DestroyWithEngine());
1965 worldBuilder.AddEngine(new ReadEntitiesWithComponentTypeEngine());
1966
1967 var world = worldBuilder.Build();
1968
1969 world.Update(0.01);
1970 world.Update(0.01);
1971
1972 readEntities.Should().BeEmpty();
1973 }
1974 }
1975 }
1976 }
@@ -0,0 +1,38
1 using NUnit.Framework;
2 using FluentAssertions;
3
4 using Encompass;
5 using Encompass.Exceptions;
6
7 namespace Tests
8 {
9 public class EntityTests
10 {
11 [Test]
12 public void Equals()
13 {
14 var worldBuilder = new WorldBuilder();
15 var entity = worldBuilder.CreateEntity();
16 var entityTwo = worldBuilder.CreateEntity();
17
18 var copyEntity = entity;
19
20 Assert.AreNotEqual(entity, entityTwo);
21 Assert.AreEqual(entity, entity);
22 Assert.IsTrue(entity == copyEntity);
23 }
24
25 [Test]
26 public void EntityOverflowException()
27 {
28 var worldBuilder = new WorldBuilder(16);
29
30 for (var i = 0; i < 16; i++)
31 {
32 worldBuilder.CreateEntity();
33 }
34
35 Assert.Throws<EntityOverflowException>(() => worldBuilder.CreateEntity());
36 }
37 }
38 }
@@ -0,0 +1,71
1 using NUnit.Framework;
2 using Encompass;
3
4 namespace Tests
5 {
6 public static class GeneralRendererTest
7 {
8 struct AComponent : IComponent { }
9
10 public class SingletonRead
11 {
12 static (AComponent, Entity) result;
13
14 class TestRenderer : GeneralRenderer
15 {
16 public override void Render()
17 {
18 ref readonly var entity = ref ReadEntity<AComponent>();
19 result = (GetComponent<AComponent>(entity), entity);
20 }
21 }
22
23 [Test]
24 public void SingletonComponent()
25 {
26 var worldBuilder = new WorldBuilder();
27 worldBuilder.AddGeneralRenderer(new TestRenderer(), 1);
28
29 AComponent aComponent;
30
31 var entity = worldBuilder.CreateEntity();
32 worldBuilder.SetComponent(entity, aComponent);
33
34 var world = worldBuilder.Build();
35
36 world.Update(0.01f);
37 world.Draw();
38
39 world.Update(0.01);
40 world.Draw();
41
42 Assert.That(result, Is.EqualTo((aComponent, entity)));
43 }
44
45 [Test]
46 public void MultipleComponents()
47 {
48 var worldBuilder = new WorldBuilder();
49 worldBuilder.AddGeneralRenderer(new TestRenderer(), 1);
50
51 AComponent aComponent;
52 AComponent aComponentTwo;
53
54 var entity = worldBuilder.CreateEntity();
55 worldBuilder.SetComponent(entity, aComponent);
56
57 var entityB = worldBuilder.CreateEntity();
58 worldBuilder.SetComponent(entityB, aComponentTwo);
59 var world = worldBuilder.Build();
60
61 world.Update(0.01f);
62 world.Draw();
63
64 world.Update(0.01f);
65 world.Draw();
66
67 Assert.That(result, Is.EqualTo((aComponent, entity)).Or.EqualTo((aComponentTwo, entityB)));
68 }
69 }
70 }
71 }
@@ -0,0 +1,109
1 using System;
2
3 using NUnit.Framework;
4 using FluentAssertions;
5 using Encompass;
6 using System.Collections.Generic;
7
8 namespace Tests
9 {
10 public class OrderedRendererTest
11 {
12 struct AComponent : IComponent { }
13 struct BComponent : IComponent { }
14 struct CComponent : IComponent { }
15
16 struct TestDrawComponent : IComponent, IDrawableComponent
17 {
18 public int Layer { get; set; }
19 }
20
21 class TestRenderer : OrderedRenderer<TestDrawComponent>
22 {
23 public override void Render(Entity entity, in TestDrawComponent testDrawComponent) { }
24 }
25
26 static bool called = false;
27 class DeactivatedRenderer : TestRenderer
28 {
29 public override void Render(Entity entity, in TestDrawComponent testDrawComponent)
30 {
31 called = true;
32 }
33 }
34
35 static bool calledOnDraw = false;
36 static (TestDrawComponent, Entity) resultComponent;
37
38 class CalledRenderer : OrderedRenderer<TestDrawComponent>
39 {
40 public override void Render(Entity entity, in TestDrawComponent testDrawComponent)
41 {
42 resultComponent = (testDrawComponent, entity);
43 calledOnDraw = true;
44 }
45 }
46
47 [Test]
48 public void RenderMethodCalledOnWorldDraw()
49 {
50 var worldBuilder = new WorldBuilder();
51 worldBuilder.RegisterDrawLayer(2);
52
53 var renderer = worldBuilder.AddOrderedRenderer(new CalledRenderer());
54
55 AComponent aComponent;
56 CComponent cComponent;
57
58 var testDrawComponent = new TestDrawComponent { Layer = 2 };
59
60 var entity = worldBuilder.CreateEntity();
61 worldBuilder.SetComponent(entity, aComponent);
62 worldBuilder.SetComponent(entity, cComponent);
63 worldBuilder.SetComponent(entity, testDrawComponent);
64
65 var world = worldBuilder.Build();
66
67 world.Update(0.01f);
68 world.Draw();
69
70 Assert.IsTrue(calledOnDraw);
71 resultComponent.Should().BeEquivalentTo((testDrawComponent, entity));
72 }
73
74 [Reads(typeof(TestDrawComponent))]
75 class DestroyerEngine : Engine
76 {
77 public override void Update(double dt)
78 {
79 foreach (var entity in ReadEntities<TestDrawComponent>())
80 {
81 Destroy(entity);
82 }
83 }
84 }
85 [Test]
86 public void RenderMethodNotCalledAfterDestroy()
87 {
88 calledOnDraw = false;
89
90 var worldBuilder = new WorldBuilder();
91 worldBuilder.RegisterDrawLayer(1);
92
93 worldBuilder.AddEngine(new DestroyerEngine());
94 var renderer = worldBuilder.AddOrderedRenderer(new CalledRenderer());
95
96 TestDrawComponent testDrawComponent = new TestDrawComponent { Layer = 1 };
97
98 var entity = worldBuilder.CreateEntity();
99 worldBuilder.SetComponent(entity, testDrawComponent);
100
101 var world = worldBuilder.Build();
102
103 world.Update(0.01);
104 world.Draw();
105
106 Assert.IsFalse(calledOnDraw);
107 }
108 }
109 }
@@ -0,0 +1,46
1 using Encompass;
2 using NUnit.Framework;
3
4 namespace Tests
5 {
6 public class SpawnerTest
7 {
8 struct TestComponent : IComponent { }
9 struct SpawnMessageA : IMessage { }
10
11 static Entity resultEntity;
12
13 [Sends(typeof(SpawnMessageA))]
14 class MessageEmitter : Engine
15 {
16 public override void Update(double dt)
17 {
18 SendMessage(new SpawnMessageA());
19 }
20 }
21
22 [WritesImmediate(typeof(TestComponent))]
23 [Writes(typeof(TestComponent))]
24 class TestSpawner : Spawner<SpawnMessageA>
25 {
26 protected override void Spawn(in SpawnMessageA message)
27 {
28 resultEntity = CreateEntity();
29 SetComponent(resultEntity, new TestComponent());
30 Assert.Pass();
31 }
32 }
33
34 [Test]
35 public void RunsSpawnMethodOnMessageRead()
36 {
37 var worldBuilder = new WorldBuilder();
38 worldBuilder.AddEngine(new MessageEmitter());
39 worldBuilder.AddEngine(new TestSpawner());
40
41 var world = worldBuilder.Build();
42
43 world.Update(0.01);
44 }
45 }
46 }
This diff has been collapsed as it changes many lines, (614 lines changed) Show them Hide them
@@ -0,0 +1,614
1 using NUnit.Framework;
2
3 using Encompass;
4 using System.Collections.Generic;
5 using Encompass.Exceptions;
6 using System.Linq;
7 using FluentAssertions;
8 using System;
9
10 namespace Tests
11 {
12 public class WorldBuilderTest
13 {
14 public class EngineCycleSimple
15 {
16 struct AMessage : IMessage { }
17 struct BMessage : IMessage { }
18
19 [Receives(typeof(AMessage))]
20 [Sends(typeof(BMessage))]
21 class AEngine : Engine
22 {
23 public override void Update(double dt)
24 {
25 BMessage message;
26 this.SendMessage(message);
27 }
28 }
29
30 [Receives(typeof(BMessage))]
31 [Sends(typeof(AMessage))]
32 class BEngine : Engine
33 {
34 public override void Update(double dt)
35 {
36 AMessage message;
37 this.SendMessage(message);
38 }
39 }
40
41 [Test]
42 public void EngineCycle()
43 {
44 var worldBuilder = new WorldBuilder();
45 worldBuilder.AddEngine(new AEngine());
46 worldBuilder.AddEngine(new BEngine());
47
48 Assert.Throws<EngineCycleException>(() => worldBuilder.Build());
49 }
50 }
51
52 public class EngineCycleComplex
53 {
54 struct AMessage : IMessage { }
55 struct BMessage : IMessage { }
56 struct CMessage : IMessage { }
57 struct DMessage : IMessage { }
58
59 [Receives(typeof(AMessage))]
60 [Sends(typeof(BMessage))]
61 class AEngine : Engine
62 {
63 public override void Update(double dt)
64 {
65 BMessage message;
66 this.SendMessage(message);
67 }
68 }
69
70 [Receives(typeof(BMessage))]
71 [Sends(typeof(CMessage))]
72 class BEngine : Engine
73 {
74 public override void Update(double dt)
75 {
76 CMessage message;
77 this.SendMessage(message);
78 }
79 }
80
81 [Receives(typeof(CMessage))]
82 [Sends(typeof(DMessage))]
83 class CEngine : Engine
84 {
85 public override void Update(double dt)
86 {
87 DMessage message;
88 this.SendMessage(message);
89 }
90 }
91
92 [Receives(typeof(DMessage))]
93 [Sends(typeof(AMessage))]
94 class DEngine : Engine
95 {
96 public override void Update(double dt)
97 {
98 AMessage message;
99 this.SendMessage(message);
100 }
101 }
102
103 [Test]
104 public void EngineCycle()
105 {
106 var worldBuilder = new WorldBuilder();
107 worldBuilder.AddEngine(new AEngine());
108 worldBuilder.AddEngine(new BEngine());
109 worldBuilder.AddEngine(new CEngine());
110 worldBuilder.AddEngine(new DEngine());
111
112 Assert.Throws<EngineCycleException>(() => worldBuilder.Build());
113 }
114 }
115
116 public class MultipleEngineWriteConflict
117 {
118 struct AComponent { }
119
120 [Writes(typeof(AComponent))]
121 class AEngine : Engine
122 {
123 public override void Update(double dt) { }
124 }
125
126 [Writes(typeof(AComponent))]
127 class BEngine : Engine
128 {
129 public override void Update(double dt) { }
130 }
131
132 [Test]
133 public void EngineWriteConflictException()
134 {
135 var worldBuilder = new WorldBuilder();
136 worldBuilder.AddEngine(new AEngine());
137 worldBuilder.AddEngine(new BEngine());
138
139 Assert.Throws<EngineWriteConflictException>(() => worldBuilder.Build());
140 }
141 }
142
143 public class MultipleEngineWriteWithPriority
144 {
145 struct SetMessage : IMessage
146 {
147 public Entity entity;
148 }
149
150 struct AComponent : IComponent
151 {
152 public int myInt;
153 }
154
155 [Receives(typeof(SetMessage))]
156 [Writes(typeof(AComponent), 0)]
157 [WritesImmediate(typeof(AComponent))]
158 class AEngine : Engine
159 {
160 public override void Update(double dt)
161 {
162 foreach (ref readonly var setMessage in ReadMessages<SetMessage>())
163 {
164 SetComponent(setMessage.entity, new AComponent { myInt = 0 });
165 }
166 }
167 }
168
169 [Receives(typeof(SetMessage))]
170 [Writes(typeof(AComponent), 1)]
171 [WritesImmediate(typeof(AComponent))]
172 class BEngine : Engine
173 {
174 public override void Update(double dt)
175 {
176 foreach (ref readonly var setMessage in ReadMessages<SetMessage>())
177 {
178 SetComponent(setMessage.entity, new AComponent { myInt = 1 });
179 }
180 }
181 }
182
183 static AComponent resultComponent;
184
185 [ReadsImmediate(typeof(AComponent))]
186 class ReadComponentEngine : Engine
187 {
188 public override void Update(double dt)
189 {
190 resultComponent = ReadComponent<AComponent>();
191 }
192 }
193
194 [Test]
195 public void LowerPriorityWrites()
196 {
197 var worldBuilder = new WorldBuilder();
198 worldBuilder.AddEngine(new AEngine());
199 worldBuilder.AddEngine(new BEngine());
200
201 var entity = worldBuilder.CreateEntity();
202 worldBuilder.SendMessage(new SetMessage { entity = entity });
203
204 var world = worldBuilder.Build();
205
206 world.Update(0.01);
207
208 Assert.That(resultComponent.myInt, Is.EqualTo(0));
209 }
210 }
211
212 public class DefaultWritePriority
213 {
214 struct SetMessage : IMessage
215 {
216 public Entity entity;
217 }
218
219 struct AComponent : IComponent
220 {
221 public int myInt;
222 }
223
224 [Receives(typeof(SetMessage))]
225 [Writes(typeof(AComponent))]
226 [WritesImmediate(typeof(AComponent))]
227 [Encompass.DefaultWritePriority(4)]
228 class AEngine : Engine
229 {
230 public override void Update(double dt)
231 {
232 foreach (ref readonly var setMessage in ReadMessages<SetMessage>())
233 {
234 SetComponent(setMessage.entity, new AComponent { myInt = 5 });
235 }
236 }
237 }
238
239
240 [Receives(typeof(SetMessage))]
241 [Writes(typeof(AComponent), 3)]
242 [WritesImmediate(typeof(AComponent))]
243 class BEngine : Engine
244 {
245 public override void Update(double dt)
246 {
247 foreach (ref readonly var setMessage in ReadMessages<SetMessage>())
248 {
249 SetComponent(setMessage.entity, new AComponent { myInt = 1 });
250 }
251 }
252 }
253
254 [Receives(typeof(SetMessage))]
255 [Writes(typeof(AComponent), 2)]
256 [WritesImmediate(typeof(AComponent))]
257 class CEngine : Engine
258 {
259 public override void Update(double dt)
260 {
261 foreach (ref readonly var setMessage in ReadMessages<SetMessage>())
262 {
263 SetComponent(setMessage.entity, new AComponent { myInt = 3 });
264 }
265 }
266 }
267
268 static AComponent resultComponent;
269
270 [ReadsImmediate(typeof(AComponent))]
271 class ReadComponentEngine : Engine
272 {
273 public override void Update(double dt)
274 {
275 resultComponent = ReadComponent<AComponent>();
276 }
277 }
278
279 [Test]
280 public void LowerPriorityWrites()
281 {
282 var worldBuilder = new WorldBuilder();
283 worldBuilder.AddEngine(new AEngine());
284 worldBuilder.AddEngine(new BEngine());
285 worldBuilder.AddEngine(new CEngine());
286 worldBuilder.AddEngine(new ReadComponentEngine());
287
288 var entity = worldBuilder.CreateEntity();
289 worldBuilder.SendMessage(new SetMessage { entity = entity });
290
291 var world = worldBuilder.Build();
292
293 world.Update(0.01);
294
295 Assert.That(resultComponent.myInt, Is.EqualTo(3));
296 }
297 }
298
299 public class EngineMessageSelfCycle
300 {
301 struct AMessage : IMessage { }
302
303 [Receives(typeof(AMessage))]
304 [Sends(typeof(AMessage))]
305 class AEngine : Engine
306 {
307 public override void Update(double dt)
308 {
309
310 }
311 }
312
313 [Test]
314 public void ThrowsError()
315 {
316 var worldBuilder = new WorldBuilder();
317
318 Assert.Throws<EngineSelfCycleException>(() => worldBuilder.AddEngine(new AEngine()), "Engine both sends and receives Message AMessage");
319 }
320 }
321
322 public class IllegalWriteType
323 {
324 struct ANonMessage { }
325
326 [Sends(typeof(ANonMessage))]
327 class MyEngine : Engine
328 {
329 public override void Update(double dt)
330 {
331
332 }
333 }
334
335 [Test]
336 public void ThrowsError()
337 {
338 var worldBuilder = new WorldBuilder();
339
340 Assert.Throws<IllegalSendTypeException>(() => worldBuilder.AddEngine(new MyEngine()), "ANonMessage must be a Message or Component");
341 }
342 }
343
344 public class PriorityConflict
345 {
346 [Writes(typeof(MockComponent), 2)]
347 class AEngine : Engine
348 {
349 public override void Update(double dt)
350 {
351
352 }
353 }
354
355 [Writes(typeof(MockComponent), 2)]
356 class BEngine : Engine
357 {
358 public override void Update(double dt)
359 {
360
361 }
362 }
363
364 [Test]
365 public void PriorityConflictTest()
366 {
367 var worldBuilder = new WorldBuilder();
368 worldBuilder.AddEngine(new AEngine());
369 worldBuilder.AddEngine(new BEngine());
370
371 Assert.Throws<EngineWriteConflictException>(() => worldBuilder.Build());
372 }
373 }
374
375 public class EngineWriteConflict
376 {
377 [Writes(typeof(MockComponent))]
378 class AEngine : Engine
379 {
380 public override void Update(double dt)
381 {
382
383 }
384 }
385
386 [Writes(typeof(MockComponent), 2)]
387 class BEngine : Engine
388 {
389 public override void Update(double dt)
390 {
391
392 }
393 }
394
395 [Test]
396 public void EngineWriteConflictPriorityAndNoPriorityTest()
397 {
398 var worldBuilder = new WorldBuilder();
399 worldBuilder.AddEngine(new AEngine());
400 worldBuilder.AddEngine(new BEngine());
401
402 Assert.Throws<EngineWriteConflictException>(() => worldBuilder.Build());
403 }
404 }
405
406 public class LegalEngines
407 {
408 static List<Engine> order = new List<Engine>();
409
410 struct AComponent : IComponent { }
411 struct BComponent : IComponent { }
412
413 struct AMessage : IMessage { }
414 struct BMessage : IMessage { }
415 struct CMessage : IMessage { }
416 struct DMessage : IMessage { }
417
418 [Sends(typeof(AMessage))]
419 class AEngine : Engine
420 {
421 public override void Update(double dt)
422 {
423 order.Add(this);
424 }
425 }
426
427 [Sends(typeof(BMessage))]
428 class BEngine : Engine
429 {
430 public override void Update(double dt)
431 {
432 order.Add(this);
433 }
434 }
435
436 [Receives(typeof(AMessage), typeof(BMessage))]
437 [Sends(typeof(DMessage))]
438 class CEngine : Engine
439 {
440 public override void Update(double dt)
441 {
442 order.Add(this);
443 }
444 }
445
446 [Receives(typeof(DMessage))]
447 class DEngine : Engine
448 {
449 public override void Update(double dt)
450 {
451 order.Add(this);
452 }
453 }
454
455 [Test]
456 public void EngineOrder()
457 {
458 var worldBuilder = new WorldBuilder();
459
460 worldBuilder.AddEngine(new AEngine());
461 worldBuilder.AddEngine(new BEngine());
462 worldBuilder.AddEngine(new CEngine());
463 worldBuilder.AddEngine(new DEngine());
464
465 Assert.DoesNotThrow(() => worldBuilder.Build());
466
467 worldBuilder = new WorldBuilder();
468
469 var engineA = worldBuilder.AddEngine(new AEngine());
470 var engineB = worldBuilder.AddEngine(new BEngine());
471 var engineC = worldBuilder.AddEngine(new CEngine());
472 var engineD = worldBuilder.AddEngine(new DEngine());
473
474 var world = worldBuilder.Build();
475
476 world.Update(0.01f);
477
478 Assert.That(order.IndexOf(engineA), Is.LessThan(order.IndexOf(engineC)));
479 Assert.That(order.IndexOf(engineB), Is.LessThan(order.IndexOf(engineC)));
480 Assert.That(order.IndexOf(engineC), Is.LessThan(order.IndexOf(engineD)));
481 }
482
483 static AMessage[] resultMessages;
484
485 [Receives(typeof(AMessage))]
486 class ReadMessageEngine : Engine
487 {
488 public override void Update(double dt)
489 {
490 resultMessages = ReadMessages<AMessage>().ToArray();
491 }
492 }
493
494 [Test]
495 public void SendMessageDelayed()
496 {
497 resultMessages = Array.Empty<AMessage>();
498
499 var worldBuilder = new WorldBuilder();
500 worldBuilder.AddEngine(new ReadMessageEngine());
501
502 worldBuilder.SendMessage(new AMessage { }, 0.5);
503
504 var world = worldBuilder.Build();
505
506 resultMessages.Should().BeEmpty();
507
508 world.Update(0.25);
509
510 resultMessages.Should().BeEmpty();
511
512 world.Update(0.25);
513
514 resultMessages.Should().NotBeEmpty();
515 resultMessages.First().Should().BeOfType<AMessage>();
516 }
517 }
518
519 public class MultipleMessagesBetweenEngines
520 {
521 static List<Engine> order = new List<Engine>();
522
523 struct AMessage : IMessage { }
524 struct BMessage : IMessage { }
525
526 [Sends(typeof(AMessage), typeof(BMessage))]
527 class AEngine : Engine
528 {
529 public override void Update(double dt)
530 {
531 order.Add(this);
532 }
533 }
534
535 [Receives(typeof(AMessage), typeof(BMessage))]
536 class BEngine : Engine
537 {
538 public override void Update(double dt)
539 {
540 order.Add(this);
541 }
542 }
543
544 [Test]
545 public void WorldBuilderDoesNotThrowError()
546 {
547 var worldBuilder = new WorldBuilder();
548
549 var engineA = worldBuilder.AddEngine(new AEngine());
550 var engineB = worldBuilder.AddEngine(new BEngine());
551
552 Assert.DoesNotThrow(() => worldBuilder.Build());
553
554 var world = worldBuilder.Build();
555
556 world.Update(0.01f);
557
558 Assert.That(order.IndexOf(engineA), Is.LessThan(order.IndexOf(engineB)));
559 }
560 }
561
562 public class DrawLayerRegister
563 {
564 struct AComponent : IComponent, IDrawableComponent
565 {
566 public int Layer { get; }
567 }
568
569 struct BComponent : IComponent, IDrawableComponent
570 {
571 public int Layer { get => 3; }
572 }
573
574 class ARenderer : OrderedRenderer<AComponent>
575 {
576 public override void Render(Entity entity, in AComponent drawComponent) { }
577 }
578
579 class BRenderer : OrderedRenderer<BComponent>
580 {
581 public override void Render(Entity entity, in BComponent drawComponent) { }
582 }
583
584 [Test]
585 public void DrawLayerRegisterAfterOrderedRendererRegisterThrows()
586 {
587 var worldBuilder = new WorldBuilder();
588
589 var rendererA = worldBuilder.AddOrderedRenderer(new ARenderer());
590
591 Assert.Throws<IllegalDrawLayerException>(() => worldBuilder.RegisterDrawLayer(1));
592 }
593
594 [Test]
595 public void DrawLayerRegisterBeforeOrderedRendererDoesNotThrow()
596 {
597 var worldBuilder = new WorldBuilder();
598
599 Assert.DoesNotThrow(() => worldBuilder.RegisterDrawLayer(1));
600 Assert.DoesNotThrow(() => worldBuilder.AddOrderedRenderer(new ARenderer()));
601 }
602
603 [Test]
604 public void DrawLayerWithProperty()
605 {
606 var worldBuilder = new WorldBuilder();
607
608 var rendererB = worldBuilder.AddOrderedRenderer(new BRenderer());
609
610 Assert.DoesNotThrow(() => worldBuilder.Build());
611 }
612 }
613 }
614 }
@@ -0,0 +1,81
1 using NUnit.Framework;
2 using FluentAssertions;
3
4 using System;
5 using System.Collections.Generic;
6 using System.Text;
7
8 using Encompass;
9
10 namespace Tests
11 {
12 public class WorldTest
13 {
14 struct TestComponent : IComponent { }
15 struct TestDrawComponent : IComponent, IDrawableComponent
16 {
17 public int Layer { get; set; }
18 }
19
20 static List<object> drawOrder = new List<object>();
21
22 class TestEntityRenderer : OrderedRenderer<TestDrawComponent>
23 {
24 public override void Render(Entity entity, in TestDrawComponent testDrawComponent)
25 {
26 drawOrder.Add(entity);
27 }
28 }
29
30 class TestGeneralRenderer : GeneralRenderer
31 {
32 public override void Render()
33 {
34 drawOrder.Add(this);
35 }
36 }
37
38 [Test]
39 public void DrawOrder()
40 {
41 var worldBuilder = new WorldBuilder();
42 worldBuilder.RegisterDrawLayer(1);
43 worldBuilder.RegisterDrawLayer(2);
44 worldBuilder.RegisterDrawLayer(3);
45 worldBuilder.RegisterDrawLayer(4);
46 worldBuilder.RegisterDrawLayer(7);
47
48 worldBuilder.AddOrderedRenderer(new TestEntityRenderer());
49 var testGeneralRenderer = worldBuilder.AddGeneralRenderer(new TestGeneralRenderer(), 7);
50
51 TestComponent testComponent;
52 TestDrawComponent drawComponentThree = new TestDrawComponent { Layer = 3 };
53 var drawComponentTwo = new TestDrawComponent { Layer = 2 };
54 var drawComponentOne = new TestDrawComponent { Layer = 1 };
55 var drawComponentFour = new TestDrawComponent { Layer = 4 };
56
57 var entity = worldBuilder.CreateEntity();
58 worldBuilder.SetComponent(entity, testComponent);
59 worldBuilder.SetComponent(entity, drawComponentThree);
60
61 var entityTwo = worldBuilder.CreateEntity();
62 worldBuilder.SetComponent(entityTwo, testComponent);
63 worldBuilder.SetComponent(entityTwo, drawComponentTwo);
64
65 var entityThree = worldBuilder.CreateEntity();
66 worldBuilder.SetComponent(entityThree, testComponent);
67 worldBuilder.SetComponent(entityThree, drawComponentThree);
68
69 var entityFour = worldBuilder.CreateEntity();
70 worldBuilder.SetComponent(entityFour, testComponent);
71 worldBuilder.SetComponent(entityFour, drawComponentFour);
72
73 var world = worldBuilder.Build();
74
75 world.Update(0.01f);
76 world.Draw();
77
78 drawOrder.Should().BeEquivalentTo(entityFour, entityTwo, entity, entityThree, testGeneralRenderer);
79 }
80 }
81 }
@@ -0,0 +1,19
1 <?xml version="1.0" encoding="utf-8"?>
2 <Project Sdk="Microsoft.NET.Sdk">
3 <PropertyGroup>
4 <TargetFramework>netcoreapp3.0</TargetFramework>
5 <IsPackable>false</IsPackable>
6 <RootNamespace>Tests</RootNamespace>
7 <AssemblyName>EncompassECS.Framework.Tests</AssemblyName>
8 <AllowUnsafeBlocks>true</AllowUnsafeBlocks>
9 </PropertyGroup>
10 <ItemGroup>
11 <PackageReference Include="FluentAssertions" Version="5.7.0" />
12 <PackageReference Include="nunit" Version="3.11.0" />
13 <PackageReference Include="NUnit3TestAdapter" Version="3.11.0" />
14 <PackageReference Include="Microsoft.NET.Test.Sdk" Version="15.9.0" />
15 </ItemGroup>
16 <ItemGroup>
17 <ProjectReference Include="..\encompass-cs\encompass-cs.csproj" />
18 </ItemGroup>
19 </Project>
1 NO CONTENT: modified file, binary diff hidden
NO CONTENT: modified file, binary diff hidden
1 NO CONTENT: modified file, binary diff hidden
NO CONTENT: modified file, binary diff hidden
1 NO CONTENT: modified file, binary diff hidden
NO CONTENT: modified file, binary diff hidden
1 NO CONTENT: modified file, binary diff hidden
NO CONTENT: modified file, binary diff hidden
@@ -558,9 +558,9
558 "project": {
558 "project": {
559 "version": "1.0.0",
559 "version": "1.0.0",
560 "restore": {
560 "restore": {
561 "projectUniqueName": "/Users/alys/repos/isometric-park-fna/SpriteFontPlus/src/SpriteFontPlus.FNA.Core.csproj",
561 "projectUniqueName": "/Users/alys/repos/isometric-park-fna/SpriteFontPlus/src/SpriteFontPlus.FNA.csproj",
562 "projectName": "SpriteFontPlus",
562 "projectName": "SpriteFontPlus",
563 "projectPath": "/Users/alys/repos/isometric-park-fna/SpriteFontPlus/src/SpriteFontPlus.FNA.Core.csproj",
563 "projectPath": "/Users/alys/repos/isometric-park-fna/SpriteFontPlus/src/SpriteFontPlus.FNA.csproj",
564 "packagesPath": "/Users/alys/.nuget/packages/",
564 "packagesPath": "/Users/alys/.nuget/packages/",
565 "outputPath": "/Users/alys/repos/isometric-park-fna/SpriteFontPlus/src/obj/",
565 "outputPath": "/Users/alys/repos/isometric-park-fna/SpriteFontPlus/src/obj/",
566 "projectStyle": "PackageReference",
566 "projectStyle": "PackageReference",
@@ -579,16 +579,8
579 "net45": {
579 "net45": {
580 "targetAlias": "net45",
580 "targetAlias": "net45",
581 "projectReferences": {
581 "projectReferences": {
582 "/Users/alys/repos/isometric-park-fna/FNA/FNA.Core.csproj": {
582 "/Users/alys/repos/isometric-park-fna/FNA/FNA.csproj": {
583 "projectPath": "/Users/alys/repos/isometric-park-fna/FNA/FNA.Core.csproj"
583 "projectPath": "/Users/alys/repos/isometric-park-fna/FNA/FNA.csproj"
584 }
585 }
586 },
587 "netstandard2.0": {
588 "targetAlias": "netstandard2.0",
589 "projectReferences": {
590 "/Users/alys/repos/isometric-park-fna/FNA/FNA.Core.csproj": {
591 "projectPath": "/Users/alys/repos/isometric-park-fna/FNA/FNA.Core.csproj"
592 }
584 }
593 }
585 }
594 }
586 }
@@ -601,39 +593,8
601 },
593 },
602 "frameworks": {
594 "frameworks": {
603 "net45": {
595 "net45": {
604 "targetAlias": "net45",
596 "runtimeIdentifierGraphPath": "/Library/Frameworks/Mono.framework/Versions/6.12.0/lib/mono/msbuild/Current/bin/RuntimeIdentifierGraph.json"
605 "dependencies": {
606 "Microsoft.NETFramework.ReferenceAssemblies": {
607 "suppressParent": "All",
608 "target": "Package",
609 "version": "[1.0.0, )",
610 "autoReferenced": true
611 }
612 },
613 "runtimeIdentifierGraphPath": "/usr/local/share/dotnet/sdk/5.0.201/RuntimeIdentifierGraph.json"
614 },
615 "netstandard2.0": {
616 "targetAlias": "netstandard2.0",
617 "dependencies": {
618 "NETStandard.Library": {
619 "suppressParent": "All",
620 "target": "Package",
621 "version": "[2.0.3, )",
622 "autoReferenced": true
623 }
624 },
625 "imports": [
626 "net461",
627 "net462",
628 "net47",
629 "net471",
630 "net472",
631 "net48"
632 ],
633 "assetTargetFallback": true,
634 "warn": true,
635 "runtimeIdentifierGraphPath": "/usr/local/share/dotnet/sdk/5.0.201/RuntimeIdentifierGraph.json"
636 }
597 }
637 }
598 }
638 }
599 }
639 } No newline at end of file
600 }
@@ -1,13 +1,8
1 {
1 {
2 "version": 2,
2 "version": 2,
3 "dgSpecHash": "AvKmYKc88bJckphAgHXheUIHeesAncWXbgk6/PQVuxOzmKwDSFnsyg9u++EKNsxOlDt4tDOOVkyfDMjvYvNCKQ==",
3 "dgSpecHash": "kUPsWi+W/I8KOuiF0fQ9nWh1vVsW3b0wAQVjldBK6JNRF3usvNENwsXcLXddNG6Wh/129HHhkfaanQmLUK7wcA==",
4 "success": true,
4 "success": true,
5 "projectFilePath": "/Users/alys/repos/isometric-park-fna/SpriteFontPlus/src/SpriteFontPlus.FNA.Core.csproj",
5 "projectFilePath": "/Users/alys/repos/isometric-park-fna/SpriteFontPlus/src/SpriteFontPlus.FNA.csproj",
6 "expectedPackageFiles": [
6 "expectedPackageFiles": [],
7 "/Users/alys/.nuget/packages/microsoft.netcore.platforms/1.1.0/microsoft.netcore.platforms.1.1.0.nupkg.sha512",
8 "/Users/alys/.nuget/packages/microsoft.netframework.referenceassemblies/1.0.0/microsoft.netframework.referenceassemblies.1.0.0.nupkg.sha512",
9 "/Users/alys/.nuget/packages/microsoft.netframework.referenceassemblies.net45/1.0.0/microsoft.netframework.referenceassemblies.net45.1.0.0.nupkg.sha512",
10 "/Users/alys/.nuget/packages/netstandard.library/2.0.3/netstandard.library.2.0.3.nupkg.sha512"
11 ],
12 "logs": []
7 "logs": []
13 } No newline at end of file
8 }
@@ -17,12 +17,10
17 using isometricparkfna.Utils;
17 using isometricparkfna.Utils;
18 using isometricparkfna.UI;
18 using isometricparkfna.UI;
19
19
20
21
22
23 using ImGuiNET.SampleProgram.XNA;
20 using ImGuiNET.SampleProgram.XNA;
24 using ImGuiNET;
21 using ImGuiNET;
25 using TraceryNet;
22 using TraceryNet;
23 using Encompass;
26
24
27 class FNAGame : Game
25 class FNAGame : Game
28 {
26 {
@@ -84,6 +82,10
84 private bool showForest;
82 private bool showForest;
85 private bool showNews;
83 private bool showNews;
86
84
85 //Encompass
86 private WorldBuilder WorldBuilder = new WorldBuilder();
87 private World World;
88
87 private static void Main(string[] args)
89 private static void Main(string[] args)
88 {
90 {
89 #if NETCOREAPP
91 #if NETCOREAPP
@@ -173,6 +175,10
173
175
174 Line.initialize(GraphicsDevice);
176 Line.initialize(GraphicsDevice);
175
177
178 //let's see if this works:
179 // WorldBuilder = new WorldBuilder();
180 var dummy_entity = WorldBuilder.CreateEntity();
181 World = WorldBuilder.Build();
176
182
177 var bakedMono = TtfFontBaker.Bake(File.OpenRead(@"Content/iosevka-term-extendedmedium.ttf"),
183 var bakedMono = TtfFontBaker.Bake(File.OpenRead(@"Content/iosevka-term-extendedmedium.ttf"),
178 15,
184 15,
@@ -478,6 +484,7
478
484
479
485
480
486
487 World.Update(gameTime.ElapsedGameTime.TotalSeconds);
481 this.simulation.update(gameTime.ElapsedGameTime);
488 this.simulation.update(gameTime.ElapsedGameTime);
482
489
483 if (this.showBudget)
490 if (this.showBudget)
@@ -942,6 +949,8
942
949
943 #endregion debug_window
950 #endregion debug_window
944
951
952 World.Draw();
953
945
954
946 stopWatch.Stop();
955 stopWatch.Stop();
947 this.drawTime = stopWatch.Elapsed;
956 this.drawTime = stopWatch.Elapsed;
@@ -18,6 +18,8
18 <AllowUnsafeBlocks>true</AllowUnsafeBlocks>
18 <AllowUnsafeBlocks>true</AllowUnsafeBlocks>
19 <UseAppHost>true</UseAppHost>
19 <UseAppHost>true</UseAppHost>
20 <ApplicationIcon />
20 <ApplicationIcon />
21 <GenerateAssemblyInfo>false</GenerateAssemblyInfo>
22 <GenerateTargetFrameworkAttribute>false</GenerateTargetFrameworkAttribute>
21 </PropertyGroup>
23 </PropertyGroup>
22 <ItemGroup>
24 <ItemGroup>
23 <Content Include="Content\**\*.*">
25 <Content Include="Content\**\*.*">
@@ -28,6 +30,7
28 <ProjectReference Include="..\FNA\FNA.Core.csproj" />
30 <ProjectReference Include="..\FNA\FNA.Core.csproj" />
29 <!-- <ProjectReference Include="..\encompass-cs\encompass-cs\encompass-cs.csproj" /> -->
31 <!-- <ProjectReference Include="..\encompass-cs\encompass-cs\encompass-cs.csproj" /> -->
30 <ProjectReference Include="..\SpriteFontPlus\src\SpriteFontPlus.FNA.Core.csproj" />
32 <ProjectReference Include="..\SpriteFontPlus\src\SpriteFontPlus.FNA.Core.csproj" />
33 <ProjectReference Include="..\encompass-cs\encompass-cs\encompass-cs.csproj" />
31 </ItemGroup>
34 </ItemGroup>
32
35
33 <ItemGroup>
36 <ItemGroup>
@@ -38,6 +41,8
38 <Reference Include="YamlDotNet" Version="9.1.4">
41 <Reference Include="YamlDotNet" Version="9.1.4">
39 <HintPath>..\packages\YamlDotNet.9.1.4\lib\net452\YamlDotNet.dll</HintPath>
42 <HintPath>..\packages\YamlDotNet.9.1.4\lib\net452\YamlDotNet.dll</HintPath>
40 </Reference>
43 </Reference>
44 <!-- <Reference Include="YamlDotNet">
45 </Reference> -->
41 <Reference Include="Tracery.Net">
46 <Reference Include="Tracery.Net">
42 <HintPath>..\packages\Tracery.Net.1.0.0\lib\net452\Tracery.Net.dll</HintPath>
47 <HintPath>..\packages\Tracery.Net.1.0.0\lib\net452\Tracery.Net.dll</HintPath>
43 </Reference>
48 </Reference>
@@ -66,6 +66,7
66 <Name>ImGui.NET</Name>
66 <Name>ImGui.NET</Name>
67 <HintPath>..\packages\ImGui.NET.1.78.0\lib\netstandard2.0\ImGui.NET.dll</HintPath>
67 <HintPath>..\packages\ImGui.NET.1.78.0\lib\netstandard2.0\ImGui.NET.dll</HintPath>
68 </ProjectReference>
68 </ProjectReference>
69 <ProjectReference Include="..\encompass-cs\encompass-cs\encompass-cs.csproj" />
69 </ItemGroup>
70 </ItemGroup>
70 <ItemGroup>
71 <ItemGroup>
71 <None Include="Content\FNASound.wav">
72 <None Include="Content\FNASound.wav">
You need to be logged in to leave comments. Login now