Sim-eth-es Troubleshootings

So since this won’t be the last question that I have, I’ve named this thread appropriately :smiley:
The first step I did was creating a Health Component and adding it to the shipEntity, the client also gets this message:
11:35:31,922 INFO [MessageDebugger] Received:ReturnComponentsMessage[66, EntityId[65], [HealthComponent[health=100], ArmorComponent[armor=0]]], so far, so good.

Now I have this code though, it’s the UI which shall display the health:

    @Override
    protected void onEnable() {
          [...]
        playerStats = ed.watchEntity(getState(GameSessionState.class).getCharacterId(), HealthComponent.class, ArmorComponent.class);
    }

    @Override
    public void update( float tpf ) {
        if (playerStats.applyChanges()) { // Update the UI
            lblHealth.setText(String.valueOf(playerStats.get(HealthComponent.class).getHealth()));
            lblArmor.setText(String.valueOf(playerStats.get(ArmorComponent.class).getArmor()));
            System.out.println("Update Changes");
        }
    }

Is there something obvious I missed? I thought a watchedEntity is the best method here, since getEntity or getComponents() would trigger a whole network roundtrip every frame and an entitySet doesn’t make sense if it’s only for the local player

Yes, WatchedEntity is designed exactly for this kind of situations.

Also in some designs you may have change by time.
For example say you have a Hunger or Energy component you want to increase/decrease as time goes. So instead of update component every frame (or every few second) there is a trick I learned fro this Decay component

So for Hunger component you can implement change by time behavior like this :

public class Hunger implements PersistentComponent {

    private int hunger;
    private int maxHunger;
    private long degenerationInterval;    // nano time
    private long lastModifiedTime; // nano time
    //private double timeScale = 1.0 / 1000000000.0; // nanos

    public Hunger() {
    }

    public Hunger(int hunger, int maxHunger) {
        this(hunger, maxHunger, 0);
    }

    public Hunger(int hunger, int maxHunger, long degenerationInterval) {
        this.hunger = hunger;
        this.maxHunger = maxHunger;
        this.degenerationInterval = degenerationInterval;
        this.lastModifiedTime = System.nanoTime();
    }

    public int getHunger() {
        return getHunger(System.nanoTime());
    }

    public int getHunger(long timeSource) {
        long timePassed = (timeSource - lastModifiedTime);
        int decrease = (int) (timePassed / degenerationInterval);
        return Math.max(0, hunger - decrease);
    }

    public int getMaxHunger() {
        return maxHunger;
    }
}
1 Like

I can’t spot your question though…

OH MY! Yeah forgot that:
Update Changes is never print, applyChanges() is always false even though I see the network packet coming in. I could only imagine that the packet comes in before the onEnable() call and thus maybe missing the first change?

Note: Currently I’m not changing the health

Yes that’s the default behavior even for EntitySet I guess.

see

It tries to fetch the components when you initialize it in

playerStats = ed.watchEntity(getState(GameSessionState.class).getCharacterId(), HealthComponent.class, ArmorComponent.class);

after initialising you should call the updateStats() method then inside update(float tpf) you can check for apply changes.

Also there is a isComplete() method, you can use it to see if your watched entity has all the components. if it is false it means one or more component is null in watchedEntity.

Edit:

update(float tpf) { 
  if(playerStats.applyChanges()){
       updateStats()
}}


updateStats(){
            lblHealth.setText(String.valueOf(playerStats.get(HealthComponent.class).getHealth()));
            lblArmor.setText(String.valueOf(playerStats.get(ArmorComponent.class).getArmor()));
            System.out.println("Update Changes");
}
1 Like

That solved it, I am so stupid :smiley: For EntityContainers I never had that Problem as addObject is called, but here I just didn’t know what to do.

Onto the next problem^^


Jaime is a Node with a guiControl and essentially the problem is: I can’t get him in line with the huge numbers, I can’t move him at all (apart from taking another Layout), it feels like something overriding setLocalTranslation?

see Monake/RealHudLabelState.java at master · MeFisto94/Monake · GitHub for reference

Yeah… because EntityContainer takes care of calling adds on the initial set for you. :slight_smile:

1 Like

And another issue: After Upgrading to SimEth 1.3.0, the following is printed into the console:
16:08:38,354 WARN [StateWriter] Mismatched time sources. Delta:36552.160090178 seconds
Probably once per frame, always only differing in the sub-second sector.
relevant commit: Upgrade Simsillica Libraries · MeFisto94/Monake@37fb8fa · GitHub

