triplea-game / triplea

TripleA is a turn based strategy game and board game engine, similar to Axis & Allies or Risk.
https://triplea-game.org/
GNU General Public License v3.0
1.32k stars 393 forks source link

BattleDelegate#doScrambling:742 - java.lang.reflect.InvocationTargetException (duplicate key via ProScrambleAi.scrambleUnitsQuery) #11597

Closed tripleabuilderbot closed 4 months ago

tripleabuilderbot commented 1 year ago

Map

total_world_war

TripleA Version

2.6.14356

Java Version

11.0.9.1

Operating System

Windows 10

Stack Trace

Exception: java.lang.reflect.UndeclaredThrowableException 
java.lang.Exception
    at com.sun.proxy.$Proxy21.scrambleUnitsQuery(Unknown Source)
    at games.strategy.triplea.delegate.battle.BattleDelegate.doScrambling(BattleDelegate.java:742)
    at games.strategy.triplea.delegate.battle.BattleDelegate.start(BattleDelegate.java:92)
    at games.strategy.engine.framework.ServerGame.startStep(ServerGame.java:575)
    at games.strategy.engine.framework.ServerGame.runStep(ServerGame.java:443)
    at games.strategy.engine.framework.ServerGame.runNextStep(ServerGame.java:345)
    at games.strategy.engine.framework.ServerGame.startGame(ServerGame.java:312)
    at games.strategy.engine.framework.startup.launcher.LocalLauncher.launchInternal(LocalLauncher.java:92)
    at games.strategy.engine.framework.startup.launcher.LocalLauncher.lambda$launch$0(LocalLauncher.java:60)
    at java.base/java.lang.Thread.run(Thread.java:834)

Exception: java.lang.reflect.InvocationTargetException 
java.lang.Exception
    at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
    at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
    at java.base/jdk.internal.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
    at java.base/java.lang.reflect.Method.invoke(Method.java:566)
    at games.strategy.engine.delegate.DelegateExecutionManager.lambda$newOutboundImplementation$1(DelegateExecutionManager.java:79)
    at com.sun.proxy.$Proxy21.scrambleUnitsQuery(Unknown Source)
    at games.strategy.triplea.delegate.battle.BattleDelegate.doScrambling(BattleDelegate.java:742)
    at games.strategy.triplea.delegate.battle.BattleDelegate.start(BattleDelegate.java:92)
    at games.strategy.engine.framework.ServerGame.startStep(ServerGame.java:575)
    at games.strategy.engine.framework.ServerGame.runStep(ServerGame.java:443)
    at games.strategy.engine.framework.ServerGame.runNextStep(ServerGame.java:345)
    at games.strategy.engine.framework.ServerGame.startGame(ServerGame.java:312)
    at games.strategy.engine.framework.startup.launcher.LocalLauncher.launchInternal(LocalLauncher.java:92)
    at games.strategy.engine.framework.startup.launcher.LocalLauncher.lambda$launch$0(LocalLauncher.java:60)
    at java.base/java.lang.Thread.run(Thread.java:834)

Exception: java.lang.RuntimeException Exception on remote
java.lang.Exception
    at games.strategy.engine.message.UnifiedInvocationHandler.invoke(UnifiedInvocationHandler.java:57)
    at com.sun.proxy.$Proxy21.scrambleUnitsQuery(Unknown Source)
    at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
    at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
    at java.base/jdk.internal.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
    at java.base/java.lang.reflect.Method.invoke(Method.java:566)
    at games.strategy.engine.delegate.DelegateExecutionManager.lambda$newOutboundImplementation$1(DelegateExecutionManager.java:79)
    at com.sun.proxy.$Proxy21.scrambleUnitsQuery(Unknown Source)
    at games.strategy.triplea.delegate.battle.BattleDelegate.doScrambling(BattleDelegate.java:742)
    at games.strategy.triplea.delegate.battle.BattleDelegate.start(BattleDelegate.java:92)
    at games.strategy.engine.framework.ServerGame.startStep(ServerGame.java:575)
    at games.strategy.engine.framework.ServerGame.runStep(ServerGame.java:443)
    at games.strategy.engine.framework.ServerGame.runNextStep(ServerGame.java:345)
    at games.strategy.engine.framework.ServerGame.startGame(ServerGame.java:312)
    at games.strategy.engine.framework.startup.launcher.LocalLauncher.launchInternal(LocalLauncher.java:92)
    at games.strategy.engine.framework.startup.launcher.LocalLauncher.lambda$launch$0(LocalLauncher.java:60)
    at java.base/java.lang.Thread.run(Thread.java:834)

