-
Notifications
You must be signed in to change notification settings - Fork 11
Support new socket parsing
B4D can read incoming sockets from the Dofus server. While creating you API, you might want to be able to parse sockets that are not parsed yet. I'll introduce you how I reversed engineer chat message sockets. Before going any futher, you'll need to understand the general structure of a dofus socket.
Dofus sockets have the following structure:
ID | Flags | Length | Payload | 0x00 0x00 | Checksum ? |
---|---|---|---|---|---|
1 | 1 | 2 - 3 | * | 2 | 4 |
The only field you'll need to reverse engineer is the payload, you don't have to parse the rest.
- Download and install WireShark
- Start Dofus and connect to a server
- Start WireShark and start listening
The objective of this step is to identify, in WireShark, the sockets you are looking for.
- Open a terminal and paste the following command to identify the dofus server you are connected to:
netstat -a -p TCP -n | findstr :5555
- In WireShark, add an ip filter to see the incoming sockets from the server:
ip.src == 172.65.232.71
- In the dofus chat, copy a word that someone said. eg: "Kamas".
- In WireShark, add a content filter to see the sockets containing the word.
ip.src == 172.65.232.71 && frame contains "Kamas"
- In WireShark, identify the socket id (0x03 in V2.59).
- In WireShark, add a byte filter to see the sockets starting with this id:
ip.src == 172.65.232.71 && data[0] == 03
The displayed sockets are now chat messages only. Those are the one you'll need to reverse engineer and parse.
This step is the most complicated and long one. You'll have to find the payload structure from scratch without any documentation. There are multiple ways of doing it. Here is a list of tips you can do:
- Start by collecting a lot of sockets in WireShark and save the records in a local file. You can then work from this file in offline without Dofus.
- In the recorded sockets, find the common bytes to identify the delimiters.
- You can also force the event to happend and look for the location of the expected data in the socket.
Be patient and try hard, ask for help if needed...
The payload structure I found for chat messages is the following:
Channel ID | Text length | Text | 0x60 | Id something | Length something | Something | Something else | Pseudo length | Pseudo |
---|---|---|---|---|---|---|---|---|---|
1 | 2 | * | 1 | 3 | 2 | * | 8 | 2 | * |
The relevant data are the channel id, the text and the pseudo of the player.
The correspondance for channel id is the following:
General | Team | Guild | Allies | Group | Business | Recruitment | Private | Information | Fight | Promotion | Kolizeum | Community |
---|---|---|---|---|---|---|---|---|---|---|---|---|
0x04 | 0x09 | ? | ? | ? | 0x05 | 0x06 | 0x09 | ? | ? | ? | ? | ? |
- In the package
fr.B4D.socket.event
, create a new event class extending theDofusEvent
class. This class will represent your dofus event once parsed. It must be a simple class containing attributs and the getters. The attributs can only be defined in the constructor.
/**
* The {@code ChatMessageEvent} class represents a message in the chat.<br><br>
* A message is defined by a pseudo, a channel and a text.
*
* @author Lucas
*
*/
public class ChatMessageEvent extends DofusEvent {
/**
* Channel of the message.
*/
private Channel channel;
/**
* Text of the message.
*/
private String text;
/**
* Pseudo of the player which send the message.
*/
private String pseudo;
/**
* Constructor of the {@code ChatMessageEvent} class with a pseudo, a channel and a text to send.
* @param channel - Channel of the message.
* @param text - Text of the message.
* @param pseudo - Pseudo of the player.
*/
public ChatMessageEvent(Channel channel, String text, String pseudo) {
this.channel = channel;
this.text = text;
this.pseudo = pseudo;
}
/**
* Returns the text of the message.
* @return Text of the message.
*/
public String getText() {
return text;
}
/**
* Returns the channel of the message.
* @return Channel of the message.
*/
public Channel getChannel() {
return channel;
}
/**
* Returns the pseudo of the player.
* @return Pseudo of the player.
*/
public String getPseudo() {
return pseudo;
}
}
- In the package
fr.B4D.socket.parse
, create a new parse class extending theSocketParser<ChatMessageEvent>
class.
/**
* The {@code ChatMessageSocketParser} class is used to parse a socket relative to chat messages.
*
* @author Lucas
*
*/
public class ChatMessageSocketParser extends SocketParser<ChatMessageEvent>{
@Override
public ChatMessageEvent parse(DofusSocket dofusSocket) throws B4DException {
//TODO
}
}
- Implement the
parse
method based on the payload structure you identified in step 2. Once all the fields have been identified, you can create the event and return it.
DofusSocketIterator iterator = new DofusSocketIterator(dofusSocket);
Channel channel = Channel.fromByte(iterator.getNextByte());
Integer textLenght = iterator.getNextSocketElement(2).asSmallEndian();
String text = iterator.getNextSocketElement(textLenght).asString();
iterator.skip(4);
Integer lengthSomething = iterator.getNextSocketElement(2).asSmallEndian();
iterator.skip(lengthSomething);
iterator.skip(8);
Integer pseudoLenght = iterator.getNextSocketElement(2).asSmallEndian();
String pseudo = iterator.getNextSocketElement(pseudoLenght).asString();
return new ChatMessageEvent(channel, text, pseudo);
- In the class
fr.B4D.socket.DofusSocketType
, map the socket id with the parser by adding a value.
/**
* Socket representing a message in the chat.
*/
CHAT_MESSAGE_SOCKET((byte) 0x03, ChatMessageSocketParser.class),
At this step, all the sockets starting with the id 0x03
will be parsed using the parser ChatMessageSocketParser
.
The event will be created and added to the event store.
When coding an API or a program, you are now able to wait for your event using the fr.B4D.socket.store.EventStore
class.
The following call is blocking the main thread:
ChatMessageEvent event = EventStore.getInstance().waitForEvent(ChatMessageEvent.class, 1000);
You can also register listeners and wait for a given number of events:
EventStore.getInstance().addEventHandler(Message.class, new EventHandler<Message>() {
@Override
public boolean onEventReceived(Message socketEvent) {
// TODO
return false;
}
});
EventStore.getInstance().read(Message.class, 100);
B4D
Utilisation
Contribution