Tuesday, October 18, 2016

Easy Network Programming For Games

When you start developing your latest multiplayer game, and get onto the networking aspect, do you wish there was a way to avoid having to re-write the same code over and over again?  All the handling of sockets, multiple network threads, keeping track of clients etc...?  Me too, so that's why I wrote the Generic Multiplayer Connector.

The Generic Multiplayer Connector (GMC) is a library to help turn any single-player game into a multi-player game by allowing clients to easily connect to a shared server and pass data to each other without having to worry about all the usual networking complications. It uses a client/server model, and when a client sends out any data, it is automatically received by all other clients in the same game. Clients also receive notifications of when a game has been won and who the winner was.

GMC is also completely open source and can be downloaded here: https://bitbucket.org/SteveSmith16384/genericmultiplayerconnector 

Why should I use GMC?

That's a very good question. There are lots of other networking libraries out there. However, the real advantage of GMC is that it requires minimal setup, configuration and handling:
  • Running the server is simple a case of running a jar. Because the server is generic, a single server can be used for any number of different games.
  • Once a client is connected, it can send data, and will automatically receive data, from all the other clients playing in the same game. All you need to do is decide when to send data, what data to send, and what to do with the data when it's received.
  • You don't need to worry about handling network connections, multithreading, keeping track of other players etc.. Just send and receive data.
  • There are built-in methods for accessing the current status of the game (waiting for players, started, finished), notification when a player has joined/left, and who the winner of the game was.
  • There is a free publicly-accessible server waiting for you to use.

 

Example Code

Running the server:-
    // Run this in a command prompt
    java -jar GMCServer.jar 
 
All the following code is run on the clients.

Connecting to the server:-
    // This will bring up a form for the user to enter an IP address etc..
    ConnectorMain connector = StartGameOptions.ShowOptionsAndConnect(this);

    // Alternatively, if you have your own method of getting the 
    // connection details:
    ConnectorMain connector = new ConnectorMain(this, "127.0.0.1", 
        9996, "Steve", "MyGame", 2, 99);
    connector.connect(); 
 
Joining a game:-
    connector.joinGame(); 
 
Sending data to all other clients:-
    // There are other ways, this sends a key/value pair by TCP.
    connector.sendKeyValueDataByTCP(code, score); 
 
Receving data
    // 'game' is your class that implements the IGameClient interface.
    game.dataReceivedByTCP(int fromplayerid, int code, int value);
 
Damn, I've been killed in the game
    connector.sendOutOfGame(); 
 
I've got to the end! Was I the first?
    connector.sendIAmTheWinner(); 
 
But who has the server confirmed as the winner?
    System.out.println("The winner was " + connector.getWinnersName() + "!");

 

 Quickstart Guide

These are step-by-step instructions on how to incorporate it into your project:-
  • Run the server GMCServer.jar, or decide to use the public server described below.
  • Add the GMCClient.jar to your project.
  • implement the IGameClient interface in a class in your game.
  • Create instance of ConnectorMain().
  • Call ConnectorMain.connect() to connect to the server
  • Call ConnectorMain.joinGame() to join a game.
  • Wait until all players have connected ("ConnectorMain.getGameStage() == GameStage.IN_PROGRESS") and then start your main game loop.
  • Send data to other clients with any of the ConnectorMain.sendKeyValue..., ConnectorMain.sendString... or ConnectorMain.sendByteArray... methods. All the other clients will receive any data sent.
  • Receive data using any of the IGameClient.dataReceived... interface methods.
  • Call ConnectorMain.sendIAmTheWinner() if your client won the game, or call ConnectorMain,sendOutOfGame() if your client is out of the game.
  • End your game when "ConnectorMain.getGameStage() == GameStage.FINISHED", or if your client has finished (i.e. the player has died), call ConnectorMain.waitForGameToFinish().
  • Once the game has finished, you can get the winner's name with ConnectorMain.GetWinnersName().
  • You can then call ConnectorMain.joinGame() to join/start another game, or ConnectorMain.disconnect() to close all network connections.

 

Public Server

I run a server at 178.62.91.22 that can be used by anyone.  As stated above, any GMC server can be used for any game or any number of games.  (See the source file Statics.java for the port). Note that this server is not guaranteed to be available 24/7 forever though, and should not be used for anything mission-critical.

Thursday, October 06, 2016

Ares:Dogfighter now Open Source

Due to lack of popular demand, and the fact that I've slowly stopped developing it, the source code and assets of Ares:Dogfighter are now available on BitBucket here.  It should be pretty useful to Java programmers looking to get started with 3D.

Here's a video showing its current state:-


Wednesday, September 28, 2016

Stuck in a Loop

I'm at that juncture again where I ponder the question to myself "Why am I writing games?"  I've been working on a game called "Ares:Dogfighter" for a few months now, but I'm slowly getting bored with it and there's a chance it will get slowly abandoned.

I always start with the best of intentions, and starting on a new game is great and exciting: the idea is epic in scale, the possibilities are endless, the screenshots in my head look great, and I know how I'm going to write all the code.  However, fast-forward a few months and I'm stuck debugging a small but frustrating problem that's taking up a lot of my time, and nobody has played it (or at least, no-one has told me they've played it).  So why am I spending the time writing it? 

