Player::Player() : pony{Pony(this)} { connected=false; inGame=0; nReceivedDups=0; lastPingNumber=0; lastPingTime=timestampNow(); port=0; IP=QString(); receivedDatas = new QByteArray(); for (int i=0;i<33;i++) udpSequenceNumbers[i]=0; for (int i=0;i<33;i++) udpRecvSequenceNumbers[i]=0; udpRecvMissing.clear(); // Prepare timers chatRollCooldownEnd = QDateTime::currentDateTime(); udpSendReliableTimer = new QTimer; udpSendReliableTimer->setInterval(UDP_RESEND_TIMEOUT); udpSendReliableTimer->setSingleShot(true); connect(udpSendReliableTimer, SIGNAL(timeout()), this, SLOT(udpResendLast())); udpSendReliableGroupTimer = new QTimer; udpSendReliableGroupTimer->setInterval(UDP_GROUPING_TIMEOUT); udpSendReliableGroupTimer->setSingleShot(true); connect(udpSendReliableGroupTimer, SIGNAL(timeout()), this, SLOT(udpDelayedSend())); }
void Widget::checkPingTimeouts() { //win.logMessage("CHECKING PING TIMEOUT :"); for (int i=0;i<udpPlayers.size();i++) { //if (!udpPlayers[i].connected || !udpPlayers[i].port) // continue; int time = (timestampNow()-udpPlayers[i]->lastPingTime); //win.logMessage(QString().setNum(time)+"s"); if (time > pingTimeout) { logMessage("UDP: Ping timeout ("+QString().setNum(((int)timestampNow()-udpPlayers[i]->lastPingTime))+"s) for " +QString().setNum(udpPlayers[i]->pony.netviewId)+" (player "+udpPlayers[i]->name+")"); udpPlayers[i]->connected = false; sendMessage(udpPlayers[i], MsgDisconnect, "Ping timeout"); Player::disconnectPlayerCleanup(udpPlayers[i]); } } }
void Player::resetNetwork() { connected=false; nReceivedDups=0; lastPingNumber=0; lastPingTime=timestampNow(); port=0; IP.clear(); receivedDatas->clear(); for (int i=0;i<33;i++) udpSequenceNumbers[i]=0; for (int i=0;i<33;i++) udpRecvSequenceNumbers[i]=0; udpRecvMissing.clear(); }
void sendMessage(Player* player,quint8 messageType, QByteArray data) { QByteArray msg(3,0); // MessageType msg[0] = messageType; // Sequence msg[1] = 0; msg[2] = 0; if (messageType == MsgPing) { msg.resize(6); // Sequence msg[1] = 0; msg[2] = 0; // Payload size msg[3] = 8; msg[4] = 0; // Ping number player->lastPingNumber++; msg[5]=(quint8)player->lastPingNumber; } else if (messageType == MsgPong) { msg.resize(6); // Payload size msg[3] = 8*5; msg[4] = 0; // Ping number msg[5]=(quint8)player->lastPingNumber; // Timestamp msg += floatToData(timestampNow()); } else if (messageType == MsgUserUnreliable) { msg.resize(5); // Sequence msg[1] = (quint8)(player->udpSequenceNumbers[32]&0xFF); msg[2] = (quint8)((player->udpSequenceNumbers[32]>>8)&0xFF); // Data size msg[3] = (quint8)((8*(data.size()))&0xFF); msg[4] = (quint8)(((8*(data.size())) >> 8)&0xFF); // Data msg += data; player->udpSequenceNumbers[32]++; //app.logMessage(QString("Sending sync data :")+msg.toHex()); }
void Player::reset() { name.clear(); connected=false; inGame=0; nReceivedDups=0; lastPingNumber=0; lastPingTime=timestampNow(); port=0; IP.clear(); receivedDatas->clear(); lastValidReceivedAnimation.clear(); pony = Pony(this); for (int i=0;i<33;i++) udpSequenceNumbers[i]=0; for (int i=0;i<33;i++) udpRecvSequenceNumbers[i]=0; udpRecvMissing.clear(); }
void Sync::sendSyncMessage(Player* source, Player* dest) { QByteArray data(2,0); data[0] = (quint8)(source->pony.netviewId&0xFF); data[1] = (quint8)((source->pony.netviewId>>8)&0xFF); data += floatToData(timestampNow()); //data += rangedSingleToData(source.pony.pos.x, XMIN, XMAX, PosRSSize); //data += rangedSingleToData(source.pony.pos.y, YMIN, YMAX, PosRSSize); //data += rangedSingleToData(source.pony.pos.z, ZMIN, ZMAX, PosRSSize); data += floatToData(source->pony.pos.x); data += floatToData(source->pony.pos.y); data += floatToData(source->pony.pos.z); //data += rangedSingleToData(source.pony.rot.x, ROTMIN, ROTMAX, RotRSSize); data += rangedSingleToData(source->pony.rot.y, ROTMIN, ROTMAX, RotRSSize); //data += rangedSingleToData(source.pony.rot.z, ROTMIN, ROTMAX, RotRSSize); sendMessage(dest, MsgUserUnreliable, data); //win.logMessage("UDP: Syncing "+QString().setNum(source->pony.netviewId)+" to "+QString().setNum(dest->pony.netviewId)); }
// Processes the commands entered directly in the server, not the chat messages void Widget::sendCmdLine() { if (!enableGameServer) { logMessage(QObject::tr("This is not a game server, commands are disabled")); return; } QString str = ui->cmdLine->text(); if (str == "clear") { ui->log->clear(); return; } else if (str == "stop") { delete this; return; } else if (str == "listTcpPlayers") { for (int i=0; i<tcpPlayers.size(); i++) { Player* p = tcpPlayers[i]; logMessage(p->name+" "+p->IP+":"+QString().setNum(p->port)); } return; } else if (str.startsWith("setPeer")) { if (udpPlayers.size() == 1) { cmdPeer = udpPlayers[0]; QString peerName = cmdPeer->IP + " " + QString().setNum(cmdPeer->port); logMessage(QObject::tr("UDP: Peer set to %1").arg(peerName)); return; } str = str.right(str.size()-8); QStringList args = str.split(':'); bool ok; if (args.size() != 2) { if (args.size() != 1) { logMessage(QObject::tr("UDP: setPeer takes a pony id or ip:port combination")); return; } quint16 id = args[0].toUInt(&ok); if (!ok) { logMessage(QObject::tr("UDP: setPeer takes a pony id as argument")); return; } for (int i=0; i<udpPlayers.size();i++) { if (udpPlayers[i]->pony.id == id) { cmdPeer = Player::findPlayer(udpPlayers,udpPlayers[i]->IP, udpPlayers[i]->port); logMessage(QObject::tr("UDP: Peer set to %1").arg(udpPlayers[i]->pony.name)); return; } } logMessage(QObject::tr("UDP: Peer not found (id %1)").arg(args[0])); return; } quint16 port = args[1].toUInt(&ok); if (!ok) { logMessage(QObject::tr("UDP: setPeer takes a port as argument")); return; } cmdPeer = Player::findPlayer(udpPlayers,args[0], port); if (cmdPeer->IP!="") logMessage(QObject::tr("UDP: Peer set to %1").arg(str)); else logMessage(QObject::tr("UDP: Peer not found (%1)").arg(str)); return; } else if (str.startsWith("listPeers")) { if (str.size()<=10) { for (int i=0; i<win.udpPlayers.size();i++) win.logMessage(QString().setNum(win.udpPlayers[i]->pony.id) +" ("+QString().setNum(win.udpPlayers[i]->pony.netviewId)+")" +" "+win.udpPlayers[i]->pony.name +" "+win.udpPlayers[i]->IP +":"+QString().setNum(win.udpPlayers[i]->port) +" "+QString().setNum((int)timestampNow()-win.udpPlayers[i]->lastPingTime)+"s"); return; } str = str.right(str.size()-10); Scene* scene = findScene(str); if (scene->name.isEmpty()) win.logMessage(QObject::tr("Can't find scene")); else for (int i=0; i<scene->players.size();i++) win.logMessage(win.udpPlayers[i]->IP +":"+QString().setNum(win.udpPlayers[i]->port) +" "+QString().setNum((int)timestampNow()-win.udpPlayers[i]->lastPingTime)+"s"); return; } else if (str.startsWith("listVortexes")) { for (int i=0; i<scenes.size(); i++) { win.logMessage("Scene "+scenes[i].name); for (int j=0; j<scenes[i].vortexes.size(); j++) win.logMessage("Vortex "+QString().setNum(scenes[i].vortexes[j].id) +" to "+scenes[i].vortexes[j].destName+" " +QString().setNum(scenes[i].vortexes[j].destPos.x)+" " +QString().setNum(scenes[i].vortexes[j].destPos.y)+" " +QString().setNum(scenes[i].vortexes[j].destPos.z)); } return; } else if (str.startsWith("sync")) { win.logMessage(QObject::tr("UDP: Syncing manually")); sync.doSync(); return; } // DEBUG global commands from now on else if (str==("dbgStressLoad")) { // Send all the players to the GemMines at the same time for (int i=0; i<udpPlayers.size(); i++) sendLoadSceneRPC(udpPlayers[i], "GemMines"); return; } else if (str.startsWith("dbgStressLoad")) { str = str.mid(14); // Send all the players to the given scene at the same time for (int i=0; i<udpPlayers.size(); i++) sendLoadSceneRPC(udpPlayers[i], str); return; } else if (str.startsWith("tele")) { str = str.right(str.size()-5); QStringList args = str.split(' '); if (args.size() != 2) { logStatusMessage(QObject::tr("Error: Usage is tele ponyIdToMove destinationPonyId")); return; } bool ok; bool ok1; bool ok2 = false; quint16 sourceID = args[0].toUInt(&ok); quint16 destID = args[1].toUInt(&ok1); Player* sourcePeer; if (!ok && !ok1) { logStatusMessage(QObject::tr("Error: Usage is tele ponyIdToMove destinationPonyId")); return; } for (int i=0; i<udpPlayers.size();i++) { if (udpPlayers[i]->pony.id == sourceID) { sourcePeer = udpPlayers[i]; ok2 = true; break; } } if (!ok2) { logStatusMessage(QObject::tr("Error: Source peer does not exist!")); return; } for (int i=0; i<udpPlayers.size();i++) { if (udpPlayers[i]->pony.id == destID) { logMessage(QObject::tr("UDP: Teleported %1 to %2").arg(sourcePeer->pony.name,udpPlayers[i]->pony.name)); if (udpPlayers[i]->pony.sceneName.toLower() != sourcePeer->pony.sceneName.toLower()) sendLoadSceneRPC(sourcePeer, udpPlayers[i]->pony.sceneName, udpPlayers[i]->pony.pos); else sendMove(sourcePeer, udpPlayers[i]->pony.pos.x, udpPlayers[i]->pony.pos.y, udpPlayers[i]->pony.pos.z); return; } } logMessage(QObject::tr("Error: Destination peer does not exist!")); } if (cmdPeer->IP=="") { logMessage(QObject::tr("Select a peer first with setPeer/listPeers")); return; } else // Refresh peer info { cmdPeer = Player::findPlayer(udpPlayers,cmdPeer->IP, cmdPeer->port); if (cmdPeer->IP=="") { logMessage(QObject::tr("UDP: Peer not found")); return; } } // User commands from now on (requires setPeer) if (str.startsWith("disconnect")) { logMessage(QObject::tr("UDP: Disconnecting")); sendMessage(cmdPeer,MsgDisconnect, "Connection closed by the server admin"); Player::disconnectPlayerCleanup(cmdPeer); // Save game and remove the player } else if (str.startsWith("load")) { str = str.mid(5); sendLoadSceneRPC(cmdPeer, str); } else if (str.startsWith("getPos")) { logMessage(QObject::tr("Pos : ","Short for position") + QString().setNum(cmdPeer->pony.pos.x) + " " + QString().setNum(cmdPeer->pony.pos.y) + " " + QString().setNum(cmdPeer->pony.pos.z)); } else if (str.startsWith("getRot")) { logMessage(QObject::tr("Rot : x=","Short for rotation") + QString().setNum(cmdPeer->pony.rot.x) + ", y=" + QString().setNum(cmdPeer->pony.rot.y) + ", z=" + QString().setNum(cmdPeer->pony.rot.z) + ", w=" + QString().setNum(cmdPeer->pony.rot.w)); } else if (str.startsWith("getPonyData")) { logMessage("ponyData : "+cmdPeer->pony.ponyData.toBase64()); } else if (str.startsWith("sendPonies")) { sendPonies(cmdPeer); } else if (str.startsWith("sendUtils3")) { logMessage(QObject::tr("UDP: Sending Utils3 request")); QByteArray data(1,3); sendMessage(cmdPeer,MsgUserReliableOrdered6,data); } else if (str.startsWith("setPlayerId")) { str = str.right(str.size()-12); QByteArray data(3,4); bool ok; unsigned id = str.toUInt(&ok); if (ok) { logMessage(QObject::tr("UDP: Sending setPlayerId request")); data[1]=(quint8)(id&0xFF); data[2]=(quint8)((id >> 8)&0xFF); sendMessage(cmdPeer,MsgUserReliableOrdered6,data); } else
void receiveMessage(Player* player) { QByteArray msg = *(player->receivedDatas); int msgSize=5 + (((unsigned char)msg[3]) + (((unsigned char)msg[4]) << 8))/8; #if UDP_SIMULATE_PACKETLOSS if (qrand() % 100 <= UDP_RECV_PERCENT_DROPPED) { //app.logMessage("UDP: Received packet dropped !"); *(player->receivedDatas) = player->receivedDatas->mid(msgSize); if (player->receivedDatas->size()) receiveMessage(player); return; // When we're done with the recursion, we still need to skip this message. } else { //app.logMessage("UDP: Received packet got through !"); } #endif // Check the sequence (seq) of the received messag if ((unsigned char)msg[0] >= MsgUserReliableOrdered1 && (unsigned char)msg[0] <= MsgUserReliableOrdered32) { quint16 seq = (quint8)msg[1] + ((quint8)msg[2]<<8); quint8 channel = ((unsigned char)msg[0])-MsgUserReliableOrdered1; if (seq <= player->udpRecvSequenceNumbers[channel] && player->udpRecvSequenceNumbers[channel]!=0) { // If this is a missing packet, accept it MessageHead missingMsg; missingMsg.channel = channel; missingMsg.seq = seq; if (player->udpRecvMissing.contains(missingMsg)) { logMessage(QObject::tr("UDP: Processing retransmission (-%1) from %2") .arg(player->udpRecvSequenceNumbers[channel]-seq).arg(player->pony.netviewId)); for (int i=0; i<player->udpRecvMissing.size(); i++) if (player->udpRecvMissing[i] == missingMsg) player->udpRecvMissing.remove(i); } else { // We already processed this packet, we should discard it #if DEBUG_LOG app.logMessage("UDP: Discarding double message (-"+QString().setNum(player->udpRecvSequenceNumbers[channel]-seq) +") from "+QString().setNum(player->pony.netviewId)); app.logMessage("UDP: Message was : "+QString(player->receivedDatas->left(msgSize).toHex().data())); #endif player->nReceivedDups++; if (player->nReceivedDups >= 100) // Kick the player if he's infinite-looping on us { logMessage(QObject::tr("UDP: Kicking %1 : Too many packet dups").arg(player->pony.netviewId)); sendMessage(player,MsgDisconnect, "You were kicked for lagging the server, sorry. You can login again."); Player::disconnectPlayerCleanup(player); // Save game and remove the player return; } *(player->receivedDatas) = player->receivedDatas->mid(msgSize); // Ack if needed, so that the client knows to move on already. if ((unsigned char)msg[0] >= MsgUserReliableOrdered1 && (unsigned char)msg[0] <= MsgUserReliableOrdered32) // UserReliableOrdered { #if DEBUG_LOG app.logMessage("UDP: ACKing discarded message"); #endif QByteArray data(3,0); data[0] = (quint8)(msg[0]); // ack type data[1] = (quint8)(((quint8)msg[1])/2); // seq data[2] = (quint8)(((quint8)msg[2])/2); // seq sendMessage(player, MsgAcknowledge, data); } if (player->receivedDatas->size()) receiveMessage(player); return; // When we're done with the recursion, we still need to skip this message. } } else if (seq > player->udpRecvSequenceNumbers[channel]+2) // If a message was skipped, keep going. { logMessage(QObject::tr("UDP: Unordered message (+%1) received from %2") .arg(seq-player->udpRecvSequenceNumbers[channel]).arg(player->pony.netviewId)); player->udpRecvSequenceNumbers[channel] = seq; // Mark the packets we skipped as missing for (int i=player->udpRecvSequenceNumbers[channel]+2; i<seq; i+=2) { MessageHead missing; missing.channel = channel; missing.seq = i; player->udpRecvMissing.append(missing); } } else { if (player->nReceivedDups > 0) // If he stopped sending dups, forgive him slowly. player->nReceivedDups--; //app.logMessage("UDP: Received message (="+QString().setNum(seq) // +") from "+QString().setNum(player->pony.netviewId)); player->udpRecvSequenceNumbers[channel] = seq; } } // Process the received message if ((unsigned char)msg[0] == MsgPing) // Ping { //app.logMessage("UDP: Ping received from "+player->IP+":"+QString().setNum(player->port) // +" ("+QString().setNum((timestampNow() - player->lastPingTime))+"s)"); player->lastPingNumber = (quint8)msg[5]; player->lastPingTime = timestampNow(); sendMessage(player,MsgPong); } else if ((unsigned char)msg[0] == MsgPong) // Pong { #if DEBUG_LOG //app.logMessage("UDP: Pong received"); #endif } else if ((unsigned char)msg[0] == MsgConnect) // Connect SYN { msg.resize(18); // Supprime le message LocalHail et le Timestamp msg = msg.right(13); // Supprime le Header #if DEBUG_LOG app.logMessage(QString("UDP: Connecting ...")); #endif for (int i=0; i<32; i++) // Reset sequence counters player->udpSequenceNumbers[i]=0; if (!player->connected) sendMessage(player, MsgConnectResponse, msg); } else if ((unsigned char)msg[0] == MsgConnectionEstablished) // Connect ACK { if (player->connected) logMessage(QObject::tr("UDP: Received duplicate connect ACK")); else { logMessage(QObject::tr("UDP: Connected to client")); player->connected=true; for (int i=0; i<32; i++) // Reset sequence counters player->udpSequenceNumbers[i]=0; onConnectAckReceived(player); // Clean the reliable message queue from SYN|ACKs // Start game #if DEBUG_LOG logMessage(QString("UDP: Starting game")); #endif // Set player id SceneEntity::lastIdMutex.lock(); player->pony.id = SceneEntity::getNewId(); player->pony.netviewId = SceneEntity::getNewNetviewId(); SceneEntity::lastIdMutex.unlock(); logMessage(QObject::tr("UDP: Set id request : %1/%2").arg(player->pony.id).arg(player->pony.netviewId)); QByteArray id(3,0); // Set player Id request id[0]=4; id[1]=(quint8)(player->pony.id&0xFF); id[2]=(quint8)((player->pony.id>>8)&0xFF); sendMessage(player,MsgUserReliableOrdered6,id); // Load characters screen request QByteArray data(1,5); data += stringToData("characters"); sendMessage(player,MsgUserReliableOrdered6,data); } } else if ((unsigned char)msg[0] == MsgAcknowledge) // Acknowledge
void Widget::tcpProcessData(QByteArray data, QTcpSocket* socket) { // Login request (forwarded) if (useRemoteLogin && tcpReceivedDatas->contains("commfunction=login&") && tcpReceivedDatas->contains("&version=")) { logMessage("TCP: Remote login not implemented yet."); // We need to add the client with his IP/port/passhash to tcpPlayers if he isn't already there Player newPlayer; newPlayer.IP = socket->peerAddress().toIPv4Address(); newPlayer.port = socket->peerPort(); QString passhash = QString(*tcpReceivedDatas); passhash = passhash.mid(passhash.indexOf("passhash=")+9); passhash.truncate(passhash.indexOf('&')); newPlayer.passhash = passhash; logMessage("IP:"+newPlayer.IP+", passhash:"+newPlayer.passhash); // Then connect to the remote and forward the client's requests if (!remoteLoginSock.isOpen()) { remoteLoginSock.connectToHost(remoteLoginIP, remoteLoginPort); remoteLoginSock.waitForConnected(remoteLoginTimeout); if (!remoteLoginSock.isOpen()) { win.logMessage("TCP: Can't connect to remote login server : timed out."); return; } } // We just blindly send everything that we're going to remove from tcpReceivedDatas at the end of tcpProcessData QByteArray toSend = *tcpReceivedDatas; toSend.left(toSend.indexOf(data) + data.size()); remoteLoginSock.write(toSend); } else if (useRemoteLogin && tcpReceivedDatas->contains("Server:")) // Login reply (forwarded) { logMessage("TCP: Remote login not implemented yet."); // First we need to find a player matching the received passhash in tcpPlayers // Use the player's IP/port to find a matching socket in tcpClientsList // The login headers are all the same, so we can just use loginHeader.bin and send back data } else if (tcpReceivedDatas->contains("commfunction=login&") && tcpReceivedDatas->contains("&version=")) // Login request { QString postData = QString(*tcpReceivedDatas); *tcpReceivedDatas = tcpReceivedDatas->right(postData.size()-postData.indexOf("version=")-8-4); // 4 : size of version number (ie:version=1344) logMessage("TCP: Login request received :"); QFile file(QString(NETDATAPATH)+"/loginHeader.bin"); QFile fileServersList(SERVERSLISTFILEPATH); QFile fileBadPassword(QString(NETDATAPATH)+"/loginWrongPassword.bin"); QFile fileAlready(QString(NETDATAPATH)+"/loginAlreadyConnected.bin"); QFile fileMaxRegistration(QString(NETDATAPATH)+"/loginMaxRegistered.bin"); QFile fileMaxConnected(QString(NETDATAPATH)+"/loginMaxConnected.bin"); if (!file.open(QIODevice::ReadOnly) || !fileBadPassword.open(QIODevice::ReadOnly) || !fileAlready.open(QIODevice::ReadOnly) || !fileMaxRegistration.open(QIODevice::ReadOnly) || !fileMaxConnected.open(QIODevice::ReadOnly) || !fileServersList.open(QIODevice::ReadOnly)) { logStatusMessage("Error reading login data files"); stopServer(); } else { bool ok=true; postData = postData.right(postData.size()-postData.indexOf("username="******"passhash=")-9); QString passhash = postData; passhash.truncate(postData.indexOf('&')); logMessage(QString("IP : ")+socket->peerAddress().toString()); logMessage(QString("Username : "******"Passhash : ")+passhash); // Add player to the players list Player* player = Player::findPlayer(tcpPlayers, username); if (player->name != username) // Not found, create a new player { // Check max registered number if (tcpPlayers.size() >= maxRegistered) { logMessage("TCP: Registration failed, too many players registered"); socket->write(fileMaxRegistration.readAll()); ok = false; } else { logMessage("TCP: Creating user "+username+" in database"); Player* newPlayer = new Player; newPlayer->name = username; newPlayer->passhash = passhash; newPlayer->IP = socket->peerAddress().toString(); newPlayer->connected = false; // The connection checks are done by the game servers tcpPlayers << newPlayer; if (!Player::savePlayers(tcpPlayers)) ok = false; } } else // Found, compare passhashes, check if already connected { if (player->passhash != passhash) // Bad password { logMessage("TCP: Login failed, wrong password"); socket->write(fileBadPassword.readAll()); ok=false; } /* else if (newPlayer.connected) // Already connected { logMessage("TCP: Login failed, player already connected"); socket->write(fileAlready.readAll()); ok=false; } */ else // Good password { /* // Check too many connected int n=0; for (int i=0;i<tcpPlayers.size();i++) if (tcpPlayers[i].connected) n++; if (n>=maxConnected) { logMessage("TCP: Login failed, too much players connected"); socket->write(fileMaxConnected.readAll()); ok=false; } else */ { //player->reset(); player->IP = socket->peerAddress().toString(); player->lastPingTime = timestampNow(); player->connected = true; } } } if (ok) // Send servers list { QByteArray customData = file.readAll(); QByteArray data1 = QByteArray::fromHex("0D0A61757468726573706F6E73653A0A747275650A"); QByteArray sesskey = QCryptographicHash::hash(QString(passhash + saltPassword).toLatin1(), QCryptographicHash::Md5).toHex(); sesskey += passhash; QByteArray data2 = QByteArray::fromHex("0A310A"); QByteArray serversList; QByteArray line; do { line = fileServersList.readLine().trimmed(); serversList+=line; serversList+=0x0A; } while (!line.isEmpty()); serversList = serversList.trimmed(); QByteArray data3 = QByteArray::fromHex("0D0A300D0A0D0A"); int dataSize = data1.size() + sesskey.size() + data2.size() + serversList.size() - 2; QString dataSizeStr = QString().setNum(dataSize, 16); customData += dataSizeStr; customData += data1; customData += sesskey; customData += data2; customData += serversList; customData += data3; logMessage("TCP: Login successful, sending servers list"); socket->write(customData); socket->close(); } } } else if (data.contains("commfunction=removesession")) { logMessage("TCP: Session closed by client"); } else // Unknown request, erase tcp buffer { // Display data logMessage("TCP: Unknown request received : "); logMessage(QString(data.data())); } }
void receiveMessage(Player* player) { QByteArray msg = *(player->receivedDatas); int msgSize=5 + (((unsigned char)msg[3]) + (((unsigned char)msg[4]) << 8))/8; // Check the sequence (seq) of the received message if ((unsigned char)msg[0] >= MsgUserReliableOrdered1 && (unsigned char)msg[0] <= MsgUserReliableOrdered32) { quint16 seq = (quint8)msg[1] + ((quint8)msg[2]<<8); quint8 channel = ((unsigned char)msg[0])-MsgUserReliableOrdered1; if (seq <= player->udpRecvSequenceNumbers[channel] && player->udpRecvSequenceNumbers[channel]!=0) { // If this is a missing packet, accept it MessageHead missingMsg; missingMsg.channel = channel; missingMsg.seq = seq; if (player->udpRecvMissing.contains(missingMsg)) { win.logMessage("UDP: Processing retransmission (-"+QString().setNum(player->udpRecvSequenceNumbers[channel]-seq) +") from "+QString().setNum(player->pony.netviewId)); for (int i=0; i<player->udpRecvMissing.size(); i++) if (player->udpRecvMissing[i] == missingMsg) player->udpRecvMissing.remove(i); } else { // We already processed this packet, we should discard it win.logMessage("UDP: Discarding double message (-"+QString().setNum(player->udpRecvSequenceNumbers[channel]-seq) +") from "+QString().setNum(player->pony.netviewId)); //win.logMessage("UDP: Message was : "+QString(player->receivedDatas->left(msgSize).toHex().data())); player->nReceivedDups++; if (player->nReceivedDups >= 100) // Kick the player if he's infinite-looping on us { win.logMessage(QString("UDP: Kicking "+QString().setNum(player->pony.netviewId)+" : Too many packet dups.")); sendMessage(player,MsgDisconnect, "You were kicked for lagging the server, sorry. You can login again."); Player::disconnectPlayerCleanup(player); // Save game and remove the player return; } *(player->receivedDatas) = player->receivedDatas->mid(msgSize); // Ack if needed, so that the client knows to move on already. if ((unsigned char)msg[0] >= MsgUserReliableOrdered1 && (unsigned char)msg[0] <= MsgUserReliableOrdered32) // UserReliableOrdered { //win.logMessage("UDP: ACKing discarded message"); QByteArray data(3,0); data[0] = (quint8)msg[0]; // ack type data[1] = ((quint8)msg[1])/2; // seq data[2] = ((quint8)msg[2])/2; // seq sendMessage(player, MsgAcknowledge, data); } if (player->receivedDatas->size()) receiveMessage(player); return; // When we're done with the recursion, we still need to skip this message. } } else if (seq > player->udpRecvSequenceNumbers[channel]+2) // If a message was skipped, keep going. { win.logMessage("UDP: Unordered message (+"+QString().setNum(seq-player->udpRecvSequenceNumbers[channel]) +") received from "+QString().setNum(player->pony.netviewId)); player->udpRecvSequenceNumbers[channel] = seq; // Mark the packets we skipped as missing for (int i=player->udpRecvSequenceNumbers[channel]+2; i<seq; i+=2) { MessageHead missing; missing.channel = channel; missing.seq = i; player->udpRecvMissing.append(missing); } } else { if (player->nReceivedDups > 0) // If he stopped sending dups, forgive him slowly. player->nReceivedDups--; //win.logMessage("UDP: Received message (="+QString().setNum(seq) // +") from "+QString().setNum(player->pony.netviewId)); player->udpRecvSequenceNumbers[channel] = seq; } } // Process the received message if ((unsigned char)msg[0] == MsgPing) // Ping { //win.logMessage("UDP: Ping received from "+player->IP+":"+QString().setNum(player->port) // +" ("+QString().setNum((timestampNow() - player->lastPingTime))+"s)"); player->lastPingNumber = (quint8)msg[5]; player->lastPingTime = timestampNow(); sendMessage(player,MsgPong); } else if ((unsigned char)msg[0] == MsgPong) // Pong { win.logMessage("UDP: Pong received"); } else if ((unsigned char)msg[0] == MsgConnect) // Connect SYN { msg.resize(18); // Supprime le message LocalHail et le Timestamp msg = msg.right(13); // Supprime le Header win.logMessage(QString("UDP: Connecting ...")); for (int i=0; i<32; i++) // Reset sequence counters player->udpSequenceNumbers[i]=0; sendMessage(player, MsgConnectResponse, msg); } else if ((unsigned char)msg[0] == MsgConnectionEstablished) // Connect ACK { win.logMessage("UDP: Connected to client"); player->connected=true; for (int i=0; i<32; i++) // Reset sequence counters player->udpSequenceNumbers[i]=0; // Start game win.logMessage(QString("UDP: Starting game")); // Set local player id win.lastId++; win.lastNetviewId++; player->pony.id = win.lastId; player->pony.netviewId = win.lastNetviewId; win.logMessage("UDP: Set id request : " + QString().setNum(player->pony.id) + "/" + QString().setNum(player->pony.netviewId)); // Set player Id request QByteArray id(3,0); id[0]=4; id[1]=player->pony.id; id[2]=player->pony.id>>8; sendMessage(player,MsgUserReliableOrdered6,id); // Sends a 48 // Load Characters screen requets QByteArray data(1,5); data += stringToData("Characters"); sendMessage(player,MsgUserReliableOrdered6,data); // Sends a 48 }
void Widget::tcpProcessData(QByteArray data, QTcpSocket* socket) { if (tcpReceivedDatas->contains("commfunction=login&") && tcpReceivedDatas->contains("&version=")) // Login request { QString postData = QString(*tcpReceivedDatas); *tcpReceivedDatas = tcpReceivedDatas->right(postData.size()-postData.indexOf("version=")-8-4); // 4 : size of version number (ie:version=1344) logMessage("TCP: Login request received :"); QFile file(QString(NETDATAPATH)+"/loginHeader.bin"); QFile fileServersList(SERVERSLISTFILEPATH); QFile fileBadPassword(QString(NETDATAPATH)+"/loginWrongPassword.bin"); QFile fileAlready(QString(NETDATAPATH)+"/loginAlreadyConnected.bin"); QFile fileMaxRegistration(QString(NETDATAPATH)+"/loginMaxRegistered.bin"); QFile fileMaxConnected(QString(NETDATAPATH)+"/loginMaxConnected.bin"); if (!file.open(QIODevice::ReadOnly) || !fileBadPassword.open(QIODevice::ReadOnly) || !fileAlready.open(QIODevice::ReadOnly) || !fileMaxRegistration.open(QIODevice::ReadOnly) || !fileMaxConnected.open(QIODevice::ReadOnly) || !fileServersList.open(QIODevice::ReadOnly)) { logStatusMessage("Error reading login data files"); stopServer(); } else { bool ok=true; postData = postData.right(postData.size()-postData.indexOf("username="******"passhash=")-9); QString passhash = postData; passhash.truncate(postData.indexOf('&')); logMessage(QString("IP : ")+socket->peerAddress().toString()); logMessage(QString("Username : "******"Passhash : ")+passhash); // Add player to the players list Player player = Player::findPlayer(tcpPlayers, username); if (player.name != username) // Not found, create a new player { // Check max registered number if (tcpPlayers.size() >= maxRegistered) { logMessage("TCP: Registration failed, too much players registered"); socket->write(fileMaxRegistration.readAll()); ok = false; } else { logMessage("TCP: Creating user in database"); Player newPlayer; newPlayer.name = username; newPlayer.passhash = passhash; newPlayer.IP = socket->peerAddress().toString(); newPlayer.connected = false; // The connection checks are done by the game servers tcpPlayers << newPlayer; if (!Player::savePlayers(tcpPlayers)) ok = false; } } else // Found, compare passhashes, check if already connected { if (player.passhash != passhash) // Bad password { logMessage("TCP: Login failed, wrong password"); socket->write(fileBadPassword.readAll()); ok=false; } /* else if (newPlayer.connected) // Already connected { logMessage("TCP: Login failed, player already connected"); socket->write(fileAlready.readAll()); ok=false; } */ else // Good password { /* // Check too many connected int n=0; for (int i=0;i<tcpPlayers.size();i++) if (tcpPlayers[i].connected) n++; if (n>=maxConnected) { logMessage("TCP: Login failed, too much players connected"); socket->write(fileMaxConnected.readAll()); ok=false; } else */ { player.reset(); player.IP = socket->peerAddress().toString(); player.lastPingTime = timestampNow(); player.connected = true; } } } if (ok) // Send servers list { QByteArray customData = file.readAll(); QByteArray data1 = QByteArray::fromHex("0D0A61757468726573706F6E73653A0A747275650A"); QByteArray sesskey = QCryptographicHash::hash(QString(passhash + saltPassword).toLatin1(), QCryptographicHash::Md5).toHex(); sesskey += passhash; QByteArray data2 = QByteArray::fromHex("0A310A"); QByteArray serversList; QByteArray line; do { line = fileServersList.readLine().trimmed(); serversList+=line; serversList+=0x0A; } while (!line.isEmpty()); serversList = serversList.trimmed(); QByteArray data3 = QByteArray::fromHex("0D0A300D0A0D0A"); int dataSize = data1.size() + sesskey.size() + data2.size() + serversList.size() - 2; QString dataSizeStr = QString().setNum(dataSize, 16); customData += dataSizeStr; customData += data1; customData += sesskey; customData += data2; customData += serversList; customData += data3; logMessage("TCP: Login successful, sending servers list"); socket->write(customData); } } } else if (data.contains("commfunction=removesession")) { logMessage("TCP: Session closed by client"); } else // Unknown request, erase tcp buffer { // Display data logMessage("TCP: Unknow request received : "); logMessage(QString(data.data())); } // Delete the processed message from the buffer *tcpReceivedDatas = tcpReceivedDatas->right(tcpReceivedDatas->size() - tcpReceivedDatas->indexOf(data) - data.size()); }