Jump to content
Eternal Lands Official Forums

svk

Members
  • Content count

    12
  • Joined

  • Last visited

About svk

  • Rank
    Rabbit
  1. Bot functions

    Have a look at the source code for the client. Pretty much anything that the client can do, you can do. Especially look into the client_serv.h file to see which messages in particular you can send to and receive from the EL server.
  2. Questions about starting a bot

    I need some help with network code structure. I want to implement some sort of system that can handle "longer conversations" with the server. Currently, if the server sends me a message, I process it in a single pass and return happily with the message forgotten. However, there may exist cases in which the server would send me a message, and I would wish to reply to that message, and wait for an answer back. For example, my bot is able to report someone's health (not that this is useful, but it serves for the purpose of an example). If someone says "my health" in local chat, my bot would answer with the current HP of whoever said the message. However, in order to do that, the following messages have to be sent between the server and the client: Server: RAW_TEXT (<playername>: my health) Client: SEND_ME_MY_ACTORS Server: ADD_NEW_ENHANCED_ACTOR (info on <playername>) Client: RAW_TEXT (report <playername>'s health) The dilemma is that within my ProcessLocalChat() function, I want to be able to return control back to the event loop so that the program can continue running as usual (processing other messages, sending heartbeats, etc.), but at the same time "remember" that we are still waiting for the server to send us an ADD_NEW_ENHANCED_ACTOR message, and again "remember" what to do with that message. I'm hoping that this is a common-enough problem that a more-or-less-standard solution already exists. Please post! Otherwise, I'll be designing a system that may end up being a bit too complex for the task: a global vector of structures which contain at least this much information: 1) the identifier of the message we are waiting for, e.g. "ADD_NEW_ENHANCED_ACTOR"; 2) a function pointer which points to where program flow should go in case this message is received - which brings up a C++ question: is it legal to have a function pointer point to a member function of a particular instance of a class?; 3) extra information, the contents of which depends on the message we are waiting for; in the case of ADD_NEW_ENHANCED_ACTOR in my health-reporting bot, this would be the name of the player who requested to have his/her health reported. The system I described is feasible, but if a better system already exists, I will be glad to hear it
  3. Questions about starting a bot

    Oh I guess I never visually interpreted it that way.
  4. Questions about starting a bot

    Thanks, I just found the defines in client_serv.h. I think I see how this works. Another quickie: whenever someone speaks, here's what a segment of what it looks like byte by byte: playername: []hello! The [] comes right after the space but before the actual message. It doesn't look like a color code (since it would equate to yellow). What is it?
  5. Questions about starting a bot

    Just a quick question about chat messages. Apparently, the protocol changed slightly since the original bot was written. When raw text is sent, I think the protocol is what is below. Just verify that my guesswork is correct, please: [0] = RAW_TEXT [1,2] = size of the data [3]: From the client source, I see this is the channel number, or 0 if it's local chat. And yet, not entirely... when I open the connection, I receive the "Test server." message. In this case, [3]=3. Surely, that message did not intend to go on channel 3, did it? So why is this different? [4]: This is apparently a color code? And I'm supposed to interpret it by subtracting 127, and *then* comparing it to the color codes? It looks like 133 is the color for normal chat, since 133-127=6, which equates to the color code for gray.
  6. Questions about starting a bot

    Some happy news: It really did turn out to be a signed/unsigned issue. Anytime the server sent me a message with the first byte greater than 127, since the variable that received it was signed, it became negative (the first bit was set). It was still logged as signed even after I cast it as unsigned. This is because it's not the variable itself that determines how it will be logged, but the format string. Observe that I used %d when I was supposed to use %u, for an unsigned integer. Finally, the function that was at fault was the one I thought I didn't need to show, processMessage(). In there, there is a switch statement that tests a signed char. I changed this to Uint8, and the problem was solved. Now, the login confirmation, which was actually sent all along, was interpreted correctly. As for the weird messages that the server sent: apparently, my copy of client_serv.h (which came with the original package of the bot code) was either outdated or edited. I replaced it with the client_serv.h that is shipped with the up-to-date client source code. 60 actually represents a PING_REQUEST, to which I simply reply with the same message. So, after battling with a couple more problems (such as forgetting to check that the length of the message received from the server is greater than 0 before attempting to process it - this resulted in me sending a PING_REPLY a bazzilion times because I thought the server kept sending me a PING_REQUEST, when in fact, the PING_REQUEST was just a remnant of the last message that was sent and was already processed. Sorry for spamming you, EL ), I finally got my code working again. Thanks to everyone!
  7. Questions about starting a bot

    That likely means that the packet size is being mangled somewhere, causing the bot to buffer the login confirmation with another packet, or split it up, etc. (On login, you'll likely receive all of those packets at once.) That would likely also explain the protocols you can't decode. I think my code takes care of multiple server messages per packet. I'll post it below for you guys to examine. It's a little difficult to check because of the extra overhead that Ethereal reports was sent. Wading through a hex dump is tough enough as is, and another one of Ethereal's features is making it even harder. When you try to put your cursor in the packet contents on the bottom, it doesn't select a single byte, but a whole group of (apparently related) bytes. This is convenient when there is one message in a packet, but when there are multiple messages, Ethereal selects them all. Without a blinking cursor, it's a bit tough to keep track of where I am. I tried to export the packet data to a text file, but I'm confronted with an error message that reads <<The path to the file "" doesn't exist.>>. In any case, it appears that even when there are multiple messages per packet, the program handles it fine. Here's a snippet of the log of the messages received: ============================================= Logging in as Calcy Received message from server: [0]=0, [1,2]=15, msg="?Test server." ?Test server. Received message from server: [0]=60, [1,2]=5, msg=""à †" Received message from server: [0]=42, [1,2]=5, msg="" Received message from server: [0]=45, [1,2]=11, msg="ÿÿÿÿÿÿÿÿÿÿ" Received message from server: [0]=5, [1,2]=3, msg="P" Received message from server: [0]=4, [1,2]=5, msg="9à †" Received message from server: [0]=3, [1,2]=3, msg="" Received message from server: [0]=7, [1,2]=25, msg="./maps/map2_insides.elm" Received message from server: [0]=-36, [1,2]=3, msg="" Received message from server: [0]=12, [1,2]=5, msg="ò" Received message from server: [0]=19, [1,2]=2, msg="" Received message from server: [0]=18, [1,2]=191, msg=" " Received message from server: [0]=55, [1,2]=26, msg="" Received message from server: [0]=60, [1,2]=5, msg="9à †" Received message from server: [0]=-6, [1,2]=1, msg="" Received message from server: [0]=71, [1,2]=14, msg="" Received message from server: [0]=51, [1,2]=35, msg="" I see: Calcy ERROR at Sat Jul 08 22:28:01 2006 : Unable to get confirmation if login was successful ERROR at Sat Jul 08 22:28:01 2006 : Unable to log in as Calcy Closing down the connection, and exiting ============================================= Some of the messages have the first byte equal to a negative number (oddly enough, even after I casted them to an unsigned int). Perhaps not being able to get a login confirmation is a signed/unsigned error? (I would doubt it, since the other server messages won't make much sense then). I would also post the packet info, but Ethereal won't let me export it (anyone know how to fix this?). Here's the code for receiving the server messages: int Connection::getPacket() { int len, size, total; // Clear the buffer if this is a new packet if( buf_in_use == 0 ) memset( msgbuf, 0, BUF_SIZE ); // Get the header if necessary if( buf_in_use < 3 ) { len = SDLNet_TCP_Recv( thesock, msgbuf+buf_in_use, 3-buf_in_use ); // If error, or if connection lost, return that there were 0 bytes received if (len < 0) return 0; // Increase the indicator of how much of the buffer is in use by the amount that was // received in the previous packet retrieval call buf_in_use += len; if (buf_in_use < 3) { if (buf_in_use > 0) return -1; log.error ("Packet underrun, len = %d, buf_in_use = %d", len, buf_in_use); return 0; } } // Retrieve the size bytes, which are immediately after the identifier byte size = (*((short *) (msgbuf+1))); // Accomodate for the 2 size bytes themselves size += 2; // If the amount of data that wants to be read exceeds the size // of the buffer that can receive it, we... don't read it at all...? if (size >= BUF_SIZE-3) { /* uh oh */ log.error ("Packet overrun ... data lost"); return 0; } // Read the data chunk by chunk until there is no more to read. // // We request to read all of the remaining data at once (size-buf_in_use, // which is basically "the size of the data" - "how much of it we // have already read", which equates to "the amount of data left to be // received"); however, if we did not receive all the data at once, // we accomodate for it using a while loop. // // We error check every iteration, except... we return -1 here, whereas // just above we returned 0 for the same breed of error. Hmm. // // Finally, we update buf_in_use, or the amount of data we read total, by // the amount we read in the current iteration. while (buf_in_use < size) { len = SDLNet_TCP_Recv (thesock, msgbuf+buf_in_use, size-buf_in_use); if (len <= 0) return -1; buf_in_use += len; } // Set total to represent the total number of bytes of data received total = buf_in_use; // Reset buf_in_use for next time (indicating that we are finished with this // message, and it will be a new one next time) buf_in_use = 0; // Return the total number of bytes of data received return total; } int Connection::getServerMessage() { // to do: ?? What's the purpose of startptr?? int nr_active_sock, reclen, startptr = 0, msglen; // to do: it doesn't appear that startptr is ever changed. Get rid of it? try_again: // Check the socket for activity. nr_active_sock = SDLNet_CheckSockets( theset, 1 ); // If no sockets are active, no need to get messages if (nr_active_sock == 0) { return 0; } // to do: the preceding function returns -1 on error. We should probably error check. // http://jonatkins.org/SDL_net/SDL_net.html#SEC45 if (!SDLNet_SocketReady (thesock)) return 0; // Read the packet reclen = getPacket(); // Error checks if (reclen == -1) return -1; if (reclen <= 0) { log.error ("Disconnected by the server!"); exitConnection (EXIT_ALL); exit (1); } // Retrieve the size of the message, and include the two size bytes themselves msglen = *((short *) (msgbuf + startptr + 1)); msglen += 2; return msglen; // to do: there used to be a call to processMessage() here, but it was moved to a different class. As such, processing the message is now to be done outside of this function. As such, there is no longer a need for this: //goto try_again; } // Here is how the bot currently logs in, using the above functions: int EL::logIn (const char *name, const char *passwd) { // This is what we send to sendToServer() char str[40]; // The message we send should be big enough to include the identifier, // a space between the name and the password, and the null terminator, // which accounts for the +3. int i, len = strlen (name) + strlen (passwd) + 3; // Name and password too long for the buffer if (len >= 40-2) { log.error ("Name or password too long!"); return 0; } // Set the identifier byte str[0] = LOG_IN; // Just after the identifier byte, write the name, followed by a space, and finally the password (the size bytes after the identifier byte will be taken care of by sendToServer() StringCbPrintf( (char *) (str+1), 40-1, "%s %s", name, passwd); if (bDebug == true) { log.info ("Logging in as %s", name); } // http://www.eternal-lands.com/forum/index.php?s=&showtopic=24328&view=findpost&p=257675 // According to ^, we may need to manually null-terminate the log in string before sending // and also send a heartbeat immediately afterwards str[ len ] = '\0'; // Try to log in (note: this function takes care of the size bytes which // we omitted here but are needed to complete the protocol). connection.sendToServer (str, len); // Send the heartbeat char heartbeat = HEART_BEAT; if( connection.sendToServer(&heartbeat, 1) == error ) { log.error( "Failed to send heartbeat immediately after sending login info" ); return error; } logged_in = 0; // Note: this is a global // Keep checking for the login confirmation or denial from the server for (i = 0; i < 100; i++) { // Get any server messages int msglen = connection.getServerMessage(); // to do: error check (and similar calls, too) // Process the server message // This call takes care of setting the global "logged_in" // appropriately if we receive the proper server message if( msglen > 0 ) { processMessage( msgbuf, msglen ); } if (logged_in == LOG_IN_OK) { if (bDebug == true) { log.info ("Login was successful"); } return 1; } else if (logged_in == LOG_IN_NOT_OK) { log.error ("Unable to log in, wrong password?"); return 0; } // If we are here, then we did not receive a message from the server yet. (NOT TRUE: means we did not get the *login confirmation* message from the server; as it happens, we got a dozen other messages already) // Wait 10 milliseconds, and try again SDL_Delay (10); } // And if we are here, that means we tried 100 times, and we are still unable // to receive a confirmation. log.error ("Unable to get confirmation if login was successful"); return 0; } Sorry, some comments sound confused. This is not a finished project yet If anyone can spot any problems, please help me! I'll be examining the Ethereal dump a bit closer... EDIT: Also, why does the log report some negative numbers for the first bytes of some of the messages received from the server? I though I took care of that: unsigned int msg0 = (unsigned int)msg[ 0 ]; log.info( "Received message from server: [0]=%d, [1,2]=%d, msg=\"%s\"", msg0, (int)(*( (short*)( msg+1 ) ) ), msg+3 );
  8. Questions about starting a bot

    Hi again! Today, after roughly a week, I finished giving the source code a major C++/STL face lift, and was able to get it more or less working again. It's still in the preliminary stage, though, since it's littered with "TO DO" and "how the hell does this work?" comments, and the class structure isn't ideal yet. And, of course, no new functionality was actually added. And I have yet to get rid of the dreaded ellipses construct, to which I have unfortunately become addicted while working with the code (it's so handy for log functions). Anyways, there's a small problem, though, which raised a couple questions for me. The problem is the server does not send the log in confirmation anymore. I read here about how you need to make sure that the login string is null terminated. So, 1. Does the null terminator actually have to be sent along with the login string? Do I have to increment the size of the login string by 1 to make sure it's included in the message that gets sent to the server? Also, from the aforementioned link, it seems that I need to send a heartbeat immediately after I send the login request. Check. Still no login confirmation from the server, though. However, it's plainly visible that I actually did log on to the server. From the bot's log file, I see that I get about a dozen other messages that I guess you're supposed to get when you log on (like HERE_YOUR_STATS, and GET_YOUR_SIGILS). I even get a couple that I don't know how to decode (one of them bears the value 60, which matches only ATT_EXP_NEXT in client_serv.h... am I supposed to get this message? And what about 71, which matches only CRA_EXP? In essence, the server recognizes my presence and sends me several messages. Unfortunately, none of them are the login confirmation. 2. Now on to my second big question. I downloaded Ethereal, which allows you examine what gets sent and received to and from your computer. I hope some of you use it, as I did hear about it on these forums. Now, every time I send something to the server, Ethereal reports that there is actually some extra stuff that gets sent before every message. Is this fine? Is this a part of the TCP protocol, which SDL_Net adds and handles for me? If so, is there a way I can tell Ethereal not to display it every time? That's it for now. I hope you guys can help!
  9. Questions about starting a bot

    OK, thanks, then I guess I'll have to reserve the bot's name on the main server. Oh, and the cryptic errors were solved. Apparently, even though the project was set up to compile as a C++ project, the .c extensions of the source files caused the compiler to treat the project as a C project. Of course, headers such as cstdlib and cstdio are C++ headers, not C. The problem was solved by simply changing the source files' extension from .c to .cpp, and then re-adding them to the project (in case anyone runs into a similar problem). Thanks all!
  10. Questions about starting a bot

    Thank you so much, guys. I successfully logged in as a bot, after only a few small modifications to the program to get it to run. Your shrine construction is under way. Unfortunately, after having tried to modify the program more extensively, mainly to convert it to a more C++ oriented style using STL, I screwed something up really bad. I'm currently getting 100 very cryptic errors, over half of which are in the standard include files (not mine). I banged my head on the desk to the point of bleeding, but I still have not a clue what's wrong. I give up for today, but I'm still glad I got a bot to run at all . By the way, what would happen if I create an account that I have on the normal game server on the testserver? Will its stats and inventory be copied from the normal server when I create it? Or will it be a fresh character? If the latter, what would happen when the data is copied between the servers? I'm worried about those things because 1) if I recreate my main account on the test server, will I lose all my stats? and, more pertinent to the subject at hand, 2) since I just created my bot with the desired username on the test server, what happens when I want to move it to the main server? Will it not let me since the account exists?
  11. Questions about starting a bot

    Hi, I'm a somewhat experienced C++ programmer. I can deal with code. Networking, on the other hand, is simply not my cup of tea. First of all, I read the sticked thread at the top about creating bots. I think I understand the restrictions. I also downloaded the source code of a simple bot that was linked to in the aforementioned thread, ELBot (I didn't find where to download the first one - appears to be no longer available). I also downloaded and installed SDL and SDL_Net (which are used in ELBot), and I even got the project to compile in Visual Studio! Now to completely obliterate any credit I have established in the last paragraph, allow me to enumerate my newbish-but-true questions: 1. The sticked thread mentions "You can use the testserver on port 2001 to develop your bot". I don't exactly understand what this means (did I mention I hate networking?). Is this a physical, hardware port on my computer? A hardware port on the EL server? Something software? The source code of the bot I downloaded has a line of code that reads "int port = 2000;", which is later used in the SDL function SDLNet_ResolveHost. I changed this to read "int port = 2001;". Is this OK if I want it to run on the test server, or are there additional steps/complications here? 2. If I am able to successfully log on to the testserver, how can *I*, as my main account, log on to the testserver to see my bot? 3. Another line in the source code reads: "char *hostname = "eternal-lands.solexine.fr";". Is this correct? Because elsewhere I saw a different address, "eternal-lands.network-studio.com". Which should I use? 4. Is there a guide for what I can send/receive from the server? All the networking code looks rather cryptic at the moment, and not so much because of the syntax, but because I have no clue *why* the program is doing what it's doing. I see that it's sending this message and that message to the server, and I understand what the message is supposed to contain, but I have no clue how this system works. (EDIT: new question) 5. How can I actually log a bot in (to the testserver)? Do I have to create a new EL character and password? And then simply change the "username" and "password" variables that my program sends to the server? Oh, I know. I suck. If anyone with experience can help me, I will build a shrine to you and pray every evening. Thanks in advance.
  12. Making money

    How are you supposed to make money in this game? The response I usually get is either "harvest flowers" or "alch fire essences". No matter the level. The problem is that both of these tasks are accessible to people that just started playing - no skill is required. You may have spent a minute, a month, or a year, but from what I hear, harvesting flowers remains the most effective method of making fast money. Before I go on with my rant, I should note that I only started playing a month ago. I may be wrong here and there, or maybe even the entire purpose of my rant is misled by misinformation. I am pulling most of my knowledge from what I read on the in-game newbie channel. But from what I hear from other experienced players who can boast many months of experience, no matter how much skill they have, they still make most of their money by harvesting flowers. This is where my beef lies with EL. In terms of the economy, we are not being rewarded for the time or effort that we invest into the game. Understand this, I can boast no experience in neither economics nor RPG development (I do program, but I am still an amateur, at best). However, at least from the standpoint of a player, I feel that if I spend a month training my skills, I should have a higher profit-per-hour figure than someone who spent a minute. And yet, too often with higher-level items (particularly in the area of manufacturing), the ingredients can be sold for more than the finished products. I realize that there exists another school of thought on this issue. There are some who believe that the amount of time invested should not determine success in an RPG. However, I think that we should look at an RPG not in terms of in-game success, but overall fun. That is, after all, the purpose of a video game: entertainment. A good RPG should be fun to play at every stage, from absolute newbie to a seasoned professional. I am yet an "intermediate newbie," so I can say nothing about being pro; however, in the month that I played, I can attest to having had fun. Yet, as I'm moving onward, I keep finding myself going back to those flowers again. I don't feel as if I am actually getting anywhere new, at least in terms of the ability to make money. I fear that such a system would eventually provoke boredom. Achievement seems to be the base concept of any RPG -- it's what gives the player the urge to go on. I feel that the economic system should somehow be corrected. I am no expert in game design (I'm working on it , but *something* should be done about the economy.I will now go back to harvesting my flowers. However, I hope that in this thread, I hope I will: -perhaps be corrected. Am I wrong about everything entirely? Is there a better way to make money once you get your skills up? -hear from you guys about what you think of this. Is this system fine? Should it be changed? Should the amount of time invested have no impact on profit-per-hour? Oh, and what the heck, I though we could have an idea dump of what could be done if it is desired that the system be corrected. However, it looks like there is another thead for this already. I hope what I said here does not overlap too much with that other thread; I'll be taking a look at it. EDIT: whoops, I wrote this in Notepad, and it garbled the formatting. Fixed it Later, svk
×