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