Simple Game Server with Zay ES Part 1

This time I'm not sure where I will end and what the result will be, but anyway I want to write this article about Game Servers, as well for my self having it documented somewhere. I will shortly describe how to use Zay ES net to implement your own very simply game server. As a base I took the sample from Paul Speed. The sample will be extremly simple and just to the point. I removed every distracting stuff. After having a starting point I will evolve it a bit to show it's coolness.

At your service

I will start with the code

package gameserver;

import com.jme3.network.HostedConnection;
import com.jme3.network.MessageConnection;
import com.jme3.network.Network;
import com.jme3.network.Server;
import com.jme3.network.serializing.Serializer;
import com.jme3.network.serializing.serializers.FieldSerializer;

import com.simsilica.es.Name;
import com.simsilica.es.base.DefaultEntityData;
import com.simsilica.es.server.EntityDataHostedService;

public class GameServer {

    public static void main(String... args) throws Exception {
        Server server = Network.createServer("My Game Server", 1, 9942, 9942);
        DefaultEntityData ed = new DefaultEntityData();
        server.getServices().addService(new EntityDataHostedService(MessageConnection.CHANNEL_DEFAULT_RELIABLE, ed));
        Serializer.registerClass(Position.class, new FieldSerializer());
        Serializer.registerClass(Name.class, new FieldSerializer());
        SimpleGameLogic gameLogic = new SimpleGameLogic(ed);
        server.start();
        System.out.println("Server started.  Press Ctrl-C to stop.");
        try {
            while (true) {
                gameLogic.update();
                server.getServices().getService(EntityDataHostedService.class).sendUpdates();
                Thread.sleep(100); // 10 times a second

            }
        } finally {
            System.out.println("Closing connections...");
            for (HostedConnection conn : server.getConnections()) {
                conn.close("Shutting down.");
            }
            System.out.println("Shutting down server...");
            server.close();
        }
    }
}

First we create a jME server where we handover a name, a version, the tcp port and the udp port, nothing special.

    Server server = Network.createServer("My Game Server", 1, 9942, 9942);

And then we bind the entity data to the network.

    DefaultEntityData ed = new DefaultEntityData();
    server.getServices().addService(new EntityDataHostedService(MessageConnection.CHANNEL_DEFAULT_RELIABLE, ed));

To be able to send Postion.class and Model.class over the wire we need to register a serializer for them.

    Serializer.registerClass(Position.class, new FieldSerializer());
    Serializer.registerClass(Model.class, new FieldSerializer());

For simplicity we hold the game logic in a separated object which we will explore later. We just instantiate it here and call update of that game logic object in the loop. As well we start the server as you can see.

The game loop is running as infinit loop in a try catch block to be able to cleanly react if the loop internals throw an exception for some reason, at least the code try its best to clean up nicely and let the clients know the server is closing. So this try catch is a good practics.
We update the game logic and send the update to the clients 10 times per second. Normally the game logic is updated 60 times per second and the updates are send 20 times per second or less depends on the game you write.

        while (true) {
            gameLogic.update();
            server.getServices().getService(EntityDataHostedService.class).sendUpdates();
            Thread.sleep(100);
        }

Why you may ask, do the game logic run at different and faster rate as the updates we send, why not both with a lower update rate then. The answer is your game logic may update often so that physics is accurate and AI is self-responsive and so on. Not all updates are neccessarily entity updates as this may be to slow and clumbsy for various aspects of your game, but this is not part of this article though.

Same old story

So we need a simply postion and model component. I take the same as I took in the invaders game like example, see my Entity System Component arcticle series.

package gameserver;

import com.jme3.math.Vector3f;
import com.simsilica.es.EntityComponent;

public class Position implements EntityComponent {

    private Vector3f location;
    private Vector3f rotation;

    protected Position() {
        // Just for serialization

    }

    public Position(Vector3f location, Vector3f rotation) {
        this.location = location;
        this.rotation = rotation;
    }

    public Position(Vector3f location) {
        this(location, new Vector3f(0, 0, 0));
    }

    public Vector3f getLocation() {
        return location;
    }

    public Vector3f getRotation() {
        return rotation;
    }

    @Override
    public String toString() {
        return getClass().getSimpleName() + "[" + location + ", " + rotation + "]";
    }
}

I adjusted the position comoponent of the invaders game slightly to be able to serialize it.

location and rotation member variable must not be final

private Vector3f location;
private Vector3f rotation;

And we need a no-argument constructor

  protected Position() {
      // Just for serialization

  }

As well I have to adjust the model component a little bit for serializing.

package gameserver;

import com.simsilica.es.EntityComponent;

public class Model implements EntityComponent {
    private String name;
    public final static String SpaceShip = "SpaceShip";
    public final static String BasicInvader = "BasicInvader";
    public final static String Bullet = "Bullet";

    public Model() {
        // Just for serialization

    }
    
    public Model(String name) {
        this.name = name;
    }
    
    public String getName() {
        return name;
    }
    
    @Override
    public String toString() {
        return "Model[" + name + "]";
    }

As well here the name must not be final

private String name;

And we need as well here a no-argument constructor

public Model() {
    // Just for serialization

}

Brains

The last thing which is missed is the SimpleGameLogic class. I held it extremely simple for the moment.

package gameserver;

import com.jme3.math.Vector3f;

import com.simsilica.es.Entity;
import com.simsilica.es.EntityData;
import com.simsilica.es.EntityId;
import com.simsilica.es.EntitySet;
import com.simsilica.es.Name;

public class SimpleGameLogic {

    private final EntityData ed;
    private final EntitySet invaders;

    public SimpleGameLogic(EntityData ed) {
        this.ed = ed;
        for (int x = -20; x < 20; x += 4) {
            for (int y = 0; y < 20; y += 4) {
                EntityId invader = ed.createEntity();
                this.ed.setComponents(invader,
                        new Model(Model.BasicInvader),
                        new Position(new Vector3f(x, y, 0))
                );
            }
        }
        invaders = ed.getEntities(Name.class, Position.class);
    }

    public void update() {
        invaders.applyChanges();
        for (Entity e : invaders) {
        }
    }
}

I think it is clear what this does I guess. Ok then. You can fire up your very first game server hit F6 in the SDK or do it in your gradle project in the command line.

Not much to see actually. We need a client to see something, I will write a followup to this article, so stay tuned.

Comments

comments powered by Disqus