Exception: java.lang.IllegalStateException java.lang.IllegalStateException: Duplicate key d5cae28d-ed70-4a56-a1bc-8857ea43c3fb (attempted merging values Britain and Britain)
java.lang.Exception
    at java.base/jdk.internal.reflect.NativeConstructorAccessorImpl.newInstance0(Native Method)
    at java.base/jdk.internal.reflect.NativeConstructorAccessorImpl.newInstance(NativeConstructorAccessorImpl.java:62)
    at java.base/jdk.internal.reflect.DelegatingConstructorAccessorImpl.newInstance(DelegatingConstructorAccessorImpl.java:45)
    at java.base/java.lang.reflect.Constructor.newInstance(Constructor.java:490)
    at java.base/java.util.concurrent.ForkJoinTask.getThrowableException(ForkJoinTask.java:600)
    at java.base/java.util.concurrent.ForkJoinTask.reportException(ForkJoinTask.java:678)
    at java.base/java.util.concurrent.ForkJoinTask.invoke(ForkJoinTask.java:737)
    at java.base/java.util.stream.ReduceOps$ReduceOp.evaluateParallel(ReduceOps.java:919)
    at java.base/java.util.stream.AbstractPipeline.evaluate(AbstractPipeline.java:233)
    at java.base/java.util.stream.ReferencePipeline.collect(ReferencePipeline.java:578)
    at games.strategy.triplea.odds.calculator.ConcurrentBattleCalculator.calculate(ConcurrentBattleCalculator.java:196)
    at games.strategy.triplea.ai.pro.util.ProOddsCalculator.callBattleCalc(ProOddsCalculator.java:250)
    at games.strategy.triplea.ai.pro.util.ProOddsCalculator.callBattleCalc(ProOddsCalculator.java:226)
    at games.strategy.triplea.ai.pro.util.ProOddsCalculator.calculateBattleResults(ProOddsCalculator.java:159)
    at games.strategy.triplea.ai.pro.util.ProOddsCalculator.calculateBattleResults(ProOddsCalculator.java:143)
    at games.strategy.triplea.ai.pro.ProScrambleAi.scrambleUnitsQuery(ProScrambleAi.java:54)
    at games.strategy.triplea.ai.pro.AbstractProAi.scrambleUnitsQuery(AbstractProAi.java:495)
    at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
    at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
    at java.base/jdk.internal.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
    at java.base/java.lang.reflect.Method.invoke(Method.java:566)
    at games.strategy.engine.message.unifiedmessenger.EndPoint.invokeSingle(EndPoint.java:136)
    at games.strategy.engine.message.unifiedmessenger.EndPoint.lambda$invokeMultiple$0(EndPoint.java:120)
    at java.base/java.util.stream.ReferencePipeline$3$1.accept(ReferencePipeline.java:195)
    at java.base/java.util.Spliterators$ArraySpliterator.forEachRemaining(Spliterators.java:948)
    at java.base/java.util.stream.AbstractPipeline.copyInto(AbstractPipeline.java:484)
    at java.base/java.util.stream.AbstractPipeline.wrapAndCopyInto(AbstractPipeline.java:474)
    at java.base/java.util.stream.ReduceOps$ReduceOp.evaluateSequential(ReduceOps.java:913)
    at java.base/java.util.stream.AbstractPipeline.evaluate(AbstractPipeline.java:234)
    at java.base/java.util.stream.ReferencePipeline.collect(ReferencePipeline.java:578)
    at games.strategy.engine.message.unifiedmessenger.EndPoint.invokeMultiple(EndPoint.java:121)
    at games.strategy.engine.message.unifiedmessenger.EndPoint.invokeLocal(EndPoint.java:111)
    at games.strategy.engine.message.unifiedmessenger.UnifiedMessenger.invokeAndWait(UnifiedMessenger.java:97)
    at games.strategy.engine.message.UnifiedInvocationHandler.invoke(UnifiedInvocationHandler.java:55)
    at com.sun.proxy.$Proxy21.scrambleUnitsQuery(Unknown Source)
    at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
    at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
    at java.base/jdk.internal.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
    at java.base/java.lang.reflect.Method.invoke(Method.java:566)
    at games.strategy.engine.delegate.DelegateExecutionManager.lambda$newOutboundImplementation$1(DelegateExecutionManager.java:79)
    at com.sun.proxy.$Proxy21.scrambleUnitsQuery(Unknown Source)
    at games.strategy.triplea.delegate.battle.BattleDelegate.doScrambling(BattleDelegate.java:742)
    at games.strategy.triplea.delegate.battle.BattleDelegate.start(BattleDelegate.java:92)
    at games.strategy.engine.framework.ServerGame.startStep(ServerGame.java:575)
    at games.strategy.engine.framework.ServerGame.runStep(ServerGame.java:443)
    at games.strategy.engine.framework.ServerGame.runNextStep(ServerGame.java:345)
    at games.strategy.engine.framework.ServerGame.startGame(ServerGame.java:312)
    at games.strategy.engine.framework.startup.launcher.LocalLauncher.launchInternal(LocalLauncher.java:92)
    at games.strategy.engine.framework.startup.launcher.LocalLauncher.lambda$launch$0(LocalLauncher.java:60)
    at java.base/java.lang.Thread.run(Thread.java:834)

