Repository: movingblocks/terasology
Issue: Crash when Characters die from fall damage during server prediction

### What you were trying to do

Move a deer with high movement speed off a cliff
### What actually happened

Server crashes with ConcurrentModificationException, similar to (but distinct from) #2468 since it's a different collection and the event is dispatch from the `update` method of a component system rather than a BT Node.
### How to reproduce

Kill a (non-player) character with fall damage (via CharacterMoveInputEvent).
### Log details and game version

```
"main@1" prio=10 tid=0x1 nid=NA runnable
  java.lang.Thread.State: RUNNABLE
      at org.terasology.logic.characters.ServerCharacterPredictionSystem.onDestroy(ServerCharacterPredictionSystem.java:97)
      at org.terasology.logic.characters.ServerCharacterPredictionSystemMethodAccess.invoke(Unknown Source:-1)
      at org.terasology.entitySystem.event.internal.EventSystemImpl$ByteCodeEventHandlerInfo.invoke(EventSystemImpl.java:506)
      at org.terasology.entitySystem.event.internal.EventSystemImpl.sendStandardEvent(EventSystemImpl.java:269)
      at org.terasology.entitySystem.event.internal.EventSystemImpl.send(EventSystemImpl.java:260)
      at org.terasology.entitySystem.entity.internal.PojoEntityManager.destroy(PojoEntityManager.java:538)
      at org.terasology.entitySystem.entity.internal.BaseEntityRef.destroy(BaseEntityRef.java:138)
      at org.terasology.logic.health.EntityDestructionAuthoritySystem.onDestroy(EntityDestructionAuthoritySystem.java:32)
      at org.terasology.logic.health.EntityDestructionAuthoritySystemMethodAccess.invoke(Unknown Source:-1)
      at org.terasology.entitySystem.event.internal.EventSystemImpl$ByteCodeEventHandlerInfo.invoke(EventSystemImpl.java:506)
      at org.terasology.entitySystem.event.internal.EventSystemImpl.sendStandardEvent(EventSystemImpl.java:269)
      at org.terasology.entitySystem.event.internal.EventSystemImpl.send(EventSystemImpl.java:260)
      at org.terasology.entitySystem.entity.internal.BaseEntityRef.send(BaseEntityRef.java:145)
      at org.terasology.logic.health.HealthAuthoritySystem.doDamage(HealthAuthoritySystem.java:185)
      at org.terasology.logic.health.HealthAuthoritySystem.checkDamage(HealthAuthoritySystem.java:200)
      at org.terasology.logic.health.HealthAuthoritySystem.onLand(HealthAuthoritySystem.java:241)
      at org.terasology.logic.health.HealthAuthoritySystemMethodAccess.invoke(Unknown Source:-1)
      at org.terasology.entitySystem.event.internal.EventSystemImpl$ByteCodeEventHandlerInfo.invoke(EventSystemImpl.java:506)
      at org.terasology.entitySystem.event.internal.EventSystemImpl.sendStandardEvent(EventSystemImpl.java:269)
      at org.terasology.entitySystem.event.internal.EventSystemImpl.send(EventSystemImpl.java:260)
      at org.terasology.entitySystem.entity.internal.BaseEntityRef.send(BaseEntityRef.java:145)
      at org.terasology.logic.characters.KinematicCharacterMover.walk(KinematicCharacterMover.java:647)
      at org.terasology.logic.characters.KinematicCharacterMover.updatePosition(KinematicCharacterMover.java:559)
      at org.terasology.logic.characters.KinematicCharacterMover.step(KinematicCharacterMover.java:103)
      at org.terasology.logic.characters.ServerCharacterPredictionSystem.stepState(ServerCharacterPredictionSystem.java:168)
      at org.terasology.logic.characters.ServerCharacterPredictionSystem.onPlayerInput(ServerCharacterPredictionSystem.java:126)
      at org.terasology.logic.characters.ServerCharacterPredictionSystemMethodAccess.invoke(Unknown Source:-1)
      at org.terasology.entitySystem.event.internal.EventSystemImpl$ByteCodeEventHandlerInfo.invoke(EventSystemImpl.java:506)
      at org.terasology.entitySystem.event.internal.EventSystemImpl.sendStandardEvent(EventSystemImpl.java:269)
      at org.terasology.entitySystem.event.internal.EventSystemImpl.send(EventSystemImpl.java:260)
      at org.terasology.entitySystem.entity.internal.BaseEntityRef.send(BaseEntityRef.java:145)
      at org.terasology.flexiblemovement.FlexibleMovementSystem.update(FlexibleMovementSystem.java:37)
      at org.terasology.engine.modes.StateIngame.update(StateIngame.java:176)
      at org.terasology.engine.TerasologyEngine.mainLoop(TerasologyEngine.java:413)
      at org.terasology.engine.TerasologyEngine.run(TerasologyEngine.java:368)
      - locked <0x1906> (a org.terasology.engine.TerasologyEngine)
      at org.terasology.engine.Terasology.main(Terasology.java:152)
```
## 


