diff options
50 files changed, 2704 insertions, 1718 deletions
@@ -1,9 +1,30 @@ 2005-09-13 Eugenio Favalli <elvenprogrammer@gmail.com> * The Mana World.dev: Updated to last changes. + * The Mana World.dev: Added latest changes to the proper branch. + * src/gui/login.cpp, src/net/messageout.cpp, src/net/messageout.h, + src/net/network.cpp, src/net/network.h: Started to use MessageOut to + send login data. 2005-09-13 Björn Steinbrink <B.Steinbrink@gmx.de> + * ChangeLog, The Mana World.dev, configure.ac, src/Makefile.am, + src/being.cpp, src/being.h, src/engine.cpp, src/floor_item.cpp, + src/floor_item.h, src/game.cpp, src/game.h, src/inventory.cpp, + src/log.h, src/main.cpp, src/playerinfo.h, src/serverinfo.h, + src/gui/buy.cpp, src/gui/buysell.cpp, src/gui/char_select.cpp, + src/gui/char_server.cpp, src/gui/chargedialog.cpp, src/gui/chat.cpp, + src/gui/inventorywindow.cpp, src/gui/login.cpp, src/gui/npc.cpp, + src/gui/npc.h, src/gui/npc_text.cpp, src/gui/npc_text.h, + src/gui/popupmenu.cpp, src/gui/requesttrade.cpp, + src/gui/requesttrade.h, src/gui/sell.cpp, src/gui/skill.cpp, + src/gui/stats.cpp, src/gui/status.cpp, src/gui/status.h, + src/gui/trade.cpp, src/net/messagein.cpp, src/net/messagein.h, + src/net/messageout.cpp, src/net/messageout.h, src/net/network.cpp, + src/net/network.h, src/net/packet.cpp, src/net/packet.h, + src/net/protocol.cpp, src/net/protocol.h, src/net/win2linux.h, + src/net/win2mac.cpp, src/net/win2mac.h: Merged with SDL_NET_TEST + branch. * src/being.cpp, src/being.h, src/game.cpp: Simplify remove_node. * src/being.cpp, src/being.h, src/game.cpp: Merged createBeing and add_node into createBeing. @@ -14,9 +35,23 @@ src/gui/login.cpp, src/gui/login.h, src/gui/updatewindow.cpp, src/gui/updatewindow.h: Unified the loops for the various dialogs that are shown before the actual game starts. + * src/gui/login.cpp: Removed a close_session call i missed. + * src/gui/login.cpp: Close the session only when it was opened. 2005-09-13 Bjørn Lindeijer <bjorn@lindeijer.nl> + * src/floor_item.cpp, src/floor_item.h, src/engine.cpp, + popupmenu.cpp: Made members private and provided more convenient + constructor. + * src/net/messageout.cpp: Fixed bug in destructor. + * src/net/network.cpp: Initialize buffers and enforce only a single + session at a time. + * src/game.cpp, src/net/protocol.h: Converted all incoming messages + handled in game.cpp to use the MessageIn class. This is a huge change + so please test if everything is still working correctly. + * src/gui/npc.cpp, src/gui/npc.h, src/gui/npc_text.cpp, + src/gui/npc_text.h: Changed argument from char* to std::string for + convenience. * src/gui/setup.cpp, src/gui/setup.h: Enabled OpenGL checkbox and added messagebox informing the user that apply this change requires restarting the client. @@ -39,15 +74,69 @@ * data/help/changes.txt, data/help/commands.txt: Added 0.0.16 changes. * data/maps/new_7-1.tmx.gz: Fixed well being in the wrong layer. +2005-09-11 Bjørn Lindeijer <bjorn@lindeijer.nl> + + * src/net/network.cpp: Improved error reporting a bit and got rid of + loop for sending data, which shouldn't be necessary according to + SDL_net documentation. + * src/Makefile.am, src/being.cpp, src/being.h, src/engine.cpp, + src/game.cpp, src/main.cpp, src/playerinfo.h, src/gui/char_server.cpp, + src/gui/chargedialog.cpp, src/gui/chat.cpp, + src/gui/inventorywindow.cpp, src/gui/popupmenu.cpp, src/gui/skill.cpp, + src/gui/stats.cpp, src/gui/status.cpp, src/gui/status.h, + src/net/protocol.cpp: Changed char_info into the array it's used as + for character selection and introduced player_info as the pointer to + the player information. Should help towards support for multiple + characters on the same account. Also changed PLAYER_INFO name field to + a std::string. + * src/net/win2mac.cpp, src/net/win2mac.h: A bit of clean up. + * src/net/packet.h, src/net/packet.cpp, src/net/messagein.h, + src/net/messagein.cpp, src/net/messageout.h, src/net/messageout.cpp: + Added these packet reading/writing helpers, taken from the new server + in development. + * src/gui/char_select.cpp: Made new character message be parsed + using MessageIn. Many other incoming messages should be ready to be + ported similarly, simplifying the parsing of packets because of + automatic incrementation of the read position. + * src/game.cpp, src/game.h, src/gui/popupmenu.cpp, + src/gui/requesttrade.cpp, src/gui/requesttrade.h: Changed + tradePartnerName to std::string. + * src/net/win2linux.h: Removed because it became redundant with + the use of SDL_net. + * src/game.cpp, src/game.h, src/gui/char_select.cpp, + src/net/messagein.cpp, src/net/messagein.h, src/net/network.cpp, + src/net/network.h: Got rid of usage of Packet by MessageIn, + simplifying both its usage and implementation. Now also handling + response to character selection through MessageIn. + * src/main.cpp, src/serverinfo.h, src/gui/char_select.cpp, + src/gui/char_server.cpp, src/gui/login.cpp, src/net/network.cpp, + src/net/network.h, src/net/protocol.cpp, src/net/protocol.h: + Introduced get_next_message function to reduce duplication of that + process. Also now MessageIn is used for all incoming messages handled + during the login sequence. + * src/being.cpp, src/being.h, src/game.cpp, src/game.h, + src/inventory.cpp, src/serverinfo.h, src/gui/login.cpp, + src/gui/trade.cpp, src/net/messagein.cpp, src/net/messagein.h, + src/net/network.cpp, src/net/protocol.cpp, src/net/protocol.h: Added + readCoordinates and readCoordinatePair to MessageIn for reading the + specific ways eAthena sends sends those, and converted part of + game.cpp to use the MessageIn class. Also simplified cases where + flush() was still called in a loop for sending and added asserts to + MessageIn methods. + 2005-09-10 Bjørn Lindeijer <bjorn@lindeijer.nl> * src/log.cpp: Committed patch by Nayr for displaying a messagebox when an error occurs on MacOS. +2005-09-10 Björn Steinbrink <B.Steinbrink@gmx.de> + + * configure.ac: Add check for SDL_net. + 2005-09-09 Eugenio Favalli <elvenprogrammer@gmail.com> * src/game.cpp: Fixes to dropped items network code - + 2005-09-09 Bjørn Lindeijer <bjorn@lindeijer.nl> * src/net/win2linux.h: Removed inclusion of malloc.h header as it diff --git a/The Mana World.dev b/The Mana World.dev index be26c0f1..ef8b7765 100644 --- a/The Mana World.dev +++ b/The Mana World.dev @@ -1,7 +1,7 @@ [Project] FileName=The Mana World.dev Name=tmw -UnitCount=161 +UnitCount=167 Type=0 Ver=1 ObjFiles= @@ -11,8 +11,8 @@ PrivateResource=The_Mana_World_private.rc ResourceIncludes= MakeIncludes= Compiler= -CppCompiler=-DUSE_OPENGL_@@_-DDEBUG_@@__@@_ -Linker=-lopengl32_@@_-lwsock32_@@_-lmingw32_@@_-lguichan_@@_-lguichan_sdl_@@_-lguichan_opengl_@@_-lSDLmain_@@_-lSDL_@@_-lSDL_image_@@_-lSDL_mixer_@@_-lcurl_@@_-lphysfs_@@_-lxml2_@@_-lz_@@_ +CppCompiler=-DUSE_OPENGL_@@_ +Linker=-lguichan_@@_-lguichan_sdl_@@_-lguichan_opengl_@@_-lwsock32_@@_-lSDL_image_@@_-lSDL_mixer_@@_-lSDL_net_@@_-lmingw32_@@_-lSDLmain_@@_-lSDL_@@_-lxml2_@@_-lopengl32_@@_-lz_@@_-lphysfs_@@_-lcurl_@@_ IsCpp=1 Icon=The Mana World.ico ExeOutput= @@ -1617,6 +1617,26 @@ Priority=1000 OverrideBuildCmd=0 BuildCmd= +[Unit166] +FileName=src\net\messagein.cpp +CompileCpp=1 +Folder=net/source +Compile=1 +Link=1 +Priority=1000 +OverrideBuildCmd=0 +BuildCmd= + +[Unit167] +FileName=src\net\messagein.h +CompileCpp=1 +Folder=net/header +Compile=1 +Link=1 +Priority=1000 +OverrideBuildCmd=0 +BuildCmd= + [Unit160] FileName=src\resources\sdlimageloader.cpp CompileCpp=1 @@ -1668,9 +1688,9 @@ OverrideBuildCmd=0 BuildCmd= [Unit162] -FileName=src\net\messageout.h +FileName=src\net\messageout.cpp CompileCpp=1 -Folder=net/header +Folder=net/source Compile=1 Link=1 Priority=1000 @@ -1678,9 +1698,9 @@ OverrideBuildCmd=0 BuildCmd= [Unit163] -FileName=src\net\messagein.cpp +FileName=src\net\messageout.h CompileCpp=1 -Folder=net/source +Folder=net/header Compile=1 Link=1 Priority=1000 @@ -1688,7 +1708,7 @@ OverrideBuildCmd=0 BuildCmd= [Unit164] -FileName=src\net\messageout.cpp +FileName=src\net\packet.cpp CompileCpp=1 Folder=net/source Compile=1 @@ -1698,12 +1718,11 @@ OverrideBuildCmd=0 BuildCmd= [Unit165] -FileName=src\net\packet.cpp +FileName=src\net\packet.h CompileCpp=1 -Folder=net/source +Folder=net/header Compile=1 Link=1 Priority=1000 OverrideBuildCmd=0 BuildCmd= - diff --git a/configure.ac b/configure.ac index 4073dcb7..4c63e860 100755 --- a/configure.ac +++ b/configure.ac @@ -40,6 +40,9 @@ AC_CHECK_LIB(SDL_mixer, Mix_OpenAudio, , AC_MSG_ERROR([ *** Unable to find SDL_mixer library (http://www.libsdl.org/projects/SDL_mixer/)])) +AC_CHECK_LIB(SDL_net, SDLNet_Init, , +AC_MSG_ERROR([ *** Unable to find SDL_net library])) + # Checks for header files. AC_HEADER_STDC AC_CHECK_HEADERS([arpa/inet.h fcntl.h malloc.h netdb.h netinet/in.h stdlib.h string.h sys/socket.h unistd.h]) diff --git a/src/Makefile.am b/src/Makefile.am index 5172a6d2..850a7f2c 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -104,8 +104,14 @@ tmw_SOURCES = graphic/spriteset.cpp \ gui/hbox.cpp \ gui/updatewindow.h \ gui/updatewindow.cpp \ + net/messagein.cpp \ + net/messagein.h \ + net/messageout.cpp \ + net/messageout.h \ net/network.cpp \ net/network.h \ + net/packet.cpp \ + net/packet.h \ net/protocol.cpp \ net/protocol.h \ net/win2linux.h \ diff --git a/src/being.cpp b/src/being.cpp index 2bb0beff..3fdc2f2b 100644 --- a/src/being.cpp +++ b/src/being.cpp @@ -64,10 +64,11 @@ Being* createBeing(unsigned int id, unsigned short job, Map *map) beings.push_back(being); // If the being is a player, request the name - if (being->getType() == Being::PLAYER) { - WFIFOW(0) = net_w_value(0x0094); - WFIFOL(2) = net_l_value(RFIFOL(2)); - WFIFOSET(6); + if (being->getType() == Being::PLAYER) + { + writeWord(0, 0x0094); + writeLong(2, being->getId());//readLong(2)); + writeSet(6); } // If the being is a monster then load the monsterset else if (being->job >= 1002 && @@ -100,7 +101,7 @@ void remove_node(Being *being) Being *findNode(unsigned int id) { std::list<Being*>::iterator i; - for (i = beings.begin(); i != beings.end(); i++) { + for (i = beings.begin(); i != beings.end(); i++) { Being *being = (*i); if (being->getId() == id) { return being; @@ -166,7 +167,6 @@ Being::Being(): damage_time(0), showSpeech(false), showDamage(false) { - strcpy(name, ""); } Being::~Being() @@ -245,11 +245,6 @@ void Being::setMap(Map *map) this->map = map; } -void Being::setName(char *text) -{ - strcpy(name, text); -} - void Being::nextStep() { if (!path.empty()) diff --git a/src/being.h b/src/being.h index 2a9a8421..98aaeb80 100644 --- a/src/being.h +++ b/src/being.h @@ -87,7 +87,6 @@ class Being unsigned char emotion_time; /**< Time until emotion disappears */ unsigned int text_x, text_y; // temp solution to fix speech position - char name[24]; /**< Name of character */ unsigned short aspd; /**< Attack speed */ /** @@ -129,11 +128,18 @@ class Being void setDamage(const std::string &text, int time); /** - * Sets the name for the being + * Returns the name of the being. + */ + const std::string& + getName() { return mName; } + + /** + * Sets the name for the being. * * @param text The name that should appear. */ - void setName(char *name); + void + setName(const std::string &name) { mName = name; } /** * Sets the hair color for this being. @@ -225,6 +231,7 @@ class Being unsigned int speech_time; unsigned int damage_time; bool showSpeech, showDamage; + std::string mName; /**< Name of character */ /** * Sets the new path for this being. diff --git a/src/engine.cpp b/src/engine.cpp index 97ba1c2c..956fbfa6 100644 --- a/src/engine.cpp +++ b/src/engine.cpp @@ -279,12 +279,13 @@ void Engine::draw() for (std::list<FloorItem*>::iterator i = floorItems.begin(); i != floorItems.end(); i++) { FloorItem *floorItem = (*i); - if (itemDb->getItemInfo(floorItem->id)->getImage() > 0) { + if (itemDb->getItemInfo(floorItem->getItemId())->getImage() > 0) { Image *image = itemset->spriteset[itemDb->getItemInfo( - floorItem->id)->getImage() - 1]; + floorItem->getItemId())->getImage() - 1]; graphics->drawImage(image, - floorItem->x * 32 - map_x, floorItem->y * 32 - map_y); + floorItem->getX() * 32 - map_x, + floorItem->getY() * 32 - map_y); } } @@ -297,10 +298,6 @@ void Engine::draw() int x = being->x * 32 - map_x; int y = being->y * 32 - map_y; -#ifdef DEBUG - graphics->setColor(gcn::Color(0, 0, 255)); - graphics->drawRectangle(gcn::Rectangle(x & ~31, y & ~31, 32, 32)); -#endif int frame; switch (being->getType()) { @@ -323,10 +320,6 @@ void Engine::draw() graphics->drawImage(playerset->spriteset[frame + 16 * dir], being->text_x - 16, being->text_y - 32); - //if (being->action == ATTACK) - //{ - // std::cout << being->name << " " << being->getWeapon() << std::endl; - //} if (being->getWeapon() != 0 && being->action == Being::ATTACK) { Image *image = weaponset->spriteset[ 16 * (being->getWeapon() - 1) + 4 * being->frame + dir]; @@ -351,9 +344,9 @@ void Engine::draw() } graphics->setFont(speechFont); - graphics->drawText(being->name, - being->text_x + 15, being->text_y + 30, - gcn::Graphics::CENTER); + graphics->drawText(being->getName(), + being->text_x + 15, being->text_y + 30, + gcn::Graphics::CENTER); graphics->setFont(gui->getFont()); break; @@ -465,6 +458,7 @@ void Engine::draw() gcn::Graphics::CENTER); } +#ifdef DEBUG std::stringstream debugStream; debugStream << "[" << fps << " fps] " << mouseTileX << ", " << mouseTileY; @@ -477,6 +471,7 @@ void Engine::draw() debugInfo->setCaption(debugStream.str()); debugInfo->adjustSize(); +#endif gui->draw(); } diff --git a/src/floor_item.cpp b/src/floor_item.cpp index d7c597fe..691c5acd 100755 --- a/src/floor_item.cpp +++ b/src/floor_item.cpp @@ -45,7 +45,7 @@ void remove_floor_item(unsigned int int_id) { std::list<FloorItem *>::iterator i; for (i = floorItems.begin(); i != floorItems.end(); i++) { - if ((*i)->int_id == int_id) { + if ((*i)->getId() == int_id) { delete (*i); floorItems.erase(i); return; @@ -58,9 +58,9 @@ unsigned int find_floor_item_by_cor(unsigned short x, unsigned short y) std::list<FloorItem *>::iterator i; for (i = floorItems.begin(); i != floorItems.end(); i++) { FloorItem *floorItem = (*i); - if (floorItem->x == x && floorItem->y == y) + if (floorItem->getX() == x && floorItem->getY() == y) { - return floorItem->int_id; + return floorItem->getId(); } } return 0; @@ -71,18 +71,9 @@ FloorItem *find_floor_item_by_id(unsigned int int_id) std::list<FloorItem*>::iterator i; for (i = floorItems.begin(); i != floorItems.end(); i++) { FloorItem *floorItem = (*i); - if (floorItem->int_id == int_id) { + if (floorItem->getId() == int_id) { return floorItem; } } return NULL; } - -FloorItem::FloorItem(): - id(0), int_id(0), x(0), y(0) -{ -} - -FloorItem::~FloorItem() -{ -} diff --git a/src/floor_item.h b/src/floor_item.h index 2c31525e..cd3293b4 100755 --- a/src/floor_item.h +++ b/src/floor_item.h @@ -24,22 +24,50 @@ #ifndef _TMW_FLOORITEM_H #define _TMW_FLOORITEM_H -class FloorItem { - private: +/** + * An item lying on the floor. + */ +class FloorItem +{ public: - unsigned int id; - unsigned int int_id; - unsigned short x, y; - /** * Constructor. */ - FloorItem(); + FloorItem(unsigned int id, + unsigned int itemId, + unsigned short x, + unsigned short y): + id(itemId), + int_id(id), + x(x), + y(y) + { + } /** - * Destructor. + * Returns instance id of this item. */ - ~FloorItem(); + unsigned int getId() { return int_id; } + + /** + * Returns the item id. + */ + unsigned int getItemId() { return id; } + + /** + * Returns the x coordinate. + */ + unsigned short getX() { return x; } + + /** + * Returns the y coordinate. + */ + unsigned short getY() { return y; } + + private: + unsigned int id; + unsigned int int_id; + unsigned short x, y; }; /** Removes all items from the list */ diff --git a/src/game.cpp b/src/game.cpp index c2620742..ba860822 100644 --- a/src/game.cpp +++ b/src/game.cpp @@ -72,7 +72,7 @@ extern Graphics *graphics; char map_path[480]; -char tradePartnerName[24]; +std::string tradePartnerName; bool refresh_beings = false; unsigned char keyb_state; @@ -81,7 +81,7 @@ volatile bool action_time = false; int server_tick; int fps = 0, frame = 0, current_npc = 0; bool displayPathToMouse = false; -int startX = 0, startY = 0; +unsigned short startX = 0, startY = 0; int gameTime = 0; Being *autoTarget = NULL; Engine *engine = NULL; @@ -121,9 +121,9 @@ const int MAX_TIME = 10000; class DeatchNoticeListener : public gcn::ActionListener { public: void action(const std::string &eventId) { - WFIFOW(0) = net_w_value(0x00b2); - WFIFOB(2) = 0; - WFIFOSET(3); + writeWord(0, 0x00b2); + writeByte(2, 0); + writeSet(3); deathNotice = NULL; } } deathNoticeListener; @@ -298,14 +298,15 @@ void do_init() player_node->x = startX; player_node->y = startY; player_node->speed = 150; - player_node->setHairColor(char_info->hair_color); - player_node->setHairStyle(char_info->hair_style); + player_node->setHairColor(player_info->hair_color); + player_node->setHairStyle(player_info->hair_style); - if (char_info->weapon == 11) { - char_info->weapon = 2; + if (player_info->weapon == 11) + { + player_info->weapon = 2; } - player_node->setWeapon(char_info->weapon); + player_node->setWeapon(player_info->weapon); remove("packet.list"); @@ -489,9 +490,9 @@ void do_input() else if (deathNotice) { deathNotice->action("ok"); - WFIFOW(0) = net_w_value(0x00b2); - WFIFOB(2) = 0; - WFIFOSET(3); + writeWord(0, 0x00b2); + writeByte(2, 0); + writeSet(3); deathNotice = NULL; } // Close the Browser if opened @@ -539,9 +540,9 @@ void do_input() if (id) { - WFIFOW(0) = net_w_value(0x009f); - WFIFOL(2) = net_l_value(id); - WFIFOSET(6); + writeWord(0, 0x009f); + writeLong(2, id); + writeSet(6); } used = true; } @@ -639,9 +640,9 @@ void do_input() if (emotion) { - WFIFOW(0) = net_w_value(0x00bf); - WFIFOB(2) = emotion; - WFIFOSET(3); + writeWord(0, 0x00bf); + writeByte(2, emotion); + writeSet(3); action_time = false; used = true; } @@ -676,20 +677,20 @@ void do_input() { // Player default: trade case Being::PLAYER: - WFIFOW(0) = net_w_value(0x00e4); - WFIFOL(2) = net_l_value(target->getId()); - WFIFOSET(6); - strcpy(tradePartnerName, target->name); + writeWord(0, 0x00e4); + writeLong(2, target->getId()); + writeSet(6); + tradePartnerName = target->getName(); break; // NPC default: talk case Being::NPC: if (!current_npc) { - WFIFOW(0) = net_w_value(0x0090); - WFIFOL(2) = net_l_value(target->getId()); - WFIFOB(6) = 0; - WFIFOSET(7); + writeWord(0, 0x0090); + writeLong(2, target->getId()); + writeByte(6, 0); + writeSet(7); current_npc = target->getId(); } break; @@ -730,26 +731,30 @@ void do_input() // "sqrt(dx*dx + dy*dy) < 2" is equal to "dx*dx + dy*dy < 4" if ((dx*dx + dy*dy) < 4) { - WFIFOW(0) = net_w_value(0x009f); - WFIFOL(2) = net_l_value(floorItemId); - WFIFOSET(6); + writeWord(0, 0x009f); + writeLong(2, floorItemId); + writeSet(6); } } - // Just cancel the popup menu if shown, and don't make the character walk + // Just cancel the popup menu if shown, and don't make the + // character walk if (popupMenu->isVisible() == true) { - // If we click elsewhere than in the window, do not use the event + // If we click elsewhere than in the window, do not use + // the event // The user wanted to close the popup. - // Still buggy : Wonder if the x, y, width, and height aren't reported partially - // with these functions. - if (event.button.x >= (popupMenu->getX() + popupMenu->getWidth()) || - event.button.x < popupMenu->getX() || - event.button.y >= (popupMenu->getY() + popupMenu->getHeight()) || - event.button.y < popupMenu->getY()) + // Still buggy : Wonder if the x, y, width, and height + // aren't reported partially with these functions. + if (event.button.x >= + (popupMenu->getX() + popupMenu->getWidth()) || + event.button.x < popupMenu->getX() || + event.button.y >= + (popupMenu->getY() + popupMenu->getHeight()) || + event.button.y < popupMenu->getY()) { - used = true; - popupMenu->setVisible(false); + used = true; + popupMenu->setVisible(false); } } @@ -795,7 +800,7 @@ void do_input() } } // End while - + // Moving player around if ((player_node->action != Being::DEAD) && (current_npc == 0) && !chatWindow->isFocused()) @@ -875,7 +880,7 @@ void do_input() yDirection = 0; // Choose a straight direction when diagonal target is blocked - if ((yDirection != 0) && (xDirection != 0) && + if ((yDirection != 0) && (xDirection != 0) && !tiledMap->getWalk(x + xDirection, y + yDirection)) xDirection = 0; @@ -919,9 +924,9 @@ void do_input() if (id != 0) { - WFIFOW(0) = net_w_value(0x009f); - WFIFOL(2) = net_l_value(id); - WFIFOSET(6); + writeWord(0, 0x009f); + writeLong(2, id); + writeSet(6); } } else if (joy[JOY_BTN2] && action_time) @@ -935,145 +940,175 @@ void do_input() } } -int get_packet_length(short id) -{ - int len = get_length(id); - if (len == -1) len = RFIFOW(2); - return len; -} - void do_parse() { - unsigned short id; - char *temp; - Being *being = NULL; - FloorItem *floorItem = NULL; - int len, n_items; + int n_items; Map *tiledMap = engine->getCurrentMap(); Equipment *equipment = Equipment::getInstance(); // We need at least 2 bytes to identify a packet - if (in_size >= 2) { - // Check if the received packet is complete - while (in_size >= (len = get_packet_length(id = RFIFOW(0)))) { -#ifdef DEBUG - printf("Packet_ID: %x\n", RFIFOW(0)); -#endif - - // Parse packet based on their id - switch (id) - { - case SMSG_LOGIN_SUCCESS: - // Connected to game server succesfully, set spawn point - player_node->x = get_x(RFIFOP(6)); - player_node->y = get_y(RFIFOP(6)); - break; + while (in_size >= 2) + { + MessageIn msg = get_next_message(); + + // Parse packet based on their id + switch (msg.getId()) + { + case SMSG_LOGIN_SUCCESS: + // Connected to game server succesfully, set spawn point + { + msg.readLong(); // server tick + msg.readCoordinates(player_node->x, + player_node->y, + player_node->direction); + msg.skip(2); // unknown + } + break; // Received speech from being - case SMSG_BEING_CHAT: - being = findNode(RFIFOL(4)); - if (being != NULL) + case SMSG_BEING_CHAT: + { + int chatMsgLength = msg.readShort() - 8; + Being *being = findNode(msg.readLong()); + + if (being != NULL && chatMsgLength > 0) { - int length = RFIFOW(2) - 8; - temp = (char*)malloc(length + 1); - temp[length] = '\0'; - memcpy(temp, RFIFOP(8), length); - std::string msg = std::string(temp); - msg.erase(0, msg.find(" : ", 0) + 3); + std::string chatMsg = msg.readString(chatMsgLength); - being->setSpeech(msg, SPEECH_TIME); - chatWindow->chat_log(temp, BY_OTHER); + chatWindow->chat_log(chatMsg, BY_OTHER); - free(temp); + chatMsg.erase(0, chatMsg.find(" : ", 0) + 3); + being->setSpeech(chatMsg, SPEECH_TIME); } - break; + } + break; - case SMSG_MY_BEING_CHAT: - case SMSG_GM_CHAT: - if (RFIFOW(2) > 4) + case SMSG_PLAYER_CHAT: + case SMSG_GM_CHAT: + { + int chatMsgLength = msg.readShort() - 4; + + if (chatMsgLength > 0) { - int length = RFIFOW(2) - 4; - temp = (char*)malloc(length + 1); - temp[length] = '\0'; - memcpy(temp, RFIFOP(4), length); - std::string msg = std::string(temp); - unsigned int pos = msg.find(" : ", 0); - - if (id == 0x008e) + std::string chatMsg = msg.readString(chatMsgLength); + + if (msg.getId() == SMSG_PLAYER_CHAT) { + chatWindow->chat_log(chatMsg, BY_PLAYER); + + unsigned int pos = chatMsg.find(" : ", 0); if (pos != std::string::npos) { - msg.erase(0, pos + 3); + chatMsg.erase(0, pos + 3); } - player_node->setSpeech(msg, SPEECH_TIME); - chatWindow->chat_log(temp, BY_PLAYER); + player_node->setSpeech(chatMsg, SPEECH_TIME); } - else + else { - chatWindow->chat_log(temp, BY_GM); + chatWindow->chat_log(chatMsg, BY_GM); } - - free(temp); - } - break; - - case SMSG_WALK_RESPONSE: - // It is assumed by the client any request to walk actually - // succeeds on the server. The plan is to have a correction - // message when the server senses the client has the wrong - // idea. - break; - - // Add new being / stop monster - case 0x0078: - - int beingId; - beingId = RFIFOL(2); - int beingJob; - beingJob = RFIFOW(14); - - // Being with id >= 110000000 and job 0 are better known - // as ghosts, so don't create those. - if (beingJob == 0 && beingId >= 110000000) - { - break; } + } + break; + + case SMSG_WALK_RESPONSE: + // It is assumed by the client any request to walk actually + // succeeds on the server. The plan is to have a correction + // message when the server senses the client has the wrong + // idea. + break; + + case SMSG_BEING_VISIBLE: + case SMSG_BEING_MOVE: + // Information about a being in range + { + int id = msg.readLong(); + unsigned short speed = msg.readShort(); + msg.readShort(); // unknown + msg.readShort(); // unknown + msg.readShort(); // option + unsigned short job = msg.readShort(); // class - being = findNode(beingId); + Being *being = findNode(id); if (being == NULL) { - being = createBeing(beingId, beingJob, tiledMap); - being->speed = RFIFOW(6); - if (being->speed == 0) { - // Else division by 0 when calculating frame - being->speed = 150; + // Being with id >= 110000000 and job 0 are better + // known as ghosts, so don't create those. + if (job == 0 && id >= 110000000) + { + break; } - being->setHairStyle(RFIFOW(16)); - being->setHairColor(RFIFOW(28)); - being->setWeapon(RFIFOW(18)); - being->setMap(tiledMap); + + being = createBeing(id, job, tiledMap); } - else + else if (msg.getId() == 0x0078) { being->clearPath(); - //being->setWeapon(RFIFOW(18)); being->frame = 0; being->walk_time = tick_time; being->action = Being::STAND; } - being->x = get_x(RFIFOP(46)); - being->y = get_y(RFIFOP(46)); - being->direction = get_direction(RFIFOP(46)); - break; - case SMSG_REMOVE_BEING: - // A being should be removed or has died - being = findNode(RFIFOL(2)); + // Prevent division by 0 when calculating frame + if (speed == 0) { speed = 150; } + + being->speed = speed; + being->job = job; + being->setHairStyle(msg.readShort()); + being->setWeapon(msg.readShort()); + msg.readShort(); // head option bottom + + if (msg.getId() == SMSG_BEING_MOVE) + { + msg.readLong(); // server tick + } + + msg.readShort(); // shield + msg.readShort(); // head option top + msg.readShort(); // head option mid + being->setHairColor(msg.readShort()); + msg.readShort(); // unknown + msg.readShort(); // head dir + msg.readShort(); // guild + msg.readShort(); // unknown + msg.readShort(); // unknown + msg.readShort(); // manner + msg.readShort(); // karma + msg.readByte(); // unknown + msg.readByte(); // sex + + if (msg.getId() == SMSG_BEING_MOVE) + { + unsigned short srcX, srcY, dstX, dstY; + msg.readCoordinatePair(srcX, srcY, dstX, dstY); + being->action = Being::STAND; + being->x = srcX; + being->y = srcY; + being->setDestination(dstX, dstY); + } + else + { + msg.readCoordinates(being->x, being->y, + being->direction); + } + + msg.readByte(); // unknown + msg.readByte(); // unknown + msg.readByte(); // unknown / sit + } + break; + + case SMSG_BEING_REMOVE: + // A being should be removed or has died + { + Being *being = findNode(msg.readLong()); + if (being != NULL) { - if (RFIFOB(6) == 1) - { // Death + if (msg.readByte() == 1) + { + // Death switch (being->getType()) { case Being::MONSTER: @@ -1086,275 +1121,326 @@ void do_parse() being->action = Being::DEAD; break; } - //remove_node(RFIFOL(2)); } - else { + else + { remove_node(being); } - if (being == autoTarget) { + if (being == autoTarget) + { autoTarget = NULL; } } - break; + } + break; + + case SMSG_PLAYER_UPDATE_1: + case SMSG_PLAYER_UPDATE_2: + case SMSG_PLAYER_MOVE: + // An update about a player, potentially including movement. + { + int id = msg.readLong(); + unsigned short speed = msg.readShort(); + msg.readShort(); // option 1 + msg.readShort(); // option 2 + msg.readShort(); // option + unsigned short job = msg.readShort(); - case SMSG_PLAYER_UPDATE_1: - case SMSG_PLAYER_UPDATE_2: - // A message about a player, doesn't include movement. - being = findNode(RFIFOL(2)); + Being *being = findNode(id); - if (being == NULL) + if (being == NULL) { - being = createBeing(RFIFOL(2), RFIFOW(14), tiledMap); + being = createBeing(id, job, tiledMap); } - being->speed = RFIFOW(6); - being->job = RFIFOW(14); - being->setHairStyle(RFIFOW(16)); - being->setHairColor(RFIFOW(28)); - being->x = get_x(RFIFOP(46)); - being->y = get_y(RFIFOP(46)); - being->direction = get_direction(RFIFOP(46)); - being->walk_time = tick_time; - being->frame = 0; - being->setWeaponById(RFIFOW(18)); + being->speed = speed; + being->job = job; + being->setHairStyle(msg.readShort()); + being->setWeaponById(msg.readShort()); // item id 1 + msg.readShort(); // item id 2 + msg.readShort(); // head option bottom - if (RFIFOB(51) == 2) + if (msg.getId() == SMSG_PLAYER_MOVE) { - being->action = Being::SIT; + msg.readLong(); // server tick } - break; - case SMSG_MOVE_BEING: - // A being nearby is moving - being = findNode(RFIFOL(2)); - - if (being == NULL) + msg.readShort(); // head option top + msg.readShort(); // head option mid + being->setHairColor(msg.readShort()); + msg.readShort(); // unknown + msg.readShort(); // head dir + msg.readLong(); // guild + msg.readLong(); // emblem + msg.readShort(); // manner + msg.readByte(); // karma + msg.readByte(); // sex + + if (msg.getId() == SMSG_PLAYER_MOVE) { - being = createBeing(RFIFOL(2), RFIFOW(14), tiledMap); + unsigned short srcX, srcY, dstX, dstY; + msg.readCoordinatePair(srcX, srcY, dstX, dstY); + being->x = srcX; + being->y = srcY; + being->setDestination(dstX, dstY); + } + else + { + msg.readCoordinates(being->x, being->y, + being->direction); } - being->action = Being::STAND; - being->x = get_src_x(RFIFOP(50)); - being->y = get_src_y(RFIFOP(50)); - being->speed = RFIFOW(6); - being->job = RFIFOW(14); - being->setDestination( - get_dest_x(RFIFOP(50)), - get_dest_y(RFIFOP(50))); - break; - - case SMSG_MOVE_PLAYER_BEING: - // A nearby player being moves - being = findNode(RFIFOL(2)); + msg.readByte(); // unknown + msg.readByte(); // unknown - if (being == NULL) + if (msg.getId() == SMSG_PLAYER_UPDATE_1) { - being = createBeing(RFIFOL(2), RFIFOW(14), tiledMap); + if (msg.readByte() == 2) + { + being->action = Being::SIT; + } + } + else if (msg.getId() == SMSG_PLAYER_MOVE) + { + msg.readByte(); // unknown } - being->speed = RFIFOW(6); - being->job = RFIFOW(14); - being->x = get_src_x(RFIFOP(50)); - being->y = get_src_y(RFIFOP(50)); - being->setHairStyle(RFIFOW(16)); - being->setWeaponById(RFIFOW(18)); - being->setHairColor(RFIFOW(32)); - - being->setDestination( - get_dest_x(RFIFOP(50)), - get_dest_y(RFIFOP(50))); - break; + msg.readByte(); // Lv + msg.readByte(); // unknown - // NPC dialog - case 0x00b4: - npcTextDialog->addText(RFIFOP(8)); - npcListDialog->setVisible(false); - npcTextDialog->setVisible(true); - current_npc = RFIFOL(4); + being->walk_time = tick_time; + being->frame = 0; + } + break; + + case SMSG_NPC_MESSAGE: + msg.readShort(); // length + current_npc = msg.readLong(); + npcTextDialog->addText(msg.readString(msg.getLength() - 8)); + npcListDialog->setVisible(false); + npcTextDialog->setVisible(true); + break; + + case SMSG_NPC_NEXT: + case SMSG_NPC_CLOSE: + // Next/Close button in NPC dialog, currently unused + break; + + case SMSG_TRADE_REQUEST: + // If a trade window or request window is already open, send a + // trade cancel to any other trade request. + // + // Note that it would be nice if the server would prevent this + // situation, and that the requesting player would get a + // special message about the player being occupied. + + if (tradeWindow->isVisible() == true || requestTradeDialogOpen) + { + writeWord(0, CMSG_TRADE_RESPONSE); + writeByte(2, 0x04); + writeSet(3); break; + } - // Trade: Receiving a request to trade - case 0x00e5: - // If a trade window is already open, send a trade cancel - // to any other trade request. - // It would still be nice to implement an independent message - // that the person you want to trade with can't do that now. - if (tradeWindow->isVisible() == true) - { - // 0xff packet means cancel - WFIFOW(0) = net_w_value(0x00e6); - WFIFOB(2) = net_b_value(4); - WFIFOSET(3); + requestTradeDialogOpen = true; + tradePartnerName = msg.readString(24); + new RequestTradeDialog(tradePartnerName); + break; + + case SMSG_TRADE_RESPONSE: + switch (msg.readByte()) + { + case 0: // Too far away + chatWindow->chat_log("Trading isn't possible. " + "Trade partner is too far away.", + BY_SERVER); break; + case 1: // Character doesn't exist + chatWindow->chat_log("Trading isn't possible. " + "Character doesn't exist.", + BY_SERVER); + break; + case 2: // Invite request check failed... + chatWindow->chat_log("Trade cancelled due to an " + "unknown reason.", BY_SERVER); + break; + case 3: // Trade accepted + tradeWindow->reset(); + tradeWindow->setCaption( + "Trade: You and " + tradePartnerName); + tradeWindow->setVisible(true); + requestTradeDialogOpen = false; + break; + case 4: // Trade cancelled + chatWindow->chat_log("Trade cancelled.", BY_SERVER); + tradeWindow->setVisible(false); + break; + default: // Shouldn't happen as well, but to be sure + chatWindow->chat_log("Unhandled trade cancel packet", + BY_SERVER); + break; + } + break; + + case SMSG_TRADE_ITEM_ADD: + { + long amount = msg.readLong(); + short type = msg.readShort(); + msg.readByte(); // identified flag + msg.readByte(); // attribute + msg.readByte(); // refine + msg.skip(8); // card (4 shorts) + + // TODO: handle also identified, etc + if (type == 0) { + tradeWindow->addMoney(amount); + } else { + tradeWindow->addItem(type, false, amount, false); } - if (!requestTradeDialogOpen) - { - requestTradeDialogOpen = true; - strcpy(tradePartnerName, RFIFOP(2)); - new RequestTradeDialog(RFIFOP(2)); - } - break; + } + break; - // Trade: Response - case 0x00e7: - switch (RFIFOB(2)) + case SMSG_TRADE_ITEM_ADD_RESPONSE: + // Trade: New Item add response (was 0x00ea, now 01b1) + { + Item *item = inventory->getItem(msg.readShort()); + short quantity = msg.readShort(); + + switch (msg.readByte()) { case 0: - // too far away - chatWindow->chat_log("Trading isn't possible. Trade partner is too far away.", BY_SERVER); + // Successfully added item + if (item->isEquipment() && item->isEquipped()) + { + inventory->unequipItem(item); + } + tradeWindow->addItem(item->getId(), true, quantity, + item->isEquipment()); + item->increaseQuantity(-quantity); break; case 1: - // Character doesn't exist - chatWindow->chat_log("Trading isn't possible. Character doesn't exist.", BY_SERVER); - break; - case 2: - // invite request check failed... - chatWindow->chat_log("Trade cancelled due to an unknown reason.", BY_SERVER); - break; - case 3: - // Trade accepted - tradeWindow->reset(); - tradeWindow->setCaption((std::string)"Trade: You and " + (std::string)tradePartnerName); - tradeWindow->setVisible(true); - requestTradeDialogOpen = false; - break; - case 4: - // Trade cancelled - chatWindow->chat_log("Trade cancelled.", BY_SERVER); - tradeWindow->setVisible(false); + // Add item failed - player overweighted + chatWindow->chat_log("Failed adding item. Trade " + "partner is over weighted.", + BY_SERVER); break; default: - // Shouldn't happen as well, but to be sure - chatWindow->chat_log("Unhandled trade cancel packet", - BY_SERVER); + chatWindow->chat_log("Failed adding item for " + "unknown reason.", BY_SERVER); break; } - break; - // Trade: Item added on trade partner's side - case 0x00e9: - // TODO: handle also identified, etc - if (RFIFOW(6) == 0) - { - tradeWindow->addMoney(RFIFOL(2)); - } - else - { - tradeWindow->addItem(RFIFOW(6), false, RFIFOL(2), false); - } - break; - // Trade: New Item add response - case 0x01b1: - { - Item *item = inventory->getItem(RFIFOW(2)); - switch (RFIFOB(6)) - { - case 0: - // Successfully added item - if (item->isEquipment() && item->isEquipped()) - { - inventory->unequipItem(item); - } - tradeWindow->addItem( - item->getId(), true, RFIFOW(4), - item->isEquipment()); - item->increaseQuantity(-RFIFOW(4)); - break; - case 1: - // Add item failed - player overweighted - chatWindow->chat_log("Failed adding item. Trade " - "partner is over weighted.", BY_SERVER); - break; - default: - //printf("Unhandled 0x00ea byte!\n"); - break; - } - } - break; - // Trade received Ok message - case 0x00ec: - // 0 means ok from myself, 1 means ok from other; - tradeWindow->receivedOk((RFIFOB(2) == 0)); - break; - - // Trade cancelled - case 0x00ee: - chatWindow->chat_log("Trade cancelled.", BY_SERVER); - tradeWindow->setVisible(false); - tradeWindow->reset(); - break; - - // Trade completed - case 0x00f0: - chatWindow->chat_log("Trade completed.", BY_SERVER); - tradeWindow->setVisible(false); - tradeWindow->reset(); - break; - // Get the items - // Only called on map load / warp - case 0x01ee: - // Reset all items to not load them twice on map change + } + break; + + case SMSG_TRADE_OK: + // 0 means ok from myself, 1 means ok from other; + tradeWindow->receivedOk(msg.readByte() == 0); + break; + + case SMSG_TRADE_CANCEL: + chatWindow->chat_log("Trade canceled.", BY_SERVER); + tradeWindow->setVisible(false); + tradeWindow->reset(); + break; + + case SMSG_TRADE_COMPLETE: + chatWindow->chat_log("Trade completed.", BY_SERVER); + tradeWindow->setVisible(false); + tradeWindow->reset(); + break; + + case SMSG_PLAYER_INVENTORY: + { + // Only called on map load / warp. First reset all items + // to not load them twice on map change. inventory->resetItems(); + msg.readShort(); // length + int number = (msg.getLength() - 4) / 18; - for (int loop = 0; loop < (RFIFOW(2) - 4) / 18; loop++) + for (int loop = 0; loop < number; loop++) { - inventory->addItem(RFIFOW(4 + loop * 18), - RFIFOW(4 + loop * 18 + 2), - RFIFOW(4 + loop * 18 + 6), false); + short index = msg.readShort(); + short itemId = msg.readShort(); + msg.readByte(); // type + msg.readByte(); // identify flag + short amount = msg.readShort(); + msg.skip(2); // unknown + msg.skip(8); // card (4 shorts) + + inventory->addItem(index, itemId, amount, false); + // Trick because arrows are not considered equipment - if (RFIFOW(4 + loop * 18 + 2) == 1199 || - RFIFOW(4 + loop * 18 + 2) == 529) + if (itemId == 1199 || itemId == 529) { - inventory->getItem( - RFIFOW(4 + loop * 18))->setEquipment(true); + inventory->getItem(index)->setEquipment(true); } } - break; + } + break; - // Get the equipments - case 0x00a4: + case SMSG_PLAYER_EQUIPMENT: + { + msg.readShort(); // length + int number = (msg.getLength() - 4) / 20; - for (int loop = 0; loop < ((RFIFOW(2) - 4) / 20); loop++) + for (int loop = 0; loop < number; loop++) { - inventory->addItem(RFIFOW(4 + loop * 20), - RFIFOW(4 + loop * 20 + 2), 1, true); - if (RFIFOW(4 + loop * 20 + 8)) + short index = msg.readShort(); + short itemId = msg.readShort(); + msg.readByte(); // type + msg.readByte(); // identify flag + msg.readShort(); // equip type + short equipPoint = msg.readShort(); + msg.readByte(); // attribute + msg.readByte(); // refine + msg.skip(8); // card + + inventory->addItem(index, itemId, 1, true); + + if (equipPoint) { int mask = 1; int position = 0; - while(!(RFIFOW(4+loop*20+8) & mask)) + while (!(equipPoint & mask)) { - mask *= 2; + mask <<= 1; position++; } - Item *item = inventory->getItem(RFIFOW(4+loop*20)); + Item *item = inventory->getItem(index); item->setEquipped(true); equipment->setEquipment(position - 1, item); } } - break; - // Can I use the item? - case 0x00a8: - if (RFIFOB(6) == 0) - { + } + break; + + case SMSG_ITEM_USE_RESPONSE: + { + short index = msg.readShort(); + short amount = msg.readShort(); + + if (msg.readByte() == 0) { chatWindow->chat_log("Failed to use item", BY_SERVER); + } else { + inventory->getItem(index)->setQuantity(amount); } - else - { - inventory->getItem(RFIFOW(2))->setQuantity(RFIFOW(4)); - } - break; - // Warp - case 0x0091: - memset(map_path, '\0', 480); - strcat(map_path, "maps/"); - strncat(map_path, RFIFOP(2), 497 - strlen(map_path)); - logger->log("Warping to %s (%d, %d)", - map_path, RFIFOW(18), RFIFOW(20)); + } + break; + + case SMSG_PLAYER_WARP: + { + // Set new map path + strcpy(map_path, + std::string("maps/" + msg.readString(16)).c_str()); strcpy(strrchr(map_path, '.') + 1, "tmx.gz"); - Map *oldMap; - oldMap = tiledMap; + int x = msg.readShort(); + int y = msg.readShort(); + + logger->log("Warping to %s (%d, %d)", map_path, x, y); + + Map *oldMap = tiledMap; tiledMap = MapReader::readMap(map_path); if (tiledMap) @@ -1371,21 +1457,23 @@ void do_parse() delete (*i); } beings.clear(); + autoTarget = NULL; + current_npc = 0; // Re-add the local player node beings.push_back(player_node); player_node->action = Being::STAND; player_node->frame = 0; - player_node->x = RFIFOW(18); - player_node->y = RFIFOW(20); + player_node->x = x; + player_node->y = y; player_node->setMap(tiledMap); - current_npc = 0; + // Send "map loaded" - WFIFOW(0) = net_w_value(0x007d); - WFIFOSET(2); - while (out_size > 0) flush(); + writeWord(0, 0x007d); + writeSet(2); + flush(); engine->setCurrentMap(tiledMap); } else @@ -1393,491 +1481,611 @@ void do_parse() logger->error("Could not find map file"); } if (oldMap) delete oldMap; - break; - // Action failed (ex. sit because you have not reached the right level) - case 0x0110: - CHATSKILL action; - action.skill = RFIFOW(2); - action.bskill = RFIFOW(4); - action.unused = RFIFOW(6); - action.success = RFIFOB(8); - action.reason = RFIFOB(9); - if(action.success != SKILL_FAILED && - action.bskill == BSKILL_EMOTE ) { - printf("Action: %d/%d", action.bskill, action.success); - } - chatWindow->chat_log(action); - break; - // Update stat values - case 0x00b0: - switch(RFIFOW(2)) { - //case 0x0000: - // player_node->speed; - // break; - case 0x0005: - char_info->hp = RFIFOW(4); - break; - case 0x0006: - char_info->max_hp = RFIFOW(4); - break; - case 0x0007: - char_info->sp = RFIFOW(4); - break; - case 0x0008: - char_info->max_sp = RFIFOW(4); - break; - case 0x000b: - char_info->lv = RFIFOW(4); - break; - case 0x000c: - char_info->skill_point = RFIFOW(4); - skillDialog->setPoints(char_info->skill_point); - break; - case 0x0018: - char_info->totalWeight = RFIFOW(4); - break; - case 0x0019: - char_info->maxWeight = RFIFOW(4); - break; - case 0x0037: - char_info->job_lv = RFIFOW(4); - break; - case 0x0009: - char_info->statsPointsToAttribute = RFIFOW(4); - break; - case 0x0035: - player_node->aspd = RFIFOW(4); - break; - } - if (char_info->hp == 0 && deathNotice == NULL) { - deathNotice = new OkDialog("Message", - "You're now dead, press ok to restart", - &deathNoticeListener); - deathNotice->releaseModalFocus(); - player_node->action = Being::DEAD; - } - break; - // Stop walking + } + break; + + case SMSG_SKILL_FAILED: + // Action failed (ex. sit because you have not reached the + // right level) + CHATSKILL action; + action.skill = msg.readShort(); + action.bskill = msg.readShort(); + action.unused = msg.readShort(); // unknown + action.success = msg.readByte(); + action.reason = msg.readByte(); + if (action.success != SKILL_FAILED && + action.bskill == BSKILL_EMOTE) + { + printf("Action: %d/%d", action.bskill, action.success); + } + chatWindow->chat_log(action); + break; + + case SMSG_PLAYER_STAT_UPDATE_1: + switch (msg.readShort()) + { + //case 0x0000: + // player_node->speed = msg.readLong(); + // break; + case 0x0005: + player_info->hp = msg.readLong(); + break; + case 0x0006: + player_info->max_hp = msg.readLong(); + break; + case 0x0007: + player_info->sp = msg.readLong(); + break; + case 0x0008: + player_info->max_sp = msg.readLong(); + break; + case 0x000b: + player_info->lv = msg.readLong(); + break; + case 0x000c: + player_info->skill_point = msg.readLong(); + skillDialog->setPoints(player_info->skill_point); + break; + case 0x0018: + player_info->totalWeight = msg.readLong(); + break; + case 0x0019: + player_info->maxWeight = msg.readLong(); + break; + case 0x0037: + player_info->job_lv = msg.readLong(); + break; + case 0x0009: + player_info->statsPointsToAttribute = msg.readLong(); + break; + case 0x0035: + player_node->aspd = msg.readLong(); + break; + } + + if (player_info->hp == 0 && deathNotice == NULL) + { + deathNotice = new OkDialog("Message", + "You're now dead, press ok to restart", + &deathNoticeListener); + deathNotice->releaseModalFocus(); + player_node->action = Being::DEAD; + } + break; + + // Stop walking // case 0x0088: // Disabled because giving some problems - //if (being = findNode(RFIFOL(2))) { - // if (being->getId()!=player_node->getId()) { - // being->action = STAND; - // being->frame = 0; - // set_coordinates(being->coordinates, RFIFOW(6), RFIFOW(8), get_direction(being->coordinates)); - // } - //} - //break; - // Damage, sit, stand up - case 0x008a: - switch (RFIFOB(26)) { + //if (being = findNode(readLong(2))) { + // if (being->getId() != player_node->getId()) { + // being->action = STAND; + // being->frame = 0; + // set_coordinates(being->coordinates, + // readWord(6), readWord(8), + // get_direction(being->coordinates)); + // } + //} + //break; + + case SMSG_BEING_ACTION: + { + Being *srcBeing = findNode(msg.readLong()); + Being *dstBeing = findNode(msg.readLong()); + msg.readLong(); // server tick + msg.readLong(); // src speed + msg.readLong(); // dst speed + short param1 = msg.readShort(); + msg.readShort(); // param 2 + char type = msg.readByte(); + msg.readShort(); // param 3 + + switch (type) + { case 0: // Damage - being = findNode(RFIFOL(6)); - if (being != NULL) { - - if (RFIFOW(22) == 0) { - // Yellow - being->setDamage("miss", SPEECH_TIME); - } else { - // Blue for monster, red for player - std::stringstream ss; - ss << RFIFOW(22); - being->setDamage(ss.str(), SPEECH_TIME); - } - - if (RFIFOL(2) != player_node->getId()) { // buggy - being = findNode(RFIFOL(2)); - if (being) { - being->action = Being::ATTACK; - being->frame = 0; - being->walk_time = tick_time; - being->frame = 0; - } - } + if (dstBeing == NULL) break; + + if (param1 == 0) { + // Yellow + dstBeing->setDamage("miss", SPEECH_TIME); + } else { + // Blue for monster, red for player + std::stringstream ss; + ss << param1; + dstBeing->setDamage(ss.str(), SPEECH_TIME); } - break; - case 2: // Sit - case 3: // Stand up - being = findNode(RFIFOL(2)); - if (being != NULL) { - being->frame = 0; - if (RFIFOB(26) == 2) { - being->action = Being::SIT; - } - else if (RFIFOB(26) == 3) { - being->action = Being::STAND; - } + + if (srcBeing != NULL && + srcBeing != player_node) + { + // buggy + srcBeing->action = Being::ATTACK; + srcBeing->frame = 0; + srcBeing->walk_time = tick_time; + srcBeing->frame = 0; } break; - } - break; - // Status change - case 0x00b1: - switch (RFIFOW(2)) { - case 1: - char_info->xp = RFIFOL(4); - break; - case 2: - char_info->job_xp = RFIFOL(4); - break; - case 20: - char_info->gp = RFIFOL(4); - break; - case 0x0016: - char_info->xpForNextLevel = RFIFOL(4); + + case 2: // Sit + if (srcBeing == NULL) break; + srcBeing->frame = 0; + srcBeing->action = Being::SIT; break; - case 0x0017: - char_info->jobXpForNextLevel = RFIFOL(4); + + case 3: // Stand up + if (srcBeing == NULL) break; + srcBeing->frame = 0; + srcBeing->action = Being::STAND; break; } - break; - // Level up - case 0x019b: + } + break; + + case SMSG_PLAYER_STAT_UPDATE_2: + switch (msg.readShort()) { + case 0x0001: + player_info->xp = msg.readLong(); + break; + case 0x0002: + player_info->job_xp = msg.readLong(); + break; + case 0x0014: + player_info->gp = msg.readLong(); + break; + case 0x0016: + player_info->xpForNextLevel = msg.readLong(); + break; + case 0x0017: + player_info->jobXpForNextLevel = msg.readLong(); + break; + } + break; + + case SMSG_BEING_LEVELUP: + if ((unsigned long)msg.readLong() == player_node->getId()) { logger->log("Level up"); - if (RFIFOL(2) == player_node->getId()) { - sound.playSfx("sfx/levelup.ogg"); - } - break; - // Emotion - case 0x00c0: - being = findNode(RFIFOL(2)); - if(being) { - being->emotion = RFIFOB(6); - being->emotion_time = EMOTION_TIME; - } - break; - // Update skill values - case 0x0141: - switch(RFIFOL(2)) { - case 0x000d: - char_info->STR = RFIFOL(6) + RFIFOL(10); // Base + Bonus - break; - case 0x000e: - char_info->AGI = RFIFOL(6) + RFIFOL(10); - break; - case 0x000f: - char_info->VIT = RFIFOL(6) + RFIFOL(10); - break; - case 0x0010: - char_info->INT = RFIFOL(6) + RFIFOL(10); - break; - case 0x0011: - char_info->DEX = RFIFOL(6) + RFIFOL(10); - break; - case 0x0012: - char_info->LUK = RFIFOL(6) + RFIFOL(10); - break; + sound.playSfx("sfx/levelup.ogg"); + } else { + logger->log("Someone else went level up"); + } + msg.readLong(); // type + break; + + case SMSG_BEING_EMOTION: + { + Being *being = findNode(msg.readLong()); + if (being == NULL) break; + + being->emotion = msg.readByte(); + being->emotion_time = EMOTION_TIME; + } + break; + + case SMSG_PLAYER_STAT_UPDATE_3: + { + long type = msg.readLong(); + long base = msg.readLong(); + long bonus = msg.readLong(); + long total = base + bonus; + + switch (type) { + case 0x000d: player_info->STR = total; break; + case 0x000e: player_info->AGI = total; break; + case 0x000f: player_info->VIT = total; break; + case 0x0010: player_info->INT = total; break; + case 0x0011: player_info->DEX = total; break; + case 0x0012: player_info->LUK = total; break; } - break; - // Buy/Sell dialog - case 0x00c4: - buyDialog->setVisible(false); - buyDialog->reset(); - sellDialog->setVisible(false); + } + break; + + case SMSG_NPC_BUY_SELL_CHOICE: + buyDialog->setVisible(false); + buyDialog->reset(); + sellDialog->setVisible(false); + sellDialog->reset(); + buySellDialog->setVisible(true); + current_npc = msg.readLong(); + break; + + case SMSG_NPC_BUY: + msg.readShort(); // length + n_items = (msg.getLength() - 4) / 11; + buyDialog->reset(); + buyDialog->setMoney(player_info->gp); + buyDialog->setVisible(true); + + for (int k = 0; k < n_items; k++) + { + long value = msg.readLong(); + msg.readLong(); // DCvalue + msg.readByte(); // type + short itemId = msg.readShort(); + buyDialog->addItem(itemId, value); + } + break; + + case SMSG_NPC_SELL: + msg.readShort(); // length + n_items = (msg.getLength() - 4) / 10; + if (n_items > 0) { sellDialog->reset(); - buySellDialog->setVisible(true); - current_npc = RFIFOL(2); - break; - // Buy dialog - case 0x00c6: - n_items = (len - 4) / 11; - buyDialog->reset(); - buyDialog->setMoney(char_info->gp); - buyDialog->setVisible(true); - for (int k = 0; k < n_items; k++) { - buyDialog->addItem(RFIFOW(4 + 11 * k + 9), RFIFOL(4 + 11 * k)); - } - break; - // Sell dialog - case 0x00c7: - n_items = (len - 4) / 10; - if (n_items > 0) { - sellDialog->reset(); - sellDialog->setVisible(true); - for (int k = 0; k < n_items; k++) { - Item *item = inventory->getItem(RFIFOW(4 + 10 * k)); - if (item && !(item->isEquipped())) { - sellDialog->addItem(item, RFIFOL(4 + 10 * k + 2)); - } + sellDialog->setVisible(true); + + for (int k = 0; k < n_items; k++) + { + short index = msg.readShort(); + long value = msg.readLong(); + msg.readLong(); // OCvalue + + Item *item = inventory->getItem(index); + if (item && !(item->isEquipped())) { + sellDialog->addItem(item, value); } } - else { - chatWindow->chat_log("Nothing to sell", BY_SERVER); - current_npc = 0; - } - break; - // Answer to buy - case 0x00ca: - if (RFIFOB(2) == 0) - chatWindow->chat_log("Thanks for buying", BY_SERVER); - else - chatWindow->chat_log("Unable to buy", BY_SERVER); - break; - // Answer to sell - case 0x00cb: - if (RFIFOB(2) == 0) - chatWindow->chat_log("Thanks for selling", BY_SERVER); - else - chatWindow->chat_log("Unable to sell", BY_SERVER); - break; - // Add item to inventory after you bought it/picked up - case 0x00a0: - if (RFIFOB(22) > 0) - chatWindow->chat_log("Unable to pick up item", BY_SERVER); - else { - inventory->addItem(RFIFOW(2), RFIFOW(6), - RFIFOW(4), RFIFOW(19) != 0); + } + else { + chatWindow->chat_log("Nothing to sell", BY_SERVER); + current_npc = 0; + } + break; + + case SMSG_NPC_BUY_RESPONSE: + if (msg.readByte() == 0) { + chatWindow->chat_log("Thanks for buying", BY_SERVER); + } else { + chatWindow->chat_log("Unable to buy", BY_SERVER); + } + break; + + case SMSG_NPC_SELL_RESPONSE: + if (msg.readByte() == 0) { + chatWindow->chat_log("Thanks for selling", BY_SERVER); + } else { + chatWindow->chat_log("Unable to sell", BY_SERVER); + } + break; + + case SMSG_PLAYER_INVENTORY_ADD: + { + short index = msg.readShort(); + short amount = msg.readShort(); + short itemId = msg.readShort(); + msg.readByte(); // identify flag + msg.readByte(); // attribute + msg.readByte(); // refine + msg.skip(8); // card + short equipType = msg.readShort(); + msg.readByte(); // type + char fail = msg.readByte(); + + if (fail > 0) { + chatWindow->chat_log("Unable to pick up item", + BY_SERVER); + } else { + inventory->addItem(index, itemId, amount, + equipType != 0); } - break; - // Decrease quantity of an item in inventory - case 0x00af: - inventory->getItem(RFIFOW(2))->increaseQuantity(-RFIFOW(4)); - break; - // Use an item - case 0x01c8: - inventory->getItem(RFIFOW(2))->setQuantity( RFIFOW(10)); - break; - // Skill list TAG - case 0x010f: - n_items = (len - 4) / 37; - skillDialog->cleanList(); - for (int k = 0; k < n_items; k++) + } + break; + + case SMSG_PLAYER_INVENTORY_REMOVE: + { + short index = msg.readShort(); + short amount = msg.readShort(); + inventory->getItem(index)->increaseQuantity(-amount); + } + break; + + case SMSG_PLAYER_INVENTORY_USE: + { + short index = msg.readShort(); + msg.readShort(); // item id + msg.readLong(); // id + short amountLeft = msg.readShort(); + msg.readByte(); // type + + inventory->getItem(index)->setQuantity(amountLeft); + } + break; + + case SMSG_PLAYER_SKILLS: + msg.readShort(); // length + n_items = (msg.getLength() - 4) / 37; + skillDialog->cleanList(); + + for (int k = 0; k < n_items; k++) + { + short skillId = msg.readShort(); + msg.readShort(); // target type + msg.readShort(); // unknown + short level = msg.readShort(); + short sp = msg.readShort(); + msg.readShort(); // range + std::string skillName = msg.readString(24); + char up = msg.readByte(); + + if (level != 0 || up != 0) { - if (RFIFOW(4 + k * 37 + 6) != 0 || - RFIFOB(4 + k * 37 + 36)!=0) - { - int skillId = RFIFOW(4 + k * 37); - if (skillDialog->hasSkill(skillId)) { - skillDialog->setSkill(skillId, - RFIFOW(4 + k * 37 + 6), - RFIFOB(4 + k * 37 + 36)); - } - else { - skillDialog->addSkill( - RFIFOW(4 + k * 37), - RFIFOW(4 + k * 37 + 6), - RFIFOW(4 + k * 37 + 8)); - } + if (skillDialog->hasSkill(skillId)) { + skillDialog->setSkill(skillId, level, sp); + } + else { + skillDialog->addSkill(skillId, level, sp); } } - break; - // Display MVP player - case 0x010c: - chatWindow->chat_log("MVP player", BY_SERVER); - break; - // Item found/dropped - case 0x009d: - case 0x009e: - floorItem = new FloorItem(); - floorItem->id = RFIFOW(6); - floorItem->x = RFIFOW(9); - floorItem->y = RFIFOW(11); - floorItem->int_id = RFIFOL(2); - add_floor_item(floorItem); - break; - // Item disappearing - case 0x00a1: - remove_floor_item(RFIFOL(2)); - break; - // Next/Close button in NPC dialog - case 0x00b5: - case 0x00b6: - // Unused - break; - // List in NPC dialog - case 0x00b7: - current_npc = RFIFOL(4); - // RFIFOW(2) seems to be the full packet length, thus -8 - npcListDialog->parseItems(RFIFOP(8), RFIFOW(2)-8); - npcListDialog->setVisible(true); - break; + } + break; - case SMSG_CHANGE_BEING_LOOKS: - being = findNode(RFIFOL(2)); - if (being) { - switch (RFIFOB(6)) { + case 0x010c: + // Display MVP player + msg.readLong(); // id + chatWindow->chat_log("MVP player", BY_SERVER); + break; + + case SMSG_ITEM_VISIBLE: + case SMSG_ITEM_DROPPED: + { + long id = msg.readLong(); + short itemId = msg.readShort(); + msg.readByte(); // identify flag + short x = msg.readShort(); + short y = msg.readShort(); + msg.skip(4); // amount,subX,subY / subX,subY,amount + + add_floor_item(new FloorItem(id, itemId, x, y)); + } + break; + + case SMSG_ITEM_REMOVE: + remove_floor_item(msg.readLong()); + break; + + case SMSG_NPC_CHOICE: + msg.readShort(); // length + current_npc = msg.readLong(); + npcListDialog->parseItems(msg.readString(msg.getLength() - 8)); + npcListDialog->setVisible(true); + break; + + case SMSG_BEING_CHANGE_LOOKS: + { + Being *being = findNode(msg.readLong()); + + if (being) + { + switch (msg.readByte()) { case 1: - being->setHairStyle(RFIFOB(7)); + being->setHairStyle(msg.readByte()); break; case 2: - being->setWeapon(RFIFOB(7)); - break; + being->setWeapon(msg.readByte()); + break; case 6: - being->setHairColor(RFIFOB(7)); + being->setHairColor(msg.readByte()); + break; + default: + msg.readByte(); // unsupported break; } } - break; + } + break; - // Answer to equip item - case 0x00aa: - logger->log("Equipping: %i %i %i", RFIFOW(2), RFIFOW(4), RFIFOB(6)); - if (RFIFOB(6) == 0) - chatWindow->chat_log("Unable to equip.", BY_SERVER); - else { - if(RFIFOW(4)) { - int mask = 1; - int position = 0; - while(!(RFIFOW(4) & mask)) { - mask *= 2; - position++; - } - logger->log("Position %i", position-1); - Item *item = equipment->getEquipment(position - 1); - if (item) - item->setEquipped(false); + case SMSG_PLAYER_EQUIP: + { + short index = msg.readShort(); + short equipPoint = msg.readShort(); + char type = msg.readByte(); - item = inventory->getItem(RFIFOW(2)); - item->setEquipped(true); - equipment->setEquipment(position - 1, item); - player_node->setWeaponById(item->getId()); + logger->log("Equipping: %i %i %i", + index, equipPoint, type); + + if (type == 0) { + chatWindow->chat_log("Unable to equip.", BY_SERVER); + } + else if (equipPoint) + { + // Unequip any existing equipped item in this position + int mask = 1; + int position = 0; + while (!equipPoint & mask) { + mask <<= 1; + position++; } + logger->log("Position %i", position - 1); + Item *item = equipment->getEquipment(position - 1); + if (item) { + item->setEquipped(false); + } + + item = inventory->getItem(index); + item->setEquipped(true); + equipment->setEquipment(position - 1, item); + player_node->setWeaponById(item->getId()); } - break; - // Equipment related - case 0x01d7: - being = findNode(RFIFOL(2)); + } + break; + + case 0x01d7: + // Equipment related + { + Being *being = findNode(msg.readLong()); + msg.readByte(); // equip point + short itemId1 = msg.readShort(); + msg.readShort(); // item id 2 + if (being != NULL) { - being->setWeaponById(RFIFOW(7)); + being->setWeaponById(itemId1); } - //logger->log("1d7 %i %i %i %i", RFIFOL(2), RFIFOB(6), RFIFOW(7), RFIFOW(9)); - break; + } + break; + + case SMSG_PLAYER_UNEQUIP: + { + short index = msg.readShort(); + short equipPoint = msg.readShort(); + char type = msg.readByte(); - // Answer to unequip item - case 0x00ac: - if (RFIFOB(6) == 0) + if (type == 0) { chatWindow->chat_log("Unable to unequip.", BY_SERVER); - else { - if(RFIFOW(4)) { - int mask = 1; - int position = 0; - while(!(RFIFOW(4) & mask)) { - mask *= 2; - position++; - } + break; + } - Item *item = inventory->getItem(RFIFOW(2)); - item->setEquipped(false); - switch (item->getId()) { - case 529: - case 1199: - equipment->setArrows(NULL); - break; - case 521: - case 522: - case 530: - case 536: - case 1200: - case 1201: - player_node->setWeapon(0); - break; // TODO : why this break ? shouldn't a weapon be unequipped in inventory too ? - default: - equipment->removeEquipment(position - 1); - break; - } - logger->log("Unequipping: %i %i(%i) %i", RFIFOW(2),RFIFOW(4),RFIFOB(6), position -1); - } + if (equipPoint == 0) { + // No point given, no point in searching + break; } - break; - // Arrows equipped - case 0x013c: - if (RFIFOW(2) > 1) { - Item *item = inventory->getItem(RFIFOW(2)); - item->setEquipped(true); - equipment->setArrows(item); - logger->log("Arrows equipped: %i", RFIFOW(2)); + + int mask = 1; + int position = 0; + while (!equipPoint & mask) { + mask <<= 1; + position++; } - break; - // Various messages - case 0x013b: - if (RFIFOW(2) == 0) - chatWindow->chat_log("Equip arrows first", BY_SERVER); - else - logger->log("0x013b: Unhandled message %i", RFIFOW(2)); - break; - // Updates a stat value - case 0x00bc: - if(RFIFOB(4)) { - switch(RFIFOW(2)) { - case 0x000d: - char_info->STR = RFIFOB(5); - break; - case 0x000e: - char_info->AGI = RFIFOB(5); - break; - case 0x000f: - char_info->VIT = RFIFOB(5); - break; - case 0x0010: - char_info->INT = RFIFOB(5); + + Item *item = inventory->getItem(index); + + if (item != NULL) + { + item->setEquipped(false); + + switch (item->getId()) { + case 529: + case 1199: + equipment->setArrows(NULL); break; - case 0x0011: - char_info->DEX = RFIFOB(5); + case 521: + case 522: + case 530: + case 536: + case 1200: + case 1201: + player_node->setWeapon(0); + // TODO: Why this break? Shouldn't a weapon be + // unequipped in inventory too? break; - case 0x0012: - char_info->LUK = RFIFOB(5); + default: + equipment->removeEquipment(position - 1); break; } + logger->log("Unequipping: %i %i(%i) %i", + index, equipPoint, type, position - 1); } - break; - // Updates stats and status points - case 0x00bd: - char_info->statsPointsToAttribute = RFIFOW(2); - char_info->STR = RFIFOB(4); - char_info->STRUp = RFIFOB(5); - char_info->AGI = RFIFOB(6); - char_info->AGIUp = RFIFOB(7); - char_info->VIT = RFIFOB(8); - char_info->VITUp = RFIFOB(9); - char_info->INT = RFIFOB(10); - char_info->INTUp = RFIFOB(11); - char_info->DEX = RFIFOB(12); - char_info->DEXUp = RFIFOB(13); - char_info->LUK = RFIFOB(14); - char_info->LUKUp = RFIFOB(15); - break; - // Updates status point - case 0x00be: - switch(RFIFOW(2)) { - case 0x0020: - char_info->STRUp = RFIFOB(4); - break; - case 0x0021: - char_info->AGIUp = RFIFOB(4); - break; - case 0x0022: - char_info->VITUp = RFIFOB(4); - break; - case 0x0023: - char_info->INTUp = RFIFOB(4); - break; - case 0x0024: - char_info->DEXUp = RFIFOB(4); + } + break; + + case SMSG_PLAYER_ARROW_EQUIP: + { + short id = msg.readShort(); + + if (id > 1) { + Item *item = inventory->getItem(id); + if (item) { + item->setEquipped(true); + equipment->setArrows(item); + logger->log("Arrows equipped: %i", id); + } + } + } + break; + + case SMSG_PLAYER_ARROW_MESSAGE: + { + short type = msg.readShort(); + + switch (type) { + case 0: + chatWindow->chat_log("Equip arrows first", + BY_SERVER); break; - case 0x0025: - char_info->LUKUp = RFIFOB(4); + default: + logger->log("0x013b: Unhandled message %i", type); break; } - break; - // Get being name - case 0x0095: - being = findNode(RFIFOL(2)); - if (being) + } + break; + + case SMSG_PLAYER_STAT_UPDATE_4: + { + short type = msg.readShort(); + char fail = msg.readByte(); + char value = msg.readByte(); + + if (fail == 1) { - //std::cout << RFIFOL(2) << " is " << RFIFOP(6) << std::endl; - strcpy(being->name, RFIFOP(6)); + switch (type) { + case 0x000d: player_info->STR = value; break; + case 0x000e: player_info->AGI = value; break; + case 0x000f: player_info->VIT = value; break; + case 0x0010: player_info->INT = value; break; + case 0x0011: player_info->DEX = value; break; + case 0x0012: player_info->LUK = value; break; + } } - break; + } + break; + + // Updates stats and status points + case SMSG_PLAYER_STAT_UPDATE_5: + player_info->statsPointsToAttribute = msg.readShort(); + player_info->STR = msg.readByte(); + player_info->STRUp = msg.readByte(); + player_info->AGI = msg.readByte(); + player_info->AGIUp = msg.readByte(); + player_info->VIT = msg.readByte(); + player_info->VITUp = msg.readByte(); + player_info->INT = msg.readByte(); + player_info->INTUp = msg.readByte(); + player_info->DEX = msg.readByte(); + player_info->DEXUp = msg.readByte(); + player_info->LUK = msg.readByte(); + player_info->LUKUp = msg.readByte(); + msg.readShort(); // ATK + msg.readShort(); // ATK bonus + msg.readShort(); // MATK max + msg.readShort(); // MATK min + msg.readShort(); // DEF + msg.readShort(); // DEF bonus + msg.readShort(); // MDEF + msg.readShort(); // MDEF bonus + msg.readShort(); // HIT + msg.readShort(); // FLEE + msg.readShort(); // FLEE bonus + msg.readShort(); // critical + msg.readShort(); // unknown + break; + + case SMSG_PLAYER_STAT_UPDATE_6: + switch (msg.readShort()) { + case 0x0020: player_info->STRUp = msg.readByte(); break; + case 0x0021: player_info->AGIUp = msg.readByte(); break; + case 0x0022: player_info->VITUp = msg.readByte(); break; + case 0x0023: player_info->INTUp = msg.readByte(); break; + case 0x0024: player_info->DEXUp = msg.readByte(); break; + case 0x0025: player_info->LUKUp = msg.readByte(); break; + } + break; + + case SMSG_BEING_NAME_RESPONSE: + { + Being *being = findNode(msg.readLong()); + + if (being) { + being->setName(msg.readString(24)); + } + } + break; + + case 0x0119: // Change in players look - case 0x0119: - break; - // Manage non implemented packets - default: - logger->log("Unhandled packet: %x", id); - break; - } + break; - RFIFOSKIP(len); + default: + // Manage non implemented packets + logger->log("Unhandled packet: %x", msg.getId()); + break; } + + skip(msg.getLength()); } } @@ -21,8 +21,10 @@ * $Id$ */ -#ifndef _TMW_GAME_H -#define _TMW_GAME_H +#ifndef _TMW_GAME_ +#define _TMW_GAME_ + +#include <string> #define SPEECH_TIME 80 #define SPEECH_MAX_TIME 100 @@ -31,12 +33,12 @@ #define IDLE 255 extern char map_path[480]; -extern char tradePartnerName[24]; +extern std::string tradePartnerName; extern int fps, frame, current_npc; extern volatile int tick_time; extern int server_tick; extern bool displayPathToMouse; -extern int startX, startY; +extern unsigned short startX, startY; enum { JOY_UP, @@ -78,11 +80,6 @@ void do_parse(); void do_exit(); /** - * Calculate packet length - */ -int get_packet_length(short); - -/** * Returns elapsed time. (Warning: very unsafe function, it supposes the delay * is always < 10 seconds) */ diff --git a/src/gui/buy.cpp b/src/gui/buy.cpp index 926f478e..3a32824a 100644 --- a/src/gui/buy.cpp +++ b/src/gui/buy.cpp @@ -235,11 +235,11 @@ void BuyDialog::action(const std::string& eventId) // there a better way to ensure this fails in an _obivous_ way in C++? else if (eventId == "buy" && (m_amountItems > 0 && m_amountItems <= m_maxItems)) { - WFIFOW(0) = net_w_value(0x00c8); - WFIFOW(2) = net_w_value(8); - WFIFOW(4) = net_w_value(m_amountItems); - WFIFOW(6) = net_w_value(shopInventory[selectedItem].id); - WFIFOSET(8); + writeWord(0, 0x00c8); + writeWord(2, 8); + writeWord(4, m_amountItems); + writeWord(6, shopInventory[selectedItem].id); + writeSet(8); // update money ! m_money -= m_amountItems * shopInventory[selectedItem].price; diff --git a/src/gui/buysell.cpp b/src/gui/buysell.cpp index c81744dc..6a56e67f 100644 --- a/src/gui/buysell.cpp +++ b/src/gui/buysell.cpp @@ -77,10 +77,10 @@ void BuySellDialog::action(const std::string& eventId) current_npc = 0; } if (actionId > -1) { - WFIFOW(0) = net_w_value(0x00c5); - WFIFOL(2) = net_l_value(current_npc); - WFIFOB(6) = net_b_value(actionId); - WFIFOSET(7); + writeWord(0, 0x00c5); + writeLong(2, current_npc); + writeByte(6, actionId); + writeSet(7); } setVisible(false); diff --git a/src/gui/char_select.cpp b/src/gui/char_select.cpp index 6389f5b0..071708b8 100644 --- a/src/gui/char_select.cpp +++ b/src/gui/char_select.cpp @@ -33,13 +33,11 @@ #include "playerbox.h" #include "textfield.h" #include "windowcontainer.h" - #include "../being.h" #include "../game.h" #include "../log.h" #include "../main.h" #include "../playerinfo.h" - #include "../net/network.h" #include "../net/protocol.h" @@ -134,7 +132,6 @@ void CharSelectDialog::action(const std::string& eventId) if (eventId == "ok" && n_character > 0) { // Start game serverCharSelect(); - close_session(); } else if (eventId == "cancel") { state = EXIT; @@ -192,90 +189,99 @@ void CharSelectDialog::setPlayerInfo(PLAYER_INFO *pi) void CharSelectDialog::serverCharDelete() { // Request character deletion - WFIFOW(0) = net_w_value(0x0068); - WFIFOL(2) = net_l_value(char_info->id); - WFIFOSET(46); + writeWord(0, 0x0068); + writeLong(2, char_info[0]->id); + writeSet(46); + + MessageIn msg = get_next_message(); - while ((in_size < 2) || (out_size > 0)) flush(); - if (RFIFOW(0) == 0x006f) { - RFIFOSKIP(2); + if (msg.getId() == 0x006f) + { + skip(msg.getLength()); + delete char_info[0]; free(char_info); n_character = 0; setPlayerInfo(NULL); new OkDialog(this, "Info", "Player deleted"); } - else if (RFIFOW(0) == 0x0070) { + else if (msg.getId() == 0x0070) + { new OkDialog(this, "Error", "Failed to delete character."); - RFIFOSKIP(3); + skip(msg.getLength()); } else { new OkDialog(this, "Error", "Unknown"); + skip(msg.getLength()); } } void CharSelectDialog::serverCharSelect() { // Request character selection - WFIFOW(0) = net_w_value(0x0066); - WFIFOB(2) = net_b_value(0); - WFIFOSET(3); + writeWord(0, 0x0066); + writeByte(2, 0); + writeSet(3); - while ((in_size < 3) || (out_size > 0)) { - flush(); - } + MessageIn msg = get_next_message(); - logger->log("CharSelect: Packet ID: %x, Length: %d, Packet_in_size %d", - RFIFOW(0), - get_length(RFIFOW(0)), - RFIFOW(2)); - logger->log("CharSelect: In_size: %d", in_size); + logger->log("CharSelect: Packet ID: %x, Length: %d, in_size: %d", + msg.getId(), msg.getLength(), in_size); - if (RFIFOW(0) == 0x0071) { - while (in_size < 28) { - flush(); - } - char_ID = RFIFOL(2); - - char mapName[17]; - mapName[17] = 0; - strncpy(mapName, RFIFOP(6), 16); - - memset(map_path, '\0', 480); - strcat(map_path, "maps/"); - strncat(map_path, mapName, 479 - strlen(map_path)); - map_address = RFIFOL(22); - map_port = RFIFOW(26); + if (msg.getId() == 0x0071) + { + char_ID = msg.readLong(); + std::string mapPath = "maps/" + msg.readString(16); + strcpy(map_path, mapPath.c_str()); + map_address = msg.readLong(); + map_port = msg.readShort(); + player_info = char_info[0]; state = GAME; - logger->log("CharSelect: Map: %s", mapName); - logger->log("CharSelect: Server: %s:%d", iptostring(map_address), map_port); - RFIFOSKIP(28); + logger->log("CharSelect: Map: %s", map_path); + logger->log("CharSelect: Server: %s:%d", iptostring(map_address), + map_port); close_session(); } - else if (RFIFOW(0) == 0x006c) { - switch (RFIFOB(2)) { + else if (msg.getId() == 0x006c) + { + switch (msg.readByte()) { case 0: new OkDialog(this, "Error", "Access denied"); break; case 1: new OkDialog(this, "Error", "Cannot use this ID"); break; + default: + new OkDialog(this, "Error", + "Unknown failure to select character"); + break; } - RFIFOSKIP(3); + skip(msg.getLength()); } - else if (RFIFOW(0) == 0x0081) { - new OkDialog(this, "Error", - "Map server is down, please try again later"); + else if (msg.getId() == 0x0081) + { + switch (msg.readByte()) { + case 3: + new OkDialog(this, "Error", "Speed hack detected"); + break; + case 8: + new OkDialog(this, "Error", "Duplicated login"); + break; + default: + new OkDialog(this, "Error", "Unkown error with 0x0081"); + break; + } close_session(); state = LOGIN; } + // Todo: add other packets } void CharSelectDialog::logic() { if (n_character > 0) { - setPlayerInfo(char_info); + setPlayerInfo(char_info[0]); } } @@ -391,63 +397,84 @@ void CharCreateDialog::action(const std::string& eventId) playerBox->hairStyle %= NR_HAIR_STYLES; } -std::string CharCreateDialog::getName() { +std::string CharCreateDialog::getName() +{ return nameField->getText(); } void CharCreateDialog::serverCharCreate() { - n_character = 1; - - WFIFOW(0) = net_w_value(0x0067); - strcpy(WFIFOP(2), getName().c_str()); - WFIFOB(26) = net_b_value(5); - WFIFOB(27) = net_b_value(5); - WFIFOB(28) = net_b_value(5); - WFIFOB(29) = net_b_value(5); - WFIFOB(30) = net_b_value(5); - WFIFOB(31) = net_b_value(5); - WFIFOB(32) = net_b_value(0); - WFIFOW(33) = net_w_value(playerBox->hairColor + 1); - WFIFOW(35) = net_w_value(playerBox->hairStyle + 1); - WFIFOSET(37); - - while ((in_size < 3) || (out_size > 0)) flush(); - if (RFIFOW(0) == 0x006d) { - while (in_size < 108) flush(); - char_info = (PLAYER_INFO *)malloc(sizeof(PLAYER_INFO)); - char_info->id = RFIFOL(2);//account_ID; - memset(char_info->name, '\0', 24); - strcpy(char_info[0].name, RFIFOP(2 + 74)); - char_info->hp = RFIFOW(2 + 42); - char_info->max_hp = RFIFOW(2 + 44); - char_info->sp = RFIFOW(2 + 46); - char_info->max_sp = RFIFOW(2 + 48); - char_info->job_lv = RFIFOL(2 + 16); - char_info->job_xp = RFIFOL(2 + 12); - char_info->lv = RFIFOW(2 + 58); - char_info->xp = RFIFOL(2 + 4); - char_info->gp = RFIFOL(2 + 8); - char_info->STR = RFIFOB(2 + 98); - char_info->AGI = RFIFOB(2 + 99); - char_info->VIT = RFIFOB(2 + 100); - char_info->INT = RFIFOB(2 + 101); - char_info->DEX = RFIFOB(2 + 102); - char_info->LUK = RFIFOB(2 + 103); - char_info->hair_style = RFIFOW(2 + 54); - char_info->hair_color = RFIFOW(2 + 70); - char_info->weapon = RFIFOW(2 + 56); - RFIFOSKIP(108); - //n_character++; - } else if (RFIFOW(0) == 0x006e) { + writeWord(0, 0x0067); + strcpy(writePointer(2), getName().c_str()); + writeByte(26, 5); + writeByte(27, 5); + writeByte(28, 5); + writeByte(29, 5); + writeByte(30, 5); + writeByte(31, 5); + writeByte(32, 0); + writeWord(33, playerBox->hairColor + 1); + writeWord(35, playerBox->hairStyle + 1); + writeSet(37); + + MessageIn msg = get_next_message(); + + if (msg.getId() == 0x006d) + { + char_info = (PLAYER_INFO**)malloc(sizeof(PLAYER_INFO*)); + char_info[0] = new PLAYER_INFO; + + char_info[0]->id = msg.readLong(); + char_info[0]->xp = msg.readLong(); + char_info[0]->gp = msg.readLong(); + char_info[0]->job_xp = msg.readLong(); + char_info[0]->job_lv = msg.readLong(); + msg.skip(8); // unknown + msg.readLong(); // option + msg.readLong(); // karma + msg.readLong(); // manner + msg.skip(2); // unknown + char_info[0]->hp = msg.readShort(); + char_info[0]->max_hp = msg.readShort(); + char_info[0]->sp = msg.readShort(); + char_info[0]->max_sp = msg.readShort(); + msg.readShort(); // speed + msg.readShort(); // class + char_info[0]->hair_style = msg.readShort(); + char_info[0]->weapon = msg.readShort(); + char_info[0]->lv = msg.readShort(); + msg.readShort(); // skill point + msg.readShort(); // head bottom + msg.readShort(); // shield + msg.readShort(); // head option top + msg.readShort(); // head option mid + char_info[0]->hair_color = msg.readShort(); + msg.readShort(); // unknown + char_info[0]->name = msg.readString(24); + char_info[0]->STR = msg.readByte(); + char_info[0]->AGI = msg.readByte(); + char_info[0]->VIT = msg.readByte(); + char_info[0]->INT = msg.readByte(); + char_info[0]->DEX = msg.readByte(); + char_info[0]->LUK = msg.readByte(); + msg.readByte(); // character number + msg.readByte(); // unknown + + n_character = 1; + } + else if (msg.getId() == 0x006e) + { new OkDialog(this, "Error", "Failed to create character"); - RFIFOSKIP(3); n_character = 0; - } else { + } + else + { new OkDialog(this, "Error", "Unknown error"); n_character = 0; } + skip(msg.getLength()); + // Remove window when succeeded if (n_character == 1) { windowContainer->scheduleDelete(this); diff --git a/src/gui/char_server.cpp b/src/gui/char_server.cpp index b4b01350..d3abfe7c 100644 --- a/src/gui/char_server.cpp +++ b/src/gui/char_server.cpp @@ -23,6 +23,7 @@ #include "char_server.h" +#include <sstream> #include <SDL.h> #include "button.h" @@ -37,6 +38,8 @@ #include "../net/network.h" +extern SERVER_INFO **server_info; + char server[30]; @@ -107,15 +110,16 @@ void ServerSelectDialog::action(const std::string& eventId) } -int ServerListModel::getNumberOfElements() { +int ServerListModel::getNumberOfElements() +{ return n_server; } -std::string ServerListModel::getElementAt(int i) { - static char buffer[30]; - sprintf(buffer, "%s (%i)", server_info[i].name, - server_info[i].online_users); - return buffer; +std::string ServerListModel::getElementAt(int i) +{ + std::stringstream s; + s << server_info[i]->name << " (" << server_info[i]->online_users << ")"; + return s.str(); } void charServerInputHandler(SDL_KeyboardEvent *keyEvent) @@ -130,12 +134,12 @@ void server_char_server(int serverIndex) { int ret; state = LOGIN; - const char *ipstring = iptostring(server_info[serverIndex].address); + const char *ipstring = iptostring(server_info[serverIndex]->address); // Connect to char server - ret = open_session(ipstring, server_info[serverIndex].port); + ret = open_session(ipstring, server_info[serverIndex]->port); - if (ret == SOCKET_ERROR) + if (ret == -1) { std::string str = std::string("Unable to connect to char server ") + std::string(ipstring); @@ -144,74 +148,93 @@ void server_char_server(int serverIndex) } // Send login infos - WFIFOW(0) = net_w_value(0x0065); - WFIFOL(2) = net_l_value(account_ID); - WFIFOL(6) = net_l_value(session_ID1); - WFIFOL(10) = net_l_value(session_ID2); - WFIFOW(14) = 0; - WFIFOB(16) = net_b_value(sex); - WFIFOSET(17); - + writeWord(0, 0x0065); + writeLong(2, account_ID); + writeLong(6, session_ID1); + writeLong(10, session_ID2); + writeWord(14, 0); + writeByte(16, sex); + writeSet(17); + + // Skipping a mysterious 4 bytes while ((in_size < 4) || (out_size > 0)) flush(); - RFIFOSKIP(4); + skip(4); - while (in_size < 3) flush(); + MessageIn msg = get_next_message(); - if (RFIFOW(0) == 0x006b) + if (msg.getId() == 0x006b) { - while (in_size < RFIFOW(2)) flush(); + // Skip length word and an additional mysterious 20 bytes + msg.skip(2 + 20); - n_character = (RFIFOW(2) - 24) / 106; - char_info = (PLAYER_INFO*)malloc(sizeof(PLAYER_INFO) * n_character); + // Derive number of characters from message length + n_character = (msg.getLength() - 24) / 106; + char_info = (PLAYER_INFO**)malloc(sizeof(PLAYER_INFO*) * n_character); for (int i = 0; i < n_character; i++) { - int n = 24 + 106 * i; - char_info[i].id = RFIFOL(n); - strcpy(char_info[i].name, RFIFOP(n + 74)); - char_info[i].hp = RFIFOW( n+ 42); - char_info[i].max_hp = RFIFOW(n + 44); - char_info[i].xp = RFIFOL(n + 4); - char_info[i].gp = RFIFOL(n + 8); - char_info[i].job_xp = RFIFOL(n + 12); - char_info[i].job_lv = RFIFOL(n + 16); - char_info[i].sp = RFIFOW(n + 46); - char_info[i].max_sp = RFIFOW(n + 48); - char_info[i].lv = RFIFOW(n + 58); - char_info[i].STR = RFIFOB(n + 98); - char_info[i].AGI = RFIFOB(n + 99); - char_info[i].VIT = RFIFOB(n + 100); - char_info[i].INT = RFIFOB(n + 101); - char_info[i].DEX = RFIFOB(n + 102); - char_info[i].LUK = RFIFOB(n + 103); - char_info[i].hair_style = RFIFOW(n + 54); - char_info[i].hair_color = RFIFOW(n + 70); - char_info[i].weapon = RFIFOW(n + 56); + char_info[i] = new PLAYER_INFO; + + char_info[i]->id = msg.readLong(); + char_info[i]->xp = msg.readLong(); + char_info[i]->gp = msg.readLong(); + char_info[i]->job_xp = msg.readLong(); + char_info[i]->job_lv = msg.readLong(); + msg.skip(8); // unknown + msg.readLong(); // option + msg.readLong(); // karma + msg.readLong(); // manner + msg.skip(2); // unknown + char_info[i]->hp = msg.readShort(); + char_info[i]->max_hp = msg.readShort(); + char_info[i]->sp = msg.readShort(); + char_info[i]->max_sp = msg.readShort(); + msg.readShort(); // speed + msg.readShort(); // class + char_info[i]->hair_style = msg.readShort(); + char_info[i]->weapon = msg.readShort(); + char_info[i]->lv = msg.readShort(); + msg.readShort(); // skill point + msg.readShort(); // head bottom + msg.readShort(); // shield + msg.readShort(); // head option top + msg.readShort(); // head option mid + char_info[i]->hair_color = msg.readShort(); + msg.readShort(); // unknown + char_info[i]->name = msg.readString(24); + char_info[i]->STR = msg.readByte(); + char_info[i]->AGI = msg.readByte(); + char_info[i]->VIT = msg.readByte(); + char_info[i]->INT = msg.readByte(); + char_info[i]->DEX = msg.readByte(); + char_info[i]->LUK = msg.readByte(); + msg.readByte(); // character number + msg.readByte(); // unknown } state = CHAR_SELECT; logger->log("CharServer: Player: %s (Packet ID: %x, Length: %d)", - char_info->name, RFIFOW(0), RFIFOW(2)); - + char_info[0]->name.c_str(), msg.getId(), msg.getLength()); - RFIFOSKIP(RFIFOW(2)); + skip(msg.getLength()); } - else if (RFIFOW(0) == 0x006c) + else if (msg.getId() == 0x006c) { std::string errorStr; - switch (RFIFOB(2)) { + switch (msg.readByte()) { case 0: errorStr = "Access denied"; break; case 1: errorStr = "Cannot use this ID"; break; default: errorStr = "Rejected from server"; break; } new OkDialog("Error", errorStr); - RFIFOSKIP(3); + skip(msg.getLength()); close_session(); } else { new OkDialog("Error", "Unknown error"); + skip(msg.getLength()); } // Todo: add other packets } diff --git a/src/gui/chargedialog.cpp b/src/gui/chargedialog.cpp index 5a475d72..797d5129 100644 --- a/src/gui/chargedialog.cpp +++ b/src/gui/chargedialog.cpp @@ -52,12 +52,15 @@ void ChargeDialog::action(const std::string& eventId) void ChargeDialog::logic() { // calculate time since the last attack was made - char_info->lastAttackTime += .01; // this a hack until someone explains + player_info->lastAttackTime += .01; // this a hack until someone explains // to me how to work the timer - if(char_info->lastAttackTime > 1){char_info->lastAttackTime=1;} - + if (player_info->lastAttackTime > 1) + { + player_info->lastAttackTime = 1; + } + // reset the progress bar to display accurate time since attack - progBar->setProgress(char_info->lastAttackTime); + progBar->setProgress(player_info->lastAttackTime); Window::logic(); } diff --git a/src/gui/chat.cpp b/src/gui/chat.cpp index 72de3c83..e6dabaaa 100644 --- a/src/gui/chat.cpp +++ b/src/gui/chat.cpp @@ -169,7 +169,7 @@ void ChatWindow::action(const std::string& eventId) curHist = history.end(); // Send the message to the server - chat_send(char_info[0].name, message.c_str()); + chat_send(player_info->name.c_str(), message.c_str()); // Clear the text from the chat input chatInput->setText(""); @@ -217,10 +217,10 @@ char *ChatWindow::chat_send(std::string nick, std::string msg) msg += "\0"; // send processed message - WFIFOW(0) = net_w_value(packid); - WFIFOW(2) = net_w_value((unsigned short)(msg.length()+4)); - memcpy(WFIFOP(4), msg.c_str(), msg.length()); - WFIFOSET((int)msg.length()+4); + writeWord(0, packid); + writeWord(2, (unsigned short)(msg.length() + 4)); + memcpy(writePointer(4), msg.c_str(), msg.length()); + writeSet((int)msg.length()+4); nick = msg = ""; return ""; } diff --git a/src/gui/inventorywindow.cpp b/src/gui/inventorywindow.cpp index b89f19a3..b65979db 100644 --- a/src/gui/inventorywindow.cpp +++ b/src/gui/inventorywindow.cpp @@ -105,7 +105,8 @@ void InventoryWindow::logic() // Update weight information std::stringstream tempstr; - tempstr << "Total Weight: " << char_info->totalWeight << " - Maximum Weight: " << char_info->maxWeight; + tempstr << "Total Weight: " << player_info->totalWeight + << " - Maximum Weight: " << player_info->maxWeight; weightLabel->setCaption(tempstr.str()); weightLabel->adjustSize(); } diff --git a/src/gui/login.cpp b/src/gui/login.cpp index 9913eb25..bc39c15f 100644 --- a/src/gui/login.cpp +++ b/src/gui/login.cpp @@ -30,6 +30,10 @@ #include <guichan/widgets/label.hpp> #include "../main.h" +#include "../configuration.h" +#include "../graphics.h" +#include "../log.h" +#include "../serverinfo.h" #include "button.h" #include "checkbox.h" @@ -37,13 +41,10 @@ #include "textfield.h" #include "ok_dialog.h" -#include "../configuration.h" -#include "../log.h" -#include "../serverinfo.h" - #include "../net/network.h" OkDialog *wrongLoginNotice = NULL; +SERVER_INFO **server_info; WrongUsernameNoticeListener wrongUsernameNoticeListener; WrongPasswordNoticeListener wrongPasswordNoticeListener; @@ -56,11 +57,11 @@ void WrongPasswordNoticeListener::setLoginDialog( void WrongPasswordNoticeListener::action(const std::string &eventId) { - // Reset the password and put the caret ready to retype it. - mLoginDialog->passField->setText(""); - mLoginDialog->passField->setCaretPosition(0); - mLoginDialog->passField->requestFocus(); - wrongLoginNotice = NULL; + // Reset the password and put the caret ready to retype it. + mLoginDialog->passField->setText(""); + mLoginDialog->passField->setCaretPosition(0); + mLoginDialog->passField->requestFocus(); + wrongLoginNotice = NULL; } void WrongUsernameNoticeListener::setLoginDialog( @@ -174,61 +175,73 @@ LoginDialog::~LoginDialog() void LoginDialog::action(const std::string& eventId) { - if (eventId == "ok") { + if (eventId == "ok") + { const std::string user = userField->getText(); logger->log("Network: Username is %s", user.c_str()); // Store config settings config.setValue("remember", keepCheck->isMarked()); - if (keepCheck->isMarked()) { + + if (keepCheck->isMarked()) + { config.setValue("username", user); config.setValue("host", serverField->getText()); - } else { + } + else + { config.setValue("username", ""); } // Check login - if (user.length() == 0) { - wrongLoginNotice = new OkDialog("Error", "Enter your username first", &wrongUsernameNoticeListener); - } else { - switch (attemptLogin(user, passField->getText()) ) - { - case LOGIN_UNKNOWN_ERROR: - wrongLoginNotice = new OkDialog("Error", "Unknown Error."); - default: - break; - - case LOGIN_WRONG_PASSWORD: - wrongLoginNotice = new OkDialog("Error", "Wrong Password", &wrongPasswordNoticeListener); - break; - - case LOGIN_UNREGISTERED_ID: - wrongLoginNotice = new OkDialog("Error", "Unregistered ID."); - break; - - case LOGIN_EXPIRED: - wrongLoginNotice = new OkDialog("Error", "This ID is expired"); - break; - - case LOGIN_REJECTED: - wrongLoginNotice = new OkDialog("Error", "Rejected from server"); - break; - - case LOGIN_BLOCKED: - wrongLoginNotice = new OkDialog("Error", "You have been blocked by the GM Team"); - break; - - case LOGIN_USERNAME_TWICE: - wrongLoginNotice = new OkDialog("Error", "The username does already exist."); - break; - + if (user.length() == 0) + { + wrongLoginNotice = new OkDialog("Error", + "Enter your username first", + &wrongUsernameNoticeListener); + } + else + { + int ret = attemptLogin(user, passField->getText()); + if (ret == LOGIN_WRONG_PASSWORD) + { + wrongLoginNotice = new OkDialog("Error", "Wrong Password", + &wrongPasswordNoticeListener); + } + else if (ret != LOGIN_OK) + { + std::string errorMsg = "Unknown error."; + + switch (ret) + { + case LOGIN_UNREGISTERED_ID: + errorMsg = "Unregistered ID."; + break; + case LOGIN_EXPIRED: + errorMsg = "This ID is expired"; + break; + case LOGIN_REJECTED: + errorMsg = "Rejected from server"; + break; + case LOGIN_BLOCKED: + errorMsg = "You have been blocked by the GM Team"; + break; + case LOGIN_USERNAME_TWICE: + errorMsg = "The username does already exist."; + break; + } + + wrongLoginNotice = new OkDialog("Error", errorMsg); } - close_session(); } - } else if (eventId == "cancel") { + } + else if (eventId == "cancel") + { state = EXIT; - } else if (eventId == "register") { + } + else if (eventId == "register") + { const std::string user = userField->getText(); logger->log("LoginDialog::register Username is %s", user.c_str()); @@ -241,46 +254,57 @@ void LoginDialog::action(const std::string& eventId) } // Check login - if (user.length() == 0) // No username + if (user.length() == 0) { - wrongLoginNotice = new OkDialog("Error", "Enter your username first.", &wrongUsernameNoticeListener); + // No username + wrongLoginNotice = new OkDialog("Error", + "Enter your username first.", + &wrongUsernameNoticeListener); } - else if (user.length() < LEN_MIN_USERNAME) // Name too short + else if (user.length() < LEN_MIN_USERNAME) { + // Name too short std::stringstream errorMessage; errorMessage << "The username needs to be at least "; errorMessage << LEN_MIN_USERNAME; errorMessage << " characters long."; - wrongLoginNotice = new OkDialog("Error", errorMessage.str(), &wrongUsernameNoticeListener); + wrongLoginNotice = new OkDialog("Error", errorMessage.str(), + &wrongUsernameNoticeListener); } - else if (user.length() > LEN_MAX_USERNAME - 1 ) // Name too long + else if (user.length() > LEN_MAX_USERNAME - 1 ) { + // Name too long std::stringstream errorMessage; errorMessage << "The username needs to be less than "; errorMessage << LEN_MAX_USERNAME; errorMessage << " characters long."; - wrongLoginNotice = new OkDialog("Error", errorMessage.str(), &wrongUsernameNoticeListener); + wrongLoginNotice = new OkDialog("Error", errorMessage.str(), + &wrongUsernameNoticeListener); } - else if (passField->getText().length() < LEN_MIN_PASSWORD) // Pass too short + else if (passField->getText().length() < LEN_MIN_PASSWORD) { + // Pass too short std::stringstream errorMessage; errorMessage << "The password needs to be at least "; errorMessage << LEN_MIN_PASSWORD; errorMessage << " characters long."; - wrongLoginNotice = new OkDialog("Error", errorMessage.str(), &wrongPasswordNoticeListener); + wrongLoginNotice = new OkDialog("Error", errorMessage.str(), + &wrongPasswordNoticeListener); } - else if (passField->getText().length() > LEN_MAX_PASSWORD - 1 ) // Pass too long + else if (passField->getText().length() > LEN_MAX_PASSWORD - 1 ) { + // Pass too long std::stringstream errorMessage; errorMessage << "The password needs to be less than "; errorMessage << LEN_MAX_PASSWORD; errorMessage << " characters long."; - wrongLoginNotice = new OkDialog("Error", errorMessage.str(), &wrongPasswordNoticeListener); + wrongLoginNotice = new OkDialog("Error", errorMessage.str(), + &wrongPasswordNoticeListener); } - else // If no errors, register the new user. + else { + // No errors detected, register the new user. attemptLogin(user + "_M", passField->getText()); - close_session(); } } } @@ -293,7 +317,8 @@ void loginInputHandler(SDL_KeyboardEvent *keyEvent) } } -int attemptLogin(const std::string& user, const std::string& pass) { +int attemptLogin(const std::string& user, const std::string& pass) +{ int ret; // Connect to login server @@ -301,55 +326,69 @@ int attemptLogin(const std::string& user, const std::string& pass) { config.getValue("host", "animesites.de").c_str(), (short)config.getValue("port", 0)); - if (ret == SOCKET_ERROR) { + if (ret == -1) { state = LOGIN; - wrongLoginNotice = new OkDialog("Error", "Unable to connect to login server"); + wrongLoginNotice = new OkDialog("Error", + "Unable to connect to login server"); return LOGIN_NO_CONNECTION; } - // Send login infos - WFIFOW(0) = net_w_value(0x0064); + // Send login infos + MessageOut outMsg; + outMsg.writeShort(0x0064); + outMsg.writeLong(0); // client version + outMsg.writeString(user, 24); + outMsg.writeString(pass, 24); + outMsg.writeByte(0); // unknown + // TODO: still have to use writeSet as skip for reading + writeSet(55); + + // Receive reply + MessageIn msg = get_next_message(); - WFIFOL(2) = 0; + // Login ok + if (msg.getId() == 0x0069) + { + // Skip the length word + msg.skip(2); - memcpy(WFIFOP(6), user.c_str(), LEN_MAX_USERNAME - 1); - memcpy(WFIFOP(30), pass.c_str(), LEN_MAX_PASSWORD - 1); - WFIFOB(54) = 0; - WFIFOSET(55); + n_server = (msg.getLength() - 47) / 32; + server_info = (SERVER_INFO**)malloc(sizeof(SERVER_INFO*) * n_server); - while ((in_size < 23) || (out_size > 0)) { - flush(); - } + session_ID1 = msg.readLong(); + account_ID = msg.readLong(); + session_ID2 = msg.readLong(); + msg.skip(30); // unknown + sex = msg.readByte(); - // Login ok - if (RFIFOW(0) == 0x0069) { - while (in_size < RFIFOW(2)) { - flush(); - } - n_server = (RFIFOW(2) - 47) / 32; - server_info = (SERVER_INFO*)malloc(sizeof(SERVER_INFO) * n_server); - account_ID = RFIFOL(8); - session_ID1 = RFIFOL(4); - session_ID2 = RFIFOL(12); - sex = RFIFOB(46); - for (int i = 0; i < n_server; i++) { - server_info[i].address = RFIFOL(47 + 32 * i); - memcpy(server_info[i].name, RFIFOP(47 + 32 * i + 6), 20); - server_info[i].online_users = RFIFOW(47 + 32 * i + 26); - server_info[i].port = RFIFOW(47 + 32 * i + 4); - state = CHAR_SERVER; + for (int i = 0; i < n_server; i++) + { + server_info[i] = new SERVER_INFO; + + server_info[i]->address = msg.readLong(); + server_info[i]->port = msg.readShort(); + server_info[i]->name = msg.readString(20); + server_info[i]->online_users = msg.readLong(); + msg.skip(2); // unknown + + logger->log("Network: Server: %s (%s:%d)", + server_info[i]->name.c_str(), + iptostring(server_info[i]->address), + server_info[i]->port); } - logger->log("Network: Server: %s (%s:%d)", server_info[0].name, - iptostring(server_info[0].address), - server_info[0].port); - RFIFOSKIP(RFIFOW(2)); - return LOGIN_OK; + + state = CHAR_SERVER; + + skip(msg.getLength()); + ret = LOGIN_OK; } - else if (RFIFOW(0) == 0x006a) { - logger->log("Login::error code: %i", RFIFOB(2)); + else if (msg.getId() == 0x006a) + { + int loginError = msg.readByte(); + logger->log("Login::error code: %i", loginError); ret = 0; - switch (RFIFOB(2)) { + switch (loginError) { case 0: ret = LOGIN_UNREGISTERED_ID; break; @@ -369,13 +408,16 @@ int attemptLogin(const std::string& user, const std::string& pass) { ret = LOGIN_USERNAME_TWICE; break; } + skip(msg.getLength()); state = LOGIN; - RFIFOSKIP(23); - return ret; } else { + skip(msg.getLength()); state = LOGIN; - return LOGIN_UNKNOWN_ERROR; + ret = LOGIN_UNKNOWN_ERROR; } // Todo: add other packets, also encrypted + + close_session(); + return ret; } diff --git a/src/gui/npc.cpp b/src/gui/npc.cpp index 0756d809..c8d426f3 100644 --- a/src/gui/npc.cpp +++ b/src/gui/npc.cpp @@ -70,20 +70,23 @@ NpcListDialog::~NpcListDialog() delete scrollArea; } -int NpcListDialog::getNumberOfElements() +int +NpcListDialog::getNumberOfElements() { return items.size(); } -std::string NpcListDialog::getElementAt(int i) +std::string +NpcListDialog::getElementAt(int i) { return items[i]; } -void NpcListDialog::parseItems(const char *string, unsigned short len) { - char *copy = new char[len + 1]; - strncpy(copy, string, len); - copy[len] = 0; +void +NpcListDialog::parseItems(const std::string &itemString) +{ + char *copy = new char[itemString.length() + 1]; + strcpy(copy, itemString.c_str()); char *token = strtok(copy, ":"); while (token) { @@ -94,20 +97,23 @@ void NpcListDialog::parseItems(const char *string, unsigned short len) { delete[] copy; } -void NpcListDialog::reset() { +void +NpcListDialog::reset() +{ items.clear(); } -void NpcListDialog::action(const std::string& eventId) +void +NpcListDialog::action(const std::string& eventId) { if (eventId == "ok") { // Send the selected index back to the server int selectedIndex = itemList->getSelected(); if (selectedIndex > -1) { - WFIFOW(0) = net_w_value(0x00b8); - WFIFOL(2) = net_l_value(current_npc); - WFIFOB(6) = net_b_value(selectedIndex + 1); - WFIFOSET(7); + writeWord(0, 0x00b8); + writeLong(2, current_npc); + writeByte(6, selectedIndex + 1); + writeSet(7); setVisible(false); current_npc = 0; reset(); @@ -115,10 +121,10 @@ void NpcListDialog::action(const std::string& eventId) } else if (eventId == "cancel") { // 0xff packet means cancel - WFIFOW(0) = net_w_value(0x00b8); - WFIFOL(2) = net_l_value(current_npc); - WFIFOB(6) = net_b_value(0xff); - WFIFOSET(7); + writeWord(0, 0x00b8); + writeLong(2, current_npc); + writeByte(6, 0xff); + writeSet(7); setVisible(false); reset(); current_npc = 0; diff --git a/src/gui/npc.h b/src/gui/npc.h index 336bad62..e9960cd2 100644 --- a/src/gui/npc.h +++ b/src/gui/npc.h @@ -58,29 +58,34 @@ class NpcListDialog : public Window, public gcn::ActionListener, /** * Called when receiving actions from the widgets. */ - void action(const std::string& eventId); + void + action(const std::string& eventId); /** * Returns the number of items in the choices list. */ - int getNumberOfElements(); + int + getNumberOfElements(); /** * Returns the name of item number i of the choices list. */ - std::string getElementAt(int i); + std::string + getElementAt(int i); /** * Fills the options list for an NPC dialog. * - * @param string A string with the options separated with colons. + * @param itemString A string with the options separated with colons. */ - void parseItems(const char *string, unsigned short len); + void + parseItems(const std::string &itemString); /** * Resets the list by removing all items. */ - void reset(); + void + reset(); private: gcn::Button *okButton; diff --git a/src/gui/npc_text.cpp b/src/gui/npc_text.cpp index 7fc59343..67a9e9a3 100644 --- a/src/gui/npc_text.cpp +++ b/src/gui/npc_text.cpp @@ -61,22 +61,24 @@ NpcTextDialog::~NpcTextDialog() delete scrollArea; } -void NpcTextDialog::setText(const char *text) +void +NpcTextDialog::setText(const char *text) { textBox->setText(text); } -void NpcTextDialog::addText(const char *text) +void +NpcTextDialog::addText(const std::string &text) { - textBox->setText( - textBox->getText() + std::string(text) + std::string("\n")); + textBox->setText(textBox->getText() + text + "\n"); } -void NpcTextDialog::action(const std::string& eventId) +void +NpcTextDialog::action(const std::string& eventId) { - WFIFOW(0) = net_w_value(0x00b9); - WFIFOL(2) = net_l_value(current_npc); - WFIFOSET(6); + writeWord(0, 0x00b9); + writeLong(2, current_npc); + writeSet(6); setText(""); setVisible(false); current_npc = 0; diff --git a/src/gui/npc_text.h b/src/gui/npc_text.h index 41301aa9..02fae7f4 100644 --- a/src/gui/npc_text.h +++ b/src/gui/npc_text.h @@ -25,11 +25,10 @@ #define _TMW_NPC_TEXT_H #include <iosfwd> - +#include <string> #include <guichan/actionlistener.hpp> #include "window.h" - #include "../guichanfwd.h" /** @@ -55,14 +54,16 @@ class NpcTextDialog : public Window, public gcn::ActionListener /** * Called when receiving actions from the widgets. */ - void action(const std::string& eventId); + void + action(const std::string &eventId); /** * Sets the text shows in the dialog. * * @param string The new text. */ - void setText(const char *string); + void + setText(const char *string); /** * Adds the text to the text shows in the dialog. Also adds a newline @@ -70,7 +71,8 @@ class NpcTextDialog : public Window, public gcn::ActionListener * * @param string The text to add. */ - void addText(const char *string); + void + addText(const std::string &string); private: gcn::Button *okButton; diff --git a/src/gui/popupmenu.cpp b/src/gui/popupmenu.cpp index 85b1b43a..3ea3e141 100644 --- a/src/gui/popupmenu.cpp +++ b/src/gui/popupmenu.cpp @@ -72,22 +72,22 @@ PopupMenu::~PopupMenu() void PopupMenu::showPopup(int x, int y, Being *being) { - std::string name; - this->being = being; browserBox->clearRows(); switch (being->getType()) { case Being::PLAYER: - // Players can be traded with. Later also attack, follow and - // add as buddy will be options in this menu. - - name = being->name; - //browserBox->addRow("@@attack|Attack " + name + "@@"); - browserBox->addRow("@@trade|Trade With " + name + "@@"); - //browserBox->addRow("@@follow|Follow " + name + "@@"); - //browserBox->addRow("@@buddy|Add " + name + " to Buddy List@@"); + { + // Players can be traded with. Later also attack, follow and + // add as buddy will be options in this menu. + const std::string &name = being->getName(); + browserBox->addRow("@@trade|Trade With " + name + "@@"); + + //browserBox->addRow("@@attack|Attack " + name + "@@"); + //browserBox->addRow("@@follow|Follow " + name + "@@"); + //browserBox->addRow("@@buddy|Add " + name + " to Buddy List@@"); + } break; case Being::NPC: @@ -114,7 +114,7 @@ void PopupMenu::showPopup(int x, int y, FloorItem *floorItem) browserBox->clearRows(); // Floor item can be picked up (single option, candidate for removal) - std::string name = itemDb->getItemInfo(floorItem->id)->getName(); + std::string name = itemDb->getItemInfo(floorItem->getItemId())->getName(); browserBox->addRow("@@pickup|Pick Up " + name + "@@"); //browserBox->addRow("@@look|Look To@@"); @@ -130,22 +130,22 @@ void PopupMenu::handleLink(const std::string& link) if ((link == "talk") && being && being->getType() == Being::NPC && (current_npc == 0)) { - WFIFOW(0) = net_w_value(0x0090); - WFIFOL(2) = net_l_value(being->getId()); - WFIFOB(6) = 0; - WFIFOSET(7); + writeWord(0, 0x0090); + writeLong(2, being->getId()); + writeByte(6, 0); + writeSet(7); current_npc = being->getId(); } // Trade action else if ((link == "trade") && being && being->getType() == Being::PLAYER) { - WFIFOW(0) = net_w_value(0x00e4); - WFIFOL(2) = net_l_value(being->getId()); - WFIFOSET(6); + writeWord(0, 0x00e4); + writeLong(2, being->getId()); + writeSet(6); //tradePartner.flush(); //tradePartner << "Trade: You and " << being->name<< ""; - strcpy(tradePartnerName, being->name); + tradePartnerName = being->getName(); } /* // Follow Player action @@ -160,15 +160,15 @@ void PopupMenu::handleLink(const std::string& link) if (!buddyWindow->isVisible()) buddyWindow->setVisible(true); - buddyWindow->addBuddy(being->name); + buddyWindow->addBuddy(being->getName()); }*/ // Pick Up Floor Item action else if ((link == "pickup") && floorItem) { - WFIFOW(0) = net_w_value(0x009f); - WFIFOL(2) = net_l_value(floorItem->int_id); - WFIFOSET(6); + writeWord(0, 0x009f); + writeLong(2, floorItem->getId()); + writeSet(6); } // Look To action diff --git a/src/gui/requesttrade.cpp b/src/gui/requesttrade.cpp index 35654562..e3b70fd3 100644 --- a/src/gui/requesttrade.cpp +++ b/src/gui/requesttrade.cpp @@ -24,16 +24,14 @@ #include "requesttrade.h" #include <sstream> - #include <guichan/widgets/label.hpp> #include "button.h" - #include "../net/network.h" bool requestTradeDialogOpen = false; -RequestTradeDialog::RequestTradeDialog(const char *name): +RequestTradeDialog::RequestTradeDialog(const std::string &name): Window("Request for Trade", true) { nameLabel[0] = new gcn::Label(""); @@ -88,16 +86,16 @@ void RequestTradeDialog::action(const std::string& eventId) { if (eventId == "accept") { // Send the selected index back to the server - WFIFOW(0) = net_w_value(0x00e6); - WFIFOB(2) = net_b_value(3); - WFIFOSET(3); + writeWord(0, 0x00e6); + writeByte(2, 3); + writeSet(3); scheduleDelete(); } else if (eventId == "cancel") { // 0xff packet means cancel - WFIFOW(0) = net_w_value(0x00e6); - WFIFOB(2) = net_b_value(4); - WFIFOSET(3); + writeWord(0, 0x00e6); + writeByte(2, 4); + writeSet(3); requestTradeDialogOpen = false; scheduleDelete(); } diff --git a/src/gui/requesttrade.h b/src/gui/requesttrade.h index 8c9ac29a..4cd33a49 100644 --- a/src/gui/requesttrade.h +++ b/src/gui/requesttrade.h @@ -21,15 +21,13 @@ * $Id$ */ -#ifndef _TMW_REQUESTTRADE_H -#define _TMW_REQUESTTRADE_H +#ifndef _TMW_REQUESTTRADE_ +#define _TMW_REQUESTTRADE_ #include <iosfwd> - #include <guichan/actionlistener.hpp> #include "window.h" - #include "../guichanfwd.h" extern bool requestTradeDialogOpen; @@ -47,7 +45,7 @@ class RequestTradeDialog : public Window, public gcn::ActionListener * * @see Window::Window */ - RequestTradeDialog(const char *name); + RequestTradeDialog(const std::string &name); /** * Destructor. diff --git a/src/gui/sell.cpp b/src/gui/sell.cpp index 2684fc2a..b6cbe5df 100644 --- a/src/gui/sell.cpp +++ b/src/gui/sell.cpp @@ -230,11 +230,11 @@ void SellDialog::action(const std::string& eventId) // Attempt sell assert(m_amountItems > 0 && m_amountItems <= m_maxItems); - WFIFOW(0) = net_w_value(0x00c9); - WFIFOW(2) = net_w_value(8); - WFIFOW(4) = net_w_value(shopInventory[selectedItem].index); - WFIFOW(6) = net_w_value(m_amountItems); - WFIFOSET(8); + writeWord(0, 0x00c9); + writeWord(2, 8); + writeWord(4, shopInventory[selectedItem].index); + writeWord(6, m_amountItems); + writeSet(8); m_maxItems -= m_amountItems; m_amountItems = 0; diff --git a/src/gui/skill.cpp b/src/gui/skill.cpp index 5244b2f3..0e641768 100644 --- a/src/gui/skill.cpp +++ b/src/gui/skill.cpp @@ -122,12 +122,11 @@ void SkillDialog::action(const std::string& eventId) { // Increment skill int selectedSkill = skillListBox->getSelected(); - if (char_info->skill_point > 0 && selectedSkill >= 0) + if (player_info->skill_point > 0 && selectedSkill >= 0) { - WFIFOW(0) = net_w_value(0x0112); - WFIFOW(2) = net_w_value( - skillList[selectedSkill]->id); - WFIFOSET(4); + writeWord(0, 0x0112); + writeWord(2, skillList[selectedSkill]->id); + writeSet(4); } } else if (eventId == "skill") diff --git a/src/gui/stats.cpp b/src/gui/stats.cpp index f03d2bfc..22f51827 100644 --- a/src/gui/stats.cpp +++ b/src/gui/stats.cpp @@ -89,36 +89,36 @@ void StatsWindow::update(){ std::stringstream remainingStatsPointsStr; statsStr[0] << "Strength:"; - figureStr[0] << (int)char_info->STR; + figureStr[0] << (int)player_info->STR; statsStr[1] << "Agility:"; - figureStr[1] << (int)char_info->AGI; + figureStr[1] << (int)player_info->AGI; statsStr[2] << "Vitality:"; - figureStr[2] << (int)char_info->VIT; + figureStr[2] << (int)player_info->VIT; statsStr[3] << "Intelligence:"; - figureStr[3] << (int)char_info->INT; + figureStr[3] << (int)player_info->INT; statsStr[4] << "Dexterity:"; - figureStr[4] << (int)char_info->DEX; + figureStr[4] << (int)player_info->DEX; statsStr[5] << "Luck:"; - figureStr[5] << (int)char_info->LUK; + figureStr[5] << (int)player_info->LUK; - int statusPoints = char_info->statsPointsToAttribute; + int statusPoints = player_info->statsPointsToAttribute; remainingStatsPointsStr << "Remaining Status Points: " << statusPoints; - - pointsStr[0] << (int)char_info->STRUp; - pointsStr[1] << (int)char_info->AGIUp; - pointsStr[2] << (int)char_info->VITUp; - pointsStr[3] << (int)char_info->INTUp; - pointsStr[4] << (int)char_info->DEXUp; - pointsStr[5] << (int)char_info->LUKUp; + + pointsStr[0] << (int)player_info->STRUp; + pointsStr[1] << (int)player_info->AGIUp; + pointsStr[2] << (int)player_info->VITUp; + pointsStr[3] << (int)player_info->INTUp; + pointsStr[4] << (int)player_info->DEXUp; + pointsStr[5] << (int)player_info->LUKUp; // Enable buttons for which there are enough status points - statsButton[0]->setEnabled(char_info->STRUp <= statusPoints); - statsButton[1]->setEnabled(char_info->AGIUp <= statusPoints); - statsButton[2]->setEnabled(char_info->VITUp <= statusPoints); - statsButton[3]->setEnabled(char_info->INTUp <= statusPoints); - statsButton[4]->setEnabled(char_info->DEXUp <= statusPoints); - statsButton[5]->setEnabled(char_info->LUKUp <= statusPoints); + statsButton[0]->setEnabled(player_info->STRUp <= statusPoints); + statsButton[1]->setEnabled(player_info->AGIUp <= statusPoints); + statsButton[2]->setEnabled(player_info->VITUp <= statusPoints); + statsButton[3]->setEnabled(player_info->INTUp <= statusPoints); + statsButton[4]->setEnabled(player_info->DEXUp <= statusPoints); + statsButton[5]->setEnabled(player_info->LUKUp <= statusPoints); // Update labels for (i = 0; i < 6; i++) { @@ -150,28 +150,28 @@ void StatsWindow::draw(gcn::Graphics *graphics) } void StatsWindow::action(const std::string& eventId) { - WFIFOW(0) = net_w_value(0x00bb); + writeWord(0, 0x00bb); if (eventId == "STR") { - WFIFOW(2) = net_w_value(0x000d); + writeWord(2, 0x000d); } if (eventId == "AGI") { - WFIFOW(2) = net_w_value(0x000e); + writeWord(2, 0x000e); } if (eventId == "VIT") { - WFIFOW(2) = net_w_value(0x000f); + writeWord(2, 0x000f); } if (eventId == "INT") { - WFIFOW(2) = net_w_value(0x0010); + writeWord(2, 0x0010); } if (eventId == "DEX") { - WFIFOW(2) = net_w_value(0x0011); + writeWord(2, 0x0011); } if (eventId == "LUK") { - WFIFOW(2) = net_w_value(0x0012); + writeWord(2, 0x0012); } flush(); - WFIFOW(4) = net_b_value(1); - WFIFOSET(5); + writeByte(4, 1); + writeSet(5); } diff --git a/src/gui/status.cpp b/src/gui/status.cpp index 7ba14fc7..7964768b 100644 --- a/src/gui/status.cpp +++ b/src/gui/status.cpp @@ -157,36 +157,36 @@ void StatusWindow::update() char *tempstr = new char[64]; sprintf(tempstr, "%s Lvl: % 2i Job: % 2i GP: % 2i", - char_info->name, char_info->lv, char_info->job_lv, - char_info->gp); + player_info->name.c_str(), player_info->lv, player_info->job_lv, + player_info->gp); setCaption(tempstr); - sprintf(tempstr, "%d/%d", char_info->hp, char_info->max_hp); + sprintf(tempstr, "%d/%d", player_info->hp, player_info->max_hp); hpValue->setCaption(tempstr); hpValue->adjustSize(); - sprintf(tempstr, "%d/%d", char_info->sp, char_info->max_sp); + sprintf(tempstr, "%d/%d", player_info->sp, player_info->max_sp); spValue->setCaption(tempstr); spValue->adjustSize(); sprintf(tempstr, "Exp: %d/%d", - (int)char_info->xp, (int)char_info->xpForNextLevel); + (int)player_info->xp, (int)player_info->xpForNextLevel); expLabel->setCaption(tempstr); expLabel->adjustSize(); sprintf(tempstr, "Job: %d/%d", - (int)char_info->job_xp, (int)char_info->jobXpForNextLevel); + (int)player_info->job_xp, (int)player_info->jobXpForNextLevel); jobExpLabel->setCaption(tempstr); jobExpLabel->adjustSize(); // HP Bar coloration - if (char_info->hp < int(char_info->max_hp / 3)) + if (player_info->hp < int(player_info->max_hp / 3)) { healthBar->setColor(223, 32, 32); // Red } else { - if (char_info->hp < int((char_info->max_hp / 3) * 2)) + if (player_info->hp < int((player_info->max_hp / 3) * 2)) { healthBar->setColor(230, 171, 34); // Orange } @@ -196,12 +196,14 @@ void StatusWindow::update() } } - healthBar->setProgress((float)char_info->hp / (float)char_info->max_hp); + healthBar->setProgress((float)player_info->hp / + (float)player_info->max_hp); - xpBar->setProgress( - (float)char_info->xp / (float)char_info->xpForNextLevel); - jobXpBar->setProgress( - (float)char_info->job_xp / (float)char_info->jobXpForNextLevel); + xpBar->setProgress((float)player_info->xp / + (float)player_info->xpForNextLevel); + + jobXpBar->setProgress((float)player_info->job_xp / + (float)player_info->jobXpForNextLevel); delete[] tempstr; } diff --git a/src/gui/status.h b/src/gui/status.h index d6abd7af..99fbadf5 100644 --- a/src/gui/status.h +++ b/src/gui/status.h @@ -64,7 +64,7 @@ class StatusWindow : public Window, public gcn::ActionListener { private: /** - * Updates this dialog with values from PLAYER_INFO *char_info + * Updates this dialog with values from <code>player_info</code> */ void update(); @@ -73,7 +73,11 @@ class StatusWindow : public Window, public gcn::ActionListener { gcn::Label *expLabel, *jobExpLabel; ProgressBar *healthBar, *manaBar; ProgressBar *xpBar, *jobXpBar; - gcn::Button *statsButton, *skillsButton, *inventoryButton, *setupButton, *equipmentButton; + gcn::Button *statsButton; + gcn::Button *skillsButton; + gcn::Button *inventoryButton; + gcn::Button *setupButton; + gcn::Button *equipmentButton; }; extern StatusWindow *statusWindow; diff --git a/src/gui/trade.cpp b/src/gui/trade.cpp index f42ca1e0..e18dc969 100644 --- a/src/gui/trade.cpp +++ b/src/gui/trade.cpp @@ -161,8 +161,7 @@ void TradeWindow::addMoney(int amount) moneyLabel->adjustSize(); } -void TradeWindow::addItem(int id, bool own, int quantity, - bool equipment) +void TradeWindow::addItem(int id, bool own, int quantity, bool equipment) { if (own) { myInventory->addItem(id, quantity, equipment); @@ -241,11 +240,11 @@ void TradeWindow::receivedOk(bool own) void TradeWindow::tradeItem(Item *item, int quantity) { - WFIFOW(0) = net_w_value(0x00e8); - WFIFOW(2) = net_w_value(item->getInvIndex()); - WFIFOL(4) = net_l_value(quantity); - WFIFOSET(8); - while ((out_size > 0)) flush(); + writeWord(0, 0x00e8); + writeWord(2, item->getInvIndex()); + writeLong(4, quantity); + writeSet(8); + flush(); } void TradeWindow::mouseClick(int x, int y, int button, int count) @@ -314,9 +313,9 @@ void TradeWindow::action(const std::string &eventId) } else if (eventId == "cancel") { - WFIFOW(0) = net_w_value(0x00ed); - WFIFOSET(2); - while ((out_size > 0)) flush(); + writeWord(0, 0x00ed); + writeSet(2); + flush(); } else if (eventId == "ok") { @@ -328,23 +327,23 @@ void TradeWindow::action(const std::string &eventId) tempMoney[1] << tempInt; moneyField->setText(tempMoney[1].str()); - WFIFOW(0) = net_w_value(0x00e8); - WFIFOW(2) = net_w_value(0); - WFIFOL(4) = net_l_value(tempInt); - WFIFOSET(8); - while ((out_size > 0)) flush(); + writeWord(0, 0x00e8); + writeWord(2, 0); + writeLong(4, tempInt); + writeSet(8); + flush(); } else { moneyField->setText(""); } moneyField->setEnabled(false); - WFIFOW(0) = net_w_value(0x00eb); - WFIFOSET(2); - while ((out_size > 0)) flush(); + writeWord(0, 0x00eb); + writeSet(2); + flush(); } else if (eventId == "trade") { - WFIFOW(0) = net_w_value(0x00ef); - WFIFOSET(2); - while ((out_size > 0)) flush(); + writeWord(0, 0x00ef); + writeSet(2); + flush(); } } diff --git a/src/inventory.cpp b/src/inventory.cpp index 9e3ad375..cf39f042 100644 --- a/src/inventory.cpp +++ b/src/inventory.cpp @@ -90,41 +90,41 @@ bool Inventory::contains(Item *item) int Inventory::useItem(Item *item) { - WFIFOW(0) = net_w_value(0x00a7); - WFIFOW(2) = net_w_value(item->getInvIndex()); - WFIFOL(4) = net_l_value(item->getId()); + writeWord(0, 0x00a7); + writeWord(2, item->getInvIndex()); + writeLong(4, item->getId()); // Note: id is dest of item, usually player_node->account_ID ?? - WFIFOSET(8); - while ((out_size > 0)) flush(); + writeSet(8); + flush(); return 0; } int Inventory::dropItem(Item *item, int quantity) { // TODO: Fix wrong coordinates of drops, serverside? - WFIFOW(0) = net_w_value(0x00a2); - WFIFOW(2) = net_w_value(item->getInvIndex()); - WFIFOW(4) = net_w_value(quantity); - WFIFOSET(6); - while ((out_size > 0)) flush(); + writeWord(0, 0x00a2); + writeWord(2, item->getInvIndex()); + writeWord(4, quantity); + writeSet(6); + flush(); return 0; } void Inventory::equipItem(Item *item) { - WFIFOW(0) = net_w_value(0x00a9); - WFIFOW(2) = net_w_value(item->getInvIndex()); - WFIFOW(4) = net_w_value(0); - WFIFOSET(6); - while ((out_size > 0)) flush(); + writeWord(0, 0x00a9); + writeWord(2, item->getInvIndex()); + writeWord(4, 0); + writeSet(6); + flush(); } void Inventory::unequipItem(Item *item) { - WFIFOW(0) = net_w_value(0x00ab); - WFIFOW(2) = net_w_value(item->getInvIndex()); - WFIFOSET(4); - while ((out_size > 0)) flush(); + writeWord(0, 0x00ab); + writeWord(2, item->getInvIndex()); + writeSet(4); + flush(); // Tidy equipment directly to avoid weapon still shown bug, by instance Equipment::getInstance()->removeEquipment(item); @@ -48,7 +48,7 @@ class Logger /** * Log an error and quit. The error will pop-up in Windows and will be - * printed to standard error everywhere else. + * printed to standard error everywhere else. */ void error(const std::string &error_text); diff --git a/src/main.cpp b/src/main.cpp index 4ea181a5..4da837c8 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -62,13 +62,11 @@ #include "resources/image.h" #include "resources/resourcemanager.h" -struct SERVER_INFO; - // Account infos int account_ID, session_ID1, session_ID2; char sex, n_server, n_character; -SERVER_INFO *server_info; -PLAYER_INFO *char_info = new PLAYER_INFO; +PLAYER_INFO **char_info; +PLAYER_INFO *player_info; Spriteset *hairset = NULL, *playerset = NULL; Graphics *graphics; diff --git a/src/net/messagein.cpp b/src/net/messagein.cpp new file mode 100644 index 00000000..2710d132 --- /dev/null +++ b/src/net/messagein.cpp @@ -0,0 +1,151 @@ +/* + * The Mana World + * Copyright 2004 The Mana World Development Team + * + * This file is part of The Mana World. + * + * The Mana World is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * any later version. + * + * The Mana World is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with The Mana World; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + * $Id$ + */ + +#include "messagein.h" +#include <SDL_net.h> +#ifdef MACOSX +#include "win2mac.h" +#endif + +#define MAKEWORD(low,high) \ + ((unsigned short)(((unsigned char)(low)) | \ + ((unsigned short)((unsigned char)(high))) << 8)) + + +MessageIn::MessageIn(const char *data, unsigned int length): + mData(data), + mLength(length), + mPos(0) +{ + // Read the message ID + mId = readShort(); +} + +char +MessageIn::readByte() +{ + assert(mPos < mLength); + return mData[mPos++]; +} + +short +MessageIn::readShort() +{ + assert(mPos + 2 <= mLength); + mPos += 2; +#ifdef MACOSX + return DR_SwapTwoBytes(*(short*)(mData + (mPos - 2))); +#else + return (*(short*)(mData + (mPos - 2))); +#endif +} + +long +MessageIn::readLong() +{ + assert(mPos + 4 <= mLength); + mPos += 4; +#ifdef MACOSX + return DR_SwapFourBytes(*(long*)(mData + (mPos - 4))); +#else + return (*(long*)(mData + (mPos - 4))); +#endif +} + +void +MessageIn::readCoordinates(unsigned short &x, + unsigned short &y, + unsigned char &direction) +{ + assert(mPos + 3 <= mLength); + + const char *data = mData + mPos; + short temp; + + temp = MAKEWORD(data[1] & 0x00c0, data[0] & 0x00ff); + x = temp >> 6; + temp = MAKEWORD(data[2] & 0x00f0, data[1] & 0x003f); + y = temp >> 4; + + direction = data[2] & 0x000f; + + mPos += 3; +} + +void +MessageIn::readCoordinatePair(unsigned short &srcX, unsigned short &srcY, + unsigned short &dstX, unsigned short &dstY) +{ + assert(mPos + 5 <= mLength); + + const char *data = mData + mPos; + short temp; + + temp = MAKEWORD(data[3], data[2] & 0x000f); + dstX = temp >> 2; + + dstY = MAKEWORD(data[4], data[3] & 0x0003); + + temp = MAKEWORD(data[1], data[0]); + srcX = temp >> 6; + + temp = MAKEWORD(data[2], data[1] & 0x003f); + srcY = temp >> 4; + + mPos += 5; +} + +void +MessageIn::skip(unsigned int length) +{ + assert(mPos + length <= mLength); + mPos += length; +} + +std::string +MessageIn::readString(int length) +{ + int stringLength = 0; + std::string readString = ""; + + // Get string length + if (length < 0) { + stringLength = readShort(); + } else { + stringLength = length; + } + + // Make sure there is enough data available + assert(mPos + length <= mLength); + + // Read the string + char *tmpString = new char[stringLength + 1]; + memcpy(tmpString, (void*)&mData[mPos], stringLength); + tmpString[stringLength] = 0; + mPos += stringLength; + + readString = tmpString; + delete tmpString; + + return readString; +} diff --git a/src/net/messagein.h b/src/net/messagein.h new file mode 100644 index 00000000..9a61ce7d --- /dev/null +++ b/src/net/messagein.h @@ -0,0 +1,94 @@ +/* + * The Mana World + * Copyright 2004 The Mana World Development Team + * + * This file is part of The Mana World. + * + * The Mana World is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * any later version. + * + * The Mana World is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with The Mana World; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + * $Id$ + */ + +#ifndef _TMW_MESSAGEIN_ +#define _TMW_MESSAGEIN_ + +#include <string> + +/** + * Used for parsing an incoming message. + */ +class MessageIn +{ + public: + /** + * Constructor. + */ + MessageIn(const char *data, unsigned int length); + + /** + * Returns the message ID. + */ + short + getId() { return mId; } + + /** + * Returns the message length. + */ + unsigned int + getLength() { return mLength; } + + char readByte(); /**< Reads a byte. */ + short readShort(); /**< Reads a short. */ + long readLong(); /**< Reads a long. */ + + /** + * Reads a special 3 byte block used by eAthena, containing x and y + * coordinates and direction. + */ + void + readCoordinates(unsigned short &x, + unsigned short &y, + unsigned char &direction); + + /** + * Reads a special 5 byte block used by eAthena, containing a source + * and destination coordinate pair. + */ + void + readCoordinatePair(unsigned short &srcX, unsigned short &srcY, + unsigned short &dstX, unsigned short &dstY); + + /** + * Skips a given number of bytes. + */ + void + skip(unsigned int length); + + /** + * Reads a string. If a length is not given (-1), it is assumed + * that the length of the string is stored in a short at the + * start of the string. + */ + std::string + readString(int length = -1); + + private: + const char* mData; /**< The message data. */ + unsigned int mLength; /**< The length of the data. */ + unsigned int mPos; /**< The position in the data. */ + short mId; /**< The message ID. */ +}; + +#endif diff --git a/src/net/messageout.cpp b/src/net/messageout.cpp new file mode 100644 index 00000000..871ba43a --- /dev/null +++ b/src/net/messageout.cpp @@ -0,0 +1,130 @@ +/* + * The Mana World + * Copyright 2004 The Mana World Development Team + * + * This file is part of The Mana World. + * + * The Mana World is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * any later version. + * + * The Mana World is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with The Mana World; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + * $Id$ + */ + +#include "messageout.h" + +#include <iostream> +#include <SDL_net.h> + +#include "network.h" + +MessageOut::MessageOut(): + mPacket(0), + mData(0), + mDataSize(0), + mPos(0) +{ + // TODO: data not to be already allocated, keep it this way unitl full + // conversion + mData = out; +} + +MessageOut::~MessageOut() +{ + if (mPacket) { + delete mPacket; + } + + // Don't free this data for now, see above. + //if (mData) { + // free(mData); + //} +} + +void MessageOut::expand(size_t bytes) +{ + mData = (char*)realloc(mData, bytes); + mDataSize = bytes; +} + +void MessageOut::writeByte(char value) +{ + expand(mPos + sizeof(char)); + mData[mPos] = value; + mPos += sizeof(char); +} + +void MessageOut::writeShort(short value) +{ + expand(mPos + sizeof(short)); +#ifdef MACOSX + (*(short *)(mData + mPos)) = DR_SwapTwoBytes(value); +#else + (*(short *)(mData + mPos)) = value; +#endif + //SDLNet_Write16(value, &mData[mPos]); + mPos += sizeof(short); +} + +void MessageOut::writeLong(long value) +{ + expand(mPos + sizeof(long)); +#ifdef MACOSX + (*(long *)(mData + mPos)) = DR_SwapFourBytes(value); +#else + (*(long *)(mData + mPos)) = value; +#endif + //SDLNet_Write32(value, &mData[mPos]); + mPos += sizeof(long); +} + +void MessageOut::writeString(const std::string &string, int length) +{ + std::string toWrite = string; + + if (length < 0) + { + // Write the length at the start if not fixed + writeShort(string.length()); + toWrite = string; + + expand(mPos + string.length()); + } + else + { + // Make sure the length of the string is no longer than specified + toWrite = string.substr(0, length); + expand(mPos + length); + } + + // Write the actual string + memcpy(&mData[mPos], (void*)toWrite.c_str(), toWrite.length()); + mPos += toWrite.length(); + + // Pad remaining space with zeros + if (length > (int)toWrite.length()) + { + memset(&mData[mPos], '\0', length - toWrite.length()); + mPos += length - toWrite.length(); + } +} + +const Packet *MessageOut::getPacket() +{ + if (!mPacket) + { + mPacket = new Packet(mData, mDataSize); + } + + return mPacket; +} diff --git a/src/net/messageout.h b/src/net/messageout.h new file mode 100644 index 00000000..d836b43f --- /dev/null +++ b/src/net/messageout.h @@ -0,0 +1,80 @@ +/* + * The Mana World + * Copyright 2004 The Mana World Development Team + * + * This file is part of The Mana World. + * + * The Mana World is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * any later version. + * + * The Mana World is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with The Mana World; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + * $Id$ + */ + +#ifndef _TMW_MESSAGEOUT_ +#define _TMW_MESSAGEOUT_ + +#include <string> + +#include "packet.h" + +/** + * Used for building an outgoing message. + */ +class MessageOut +{ + public: + /** + * Constructor. + */ + MessageOut(); + + /** + * Destructor. + */ + ~MessageOut(); + + void writeByte(char value); /**< Writes a byte. */ + void writeShort(short value); /**< Writes a short. */ + void writeLong(long value); /**< Writes a long. */ + + /** + * Writes a string. If a fixed length is not given (-1), it is stored + * as a short at the start of the string. + */ + void writeString(const std::string &string, int length = -1); + + /** + * Returns an instance of Packet derived from the written data. Use for + * sending the packet. No more writing to the packet may be done after + * a call to this method. + */ + const Packet *getPacket(); + + private: + /** + * Expand the packet data to be able to hold more data. + * + * NOTE: For performance enhancements this method could allocate extra + * memory in advance instead of expanding size every time more data is + * added. + */ + void expand(unsigned int size); + + Packet *mPacket; /**< Created packet. */ + char *mData; /**< Data building up. */ + unsigned int mDataSize; /**< Size of data. */ + unsigned int mPos; /**< Position in the data. */ +}; + +#endif diff --git a/src/net/network.cpp b/src/net/network.cpp index b0d27be9..6624ee1e 100644 --- a/src/net/network.cpp +++ b/src/net/network.cpp @@ -23,162 +23,309 @@ #include "../log.h" #include "network.h" +#include "protocol.h" +#ifdef MACOSX +#include "win2mac.h" +#endif #ifndef WIN32 #include <unistd.h> #include <fcntl.h> #include <errno.h> #endif +#include <sstream> /** Warning: buffers and other variables are shared, so there can be only one connection active at a time */ -int buffer_size = 65536; -char *in, *out; -int in_size, out_size; +short packet_lengths[] = { + 10, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, +// #0x0040 + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 55, 17, 3, 37, 46, -1, 23, -1, 3,108, 3, 2, + 3, 28, 19, 11, 3, -1, 9, 5, 54, 53, 58, 60, 41, 2, 6, 6, +// #0x0080 + 7, 3, 2, 2, 2, 5, 16, 12, 10, 7, 29, 23, -1, -1, -1, 0, + 7, 22, 28, 2, 6, 30, -1, -1, 3, -1, -1, 5, 9, 17, 17, 6, + 23, 6, 6, -1, -1, -1, -1, 8, 7, 6, 7, 4, 7, 0, -1, 6, + 8, 8, 3, 3, -1, 6, 6, -1, 7, 6, 2, 5, 6, 44, 5, 3, +// #0x00C0 + 7, 2, 6, 8, 6, 7, -1, -1, -1, -1, 3, 3, 6, 6, 2, 27, + 3, 4, 4, 2, -1, -1, 3, -1, 6, 14, 3, -1, 28, 29, -1, -1, + 30, 30, 26, 2, 6, 26, 3, 3, 8, 19, 5, 2, 3, 2, 2, 2, + 3, 2, 6, 8, 21, 8, 8, 2, 2, 26, 3, -1, 6, 27, 30, 10, +// #0x0100 + 2, 6, 6, 30, 79, 31, 10, 10, -1, -1, 4, 6, 6, 2, 11, -1, + 10, 39, 4, 10, 31, 35, 10, 18, 2, 13, 15, 20, 68, 2, 3, 16, + 6, 14, -1, -1, 21, 8, 8, 8, 8, 8, 2, 2, 3, 4, 2, -1, + 6, 86, 6, -1, -1, 7, -1, 6, 3, 16, 4, 4, 4, 6, 24, 26, +// #0x0140 + 22, 14, 6, 10, 23, 19, 6, 39, 8, 9, 6, 27, -1, 2, 6, 6, + 110, 6, -1, -1, -1, -1, -1, 6, -1, 54, 66, 54, 90, 42, 6, 42, + -1, -1, -1, -1, -1, 30, -1, 3, 14, 3, 30, 10, 43, 14,186,182, + 14, 30, 10, 3, -1, 6,106, -1, 4, 5, 4, -1, 6, 7, -1, -1, +// #0x0180 + 6, 3,106, 10, 10, 34, 0, 6, 8, 4, 4, 4, 29, -1, 10, 6, + 90, 86, 24, 6, 30,102, 9, 4, 8, 4, 14, 10, 4, 6, 2, 6, + 3, 3, 35, 5, 11, 26, -1, 4, 4, 6, 10, 12, 6, -1, 4, 4, + 11, 7, -1, 67, 12, 18,114, 6, 3, 6, 26, 26, 26, 26, 2, 3, +// #0x01C0 + 2, 14, 10, -1, 22, 22, 4, 2, 13, 97, 0, 9, 9, 29, 6, 28, + 8, 14, 10, 35, 6, 8, 4, 11, 54, 53, 60, 2, -1, 47, 33, 6, + 30, 8, 34, 14, 2, 6, 26, 2, 28, 81, 6, 10, 26, 2, -1, -1, + -1, -1, 20, 10, 32, 9, 34, 14, 2, 6, 48, 56, -1, 4, 5, 10, +// #0x200 + 26, 0, 0, 0, 18, 0, 0, 0, 0, 0, 0, 19, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, +}; -SOCKET sock; -SOCKADDR_IN addr; -// File descriptors attached to socket -fd_set read_socket; -fd_set write_socket; +unsigned int buffer_size = 65536; +char *in = NULL; +char *out = NULL; +unsigned int in_size = 0; +unsigned int out_size = 0; +bool connectionOpen = false; -void WFIFOSET(int len) -{ - if (out_size + len >= buffer_size) { - logger->log("Warning: Output buffer full"); - } - else { - out_size += len; - } -} +TCPsocket sock; +SDLNet_SocketSet set; char *iptostring(int address) { - short temp1, temp2; static char asciiIP[16]; - temp1 = LOWORD(address); - temp2 = HIWORD(address); - sprintf(asciiIP, "%i.%i.%i.%i", LOBYTE(temp1), HIBYTE(temp1), LOBYTE(temp2), HIBYTE(temp2)); + sprintf(asciiIP, "%i.%i.%i.%i", + (unsigned char)(address), + (unsigned char)(address >> 8), + (unsigned char)(address >> 16), + (unsigned char)(address >> 24)); + return asciiIP; } -SOCKET open_session(const char* address, short port) +int open_session(const char* address, short port) { - #ifdef WIN32 - WSADATA wsda; - #endif - struct hostent *server; - int ret; - - // Init WinSock and connect the socket - #ifdef WIN32 - WSAStartup(MAKEWORD(2,0), &wsda); - #endif - - sock = socket(PF_INET, SOCK_STREAM, 0); // Create socket for current session - if(sock==SOCKET_ERROR)return SOCKET_ERROR; - - addr.sin_family = AF_INET; - addr.sin_port = htons(port); - addr.sin_addr.s_addr = inet_addr(address); - if(addr.sin_addr.s_addr == INADDR_NONE){ - server = NULL; - server = gethostbyname(address); - if(server == NULL)return SOCKET_ERROR; - memcpy(&addr.sin_addr, server->h_addr_list[0], server->h_length); + assert(!connectionOpen); + + // Initialize SDL_net + if (SDLNet_Init() == -1) + { + logger->log("Error in SDLNet_Init(): %s", SDLNet_GetError()); + return -1; } - ret = connect(sock, (struct sockaddr *) &addr, sizeof(addr)); - if(ret == SOCKET_ERROR)return SOCKET_ERROR; + IPaddress ip; + + // Resolve host name + if (SDLNet_ResolveHost(&ip, address, port) == -1) + { + logger->log("Error in SDLNet_ResolveHost(): %s", SDLNet_GetError()); + return -1; + } + + // Create the socket for the current session + sock = SDLNet_TCP_Open(&ip); + if (!sock) + { + logger->log("Error in SDLNet_TCP_Open(): %s", SDLNet_GetError()); + return -1; + } + + // Create a socket set to listen to socket + set = SDLNet_AllocSocketSet(1); + if (!set) + { + logger->log("Error in SDLNet_AllocSocketSet(): %s", SDLNet_GetError()); + return -1; + } + + // Add the socket to the set + int ret = SDLNet_TCP_AddSocket(set, sock); + if (ret == -1) + { + logger->log("Error in SDLNet_AddSocket(): %s", SDLNet_GetError()); + return -1; + } // Init buffers - in = (char *)malloc(buffer_size); - out = (char *)malloc(buffer_size); + in = (char*)malloc(buffer_size); + out = (char*)malloc(buffer_size); memset(in, '\0', buffer_size); memset(out, '\0', buffer_size); in_size = 0; out_size = 0; - FD_CLR(sock, &read_socket); - FD_CLR(sock, &write_socket); - return sock; + logger->log("Network::Started session with %s:%i", address, port); + connectionOpen = true; + + return 0; } void close_session() { - FD_CLR(sock,&read_socket); - FD_CLR(sock,&write_socket); - closesocket(sock); - if(in!=NULL) { + assert(connectionOpen); + + // Remove the socket from the socket set + int ret = SDLNet_TCP_DelSocket(set, sock); + if (ret == -1) + { + logger->log("Error in SDLNet_DelSocket(): %s", SDLNet_GetError()); + } + + // Close the TCP connection + SDLNet_TCP_Close(sock); + + // Free the socket set + SDLNet_FreeSocketSet(set); + set = NULL; + + // Clear buffers + if (in != NULL) + { free(in); + in = NULL; } - if(out!=NULL) { + + if (out != NULL) + { free(out); + out = NULL; } - in = NULL; - out = NULL; + in_size = 0; out_size = 0; - WSACleanup(); + + // Shutdown the network API + SDLNet_Quit(); + + logger->log("Network::Closed session"); + connectionOpen = false; } void flush() { - int ret = 0; - void *buf = out; - timeval time_out; - - // Init the time_out struct to 0s so it won't block - time_out.tv_sec=0; - time_out.tv_usec=0; - - // Clear file descriptors and set them to socket - FD_ZERO(&read_socket); - FD_ZERO(&write_socket); - FD_SET(sock, &read_socket); - FD_SET(sock, &write_socket); - - // Check if socket has available data by evaluating attached file descriptors - select(FD_SETSIZE, &read_socket, &write_socket, NULL, &time_out); - - // Send data if available - if(FD_ISSET(sock, &write_socket)) { - // While there wasn't a error or sent the whole data: handles partial packet send - while((ret!=SOCKET_ERROR)&&(out_size>0)) { - ret = send(sock, (char *)buf, out_size, 0); - - if(ret!=SOCKET_ERROR && ret>0) { - buf = (char*)buf+ret; - out_size -= ret; - } + int numReady = SDLNet_CheckSockets(set, 0); + if (numReady == -1) + { + logger->log("Error: SDLNet_CheckSockets"); + return; + } + else if (numReady) + { + // Receive data from the socket + int ret = SDLNet_TCP_Recv(sock, in + in_size, buffer_size - in_size); + if (ret <= 0) + { + logger->log("Warning: unknown error when receiving data"); + logger->log("SDLNet_GetError(): %s", SDLNet_GetError()); + // The client disconnected, notify it somewhere + logger->error("Disconnected from server"); + return; + } + else { + in_size += ret; + } + } + + // Send all available data, waits if not all data can be sent immediately + if (out_size > 0) + { + int ret = SDLNet_TCP_Send(sock, (char*)out, out_size); + if (ret < (int)out_size) + { + // It is likely that the server disconnected + std::stringstream ss; + ss << "Error in SDLNet_TCP_Send(): " << SDLNet_GetError(); + logger->error(ss.str()); + return; } - if (ret == SOCKET_ERROR) { - logger->error("Socket Error"); -#ifdef WIN32 - logger->log("Error: Socket error: %i ", WSAGetLastError()); - if (WSAGetLastError() == 10053) - logger->log("Error: Packet size error"); - /** Probably the last packet you sent, was defined with - * wrong size: WFIFOSET(size); - */ + out_size -= ret; + } +} + +unsigned short readWord(int pos) +{ +#ifdef MACOSX + return DR_SwapTwoBytes((*(unsigned short*)(in+(pos)))); #else - logger->log("Error: Undefined socket error"); + return (*(unsigned short *)(in+(pos))); #endif - } +} + +MessageIn +get_next_message() +{ + // At least 2 bytes should be received for the message ID + while (in_size < 2) flush(); + + int length = packet_lengths[readWord(0)]; + + if (length == -1) + { + // Another 2 bytes should be received for the length + while (in_size < 4) flush(); + length = readWord(2); } - // Read data, if available - if (FD_ISSET(sock, &read_socket)) { - /* There's no check for partial received packets because at this level - the app doesn't know packet length, but it will done when parsing received data */ - ret = recv(sock, in+in_size, RFIFOSPACE, 0); - if (ret == SOCKET_ERROR) { -#ifdef WIN32 - logger->log("Error: Socket error: %i ", WSAGetLastError()); +#ifdef DEBUG + printf("Received packet 0x%x of length %d\n", readWord(0), length); +#endif + + // Make sure the whole packet is received + while (in_size < (unsigned int)length) flush(); + + return MessageIn(in, length); +} + +void writeByte(int pos, unsigned char value)//writeByte(unsigned char value) +{ + (*(unsigned char *)(out + pos + out_size)) = value; + //out_size++; +} + +void writeWord(int pos, unsigned short value)//writeWord(unsigned short value) +{ +#ifdef MACOSX + (*(unsigned short *)(out + pos + out_size)) = DR_SwapTwoBytes(value); +#else + (*(unsigned short *)(out + pos + out_size)) = value; +#endif + //SDLNet_Write16(value, (out + (pos + out_size))); + //out_size += 2; +} + +void writeLong(int pos, unsigned int value)//writeLong(int value) +{ +#ifdef MACOSX + (*(unsigned int *)(out + pos + out_size)) = DR_SwapFourBytes(value); #else - logger->log("Error: Undefined socket error"); + (*(unsigned int *)(out + pos + out_size)) = value; #endif - } else RFIFOSET(ret); // Set size of available data to read + //SDLNet_Write32((Uint32)value, (out + (pos + out_size))); + //out_size += 4; +} + +char *writePointer(int pos)//writeString(const std::string &string, int length) +{ + return (out+(pos+out_size)); + //memcpy((out + out_size), string.c_str(), length); + //out_size += length; +} + +void writeSet(unsigned int value) +{ + if (out_size + value >= buffer_size) { + logger->log("Warning: Output buffer full"); + } + else { + out_size += value; } } + +void skip(int len) +{ + memcpy(in, in + len, in_size - len); + in_size -= len; +} diff --git a/src/net/network.h b/src/net/network.h index cc5e542f..a037c6fa 100644 --- a/src/net/network.h +++ b/src/net/network.h @@ -21,67 +21,20 @@ * $Id$ */ -#ifndef _TMW_NETWORK_H -#define _TMW_NETWORK_H - -#ifndef WIN32 -#include "win2linux.h" -#else -#include <winsock.h> -#endif - -#include <stdio.h> - -#ifdef MACOSX -#include "win2mac.h" -#endif - -/** Macros to write in output buffer, pos is the location where to write data - After you wrote len bytes, you have to use WFIFOSET */ -#define WFIFOSPACE (buffer_size-out_size) // Number of bytes currently written in uotput buffer -#define WFIFOP(pos) (out+(pos+out_size)) // Return a pointer to a specific location in output buffer -#define WFIFOB(pos) (*(unsigned char *)(out+pos+out_size)) // Write a byte (1 byte) -#define WFIFOW(pos) (*(unsigned short *)(out+pos+out_size)) // Write a word (2 byte) -#define WFIFOL(pos) (*(unsigned int *)(out+pos+out_size)) // Write a long (4 byte) -//#define WFIFOSET(len) out_size+=len // Increase size of written data - -#ifdef MACOSX - #define net_b_value(id) (id) - #define net_w_value(id) DR_SwapTwoBytes(id) - #define net_l_value(id) DR_SwapFourBytes(id) -#else - #define net_b_value(id) (id) - #define net_w_value(id) (id) - #define net_l_value(id) (id) -#endif +#ifndef _TMW_NETWORK_ +#define _TMW_NETWORK_ +#include <string> -/** Macros to read from input buffer, pos is the location of data to be read - After you read len bytes, you should use RFIFOSKIP */ -#define RFIFOP(pos) (in+(pos)) // Get a pointer from a specific location in input buffer - -#ifdef MACOSX - #define RFIFOB(pos) ((*(unsigned char*)(in+(pos)))) // Read a byte - #define RFIFOW(pos) DR_SwapTwoBytes((*(unsigned short*)(in+(pos)))) // Read a word - #define RFIFOL(pos) DR_SwapFourBytes((*(unsigned int*)(in+(pos)))) // Read a long -#else - #define RFIFOB(pos) (*(unsigned char*)(in+(pos))) // Read a byte - #define RFIFOW(pos) (*(unsigned short*)(in+(pos))) // Read a word - #define RFIFOL(pos) (*(unsigned int*)(in+(pos))) // Read a long -#endif - -#define RFIFOSKIP(len) (memcpy(in,in+len,in_size-len));in_size-=len; // Empty len bytes from input buffer -#define RFIFOSPACE (buffer_size-in_size) // Return input buffer size -#define RFIFOSET(len) in_size+=len; - -/** Increase size of written data */ -void WFIFOSET(int len); +#include "SDL_net.h" +#include "messagein.h" +#include "messageout.h" /** Convert an address from int format to string */ char *iptostring(int address); /** Open a session with a server */ -SOCKET open_session(const char* address, short port); +int open_session(const char* address, short port); /** Close a session */ void close_session(); @@ -89,7 +42,21 @@ void close_session(); /** Send and receive data waiting in the buffers */ void flush(); -extern char *in, *out; // Input, output buffer -extern int in_size, out_size; // Input, output buffer size +/** + * Returns the next arriving message, waiting for it if necessary. + */ +MessageIn get_next_message(); +extern char *out; + + +void writeByte(int pos, unsigned char value);//writeByte(char value); +void writeWord(int pos, unsigned short value);//writeWord(short value); +void writeLong(int pos, unsigned int value);//writeLong(int value); +char *writePointer(int pos); //writeString(const std::string &string, int length); +void writeSet(unsigned int value); +void skip(int len); + +extern unsigned int in_size; /**< Amount of data in input buffer. */ +extern unsigned int out_size; /**< Amount of data in output buffer. */ #endif diff --git a/src/net/packet.cpp b/src/net/packet.cpp new file mode 100644 index 00000000..b8ee1e7f --- /dev/null +++ b/src/net/packet.cpp @@ -0,0 +1,40 @@ +/* + * The Mana World + * Copyright 2004 The Mana World Development Team + * + * This file is part of The Mana World. + * + * The Mana World is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * any later version. + * + * The Mana World is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with The Mana World; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + * $Id$ + */ + +#include "packet.h" +#include <string.h> +#include <cstdlib> + +Packet::Packet(const char *data, int length): + length(length) +{ + // Create a copy of the data + this->data = new char[length]; + memcpy(this->data, data, length); +} + +Packet::~Packet() +{ + // Clean up the data + delete[] data; +} diff --git a/src/net/packet.h b/src/net/packet.h new file mode 100644 index 00000000..6b9fd18d --- /dev/null +++ b/src/net/packet.h @@ -0,0 +1,47 @@ +/* + * The Mana World + * Copyright 2004 The Mana World Development Team + * + * This file is part of The Mana World. + * + * The Mana World is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * any later version. + * + * The Mana World is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with The Mana World; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + * $Id$ + */ + +#ifndef _TMW_PACKET_ +#define _TMW_PACKET_ + +/** + * A packet wraps a certain amount of bytes for sending and receiving. + */ +class Packet +{ + public: + /** + * Constructor. + */ + Packet(const char *data, int length); + + /** + * Destructor. + */ + ~Packet(); + + char *data; /**< Packet data */ + unsigned int length; /**< Length of data in bytes */ +}; + +#endif diff --git a/src/net/protocol.cpp b/src/net/protocol.cpp index ce27b7ea..17076b90 100644 --- a/src/net/protocol.cpp +++ b/src/net/protocol.cpp @@ -23,10 +23,6 @@ #include "protocol.h" -#ifndef WIN32 -#include "win2linux.h" -#endif - #include "network.h" #include "../being.h" @@ -36,111 +32,24 @@ #include "../playerinfo.h" #include "../sound.h" +#define LOBYTE(w) ((unsigned char)(w)) +#define HIBYTE(w) ((unsigned char)(((unsigned short)(w)) >> 8)) -short packet_lengths[] = { - 10, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, -// #0x0040 - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 55, 17, 3, 37, 46, -1, 23, -1, 3,108, 3, 2, - 3, 28, 19, 11, 3, -1, 9, 5, 54, 53, 58, 60, 41, 2, 6, 6, -// #0x0080 - 7, 3, 2, 2, 2, 5, 16, 12, 10, 7, 29, 23, -1, -1, -1, 0, - 7, 22, 28, 2, 6, 30, -1, -1, 3, -1, -1, 5, 9, 17, 17, 6, - 23, 6, 6, -1, -1, -1, -1, 8, 7, 6, 7, 4, 7, 0, -1, 6, - 8, 8, 3, 3, -1, 6, 6, -1, 7, 6, 2, 5, 6, 44, 5, 3, -// #0x00C0 - 7, 2, 6, 8, 6, 7, -1, -1, -1, -1, 3, 3, 6, 6, 2, 27, - 3, 4, 4, 2, -1, -1, 3, -1, 6, 14, 3, -1, 28, 29, -1, -1, - 30, 30, 26, 2, 6, 26, 3, 3, 8, 19, 5, 2, 3, 2, 2, 2, - 3, 2, 6, 8, 21, 8, 8, 2, 2, 26, 3, -1, 6, 27, 30, 10, -// #0x0100 - 2, 6, 6, 30, 79, 31, 10, 10, -1, -1, 4, 6, 6, 2, 11, -1, - 10, 39, 4, 10, 31, 35, 10, 18, 2, 13, 15, 20, 68, 2, 3, 16, - 6, 14, -1, -1, 21, 8, 8, 8, 8, 8, 2, 2, 3, 4, 2, -1, - 6, 86, 6, -1, -1, 7, -1, 6, 3, 16, 4, 4, 4, 6, 24, 26, -// #0x0140 - 22, 14, 6, 10, 23, 19, 6, 39, 8, 9, 6, 27, -1, 2, 6, 6, - 110, 6, -1, -1, -1, -1, -1, 6, -1, 54, 66, 54, 90, 42, 6, 42, - -1, -1, -1, -1, -1, 30, -1, 3, 14, 3, 30, 10, 43, 14,186,182, - 14, 30, 10, 3, -1, 6,106, -1, 4, 5, 4, -1, 6, 7, -1, -1, -// #0x0180 - 6, 3,106, 10, 10, 34, 0, 6, 8, 4, 4, 4, 29, -1, 10, 6, - 90, 86, 24, 6, 30,102, 9, 4, 8, 4, 14, 10, 4, 6, 2, 6, - 3, 3, 35, 5, 11, 26, -1, 4, 4, 6, 10, 12, 6, -1, 4, 4, - 11, 7, -1, 67, 12, 18,114, 6, 3, 6, 26, 26, 26, 26, 2, 3, -// #0x01C0 - 2, 14, 10, -1, 22, 22, 4, 2, 13, 97, 0, 9, 9, 29, 6, 28, - 8, 14, 10, 35, 6, 8, 4, 11, 54, 53, 60, 2, -1, 47, 33, 6, - 30, 8, 34, 14, 2, 6, 26, 2, 28, 81, 6, 10, 26, 2, -1, -1, - -1, -1, 20, 10, 32, 9, 34, 14, 2, 6, 48, 56, -1, 4, 5, 10, -// #0x200 - 26, 0, 0, 0, 18, 0, 0, 0, 0, 0, 0, 19, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, -}; - -short get_length(short id) { - return packet_lengths[id]; -} - -unsigned short get_dest_x(const char *data) { - short temp; - temp = MAKEWORD(data[3], data[2] & 0x000f); - temp >>= 2; - return temp; -} - -unsigned short get_dest_y(const char *data) { - return MAKEWORD(data[4], data[3] & 0x0003); -} - -unsigned short get_src_x(const char *data) { - short temp; - temp = MAKEWORD(data[1], data[0]); - temp >>= 6; - return temp; -} - -unsigned short get_src_y(const char *data) { - short temp; - temp = MAKEWORD(data[2], data[1] & 0x003f); - temp >>= 4; - return temp; -} - -unsigned char get_src_direction(char data) { +unsigned char get_src_direction(char data) +{ data >>= 4; return data; } -unsigned char get_dest_direction(char data) { +unsigned char get_dest_direction(char data) +{ return data & 0x000f; } -unsigned short get_x(const char *data) { - short temp; - temp = MAKEWORD(data[1] & 0x00c0, data[0] & 0x00ff); - temp >>= 6; - return temp; -} - -unsigned short get_y(const char *data) { - short temp; - if (!data) throw "Corrupted data"; - temp = MAKEWORD(data[2] & 0x00f0, data[1] & 0x003f); - temp >>= 4; - return temp; -} - -unsigned char get_direction(const char *data) { - return data[2] & 0x000f; -} - -void set_coordinates(char *data, unsigned short x, unsigned short y, - unsigned char direction) +void set_coordinates(char *data, + unsigned short x, + unsigned short y, + unsigned char direction) { short temp; temp = x; @@ -149,7 +58,7 @@ void set_coordinates(char *data, unsigned short x, unsigned short y, data[1] = 1; data[2] = 2; data[0] = HIBYTE(temp); - data[1] = LOBYTE(temp); + data[1] = (unsigned char)(temp); temp = y; temp <<= 4; data[1] |= HIBYTE(temp); @@ -160,7 +69,7 @@ void set_coordinates(char *data, unsigned short x, unsigned short y, void map_start() { // Connect to map server - if (open_session(iptostring(map_address), map_port) == SOCKET_ERROR) + if (open_session(iptostring(map_address), map_port) == -1) { logger->log("Warning: Unable to connect to map server"); throw "Unable to connect to map server"; @@ -168,63 +77,70 @@ void map_start() } // Send login infos - WFIFOW(0) = net_w_value(0x0072); - WFIFOL(2) = net_l_value(account_ID); - WFIFOL(6) = net_l_value(char_ID); - WFIFOL(10) = net_l_value(session_ID1); - WFIFOL(14) = net_l_value(session_ID2); - WFIFOB(18) = net_b_value(sex); - WFIFOSET(19); - + writeWord(0, 0x0072); + writeLong(2, account_ID); + writeLong(6, char_ID); + writeLong(10, session_ID1); + writeLong(14, session_ID2); + writeByte(18, sex); + writeSet(19); + + // Skip a mysterious 4 bytes while ((in_size < 4)|| (out_size > 0)) flush(); - RFIFOSKIP(4); + skip(4); - while (in_size < 2) flush(); + MessageIn msg = get_next_message(); - if (RFIFOW(0) == SMSG_LOGIN_SUCCESS) { - while (in_size < 11) flush(); - startX = get_x(RFIFOP(6)); - startY = get_y(RFIFOP(6)); - int direction = get_direction(RFIFOP(6)); + if (msg.getId() == SMSG_LOGIN_SUCCESS) + { + unsigned char direction; + msg.readLong(); // server tick + msg.readCoordinates(startX, startY, direction); + msg.skip(2); // unknown logger->log("Protocol: Player start position: (%d, %d), Direction: %d", startX, startY, direction); - RFIFOSKIP(11); - } else if (0x0081) { + } + else if (msg.getId() == 0x0081) + { logger->log("Warning: Map server D/C"); - } else { + } + else + { logger->error("Unknown packet: map_start"); } + skip(msg.getLength()); + // Send "map loaded" - WFIFOW(0) = net_w_value(0x007d); - WFIFOSET(2); - while (out_size > 0) flush(); + writeWord(0, 0x007d); + writeSet(2); + flush(); } void walk(unsigned short x, unsigned short y, unsigned char direction) { char temp[3]; set_coordinates(temp, x, y, direction); - WFIFOW(0) = net_w_value(0x0085); - memcpy(WFIFOP(2), temp, 3); - WFIFOSET(5); + writeWord(0, 0x0085); + memcpy(writePointer(2), temp, 3); + writeSet(5); } void speak(char *speech) { int len = (int)strlen(speech); - WFIFOW(0) = net_w_value(0x008c); - WFIFOW(2) = net_w_value(len + 4); - memcpy(WFIFOP(4), speech, len); - WFIFOSET(len + 4); + writeWord(0, 0x008c); + writeWord(2, len + 4); + memcpy(writePointer(4), speech, len); + writeSet(len + 4); } void action(char type, int id) { - WFIFOW(0) = net_w_value(0x0089); - WFIFOL(2) = net_l_value(id); - WFIFOB(6) = net_b_value(type); - WFIFOSET(7); + writeWord(0, 0x0089); + writeLong(2, id); + writeByte(6, type); + writeSet(7); } Being* attack(unsigned short x, unsigned short y, unsigned char direction) @@ -278,7 +194,7 @@ void attack(Being *target) } // Implement charging attacks here - char_info->lastAttackTime = 0; + player_info->lastAttackTime = 0; player_node->action = Being::ATTACK; action(0, target->getId()); diff --git a/src/net/protocol.h b/src/net/protocol.h index 3fa039ba..69b36783 100644 --- a/src/net/protocol.h +++ b/src/net/protocol.h @@ -21,52 +21,74 @@ * $Id$ */ -#ifndef _TMW_PROTOCOL_H -#define _TMW_PROTOCOL_H +#ifndef _TMW_PROTOCOL_ +#define _TMW_PROTOCOL_ class Being; // Packets from server to client -#define SMSG_LOGIN_SUCCESS 0x0073 /**< Logged in, starting location */ -#define SMSG_REMOVE_BEING 0x0080 /**< Died, logged out, teleport ... */ -#define SMSG_MOVE_BEING 0x007b /**< A nearby monster moves */ -#define SMSG_PLAYER_UPDATE_1 0x01d8 -#define SMSG_PLAYER_UPDATE_2 0x01d9 -#define SMSG_MOVE_PLAYER_BEING 0x01da /**< A nearby player moves */ -#define SMSG_CHANGE_BEING_LOOKS 0x00c3 -#define SMSG_BEING_CHAT 0x008d /**< A being talks */ -#define SMSG_MY_BEING_CHAT 0x008e /**< My being talks */ -#define SMSG_GM_CHAT 0x009a /**< GM announce */ -#define SMSG_WALK_RESPONSE 0x0087 +#define SMSG_LOGIN_SUCCESS 0x0073 /**< Contains starting location */ +#define SMSG_PLAYER_UPDATE_1 0x01d8 +#define SMSG_PLAYER_UPDATE_2 0x01d9 +#define SMSG_PLAYER_MOVE 0x01da /**< A nearby player moves */ +#define SMSG_PLAYER_STAT_UPDATE_1 0x00b0 +#define SMSG_PLAYER_STAT_UPDATE_2 0x00b1 +#define SMSG_PLAYER_STAT_UPDATE_3 0x0141 +#define SMSG_PLAYER_STAT_UPDATE_4 0x00bc +#define SMSG_PLAYER_STAT_UPDATE_5 0x00bd +#define SMSG_PLAYER_STAT_UPDATE_6 0x00be +#define SMSG_PLAYER_WARP 0x0091 /**< Warp player to map/location */ +#define SMSG_PLAYER_INVENTORY 0x01ee +#define SMSG_PLAYER_INVENTORY_ADD 0x00a0 +#define SMSG_PLAYER_INVENTORY_REMOVE 0x00af +#define SMSG_PLAYER_INVENTORY_USE 0x01c8 +#define SMSG_PLAYER_EQUIPMENT 0x00a4 +#define SMSG_PLAYER_EQUIP 0x00aa +#define SMSG_PLAYER_UNEQUIP 0x00ac +#define SMSG_PLAYER_ARROW_EQUIP 0x013c +#define SMSG_PLAYER_ARROW_MESSAGE 0x013b +#define SMSG_PLAYER_SKILLS 0x010f +#define SMSG_SKILL_FAILED 0x0110 +#define SMSG_ITEM_USE_RESPONSE 0x00a8 +#define SMSG_ITEM_VISIBLE 0x009d /**< An item is on the floor */ +#define SMSG_ITEM_DROPPED 0x009e /**< An item is dropped */ +#define SMSG_ITEM_REMOVE 0x00a1 /**< An item disappers */ +#define SMSG_BEING_VISIBLE 0x0078 +#define SMSG_BEING_MOVE 0x007b /**< A nearby monster moves */ +#define SMSG_BEING_REMOVE 0x0080 +#define SMSG_BEING_CHANGE_LOOKS 0x00c3 +#define SMSG_BEING_LEVELUP 0x019b +#define SMSG_BEING_EMOTION 0x00c0 +#define SMSG_BEING_ACTION 0x008a /**< Attack, sit, stand up, ... */ +#define SMSG_BEING_CHAT 0x008d /**< A being talks */ +#define SMSG_BEING_NAME_RESPONSE 0x0095 /**< Has to be requested */ +#define SMSG_NPC_MESSAGE 0x00b4 +#define SMSG_NPC_NEXT 0x00b5 +#define SMSG_NPC_CLOSE 0x00b6 +#define SMSG_NPC_CHOICE 0x00b7 /**< Display a choice */ +#define SMSG_NPC_BUY_SELL_CHOICE 0x00c4 +#define SMSG_NPC_BUY 0x00c6 +#define SMSG_NPC_SELL 0x00c7 +#define SMSG_NPC_BUY_RESPONSE 0x00ca +#define SMSG_NPC_SELL_RESPONSE 0x00cb +#define SMSG_PLAYER_CHAT 0x008e /**< Player talks */ +#define SMSG_GM_CHAT 0x009a /**< GM announce */ +#define SMSG_WALK_RESPONSE 0x0087 +#define SMSG_TRADE_REQUEST 0x00e5 /**< Receiving a request to trade */ +#define SMSG_TRADE_RESPONSE 0x00e7 +#define SMSG_TRADE_ITEM_ADD 0x00e9 +#define SMSG_TRADE_ITEM_ADD_RESPONSE 0x01b1 /**< Not standard eAthena! */ +#define SMSG_TRADE_OK 0x00ec +#define SMSG_TRADE_CANCEL 0x00ee +#define SMSG_TRADE_COMPLETE 0x00f0 + +// Packets from client to server +#define CMSG_TRADE_RESPONSE 0x00e6 -/** Packet length by id */ -short get_length(short id); - -/** Decodes x coord */ -unsigned short get_x(const char *data); - -/** Decodes y coord */ -unsigned short get_y(const char *data); - -/** Decodes direction */ -unsigned char get_direction(const char *data); - -/** Decodes src x coord */ -unsigned short get_src_x(const char *data); - -/** Decodes src y coord */ -unsigned short get_src_y(const char *data); - /** Decodes src direction */ unsigned char get_src_direction(char data); -/** Decodes dest x coord */ -unsigned short get_dest_x(const char *data); - -/** Decodes dest y coord */ -unsigned short get_dest_y(const char *data); - /** Decodes dest direction */ unsigned char get_dest_direction(char data); diff --git a/src/net/win2linux.h b/src/net/win2linux.h deleted file mode 100644 index 1371f4d9..00000000 --- a/src/net/win2linux.h +++ /dev/null @@ -1,51 +0,0 @@ -#include <stdio.h> -#include <stdlib.h> -#include <string.h> -#include <sys/socket.h> -#include <netinet/in.h> -#include <stdio.h> -#include <unistd.h> -#include <arpa/inet.h> -#include <netdb.h> - -typedef unsigned char BYTE; -typedef unsigned short WORD; -#define MAKEWORD(low,high) \ - ((WORD)(((BYTE)(low)) | ((WORD)((BYTE)(high))) << 8)) -#define closesocket(a) close(a) -#define SOCKET int -#define SOCKET_ERROR -1 -#define SOCKADDR_IN struct sockaddr_in -typedef struct sockaddr SOCKADDR; -typedef SOCKADDR * LPSOCKADDR; -#define WSACleanup() ; - - - -typedef unsigned short WORD; -typedef unsigned long int LWORD; -typedef unsigned char BYTE; -#define LOBYTE(w) ((BYTE) (w) ) -#define HIBYTE(w) ((BYTE) (((WORD)(w)) >> 8) ) -#define LOWORD(l) ((WORD) (l) ) -#define HIWORD(l) ((WORD) (((LWORD)(l)) >> 16) ) -#define HANDLE int -#define HANDLE int -#define PHANDLE int -#define SMALL_RECT int -//#define WORD int -#define DWORD int -#define PDWORD int -#define BOOL int -#define LPBOOL int -#define LPSTR int -#define LPTSTR int -#define LPCTSTR int -#define LPDWORD int -#define LPVOID int -#define WINAPI - -#define LOBYTE(w) ((BYTE) (w) ) -#define HIBYTE(w) ((BYTE) (((WORD)(w)) >> 8) ) -#define LPTHREAD_START_ROUTINE void *(*)(void *) -#define CloseHandle close diff --git a/src/net/win2mac.cpp b/src/net/win2mac.cpp index 6ddc59cc..d6da2cf2 100644 --- a/src/net/win2mac.cpp +++ b/src/net/win2mac.cpp @@ -1,5 +1,7 @@ #include "win2mac.h" +#define SWAP( a, b ) { char c; c=a; a=b; b=c; } + UInt32 DR_SwapFourBytes(UInt32 dw) { UInt32 tmp; @@ -21,15 +23,9 @@ UInt16 DR_SwapTwoBytes(UInt16 w) char* SwapChar(char charlist[]) { for (int i = 0; i < 24 / 2; i++) - SWAP(charlist[i],charlist[24 - i]); - return charlist; -} + { + SWAP(charlist[i], charlist[24 - i]); + } -/* -char* SwapChar(char charlist[]) -{ - for (int i = 0; i < sizeof(charlist) * 4 / 2; i++) - SWAP(charlist[i],charlist[sizeof(charlist) * 4 - i]); return charlist; } -*/ diff --git a/src/net/win2mac.h b/src/net/win2mac.h index 8159f4a3..29102fae 100644 --- a/src/net/win2mac.h +++ b/src/net/win2mac.h @@ -1,13 +1,10 @@ -#ifndef _TMW_MAC_H -#define _TMW_MAC_H +#ifndef _TMW_WIN2MAC_ +#define _TMW_WIN2MAC_ #include <stdio.h> #define UInt16 unsigned short int -#define INT16 short int #define UInt32 unsigned long int -#define INT32 long int -#define SWAP( a, b ) { char c; c=a; a=b; b=c; } UInt32 DR_SwapFourBytes(UInt32 dw); UInt16 DR_SwapTwoBytes(UInt16 w); diff --git a/src/playerinfo.h b/src/playerinfo.h index 29109fb6..c4028131 100644 --- a/src/playerinfo.h +++ b/src/playerinfo.h @@ -21,15 +21,16 @@ * $Id$ */ -#ifndef _TMW_PLAYERINFO_H -#define _TMW_PLAYERINFO_H +#ifndef _TMW_PLAYERINFO_ +#define _TMW_PLAYERINFO_ -#include <vector> +#include <string> -struct PLAYER_INFO { +struct PLAYER_INFO +{ int id; - float lastAttackTime; // used to synchronize the charge dialog - char name[24]; + float lastAttackTime; /**< Used to synchronize the charge dialog */ + std::string name; /**< Player name */ short hp, max_hp, sp, max_sp, lv; short statsPointsToAttribute; int xp, xpForNextLevel, gp, job_xp, jobXpForNextLevel, job_lv; @@ -40,6 +41,7 @@ struct PLAYER_INFO { short weapon; }; -extern PLAYER_INFO *char_info; +extern PLAYER_INFO **char_info; +extern PLAYER_INFO *player_info; #endif diff --git a/src/serverinfo.h b/src/serverinfo.h index be3fccaa..b317b87b 100644 --- a/src/serverinfo.h +++ b/src/serverinfo.h @@ -21,16 +21,17 @@ * $Id$ */ -#ifndef _TMW_SERVERINFO_H -#define _TMW_SERVERINFO_H +#ifndef _TMW_SERVERINFO_ +#define _TMW_SERVERINFO_ -typedef struct { +#include <string> + +struct SERVER_INFO +{ int address; short port; - char name[20]; + std::string name; short online_users; -} SERVER_INFO; - -extern SERVER_INFO *server_info; +}; #endif |