Exception: java.lang.IllegalStateException Duplicate key d5cae28d-ed70-4a56-a1bc-8857ea43c3fb (attempted merging values Britain and Britain)
java.lang.Exception
    at java.base/java.util.stream.Collectors.duplicateKeyException(Collectors.java:133)
    at java.base/java.util.stream.Collectors.lambda$uniqKeysMapAccumulator$1(Collectors.java:180)
    at java.base/java.util.stream.ReduceOps$3ReducingSink.accept(ReduceOps.java:169)
    at java.base/java.util.ArrayList$ArrayListSpliterator.forEachRemaining(ArrayList.java:1655)
    at java.base/java.util.stream.AbstractPipeline.copyInto(AbstractPipeline.java:484)
    at java.base/java.util.stream.AbstractPipeline.wrapAndCopyInto(AbstractPipeline.java:474)
    at java.base/java.util.stream.ReduceOps$ReduceOp.evaluateSequential(ReduceOps.java:913)
    at java.base/java.util.stream.AbstractPipeline.evaluate(AbstractPipeline.java:234)
    at java.base/java.util.stream.ReferencePipeline.collect(ReferencePipeline.java:578)
    at games.strategy.engine.data.changefactory.AddUnits.buildUnitOwnerMap(AddUnits.java:47)
    at games.strategy.engine.data.changefactory.RemoveUnits.<init>(RemoveUnits.java:32)
    at games.strategy.engine.data.changefactory.RemoveUnits.<init>(RemoveUnits.java:28)
    at games.strategy.engine.data.changefactory.ChangeFactory.removeUnits(ChangeFactory.java:80)
    at games.strategy.engine.history.change.units.RemoveUnitsHistoryChange.perform(RemoveUnitsHistoryChange.java:108)
    at games.strategy.triplea.delegate.battle.MustFightBattle.removeUnits(MustFightBattle.java:604)
    at games.strategy.triplea.delegate.battle.MustFightBattle.clearWaitingToDieAndDamagedChangesInto(MustFightBattle.java:524)
    at games.strategy.triplea.delegate.battle.steps.change.ClearGeneralCasualties.execute(ClearGeneralCasualties.java:33)
    at games.strategy.triplea.delegate.ExecutionStack.execute(ExecutionStack.java:34)
    at games.strategy.triplea.delegate.battle.MustFightBattle.fight(MustFightBattle.java:712)
    at games.strategy.triplea.odds.calculator.BattleCalculator.calculate(BattleCalculator.java:124)
    at games.strategy.triplea.odds.calculator.ConcurrentBattleCalculator.lambda$calculate$4(ConcurrentBattleCalculator.java:184)
    at java.base/java.util.stream.ReferencePipeline$3$1.accept(ReferencePipeline.java:195)
    at java.base/java.util.Spliterators$ArraySpliterator.forEachRemaining(Spliterators.java:948)
    at java.base/java.util.stream.AbstractPipeline.copyInto(AbstractPipeline.java:484)
    at java.base/java.util.stream.AbstractPipeline.wrapAndCopyInto(AbstractPipeline.java:474)
    at java.base/java.util.stream.ReduceOps$ReduceTask.doLeaf(ReduceOps.java:952)
    at java.base/java.util.stream.ReduceOps$ReduceTask.doLeaf(ReduceOps.java:926)
    at java.base/java.util.stream.AbstractTask.compute(AbstractTask.java:327)
    at java.base/java.util.concurrent.CountedCompleter.exec(CountedCompleter.java:746)
    at java.base/java.util.concurrent.ForkJoinTask.doExec(ForkJoinTask.java:290)
    at java.base/java.util.concurrent.ForkJoinPool$WorkQueue.topLevelExec(ForkJoinPool.java:1020)
    at java.base/java.util.concurrent.ForkJoinPool.scan(ForkJoinPool.java:1656)
    at java.base/java.util.concurrent.ForkJoinPool.runWorker(ForkJoinPool.java:1594)
    at java.base/java.util.concurrent.ForkJoinWorkerThread.run(ForkJoinWorkerThread.java:183)
asvitkine commented 1 year ago

"attempted merging values Britain and Britain" is the relevant part - so the duplicates being merged aren't units in this case...

EDIT: Actually, the above is incorrect, it's still merging units via buildUnitOwnerMap(), it's just we map them to country names.

asvitkine commented 1 year ago

Given the clearWaitingToDieAndDamagedChangesInto(), it seems we end up with attackingWaitingToDie having duplicates.

That's hard to debug since it's pretty core battle logic. We could add some instrumentation to catch this error earlier to see if we can make some sense of what's going wrong. Or if someone has a repro save, that would be ideal.

asvitkine commented 1 year ago

One option would be to just check for and omit duplicates when adding to attackingWaitingToDie. But that would be a bit of an unsatisfactory solution. But maybe still worth doing to make the engine more stable.