ManaPlus
network.cpp
Go to the documentation of this file.
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 
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 
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 
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;
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 
140 {
141  BLOCK_START("Network::disconnect")
142  mState = IDLE;
143 
145  mWorkerThread = nullptr;
146 
147  if (mSocket != nullptr)
148  {
150  mSocket = nullptr;
151  if (mSleep > 0)
152  SDL_Delay(mSleep);
153  }
154  BLOCK_END("Network::disconnect")
155 }
156 
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;
195  mToSkip = 0;
196  }
197  else
198  {
199  mToSkip -= mInSize;
200  mInSize = 0;
201  }
202  SDL_mutexV(mMutexIn);
203 }
204 
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("\".");
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());
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 
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,
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,
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 
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 
363 {
364  if (mOutSize > BUFFER_LIMIT)
365  {
366  if (mState != CONNECTED)
367  mOutSize = 0;
368  else
369  flush();
370  }
371 }
372 
373 } // namespace Ea
#define CAST_S32
Definition: cast.h:30
#define CAST_U32
Definition: cast.h:31
#define CAST_SIZE
Definition: cast.h:34
void receive()
Definition: network.cpp:244
unsigned int mToSkip
Definition: network.h:108
SDL_Thread * mWorkerThread
Definition: network.h:113
void flush()
Definition: network.cpp:157
SDL_mutex * mMutexIn
Definition: network.h:114
friend int networkThread(void *data)
Definition: network.cpp:52
int mSleep
Definition: network.h:116
SDL_mutex * mMutexOut
Definition: network.h:115
@ NET_ERROR
Definition: network.h:83
@ CONNECTED
Definition: network.h:80
@ CONNECTING
Definition: network.h:81
bool connect(const ServerInfo &server)
Definition: network.cpp:101
virtual ~Network()
Definition: network.cpp:84
bool realConnect()
Definition: network.cpp:205
uint16_t readWord(const int pos) const
Definition: network.cpp:350
ServerInfo mServer
Definition: network.h:99
std::string mError
Definition: network.h:111
unsigned int mOutSize
Definition: network.h:106
int mState
Definition: network.h:110
PacketInfo * mPackets
Definition: network.h:101
void fixSendBuffer()
Definition: network.cpp:362
char * mInBuffer
Definition: network.h:103
void setError(const std::string &error)
Definition: network.cpp:343
char * mOutBuffer
Definition: network.h:104
unsigned int mInSize
Definition: network.h:105
void skip(const int len)
Definition: network.cpp:181
TcpNet::Socket mSocket
Definition: network.h:97
void disconnect()
Definition: network.cpp:139
void log(const char *const log_text,...)
Definition: logger.cpp:269
void log_r(const char *const log_text,...)
Definition: logger.cpp:365
void log1(const char *const log_text)
Definition: logger.cpp:238
std::string hostname
Definition: serverinfo.h:45
std::string althostname
Definition: serverinfo.h:46
uint16_t port
Definition: serverinfo.h:58
Configuration config
#define new
Definition: debug_new.h:147
#define delete2Arr(var)
Definition: delete2.h:31
std::string errorMessage
Definition: client.cpp:116
unsigned int mLastHost
Definition: client.cpp:136
#define _(s)
Definition: gettext.h:35
#define nullptr
Definition: localconsts.h:45
Logger * logger
Definition: logger.cpp:89
uint32_t data
bool error(InputEvent &event) __attribute__((noreturn))
Definition: actions.cpp:82
const unsigned int BUFFER_SIZE
Definition: network.cpp:49
const unsigned int BUFFER_LIMIT
Definition: network.cpp:50
int networkThread(void *data)
Definition: network.cpp:52
SDL_Thread * createThread(int(*fn)(void *), const char *const name, void *const data)
Definition: sdlhelper.cpp:118
void WaitThread(SDL_Thread *const thread)
Definition: sdlhelper.cpp:195
int delSocket(const TcpNet::SocketSet set, const TcpNet::Socket sock)
Definition: sdltcpnet.cpp:185
::SDLNet_SocketSet SocketSet
Definition: sdltcpnet.h:37
int send(const TcpNet::Socket sock, const void *const data, const int len)
Definition: sdltcpnet.cpp:93
int resolveHost(IPaddress *const address, const char *const host, const Uint16 port)
Definition: sdltcpnet.cpp:104
void quit()
Definition: sdltcpnet.cpp:83
void freeSocketSet(const TcpNet::SocketSet set)
Definition: sdltcpnet.cpp:193
TcpNet::Socket open(IPaddress *const ip)
Definition: sdltcpnet.cpp:110
void init()
Definition: sdltcpnet.cpp:78
int addSocket(const TcpNet::SocketSet set, const TcpNet::Socket sock)
Definition: sdltcpnet.cpp:156
SocketSet allocSocketSet(const int maxsockets)
Definition: sdltcpnet.cpp:151
const char * getError()
Definition: sdltcpnet.cpp:99
int checkSockets(const TcpNet::SocketSet set, const Uint32 timeout)
Definition: sdltcpnet.cpp:175
void closeSocket(const TcpNet::Socket socket)
Definition: sdltcpnet.cpp:88
int recv(const TcpNet::Socket sock, void *const data, const int maxlen)
Definition: sdltcpnet.cpp:180
#define BLOCK_END(name)
Definition: perfomance.h:80
#define BLOCK_START(name)
Definition: perfomance.h:79
const char * ipToString(const uint32_t address)
Definition: stringutils.cpp:86