Multiplayer Online Game

Pages: 12
Hi,
I am just wondering what would be the best method for the networking part of an online game. In the server in my client class I defined two streams. A local stream and a global stream. Anything written to the local stream is sent to the client only. This would be good for updating user pacific things such as dropping an item in their inventory. And the global stream could be good for sending player coordinates ect ect

Is this a bad design

Here is some code:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
#include <stdio.h>
#include "PlayerHandler.h"
#include "Stream.h"
#include "Client.h"
#include "Player.h"

Client::Client(int sock, int slot)
{
    _socket = sock;
    _playerID = slot;
    gstream = new Stream(new char[DEFAULT_BUFFER_SIZE], DEFAULT_BUFFER_SIZE);
    lstream = new Stream(new char[DEFAULT_BUFFER_SIZE], DEFAULT_BUFFER_SIZE);
}

void Client::flushGlobalStream()
{
    for (int i = 0; i < PlayerHandler::maxPlayers; i++)
    {
        char* gbuf = gstream->getBuffer();
        if (PlayerHandler::player[i] != 0)
        {
            send(PlayerHandler::player[i]->getSocket(), gbuf, gstream->getBufferLen(), 0);
        }
    }
    gstream->clearBuffer();
}
void Client::flushLocalStream()
{
    char* lbuf = lstream->getBuffer();
    send(_socket, lbuf, lstream->getBufferLen(), 0);
    lstream->clearBuffer();
}

int Client::getSocket()
{
    return _socket;
}
void Client::update()
{
    gstream->writeByte(_playerID);
    gstream->writeByte('A');
        gstream->writeByte('B');
            gstream->writeByte('C');
    flushLocalStream();
    flushGlobalStream();
}
void Client::loginProcedure()
{
    printf("Login Procedure\n");
    const char* c = {"Login"};
    send(_socket, c, 6, 0);
    _authenticated = true;
}

DWORD WINAPI Client::process(LPVOID lParam)
{
    Client* client = (Client*)(lParam);
    printf("Player connected with playerID %d\n", client->_playerID);
    // Start our login procedure and wait for the user to authenticate
    client->loginProcedure();
    return 0;
}


Obviously you would never call the flushLocalStream in the process method because its a thread they will collide so the idea is in the update method you do the flushing for the local stream.
Last edited on
Take a look at Raknet.
I would rather do it from scratch using nothing but TCP/IP and winsock 2 as I want full control over the networking and I want to have a learning experience. Is my example above decent if not could you explain the flaws and could you provide a better way of doing things

Cheers
Don't get stuck on reinventing the wheel consantly. Doing it a bit here and there isn't a big deal, but unless you really have a good reason, just use what's already done. Raknet is very fast, most likely faster than anything you can write. (No offense, I'm just saying it's a very developed library with a good team). Using the Windows API for a game usually introduces several problems.

1.) It only works on Windows. If you want to port over then you have to completely redo your networking code
2.) Error prone: A lot can go wrong when it comes to networking with nearly any library or API
3.) You'll spend a huge amount of time implementing just networking. Assuming your game isn't very simple, there will be a lot of time to invest, time that you could spend actually working directly on the game.

1.) Raknet is portable
2.) Raknet is less error prone
3.) Raknet is more suited directly to your needs in this case and will save time

updating user pacific things

The word you're looking for is "specific", not "pacific".
Mikeyboy no need to get cocky, and like I said I am planning to do this with winsock so if anyone actually has any advice for this type of setup feel free to comment please don't try and point me to libraries this is not what I want. Its a learning experience and those who can't appreciate that don't comment. The above code all I need to know if is that the right way to go about things is a global stream and a local stream if not then could you explain why and could you also give a better example or tell me what I could do to improve it don't point me to libraries I am not interested in learning any. I prefer to do it this way
Last edited on
Mikeyboy no need to get cocky


I'm not being cocky, I'm trying to help. Communication is important. If I was misusing a word when communicating with someone else, I'd be grateful if someone helped me to fix a mistake I was making - and have been grateful on several occasions.

No need to get defensive.
Last edited on
Ok I didn't see it from that perspective sorry about that, I just looked at raknet it looks pretty good but just like above id rather do this using winsock 2 so is my global and local streams the right approach for this?

