Compare commits

...

56 Commits
0.20.0 ... main

Author SHA1 Message Date
cosmonaut 00614661ec fix broken test
continuous-integration/drone/push Build is passing Details
2021-03-23 17:50:05 -07:00
cosmonaut 36e98b0376 add dt and alpha params to renderers
continuous-integration/drone/push Build is failing Details
2021-03-23 17:37:47 -07:00
cosmonaut 6858ec1b0f Mega Refactor (#20)
continuous-integration/drone/push Build is passing Details
Co-Authored-By: cosmonaut <evan@moonside.games>
Co-Committed-By: cosmonaut <evan@moonside.games>
2021-03-12 08:25:25 +00:00
cosmonaut df0e251650 Merge pull request 'fix configuration' (#19) from fix_sln into main
continuous-integration/drone/push Build is passing Details
Reviewed-on: #19
2021-01-30 01:09:35 +00:00
cosmonaut 5183c04632 fix configuration
continuous-integration/drone/push Build is passing Details
continuous-integration/drone/pr Build is passing Details
2021-01-29 17:04:13 -08:00
cosmonaut d5186e6011 remove some dependencies (#18)
continuous-integration/drone/push Build is passing Details
Co-Authored-By: cosmonaut <evan@moonside.games>
Co-Committed-By: cosmonaut <evan@moonside.games>
2021-01-30 00:36:42 +00:00
cosmonaut 49ea9e7a5d 0.23.0 (#16)
continuous-integration/drone/push Build is passing Details
continuous-integration/drone/tag Build is passing Details
2020-11-16 07:24:37 +00:00
cosmonaut 40e6d75638 Expose some enumerable iterators + some tweaks (#15)
continuous-integration/drone/push Build is passing Details
2020-11-16 07:21:51 +00:00
cosmonaut 327a458f17 Update 'encompass-cs/encompass-cs.csproj'
continuous-integration/drone/pr Build is passing Details
continuous-integration/drone/push Build is passing Details
continuous-integration/drone/tag Build is passing Details
2020-07-22 01:10:45 +00:00
Evan Hemsley d661ed5b17 Spawner.Spawn now requires in param
continuous-integration/drone/pr Build is passing Details
continuous-integration/drone/push Build is passing Details
2020-07-21 18:05:47 -07:00
cosmonaut 1d458e3563 0.21.3
continuous-integration/drone/pr Build is passing Details
continuous-integration/drone/push Build is passing Details
continuous-integration/drone/tag Build is passing Details
2020-07-20 03:18:12 +00:00
cosmonaut b4a5a4c66a Merge pull request 'fix silent failure when performing singular reads' (#11) from SingularReadFix into main
continuous-integration/drone/push Build is passing Details
Reviewed-on: #11
2020-07-20 03:12:45 +00:00
Evan Hemsley a5fd322791 fix silent failure when performing singular reads
continuous-integration/drone/push Build is passing Details
continuous-integration/drone/pr Build is passing Details
2020-07-19 20:10:38 -07:00
cosmonaut 94e81a0258 0.21.2
continuous-integration/drone/tag Build is passing Details
continuous-integration/drone/push Build is passing Details
2020-07-16 07:15:08 +00:00
Evan Hemsley b234b9bad1 0.21.2
continuous-integration/drone/push Build is passing Details
continuous-integration/drone/pr Build is passing Details
2020-07-16 00:12:31 -07:00
cosmonaut 703e088430 fix IDrawableComponent throwing if non auto property used for Layer (#9)
continuous-integration/drone/push Build is passing Details
2020-07-16 07:10:21 +00:00
cosmonaut 59014c7a9e QueryWith and QueryWithout implicitly Read (#8)
continuous-integration/drone/push Build is passing Details
2020-07-12 22:17:38 +00:00
cosmonaut e76161c8af license (#7)
continuous-integration/drone/push Build is passing Details
2020-07-10 19:49:17 +00:00
cosmonaut f82c925233 Merge pull request '0.21.1' (#6) from 0.21.1 into master
continuous-integration/drone/push Build is passing Details
continuous-integration/drone/tag Build is passing Details
2020-07-10 19:41:41 +00:00
Evan Hemsley 154c26a39c 0.21.1
continuous-integration/drone/push Build is passing Details
continuous-integration/drone/pr Build is passing Details
2020-07-10 12:40:23 -07:00
cosmonaut bb2a678853 Merge pull request 'throw if RegisterDrawLayer called after adding renderer' (#5) from DrawLayerValidation into master
continuous-integration/drone/push Build is passing Details
2020-07-10 19:39:17 +00:00
Evan Hemsley a4be308f41 throw if RegisterDrawLayer called after adding renderer
continuous-integration/drone/push Build is passing Details
continuous-integration/drone/pr Build is passing Details
2020-07-10 12:37:10 -07:00
Evan Hemsley 72e9566041 0.21.0
continuous-integration/drone/push Build is passing Details
continuous-integration/drone/tag Build is passing Details
2020-07-05 00:06:37 -07:00
cosmonaut 428cccecee Merge pull request 'fix reflection breaking on .net framework' (#4) from fix_framework into master
continuous-integration/drone/push Build is passing Details
2020-03-25 21:28:22 +00:00
Evan Hemsley 045b0917ba fix reflection breaking on .net framework
continuous-integration/drone/push Build is passing Details
continuous-integration/drone/pr Build is passing Details
2020-03-25 14:19:40 -07:00
cosmonaut 8f200fd56f Merge pull request 'Storage Optimizations' (#3) from component_store_optimize into master
continuous-integration/drone/push Build is passing Details
2020-03-25 19:08:04 +00:00
Evan Hemsley 18fd4ceaa6 remove mutable get
continuous-integration/drone/push Build is passing Details
continuous-integration/drone/pr Build is passing Details
2020-03-25 11:56:56 -07:00
Evan Hemsley df4a953628 remove unnecessary lookup checks
continuous-integration/drone/push Build is passing Details
2020-03-25 11:52:42 -07:00
Evan Hemsley 20d382f51a JIT Renderer methods
continuous-integration/drone/push Build is passing Details
2020-03-24 22:19:24 -07:00
Evan Hemsley 0df0916347 update JIT preload
continuous-integration/drone/push Build is passing Details
2020-03-24 21:28:56 -07:00
Evan Hemsley 7c8154efdd fix some reads
continuous-integration/drone/push Build is passing Details
2020-03-22 19:20:55 -07:00
Evan Hemsley ca9c9c84a3 getting rid of tuple returns
continuous-integration/drone/push Build is passing Details
2020-03-22 19:10:28 -07:00
Evan Hemsley d5a45d6419 fix bug in delta store + storage index map is now an array
continuous-integration/drone/push Build is passing Details
2020-03-22 17:18:23 -07:00
Evan Hemsley 61845d752a contiguous component storage
continuous-integration/drone/push Build is passing Details
2020-03-22 17:03:18 -07:00
Evan Hemsley e4d4df9fdd resizeable message store 2020-03-22 16:07:58 -07:00
Evan Hemsley 5bc0294158 buhhhh
continuous-integration/drone/push Build is passing Details
2020-03-22 14:24:59 -07:00
Evan Hemsley a00735650a update another ref readonly
continuous-integration/drone/push Build is passing Details
2020-03-22 14:21:59 -07:00
Evan Hemsley 26216a295f singular message reads are ref readonly
continuous-integration/drone/push Build is passing Details
2020-03-22 14:20:22 -07:00
Evan Hemsley 0d969322ad trying PrepareMethod instead of Invoke
continuous-integration/drone/push Build is passing Details
2020-03-22 14:04:09 -07:00
Evan Hemsley 5a536e6123 optimize singular entity message read
continuous-integration/drone/push Build is passing Details
2020-03-22 13:53:23 -07:00
Evan Hemsley 971993f605 update ReadMessages loops to use ref readonly
continuous-integration/drone/push Build is passing Details
2020-03-22 13:45:28 -07:00
Evan Hemsley cdc68e46e5 rework ReadMessages to return a Span
continuous-integration/drone/push Build is passing Details
2020-03-22 13:41:55 -07:00
Evan Hemsley 67bc55e780 actually free IDs on message clear
continuous-integration/drone/push Build is passing Details
2020-03-22 12:14:57 -07:00
Evan Hemsley 002cff1c2e rework typed message store
continuous-integration/drone/push Build is passing Details
2020-03-22 12:12:26 -07:00
Evan Hemsley e3d8f42bd9 fix not allowing same engine to write twice
continuous-integration/drone/push Build is passing Details
2020-03-21 22:53:05 -07:00
Evan Hemsley 150043b0e5 fix bugs in component storage and draw layer manager
continuous-integration/drone/push Build is failing Details
2020-03-21 22:19:31 -07:00
Evan Hemsley 4178e8f02c rework delta store to not use inheritance
continuous-integration/drone/push Build is passing Details
2020-03-21 20:29:15 -07:00
Evan Hemsley 812fe10ca1 remove unmanaged constraint from component storage
continuous-integration/drone/push Build is passing Details
2020-03-21 17:38:35 -07:00
Evan Hemsley 255760a3d7 unmanaged component store
continuous-integration/drone/push Build is passing Details
2020-03-20 18:09:24 -07:00
Evan Hemsley 408dd9bfeb unmanaged structs
continuous-integration/drone/push Build is passing Details
2020-03-20 15:45:58 -07:00
Evan Hemsley 6d699e4b17 passing draw components by ref
continuous-integration/drone/push Build is passing Details
2020-03-20 14:13:26 -07:00
Evan Hemsley b6ae9eaebc more cleanup
continuous-integration/drone/push Build is passing Details
2020-03-20 02:28:10 -07:00
Evan Hemsley 63e9ca63c0 get rid of component stores on DrawLayerManager
continuous-integration/drone/push Build is passing Details
2020-03-20 00:58:33 -07:00
Evan Hemsley 7f89e9b4a0 fix style issues
continuous-integration/drone/push Build is passing Details
2020-03-20 00:09:57 -07:00
Evan Hemsley ffc052ded3 fix creating new component index on every set
continuous-integration/drone/push Build is passing Details
2020-03-19 17:26:22 -07:00
Evan Hemsley 98955f3048 converting component storage to pack data and return refs
continuous-integration/drone/push Build is passing Details
2020-03-19 15:44:04 -07:00
59 changed files with 1914 additions and 2070 deletions

View File

@ -7,18 +7,18 @@
steps:
- name: test
image: mcr.microsoft.com/dotnet/core/sdk:3.1
image: mcr.microsoft.com/dotnet/sdk:5.0.102-ca-patch-buster-slim
commands:
- dotnet build -c Release
- dotnet test -c Release
- name: deploy
image: mcr.microsoft.com/dotnet/core/sdk:3.1
image: mcr.microsoft.com/dotnet/sdk:5.0.102-ca-patch-buster-slim
environment:
API_KEY:
from_secret: API_KEY
commands:
- dotnet nuget push /build/encompass-cs/bin/Release/EncompassECS.Framework.*.nupkg -s https://api.nuget.org/v3/index.json -k $API_KEY
- dotnet nuget push /build/encompass-cs/bin/Release/EncompassECS.Framework.*.nupkg -s https://api.nuget.org/v3/index.json -k $API_KEY
when:
ref:
- refs/tags/*.*.*

424
LICENSE
View File

@ -1,415 +1,19 @@
encompass-cs
Copyright Evan Hemsley 2019
COOPERATIVE SOFTWARE LICENSE
Copyright (c) 2020 Evan Hemsley <evan@moonside.games>
THE WORK (AS DEFINED BELOW) IS PROVIDED UNDER THE TERMS OF THIS
COPYFARLEFT PUBLIC LICENSE ("LICENSE"). THE WORK IS PROTECTED BY
COPYRIGHT AND ALL OTHER APPLICABLE LAWS. ANY USE OF THE WORK OTHER THAN
AS AUTHORIZED UNDER THIS LICENSE OR COPYRIGHT LAW IS PROHIBITED. BY
EXERCISING ANY RIGHTS TO THE WORK PROVIDED IN THIS LICENSE, YOU AGREE
TO BE BOUND BY THE TERMS OF THIS LICENSE. TO THE EXTENT THIS LICENSE
MAY BE CONSIDERED TO BE A CONTRACT, THE LICENSOR GRANTS YOU THE RIGHTS
CONTAINED HERE IN AS CONSIDERATION FOR ACCEPTING THE TERMS AND
CONDITIONS OF THIS LICENSE AND FOR AGREEING TO BE BOUND BY THE TERMS
AND CONDITIONS OF THIS LICENSE.
This software is provided 'as-is', without any express or implied
warranty. In no event will the authors be held liable for any damages
arising from the use of this software.
1. DEFINITIONS
a. "Adaptation" means a work based upon the Work, or upon the
Work and other pre-existing works, such as a translation,
adaptation, derivative work, arrangement of music or other
alterations of a literary or artistic work, or phonogram or
performance and includes cinematographic adaptations or any
other form in which the Work may be recast, transformed, or
adapted including in any form recognizably derived from the
original, except that a work that constitutes a Collection will
not be considered an Adaptation for the purpose of this License.
For the avoidance of doubt, where the Work is a musical work,
performance or phonogram, the synchronization of the Work in
timed-relation with a moving image ("synching") will be
considered an Adaptation for the purpose of this License.
b. "Collection" means a collection of literary or artistic
works, such as encyclopedias and anthologies, or performances,
phonograms or broadcasts, or other works or subject matter other
than works listed in Section 1(f) below, which, by reason of the
selection and arrangement of their contents, constitute
intellectual creations, in which the Work is included in its
entirety in unmodified form along with one or more other
contributions, each constituting separate and independent works
in themselves, which together are assembled into a collective
whole. A work that constitutes a Collection will not be
considered an Adaptation (as defined above) for the purposes of
this License.
c. "Distribute" means to make available to the public the
original and copies of the Work or Adaptation, as appropriate,
through sale, gift or any other transfer of possession or
ownership.
d. "Licensor" means the individual, individuals, entity or
entities that offer(s) the Work under the terms of this License.
e. "Original Author" means, in the case of a literary or
artistic work, the individual, individuals, entity or entities
who created the Work or if no individual or entity can be
identified, the publisher; and in addition (i) in the case of a
performance the actors, singers, musicians, dancers, and other
persons who act, sing, deliver, declaim, play in, interpret or
otherwise perform literary or artistic works or expressions of
folklore; (ii) in the case of a phonogram the producer being the
person or legal entity who first fixes the sounds of a
performance or other sounds; and, (iii) in the case of
f. "Work" means the literary and/or artistic work offered under
the terms of this License including without limitation any
production in the literary, scientific and artistic domain,
whatever may be the mode or form of its expression including
digital form, such as a book, pamphlet and other writing; a
lecture, address, sermon or other work of the same nature; a
dramatic or dramatico-musical work; a choreographic work or
entertainment in dumb show; a musical composition with or
without words; a cinematographic work to which are assimilated
works expressed by a process analogous to cinematography; a work
of drawing, painting, architecture, sculpture, engraving or
lithography; a photographic work to which are assimilated works
expressed by a process analogous to photography; a work of
applied art; an illustration, map, plan, sketch or
three-dimensional work relative to geography, topography,
architecture or science; a performance; a broadcast; a
phonogram; a compilation of data to the extent it is protected
as a copyrightable work; or a work performed by a variety or
circus performer to the extent it is not otherwise considered a
literary or artistic work.
g. "You" means an individual or entity exercising rights under
this License who has not previously violated the terms of this
License with respect to the Work, or who has received express
permission from the Licensor to exercise rights under this
License despite a previous violation.
h. "Publicly Perform" means to perform public recitations of the
Work and to communicate to the public those public recitations,
by any means or process, including by wire or wireless means or
public digital performances; to make available to the public
Works in such a way that members of the public may access these
Works from a place and at a place individually chosen by them;
to perform the Work to the public by any means or process and
the communication to the public of the performances of the Work,
including by public digital performance; to broadcast and
rebroadcast the Work by any means including signs, sounds or
images.
i. "Reproduce" means to make copies of the Work by any means
including without limitation by sound or visual recordings and
the right of fixation and reproducing fixations of the Work,
including storage of a protected performance or phonogram in
digital form or other electronic medium.
j. "Software" means any digital Work which, through use of a
third-party piece of Software or through the direct usage of
itself on a computer system, the memory of the computer is
modified dynamically or semi-dynamically. "Software",
secondly, processes or interprets information.
k. "Source Code" means the human-readable form of Software
through which the Original Author and/or Distributor originally
created, derived, and/or modified it.
l. "Web Service" means the use of a piece of Software to
interpret or modify information that is subsequently and directly
served to users over the Internet.
2. FAIR DEALING RIGHTS
Nothing in this License is intended to reduce, limit, or restrict any
uses free from copyright or rights arising from limitations or
exceptions that are provided for in connection with the copyright
protection under copyright law or other applicable laws.
3. LICENSE GRANT
Subject to the terms and conditions of this License, Licensor hereby
grants You a worldwide, royalty-free, non-exclusive, perpetual (for the
duration of the applicable copyright) license to exercise the rights in
the Work as stated below:
a. to Reproduce the Work, to incorporate the Work into one or
more Collections, and to Reproduce the Work as incorporated in
the Collections;
b. to create and Reproduce Adaptations provided that any such
Adaptation, including any translation in any medium, takes
reasonable steps to clearly label, demarcate or otherwise
identify that changes were made to the original Work. For
example, a translation could be marked "The original work was
translated from English to Spanish," or a modification could
indicate "The original work has been modified.";
c. to Distribute and Publicly Perform the Work including as
incorporated in Collections; and,
d. to Distribute and Publicly Perform Adaptations. The above
rights may be exercised in all media and formats whether now
known or hereafter devised. The above rights include the right
to make such modifications as are technically necessary to
exercise the rights in other media and formats. Subject to
Section 8(g), all rights not expressly granted by Licensor are
hereby reserved, including but not limited to the rights set
forth in Section 4(h).
4. RESTRICTIONS
The license granted in Section 3 above is expressly made subject to and
limited by the following restrictions:
a. You may Distribute or Publicly Perform the Work only under
the terms of this License. You must include a copy of, or the
Uniform Resource Identifier (URI) for, this License with every
copy of the Work You Distribute or Publicly Perform. You may not
offer or impose any terms on the Work that restrict the terms of
this License or the ability of the recipient of the Work to
exercise the rights granted to that recipient under the terms of
the License. You may not sublicense the Work. You must keep
intact all notices that refer to this License and to the
disclaimer of warranties with every copy of the Work You
Distribute or Publicly Perform. When You Distribute or Publicly
Perform the Work, You may not impose any effective technological
measures on the Work that restrict the ability of a recipient of
the Work from You to exercise the rights granted to that
recipient under the terms of the License. This Section 4(a)
applies to the Work as incorporated in a Collection, but this
does not require the Collection apart from the Work itself to be
made subject to the terms of this License. If You create a
Collection, upon notice from any Licensor You must, to the
extent practicable, remove from the Collection any credit as
required by Section 4(f), as requested. If You create an
Adaptation, upon notice from any Licensor You must, to the
extent practicable, remove from the Adaptation any credit as
required by Section 4(f), as requested.
b. Subject to the exception in Section 4(e), you may not
exercise any of the rights granted to You in Section 3 above in
any manner that is primarily intended for or directed toward
commercial advantage or private monetary compensation. The
exchange of the Work for other copyrighted works by means of
digital file-sharing or otherwise shall not be considered to be
intended for or directed toward commercial advantage or private
monetary compensation, provided there is no payment of any
monetary compensation in connection with the exchange of
copyrighted works.
c. If the Work meets the definition of Software, You may exercise
the rights granted in Section 3 only if You provide a copy of the
corresponding Source Code from which the Work was derived in digital
form, or You provide a URI for the corresponding Source Code of
the Work, to any recipients upon request.
d. If the Work is used as or for a Web Service, You may exercise
the rights granted in Section 3 only if You provide a copy of the
corresponding Source Code from which the Work was derived in digital
form, or You provide a URI for the corresponding Source Code to the
Work, to any recipients of the data served or modified by the Web
Service.
e. You may exercise the rights granted in Section 3 for
commercial purposes only if you statisy any of the following:
i. You are a worker-owned business or worker-owned
collective; and
ii. after tax, all financial gain, surplus, profits and
benefits produced by the business or collective are
distributed among the worker-owners
iii. You are not using such rights on behalf of a business
other than those specified in 4(e.i) and elaborated upon in
4(e.ii), nor are using such rights as a proxy on behalf of a
business with the intent to circumvent the aformentioned
restrictions on such a business.
f. Any use by a business that is privately owned and managed,
and that seeks to generate profit from the labor of employees
paid by salary or other wages, is not permitted under this
license.
g. If You Distribute, or Publicly Perform the Work or any
Adaptations or Collections, You must, unless a request has been
made pursuant to Section 4(a), keep intact all copyright notices
for the Work and provide, reasonable to the medium or means You
are utilizing: (i) the name of the Original Author (or
pseudonym, if applicable) if supplied, and/or if the Original
Author and/or Licensor designate another party or parties (e.g.,
a sponsor institute, publishing entity, journal) for attribution
("Attribution Parties") in Licensor!s copyright notice, terms of
service or by other reasonable means, the name of such party or
parties; (ii) the title of the Work if supplied; (iii) to the
extent reasonably practicable, the URI, if any, that Licensor
specifies to be associated with the Work, unless such URI does
not refer to the copyright notice or licensing information for
the Work; and, (iv) consistent with Section 3(b), in the case of
an Adaptation, a credit identifying the use of the Work in the
Adaptation (e.g., "French translation of the Work by Original
Author," or "Screenplay based on original Work by Original
Author"). The credit required by this Section 4(f) may be
implemented in any reasonable manner; provided, however, that in
the case of a Adaptation or Collection, at a minimum such credit
will appear, if a credit for all contributing authors of the
Adaptation or Collection appears, then as part of these credits
and in a manner at least as prominent as the credits for the
other contributing authors. For the avoidance of doubt, You may
only use the credit required by this Section for the purpose of
attribution in the manner set out above and, by exercising Your
rights under this License, You may not implicitly or explicitly
assert or imply any connection with, sponsorship or endorsement
by the Original Author, Licensor and/or Attribution Parties, as
appropriate, of You or Your use of the Work, without the
separate, express prior written permission of the Original
Author, Licensor and/or Attribution Parties.
h. For the avoidance of doubt:
i. Non-waivable Compulsory License Schemes. In those
jurisdictions in which the right to collect royalties
through any statutory or compulsory licensing scheme
cannot be waived, the Licensor reserves the exclusive
right to collect such royalties for any exercise by You of
the rights granted under this License;
ii. Waivable Compulsory License Schemes. In those
jurisdictions in which the right to collect royalties
through any statutory or compulsory licensing scheme can
be waived, the Licensor reserves the exclusive right to
collect such royalties for any exercise by You of the
rights granted under this License if Your exercise of such
rights is for a purpose or use which is otherwise than
noncommercial as permitted under Section 4(b) and
otherwise waives the right to collect royalties through
any statutory or compulsory licensing scheme; and,
iii.Voluntary License Schemes. The Licensor reserves the
right to collect royalties, whether individually or, in
the event that the Licensor is a member of a collecting
society that administers voluntary licensing schemes, via
that society, from any exercise by You of the rights
granted under this License that is for a purpose or use
which is otherwise than noncommercial as permitted under
Section 4(b).
i. Except as otherwise agreed in writing by the Licensor or as
may be otherwise permitted by applicable law, if You Reproduce,
Distribute or Publicly Perform the Work either by itself or as
part of any Adaptations or Collections, You must not distort,
mutilate, modify or take other derogatory action in relation to
the Work which would be prejudicial to the Original Author's
honor or reputation. Licensor agrees that in those jurisdictions
(e.g. Japan), in which any exercise of the right granted in
Section 3(b) of this License (the right to make Adaptations)
would be deemed to be a distortion, mutilation, modification or
other derogatory action prejudicial to the Original Author's
honor and reputation, the Licensor will waive or not assert, as
appropriate, this Section, to the fullest extent permitted by
the applicable national law, to enable You to reasonably
exercise Your right under Section 3(b) of this License (right to
make Adaptations) but not otherwise.
5. REPRESENTATIONS, WARRANTIES AND DISCLAIMER
UNLESS OTHERWISE MUTUALLY AGREED TO BY THE PARTIES IN WRITING, LICENSOR
OFFERS THE WORK AS-IS AND MAKES NO REPRESENTATIONS OR WARRANTIES OF ANY
KIND CONCERNING THE WORK, EXPRESS, IMPLIED, STATUTORY OR OTHERWISE,
INCLUDING, WITHOUT LIMITATION, WARRANTIES OF TITLE, MERCHANTIBILITY,
FITNESS FOR A PARTICULAR PURPOSE, NONINFRINGEMENT, OR THE ABSENCE OF
LATENT OR OTHER DEFECTS, ACCURACY, OR THE PRESENCE OF ABSENCE OF
ERRORS, WHETHER OR NOT DISCOVERABLE. SOME JURISDICTIONS DO NOT ALLOW
THE EXCLUSION OF IMPLIED WARRANTIES, SO SUCH EXCLUSION MAY NOT APPLY TO
YOU.
6. LIMITATION ON LIABILITY
EXCEPT TO THE EXTENT REQUIRED BY APPLICABLE LAW, IN NO EVENT WILL
LICENSOR BE LIABLE TO YOU ON ANY LEGAL THEORY FOR ANY SPECIAL,
INCIDENTAL, CONSEQUENTIAL, PUNITIVE OR EXEMPLARY DAMAGES ARISING OUT OF
THIS LICENSE OR THE USE OF THE WORK, EVEN IF LICENSOR HAS BEEN ADVISED
OF THE POSSIBILITY OF SUCH DAMAGES.
7. TERMINATION
a. This License and the rights granted hereunder will terminate
automatically upon any breach by You of the terms of this
License. Individuals or entities who have received Adaptations
or Collections from You under this License, however, will not
have their licenses terminated provided such individuals or
entities remain in full compliance with those licenses. Sections
1, 2, 5, 6, 7, and 8 will survive any termination of this
License.
b. Subject to the above terms and conditions, the license
granted here is perpetual (for the duration of the applicable
copyright in the Work). Notwithstanding the above, Licensor
reserves the right to release the Work under different license
terms or to stop distributing the Work at any time; provided,
however that any such election will not serve to withdraw this
License (or any other license that has been, or is required to
be, granted under the terms of this License), and this License
will continue in full force and effect unless terminated as
stated above.
8. MISCELLANEOUS
a. Each time You Distribute or Publicly Perform the Work or a
Collection, the Licensor offers to the recipient a license to
the Work on the same terms and conditions as the license granted
to You under this License.
b. Each time You Distribute or Publicly Perform an Adaptation,
Licensor offers to the recipient a license to the original Work
on the same terms and conditions as the license granted to You
under this License.
c. If the Work is classified as Software, each time You Distribute
or Publicly Perform an Adaptation, Licensor offers to the recipient
a copy and/or URI of the corresponding Source Code on the same
terms and conditions as the license granted to You under this License.
d. If the Work is used as a Web Service, each time You Distribute
or Publicly Perform an Adaptation, or serve data derived from the
Software, the Licensor offers to any recipients of the data a copy
and/or URI of the corresponding Source Code on the same terms and
conditions as the license granted to You under this License.
e. If any provision of this License is invalid or unenforceable
under applicable law, it shall not affect the validity or
enforceability of the remainder of the terms of this License,
and without further action by the parties to this agreement,
such provision shall be reformed to the minimum extent necessary
to make such provision valid and enforceable.
f. No term or provision of this License shall be deemed waived
and no breach consented to unless such waiver or consent shall
be in writing and signed by the party to be charged with such
waiver or consent.
g. This License constitutes the entire agreement between the
parties with respect to the Work licensed here. There are no
understandings, agreements or representations with respect to
the Work not specified here. Licensor shall not be bound by any
additional provisions that may appear in any communication from
You. This License may not be modified without the mutual written
agreement of the Licensor and You.
h. The rights granted under, and the subject matter referenced,
in this License were drafted utilizing the terminology of the
Berne Convention for the Protection of Literary and Artistic
Works (as amended on September 28, 1979), the Rome Convention of
1961, the WIPO Copyright Treaty of 1996, the WIPO Performances
and Phonograms Treaty of 1996 and the Universal Copyright
Convention (as revised on July 24, 1971). These rights and
subject matter take effect in the relevant jurisdiction in which
the License terms are sought to be enforced according to the
corresponding provisions of the implementation of those treaty
provisions in the applicable national law. If the standard suite
of rights granted under applicable copyright law includes
additional rights not granted under this License, such
additional rights are deemed to be included in the License; this
License is not intended to restrict the license of any rights
under applicable law.
Permission is granted to anyone to use this software for any purpose,
including commercial applications, and to alter it and redistribute it
freely, subject to the following restrictions:
1. The origin of this software must not be misrepresented; you must not
claim that you wrote the original software. If you use this software
in a product, an acknowledgment in the product documentation would be
appreciated but is not required.
2. Altered source versions must be plainly marked as such, and must not be
misrepresented as being the original software.
3. This notice may not be removed or altered from any source distribution.

7
TODO
View File

@ -1,7 +0,0 @@
- immutable component system?
- method to remove all components of a type without destroying Entities
- method to remove a component of a type without destroying entity
- look at test coverage
- docs

View File

@ -1,7 +1,7 @@

Microsoft Visual Studio Solution File, Format Version 12.00
# Visual Studio 15
VisualStudioVersion = 15.0.26124.0
# Visual Studio Version 16
VisualStudioVersion = 16.0.30717.126
MinimumVisualStudioVersion = 15.0.26124.0
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "encompass-cs", "encompass-cs\encompass-cs.csproj", "{B862FC25-0740-4CEA-BC53-3C5F43DCD985}"
EndProject
@ -9,38 +9,28 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "test", "test\test.csproj",
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
Debug|x64 = Debug|x64
Debug|x86 = Debug|x86
Release|Any CPU = Release|Any CPU
Release|x64 = Release|x64
Release|x86 = Release|x86
EndGlobalSection
GlobalSection(ProjectConfigurationPlatforms) = postSolution
{B862FC25-0740-4CEA-BC53-3C5F43DCD985}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{B862FC25-0740-4CEA-BC53-3C5F43DCD985}.Debug|Any CPU.Build.0 = Debug|Any CPU
{B862FC25-0740-4CEA-BC53-3C5F43DCD985}.Debug|x64.ActiveCfg = Debug|Any CPU
{B862FC25-0740-4CEA-BC53-3C5F43DCD985}.Debug|x64.Build.0 = Debug|Any CPU
{B862FC25-0740-4CEA-BC53-3C5F43DCD985}.Debug|x86.ActiveCfg = Debug|Any CPU
{B862FC25-0740-4CEA-BC53-3C5F43DCD985}.Debug|x86.Build.0 = Debug|Any CPU
{B862FC25-0740-4CEA-BC53-3C5F43DCD985}.Release|Any CPU.ActiveCfg = Release|Any CPU
{B862FC25-0740-4CEA-BC53-3C5F43DCD985}.Release|Any CPU.Build.0 = Release|Any CPU
{B862FC25-0740-4CEA-BC53-3C5F43DCD985}.Release|x64.ActiveCfg = Release|Any CPU
{B862FC25-0740-4CEA-BC53-3C5F43DCD985}.Release|x64.Build.0 = Release|Any CPU
{B862FC25-0740-4CEA-BC53-3C5F43DCD985}.Release|x86.ActiveCfg = Release|Any CPU
{B862FC25-0740-4CEA-BC53-3C5F43DCD985}.Release|x86.Build.0 = Release|Any CPU
{23D6C29C-A310-4D3E-8455-8D0B8CA1FC06}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{23D6C29C-A310-4D3E-8455-8D0B8CA1FC06}.Debug|Any CPU.Build.0 = Debug|Any CPU
{23D6C29C-A310-4D3E-8455-8D0B8CA1FC06}.Debug|x64.ActiveCfg = Debug|Any CPU
{23D6C29C-A310-4D3E-8455-8D0B8CA1FC06}.Debug|x64.Build.0 = Debug|Any CPU
{23D6C29C-A310-4D3E-8455-8D0B8CA1FC06}.Debug|x86.ActiveCfg = Debug|Any CPU
{23D6C29C-A310-4D3E-8455-8D0B8CA1FC06}.Debug|x86.Build.0 = Debug|Any CPU
{23D6C29C-A310-4D3E-8455-8D0B8CA1FC06}.Release|Any CPU.ActiveCfg = Release|Any CPU
{23D6C29C-A310-4D3E-8455-8D0B8CA1FC06}.Release|Any CPU.Build.0 = Release|Any CPU
{23D6C29C-A310-4D3E-8455-8D0B8CA1FC06}.Release|x64.ActiveCfg = Release|Any CPU
{23D6C29C-A310-4D3E-8455-8D0B8CA1FC06}.Release|x64.Build.0 = Release|Any CPU
{23D6C29C-A310-4D3E-8455-8D0B8CA1FC06}.Release|x86.ActiveCfg = Release|Any CPU
{23D6C29C-A310-4D3E-8455-8D0B8CA1FC06}.Release|x86.Build.0 = Release|Any CPU
{B862FC25-0740-4CEA-BC53-3C5F43DCD985}.Debug|x64.ActiveCfg = Debug|x64
{B862FC25-0740-4CEA-BC53-3C5F43DCD985}.Debug|x64.Build.0 = Debug|x64
{B862FC25-0740-4CEA-BC53-3C5F43DCD985}.Debug|x86.ActiveCfg = Debug|x86
{B862FC25-0740-4CEA-BC53-3C5F43DCD985}.Debug|x86.Build.0 = Debug|x86
{B862FC25-0740-4CEA-BC53-3C5F43DCD985}.Release|x64.ActiveCfg = Release|x64
{B862FC25-0740-4CEA-BC53-3C5F43DCD985}.Release|x64.Build.0 = Release|x64
{B862FC25-0740-4CEA-BC53-3C5F43DCD985}.Release|x86.ActiveCfg = Release|x86
{B862FC25-0740-4CEA-BC53-3C5F43DCD985}.Release|x86.Build.0 = Release|x86
{23D6C29C-A310-4D3E-8455-8D0B8CA1FC06}.Debug|x64.ActiveCfg = Debug|x64
{23D6C29C-A310-4D3E-8455-8D0B8CA1FC06}.Debug|x64.Build.0 = Debug|x64
{23D6C29C-A310-4D3E-8455-8D0B8CA1FC06}.Debug|x86.ActiveCfg = Debug|x86
{23D6C29C-A310-4D3E-8455-8D0B8CA1FC06}.Debug|x86.Build.0 = Debug|x86
{23D6C29C-A310-4D3E-8455-8D0B8CA1FC06}.Release|x64.ActiveCfg = Release|x64
{23D6C29C-A310-4D3E-8455-8D0B8CA1FC06}.Release|x64.Build.0 = Release|x64
{23D6C29C-A310-4D3E-8455-8D0B8CA1FC06}.Release|x86.ActiveCfg = Release|x86
{23D6C29C-A310-4D3E-8455-8D0B8CA1FC06}.Release|x86.Build.0 = Release|x86
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE

View File

@ -0,0 +1,16 @@
using System;
using System.Collections.Generic;
namespace Encompass
{
[AttributeUsage(AttributeTargets.Class)]
public class Adds : Attribute
{
public readonly HashSet<Type> addTypes;
public Adds(params Type[] addTypes)
{
this.addTypes = new HashSet<Type>(addTypes);
}
}
}

View File

@ -5,11 +5,11 @@ namespace Encompass
[AttributeUsage(AttributeTargets.Class)]
public class DefaultWritePriority : Attribute
{
public int writePriority;
public int WritePriority { get; }
public DefaultWritePriority(int writePriority)
{
this.writePriority = writePriority;
WritePriority = writePriority;
}
}
}

View File

@ -1,27 +1,19 @@
using Encompass.Exceptions;
using System;
using System.Collections.Generic;
using System.Linq;
namespace Encompass
{
[AttributeUsage(AttributeTargets.Class)]
public class QueryWith : Attribute
{
public readonly HashSet<Type> queryWithTypes = new HashSet<Type>();
public readonly HashSet<Type> QueryWithTypes = new HashSet<Type>();
public QueryWith(params Type[] queryWithTypes)
{
foreach (var queryWithType in queryWithTypes)
{
var isComponent = queryWithType.GetInterfaces().Contains(typeof(IComponent));
if (!isComponent)
{
throw new IllegalReadTypeException("{0} must be a Component", queryWithType.Name);
}
this.queryWithTypes.Add(queryWithType);
QueryWithTypes.Add(queryWithType);
}
}
}

View File

@ -1,27 +1,18 @@
using Encompass.Exceptions;
using System;
using System;
using System.Collections.Generic;
using System.Linq;
namespace Encompass
{
[AttributeUsage(AttributeTargets.Class)]
public class QueryWithout : Attribute
{
public readonly HashSet<Type> queryWithoutTypes = new HashSet<Type>();
public readonly HashSet<Type> QueryWithoutTypes = new HashSet<Type>();
public QueryWithout(params Type[] queryWithoutTypes)
{
foreach (var type in queryWithoutTypes)
{
var isComponent = type.GetInterfaces().Contains(typeof(IComponent));
if (!isComponent)
{
throw new IllegalReadTypeException("{0} must be a Component", type.Name);
}
this.queryWithoutTypes.Add(type);
QueryWithoutTypes.Add(type);
}
}
}

View File

@ -1,27 +1,18 @@
using System;
using System.Collections.Generic;
using System.Linq;
using Encompass.Exceptions;
namespace Encompass
{
[AttributeUsage(AttributeTargets.Class)]
public class Reads : Attribute
{
public readonly HashSet<Type> readTypes = new HashSet<Type>();
public readonly HashSet<Type> ReadTypes = new HashSet<Type>();
public Reads(params Type[] readTypes)
{
foreach (var readType in readTypes)
{
var isComponent = readType.GetInterfaces().Contains(typeof(IComponent));
if (!isComponent)
{
throw new IllegalReadTypeException("{0} must be a Component", readType.Name);
}
this.readTypes.Add(readType);
ReadTypes.Add(readType);
}
}
}

View File

@ -1,27 +1,18 @@
using System;
using System.Collections.Generic;
using System.Linq;
using Encompass.Exceptions;
namespace Encompass
{
[AttributeUsage(AttributeTargets.Class)]
public class ReadsImmediate : Attribute
{
public readonly HashSet<Type> readImmediateTypes = new HashSet<Type>();
public readonly HashSet<Type> ReadImmediateTypes = new HashSet<Type>();
public ReadsImmediate(params Type[] readImmediateTypes)
{
foreach (var readImmediateType in readImmediateTypes)
{
var isComponent = readImmediateType.GetInterfaces().Contains(typeof(IComponent));
if (!isComponent)
{
throw new IllegalReadTypeException("{0} must be a Component", readImmediateType.Name);
}
this.readImmediateTypes.Add(readImmediateType);
ReadImmediateTypes.Add(readImmediateType);
}
}
}

View File

@ -1,27 +1,16 @@
using System;
using System.Collections.Generic;
using System.Linq;
using Encompass.Exceptions;
namespace Encompass
{
[AttributeUsage(AttributeTargets.Class)]
public class Receives : Attribute
{
public readonly HashSet<Type> receiveTypes;
public readonly HashSet<Type> ReceiveTypes;
public Receives(params Type[] receiveTypes)
{
foreach (var receiveType in receiveTypes)
{
var isMessage = receiveType.GetInterfaces().Contains(typeof(IMessage));
if (!isMessage)
{
throw new IllegalSendTypeException("{0} must be a Message", receiveType.Name);
}
}
this.receiveTypes = new HashSet<Type>(receiveTypes);
ReceiveTypes = new HashSet<Type>(receiveTypes);
}
}
}

View File

@ -1,27 +1,16 @@
using System;
using System.Collections.Generic;
using System.Linq;
using Encompass.Exceptions;
namespace Encompass
{
[AttributeUsage(AttributeTargets.Class)]
public class Sends : Attribute
{
public readonly HashSet<Type> sendTypes;
public readonly HashSet<Type> SendTypes;
public Sends(params Type[] sendTypes)
{
foreach (var sendType in sendTypes)
{
var isMessage = sendType.GetInterfaces().Contains(typeof(IMessage));
if (!isMessage)
{
throw new IllegalSendTypeException("{0} must be a Message", sendType.Name);
}
}
this.sendTypes = new HashSet<Type>(sendTypes);
this.SendTypes = new HashSet<Type>(sendTypes);
}
}
}

View File

@ -1,40 +1,26 @@
using System;
using System.Collections.Generic;
using System.Linq;
using Encompass.Exceptions;
namespace Encompass
{
[AttributeUsage(AttributeTargets.Class, AllowMultiple = true)]
public class Writes : Attribute
{
public readonly HashSet<Type> writeTypes = new HashSet<Type>();
public Dictionary<Type, int> priorities = new Dictionary<Type, int>();
public readonly HashSet<Type> WriteTypes = new HashSet<Type>();
public readonly Dictionary<Type, int> Priorities = new Dictionary<Type, int>();
public Writes(params Type[] writeTypes)
{
foreach (var writeType in writeTypes)
{
var isComponent = writeType.GetInterfaces().Contains(typeof(IComponent));
if (!isComponent)
{
throw new IllegalWriteTypeException("{0} must be a Component", writeType.Name);
}
this.writeTypes.Add(writeType);
WriteTypes.Add(writeType);
}
}
public Writes(Type writeType, int priority)
{
var isComponent = writeType.GetInterfaces().Contains(typeof(IComponent));
if (!isComponent)
{
throw new IllegalWriteTypeException("{0} must be a Component", writeType.Name);
}
writeTypes.Add(writeType);
priorities.Add(writeType, priority);
WriteTypes.Add(writeType);
Priorities.Add(writeType, priority);
}
}
}

View File

@ -1,26 +1,18 @@
using System;
using System.Collections.Generic;
using System.Linq;
using Encompass.Exceptions;
namespace Encompass
{
[AttributeUsage(AttributeTargets.Class)]
public class WritesImmediate : Attribute
{
public readonly HashSet<Type> writeImmediateTypes = new HashSet<Type>();
public readonly HashSet<Type> WriteImmediateTypes = new HashSet<Type>();
public WritesImmediate(params Type[] writeImmediateTypes)
{
foreach (var writeImmediateType in writeImmediateTypes)
{
var isComponent = writeImmediateType.GetInterfaces().Contains(typeof(IComponent));
if (!isComponent)
{
throw new IllegalWriteImmediateTypeException("{0} must be a Component", writeImmediateType.Name);
}
this.writeImmediateTypes.Add(writeImmediateType);
WriteImmediateTypes.Add(writeImmediateType);
}
}
}

View File

@ -0,0 +1,156 @@
using System;
namespace Encompass
{
public static unsafe class MemoryHelper
{
public static void Copy(uint* src, uint* dest, int count)
{
for (; count != 0; count--) *dest++ = *src++;
}
public static void Fill(uint* p, int count, uint value)
{
for (; count != 0; count--) *p++ = value;
}
public static void And(uint* p, uint* q, uint* result, int count)
{
for (; count != 0; count--) *result++ = *p++ & *q++;
}
public static void Or(uint* p, uint* q, uint* result, int count)
{
for (; count != 0; count--) *result++ = *p++ | *q++;
}
public static void Not(uint* p, uint* result, int count)
{
for (; count != 0; count--) *result++ = ~*p++;
}
public static bool Equal(uint* p, uint* q, int count)
{
for (; count != 0; count--) if (*p++ != *q++) { return false; }
return true;
}
}
public unsafe struct BitSet512 : IEquatable<BitSet512>
{
public static BitSet512 Zero { get; } = new BitSet512(0);
public static BitSet512 Ones { get; } = new BitSet512(uint.MaxValue);
private const int _uintLength = 16;
private fixed uint _buffer[_uintLength];
public BitSet512(uint value)
{
fixed (uint* p = _buffer) MemoryHelper.Fill(p, _uintLength, value);
}
public BitSet512(uint* src)
{
fixed (uint* dest = _buffer) MemoryHelper.Copy(src, dest, _uintLength);
}
public static BitSet512 operator &(BitSet512 a, BitSet512 b)
{
var tmp = stackalloc uint[_uintLength];
MemoryHelper.And(a._buffer, b._buffer, tmp, _uintLength);
return new BitSet512(tmp);
}
public static BitSet512 operator |(BitSet512 a, BitSet512 b)
{
var tmp = stackalloc uint[_uintLength];
MemoryHelper.Or(a._buffer, b._buffer, tmp, _uintLength);
return new BitSet512(tmp);
}
public static BitSet512 operator ~(BitSet512 a)
{
var tmp = stackalloc uint[_uintLength];
MemoryHelper.Not(a._buffer, tmp, _uintLength);
return new BitSet512(tmp);
}
public static bool operator ==(BitSet512 left, BitSet512 right)
{
return left.Equals(right);
}
public static bool operator !=(BitSet512 left, BitSet512 right)
{
return !(left == right);
}
public BitSet512 Set(int index)
{
var tmp = stackalloc uint[_uintLength];
fixed (uint* p = _buffer) MemoryHelper.Copy(p, tmp, _uintLength);
tmp[index / 32] |= (uint)(1 << index % 32);
return new BitSet512(tmp);
}
public BitSet512 UnSet(int index)
{
var tmp = stackalloc uint[_uintLength];
fixed (uint* p = _buffer) MemoryHelper.Copy(p, tmp, _uintLength);
tmp[index / 32] &= ~(uint)(1 << index % 32);
return new BitSet512(tmp);
}
public bool Get(int bitIndex)
{
var bitInt = (uint)(1 << bitIndex % 32);
return (_buffer[bitIndex / 32] & bitInt) == bitInt;
}
public bool AllTrue()
{
return this == Ones;
}
public bool AllFalse()
{
return this == Zero;
}
public static BitSet512 BitwiseAnd(BitSet512 left, BitSet512 right)
{
return left & right;
}
public static BitSet512 BitwiseOr(BitSet512 left, BitSet512 right)
{
return left | right;
}
public static BitSet512 OnesComplement(BitSet512 bitSet)
{
return ~bitSet;
}
public override bool Equals(object obj)
{
return obj is BitSet512 set && Equals(set);
}
public bool Equals(BitSet512 other)
{
fixed (uint* p = _buffer) return MemoryHelper.Equal(p, other._buffer, _uintLength);
}
public override int GetHashCode()
{
var hc = 0;
for (var i = 0; i < _uintLength; i++)
{
hc ^= _buffer[i].GetHashCode();
}
return hc;
}
}
}

View File

@ -1,4 +1,3 @@
using MoonTools.FastCollections;
using System;
using System.Collections.Generic;
@ -6,49 +5,49 @@ namespace Encompass
{
internal class ComponentBitSet
{
Dictionary<int, BitSet512> entities = new Dictionary<int, BitSet512>();
Dictionary<Type, int> TypeToIndex { get; }
private readonly Dictionary<int, BitSet512> _entities = new Dictionary<int, BitSet512>();
private readonly Dictionary<Type, int> _typeToIndex;
public ComponentBitSet(Dictionary<Type, int> typeToIndex)
{
TypeToIndex = typeToIndex;
_typeToIndex = typeToIndex;
}
public void Clear()
{
entities.Clear();
_entities.Clear();
}
public void AddEntity(int entityID)
{
entities.Add(entityID, BitSet512.Zero);
_entities.Add(entityID, BitSet512.Zero);
}
public void Set<TComponent>(int entityID) where TComponent : struct, IComponent
public void Set<TComponent>(int entityID) where TComponent : struct
{
if (!entities.ContainsKey(entityID)) { AddEntity(entityID); }
entities[entityID] = entities[entityID].Set(TypeToIndex[typeof(TComponent)]);
if (!_entities.ContainsKey(entityID)) { AddEntity(entityID); }
_entities[entityID] = _entities[entityID].Set(_typeToIndex[typeof(TComponent)]);
}
public void RemoveComponent<TComponent>(int entityID) where TComponent : struct, IComponent
public void RemoveComponent<TComponent>(int entityID) where TComponent : struct
{
if (entities.ContainsKey(entityID))
if (_entities.ContainsKey(entityID))
{
entities[entityID] = entities[entityID].UnSet(TypeToIndex[typeof(TComponent)]);
_entities[entityID] = _entities[entityID].UnSet(_typeToIndex[typeof(TComponent)]);
}
}
public void RemoveEntity(int entityID)
{
if (entities.ContainsKey(entityID))
if (_entities.ContainsKey(entityID))
{
entities.Remove(entityID);
_entities.Remove(entityID);
}
}
public BitSet512 EntityBitArray(int entityID)
{
return entities.ContainsKey(entityID) ? entities[entityID] : BitSet512.Zero;
return _entities.ContainsKey(entityID) ? _entities[entityID] : BitSet512.Zero;
}
}
}

View File

@ -3,39 +3,48 @@ using System.Collections.Generic;
namespace Encompass
{
internal class ComponentDeltaStore : ComponentStore
internal class ComponentDeltaStore
{
private readonly ComponentStore _store;
private readonly Dictionary<Type, Replayer> _replayers = new Dictionary<Type, Replayer>();
private readonly HashSet<Replayer> _currentReplayers = new HashSet<Replayer>();
public IEnumerable<Replayer> CurrentReplayers { get { return _currentReplayers; } }
public ComponentDeltaStore(Dictionary<Type, int> typeToIndex) : base(typeToIndex)
public ComponentDeltaStore(Dictionary<Type, int> typeToIndex)
{
_store = new ComponentStore(typeToIndex);
}
public override void RegisterComponentType<TComponent>()
public void RegisterComponentType<TComponent>() where TComponent : struct
{
base.RegisterComponentType<TComponent>();
_store.RegisterComponentType<TComponent>();
if (!_replayers.ContainsKey(typeof(TComponent)))
{
_replayers.Add(typeof(TComponent), new Replayer<TComponent>(this));
}
}
public override void Set<TComponent>(int entityID, TComponent component)
public ref readonly TComponent GetComponent<TComponent>(int entityID) where TComponent : struct
{
base.Set(entityID, component);
return ref _store.Get<TComponent>(entityID);
}
public void Set<TComponent>(int entityID, in TComponent component) where TComponent : struct
{
_store.Set(entityID, component);
RegisterComponentType<TComponent>();
var replayer = _replayers[typeof(TComponent)];
_currentReplayers.Add(replayer);
replayer.UnMarkRemoval(entityID);
}
public override bool Set<TComponent>(int entityID, TComponent component, int priority)
public bool Set<TComponent>(int entityID, TComponent component, int priority) where TComponent : struct
{
var result = base.Set(entityID, component, priority);
var result = _store.Set(entityID, component, priority);
if (result)
{
RegisterComponentType<TComponent>();
var replayer = _replayers[typeof(TComponent)];
_currentReplayers.Add(replayer);
replayer.UnMarkRemoval(entityID);
@ -43,11 +52,12 @@ namespace Encompass
return result;
}
public override bool Remove<TComponent>(int entityID, int priority)
public bool Remove<TComponent>(int entityID, int priority) where TComponent : struct
{
var result = base.Remove<TComponent>(entityID, priority);
var result = _store.Remove<TComponent>(entityID, priority);
if (result)
{
RegisterComponentType<TComponent>();
var replayer = _replayers[typeof(TComponent)];
_currentReplayers.Add(replayer);
replayer.MarkRemoval(entityID);
@ -55,18 +65,23 @@ namespace Encompass
return result;
}
public override void Remove(int entityID)
public void Remove(int entityID)
{
base.Remove(entityID);
_store.Remove(entityID);
foreach (var replayer in CurrentReplayers)
{
replayer.MarkRemoval(entityID);
}
}
public override void ClearAll()
public ReadOnlySpan<Entity> AllEntities<TComponent>() where TComponent : struct
{
base.ClearAll();
return _store.AllEntities<TComponent>();
}
public void ClearAll()
{
_store.ClearAll();
foreach (var replayer in _currentReplayers)
{
replayer.Clear();

View File

@ -1,49 +1,45 @@
using MoonTools.FastCollections;
using Encompass.Exceptions;
using System;
using System.Collections.Generic;
namespace Encompass
{
internal class ComponentStore
internal sealed class ComponentStore
{
private Dictionary<Type, TypedComponentStore> Stores = new Dictionary<Type, TypedComponentStore>(512);
private Dictionary<Type, TypedComponentStore> _stores = new Dictionary<Type, TypedComponentStore>(512);
public ComponentBitSet ComponentBitSet { get; private set; }
private readonly Dictionary<Type, int> _typeToIndex;
public ComponentStore(Dictionary<Type, int> typeToIndex)
{
_typeToIndex = typeToIndex;
ComponentBitSet = new ComponentBitSet(typeToIndex);
}
public IEnumerable<(Type, TypedComponentStore)> StoresEnumerable()
public void RegisterComponentType<TComponent>() where TComponent : struct
{
foreach (var entry in Stores)
{
yield return (entry.Key, entry.Value);
}
}
public virtual void RegisterComponentType<TComponent>() where TComponent : struct, IComponent
{
if (!Stores.ContainsKey(typeof(TComponent)))
if (!_stores.ContainsKey(typeof(TComponent)))
{
var store = new TypedComponentStore<TComponent>();
Stores.Add(typeof(TComponent), store);
_stores.Add(typeof(TComponent), store);
}
if (!_typeToIndex.ContainsKey(typeof(TComponent))) { _typeToIndex.Add(typeof(TComponent), _typeToIndex.Count); }
}
private TypedComponentStore<TComponent> Lookup<TComponent>() where TComponent : struct, IComponent
private TypedComponentStore<TComponent> Lookup<TComponent>() where TComponent : struct
{
return Stores[typeof(TComponent)] as TypedComponentStore<TComponent>;
return _stores[typeof(TComponent)] as TypedComponentStore<TComponent>;
}
public bool Has<TComponent>(int entityID) where TComponent : struct, IComponent
public bool Has<TComponent>(int entityID) where TComponent : struct
{
return Lookup<TComponent>().Has(entityID);
}
public bool Has(Type type, int entityID)
{
return Stores.ContainsKey(type) && Stores[type].Has(entityID);
return _stores[type].Has(entityID);
}
public BitSet512 EntityBitArray(int entityID)
@ -51,18 +47,32 @@ namespace Encompass
return ComponentBitSet.EntityBitArray(entityID);
}
public TComponent Get<TComponent>(int entityID) where TComponent : struct, IComponent
public ref TComponent Get<TComponent>(int entityID) where TComponent : struct
{
return Lookup<TComponent>().Get(entityID);
return ref Lookup<TComponent>().Get(entityID);
}
public virtual void Set<TComponent>(int entityID, TComponent component) where TComponent : struct, IComponent
public ref readonly TComponent Singular<TComponent>() where TComponent : struct
{
if (!Any<TComponent>()) { throw new NoComponentOfTypeException("No component of type {0} exists", typeof(TComponent).Name); }
return ref Lookup<TComponent>().Singular();
}
public ref readonly Entity SingularEntity<TComponent>() where TComponent : struct
{
if (!Any<TComponent>()) { throw new NoComponentOfTypeException("No component of type {0} exists", typeof(TComponent).Name); }
return ref Lookup<TComponent>().SingularEntity();
}
public void Set<TComponent>(int entityID, in TComponent component) where TComponent : struct
{
Lookup<TComponent>().Set(entityID, component);
ComponentBitSet.Set<TComponent>(entityID);
}
public virtual bool Set<TComponent>(int entityID, TComponent component, int priority) where TComponent : struct, IComponent
public bool Set<TComponent>(int entityID, in TComponent component, int priority) where TComponent : struct
{
if (Lookup<TComponent>().Set(entityID, component, priority))
{
@ -72,7 +82,7 @@ namespace Encompass
return false;
}
public virtual bool Remove<TComponent>(int entityID, int priority) where TComponent : struct, IComponent
public bool Remove<TComponent>(int entityID, int priority) where TComponent : struct
{
if (Lookup<TComponent>().Remove(entityID, priority))
{
@ -82,59 +92,63 @@ namespace Encompass
return false;
}
public void ForceRemove<TComponent>(int entityID) where TComponent : struct, IComponent
public void ForceRemove<TComponent>(int entityID) where TComponent : struct
{
Lookup<TComponent>().ForceRemove(entityID);
ComponentBitSet.RemoveComponent<TComponent>(entityID);
}
public virtual void Remove(int entityID)
public void Remove(int entityID)
{
foreach (var entry in Stores.Values)
foreach (var entry in _stores.Values)
{
entry.ForceRemove(entityID);
}
ComponentBitSet.RemoveEntity(entityID);
}
public bool Any<TComponent>() where TComponent : struct, IComponent
public bool Any<TComponent>() where TComponent : struct
{
return Lookup<TComponent>().Count > 0;
}
public IEnumerable<(int, Type, IComponent)> AllInterfaceTyped()
public ReadOnlySpan<TComponent> All<TComponent>() where TComponent : struct
{
foreach (var store in Stores.Values)
{
foreach (var thing in store.AllInterfaceTyped())
{
yield return thing;
}
}
return Lookup<TComponent>().AllComponents();
}
public IEnumerable<(TComponent, int)> All<TComponent>() where TComponent : struct, IComponent
public IEnumerable<TComponent> AllAsEnumerable<TComponent>() where TComponent : struct
{
return Lookup<TComponent>().All();
return Lookup<TComponent>().AllComponentsAsEnumerable();
}
public void Clear<TComponent>() where TComponent : struct, IComponent
public ReadOnlySpan<Entity> AllEntities<TComponent>() where TComponent : struct
{
return Lookup<TComponent>().AllEntities();
}
public IEnumerable<Entity> AllEntitiesAsEnumerable<TComponent>() where TComponent : struct
{
return Lookup<TComponent>().AllEntitiesAsEnumerable();
}
public void Clear<TComponent>() where TComponent : struct
{
Lookup<TComponent>().Clear();
}
public virtual void ClearAllPriorities()
public void ClearAllPriorities()
{
foreach (var store in Stores.Values)
foreach (var store in _stores.Values)
{
store.ClearPriorities();
}
}
public virtual void ClearAll()
public void ClearAll()
{
ComponentBitSet.Clear();
foreach (var store in Stores.Values)
foreach (var store in _stores.Values)
{
store.Clear();
}
@ -142,7 +156,7 @@ namespace Encompass
public void SwapWith(ComponentStore other)
{
(Stores, other.Stores) = (other.Stores, Stores);
(_stores, other._stores) = (other._stores, _stores);
(ComponentBitSet, other.ComponentBitSet) = (other.ComponentBitSet, ComponentBitSet);
}

View File

@ -1,66 +1,74 @@
using System;
using System.Collections.Generic;
using Encompass.Exceptions;
namespace Encompass
{
internal class MessageStore
{
private Dictionary<Type, TypedMessageStore> Stores = new Dictionary<Type, TypedMessageStore>(512);
private readonly Dictionary<Type, TypedMessageStore> _stores = new Dictionary<Type, TypedMessageStore>(512);
private void RegisterMessageType<TMessage>() where TMessage : struct, IMessage
private void RegisterMessageType<TMessage>() where TMessage : struct
{
Stores.Add(typeof(TMessage), new TypedMessageStore<TMessage>());
_stores.Add(typeof(TMessage), new TypedMessageStore<TMessage>());
}
private TypedMessageStore<TMessage> Lookup<TMessage>() where TMessage : struct, IMessage
private TypedMessageStore<TMessage> Lookup<TMessage>() where TMessage : struct
{
if (!Stores.ContainsKey(typeof(TMessage))) { RegisterMessageType<TMessage>(); }
return Stores[typeof(TMessage)] as TypedMessageStore<TMessage>;
if (!_stores.ContainsKey(typeof(TMessage))) { RegisterMessageType<TMessage>(); }
return _stores[typeof(TMessage)] as TypedMessageStore<TMessage>;
}
public void AddMessage<TMessage>(TMessage message) where TMessage : struct, IMessage
public void AddMessage<TMessage>(in TMessage message) where TMessage : struct
{
Lookup<TMessage>().Add(message);
}
public void AddMessage<TMessage>(TMessage message, double time) where TMessage : struct, IMessage
public void AddMessage<TMessage>(in TMessage message, double time) where TMessage : struct
{
Lookup<TMessage>().Add(message, time);
}
public void AddMessageIgnoringTimeDilation<TMessage>(TMessage message, double time) where TMessage : struct, IMessage
public void AddMessageIgnoringTimeDilation<TMessage>(in TMessage message, double time) where TMessage : struct
{
Lookup<TMessage>().AddIgnoringTimeDilation(message, time);
}
public TMessage First<TMessage>() where TMessage : struct, IMessage
public ref readonly TMessage First<TMessage>() where TMessage : struct
{
return Lookup<TMessage>().First();
if (!Any<TMessage>()) { throw new NoMessageOfTypeException("No Message of type {0} exists", typeof(TMessage).Name); }
return ref Lookup<TMessage>().First();
}
public IEnumerable<TMessage> All<TMessage>() where TMessage : struct, IMessage
public ReadOnlySpan<TMessage> All<TMessage>() where TMessage : struct
{
return Lookup<TMessage>().All();
}
public bool Any<TMessage>() where TMessage : struct, IMessage
public bool Any<TMessage>() where TMessage : struct
{
return Lookup<TMessage>().Any();
}
public IEnumerable<TMessage> WithEntity<TMessage>(int entityID) where TMessage : struct, IMessage, IHasEntity
public IEnumerable<TMessage> WithEntity<TMessage>(int entityID) where TMessage : struct, IHasEntity
{
return Lookup<TMessage>().WithEntity(entityID);
}
public bool SomeWithEntity<TMessage>(int entityID) where TMessage : struct, IMessage, IHasEntity
public ref readonly TMessage FirstWithEntity<TMessage>(int entityID) where TMessage : struct
{
return ref Lookup<TMessage>().FirstWithEntity(entityID);
}
public bool SomeWithEntity<TMessage>(int entityID) where TMessage : struct, IHasEntity
{
return Lookup<TMessage>().SomeWithEntity(entityID);
}
public void ProcessDelayedMessages(double dilatedDelta, double realtimeDelta)
{
foreach (var store in Stores.Values)
foreach (var store in _stores.Values)
{
store.ProcessDelayedMessages(dilatedDelta, realtimeDelta);
}
@ -68,7 +76,7 @@ namespace Encompass
public void ClearAll()
{
foreach (var store in Stores.Values)
foreach (var store in _stores.Values)
{
store.Clear();
}

View File

@ -2,7 +2,7 @@
namespace Encompass
{
internal abstract class Replayer
internal abstract class Replayer
{
public abstract void Replay(ComponentStore store);
public abstract void MarkRemoval(int entityID);
@ -10,7 +10,7 @@ namespace Encompass
public abstract void Clear();
}
internal class Replayer<TComponent> : Replayer where TComponent : struct, IComponent
internal class Replayer<TComponent> : Replayer where TComponent : struct
{
private readonly ComponentDeltaStore _deltaStore;
private readonly HashSet<int> _removals = new HashSet<int>();
@ -22,9 +22,10 @@ namespace Encompass
public override void Replay(ComponentStore store)
{
foreach (var (component, entityID) in _deltaStore.All<TComponent>())
foreach (ref readonly var entity in _deltaStore.AllEntities<TComponent>())
{
store.Set(entityID, component);
ref readonly var component = ref _deltaStore.GetComponent<TComponent>(entity.ID);
store.Set(entity.ID, component);
}
foreach (var entityID in _removals)

View File

@ -1,12 +1,12 @@
using System;
using System.Collections.Generic;
using System.Runtime.CompilerServices;
namespace Encompass
{
internal abstract class TypedComponentStore
{
public abstract int Count { get; }
public abstract IEnumerable<(int, Type, IComponent)> AllInterfaceTyped();
public abstract bool Has(int entity);
public abstract bool Remove(int entity, int priority);
public abstract void ForceRemove(int entity);
@ -14,43 +14,72 @@ namespace Encompass
public abstract void ClearPriorities();
}
internal class TypedComponentStore<TComponent> : TypedComponentStore where TComponent : struct, IComponent
internal class TypedComponentStore<TComponent> : TypedComponentStore where TComponent : struct
{
private readonly Dictionary<int, TComponent> store = new Dictionary<int, TComponent>(128);
private readonly Dictionary<int, int> priorities = new Dictionary<int, int>(128);
private int _nextID = 0;
private readonly Dictionary<int, int> _entityIDToStorageIndex = new Dictionary<int, int>(128);
private readonly Dictionary<int, int> _priorities = new Dictionary<int, int>(128);
private Entity[] _storageIndexToEntities = new Entity[128];
private TComponent[] _components = new TComponent[128];
public override int Count { get => store.Count; }
public override int Count { get => _entityIDToStorageIndex.Count; }
public TComponent Get(int entityID)
public ref TComponent Get(int entityID)
{
if (!store.ContainsKey(entityID)) { throw new Exceptions.NoComponentOfTypeOnEntityException("No component of type {0} exists on Entity with ID {1}", typeof(TComponent), entityID); }
return store[entityID];
if (!_entityIDToStorageIndex.ContainsKey(entityID)) { throw new Exceptions.NoComponentOfTypeOnEntityException("No component of type {0} exists on Entity with ID {1}", typeof(TComponent), entityID); }
return ref _components[_entityIDToStorageIndex[entityID]];
}
public void Set(int entityID, TComponent component)
public ref TComponent Singular()
{
store[entityID] = component;
return ref _components[0];
}
public bool Set(int entityID, TComponent component, int priority)
public ref Entity SingularEntity()
{
if (!priorities.ContainsKey(entityID) || priority < priorities[entityID])
return ref _storageIndexToEntities[0];
}
public void Set(int entityID, in TComponent component)
{
InternalSet(entityID, component);
}
public bool Set(int entityID, in TComponent component, int priority)
{
if (!_priorities.ContainsKey(entityID) || priority <= _priorities[entityID]) // if priorities are equal that means it's the same engine
{
store[entityID] = component;
priorities[entityID] = priority;
InternalSet(entityID, component);
_priorities[entityID] = priority;
return true;
}
return false;
}
private void InternalSet(int entityID, in TComponent component)
{
if (!_entityIDToStorageIndex.ContainsKey(entityID))
{
var index = _nextID++;
if (index >= _components.Length)
{
System.Array.Resize(ref _components, _components.Length * 2);
System.Array.Resize(ref _storageIndexToEntities, _storageIndexToEntities.Length * 2);
}
_entityIDToStorageIndex[entityID] = index;
_storageIndexToEntities[index] = new Entity(entityID);
}
_components[_entityIDToStorageIndex[entityID]] = component;
}
public override bool Remove(int entityID, int priority)
{
if (!priorities.ContainsKey(entityID) || priority < priorities[entityID])
if (!_priorities.ContainsKey(entityID) || priority <= _priorities[entityID]) // if priorities are equal that means it's the same engine
{
priorities[entityID] = priority;
store.Remove(entityID);
priorities.Remove(entityID);
ForceRemove(entityID);
_priorities[entityID] = priority;
return true;
}
@ -59,40 +88,62 @@ namespace Encompass
public override void ForceRemove(int entityID)
{
store.Remove(entityID);
priorities.Remove(entityID);
if (_entityIDToStorageIndex.ContainsKey(entityID))
{
var storageIndex = _entityIDToStorageIndex[entityID];
_entityIDToStorageIndex.Remove(entityID);
// move a component into the hole to maintain contiguous memory
if (_nextID > 1 && storageIndex != _nextID - 1)
{
var lastStorageIndex = _nextID - 1;
ref readonly var lastEntity = ref _storageIndexToEntities[lastStorageIndex];
_entityIDToStorageIndex[lastEntity.ID] = storageIndex;
_storageIndexToEntities[storageIndex] = lastEntity;
_components[storageIndex] = _components[lastStorageIndex];
}
_nextID--;
}
}
public override bool Has(int entityID)
{
return store.ContainsKey(entityID);
return _entityIDToStorageIndex.ContainsKey(entityID);
}
public override void Clear()
{
store.Clear();
priorities.Clear();
_nextID = 0;
_entityIDToStorageIndex.Clear();
_priorities.Clear();
}
public override void ClearPriorities()
{
priorities.Clear();
_priorities.Clear();
}
public IEnumerable<(TComponent, int)> All()
public ReadOnlySpan<Entity> AllEntities()
{
foreach (var kvp in store)
{
yield return (kvp.Value, kvp.Key);
}
return new ReadOnlySpan<Entity>(_storageIndexToEntities, 0, _nextID);
}
public override IEnumerable<(int, Type, IComponent)> AllInterfaceTyped()
public IEnumerable<Entity> AllEntitiesAsEnumerable()
{
foreach (var kvp in store)
{
yield return (kvp.Key, typeof(TComponent), (IComponent)kvp.Value);
}
return new ArraySegment<Entity>(_storageIndexToEntities, 0, _nextID);
}
public ReadOnlySpan<TComponent> AllComponents()
{
return new ReadOnlySpan<TComponent>(_components, 0, _nextID);
}
public IEnumerable<TComponent> AllComponentsAsEnumerable()
{
return new ArraySegment<TComponent>(_components, 0, _nextID);
}
}
}

View File

@ -1,3 +1,4 @@
using System;
using System.Collections.Generic;
namespace Encompass
@ -8,100 +9,119 @@ namespace Encompass
public abstract void Clear();
}
internal class TypedMessageStore<TMessage> : TypedMessageStore where TMessage : struct, IMessage
internal class TypedMessageStore<TMessage> : TypedMessageStore where TMessage : struct
{
private readonly List<TMessage> store = new List<TMessage>(128);
private readonly List<(TMessage, double)> delayedStore = new List<(TMessage, double)>(128);
private readonly List<(TMessage, double)> delayedStoreIgnoringTimeDilation = new List<(TMessage, double)>(128);
private readonly Dictionary<int, List<TMessage>> entityToMessage = new Dictionary<int, List<TMessage>>();
// messages are placed in a contiguous region
// so we can return the collection as a Span
private int _nextIndex = 0;
private TMessage[] _store = new TMessage[128];
private readonly List<(TMessage, double)> _delayedStore = new List<(TMessage, double)>(128);
private readonly List<(TMessage, double)> _delayedStoreIgnoringTimeDilation = new List<(TMessage, double)>(128);
private readonly Dictionary<int, List<int>> _entityToIndices = new Dictionary<int, List<int>>();
public override void ProcessDelayedMessages(double dilatedDelta, double realtimeDelta)
{
for (int i = delayedStore.Count - 1; i >= 0; i--)
for (var i = _delayedStore.Count - 1; i >= 0; i--)
{
var (message, time) = delayedStore[i];
var (message, time) = _delayedStore[i];
var updatedTime = time - dilatedDelta;
if (updatedTime <= 0)
{
Add(message);
delayedStore.RemoveAt(i);
_delayedStore.RemoveAt(i);
}
else
{
delayedStore[i] = (message, updatedTime);
_delayedStore[i] = (message, updatedTime);
}
}
for (int i = delayedStoreIgnoringTimeDilation.Count - 1; i >= 0; i--)
for (var i = _delayedStoreIgnoringTimeDilation.Count - 1; i >= 0; i--)
{
var (message, time) = delayedStoreIgnoringTimeDilation[i];
var (message, time) = _delayedStoreIgnoringTimeDilation[i];
var updatedTime = time - realtimeDelta;
if (updatedTime <= 0)
{
Add(message);
delayedStoreIgnoringTimeDilation.RemoveAt(i);
_delayedStoreIgnoringTimeDilation.RemoveAt(i);
}
else
{
delayedStoreIgnoringTimeDilation[i] = (message, updatedTime);
_delayedStoreIgnoringTimeDilation[i] = (message, updatedTime);
}
}
}
public void Add(TMessage message)
public void Add(in TMessage message)
{
store.Add(message);
var index = _nextIndex++;
if (index >= _store.Length)
{
Array.Resize(ref _store, _store.Length * 2);
}
_store[index] = message;
if (message is IHasEntity entityMessage)
{
var entityID = entityMessage.Entity.ID;
if (!entityToMessage.ContainsKey(entityID)) { entityToMessage.Add(entityID, new List<TMessage>()); }
entityToMessage[entityID].Add(message);
if (!_entityToIndices.ContainsKey(entityID)) { _entityToIndices.Add(entityID, new List<int>()); }
_entityToIndices[entityID].Add(index);
}
}
public void Add(TMessage message, double time)
public void Add(in TMessage message, double time)
{
delayedStore.Add((message, time));
_delayedStore.Add((message, time));
}
public void AddIgnoringTimeDilation(TMessage message, double time)
public void AddIgnoringTimeDilation(in TMessage message, double time)
{
delayedStoreIgnoringTimeDilation.Add((message, time));
_delayedStoreIgnoringTimeDilation.Add((message, time));
}
public TMessage First()
public ref readonly TMessage First()
{
return store[0];
return ref _store[0];
}
public bool Any()
{
return store.Count > 0;
return _nextIndex != 0;
}
public IEnumerable<TMessage> All()
public ReadOnlySpan<TMessage> All()
{
return store;
return new ReadOnlySpan<TMessage>(_store, 0, _nextIndex);
}
public IEnumerable<TMessage> WithEntity(int entityID)
{
return entityToMessage.ContainsKey(entityID) ? entityToMessage[entityID] : System.Linq.Enumerable.Empty<TMessage>();
if (_entityToIndices.ContainsKey(entityID))
{
foreach (var index in _entityToIndices[entityID])
{
yield return _store[index];
}
}
}
public ref readonly TMessage FirstWithEntity(int entityID)
{
return ref _store[_entityToIndices[entityID][0]];
}
public bool SomeWithEntity(int entityID)
{
return entityToMessage.ContainsKey(entityID) && entityToMessage[entityID].Count > 0;
return _entityToIndices.ContainsKey(entityID) && _entityToIndices[entityID].Count > 0;
}
public override void Clear()
{
store.Clear();
foreach (var set in entityToMessage.Values)
_nextIndex = 0;
foreach (var set in _entityToIndices.Values)
{
set.Clear();
}

View File

@ -1,272 +1,288 @@
using System;
using System.Collections.Generic;
using MoonTools.FastCollections;
namespace Encompass
{
internal class ComponentManager
{
private readonly DrawLayerManager drawLayerManager;
private readonly ComponentStore existingComponentStore;
private readonly ComponentStore immediateComponentStore;
private readonly ComponentDeltaStore replayStore;
private ComponentStore upToDateComponentStore;
private readonly ComponentStore _existingComponentStore;
private readonly ComponentStore _immediateComponentStore;
private readonly ComponentDeltaStore _replayStore;
private readonly ComponentStore _upToDateComponentStore;
public Dictionary<Type, int> TypeToIndex { get; }
private readonly HashSet<int> entitiesMarkedForRemoval = new HashSet<int>();
private readonly HashSet<int> _entitiesMarkedForRemoval = new HashSet<int>();
internal ComponentBitSet ImmediateBits { get { return immediateComponentStore.ComponentBitSet; } }
internal ComponentBitSet ExistingBits { get { return existingComponentStore.ComponentBitSet; } }
internal ComponentBitSet ImmediateBits { get { return _immediateComponentStore.ComponentBitSet; } }
internal ComponentBitSet ExistingBits { get { return _existingComponentStore.ComponentBitSet; } }
public ComponentManager(DrawLayerManager drawLayerManager, Dictionary<Type, int> typeToIndex)
public ComponentManager(Dictionary<Type, int> typeToIndex)
{
this.drawLayerManager = drawLayerManager;
existingComponentStore = new ComponentStore(typeToIndex);
immediateComponentStore = new ComponentStore(typeToIndex);
replayStore = new ComponentDeltaStore(typeToIndex);
upToDateComponentStore = new ComponentStore(typeToIndex);
_existingComponentStore = new ComponentStore(typeToIndex);
_immediateComponentStore = new ComponentStore(typeToIndex);
_replayStore = new ComponentDeltaStore(typeToIndex);
_upToDateComponentStore = new ComponentStore(typeToIndex);
TypeToIndex = typeToIndex;
}
public void RegisterComponentType<TComponent>() where TComponent : struct, IComponent
public void RegisterComponentType<TComponent>() where TComponent : struct
{
existingComponentStore.RegisterComponentType<TComponent>();
immediateComponentStore.RegisterComponentType<TComponent>();
replayStore.RegisterComponentType<TComponent>();
upToDateComponentStore.RegisterComponentType<TComponent>();
_existingComponentStore.RegisterComponentType<TComponent>();
_immediateComponentStore.RegisterComponentType<TComponent>();
_replayStore.RegisterComponentType<TComponent>();
_upToDateComponentStore.RegisterComponentType<TComponent>();
}
internal void SetExistingComponentStore(ComponentStore componentStore)
{
existingComponentStore.SwapWith(componentStore);
_existingComponentStore.SwapWith(componentStore);
}
internal void SetUpToDateComponentStore(ComponentStore componentStore)
{
upToDateComponentStore.SwapWith(componentStore);
}
internal void RegisterDrawableComponent<TComponent>(int entityID, TComponent component, int layer) where TComponent : struct, IComponent
{
drawLayerManager.RegisterComponentWithLayer(entityID, component, layer);
_upToDateComponentStore.SwapWith(componentStore);
}
internal void WriteComponents()
{
existingComponentStore.UpdateUsing(replayStore);
existingComponentStore.ClearAllPriorities();
upToDateComponentStore.ClearAllPriorities();
immediateComponentStore.ClearAll();
replayStore.ClearAll();
_existingComponentStore.UpdateUsing(_replayStore);
_existingComponentStore.ClearAllPriorities();
_upToDateComponentStore.ClearAllPriorities();
_immediateComponentStore.ClearAll();
_replayStore.ClearAll();
}
internal bool AddImmediateComponent<TComponent>(int entityID, TComponent component, int priority) where TComponent : struct, IComponent
internal bool AddImmediateComponent<TComponent>(int entityID, in TComponent component, int priority) where TComponent : struct
{
if (immediateComponentStore.Set(entityID, component, priority))
if (_immediateComponentStore.Set(entityID, component, priority))
{
replayStore.Set(entityID, component);
upToDateComponentStore.Set(entityID, component);
_replayStore.Set(entityID, component);
_upToDateComponentStore.Set(entityID, component);
return true;
}
return false;
}
internal void AddImmediateComponent<TComponent>(int entityID, TComponent component) where TComponent : struct, IComponent
internal void AddImmediateComponent<TComponent>(int entityID, in TComponent component) where TComponent : struct
{
immediateComponentStore.Set(entityID, component);
replayStore.Set(entityID, component);
upToDateComponentStore.Set(entityID, component);
_immediateComponentStore.Set(entityID, component);
_replayStore.Set(entityID, component);
_upToDateComponentStore.Set(entityID, component);
}
internal bool UpdateComponent<TComponent>(int entityID, TComponent component, int priority) where TComponent : struct, IComponent
internal bool UpdateComponent<TComponent>(int entityID, in TComponent component, int priority) where TComponent : struct
{
var result = upToDateComponentStore.Set(entityID, component, priority);
var result = _upToDateComponentStore.Set(entityID, component, priority);
if (result)
{
replayStore.Set(entityID, component);
_replayStore.Set(entityID, component);
}
return result;
}
internal void AddComponent<TComponent>(int entityID, TComponent component) where TComponent : struct, IComponent
internal void AddComponent<TComponent>(int entityID, in TComponent component) where TComponent : struct
{
upToDateComponentStore.Set(entityID, component);
replayStore.Set(entityID, component);
_upToDateComponentStore.Set(entityID, component);
_replayStore.Set(entityID, component);
}
// existing or immediate reads
internal IEnumerable<(TComponent, int)> ReadExistingAndImmediateComponentsByType<TComponent>() where TComponent : struct, IComponent
internal ReadOnlySpan<TComponent> ReadExistingAndImmediateComponentsByType<TComponent>() where TComponent : struct
{
return upToDateComponentStore.All<TComponent>();
return _upToDateComponentStore.All<TComponent>();
}
internal (TComponent, int) ReadFirstExistingOrImmediateComponentByType<TComponent>() where TComponent : struct, IComponent
internal ref readonly TComponent ExistingOrImmediateSingular<TComponent>() where TComponent : struct
{
if (!SomeExistingOrImmediateComponent<TComponent>()) { throw new Exceptions.NoComponentOfTypeException($"No Component with type {typeof(TComponent)} exists"); }
var enumerator = ReadExistingAndImmediateComponentsByType<TComponent>().GetEnumerator();
enumerator.MoveNext();
return enumerator.Current;
return ref _upToDateComponentStore.Singular<TComponent>();
}
internal bool SomeExistingOrImmediateComponent<TComponent>() where TComponent : struct, IComponent
internal ReadOnlySpan<Entity> GetExistingAndImmediateEntities<TComponent>() where TComponent : struct
{
return upToDateComponentStore.Any<TComponent>();
return _upToDateComponentStore.AllEntities<TComponent>();
}
internal ref readonly Entity ExistingOrImmediateSingularEntity<TComponent>() where TComponent : struct
{
return ref _upToDateComponentStore.SingularEntity<TComponent>();
}
internal bool SomeExistingOrImmediateComponent<TComponent>() where TComponent : struct
{
return _upToDateComponentStore.Any<TComponent>();
}
// existing reads
internal (TComponent, int) ReadFirstExistingComponentByType<TComponent>() where TComponent : struct, IComponent
internal ReadOnlySpan<TComponent> GetExistingComponents<TComponent>() where TComponent : struct
{
if (!SomeExistingComponent<TComponent>()) { throw new Exceptions.NoComponentOfTypeException($"No Component with type {typeof(TComponent)} exists"); }
var enumerator = GetComponentsIncludingEntity<TComponent>().GetEnumerator();
enumerator.MoveNext();
return enumerator.Current;
return _existingComponentStore.All<TComponent>();
}
internal bool SomeExistingComponent<TComponent>() where TComponent : struct, IComponent
internal ref readonly TComponent ExistingSingular<TComponent>() where TComponent : struct
{
return existingComponentStore.Any<TComponent>();
return ref _existingComponentStore.Singular<TComponent>();
}
internal ReadOnlySpan<Entity> GetExistingEntities<TComponent>() where TComponent : struct
{
return _existingComponentStore.AllEntities<TComponent>();
}
internal IEnumerable<Entity> GetExistingEntitiesAsEnumerable<TComponent>() where TComponent : struct
{
return _existingComponentStore.AllEntitiesAsEnumerable<TComponent>();
}
internal ref readonly Entity ExistingSingularEntity<TComponent>() where TComponent : struct
{
return ref _existingComponentStore.SingularEntity<TComponent>();
}
internal bool SomeExistingComponent<TComponent>() where TComponent : struct
{
return _existingComponentStore.Any<TComponent>();
}
// immediate reads
internal IEnumerable<(TComponent, int)> ReadImmediateComponentsByType<TComponent>() where TComponent : struct, IComponent
internal ReadOnlySpan<TComponent> ReadImmediateComponentsByType<TComponent>() where TComponent : struct
{
return immediateComponentStore.All<TComponent>();
return _immediateComponentStore.All<TComponent>();
}
internal (TComponent, int) ReadFirstImmediateComponentByType<TComponent>() where TComponent : struct, IComponent
internal ref readonly TComponent ImmediateSingular<TComponent>() where TComponent : struct
{
if (!SomeImmediateComponent<TComponent>()) { throw new Exceptions.NoComponentOfTypeException($"No Component with type {typeof(TComponent)} exists"); }
var enumerator = ReadImmediateComponentsByType<TComponent>().GetEnumerator();
enumerator.MoveNext();
return enumerator.Current;
return ref _immediateComponentStore.Singular<TComponent>();
}
internal bool SomeImmediateComponent<TComponent>() where TComponent : struct, IComponent
internal ReadOnlySpan<Entity> GetImmediateEntities<TComponent>() where TComponent : struct
{
return immediateComponentStore.Any<TComponent>();
return _immediateComponentStore.AllEntities<TComponent>();
}
internal ref readonly Entity ImmediateSingularEntity<TComponent>() where TComponent : struct
{
return ref _immediateComponentStore.SingularEntity<TComponent>();
}
internal bool SomeImmediateComponent<TComponent>() where TComponent : struct
{
return _immediateComponentStore.Any<TComponent>();
}
// component getters
internal TComponent ReadImmediateOrExistingComponentByEntityAndType<TComponent>(int entityID) where TComponent : struct, IComponent
internal ref TComponent ReadImmediateOrExistingComponentByEntityAndType<TComponent>(int entityID) where TComponent : struct
{
return upToDateComponentStore.Get<TComponent>(entityID);
return ref _upToDateComponentStore.Get<TComponent>(entityID);
}
internal TComponent ReadExistingComponentByEntityAndType<TComponent>(int entityID) where TComponent : struct, IComponent
internal ref TComponent ReadExistingComponentByEntityAndType<TComponent>(int entityID) where TComponent : struct
{
return existingComponentStore.Get<TComponent>(entityID);
return ref _existingComponentStore.Get<TComponent>(entityID);
}
internal TComponent ReadImmediateComponentByEntityAndType<TComponent>(int entityID) where TComponent : struct, IComponent
internal ref TComponent ReadImmediateComponentByEntityAndType<TComponent>(int entityID) where TComponent : struct
{
return immediateComponentStore.Get<TComponent>(entityID);
return ref _immediateComponentStore.Get<TComponent>(entityID);
}
// has checkers
internal bool HasExistingOrImmediateComponent<TComponent>(int entityID) where TComponent : struct, IComponent
internal bool HasExistingOrImmediateComponent<TComponent>(int entityID) where TComponent : struct
{
return upToDateComponentStore.Has<TComponent>(entityID);
return _upToDateComponentStore.Has<TComponent>(entityID);
}
internal bool HasExistingOrImmediateComponent(int entityID, Type type)
{
return upToDateComponentStore.Has(type, entityID);
return _upToDateComponentStore.Has(type, entityID);
}
internal bool HasExistingComponent<TComponent>(int entityID) where TComponent : struct, IComponent
internal bool HasExistingComponent<TComponent>(int entityID) where TComponent : struct
{
return existingComponentStore.Has<TComponent>(entityID);
return _existingComponentStore.Has<TComponent>(entityID);
}
internal bool HasExistingComponent(int entityID, Type type)
{
return existingComponentStore.Has(type, entityID);
return _existingComponentStore.Has(type, entityID);
}
internal bool HasImmediateComponent<TComponent>(int entityID) where TComponent : struct, IComponent
internal bool HasImmediateComponent<TComponent>(int entityID) where TComponent : struct
{
return immediateComponentStore.Has<TComponent>(entityID);
return _immediateComponentStore.Has<TComponent>(entityID);
}
internal bool HasImmediateComponent(int entityID, Type type)
{
return immediateComponentStore.Has(type, entityID);
return _immediateComponentStore.Has(type, entityID);
}
internal IEnumerable<(TComponent, int)> GetComponentsIncludingEntity<TComponent>() where TComponent : struct, IComponent
internal ReadOnlySpan<TComponent> GetComponentsByType<TComponent>() where TComponent : struct
{
return existingComponentStore.All<TComponent>();
return _existingComponentStore.All<TComponent>();
}
internal IEnumerable<TComponent> GetComponentsByType<TComponent>() where TComponent : struct, IComponent
internal IEnumerable<TComponent> GetComponentsByTypeEnumerable<TComponent>() where TComponent : struct
{
foreach (var pair in existingComponentStore.All<TComponent>())
{
yield return pair.Item1;
}
return _existingComponentStore.AllAsEnumerable<TComponent>();
}
internal TComponent GetComponentByEntityAndType<TComponent>(int entityID) where TComponent : struct, IComponent
internal ref readonly TComponent GetComponentByEntityAndType<TComponent>(int entityID) where TComponent : struct
{
return existingComponentStore.Get<TComponent>(entityID);
return ref _existingComponentStore.Get<TComponent>(entityID);
}
internal bool EntityHasComponentOfType<TComponent>(int entityID) where TComponent : struct, IComponent
internal bool EntityHasComponentOfType<TComponent>(int entityID) where TComponent : struct
{
return existingComponentStore.Has<TComponent>(entityID);
return _existingComponentStore.Has<TComponent>(entityID);
}
internal void MarkAllComponentsOnEntityForRemoval(int entityID)
{
entitiesMarkedForRemoval.Add(entityID);
_entitiesMarkedForRemoval.Add(entityID);
}
internal void RemoveMarkedComponents()
{
foreach (var entityID in entitiesMarkedForRemoval)
foreach (var entityID in _entitiesMarkedForRemoval)
{
existingComponentStore.Remove(entityID);
immediateComponentStore.Remove(entityID);
replayStore.Remove(entityID);
upToDateComponentStore.Remove(entityID);
drawLayerManager.UnRegisterEntityWithLayer(entityID);
_existingComponentStore.Remove(entityID);
_immediateComponentStore.Remove(entityID);
_replayStore.Remove(entityID);
_upToDateComponentStore.Remove(entityID);
}
entitiesMarkedForRemoval.Clear();
_entitiesMarkedForRemoval.Clear();
}
public bool RemoveImmediate<TComponent>(int entityID, int priority) where TComponent : struct, IComponent
public bool RemoveImmediate<TComponent>(int entityID, int priority) where TComponent : struct
{
if (immediateComponentStore.Remove<TComponent>(entityID, priority))
if (_immediateComponentStore.Remove<TComponent>(entityID, priority))
{
replayStore.Remove<TComponent>(entityID, priority);
upToDateComponentStore.Remove<TComponent>(entityID, priority);
drawLayerManager.UnRegisterComponentWithLayer<TComponent>(entityID);
_replayStore.Remove<TComponent>(entityID, priority);
_upToDateComponentStore.Remove<TComponent>(entityID, priority);
return true;
}
return false;
}
public void Remove<TComponent>(int entityID, int priority) where TComponent : struct, IComponent
public void Remove<TComponent>(int entityID, int priority) where TComponent : struct
{
if (upToDateComponentStore.Remove<TComponent>(entityID, priority))
if (_upToDateComponentStore.Remove<TComponent>(entityID, priority))
{
replayStore.Remove<TComponent>(entityID, priority);
drawLayerManager.UnRegisterComponentWithLayer<TComponent>(entityID);
_replayStore.Remove<TComponent>(entityID, priority);
}
}
public bool UpToDateEntityIsEmpty(int entityID)
{
return upToDateComponentStore.EntityBitArray(entityID).AllFalse();
return _upToDateComponentStore.EntityBitArray(entityID).AllFalse();
}
}
}

View File

@ -1,118 +0,0 @@
using System;
using System.Collections.Generic;
using System.Linq;
using Encompass.Exceptions;
namespace Encompass
{
internal class DrawLayerManager
{
private readonly SortedList<int, int> layerOrder = new SortedList<int, int>();
private readonly Dictionary<int, ComponentStore> layerIndexToComponentStore = new Dictionary<int, ComponentStore>(512);
private readonly Dictionary<int, HashSet<GeneralRenderer>> layerIndexToGeneralRenderers = new Dictionary<int, HashSet<GeneralRenderer>>(512);
private readonly Dictionary<Type, Dictionary<int, int>> typeToEntityToLayer = new Dictionary<Type, Dictionary<int, int>>(512);
private Dictionary<Type, int> typeToIndex;
public IEnumerable<int> LayerOrder { get { return layerOrder.Values; } }
public DrawLayerManager(Dictionary<Type, int> typeToIndex)
{
this.typeToIndex = typeToIndex;
RegisterDrawLayer(0);
}
public void RegisterDrawLayer(int layer)
{
if (!layerIndexToComponentStore.ContainsKey(layer))
{
layerOrder.Add(layer, layer);
layerIndexToGeneralRenderers.Add(layer, new HashSet<GeneralRenderer>());
layerIndexToComponentStore.Add(layer, new ComponentStore(typeToIndex));
}
}
public void RegisterOrderedDrawable<TComponent>() where TComponent : struct, IComponent
{
if (!typeToEntityToLayer.ContainsKey(typeof(TComponent)))
{
typeToEntityToLayer.Add(typeof(TComponent), new Dictionary<int, int>(128));
}
foreach (var pair in layerIndexToComponentStore)
{
pair.Value.RegisterComponentType<TComponent>();
}
}
public void RegisterGeneralRendererWithLayer(GeneralRenderer renderer, int layer)
{
RegisterDrawLayer(layer);
var set = layerIndexToGeneralRenderers[layer];
set.Add(renderer);
}
public void UnregisterGeneralRendererWithLayer(GeneralRenderer renderer, int layer)
{
if (layerIndexToGeneralRenderers.ContainsKey(layer))
{
layerIndexToGeneralRenderers[layer].Remove(renderer);
}
}
public void AdjustRendererLayer(GeneralRenderer renderer, int oldLayer, int newLayer)
{
UnregisterGeneralRendererWithLayer(renderer, oldLayer);
RegisterGeneralRendererWithLayer(renderer, newLayer);
}
public void RegisterComponentWithLayer<TComponent>(int entityID, TComponent component, int layer) where TComponent : struct, IComponent
{
if (!layerIndexToComponentStore.ContainsKey(layer))
{
throw new UndefinedLayerException("Layer {0} is not defined. Use WorldBuilder.RegisterDrawLayer to register the layer.", layer);
}
if (typeToEntityToLayer[typeof(TComponent)].ContainsKey(entityID)) { UnRegisterComponentWithLayer<TComponent>(entityID); }
var set = layerIndexToComponentStore[layer];
set.Set<TComponent>(entityID, component);
typeToEntityToLayer[typeof(TComponent)].Add(entityID, layer);
}
public void UnRegisterComponentWithLayer<TComponent>(int entityID) where TComponent : struct, IComponent
{
if (!typeToEntityToLayer.ContainsKey(typeof(TComponent))) { return; }
if (typeToEntityToLayer[typeof(TComponent)].ContainsKey(entityID))
{
var layer = typeToEntityToLayer[typeof(TComponent)][entityID];
layerIndexToComponentStore[layer].ForceRemove<TComponent>(entityID);
}
typeToEntityToLayer[typeof(TComponent)].Remove(entityID);
}
public void UnRegisterEntityWithLayer(int entityID)
{
foreach (var store in layerIndexToComponentStore.Values)
{
store.Remove(entityID);
}
}
public IEnumerable<GeneralRenderer> GeneralRenderersByLayer(int layer)
{
return layerIndexToGeneralRenderers.ContainsKey(layer) ?
layerIndexToGeneralRenderers[layer] :
Enumerable.Empty<GeneralRenderer>();
}
public IEnumerable<(int, Type, IComponent)> AllInLayer(int layer)
{
return layerIndexToComponentStore.ContainsKey(layer) ?
layerIndexToComponentStore[layer].AllInterfaceTyped() :
Enumerable.Empty<(int, Type, IComponent)>();
}
}
}

View File

@ -3,7 +3,6 @@ using System.Reflection;
using System.Collections.Generic;
using System.Linq;
using Encompass.Exceptions;
using MoonTools.FastCollections;
namespace Encompass
{
@ -14,104 +13,121 @@ namespace Encompass
/// </summary>
public abstract class Engine : IEquatable<Engine>
{
internal Guid ID;
internal Guid _id;
internal readonly HashSet<Type> readTypes = new HashSet<Type>();
internal readonly HashSet<Type> readImmediateTypes = new HashSet<Type>();
internal readonly HashSet<Type> sendTypes = new HashSet<Type>();
internal readonly HashSet<Type> receiveTypes = new HashSet<Type>();
internal readonly HashSet<Type> writeTypes = new HashSet<Type>();
internal readonly HashSet<Type> writeImmediateTypes = new HashSet<Type>();
internal readonly HashSet<Type> queryWithTypes = new HashSet<Type>();
internal readonly HashSet<Type> queryWithoutTypes = new HashSet<Type>();
internal readonly Dictionary<Type, int> writePriorities = new Dictionary<Type, int>();
internal readonly int defaultWritePriority = 0;
internal readonly HashSet<Type> ReadTypes = new HashSet<Type>();
internal readonly HashSet<Type> ReadImmediateTypes = new HashSet<Type>();
internal readonly HashSet<Type> SendTypes = new HashSet<Type>();
internal readonly HashSet<Type> ReceiveTypes = new HashSet<Type>();
internal readonly HashSet<Type> WriteTypes = new HashSet<Type>();
internal readonly HashSet<Type> WriteImmediateTypes = new HashSet<Type>();
internal readonly HashSet<Type> QueryWithTypes = new HashSet<Type>();
internal readonly HashSet<Type> QueryWithoutTypes = new HashSet<Type>();
internal readonly HashSet<Type> AddTypes = new HashSet<Type>();
internal readonly Dictionary<Type, int> WritePriorities = new Dictionary<Type, int>();
internal readonly int DefaultWritePriority = 0;
/// <summary>
/// If false, the Engine will ignore time dilation.
/// </summary>
internal bool usesTimeDilation = true;
public bool TimeDilationActive { get => usesTimeDilation && timeManager.TimeDilationActive; }
internal bool _usesTimeDilation = true;
public bool TimeDilationActive { get => _usesTimeDilation && _timeManager.TimeDilationActive; }
private EntityManager entityManager;
private MessageManager messageManager;
private ComponentManager componentManager;
private TimeManager timeManager;
private TrackingManager trackingManager;
private EntityManager _entityManager;
private MessageManager _messageManager;
private ComponentManager _componentManager;
private TimeManager _timeManager;
private TrackingManager _trackingManager;
private EntitySetQuery entityQuery;
private EntitySetQuery _entityQuery;
private HashSet<int> _trackedEntities = new HashSet<int>();
private readonly HashSet<int> _trackedEntities = new HashSet<int>();
protected IEnumerable<Entity> TrackedEntities
{
get
{
foreach (var entityID in _trackedEntities)
{
yield return entityManager.GetEntity(entityID);
yield return _entityManager.GetEntity(entityID);
}
}
}
private HashSet<int> _newlyCreatedEntities = new HashSet<int>();
private readonly HashSet<int> _newlyCreatedEntities = new HashSet<int>();
protected Engine()
{
ID = Guid.NewGuid();
_id = Guid.NewGuid();
var addsAttribute = GetType().GetCustomAttribute<Adds>(false);
if (addsAttribute != null)
{
AddTypes = addsAttribute.addTypes;
}
var sendsAttribute = GetType().GetCustomAttribute<Sends>(false);
if (sendsAttribute != null)
{
sendTypes = sendsAttribute.sendTypes;
SendTypes = sendsAttribute.SendTypes;
}
var activatesAttribute = GetType().GetCustomAttribute<WritesImmediate>(false);
if (activatesAttribute != null)
{
writeImmediateTypes = activatesAttribute.writeImmediateTypes;
WriteImmediateTypes = activatesAttribute.WriteImmediateTypes;
}
var defaultWritePriorityAttribute = GetType().GetCustomAttribute<DefaultWritePriority>(false);
if (defaultWritePriorityAttribute != null)
{
defaultWritePriority = defaultWritePriorityAttribute.writePriority;
DefaultWritePriority = defaultWritePriorityAttribute.WritePriority;
}
foreach (var writesAttribute in GetType().GetCustomAttributes<Writes>(false))
{
writeTypes.UnionWith(writesAttribute.writeTypes);
writePriorities = new Dictionary<Type, int>[2] { writePriorities, writesAttribute.priorities }.SelectMany(dict => dict).ToDictionary(pair => pair.Key, pair => pair.Value);
WriteTypes.UnionWith(writesAttribute.WriteTypes);
WritePriorities = new Dictionary<Type, int>[2] { WritePriorities, writesAttribute.Priorities }.SelectMany(dict => dict).ToDictionary(pair => pair.Key, pair => pair.Value);
}
var receivesAttribute = GetType().GetCustomAttribute<Receives>(false);
if (receivesAttribute != null)
{
receiveTypes = receivesAttribute.receiveTypes;
ReceiveTypes = receivesAttribute.ReceiveTypes;
}
var readsAttribute = GetType().GetCustomAttribute<Reads>(false);
if (readsAttribute != null)
{
readTypes = readsAttribute.readTypes;
ReadTypes = readsAttribute.ReadTypes;
}
var readsImmediateAttribute = GetType().GetCustomAttribute<ReadsImmediate>(false);
if (readsImmediateAttribute != null)
{
readImmediateTypes = readsImmediateAttribute.readImmediateTypes;
ReadImmediateTypes = readsImmediateAttribute.ReadImmediateTypes;
}
var queryWithAttribute = GetType().GetCustomAttribute<QueryWith>(false);
if (queryWithAttribute != null)
{
queryWithTypes = queryWithAttribute.queryWithTypes;
QueryWithTypes = queryWithAttribute.QueryWithTypes;
}
foreach (var queryType in QueryWithTypes)
{
ReadTypes.Add(queryType);
}
var queryWithoutAttribute = GetType().GetCustomAttribute<QueryWithout>(false);
if (queryWithoutAttribute != null)
{
queryWithoutTypes = queryWithoutAttribute.queryWithoutTypes;
QueryWithoutTypes = queryWithoutAttribute.QueryWithoutTypes;
}
foreach (var queryType in QueryWithoutTypes)
{
ReadTypes.Add(queryType);
}
}
@ -127,42 +143,42 @@ namespace Encompass
public bool Equals(Engine other)
{
return other.ID == ID;
return other._id == _id;
}
public override int GetHashCode()
{
return ID.GetHashCode();
return HashCode.Combine(_id);
}
internal void AssignEntityManager(EntityManager entityManager)
{
this.entityManager = entityManager;
_entityManager = entityManager;
}
internal void AssignComponentManager(ComponentManager componentManager)
{
this.componentManager = componentManager;
_componentManager = componentManager;
}
internal void AssignMessageManager(MessageManager messageManager)
{
this.messageManager = messageManager;
_messageManager = messageManager;
}
internal void AssignTimeManager(TimeManager timeManager)
{
this.timeManager = timeManager;
_timeManager = timeManager;
}
internal void AssignTrackingManager(TrackingManager trackingManager)
{
this.trackingManager = trackingManager;
_trackingManager = trackingManager;
}
internal void CheckMessageRead<TMessage>() where TMessage : struct, IMessage
internal void CheckMessageRead<TMessage>() where TMessage : struct
{
if (!receiveTypes.Contains(typeof(TMessage)))
if (!ReceiveTypes.Contains(typeof(TMessage)))
{
throw new IllegalReadException("Engine {0} tried to read undeclared Message {1}", this.GetType().Name, typeof(TMessage).Name);
}
@ -189,7 +205,7 @@ namespace Encompass
/// </summary>
protected Entity CreateEntity()
{
var entity = entityManager.CreateEntity();
var entity = _entityManager.CreateEntity();
_newlyCreatedEntities.Add(entity.ID);
return entity;
}
@ -197,9 +213,9 @@ namespace Encompass
/// <summary>
/// Returns true if an Entity with the specified ID exists.
/// </summary>
protected bool EntityExists(Entity entity)
protected bool EntityExists(in Entity entity)
{
return entityManager.EntityExists(entity.ID);
return _entityManager.EntityExists(entity.ID);
}
/// <summary>
@ -207,50 +223,52 @@ namespace Encompass
/// </summary>
protected bool EntityExists(int entityID)
{
return entityManager.EntityExists(entityID);
}
/// <summary>
/// Returns an Entity containing the specified Component type.
/// </summary>
protected Entity ReadEntity<TComponent>() where TComponent : struct, IComponent
{
return entityManager.GetEntity(ReadComponentHelper<TComponent>().Item2);
return _entityManager.EntityExists(entityID);
}
/// <summary>
/// Returns all Entities containing the specified Component type.
/// </summary>
protected IEnumerable<Entity> ReadEntities<TComponent>() where TComponent : struct, IComponent
protected ReadOnlySpan<Entity> ReadEntities<TComponent>() where TComponent : struct
{
foreach (var pair in ReadComponentsHelper<TComponent>())
{
yield return entityManager.GetEntity(pair.Item2);
}
}
// these next two are for the ComponentMessageEmitter only
internal IEnumerable<TComponent> ReadComponentsFromWorld<TComponent>() where TComponent : struct, IComponent
{
return componentManager.GetComponentsByType<TComponent>();
}
private IEnumerable<(TComponent, int)> ReadComponentsHelper<TComponent>() where TComponent : struct, IComponent
{
var immediateRead = readImmediateTypes.Contains(typeof(TComponent));
var existingRead = readTypes.Contains(typeof(TComponent));
var immediateRead = ReadImmediateTypes.Contains(typeof(TComponent));
var existingRead = ReadTypes.Contains(typeof(TComponent));
if (existingRead && immediateRead)
{
return componentManager.ReadExistingAndImmediateComponentsByType<TComponent>();
return _componentManager.GetExistingAndImmediateEntities<TComponent>();
}
else if (existingRead)
{
return componentManager.GetComponentsIncludingEntity<TComponent>();
return _componentManager.GetExistingEntities<TComponent>();
}
else if (immediateRead)
{
return componentManager.ReadImmediateComponentsByType<TComponent>();
return _componentManager.GetImmediateEntities<TComponent>();
}
else
{
throw new IllegalReadException("Engine {0} tried to read undeclared Component {1}", GetType().Name, typeof(TComponent).Name);
}
}
/// <summary>
/// Returns an Entity containing the specified Component type.
/// </summary>
protected ref readonly Entity ReadEntity<TComponent>() where TComponent : struct
{
var immediateRead = ReadImmediateTypes.Contains(typeof(TComponent));
var existingRead = ReadTypes.Contains(typeof(TComponent));
if (existingRead && immediateRead)
{
return ref _componentManager.ExistingOrImmediateSingularEntity<TComponent>();
}
else if (existingRead)
{
return ref _componentManager.ExistingSingularEntity<TComponent>();
}
else if (immediateRead)
{
return ref _componentManager.ImmediateSingularEntity<TComponent>();
}
else
{
@ -261,40 +279,21 @@ namespace Encompass
/// <summary>
/// Returns all of the Components with the specified Component Type.
/// </summary>
protected IEnumerable<TComponent> ReadComponents<TComponent>() where TComponent : struct, IComponent
protected ReadOnlySpan<TComponent> ReadComponents<TComponent>() where TComponent : struct
{
foreach (var pair in ReadComponentsHelper<TComponent>())
{
yield return pair.Item1;
}
}
/// <summary>
/// Returns all of the components of the specified type including an Entity reference for each Component.
/// </summary>
protected IEnumerable<(TComponent, Entity)> ReadComponentsIncludingEntity<TComponent>() where TComponent : struct, IComponent
{
foreach (var (component, id) in ReadComponentsHelper<TComponent>())
{
yield return (component, entityManager.GetEntity(id));
}
}
private (TComponent, int) ReadComponentHelper<TComponent>() where TComponent : struct, IComponent
{
var immediateRead = readImmediateTypes.Contains(typeof(TComponent));
var existingRead = readTypes.Contains(typeof(TComponent));
var immediateRead = ReadImmediateTypes.Contains(typeof(TComponent));
var existingRead = ReadTypes.Contains(typeof(TComponent));
if (existingRead && immediateRead)
{
return componentManager.ReadFirstExistingOrImmediateComponentByType<TComponent>();
return _componentManager.ReadExistingAndImmediateComponentsByType<TComponent>();
}
else if (existingRead)
{
return componentManager.ReadFirstExistingComponentByType<TComponent>();
return _componentManager.GetExistingComponents<TComponent>();
}
else if (immediateRead)
{
return componentManager.ReadFirstImmediateComponentByType<TComponent>();
return _componentManager.ReadImmediateComponentsByType<TComponent>();
}
else
{
@ -305,38 +304,21 @@ namespace Encompass
/// <summary>
/// Returns a Component with the specified Component Type. If multiples exist, an arbitrary Component is returned.
/// </summary>
protected TComponent ReadComponent<TComponent>() where TComponent : struct, IComponent
protected ref readonly TComponent ReadComponent<TComponent>() where TComponent : struct
{
return ReadComponentHelper<TComponent>().Item1;
}
/// <summary>
/// Returns a component of the specified type including its Entity reference. If multiples exist, an arbitrary Component is returned.
/// </summary>
protected (TComponent, Entity) ReadComponentIncludingEntity<TComponent>() where TComponent : struct, IComponent
{
var (component, id) = ReadComponentHelper<TComponent>();
return (component, entityManager.GetEntity(id));
}
/// <summary>
/// Returns true if any Component with the specified Component Type exists.
/// </summary>
protected bool SomeComponent<TComponent>() where TComponent : struct, IComponent
{
var immediateRead = readImmediateTypes.Contains(typeof(TComponent));
var existingRead = readTypes.Contains(typeof(TComponent));
var immediateRead = ReadImmediateTypes.Contains(typeof(TComponent));
var existingRead = ReadTypes.Contains(typeof(TComponent));
if (existingRead && immediateRead)
{
return componentManager.SomeExistingOrImmediateComponent<TComponent>();
return ref _componentManager.ExistingOrImmediateSingular<TComponent>();
}
else if (existingRead)
{
return componentManager.SomeExistingComponent<TComponent>();
return ref _componentManager.ExistingSingular<TComponent>();
}
else if (immediateRead)
{
return componentManager.SomeImmediateComponent<TComponent>();
return ref _componentManager.ImmediateSingular<TComponent>();
}
else
{
@ -344,21 +326,46 @@ namespace Encompass
}
}
private TComponent GetComponentHelper<TComponent>(int entityID) where TComponent : struct, IComponent
/// <summary>
/// Returns true if any Component with the specified Component Type exists.
/// </summary>
protected bool SomeComponent<TComponent>() where TComponent : struct
{
var immediateRead = readImmediateTypes.Contains(typeof(TComponent));
var existingRead = readTypes.Contains(typeof(TComponent));
var immediateRead = ReadImmediateTypes.Contains(typeof(TComponent));
var existingRead = ReadTypes.Contains(typeof(TComponent));
if (existingRead && immediateRead)
{
return componentManager.ReadImmediateOrExistingComponentByEntityAndType<TComponent>(entityID);
return _componentManager.SomeExistingOrImmediateComponent<TComponent>();
}
else if (existingRead)
{
return componentManager.ReadExistingComponentByEntityAndType<TComponent>(entityID);
return _componentManager.SomeExistingComponent<TComponent>();
}
else if (immediateRead)
{
return componentManager.ReadImmediateComponentByEntityAndType<TComponent>(entityID);
return _componentManager.SomeImmediateComponent<TComponent>();
}
else
{
throw new IllegalReadException("Engine {0} tried to read undeclared Component {1}", GetType().Name, typeof(TComponent).Name);
}
}
private ref TComponent GetComponentHelper<TComponent>(int entityID) where TComponent : struct
{
var immediateRead = ReadImmediateTypes.Contains(typeof(TComponent));
var existingRead = ReadTypes.Contains(typeof(TComponent));
if (existingRead && immediateRead)
{
return ref _componentManager.ReadImmediateOrExistingComponentByEntityAndType<TComponent>(entityID);
}
else if (existingRead)
{
return ref _componentManager.ReadExistingComponentByEntityAndType<TComponent>(entityID);
}
else if (immediateRead)
{
return ref _componentManager.ReadImmediateComponentByEntityAndType<TComponent>(entityID);
}
else
{
@ -375,9 +382,9 @@ namespace Encompass
/// <exception cref="Encompass.Exceptions.IllegalReadException">
/// Thrown when the Engine does not declare that it reads the given Component Type.
/// </exception>
protected TComponent GetComponent<TComponent>(Entity entity) where TComponent : struct, IComponent
protected ref readonly TComponent GetComponent<TComponent>(in Entity entity) where TComponent : struct
{
return GetComponentHelper<TComponent>(entity.ID);
return ref GetComponentHelper<TComponent>(entity.ID);
}
/// <summary>
@ -386,22 +393,22 @@ namespace Encompass
/// <exception cref="Encompass.Exceptions.IllegalReadException">
/// Thrown when the Engine does not declare that is Reads the given Component Type.
/// </exception>
protected bool HasComponent<TComponent>(Entity entity) where TComponent : struct, IComponent
protected bool HasComponent<TComponent>(in Entity entity) where TComponent : struct
{
var immediateRead = readImmediateTypes.Contains(typeof(TComponent));
var existingRead = readTypes.Contains(typeof(TComponent));
var immediateRead = ReadImmediateTypes.Contains(typeof(TComponent));
var existingRead = ReadTypes.Contains(typeof(TComponent));
if (immediateRead && existingRead)
{
return componentManager.HasExistingOrImmediateComponent<TComponent>(entity.ID);
return _componentManager.HasExistingOrImmediateComponent<TComponent>(entity.ID);
}
else if (existingRead)
{
return componentManager.HasExistingComponent<TComponent>(entity.ID);
return _componentManager.HasExistingComponent<TComponent>(entity.ID);
}
else if (immediateRead)
{
return componentManager.HasImmediateComponent<TComponent>(entity.ID);
return _componentManager.HasImmediateComponent<TComponent>(entity.ID);
}
else
{
@ -415,22 +422,22 @@ namespace Encompass
/// <exception cref="Encompass.Exceptions.IllegalReadException">
/// Thrown when the Engine does not declare that is Reads the given Component Type.
/// </exception>
protected bool HasComponent(Entity entity, Type type)
protected bool HasComponent(in Entity entity, Type type)
{
var immediateRead = readImmediateTypes.Contains(type);
var existingRead = readTypes.Contains(type);
var immediateRead = ReadImmediateTypes.Contains(type);
var existingRead = ReadTypes.Contains(type);
if (immediateRead && existingRead)
{
return componentManager.HasExistingOrImmediateComponent(entity.ID, type);
return _componentManager.HasExistingOrImmediateComponent(entity.ID, type);
}
else if (existingRead)
{
return componentManager.HasExistingComponent(entity.ID, type);
return _componentManager.HasExistingComponent(entity.ID, type);
}
else if (immediateRead)
{
return componentManager.HasImmediateComponent(entity.ID, type);
return _componentManager.HasImmediateComponent(entity.ID, type);
}
else
{
@ -444,37 +451,32 @@ namespace Encompass
/// <exception cref="Encompass.Exceptions.IllegalWriteException">
/// Thrown when the Engine does not declare that it Writes the given Component Type.
/// </exception>
protected void SetComponent<TComponent>(Entity entity, TComponent component) where TComponent : struct, IComponent
protected void SetComponent<TComponent>(in Entity entity, in TComponent component) where TComponent : struct
{
var priority = writePriorities.ContainsKey(typeof(TComponent)) ? writePriorities[typeof(TComponent)] : defaultWritePriority;
var priority = WritePriorities.ContainsKey(typeof(TComponent)) ? WritePriorities[typeof(TComponent)] : DefaultWritePriority;
if (!writeTypes.Contains(typeof(TComponent)))
if (!WriteTypes.Contains(typeof(TComponent)))
{
throw new IllegalWriteException("Engine {0} tried to update undeclared Component {1}", GetType().Name, typeof(TComponent).Name);
}
bool written;
if (writeImmediateTypes.Contains(typeof(TComponent)))
if (WriteImmediateTypes.Contains(typeof(TComponent)))
{
written = componentManager.AddImmediateComponent(entity.ID, component, priority);
written = _componentManager.AddImmediateComponent(entity.ID, component, priority);
if (written)
{
trackingManager.ImmediateUpdateTracking(entity.ID, typeof(TComponent));
_trackingManager.ImmediateUpdateTracking(entity.ID, typeof(TComponent));
}
}
else
{
written = componentManager.UpdateComponent(entity.ID, component, priority);
written = _componentManager.UpdateComponent(entity.ID, component, priority);
}
if (!componentManager.HasExistingComponent<TComponent>(entity.ID))
if (!_componentManager.HasExistingComponent<TComponent>(entity.ID))
{
trackingManager.RegisterAddition(entity.ID, typeof(TComponent));
}
if (written && component is IDrawableComponent drawableComponent)
{
componentManager.RegisterDrawableComponent(entity.ID, component, drawableComponent.Layer);
_trackingManager.RegisterAddition(entity.ID, typeof(TComponent));
}
}
@ -484,29 +486,29 @@ namespace Encompass
/// <exception cref="Encompass.Exceptions.IllegalWriteException">
/// Thrown when the Engine does not declare that it Writes the given Component Type.
/// </exception>
protected void AddComponent<TComponent>(Entity entity, TComponent component) where TComponent : struct, IComponent
protected void AddComponent<TComponent>(in Entity entity, in TComponent component) where TComponent : struct
{
if (!AddTypes.Contains(typeof(TComponent)))
{
throw new IllegalWriteException("Engine {0} tried to add undeclared Component {1}", GetType().Name, typeof(TComponent).Name);
}
if (!EntityCreatedThisFrame(entity.ID))
{
throw new IllegalWriteException("AddComponent used on Entity that was not created in this context. Use SetComponent instead.");
}
if (writeImmediateTypes.Contains(typeof(TComponent)))
if (WriteImmediateTypes.Contains(typeof(TComponent)))
{
componentManager.AddImmediateComponent(entity.ID, component);
trackingManager.ImmediateUpdateTracking(entity.ID, typeof(TComponent));
_componentManager.AddImmediateComponent(entity.ID, component);
_trackingManager.ImmediateUpdateTracking(entity.ID, typeof(TComponent));
}
else
{
componentManager.AddComponent(entity.ID, component);
_componentManager.AddComponent(entity.ID, component);
}
trackingManager.RegisterAddition(entity.ID, typeof(TComponent));
if (component is IDrawableComponent drawableComponent)
{
componentManager.RegisterDrawableComponent(entity.ID, component, drawableComponent.Layer);
}
_trackingManager.RegisterAddition(entity.ID, typeof(TComponent));
}
/// <summary>
@ -515,32 +517,32 @@ namespace Encompass
/// <exception cref="Encompass.Exceptions.IllegalSendException">
/// Thrown when the Engine does not declare that it Sends the Message Type.
/// </exception>
protected void SendMessage<TMessage>(TMessage message) where TMessage : struct, IMessage
protected void SendMessage<TMessage>(in TMessage message) where TMessage : struct
{
if (!sendTypes.Contains(typeof(TMessage)))
if (!SendTypes.Contains(typeof(TMessage)))
{
throw new IllegalSendException("Engine {0} tried to send undeclared Message {1}", GetType().Name, typeof(TMessage).Name);
}
messageManager.AddMessage(message);
_messageManager.AddMessage(message);
}
/// <summary>
/// Sends a message after the specified number of seconds, respecting time dilation.
/// </summary>
/// <param name="time">The time in seconds that will elapse before the message is sent.</param>
protected void SendMessage<TMessage>(TMessage message, double time) where TMessage : struct, IMessage
protected void SendMessage<TMessage>(in TMessage message, double time) where TMessage : struct
{
messageManager.AddMessage(message, time);
_messageManager.AddMessage(message, time);
}
/// <summary>
/// Sends a message after the specified number of seconds, ignoring time dilation.
/// </summary>
/// <param name="time">The time in seconds that will elapse before the message is sent.</param>
protected void SendMessageIgnoringTimeDilation<TMessage>(TMessage message, double time) where TMessage : struct, IMessage
protected void SendMessageIgnoringTimeDilation<TMessage>(in TMessage message, double time) where TMessage : struct
{
messageManager.AddMessageIgnoringTimeDilation(message, time);
_messageManager.AddMessageIgnoringTimeDilation(message, time);
}
/// <summary>
@ -549,10 +551,10 @@ namespace Encompass
/// <exception cref="Encompass.Exceptions.IllegalReadException">
/// Thrown when the Engine does not declare that it Receives the specified Message Type.
/// </exception>
protected IEnumerable<TMessage> ReadMessages<TMessage>() where TMessage : struct, IMessage
protected ReadOnlySpan<TMessage> ReadMessages<TMessage>() where TMessage : struct
{
CheckMessageRead<TMessage>();
return messageManager.GetMessagesByType<TMessage>();
return _messageManager.GetMessagesByType<TMessage>();
}
/// <summary>
@ -561,10 +563,10 @@ namespace Encompass
/// <exception cref="Encompass.Exceptions.IllegalReadException">
/// Thrown when the Engine does not declare that it Receives the specified Message Type.
/// </exception>
protected TMessage ReadMessage<TMessage>() where TMessage : struct, IMessage
protected ref readonly TMessage ReadMessage<TMessage>() where TMessage : struct
{
CheckMessageRead<TMessage>();
return messageManager.First<TMessage>();
return ref _messageManager.First<TMessage>();
}
/// <summary>
@ -573,26 +575,26 @@ namespace Encompass
/// <exception cref="Encompass.Exceptions.IllegalReadException">
/// Thrown when the Engine does not declare that it Receives the specified Message Type.
/// </exception>
protected bool SomeMessage<TMessage>() where TMessage : struct, IMessage
protected bool SomeMessage<TMessage>() where TMessage : struct
{
CheckMessageRead<TMessage>();
return messageManager.Any<TMessage>();
return _messageManager.Any<TMessage>();
}
/// <summary>
/// Destroys the specified Entity. This also removes all of the Components associated with the Entity.
/// Entity destruction takes place after all the Engines have been processed by World Update.
/// </summary>
protected void Destroy(Entity entity)
protected void Destroy(in Entity entity)
{
entityManager.MarkForDestroy(entity.ID);
_entityManager.MarkForDestroy(entity.ID);
}
/// <summary>
/// Destroys an arbitrary Entity containing a Component of the specified Type.
/// Entity destruction takes place after all the Engines have been processed by World Update.
/// </summary>
protected void DestroyWith<TComponent>() where TComponent : struct, IComponent
protected void DestroyWith<TComponent>() where TComponent : struct
{
Destroy(ReadEntity<TComponent>());
}
@ -601,7 +603,7 @@ namespace Encompass
/// Destroys all Entities containing a Component of the specified Type.
/// Entity destruction takes place after all the Engines have been processed by World Update.
/// </summary>
protected void DestroyAllWith<TComponent>() where TComponent : struct, IComponent
protected void DestroyAllWith<TComponent>() where TComponent : struct
{
foreach (var entity in ReadEntities<TComponent>())
{
@ -614,30 +616,30 @@ namespace Encompass
/// Note that the Engine must Read the Component type that is being removed.
/// If a Component with the specified type does not exist on the Entity, returns false and does not mutate the Entity.
/// </summary>
protected void RemoveComponent<TComponent>(Entity entity) where TComponent : struct, IComponent
protected void RemoveComponent<TComponent>(in Entity entity) where TComponent : struct
{
var priority = writePriorities.ContainsKey(typeof(TComponent)) ? writePriorities[typeof(TComponent)] : defaultWritePriority;
var priority = WritePriorities.ContainsKey(typeof(TComponent)) ? WritePriorities[typeof(TComponent)] : DefaultWritePriority;
if (!writeTypes.Contains(typeof(TComponent)))
if (!WriteTypes.Contains(typeof(TComponent)))
{
throw new IllegalWriteException("Engine {0} tried to remove undeclared Component {1}. Declare with Writes attribute.", GetType().Name, typeof(TComponent).Name);
}
if (writeImmediateTypes.Contains(typeof(TComponent)))
if (WriteImmediateTypes.Contains(typeof(TComponent)))
{
if (componentManager.RemoveImmediate<TComponent>(entity.ID, priority))
if (_componentManager.RemoveImmediate<TComponent>(entity.ID, priority))
{
trackingManager.ImmediateUpdateTracking(entity.ID, typeof(TComponent));
_trackingManager.ImmediateUpdateTracking(entity.ID, typeof(TComponent));
}
}
else
{
componentManager.Remove<TComponent>(entity.ID, priority);
_componentManager.Remove<TComponent>(entity.ID, priority);
}
if (componentManager.HasExistingComponent<TComponent>(entity.ID))
if (_componentManager.HasExistingComponent<TComponent>(entity.ID))
{
trackingManager.RegisterRemoval(entity.ID, typeof(TComponent));
_trackingManager.RegisterRemoval(entity.ID, typeof(TComponent));
}
}
@ -652,7 +654,7 @@ namespace Encompass
/// <param name="easeOutTime">The time that will elapse before time is fully undilated.</param>
protected void ActivateTimeDilation(double factor, double easeInTime, double activeTime, double easeOutTime)
{
timeManager.ActivateTimeDilation(factor, easeInTime, activeTime, easeOutTime);
_timeManager.ActivateTimeDilation(factor, easeInTime, activeTime, easeOutTime);
}
/// <summary>
@ -667,7 +669,7 @@ namespace Encompass
/// <param name="easeOutTime">The time that will elapse before time is fully undilated.</param>
protected void ActivateTimeDilation(double factor, double easeInTime, System.Func<double, double, double, double, double> easeInFunction, double activeTime, double easeOutTime)
{
timeManager.ActivateTimeDilation(factor, easeInTime, easeInFunction, activeTime, easeOutTime);
_timeManager.ActivateTimeDilation(factor, easeInTime, easeInFunction, activeTime, easeOutTime);
}
/// <summary>
@ -682,7 +684,7 @@ namespace Encompass
/// <param name="easeOutFunction">An easing function for the easing out of time dilation.</param>
protected void ActivateTimeDilation(double factor, double easeInTime, double activeTime, double easeOutTime, System.Func<double, double, double, double, double> easeOutFunction)
{
timeManager.ActivateTimeDilation(factor, easeInTime, activeTime, easeOutTime, easeOutFunction);
_timeManager.ActivateTimeDilation(factor, easeInTime, activeTime, easeOutTime, easeOutFunction);
}
/// <summary>
@ -698,7 +700,7 @@ namespace Encompass
/// <param name="easeOutFunction">An easing function for the easing out of time dilation.</param>
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)
{
timeManager.ActivateTimeDilation(factor, easeInTime, easeInFunction, activeTime, easeOutTime, easeOutFunction);
_timeManager.ActivateTimeDilation(factor, easeInTime, easeInFunction, activeTime, easeOutTime, easeOutFunction);
}
/// <summary>
@ -707,38 +709,38 @@ namespace Encompass
/// <typeparam name="TMessage">The Message subtype.</typeparam>
/// <param name="entity">The entity that all messages in the IEnumerable refer to.</param>
/// <returns></returns>
protected IEnumerable<TMessage> ReadMessagesWithEntity<TMessage>(Entity entity) where TMessage : struct, IMessage, IHasEntity
protected IEnumerable<TMessage> ReadMessagesWithEntity<TMessage>(in Entity entity) where TMessage : struct, IHasEntity
{
CheckMessageRead<TMessage>();
return messageManager.WithEntity<TMessage>(entity.ID);
return _messageManager.WithEntity<TMessage>(entity.ID);
}
/// <summary>
/// Efficiently reads a single Message of a given type that references a given Entity.
/// It is recommended to use this method in conjunction with SomeMessageWithEntity to prevent errors.
/// </summary>
protected TMessage ReadMessageWithEntity<TMessage>(Entity entity) where TMessage : struct, IMessage, IHasEntity
protected ref readonly TMessage ReadMessageWithEntity<TMessage>(in Entity entity) where TMessage : struct, IHasEntity
{
CheckMessageRead<TMessage>();
return messageManager.WithEntitySingular<TMessage>(entity.ID);
return ref _messageManager.WithEntitySingular<TMessage>(entity.ID);
}
/// <summary>
/// Efficiently checks if any Message of a given type referencing a given Entity exists.
/// </summary>
protected bool SomeMessageWithEntity<TMessage>(Entity entity) where TMessage : struct, IMessage, IHasEntity
protected bool SomeMessageWithEntity<TMessage>(in Entity entity) where TMessage : struct, IHasEntity
{
CheckMessageRead<TMessage>();
return messageManager.SomeWithEntity<TMessage>(entity.ID);
return _messageManager.SomeWithEntity<TMessage>(entity.ID);
}
internal void CheckAndUpdateTracking(int entityID)
{
if (_trackedEntities.Contains(entityID) && !entityQuery.CheckEntity(entityID, componentManager.ExistingBits))
if (_trackedEntities.Contains(entityID) && !_entityQuery.CheckEntity(entityID, _componentManager.ExistingBits))
{
_trackedEntities.Remove(entityID);
}
else if (!_trackedEntities.Contains(entityID) && entityQuery.CheckEntity(entityID, componentManager.ExistingBits))
else if (!_trackedEntities.Contains(entityID) && _entityQuery.CheckEntity(entityID, _componentManager.ExistingBits))
{
_trackedEntities.Add(entityID);
}
@ -746,11 +748,11 @@ namespace Encompass
internal void ImmediateCheckAndUpdateTracking(int entityID)
{
if (_trackedEntities.Contains(entityID) && !entityQuery.ImmediateCheckEntity(entityID, componentManager.ImmediateBits, componentManager.ExistingBits))
if (_trackedEntities.Contains(entityID) && !_entityQuery.ImmediateCheckEntity(entityID, _componentManager.ImmediateBits, _componentManager.ExistingBits))
{
_trackedEntities.Remove(entityID);
}
else if (!_trackedEntities.Contains(entityID) && entityQuery.ImmediateCheckEntity(entityID, componentManager.ImmediateBits, componentManager.ExistingBits))
else if (!_trackedEntities.Contains(entityID) && _entityQuery.ImmediateCheckEntity(entityID, _componentManager.ImmediateBits, _componentManager.ExistingBits))
{
_trackedEntities.Add(entityID);
}
@ -762,30 +764,34 @@ namespace Encompass
internal void BuildEntityQuery()
{
var withMask = BitSet512.Zero;
foreach (var type in queryWithTypes)
foreach (var type in QueryWithTypes)
{
withMask = withMask.Set(componentManager.TypeToIndex[type]);
if (!_componentManager.TypeToIndex.ContainsKey(type)) { _componentManager.TypeToIndex.Add(type, _componentManager.TypeToIndex.Count); }
withMask = withMask.Set(_componentManager.TypeToIndex[type]);
}
var withoutMask = BitSet512.Zero;
foreach (var type in queryWithoutTypes)
foreach (var type in QueryWithoutTypes)
{
withoutMask = withoutMask.Set(componentManager.TypeToIndex[type]);
if (!_componentManager.TypeToIndex.ContainsKey(type)) { _componentManager.TypeToIndex.Add(type, _componentManager.TypeToIndex.Count); }
withoutMask = withoutMask.Set(_componentManager.TypeToIndex[type]);
}
var immediateMask = BitSet512.Zero;
foreach (var type in readImmediateTypes)
foreach (var type in ReadImmediateTypes)
{
immediateMask = immediateMask.Set(componentManager.TypeToIndex[type]);
if (!_componentManager.TypeToIndex.ContainsKey(type)) { _componentManager.TypeToIndex.Add(type, _componentManager.TypeToIndex.Count); }
immediateMask = immediateMask.Set(_componentManager.TypeToIndex[type]);
}
var existingMask = BitSet512.Zero;
foreach (var type in readTypes)
foreach (var type in ReadTypes)
{
existingMask = existingMask.Set(componentManager.TypeToIndex[type]);
if (!_componentManager.TypeToIndex.ContainsKey(type)) { _componentManager.TypeToIndex.Add(type, _componentManager.TypeToIndex.Count); }
existingMask = existingMask.Set(_componentManager.TypeToIndex[type]);
}
entityQuery = new EntitySetQuery(
_entityQuery = new EntitySetQuery(
withMask & immediateMask,
withMask & existingMask,
withoutMask & immediateMask,

View File

@ -6,27 +6,27 @@ namespace Encompass
/// A Spawner is a special type of Engine that runs a Spawn method in response to each Message it receives.
/// Spawners are useful for organizing the building of new Entities in your game.
/// </summary>
public abstract class Spawner<TMessage> : Engine where TMessage : struct, IMessage
public abstract class Spawner<TMessage> : Engine where TMessage : struct
{
protected Spawner() : base()
{
var readsAttribute = GetType().GetCustomAttribute<Reads>(false);
if (readsAttribute != null)
{
readsAttribute.readTypes.Add(typeof(TMessage));
readsAttribute.ReadTypes.Add(typeof(TMessage));
}
receiveTypes.Add(typeof(TMessage));
ReceiveTypes.Add(typeof(TMessage));
}
public override void Update(double dt)
{
foreach (var message in ReadMessages<TMessage>())
foreach (ref readonly var message in ReadMessages<TMessage>())
{
Spawn(message);
}
}
protected abstract void Spawn(TMessage message);
protected abstract void Spawn(in TMessage message);
}
}

View File

@ -37,7 +37,7 @@ namespace Encompass
public override int GetHashCode()
{
return ID.GetHashCode();
return HashCode.Combine(ID);
}
}
}

View File

@ -5,48 +5,48 @@ namespace Encompass
{
internal class EntityManager
{
private readonly int entityCapacity;
private readonly IDManager idManager = new IDManager();
private readonly HashSet<int> IDs = new HashSet<int>();
private readonly int _entityCapacity;
private readonly IDManager _idManager = new IDManager();
private readonly HashSet<int> _ids = new HashSet<int>();
private readonly HashSet<int> entitiesMarkedForDestroy = new HashSet<int>();
private readonly HashSet<int> _entitiesMarkedForDestroy = new HashSet<int>();
private readonly ComponentManager componentManager;
private readonly ComponentManager _componentManager;
public IEnumerable<int> EntityIDs
{
get { return IDs; }
get { return _ids; }
}
public EntityManager(ComponentManager componentManager, int entityCapacity)
{
this.componentManager = componentManager;
this.entityCapacity = entityCapacity;
_componentManager = componentManager;
_entityCapacity = entityCapacity;
}
private int NextID()
{
return idManager.NextID();
return _idManager.NextID();
}
public Entity CreateEntity()
{
if (IDs.Count < entityCapacity)
if (_ids.Count < _entityCapacity)
{
var id = NextID();
var entity = new Entity(id);
IDs.Add(id);
_ids.Add(id);
return entity;
}
else
{
throw new EntityOverflowException("The number of entities has exceeded the entity capacity of {0}", entityCapacity);
throw new EntityOverflowException("The number of entities has exceeded the entity capacity of {0}", _entityCapacity);
}
}
public bool EntityExists(int id)
{
return IDs.Contains(id);
return _ids.Contains(id);
}
public Entity GetEntity(int id)
@ -61,20 +61,20 @@ namespace Encompass
public void MarkForDestroy(int entityID)
{
entitiesMarkedForDestroy.Add(entityID);
_entitiesMarkedForDestroy.Add(entityID);
}
public void DestroyMarkedEntities(IEnumerable<Engine> engines)
{
foreach (var entityID in entitiesMarkedForDestroy)
foreach (var entityID in _entitiesMarkedForDestroy)
{
foreach (var engine in engines) { engine.RegisterDestroyedEntity(entityID); }
componentManager.MarkAllComponentsOnEntityForRemoval(entityID);
IDs.Remove(entityID);
idManager.Free(entityID);
_componentManager.MarkAllComponentsOnEntityForRemoval(entityID);
_ids.Remove(entityID);
_idManager.Free(entityID);
}
entitiesMarkedForDestroy.Clear();
_entitiesMarkedForDestroy.Clear();
}
// NOTE: this is very suboptimal
@ -82,7 +82,7 @@ namespace Encompass
{
foreach (var id in EntityIDs)
{
if (componentManager.UpToDateEntityIsEmpty(id))
if (_componentManager.UpToDateEntityIsEmpty(id))
{
MarkForDestroy(id);
}

View File

@ -1,6 +1,4 @@
using MoonTools.FastCollections;
namespace Encompass
namespace Encompass
{
internal struct EntitySetQuery
{

View File

@ -1,10 +1,11 @@
using System;
namespace Encompass.Exceptions
{
public class IllegalSendTypeException : Exception
public class IllegalDrawLayerException : Exception
{
public IllegalSendTypeException(
public IllegalDrawLayerException(
string format,
params object[] args
) : base(string.Format(format, args)) { }

View File

@ -1,25 +1,12 @@
using System;
using System.Runtime.Serialization;
namespace Encompass.Exceptions
{
[Serializable]
internal class NoComponentOfTypeException : Exception
public class NoComponentOfTypeException : Exception
{
public NoComponentOfTypeException()
{
}
public NoComponentOfTypeException(string message) : base(message)
{
}
public NoComponentOfTypeException(string message, Exception innerException) : base(message, innerException)
{
}
protected NoComponentOfTypeException(SerializationInfo info, StreamingContext context) : base(info, context)
{
}
public NoComponentOfTypeException(
string format,
params object[] args
) : base(string.Format(format, args)) { }
}
}
}

View File

@ -2,9 +2,9 @@ using System;
namespace Encompass.Exceptions
{
public class IllegalReadTypeException : Exception
public class NoMessageOfTypeException : Exception
{
public IllegalReadTypeException(
public NoMessageOfTypeException(
string format,
params object[] args
) : base(string.Format(format, args)) { }

View File

@ -0,0 +1,390 @@
using System;
using System.Collections.Generic;
using System.Linq;
namespace Encompass
{
internal class DirectedGraph<TNode> where TNode : IEquatable<TNode>
{
protected HashSet<TNode> nodes = new HashSet<TNode>();
protected Dictionary<TNode, HashSet<TNode>> neighbors = new Dictionary<TNode, HashSet<TNode>>();
protected HashSet<(TNode, TNode)> edges = new HashSet<(TNode, TNode)>();
public IEnumerable<TNode> Nodes => nodes;
public IEnumerable<(TNode, TNode)> Edges => edges;
public void AddNode(TNode node)
{
if (!Exists(node))
{
nodes.Add(node);
neighbors.Add(node, new HashSet<TNode>());
}
}
public void AddNodes(params TNode[] nodes)
{
foreach (var node in nodes)
{
AddNode(node);
}
}
public void RemoveNode(TNode node)
{
CheckNodes(node);
var edgesToRemove = new List<(TNode, TNode)>();
foreach (var entry in neighbors)
{
if (entry.Value.Contains(node))
{
edgesToRemove.Add((entry.Key, node));
}
}
foreach (var edge in edgesToRemove)
{
RemoveEdge(edge.Item1, edge.Item2);
}
nodes.Remove(node);
neighbors.Remove(node);
}
public void RemoveEdge(TNode v, TNode u)
{
CheckEdge(v, u);
neighbors[v].Remove(u);
edges.Remove((v, u));
}
public void AddEdge(TNode v, TNode u)
{
CheckNodes(v, u);
if (Exists(v, u)) { throw new ArgumentException($"Edge between {v} and {u} already exists in the graph"); }
if (v.Equals(u)) { throw new ArgumentException("Self-edges are not allowed in a simple graph. Use a multigraph instead"); }
neighbors[v].Add(u);
edges.Add((v, u));
}
public bool Exists(TNode node)
{
return nodes.Contains(node);
}
public bool Exists(TNode v, TNode u)
{
CheckNodes(v, u);
return edges.Contains((v, u));
}
protected void CheckNodes(params TNode[] givenNodes)
{
foreach (var node in givenNodes)
{
if (!Exists(node))
{
throw new System.ArgumentException($"Vertex {node} does not exist in the graph");
}
}
}
protected void CheckEdge(TNode v, TNode u)
{
CheckNodes(v, u);
if (!Exists(v, u)) { throw new ArgumentException($"Edge between vertex {v} and vertex {u} does not exist in the graph"); }
}
public IEnumerable<TNode> Neighbors(TNode node)
{
CheckNodes(node);
return neighbors[node];
}
public DirectedGraph<TNode> Clone()
{
var clone = new DirectedGraph<TNode>();
clone.AddNodes(Nodes.ToArray());
foreach (var v in Nodes)
{
foreach (var n in Neighbors(v))
{
clone.AddEdge(v, n);
}
}
return clone;
}
public DirectedGraph<TNode> SubGraph(params TNode[] subVertices)
{
var subGraph = new DirectedGraph<TNode>();
subGraph.AddNodes(subVertices.ToArray());
foreach (var n in Nodes)
{
if (Nodes.Contains(n))
{
var neighbors = Neighbors(n);
foreach (var u in neighbors)
{
if (subVertices.Contains(u))
{
subGraph.AddEdge(n, u);
}
}
}
}
return subGraph;
}
private IEnumerable<TNode> PostorderNodeDFSHelper(HashSet<TNode> discovered, TNode v)
{
discovered.Add(v);
foreach (var neighbor in Neighbors(v))
{
if (!discovered.Contains(neighbor))
{
foreach (var node in PostorderNodeDFSHelper(discovered, neighbor))
{
yield return node;
}
}
}
yield return v;
}
protected IEnumerable<TNode> PostorderNodeDFS()
{
var dfsDiscovered = new HashSet<TNode>();
foreach (var node in Nodes)
{
if (!dfsDiscovered.Contains(node))
{
foreach (var thing in PostorderNodeDFSHelper(dfsDiscovered, node))
{
yield return thing;
}
}
}
}
public IEnumerable<TNode> TopologicalSort()
{
return PostorderNodeDFS().Reverse();
}
public bool Cyclic()
{
return StronglyConnectedComponents().Any((scc) => scc.Count() > 1);
}
public IEnumerable<IEnumerable<TNode>> SimpleCycles()
{
void unblock(TNode thisnode, HashSet<TNode> blocked, Dictionary<TNode, HashSet<TNode>> B) //refactor to remove closure
{
var stack = new Stack<TNode>();
stack.Push(thisnode);
while (stack.Count > 0)
{
var node = stack.Pop();
if (blocked.Contains(thisnode))
{
blocked.Remove(thisnode);
if (B.ContainsKey(node))
{
foreach (var n in B[node])
{
if (!stack.Contains(n))
{
stack.Push(n);
}
}
B[node].Clear();
}
}
}
}
List<List<TNode>> result = new List<List<TNode>>();
var subGraph = Clone();
var sccs = new Stack<IEnumerable<TNode>>();
foreach (var scc in StronglyConnectedComponents())
{
sccs.Push(scc);
}
while (sccs.Count > 0)
{
var scc = new Stack<TNode>(sccs.Pop());
var startNode = scc.Pop();
var path = new Stack<TNode>();
path.Push(startNode);
var blocked = new HashSet<TNode>
{
startNode
};
var closed = new HashSet<TNode>();
var B = new Dictionary<TNode, HashSet<TNode>>();
var stack = new Stack<(TNode, Stack<TNode>)>();
stack.Push((startNode, new Stack<TNode>(subGraph.Neighbors(startNode))));
while (stack.Count > 0)
{
var entry = stack.Peek();
var thisnode = entry.Item1;
var neighbors = entry.Item2;
if (neighbors.Count > 0)
{
var nextNode = neighbors.Pop();
if (nextNode.Equals(startNode))
{
var resultPath = new List<TNode>();
foreach (var v in path)
{
resultPath.Add(v);
}
result.Add(resultPath);
foreach (var v in path)
{
closed.Add(v);
}
}
else if (!blocked.Contains(nextNode))
{
path.Push(nextNode);
stack.Push((nextNode, new Stack<TNode>(subGraph.Neighbors(nextNode))));
closed.Remove(nextNode);
blocked.Add(nextNode);
continue;
}
}
if (neighbors.Count == 0)
{
if (closed.Contains(thisnode))
{
unblock(thisnode, blocked, B);
}
else
{
foreach (var neighbor in subGraph.Neighbors(thisnode))
{
if (!B.ContainsKey(neighbor))
{
B[neighbor] = new HashSet<TNode>();
}
B[neighbor].Add(thisnode);
}
}
stack.Pop();
path.Pop();
}
}
subGraph.RemoveNode(startNode);
var H = subGraph.SubGraph(scc.ToArray());
var HSccs = H.StronglyConnectedComponents();
foreach (var HScc in HSccs)
{
sccs.Push(HScc);
}
}
return result.Distinct(new SimpleCycleComparer<TNode>());
}
protected IEnumerable<IEnumerable<TNode>> StronglyConnectedComponents()
{
var preorder = new Dictionary<TNode, uint>();
var lowlink = new Dictionary<TNode, uint>();
var sccFound = new Dictionary<TNode, bool>();
var sccQueue = new Stack<TNode>();
uint preorderCounter = 0;
foreach (var source in Nodes)
{
if (!sccFound.ContainsKey(source))
{
var queue = new Stack<TNode>();
queue.Push(source);
while (queue.Count > 0)
{
var v = queue.Peek();
if (!preorder.ContainsKey(v))
{
preorderCounter++;
preorder[v] = preorderCounter;
}
var done = true;
var vNeighbors = Neighbors(v);
foreach (var w in vNeighbors)
{
if (!preorder.ContainsKey(w))
{
queue.Push(w);
done = false;
break;
}
}
if (done)
{
lowlink[v] = preorder[v];
foreach (var w in vNeighbors)
{
if (!sccFound.ContainsKey(w))
{
if (preorder[w] > preorder[v])
{
lowlink[v] = Math.Min(lowlink[v], lowlink[w]);
}
else
{
lowlink[v] = Math.Min(lowlink[v], preorder[w]);
}
}
}
queue.Pop();
if (lowlink[v] == preorder[v])
{
sccFound[v] = true;
var scc = new List<TNode>() { v };
while (sccQueue.Count > 0 && preorder[sccQueue.Peek()] > preorder[v])
{
var k = sccQueue.Pop();
sccFound[k] = true;
scc.Add(k);
}
yield return scc;
}
else
{
sccQueue.Push(v);
}
}
}
}
}
}
}
}

View File

@ -0,0 +1,18 @@
using System.Collections.Generic;
using System.Linq;
namespace Encompass
{
internal class SimpleCycleComparer<TNode> : IEqualityComparer<IEnumerable<TNode>>
{
public bool Equals(IEnumerable<TNode> x, IEnumerable<TNode> y)
{
return x.SequenceEqual(y);
}
public int GetHashCode(IEnumerable<TNode> obj)
{
return obj.Aggregate(0, (current, next) => current.GetHashCode() ^ next.GetHashCode());
}
}
}

View File

@ -1,7 +0,0 @@
namespace Encompass
{
/// <summary>
/// Structs that implement IComponent are considered to be Components.
/// </summary>
public interface IComponent { }
}

View File

@ -4,25 +4,25 @@ namespace Encompass
{
internal class IDManager
{
int nextID = 0;
int _nextID = 0;
private Stack<int> availableIDs = new Stack<int>();
private readonly Stack<int> _availableIDs = new Stack<int>();
public int NextID()
{
if (availableIDs.Count > 0)
if (_availableIDs.Count > 0)
{
return availableIDs.Pop();
return _availableIDs.Pop();
}
else
{
return nextID++;
return _nextID++;
}
}
public void Free(int ID)
public void Free(int id)
{
availableIDs.Push(ID);
_availableIDs.Push(id);
}
}
}

View File

@ -1,7 +0,0 @@
namespace Encompass
{
/// <summary>
/// Structs that implement IMessage are considered to be Messages.
/// </summary>
public interface IMessage { }
}

View File

@ -1,7 +0,0 @@
namespace Encompass
{
public interface IDrawableComponent
{
int Layer { get; set; }
}
}

View File

@ -1,72 +1,71 @@
using System;
using System.Collections.Generic;
namespace Encompass
{
internal class MessageManager
{
private readonly TimeManager timeManager;
private readonly MessageStore messageStore = new MessageStore();
private readonly TimeManager _timeManager;
private readonly MessageStore _messageStore = new MessageStore();
public MessageManager(TimeManager timeManager)
{
this.timeManager = timeManager;
_timeManager = timeManager;
}
internal void AddMessage<TMessage>(TMessage message) where TMessage : struct, IMessage
internal void AddMessage<TMessage>(in TMessage message) where TMessage : struct
{
messageStore.AddMessage(message);
_messageStore.AddMessage(message);
}
internal void AddMessage<TMessage>(TMessage message, double time) where TMessage : struct, IMessage
internal void AddMessage<TMessage>(in TMessage message, double time) where TMessage : struct
{
messageStore.AddMessage(message, time);
_messageStore.AddMessage(message, time);
}
internal void AddMessageIgnoringTimeDilation<TMessage>(TMessage message, double time) where TMessage : struct, IMessage
internal void AddMessageIgnoringTimeDilation<TMessage>(in TMessage message, double time) where TMessage : struct
{
messageStore.AddMessageIgnoringTimeDilation(message, time);
_messageStore.AddMessageIgnoringTimeDilation(message, time);
}
internal void ClearMessages()
{
messageStore.ClearAll();
_messageStore.ClearAll();
}
internal void ProcessDelayedMessages(double dt)
{
messageStore.ProcessDelayedMessages(dt * timeManager.TimeDilationFactor, dt);
_messageStore.ProcessDelayedMessages(dt * _timeManager.TimeDilationFactor, dt);
}
internal IEnumerable<TMessage> GetMessagesByType<TMessage>() where TMessage : struct, IMessage
internal ReadOnlySpan<TMessage> GetMessagesByType<TMessage>() where TMessage : struct
{
return messageStore.All<TMessage>();
return _messageStore.All<TMessage>();
}
internal bool Any<TMessage>() where TMessage : struct, IMessage
internal bool Any<TMessage>() where TMessage : struct
{
return messageStore.Any<TMessage>();
return _messageStore.Any<TMessage>();
}
internal TMessage First<TMessage>() where TMessage : struct, IMessage
internal ref readonly TMessage First<TMessage>() where TMessage : struct
{
return messageStore.First<TMessage>();
return ref _messageStore.First<TMessage>();
}
internal IEnumerable<TMessage> WithEntity<TMessage>(int entityID) where TMessage : struct, IMessage, IHasEntity
internal IEnumerable<TMessage> WithEntity<TMessage>(int entityID) where TMessage : struct, IHasEntity
{
return messageStore.WithEntity<TMessage>(entityID);
return _messageStore.WithEntity<TMessage>(entityID);
}
internal TMessage WithEntitySingular<TMessage>(int entityID) where TMessage : struct, IMessage, IHasEntity
internal ref readonly TMessage WithEntitySingular<TMessage>(int entityID) where TMessage : struct, IHasEntity
{
var enumerator = messageStore.WithEntity<TMessage>(entityID).GetEnumerator();
enumerator.MoveNext();
return enumerator.Current;
return ref _messageStore.FirstWithEntity<TMessage>(entityID);
}
internal bool SomeWithEntity<TMessage>(int entityID) where TMessage : struct, IMessage, IHasEntity
internal bool SomeWithEntity<TMessage>(int entityID) where TMessage : struct, IHasEntity
{
return messageStore.SomeWithEntity<TMessage>(entityID);
return _messageStore.SomeWithEntity<TMessage>(entityID);
}
}
}

View File

@ -1,51 +1,27 @@
using System;
using System.Collections.Generic;
using System.Collections.Generic;
namespace Encompass
{
internal class RenderManager
{
private readonly EntityManager entityManager;
private readonly DrawLayerManager drawLayerManager;
private readonly EntityManager _entityManager;
private readonly List<Renderer> renderers = new List<Renderer>();
private readonly Dictionary<Type, Action<Entity, IComponent>> drawComponentTypeToOrderedRenderer = new Dictionary<Type, Action<Entity, IComponent>>(256);
public RenderManager(EntityManager entityManager, DrawLayerManager drawLayerManager)
public RenderManager(EntityManager entityManager)
{
this.entityManager = entityManager;
this.drawLayerManager = drawLayerManager;
_entityManager = entityManager;
}
public void RegisterOrderedRenderer<TComponent>(Action<Entity, IComponent> renderAction) where TComponent : struct, IComponent
public void AddRenderer(Renderer renderer)
{
drawComponentTypeToOrderedRenderer.Add(typeof(TComponent), renderAction);
drawLayerManager.RegisterOrderedDrawable<TComponent>();
renderers.Add(renderer);
}
public void RegisterGeneralRendererWithLayer(GeneralRenderer renderer, int layer)
public void Draw(double dt, double alpha)
{
drawLayerManager.RegisterGeneralRendererWithLayer(renderer, layer);
}
public void Draw()
{
foreach (var layer in drawLayerManager.LayerOrder)
foreach (var renderer in renderers)
{
var generalRendererSet = drawLayerManager.GeneralRenderersByLayer(layer);
foreach (var (entityID, componentType, component) in drawLayerManager.AllInLayer(layer))
{
if (drawComponentTypeToOrderedRenderer.ContainsKey(componentType))
{
var internalRenderAction = drawComponentTypeToOrderedRenderer[componentType];
internalRenderAction(entityManager.GetEntity(entityID), component);
}
}
foreach (var generalRenderer in generalRendererSet)
{
generalRenderer.Render();
}
renderer.Render(dt, alpha);
}
}
}

View File

@ -1,75 +1,68 @@
using System.Collections.Generic;
using System;
using System.Collections.Generic;
namespace Encompass
{
public abstract class Renderer
{
internal EntityManager entityManager;
internal ComponentManager componentManager;
internal EntityManager _entityManager;
internal ComponentManager _componentManager;
internal void AssignEntityManager(EntityManager entityManager)
{
this.entityManager = entityManager;
_entityManager = entityManager;
}
internal void AssignComponentManager(ComponentManager componentManager)
{
this.componentManager = componentManager;
_componentManager = componentManager;
}
protected IEnumerable<Entity> ReadEntities<TComponent>() where TComponent : struct, IComponent
public abstract void Render(double dt, double alpha);
protected ReadOnlySpan<Entity> ReadEntities<TComponent>() where TComponent : struct
{
foreach (var pair in ReadComponentsIncludingEntity<TComponent>())
{
yield return pair.Item2;
}
return _componentManager.GetExistingEntities<TComponent>();
}
protected Entity ReadEntity<TComponent>() where TComponent : struct, IComponent
protected IEnumerable<Entity> ReadEntitiesAsEnumerable<TComponent>() where TComponent : struct
{
return ReadComponentIncludingEntity<TComponent>().Item2;
return _componentManager.GetExistingEntitiesAsEnumerable<TComponent>();
}
protected IEnumerable<TComponent> ReadComponents<TComponent>() where TComponent : struct, IComponent
protected ref readonly Entity ReadEntity<TComponent>() where TComponent : struct
{
return componentManager.GetComponentsByType<TComponent>();
return ref _componentManager.ExistingSingularEntity<TComponent>();
}
protected IEnumerable<(TComponent, Entity)> ReadComponentsIncludingEntity<TComponent>() where TComponent : struct, IComponent
protected ReadOnlySpan<TComponent> ReadComponents<TComponent>() where TComponent : struct
{
foreach (var (component, id) in componentManager.GetComponentsIncludingEntity<TComponent>())
{
yield return (component, entityManager.GetEntity(id));
}
return _componentManager.GetComponentsByType<TComponent>();
}
protected TComponent ReadComponent<TComponent>() where TComponent : struct, IComponent
protected IEnumerable<TComponent> ReadComponentsAsEnumerable<TComponent>() where TComponent : struct
{
var enumerator = ReadComponents<TComponent>().GetEnumerator();
enumerator.MoveNext();
return enumerator.Current;
return _componentManager.GetComponentsByTypeEnumerable<TComponent>();
}
protected (TComponent, Entity) ReadComponentIncludingEntity<TComponent>() where TComponent : struct, IComponent
protected ref readonly TComponent ReadComponent<TComponent>() where TComponent : struct
{
var enumerator = ReadComponentsIncludingEntity<TComponent>().GetEnumerator();
enumerator.MoveNext();
return enumerator.Current;
return ref _componentManager.ExistingSingular<TComponent>();
}
protected TComponent GetComponent<TComponent>(Entity entity) where TComponent : struct, IComponent
protected ref readonly TComponent GetComponent<TComponent>(Entity entity) where TComponent : struct
{
return componentManager.GetComponentByEntityAndType<TComponent>(entity.ID);
return ref _componentManager.GetComponentByEntityAndType<TComponent>(entity.ID);
}
protected bool HasComponent<TComponent>(Entity entity) where TComponent : struct, IComponent
protected bool HasComponent<TComponent>(Entity entity) where TComponent : struct
{
return componentManager.EntityHasComponentOfType<TComponent>(entity.ID);
return _componentManager.EntityHasComponentOfType<TComponent>(entity.ID);
}
protected bool SomeComponent<TComponent>() where TComponent : struct, IComponent
protected bool SomeComponent<TComponent>() where TComponent : struct
{
return componentManager.SomeExistingComponent<TComponent>();
return _componentManager.SomeExistingComponent<TComponent>();
}
}
}

View File

@ -1,11 +0,0 @@
namespace Encompass
{
/// <summary>
/// GeneralRenderer is a Renderer which generically reads the game state in order to draw elements to the screen.
/// GeneralRenderers have a layer specified when they are added to the World.
/// </summary>
public abstract class GeneralRenderer : Renderer
{
public abstract void Render();
}
}

View File

@ -1,17 +0,0 @@
using System;
namespace Encompass
{
/// <summary>
/// OrdereredRenderer provides a structure for the common pattern of wishing to draw a specific DrawComponent at a specific layer.
/// </summary>
public abstract class OrderedRenderer<TComponent> : Renderer where TComponent : struct, IComponent, IDrawableComponent
{
public abstract void Render(Entity entity, TComponent drawComponent);
internal void InternalRender(Entity entity, IComponent component)
{
Render(entity, (TComponent)component);
}
}
}

View File

@ -1,35 +1,56 @@
using System;
namespace Encompass
{
internal struct TimeDilationData
{
public double elapsedTime;
public double easeInTime;
public System.Func<double, double, double, double, double> easeInFunction;
public double activeTime;
public double easeOutTime;
public System.Func<double, double, double, double, double> easeOutFunction;
public double factor;
private readonly double _factor;
public double Factor
{
get
{
if (elapsedTime < easeInTime)
if (ElapsedTime < EaseInTime)
{
return easeInFunction(elapsedTime, 1, factor - 1, easeInTime);
return EaseInFunction(ElapsedTime, 1, _factor - 1, EaseInTime);
}
else if (elapsedTime < easeInTime + activeTime)
else if (ElapsedTime < EaseInTime + ActiveTime)
{
return factor;
return _factor;
}
else if (elapsedTime < easeInTime + activeTime + easeOutTime)
else if (ElapsedTime < EaseInTime + ActiveTime + EaseOutTime)
{
var elapsedOutTime = elapsedTime - easeInTime - activeTime;
return easeOutFunction(elapsedOutTime, factor, 1 - factor, easeOutTime);
var elapsedOutTime = ElapsedTime - EaseInTime - ActiveTime;
return EaseOutFunction(elapsedOutTime, _factor, 1 - _factor, EaseOutTime);
}
return 1;
}
}
public double ElapsedTime { get; set; }
public double EaseInTime { get; }
public Func<double, double, double, double, double> EaseInFunction { get; }
public double ActiveTime { get; }
public double EaseOutTime { get; }
public Func<double, double, double, double, double> EaseOutFunction { get; }
public TimeDilationData(
double factor,
double easeInTime,
Func<double, double, double, double, double> easeInfunction,
double activeTime,
double easeOutTime,
Func<double, double, double, double, double> easeOutFunction
)
{
_factor = factor;
EaseInTime = easeInTime;
EaseInFunction = easeInfunction;
ActiveTime = activeTime;
EaseOutTime = easeOutTime;
EaseOutFunction = easeOutFunction;
ElapsedTime = 0;
}
}
}
}

View File

@ -4,7 +4,7 @@ namespace Encompass
{
internal class TimeManager
{
private readonly List<TimeDilationData> timeDilationDatas = new List<TimeDilationData>(32);
private readonly List<TimeDilationData> _timeDilationDatas = new List<TimeDilationData>(32);
private double Linear(double t, double b, double c, double d)
{
@ -15,36 +15,36 @@ namespace Encompass
{
get
{
if (timeDilationDatas.Count == 0) { return 1; }
if (_timeDilationDatas.Count == 0) { return 1; }
var average = 0.0;
foreach (var data in timeDilationDatas)
foreach (var data in _timeDilationDatas)
{
average += data.Factor;
}
return average / timeDilationDatas.Count;
return average / _timeDilationDatas.Count;
}
}
public bool TimeDilationActive
{
get => timeDilationDatas.Count != 0;
get => _timeDilationDatas.Count != 0;
}
public void Update(double dt)
{
for (var i = timeDilationDatas.Count - 1; i >= 0; i--)
for (var i = _timeDilationDatas.Count - 1; i >= 0; i--)
{
var data = timeDilationDatas[i];
var data = _timeDilationDatas[i];
data.elapsedTime += dt;
data.ElapsedTime += dt;
if (data.elapsedTime > data.easeInTime + data.activeTime + data.easeOutTime)
if (data.ElapsedTime > data.EaseInTime + data.ActiveTime + data.EaseOutTime)
{
timeDilationDatas.RemoveAt(i);
_timeDilationDatas.RemoveAt(i);
}
else
{
timeDilationDatas[i] = data;
_timeDilationDatas[i] = data;
}
}
}
@ -66,16 +66,15 @@ namespace Encompass
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)
{
timeDilationDatas.Add(new TimeDilationData
{
elapsedTime = 0,
easeInTime = easeInTime,
easeInFunction = easeInFunction,
activeTime = activeTime,
easeOutTime = easeOutTime,
easeOutFunction = easeOutFunction,
factor = factor
});
_timeDilationDatas.Add(new TimeDilationData
(
factor,
easeInTime,
easeInFunction,
activeTime,
easeOutTime,
easeOutFunction
));
}
}
}

View File

@ -5,13 +5,13 @@ namespace Encompass
{
internal class TrackingManager
{
private Dictionary<Type, HashSet<Engine>> _immediateComponentTypesToEngines = new Dictionary<Type, HashSet<Engine>>();
private Dictionary<Type, HashSet<Engine>> _componentTypesToEngines = new Dictionary<Type, HashSet<Engine>>();
private readonly Dictionary<Type, HashSet<Engine>> _immediateComponentTypesToEngines = new Dictionary<Type, HashSet<Engine>>();
private readonly Dictionary<Type, HashSet<Engine>> _componentTypesToEngines = new Dictionary<Type, HashSet<Engine>>();
private HashSet<(int, Type)> _additions = new HashSet<(int, Type)>();
private HashSet<(int, Type)> _removals = new HashSet<(int, Type)>();
private readonly HashSet<(int, Type)> _additions = new HashSet<(int, Type)>();
private readonly HashSet<(int, Type)> _removals = new HashSet<(int, Type)>();
private HashSet<(int, Engine)> _pairsToCheck = new HashSet<(int, Engine)>();
private readonly HashSet<(int, Engine)> _pairsToCheck = new HashSet<(int, Engine)>();
public void RegisterComponentTypeToEngine(Type type, Engine engine)
{

View File

@ -1,24 +1,24 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Reflection;
namespace Encompass
{
internal class UberEngine : Engine
{
private IEnumerable<Type> _componentTypes;
private IEnumerable<Type> _messageTypes;
private readonly IEnumerable<Type> _componentTypes;
private readonly IEnumerable<Type> _messageTypes;
public Entity Entity { get; private set; }
public UberEngine(IEnumerable<Type> componentTypes, IEnumerable<Type> messageTypes)
{
_componentTypes = componentTypes;
_messageTypes = messageTypes;
readTypes.UnionWith(componentTypes);
writeTypes.UnionWith(componentTypes);
sendTypes.UnionWith(messageTypes);
receiveTypes.UnionWith(messageTypes);
AddTypes.UnionWith(componentTypes);
ReadTypes.UnionWith(componentTypes);
WriteTypes.UnionWith(componentTypes);
SendTypes.UnionWith(messageTypes);
ReceiveTypes.UnionWith(messageTypes);
}
public void Write()
@ -27,7 +27,8 @@ namespace Encompass
foreach (var type in _componentTypes)
{
var instanceParam = new object[] { Entity, Activator.CreateInstance(type) };
var instance = Activator.CreateInstance(type);
var instanceParam = new object[] { Entity, instance };
var setComponentMethod = typeof(Engine).GetMethod("SetComponent", BindingFlags.NonPublic | BindingFlags.Instance);
var genericSetComponentMethod = setComponentMethod.MakeGenericMethod(type);
genericSetComponentMethod.Invoke(this, instanceParam);
@ -38,54 +39,59 @@ namespace Encompass
{
foreach (var type in _componentTypes)
{
CallGenericMethod(type, "ReadComponent", null);
CallGenericMethod(type, "ReadComponentIncludingEntity", null);
CallGenericMethod(type, "ReadComponents", null);
CallGenericMethod(type, "ReadComponentsIncludingEntity", null);
CallGenericMethod(type, "ReadEntity", null);
CallGenericMethod(type, "ReadEntities", null);
CallGenericMethod(type, "GetComponent", new Type[] { typeof(Entity) }, new object[] { Entity });
CallGenericMethod(type, "HasComponent", new Type[] { typeof(Entity) }, new object[] { Entity });
CallGenericMethod(type, "SomeComponent", null);
CallGenericMethod(type, "DestroyWith", null);
CallGenericMethod(type, "DestroyAllWith", null);
CallGenericMethod(type, "RemoveComponent", new Type[] { typeof(Entity) }, new object[] { Entity });
CallGenericWrappedMethod(type, "CallAllComponentMethods", null);
}
foreach (var type in _messageTypes)
{
CallGenericMethod(type, "SendMessageIgnoringTimeDilation", new object[] { Activator.CreateInstance(type), 1 });
CallGenericMethod(type, "SendMessage", 1, new object[] { Activator.CreateInstance(type) });
CallGenericMethod(type, "SendMessage", 2, new object[] { Activator.CreateInstance(type), 1 });
CallGenericMethod(type, "ReadMessage", null);
CallGenericMethod(type, "ReadMessages", null);
CallGenericMethod(type, "SomeMessage", null);
CallGenericWrappedMethod(type, "CallAllMessageMethods", null);
if (typeof(IHasEntity).IsAssignableFrom(type))
{
CallGenericMethod(type, "ReadMessagesWithEntity", new Type[] { typeof(Entity) }, new object[] { Entity });
CallGenericWrappedMethod(type, "CallAllEntityMessageMethods", null);
}
}
}
private void CallGenericMethod(Type type, string methodName, object[] parameters)
// we can't reflect invoke on byref returns or Span returns right now... so we have non-return wrapper methods
protected void CallAllComponentMethods<TComponent>() where TComponent : struct
{
var readComponentMethod = typeof(Engine).GetMethod(methodName, BindingFlags.NonPublic | BindingFlags.Instance);
var genericReadComponentMethod = readComponentMethod.MakeGenericMethod(type);
genericReadComponentMethod.Invoke(this, parameters);
ReadComponent<TComponent>();
ReadComponents<TComponent>();
ReadEntity<TComponent>();
ReadEntities<TComponent>();
GetComponent<TComponent>(Entity);
HasComponent<TComponent>(Entity);
SomeComponent<TComponent>();
DestroyWith<TComponent>();
DestroyAllWith<TComponent>();
RemoveComponent<TComponent>(Entity);
AddComponent<TComponent>(Entity, default);
}
private void CallGenericMethod(Type type, string methodName, Type[] types, object[] parameters)
protected void CallAllMessageMethods<TMessage>() where TMessage : struct
{
var readComponentMethod = typeof(Engine).GetMethod(methodName, BindingFlags.NonPublic | BindingFlags.Instance, null, types, null);
var genericReadComponentMethod = readComponentMethod.MakeGenericMethod(type);
genericReadComponentMethod.Invoke(this, parameters);
SendMessageIgnoringTimeDilation<TMessage>(default, 0.1);
SendMessage<TMessage>(default);
SendMessage<TMessage>(default, 0.1);
ReadMessage<TMessage>();
ReadMessages<TMessage>();
SomeMessage<TMessage>();
}
private void CallGenericMethod(Type type, string methodName, int argumentNum, object[] parameters)
protected void CallAllEntityMessageMethods<TMessage>() where TMessage : struct, IHasEntity
{
var method = typeof(Engine).GetRuntimeMethods().Where(m => m.Name == methodName && m.GetParameters().Length == argumentNum).First();
var genericMethod = method.MakeGenericMethod(type);
genericMethod.Invoke(this, parameters);
ReadMessagesWithEntity<TMessage>(Entity);
ReadMessageWithEntity<TMessage>(Entity);
SomeMessageWithEntity<TMessage>(Entity);
}
private void CallGenericWrappedMethod(Type type, string methodName, object[] parameters)
{
var readComponentMethod = typeof(UberEngine).GetMethod(methodName, BindingFlags.NonPublic | BindingFlags.Instance);
var genericReadComponentMethod = readComponentMethod.MakeGenericMethod(type);
genericReadComponentMethod.Invoke(this, parameters);
}
}
}

View File

@ -6,7 +6,7 @@ namespace Encompass
{
class UberRenderer : Renderer
{
private IEnumerable<Type> _componentTypes;
private readonly IEnumerable<Type> _componentTypes;
private Entity _entity;
public UberRenderer(IEnumerable<Type> componentTypes)
@ -19,25 +19,29 @@ namespace Encompass
_entity = entity;
}
public void Render()
// can't reflect invoke on Span returns...
public override void Render(double dt, double alpha)
{
foreach (var type in _componentTypes)
{
CallGenericMethod(type, "ReadEntities", null);
CallGenericMethod(type, "ReadEntity", null);
CallGenericMethod(type, "ReadComponent", null);
CallGenericMethod(type, "ReadComponentIncludingEntity", null);
CallGenericMethod(type, "ReadComponents", null);
CallGenericMethod(type, "ReadComponentsIncludingEntity", null);
CallGenericMethod(type, "GetComponent", new object[] { _entity });
CallGenericMethod(type, "HasComponent", new object[] { _entity });
CallGenericMethod(type, "SomeComponent", null);
CallGenericWrappedMethod(type, "CallAllComponentMethods", null);
}
}
private void CallGenericMethod(Type type, string methodName, object[] parameters)
protected void CallAllComponentMethods<TComponent>() where TComponent : struct
{
var readComponentMethod = typeof(Renderer).GetMethod(methodName, BindingFlags.NonPublic | BindingFlags.Instance);
ReadEntity<TComponent>();
ReadEntities<TComponent>();
ReadComponent<TComponent>();
ReadComponents<TComponent>();
GetComponent<TComponent>(_entity);
HasComponent<TComponent>(_entity);
SomeComponent<TComponent>();
}
private void CallGenericWrappedMethod(Type type, string methodName, object[] parameters)
{
var readComponentMethod = typeof(UberRenderer).GetMethod(methodName, BindingFlags.NonPublic | BindingFlags.Instance);
var genericReadComponentMethod = readComponentMethod.MakeGenericMethod(type);
genericReadComponentMethod.Invoke(this, parameters);
}

View File

@ -7,13 +7,13 @@ namespace Encompass
/// </summary>
public class World
{
private readonly List<Engine> enginesInOrder;
private readonly EntityManager entityManager;
private readonly ComponentManager componentManager;
private readonly TrackingManager trackingManager;
private readonly MessageManager messageManager;
private readonly TimeManager timeManager;
private readonly RenderManager renderManager;
private readonly List<Engine> _enginesInOrder;
private readonly EntityManager _entityManager;
private readonly ComponentManager _componentManager;
private readonly TrackingManager _trackingManager;
private readonly MessageManager _messageManager;
private readonly TimeManager _timeManager;
private readonly RenderManager _renderManager;
internal World(
List<Engine> enginesInOrder,
@ -25,13 +25,13 @@ namespace Encompass
RenderManager renderManager
)
{
this.enginesInOrder = enginesInOrder;
this.entityManager = entityManager;
this.componentManager = componentManager;
this.trackingManager = trackingManager;
this.messageManager = messageManager;
this.timeManager = timeManager;
this.renderManager = renderManager;
_enginesInOrder = enginesInOrder;
_entityManager = entityManager;
_componentManager = componentManager;
_trackingManager = trackingManager;
_messageManager = messageManager;
_timeManager = timeManager;
_renderManager = renderManager;
}
/// <summary>
@ -40,15 +40,15 @@ namespace Encompass
/// <param name="dt">The time in seconds that has passed since the previous frame.</param>
public void Update(double dt)
{
trackingManager.UpdateTracking();
messageManager.ProcessDelayedMessages(dt);
timeManager.Update(dt);
_trackingManager.UpdateTracking();
_messageManager.ProcessDelayedMessages(dt);
_timeManager.Update(dt);
foreach (var engine in enginesInOrder)
foreach (var engine in _enginesInOrder)
{
if (engine.usesTimeDilation)
if (engine._usesTimeDilation)
{
engine.Update(dt * timeManager.TimeDilationFactor);
engine.Update(dt * _timeManager.TimeDilationFactor);
}
else
{
@ -58,20 +58,20 @@ namespace Encompass
engine.ClearNewlyCreatedEntities();
}
messageManager.ClearMessages();
entityManager.PruneEmptyEntities();
entityManager.DestroyMarkedEntities(enginesInOrder);
_messageManager.ClearMessages();
_entityManager.PruneEmptyEntities();
_entityManager.DestroyMarkedEntities(_enginesInOrder);
componentManager.RemoveMarkedComponents();
componentManager.WriteComponents();
_componentManager.RemoveMarkedComponents();
_componentManager.WriteComponents();
}
/// <summary>
/// Causes the Renderers to draw.
/// </summary>
public void Draw()
public void Draw(double dt, double alpha)
{
renderManager.Draw();
_renderManager.Draw(dt, alpha);
}
}
}

View File

@ -3,8 +3,6 @@ using System.Collections.Generic;
using System.Reflection;
using System.Linq;
using Encompass.Exceptions;
using MoonTools.Core.Graph;
using MoonTools.Core.Graph.Extensions;
namespace Encompass
{
@ -18,43 +16,37 @@ namespace Encompass
/// </remarks>
public class WorldBuilder
{
private readonly int entityCapacity;
private readonly List<Engine> engines = new List<Engine>();
private readonly DirectedGraph<Engine, Unit> engineGraph = GraphBuilder.DirectedGraph<Engine>();
private readonly ComponentStore startingExistingComponentStore;
private readonly ComponentStore startingUpToDateComponentStore;
private readonly int _entityCapacity;
private readonly List<Engine> _engines = new List<Engine>();
private readonly DirectedGraph<Engine> _engineGraph = new DirectedGraph<Engine>();
private readonly ComponentStore _startingExistingComponentStore;
private readonly ComponentStore _startingUpToDateComponentStore;
private readonly ComponentManager componentManager;
private readonly EntityManager entityManager;
private readonly MessageManager messageManager;
private readonly TimeManager timeManager;
private readonly DrawLayerManager drawLayerManager;
private readonly RenderManager renderManager;
private readonly TrackingManager trackingManager;
private readonly ComponentManager _componentManager;
private readonly EntityManager _entityManager;
private readonly MessageManager _messageManager;
private readonly TimeManager _timeManager;
private readonly RenderManager _renderManager;
private readonly TrackingManager _trackingManager;
private readonly Dictionary<Type, HashSet<Engine>> typeToReaders = new Dictionary<Type, HashSet<Engine>>();
private readonly HashSet<Engine> senders = new HashSet<Engine>();
private readonly HashSet<Type> componentTypesToPreload = new HashSet<Type>();
private readonly HashSet<Type> messageTypes = new HashSet<Type>();
private readonly Dictionary<Type, int> typeToIndex = new Dictionary<Type, int>();
private readonly Dictionary<Type, HashSet<Engine>> _typeToReaders = new Dictionary<Type, HashSet<Engine>>();
private readonly HashSet<Engine> _senders = new HashSet<Engine>();
private readonly HashSet<Type> _componentTypesToPreload = new HashSet<Type>();
private readonly HashSet<Type> _messageTypes = new HashSet<Type>();
private readonly Dictionary<Type, int> _typeToIndex = new Dictionary<Type, int>();
public WorldBuilder(int entityCapacity = 32768)
{
this.entityCapacity = entityCapacity;
drawLayerManager = new DrawLayerManager(typeToIndex);
timeManager = new TimeManager();
trackingManager = new TrackingManager();
componentManager = new ComponentManager(drawLayerManager, typeToIndex);
messageManager = new MessageManager(timeManager);
entityManager = new EntityManager(componentManager, entityCapacity);
renderManager = new RenderManager(entityManager, drawLayerManager);
_entityCapacity = entityCapacity;
_timeManager = new TimeManager();
_trackingManager = new TrackingManager();
_componentManager = new ComponentManager(_typeToIndex);
_messageManager = new MessageManager(_timeManager);
_entityManager = new EntityManager(_componentManager, entityCapacity);
_renderManager = new RenderManager(_entityManager);
startingExistingComponentStore = new ComponentStore(typeToIndex);
startingUpToDateComponentStore = new ComponentStore(typeToIndex);
_startingExistingComponentStore = new ComponentStore(_typeToIndex);
_startingUpToDateComponentStore = new ComponentStore(_typeToIndex);
}
/// <summary>
@ -62,56 +54,57 @@ namespace Encompass
/// </summary>
public Entity CreateEntity()
{
return entityManager.CreateEntity();
return _entityManager.CreateEntity();
}
/// <summary>
/// Specifies that the given Message should be sent immediately on the first World Update.
/// </summary>
public void SendMessage<TMessage>(TMessage message) where TMessage : struct, IMessage
public void SendMessage<TMessage>(in TMessage message) where TMessage : struct
{
messageManager.AddMessage(message);
_messageManager.AddMessage(message);
}
/// <summary>
/// Specifies that the given Message should be sent after the specified number of seconds after the first World Update.
/// </summary>
public void SendMessage<TMessage>(TMessage message, double time) where TMessage : struct, IMessage
public void SendMessage<TMessage>(in TMessage message, double time) where TMessage : struct
{
messageManager.AddMessage<TMessage>(message, time);
_messageManager.AddMessage<TMessage>(message, time);
}
/// <summary>
/// Sets Component data for the specified Component Type on the specified Entity.
/// </summary>
public void SetComponent<TComponent>(Entity entity, TComponent component) where TComponent : struct, IComponent
public void SetComponent<TComponent>(Entity entity, in TComponent component) where TComponent : struct
{
RegisterComponentType<TComponent>();
startingExistingComponentStore.Set(entity.ID, component);
startingUpToDateComponentStore.Set(entity.ID, component);
if (component is IDrawableComponent drawableComponent)
{
componentManager.RegisterDrawableComponent(entity.ID, component, drawableComponent.Layer);
drawLayerManager.RegisterOrderedDrawable<TComponent>();
}
_startingExistingComponentStore.Set(entity.ID, component);
_startingUpToDateComponentStore.Set(entity.ID, component);
}
internal void RegisterComponentType<TComponent>() where TComponent : struct, IComponent
internal void RegisterComponentTypeNonGeneric(Type type)
{
if (!typeToIndex.ContainsKey(typeof(TComponent)))
var method = GetType().GetMethod("RegisterComponentType", BindingFlags.NonPublic | BindingFlags.Instance);
var generic = method.MakeGenericMethod(type);
generic.Invoke(this, null);
}
internal void RegisterComponentType<TComponent>() where TComponent : struct
{
if (!_typeToIndex.ContainsKey(typeof(TComponent)))
{
typeToIndex.Add(typeof(TComponent), typeToIndex.Count);
componentTypesToPreload.Add(typeof(TComponent));
componentManager.RegisterComponentType<TComponent>();
startingExistingComponentStore.RegisterComponentType<TComponent>();
startingUpToDateComponentStore.RegisterComponentType<TComponent>();
_typeToIndex.Add(typeof(TComponent), _typeToIndex.Count);
_componentTypesToPreload.Add(typeof(TComponent));
_componentManager.RegisterComponentType<TComponent>();
_startingExistingComponentStore.RegisterComponentType<TComponent>();
_startingUpToDateComponentStore.RegisterComponentType<TComponent>();
}
}
internal void RegisterMessageTypes(IEnumerable<Type> types)
{
messageTypes.UnionWith(types);
_messageTypes.UnionWith(types);
}
/// <summary>
@ -120,109 +113,151 @@ namespace Encompass
/// <param name="engine">An instance of an Engine.</param>
public Engine AddEngine<TEngine>(TEngine engine) where TEngine : Engine
{
engine.AssignEntityManager(entityManager);
engine.AssignComponentManager(componentManager);
engine.AssignMessageManager(messageManager);
engine.AssignTimeManager(timeManager);
engine.AssignTrackingManager(trackingManager);
engine.AssignEntityManager(_entityManager);
engine.AssignComponentManager(_componentManager);
engine.AssignMessageManager(_messageManager);
engine.AssignTimeManager(_timeManager);
engine.AssignTrackingManager(_trackingManager);
engines.Add(engine);
engineGraph.AddNode(engine);
_engines.Add(engine);
_engineGraph.AddNode(engine);
var messageReceiveTypes = engine.receiveTypes;
var messageSendTypes = engine.sendTypes;
var messageReceiveTypes = engine.ReceiveTypes;
var messageSendTypes = engine.SendTypes;
RegisterMessageTypes(engine.receiveTypes.Union(engine.sendTypes));
RegisterMessageTypes(engine.ReceiveTypes);
RegisterMessageTypes(engine.SendTypes);
foreach (var writeImmediateType in engine.writeImmediateTypes.Intersect(engine.readImmediateTypes))
foreach (var writeImmediateType in engine.WriteImmediateTypes)
{
throw new EngineSelfCycleException("Engine {0} both writes and reads immediate Component {1}", engine.GetType().Name, writeImmediateType.Name);
}
foreach (var messageType in messageReceiveTypes.Intersect(messageSendTypes))
{
throw new EngineSelfCycleException("Engine {0} both receives and sends Message {1}", engine.GetType().Name, messageType.Name);
}
if (messageSendTypes.Count > 0 || engine.writeImmediateTypes.Count > 0)
{
senders.Add(engine);
}
foreach (var componentType in engine.queryWithTypes.Union(engine.queryWithoutTypes))
{
trackingManager.RegisterComponentTypeToEngine(componentType, engine);
if (engine.readImmediateTypes.Contains(componentType))
foreach (var readImmediateType in engine.ReadImmediateTypes)
{
trackingManager.RegisterImmediateComponentTypeToEngine(componentType, engine);
if (readImmediateType == writeImmediateType)
{
throw new EngineSelfCycleException("Engine {0} both writes and reads immediate Component {1}", engine.GetType().Name, writeImmediateType.Name);
}
}
}
foreach (var receiveType in engine.receiveTypes.Union(engine.readImmediateTypes))
foreach (var messageReceiveType in messageReceiveTypes)
{
if (!typeToReaders.ContainsKey(receiveType))
foreach (var messageSendType in messageSendTypes)
{
typeToReaders.Add(receiveType, new HashSet<Engine>());
if (messageReceiveType == messageSendType)
{
throw new EngineSelfCycleException("Engine {0} both receives and sends Message {1}", engine.GetType().Name, messageReceiveType.Name);
}
}
}
if (messageSendTypes.Count > 0 || engine.WriteImmediateTypes.Count > 0)
{
_senders.Add(engine);
}
foreach (var componentType in engine.ReadTypes)
{
RegisterComponentTypeNonGeneric(componentType);
}
foreach (var componentType in engine.AddTypes)
{
RegisterComponentTypeNonGeneric(componentType);
}
foreach (var componentType in engine.WriteTypes)
{
RegisterComponentTypeNonGeneric(componentType);
}
foreach (var componentType in engine.WriteImmediateTypes)
{
RegisterComponentTypeNonGeneric(componentType);
}
foreach (var componentType in engine.QueryWithTypes)
{
_trackingManager.RegisterComponentTypeToEngine(componentType, engine);
if (engine.ReadImmediateTypes.Contains(componentType))
{
_trackingManager.RegisterImmediateComponentTypeToEngine(componentType, engine);
}
}
foreach (var componentType in engine.QueryWithoutTypes)
{
_trackingManager.RegisterComponentTypeToEngine(componentType, engine);
if (engine.ReadImmediateTypes.Contains(componentType))
{
_trackingManager.RegisterImmediateComponentTypeToEngine(componentType, engine);
}
}
foreach (var receiveType in engine.ReceiveTypes)
{
if (!_typeToReaders.ContainsKey(receiveType))
{
_typeToReaders.Add(receiveType, new HashSet<Engine>());
}
typeToReaders[receiveType].Add(engine);
_typeToReaders[receiveType].Add(engine);
}
foreach (var receiveType in engine.ReadImmediateTypes)
{
if (!_typeToReaders.ContainsKey(receiveType))
{
_typeToReaders.Add(receiveType, new HashSet<Engine>());
}
_typeToReaders[receiveType].Add(engine);
}
return engine;
}
/// <summary>
/// Registers a draw layer. This must be done before any Renderers are registered.
/// Adds the specified Renderer to the World.
/// </summary>
/// <param name="layer">The draw layer to register.</param>
public void RegisterDrawLayer(int layer)
public void AddRenderer(Renderer renderer)
{
drawLayerManager.RegisterDrawLayer(layer);
}
/// <summary>
/// Adds the specified OrderedRenderer to the World.
/// </summary>
public OrderedRenderer<TComponent> AddOrderedRenderer<TComponent>(OrderedRenderer<TComponent> renderer) where TComponent : struct, IComponent, IDrawableComponent
{
RegisterComponentType<TComponent>();
renderer.AssignEntityManager(entityManager);
renderer.AssignComponentManager(componentManager);
renderManager.RegisterOrderedRenderer<TComponent>(renderer.InternalRender);
return renderer;
}
/// <summary>
/// Adds the specified GeneralRenderer to the World at the specified layer.
/// Higher layer numbers draw on top of lower layer numbers.
/// </summary>
/// <param name="renderer">An instance of a GeneralRenderer.</param>
/// <param name="layer">The layer at which the GeneralRenderer should render. Higher numbers draw over lower numbers.</param>
public TRenderer AddGeneralRenderer<TRenderer>(TRenderer renderer, int layer) where TRenderer : GeneralRenderer
{
renderer.AssignEntityManager(entityManager);
renderer.AssignComponentManager(componentManager);
renderManager.RegisterGeneralRendererWithLayer(renderer, layer);
return renderer;
renderer.AssignComponentManager(_componentManager);
renderer.AssignEntityManager(_entityManager);
_renderManager.AddRenderer(renderer);
}
private void BuildEngineGraph()
{
foreach (var senderEngine in senders)
foreach (var senderEngine in _senders)
{
foreach (var messageType in senderEngine.sendTypes.Union(senderEngine.writeImmediateTypes))
foreach (var messageType in senderEngine.SendTypes)
{
if (typeToReaders.ContainsKey(messageType))
if (_typeToReaders.ContainsKey(messageType))
{
foreach (var readerEngine in typeToReaders[messageType])
foreach (var readerEngine in _typeToReaders[messageType])
{
if (senderEngine != readerEngine)
{
if (!engineGraph.Exists(senderEngine, readerEngine))
if (!_engineGraph.Exists(senderEngine, readerEngine))
{
engineGraph.AddEdge(senderEngine, readerEngine);
_engineGraph.AddEdge(senderEngine, readerEngine);
}
}
}
}
}
foreach (var messageType in senderEngine.WriteImmediateTypes)
{
if (_typeToReaders.ContainsKey(messageType))
{
foreach (var readerEngine in _typeToReaders[messageType])
{
if (senderEngine != readerEngine)
{
if (!_engineGraph.Exists(senderEngine, readerEngine))
{
_engineGraph.AddEdge(senderEngine, readerEngine);
}
}
}
@ -240,9 +275,9 @@ namespace Encompass
{
BuildEngineGraph();
if (engineGraph.Cyclic())
if (_engineGraph.Cyclic())
{
var cycles = engineGraph.SimpleCycles();
var cycles = _engineGraph.SimpleCycles();
var errorString = "Cycle(s) found in Engines: ";
foreach (var cycle in cycles)
{
@ -262,25 +297,25 @@ namespace Encompass
var writePriorities = new Dictionary<Type, HashSet<int>>();
var writeMessageToEngines = new Dictionary<Type, List<Engine>>();
foreach (var engine in engines)
foreach (var engine in _engines)
{
if (engine.GetType().GetCustomAttribute<IgnoresTimeDilation>() != null)
{
engine.usesTimeDilation = false;
engine._usesTimeDilation = false;
}
var defaultWritePriorityAttribute = engine.GetType().GetCustomAttribute<DefaultWritePriority>(false);
foreach (var writeType in engine.writeTypes)
foreach (var writeType in engine.WriteTypes)
{
int? priority = null;
if (engine.writePriorities.ContainsKey(writeType))
if (engine.WritePriorities.ContainsKey(writeType))
{
priority = engine.writePriorities[writeType];
priority = engine.WritePriorities[writeType];
}
else if (defaultWritePriorityAttribute != null)
{
priority = defaultWritePriorityAttribute.writePriority;
priority = defaultWritePriorityAttribute.WritePriority;
}
if (priority.HasValue)
@ -354,25 +389,11 @@ namespace Encompass
throw new EngineWriteConflictException(errorString);
}
// doing reflection to grab all component types, because not all writes need to be declared
foreach (var assembly in AppDomain.CurrentDomain.GetAssemblies())
{
foreach (var componentType in assembly.GetTypes())
{
if (typeof(IComponent).IsAssignableFrom(componentType) && componentType.IsValueType && !componentType.IsEnum && !componentType.IsPrimitive)
{
var method = typeof(WorldBuilder).GetMethod("RegisterComponentType", BindingFlags.NonPublic | BindingFlags.Instance);
var generic = method.MakeGenericMethod(componentType);
generic.Invoke(this, null);
}
}
}
PreloadJIT(componentTypesToPreload, messageTypes);
PreloadJIT(_messageTypes);
var engineOrder = new List<Engine>();
foreach (var engine in engineGraph.TopologicalSort())
foreach (var engine in _engineGraph.TopologicalSort())
{
engineOrder.Add(engine);
engine.BuildEntityQuery();
@ -380,18 +401,18 @@ namespace Encompass
var world = new World(
engineOrder,
entityManager,
componentManager,
trackingManager,
messageManager,
timeManager,
renderManager
_entityManager,
_componentManager,
_trackingManager,
_messageManager,
_timeManager,
_renderManager
);
componentManager.SetExistingComponentStore(startingExistingComponentStore);
componentManager.SetUpToDateComponentStore(startingUpToDateComponentStore);
_componentManager.SetExistingComponentStore(_startingExistingComponentStore);
_componentManager.SetUpToDateComponentStore(_startingUpToDateComponentStore);
trackingManager.InitializeTracking(entityManager.EntityIDs);
_trackingManager.InitializeTracking(_entityManager.EntityIDs);
return world;
}
@ -402,19 +423,25 @@ namespace Encompass
/// It does so by grabbing all component and message types known to the WorldBuilder and
/// executing every possible generic method that could be executed with those types.
/// </summary>
private void PreloadJIT(IEnumerable<Type> componentTypes, IEnumerable<Type> messageTypes)
private void PreloadJIT(IEnumerable<Type> messageTypes)
{
var dummyTimeManager = new TimeManager();
var dummyMessageManager = new MessageManager(dummyTimeManager);
var dummyDrawLayerManager = new DrawLayerManager(typeToIndex);
var dummyTrackingManager = new TrackingManager();
var dummyComponentManager = new ComponentManager(dummyDrawLayerManager, typeToIndex);
var dummyEntityManager = new EntityManager(dummyComponentManager, entityCapacity);
var dummyRenderManager = new RenderManager(dummyEntityManager, dummyDrawLayerManager);
var dummyComponentManager = new ComponentManager(_typeToIndex);
var dummyEntityManager = new EntityManager(dummyComponentManager, _entityCapacity);
var dummyRenderManager = new RenderManager(dummyEntityManager);
var prepEngineOrder = new List<Engine>();
var uberEngine = new UberEngine(componentTypes, messageTypes);
foreach (var componentType in _componentTypesToPreload)
{
var dummyRegisterMethod = typeof(ComponentManager).GetMethod("RegisterComponentType", BindingFlags.Public | BindingFlags.Instance);
var dummyGeneric = dummyRegisterMethod.MakeGenericMethod(componentType);
dummyGeneric.Invoke(dummyComponentManager, null);
}
var uberEngine = new UberEngine(_componentTypesToPreload, messageTypes);
uberEngine.AssignEntityManager(dummyEntityManager);
uberEngine.AssignComponentManager(dummyComponentManager);
@ -422,24 +449,10 @@ namespace Encompass
uberEngine.AssignTimeManager(dummyTimeManager);
uberEngine.AssignTrackingManager(dummyTrackingManager);
var uberRenderer = new UberRenderer(componentTypes);
var uberRenderer = new UberRenderer(_componentTypesToPreload);
uberRenderer.AssignComponentManager(dummyComponentManager);
uberRenderer.AssignEntityManager(dummyEntityManager);
foreach (var type in componentTypes)
{
var componentManagerRegisterMethod = typeof(ComponentManager).GetMethod("RegisterComponentType");
var componentManagerRegisterGenericMethod = componentManagerRegisterMethod.MakeGenericMethod(type);
componentManagerRegisterGenericMethod.Invoke(dummyComponentManager, null);
if (type.GetInterface("IDrawableComponent") != null)
{
var drawLayerManagerRegisterMethod = typeof(DrawLayerManager).GetMethod("RegisterOrderedDrawable");
var drawLayerManagerRegisterGenericMethod = drawLayerManagerRegisterMethod.MakeGenericMethod(type);
drawLayerManagerRegisterGenericMethod.Invoke(dummyDrawLayerManager, null);
}
}
prepEngineOrder.Add(uberEngine);
var dummyWorld = new World(
@ -461,7 +474,7 @@ namespace Encompass
dummyComponentManager.WriteComponents();
uberRenderer.SetEntity(uberEngine.Entity);
uberRenderer.Render();
uberRenderer.Render(1, 0);
}
}
}

View File

@ -1,20 +1,23 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>netstandard2.0</TargetFramework>
<LangVersion>8.0</LangVersion>
<AllowUnsafeBlocks>true</AllowUnsafeBlocks>
<RootNamespace>Encompass</RootNamespace>
<PackageId>EncompassECS.Framework</PackageId>
<Version>0.20.0</Version>
<Version>0.23.0</Version>
<Authors>Evan Hemsley</Authors>
<PackageRequireLicenseAcceptance>true</PackageRequireLicenseAcceptance>
<Company>Moonside Games</Company>
<Product>Encompass ECS</Product>
<PackageProjectUrl>https://github.com/encompass-ecs</PackageProjectUrl>
<PackageLicenseUrl />
<Copyright>Evan Hemsley 2019</Copyright>
<Description>Encompass is an engine-agnostic Hyper ECS framework to help you code games, or other kinds of simulations.</Description>
<Copyright>Evan Hemsley 2020</Copyright>
<Description>Encompass is an engine-agnostic MECS framework to help you code games, or other kinds of simulations.</Description>
<GeneratePackageOnBuild>true</GeneratePackageOnBuild>
<AssemblyName>EncompassECS.Framework</AssemblyName>
<PackageLicenseFile>LICENSE</PackageLicenseFile>
<Platforms>x64;x86</Platforms>
</PropertyGroup>
<ItemGroup>
<None Include="..\LICENSE">
@ -23,8 +26,7 @@
</None>
</ItemGroup>
<ItemGroup>
<PackageReference Include="MoonTools.Core.Graph" Version="1.0.0" />
<PackageReference Include="MoonTools.FastCollections" Version="1.0.0" />
<PackageReference Include="System.Collections.Immutable" Version="1.7.0" />
<PackageReference Include="Microsoft.Bcl.HashCode" Version="1.1.0" />
</ItemGroup>
</Project>

View File

@ -2,18 +2,18 @@ using NUnit.Framework;
using FluentAssertions;
using Encompass;
using System.Runtime.CompilerServices;
namespace Tests
{
public class ComponentTests
{
struct MockComponent : Encompass.IComponent
struct MockComponent
{
public string myString;
public int myInt;
}
struct EntityMessage : IMessage
struct EntityMessage
{
public Entity entity;
}
@ -26,14 +26,14 @@ namespace Tests
{
public override void Update(double dt)
{
foreach (var entityMessage in ReadMessages<EntityMessage>())
foreach (ref readonly var entityMessage in ReadMessages<EntityMessage>())
{
gottenMockComponent = GetComponent<MockComponent>(entityMessage.entity);
}
}
}
struct AddComponentTestMessage : IMessage
struct AddComponentTestMessage
{
public Entity entity;
public MockComponent mockComponent;
@ -45,16 +45,17 @@ namespace Tests
{
public override void Update(double dt)
{
foreach (var addComponentTestMessage in ReadMessages<AddComponentTestMessage>())
foreach (ref readonly var addComponentTestMessage in ReadMessages<AddComponentTestMessage>())
{
Assert.IsTrue(HasComponent<MockComponent>(addComponentTestMessage.entity));
Assert.That(GetComponent<MockComponent>(addComponentTestMessage.entity), Is.EqualTo(addComponentTestMessage.mockComponent));
ref readonly var gottenComponent = ref GetComponent<MockComponent>(addComponentTestMessage.entity);
gottenComponent.Should().BeEquivalentTo(addComponentTestMessage.mockComponent);
}
}
}
[Test]
public void AddComponent()
public unsafe void AddComponent()
{
var worldBuilder = new WorldBuilder();
worldBuilder.AddEngine(new AddComponentTestEngine());
@ -63,7 +64,6 @@ namespace Tests
MockComponent mockComponent;
mockComponent.myInt = 3;
mockComponent.myString = "hello";
worldBuilder.SetComponent(entity, mockComponent);
@ -85,16 +85,15 @@ namespace Tests
worldBuilder.AddEngine(new ReadMockComponentEngine());
var entity = worldBuilder.CreateEntity();
worldBuilder.SetComponent(entity, new MockComponent { myInt = 20, myString = "what" });
worldBuilder.SetComponent(entity, new MockComponent { myInt = 50, myString = "hi" });
worldBuilder.SetComponent(entity, new MockComponent { myInt = 40, myString = "wassup" });
worldBuilder.SetComponent(entity, new MockComponent { myInt = 20 });
worldBuilder.SetComponent(entity, new MockComponent { myInt = 50 });
worldBuilder.SetComponent(entity, new MockComponent { myInt = 40 });
var world = worldBuilder.Build();
world.Update(0.01);
Assert.That(gottenMockComponent.myInt, Is.EqualTo(40));
Assert.That(gottenMockComponent.myString, Is.EqualTo("wassup"));
}
[Reads(typeof(MockComponent))]
@ -103,8 +102,9 @@ namespace Tests
{
public override void Update(double dt)
{
foreach (var (mockComponent, entity) in ReadComponentsIncludingEntity<MockComponent>())
foreach (ref readonly var entity in ReadEntities<MockComponent>())
{
ref readonly var mockComponent = ref GetComponent<MockComponent>(entity);
SetComponent(entity, new MockComponent { myInt = mockComponent.myInt + 1 });
}
}
@ -149,8 +149,9 @@ namespace Tests
{
public override void Update(double dt)
{
foreach (var (mockComponent, entity) in ReadComponentsIncludingEntity<MockComponent>())
foreach (ref readonly var entity in ReadEntities<MockComponent>())
{
ref readonly var mockComponent = ref GetComponent<MockComponent>(entity);
SetComponent(entity, mockComponent);
RemoveComponent<MockComponent>(entity);
}
@ -171,7 +172,7 @@ namespace Tests
Assert.DoesNotThrow(() => world.Update(0.01));
}
struct AddMockComponentMessage : IMessage
struct AddMockComponentMessage
{
public Entity entity;
public MockComponent mockComponent;
@ -191,7 +192,6 @@ namespace Tests
{
MockComponent mockComponent;
mockComponent.myInt = 10;
mockComponent.myString = "four";
AddMockComponentMessage addMockComponentMessage;
addMockComponentMessage.entity = entity;
@ -207,7 +207,7 @@ namespace Tests
{
public override void Update(double dt)
{
foreach (var message in ReadMessages<AddMockComponentMessage>())
foreach (ref readonly var message in ReadMessages<AddMockComponentMessage>())
{
SetComponent(message.entity, message.mockComponent);
}
@ -255,9 +255,8 @@ namespace Tests
MockComponent mockComponent;
mockComponent.myInt = 3;
mockComponent.myString = "hello";
worldBuilder.SetComponent<MockComponent>(entity, mockComponent);
worldBuilder.SetComponent(entity, mockComponent);
EntityMessage entityMessage;
entityMessage.entity = entity;
@ -270,7 +269,7 @@ namespace Tests
Assert.AreEqual(mockComponent, gottenMockComponent);
}
struct HasComponentTestMessage : IMessage
struct HasComponentTestMessage
{
public Entity entity;
}
@ -281,7 +280,7 @@ namespace Tests
{
public override void Update(double dt)
{
foreach (var hasComponentTestEngine in ReadMessages<HasComponentTestMessage>())
foreach (ref readonly var hasComponentTestEngine in ReadMessages<HasComponentTestMessage>())
{
Assert.IsTrue(HasComponent<MockComponent>(hasComponentTestEngine.entity));
}
@ -298,7 +297,6 @@ namespace Tests
MockComponent mockComponent;
mockComponent.myInt = 3;
mockComponent.myString = "hello";
worldBuilder.SetComponent(entity, mockComponent);
@ -319,7 +317,7 @@ namespace Tests
{
public override void Update(double dt)
{
foreach (var hasComponentTestEngine in ReadMessages<HasComponentTestMessage>())
foreach (ref readonly var hasComponentTestEngine in ReadMessages<HasComponentTestMessage>())
{
hasComponentRuntimeTypeResult = HasComponent(hasComponentTestEngine.entity, typeof(MockComponent));
}
@ -336,7 +334,6 @@ namespace Tests
MockComponent mockComponent;
mockComponent.myInt = 3;
mockComponent.myString = "hello";
worldBuilder.SetComponent(entity, mockComponent);
@ -370,7 +367,7 @@ namespace Tests
Assert.IsFalse(hasComponentRuntimeTypeResult);
}
struct RemoveComponentTestMessage : IMessage
struct RemoveComponentTestMessage
{
public Entity entity;
}
@ -382,7 +379,7 @@ namespace Tests
{
public override void Update(double dt)
{
foreach (var removeComponentMessage in ReadMessages<RemoveComponentTestMessage>())
foreach (ref readonly var removeComponentMessage in ReadMessages<RemoveComponentTestMessage>())
{
RemoveComponent<MockComponent>(removeComponentMessage.entity);
}
@ -427,7 +424,7 @@ namespace Tests
{
public override void Update(double dt)
{
foreach (var checkHasMockComponentMessage in ReadMessages<CheckHasMockComponentMessage>())
foreach (ref readonly var checkHasMockComponentMessage in ReadMessages<CheckHasMockComponentMessage>())
{
hasComponentResult = HasComponent<MockComponent>(checkHasMockComponentMessage.entity);
}
@ -446,7 +443,6 @@ namespace Tests
MockComponent mockComponent;
mockComponent.myInt = 3;
mockComponent.myString = "hello";
worldBuilder.SetComponent(entity, mockComponent);
@ -465,7 +461,7 @@ namespace Tests
hasComponentResult.Should().BeFalse();
}
struct CheckHasMockComponentMessage : IMessage
struct CheckHasMockComponentMessage
{
public Entity entity;
public bool shouldHaveComponent;
@ -477,7 +473,7 @@ namespace Tests
{
public override void Update(double dt)
{
foreach (var checkHasMockComponentMessage in ReadMessages<CheckHasMockComponentMessage>())
foreach (ref readonly var checkHasMockComponentMessage in ReadMessages<CheckHasMockComponentMessage>())
{
Assert.IsTrue(HasComponent<MockComponent>(checkHasMockComponentMessage.entity));
}

View File

@ -10,29 +10,29 @@ using Encompass.Exceptions;
namespace Tests
{
struct MockComponent : IComponent
struct MockComponent
{
public int myInt;
public string myString;
}
public class EngineTest
{
static List<MockComponent> resultComponents;
static MockComponent resultComponent;
static MockComponent[] resultComponents = new MockComponent[1];
static List<MockMessage> resultMessages = new List<MockMessage>();
static MockMessage resultMessage;
static MockMessage[] resultMessages = new MockMessage[1];
[Reads(typeof(MockComponent))]
public class ReadComponentsTestEngine : Engine
{
public override void Update(double dt)
{
resultComponents = ReadComponents<MockComponent>().ToList();
resultComponents = ReadComponents<MockComponent>().ToArray();
}
}
static List<(MockComponent, Entity)> resultComponentsIncludingEntity;
static List<(MockComponent, Entity)> resultComponentsIncludingEntity = new List<(MockComponent, Entity)>();
static (MockComponent, Entity) resultComponentIncludingEntity;
[Reads(typeof(MockComponent))]
@ -40,7 +40,11 @@ namespace Tests
{
public override void Update(double dt)
{
resultComponentsIncludingEntity = ReadComponentsIncludingEntity<MockComponent>().ToList();
foreach (ref readonly var entity in ReadEntities<MockComponent>())
{
ref readonly var mockComponent = ref GetComponent<MockComponent>(entity);
resultComponentsIncludingEntity.Add((mockComponent, entity));
}
}
}
@ -58,7 +62,9 @@ namespace Tests
{
public override void Update(double dt)
{
resultComponentIncludingEntity = ReadComponentIncludingEntity<MockComponent>();
ref readonly var entity = ref ReadEntity<MockComponent>();
ref readonly var mockComponent = ref GetComponent<MockComponent>(entity);
resultComponentIncludingEntity = (mockComponent, entity);
}
}
@ -72,12 +78,10 @@ namespace Tests
var entityB = worldBuilder.CreateEntity();
MockComponent mockComponent;
mockComponent.myInt = 0;
mockComponent.myString = "hello";
mockComponent.myInt = 2;
MockComponent mockComponentB;
mockComponentB.myInt = 1;
mockComponentB.myString = "howdy";
worldBuilder.SetComponent(entity, mockComponent);
worldBuilder.SetComponent(entityB, mockComponentB);
@ -93,6 +97,8 @@ namespace Tests
[Test]
public void ReadComponentsIncludingEntity()
{
resultComponentsIncludingEntity.Clear();
var worldBuilder = new WorldBuilder();
worldBuilder.AddEngine(new ReadComponentsIncludingEntityEngine());
@ -100,12 +106,10 @@ namespace Tests
var entityB = worldBuilder.CreateEntity();
MockComponent mockComponent;
mockComponent.myInt = 0;
mockComponent.myString = "hello";
mockComponent.myInt = 2;
MockComponent mockComponentB;
mockComponentB.myInt = 1;
mockComponentB.myString = "howdy";
worldBuilder.SetComponent(entity, mockComponent);
worldBuilder.SetComponent(entityB, mockComponentB);
@ -135,8 +139,7 @@ namespace Tests
var entity = worldBuilder.CreateEntity();
MockComponent mockComponent;
mockComponent.myInt = 0;
mockComponent.myString = "hello";
mockComponent.myInt = 3;
worldBuilder.SetComponent(entity, mockComponent);
@ -147,6 +150,17 @@ namespace Tests
Assert.AreEqual(mockComponent, resultComponent);
}
[Test]
public void ReadComponentThrowsWhenNoneExist()
{
var worldBuilder = new WorldBuilder();
worldBuilder.AddEngine(new ReadComponentTestEngine());
var world = worldBuilder.Build();
Assert.Throws<NoComponentOfTypeException>(() => world.Update(0.01f), "No component of type MockComponent exists");
}
[Test]
public void ReadComponentWhenMultipleComponents()
{
@ -157,12 +171,10 @@ namespace Tests
var entityB = worldBuilder.CreateEntity();
MockComponent mockComponent;
mockComponent.myInt = 0;
mockComponent.myString = "hello";
mockComponent.myInt = 2;
MockComponent mockComponentB;
mockComponentB.myInt = 1;
mockComponentB.myString = "howdy";
worldBuilder.SetComponent(entity, mockComponent);
worldBuilder.SetComponent(entityB, mockComponentB);
@ -183,8 +195,7 @@ namespace Tests
var entity = worldBuilder.CreateEntity();
MockComponent mockComponent;
mockComponent.myInt = 0;
mockComponent.myString = "hello";
mockComponent.myInt = 2;
worldBuilder.SetComponent(entity, mockComponent);
@ -201,11 +212,8 @@ namespace Tests
{
public override void Update(double dt)
{
var (component, entity) = ReadComponentIncludingEntity<MockComponent>();
component.myInt = 420;
component.myString = "blaze it";
SetComponent(entity, component);
ref readonly var entity = ref ReadEntity<MockComponent>();
SetComponent(entity, new MockComponent { myInt = 420 });
}
}
@ -222,8 +230,7 @@ namespace Tests
var entity = worldBuilder.CreateEntity();
MockComponent mockComponent;
mockComponent.myInt = 0;
mockComponent.myString = "hello";
mockComponent.myInt = 3;
worldBuilder.SetComponent(entity, mockComponent);
@ -233,7 +240,6 @@ namespace Tests
world.Update(0.01);
Assert.AreEqual(420, resultComponent.myInt);
Assert.AreEqual("blaze it", resultComponent.myString);
}
[Reads(typeof(MockComponent))]
@ -241,13 +247,8 @@ namespace Tests
{
public override void Update(double dt)
{
var (component, entity) = ReadComponentIncludingEntity<MockComponent>();
component.myInt = 420;
component.myString = "blaze it";
SetComponent(entity, component);
component = ReadComponent<MockComponent>();
ref readonly var entity = ref ReadEntity<MockComponent>();
SetComponent(entity, new MockComponent { myInt = 420 });
}
}
@ -260,8 +261,7 @@ namespace Tests
var entity = worldBuilder.CreateEntity();
MockComponent mockComponent;
mockComponent.myInt = 0;
mockComponent.myString = "hello";
mockComponent.myInt = 3;
worldBuilder.SetComponent(entity, mockComponent);
@ -271,7 +271,7 @@ namespace Tests
Assert.That(ex.Message, Is.EqualTo("Engine UndeclaredUpdateComponentTestEngine tried to update undeclared Component MockComponent"));
}
struct MockMessage : IMessage
struct MockMessage
{
public string myString;
}
@ -289,11 +289,20 @@ namespace Tests
}
[Receives(typeof(MockMessage))]
public class MessageReadEngine : Engine
public class ReadMessagesEngine : Engine
{
public override void Update(double dt)
{
resultMessages = this.ReadMessages<MockMessage>().ToList();
resultMessages = ReadMessages<MockMessage>().ToArray();
}
}
[Receives(typeof(MockMessage))]
public class ReadMessageEngine : Engine
{
public override void Update(double dt)
{
resultMessage = ReadMessage<MockMessage>();
}
}
@ -302,13 +311,24 @@ namespace Tests
{
var worldBuilder = new WorldBuilder();
worldBuilder.AddEngine(new MessageEmitEngine());
worldBuilder.AddEngine(new MessageReadEngine());
worldBuilder.AddEngine(new ReadMessageEngine());
var world = worldBuilder.Build();
world.Update(0.01f);
Assert.AreEqual(resultMessages.First().myString, "howdy");
Assert.AreEqual(resultMessage.myString, "howdy");
}
[Test]
public void ReadMessageThrowsWhenNoneOfTypeExist()
{
var worldBuilder = new WorldBuilder();
worldBuilder.AddEngine(new ReadMessageEngine());
var world = worldBuilder.Build();
Assert.Throws<NoMessageOfTypeException>(() => world.Update(0.01), "No Message of type MockMessage exists");
}
public class UndeclaredMessageEmitEngine : Engine
@ -322,14 +342,14 @@ namespace Tests
}
}
static IEnumerable<MockMessage> emptyReadMessagesResult;
static MockMessage[] emptyReadMessagesResult;
[Receives(typeof(MockMessage))]
class ReadMessagesWhenNoneExistEngine : Engine
{
public override void Update(double dt)
{
emptyReadMessagesResult = ReadMessages<MockMessage>();
emptyReadMessagesResult = ReadMessages<MockMessage>().ToArray();
}
}
@ -415,7 +435,7 @@ namespace Tests
Assert.Throws<IllegalReadException>(() => world.Update(0.01f));
}
struct EntityMessage : IMessage, IHasEntity
struct EntityMessage : IHasEntity
{
public EntityMessage(Entity entity, int myInt)
{
@ -578,10 +598,10 @@ namespace Tests
{
public override void Update(double dt)
{
var components = ReadComponentsIncludingEntity<MockComponent>();
var entities = ReadEntities<MockComponent>();
pairA = components.First();
pairB = components.Last();
pairA = (GetComponent<MockComponent>(entities[0]), entities[0]);
pairB = (GetComponent<MockComponent>(entities[1]), entities[1]);
}
}
@ -594,11 +614,9 @@ namespace Tests
MockComponent componentA;
componentA.myInt = 20;
componentA.myString = "hello";
MockComponent componentB;
componentB.myInt = 20;
componentB.myString = "hello";
var entity = worldBuilder.CreateEntity();
worldBuilder.SetComponent(entity, componentA);
@ -613,14 +631,12 @@ namespace Tests
Assert.That(EngineTest.pairA.Item1, Is.EqualTo(EngineTest.pairB.Item1));
}
static IEnumerable<(MockComponent, Entity)> emptyComponentReadResult;
[Reads(typeof(MockComponent))]
class ReadEmptyMockComponentsEngine : Engine
{
public override void Update(double dt)
{
emptyComponentReadResult = ReadComponentsIncludingEntity<MockComponent>();
ReadEntities<MockComponent>().ToArray().Should().BeEmpty();
}
}
@ -632,32 +648,36 @@ namespace Tests
var world = worldBuilder.Build();
world.Update(0.01f);
Assert.That(emptyComponentReadResult, Is.Empty);
}
struct DestroyerComponent : IComponent { }
struct DestroyerComponent { }
[Reads(typeof(DestroyerComponent))]
class DestroyerEngine : Engine
{
public override void Update(double dt)
{
foreach (var (component, entity) in ReadComponentsIncludingEntity<DestroyerComponent>())
foreach (ref readonly var entity in ReadEntities<DestroyerComponent>())
{
Destroy(entity);
}
}
}
static List<(MockComponent, Entity)> results;
static List<(MockComponent, Entity)> results = new List<(MockComponent, Entity)>();
[Reads(typeof(MockComponent))]
class ReaderEngine : Engine
{
public override void Update(double dt)
{
results = ReadComponentsIncludingEntity<MockComponent>().ToList();
results.Clear();
foreach (ref readonly var entity in ReadEntities<MockComponent>())
{
ref readonly var mockComponent = ref GetComponent<MockComponent>(entity);
results.Add((mockComponent, entity));
}
}
}
@ -675,7 +695,6 @@ namespace Tests
DestroyerComponent destroyerComponent;
MockComponent mockComponent;
mockComponent.myInt = 2;
mockComponent.myString = "blah";
worldBuilder.SetComponent(entity, destroyerComponent);
worldBuilder.SetComponent(entity, mockComponent);
@ -700,7 +719,7 @@ namespace Tests
{
public override void Update(double dt)
{
foreach (var message in ReadMessages<DestroyComponentMessage>())
foreach (ref readonly var message in ReadMessages<DestroyComponentMessage>())
{
Destroy(message.entity);
}
@ -710,6 +729,8 @@ namespace Tests
[Test]
public void DestroyEntityWithoutID()
{
results.Clear();
var worldBuilder = new WorldBuilder();
worldBuilder.AddEngine(new AddComponentEngine());
worldBuilder.AddEngine(new DestroyEntityEngine());
@ -733,7 +754,7 @@ namespace Tests
{
public override void Update(double dt)
{
foreach (var (componentPair, entity) in ReadComponentsIncludingEntity<DestroyerComponent>())
foreach (ref readonly var entity in ReadEntities<DestroyerComponent>())
{
RemoveComponent<MockComponent>(entity);
Destroy(entity);
@ -765,7 +786,7 @@ namespace Tests
{
public override void Update(double dt)
{
foreach (var (mockComponent, entity) in ReadComponentsIncludingEntity<MockComponent>())
foreach (ref readonly var entity in ReadEntities<MockComponent>())
{
RemoveComponent<MockComponent>(entity);
SetComponent(entity, new MockComponent());
@ -780,7 +801,7 @@ namespace Tests
{
public override void Update(double dt)
{
var (_, entity) = ReadComponentIncludingEntity<MockComponent>();
ref readonly var entity = ref ReadEntity<MockComponent>();
}
}
@ -805,7 +826,7 @@ namespace Tests
{
public override void Update(double dt)
{
foreach (var (component, entity) in ReadComponentsIncludingEntity<MockComponent>())
foreach (ref readonly var entity in ReadEntities<MockComponent>())
{
RemoveComponent<MockComponent>(entity);
SendMessage(new MockMessage { }, 1);
@ -816,12 +837,12 @@ namespace Tests
[Test]
public void EngineSendMessageDelayed()
{
resultMessages.Clear();
Array.Clear(resultMessages, 0, resultMessages.Length);
var worldBuilder = new WorldBuilder();
worldBuilder.AddEngine(new ActivateTimeDilationEngine());
worldBuilder.AddEngine(new DelayedMessageEngine());
worldBuilder.AddEngine(new MessageReadEngine());
worldBuilder.AddEngine(new ReadMessagesEngine());
var entity = worldBuilder.CreateEntity();
worldBuilder.SetComponent(entity, new MockComponent { });
@ -852,7 +873,7 @@ namespace Tests
{
public override void Update(double dt)
{
foreach (var (component, entity) in ReadComponentsIncludingEntity<MockComponent>())
foreach (ref readonly var entity in ReadEntities<MockComponent>())
{
RemoveComponent<MockComponent>(entity);
SendMessageIgnoringTimeDilation(new MockMessage { }, 1);
@ -863,12 +884,12 @@ namespace Tests
[Test]
public void EngineSendMessageDelayedIgnoringTimeDilation()
{
resultMessages.Clear();
Array.Clear(resultMessages, 0, resultMessages.Length);
var worldBuilder = new WorldBuilder();
worldBuilder.AddEngine(new ActivateTimeDilationEngine());
worldBuilder.AddEngine(new DelayedMessageIgnoringTimeDilationEngine());
worldBuilder.AddEngine(new MessageReadEngine());
worldBuilder.AddEngine(new ReadMessagesEngine());
var entity = worldBuilder.CreateEntity();
worldBuilder.SetComponent(entity, new MockComponent { });
@ -896,7 +917,7 @@ namespace Tests
{
public override void Update(double dt)
{
foreach (var message in ReadMessages<MockMessage>())
foreach (ref readonly var message in ReadMessages<MockMessage>())
{
var entity = CreateEntity();
SetComponent(entity, new MockComponent { });
@ -934,7 +955,7 @@ namespace Tests
resultComponents.Should().BeEmpty();
}
struct DestroyComponentMessage : IMessage { public Entity entity; }
struct DestroyComponentMessage { public Entity entity; }
[Reads(typeof(MockComponent))]
[Writes(typeof(MockComponent))]
@ -961,7 +982,7 @@ namespace Tests
}
[Test]
public void GetEntityByComponentType()
public void ReadEntity()
{
var worldBuilder = new WorldBuilder();
worldBuilder.AddEngine(new ReadEntityByComponentTypeEngine());
@ -975,14 +996,25 @@ namespace Tests
entity.Should().BeEquivalentTo(readEntity);
}
struct MockComponentB : IComponent
[Test]
public void ReadEntityThrowsWhenNoComponentOfTypeExists()
{
var worldBuilder = new WorldBuilder();
worldBuilder.AddEngine(new ReadEntityByComponentTypeEngine());
var world = worldBuilder.Build();
Assert.Throws<NoComponentOfTypeException>(() => world.Update(0.01), "No component of type MockComponent exists");
}
struct MockComponentB
{
private int value;
public MockComponentB(int value)
{
this.value = value;
}
int value;
}
static MockComponentB getComponentResult;
@ -1134,7 +1166,7 @@ namespace Tests
{
public override void Update(double dt)
{
foreach (var (_, entity) in ReadComponentsIncludingEntity<MockComponent>())
foreach (ref readonly var entity in ReadEntities<MockComponent>())
{
RemoveComponent<MockComponent>(entity);
}
@ -1292,6 +1324,7 @@ namespace Tests
undilatedDeltaTime.Should().Be(0.5);
}
[Adds(typeof(MockComponent))]
class AddComponentWithoutPriorityEngine : Engine
{
public override void Update(double dt)
@ -1329,8 +1362,7 @@ namespace Tests
{
public override void Update(double dt)
{
var (component, entity) = ReadComponentIncludingEntity<MockComponent>();
ref readonly var entity = ref ReadEntity<MockComponent>();
AddComponent(entity, new MockComponent());
}
}
@ -1349,6 +1381,7 @@ namespace Tests
Assert.Throws<IllegalWriteException>(() => world.Update(0.01));
}
[Adds(typeof(MockComponentB))]
[WritesImmediate(typeof(MockComponentB))]
class AddImmediateComponentEngine : Engine
{
@ -1364,8 +1397,7 @@ namespace Tests
{
public override void Update(double dt)
{
var (component, entity) = ReadComponentIncludingEntity<MockComponentB>();
ref readonly var component = ref ReadComponent<MockComponentB>();
getComponentResult = component;
}
}
@ -1420,11 +1452,10 @@ namespace Tests
public class QueryTests
{
struct MockComponentB : IComponent { }
struct MockComponentC : IComponent { }
struct MockComponentD : IComponent { }
struct MockComponentB { }
struct MockComponentC { }
struct MockComponentD { }
[Reads(typeof(MockComponent), typeof(MockComponentB))]
[Writes(typeof(MockComponentB))]
[QueryWith(typeof(MockComponent), typeof(MockComponentB))]
class EntityQueryWithComponentsEngine : Engine
@ -1478,7 +1509,6 @@ namespace Tests
queriedEntities.Should().BeEmpty();
}
[Reads(typeof(MockComponent))]
[Writes(typeof(MockComponent))]
[QueryWithout(typeof(MockComponent))]
class EntityQueryWithoutComponentsEngine : Engine
@ -1532,7 +1562,6 @@ namespace Tests
queriedEntities.Should().BeEmpty();
}
[Reads(typeof(MockComponent), typeof(MockComponentB), typeof(MockComponentD))]
[QueryWith(typeof(MockComponent), typeof(MockComponentB))]
[QueryWithout(typeof(MockComponentD))]
class EntityQueryWithandWithoutComponentsEngine : Engine
@ -1758,7 +1787,6 @@ namespace Tests
}
[ReadsImmediate(typeof(MockComponentB))]
[Reads(typeof(MockComponent))]
[QueryWith(typeof(MockComponent), typeof(MockComponentB))]
class EntityQueryWithImmediateAndNonImmediateComponents : Engine
{
@ -1811,7 +1839,7 @@ namespace Tests
}
public override void Update(double dt)
{
_components.AddRange(ReadComponents<MockComponentB>());
_components.AddRange(ReadComponents<MockComponentB>().ToArray());
}
}
@ -1847,12 +1875,11 @@ namespace Tests
public override void Update(double dt)
{
foreach (var (component, entity) in ReadComponentsIncludingEntity<MockComponent>())
foreach (ref readonly var entity in ReadEntities<MockComponent>())
{
if (HasComponent<MockComponentB>(entity))
{
_components.Add(GetComponent<MockComponentB>(entity));
}
}
}
@ -1877,14 +1904,14 @@ namespace Tests
_components.Should().NotBeEmpty();
}
struct MockTimerComponent : IComponent
struct MockTimerComponent
{
public double Timer { get; }
public MockTimerComponent(double time)
{
Timer = time;
}
public double Timer { get; set; }
}
[Reads(typeof(MockTimerComponent))]
@ -1893,19 +1920,18 @@ namespace Tests
{
public override void Update(double dt)
{
foreach (var (component, entity) in ReadComponentsIncludingEntity<MockTimerComponent>())
foreach (ref readonly var entity in ReadEntities<MockTimerComponent>())
{
var updatedComponent = component;
updatedComponent.Timer -= dt;
ref readonly var component = ref GetComponent<MockTimerComponent>(entity);
if (updatedComponent.Timer <= 0)
if (component.Timer - dt <= 0)
{
RemoveComponent<MockTimerComponent>(entity);
}
else
{
SetComponent<MockTimerComponent>(entity, updatedComponent);
SetComponent<MockTimerComponent>(entity, new MockTimerComponent(component.Timer - dt));
}
}
}

View File

@ -1,109 +0,0 @@
using System;
using NUnit.Framework;
using FluentAssertions;
using Encompass;
using System.Collections.Generic;
namespace Tests
{
public class OrderedRendererTest
{
struct AComponent : IComponent { }
struct BComponent : IComponent { }
struct CComponent : IComponent { }
struct TestDrawComponent : IComponent, IDrawableComponent
{
public int Layer { get; set; }
}
class TestRenderer : OrderedRenderer<TestDrawComponent>
{
public override void Render(Entity entity, TestDrawComponent testDrawComponent) { }
}
static bool called = false;
class DeactivatedRenderer : TestRenderer
{
public override void Render(Entity entity, TestDrawComponent testDrawComponent)
{
called = true;
}
}
static bool calledOnDraw = false;
static (TestDrawComponent, Entity) resultComponent;
class CalledRenderer : OrderedRenderer<TestDrawComponent>
{
public override void Render(Entity entity, TestDrawComponent testDrawComponent)
{
resultComponent = (testDrawComponent, entity);
calledOnDraw = true;
}
}
[Test]
public void RenderMethodCalledOnWorldDraw()
{
var worldBuilder = new WorldBuilder();
worldBuilder.RegisterDrawLayer(2);
var renderer = worldBuilder.AddOrderedRenderer(new CalledRenderer());
AComponent aComponent;
CComponent cComponent;
var testDrawComponent = new TestDrawComponent { Layer = 2 };
var entity = worldBuilder.CreateEntity();
worldBuilder.SetComponent(entity, aComponent);
worldBuilder.SetComponent(entity, cComponent);
worldBuilder.SetComponent(entity, testDrawComponent);
var world = worldBuilder.Build();
world.Update(0.01f);
world.Draw();
Assert.IsTrue(calledOnDraw);
resultComponent.Should().BeEquivalentTo((testDrawComponent, entity));
}
[Reads(typeof(TestDrawComponent))]
class DestroyerEngine : Engine
{
public override void Update(double dt)
{
foreach (var entity in ReadEntities<TestDrawComponent>())
{
Destroy(entity);
}
}
}
[Test]
public void RenderMethodNotCalledAfterDestroy()
{
calledOnDraw = false;
var worldBuilder = new WorldBuilder();
worldBuilder.RegisterDrawLayer(1);
worldBuilder.AddEngine(new DestroyerEngine());
var renderer = worldBuilder.AddOrderedRenderer(new CalledRenderer());
TestDrawComponent testDrawComponent = new TestDrawComponent { Layer = 1 };
var entity = worldBuilder.CreateEntity();
worldBuilder.SetComponent(entity, testDrawComponent);
var world = worldBuilder.Build();
world.Update(0.01);
world.Draw();
Assert.IsFalse(calledOnDraw);
}
}
}

View File

@ -5,17 +5,18 @@ namespace Tests
{
public static class GeneralRendererTest
{
struct AComponent : IComponent { }
struct AComponent { }
public class SingletonRead
{
static (AComponent, Entity) result;
class TestRenderer : GeneralRenderer
class TestRenderer : Renderer
{
public override void Render()
public override void Render(double dt, double alpha)
{
result = ReadComponentIncludingEntity<AComponent>();
ref readonly var entity = ref ReadEntity<AComponent>();
result = (GetComponent<AComponent>(entity), entity);
}
}
@ -23,7 +24,7 @@ namespace Tests
public void SingletonComponent()
{
var worldBuilder = new WorldBuilder();
worldBuilder.AddGeneralRenderer(new TestRenderer(), 1);
worldBuilder.AddRenderer(new TestRenderer());
AComponent aComponent;
@ -33,10 +34,10 @@ namespace Tests
var world = worldBuilder.Build();
world.Update(0.01f);
world.Draw();
world.Draw(0.01f, 0);
world.Update(0.01);
world.Draw();
world.Draw(0.01f, 0);
Assert.That(result, Is.EqualTo((aComponent, entity)));
}
@ -45,7 +46,7 @@ namespace Tests
public void MultipleComponents()
{
var worldBuilder = new WorldBuilder();
worldBuilder.AddGeneralRenderer(new TestRenderer(), 1);
worldBuilder.AddRenderer(new TestRenderer());
AComponent aComponent;
AComponent aComponentTwo;
@ -58,10 +59,10 @@ namespace Tests
var world = worldBuilder.Build();
world.Update(0.01f);
world.Draw();
world.Draw(0.01f, 0);
world.Update(0.01f);
world.Draw();
world.Draw(0.01f, 0);
Assert.That(result, Is.EqualTo((aComponent, entity)).Or.EqualTo((aComponentTwo, entityB)));
}

View File

@ -5,8 +5,8 @@ namespace Tests
{
public class SpawnerTest
{
struct TestComponent : IComponent { }
struct SpawnMessageA : IMessage { }
struct TestComponent { }
struct SpawnMessageA { }
static Entity resultEntity;
@ -23,7 +23,7 @@ namespace Tests
[Writes(typeof(TestComponent))]
class TestSpawner : Spawner<SpawnMessageA>
{
protected override void Spawn(SpawnMessageA message)
protected override void Spawn(in SpawnMessageA message)
{
resultEntity = CreateEntity();
SetComponent(resultEntity, new TestComponent());

View File

@ -5,6 +5,7 @@ using System.Collections.Generic;
using Encompass.Exceptions;
using System.Linq;
using FluentAssertions;
using System;
namespace Tests
{
@ -12,8 +13,8 @@ namespace Tests
{
public class EngineCycleSimple
{
struct AMessage : IMessage { }
struct BMessage : IMessage { }
struct AMessage { }
struct BMessage { }
[Receives(typeof(AMessage))]
[Sends(typeof(BMessage))]
@ -50,10 +51,10 @@ namespace Tests
public class EngineCycleComplex
{
struct AMessage : IMessage { }
struct BMessage : IMessage { }
struct CMessage : IMessage { }
struct DMessage : IMessage { }
struct AMessage { }
struct BMessage { }
struct CMessage { }
struct DMessage { }
[Receives(typeof(AMessage))]
[Sends(typeof(BMessage))]
@ -114,7 +115,7 @@ namespace Tests
public class MultipleEngineWriteConflict
{
struct AComponent : IComponent { }
struct AComponent { }
[Writes(typeof(AComponent))]
class AEngine : Engine
@ -141,12 +142,12 @@ namespace Tests
public class MultipleEngineWriteWithPriority
{
struct SetMessage : IMessage
struct SetMessage
{
public Entity entity;
}
struct AComponent : IComponent
struct AComponent
{
public int myInt;
}
@ -158,7 +159,7 @@ namespace Tests
{
public override void Update(double dt)
{
foreach (var setMessage in ReadMessages<SetMessage>())
foreach (ref readonly var setMessage in ReadMessages<SetMessage>())
{
SetComponent(setMessage.entity, new AComponent { myInt = 0 });
}
@ -172,7 +173,7 @@ namespace Tests
{
public override void Update(double dt)
{
foreach (var setMessage in ReadMessages<SetMessage>())
foreach (ref readonly var setMessage in ReadMessages<SetMessage>())
{
SetComponent(setMessage.entity, new AComponent { myInt = 1 });
}
@ -210,12 +211,12 @@ namespace Tests
public class DefaultWritePriority
{
struct SetMessage : IMessage
struct SetMessage
{
public Entity entity;
}
struct AComponent : IComponent
struct AComponent
{
public int myInt;
}
@ -228,7 +229,7 @@ namespace Tests
{
public override void Update(double dt)
{
foreach (var setMessage in ReadMessages<SetMessage>())
foreach (ref readonly var setMessage in ReadMessages<SetMessage>())
{
SetComponent(setMessage.entity, new AComponent { myInt = 5 });
}
@ -243,7 +244,7 @@ namespace Tests
{
public override void Update(double dt)
{
foreach (var setMessage in ReadMessages<SetMessage>())
foreach (ref readonly var setMessage in ReadMessages<SetMessage>())
{
SetComponent(setMessage.entity, new AComponent { myInt = 1 });
}
@ -257,7 +258,7 @@ namespace Tests
{
public override void Update(double dt)
{
foreach (var setMessage in ReadMessages<SetMessage>())
foreach (ref readonly var setMessage in ReadMessages<SetMessage>())
{
SetComponent(setMessage.entity, new AComponent { myInt = 3 });
}
@ -297,7 +298,7 @@ namespace Tests
public class EngineMessageSelfCycle
{
struct AMessage : IMessage { }
struct AMessage { }
[Receives(typeof(AMessage))]
[Sends(typeof(AMessage))]
@ -318,50 +319,6 @@ namespace Tests
}
}
public class IllegalReadType
{
struct ANonMessage { }
[Reads(typeof(ANonMessage))]
class MyEngine : Engine
{
public override void Update(double dt)
{
}
}
[Test]
public void ThrowsError()
{
var worldBuilder = new WorldBuilder();
Assert.Throws<IllegalReadTypeException>(() => worldBuilder.AddEngine(new MyEngine()), "ANonMessage must be a Message or Component");
}
}
public class IllegalWriteType
{
struct ANonMessage { }
[Sends(typeof(ANonMessage))]
class MyEngine : Engine
{
public override void Update(double dt)
{
}
}
[Test]
public void ThrowsError()
{
var worldBuilder = new WorldBuilder();
Assert.Throws<IllegalSendTypeException>(() => worldBuilder.AddEngine(new MyEngine()), "ANonMessage must be a Message or Component");
}
}
public class PriorityConflict
{
[Writes(typeof(MockComponent), 2)]
@ -428,13 +385,13 @@ namespace Tests
{
static List<Engine> order = new List<Engine>();
struct AComponent : IComponent { }
struct BComponent : IComponent { }
struct AComponent { }
struct BComponent { }
struct AMessage : IMessage { }
struct BMessage : IMessage { }
struct CMessage : IMessage { }
struct DMessage : IMessage { }
struct AMessage { }
struct BMessage { }
struct CMessage { }
struct DMessage { }
[Sends(typeof(AMessage))]
class AEngine : Engine
@ -501,20 +458,22 @@ namespace Tests
Assert.That(order.IndexOf(engineC), Is.LessThan(order.IndexOf(engineD)));
}
static List<AMessage> resultMessages = new List<AMessage>();
static AMessage[] resultMessages;
[Receives(typeof(AMessage))]
class ReadMessageEngine : Engine
{
public override void Update(double dt)
{
resultMessages = ReadMessages<AMessage>().ToList();
resultMessages = ReadMessages<AMessage>().ToArray();
}
}
[Test]
public void SendMessageDelayed()
{
resultMessages = Array.Empty<AMessage>();
var worldBuilder = new WorldBuilder();
worldBuilder.AddEngine(new ReadMessageEngine());
@ -539,8 +498,8 @@ namespace Tests
{
static List<Engine> order = new List<Engine>();
struct AMessage : IMessage { }
struct BMessage : IMessage { }
struct AMessage { }
struct BMessage { }
[Sends(typeof(AMessage), typeof(BMessage))]
class AEngine : Engine

View File

@ -1,81 +0,0 @@
using NUnit.Framework;
using FluentAssertions;
using System;
using System.Collections.Generic;
using System.Text;
using Encompass;
namespace Tests
{
public class WorldTest
{
struct TestComponent : IComponent { }
struct TestDrawComponent : IComponent, IDrawableComponent
{
public int Layer { get; set; }
}
static List<object> drawOrder = new List<object>();
class TestEntityRenderer : OrderedRenderer<TestDrawComponent>
{
public override void Render(Entity entity, TestDrawComponent testDrawComponent)
{
drawOrder.Add(entity);
}
}
class TestGeneralRenderer : GeneralRenderer
{
public override void Render()
{
drawOrder.Add(this);
}
}
[Test]
public void DrawOrder()
{
var worldBuilder = new WorldBuilder();
worldBuilder.RegisterDrawLayer(1);
worldBuilder.RegisterDrawLayer(2);
worldBuilder.RegisterDrawLayer(3);
worldBuilder.RegisterDrawLayer(4);
worldBuilder.RegisterDrawLayer(7);
worldBuilder.AddOrderedRenderer(new TestEntityRenderer());
var testGeneralRenderer = worldBuilder.AddGeneralRenderer(new TestGeneralRenderer(), 7);
TestComponent testComponent;
TestDrawComponent drawComponentThree = new TestDrawComponent { Layer = 3 };
var drawComponentTwo = new TestDrawComponent { Layer = 2 };
var drawComponentOne = new TestDrawComponent { Layer = 1 };
var drawComponentFour = new TestDrawComponent { Layer = 4 };
var entity = worldBuilder.CreateEntity();
worldBuilder.SetComponent(entity, testComponent);
worldBuilder.SetComponent(entity, drawComponentThree);
var entityTwo = worldBuilder.CreateEntity();
worldBuilder.SetComponent(entityTwo, testComponent);
worldBuilder.SetComponent(entityTwo, drawComponentTwo);
var entityThree = worldBuilder.CreateEntity();
worldBuilder.SetComponent(entityThree, testComponent);
worldBuilder.SetComponent(entityThree, drawComponentThree);
var entityFour = worldBuilder.CreateEntity();
worldBuilder.SetComponent(entityFour, testComponent);
worldBuilder.SetComponent(entityFour, drawComponentFour);
var world = worldBuilder.Build();
world.Update(0.01f);
world.Draw();
drawOrder.Should().BeEquivalentTo(entityFour, entityTwo, entity, entityThree, testGeneralRenderer);
}
}
}

View File

@ -1,10 +1,12 @@
<?xml version="1.0" encoding="utf-8"?>
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>netcoreapp3.0</TargetFramework>
<TargetFramework>net5.0</TargetFramework>
<IsPackable>false</IsPackable>
<RootNamespace>Tests</RootNamespace>
<AssemblyName>EncompassECS.Framework.Tests</AssemblyName>
<AllowUnsafeBlocks>true</AllowUnsafeBlocks>
<Platforms>x64;x86</Platforms>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="FluentAssertions" Version="5.7.0" />