Jump to content
Eternal Lands Official Forums
Wytter

New trade protocol

Recommended Posts

In 1.0.2 trading will change slightly, i.e. to make quantities 32-bit instead of 16 bit, and to reduce the chance of scamming.

 

When you want to trade with a player it will be the normal procedure:

 

str[0]=TRADE_WITH;
*((Uint16*)(str+1)) = 5;
*((Uint32*)(str+3)) = player_id;

 

This will cause the other player to get the same trade request string as with 1.0.1.

 

str[0]=127+c_yellow2;
*((Uint16*)(str+1))=length;
str+3="<player_name> wants to trade with you. Use the trade icon then click on him/her in order to accept the trade.";

 

With multi-channel chat it will be sent to CHAT_SERVER.

 

Once both parties have accepted the server will send GET_YOUR_TRADEOBJECTS in order to make sure that the items are in sync:

 

str[0]=GET_YOUR_TRADEOBJECTS;
*((Uint16*)(str+1)) = length;
*((Uint8*)(str+3)) = no_items;

ptr=str+4;
for(i=0;i<no_items;i++){
      *((Uint8*)(ptr+i*8+6))=pos;//The position in the items list
      *((Uint16*)(ptr+i*8)) = item_list[pos].image_id;
      *((Uint32*)(ptr+i*8+2)) = item_list[pos].quantity;
      *((Uint8*)(ptr+i*8+7)) = item_list[pos].flags;
}

 

