QTfrontend/net/tcpBase.cpp
branchsdl2transition
changeset 11342 ed5a6478e710
parent 11046 47a8c19ecb60
child 11071 3851ce4f2061
child 12829 c75781937859
equal deleted inserted replaced
11340:31570b766315 11342:ed5a6478e710
     1 /*
     1 /*
     2  * Hedgewars, a free turn based strategy game
     2  * Hedgewars, a free turn based strategy game
     3  * Copyright (c) 2006-2007 Igor Ulyanov <iulyanov@gmail.com>
     3  * Copyright (c) 2006-2007 Igor Ulyanov <iulyanov@gmail.com>
     4  * Copyright (c) 2004-2013 Andrey Korotaev <unC0Rr@gmail.com>
     4  * Copyright (c) 2004-2015 Andrey Korotaev <unC0Rr@gmail.com>
     5  *
     5  *
     6  * This program is free software; you can redistribute it and/or modify
     6  * This program is free software; you can redistribute it and/or modify
     7  * it under the terms of the GNU General Public License as published by
     7  * it under the terms of the GNU General Public License as published by
     8  * the Free Software Foundation; version 2 of the License
     8  * the Free Software Foundation; version 2 of the License
     9  *
     9  *
    12  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    12  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    13  * GNU General Public License for more details.
    13  * GNU General Public License for more details.
    14  *
    14  *
    15  * You should have received a copy of the GNU General Public License
    15  * You should have received a copy of the GNU General Public License
    16  * along with this program; if not, write to the Free Software
    16  * along with this program; if not, write to the Free Software
    17  * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA
    17  * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
    18  */
    18  */
    19 
       
    20 #include "tcpBase.h"
       
    21 
    19 
    22 #include <QList>
    20 #include <QList>
    23 #include <QImage>
    21 #include <QImage>
    24 #include <QThread>
    22 #include <QThread>
    25 
    23 #include <QApplication>
       
    24 
       
    25 #include "tcpBase.h"
    26 #include "hwconsts.h"
    26 #include "hwconsts.h"
    27 #include "MessageDialog.h"
    27 #include "MessageDialog.h"
    28 
    28 
    29 #ifdef HWLIBRARY
    29 #ifdef HWLIBRARY
    30 extern "C" void Game(char**arguments);
    30 extern "C" {
    31 extern "C" void GenLandPreview(int port);
    31     void RunEngine(int argc, char ** argv);
       
    32 
       
    33     int operatingsystem_parameter_argc;
       
    34     char ** operatingsystem_parameter_argv;
       
    35 }
    32 
    36 
    33 
    37 
    34 EngineInstance::EngineInstance(QObject *parent)
    38 EngineInstance::EngineInstance(QObject *parent)
    35     : QObject(parent)
    39     : QObject(parent)
    36 {
    40 {
    37     port = 0;
    41 
    38 }
    42 }
    39 
    43 
    40 EngineInstance::~EngineInstance()
    44 EngineInstance::~EngineInstance()
    41 {
    45 {
       
    46     qDebug() << "EngineInstance delete" << QThread::currentThreadId();
       
    47 }
       
    48 
       
    49 void EngineInstance::setArguments(const QStringList & arguments)
       
    50 {
       
    51     m_arguments.clear();
       
    52     m_arguments << qApp->arguments().at(0).toUtf8();
       
    53 
       
    54     m_argv.resize(arguments.size() + 1);
       
    55     m_argv[0] = m_arguments.last().data();
       
    56 
       
    57     int i = 1;
       
    58     foreach(const QString & s, arguments)
       
    59     {
       
    60         m_arguments << s.toUtf8();
       
    61         m_argv[i] = m_arguments.last().data();
       
    62         ++i;
       
    63     }
    42 }
    64 }
    43 
    65 
    44 void EngineInstance::start()
    66 void EngineInstance::start()
    45 {
    67 {
    46 #if 0
    68     qDebug() << "EngineInstance start" << QThread::currentThreadId();
    47     char *args[11];
    69 
    48     args[0] = "65000";  //ipcPort
    70     RunEngine(m_argv.size(), m_argv.data());
    49     args[1] = "1024";   //cScreenWidth
    71 
    50     args[2] = "768";    //cScreenHeight
    72     emit finished();
    51     args[3] = "0";      //cReducedQuality
       
    52     args[4] = "en.txt"; //cLocaleFName
       
    53     args[5] = "koda";   //UserNick
       
    54     args[6] = "1";      //SetSound
       
    55     args[7] = "1";      //SetMusic
       
    56     args[8] = "0";      //cAltDamage
       
    57     args[9]= datadir->absolutePath().toAscii().data(); //cPathPrefix
       
    58     args[10]= NULL;     //recordFileName
       
    59     Game(args);
       
    60 #endif
       
    61     GenLandPreview(port);
       
    62 }
    73 }
    63 
    74 
    64 #endif
    75 #endif
    65 
    76 
    66 QList<TCPBase*> srvsList;
    77 QList<TCPBase*> srvsList;
    67 QPointer<QTcpServer> TCPBase::IPCServer(0);
    78 QPointer<QTcpServer> TCPBase::IPCServer(0);
    68 
    79 
    69 TCPBase::~TCPBase()
    80 TCPBase::~TCPBase()
    70 {
    81 {
       
    82     if(m_hasStarted)
       
    83     {
       
    84         if(IPCSocket)
       
    85             IPCSocket->close();
       
    86 
       
    87         if(m_connected)
       
    88         {
       
    89 #ifdef HWLIBRARY
       
    90             if(!thread)
       
    91                 qDebug("WTF");
       
    92             thread->quit();
       
    93             thread->wait();
       
    94 #else
       
    95             process->waitForFinished(1000);
       
    96 #endif
       
    97         }
       
    98     }
    71     // make sure this object is not in the server list anymore
    99     // make sure this object is not in the server list anymore
    72     srvsList.removeOne(this);
   100     srvsList.removeOne(this);
    73 
   101 
    74     if (IPCSocket)
   102     if (IPCSocket)
    75         IPCSocket->deleteLater();
   103         IPCSocket->deleteLater();
    78 
   106 
    79 TCPBase::TCPBase(bool demoMode, QObject *parent) :
   107 TCPBase::TCPBase(bool demoMode, QObject *parent) :
    80     QObject(parent),
   108     QObject(parent),
    81     m_hasStarted(false),
   109     m_hasStarted(false),
    82     m_isDemoMode(demoMode),
   110     m_isDemoMode(demoMode),
       
   111     m_connected(false),
    83     IPCSocket(0)
   112     IPCSocket(0)
    84 {
   113 {
       
   114     process = 0;
       
   115 
    85     if(!IPCServer)
   116     if(!IPCServer)
    86     {
   117     {
    87         IPCServer = new QTcpServer(0);
   118         IPCServer = new QTcpServer(0);
    88         IPCServer->setMaxPendingConnections(1);
   119         IPCServer->setMaxPendingConnections(1);
    89         if (!IPCServer->listen(QHostAddress::LocalHost))
   120         if (!IPCServer->listen(QHostAddress::LocalHost))
   101     if(IPCSocket)
   132     if(IPCSocket)
   102     {
   133     {
   103         // connection should be already finished
   134         // connection should be already finished
   104         return;
   135         return;
   105     }
   136     }
       
   137 
   106     disconnect(IPCServer, SIGNAL(newConnection()), this, SLOT(NewConnection()));
   138     disconnect(IPCServer, SIGNAL(newConnection()), this, SLOT(NewConnection()));
   107     IPCSocket = IPCServer->nextPendingConnection();
   139     IPCSocket = IPCServer->nextPendingConnection();
       
   140 
   108     if(!IPCSocket) return;
   141     if(!IPCSocket) return;
       
   142 
       
   143     m_connected = true;
       
   144 
   109     connect(IPCSocket, SIGNAL(disconnected()), this, SLOT(ClientDisconnect()));
   145     connect(IPCSocket, SIGNAL(disconnected()), this, SLOT(ClientDisconnect()));
   110     connect(IPCSocket, SIGNAL(readyRead()), this, SLOT(ClientRead()));
   146     connect(IPCSocket, SIGNAL(readyRead()), this, SLOT(ClientRead()));
   111     SendToClientFirst();
   147     SendToClientFirst();
       
   148 
       
   149     if(simultaneousRun())
       
   150     {
       
   151         srvsList.removeOne(this);
       
   152         emit isReadyNow();
       
   153     }
   112 }
   154 }
   113 
   155 
   114 void TCPBase::RealStart()
   156 void TCPBase::RealStart()
   115 {
   157 {
   116     connect(IPCServer, SIGNAL(newConnection()), this, SLOT(NewConnection()));
   158     connect(IPCServer, SIGNAL(newConnection()), this, SLOT(NewConnection()));
   117     IPCSocket = 0;
   159     IPCSocket = 0;
   118 
   160 
   119 #ifdef HWLIBRARY
   161 #ifdef HWLIBRARY
   120     QThread *thread = new QThread;
   162     thread = new QThread(this);
   121     EngineInstance *instance = new EngineInstance;
   163     EngineInstance *instance = new EngineInstance();
   122     instance->port = IPCServer->serverPort();
   164     instance->setArguments(getArguments());
   123 
   165 
   124     instance->moveToThread(thread);
   166     instance->moveToThread(thread);
   125 
   167 
   126     connect(thread, SIGNAL(started()), instance, SLOT(start(void)));
   168     connect(thread, SIGNAL(started()), instance, SLOT(start(void)));
   127     connect(instance, SIGNAL(finished()), thread, SLOT(quit()));
   169     connect(instance, SIGNAL(finished()), thread, SLOT(quit()));
   128     connect(instance, SIGNAL(finished()), instance, SLOT(deleteLater()));
   170     connect(instance, SIGNAL(finished()), instance, SLOT(deleteLater()));
   129     connect(instance, SIGNAL(finished()), thread, SLOT(deleteLater()));
   171     connect(instance, SIGNAL(finished()), thread, SLOT(deleteLater()));
   130     thread->start();
   172     thread->start();
   131 #else
   173 #else
   132     QProcess * process;
   174     process = new QProcess(this);
   133     process = new QProcess();
   175     connect(process, SIGNAL(error(QProcess::ProcessError)),
   134     connect(process, SIGNAL(error(QProcess::ProcessError)), this, SLOT(StartProcessError(QProcess::ProcessError)));
   176         this, SLOT(StartProcessError(QProcess::ProcessError)));
   135     QStringList arguments=getArguments();
   177     connect(process, SIGNAL(finished(int, QProcess::ExitStatus)),
       
   178         this, SLOT(onEngineDeath(int, QProcess::ExitStatus)));
       
   179     QStringList arguments = getArguments();
   136 
   180 
   137 #ifdef QT_DEBUG
   181 #ifdef QT_DEBUG
   138     // redirect everything written on stdout/stderr
   182     // redirect everything written on stdout/stderr
   139     process->setProcessChannelMode(QProcess::ForwardedChannels);
   183     process->setProcessChannelMode(QProcess::ForwardedChannels);
   140 #endif
   184 #endif
   147 void TCPBase::ClientDisconnect()
   191 void TCPBase::ClientDisconnect()
   148 {
   192 {
   149     disconnect(IPCSocket, SIGNAL(readyRead()), this, SLOT(ClientRead()));
   193     disconnect(IPCSocket, SIGNAL(readyRead()), this, SLOT(ClientRead()));
   150     onClientDisconnect();
   194     onClientDisconnect();
   151 
   195 
   152     emit isReadyNow();
   196     if(!simultaneousRun())
       
   197     {
       
   198 #ifdef HWLIBRARY
       
   199         thread->quit();
       
   200         thread->wait();
       
   201 #endif
       
   202         emit isReadyNow();
       
   203     }
       
   204 
   153     IPCSocket->deleteLater();
   205     IPCSocket->deleteLater();
       
   206     IPCSocket = NULL;
   154 
   207 
   155     deleteLater();
   208     deleteLater();
   156 }
   209 }
   157 
   210 
   158 void TCPBase::ClientRead()
   211 void TCPBase::ClientRead()
   159 {
   212 {
   160     QByteArray readed=IPCSocket->readAll();
   213     QByteArray read = IPCSocket->readAll();
   161     if(readed.isEmpty()) return;
   214     if(read.isEmpty()) return;
   162     readbuffer.append(readed);
   215     readbuffer.append(read);
   163     onClientRead();
   216     onClientRead();
   164 }
   217 }
   165 
   218 
   166 void TCPBase::StartProcessError(QProcess::ProcessError error)
   219 void TCPBase::StartProcessError(QProcess::ProcessError error)
   167 {
   220 {
   168     MessageDialog::ShowFatalMessage(tr("Unable to run engine at %1\nError code: %2").arg(bindir->absolutePath() + "/hwengine").arg(error));
   221     MessageDialog::ShowFatalMessage(tr("Unable to run engine at %1\nError code: %2").arg(bindir->absolutePath() + "/hwengine").arg(error));
   169     ClientDisconnect();
   222     ClientDisconnect();
       
   223 }
       
   224 
       
   225 void TCPBase::onEngineDeath(int exitCode, QProcess::ExitStatus exitStatus)
       
   226 {
       
   227     Q_UNUSED(exitStatus);
       
   228 
       
   229     ClientDisconnect();
       
   230 
       
   231     // show error message if there was an error that was not an engine's
       
   232     // fatal error - because that one already sent a info via IPC
       
   233     if ((exitCode != 0) && (exitCode != 2))
       
   234     {
       
   235         // inform user that something bad happened
       
   236         MessageDialog::ShowFatalMessage(
       
   237             tr("The game engine died unexpectedly!\n"
       
   238             "(exit code %1)\n\n"
       
   239             "We are very sorry for the inconvenience :(\n\n"
       
   240             "If this keeps happening, please click the '%2' button in the main menu!")
       
   241             .arg(exitCode)
       
   242             .arg("Feedback"));
       
   243 
       
   244     }
       
   245 
       
   246     // cleanup up
       
   247     if (IPCSocket)
       
   248     {
       
   249         IPCSocket->close();
       
   250         IPCSocket->deleteLater();
       
   251     }
       
   252 
       
   253     // plot suicide
       
   254     deleteLater();
   170 }
   255 }
   171 
   256 
   172 void TCPBase::tcpServerReady()
   257 void TCPBase::tcpServerReady()
   173 {
   258 {
   174     disconnect(srvsList.first(), SIGNAL(isReadyNow()), this, SLOT(tcpServerReady()));
   259     disconnect(srvsList.first(), SIGNAL(isReadyNow()), this, SLOT(tcpServerReady()));
   186     else
   271     else
   187     {
   272     {
   188         TCPBase * last = srvsList.last();
   273         TCPBase * last = srvsList.last();
   189         if(couldCancelPreviousRequest
   274         if(couldCancelPreviousRequest
   190             && last->couldBeRemoved()
   275             && last->couldBeRemoved()
       
   276             && (last->isConnected() || !last->hasStarted())
   191             && (last->parent() == parent()))
   277             && (last->parent() == parent()))
   192         {
   278         {
   193             srvsList.removeLast();
   279             srvsList.removeLast();
   194             last->deleteLater();
   280             delete last;
   195             Start(couldCancelPreviousRequest);
   281             Start(couldCancelPreviousRequest);
   196         } else
   282         } else
   197         {
   283         {
   198             connect(srvsList.last(), SIGNAL(isReadyNow()), this, SLOT(tcpServerReady()));
   284             connect(last, SIGNAL(isReadyNow()), this, SLOT(tcpServerReady()));
   199             srvsList.push_back(this);
   285             srvsList.push_back(this);
   200         }
   286         }
   201     }
   287     }
   202 }
   288 }
   203 
   289 
   244 
   330 
   245 bool TCPBase::couldBeRemoved()
   331 bool TCPBase::couldBeRemoved()
   246 {
   332 {
   247     return false;
   333     return false;
   248 }
   334 }
       
   335 
       
   336 bool TCPBase::isConnected()
       
   337 {
       
   338     return m_connected;
       
   339 }
       
   340 
       
   341 bool TCPBase::simultaneousRun()
       
   342 {
       
   343     return false;
       
   344 }
       
   345 
       
   346 bool TCPBase::hasStarted()
       
   347 {
       
   348     return m_hasStarted;
       
   349 }