RPG server - client architecture

Started by
3 comments, last by MichaelCrook 12 years ago
hi,
i've been making a client - server architecture and id like to know if the design is ok biggrin.png

There are 2 structs that contain the player information



struct ClientPacket
{

Player p;

};

struct ServerPacket
{

Player players[5];

};


The array Player players[5] contains the information of all the players

The server is basicly this :




class ServerAppFramework
{
public:
ServerAppFramework();
~ServerAppFramework();
bool IsConnected(){return m_bIsConnected;} // returns connection status
void StartListenClient(); // Listen to client
int SendMessagePort( SocketPort ); // Send message to all clients.
int RecClient(SOCKET sRecSocket); // receive message for a particulat socket
private:
bool m_bIsConnected; // true - connected false - not connected
int m_iServerPort;
list<SOCKET> m_vClientList; // All socket connected to client
SOCKET m_SClient;
SOCKET m_SListenClient; // socket listening for client calls
};



Implementation of SendMessagePort( SocketPort ) :
This sends the packet to all clients



int ServerAppFramework::SendMessagePort( SocketPort sp)
{
int iStat = 0,i,j,k;
list<SOCKET>::iterator itl;

if(m_vClientList.size() == 0)
return 0;

for(i=0;i<5;i++)
{
spacket.players.id = player_server_data.id;
spacket.players.x = player_server_data.x;
spacket.players.y = player_server_data.y;
spacket.players.plane = player_server_data.plane;
spacket.players.xvel = player_server_data.xvel;
spacket.players.yvel = player_server_data.yvel;
spacket.players.curhp = player_server_data.curhp;
spacket.players.hp = player_server_data.hp;
spacket.players.level = player_server_data.level;
spacket.players.exp = player_server_data.exp;
spacket.players.stunned = player_server_data.stunned;
spacket.players.slowed = player_server_data.slowed;

}

for(itl = m_vClientList.begin();itl != m_vClientList.end();itl++)
{
iStat = send(*itl,(char const *)&spacket, sizeof(spacket),0);
if(iStat == -1)
m_vClientList.remove(*itl);
}

if(iStat == -1)
return 1;

return 0;

}




The RecClient() functions recieves packets from the client and modifies the Player players[5].
The new Player players[5] is therefor sent to all clients for update.