Cheers
closed account (N36fSL3A)
Austin J wrote:
1.) It only works on Windows. If you want to port over then you have to completely redo your networking code
That's a lie, Winsock was designed to get *nix users to write networking code for Windows. It's pretty much a direct recompile. Every major platform uses BSD-based sockets.

2.) Error prone: A lot can go wrong when it comes to networking with nearly any library or API
First off:

2.) Error prone: A lot can go wrong when it comes to networking with nearly any every library or API
Fixed.


Why are you even mentioning this? You're acting like you can only get errors with WinAPI (which is utter nonsense).

Raknet is a wrapper over WinSock, so you're pretty much saying Raknet is just as, or even more error prone than WinSock.
___________________________________________
Ok I didn't see it from that perspective sorry about that, I just looked at raknet it looks pretty good but just like above id rather do this using winsock 2 so is my global and local streams the right approach for this?

Cheers
First off, never use TCP sockets for game programming. Your best bet would be to use UDP, because it's fast with little overhead (in contrast to UDP).

TCP should be used in applications where speed is not critical, but the data sent is.

UDP is meant for realtime applications where data being sent isn't really important (ex. Video Streaming, games)

You should also be sending a packet over in a packet structure similar to this:
1
2
3
4
5
6
7
8
9
struct GamePacket
{
    uint8_t header[4]; // So the client/server can identify what type of message it is.
    uint64_t timestamp; // If multiple packets from the same address have the same timestamp, disregard the packet.
    uint32_t from; // Where the packet originated
    uint32_t to;    // Where the packet should have ended up (disregard if it isn't equal to your IP)
    uint32_t dataSize; // Size of dynamic data
    uint8_t* data; // Array of data
};


Send the entire structure directly...
1
2
3
4
5
6
GamePacket packet = {};
packet.header[0] = PK_UPDATE;

// Fill out rest of fields...

sockHnd.Send((const char*)&packet, sizeof(GamePacket));


This way the client/server can decipher the data sent to them.
Last edited on
Yeah because I am using TCP their is no need to use a packet structure like that. Have you ever heard of runescape? they use TCP. I understand UDP is better for online gaming because TCP stacks up their buffer I believe but you can disable that by setting the TCP_NO_DELAY flag. Their are also many other reasons not to use TCP but I can't remember them. But their is going to be no way to control the player with the arrow keys you will click on the map where you want to walk to ect. So as long as all the clients receive the frame within a few milliseconds of each other it will give the illusion that everything is happening at the same time. And of course I would also need full player updates which would be sent when a user first logs in so they know the players real position before receiving walk requests ect. But could you tell me if I am on the right track with the global and local streams? local for just your client global for all clients

So I am thinking of doing something like this

The frame for walk updates would be like this:

Server-> (UByte) Player Walk Update
Server-> (short) PLAYER ID
Server-> (short) X
Server-> (short) Y


The client would then know how to update the player is this the right approach?
Last edited on
Then someone who clicks a button on the client such as the Logout button it could send something like this

Client-> (UByte) Interface Invoked
Client-> (UByte) Interface Object

Then the server could respond with

Server-> (UByte) Logout


The reason I wouldn't let the client just end the connection is in case they are in battle or something I wouldn't want them to just quit in the middle of a fight and if they close the connection manually by closing the client then the server would keep their player active while still in combat
Last edited on
Hey this is a bit off topic but could you tell me where you learned to code for servers, I'm looking into making a server based game. Thanks
Their isn't one tutorial that can tell you everything you will need to know, I learnt about winsock years ago in visual basic. You can find an example of a c++ client / server by searching for c++ tcp client, tcp server. Getting a connection isn't the hard part the hard part is structuring your server and client properly that's why I made this post here to get other peoples opinions on using a global stream and a local stream.


Anyway the first thing you need to do is start up winsock using the WSAStartup function, you have to pass the version of winsock and the address of a WSADATA structure.

Then you need to setup the socket it will look a bit like this

1
2
3
/* Socket descriptor */
 int sock;
sock = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);


so for the server you have to make a sockaddr_in structure then bind it to your socket descriptor which is in the code above its simply an int which contains a value unique to your socket.

Your sockaddr_in structure contains things like the port and ip

So once you are binded you then need to listen you can do this like this

listen(sock, 5);

