Adding networking to my game

Started by
10 comments, last by BeerNutts 11 years, 11 months ago
I just started working on my RPG and I want it to be multiplayer. I haven't put too much work into it too far because I want to get the networking down first. Basically it's only sprites moving around on a map with basic collision detection. I downloaded KryoNet and got it so I can send and receive objects and it reacts accordingly to whatever object is passed in from the client to the server.

I literally have no idea how the logic for this works, as this is my first networked project. Any suggestions would help me greatly, I was thinking of going with a single client/server in one and having the user be able to select hosting a game or joining a game that is already made.

Any resources you can show me would help a lot as well.

Thanks.
Advertisement
Did you read through the appropriate parts of the Forum FAQ?
enum Bool { True, False, FileNotFound };
I wasn't aware this forum had a FAQ.
Okay, I'm pretty sure I've got it figured out now. I just need to know what I should be sending to my server? Like say the client-end user presses an arrow key to move in a direction. How should I be sending that to the server? Would I make another class to represent directions or something like that? Or would I just send the whole player class and pass it into some update method for the class? I really have no idea what I'm doing here.

Thanks
Typically, you define a protocol. Part of a protocol is typically a PDU (packet data unit) definition. A typical implementation of a PDU is a C-style "struct," or a defined binary serialization format of some sort. Another part is the surrounding framing -- letting the receiver know what kind of PDU is coming in, and how much data it is, so the PDUs don't run together.

A typical PDU framing header might look like:

byte: type
byte: datalength
byte[]: data


What "data" means depends on the "type" field. For example, you can define a byte with bit values for different keys:


enum KeyType {
KeyLeft = 0x1,
KeyRight = 0x2,
KeyUp = 0x4,
KeyDown = 0x8,
KeyJump = 0x10,
KeyAction = 0x20
};

enum PduType {
PduTypeKeys = 1,
PduTypeChat = 2
};


At this point, you'd send a PDU of type 1, containing a single byte, which is the bitwise OR of the keys that are down. You could also send a PDU of type 2, where the "data" would be the text of a chat message from the client.

The network interface layer then has to translate between whatever the input/actor/entity system uses for data, and the PDU format. Then it has to translate back on the receiving side. This translation mechanism is generally known as "marshaling" and is related to, but not the same thing as, serialization. Note that some serialization mechanisms use 10x or even 100x the amount of storage/bandwidth as a plain marshaling system, so don't just apply some random serialization library that happens to be available.
enum Bool { True, False, FileNotFound };
I still don't really get what I need to do. Like for example I have a method in my client like this.


public void sendSomething(Object obj){
try {
client.start();
client.connect(500, "192.168.2.5", 7187);
client.sendTCP(obj);
client.stop();
} catch (IOException e) {
e.printStackTrace();
}
}


And I have my server set up like this


public void init(){
server = new Server();
server.start();
try {
server.bind(7187);


server.addListener(new Listener(){
public void received (Connection connection, Object object) {
if(object instanceof String){
System.out.println(object);
}
}
});

} catch (IOException e) {
e.printStackTrace();
}
}


That's just for testing purposes, but say I want to like I said, pass in the player and move it accordingly, what would I do? Or am I going about this all wrong? I'm really confused here.
You set up the connection at the beginning, and then you keep it open. You send each new command/message on the same connection. Same thing with receiving, you "drain" the connection each time through your main loop, and decode the messages you receive this way, and hang on to whatever data is left at the end, as it will be the beginning of whatever message comes next.
enum Bool { True, False, FileNotFound };
Okay, I see what you're saying there. But I still don't understand how I can tell the server to move the player. Blah, networking is really confusing.
Firstly, note that I don't use C++. I use Python, so some terminology will differ, and you may need to add intermediary steps to what I write.

Most of the game events will work along these lines:

EVENT -> CONDITION -> EXECUTION (-> FEEDBACK)

whereby in your case, an event would be a keypress, a condition verifies if the event is valid and execution is movement of the player.
Many multiplayer games will transmit only the client events (Keyboard inputs, Mouse position, Mouse inputs). Then, the server acts accordingly.
Simply establish first a protocol which enables you to identify a packet as an event packet, connection request packet, disconnect request packet and so on.
In python, I use a list (A form of an array) with the first index as an integer that defines the packet type. The server has a list of callbacks which are executed from a dictionary (list and dictionaries are differing forms of an array, lists only have integer indexes, and both are single dimensional) e.g 0 -> DoMovement()
You want to isolate the processing of events from the feedback; the server can update the client of the world changes at a slower rate than it needs to sample inputs.

Okay, I see what you're saying there. But I still don't understand how I can tell the server to move the player. Blah, networking is really confusing.


In pseudo code, if you want to move a player (only passing minimal info here), you would do something like this:

// defines the packet type being send
enum etPacketType
{
MOVE_PLAYER_TYPE,
BROADCAST_CHAT_TYPE
};

// packets defined in client and server:
struct tPacketHeader
{

etPacketType PacketType;
uint8_t DataLength;
}

struct tPlayerMoveData
{
uint32_t PlayerIdToMove;
double PositionX;
double PositionY;
double VelocityX;
double VelocityY;
}

struct tMovePacket
{
tPacketHeader PacketHeader;
tPlayerMoveData PlayerMoveData;

}

// Client code, where the MovePacket has been filled out
void MovePlayer(tMovePacket &PlayerToMove)
{
// Send this data to server, tell it to move this player
DataSend(ServerSocket, &PlayerToMove, sizeof(tMovePacket));
}

// in Server code, assuming you get notified when data comes in
void ReadData()
{
tPacketHeader PacketHeader;
tPlayerMoveData PlayerMoveData;

// Read the PacketHeader only 1st
RecvData(ClientSocket, &PacketHeader, sizeof(tPacketHeader);

switch (PacketHeader.PacketType) {
case MOVE_PLAYER_TYPE:
// Read the rest of the data into PlayerMoveData
RecvData(ClientSocket, &PlayerMoveData, sizeof(tPlayerMoveData);
// Now handle moving the Player given by playerId, located at the given positions,
// and mvoe it the given velocities
HandlePlayerMove(PlayerMoveData);

// handle others below
default:
break;


This is just a general idea of what you could do. There are tons of ways to do this, like just sending the key presses, and have the server move the player based on the keys it gets.

My Gamedev Journal: 2D Game Making, the Easy Way

---(Old Blog, still has good info): 2dGameMaking
-----
"No one ever posts on that message board; it's too crowded." - Yoga Berra (sorta)

This topic is closed to new replies.

Advertisement