int AppFramework::RecMessagePort()
{

char com=' ';
int iStat = 0, exit = 0, i;

//iStat = recv(conn,acRetData,4096,0);
iStat = recv(conn,(char *)&spacket,sizeof(spacket),0);
if(iStat == -1)
return 1;

if ( spacket.update == -1 )
{
for(i=0;i<5;i++)
{
players.id = spacket.players.id;
players.x = spacket.players.x;
players.y = spacket.players.y;
players.plane = spacket.players.plane;
players.xvel = spacket.players.xvel;
players.yvel = spacket.players.yvel;
players.curhp = spacket.players.curhp;
players.hp = spacket.players.hp;
players.level = spacket.players.level;
players.exp = spacket.players.exp;
players.stunned = spacket.players.stunned;
players.slowed = spacket.players.slowed;
}


return 0;
}



I'd like to know if the methods im using to send and recieve packets are ok for a game
Advertisement
Instead of the network class constructing the packet and reasing it, it might be better if the player class had methods like OutOfPacket and BuildPacket which take in/output the packet buffer thing and its size.

o3o

It appears you are using TCP. TCP guarantees delivery* of each byte it accepts, in order, as a stream. It has no concept of packets or packet boundaries.

It does not guarantee that it will send all the bytes you request. That is, if you try to send() 100 bytes, TCP might accept 50 of them now. It is up to you to remember the other 50 bytes you want to send, or loop until they've all been sent (the latter will potentially block you much more than the former, though blocking in general is still possible either way).

Similarly, when you recv() data, there are no guarantees that it will arrive in the same sized "chunks" you sent it. For example, if you were able to successfully send() the lowercase alphabet in 2 letter couples (with the caveat above), the receiver might receive them all in one big group, or in several bunches at odd boundaries, or each letter in a "packet" of its own.

The only guarantee you have is that the data will eventually arrive*, in order.

If you want a packet based protocol, you must build it on top of TCP, perhaps using one or more of the following common building blocks:

  • "Packets end with the byte sequence \r\n"

    • Suitable for simple text protocols

  • "Every N bytes is a packet".

    • This is if all your packets have some upper fixed length.

  • "A packet starts with a three byte, big endian length field. The packet ends when this number of additional bytes have been consumed from the stream".

    • This allows variable length data.

  • "A packet starts with a two byte identifier. Packet length is inferred by the value of this identifier"

    • In the last schema, each number might correspong with a packet identifier - byte 0 might represent a "JOIN" message, byte 1 might represent an INPUT message, and so on.

      • "If the first byte is 0, then the packet is 10 additional bytes long."
      • "If the first byte is 1, then the packet is 30 bytes long"
      • (etc).




  • Essentially, you need to be prepared to buffer all the data you cannot immediately send. Before sending new data, you should try send as much of the old data as possible. If you cannot clear the backlog, buffer the new data too. If the buffer gets big for an extended period of time, the client might be too slow to catch up and might need to be dropped.

    You should also buffer all received data, until you can determine a full "packet" (as you have defined above). Remove the packet from the buffer, and process it (remember, you might have the start of the next packet in the buffer too).

    [size=2]* Well, it guarantees delivery or an error code. What I really mean is it won't forget about any data you give it.

    [color=#282828][font=helvetica, arial, verdana, tahoma, sans-serif]

    If you want a packet based protocol, you must build it on top of TCP, perhaps using one or more of the following common building blocks

    [/font]
    [/quote]

    I don't understand the advantages of using a [color=#282828][font=helvetica, arial, verdana, tahoma, sans-serif]

    packet

    [/font][color=#282828][font=helvetica, arial, verdana, tahoma, sans-serif]

    based

    [/font][color=#282828][font=helvetica, arial, verdana, tahoma, sans-serif]

    protocol. The system i'm using isn't giving problems with the sending or recieving of data.

    [/font]

    Other than that is the following method :

    [color=#000088]for[color=#666600]([color=#000000]itl [color=#666600]=[color=#000000] m_vClientList[color=#666600].[color=#000088]begin[color=#666600]();[color=#000000]itl [color=#666600]!=[color=#000000] m_vClientList[color=#666600].[color=#000088]end[color=#666600]();[color=#000000]itl[color=#666600]++)
    [color=#666600]{
    [color=#000000]iStat [color=#666600]=[color=#000000] send[color=#666600](*[color=#000000]itl[color=#666600],([color=#000088]char[color=#000000] [color=#000088]const[color=#000000] [color=#666600]*)&[color=#000000]spacket[color=#666600],[color=#000000] [color=#000088]sizeof[color=#666600]([color=#000000]spacket[color=#666600]),[color=#006666]0[color=#666600]);
    [color=#000088]if[color=#666600]([color=#000000]iStat [color=#666600]==[color=#000000] [color=#666600]-[color=#006666]1[color=#666600])
    [color=#000000]m_vClientList[color=#666600].[color=#000000]remove[color=#666600](*[color=#000000]itl[color=#666600]);
    [color=#666600]}

    stable for sending the data to all the clients ?

    [color=#282828][font=helvetica, arial, verdana, tahoma, sans-serif]

    Quote

    [/font]
    [color=#282828][font=helvetica, arial, verdana, tahoma, sans-serif]



    If you want a packet based protocol, you must build it on top of TCP, perhaps using one or more of the following common building blocks

    [/font]

    [color=#282828][font=helvetica, arial, verdana, tahoma, sans-serif]

    I don't understand the advantages of using a packet based protocol.. t

    [/font][color=#282828][font=helvetica, arial, verdana, tahoma, sans-serif]

    he system i'm using isn't giving problems with the sending or recieving of data.

    [/font]


    [color=#282828][font=helvetica, arial, verdana, tahoma, sans-serif]

    Other than that is the following method :

    [/font]

    [color=#000088][font=helvetica, arial, verdana, tahoma, sans-serif]

    for

    [/font][color=#666600][font=helvetica, arial, verdana, tahoma, sans-serif]

    (

    [/font][color=#000000][font=helvetica, arial, verdana, tahoma, sans-serif]

    itl

    [/font][color=#666600][font=helvetica, arial, verdana, tahoma, sans-serif]

    =

    [/font][color=#000000][font=helvetica, arial, verdana, tahoma, sans-serif]

    m_vClientList

    [/font][color=#666600][font=helvetica, arial, verdana, tahoma, sans-serif]

    .

    [/font][color=#000088][font=helvetica, arial, verdana, tahoma, sans-serif]

    begin

    [/font][color=#666600][font=helvetica, arial, verdana, tahoma, sans-serif]

    ();

    [/font][color=#000000][font=helvetica, arial, verdana, tahoma, sans-serif]

    itl

    [/font][color=#666600][font=helvetica, arial, verdana, tahoma, sans-serif]

    !=

    [/font][color=#000000][font=helvetica, arial, verdana, tahoma, sans-serif]

    m_vClientList

    [/font][color=#666600][font=helvetica, arial, verdana, tahoma, sans-serif]

    .

    [/font][color=#000088][font=helvetica, arial, verdana, tahoma, sans-serif]

    end

    [/font][color=#666600][font=helvetica, arial, verdana, tahoma, sans-serif]

    ();

    [/font][color=#000000][font=helvetica, arial, verdana, tahoma, sans-serif]

    itl

    [/font][color=#666600][font=helvetica, arial, verdana, tahoma, sans-serif]

    ++)

    [/font]
    [color=#666600][font=helvetica, arial, verdana, tahoma, sans-serif]

    {

    [/font]
    [color=#000000][font=helvetica, arial, verdana, tahoma, sans-serif]

    iStat

    [/font][color=#666600][font=helvetica, arial, verdana, tahoma, sans-serif]

    =

    [/font][color=#000000][font=helvetica, arial, verdana, tahoma, sans-serif]

    send

    [/font][color=#666600][font=helvetica, arial, verdana, tahoma, sans-serif]

    (*

    [/font][color=#000000][font=helvetica, arial, verdana, tahoma, sans-serif]

    itl

    [/font][color=#666600][font=helvetica, arial, verdana, tahoma, sans-serif]

    ,(

    [/font][color=#000088][font=helvetica, arial, verdana, tahoma, sans-serif]

    char

    [/font][color=#000000][font=helvetica, arial, verdana, tahoma, sans-serif]

    [/font][color=#000088][font=helvetica, arial, verdana, tahoma, sans-serif]

    const

    [/font][color=#000000][font=helvetica, arial, verdana, tahoma, sans-serif]

    [/font][color=#666600][font=helvetica, arial, verdana, tahoma, sans-serif]

    *)&

    [/font][color=#000000][font=helvetica, arial, verdana, tahoma, sans-serif]

    spacket

    [/font][color=#666600][font=helvetica, arial, verdana, tahoma, sans-serif]

    ,

    [/font][color=#000000][font=helvetica, arial, verdana, tahoma, sans-serif]

    [/font][color=#000088][font=helvetica, arial, verdana, tahoma, sans-serif]

    sizeof

    [/font][color=#666600][font=helvetica, arial, verdana, tahoma, sans-serif]

    (

    [/font][color=#000000][font=helvetica, arial, verdana, tahoma, sans-serif]

    spacket

    [/font][color=#666600][font=helvetica, arial, verdana, tahoma, sans-serif]

    ),

    [/font][color=#006666][font=helvetica, arial, verdana, tahoma, sans-serif]

    0

    [/font][color=#666600][font=helvetica, arial, verdana, tahoma, sans-serif]

    );

    [/font]
    [color=#000088][font=helvetica, arial, verdana, tahoma, sans-serif]

    if

    [/font][color=#666600][font=helvetica, arial, verdana, tahoma, sans-serif]

    (

    [/font][color=#000000][font=helvetica, arial, verdana, tahoma, sans-serif]

    iStat

    [/font][color=#666600][font=helvetica, arial, verdana, tahoma, sans-serif]

    ==

    [/font][color=#000000][font=helvetica, arial, verdana, tahoma, sans-serif]

    [/font][color=#666600][font=helvetica, arial, verdana, tahoma, sans-serif]

    -

    [/font][color=#006666][font=helvetica, arial, verdana, tahoma, sans-serif]

    1

    [/font][color=#666600][font=helvetica, arial, verdana, tahoma, sans-serif]

    )

    [/font]
    [color=#000000][font=helvetica, arial, verdana, tahoma, sans-serif]

    m_vClientList

    [/font][color=#666600][font=helvetica, arial, verdana, tahoma, sans-serif]

    .

    [/font][color=#000000][font=helvetica, arial, verdana, tahoma, sans-serif]

    remove

    [/font][color=#666600][font=helvetica, arial, verdana, tahoma, sans-serif]

    (*

    [/font][color=#000000][font=helvetica, arial, verdana, tahoma, sans-serif]

    itl

    [/font][color=#666600][font=helvetica, arial, verdana, tahoma, sans-serif]

    );

    [/font]
    [color=#666600][font=helvetica, arial, verdana, tahoma, sans-serif]

    }

    [/font]

    [color=#282828][font=helvetica, arial, verdana, tahoma, sans-serif]

    stable for sending the data to all the clients ?

    [/font]

    [color=#282828][font=helvetica, arial, verdana, tahoma, sans-serif]

    Quote

    [/font]
    [color=#282828][font=helvetica, arial, verdana, tahoma, sans-serif]



    If you want a packet based protocol, you must build it on top of TCP, perhaps using one or more of the following common building blocks

    [/font]

    [color=#282828][font=helvetica, arial, verdana, tahoma, sans-serif]

    I don't understand the advantages of using a packet based protocol ..

    [/font][color=#282828][font=helvetica, arial, verdana, tahoma, sans-serif]

    the system i'm using isn't giving problems with the sending or recieving of data.

    [/font]


    [color=#282828][font=helvetica, arial, verdana, tahoma, sans-serif]

    Other than that is the following method :

    [/font]


    [color=#000088]for[color=#666600]([color=#000000]itl [color=#666600]=[color=#000000] m_vClientList[color=#666600].[color=#000088]begin[color=#666600]();[color=#000000]itl [color=#666600]!=[color=#000000] m_vClientList[color=#666600].[color=#000088]end[color=#666600]();[color=#000000]itl[color=#666600]++)
    [color=#666600]{
    [color=#000000]iStat [color=#666600]=[color=#000000] send[color=#666600](*[color=#000000]itl[color=#666600],([color=#000088]char[color=#000000] [color=#000088]const[color=#000000] [color=#666600]*)&[color=#000000]spacket[color=#666600],[color=#000000] [color=#000088]sizeof[color=#666600]([color=#000000]spacket[color=#666600]),[color=#006666]0[color=#666600]);
    [color=#000088]if[color=#666600]([color=#000000]iStat [color=#666600]==[color=#000000] [color=#666600]-[color=#006666]1[color=#666600])
    [color=#000000]m_vClientList[color=#666600].[color=#000000]remove[color=#666600](*[color=#000000]itl[color=#666600]);
    [color=#666600]}

    [color=#282828][font=helvetica, arial, verdana, tahoma, sans-serif]

    stable for sending the data to all the clients ?

    [/font]

    my favorite architecture is TCP using this rough idea:
    server sends a start game message/welcome
    player sends server commands (i.e, I want to move to X,Y,Z)
    server reads commands, updates player instance through a component controller, sends confirmation of packet retrieval + updates on any components that have changed since last update sent.
    while the server was doing that, the user starts making a list of new commands (as it waits) if a command that over rides another comes in, it will remove that command and place itself in (i.e. update where I want to be going) this will form a list of commands, which on retrial of the confirmation will be sent.
    as the client was doing this, the server was doing the same, but in updates for components. (edit) you make a player packet instance for each player on the server and the client only has the server it connects to. (/edit)

    In this system, the server does all of the processing (i.e. A*) and the client receives updates on components (i.e. location of an entity) and sends requests... With this system hacking is harder as clients have no direct access to restricted data and the clients system can balance the CPU load more heavily as they are not doing complex calculations, so you could optimize a graphical component, making it look better/require less of the GPU.

    This topic is closed to new replies.

    Advertisement