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
20KBlocksNetServer::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
41KBlocksNetServer::~KBlocksNetServer()
42{
43 delete mpServerSocket;
44}
45
46int 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
125void KBlocksNetServer::setSendLength(int initLen, int lvUpLen)
126{
127 mInitSendLength = initLen;
128 mLvUpSendLength = lvUpLen;
129}
130
131void KBlocksNetServer::setRecordFile(const char * fileName, bool binaryMode)
132{
133 mRecordFileName = string(fileName);
134 mRecordFileType = binaryMode;
135}
136
137void 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
163int 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
218void 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
229void 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
238void 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
274void 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
324void 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
343void 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
390int 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
445int 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
498bool 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
506QString KBlocksNetServer::formIPString(const QHostAddress& inAddr, quint16 inPort)
507{
508 QString result = inAddr.toString() + QString(":%1").arg(inPort);
509 return result;
510}
511
512void 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
520void 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