Part of the problem is the time that marketing a game now requires, but without marketing, no-one will see the game among the other trillion games out there.  Setting up the blog/website/twitter account and regularly posting to it takes time, and my time is already limited as it is.

I'll probably stop developing games for a few months and start playing them again.  Or I could jump onto an open-source game and contribute to that, but I find it very hard to find semi-decent Java open-source games.  Eventually I'll have an amazing idea for a game, start work on it, and the process will start again.

Wednesday, August 17, 2016

My take on No Man's Sky missing multiplayer feature

IMHO, what has happened to No Man's Sky's "missing" multiplayer feature is a no-brainer.  I think they originally planned to have it in, but eventually came late to the decision that it was not going to be completed in time for the release date, which couldn't be put back again.  However, Sean will have been under immense pressure to never actually say "NMS will *not* have multiplayer", since sales would have been adversely affected.  His hope was that no-one would manage to meet up until they had added the multiplayer feature in an update (and also try and discourage people from even trying to meet up due to the immense odds of that happening), but unfortunately, they did.

So Sean did the only thing he could: avoided directly answering any questions (since to say anything other than "multiplayer doesn't exist" would be an outright lie) and is now hoping that the multiplayer feature could be completed ASAP and added as a "multiplayer fix", since the feature was already there, just wasn't working properly.  Right?

Monday, August 15, 2016

Use InputStream.available() with caution

You might assume that InputStream.available() returns how many bytes are available to be read from your stream. And you'd be almost right; it's really how many bytes are guaranteed to be available, not how many are actually available. 

Okay, to get to specifics, my problem occurred when I used a ZipInputStream wrapped around a file. I (stupidly) assumed that all the bytes must be available, since it's a file sat on my computer. However, since ZipInputStream reads a compressed file, the number of bytes available isn't the same as the original file size. (I should mention that at this point I didn't actually know I was reading a zipped file, as the whole point of streams is that you don't normally need to bother yourself with the implementation).

In addition, ZipInputStream.available() only returns either 1 or 0, so it would be unwise to use that size as the basis for an array to ready the data in.

So, my point is, available() should really be [treated as] a boolean.  If it's > 0, great, you can read something.  Just don't put any reasoning behind the actual number.



Tuesday, July 26, 2016

Zero-Time Programming

Welcome to a description of my new programming paradigm: zero-time programming.  Being a busy family dad, I've got no time for programming.  I've got no time for even reading blogs, never mind actually writing one.  Or that's what I thought until I developed zero-time-programming, for people who have zero spare time but still want to create and release something.

The principle behind zero-time programming is to write code, run it yourself (if you've got time) and then release it.  The main requirement is that you have the facility in your released software to inform you immediately if there's a problem; my usual choice is by email, so I get an email every time there is an error.

"But this is crazy" I hear you say.  Sorry, I've got no time to discuss it.  I will concede that this is not the best method for writing mission-critical code for NASA, but if you're a hobby programmer like me who just wants to get games out there while also having family life and full-time job, there's no better choice.

Here's the main bullet points to help you out though:-
 
* Wrap all your new code in a try/catch, and in the catch, send yourself the error.  That way, the whole program won't grind to a halt.
* Throw exceptions wherever something happens that shouldn't, i.e. at the end of switch statements.
* Make a note of any thoughts you have about what to code, immediately.  Whether in the shower, on the toilet etc.., your subconscious will do a lot of the thinking for you, so don't ignore it or forget what it's telling you.  I've "found" plenty of bugs whilst sitting on the toilet.
* Use your own code, and make other code your own.  Avoid moving ground.  Who's got time to refactor code when your favourite library decides to change their method signatures?
* Send yourself a message from your software when something you need to check for happens, so you know it is happening (or not).
* Sanity checks - for example check variables aren't null.
* Ensure your error messages state which version has the error, so you can ignore old errors that have already been fixed.
* As you code, if you think of an extra bit of code that needs to be written in addition to the bit you're currently writing, e.g. a null-check that needs to be made, quickly write a todo, e.g. "// todo - nullcheck socket".  Otherwise it will fall out of your head and bite you later.  Once you've written the bit you're writing, do a search for all the "todo's" in your code and addess them.

All this will save you loads of time.  How long does it take to go through a testing plan?  Too long.  Of course, this method of programming may mean you get a stream of messages informing you of bugs, but that just means you can fix them as soon as you have a spare couple of minutes, and then upload the next version.

Wednesday, June 29, 2016

Announcing Ares:Dogfighter

My "very early space game demo" has transmogrified into a game!  Although still very early in development, there is now something to download and play.  It's called Ares: Dogfighter, and you take control of a spaceship and can fight enemy ships using lasers and missiles.

Here's a brief gameplay video:-


The game is uses the excellent JMonkeyEngine for the 3D, and lots of complicated Java code for the rest of it.  Getting the AI to fly realistically was a challenge; the standard response for a pilot, whether AI or human, is to try and point their ship at the enemy ship in order to shoot them.  However, this has a tendency to result in both ships flying around in circles until someone dies of boredom.  I've made the AI check for this, and if things aren't happening then the AI will react with some random evasive manoeuvres.

The game is free to download and there are versions for Windows and Linux.  Please let me know what you think!  (Or at least whether it works).