Entity Rewrite Possibilities - Printable Version +- Cuberite Forum (https://forum.cuberite.org) +-- Forum: Cuberite (https://forum.cuberite.org/forum-4.html) +--- Forum: Development (https://forum.cuberite.org/forum-13.html) +--- Thread: Entity Rewrite Possibilities (/thread-3281.html) |
Entity Rewrite Possibilities - Lazauya - 04-06-2020 Entity Inheritance Structure Forward Forward note It seems like the post editor was being really weird, I have no clue why. Please excuse some blank bullet points, those are just bugs and there isn't meant to be anything there. Forward note This is list of plans proposed by me (Lazauya) on how to replace the current entity scheme. I have been informed that the current enity scheme is too rigid, and that "modules" should replace much of the logic. I tried picking out the modules that would apply to any entity, but not necessarily every entity. This means that for much of the entities, the module functions will be very sparse or non-existent. While I think the modules I outlined are already pretty exhaustive, I would like to see discussion on what should change/go/be added. The issues that definitely still need discussion and clarification:
All of these plans differ on how/where the NBT data for the entity is stored. I chose to focus on that, as it seems this will either make Cuberite very efficient or end up in a huge disaster if done wrong. The current system is very memory efficient, but it is also apparently very inflexible. Reaching the current level of memory efficiency/speed probably won't be possible with completely flat class structures, but hopefully one of these plans will provide inspiration for the system that will come close to the current level while being flexible. Again, none of these plans are likely to be the final entity handling system, and discussion is required moving forward. Plan 1 Overview There is 1 class, cEntity. The entity has pluggable functions dictating behavior, and contains fields for all potential NBT data attached to this entity. Class structure
Pros:
Cons:
Overview There is 1 parent class and 6 children. The parent class has the same function fields as Plan 1. The parent class houses NBT data that is common to all entities Each child class houses the fields for the NBT data associated with any entity of the child type; e.g. cProjectile has fields specific to arrows, fields specific to Class structure
Pros:
Cons:
Overview There is 1 class, cEntity. The entity has pluggable function fields dictating behavior, and contains a dictionary of dictionaries to store NBT data. Class structure
Pros:
Cons:
Overview There is 1 class, cEntity. The entity has pluggable function fields dictating behavior, and contains a custom NBT tree structure to store NBT data. This would basically be structs within structs; each struct represents a possible tag, and each struct contains the possible fields for those tags. Class structure
Pros:
Cons:
RE: Entity Rewrite Possibilities - xoft - 04-06-2020 How about this: Single class, cEntity. Contains fields for plugging "subsystems" - a subsystem for pathfinding, a subsystem for physics, a subsystem for handling interactions, a subsystem dictating the graphical look of the entity, a subsystem for handling sounds, etc. Basically what your interaction functions do, I'd put into a subsystem (an interface, a class with virtual functions, descendants implement actual behavior). Each of these subsystems can also load and save specific NBT data; only the subsystem knows what NBT data it can load or save. Each cEntity also has a dictionary / hashmap for storing custom values. RE: Entity Rewrite Possibilities - Lazauya - 04-07-2020 (04-06-2020, 07:27 PM)xoft Wrote: How about this:So by "load and save" NBT data, you mean that the data an entity needs to use is only loaded into memory when the subsystem needs it? Or at least, it will be fetched from the chunk region file every time (which could or could not already be loaded). That's not what happens currently as far as I can tell by the code. This seems like it would be very inefficient. I was going to bring up the point of multiple subsystems needing the same piece of data, which this would seem to fix, but if the load-save method isn't very efficient, I think that having each component deal with its own NBT data just isn't a very simple solution. There it seems like there needs to be faster-to-access source of data than just the chunks. Also, what do you mean by "custom values"? Do you mean non-default NBT data? I think this is a generally a good idea; I can definitely see how things like path finding should have some kind of internal state that just can't be easily encapsulated in a function. So I will revise the proposal to:
RE: Entity Rewrite Possibilities - Lazauya - 04-09-2020 I'm going to be using "component", "module", and "subsystem" interchangeably from now on. The question of where the data should be stored in the class is still very important. Should any data be stored inside the entity class, or are we just going to store everything in chunks and let modules access the chunks? I don't believe it's wise to have individual modules responsible for their own NBT data. It seems like a lot of the modules would need to access the same data, like position/rotation. Perhaps we can just group modules that need direct access to that information, and call those modules when we need to modify that NBT data, like the module for burning calling the module for damage. As I'm writing this, it seems like a promising solution but more research is required. The downside to this would be that it may be slower than if we let the modules access the data directly, but I don't see a particularly big performance hit. Besides, we already abstract away access right now as far as I can tell, this would just be adding an extra layer or two of function calls at most. On a different note, here's an alternate idea for storing the NBT data. We have a root struct, say cNBT (or sNBT if you use those naming conventions). We then can create a cEntityNBT, which all further entity NBT classes should inherit from. We also should extend the cNBT class with the necessary data for each type of entity; we have cProjectileNBT, cMobNBT, cBreedableMobNBT, etc. Within each type, we further subdivide it into cArrowNBT, cVillagerNBT, etc, for the entity-specific NBT data. cArrowNBT can inherit from cEntity and cProjectileNBT, while cVillagerNBT can inherit from cEntityNBT, cMobNBT, cBreedableMobNBT. The obvious downside to this seems to be that this may not be as generic as was initially hoped. The plus side is that this is still a super generic way to do things, and it promises to be very very fast; each access to the data would only need to be a single dereference. And of course, you could abstract it away to make it thread-safe. Feedback is welcomed. RE: Entity Rewrite Possibilities - xoft - 04-12-2020 I guess I wasn't too clear. I think the entity should have its data stored either in the entity class itself, or in the individual subsystem classes (whatever makes more sense for the particular kind of data), but it is each subsystem's responsibility to know how to save and load that data from / to NBT, *when serializing the entity*. So when the entity gets loaded, its subsystems load their internal data from the entity's NBT, and when it's saved, the subsystems save the data. They store the data within themselves, as they see fit. If there is need to share the data between multiple subsystems, that needs individual consideration. In a general case, such a sharing means the subsystems are not designed properly. From this, you can see that a subsystem cannot be a class with a single operator(), but instead many virtual methods. For example: /** The interface of the high-level AI subclasses */ class cEntity::IBehavior { /** Called when aAttacker has attacked this entity. */ virtual void OnAttacked(const cEntity & aAttacker) = 0; /** Called when sight of the tracked target was lost (such as when targeting a player, or hunting) */ virtual void OnLostSightOfTarget(const cEntity & aTarget) = 0; /** Called when a new entity comes into the sight range. This doesn't mean the entity can be seen, it is only close enough to actually check the line-of-sight, if desired. */ virtual void OnEntityInSightRange(const cEntity & aEntity) = 0; // ... /** Saves the behavior's data to the NBT */ virtual void SaveToNBT(...) = 0; /** Loads the behavior's data to the NTB. */ virtual void LoadFromNBT(...) = 0; } |