void sendDialogMessage(Player* player, QString& message, QString NPCName, quint16 iconId) { QByteArray data(1,0); data[0] = 0x11; // Request number data += stringToData(message); data += stringToData(NPCName); data += (quint8)(iconId&0xFF); data += (quint8)((iconId>>8)&0xFF); sendMessage(player,MsgUserReliableOrdered4, data); }
void sendChatMessage(Player* player, QString message, QString author, quint8 chatType) { QByteArray idAndAccess(5,0); idAndAccess[0] = player->pony.netviewId; idAndAccess[1] = player->pony.netviewId << 8; idAndAccess[2] = player->pony.netviewId << 16; idAndAccess[3] = player->pony.netviewId << 24; idAndAccess[4] = 0x0; // Access level QByteArray data(2,0); data[0] = 0xf; // RPC ID data[1] = chatType; data += stringToData(author); data += stringToData(message); data += idAndAccess; sendMessage(player,MsgUserReliableOrdered4,data); // Sends a 46 }
void Pony::saveInventory() { win.logMsg(QObject::tr("%1 UDP: Saving Inventory for %2 (%3)").arg(LOG_INFO).arg(QString().setNum(netviewId)).arg(owner->name)); QDir playerPath(QDir::currentPath()); playerPath.cd("data"); playerPath.cd("players"); playerPath.mkdir(owner->name.toLatin1()); QFile file(QDir::currentPath()+"/data/players/"+owner->name.toLatin1()+"/inventory.lpd"); if (!file.open(QIODevice::ReadWrite)) { win.logMsg(QObject::tr("%1 UDP: Error Saving Inventory for %2 (%3)").arg(LOG_SEVERE).arg(QString().setNum(netviewId)).arg(owner->name)); return; } QByteArray invData = file.readAll(); // Try to find an existing entry for this pony, if found delete it. Then go at the end. for (int i=0; i<invData.size();) { // Read the name QString entryName = dataToString(invData.mid(i)); int nameSize = entryName.size()+getVUint32Size(invData.mid(i)); //win.logMessage("saveInventory : Reading entry "+entryName); quint8 invSize = invData[i+nameSize+4]; quint8 wornSize = invData[i+nameSize+4+1+invSize*9]; // Serialized sizeof InventoryItem is 9 if (entryName == this->name) // Delete the entry, we'll rewrite it at the end { invData.remove(i,nameSize+4+1+invSize*9+1+wornSize*5); break; } else // Skip this entry i += nameSize+4+1+invSize*9+1+wornSize*5; } // Now add our data at the end of the file QByteArray newEntry = stringToData(this->name); newEntry += uint32ToData(nBits); newEntry += uint8ToData(inv.size()); for (const InventoryItem& item : inv) { newEntry += uint8ToData(item.index); newEntry += uint32ToData(item.id); newEntry += uint32ToData(item.amount); } newEntry += uint8ToData(wornItm.size()); for (const WearableItem& item : wornItm) { newEntry += uint8ToData(item.index); newEntry += uint32ToData(item.id); } invData += newEntry; file.resize(0); file.write(invData); file.close(); }
void sendAddViewAddShop(Player* player, Pony* npcShop) { QByteArray data; data += 0x0A; // AddView RPC data += uint16ToData(npcShop->netviewId); data += uint16ToData(npcShop->netviewId); //data += uint16ToData(player->pony.netviewId); data += stringToData("AddShop"); sendMessage(player, MsgUserReliableOrdered6, data); }
void sendDialogOptions(Player* player, QList<QString>& answers) { QByteArray data(5,0); data[0] = 0xC; // Request number data[1] = (quint8)(answers.size()&0xFF); data[2] = (quint8)((answers.size()>>8)&0xFF); data[3] = (quint8)((answers.size()>>16)&0xFF); data[4] = (quint8)((answers.size()>>24)&0xFF); for (int i=0; i<answers.size(); i++) data += stringToData(answers[i]); sendMessage(player,MsgUserReliableOrdered4, data); }
void Pony::saveQuests() { win.logMessage("UDP: Saving quests for "+QString().setNum(netviewId)+" ("+owner->name+")", udpTag); QDir playerPath(QDir::currentPath()); playerPath.cd("data"); playerPath.cd("players"); playerPath.mkdir(owner->name.toLatin1()); QFile file(QDir::currentPath()+"/data/players/"+owner->name.toLatin1()+"/quests.dat"); if (!file.open(QIODevice::ReadWrite)) { win.logMessage("Error saving quests for "+QString().setNum(netviewId)+" ("+owner->name+")", udpTag); return; } QByteArray questData = file.readAll(); // Try to find an existing entry for this pony, if found delete it. Then go at the end. for (int i=0; i<questData.size();) { // Read the name QString entryName = dataToString(questData.mid(i)); int nameSize = entryName.size()+getVUint32Size(questData.mid(i)); //win.logMessage("saveQuests : Reading entry "+entryName); quint16 entryDataSize = 4 * dataToUint16(questData.mid(i+nameSize)); if (entryName == this->name) // Delete the entry, we'll rewrite it at the end { questData.remove(i,nameSize+entryDataSize+2); break; } else i += nameSize+entryDataSize+2; } // Now add our data at the end of the file QByteArray newEntry = stringToData(this->name); newEntry += (quint8)(quests.size() & 0xFF); newEntry += (quint8)((quests.size() >> 8) & 0xFF); for (const Quest& quest : quests) { newEntry += (quint8)(quest.id & 0xFF); newEntry += (quint8)((quest.id >> 8) & 0xFF); newEntry += (quint8)(quest.state & 0xFF); newEntry += (quint8)((quest.state >> 8) & 0xFF); } questData += newEntry; file.resize(0); file.write(questData); file.close(); }
void sendNetviewInstantiate(Player *player, QString key, quint16 ViewId, quint16 OwnerId, UVector pos, UQuaternion rot) { QByteArray data(1,1); data += stringToData(key); QByteArray data2(4,0); data2[0]=ViewId; data2[1]=ViewId>>8; data2[2]=OwnerId; data2[3]=OwnerId>>8; data += data2; data += vectorToData(pos); data += quaternionToData(rot); sendMessage(player, MsgUserReliableOrdered6, data); }
void sendBeginShop(Player* player, Pony* npcShop) { QByteArray data; data += uint16ToData(npcShop->netviewId); data += 0x16; // BeginShop data += uint32ToData(npcShop->inv.size()); for (const InventoryItem& item : npcShop->inv) { data += uint32ToData(item.id); data += uint32ToData(item.amount); } data += stringToData("Wearable Items"); sendMessage(player, MsgUserReliableOrdered18, data); }
void sendLoadSceneRPC(Player* player, QString sceneName) // Loads a scene and send to the default spawn { win.logMessage(QString("UDP: Loading scene \"") + sceneName + "\" on "+QString().setNum(player->pony.netviewId)); Vortex vortex = findVortex(sceneName, 0); if (vortex.destName.isEmpty()) { win.logMessage("UDP: Scene not in vortex DB. Aborting scene load."); return; } Scene* scene = findScene(sceneName); Scene* oldScene = findScene(player->pony.sceneName); if (scene->name.isEmpty() || oldScene->name.isEmpty()) { win.logMessage("UDP: Can't find the scene, aborting"); return; } // Update scene players //win.logMessage("sendLoadSceneRPC: locking"); levelLoadMutex.lock(); player->inGame=1; player->pony.pos = vortex.destPos; player->pony.sceneName = sceneName; player->lastValidReceivedAnimation.clear(); // Changing scenes resets animations Player::removePlayer(oldScene->players, player->IP, player->port); if (oldScene->name != sceneName) { // Send remove RPC to the other players of the old scene for (int i=0; i<oldScene->players.size(); i++) sendNetviewRemove(oldScene->players[i], player->pony.netviewId); // Send instantiate to the players of the new scene for (int i=0; i<scene->players.size(); i++) if (scene->players[i]->inGame>=2) sendNetviewInstantiate(player, scene->players[i]); } scene->players << player; QByteArray data(1,5); data += stringToData(sceneName); sendMessage(player,MsgUserReliableOrdered6,data); // Sends a 48 //win.logMessage("sendLoadSceneRPC: unlocking"); levelLoadMutex.unlock(); }
void Player::savePonies(Player *player, QList<Pony> ponies) { win.logMessage("UDP: Saving ponies for "+QString().setNum(player->pony.netviewId)+" ("+player->name+")", sysTag); QDir playerPath(QDir::currentPath()); playerPath.cd("data"); playerPath.cd("players"); playerPath.mkdir(player->name.toLatin1()); QFile file(QDir::currentPath()+"/data/players/"+player->name.toLatin1()+"/ponies.dat"); file.open(QIODevice::ReadWrite | QIODevice::Truncate); for (int i=0; i<ponies.size(); i++) { file.write(ponies[i].ponyData); file.write(vectorToData(ponies[i].pos)); file.write(stringToData(ponies[i].sceneName.toLower())); } }
void sendNetviewInstantiate(Player *player) { win.logMessage("UDP: Send instantiate for/to "+QString().setNum(player->pony.netviewId)); QByteArray data(1,1); data += stringToData("PlayerBase"); QByteArray data2(4,0); data2[0]=player->pony.netviewId; data2[1]=player->pony.netviewId>>8; data2[2]=player->pony.id; data2[3]=player->pony.id>>8; data += data2; data += vectorToData(player->pony.pos); data += quaternionToData(player->pony.rot); sendMessage(player, MsgUserReliableOrdered6, data); win.logMessage(QString("Instantiate at ")+QString().setNum(player->pony.pos.x)+" " +QString().setNum(player->pony.pos.y)+" " +QString().setNum(player->pony.pos.z)); }
void sendLoadSceneRPC(Player* player, QString sceneName, UVector pos) // Loads a scene and send to the given pos { win.logMessage(QString(QString("UDP: Loading scene \"")+sceneName +"\" to "+QString().setNum(player->pony.netviewId) +" at "+QString().setNum(pos.x)+" " +QString().setNum(pos.y)+" " +QString().setNum(pos.z))); Scene* scene = findScene(sceneName); Scene* oldScene = findScene(player->pony.sceneName); if (scene->name.isEmpty() || oldScene->name.isEmpty()) { win.logMessage("UDP: Can't find the scene, aborting"); return; } // Update scene players //win.logMessage("sendLoadSceneRPC pos: locking"); levelLoadMutex.lock(); player->inGame=1; player->pony.pos = pos; player->pony.sceneName = sceneName.toLower(); player->lastValidReceivedAnimation.clear(); // Changing scenes resets animations Player::removePlayer(oldScene->players, player->IP, player->port); if (oldScene->name.toLower() != sceneName.toLower()) { // Send remove RPC to the other players of the old scene for (int i=0; i<oldScene->players.size(); i++) sendNetviewRemove(oldScene->players[i], player->pony.netviewId); // Send instantiate to the players of the new scene for (int i=0; i<scene->players.size(); i++) if (scene->players[i]->inGame>=2) sendNetviewInstantiate(&player->pony, scene->players[i]); } scene->players << player; QByteArray data(1,5); data += stringToData(sceneName.toLower()); sendMessage(player,MsgUserReliableOrdered6,data); // Sends a 48 //win.logMessage("sendLoadSceneRPC pos: unlocking"); levelLoadMutex.unlock(); }
void sendNetviewInstantiate(Player *src, Player *dst) { win.logMessage("UDP: Send instantiate for "+QString().setNum(src->pony.netviewId) +" to "+QString().setNum(dst->pony.netviewId)); QByteArray data(1,1); data += stringToData("PlayerBase"); QByteArray data2(4,0); data2[0]=src->pony.netviewId; data2[1]=src->pony.netviewId>>8; data2[2]=src->pony.id; data2[3]=src->pony.id>>8; data += data2; data += vectorToData(src->pony.pos); data += quaternionToData(src->pony.rot); sendMessage(dst, MsgUserReliableOrdered6, data); //win.logMessage(QString("Instantiate at ")+QString().setNum(rSrc.pony.pos.x)+" " // +QString().setNum(rSrc.pony.pos.y)+" " // +QString().setNum(rSrc.pony.pos.z)); }
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 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 }