This was covered in the release thread, I think… checking…

That’s what one gets for not reading the threads but only bumping version numbers and then checking commits :smiley:
Now the only existing issue is the Jaime-misplacement currently

And another problem that is related to SiO2 Bullet Extension:
Currently I don’t see any “movement”, I’d guess that this is because of multiple “Position” Components available and Bullet using different ones than sim-eth-es, however I’ve not found much code in SiO2 which would set Position Components either.

From the PhysicsDebugStates I gather that they rely on regular Position components, but it seems BulletSystem isn’t doing anything and that’s the job of Physics Listeners? But the DebugPhysicsListener doesn’t do this either, so what am I missing?

Edit: Just now I’ve found the PositionPublisher Class in the demos, which is what I’ve missed, because there are two Listeners added and for some reason I’ve only picked up the debug listener

So if I understand right… you are trying to integrate the SiO2 bullet stuff with an app based on the sim-eth demo?

Yeah, you will need to register a listener with the physics space to update the zone manager.

This is the one I use in SpaceBugs:

/*
 * $Id$
 * 
 * Copyright (c) 2016, Simsilica, LLC
 * All rights reserved.
 * 
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions 
 * are met:
 * 
 * 1. Redistributions of source code must retain the above copyright 
 *    notice, this list of conditions and the following disclaimer.
 * 
 * 2. Redistributions in binary form must reproduce the above copyright 
 *    notice, this list of conditions and the following disclaimer in 
 *    the documentation and/or other materials provided with the 
 *    distribution.
 * 
 * 3. Neither the name of the copyright holder nor the names of its 
 *    contributors may be used to endorse or promote products derived 
 *    from this software without specific prior written permission.
 * 
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 
 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 
 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS 
 * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE 
 * COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, 
 * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES 
 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR 
 * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 
 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, 
 * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 
 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED 
 * OF THE POSSIBILITY OF SUCH DAMAGE.
 */

package com.simsilica.sb.server;

import org.slf4j.*;

import com.jme3.math.Quaternion;
import com.jme3.math.Vector3f;

import com.simsilica.mathd.*;
import com.simsilica.ethereal.zone.ZoneManager;
import com.simsilica.sim.AbstractGameSystem;
import com.simsilica.sim.SimTime;

import com.simsilica.bullet.BulletSystem;
import com.simsilica.bullet.EntityPhysicsObject;
import com.simsilica.bullet.EntityRigidBody;
import com.simsilica.bullet.PhysicsObjectListener;


/**
 *  A game system that registers a listener with the SimplePhysics
 *  system and then forwards those events to the SimEtheral zone manager,
 *  which in turn will package them up for the clients in an efficient way.
 *
 *  @author    Paul Speed
 */
public class ZoneNetworkSystem extends AbstractGameSystem {
 
    static Logger log = LoggerFactory.getLogger(ZoneNetworkSystem.class);
    
    private ZoneManager zones;
    private PhysicsObserver physicsObserver = new PhysicsObserver();
    
    public ZoneNetworkSystem( ZoneManager zones ) {
        this.zones = zones;
    }
     
    @Override
    protected void initialize() {    
        getSystem(BulletSystem.class, true).addPhysicsObjectListener(physicsObserver);
    }

    @Override
    protected void terminate() {
        getSystem(BulletSystem.class, true).removePhysicsObjectListener(physicsObserver);
    }
    
    /**
     *  Listens for changes in the physics objects and sends them
     *  to the zone manager.
     */
    private class PhysicsObserver implements PhysicsObjectListener {

        private Vector3f posf = new Vector3f();
        private Quaternion orientf = new Quaternion();        
        
        private Vec3d pos = new Vec3d();
        private Quatd orient = new Quatd();
        
        // We probably won't have many zones, if we even have more than one.
        // The physics objects do not provide any sort of accurate bounds so
        // we'll guess at a size that is "big enough" for any particular mobile
        // object.  2x2x2 meters should be good enough... until it isn't.
        private AaBBox box = new AaBBox(1);

        @Override 
        public void startFrame( SimTime time ) {
            zones.beginUpdate(time.getTime());
        }
    
        @Override 
        public void endFrame() {
            zones.endUpdate();
        }
    
        @Override 
        public void added( EntityPhysicsObject object ) {
            // Don't really care about this
        }
    