Changed files:
engine/src/main/java/org/terasology/logic/characters/ServerCharacterPredictionSystem.java

diff --git a/engine/src/main/java/org/terasology/logic/characters/ServerCharacterPredictionSystem.java b/engine/src/main/java/org/terasology/logic/characters/ServerCharacterPredictionSystem.java
index 91906d244..562c279f1 100644
--- a/engine/src/main/java/org/terasology/logic/characters/ServerCharacterPredictionSystem.java
+++ b/engine/src/main/java/org/terasology/logic/characters/ServerCharacterPredictionSystem.java
@@ -16,6 +16,8 @@
 
 package org.terasology.logic.characters;
 
+
+import com.google.common.collect.Lists;
 import com.google.common.collect.Maps;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
@@ -43,6 +45,7 @@
 import org.terasology.utilities.collection.CircularBuffer;
 import org.terasology.world.WorldProvider;
 
+import java.util.List;
 import java.util.Map;
 
 @RegisterSystem(RegisterMode.AUTHORITY)
@@ -81,6 +84,7 @@ public class ServerCharacterPredictionSystem extends BaseComponentSystem impleme
 
     private CharacterMover characterMover;
     private Map<EntityRef, CircularBuffer<CharacterStateEvent>> characterStates = Maps.newHashMap();
+    private List<EntityRef> characterStatesToRemove = Lists.newArrayList();
     private Map<EntityRef, CharacterMoveInputEvent> lastInputEvent = Maps.newHashMap();
     private long nextSendState;
     private CharacterMovementSystemUtility characterMovementSystemUtility;
@@ -103,7 +107,7 @@ public void onCreate(final OnActivatedComponent event, final EntityRef entity) {
     @ReceiveEvent(components = {CharacterMovementComponent.class, LocationComponent.class, AliveCharacterComponent.class})
     public void onDestroy(final BeforeDeactivateComponent event, final EntityRef entity) {
         physics.removeCharacterCollider(entity);
-        characterStates.remove(entity);
+        characterStatesToRemove.add(entity);
         lastInputEvent.remove(entity);
     }
 
@@ -200,7 +204,8 @@ public void update(float delta) {
                         // Haven't received input in a while, repeat last input
                         CharacterMoveInputEvent lastInput = lastInputEvent.get(entry.getKey());
                         if (lastInput != null) {
-                            CharacterMoveInputEvent newInput = new CharacterMoveInputEvent(lastInput, (int) (time.getGameTimeInMs() - state.getTime()));
+                            CharacterMoveInputEvent newInput = new CharacterMoveInputEvent(lastInput,
+                                    (int) (time.getGameTimeInMs() - state.getTime()));
                             onPlayerInput(newInput, entry.getKey());
                         }
                         entry.getKey().send(state);
@@ -214,9 +219,12 @@ public void update(float delta) {
             if (entry.getKey().equals(localPlayer.getCharacterEntity())) {
                 continue;
             }
-
-            setToTime(renderTime, entry.getKey(), entry.getValue());
+            if (!characterStatesToRemove.contains(entry.getKey())) {
+                setToTime(renderTime, entry.getKey(), entry.getValue());
+            }
         }
+        characterStates.keySet().removeAll(characterStatesToRemove);
+        characterStatesToRemove.clear();
     }
 
     private void setToTime(long renderTime, EntityRef entity, CircularBuffer<CharacterStateEvent> buffer) {