I you want to do this without code it is easy to setup using a similar model 
then that I gave you, maybe even the same. However, it will be horror to use 
for coders since it is inevitable that these traits will interact, and often in 
unintended ways. They will likely also have to work together and then the type 
safety helps a lot.

However, there was a saying in the 80's that said that if someone wanted a 
language where you could just say what you wanted, then give him a lollipop. 
The problem you're trying to solve is ancient and would be worth a lot of money 
if you could pull it off. So far these systems generally tend to turn into 
bloated monsters that are hard to use for users as well as developers.

Unless you user trait integration is just selecting them on an object, you 
could take a look at Javascript. It is quite a nice integration language and 
there are zillion of tutorials. The trait model works very well for Javascript 
since you can easily add fields and functions and everything is visible to 
everybody. However, that means giving up type safety. 

Good luck, 

        Peter Kriens

> On 18 Sep 2020, at 07:49, Zyle Moore <moore.z...@gmail.com> wrote:
> 
> Thanks so much for looking at this. I'll be playing around with the code you 
> provided and seeing where that gets me. An additional complexity I didn't 
> mention in the initial question was that I also want users, not developers, 
> of this game to have nearly as much flexibility with the "traits" as the 
> developers themselves. Meaning that developers would provide the traits, and 
> the users could configure them into whatever combination they want, without 
> the need to write code to wire it together, and figured I could leverage the 
> developer way more easily for the users, but it seems I still have a lot of 
> thinking to do. Thanks again.
> 
> On Thu, Sep 17, 2020 at 3:31 AM Peter Kriens <peter.kri...@aqute.biz 
> <mailto:peter.kri...@aqute.biz>> wrote:
> You're moving in a direction that can be a siren song. You basically want to 
> add what I'd call 'traits' to actor dynamically. Now, this is not so 
> difficult to do. In Smalltalk and Javascript this is quite easy to do because 
> you can dynamically extend the fields of an instance. In Java, everything 
> must statically compile. The advantage is of course that then everything is 
> statically compiled and the compiler can help you tremendously to write 
> error-free code.
> 
> If you implement the trait model in a type safe way, it tends to become a lot 
> harder to write code for it. That is why it is very easy to implement such a 
> model in languages like Smalltalk, Javascript, and I recall Python. You just 
> add extra fields and methods. Since nothing is type checked in the compiler, 
> you do not have to provide any metadata that makes the compiler happy. 
> However, if you want to do this
> type safe in Java then the APIs become a lot more ugly. And if you're willing 
> to give up type safety, the API is a lot harder to use than plain Java 
> becausYou e you need to use some kind of property model.
> 
> That said, there is a trick I am very fond of. Actually it is two tricks. 
> First, instead of focusing how to implement something it often helps to view 
> it from the perspective how to use something. As we're all implementers, we 
> tend to want to provide it all to our customers, as easy as possible. 
> However, we often forget that the client just has more knowledge we as 
> providers have. A given client of these
> traits knows exactly which traits are needed. For type safety, that client 
> muse depend on the actual trait types. Something that is impossible for the 
> provider, the whole idea of the trait provider is to be oblivious of the 
> actual traits. Unlike the client ...
> 
> The second trick is the magic of interface proxies. It is already 6 years ago 
> I came up with the idea to use the annotation interfaces as a front for the 
> OSGi configuration data. I still love it every day I program a component. 
> I've used this trick in lots of places. It allows you to write code almost as 
> simple as in Python, Javascript, or Smalltalk but you get much more type 
> safety.
> 
> So if we look from the perspective of the provider we do actually have normal 
> Java type dependencies. A client that uses Entity and Position knows exactly 
> that it uses these types and it will have import packages to prove it. Since 
> it has type dependencies, OSGi tends to be invisible. If your code runs with 
> these dependencies all things work ok. This is very important in a dynamic 
> system.
> 
> So lets say we have a Trait Manager service. How would a client that has 
> Entity & Position wants to work?
> 
>         interface Position { int x(); int y(); }
>         interface Entity { String id(); }
> 
> So if the client wants to use both at the same time, they can create a new 
> interface:
> 
>         interface ClientView extends Position, Entity {}
> 
> This new interfaces is your binding, it binds whatever types the client feels 
> like. So assume we have a Trait Manager service:
> 
>         interface TraitManager {
>                 <T> T create( Class<T> type )
>         }
> 
> The Trait Manager returns a proxy to an underlying host object. The proxy 
> implements all the interfaces from the given type. Totally type safe and easy 
> to use. 
> 
> 
>         void client() {
>                 ClientView cv = tm.create(ClientView.class);
>                 int cv.x();
>                 String id = cv.id <http://cv.id/>();
>         }
> 
> 
> How to implement this? Well, the proxy must dispatch the method calls to an 
> object that knows the trait type, either Position or Entity. We want those 
> types to come from a Trait service.
> 
>         interface Trait {
>                 boolean handles(Class<?> c);
>                 void augment(TraitHost host);
>         }
> 
> The create method of the Trait Manager service could look like this:
> 
>         public <T> T create(Class<T> view) {
>                 TraitHostImpl host = new TraitHostImpl();
> 
>                 for (Class<?> c : view.getInterfaces()) {
>                         findTrait(c).augment(host);
>                 }
>                 return 
> view.cast(Proxy.newProxyInstance(view.getClassLoader(), new Class[] {
>                         view
>                 }, host));
>         }
> 
> The host object is a hidden object that contains the mapping from class -> 
> instance. It acts as the Invocation Handler for the proxy
> but it needs a public interface because the Trait services must be able to 
> _add_ their trait to it. 
> 
>         interface TraitHost {
>                 <T> void addInstance(Class<T> c, T instance);
>         }
> 
> 
> So how would this look like if you want to provide a trait in a separate 
> Bundle?
> 
>         interface Entity { String id(); }
> 
>         class EntityTrait implements TraitManager.Trait { 
>                 @Override public void augment(TraitHost host) { 
>                         String id = UUID.randomUUID() .toString(); 
>                         host.addInstance(Entity.class, () -> id); 
>                 } 
>                 @Override public boolean handles(Class<?> c) { return c == 
> Entity.class; } 
>         }
> 
> This is of course a sketch. A major problem with these kind of solutions is 
> the dynamicity of OSGi. The solution here is I think quite ok if you make 
> sure the trait's type is exported by the same bundle that provides the Trait 
> service for that type and there are no other exporters. The type dependency 
> will then ensure that you can never create a view  where the corresponding 
> Trait is missing. This can of course also be achieved with explicit 
> capabilities.
> 
> However, the problem gets more problematic when you start to share your 
> objects with parties that want to augment the trait host further. I.e. I get 
> a Position object in my code but now I want to add a Foo trait if it is not 
> already there:
> 
>         Foo f = tm.ensure( Foo.class, cv );
> 
> This imho inevitable extension will create multiple proxies on the same 
> underlying trait host. With one proxy, the trait host will get garbage 
> collected if the client goes away. However, when you have multiple proxies 
> the trait host will not be garbage collected until _all_ proxies become 
> unreachable. This means that in some cases the trait host is holding on to 
> classes that should be garbage collected. This is solvable but to much for 
> this mail.
> 
> Anyway, some caution is in place. When I was first hired by Ericsson in 1992 
> I developed a fantastic framework to handle data in a network management 
> system with some of the qualities you're looking. I really think it worked 
> rather well but developers hated it because it was always something extra 
> over normal code. I do think the proxy trick is quite powerful and works very 
> nice but almost nothing beats plain old java code. So be aware. The rules 
> that this model will oblige your developers to follow will not always be 
> appreciated. And of course as the GC showed, there are lots of corner cases 
> to be aware of.
> 
> I've enclosed the files I used to verify that I did not make compiler errors 
> ... which I probably did anyway. You can try them out. This is of course an 
> extremely minimal implementation.
> 
> Hope this helps. Kind regards,
> 
>         Peter Kriens
> 
> 
> 
> 
> 
> > On 17 Sep 2020, at 03:37, Zyle Moore via osgi-dev <osgi-dev@mail.osgi.org 
> > <mailto:osgi-dev@mail.osgi.org>> wrote:
> > 
> > In my game, all content, mechanics, and systems will be modular, and
> > based on OSGi bundles. One of these bundles is for an Entity. Entities
> > are uniquely identifiable things in the game world. Examples would
> > include trees, rocks, and characters. The game though does not
> > necessarily have to have any of those. If the game doesn't need trees,
> > it shouldn't have trees. If it doesn't need rocks, it shouldn't have
> > rocks. The Entity is the unique identity other things are associated
> > with. One of the things an Entity can be associated with is a Position.
> > A Position is an (x, y) coordinate in the world. Positions are in a
> > separate bundle than Entities. Positions are not inherently tied to
> > Entities, and Entities do not inherently have Positions, even though
> > they will likely be used together, depending on the game.
> > 
> > At this point, there are two bundles; Entity, and Position. To get more
> > functionality out of these two, a third bundle is needed;
> > EntityPosition, which provides a glue class (EntityPosition) which
> > associates a Position with an Entity.
> > 
> > ```
> > +------------+    +--------------------+     +----------+
> > |            |    |                    |     |          |
> > | Entity     |    | EntityPosition     |     | Position |
> > |            |    |                    |     |          |
> > +------------+    +--------------------+     +----------+
> > |            |    |                    |     |          |
> > | id: String +--->+ entity: Entity     |     | x: Int   |
> > |            |    | position: Position +<----+ y: Int   |
> > +------------+    |                    |     |          |
> >                   +--------------------+     +----------+
> > ```
> > 
> > In this simple example, two bundles became three, in order to associate
> > them. If I only needed to turn two into three, that wouldn't be so bad.
> > But if I wanted to associate a name with an Entity, that adds another
> > bundle. If I wanted to associate a sprite with an Entity, that adds two
> > other bundles, one for the sprite, one for the EntitySprite association.
> > This results in an explosion of small, but simple, bundles; each one
> > only binding two things from other bundles together.
> > 
> > - Entity
> > - Position
> >  - EntityPosition
> > - Name
> >  - EntityName
> > - Sprite
> >  - EntitySprite
> > - Sound
> >  - EntitySound
> > 
> > This has always seemed off to me somehow. Even though the code is small,
> > easy, and sharing a common pattern, the sheer scale seems overwhelming.
> > Each one has to be built, tested, deployed, and referenced; all for the
> > sole purpose of binding two things together.
> > 
> > In order to try to avoid this explosion of bundles, a possible solution
> > came to me. Each service registered in the framework service registry
> > can have properties associated with it. Could I use these properties to
> > provide the relationship between the services? In this example, we
> > would have one Entity component configuration, one Position component
> > configuration, and one of the properties of the Position configuration
> > would reference the Entity that the Position belongs to.
> > 
> > ```
> > entity.uuid.1:
> >     entity.id <http://entity.id/>: tree
> > 
> > position.uuid.1:
> >     position.for: entity.uuid.1
> >     position.x: 0
> >     position.y: 0
> > ```
> > 
> > My thinking is that the service properties can provide the glue, or
> > associations, between components/services, reducing the need for a
> > separate glue class. Additionally, if a Position needed to be associated
> > with something other than an Entity, I still don't need to create a
> > separate bundle, I can just change the `position.for` property to point
> > to whatever the new thing is, assuming the service can be filtered. This
> > way, the Position and Entity classes remain ignorant of the binding, and
> > can be focused on only its x and y values, and if I ever need to know
> > the Position of something, I can simply filter the Position services on
> > the `position.for` property.
> > _______________________________________________
> > OSGi Developer Mail List
> > osgi-dev@mail.osgi.org <mailto:osgi-dev@mail.osgi.org>
> > https://mail.osgi.org/mailman/listinfo/osgi-dev 
> > <https://mail.osgi.org/mailman/listinfo/osgi-dev>
> 

_______________________________________________
OSGi Developer Mail List
osgi-dev@mail.osgi.org
https://mail.osgi.org/mailman/listinfo/osgi-dev

Reply via email to