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(); |
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 |