-
Notifications
You must be signed in to change notification settings - Fork 0
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Bug fixes and method cleanup #2
Conversation
Entities were not being properly assigned to pools, and the pools were not being properly iterated over. This meant that serialization and iteration over entities that aren't in the global pool didn't work properly.
Combine createEntityRefWithId and createEntityRef into getEntityRef, which is a more accurate name for what the functions do.
Make the PojoEntityManager's createEntityWithoutLifecycleEvents methods call the global pool's methods, instead of duplicating the functionality.
The getEntityPool method makes it easier to get the pool of an entity, and debug when the entity doesn't have an assigned pool.
The EntityManager had a getEntity method, but also inherited the getEntityRef method from the EntityPool. Both of these methods did the same thing, so they weren't both needed on the EntityManager. This change renames EntityPool's getEntityRef to getEntity, and merges the two methods in EntityManager (so only getEntity remains).
EntityPool's method getExistingEntity was not needed, as getEntity did the same thing (but still worked even if there wasn't an EntityRef already saved). This commit removes getExistingEntity, and replaces calls to it with calls to getEntity.
The createEntity method of PojoEntityManager is now not used anywhere, so it can be removed. Its functionality has been replaced by the standard create methods.
PojoEntityManager's createEntityRef method was duplicating functionality from the EntityPool's getEntity method. It also meant that there were multiple ways that EntityRefs were created, so code had to be updated in multiple places. This led to bugs, such as entities not being assigned to a pool when they are created, because the code wasn't updated in all places. This commit removes the createEntityRef method, and replaecs all references to it with the getEntity method.
The internal EntityIterable class is no longer used, as its functionality can be simplified to using a lambda to return an instance of the EntityIterator class.
* | ||
* @param id | ||
* @return The entityRef for the given id | ||
* @return the {@link EntityRef}, if it exists; {@link EntityRef#NULL} otherwise |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
👍
@@ -467,7 +407,8 @@ public boolean isActiveEntity(long id) { | |||
@Override | |||
//Todo: implement iterating over multiple pools | |||
public Iterable<Component> iterateComponents(long entityId) { | |||
return globalPool.getComponentStore().iterateComponents(entityId); | |||
return Iterables.concat(globalPool.getComponentStore().iterateComponents(entityId), | |||
sectorManager.getComponentStore().iterateComponents(entityId)); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This is basically an "either ... or ..." in case an entity is either in sector or global scope, is that correct? If, for some reason, an entity ends up to be in both, this will duplicate all the components, won't it?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Ah, this was just the general form I was using to combine iterators. It works because the entity is only in one pool at a time, but it would be better to do it by just finding out which pool the entity is in and iterating over it in that pool. That would be more efficient, communicate better what's actually happening, and probably be more resistant to bugs.
There's currently no way of an entity ending up in multiple pools (and I don't think there should be), so I don't think this would actually be a problem in terms of program correctness, but it's certainly not the best way to do it.
@@ -575,18 +504,13 @@ public void notifyComponentRemovalAndEntityDestruction(long entityId, EntityRef | |||
@Override | |||
//Todo: be able to save components for entities in any pool |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Is this TODO item fixed with the new implementation? it looks like it will select the correct entity pool for the given id.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yep, this is fixed. I need to go back and review my comments and TODOs and stuff to make sure everything's up to date.
private EngineEntityPool getEntityPool(long id) { | ||
EngineEntityPool pool = poolMap.get(id); | ||
if (pool == null) { | ||
logger.error("Entity {} doesn't have an assigned pool", id); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
At this point, should the stray entity just be assigned to the global pool? What is the behavior here if I query for the pool of a non-existing entity id? It looks like I get the global pool, which looks like the a valid value from which I can retrieve the entity as a consumer. Thus, it either
- should return null in case of error, which would lead to null checks everywhere, ...
- assign the stray entity to the global pool before returning
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I kept this in because it was very helpful to debug when I was having issues with entities not being put into the pools correctly.
With the latest round of fixes this shouldn't actually occur, but I think providing a sensible fallback in case of error is worthwhile.
Querying for the pool of a non-existing id isn't something I thought about; it would currently return the global pool, but should probably check for that and return some sort of null value.
We could introduce an EntityPool.NULL, analogous to the EntityRef.NULL. This would provide sensible defaults in the case of an error (i.e. the pool appears empty for any methods calling it).
I'm not sure about the case of an entity that isn't assigned to a pool. It shouldn't currently be possible to have that situation (I have just realised that I should probably make createEntity() protected, rather than public, to stop this kind of stuff). But I think assigning the stray entity to the global pool in case of error is a safe bet.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Alternatively, you can use an Optional<Pool>
as return value (easier to deal with than with null types).
return globalPool; | ||
} else { | ||
return pool; | ||
} | ||
} | ||
|
||
protected void assignToPool(long entityId, EngineEntityPool pool) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Do we also want to have a moveToPool
, or how will the different contexts be modelled (switching from chunk to sector scope and vice versa)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yep, I think that will be needed to move entities between scopes, and to allow the sector manager to split up/join the sectors.
Should it send an event when entities are moved between pools?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Hmm, I need to think about that... It probably should so that system can react on this change. It might be especially interesting if some systems wants to collect some resources or information just before the entity gets shifted from chunk scope to sector scope.
Merged in MovingBlocks#3009 |
This PR fixes some bugs with the sectors/pools implementation by consolidating the multiple entity retrieval methods into just one method,
getEntity
.The existence of multiple methods to do the same thing led to code reuse, and some of the code wasn't properly changed in all locations. This caused a problem where the entity wasn't assigned a cache properly if it was created with one of the methods, leading to bugs in serialization and iteration. This PR fixes that problem, and reorganises the methods to help reduce similar bugs in the future.
The methods
createEntityRef
,createEntityRefWithId
,getExistingEntity
,getEntityRef
, andgetEntity
all did the same job of returning theEntityRef
corresponding to a given id. These are all consolidated into one method,getEntity
, which I believe has the most descriptive name (the presence of 'create' in the name was confusing, as these methods aren't used to create entities. They are used to create anEntityRef
corresponding to an id, but that should not have any effect on the method caller, so I believe it's best to leave it out of the name).