It really happened..

Started by
8 comments, last by hplus0603 16 years, 10 months ago
Well, i never thoud i will write that - but Yes that's true, my project stopped developing because of a single problem we have ignored at the verry beggining... Now, where is the issue ? Let me tell you the story.. We have coded a 'small' multiplayer game, that used to work v.good basing on tiles. You could move your unit to a X1,Z1 position by clicking there on map and server did the pathfinding, and sent you every 250ms your actual position and next step position. That was quite good, with a few minor problems like non precise walking caused by integer based destination - anyway it was ok. Small packets created with: char PACKET[30]; PACKET[0]=START_DATA; PACKET[1]=actualx; PACKET[2]=actualz; send(...); Now, we handle up to 95 gamers.. the map we used to play on, was around 50x50 - it is definetly too small now, and we cannot extend it because of ... packet limitations.. If we could somehow glScalef(3,3,3) all models in the map, and use 'bigger' integers in our packets, we could make our world much bigger, and force the models to move precisely... Guys, a question to the masters of orion.. how /if possible/ to modify the code without rewriting whole application ? I need to send bigger integer values like 300,400,500 to the clients .. And just FYI - i even made a small change, but during implementation my parsing failed on: struct packet_pos{ int x; int z; int id; }; send(...,&packet_pos) but on the receiver, i couldn't go through the encapsulated packets and i always loose the 2nd,3rd... that belongs to one big packet. As i did that before by: l=recv(..) for(a=0;a<=l;a++) if(received[a]==NETWORK_START_PACKET){ ... } i'm not sure, how to parse structures encapsulated...
Advertisement
Forget your packet being an array of chars - it's just a block of memory.

You can define it as a structure to have whatever values you like in it. You can even overlay different structures on it - Just make sure the first byte is a type code that tells you which one it is, then just cast a pointer of the appropriate structure over the array when you want to access the values.

Also, you might/should need to look at using htons() and it's ilk in case the data comes through munged into the wrong endian format. Your own data in that structure will survive, but anything you read from the socket control data will need passing through them.

Unless I've missed your point. tbh I can't imagine anyone capable of actually writing a multiplayer networked game struggling with how to pass the data around using a struct, so I probably have
------------------------------Great Little War Game
RubiconMobile - here is how do i started to implement the new code:


I code the packets as structures, each having
unsigned short packettype at the beggining
and each having own types of data inside.


[SERVER/CLIENT]

struct PACKET_playerpdu{
unsigned short packettype;
int x;
int z;
int health;
};


union {
some_other_packets_like_weather;
...
struct PACKET_playerpdu packet_playerpdu;
}PACKET_received;





Now, when it comes up to server to send login and then immediately player inventory - server does:


struct PACKET_login packetlogin;

packetlogin.packettype=NETPACKET_LOGIN;
packetlogin.message=LOGIN_OK;

send(socket, (char const *)&packetlogin, sizeof(packetlogin), 0);

struct PACKET_inventory packetinv;

packetinv.packettype=NETPACKET_INVENTORY;
packetinv.item=ITEM_POISION;

send(socket, (char const *)&packetinv, sizeof(packetinv), 0);



Data is being sent to the receiving client, and here comes the problem.
The receiving end (client) does that in separate thread:

while(1){
len=recv(sock,(char *)&PACKET_received,sizeof(PACKET_received),0);
if(len){
switch(PACKET_received.somepacket.code){

case NETPACKET_LOGIN:
PERFORM_LOGIN(...);
break;

case NETPACKET_INVENTORY;
INVENTORY_add(...);
break;
}
}
}



Now, everytime after login - the data is being encapsulated into one packet - data i mean: inventorypacket + loginpacket, and then sent to the client.
The problem is that i cannot think of any way to receive all of the packets, to parse somehow the PACKET_received structure after recv(...) is done..
http://gpwiki.org/index.php/Binary_Packet