If you are near a storage, the server will also send the storage categories (see http://www.eternal-lands.com/forum/index.php?showtopic=16313 for more info):

 

*((Uint8*)(str+0)=STORAGE_LIST;
*((Uint16*)(str+1)=length;
*((Uint8*)(str+3)=no_categories;
ptr=str+4;
for(i=0;i<length-3;i++){
     *((Uint8*)(ptr+0))=category_id;
      ((Uint8*)(ptr+1))=category_name; //terminated by \0
      increase(ptr);
}

 

If you've previously opened a category it'll send you the items in that category as well:


*((Uint8*)(str+0))=STORAGE_ITEMS;
*((Uint16*)(str+1))=length;
*((Uint8*)(str+3))=no_items;
if(no_items==255){
     //It's just an update containing 1 item for the current category
     *((Uint8*)((str+4))=category;
     *((Uint16*)(str+5))=image_id;
     *((Uint32*)(str+7))=quantity;
     *((Uint8*)(str+11))=storage_pos;//Position in the server-side storage
} else {
      *((Uint8*)(str+4))=category;
      ptr=str+5;
      for(i=0;i<no_items;i++){
              *((Uint16*)(ptr))=image_id;
              *((Uint32*)(ptr+2))=quantity;
              *((Uint8*)(ptr+6))=storage_pos;
              increase(ptr);
      }
}

 

Next you will recieve the name of the trade partner. This has changed slightly, since the server will actually tell you whether you have storage available or not (a bit redundant):

str[0]=GET_TRADE_PARTNER_NAME;
*((Uint16*)(str+1))=length;
*((Uint8*)(str+3))=storage_available;//0 for FALSE, 1 for TRUE
((Uint8 *) str+4)="<trade partner name>";//Terminated by \0

 

Once this is in place, the trade session can begin. You add an item to trade using this:

 

str[0]=PUT_OBJECT_ON_TRADE;
*((Uint16*)(str+1))=7
*((Uint8*)(str+3))=ITEM_INVENTORY || ITEM_BANK; //Depending on whether you're adding an item from your bank or from your inventory.
*((Uint8*)(str+4))=pos;//The position in either your inventory or your bank
*((Uint32*)(str+5))=quantity;

 

Adding an object to the trade will either remove it from your inventory or storage or just reduce the quantity.

 

If the quantity is 0 after adding the item, it will be completely removed from your inventory:

str[0]=REMOVE_ITEM_FROM_INVENTORY;
*((Uint16*)(str+1))=2;
*((Uint8*)(str+3))=pos;

 

If the quantity is >0 after adding the item the inventory item will be updated

str[0]=GET_NEW_INVENTORY_ITEM;
*((Uint16*)(str+1))=9;
*((Uint16*)(str+3))=image_id;
*((Uint32*)(str+5))=quantity;
*((Uint8*)(str+9))=pos;
*((Uint8*)(str+10))=flags;

 

If it's a storage item and you are in the same category as the item, the following will be sent:

str[0]=STORAGE_ITEMS;
*((Uint16*)(str+1))=10;
*((Uint8*)(str+3))=255;//Indicates an update
*((Uint8*)(str+4))=category;
*((Uint16*)(str+5))=image_id;
*((Uint32*)(str+7))=quantity;
*((Uint8*)(str+11))=pos;

 

To remove a tradeobject do the following:

str[0]=REMOVE_ITEM_FROM_TRADE;
*((Uint16*)(str+1))=6;
*((Uint8*)(str+3))=pos;
*((Uint32*)(str+4))=quantity;

 

This will add an item to the inventory or storage:

 

str[0]=GET_NEW_INVENTORY_ITEM;
*((Uint16*)(str+1))=9;
*((Uint16*)(str+3))=image_id;
*((Uint32*)(str+5))=quantity;
*((Uint8*)(str+9))=pos;
*((Uint8*)(str+10))=flags;

 

The storage item will only be sent if you are in that category.

str[0]=STORAGE_ITEMS;
*((Uint16*)(str+1))=10;
*((Uint8*)(str+3))=255;//Indicates an update
*((Uint8*)(str+4))=category;
*((Uint16*)(str+5))=image_id;
*((Uint32*)(str+7))=quantity;
*((Uint8*)(str+11))=pos;

 

If you want to see the item string for that given object, do the following:

 

#define YOUR_TRADE_LIST 0
#define OTHERS_TRADE_LIST 1

str[0]=LOOK_AT_TRADE_ITEM;
*((Uint16*)(str+1))=length;
*((Uint8*)(str+3))=pos;
*((Uint8*)(str+4))=YOUR_TRADE_LIST || OTHERS_TRADE_LIST;//Whether you want to see the item from your or the others trade list.

 

Whenever a trade object is added the following will be sent to the client:

 

str[0]=GET_TRADE_OBJECT;
*((Uint16*)(str+1))=10;
*((Uint16*)(str+3))=image_id;
*((Uint32*)(str+5))=quantity;
*((Uint8*)(str+9))=type;
*((Uint8*)(str+10))=pos;
*((Uint8*)(str+11))=YOUR_TRADE_LIST || OTHERS_TRADE_LIST;

 

If an object is removed the following will be sent:

 

str[0]=REMOVE_TRADE_OBJECT;
*((Uint16*)(str+1))=7;
*((Uint32*)(str+3))=quantity;
*((Uint8*)(str+7))=pos;
*((Uint8*)(str+8))=YOUR_TRADE_LIST || OTHERS_TRADE_LIST;

 

To accept the trade, send the following to the server:

str[0]=ACCEPT_TRADE;
*((Uint16*)(str+1))=1;

If you have already accepted the trade once, you have to send the targets for all of the items as well:

 

#define ITEM_INVENTORY 1
#define ITEM_BANK 2

str[0]=ACCEPT_TRADE;
*((Uint16*)(str+1))=17;
for(i=0;i<16;i++){
      *((Uint8*)(str+3+i))=ITEM_INVENTORY || ITEM_BANK;
}

 

Whenever a trade accept is recieved by the server it will send the following to the clients:

str[0]=GET_TRADE_ACCEPT;
*((Uint16*)(str+1))=2;
*((Uint8*)(str+3))=YOUR_TRADE_LIST || OTHERS_TRADE_LIST;//Whether it's you or your trade partner that has accepted.

 

In 1.0.2 the accept works slightly different than in 1.0.1.

As soon as both parties have pressed ACCEPT_TRADE, the trade session will be locked - this means that nothing will be put on trade or removed from the trade - this will give you time to evaluate all items and see that they are correct.

Once you've evaluated the trade session and accepted it you can send another ACCEPT_TRADE, then wait for your trade partner to do the same - once this is done you will get your new items:

 

str[0]=GET_YOUR_ITEMS;
*((Uint16*)(str+1)) = length;
*((Uint8*)(str+3)) = no_items;

ptr=str+4;
for(i=0;i<no_items;i++){
      *((Uint8*)(ptr+i*8+6))=pos;//The position in the items list
      *((Uint16*)(ptr+i*8)) = item_list[pos].image_id;
      *((Uint32*)(ptr+i*8+2)) = item_list[pos].quantity;
      *((Uint8*)(ptr+i*8+7)) = item_list[pos].flags;
}

 

And the last storage category you opened will be resend:


*((Uint8*)(str+0))=STORAGE_ITEMS;
*((Uint16*)(str+1))=length;
*((Uint8*)(str+3))=no_items;
if(no_items==255){
     //It's just an update containing 1 item for the current category
     *((Uint8*)((str+4))=category;
     *((Uint16*)(str+5))=image_id;
     *((Uint32*)(str+7))=quantity;
     *((Uint8*)(str+11))=storage_pos;//Position in the server-side storage
} else {
      *((Uint8*)(str+4))=category;
      ptr=str+5;
      for(i=0;i<no_items;i++){
              *((Uint16*)(ptr))=image_id;
              *((Uint32*)(ptr+2))=quantity;
              *((Uint8*)(ptr+6))=storage_pos;
              increase(ptr);
      }
}

 

If you wish to reject the trade, send the following to the server:

str[0]=REJECT_TRADE;
*((Uint16*)(str+1))=1;

 

Whenever the server recieves a REJECT_TRADE it will send the following to the clients:

 

str[0]=GET_TRADE_REJECT;
*((Uint16*)(str+1))=2;
*((Uint8*)(str+3))=YOUR_TRADE_LIST || OTHERS_TRADE_LIST;//Whether it's you or your trade partner that has rejected.

Once rejecting your trade partner will also automatically reject the trade in order to reduce scamming.

 

When the trade has ended you'll recieve:

str[0]=GET_TRADE_EXIT;
*((Uint16*)(str+1))=1;

 

Hope this will help you.

Share this post


Link to post
Share on other sites

Did anyone try to implement this yet? And did you succeed? If you need more info, please ask - I think I added all of it, but you never know ;).

Share this post


Link to post
Share on other sites
Ugh...

*blows the dust off Nera's code

Guess I'd better get to it...

190249[/snapback]

 

My Hero!

I had just realised Nera ws gong to need updated, and your coding style is conceptually alien to me. (Im mean how to you make loops interesting if i isnt a global variable as well ;-) )

Share this post


Link to post
Share on other sites

I've tried to implement the new trade system on our bot.

The trading works, but the bot dont get the stuff from trading ;)

 

My testplayer trade for example 5 lilacs for 5gc. He get the coins, but the

bot not the 5 lilacs, and also the player have no longer the 5 lilacs.

Where are the lilacs gone ?

 

I make a little break now, maybe i find the solution some days later :icon13:

 

greetings

 

thunderous

Share this post


Link to post
Share on other sites

check that the bot isn't overloaded (yes, this is a NASTY bug. overloaded trades still go ahead and discard half the trade last I checked)

Share this post


Link to post
Share on other sites
Looks like I have to haul out the storebot code soon... erk

192517[/snapback]

Do what I do. Half implement a trade function in your bot, so that everytime you get a trade request, you get a proken pipe. Now THATS programming xD

Share this post


Link to post
Share on other sites
check that the bot isn't overloaded (yes, this is a NASTY bug. overloaded trades still go ahead and discard half the trade last I checked)

192514[/snapback]

When was the last time you checked? I thought I fixed that.

Share this post


Link to post
Share on other sites
When the trade has ended you'll recieve:

str[0]=GET_TRADE_EXIT;
*((Uint16*)(str+1))=1;

 

Hope this will help you.

 

 

 

Thank you for the details.. very much

Share this post


Link to post
Share on other sites

I saw the question asking if anyone has succeeded with the implementation.. Charn is up and running!! ready for the update :D

Share this post


Link to post
Share on other sites

I'm seeing an extra byte at the end of this from the server which throws off str+10

 

Is it just the beer, or is this still a work in progress? My bot code wants to know.

Thanks :P

 

 

str[0]=GET_TRADE_OBJECT;
*((Uint16*)(str+1))=9;
*((Uint16*)(str+3))=image_id;
*((Uint32*)(str+5))=quantity;
*((Uint8*)(str+9))=pos;
*((Uint8*)(str+10))=YOUR_TRADE_LIST || OTHERS_TRADE_LIST;

Share this post


Link to post
Share on other sites
I'm seeing an extra byte at the end of this from the server which throws off str+10

 

Is it just the beer, or is this still a work in progress? My bot code wants to know.

Thanks  :P

195580[/snapback]

The trading with the new clients works. So now you just need to implement what was described here and also shown in the new client code in CVS.

Share this post


Link to post
Share on other sites

Damn, you're right - it's:

 

str[0]=GET_TRADE_OBJECT;
*((Uint16*)(str+1))=10;
*((Uint16*)(str+3))=image_id;
*((Uint32*)(str+5))=quantity;
*((Uint8*)(str+9))=type;
*((Uint8*)(str+10))=pos;
*((Uint8*)(str+11))=YOUR_TRADE_LIST || OTHERS_TRADE_LIST;

 

Where the type is determining whether it should be in the storage or in the inventory.

Share this post


Link to post
Share on other sites
Damn, you're right - it's:

 

str[0]=GET_TRADE_OBJECT;
*((Uint16*)(str+1))=10;
*((Uint16*)(str+3))=image_id;
*((Uint32*)(str+5))=quantity;
*((Uint8*)(str+9))=type;
*((Uint8*)(str+10))=pos;
*((Uint8*)(str+11))=YOUR_TRADE_LIST || OTHERS_TRADE_LIST;

 

Where the type is determining whether it should be in the storage or in the inventory.

195705[/snapback]

 

 

It's okay, I'm mostly looking for concept changes, and your post helped a lot. Thanks much. last time it was Tcl, this time I think I'll try Lisp! I like a challenge :confused:

Share this post


Link to post
Share on other sites

So, after both parties have accepted two times and the trade is to be accepted.. what happens when the person recieving the goods can not carry them all?

 

Is there a message that says trade failed?

Share this post


Link to post
Share on other sites
So, after both parties have accepted two times and the trade is to be accepted.. what happens when the person recieving the goods can not carry them all?

 

Is there a message that says trade failed?

202030[/snapback]

Yeah, RAW_TEXT I would assume, saying:

"Trade session failed. <Playername> could not carry all the items"

Share this post


Link to post
Share on other sites

str[0]=GET_TRADE_OBJECT;
*((Uint16*)(str+1))=10;
*((Uint16*)(str+3))=image_id;
*((Uint32*)(str+5))=quantity;
*((Uint8*)(str+9))=type;
*((Uint8*)(str+10))=pos;
*((Uint8*)(str+11))=YOUR_TRADE_LIST || OTHERS_TRADE_LIST;

 

 

What is

*((Uint8*)(str+9))=type;

 

identifing? Is it the same as the item flags?

Share this post


Link to post
Share on other sites
Damn, you're right - it's:

 

str[0]=GET_TRADE_OBJECT;
*((Uint16*)(str+1))=10;
*((Uint16*)(str+3))=image_id;
*((Uint32*)(str+5))=quantity;
*((Uint8*)(str+9))=type;
*((Uint8*)(str+10))=pos;
*((Uint8*)(str+11))=YOUR_TRADE_LIST || OTHERS_TRADE_LIST;

 

Where the type is determining whether it should be in the storage or in the inventory.

195705[/snapback]

Share this post


Link to post
Share on other sites

If you want to see the item string for that given object, do the following:

 

#define YOUR_TRADE_LIST 0
#define OTHERS_TRADE_LIST 1

str[0]=LOOK_AT_TRADE_ITEM;
*((Uint16*)(str+1))=length;
*((Uint8*)(str+3))=pos;
*((Uint8*)(str+4))=YOUR_TRADE_LIST || OTHERS_TRADE_LIST;//Whether you want to see the item from your or the others trade list.

 

You never mentioned what the reply to this looks like. Also, Since the image ID's are not unique, how are we to know what item is really in a given slot? do a look at trade item in that slot.. ok, then we get the reply but the reply does not say what slot the reply is for. So, if I send a look at every slot in inventory to determine what each item is (by description) then when the replies come back, there is no way for me to map them to the correct item slot.

 

Since TCPIP messaging is async, there is not even a guarentee that my messages will get to the server in order. So I am having major difficulty telling the difference between different books which use the same image and items like LE and ELE.

 

If there is currently no solution to this, can I propose implementing a UNIQUE id for each item? then also having an imageID. And add the unique ID to the all packets dealing with inventory and looking at an item?

 

OR, add the slot to the reply for LOOK_AT_TRADE_ITEM (you would also have to add who's list, storage, inventory, bag, etc).

 

Any help would be greatly appreciated...

Share this post


Link to post
Share on other sites
You never mentioned what the reply to this looks like.  Also, Since the image ID's are not unique, how are we to know what item is really in a given slot?  do a look at trade item in that slot.. ok, then we get the reply but the reply does not say what slot the reply is for.  So, if I send a look at every slot in inventory to determine what each item is (by description) then when the replies come back, there is no way for me to map them to the correct item slot.

 

Since TCPIP messaging is async, there is not even a guarentee that my messages will get to the server in order.  So I am having major difficulty telling the difference between different books which use the same image and items like LE and ELE.

 

If there is currently no solution to this, can I propose implementing a UNIQUE id for each item? then also having an imageID.  And add the unique ID to the all packets dealing with inventory and looking at an item?

 

OR, add the slot to the reply for LOOK_AT_TRADE_ITEM (you would also have to add who's list, storage, inventory, bag, etc).

 

Any help would be greatly appreciated...

209402[/snapback]

TCP/IP is guaranteed to that the packets will be received in the same order as sent. UDP is the protocol where the packets might be processed in a different order.

Share this post


Link to post
Share on other sites

Create an account or sign in to comment

You need to be a member in order to leave a comment

Create an account

Sign up for a new account in our community. It's easy!

Register a new account

Sign in

Already have an account? Sign in here.

Sign In Now

  • Recently Browsing   0 members

    No registered users viewing this page.

×