My 2D Top Down Online RPG

Discussions Regarding Coding (C/C++/Objective-C Mainly)

My 2D Top Down Online RPG

Postby Fouf » Tue Nov 08, 2011 5:49 pm

Hey, I'm going to throw this thread up to ask any things I'd like input on... such as a way of doing a certain thing.

Firstly, at the moment a client connects to the server, they can either send a packet to create an account, or login, should all the packets in ym game be encrypted, or would it be simpler to just encrypt the password before it sent and decrypted on the server side.

Also, at the moment this is how I handle my packets, I use SFML for the window manager, audio/input, and network modules. Here is a simple packet that is in my game.. [Just holds the type, for things such as the heartbeat, and what not.]
Code: Select all
struct dataPacket
{
   dataPacket(sf::Int8 type)
   {
      this->packetType = type;
   }
   void loadPacket(sf::Packet *packet)
   {
      packet->Clear();
      packet->Append(this, sizeof(*this));
   }
   sf::Int8 packetType;
};


so I pass it an address of a sf::Packet and it appends itself to it, [append just add it to the char* of data the packet has, which can be later be sent to the server/client.
If a struct has many members, such as two ints, will they always be appended in the same way? I've looked into this and I inclined to say it happens in the way the variables are declared. Heres a disconnect packet.
Code: Select all
struct disconnectPacket
{
   disconnectPacket(sf::Int32 playerId)
   {
      this->packetType = DISCONNECT;
      this->playerId = playerId;
   }
   void loadPacket(sf::Packet *packet)
   {
      packet->Clear();
      packet->Append(this, sizeof(*this));
   }
   sf::Int8 packetType;
   sf::Int32 playerId;
   sf::Int8 errorType;
};


Is this a good way to go about things? So each different packet would have a different struct?
Any input appreciated.
Thanks, Fouf.
Fouf
Fouf
I Have a Question
 
Posts: 6
Joined: Sat Oct 29, 2011 1:16 pm

Re: My 2D Top Down Online RPG

Postby L. Spiro » Wed Nov 09, 2011 2:43 am

#1: Should I encrypt only passwords etc.?
-> The encryption process is actually very involved. Briefly, you should encrypt everything. I will post a more detailed article when I get home.
One thing to mention is that all packet data should also be compressed, and if you make your own compression routine then this acts as a second layer of encryption.

#2: Will structures always be appended the same way?
-> If you are asking whether all of the bytes in a structure will be aligned exactly the same way (and in the same order) once appended to a stream of bytes, aligned or not, the answer is Yes.

#3: Should each packet have a different structure?
-> It depends on your needs. If you are making a small game and feel that it is fairly easy to define a finite number of structures to handle your packeting needs, you could make one structure for one packet.
However it is not really recommended, at least not to be implemented in that way.
An example of how Torque Engine works is that when the player class wants to send its full update to the server, we manually add the integers and floats that we want to send (just appending them to the packet buffer), and the server manually reads them out in the same order.
Effectively the result would be the same as if you had defined a structure for the player class to send, as far as how the data ends up looking in RAM.
However structures have a few downsides. If you want to conditionally send or omit data, you would need a new structure to hold the new layout of the packet as it appears in RAM. Plus making sure that all the flags that indicate what data is or is not there must appear at the beginning of the packet instead of in the middle, making it much harder to just append data to the stream as you go along.
If you ever decide to expand the types of packets you want to send, you will find it quite a hassle to define new structures for every packet, and by the 500th structure you will be kicking yourself in the ass for going down this road.

Think of it as making a file saver and loader. The saver knows what kind of file it is and know what to put into it, including flags that indicate some data is missing from the file.
The loader knows about all the types of files there could be and for any given file it knows the order of the data in it, and can handle missing data given the flags it encounters while reading the data.

#4: I didn't ask about compression but it is also something worth mentioning. Could you mention it?
-> Well, since you asked.
Do make every effort to reduce the amount of data you are sending over the network. This is where being clever really pays off, and you can't be clever enough.
Here are a few ideas:
* A: Scale data is often uniform. That is X, Y, and Z are often the same. This means you can add one bit indicating they are all the same and then send only one float value instead of 3.
* B: Normals can be reduced to 65 bits from 96, as you may have seen on the blog part of this site.
* C: I had more but I have to go in a few seconds.


L. Spiro


PS: I am always thinking in 3D, but these tricks apply to 2D as well.
It is amazing how often people try to be unique, and yet they are always trying to make others be like them.
- L. Spiro 2011
L. Spiro
Site Admin
 
Posts: 54
Joined: Thu Jul 21, 2011 2:59 pm
Location: Tokyo, Japan

Re: My 2D Top Down Online RPG

Postby Fouf » Wed Nov 09, 2011 4:44 am

Thanks for clearing up some stuff Spiro. I do have a question though, so if I wanted to change away from structs for each packet.. what direction should I take?
At the moment it's like
Code: Select all
packet packet;
disconnectPacket dcpack(arguments);
dcpack.fill(&packet);
send(packet);

disconnect would write itself to the packet buffer.. which is sent..

Also, I'll look more into compression and I await the article of yours!


Thanks,
Fouf
Fouf
I Have a Question
 
Posts: 6
Joined: Sat Oct 29, 2011 1:16 pm

Re: My 2D Top Down Online RPG

Postby L. Spiro » Thu Nov 10, 2011 1:35 am

Too many specialized structures.
For each type of packet, only an enumeration is really necessary to tell what kind of packet it is. After that, treat the data in the packet as a stream.
Append to it as you want.

In this case you have made a structure as a replacement for a function.


For scalability, your packets really must be just a buffer of bytes or bits that can grow as data is added.
The actual act of adding data goes to various locations.

Assuming you have a network manager, some of the packets that are specifically related to networking could be created by the manager itself.
A disconnect packet could be issued when the game decides to call a function on the network manager. No structures involved. A function creates a packet, fills it with the disconnect data (if any), and it is sent.

Rather than creating specialized structures, create specialized functions.

But remember, specializing is rare. Packets needed for standard network communication can be wrapped inside specialized functions, but packets related to players should be quite dynamic.


When it comes to players, one suggestion would be to have your scene manager decide when to update players etc., and ask for their packets by calling a virtual (optionally) function that accepts a packet and adds its data to it.
The benefit of using the scene manager for this is because it allows you to make one big packet that has the data for many players instead of sending a bunch of smaller ones.
Each player appends data to the packet only as necessary. If its position has not changed, don't send positional coordinates.

This is why structures can't really work.


That is a global overview. As for the actual act of specifically adding data to a packet:
Making a packet class is fine (I noticed you are also using structures with constructors and methods; the frequently used standard is to use structures for plain-old-data only and if any constructors or methods are needed use a class).
It can be initialized with its type etc.
Then add methods to it such as appendInt32() etc.
All parts of your engine use these to construct the packet data.
This is an example of a function on the player class that adds its data to a packet that was given to it by the scene manager:

Code: Select all
void playerClass::fillPacket( sf::Packet &packet ) {
    // Set a bunch of flags to tell the receiver what is present in the packet.
    unsigned long packetFlags = 0;

    if ( m_pos != m_lastSentPos ) {
        packetFlags |= POS_BIT_FLAG;
    }
    if ( m_heath != m_lastSentHealth ) {
        packetFlags |= HP_BIT_FLAG;
    }

    // Packet starts with the flags so the receiver knows what to read.
    packet.addUint32( packetFlags );

    if ( m_pos != m_lastSentPos ) {
        packet.addPos( m_pos );
        m_lastSentPos = m_pos;
    }
    if ( m_heath != m_lastSentHealth ) {
        packet.addUint32( m_heath );
        m_lastSentHealth = m_heath;
    }
}



Again, just think of it as creating a file with the goal of keeping the size to its absolute minimum.
Also note that I made a specialized addPos() function. Don't add too many of these. The reason for adding it is because you may have a way to reduce the size of the data if you know that it is positional. Just as with normals. The function would compress the position data for you.
Where compression is not possible, just stick to addUint32() and friends.


L. Spiro
It is amazing how often people try to be unique, and yet they are always trying to make others be like them.
- L. Spiro 2011
L. Spiro
Site Admin
 
Posts: 54
Joined: Thu Jul 21, 2011 2:59 pm
Location: Tokyo, Japan

Re: My 2D Top Down Online RPG

Postby Johnson » Thu Nov 10, 2011 1:50 am

How is the order of data respected?

Is there a strict precedence?
Johnson
I Have a Question
 
Posts: 2
Joined: Sun Oct 16, 2011 11:15 pm

Re: My 2D Top Down Online RPG

Postby L. Spiro » Thu Nov 10, 2011 2:23 am

That reminds me to mention that addUint32() and friends are responsible for sending the values in the correct endian.


The order of the elements is up to you. As the person who designs the structure of the outgoing packet, you are also the person who handles the code that reads the packet.
Again, it is basically like writing to a file and reading from it later.

There are not really any guidelines for the order in which you output the data, just as long as your reader knows the order.


L. Spiro
It is amazing how often people try to be unique, and yet they are always trying to make others be like them.
- L. Spiro 2011
L. Spiro
Site Admin
 
Posts: 54
Joined: Thu Jul 21, 2011 2:59 pm
Location: Tokyo, Japan

Re: My 2D Top Down Online RPG

Postby Fouf » Thu Nov 10, 2011 6:18 am

Thanks for the reply!
Now, let's see if I understand this correctly.
So instead of doing.. structures use functions
Say this is my receive function [on the servers end]:
Code: Select all
void disonnectFill(sf::Packet &packet)
{
   packet->addInt(DISCONNECT); //enumeration of disconnect packet
   packet->addInt(playerId); // players id
   // add this packet to be sent
}
void receive()
{
   switch(packet.type == DISCONNECT)
   {
      // To let everyone know this client disconnect I'll send a disconnect packet to everyone with that players ID.
      sf::Packet dcPacket;
      disconnectFill(dcPacket); // fill
      packetManager->addPacket(dcPacket); // to send
   }
}

// This code is just made up on the spot.. so there's lots of logic errors, just trying to do what you showed ^^
Is that what your trying to get across? Append the type of packet, then whatever data, which is just read on the other side? With a function for each type instead of many structures..?

All your input greatly appreciated,
Fouf
Fouf
I Have a Question
 
Posts: 6
Joined: Sat Oct 29, 2011 1:16 pm

Re: My 2D Top Down Online RPG

Postby L. Spiro » Thu Nov 10, 2011 7:32 am

Basically, although your switch case will need modifying.

Code: Select all
switch ( packet.type ) {
    case DISCONNECT : {
    }
}


And for this to work, you wouldn't set the type of the packet via addInt(). You would simply set the .type property and then add the rest of the data.


L. Spiro
It is amazing how often people try to be unique, and yet they are always trying to make others be like them.
- L. Spiro 2011
L. Spiro
Site Admin
 
Posts: 54
Joined: Thu Jul 21, 2011 2:59 pm
Location: Tokyo, Japan

Re: My 2D Top Down Online RPG

Postby Fouf » Thu Nov 10, 2011 3:01 pm

Ah yeah, of course.. case Disconnect.. I know that, sorry it was 2 AM.. I don't know what I'm typing that late.
Thanks for everything Spiro,
Fouf
Fouf
I Have a Question
 
Posts: 6
Joined: Sat Oct 29, 2011 1:16 pm

Re: My 2D Top Down Online RPG

Postby Fouf » Sat Dec 03, 2011 9:13 pm

I would like to get some input on how I'm planning to manage user information, I'm planning to use XML files, at first I thought multiple xml files, one for each account, but then I was told one would work, and I could have the server just save to this file once every so and so, etc, and that would faster.
so an example,
users.xml
Code: Select all
<account>
    <name>Fouf</name>
    <password>1JFJij2D2</password>
    <character>
          <characterName> 123 </characterName>
          <location ...>
    </character>
   <character>..
   </character>
</account>
<account>
..
</account>


Is xml a bad idea for this? I'm not planning to make it a MMORPG or anything, maybe something people could host servers for some friends, but I might go with hosting some dedicated servers.. I'm kind of unsure atm hehe.
Fouf
Fouf
I Have a Question
 
Posts: 6
Joined: Sat Oct 29, 2011 1:16 pm

Next

Return to Coding

Who is online

Users browsing this forum: No registered users and 0 guests

cron