-
Notifications
You must be signed in to change notification settings - Fork 396
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
Save the owner of the new units in case another action changes the ownership #6943
Save the owner of the new units in case another action changes the ownership #6943
Conversation
The smoke test failed but I don't think this change caused that. |
Wow! Welcome to the team! @trevan |
Perhaps a retry on the smoke-test would be in order (in general), it failed here due to a transient download problem:
|
this.type = type; | ||
this.name = name; | ||
} | ||
|
||
private Map<UUID, String> getUnitPlayerMap(final Collection<Unit> units) { |
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.
nit: 'get' methods usually are just 'getters'. If computation is done a different verb could be easier for future maintainers. Perhaps rewording this to buildUnitPlayerMap
would help that. WDYT @trevan ?
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 could be a moot comment if it turns out we do not need a map.
private Collection<Unit> getUnitsWithOwner(final GameData data) { | ||
final Map<UUID, Unit> uuidToUnits = | ||
units.stream().collect(Collectors.toMap(Unit::getId, unit -> unit)); | ||
return unitPlayerMap.entrySet().stream() |
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.
If we're iterating over the entry set of the unitPlayerMap
, why do we need a map for this? Can we not just iterate over the set of units and avoid the map data structure altogether?
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.
The original code just iterated over the set of units. That's the problem. We need to store the map of unit -> owner outside of the units so that when the owner changes in the unit, it doesn't change the map here.
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.
Cool, I think that probably should be called out to make it clear & obvious. Perhaps a javadoc comment on the map property would do the trick. WDYT?
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 added some documentation and changed the method names. WDYT?
Here is an overview of what got changed by this pull request: Complexity increasing per file
==============================
- game-core/src/main/java/games/strategy/engine/data/changefactory/AddUnits.java 1
See the complete overview on Codacy |
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.type = type; | ||
this.name = name; | ||
} | ||
|
||
private Map<UUID, String> buildUnitOwnerMap(final Collection<Unit> units) { |
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.
side-note / big nit, this method could be static. I think we need to try and see if we can share a config that turns on the static analysis warning that methods can be marked as static.
For me it's really helpful as I watch for private methods being static. If they are, I know they do not interact with the class state. If they are not static, I assume they cannot be static and hence interact with class state.
@@ -36,11 +53,36 @@ public Change invert() { | |||
@Override | |||
protected void perform(final GameData data) { | |||
final UnitHolder holder = data.getUnitHolder(name, type); | |||
holder.getUnitCollection().addAll(units); | |||
final Collection<Unit> unitsWithCorrectOwner = buildUnitsWithOwner(data); | |||
holder.getUnitCollection().addAll(unitsWithCorrectOwner); |
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.
A test of some sort to verify the right functionality would be a good thing to have. If we change & break this, we'll have the extra safety net. Testing is really good for ensuring correctness, which is a further safety net that would be good to have as well.
Waiting for build to complete, good to merge once that finishes. |
I kind of feel like this is more of a band-aid vs a true fix. I bet I could cause this same type of problem by adding a new unit and then changing its hitpoints or any other edit to the unit. I think the long term fix is to clone the unit when it is added to this change (and other changes). That way, the original unit can be changed by later changes but the unit in this change object will stay the way it was initially. Is it it ok to try and implement |
Would an immutable copy of the unit state work instead of |
IMO changes should generally contain immutable data. If we maintained a stack of change data, it could be a really good avenue for having better save games. We could serialize the change stack and replay them on a base game data instead of serializing a game data. That kind of change I think would be a big breakthrough for compatibility. |
That should work as well, though I'm not entirely sure how to do that in a non-clone manner. Is there something like that in the code base already? |
Overall I think the band-aid is probably fine for now until we redo how game serialization is done. |
Fixes #6439
In Warcraft War Heroes, units are transformed into "loot" that is then captured by the winner. When the loot is first added to the battle, it is owned by the loser. This causes a series of change commands:
When the AI is playing and the user is spectating, the history also sees these change commands and converts them to its local gameData.
The problem occurs when the steps happen in the following manner:
When 3 runs, it is using the unit that had its owner changed in 2. So the change owner in 4 throws an error because the owner is different from what it is expecting.
This fix stores the original owner when the unit is added. So when 3 runs, it can ensure that the unit has the original owner and not the new one.
Functional Changes
[] New map or map update
[] New Feature
[] Feature update or enhancement
[] Feature Removal
[] Code Cleanup or refactor
[] Configuration Change
[x] Problem fix
[] Other:
Testing
Loaded a save that had the exception and ensured that the exception didn't happen. Also checked that save games still loaded.
Screens Shots
Additional Notes to Reviewer
Release Note
FIX|PlayerOwnerChange exception in Warcraft War Heroes is fixed