After thinking about this for a while, I don’t like the idea of having the entities themselves be able to get their components. It should either be static on the component or on another class. This way we’ll be able to manage context of entity component systems, which I think could be useful. E.g with what was previously suggested
Only allows for one semantic ‘context’. Meanwhile, if we do:
context.get(entity, Component.class);
This will allow for different semantic ‘contexts’ isolated from mod to mod that may not be useful for the server. So the server will have a context:
The problem I see with this is that you will have multiple instances of the health component attached to the same entity. One Plugin will change the health in its own context but the server won’t know.
I would stick with one unique content instance on the same entity to avoid confusion and strange behavior
Yes, that’s the point. It’ll allow plugins to store their own data on the entity without the server knowing. If they wish to update the data so the server knows, they simply use the server’s context. There is absolutely nothing that a plugin can do in its own context that the server can see.
For instance, adding a health component in the plugin’s context and setting the health will not set the health in the server’s view, because it’s not the same context. That make sense?
The reason you would want to do this is for separation and testing. Anything that can further abstract out the server from its implementation.
This brings up a good point. If we have contexts, we should tie events to the contexts of the component system, and this would be a super-interesting feature. That would solve the problem of inter-system communication in a super-clean elegant way…
The data structure is as proposed. Either using contexts, .get(Component.class), or something similar.
Really hope this will be used.
Just like Unity assign some components to a object and you create something custom.
And yeah please go for the .class instead of string.
This allows a plugin to define its own system. setHealth would fire off an event…?
Throwing out ideas based on above…
API:
interface Game {
<T extends ComponentSystem> getSystem(Class<T> clazz);
<T extends ComponentSystem> registerSystem(Class<T> clazz);
}
interface Component {
Game getGame();
}
interface ComponentSystem<B, C extends Component> {
C set(B instance, C type);
C get(B instance);
}
interface Health extends Component {
double getHealth();
void setHealth(double value);
}
Impl:
class HealthImpl implements Health {
private double health;
public double getHealth() {
return health;
}
public void setHealth(double value) {
this.health = health;
}
}
class HealthSystem extends ComponentSystem<Entity, Health> {
public Health set(B instance, C type) {
// Set in the system if not present or overwrite?
return health;
}
public Health get(Entity instance) {
// Check in the system if the entity has health...
return health;
}
}
Oh so now you’re talking full on separate components and using systems at the end of a tick to sync Minecraft and the components. I was thinking of just having the components for now which directly chain to Minecraft internals when modified. Basically pretending we have components when we really don’t. I’ll have to think on that one some more.
As far as some of the other posts having different contexts makes no sense, let’s not do that. As far as plugins adding their own components, that’s tricky because we don’t have a good way of serializing them so it’d basically be Bukkit’s Metadata again. Finally as far as plugins adding their own systems, that’s kind of the point. Your systems, if properly defined, will work on any new entities (added by mods, for example) that match your criteria without having to do extra work to detect them. Makes things more flexible and future proof.
On systems, I’m not sure I understand why you’d get the system and call something on it. Only the server itself should be making any calls to systems, plugins should be modifying components.
@components-access
Maybe,
but Components are only “data driven accessors” used by the systems …
If you change data direct in the components, it is like changeing a field/member of a object directly over reflection (or because it is public).
The system can not react …
Systems create and manage Components …
If you create a CustomComponent, you also need a CustomSystem that load, save, create, the components …
If we’re going to do real Components and not just proxies then we should do them right. Each Component has three copies of its data: previous tick, current tick, next tick. Modifying the component only modifies the next tick values. Systems should only do anything once a tick so calling them directly is weird.
I like the tracking of data idea … (previous, current, next)
Not sure if that put to much logic in the Components …
Should an API not hide this system?
Also everything can change anything silent…
Last Caller wins?
If you use the system, we can trigger events, inform other systems, …
(debug, track, …)
I’m not sure the semantics you have here, but it seems like there’s one System per component? To me that seems verbose, very not DRY, and honestly not really necessary.
I created a gist that compiles and runs fine, just as a quick test. Here’s a summary of what the interfaces look like:
public interface Component<H> {
public H getHolder();
public void setHolder(H holder);
}
public interface ComponentHolder<C> {
}
public interface ComponentSystem {
public <T extends Component>
T lookup(ComponentHolder<? super T> holder, Class<T> clazz);
public <T extends Component> boolean
has(ComponentHolder<? super T> holder, Class<T> clazz);
public <T extends Component> void
remove(ComponentHolder<? super T> holder, Class<T> clazz);
public <T extends Component> void
register(ComponentHolder<? super T> holder, T component);
}
The best thing is, the whole thing is completely typesafe, and without much reflection magic! I may consider ‘duplicating’ the ComponentSystem api over to individual ComponentHolders.
Imo the different components should only hold the data. A system then can see if the value got changed and updates the data (sends updates to clients / server). This should be completely up to the author; A health component would also have a health system that then sends the proper packets. A custom implementation might use the Plugin channel for that - the “serialization” would be up to the developer then.
Aaah! I want to be really careful here, what do we mean by “serialization is up to the developer”? Does that mean we have to have 80 connections to a MySQL server again? Because I hated that part of Bukkit metadata. Component systems hit all my separation-of-concerns buttons, I love this idea. Please, please, please give us a switch to just make this data persistent!