/* Example 2: Packet Identification. In this example I will expain the basics of packet identification. For a more basic introduction please refer to the tutorial first. (Recommended) */ // Do the usual things here. #include #include using namespace irr; #pragma comment(lib, "irrNetLite.lib") #pragma comment(lib, "ws2_32.lib") // Now, in this example, I will teach you the basics of packet // identification. What is packet identification? For instance, // lets say the client sends 3 floating point values to the server, // how will the server now if these represent a position, a rotation, // or something completely different? How will it even know these // are floating point values and not integers, or maybe a 12-byte // string? Obviously, we need some kind of mechanism to define this. // So in this example, we are going to define a few basic packet // types, that let us differentiate between the different kinds of // information coming into our client/server. // I will start off by creating an enum that holds a simple id // for each of our packet types. (Note, you don't have to // explicitly set the values like I do, I am just doing that // for clarity.) enum E_PACKET_TYPE { EPT_ROTATION = 1, EPT_POWER = 2, EPT_MESSAGE = 3 }; // In this example we will create 2 different callback classes, one // for the clients and one for the server. We will have an imaginary // cannon on the server, and the clients can send messages to change // it's angle and power. // The server callback. class ServerNetCallback : public net::INetCallback { // We will store a pointer to the net manager. net::INetManager* netManager; // Here we will store the cannon's power and angle. // Remember that f32 is just a typedef for float. f32 cannonPower; f32 cannonAngle; public: ServerNetCallback(net::INetManager* netManagerIn) : netManager(netManagerIn) {} // Our handlePacket function. virtual void handlePacket(net::SInPacket& packet) { // The packets will use a single char to store // the packet identifier, remember to use the // smallest possible datatype for storing your // packet identifiers. c8 is a typedef for char. c8 packetid; packet >> packetid; // Here we will switch based on the packet id. switch((E_PACKET_TYPE)packetid) { case EPT_ROTATION: // We obtain the cannon angle from the packet. packet >> cannonAngle; std::cout << "The cannon angle is now " << cannonAngle << std::endl; break; case EPT_POWER: // Same here. packet >> cannonPower; std::cout << "The cannon power is now " << cannonPower << std::endl; break; case EPT_MESSAGE: // Now this is a special case (Pun intended, or is it even a pun?... nevermind), // The client has sent us a message as a string, so we will just print that // to the console. core::stringc message; packet >> message; // We can obtain a unique "player id" from a packet identifying the client that // sent it. More details about the player id in a later example, for now we will // just print it out with the message so we know whos saying what. std::cout << "Client " << packet.getPlayerId() << " says: " << message.c_str(); std::cout << std::endl; break; } // After handling a packet, we will send an updated status of the cannon to all clients. net::SOutPacket rotationPacket; // The packet id is the first thing that goes in a packet. Note that I am casting it to a char, // because that is what we want to store it as, to save space. Be careful to use the same types // when sending and receiving, don't send as a char and receive as an int, it will cause trouble. rotationPacket << (c8)EPT_ROTATION; rotationPacket << cannonAngle; // Send the packet to all connected clients. netManager->sendOutPacket(rotationPacket); // Send a power update too. net::SOutPacket powerPacket; powerPacket << (c8)EPT_POWER; powerPacket << cannonPower; netManager->sendOutPacket(powerPacket); } }; // The client callback. class ClientNetCallback : public net::INetCallback { public: // Our handlePacket function. virtual void handlePacket(net::SInPacket& packet) { // Just like the server, we obtain the packet id and print // the information based on the packet we received. I hope the // rest of this function is self-explanatory. c8 packetid; packet >> packetid; switch((E_PACKET_TYPE)packetid) { case EPT_ROTATION: f32 cannonAngle; packet >> cannonAngle; std::cout << "Server says that the cannon angle is now " << cannonAngle << std::endl; break; case EPT_POWER: f32 cannonPower; packet >> cannonPower; std::cout << "Server says that the cannon power is now " << cannonPower << std::endl; break; default: // We don't care about any other types, so we catch them here and break. break; } } }; int main() { // Ask the user whether they want to be the server or a client. std::cout << "Client (c) or Server (s)?"; char i; std::cin >> i; // If they typed 's' they are the server else they are the client. if(i == 's') { // Create an irrNetLite server. net::INetManager* netManager = net::createIrrNetServer(0); // Pass in a server specific net callback. ServerNetCallback* serverCallback = new ServerNetCallback(netManager); netManager->setNetCallback(serverCallback); // Here we update like usual, most of the logic is in the callback. while(netManager->getConnectionStatus() != net::EICS_FAILED) netManager->update(1000); // Delete everything. delete netManager; delete serverCallback; } else { // Create a client and pass in the client callback. // You may want to change the ip address to a remote one and experiment // with connecting to a remote host. ClientNetCallback* clientCallback = new ClientNetCallback(); net::INetManager* netManager = net::createIrrNetClient(clientCallback, "127.0.0.1"); // Print a simple menu. std::cout << "Example 1. What would you like to do?" << std::endl << "1. Change the cannon rotation." << std::endl << "2. Change the cannon power." << std::endl << "3. Send a message." << std::endl; // Take the input. char i = 0; std::cin >> i; // Switch based on input. switch(i) { case '1': { // Here we create a rotation packet and send it to the server. net::SOutPacket rotationPacket; rotationPacket << (c8)EPT_ROTATION; // Remember to cast to the correct type. // Ask for the rotation. f32 rotation; std::cout << "Please enter a rotation: "; std::cin >> rotation; rotationPacket << rotation; netManager->sendOutPacket(rotationPacket); break; } case '2': { // And here we create a power packet and send it to the server. net::SOutPacket powerPacket; powerPacket << (c8)EPT_POWER; // Remember to cast to the correct type. // Ask for the power. f32 power; std::cout << "Please enter the power: "; std::cin >> power; powerPacket << power; netManager->sendOutPacket(powerPacket); break; } case '3': { // We'll also send the server a message in the form of a string. net::SOutPacket messagePacket; messagePacket << (c8)EPT_MESSAGE; // Remember to cast to the correct type. // Ask for the message. char message[512] = {}; std::cout << "Please enter a message:" << std::endl; std::cin >> message; messagePacket << message; netManager->sendOutPacket(messagePacket); break; } default: break; } // Here is the update loop, we will exit if there is a connection problem. while(netManager->getConnectionStatus() != net::EICS_FAILED) { // Here we update. netManager->update(1000); } // Clean up. delete netManager; delete clientCallback; } // And we're done, return 0 and make like a banana. return 0; }