Then you use the accept function and as long as you haven't set the non blocking flag it won't execute any code after it until someone is connected. But that's just for the thread the accept function was called in of course.

Once someone connects the accept function will return a socket descriptor. You can then send data to it using the send function and receive data using the recv function.


And its a similar thing for the client except you don't need to bind it or listen and you use the connect function.

This is just for TCP though, UDP is a bit of a different setup and its connectionless so you send data using the sendto function.
Last edited on
+1 for raknet, -1 for fredbill.
Winsock code is not directly recompilable.
Winsock misses a lot of "standard" sockets functionalities you may need, requiring the Wsa prefix and different parameters.
For this reason you should use RakNet.
The interface is clean (and IS directly recompilable, having a unified interface), handles threading automatically and makes sure over UDP that every packet is intact.
It also provides 32 reliability layers.

In my game I've ended up with 8 being used (probably more soon).

Wonder why lowbudget projects use RakNet:
VCMP, SAMP... Even some commercial games use it.

Also don't directly send a packet like that.
You're gonna lose when you try to get your game on a different platform.

You should encode all variables native-to-lowendian prior sending client-to-server, server converts lowendian-to-native, handles, reconverts native-to-lowendian, and client converts back lowendian to native.

But raknet takes care of it, if you use RakNet::RakPacket.
Last edited on
closed account (3hM2Nwbp)
http://www.think-async.com that is if you want scalability. I prefer asio to all of the other APIs that I've used. Try it for yourself to see if it *clicks* with you as well.
closed account (N36fSL3A)
EssGeEich wrote:
Winsock code is not directly recompilable.
I said "pretty much recompile".

You should encode all variables native-to-lowendian prior sending client-to-server, server converts lowendian-to-native, handles, reconverts native-to-lowendian, and client converts back lowendian to native.
Well it was an elementary example.

makes sure over UDP that every packet is intact.
UDP makes sure that every packet is received or not received. Your data will never be corrupted if the other side gets it.

danielmccarthy wrote:
Yeah because I am using TCP their is no need to use a packet structure like that.
You have no guarantee in which order your packets will arrive. The server has no idea of what is what.

The way you're showing is just emulating a data structure. You should send everything in one go.

Have you ever heard of runescape? they use TCP.
Well Runescape doesn't exactly require fast connections, lol.

Last edited on
I know I may look rude by posting again, but please excute me, and don't use TCP.
TCP games are those kind of games who don't allow packet loss, but almost any game must allow (to some point) some packets to be lost.

@Fredbill: I meant there will be no problems about packets being edited by some kind of "virus" or "hacker".

Also RakNet as I said allows multiple reliability channels, allowing some kind of packets to be lost, some to arrive in correct order, some to arrive unordered.

For TCP tho, it's a byte-to-byte copy, and you are guaranteed they WILL arrive and they WILL arrive in order, otherwise it will count as a connection lost (aka timeout).
Last edited on
@Fredbill - So you're telling me Winsock is just as easily portable as Raknet? Yeah, can't say I agree with you there.

No, I did not say you can only get errors with Winsock, I said it was more error prone. Raknet is easier to use, and unless you're pretty good with Winsock and have enough time, probably faster. Of course you can have problems with Raknet, just not as frequently as Winsock.

Honestly in my opinion using Winsock for game networking is like deciding you want to use Windows MFC for simple game graphics. It's really just a silly decision. Sure, it's possible, but it won't benefit you as near as much as learning what people are actually using for games, no matter how simple the game may be.

@OP Unless you're not sharing much data, TCP is a bad idea. True, TCP will make sure you don't lose information on the way, but that comes at a cost of speed. If you really don't need to worry about that speed loss, then TCP is fine(if not better in that case), Raknet supports TCP also >:)

closed account (N36fSL3A)
unless you're pretty good with Winsock and have enough time, probably faster
I do not understand how Raknet is faster than the library it wraps.

Honestly in my opinion using Winsock for game networking is like deciding you want to use Windows MFC for simple game graphics. It's really just a silly decision. Sure, it's possible, but it won't benefit you as near as much as learning what people are actually using for games, no matter how simple the game may be.
Well WinSock is actually useful in the real world to a large extent.
I do not understand how Raknet is faster than the library it wraps.

I assume the intended meaning of "faster" was "faster development time".
Pages: 12