        @Override 
        public void updated( EntityPhysicsObject object ) {
            if( object instanceof EntityRigidBody && ((EntityRigidBody)object).getMass() != 0 ) {
                EntityRigidBody body = (EntityRigidBody)object;
  
                // Grab the latest reference frame in our temp variables               
                body.getPhysicsLocation(posf);
                body.getPhysicsRotation(orientf);
 
                // Convert them to mathd values               
                pos.set(posf);
                orient.set(orientf);
 
                if( log.isTraceEnabled() ) {
                    log.trace("body:" + object.getId() + "  pos:" + pos);
                }
                 
                // Move the box as appropriate
                // Note: we don't so much care about bounds as there won't be many zones.
                // So we set it rather arbitrarily to 'big enough'.
                box.setCenter(pos);
                 
                zones.updateEntity(body.getId().getId(), true, pos, orient, box);
            }   
        }
    
        @Override 
        public void removed( EntityPhysicsObject object ) {
            zones.remove(object.getId().getId());
        }
    }
}



1 Like

Well in my case I was not using zones but setting the Position component directly, is that because spacebugs is sim-eth but I’ve based mine off sim-eth-es?

While we’re at it, how would I go about sending the input to the server for the server to move the physics object and hence moving the entities so these updates get synched to the client?

Most certainly I do not want the client to send ClientInput Components as that transport is not made for these inreliable updates? Would I have 200ms lag that way though? as the server is generally 100 ms behind and I got to send the client inputs to the server which might have the same round trip time?

Edit: And thanks for the prompt replies as always :slight_smile:

As their names imply, sim-eth examples are “SimEthereal” examples. So they use SimEthereal and you should not be updating positions directly, really. On the server, let the physics engine update the ZoneManager and then on the client catch the shared object events and update your local objects. (I guess the examples use BodyPosition?)

The demos are already sending control input to the server… so I’m not sure why you need to do it differently than that.

Using the SiO2 bullet support, you’d give your player controlled objects a driver that is given the incoming input data to decide how to move that rigid body.

Well I was confused there because there is a BodyPosition, a Position using SimMath’s Double Classes coming from sim-eth-es and another Position class coming from the Bullet Example.

OH, now I get it, the code for the ships is already there and I just have to adjust that for Bullet.

Bare with me, this is the first time I’m seeing all of these systems and I’m kind of frustrated because of the learning curve, but I’ll try my best to only ask the less-dumbest questions soon^^ But I guess this is also a Hint that I have to take a break from it for today because of overseeing the most simple things :slight_smile:

Well, there is a lot going on but each part is simple on its own… if you can pick it out.

In the demos, Position is used as sort of a static ‘spawn’ position for objects. In newer code, I actually call this “SpawnPosition” to distinguish it.

Basically, you have the Position that will tell the physics engine where to start. The physics engine will then notify listeners when things have moved.

On the server, it’s these listeners that will publish changes to the ZoneManager so that the clients will see them. It will also push changes to server-side BodyPosition objects so that things like AI or other game object know where the bodies are. (Also, the ES will need a BodyPosition created on the server so that the clients will have a BodyPosition instance to mess with.)

On the client, a shared object listener will update the client-side BodyPosition objects with the latest synched position.

as for control… the ship movement is already driving a control driver with the simple physics engine included in the demos. That same concept just needs to be adapted to the SiO2 bullet control drivers. For a walking/jumping character, something like:

1 Like

And in case it’s helpful and you might have forgotten… there is some basic documentation on the wiki:

And I mention this because when I went to inject some new code, I spent like two days relearning all of this stuff when the second link would have caught me up in 20 minutes. Wasted days.

Would you mind to also share the code for BodyPositionPublisher, please ?

Yeah, I don’t know how different it is:

/*
 * $Id$
 * 
 * Copyright (c) 2018, Simsilica, LLC
 * All rights reserved.
 * 
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions 
 * are met:
 * 
 * 1. Redistributions of source code must retain the above copyright 
 *    notice, this list of conditions and the following disclaimer.
 * 
 * 2. Redistributions in binary form must reproduce the above copyright 
 *    notice, this list of conditions and the following disclaimer in 
 *    the documentation and/or other materials provided with the 
 *    distribution.
 * 
 * 3. Neither the name of the copyright holder nor the names of its 
 *    contributors may be used to endorse or promote products derived 
 *    from this software without specific prior written permission.
 * 
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 
 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 
 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS 
 * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE 
 * COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, 
 * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES 
 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR 
 * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 
 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, 
 * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 
 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED 
 * OF THE POSSIBILITY OF SUCH DAMAGE.
 */

package com.simsilica.sb.sim;

import com.jme3.math.Quaternion;
import com.jme3.math.Vector3f;

