GCC Code Coverage Report
Directory: src/ Exec Total Coverage
File: src/net/ea/network.cpp Lines: 0 158 0.0 %
Date: 2017-11-29 Branches: 0 142 0.0 %

Line Branch Exec Source
1
/*
2
 *  The ManaPlus Client
3
 *  Copyright (C) 2004-2009  The Mana World Development Team
4
 *  Copyright (C) 2009-2010  The Mana Developers
5
 *  Copyright (C) 2011-2017  The ManaPlus Developers
6
 *
7
 *  This file is part of The ManaPlus Client.
8
 *
9
 *  This program is free software; you can redistribute it and/or modify
10
 *  it under the terms of the GNU General Public License as published by
11
 *  the Free Software Foundation; either version 2 of the License, or
12
 *  any later version.
13
 *
14
 *  This program is distributed in the hope that it will be useful,
15
 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
16
 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
17
 *  GNU General Public License for more details.
18
 *
19
 *  You should have received a copy of the GNU General Public License
20
 *  along with this program.  If not, see <http://www.gnu.org/licenses/>.
21
 */
22
23
#include "net/ea/network.h"
24
25
#include "configuration.h"
26
#include "logger.h"
27
28
#include "net/packetinfo.h"
29
30
#include "utils/cast.h"
31
#include "utils/delete2.h"
32
#include "utils/gettext.h"
33
#include "utils/sdlhelper.h"
34
35
#include <sstream>
36
37
#include "debug.h"
38
39
#ifndef SDL_BIG_ENDIAN
40
#error missing SDL_endian.h
41
#endif  // SDL_BYTEORDER
42
43
extern unsigned int mLastHost;
44
45
namespace Ea
46
{
47
48
const unsigned int BUFFER_SIZE = 1000000;
49
const unsigned int BUFFER_LIMIT = 930000;
50
51
int networkThread(void *data)
52
{
53
    Network *const network = static_cast<Network *>(data);
54
55
    if ((network == nullptr) || !network->realConnect())
56
        return -1;
57
58
    network->receive();
59
60
    return 0;
61
}
62
63
Network::Network() :
64
    mSocket(nullptr),
65
    mServer(),
66
    mPackets(nullptr),
67
    mInBuffer(new char[BUFFER_SIZE]),
68
    mOutBuffer(new char[BUFFER_SIZE]),
69
    mInSize(0),
70
    mOutSize(0),
71
    mToSkip(0),
72
    mState(IDLE),
73
    mError(),
74
    mWorkerThread(nullptr),
75
    mMutexIn(SDL_CreateMutex()),
76
    mMutexOut(SDL_CreateMutex()),
77
    mSleep(config.getIntValue("networksleep")),
78
    mPauseDispatch(false)
79
{
80
    TcpNet::init();
81
}
82
83
Network::~Network()
84
{
85
    if (mState != IDLE && mState != NET_ERROR)
86
        disconnect();
87
88
    SDL_DestroyMutex(mMutexIn);
89
    mMutexIn = nullptr;
90
    SDL_DestroyMutex(mMutexOut);
91
    mMutexOut = nullptr;
92
93
    delete2Arr(mInBuffer);
94
    delete2Arr(mOutBuffer);
95
    delete2Arr(mPackets);
96
97
    TcpNet::quit();
98
}
99
100
bool Network::connect(const ServerInfo &server)
101
{
102
    if (mState != IDLE && mState != NET_ERROR)
103
    {
104
        logger->log1("Tried to connect an already connected socket!");
105
        return false;
106
    }
107
108
    if (server.hostname.empty())
109
    {
110
        // TRANSLATORS: error message
111
        setError(_("Empty address given to Network::connect()!"));
112
        return false;
113
    }
114
115
    logger->log("Network::Connecting to %s:%i",
116
        server.hostname.c_str(), server.port);
117
118
    mServer.hostname = server.hostname;
119
    mServer.althostname = server.althostname;
120
    mServer.port = server.port;
121
122
    // Reset to sane values
123
    mOutSize = 0;
124
    mInSize = 0;
125
    mToSkip = 0;
126
127
    mState = CONNECTING;
128
    mWorkerThread = SDL::createThread(&networkThread, "network", this);
129
    if (mWorkerThread == nullptr)
130
    {
131
        setError("Unable to create network worker thread");
132
        return false;
133
    }
134
135
    return true;
136
}
137
138
void Network::disconnect()
139
{
140
    BLOCK_START("Network::disconnect")
141
    mState = IDLE;
142
143
    SDL::WaitThread(mWorkerThread);
144
    mWorkerThread = nullptr;
145
146
    if (mSocket != nullptr)
147
    {
148
        TcpNet::closeSocket(mSocket);
149
        mSocket = nullptr;
150
        if (mSleep > 0)
151
            SDL_Delay(mSleep);
152
    }
153
    BLOCK_END("Network::disconnect")
154
}
155
156
void Network::flush()
157
{
158
    if ((mOutSize == 0u) || mState != CONNECTED)
159
        return;
160
161
    SDL_mutexP(mMutexOut);
162
    const int ret = TcpNet::send(mSocket, mOutBuffer, mOutSize);
163
/*
164
    if (logger)
165
    {
166
        logger->dlog(std::string("Send ").append(
167
            toString(mOutSize)).append(" bytes"));
168
    }
169
*/
170
    if (ret < CAST_S32(mOutSize))
171
    {
172
        SDL_mutexV(mMutexOut);
173
        setError("Error in TcpNet::send(): " +
174
            std::string(TcpNet::getError()));
175
    }
176
    mOutSize = 0;
177
    SDL_mutexV(mMutexOut);
178
}
179
180
void Network::skip(const int len)
181
{
182
    SDL_mutexP(mMutexIn);
183
    mToSkip += len;
184
    if (mInSize == 0u)
185
    {
186
        SDL_mutexV(mMutexIn);
187
        return;
188
    }
189
190
    if (mInSize >= mToSkip)
191
    {
192
        mInSize -= mToSkip;
193
        memmove(mInBuffer, mInBuffer + CAST_SIZE(mToSkip), mInSize);
194
        mToSkip = 0;
195
    }
196
    else
197
    {
198
        mToSkip -= mInSize;
199
        mInSize = 0;
200
    }
201
    SDL_mutexV(mMutexIn);
202
}
203
204
bool Network::realConnect()
205
{
206
    IPaddress ipAddress;
207
208
    if (TcpNet::resolveHost(&ipAddress, mServer.hostname.c_str(),
209
        mServer.port) == -1)
210
    {
211
        if (mServer.althostname.empty() || TcpNet::resolveHost(&ipAddress,
212
            mServer.althostname.c_str(), mServer.port) == -1)
213
        {
214
            const std::string errorMessage = std::string(
215
                // TRANSLATORS: error message
216
                _("Unable to resolve host \"")).append(
217
                mServer.hostname).append("\".");
218
            setError(errorMessage);
219
            logger->log_r("TcpNet::ResolveHost: %s", errorMessage.c_str());
220
            return false;
221
        }
222
        logger->log_r("using alt host name: %s", mServer.althostname.c_str());
223
    }
224
225
    mState = CONNECTING;
226
227
    mSocket = TcpNet::open(&ipAddress);
228
    if (mSocket == nullptr)
229
    {
230
        logger->log_r("Error in TcpNet::open(): %s", TcpNet::getError());
231
        setError(TcpNet::getError());
232
        return false;
233
    }
234
235
    mLastHost = ipAddress.host;
236
    logger->log_r("Network::Started session with %s:%i",
237
        ipToString(ipAddress.host), ipAddress.port);
238
239
    mState = CONNECTED;
240
    return true;
241
}
242
243
void Network::receive()
244
{
245
    TcpNet::SocketSet set;
246
247
    if ((set = TcpNet::allocSocketSet(1)) == nullptr)
248
    {
249
        setError("Error in TcpNet::allocSocketSet(): " +
250
            std::string(TcpNet::getError()));
251
        return;
252
    }
253
254
    if (TcpNet::addSocket(set, mSocket) == -1)
255
    {
256
        setError("Error in TcpNet::addSocket(): " +
257
            std::string(TcpNet::getError()));
258
    }
259
260
    while (mState == CONNECTED)
261
    {
262
        const int numReady = TcpNet::checkSockets(
263
            set, (CAST_U32(500)));
264
        switch (numReady)
265
        {
266
            case -1:
267
                logger->log_r("Error: TcpNet::checkSockets");
268
                break;
269
                // FALLTHROUGH
270
            case 0:
271
                break;
272
273
            case 1:
274
            {
275
                // Receive data from the socket
276
                SDL_mutexP(mMutexIn);
277
                if (mInSize > BUFFER_LIMIT)
278
                {
279
                    SDL_mutexV(mMutexIn);
280
                    SDL_Delay(100);
281
                    continue;
282
                }
283
284
                const int ret = TcpNet::recv(mSocket,
285
                    mInBuffer + CAST_SIZE(mInSize),
286
                    BUFFER_SIZE - mInSize);
287
288
                if (ret == 0)
289
                {
290
                    // We got disconnected
291
                    mState = IDLE;
292
                    logger->log_r("Disconnected.");
293
                }
294
                else if (ret < 0)
295
                {
296
                    // TRANSLATORS: error message
297
                    setError(_("Connection to server terminated. ") +
298
                             std::string(TcpNet::getError()));
299
                }
300
                else
301
                {
302
//                    DEBUGLOG("Receive " + toString(ret) + " bytes");
303
                    mInSize += ret;
304
                    if (mToSkip != 0u)
305
                    {
306
                        if (mInSize >= mToSkip)
307
                        {
308
                            mInSize -= mToSkip;
309
                            memmove(mInBuffer,
310
                                mInBuffer + CAST_SIZE(mToSkip),
311
                                mInSize);
312
                            mToSkip = 0;
313
                        }
314
                        else
315
                        {
316
                            mToSkip -= mInSize;
317
                            mInSize = 0;
318
                        }
319
                    }
320
                }
321
                SDL_mutexV(mMutexIn);
322
                break;
323
            }
324
325
            default:
326
                // more than one socket is ready..
327
                // this should not happen since we only listen once socket.
328
                std::stringstream errorStream;
329
                errorStream << "Error in TcpNet::recv(), " << numReady
330
                            << " sockets are ready: " << TcpNet::getError();
331
                setError(errorStream.str());
332
                break;
333
        }
334
    }
335
336
    if (TcpNet::delSocket(set, mSocket) == -1)
337
        logger->log_r("Error in TcpNet::delSocket(): %s", TcpNet::getError());
338
339
    TcpNet::freeSocketSet(set);
340
}
341
342
void Network::setError(const std::string &error)
343
{
344
    logger->log_r("Network error: %s", error.c_str());
345
    mError = error;
346
    mState = NET_ERROR;
347
}
348
349
uint16_t Network::readWord(const int pos) const
350
{
351
#if SDL_BYTEORDER == SDL_BIG_ENDIAN
352
    return SDL_Swap16(*reinterpret_cast<uint16_t*>(
353
        mInBuffer + CAST_SIZE(pos)));
354
#else  // SDL_BYTEORDER == SDL_BIG_ENDIAN
355
356
    return (*reinterpret_cast<uint16_t*>(
357
        mInBuffer + CAST_SIZE(pos)));
358
#endif  // SDL_BYTEORDER == SDL_BIG_ENDIAN
359
}
360
361
void Network::fixSendBuffer()
362
{
363
    if (mOutSize > BUFFER_LIMIT)
364
    {
365
        if (mState != CONNECTED)
366
            mOutSize = 0;
367
        else
368
            flush();
369
    }
370
}
371
372
}  // namespace Ea