Mana
Loading...
Searching...
No Matches
network.cpp
Go to the documentation of this file.
1/*
2 * The Mana Client
3 * Copyright (C) 2004-2009 The Mana World Development Team
4 * Copyright (C) 2009-2012 The Mana Developers
5 *
6 * This file is part of The Mana Client.
7 *
8 * This program is free software; you can redistribute it and/or modify
9 * it under the terms of the GNU General Public License as published by
10 * the Free Software Foundation; either version 2 of the License, or
11 * any later version.
12 *
13 * This program is distributed in the hope that it will be useful,
14 * but WITHOUT ANY WARRANTY; without even the implied warranty of
15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16 * GNU General Public License for more details.
17 *
18 * You should have received a copy of the GNU General Public License
19 * along with this program. If not, see <http://www.gnu.org/licenses/>.
20 */
21
22#include "net/tmwa/network.h"
23
24#include "log.h"
25
26#include "net/tmwa/messagein.h"
27#include "net/tmwa/protocol.h"
28
29#include "utils/gettext.h"
30#include "utils/stringutils.h"
31
32#include <cassert>
33#include <sstream>
34
38namespace TmwAthena {
39
41{
42 uint16_t id;
43 uint16_t length;
44 const char *name;
45};
46
47// indicator for a variable-length packet
48constexpr uint16_t VAR = 1;
49
50static constexpr PacketInfo packet_infos[] = {
51 // login server messages
52 { SMSG_UPDATE_HOST, VAR, "SMSG_UPDATE_HOST" },
53 { CMSG_LOGIN_REGISTER, 55, "CMSG_LOGIN_REGISTER" },
54 { SMSG_LOGIN_DATA, VAR, "SMSG_LOGIN_DATA" },
55 { SMSG_LOGIN_ERROR, 23, "SMSG_LOGIN_ERROR" },
56
57 // char server messages
58 { CMSG_CHAR_PASSWORD_CHANGE, 50, "CMSG_CHAR_PASSWORD_CHANGE" },
59 { SMSG_CHAR_PASSWORD_RESPONSE, 3, "SMSG_CHAR_PASSWORD_RESPONSE" },
60 { CMSG_CHAR_SERVER_CONNECT, 17, "CMSG_CHAR_SERVER_CONNECT" },
61 { CMSG_CHAR_SELECT, 3, "CMSG_CHAR_SELECT" },
62 { CMSG_CHAR_CREATE, 37, "CMSG_CHAR_CREATE" },
63 { CMSG_CHAR_DELETE, 46, "CMSG_CHAR_DELETE" },
64 { SMSG_CHAR_LOGIN, VAR, "SMSG_CHAR_LOGIN" },
65 { SMSG_CHAR_LOGIN_ERROR, 3, "SMSG_CHAR_LOGIN_ERROR" },
66 { SMSG_CHAR_CREATE_SUCCEEDED, 108, "SMSG_CHAR_CREATE_SUCCEEDED" },
67 { SMSG_CHAR_CREATE_FAILED, 3, "SMSG_CHAR_CREATE_FAILED" },
68 { SMSG_CHAR_DELETE_SUCCEEDED, 2, "SMSG_CHAR_DELETE_SUCCEEDED" },
69 { SMSG_CHAR_DELETE_FAILED, 3, "SMSG_CHAR_DELETE_FAILED" },
70 { SMSG_CHAR_MAP_INFO, 28, "SMSG_CHAR_MAP_INFO" },
71
72 // map server messages
73 { CMSG_MAP_SERVER_CONNECT, 19, "CMSG_MAP_SERVER_CONNECT" },
74 { SMSG_MAP_LOGIN_SUCCESS, 11, "SMSG_MAP_LOGIN_SUCCESS" },
75 { SMSG_BEING_VISIBLE, 54, "SMSG_BEING_VISIBLE" },
76 { SMSG_BEING_MOVE, 60, "SMSG_BEING_MOVE" },
77 { SMSG_BEING_SPAWN, 41, "SMSG_BEING_SPAWN" },
78 { CMSG_MAP_LOADED, 2, "CMSG_MAP_LOADED" },
79 { CMSG_MAP_PING, 6, "CMSG_MAP_PING" },
80 { SMSG_SERVER_PING, 6, "SMSG_SERVER_PING" },
81 { SMSG_BEING_REMOVE, 7, "SMSG_BEING_REMOVE" },
82 { CMSG_PLAYER_CHANGE_DEST, 5, "CMSG_PLAYER_CHANGE_DEST" },
83 { SMSG_WALK_RESPONSE, 12, "SMSG_WALK_RESPONSE" },
84 { SMSG_PLAYER_STOP, 10, "SMSG_PLAYER_STOP" },
85 { CMSG_PLAYER_CHANGE_ACT, 7, "CMSG_PLAYER_CHANGE_ACT" },
86 { SMSG_BEING_ACTION, 29, "SMSG_BEING_ACTION" },
87 { CMSG_CHAT_MESSAGE, VAR, "CMSG_CHAT_MESSAGE" },
88 { SMSG_BEING_CHAT, VAR, "SMSG_BEING_CHAT" },
89 { SMSG_PLAYER_CHAT, VAR, "SMSG_PLAYER_CHAT" },
90 { CMSG_NPC_TALK, 7, "CMSG_NPC_TALK" },
91 { SMSG_PLAYER_WARP, 22, "SMSG_PLAYER_WARP" },
92 { SMSG_CHANGE_MAP_SERVER, 28, "SMSG_CHANGE_MAP_SERVER" },
93 { CMSG_NAME_REQUEST, 6, "CMSG_NAME_REQUEST" },
94 { SMSG_BEING_NAME_RESPONSE, 30, "SMSG_BEING_NAME_RESPONSE" },
95 { CMSG_CHAT_WHISPER, VAR, "CMSG_CHAT_WHISPER" },
96 { SMSG_WHISPER, VAR, "SMSG_WHISPER" },
97 { SMSG_WHISPER_RESPONSE, 3, "SMSG_WHISPER_RESPONSE" },
98 { SMSG_GM_CHAT, VAR, "SMSG_GM_CHAT" },
99 { CMSG_PLAYER_CHANGE_DIR, 5, "CMSG_PLAYER_CHANGE_DIR" },
100 { SMSG_BEING_CHANGE_DIRECTION, 9, "SMSG_BEING_CHANGE_DIRECTION" },
101 { SMSG_ITEM_VISIBLE, 17, "SMSG_ITEM_VISIBLE" },
102 { SMSG_ITEM_DROPPED, 17, "SMSG_ITEM_DROPPED" },
103 { CMSG_ITEM_PICKUP, 6, "CMSG_ITEM_PICKUP" },
104 { SMSG_PLAYER_INVENTORY_ADD, 23, "SMSG_PLAYER_INVENTORY_ADD" },
105 { SMSG_ITEM_REMOVE, 6, "SMSG_ITEM_REMOVE" },
106 { CMSG_PLAYER_INVENTORY_DROP, 6, "CMSG_PLAYER_INVENTORY_DROP" },
107 { SMSG_PLAYER_EQUIPMENT, VAR, "SMSG_PLAYER_EQUIPMENT" },
108 { SMSG_PLAYER_STORAGE_EQUIP, VAR, "SMSG_PLAYER_STORAGE_EQUIP" },
109 { CMSG_PLAYER_INVENTORY_USE, 8, "CMSG_PLAYER_INVENTORY_USE" },
110 { SMSG_ITEM_USE_RESPONSE, 7, "SMSG_ITEM_USE_RESPONSE" },
111 { CMSG_PLAYER_EQUIP, 6, "CMSG_PLAYER_EQUIP" },
112 { SMSG_PLAYER_EQUIP, 7, "SMSG_PLAYER_EQUIP" },
113 { CMSG_PLAYER_UNEQUIP, 4, "CMSG_PLAYER_UNEQUIP" },
114 { SMSG_PLAYER_UNEQUIP, 7, "SMSG_PLAYER_UNEQUIP" },
115 { SMSG_PLAYER_INVENTORY_REMOVE, 6, "SMSG_PLAYER_INVENTORY_REMOVE" },
116 { SMSG_PLAYER_STAT_UPDATE_1, 8, "SMSG_PLAYER_STAT_UPDATE_1" },
117 { SMSG_PLAYER_STAT_UPDATE_2, 8, "SMSG_PLAYER_STAT_UPDATE_2" },
118 { CMSG_PLAYER_REBOOT, 3, "CMSG_PLAYER_REBOOT" },
119 { SMSG_CHAR_SWITCH_RESPONSE, 3, "SMSG_CHAR_SWITCH_RESPONSE" },
120 { SMSG_NPC_MESSAGE, VAR, "SMSG_NPC_MESSAGE" },
121 { SMSG_NPC_NEXT, 6, "SMSG_NPC_NEXT" },
122 { SMSG_NPC_CLOSE, 6, "SMSG_NPC_CLOSE" },
123 { SMSG_NPC_CHOICE, VAR, "SMSG_NPC_CHOICE" },
124 { CMSG_NPC_LIST_CHOICE, 7, "CMSG_NPC_LIST_CHOICE" },
125 { CMSG_NPC_NEXT_REQUEST, 6, "CMSG_NPC_NEXT_REQUEST" },
126 { CMSG_STAT_UPDATE_REQUEST, 5, "CMSG_STAT_UPDATE_REQUEST" },
127 { SMSG_PLAYER_STAT_UPDATE_4, 6, "SMSG_PLAYER_STAT_UPDATE_4" },
128 { SMSG_PLAYER_STAT_UPDATE_5, 44, "SMSG_PLAYER_STAT_UPDATE_5" },
129 { SMSG_PLAYER_STAT_UPDATE_6, 5, "SMSG_PLAYER_STAT_UPDATE_6" },
130 { CMSG_PLAYER_EMOTE, 3, "CMSG_PLAYER_EMOTE" },
131 { SMSG_BEING_EMOTION, 7, "SMSG_BEING_EMOTION" },
132 { SMSG_NPC_BUY_SELL_CHOICE, 6, "SMSG_NPC_BUY_SELL_CHOICE" },
133 { CMSG_NPC_BUY_SELL_REQUEST, 7, "CMSG_NPC_BUY_SELL_REQUEST" },
134 { SMSG_NPC_BUY, VAR, "SMSG_NPC_BUY" },
135 { SMSG_NPC_SELL, VAR, "SMSG_NPC_SELL" },
136 { CMSG_NPC_BUY_REQUEST, VAR, "CMSG_NPC_BUY_REQUEST" },
137 { CMSG_NPC_SELL_REQUEST, VAR, "CMSG_NPC_SELL_REQUEST" },
138 { SMSG_NPC_BUY_RESPONSE, 3, "SMSG_NPC_BUY_RESPONSE" },
139 { SMSG_NPC_SELL_RESPONSE, 3, "SMSG_NPC_SELL_RESPONSE" },
140 { SMSG_ADMIN_KICK_ACK, 6, "SMSG_ADMIN_KICK_ACK" },
141 { CMSG_TRADE_REQUEST, 6, "CMSG_TRADE_REQUEST" },
142 { SMSG_TRADE_REQUEST, 26, "SMSG_TRADE_REQUEST" },
143 { CMSG_TRADE_RESPONSE, 3, "CMSG_TRADE_RESPONSE" },
144 { SMSG_TRADE_RESPONSE, 3, "SMSG_TRADE_RESPONSE" },
145 { CMSG_TRADE_ITEM_ADD_REQUEST, 8, "CMSG_TRADE_ITEM_ADD_REQUEST" },
146 { SMSG_TRADE_ITEM_ADD, 19, "SMSG_TRADE_ITEM_ADD" },
147 { CMSG_TRADE_ADD_COMPLETE, 2, "CMSG_TRADE_ADD_COMPLETE" },
148 { SMSG_TRADE_OK, 3, "SMSG_TRADE_OK" },
149 { CMSG_TRADE_CANCEL_REQUEST, 2, "CMSG_TRADE_CANCEL_REQUEST" },
150 { SMSG_TRADE_CANCEL, 2, "SMSG_TRADE_CANCEL" },
151 { CMSG_TRADE_OK, 2, "CMSG_TRADE_OK" },
152 { SMSG_TRADE_COMPLETE, 3, "SMSG_TRADE_COMPLETE" },
153 { SMSG_PLAYER_STORAGE_STATUS, 6, "SMSG_PLAYER_STORAGE_STATUS" },
154 { CMSG_MOVE_TO_STORAGE, 8, "CMSG_MOVE_TO_STORAGE" },
155 { SMSG_PLAYER_STORAGE_ADD, 21, "SMSG_PLAYER_STORAGE_ADD" },
156 { CMSG_MOVE_FROM_STORAGE, 8, "CMSG_MOVE_FROM_STORAGE" },
157 { SMSG_PLAYER_STORAGE_REMOVE, 8, "SMSG_PLAYER_STORAGE_REMOVE" },
158 { CMSG_CLOSE_STORAGE, 2, "CMSG_CLOSE_STORAGE" },
159 { SMSG_PLAYER_STORAGE_CLOSE, 2, "SMSG_PLAYER_STORAGE_CLOSE" },
160 { CMSG_PARTY_CREATE, 26, "CMSG_PARTY_CREATE" },
161 { SMSG_PARTY_CREATE, 3, "SMSG_PARTY_CREATE" },
162 { SMSG_PARTY_INFO, VAR, "SMSG_PARTY_INFO" },
163 { CMSG_PARTY_INVITE, 6, "CMSG_PARTY_INVITE" },
164 { SMSG_PARTY_INVITE_RESPONSE, 27, "SMSG_PARTY_INVITE_RESPONSE" },
165 { SMSG_PARTY_INVITED, 30, "SMSG_PARTY_INVITED" },
166 { CMSG_PARTY_INVITED, 10, "CMSG_PARTY_INVITED" },
167 { CMSG_PARTY_LEAVE, 2, "CMSG_PARTY_LEAVE" },
168 { SMSG_PARTY_SETTINGS, 6, "SMSG_PARTY_SETTINGS" },
169 { CMSG_PARTY_SETTINGS, 6, "CMSG_PARTY_SETTINGS" },
170 { CMSG_PARTY_KICK, 30, "CMSG_PARTY_KICK" },
171 { SMSG_PARTY_LEAVE, 31, "SMSG_PARTY_LEAVE" },
172 { SMSG_PARTY_UPDATE_HP, 10, "SMSG_PARTY_UPDATE_HP" },
173 { SMSG_PARTY_UPDATE_COORDS, 10, "SMSG_PARTY_UPDATE_COORDS" },
174 { CMSG_PARTY_MESSAGE, VAR, "CMSG_PARTY_MESSAGE" },
175 { SMSG_PARTY_MESSAGE, VAR, "SMSG_PARTY_MESSAGE" },
176 { SMSG_PLAYER_SKILL_UP, 11, "SMSG_PLAYER_SKILL_UP" },
177 { SMSG_PLAYER_SKILLS, VAR, "SMSG_PLAYER_SKILLS" },
178 { SMSG_SKILL_FAILED, 10, "SMSG_SKILL_FAILED" },
179 { CMSG_SKILL_LEVELUP_REQUEST, 4, "CMSG_SKILL_LEVELUP_REQUEST" },
180 { CMSG_PLAYER_STOP_ATTACK, 2, "CMSG_PLAYER_STOP_ATTACK" },
181 { SMSG_PLAYER_STATUS_CHANGE, 13, "SMSG_PLAYER_STATUS_CHANGE" },
182 { SMSG_PLAYER_MOVE_TO_ATTACK, 16, "SMSG_PLAYER_MOVE_TO_ATTACK" },
183 { SMSG_PLAYER_ATTACK_RANGE, 4, "SMSG_PLAYER_ATTACK_RANGE" },
184 { SMSG_PLAYER_ARROW_MESSAGE, 4, "SMSG_PLAYER_ARROW_MESSAGE" },
185 { SMSG_PLAYER_ARROW_EQUIP, 4, "SMSG_PLAYER_ARROW_EQUIP" },
186 { SMSG_PLAYER_STAT_UPDATE_3, 14, "SMSG_PLAYER_STAT_UPDATE_3" },
187 { SMSG_NPC_INT_INPUT, 6, "SMSG_NPC_INT_INPUT" },
188 { CMSG_NPC_INT_RESPONSE, 10, "CMSG_NPC_INT_RESPONSE" },
189 { CMSG_NPC_CLOSE, 6, "CMSG_NPC_CLOSE" },
190 { SMSG_BEING_RESURRECT, 8, "SMSG_BEING_RESURRECT" },
191 { CMSG_CLIENT_QUIT, 4, "CMSG_CLIENT_QUIT" },
192 { SMSG_MAP_QUIT_RESPONSE, 4, "SMSG_MAP_QUIT_RESPONSE" },
193 { SMSG_PLAYER_GUILD_PARTY_INFO, 102, "SMSG_PLAYER_GUILD_PARTY_INFO" },
194 { SMSG_BEING_STATUS_CHANGE, 9, "SMSG_BEING_STATUS_CHANGE" },
195 { SMSG_PVP_MAP_MODE, 4, "SMSG_PVP_MAP_MODE" },
196 { SMSG_PVP_SET, 14, "SMSG_PVP_SET" },
197 { SMSG_BEING_SELFEFFECT, 10, "SMSG_BEING_SELFEFFECT" },
198 { SMSG_TRADE_ITEM_ADD_RESPONSE, 7, "SMSG_TRADE_ITEM_ADD_RESPONSE" },
199 { SMSG_PLAYER_INVENTORY_USE, 13, "SMSG_PLAYER_INVENTORY_USE" },
200 { SMSG_NPC_STR_INPUT, 6, "SMSG_NPC_STR_INPUT" },
201 { CMSG_NPC_STR_RESPONSE, VAR, "CMSG_NPC_STR_RESPONSE" },
202 { SMSG_BEING_CHANGE_LOOKS2, 11, "SMSG_BEING_CHANGE_LOOKS2" },
203 { SMSG_PLAYER_UPDATE_1, 54, "SMSG_PLAYER_UPDATE_1" },
204 { SMSG_PLAYER_UPDATE_2, 53, "SMSG_PLAYER_UPDATE_2" },
205 { SMSG_PLAYER_MOVE, 60, "SMSG_PLAYER_MOVE" },
206 { SMSG_SKILL_DAMAGE, 33, "SMSG_SKILL_DAMAGE" },
207 { SMSG_PLAYER_INVENTORY, VAR, "SMSG_PLAYER_INVENTORY" },
208 { SMSG_PLAYER_STORAGE_ITEMS, VAR, "SMSG_PLAYER_STORAGE_ITEMS" },
209 { SMSG_BEING_IP_RESPONSE, 10, "SMSG_BEING_IP_RESPONSE" },
210 { CMSG_ONLINE_LIST, 2, "CMSG_ONLINE_LIST" },
211 { SMSG_ONLINE_LIST, VAR, "SMSG_ONLINE_LIST" },
212 { SMSG_NPC_COMMAND, 16, "SMSG_NPC_COMMAND" },
213 { SMSG_QUEST_SET_VAR, 8, "SMSG_QUEST_SET_VAR" },
214 { SMSG_QUEST_PLAYER_VARS, VAR, "SMSG_QUEST_PLAYER_VARS" },
215 { SMSG_BEING_MOVE3, VAR, "SMSG_BEING_MOVE3" },
216 { SMSG_MAP_MASK, 10, "SMSG_MAP_MASK" },
217 { SMSG_MAP_MUSIC, VAR, "SMSG_MAP_MUSIC" },
218 { SMSG_NPC_CHANGETITLE, VAR, "SMSG_NPC_CHANGETITLE" },
219 { SMSG_SCRIPT_MESSAGE, VAR, "SMSG_SCRIPT_MESSAGE" },
220 { SMSG_PLAYER_CLIENT_COMMAND, VAR, "SMSG_PLAYER_CLIENT_COMMAND" },
221 { SMSG_MAP_SET_TILES_TYPE, 34, "SMSG_MAP_SET_TILES_TYPE" },
222 { SMSG_PLAYER_HP, 10, "SMSG_PLAYER_HP" },
223 { SMSG_PLAYER_HP_FULL, 14, "SMSG_PLAYER_HP_FULL" },
224
225 // any server messages
226 { SMSG_CONNECTION_PROBLEM, 3, "SMSG_CONNECTION_PROBLEM" },
227 { CMSG_SERVER_VERSION_REQUEST, 2, "CMSG_SERVER_VERSION_REQUEST" },
228 { SMSG_SERVER_VERSION_RESPONSE, 10, "SMSG_SERVER_VERSION_RESPONSE" },
229 { CMSG_CLIENT_DISCONNECT, 2, "CMSG_CLIENT_DISCONNECT" },
230};
231
232const unsigned int BUFFER_SIZE = 65536;
233
234int networkThread(void *data)
235{
236 auto *network = static_cast<Network*>(data);
237
238 if (!network->realConnect())
239 return -1;
240
241 network->receive();
242
243 return 0;
244}
245
246Network *Network::mInstance = nullptr;
247
249 mInBuffer(new char[BUFFER_SIZE]),
250 mOutBuffer(new char[BUFFER_SIZE])
251{
252 SDLNet_Init();
253
254 mInstance = this;
255
256 for (const auto &packetInfo : packet_infos)
257 {
258 assert(packetInfo.length != 0);
259 mPacketInfo[packetInfo.id] = &packetInfo;
260 }
261}
262
264{
266
267 if (mState != IDLE && mState != NET_ERROR)
268 disconnect();
269
270 mInstance = nullptr;
271
272 delete[] mInBuffer;
273 delete[] mOutBuffer;
274
275 SDLNet_Quit();
276}
277
278bool Network::connect(const ServerInfo &server)
279{
280 if (mState != IDLE && mState != NET_ERROR)
281 {
282 Log::info("Tried to connect an already connected socket!");
283 assert(false);
284 return false;
285 }
286
287 if (server.hostname.empty())
288 {
289 setError(_("Empty address given to Network::connect()!"));
290 return false;
291 }
292
293 Log::info("Network::Connecting to %s:%i", server.hostname.c_str(),
294 server.port);
295
296 mServer.hostname = server.hostname;
297 mServer.port = server.port;
298
299 // Reset to sane values
300 mOutSize = 0;
301 mInSize = 0;
302 mToSkip = 0;
303
305 mWorkerThread = SDL_CreateThread(networkThread, "Network", this);
306 if (!mWorkerThread)
307 {
308 setError("Unable to create network worker thread");
309 return false;
310 }
311
312 return true;
313}
314
316{
317 mState = IDLE;
318
319 if (mWorkerThread)
320 {
321 SDL_WaitThread(mWorkerThread, nullptr);
322 mWorkerThread = nullptr;
323 }
324
325 if (mSocket)
326 {
327 SDLNet_TCP_Close(mSocket);
328 mSocket = nullptr;
329 }
330}
331
333{
334 for (const uint16_t *i = handler->handledMessages; *i; ++i)
335 mMessageHandlers[*i] = handler;
336
337 handler->setNetwork(this);
338}
339
341{
342 for (const uint16_t *i = handler->handledMessages; *i; ++i)
343 mMessageHandlers.erase(*i);
344
345 handler->setNetwork(nullptr);
346}
347
349{
350 for (auto& [_, messageHandler] : mMessageHandlers)
351 messageHandler->setNetwork(nullptr);
352
353 mMessageHandlers.clear();
354}
355
356const char *Network::messageName(uint16_t id) const
357{
358 auto packetInfoIt = mPacketInfo.find(id);
359 if (packetInfoIt != mPacketInfo.end())
360 return packetInfoIt->second->name;
361
362 return "Unknown";
363}
364
366{
367 MutexLocker lock(&mMutex);
368
369 while (true) {
370 // Not even a message ID has been received
371 if (mInSize < 2)
372 break;
373
374 const uint16_t msgId = readWord(0);
375
376 auto packetInfoIt = mPacketInfo.find(msgId);
377 if (packetInfoIt == mPacketInfo.end())
378 {
379 Log::critical(strprintf("Unknown packet 0x%x received.", msgId));
380 break;
381 }
382
383 auto packetInfo = packetInfoIt->second;
384
385 // Determine the length of the packet
386 uint16_t len = packetInfo->length;
387 if (len == VAR)
388 {
389 // We have not received the length yet
390 if (mInSize < 4)
391 break;
392
393 len = readWord(2);
394
395 if (len < 4)
396 {
397 Log::critical(strprintf("Variable length packet 0x%x has invalid length %d.",
398 msgId, len));
399 break;
400 }
401 }
402
403 // The message has not been fully received yet
404 if (mInSize < len)
405 break;
406
407 MessageIn message(mInBuffer, len);
408
409 // Dispatch the message to the appropriate handler
410 auto iter = mMessageHandlers.find(msgId);
411 if (iter != mMessageHandlers.end())
412 {
413#ifdef DEBUG
414 Log::info("Handling %s (0x%x) of length %d", packetInfo->name, msgId, len);
415#endif
416
417 iter->second->handleMessage(message);
418 }
419 else
420 {
421 Log::info("Unhandled %s (0x%x) of length %d", packetInfo->name, msgId, len);
422 }
423
424 skip(len);
425 }
426}
427
429{
430 if (!mOutSize || mState != CONNECTED)
431 return;
432
433 int ret;
434
435 MutexLocker lock(&mMutex);
436 ret = SDLNet_TCP_Send(mSocket, mOutBuffer, mOutSize);
437 if (ret < (int)mOutSize)
438 {
439 setError("Error in SDLNet_TCP_Send(): " +
440 std::string(SDLNet_GetError()));
441 }
442 mOutSize = 0;
443}
444
445void Network::skip(int len)
446{
447 MutexLocker lock(&mMutex);
448 mToSkip += len;
449 if (!mInSize)
450 return;
451
452 if (mInSize >= mToSkip)
453 {
454 mInSize -= mToSkip;
455 memmove(mInBuffer, mInBuffer + mToSkip, mInSize);
456 mToSkip = 0;
457 }
458 else
459 {
460 mToSkip -= mInSize;
461 mInSize = 0;
462 }
463}
464
466{
467 IPaddress ipAddress;
468
469 if (SDLNet_ResolveHost(&ipAddress, mServer.hostname.c_str(),
470 mServer.port) == -1)
471 {
472 std::string errorMessage = strprintf(_("Unable to resolve host \"%s\""),
473 mServer.hostname.c_str());
475 Log::info("SDLNet_ResolveHost: %s", errorMessage.c_str());
476 return false;
477 }
478
480
481 mSocket = SDLNet_TCP_Open(&ipAddress);
482 if (!mSocket)
483 {
484 Log::info("Error in SDLNet_TCP_Open(): %s", SDLNet_GetError());
485 setError(SDLNet_GetError());
486 return false;
487 }
488
489 Log::info("Network::Started session with %s:%i",
490 ipToString(ipAddress.host), ipAddress.port);
491
493
494 return true;
495}
496
498{
499 SDLNet_SocketSet set;
500
501 if (!(set = SDLNet_AllocSocketSet(1)))
502 {
503 setError("Error in SDLNet_AllocSocketSet(): " +
504 std::string(SDLNet_GetError()));
505 return;
506 }
507
508 if (SDLNet_TCP_AddSocket(set, mSocket) == -1)
509 {
510 setError("Error in SDLNet_AddSocket(): " +
511 std::string(SDLNet_GetError()));
512 }
513
514 while (mState == CONNECTED)
515 {
516 // TODO Try to get this to block all the time while still being able
517 // to escape the loop
518 int numReady = SDLNet_CheckSockets(set, ((Uint32)500));
519 int ret;
520 switch (numReady)
521 {
522 case -1:
523 Log::error("SDLNet_CheckSockets");
524 // FALLTHROUGH
525 case 0:
526 break;
527
528 case 1:
529 {
530 // Receive data from the socket
531 MutexLocker lock(&mMutex);
532 ret = SDLNet_TCP_Recv(mSocket, mInBuffer + mInSize, BUFFER_SIZE - mInSize);
533
534 if (!ret)
535 {
536 // We got disconnected
537 mState = IDLE;
538 Log::info("Disconnected.");
539 }
540 else if (ret < 0)
541 {
542 setError(_("Connection to server terminated. ") +
543 std::string(SDLNet_GetError()));
544 }
545 else
546 {
547 mInSize += ret;
548 if (mToSkip)
549 {
550 if (mInSize >= mToSkip)
551 {
552 mInSize -= mToSkip;
553 memmove(mInBuffer, mInBuffer + mToSkip, mInSize);
554 mToSkip = 0;
555 }
556 else
557 {
558 mToSkip -= mInSize;
559 mInSize = 0;
560 }
561 }
562 }
563 break;
564 }
565
566 default:
567 // more than one socket is ready..
568 // this should not happen since we only listen once socket.
569 std::stringstream errorStream;
570 errorStream << "Error in SDLNet_TCP_Recv(), " << numReady
571 << " sockets are ready: " << SDLNet_GetError();
572 setError(errorStream.str());
573 break;
574 }
575 }
576
577 if (SDLNet_TCP_DelSocket(set, mSocket) == -1)
578 {
579 Log::info("Error in SDLNet_DelSocket(): %s", SDLNet_GetError());
580 }
581
582 SDLNet_FreeSocketSet(set);
583}
584
585void Network::setError(const std::string &error)
586{
587 Log::info("Network error: %s", error.c_str());
588 mError = error;
590}
591
592uint16_t Network::readWord(int pos)
593{
594 uint16_t value;
595 memcpy(&value, mInBuffer + pos, sizeof(uint16_t));
596 return SDL_SwapLE16(value);
597}
598
599} // namespace TmwAthena
A convenience class for locking a mutex.
Definition mutex.h:52
const uint16_t * handledMessages
std::string hostname
Definition serverinfo.h:42
uint16_t port
Definition serverinfo.h:43
void setNetwork(Network *network)
Used for parsing an incoming message from eAthena.
Definition messagein.h:35
void setError(const std::string &error)
Definition network.cpp:585
unsigned int mOutSize
Definition network.h:112
bool connect(const ServerInfo &server)
Definition network.cpp:278
SDL_Thread * mWorkerThread
Definition network.h:119
void skip(int len)
Definition network.cpp:445
void unregisterHandler(MessageHandler *handler)
Definition network.cpp:340
std::unordered_map< uint16_t, const PacketInfo * > mPacketInfo
Definition network.h:122
void registerHandler(MessageHandler *handler)
Definition network.cpp:332
unsigned int mInSize
Definition network.h:111
friend int networkThread(void *data)
Definition network.cpp:234
TCPsocket mSocket
Definition network.h:106
uint16_t readWord(int pos)
Definition network.cpp:592
void dispatchMessages()
Definition network.cpp:365
static Network * mInstance
Definition network.h:125
std::map< uint16_t, MessageHandler * > mMessageHandlers
Definition network.h:123
ServerInfo mServer
Definition network.h:108
unsigned int mToSkip
Definition network.h:114
std::string mError
Definition network.h:117
const char * messageName(uint16_t id) const
Definition network.cpp:356
std::string errorMessage
Definition client.cpp:94
#define _(s)
Definition gettext.h:38
void info(const char *log_text,...) LOG_PRINTF_ATTR
void error(const char *log_text,...) LOG_PRINTF_ATTR
Warning: buffers and other variables are shared, so there can be only one connection active at a time...
constexpr uint16_t VAR
Definition network.cpp:48
const unsigned int BUFFER_SIZE
Definition network.cpp:232
@ SMSG_PLAYER_STAT_UPDATE_1
Definition protocol.h:202
@ SMSG_PLAYER_STOP
Definition protocol.h:170
@ SMSG_PARTY_INFO
Definition protocol.h:248
@ SMSG_PARTY_UPDATE_COORDS
Definition protocol.h:259
@ SMSG_PLAYER_STAT_UPDATE_4
Definition protocol.h:213
@ CMSG_TRADE_RESPONSE
Definition protocol.h:229
@ SMSG_PARTY_MESSAGE
Definition protocol.h:261
@ SMSG_PLAYER_ATTACK_RANGE
Definition protocol.h:269
@ CMSG_MOVE_TO_STORAGE
Definition protocol.h:240
@ SMSG_PARTY_INVITED
Definition protocol.h:251
@ SMSG_PVP_MAP_MODE
Definition protocol.h:281
@ SMSG_BEING_CHANGE_DIRECTION
Definition protocol.h:186
@ SMSG_CHAR_PASSWORD_RESPONSE
Definition protocol.h:145
@ SMSG_PARTY_LEAVE
Definition protocol.h:257
@ SMSG_PLAYER_INVENTORY_ADD
Definition protocol.h:190
@ CMSG_NPC_SELL_REQUEST
Definition protocol.h:223
@ SMSG_ITEM_REMOVE
Definition protocol.h:191
@ CMSG_NPC_NEXT_REQUEST
Definition protocol.h:211
@ CMSG_TRADE_ADD_COMPLETE
Definition protocol.h:233
@ CMSG_PLAYER_UNEQUIP
Definition protocol.h:199
@ SMSG_NPC_COMMAND
Definition protocol.h:298
@ CMSG_NPC_STR_RESPONSE
Definition protocol.h:287
@ SMSG_PLAYER_SKILLS
Definition protocol.h:263
@ SMSG_SKILL_FAILED
Definition protocol.h:264
@ SMSG_LOGIN_ERROR
Definition protocol.h:141
@ SMSG_CHAR_SWITCH_RESPONSE
Definition protocol.h:205
@ SMSG_PLAYER_INVENTORY_USE
Definition protocol.h:285
@ CMSG_PARTY_LEAVE
Definition protocol.h:253
@ SMSG_TRADE_COMPLETE
Definition protocol.h:238
@ CMSG_PARTY_INVITED
Definition protocol.h:252
@ SMSG_PLAYER_STORAGE_CLOSE
Definition protocol.h:245
@ SMSG_CONNECTION_PROBLEM
Definition protocol.h:312
@ CMSG_ITEM_PICKUP
Definition protocol.h:189
@ SMSG_NPC_INT_INPUT
Definition protocol.h:273
@ SMSG_PLAYER_MOVE
Definition protocol.h:291
@ SMSG_ONLINE_LIST
Definition protocol.h:297
@ SMSG_BEING_MOVE
Definition protocol.h:162
@ SMSG_UPDATE_HOST
Definition protocol.h:138
@ SMSG_SERVER_PING
Definition protocol.h:166
@ SMSG_BEING_ACTION
Definition protocol.h:172
@ SMSG_PARTY_INVITE_RESPONSE
Definition protocol.h:250
@ SMSG_BEING_RESURRECT
Definition protocol.h:276
@ SMSG_NPC_NEXT
Definition protocol.h:207
@ SMSG_BEING_SELFEFFECT
Definition protocol.h:283
@ CMSG_CHAR_CREATE
Definition protocol.h:148
@ SMSG_TRADE_ITEM_ADD
Definition protocol.h:232
@ SMSG_PLAYER_ARROW_MESSAGE
Definition protocol.h:270
@ SMSG_PLAYER_STAT_UPDATE_5
Definition protocol.h:214
@ CMSG_CLOSE_STORAGE
Definition protocol.h:244
@ SMSG_BEING_NAME_RESPONSE
Definition protocol.h:180
@ SMSG_PLAYER_STORAGE_REMOVE
Definition protocol.h:243
@ CMSG_PLAYER_CHANGE_DIR
Definition protocol.h:185
@ CMSG_PARTY_INVITE
Definition protocol.h:249
@ SMSG_NPC_BUY_RESPONSE
Definition protocol.h:224
@ SMSG_NPC_MESSAGE
Definition protocol.h:206
@ CMSG_PARTY_KICK
Definition protocol.h:256
@ SMSG_PLAYER_WARP
Definition protocol.h:177
@ CMSG_CHAR_DELETE
Definition protocol.h:149
@ SMSG_MAP_QUIT_RESPONSE
Definition protocol.h:278
@ CMSG_PLAYER_REBOOT
Definition protocol.h:204
@ CMSG_TRADE_ITEM_ADD_REQUEST
Definition protocol.h:231
@ CMSG_CLIENT_DISCONNECT
Definition protocol.h:315
@ SMSG_CHAR_DELETE_SUCCEEDED
Definition protocol.h:154
@ CMSG_LOGIN_REGISTER
Definition protocol.h:139
@ CMSG_CHAR_SELECT
Definition protocol.h:147
@ CMSG_STAT_UPDATE_REQUEST
Definition protocol.h:212
@ CMSG_NPC_INT_RESPONSE
Definition protocol.h:274
@ SMSG_PLAYER_INVENTORY_REMOVE
Definition protocol.h:201
@ SMSG_BEING_IP_RESPONSE
Definition protocol.h:295
@ CMSG_PLAYER_INVENTORY_USE
Definition protocol.h:195
@ SMSG_CHAR_CREATE_SUCCEEDED
Definition protocol.h:152
@ SMSG_PLAYER_MOVE_TO_ATTACK
Definition protocol.h:268
@ SMSG_TRADE_OK
Definition protocol.h:234
@ SMSG_CHAR_DELETE_FAILED
Definition protocol.h:155
@ SMSG_PLAYER_EQUIP
Definition protocol.h:198
@ CMSG_ONLINE_LIST
Definition protocol.h:296
@ SMSG_QUEST_PLAYER_VARS
Definition protocol.h:300
@ SMSG_PLAYER_STORAGE_ADD
Definition protocol.h:241
@ SMSG_MAP_MUSIC
Definition protocol.h:303
@ CMSG_SKILL_LEVELUP_REQUEST
Definition protocol.h:265
@ SMSG_NPC_BUY_SELL_CHOICE
Definition protocol.h:218
@ SMSG_NPC_BUY
Definition protocol.h:220
@ SMSG_QUEST_SET_VAR
Definition protocol.h:299
@ SMSG_PLAYER_EQUIPMENT
Definition protocol.h:193
@ SMSG_PLAYER_STORAGE_EQUIP
Definition protocol.h:194
@ SMSG_PLAYER_HP
Definition protocol.h:308
@ SMSG_BEING_EMOTION
Definition protocol.h:217
@ CMSG_NPC_LIST_CHOICE
Definition protocol.h:210
@ SMSG_BEING_CHANGE_LOOKS2
Definition protocol.h:288
@ SMSG_CHAR_LOGIN_ERROR
Definition protocol.h:151
@ SMSG_PLAYER_STAT_UPDATE_6
Definition protocol.h:215
@ SMSG_NPC_STR_INPUT
Definition protocol.h:286
@ SMSG_PLAYER_UPDATE_1
Definition protocol.h:289
@ CMSG_PARTY_MESSAGE
Definition protocol.h:260
@ SMSG_SKILL_DAMAGE
Definition protocol.h:292
@ SMSG_PLAYER_UNEQUIP
Definition protocol.h:200
@ SMSG_PLAYER_INVENTORY
Definition protocol.h:293
@ SMSG_TRADE_RESPONSE
Definition protocol.h:230
@ SMSG_CHAR_CREATE_FAILED
Definition protocol.h:153
@ CMSG_PLAYER_EQUIP
Definition protocol.h:197
@ CMSG_NPC_TALK
Definition protocol.h:176
@ SMSG_BEING_MOVE3
Definition protocol.h:301
@ CMSG_TRADE_OK
Definition protocol.h:237
@ SMSG_PLAYER_SKILL_UP
Definition protocol.h:262
@ SMSG_CHANGE_MAP_SERVER
Definition protocol.h:178
@ CMSG_MAP_PING
Definition protocol.h:165
@ CMSG_PLAYER_CHANGE_DEST
Definition protocol.h:168
@ SMSG_MAP_SET_TILES_TYPE
Definition protocol.h:307
@ SMSG_BEING_SPAWN
Definition protocol.h:163
@ SMSG_CHAR_LOGIN
Definition protocol.h:150
@ CMSG_CHAR_SERVER_CONNECT
Definition protocol.h:146
@ CMSG_NPC_BUY_REQUEST
Definition protocol.h:222
@ SMSG_PLAYER_GUILD_PARTY_INFO
Definition protocol.h:279
@ SMSG_TRADE_REQUEST
Definition protocol.h:228
@ SMSG_TRADE_ITEM_ADD_RESPONSE
Definition protocol.h:284
@ CMSG_PARTY_CREATE
Definition protocol.h:246
@ CMSG_PLAYER_INVENTORY_DROP
Definition protocol.h:192
@ SMSG_PLAYER_UPDATE_2
Definition protocol.h:290
@ SMSG_PARTY_CREATE
Definition protocol.h:247
@ SMSG_PLAYER_STATUS_CHANGE
Definition protocol.h:267
@ SMSG_PARTY_SETTINGS
Definition protocol.h:254
@ SMSG_TRADE_CANCEL
Definition protocol.h:236
@ CMSG_NAME_REQUEST
Definition protocol.h:179
@ SMSG_ITEM_DROPPED
Definition protocol.h:188
@ SMSG_MAP_MASK
Definition protocol.h:302
@ CMSG_CHAT_WHISPER
Definition protocol.h:181
@ SMSG_BEING_STATUS_CHANGE
Definition protocol.h:280
@ SMSG_PLAYER_ARROW_EQUIP
Definition protocol.h:271
@ SMSG_PLAYER_HP_FULL
Definition protocol.h:309
@ SMSG_WHISPER
Definition protocol.h:182
@ CMSG_MAP_SERVER_CONNECT
Definition protocol.h:159
@ SMSG_ADMIN_KICK_ACK
Definition protocol.h:226
@ CMSG_CHAR_PASSWORD_CHANGE
Definition protocol.h:144
@ SMSG_PLAYER_CLIENT_COMMAND
Definition protocol.h:306
@ CMSG_MAP_LOADED
Definition protocol.h:164
@ CMSG_TRADE_CANCEL_REQUEST
Definition protocol.h:235
@ SMSG_NPC_SELL_RESPONSE
Definition protocol.h:225
@ CMSG_PARTY_SETTINGS
Definition protocol.h:255
@ CMSG_MOVE_FROM_STORAGE
Definition protocol.h:242
@ CMSG_CLIENT_QUIT
Definition protocol.h:277
@ SMSG_CHAR_MAP_INFO
Definition protocol.h:156
@ SMSG_PVP_SET
Definition protocol.h:282
@ SMSG_BEING_CHAT
Definition protocol.h:174
@ SMSG_PLAYER_CHAT
Definition protocol.h:175
@ SMSG_SCRIPT_MESSAGE
Definition protocol.h:305
@ CMSG_NPC_CLOSE
Definition protocol.h:275
@ CMSG_SERVER_VERSION_REQUEST
Definition protocol.h:313
@ SMSG_PLAYER_STORAGE_STATUS
Definition protocol.h:239
@ SMSG_PLAYER_STORAGE_ITEMS
Definition protocol.h:294
@ CMSG_PLAYER_STOP_ATTACK
Definition protocol.h:266
@ SMSG_BEING_REMOVE
Definition protocol.h:167
@ SMSG_WALK_RESPONSE
Definition protocol.h:169
@ SMSG_MAP_LOGIN_SUCCESS
Definition protocol.h:160
@ SMSG_BEING_VISIBLE
Definition protocol.h:161
@ CMSG_NPC_BUY_SELL_REQUEST
Definition protocol.h:219
@ SMSG_PARTY_UPDATE_HP
Definition protocol.h:258
@ SMSG_NPC_SELL
Definition protocol.h:221
@ SMSG_ITEM_USE_RESPONSE
Definition protocol.h:196
@ SMSG_NPC_CHOICE
Definition protocol.h:209
@ CMSG_PLAYER_CHANGE_ACT
Definition protocol.h:171
@ SMSG_ITEM_VISIBLE
Definition protocol.h:187
@ SMSG_NPC_CHANGETITLE
Definition protocol.h:304
@ SMSG_GM_CHAT
Definition protocol.h:184
@ SMSG_WHISPER_RESPONSE
Definition protocol.h:183
@ SMSG_SERVER_VERSION_RESPONSE
Definition protocol.h:314
@ CMSG_PLAYER_EMOTE
Definition protocol.h:216
@ CMSG_TRADE_REQUEST
Definition protocol.h:227
@ SMSG_PLAYER_STAT_UPDATE_2
Definition protocol.h:203
@ CMSG_CHAT_MESSAGE
Definition protocol.h:173
@ SMSG_LOGIN_DATA
Definition protocol.h:140
@ SMSG_PLAYER_STAT_UPDATE_3
Definition protocol.h:272
@ SMSG_NPC_CLOSE
Definition protocol.h:208
int networkThread(void *data)
Definition network.cpp:234
std::string strprintf(char const *format,...)
A safe version of sprintf that returns a std::string of the result.
const char * ipToString(int address)
Converts the given IP address to a string.
const char * name
Definition network.cpp:44