Hello dear community,
I really enjoy working with zay-es but now I need your help. This issue is a difficult one but I try to explain it.
I got two player models: A demon and a human player model. Those two have a different skeleton for animation. I created a SkeletonComponent for the entities so the animation systems know which entities to handle and which not. I got 2 animation app states: one for the human and one for the monster. Each of them uses a FieldFilter to get the right entities.
In singleplayer everything works fine but for some reason in multiplayer games something really weird happens: When a client connects to the server, a human player is created (exactly like in Singleplayer), again, a human player, not a demon! When another client connects to the game the first client crashes because of a really unexplainable exception. For some reason the DemonAnimationAppState wants to create a DemonAnimationControl for the human player. WTF? I had a look at the entity set and actually there was the human player contained in this set even though this entity had another SkeletonType so it mustn’t even be part of the EntitySet.
I hope I could explain this issue a little.
Here is the code of the DemonAnimationAppState with some System.out.println()
public class DemonAnimationAppState extends AbstractAppState {
private EntitySet monsters;
private ModelViewAppState modelViewAppState;
private HashMap<EntityId, DemonAnimationControl> controls = new HashMap<>();
@Override
public void initialize(AppStateManager stateManager, Application app) {
EntityData entityData = stateManager.getState(EntityDataState.class).getEntityData();
this.monsters = entityData.getEntities(new FieldFilter<>(SkeletonComponent.class, "skeletonType", SkeletonType.Demon), SkeletonComponent.class, Model.class, CharacterMovementState.class);
this.modelViewAppState = stateManager.getState(ModelViewAppState.class);
for (Entity entity : monsters) {
addControl(entity);
}
super.initialize(stateManager, app);
}
@Override
public void update(float tpf) {
if (monsters.applyChanges()) {
for (Entity e : monsters) {
System.out.println("SKEL: " + e.get(SkeletonComponent.class).getSkeletonType());
}
System.out.println(Thread.currentThread().getName());
for (Entity entity : monsters.getAddedEntities()) {
System.out.println("for: " + entity.getId() + entity.get(SkeletonComponent.class).getSkeletonType());
addControl(entity);
}
for (Entity entity : monsters.getChangedEntities()) {
updateControl(entity);
}
for (Entity entity : monsters.getRemovedEntities()) {
removeControl(entity);
}
}
}
private void addControl(Entity entity) {
DemonAnimationControl control = new DemonAnimationControl();
Spatial demon = modelViewAppState.getSpatial(entity.getId());
demon.addControl(control);
control.setMovementState(entity.get(CharacterMovementState.class).getMovementState());
controls.put(entity.getId(), control);
}
private void updateControl(Entity entity) {
controls.get(entity.getId()).setMovementState(entity.get(CharacterMovementState.class).getMovementState());
}
private void removeControl(Entity entity) {
DemonAnimationControl c = controls.remove(entity.getId());
if (c.getSpatial() != null) {
c.getSpatial().removeControl(c);
}
}
@Override
public void cleanup() {
for (Entity entity : monsters) {
removeControl(entity);
}
this.monsters.release();
this.monsters.clear();
this.monsters = null;
super.cleanup();
}
}
This the console output:
SKEL: Human
jME3 Main
for: EntityId[9]Human
DE:: jME3 Main
Jan 21, 2018 6:33:00 PM com.jme3.app.LegacyApplication handleError
SEVERE: Uncaught exception thrown in Thread[jME3 Main,5,main]
java.lang.IndexOutOfBoundsException: bitIndex < 0: -1
at java.util.BitSet.set(BitSet.java:444)
at com.jme3.animation.AnimChannel.addBone(AnimChannel.java:258)
at com.jme3.animation.AnimChannel.addBone(AnimChannel.java:247)
at de.gamedevbaden.crucified.controls.DemonAnimationControl.setSpatial(DemonAnimationControl.java:33)
at com.jme3.scene.Spatial.addControl(Spatial.java:769)
at de.gamedevbaden.crucified.appstates.view.DemonAnimationAppState.addControl(DemonAnimationAppState.java:72)
at de.gamedevbaden.crucified.appstates.view.DemonAnimationAppState.update(DemonAnimationAppState.java:54)
at com.jme3.app.state.AppStateManager.update(AppStateManager.java:287)
at com.jme3.app.SimpleApplication.update(SimpleApplication.java:236)
at com.jme3.system.lwjgl.LwjglAbstractDisplay.runLoop(LwjglAbstractDisplay.java:151)
at com.jme3.system.lwjgl.LwjglDisplay.runLoop(LwjglDisplay.java:197)
at com.jme3.system.lwjgl.LwjglAbstractDisplay.run(LwjglAbstractDisplay.java:232)
at java.lang.Thread.run(Thread.java:748)
The exception is not really that important but the output of the System.out.println() is.
I don’t know why this set contains entities which should not be in there?
Does anybody know what is going on there and why this only happens in Multiplayer and always then when the second client connects?