Eternal Lands Network Protocol
From beplacid.net
Contents |
[edit] Eternal Lands Network Protocol Documentation
[edit] Introduction
The purpose of this Wiki page is to provide a central resource for the Eternal Lands network protocol. Whilst the protocol is generally easy to use, coding for the official client or writing your own bot has a steep learning curve with regards to the protocol messages. This Wiki attempts to alleviate that and provide a knowledge base for seasoned EL programmers and newcomers alike.
[edit] How To Read This Article
It is worth noting that this article is to be independent of any form of implementation (except where the official Eternal Lands client is referenced). The aim is to keep a clean and easy-to-use reference for the protocol, not a bot/client development how-to. Therefore, the target audience for this article are those that have at least the basic knowledge of computing and networking in particular. Where possible, correct terminology has been used to ensure clarity and consistency.
Whilst this article will provide code examples, it will do so only in C. If you would prefer to read examples in another language, or contribute an example in a language other than C, please visit the Eternal Lands Protocol Code Examples page.
If you would like to contribute, please create a user and check the discussion page for desired information or actions.
[edit] The Protocol
From here on, this article will describe the network protocol in technical detail.
[edit] Message Format
A standard message from or to the server consists of 3 sections. The first is the message type (specified below in the Client to server and Server to client sections). The second is the length of the data. As you may have guessed, data is the third and last section of a message. This however, is not always required (as you will see later on). Therefore, a message can be described as follows:
[PROTOCOL TYPE][MESSAGE LENGTH][DATA]
PROTOCOL TYPE is an 8 bit integer.
MESSAGE LENGTH is a 16 bit unsigned integer and is calculated thus:
(length of PROTOCOL TYPE)+(length of DATA) = 1+(length of DATA)
The description of DATA can be found below.
[edit] Message Parsing
An easy assumption at this point is to assume that packets received are sent one-by-one. This is incorrect. Network data is streamed (or, constant), therefore you will need to identify the sections of a packet correctly (described above). The length section is important here, as it defines how much data the packet contains and thus how much you should read from the 'stream'.
Here's an example in C:
const int base_size_buf = 1024;
void read_message() {
int size_buf = base_size_buf;
unsigned char* buf = calloc(size_buf, 1); //to prevent having to 0 the memory
unsigned short int msg_len = 0; //or Uint8. I assume 32-bit little-endian, because that's what people actually *use*
int buf_read = 0;
while(true) {
int read = read_data(buf, buf_read, buf_size - buf_read); //we assume this returns -1 on failure
if(read < 0)
break;
if(read==0)
continue; //might want to sleep
buf_read += read;
if(buf_read + 5 >= size_buf) { //to give us wiggle room for large messages
int new_buf_size = buf_size * 2;
realloc(buf, new_buf_size);
for(int i = buf_size; i < new_buf_size; i++)
buf[i] = 0;
buf_size = new_buf_size;
}
if(msg_len==0 && buf_read >= 3)
msg_len = ((unsigned short int)buf[1]) | (((unsigned short int)buf[2]) << 8);
int msg_off = msg_len + 2;
if(msg_len >= buf_read) {
int str_len = msg_len - 1;
unsigned char type = buf[0];
char* str = calloc(str_len, 1);
memmove(buf+3, str, str_len);
buf_read -= msg_off;
memmove(buf+msg_off, buf, buf_read);
process_data(type, str, str_len); //other-thread this if at all possible - and trust me, it is
msg_len = 0;
}
}
free(buf);
}
void process_data(unsigned char type, unsigned char* data, int data_len) {
//do stuff with data, possibly after a switch, case. For example purposes:
if(type==RAW_TEXT || type==PM) { //0 and 2, respectively
printf("%s", data);
}
}
You read until you have "enough", according to the length specified in the packet. The format is very important.
This reading routine reads the data in place, without the risk of overflows or underflows (assuming that your read_data method respects the offset and length given). Note that nothing else will happen if you're doing this single-threaded, which will cause a lag out. Make sure that you do the reading in another thread from the heartbeat timer, and the responses in another thread from the reading, for the best results.
[edit] Common (both server and client)
| Byte/Type (Number) | Data | Explanation | Notes |
|---|---|---|---|
| BYE (255) | Not used by the client, however the server may send this when it's full. | ||
[edit] Channels
| Number | Name | What |
|---|---|---|
| 0 | CHAT_LOCAL | Local chat and some local server messages (eg: grue) |
| 1 | CHAT_PERSONAL | PMs |
| 2 | CHAT_GM | GMs and IGs |
| 3 | CHAT_SERVER | System messages, BCs, etc. |
| 4 | CHAT_MOD | Mod chat |
| 5 | CHAT_CHANNEL1 | Your chat channel #1 |
| 6 | CHAT_CHANNEL2 | Your chat channel #2 |
| 7 | CHAT_CHANNEL3 | Your chat channel #3 |
| 8 | CHAT_MODPM | Mod PMs |
[edit] Client to server
| Byte/Type (Number) | Data | Explanation | Notes | |
|---|---|---|---|---|
| RAW_TEXT (0) | MESSAGE | Sends MESSAGE | Some messages may include a colour byte, a list of which can be found at EL Colours | |
| MOVE_TO (1) | [X][Y] | Asks the server to move your character to (X,Y). Both coordinates are unsigned 16-bit integers. | MOVE_TO does not allow full map traversal, only a small area can be walked at a time. Max path length = 25 tiles? (Unconfirmed) | |
| SEND_PM (2) | NAME[SPACE]MESSAGE | Sends a PM to NAME with message MESSAGE, separated by a space (0x20). | ||
| GET_PLAYER_INFO (5) | [ACTOR ID] | Returns a message in CHAT_SERVER, "You see: NAME"/"You see a CREATURE". ACTOR ID is a 32 bit unsigned integer. | Used when eye-clicking an actor. | |
| RUN_TO (6) | [X][Y]? | Not used. Future server feature? | Sending this command will disconnect you from the game server. | |
| SIT_DOWN (7) | [BOOL] | Sit down or stand up. BOOL is a 1 byte boolean value, 0 to stand up, 1 to sit down. | ||
| SEND_ME_MY_ACTORS (8) | Request all nearby actors from the server. | Only use when you're lagging behind. The Client uses this for resyncs. | ||
| SEND_OPENING_SCREEN (9) | ||||
| SEND_VERSION (10) | [FIRST_DIGIT][SECOND_DIGIT] [MAJOR][MINOR][RELEASE][PATCH] [HOST1][HOST2][HOST3][HOST4] [PORT1][PORT2] | Send client version and server ip/port info to server. FIRST_DIGIT and SECOND_DIGIT are 16-bit integers, the rest are 8-bit. | Sending this is optional. | |
| TURN_LEFT (11) | Turn your actor left 45 degrees. | |||
| TURN_RIGHT (12) | Turn your actor right 45 degrees. | |||
| PING (13) | [YOUR DATA] | Used to calculate server latency. YOUR DATA is 4 bytes (32 bits), which are returned untouched by the server as a PONG message. | The official #ping command uses this. | |
| HEART_BEAT (14) | An empty message notifying the server you are still connected | See the Message Examples section for more information | ||
| LOCATE_ME (15) | Requests a location string from the server. ("You are in MAP - SECTION [X, Y]", channel CHAT_SERVER) | Same as clicking the compass in the Client. | ||
| USE_MAP_OBJECT (16) | [OBJECT ID][USE-WITH POSITION] | Use a map object (optionally with some other item in your inventory). OBJECT ID is the object's ID on the map (32 bits), USE-WITH POSITION is the position of the item in your inventory to use with the object. (also 32 bits) | If you're not using with an item, set USE-WITH POSITION to -1. | |
| SEND_MY_STATS (17) | Unused | |||
| SEND_MY_INVENTORY (18) | ||||
| LOOK_AT_INVENTORY_ITEM (19) | [ITEM POSITION] | 'Look' at inventory item, response sent as INVENTORY_ITEM_TEXT. ITEM POSITION is an 8-bit integer. | Used when eye-clicking an item. | |
| MOVE_INVENTORY_ITEM (20) | [FROM POSITION][TO POSITION] | Move an item from one slot to another (0..35 are normal slots, 36..43 are equipment slots). Both arguments are 8-bit integers. | You cannot move an item from slots 36..43 to elsewhere in slots 36..43 | |
| HARVEST (21) | [OBJECT_ID] | Harvest from a map object. OBJECT_ID is an unsigned 16 bit integer. | Note the discrepancy between the 32 bit OBJECT ID sent in USE_MAP_OBJECT | |
| DROP_ITEM (22) | [POSITION][QUANTITY] | Drop QUANTITY (32-bit unsigned integer) items into a bag from slot POSITION (8-bit unsigned integer). | Server just drops to the actual quantity the slot holds if you attempt to drop more than the inventory amount. | |
| PICK_UP_ITEM (23) | [POSITION][QUANTITY] | Pick up QUANTITY (32-bit integer) items from slot POSITION (8-bit integer) in a bag. | You must stand on a bag at the time, and the bag must be open (see INSPECT_BAG) | |
| LOOK_AT_GROUND_ITEM (24) | [POSITION] | Examine an item in a bag at a certain position (8-bit integer). Response is sent as INVENTORY_ITEM_TEXT. | (see LOOK_AT_INVENTORY_ITEM) You must stand on the bag at the time, and it must be open (see INSPECT_BAG). | |
| INSPECT_BAG (25) | [BAG] | Open a bag. BAG (8-bit integer) is the position of the bag in the bag list. | You must stand on BAG at the time. | |
| S_CLOSE_BAG (26) | Close a bag opened by INSPECT_BAG. | |||
| LOOK_AT_MAP_OBJECT (27) | [OBJECT_ID] | Examine an item on the map with object id OBJECT_ID (32-bit integer). Response sent to CHAT_SERVER. "You see a ITEM" | ||
| TOUCH_PLAYER (28) | [OBJECT_ID] | Begin to interact with a player/NPC/monster. OBJECT_ID is a 32-bit integer. | Depending on the OBJECT_ID this may result in a dialog box/cast a spell | |
| RESPOND_TO_NPC (29) | [TO_ACTOR][RESPONSE_ID] | Reply to an NPC dialog choice. Both parameters are unsigned 16-bit integers. | ||
| MANUFACTURE_THIS (30) | [POS1][QTY1] ... [POS6][QTY6] [QTY_TO_MAKE] | Manufacture an item from the ingredients and quantities given in slots 1-6 above. POS is an 8-bit integer, QTY is a 16-bit integer. QTY_TO_MAKE is an 8-bit unsigned integer specifying the quantity of items to make of the given formula. | Empty slots have to be left out from the message. | |
| USE_INVENTORY_ITEM (31) | [POS] | Use the item that is at position POS in your inventory. POS is a 16 bit integer. | This does the action that the client does using the 'use/finger' icon. I'm not sure why POS is 16 bit? | |
| TRADE_WITH (32) | [ACTORID] | Begin trading with another player/actor. ACTORID is a 32-bit integer representing the actor to trade with. | The ACTORID for each player is got from the ADD_NEW_ACTOR and ADD_NEW_ENHANCED_ACTOR packets. | |
| ACCEPT_TRADE (33) | [ACTORID] | ACTORID is a 32-bit integer representing the actor you are trading with. Additionally, your final trade accept needs to pass an additional byte per storage slot to indicate whether the item is going to storage (2) or inventory (1). | ||
| REJECT_TRADE (34) | ||||
| EXIT_TRADE (35) | Abort a trade session. | |||
| PUT_OBJECT_ON_TRADE (36) | [WHERE][POS][QTY] | WHERE is an 8-bit integer, 1 means object comes from inventory, 2 from storage. QTY (32-bit integer) is the amount of the item at position POS (8-bit integer) in your inventory/storage. | ||
| REMOVE_OBJECT_FROM_TRADE (37) | [POS][QUANTITY] | Remove QUANTITY (unsigned 32-bit integer) items from position POS (8-bit integer) | ||
| LOOK_AT_TRADE_ITEM (38) | [POS][WHO] | Request a description of the item for trade at position POS, in WHO's list. WHO can be either 0 (your items) or 1 (trade partner's items). Both parameters are 8-bit integers. Response is sent as INVENTORY_ITEM_TEXT. | ||
| CAST_SPELL (39) | [SIGIL_COUNT][SIGIL1]...[SIGIL6] | Cast a spell. SIGIL_COUNT is the number of sigils you're sending, min. 2, max. 6. [SIGIL] is the sigil's image ID. All parameters are 8-bit integers. | ||
| ATTACK_SOMEONE (40) | [ACTOR_ID] | Attack actor with ID ACTOR_ID (32-bit integer). | Works with 16-bit integers too. | |
| GET_KNOWLEDGE_INFO (41) | [BOOK_ID] | Request description of the knowledge book at position BOOK_ID (16-bit integer). Response sent as GET_KNOWLEDGE_TEXT. | ||
| ITEM_ON_ITEM (42) | [POS1][POS2] | Use the item at POS1 with the item at POS2 in your inventory. Both arguments are 8-bit integers. | ||
| SEND_BOOK (43) | ||||
| GET_STORAGE_CATEGORY (44) | ||||
| DEPOSITE_ITEM (45) | [POS][QUANTITY] | Deposit QUANTITY (unsigned 32-bit integer) items in storage from position POS (8-bit integer) in your inventory. | ||
| WITHDRAW_ITEM (46) | [POS][QUANTITY] | Withdraw QUANTITY (unsigned 32-bit integer) items from position POS (8-bit integer) in storage. | ||
| LOOK_AT_STORAGE_ITEM (47) | [POS] | Request a description of the item at position POS (8-bit unsigned integer) in storage. Response sent as STORAGE_TEXT. | ||
| SPELL_NAME (48) | [SPELL ID] | Request the name of the spell with id SPELL ID (8-bit integer). See SPELL_CAST | ||
| PING_RESPONSE (60) | Used when responding to a PING_REQUEST. | See PING_REQUEST for more info. | ||
| SET_ACTIVE_CHANNEL (61) | [CHANNEL] | Set active channel to CHANNEL (8-bit integer). | See Channels for valid values. | |
| LOG_IN (140) | USERNAME[SPACE]PASSWORD[NULL] | Tells the server you wish to connect as USERNAME with password PASSWORD. | Must be null terminated (\0). Space is 0x20. See Message Examples. | |
| CREATE_CHAR (141) | NAME[SPACE]PASSWORD[NULL] [SKIN][HAIR][SHIRT][PANTS] [BOOTS][TYPE][HEAD] | Create a character with the specified attributes. All parameters from SKIN to HEAD are 8-bit integers. See below tables for valid values. | Actor Types | |
| GET_DATE (230) | Returns game date in CHAT_SERVER. "Today is the Nth day in the month of MONTH, the year YYYY, Age of the Eternals" | The equivalent of #date in the Client | ||
| GET_TIME (231) | Returns game time in CHAT_SERVER. "Game Time: HH:MM:SS" | The equivalent of #time in the Client | ||
| SERVER_STATS (232) | Returns server statistics in CHAT_SERVER. | The equivalent of #stats in the Client | ||
| ORIGINAL_IP (233) | Unused | |||
[edit] Server to client
| Byte/Type (Number) | Data | Explanation | Notes | ||||||||||||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| RAW_TEXT (0) | [CHANNEL][MESSAGE] | Channel is an unsigned char, representing the channel number (see channel numbers). Message is text string. | Color bytes can be placed anywhere (more than once, possibly) in the text, indicating a change of color (e.g.: like in local chat lines) | ||||||||||||||||||||
| ADD_NEW_ACTOR (1) | |||||||||||||||||||||||
| ADD_ACTOR_COMMAND (2) | [ID] [COMMAND] | 2 bytes actor_id, 1 byte command | |||||||||||||||||||||
| YOU_ARE (3) | [ID] | ID is the 16 bit integer that represents 'you', your actor ID. | See ADD_NEW_ENHANCED_ACTOR | ||||||||||||||||||||
| SYNC_CLOCK (4) | |||||||||||||||||||||||
| NEW_MINUTE (5) | [TIME] | TIME is a 16-bit integer representing the time, as a total number of minutes so far this day. A day in EL is 6 hours long, 60 minutes to an hour. | |||||||||||||||||||||
| REMOVE_ACTOR (6) | [ID] | ID is a 16-bit integer representing an actor (the actor ID) that just moved out of sight. | |||||||||||||||||||||
| CHANGE_MAP (7) | [MAPFILE] | MAPFILE is a null terminated string containing the path to the new map relative to the Eternal Lands installation directory | |||||||||||||||||||||
| COMBAT_MODE (8) | |||||||||||||||||||||||
| KILL_ALL_ACTORS (9) | |||||||||||||||||||||||
| GET_TELEPORTERS_LIST (10) | |||||||||||||||||||||||
| PONG (11) | [YOUR DATA] | Reply to a PING message. YOUR DATA is the 32 bits you sent earlier. (Untouched). | |||||||||||||||||||||
| TELEPORT_IN (12) | |||||||||||||||||||||||
| TELEPORT_OUT (13) | |||||||||||||||||||||||
| PLAY_SOUND (14) | |||||||||||||||||||||||
| START_RAIN (15) | |||||||||||||||||||||||
| STOP_RAIN (16) | |||||||||||||||||||||||
| THUNDER (17) | |||||||||||||||||||||||
| HERE_YOUR_STATS (18) | |||||||||||||||||||||||
| HERE_YOUR_INVENTORY (19) | [NbrOfItems} UInt8 (send once) then [ItemID] UInt16 [Quantity] UInt32 [PosInInventory] UInt8 [Flags] Uint8 for each single item. | ||||||||||||||||||||||
| INVENTORY_ITEM_TEXT (20) | TEXT | Response to LOOK_AT_INVENTORY_ITEM, LOOK_AT_TRADE_ITEM or LOOK_AT_GROUND_ITEM | |||||||||||||||||||||
| GET_NEW_INVENTORY_ITEM (21) | |||||||||||||||||||||||
| REMOVE_ITEM_FROM_INVENTORY (22) | [POS1]...[POSN] | The item at position POS (8-bit unsigned integer) in your inventory has been removed. | Can remove multiple items in one message, make sure you check message length. (Total items removed: MESSAGE LENGTH-1) | ||||||||||||||||||||
| HERE_YOUR_GROUND_ITEMS (23) | |||||||||||||||||||||||
| GET_NEW_GROUND_ITEM (24) | |||||||||||||||||||||||
| REMOVE_ITEM_FROM_GROUND (25) | |||||||||||||||||||||||
| CLOSE_BAG (26) | |||||||||||||||||||||||
| GET_NEW_BAG (27) | |||||||||||||||||||||||
| GET_BAGS_LIST (28) | |||||||||||||||||||||||
| DESTROY_BAG (29) | [BAG] | BAG is a 16-bit integer representing a bag (the bag's position in the bag list) that has disappeared from the map. | |||||||||||||||||||||
| NPC_TEXT (30) | |||||||||||||||||||||||
| NPC_OPTIONS_LIST (31) | |||||||||||||||||||||||
| CLOSE_NPC_MENU (32) | |||||||||||||||||||||||
| SEND_NPC_INFO (33) | |||||||||||||||||||||||
| GET_TRADE_INFO (34) | Not used anymore (comment: //delete later on) | ||||||||||||||||||||||
| GET_TRADE_OBJECT (35) | |||||||||||||||||||||||
| GET_TRADE_ACCEPT (36) | |||||||||||||||||||||||
| GET_TRADE_REJECT (37) | |||||||||||||||||||||||
| GET_TRADE_EXIT (38) | |||||||||||||||||||||||
| REMOVE_TRADE_OBJECT (39) | |||||||||||||||||||||||
| GET_YOUR_TRADEOBJECTS (40) | |||||||||||||||||||||||
| GET_TRADE_PARTNER_NAME (41) | [TEXT] | Trade partner's name | Sent when the trade starts | ||||||||||||||||||||
| GET_YOUR_SIGILS (42) | |||||||||||||||||||||||
| SPELL_ITEM_TEXT (43) | |||||||||||||||||||||||
| GET_ACTIVE_SPELL (44) | |||||||||||||||||||||||
| GET_ACTIVE_SPELL_LIST (45) | |||||||||||||||||||||||
| REMOVE_ACTIVE_SPELL (46) | |||||||||||||||||||||||
| GET_ACTOR_DAMAGE (47) | [ID][DMG] | 16-bit integer indicating the actor id, and a 16-bit integer indicating the amount of damage taken | |||||||||||||||||||||
| GET_ACTOR_HEAL (48) | [ID][QTY] | 16-bit integer indicating the actor id, and a 16-bit integer indicating the amount of material points restored | |||||||||||||||||||||
| SEND_PARTIAL_STAT (49) | attribute value | 1 byte attribute type, 4 bytes attribute level | |||||||||||||||||||||
| SPAWN_BAG_PARTICLES (50) | |||||||||||||||||||||||
| ADD_NEW_ENHANCED_ACTOR (51) | |||||||||||||||||||||||
| ACTOR_WEAR_ITEM (52) | [ID][?][ITEM_TYPE][ITEM_ID] | ID is the unsigned 16 bit integer for the Actor. Item type and item ID are 8-bit unsigned integers | Unsure as to what the second byte is. Some sort of flag? Item type is the position/section being worn - helmet, body armour, leg etc. | ||||||||||||||||||||
| ACTOR_UNWEAR_ITEM (53) | [ID][?][ITEM_TYPE] | ID is the unsigned 16 bit integer for the Actor. Item type is the same as ACTOR_WEAR_ITEM | |||||||||||||||||||||
| PLAY_MUSIC (54) | |||||||||||||||||||||||
| GET_KNOWLEDGE_LIST (55) | |||||||||||||||||||||||
| GET_NEW_KNOWLEDGE (56) | |||||||||||||||||||||||
| GET_KNOWLEDGE_TEXT (57) | TEXT | Response to GET_KNOWLEDGE_INFO. | |||||||||||||||||||||
| BUDDY_EVENT (59) | |||||||||||||||||||||||
| PING_REQUEST (60) | Should be returned as-is, just change the protocol to PING_RESPONSE. | PING_REQUEST = PING_RESPONSE, so changing the protocol isn't really required. | |||||||||||||||||||||
| FIRE_PARTICLES (61) | |||||||||||||||||||||||
| REMOVE_FIRE_AT (62) | |||||||||||||||||||||||
| DISPLAY_CLIENT_WINDOW (63) | |||||||||||||||||||||||
| OPEN_BOOK (64) | |||||||||||||||||||||||
| READ_BOOK (65) | |||||||||||||||||||||||
| CLOSE_BOOK (66) | |||||||||||||||||||||||
| STORAGE_LIST (67) | [NMBR_OF_CATS] then [NUM][NAME] for each catagory | All except the text are 8bit integers. | Each catagory name ends with 0x00 | ||||||||||||||||||||
| STORAGE_ITEMS (68) | |||||||||||||||||||||||
| STORAGE_TEXT (69) | TEXT | Response to LOOK_AT_STORAGE_ITEM. | |||||||||||||||||||||
| SPELL_CAST (70) | [SUB PROTOCOL][SUB DATA]
| ||||||||||||||||||||||
| GET_ACTIVE_CHANNELS (71) | [I][1][2][3] | I is the 8-bit index of the active channel in the last three bytes. 1, 2 and 3 are 32bit ints representing the channel number you're on | 0 in the channel bytes denotes no channel assigned | ||||||||||||||||||||
| MAP_FLAGS (72) | |||||||||||||||||||||||
| GET_ACTOR_HEALTH (73) | |||||||||||||||||||||||
| GET_3D_OBJ_LIST (74) | |||||||||||||||||||||||
| GET_3D_OBJ (75) | |||||||||||||||||||||||
| REMOVE_3D_OBJ (76) | |||||||||||||||||||||||
| GET_ITEMS_COOLDOWN (77) | |||||||||||||||||||||||
| SEND_BUFFS (78) | |||||||||||||||||||||||
| SEND_SPECIAL_EFFECT (79) | |||||||||||||||||||||||
| REMOVE_MINE (80) | [MINE] | MINE is an unsigned 8 bit integer representing the position in the mines list of the mine that has disappeared from the map. | Under development and subject to change | ||||||||||||||||||||
| GET_NEW_MINE (81) | [X][Y][TYPE][ID] | X and Y are unsigned 16 bit integers representing the X and Y coordinates of the mine. TYPE is an 8 bit integer representing the type of the mine. ID is an 8 bit integer representing the mine's position in the mines list. | Under development and subject to change | ||||||||||||||||||||
| GET_MINES_LIST (82) | [NUM][[X][Y][TYPE][ID]|...] | NUM is an unsigned 8 bit integer representing the number of mines in the packet. The other bits of data are as per the GET_NEW_MINE packet, repeated NUM times. | Under development and subject to change | ||||||||||||||||||||
| SEND_WEATHER (100) | Uint8 type, Uint8 direction, Uint8 intensity | Type will be rain, snow, wind, etc. Direction is degrees (from north) divided by 2. Intensity is the strength of the effect, up to 100 | Currently unused and very likely to change server-side ID when implemented!!! | ||||||||||||||||||||
| MAP_SET_OBJECTS (220) | Reserved for future expansion | ||||||||||||||||||||||
| MAP_STATE_OBJECTS (221) | |||||||||||||||||||||||
| (222) | |||||||||||||||||||||||
| (223) | |||||||||||||||||||||||
| (224) | |||||||||||||||||||||||
| (225) | |||||||||||||||||||||||
| (226) | |||||||||||||||||||||||
| (227) | |||||||||||||||||||||||
| (228) | |||||||||||||||||||||||
| (229) | |||||||||||||||||||||||
| UPGRADE_NEW_VERSION (240) | |||||||||||||||||||||||
| UPGRADE_TOO_OLD (241) | |||||||||||||||||||||||
| REDEFINE_YOUR_COLORS (248) | |||||||||||||||||||||||
| YOU_DONT_EXIST (249) | |||||||||||||||||||||||
| LOG_IN_OK (250) | |||||||||||||||||||||||
| LOG_IN_NOT_OK (251) | |||||||||||||||||||||||
| CREATE_CHAR_OK (252) | |||||||||||||||||||||||
| CREATE_CHAR_NOT_OK (253) | |||||||||||||||||||||||
[edit] Actor Data - Commands and Types
The term "actor commands" refers to the data sent to a client, instructing the player to perform a particular function in-game. An "actor type" describes what an actor within the client actually is; whether it be monster, animal or another player.
Due to the actor-related data being quite long, the tables describing these functions can be found on the EL Actor Data page.
[edit] Item Image IDs
The item image IDs refer to the numbers used to represent each item via its image. The official client uses this information to recognise an item's picture (or icon). The list is large in itself, and has therefore been move to its own page, which can be found on the EL Item Image IDs page.
[edit] Message Examples
This section provides examples of simple messages you may encounter. The examples provided here are in C, however there is the EL Protocol Code Examples article that has code listings in other languages.
[edit] Client to Server
[edit] The LOG_IN message
As per the previous sections, the standard format is used and the login message should be structured as follows:
[LOG_IN (140)][LENGTH]USERNAME[SPACE]PASSWORD[NULL]
That is, the username and password should be separated by a space and followed by a null character.
Upon sending this message you should expect one of LOG_IN_OK (250) or LOG_IN_NOT_OK (251). In C, this could be done as follows:
char buffer[255]; //255 should be enough const char *name = "MyBot"; const char *pass = "mybot123"; buffer[0] = 140; *((short *)(buffer+1)) = 1+strlen(name)+1+strlen(pass)+1; sprintf(buffer+3, "%s %s", name, pass); // send to the server
[edit] The HEART_BEAT message
Perhaps the second most important message your bot needs to send. This notifies the server that you are alive. If not sent every 30 seconds (as a maximum, 25 is safe), the server will disconnect you (hence the famous 'Grue'). The server expects the message in the following format:
[HEART_BEAT (14)][LENGTH (1)]
As you'll notice there is no data section to this message and a LENGTH of 1.
Here's an example in C:
char buffer[255]; buffer[0] = 14; *((short *)(buffer+1)) = 1; // send to the server
[edit] The SIT_DOWN message
Whilst quite trivial, this message is a simple way of providing realtime interaction and a fun way to test your code. You should structure it as follows:
[SIT_DOWN (7)][LENGTH (2)][1 or 0]
See the Client to server table for a description of the message. Just as a summary, sending 1 would make your bot character sit down, 0 to stand up.
Note: the length is always two bytes, therefore the message would look like [7][2][0][1 or 0]
[edit] The RAW_TEXT message
Sending RAW_TEXT is a common occurrence. It can be used for chatting to the vicinity right up to chatting to people on global channels. If you read the Channels table, you will see each type of channel available to send to. A client should keep track of the channels it is connected to with regards to global channels but other than that, the general message format is the same. Here's the structure for sending to the vicinity (local chat):
[RAW_TEXT (0)][LENGTH (X*)][CHANNEL_LOCAL (0)]MESSAGE
X - See the Message Format section for calculating the length
The following is an example of this message written in C:
char buffer[255]; char *msg = "This is a message to local chat!"; buffer[0] = 0; *((short *)(buffer+1)) = 1+strlen(message); sprintf(buffer+3, "%s", msg); // Send to the server...
Notes:
- You do not need to send the \0 (and thus do not need to add +1 to the length)
- The server may send a text colour byte in the message message data. You can see a list of the colours used in the official client on the EL Colours page
[edit] The SEND_PM message
Sending a private message (PM) to another player can be used when listing inventory items for sale for instance. The SEND_PM message structure could be compared to that of the LOG_IN; as it has a similar format:
[SEND_PM (2)][LENGTH (X*)]NAME[SPACE]MESSAGE
X - the length should be calculated as per the Message format section. You do not need to send the NULL character in this message.
Here's an example in C:
char buffer[255]; char *name = "Placid"; char *msg = "Hi there here's the 200kgc I owe you!"; buffer[0] = 2;// message type *((short *)(buffer+1)) = 1+strlen(name)+1+strlen(msg);// the length sprintf(buffer+3, "%s %s", name, msg); // Send to the server...
[edit] Server To Client
[edit] The HERE_YOUR_INVENTORY message
The server sends the amount of filled slots in your inventory and the items, the amount of them and the position in inventory.
Example in C:
int i, pos, total_inv = data[0]; /* data[0] = no_items */
data++;
for (i = 0; i < 36+8; i++) {
inv[i].quantity = 0;
}
for(i = 0; i < total_inv; i++) {
pos = data[i*8+6];
inv[pos].id = *((Uint16 *)(data+i*8));
inv[pos].quantity = *((Uint32 *)(data+i*8+2));
inv[pos].flags = *((Uint8 *)(data+i*8+7));
}
[edit] Summary
It would be impractical and extremely time consuming (not to mention pointless) to document every single message sent and received from the server. If you have a basic understanding of network programming, you should now be proficient enough to work out the rest of the messages. The protocol is generally easy to use (bar some inconsistencies) so you should not have many issues.
If you would prefer to read some examples in other languages (or add to them), you can visit the EL Protocol Code Examples page.
[edit] Glossary of Terms
This section contains an explanation of any terms used within this page.
- Byte - eight bits on a typical x86 processor system
- Data - the section of a message to or from the server that is not the protocol type and not the length of the message (which includes the length of the data)
- Actor - the term given to any character/player or AI entity on the server. This is taken from the official Eternal Lands client
- AI - Artificial Intelligence
- Channel - chat 'entity' such as local (vicinity) chat, guild messages or global channels. This is represented in the client by CHANNEL_PM etc, see Channels for more information
- PM - 'Private Message'; a message from one player to another (only visible to the two people involved)
- EMU - Ethereal Matter Unit; a unit of measurement (equivalent to weight) in Eternal Lands
- EL - Eternal Lands
[edit] External Links and References
Below you will find a categorised list of useful links outside of this Wiki.
[edit] Programming Resources
These links are relative to client programming (not general programming resources):
- LibSDL - the Simple Direct media Layer (SDL) library for graphics and networking programming (bindings for C/C++, Java et al)
- CPAN EL Perl Modules - modules written by Darth_Carter for programming an EL client (bot) in Perl
- C Socket Tutorial - a socket tutorial for C programmers
[edit] Miscellaneous
- Eternal Lands - the official Eternal Lands web site
- Eternal Lands Forums - the official Eternal Lands forums
- Berlios EL Development Page - EL's development site on Berlios
- Google - everyone's best friend ;)
[edit] Acknowledgements
The following people were either involved directly in the creation of this Wiki page or indirectly (by sharing their code):
- Placid - for providing the webserver, Wiki and plenty of content
- Vegar - C code examples and the majority of the page's content
- robotbob - for volunteering to document the protocol in the first place
- LabRat - for providing a lot of required information
- Nook1e, benfish and Molime - for providing rare items so we could obtain the image IDs
- Entropy and Roja - for providing the game
- Mediawiki - excellent open source Wiki software

