Closed junkdog closed 10 years ago
More granular tuning is planned for a later issue, this is mostly about getting something useful in there asap.
edit: in part covered by @Sticky
, but user-supplied strategies are not included.
Very smart design! I Like this a lot.
Just a small comment about the staging that might benefit from some tweaking: Stage 1: Define archetype composition, not state. Stage 2: Define archetype state + instance.
That means that any state changes during entity creation carry over to the next instancing, which may be undesirable. It also creates a bit of duplicity. (When I call position(1,1)
, The need for Position.class is implied.)
Maybe you can see a way to change it to: Stage 1: Define archetype composition + state Stage 2: Define entity divergent state + instance
Make it so the setters implicitly add components while assembling the archetype. It would increase utility immensely by all the boilerplate we would get rid of. so instead of :
new ArchetypeBuilder().add(...).add(...).add(...).buildTemplate(world, Ship.class)
do:
Ship shipSlow = Archetype.of(world, Ship.class) // no constructor, so we can return Ship
.with(Size.class, Hitpoints.class)
.position(120, 200)
.asset("ship14")
.velocity(5, 0);
Not sure how to solve setting properties for a specific instance pollute the archetype. First thought was to create two interface implementations, but it just ends one big inappropriate mess.
Some other small suggestions.
public interface Archetype<T> {
T copy();
Archetype<T> with(.. components);
Archetype<T> tag(String tag);
Archetype<T> group(String[] group);
Entity create();
}
Another comment on usability, having one interface per archetype is not something I'd use in practice, especially since it doesn't define structure at all. It's just extra work.
I'd probably have more use for the same concept as you wrote up as a builder to assemble archetypes with, not as an actual concrete archetype.
Just to clarify what I mean,
If your goal is a builder, maybe it makes more sense to allow the user to create a single implementation of the auto completed interface with setters for all my components to bake archetypes with. And provide a different solution for post creation. (since post creation the usecases are a little different anyway. like set UUID, removing components, set up hierarchies, etc).
Or alternatively, if you /do/ want the user to define concrete types, why not in the form of Pojo's so they get to save time defining types and just hook up whatever serialization they desire. Edit: PERFORMANCE OFC SILLY DAAN!
interface ConcreteArchetype<T> { Entity getEntity(); }
class Ship implements ConcreteArchetype<Ship> {
public Pos pos = new Pos(5,5);
}
class World {
public <T> T create(ConcreteArchetype<T> archetype) { .. }
}
public void test()
{
Ship ship = world.create(myExampleShip);
ship.pos.x = 1;
ship.pos.y = 1;
}
That means that any state changes during entity creation carry over to the next instancing, which may be undesirable. It also creates a bit of duplicity. (When I call position(1,1), The need for Position.class is implied.)
That's what @Sticky
is for; it's effectively a default value; could even forbid changing it; as it carries over to the next created instance, if the ParameterizedArchetype is changed (just using the name now to avoid confusion btw), but it makes it harder to create a couple of different templates per entity.
This could probably be worked around though.
Make it so the setters implicitly add components while assembling the archetype. It would increase utility immensely by all the boilerplate we would get rid of. so instead of
Yup, much cleaner implicitly adding them. @CRef
is pretty much a required annotation - to avoid problems with incremental compilation - not even JMH handles it properly. I've used the reflections library in the past to overcome this, but it doesn't like reading from anything which comes from external libraries and projects.
It would then become:
Ship shipSlow = Archetype.of(Ship.class) // no constructor, so we can return Ship
// .with(Size.class, Hitpoints.class)
.position(120, 200)
.asset("ship14")
.velocity(5, 0)
.build(world);
The build(world)
call is necessary for determining compositionId and resolving component classes to CompontentTypes. Also need a way to serialize these to json and other external formats, existing independently from a world instance.
We're going to type it constantly, Just name the interface Archetype.
But that breaks stuff :( - how about Template? Besides, apart from defining interfaces, it won't ever be typed in code.
Make group an array.
:+1:
Another comment on usability, having one interface per archetype is not something I'd use in practice, especially since it doesn't define structure at all. It's just extra work.
With @CRef
we'd get rid of the first step completely. In practice, most games would have less than (10 templates) + variations.
We need to define the interface somewhere, so that the IDE can intellisense it.
In practice, this would demote Archetype to an internal class, which is a good thing.
...
Posting what I have while I answer the next comment..
Edit: PERFORMANCE OFC SILLY DAAN!
That and we can't propagate component values in a meaningful way to the created entities.
That and we can't propagate component values in a meaningful way to the created entities.
Can you elaborate? Generating a clone(C target)
method shouldn't be much harder than populating an interface right?
The approach I'm going for is to use an annotation processor to generate the code: meaning, no maven/gradle plugin + works with GWT out of the box. Using an AP also limits us to working on interfaces* - AP:s can't modify existing sources - only generate sources/classes as part of the compilation process (this leads to smoother IDE integration).
Clone wouldn't work with packed or pooled components as these require the World for instantiation. It could be overcome - especially in the case of pooled components - but it'd open up a can of worms (requiring changes to the weaver + making sure it actually works for all cases; multi-module projects encountering out-of-sync states may be another problem).
edit: Another issue is dealing with clone for fields which aren't primitives. It's possible these wouldn't work as expected.
*) It doesn't limit us to working on interfaces, but if we extract the actual implementation from classes, we might give the end-user the idea that they are actually working on the pojo, not a behind-the-scenes implementation exposed via an interface. If we choose to actually use the pojo, we need to take care multiple instances of the same pojo and communicate that to the end-users (another brittle set of edge-cases).
Updated issue description with rev 2.
The approach I'm going for is WALL OF TEXT SNIPPED
Pretty cool, I should read up on annotation processors! Nb. I've seen IntellIJ intellisense generated classes without predefined interface. No clue how it works though. Might be making it up.
I agree the Pojos are a bad idea, given the impression of concrete types. Would make more sense in the form of Aspects as Pojos, and allow entity instancing and accessing using these. A discussion better held elsewhere though. After reflection I like your plan better.
That's what @Sticky is for; it's effectively a default value; could even forbid changing it; as it carries over to the next created instance, if the ParameterizedArchetype is changed (just using the name now to avoid confusion btw), but it makes it harder to create a couple of different templates per entity.
Hey no fair, you ninja-ed in some changes! XD I'm liking this evolution though, especially getting rid of the boilerplate.
Still not sure about sticky though. It doesn't feel very intuitive. With templates, I'd expect the setters to 'stick' by default. maybe it makes more sense having an annotation for properties that are considered arguments for construction? That way there's no ambiguousness going on. The value doesn't persist, and they get an exception when they forget to set it.
Most of the effort is pre-creation, making it limited but crystal clear would be more beneficial than trying to cover all post creation cases as well. Composition divergence after creation is something that quickly grows far beyond this system. Think effects of Diablo specials, uuid, entity hierarchy.
Nb. Can you explain the process behind archetype composition, especially with Sticky? I assume the CRefs are always added?
Nb: support for parameter components please! Nb 2: interface inheritance pretty please!
But that breaks stuff :( - how about Template? Besides, apart from defining interfaces, it won't ever be typed in code.
Template can work, with Archetype as an internalized class that makes sense.
Nb. If I make a space shooter and have 50 different ship types that share composition but not state, what's the plan there? User needs to set it pre creation? Edit: I assume we do Archetype.of(Ship.class) x 50, and each instance is distinct.
The build(world) call is necessary for determining compositionId and resolving component classes to CompontentTypes. Also need a way to serialize these to json and other external formats, existing independently from a world instance.
If you put world in the Archetype.of
method we can save the user some boilerplate when they make their own convenience methods. ;) createShip().bla.build();
Continuation on inverting @Sticky
, assuming all components are added to the template by default, we could broaden the use for instancing by providing an @Optional
parameter.
public interface Chicken extends Template<Chicken> {
// optional components are always instance parameters, and not added if not called pre create.
@CRef(Flaming.class, optional=true) Chicken flaming();
// not optional instance parameters throw exception when missing.
@CRef(Position.class, instanceParameter=true) Chicken pos(float x, float y);
// by default, component is sticky, and always added.
@CRef(Squawk.class) Chicken squawks(int volume);
}
or perhaps:
public interface Chicken extends Template<Chicken> {
@CRef(Flaming.class) @Optional Chicken flaming();
@CRef(Position.class) @Manual Chicken pos(float x, float y);
@CRef(Squawk.class) Chicken squawks(int volume);
}
or perhaps:
public interface Chicken extends Template<Chicken> {
@OptionalArgument(Flaming.class) Chicken flaming();
@RequiredArgument(Position.class) Chicken pos(float x, float y);
@CRef(Squawk.class) Chicken squawks(int volume);
}
bleh can't come up with sane names, but you get the idea.
Just to clarify why inverting sticky, @Sticky
methods will be more common than nonsticky. Plus feels a bit like the equivalent of flagging fields on a persistence Entity @NotTransient
.
Still, I hope you can come up with better names for @NotSticky
XD.
Just for the sake of DRY and boilerplate:
public interface MobileActor<T> extends Template<T> {
@CRef(Position.class) @Manual Chicken pos(float x, float y);
@CRef(Anim.class) @Manual Anim anim(String id);
}
public interface Chicken extends MobileActor<Chicken> {
@CRef(Flaming.class) @Optional Chicken flaming();
@CRef(Squawk.class) Chicken squawks(int volume);
}
Here's an edge case to keep in mind when dealing with (un)sticky. (Support for this would be desirable, though you could argue people need to break up their components when this happens or apply the same (un)sticky and optional parameters on each).
public interface Cookie extends Template<Cookie > {
@CRef(Color.class) Chicken color(float r,float g, floatb);
@CRef(Color.class) @Manual Chicken alpha(float alpha);
}
Here's another one. Maybe we can come up with an annotation/contract to allow hooking up managers?
public interface MyTemplate extends Template<MyTemplate> {
// resolve manager + maps to TagManager#tag(entity, tag);
@MRef(TagManager.class)
MyTemplate tag(String tag);
// resolve manager + maps to GroupManager#group(entity, group);
@MRef(GroupManager.class)
MyTemplate group(String group);
// resolve manager + maps to GroupManager#fruitit(entity, apple, pear, midgets);
@MRef(MyCustomManager.class)
MyTemplate fruitIt( Apple apple, Pear pear, String []midgets );
@MRef(UuidManager.class) @Optional // optional so creation only!
MyTemplate uuid( UUID uuid );
}
Ok why didn't I know about annotation processors, those are seriously cool :P
maybe it makes more sense having an annotation for properties that are considered arguments for construction? That way there's no ambiguousness going on. The value doesn't persist, and they get an exception when they forget to set it.
It's mostly about minimizing boilerplate; default valued components are, I think, much less frequently occurring than per-Entity values. reads newly added comments But you disagree. Hmm, I'll look into how it looks in my games - it might be more evenly distributed.
I agree that @Sticky
is a crappy name; dropping a few alternatives:
Nb. If I make a space shooter and have 50 different ship types that share composition but not state, what's the plan there? User needs to set it pre creation?
Common conceptual groups of enemties could copy() a more base Template (baseShip) and make changes to it:
// hitpoints, yadi & yada are @Sticky
bossShip = ship.copy().hitpoints(20234324).yadi("something").yada(32).rebuild();
If so, it might actually be best to force a subsequent build(world)
(or just rebuild()) to seal the default values.
Then when creating:
factory.bossShip.pos(x, y).vel(20, 30).create();
Continuation on inverting @Sticky, assuming all components are added to the template by default, we could broaden the use for instancing by providing an @Optional parameter.
Optional parameters would be nice, but adds a lot of logic (need to track all optional-imposed permutations, as these affect compositionId). I'd rather have extended Templates/template inheritance (in the future we could weave away the inheritance, until then one can do it by hand).
Here's an edge case to keep in mind when dealing with (un)sticky.
Already implied by the design (that's why interface param names are enforced even if there's only one field in the referenced component)!
The @MRef
ideas I really like - will only have group and tag from the start; need to come up with a good way of associating generic managers with the same/similar approach.
Just for the sake of DRY and boilerplate:
:+1:
Ok why didn't I know about annotation processors, those are seriously cool :P
Because when you run into the limitations they really start bothering you! Also, there used to be a real lack of tutorials, especially those which did something useful.
I like the whole idea at the current point (edit #3). + Daan's managers + Sticky->Default.
I first thought that @Default
(@Sticky
) should be the default and it would be better to have @NonPersisted
(@NonSticky
) but now I'm rather confused. You're both posting too much, I can't keep up with all of this!
I checked out my entity compositions in shaman's:
In another game it's mostly 50/50 - in part because many transient components are bootstrapped by managers.
You're both posting too much, I can't keep up with all of this!
And the details are changing so much! So, if I overlooked responding to something, poke me.
Implementation for interface Ship extends ParameterizedArchetype
would be provided automatically? How it works? Is it based on on Components' constructors? Like, Position
component have constructor Position(float x, float y)
then I can put pos(float x, float y)
into archetype?
Coming back to Sticky topic. Would it be possible to create some @RequiredArgument
(but I like @CRef
+@Manual
more) to throw some exception (which Daan posted about)?
Implementation for interface Ship extends ParameterizedArchetype would be provided automatically? How it works? Is it based on on Components' constructors? Like, Position component have constructor Position(float x, float y) then I can put pos(float x, float y) into archetype?
It would use an Annotation Processor to generate the required implementation. Internally it would use Archetype + ComponentMappers to modify the component values upon creating the entity, but before returning it to the end-user, as this is common denominator for working with all component types while also being the fastest/most efficient).
Coming back to Sticky topic. Would it be possible to create some @RequiredArgument (but I like @CRef+@Manual more) to throw some exception (which Daan posted about)?
Yup, it will, but when trying to set a shared/sticky/persisted value on an already built template.
It's mostly about minimizing boilerplate; default valued components are, I think, much less frequently occurring than per-Entity values. reads newly added comments But you disagree. Hmm, I'll look into how it looks in my games - it might be more evenly distributed.
Ok last thing I'll post, then I'll shut up about it since it doesn't matter /that/ much ;)
I think all of this can be solved by deciding if this is a Factory or a Template. The whole point of Templating from the users perspective is a blueprint for entities that detangles entity definition from instancing. If it's mainly about instancing that happens to support sticky some previous setters then it isn't primarily a Template but a factory. If we add the optional components it would be closer to a builder. Bit semantic perhaps, forgive the nitpicking.
If I need to do anything more than position and perhaps apply a physics force onto my templates, I'm not sure if I'm actually templating. (Though some things, like UI, will probably be mainly far less concrete templates than in game entities).
Persisted Shared Default
I'd name Template differently if you prefer to keep Sticky. thinking of it like a factory instead of a template might make @Sticky more intuitive.
Common conceptual groups of enemties could copy() a more base Template (baseShip) and make changes to it:
:+1:
(or just rebuild()) to seal the default values.
I'd prefer annotations over sealing default values, it's more granular and less of a case of function defining form.
Optional parameters would be nice, but adds a lot of logic (need to track all optional-imposed permutations, as these affect compositionId). I'd rather have extended Templates/template inheritance (in the future we could weave away the inheritance, until then one can do it by hand).
Optional setters are all instance time though. If I want to add some random flag components on my spaceships I'm going to have to make either hundreds of templates, or do it myself after instancing anyway. Being able to do it with template as an optional setter would be preferable.
Already implied by the design (that's why interface param names are enforced even if there's only one field in the referenced component)!
I checked out my entity compositions in shaman's: 13 sticky vs 3 optional
Just so we're talking about the same thing, @optional
would mean a instance only setter, if when not called the component is not added either. I think here you're talking about nonsticky?
Just did a quick scan, for me it's mainly Pos and Physics and callbacks being an instance-time parameter. For UI concrete templates start to make far less sense, more about generic templates,
The build(world) call is necessary for determining compositionId and resolving component classes to CompontentTypes. Also need a way to serialize these to json and other external formats, existing independently from a world instance.
Is there are reason not to do .build()
upon .create()
via dirty flag? I assume the once-off delay will be negligible and it saves the user some complexity.
Nb. Maybe just park @Optional
for now, get the basics in place first.
Ok last thing I'll post, then I'll shut up about it since it doesn't matter /that/ much ;)
But it does! Otherwise we might corner ourselves in a mess in the future.
If it's mainly about instancing that happens to support sticky some previous setters then it isn't primarily a Template but a factory.
Factory it is.
I'd prefer annotations over sealing default values, it's more granular and less of a case of function defining form.
How do you mean - via extended interfaces?
Optional setters are all instance time though. If I want to add some random flag components on my spaceships I'm going to have to make either hundreds of templates, or do it myself after instancing anyway. Being able to do it with template as an optional setter would be preferable.
I'd be tricky to do this efficiently - meaning without invoking an edit().add() internally each time. It'd be potentially OptionalComponent x OptionalComponent permutations.
Is there are reason not to do .build() upon .create() via dirty flag? I assume the once-off delay will be negligible and it saves the user some complexity.
Fair enough.
Implementation for interface Ship extends ParameterizedArchetype would be provided automatically? How it works? Is it based on on Components' constructors? Like, Position component have constructor Position(float x, float y) then I can put pos(float x, float y) into archetype?
I assume @junkdog plans to match fields by name, since using constructors cause a lot of precedence issues, and are fubar with packed/pooled components anyway.
Exaclty.
But, so wait, if we rename Template->Factory, they almost need some more descriptive prefix - factory feels very generic.
Just so we're talking about the same thing, @optional would mean a instance only setter, if when not called the component is not added either. I think here you're talking about nonsticky?
Oops, I meant to write per-instance.
Factory it is.
I realize this came about of me moaning about ParameterizedArchetype being too long, since @Sticky
sorta works with it. ;) Like (Entity)Factory better though.
How do you mean - via extended interfaces?
Got to retrace the conversation now! XD You mentioned sealing default values via a method call. I thought this implied no @Sticky
, but instead having the method freeze all the current values as sticky?
I'd be tricky to do this efficiently - meaning without invoking an edit().add() internally each time. It'd be potentially OptionalComponent x OptionalComponent permutations.
Figured it would just be a Bag with components that you flush before create.;) Curses!
In the end it's about making things convenient while luring users away from bad decisions. Maybe forgetting about this and force users to post factory dynamic components is the most sensible option. People can make their own post-factory parametrizers.
Nb. Same with Tag and Group though, are those sticky or non sticky by default?
So how about renaming this ticket 'First draft of plan for X' after we're done and compacting all this into a new ticket so people who don't have ten hours to read through our brainfarting can weigh in. ;)
But it does! Otherwise we might corner ourselves in a mess in the future.
Would expect that more in the serialization department, not sure how it connects with factories. I'd want the option to define composition externally, factories wouldn't help here.
But, so wait, if we rename Template->Factory, they almost need some more descriptive prefix - factory feels very generic.
Sweatshop ComponentMines InstanceMill AssemblyPlant EntityFactory
;)
Got to retrace the conversation now! XD You mentioned sealing default values via a method call. I thought this implied no @Sticky, but instead having the method freeze all the current values as sticky?
No, only finalizes the @Sticky
values - applicable in the copy() case, when you want to make changes to a default values. Once a factory is setup, you don't want to accidentally change a per-Factory value, as it'd affect all future entities, and probably an annoyance to debug until you know what to look for.
Nb. Same with Tag and Group though, are those sticky or non sticky by default?
Think they'll need the Sticky
annotation, otherwise per-instance, to adhere to the same semantics.
Figured it would just be a Bag with components that you flush before create.;) Curses!
It'd work, but it would remove all optimizations pertaining to Archetypes.
Would expect that more in the serialization department, not sure how you plan to connect the two. I'd want to define composition externally, factories wouldn't help here.
It could if the interface is linked to an external definition; it could even be generated (by a future extension of the plugin), but you're right - it's very speculative and not necessarily practical.
Ok, I'll update with what we've concluded and post it in a comment; if there are any omissions, update it and once it's done, create the new ticket matching/based on the most recent comment.
Remains pretty much the same, except for overloaded group:s.
public interface EntityFactory<T> {
EntityFactory<T> copy(); // create a new EntityFactor based on this
EntityFactory<T> tag(String tag); // for now, per-Instance
EntityFactory<T> group(String group); // per Factory (?)
EntityFactory<T> group(String groupA, String... groups);
EntityFactory<T> group(String groupA, String groupB, String... groups);
Entity create();
}
Adds class-level @Cref
to avoid using the archetype builder at all.
// declare components not covered by methods (flag-type components etc)
@Cref({FlagCompoentA.class, SomeOtherFlagTypeComponent.class})
public interface Ship extends EntityFactory<Ship> {
@CRef(Position.class) Ship pos(float x, float y);
@CRef(Velocity.class) Ship velocity(float x, float y);
@CRef(Asset.class) @Sticky Ship asset(String path);
@CRef(Size.class) @Sticky Ship size(float width, float height);
@CRef(HitPoints.class) Ship hitPoints(int current);
}
EntityFactories can be extended:
public interface BossShip extends Ship {
@CRef(Position.class) BossShip superWeapon(String name);
}
public class EntityInstanceMill extends Manager {
public Ship shipA;
public Ship shipB;
@Override
protected void initialize() {
shipA = Archetype.of(world, Ship.class)
.asset("ship42")
.size(20, 15)
// copy() allows updating the factory with new stickied values
// but the component composition remains
shipB = shipA.copy().asset("ship21");
}
EntityInstanceMill factory = world.getManager(EntityInstanceMill.class);
Entity player = factory.shipA.position(0, 0).tag("player").create();
The MRef thing might actually be required right away, even if we choose to only include support for group, tag and uuid managers: mainly, because of group (might be sticky) and tag (probably not sticky).
I kept the class-level CRef, as we need somewhere to define components whch aren't part of parameterized creation.
Updated the post one more time; have I missed anything? It's been discussed over "a few" pages now.
No, only finalizes the @Sticky values - applicable in the copy() case, when you want to make changes to a default values. Once a factory is setup, you don't want to accidentally change a per-Factory value, as it'd affect all future entities, and probably an annoyance to debug until you know what to look for.
Altering immutable values would throw an exception? Some users might use @Sticky for batching post create
, maybe make freezing explicit action?
Edit: Might make sense to keep it in, the moment of freeze isn't completely intuitive though.
Think they'll need the Sticky annotation, otherwise per-instance, to adhere to the same semantics.
Sticky tag only makes sense for singleton entities.
It could if the interface is linked to an external definition; it could even be generated (by a future extension of the plugin), but you're right - it's very speculative and not necessarily practical.
I like your design now though. Factories are ideal for lightweight prototyping and simple json can be easily added to branch out. When people go full json crazy they'll more than likely go straight for lower level Archetypes, factories have no added value then.
Edit: Might make sense to keep it in, the moment of freeze isn't completely intuitive though.
Yeah, otherwise I agree. copy() could overcome it though, but is not as versatile.
When people go full json crazy they'll more than likely go straight for lower level Archetypes, factories have no added value then.
:+1:
Yeah, otherwise I agree. copy() could overcome it though, but is not as versatile.
Maybe make stickies immutable upon .create()
, unfreeze copies made with .copy()
, and wait for use cases to appear in ticketing.
Since interfaces allow pretty flexible inheritance, maybe it makes sense splitting off the group/tag so people can mixin their own replacement managers? The Artemis ones are not exactly the greatest
public interface MinimalEntityFactory<T> {
MinimalEntityFactory<T> copy();
MinimalEntityFactory create();
}
public interface EntityFactory<T> extends MinimalEntityFactory<T> {
EntityFactory<T> tag(String tag); // for now, per-Instance
EntityFactory<T> group(String group); // per Factory (?)
EntityFactory<T> group(String groupA, String... groups);
EntityFactory<T> group(String groupA, String groupB, String... groups);
}
interface Ship extends EntityFactory<Ship> {
..
}
Updated the post one more time; have I missed anything? It's been discussed over "a few" pages now.
Sure! I'll keep you occupied!:
@Sticky
mini javadoc:
@CRef
/setter mini javadoc:
@CRef(Color.class) @Sticky Ship color(float r,float g, floatb);
@CRef(Color.class) @Sticky Ship alpha(float alpha);
Sooo, will current ArchetypeBuilder
stay? I refer to rev5 on wiki
Yes, for now at least. We'll see about the future.
Since interfaces allow pretty flexible inheritance, maybe it makes sense splitting off the group/tag so people can mixin their own replacement managers? The Artemis ones are not exactly the greatest
We'll fix mixin-style inheritance in time for 0.8.0
Looks good, can you add a small note on what happens when a setter lacks @Sticky
, and is not called before a random .create()
? Exception?
Ah, it's just added - ie; calling the default constructor.
is it coded yet? What about now.
Dishes, lunch followed by #189 - might do a little coding during the gaps.
ugh....
// breaks, need to put generic methods last
// no.... fac.hitPoints(20).tag("hello").size(20, 10);
fac.hitPoints(20).size(20, 10).tag("hello"); // works
make it return T
? instead of ParameterizedArchetype<T>
public interface ParameterizedArchetype<T extends ParameterizedArchetype> {
T cookies();
}
It is, the problems lie within the limitations of java's type inference. At least, I think there's no way around it - except if using java 8; think it improves TI. Saving the variable to a new local variable after invoking tag() or group() correctly deduces T to the right type - the problem only arises when chaining.
Rev 2:
New core interface:
Remains pretty much the same, except for overloaded group:s.
Declare Ship interface
Adds class-level
@Cref
to avoid using the archetype builder at all.Create ship template and factory
Original desription
Ok, so this is what I suggest: Class, annotation and interface names are very much placeholders, feel free to chime in.
New core interface:
Usage: Example Ship entity and EntityFactory
Declare Ship interface
Edit
#3
: Added CRef annotation; makes life simpler for the annotation processor.There's a bit of magic going on here, unfortunately, but it's done in the interest of keeping boilerplate to a minimum:
method names match component names, position=Position, hitPoints=HitPoints etc.delayed until a later iteration.Create ship template and factory
Creating entities
edit: added
@Sticky