1 | /*************************************************************************** |
2 | * KBlocks, a falling blocks game for KDE * |
3 | * Copyright (C) 2010 Zhongjie Cai <squall.leonhart.cai@gmail.com> * |
4 | * * |
5 | * This program is free software; you can redistribute it and/or modify * |
6 | * it under the terms of the GNU General Public License as published by * |
7 | * the Free Software Foundation; either version 2 of the License, or * |
8 | * (at your option) any later version. * |
9 | ***************************************************************************/ |
10 | #include "KBlocksNetServer.h" |
11 | #include "GamePlayerInterface.h" |
12 | #include "KBlocksDefine.h" |
13 | #include "KBlocksSingleGame.h" |
14 | #include "KBlocksField.h" |
15 | #include "KBlocksPiece.h" |
16 | |
17 | #include <QVector> |
18 | #include <QVarLengthArray> |
19 | |
20 | KBlocksNetServer::KBlocksNetServer(KBlocksGameLogic * p, const QString& localIP) |
21 | { |
22 | mpGameLogic = p; |
23 | |
24 | mGameCount = 0; |
25 | mGameStarted = false; |
26 | mWaitForAll = false; |
27 | mInitSendLength = 0; |
28 | mLvUpSendLength = 0; |
29 | |
30 | parseIPString(localIP, &mLocalAddress, &mLocalPort); |
31 | |
32 | mpServerSocket = new QUdpSocket(this); |
33 | mpServerSocket->bind(mLocalAddress, mLocalPort); |
34 | |
35 | mRunningFlag = false; |
36 | |
37 | mRecordFileName = string("" ); |
38 | mRecordFileType = true; |
39 | } |
40 | |
41 | KBlocksNetServer::~KBlocksNetServer() |
42 | { |
43 | delete mpServerSocket; |
44 | } |
45 | |
46 | int KBlocksNetServer::executeGame(int gameCount, bool waitForAll) |
47 | { |
48 | mGameCount = gameCount; |
49 | mWaitForAll = waitForAll; |
50 | maGameScoreList = new KBlocksScore*[mGameCount]; |
51 | mTopGameLevel = -1; |
52 | for(int i = 0; i < mGameCount; i++) |
53 | { |
54 | maGameScoreList[i] = new KBlocksScore(); |
55 | maGameScoreList[i]->setLevelUpFactor(KBlocksScore_Level_x_Level_x_Factor, 1000); |
56 | maGameScoreList[i]->setScoreUpFactor(10); |
57 | } |
58 | |
59 | mRunningFlag = true; |
60 | |
61 | QVector<QByteArray> tmpRecvData; |
62 | QVector<QString> tmpRecvAddr; |
63 | int recvResult = 0; |
64 | while (mRunningFlag) |
65 | { |
66 | recvRemoteData(&tmpRecvData, &tmpRecvAddr); |
67 | |
68 | if (tmpRecvData.isEmpty()) |
69 | { |
70 | recvResult = processGame(-1); |
71 | } |
72 | else |
73 | { |
74 | recvResult = 0; |
75 | for (int i = 0; i < tmpRecvData.size(); ++i) |
76 | { |
77 | int result = parseRemoteData(tmpRecvData[i], tmpRecvAddr[i]); |
78 | if (result != -1) |
79 | { |
80 | processGame(result); |
81 | } |
82 | else |
83 | { |
84 | recvResult = 1; |
85 | } |
86 | } |
87 | |
88 | if ((!mWaitForAll) && (recvResult == 0)) |
89 | { |
90 | recvResult = processGame(-1); |
91 | } |
92 | } |
93 | |
94 | if ((recvResult < -1) && mGameStarted) |
95 | { |
96 | break; |
97 | } |
98 | } |
99 | |
100 | mRunningFlag = false; |
101 | |
102 | if (!mRecordFileName.empty()) |
103 | { |
104 | mpGameLogic->saveRecord(mRecordFileName.c_str(), mRecordFileType); |
105 | } |
106 | |
107 | sendGameOver(); |
108 | |
109 | printGameResult(); |
110 | |
111 | for(int i = 0; i < mGameCount; i++) |
112 | { |
113 | delete maGameScoreList[i]; |
114 | } |
115 | delete [] maGameScoreList; |
116 | |
117 | if (recvResult != -2) |
118 | { |
119 | return 0; |
120 | } |
121 | |
122 | return recvResult; |
123 | } |
124 | |
125 | void KBlocksNetServer::setSendLength(int initLen, int lvUpLen) |
126 | { |
127 | mInitSendLength = initLen; |
128 | mLvUpSendLength = lvUpLen; |
129 | } |
130 | |
131 | void KBlocksNetServer::setRecordFile(const char * fileName, bool binaryMode) |
132 | { |
133 | mRecordFileName = string(fileName); |
134 | mRecordFileType = binaryMode; |
135 | } |
136 | |
137 | void KBlocksNetServer::recvRemoteData(QVector<QByteArray> * recvData, QVector<QString> * recvAddr) |
138 | { |
139 | if (!mRunningFlag) |
140 | { |
141 | return; |
142 | } |
143 | |
144 | recvData->clear(); |
145 | recvAddr->clear(); |
146 | |
147 | while (mpServerSocket->hasPendingDatagrams()) |
148 | { |
149 | QByteArray tmpData; |
150 | tmpData.resize(mpServerSocket->pendingDatagramSize()); |
151 | |
152 | mpServerSocket->readDatagram(tmpData.data(), tmpData.size(), &mRemoteAddress, &mRemotePort); |
153 | QString tmpAddr = formIPString(mRemoteAddress, mRemotePort); |
154 | |
155 | recvData->append(tmpData); |
156 | recvAddr->append(tmpAddr); |
157 | |
158 | //printf(" Recv From %s : %s\n", tmpAddr.toStdString().c_str(), tmpData.data()); |
159 | //printf(" [%s:%d]\n", mRemoteAddress.toString().toStdString().c_str(), mRemotePort); |
160 | } |
161 | } |
162 | |
163 | int KBlocksNetServer::processGame(int gameIndex) |
164 | { |
165 | if (gameIndex >= 0) |
166 | { |
167 | //printf("\tStepping game (%d)...\n", gameIndex); |
168 | mpGameLogic->getSingleGame(gameIndex)->updateGame(); |
169 | //printf("\tSending new play data (%d)...\n", gameIndex); |
170 | sendPlayerData(gameIndex); |
171 | return gameIndex; |
172 | } |
173 | else |
174 | { |
175 | QVarLengthArray<int, 16> removedLines(mGameCount); |
176 | int activeCount = mpGameLogic->getActiveGameCount(); |
177 | if ((activeCount > 1) || (mGameCount == activeCount)) |
178 | { |
179 | mpGameLogic->updateGame(removedLines.data()); |
180 | for(int i = 0; i < mGameCount; i++) |
181 | { |
182 | if (maGameScoreList[i]->addScore(removedLines[i])) |
183 | { |
184 | int tmpLevel = maGameScoreList[i]->getGameLevel(); |
185 | if (mTopGameLevel < tmpLevel) |
186 | { |
187 | mpGameLogic->levelUpGame(tmpLevel - mTopGameLevel); |
188 | mTopGameLevel = tmpLevel; |
189 | sendPlayerActionLength(); |
190 | } |
191 | } |
192 | } |
193 | if (mWaitForAll) |
194 | { |
195 | bool allWait = true; |
196 | for(int i = 0; i < mGameCount; i++) |
197 | { |
198 | if (mpGameLogic->getSingleGame(i)->isActive()) |
199 | { |
200 | allWait = false; |
201 | break; |
202 | } |
203 | } |
204 | if (allWait) |
205 | { |
206 | mpGameLogic->continueGame(); |
207 | } |
208 | } |
209 | return -1; |
210 | } |
211 | else |
212 | { |
213 | return -2; |
214 | } |
215 | } |
216 | } |
217 | |
218 | void KBlocksNetServer::addPlayerIP(int gameIndex, const QByteArray& data, const QString& addr) |
219 | { |
220 | QString playerName = QString(data).mid(6); |
221 | if (mPlayerMapping.find(addr) == mPlayerMapping.end()) |
222 | { |
223 | mPlayerIPList.append(addr); |
224 | } |
225 | mPlayerMapping[addr] = gameIndex; |
226 | mPlayerName[gameIndex] = playerName; |
227 | } |
228 | |
229 | void KBlocksNetServer::delPlayerIP(const QString& addr) |
230 | { |
231 | if (mPlayerMapping.find(addr) != mPlayerMapping.end()) |
232 | { |
233 | mPlayerMapping.remove(addr); |
234 | mPlayerIPList.removeOne(addr); |
235 | } |
236 | } |
237 | |
238 | void KBlocksNetServer::sendPlayerActionLength() |
239 | { |
240 | QHostAddress tmpIP; |
241 | quint16 tmpPort; |
242 | QByteArray tmpByteData; |
243 | |
244 | int tmpLength = mInitSendLength - mTopGameLevel * mLvUpSendLength; |
245 | if (mInitSendLength <= 0) |
246 | { |
247 | return; |
248 | } |
249 | else |
250 | { |
251 | if (tmpLength < 1) |
252 | { |
253 | tmpLength = 1; |
254 | } |
255 | else if (tmpLength > 255) |
256 | { |
257 | tmpLength = 255; |
258 | } |
259 | } |
260 | |
261 | QList<QString>::iterator it; |
262 | for(it = mPlayerIPList.begin(); it != mPlayerIPList.end(); it++) |
263 | { |
264 | parseIPString(*it, &tmpIP, &tmpPort); |
265 | |
266 | tmpByteData.append((char)-1); |
267 | tmpByteData.append((char)tmpLength); |
268 | tmpByteData.append((char)0); |
269 | |
270 | mpServerSocket->writeDatagram(tmpByteData, tmpIP, tmpPort); |
271 | } |
272 | } |
273 | |
274 | void KBlocksNetServer::sendPlayerData(int gameIndex) |
275 | { |
276 | char tmpData[256]; |
277 | int tmpByteCount; |
278 | QByteArray tmpByteData; |
279 | |
280 | QList<QString>::iterator it; |
281 | for(it = mPlayerIPList.begin(); it != mPlayerIPList.end(); ++it) |
282 | { |
283 | int gameID = mPlayerMapping[*it]; |
284 | |
285 | if (gameIndex == gameID) |
286 | { |
287 | tmpByteData.clear(); |
288 | |
289 | tmpByteData.append((char)gameID); |
290 | |
291 | formByteFromInt(maGameScoreList[gameID]->getScorePoint(), tmpData + 0); |
292 | formByteFromInt(maGameScoreList[gameID]->getLineCount(), tmpData + 4); |
293 | formByteFromInt(maGameScoreList[gameID]->getGameLevel(), tmpData + 8); |
294 | tmpByteData.append(tmpData, 12); |
295 | |
296 | int tmpPieceCount = mpGameLogic->getSingleGame(gameID)->getPieceCount(); |
297 | formByteFromInt(tmpPieceCount, tmpData); |
298 | tmpByteData.append(tmpData, 4); |
299 | |
300 | for (int i = 0; i < tmpPieceCount; ++i) |
301 | { |
302 | mpGameLogic->getSingleGame(gameID)->getPiece(i)->encodeData((unsigned char *)tmpData + i * 4); |
303 | } |
304 | tmpByteData.append(tmpData, tmpPieceCount * 4); |
305 | |
306 | tmpByteCount = mpGameLogic->getSingleGame(gameID)->getField()->encodeData((unsigned char *)tmpData); |
307 | tmpByteData.append((char)tmpByteCount); |
308 | tmpByteData.append(tmpData, tmpByteCount); |
309 | |
310 | formByteFromInt(mpGameLogic->getSingleGame(gameID)->getField()->getModifyID(), tmpData); |
311 | tmpByteData.append(tmpData, 4); |
312 | |
313 | QHostAddress tmpIP; |
314 | quint16 tmpPort; |
315 | parseIPString(*it, &tmpIP, &tmpPort); |
316 | mpServerSocket->writeDatagram(tmpByteData, tmpIP, tmpPort); |
317 | |
318 | //printf(" Sending data to %d @ %s\n", gameID, it->toStdString().c_str()); |
319 | return; |
320 | } |
321 | } |
322 | } |
323 | |
324 | void KBlocksNetServer::sendGameOver() |
325 | { |
326 | QHostAddress tmpIP; |
327 | quint16 tmpPort; |
328 | QByteArray tmpByteData; |
329 | |
330 | QList<QString>::iterator it; |
331 | for(it = mPlayerIPList.begin(); it != mPlayerIPList.end(); ++it) |
332 | { |
333 | parseIPString(*it, &tmpIP, &tmpPort); |
334 | |
335 | tmpByteData.append((char)127); |
336 | tmpByteData.append((char)-1); |
337 | tmpByteData.append((char)0); |
338 | |
339 | mpServerSocket->writeDatagram(tmpByteData, tmpIP, tmpPort); |
340 | } |
341 | } |
342 | |
343 | void KBlocksNetServer::sendGuiData(const QString& addr) |
344 | { |
345 | char tmpData[256]; |
346 | int tmpByteCount; |
347 | QByteArray tmpByteData; |
348 | |
349 | if (!mGameStarted) |
350 | { |
351 | return; |
352 | } |
353 | |
354 | QHostAddress tmpIP; |
355 | quint16 tmpPort; |
356 | parseIPString(addr, &tmpIP, &tmpPort); |
357 | |
358 | for(int gameID = 0; gameID < mGameCount; ++gameID) |
359 | { |
360 | tmpByteData.clear(); |
361 | |
362 | tmpByteData.append((char)gameID); |
363 | |
364 | formByteFromInt(maGameScoreList[gameID]->getScorePoint(), tmpData + 0); |
365 | formByteFromInt(maGameScoreList[gameID]->getLineCount(), tmpData + 4); |
366 | formByteFromInt(maGameScoreList[gameID]->getGameLevel(), tmpData + 8); |
367 | tmpByteData.append(tmpData, 12); |
368 | |
369 | int tmpPieceCount = mpGameLogic->getSingleGame(gameID)->getPieceCount(); |
370 | formByteFromInt(tmpPieceCount, tmpData); |
371 | tmpByteData.append(tmpData, 4); |
372 | |
373 | for (int i = 0; i < tmpPieceCount; ++i) |
374 | { |
375 | mpGameLogic->getSingleGame(gameID)->getPiece(i)->encodeData((unsigned char *)tmpData + i * 4); |
376 | } |
377 | tmpByteData.append(tmpData, tmpPieceCount * 4); |
378 | |
379 | tmpByteCount = mpGameLogic->getSingleGame(gameID)->getField()->encodeData((unsigned char *)tmpData); |
380 | tmpByteData.append((char)tmpByteCount); |
381 | tmpByteData.append(tmpData, tmpByteCount); |
382 | |
383 | formByteFromInt(mpGameLogic->getSingleGame(gameID)->getField()->getModifyID(), tmpData); |
384 | tmpByteData.append(tmpData, 4); |
385 | |
386 | mpServerSocket->writeDatagram(tmpByteData, tmpIP, tmpPort); |
387 | } |
388 | } |
389 | |
390 | int KBlocksNetServer::parseRemoteData(const QByteArray& data, const QString& addr) |
391 | { |
392 | int result = -1; |
393 | QString input = QString(data); |
394 | |
395 | if (input.contains(QString("|ap|" ))) |
396 | { |
397 | addPlayerIP((int)(data[4] - '0'), data, addr); |
398 | //printf("Added player @ %s\n", input.toStdString().c_str()); |
399 | } |
400 | else if (input.contains("|dp|" )) |
401 | { |
402 | delPlayerIP(addr); |
403 | //printf("Deleted player @ %s\n", input.toStdString().c_str()); |
404 | } |
405 | else if (input.contains("|rp|" )) |
406 | { |
407 | result = parsePlayerReply(data, addr); |
408 | //printf("Received Player Data @ %s\n", input.toStdString().c_str()); |
409 | } |
410 | else if (input.contains("|rg|" )) |
411 | { |
412 | sendGuiData(addr); |
413 | //printf("Send Gui Data @ %s\n", input.toStdString().c_str()); |
414 | } |
415 | else if (input.contains("|s|" )) |
416 | { |
417 | if (!mGameStarted) |
418 | { |
419 | mpGameLogic->startGame(mGameCount); |
420 | mGameStarted = true; |
421 | for(int i = 0; i < mGameCount; i++) |
422 | { |
423 | sendPlayerData(i); |
424 | } |
425 | //printf("Game started\n"); |
426 | } |
427 | } |
428 | else if (input.contains("|c|" )) |
429 | { |
430 | if (mGameStarted) |
431 | { |
432 | mGameStarted = false; |
433 | mpGameLogic->stopGame(); |
434 | //printf("Game stopped\n"); |
435 | } |
436 | } |
437 | else |
438 | { |
439 | printf("Error transmission data : %s\n" , data.data()); |
440 | } |
441 | |
442 | return result; |
443 | } |
444 | |
445 | int KBlocksNetServer::parsePlayerReply(const QByteArray& data, const QString& addr) |
446 | { |
447 | if (!mGameStarted) |
448 | { |
449 | return -1; |
450 | } |
451 | |
452 | if (mPlayerMapping.find(addr) == mPlayerMapping.end()) |
453 | { |
454 | return -1; |
455 | } |
456 | |
457 | int gameID = mPlayerMapping[addr]; |
458 | KBlocksSingleGame * mpSingleGame = mpGameLogic->getSingleGame(gameID); |
459 | if (mpSingleGame == 0) |
460 | { |
461 | return -1; |
462 | } |
463 | |
464 | int counter = 4; |
465 | while(data[counter] != '|') |
466 | { |
467 | switch(data[counter] - '0') |
468 | { |
469 | case PlayerAction_Move_Left: |
470 | mpSingleGame->setCurrentPiece(-1, 0, 0); |
471 | break; |
472 | case PlayerAction_Move_Right: |
473 | mpSingleGame->setCurrentPiece(1, 0, 0); |
474 | break; |
475 | case PlayerAction_Move_Down: |
476 | mpSingleGame->setCurrentPiece(0, 1, 0); |
477 | break; |
478 | case PlayerAction_Push_Down: |
479 | while(mpSingleGame->setCurrentPiece(0, 1, 0)) ; |
480 | mpSingleGame->forceUpdateGame(); |
481 | break; |
482 | case PlayerAction_Rotate_CW: |
483 | mpSingleGame->setCurrentPiece(0, 0, 1); |
484 | break; |
485 | case PlayerAction_Rotate_CCW: |
486 | mpSingleGame->setCurrentPiece(0, 0, -1); |
487 | break; |
488 | case PlayerAction_None: |
489 | default: |
490 | break; |
491 | } |
492 | ++counter; |
493 | } |
494 | |
495 | return gameID; |
496 | } |
497 | |
498 | bool KBlocksNetServer::parseIPString(const QString& input, QHostAddress * ip, quint16 * port) |
499 | { |
500 | bool result = false; |
501 | ip->setAddress(input.left(input.indexOf(":" ))); |
502 | *port = input.mid(input.indexOf(":" ) + 1).toUInt(&result); |
503 | return result; |
504 | } |
505 | |
506 | QString KBlocksNetServer::formIPString(const QHostAddress& inAddr, quint16 inPort) |
507 | { |
508 | QString result = inAddr.toString() + QString(":%1" ).arg(inPort); |
509 | return result; |
510 | } |
511 | |
512 | void KBlocksNetServer::formByteFromInt(int value, char * data) |
513 | { |
514 | data[0] = value & 0x00FF; |
515 | data[1] = (value >> 8) & 0x00FF; |
516 | data[2] = (value >> 16) & 0x00FF; |
517 | data[3] = (value >> 24) & 0x00FF; |
518 | } |
519 | |
520 | void KBlocksNetServer::printGameResult() |
521 | { |
522 | printf("-------- Game Report --------\n" ); |
523 | for(int gameID = 0; gameID < mGameCount; gameID++) |
524 | { |
525 | QString tmpPlayerName = mPlayerName[gameID]; |
526 | printf("Game ID : %d\n" , gameID); |
527 | printf("\tPlayer Name : %s\n" , tmpPlayerName.toAscii().constData()); |
528 | printf("\tTotal Score : %d\n" , maGameScoreList[gameID]->getLineCount()); |
529 | } |
530 | printf("-----------------------------\n" ); |
531 | } |
532 | |
533 | |