Networking for Cowards - Part 1 - connection and messages

Previous topic - Next topic

Kitty Hello

You're right for cowards. That's what you are :P. But this tutorial's gonna make you a cowboy, whee-haa!

So, in this tutorial I'll explain the NET.. commands of GLBasic, which are very easy to use.

The NET commands are designed for a client-server application, means one computer is the "main" computer, and all other computers connect as "clients" to this server. The server itself can have the same functionality as the clients, but internally it's doing things differently. All clients send messages to the server process. By process I mean the program on the server computer, which can be the same as the client computer. You can have 2 processes on one machine and they talk to each other.
The server collects all these messages and sends some of them back to the clients that should receive this message initially.
So, for a client-server application, the messages from client 1 to client 2 are always Client1->Server->Client2.
That's the internals, just to let you know.

First, make a server:
Code (glbasic) Select

SYSTEMPOINTER TRUE
AUTOPAUSE FALSE

You surely want to debug your program on _one_ computer, so use these 2 commands to have them both working in parallel.

Code (glbasic) Select

ok% = NETHOSTGAME(0)
IF ok% = FALSE THEN END

Start up the server. It will now idle and wait for clients to connect to it in the background. A separate thread is created to handle the message shuffling. Again, just in case you care.

On the client machine you want to connect to this server now:
Code (glbasic) Select

ok% = NETJOINGAME("127.0.0.1", 0)
IF ok%=FALSE THEN END

Now "127.0.0.1" is an IP address for "this computer". It's also called a "loopback device", which just means that you can connect to a process on the same machine with this virtual network adapter.
If you have a network game in the internet, insert the internet IP of the server program here. You can get you IP in internet from here: http://whatismyipaddress.com
If you're in a LAN, you can simply insert the network computer name here, too. So, my PC is called "PC-GF" and when I write this here it's working for my computer. If my mate starts the server, I put "PC-XY" here and I can connect to it.

If you have a problem here, print the result of
Code (glbasic) Select

err$ = NETGETLASTERR$()

somewhere. It tells you an error number and a long error string you might find useable for debugging the problem.
In both cases I specified a parameter "0" as the port, which means: Take the GLBAsic default port 27910 TCP. You have to open this port for TCP connections in:

  • Your Firewall  (ZoneAlarm?)
  • The Windows Firewall (if enabled)
  • Some Worm-Protection software (such as Norton AV)
  • Your gateway to the internet (if you're playing over the INET, and your box (FritzBox?) provides a port filter

Surely you can use another port above 1024 for your game to be unique.
Here's a list of ports and associated applications you might want to avoid:
http://www.iana.org/assignments/port-numbers

If both calls above succeeded, server and client will have a little chat about the weather, kids and if the server's still alive.
Next thing is to create "players". A player in the NET... world is simply a "mailslot" you can use to send data to or receive data from. Each program can create any number of players in one instance. Usually one player suffices for most applications. You can use different players to distinguish between certain actions, though.

Code (glbasic) Select

id% = NETCREATEPLAYER("John Doe")
IF id% = 0 THEN END

OK, if the return value is non-zero the server is now aware of this player and the process that called NETCREATEPLAYER is, too. This can be the same process, however. So the server can create a player "Sam" and the client can create a player "Ken" and both can have a chat now. The name can be anything you want in a string. I used it to send both player name and character sprite id, once.
The returned player ids will be unique on each machine.

If you want to know how many players the server is aware of, call
Code (glbasic) Select

num% = NETNUMPLAYERS()

This will send a query to the server and the server will send a response message. This call might take a little time, so be sure not to call it every frame update. If someone stepped over the server's power cord and closed the server, you will get a return value of "0" here (despite your knowledge you already created at least one player). You can quit now, too. There's no way to reach other clients any more. At least not using NET... commands, only.

Now you have the number of players totally, but you don't know their IDs.

Code (glbasic) Select

GLOBAL gIDs%[]
DIM gIDs%[num%]
FOR i% = 0 TO num%-1
   gIDs%[i] = NETGETPLAYERID(index%)
NEXT


So you can query the player ID from the player's index in the server's player list. New players will be put to the end of the list. A client should, however, only get the player IDs after the server has decided to
Code (glbasic) Select

NETALLOWJOINING FALSE

so no more players can connect. Send a message to the clients then. See later.

Maybe you want to display who you are fighting, so query the player's name by the id with
Code (glbasic) Select

name$ = NETGETPLAYERNAME$(id%)

Again, it's a query and as such takes some time. Don't do for every frame, but only once for each player.

If you want to quit, it's nice to call
Code (glbasic) Select

NETDESTROYPLAYER id%
NETSHUTDOWN


So the server knows it instead of "guessing" you've died.

Finally you can send messages across the game with
Code (glbasic) Select

ok% = NETSENDMSG(id_from%, id_to%, text$)

where id_from% must be a player that you created with this process, and id_to% can be any other valid player id. The server gets this message and forwards it to the players mailbox then. If you send to player id "0", the message gets sent to all except the sender (who should already know about it, doesn't he?)

If you have a stack of messages in your mailbox, you must call
Code (glbasic) Select

msg$ = NETGETMSG$(id%)

If msg$ is empty, there's no more messages for you, well done. If it's not, please call this message again until it is to free up the internal post office space.

With
Code (glbasic) Select

id% = NETGETSENDER()

you can find the id of the sender of the last message you recieved with NETGETMSG$(). In case you are nosy.

Be sure, that during a query, the player queues up incoming messages until the query result arrives. Thus, it's no good idea to send a few thousand messages per second to a player. It will simply choke and probably die.

So, what's the alternative to sending player messages rapidly?
- Send key changes and handle each player on each client. Send a sync-position update every 5 seconds.
- Only send when things changed
- Only send "informations" about events, not the event flow itself.

OK, that's pretty enough for today. I hope you will find some time to try the new network commands, since they really work good now. And I really want to encourage you to write a network game. It's no dark magic any more with GLBasic's NET commands.

FutureCow

Is there any way to query a server as to whether it has any games open?

For example, if you were running a world of warcraft server (we all know WOW was written in GLbasic  :whistle:) and had multiple games running on it for people to join, how would the client query the server to say "do you have any free games available, and if so, which games are they?"

MrTAToad

In my hosting system, the server retains the number of joined players, which in turn defines whether a player is allowed to join or not...

The above tutorial was very good, by the way.  What else would be good to see is some code for movement prediction.

Kitty Hello

There is 2 ways of doing this. For LAN sessions you can perform a broadcast message, for internet games you can query a session IP from a php script. I'll write a tutorial for both, soon.

bigsofty

A nice read Gernot, thank you.  :good:
Cheers,

Ian.

"It is practically impossible to teach good programming style to students that have had prior exposure to BASIC.  As potential programmers, they are mentally mutilated beyond hope of regeneration."
(E. W. Dijkstra)