import com.simsilica.bullet.BulletSystem;
import com.simsilica.bullet.EntityPhysicsObject;
import com.simsilica.bullet.EntityRigidBody;
import com.simsilica.bullet.PhysicsObjectListener;

import com.simsilica.es.*;
import com.simsilica.mathd.*;
import com.simsilica.sim.*;

import com.simsilica.sb.es.BodyPosition;
import com.simsilica.sb.es.Position;


/**
 *  Publishes to a BodyPosition component so that server-side systems
 *  have easy access to the mobile entity positions.  Since we wrote
 *  our own simple physics engine for this example, we could have just
 *  added BodyPosition as a field to the Body class but I wanted to show
 *  how one might integrate this component using a physics system that
 *  wouldn't let you do that.
 *
 *  Note: also adding the BodyPosition to the entity on the server
 *  is what makes it available on the client so that it can have a place
 *  to add its object update events from the network.  The BodyPosition
 *  component itself is actually transferred empty.
 *
 *  @author    Paul Speed
 */
public class BodyPositionPublisher extends AbstractGameSystem  
                                   implements PhysicsObjectListener {
 
    private EntityData ed;
    private SimTime time;
    
    public BodyPositionPublisher() { 
    }
 
    @Override
    protected void initialize() {
        this.ed = getSystem(EntityData.class);
        
        getSystem(BulletSystem.class).addPhysicsObjectListener(this);
    }

    @Override
    protected void terminate() {
        getSystem(BulletSystem.class).removePhysicsObjectListener(this);
    }
 
    @Override
    public void startFrame( SimTime time ) {
        this.time = time;
    }
    
    @Override
    public void added( EntityPhysicsObject object ) {
        /*if( !(object instanceof EntityRigidBody) ) {
            return;
        }
        EntityRigidBody body = (EntityRigidBody)object;
        if( body.getMass() == 0 ) {
            // We don't really care about static objects here
            return;
        }*/
        // We don't really care about static objects here since they
        // don't require special BodyPosition setup.
        if( object instanceof EntityRigidBody && ((EntityRigidBody)object).getMass() == 0 ) {
            return;
        }
        
        // The server side needs hardly any backlog.  We'll use 3 just in case
        // but 2 (even possibly 1) should be fine.  If we ever need to rewind
        // for shot resolution then we can increase the backlog as necessary
        BodyPosition bPos = new BodyPosition(3);

        // We should probably give the position its initial value
        // but it's about to get one in updated() right after this.
        // If we come up with cases that slip between these two states
        // then we can readdress
        
        ed.setComponent(object.getId(), bPos);       
    }
    
    @Override
    public void updated( EntityPhysicsObject object ) {
        //if( !(object instanceof EntityRigidBody) ) {
        //    return;
        //}
 
        //EntityRigidBody body = (EntityRigidBody)object;
        Vector3f loc = object.getPhysicsLocation(null);
        Quaternion orient = object.getPhysicsRotation(null);
        
        //if( body.getMass() == 0 ) {
        if( object instanceof EntityRigidBody && ((EntityRigidBody)object).getMass() == 0 ) {
            // It's a static object so use standard Position publishing
//System.out.println("update(" + object + ")");    
            Position pos = new Position(loc, orient);
//System.out.println("  pos:" + pos);        
            ed.setComponents(object.getId(), pos); 
        } else {
            // It's a mob or a ghost
            
//System.out.println("updateMob(" + body + ") loc:" + loc);            
            // Grab it's buffer which we are guaranteed to have because we set it in
            // added()
            BodyPosition pos = ed.getComponent(object.getId(), BodyPosition.class);
            pos.addFrame(time.getTime(), loc, orient, true);           
        }
    }
    
    @Override
    public void removed( EntityPhysicsObject object ) {
        // We don't really care about static objects here since they
        // don't require special BodyPosition management.
        if( object instanceof EntityRigidBody && ((EntityRigidBody)object).getMass() == 0 ) {
            return;
        }
        //if( !(object instanceof EntityRigidBody) ) {
        //    return;
        //}
        BodyPosition pos = ed.getComponent(object.getId(), BodyPosition.class);
        if( pos == null ) {
            // It was a static object or somehow initialized poorly
            return;
        }
        
        // Give one last position update with the visibility shut off
        Vector3f loc = object.getPhysicsLocation(null);
        Quaternion orient = object.getPhysicsRotation(null);
        pos.addFrame(time.getTime(), loc, orient, false);
    }

    @Override
    public void endFrame() {
    }    
}



1 Like