this way has worked for me. There's a C++ packet at the bottom which ideally should do what you want.
Sure, but this is a C++ code - whole game - server/client is in pure C..

still using structure sending, i get my packets 'merged' - and i cannot parse the received data as i did using char packets.

so when i send login_ok and in the same time your_position

receiving end sees login_ok but no your_position - it is in the recv but is not at the switch(...)..

it kills me.

[Edited by - xcelldev on May 20, 2007 10:28:54 AM]
Eternal Lands network protocol quote:

void move_to (short int x, short int y)
{
Uint8 str[5];

str[0]= MOVE_TO;
*((short *)(str+1))= SDL_SwapLE16 (x);
*((short *)(str+3))= SDL_SwapLE16 (y);
my_tcp_send(my_socket, str, 5);
}


they do the same thing, a simple buffer and data data inside.


how big values they can send with this method?
Quote:Original post by xcelldev
Eternal Lands network protocol quote:

void move_to (short int x, short int y)
{
Uint8 str[5];

str[0]= MOVE_TO;
*((short *)(str+1))= SDL_SwapLE16 (x);
*((short *)(str+3))= SDL_SwapLE16 (y);
my_tcp_send(my_socket, str, 5);
}


they do the same thing, a simple buffer and data data inside.


how big values they can send with this method?
There's no limit. If you use shorts (2 bytes), then that's -32768 to +32767. If you use ints, then you get a range of -2^31 to 2^31, __int64's gives you -2^63 to 2^63 and you can implement your own number classes to handle larger than 64-bvit values if you really want.
xcelldev, I think the problem is that your read buffer contains variable-sized messages, but you're treating the buffer as essentially an array of fixed-size structs (in this case your PACKET_received union). If an incoming message is shorter than sizeof(PACKET_received), the beginning of the subsequent message gets put in the buffer in the call to recv() (and is then lost), so at the next call to recv() you're reading the latter portion of the subsequent message and your case statement fails, etc.

For example:

Say that sizeof(PACKET_A) == 16 bytes and sizeof(PACKET_B) == 32 bytes, and PACKET_received is a union of PACKET_A and PACKET_B, so sizeof(PACKET_received) == 32 bytes.

1) Client sends a PACKET_A followed by a PACKET_B.
2) Server reads sizeof(PACKET_received) (32) bytes in call to recv().
3) The buffer now contains a PACKET_A (16 bytes) and the first 16 bytes of the PACKET_B.
4) PACKET_A is processed successfully.
5) Server tries to read sizeof(PACKET_received) (32) bytes in next call to recv(), but only the last 16 bytes of the PACKET_B are returned, since that's all that's in the socket buffer (this assumes the socket is properly configured to not wait for all requested data).
6) The buffer now contains the last 16 bytes of the PACKET_B, and the processing will fail since the code thinks it's an invalid message.

I'm pretty sure this is your problem, at least as you describe it above. Your code needs to account for the fact that your incoming PACKET_* structs are of variable size, and you need to advance through the read buffer accordingly, not in fixed-size steps.

Also, be aware of struct member alignment/padding issues when mixing structs with network buffers.
Hacked, quick-and-dirty solution:

PACKET[1]=Xa;
PACKET[2]=Xb;
PACKET[3]=Ya;
PACKET[4]=Yb;

Where Xa=x div 256; Xb=x mod 256;
Reconstruction: x=Xa*256+Xb;

Remember, "mod" is expensive.
-----------------------------------"After you finish the first 90% of a project, you have to finish the other 90%." - Michael Abrashstickman.hu <=my game (please tell me your opinion about it)
Quote:Original post by Gagyi
Remember, "mod" is expensive.


Not nearly as expensive as sending a byte over the network. As long as you're doing this math on packing/unpacking packets, you don't need to worry; the possible throughput on the network is much less than the possible throughput on the CPU.
enum Bool { True, False, FileNotFound };

This topic is closed to new replies.

Advertisement