Once a connection is made the caller sends a connection string; which looks like:
"CHAT:<chat name>\n<ip address><port>".
The sprintf syntax is:
"CHAT:%s\n%s%-5u"
The port must be 5 characters, padded on the right side with spaces. Once this string has been sent it waits for a response from the other side. If a "NO" is received the call is cancelled. If the call was accepted the string
"YES:<chat name>\n"
is received. Next the MM version number is sent out. The version number sending is not required and can happen independently of the connection. It's just a convenient place to do it.
When a socket call is detected it accepts the socket then waits for the "CHAT:" string to be send from the caller. If the receiver wishes to deny the call, the string "NO" needs to be sent back to the caller. To accept the call, the string
"YES:<chat name>\n"
is sent back. That's all the receiver has to do. Next the MM version number is sent out. This is not required.
A chat data block looks like this:
<block ID byte><data><end of data byte>
All data dealing with needs to follow this format with a couple exceptions. The connection process doesn't use the data blocks and the file transfer blocks are a fixed size and don't need the <end of data> byte.
Below is a list of the <block ID> values:
#define CHAT_NAME_CHANGE 1
#define CHAT_REQUEST_CONNECTIONS 2
#define CHAT_CONNECTION_LIST 3
#define CHAT_TEXT_EVERYBODY 4
#define CHAT_TEXT_PERSONAL 5
#define CHAT_TEXT_GROUP 6
#define CHAT_MESSAGE 7
#define CHAT_DO_NOT_DISTURB 8
#define CHAT_SEND_ACTION 9
#define CHAT_SEND_ALIAS 10
#define CHAT_SEND_MACRO 11
#define CHAT_SEND_VARIABLE 12
#define CHAT_SEND_EVENT 13
#define CHAT_SEND_GAG 14
#define CHAT_SEND_HIGHLIGHT 15
#define CHAT_SEND_LIST 16
#define CHAT_SEND_ARRAY 17
#define CHAT_SEND_BARITEM 18
#define CHAT_VERSION 19
#define CHAT_FILE_START 20
#define CHAT_FILE_DENY 21
#define CHAT_FILE_BLOCK_REQUEST 22
#define CHAT_FILE_BLOCK 23
#define CHAT_FILE_END 24
#define CHAT_FILE_CANCEL 25
#define CHAT_PING_REQUEST 26
#define CHAT_PING_RESPONSE 27
#define CHAT_PEEK_CONNECTIONS 28
#define CHAT_PEEK_LIST 29
#define CHAT_SNOOP 30
#define CHAT_SNOOP_DATA 31
The <end of data> byte is 255:
#define CHAT_END_OF_COMMAND 255
| CHAT_NAME_CHANGE | 1 |
| CHAT_REQUEST_CONNECTIONS | 2 |
| CHAT_CONNECTION_LIST | 3 |
| CHAT_TEXT_EVERYBODY | 4 |
| CHAT_TEXT_PERSONAL | 5 |
| CHAT_TEXT_GROUP | 6 |
| CHAT_MESSAGE | 7 |
| CHAT_DO_NOT_DISTURB | 8 |
| CHAT_SEND_ACTION | 9 |
| CHAT_SEND_ALIAS | 10 |
| CHAT_SEND_MACRO | 11 |
| CHAT_SEND_VARIABLE | 12 |
| CHAT_SEND_EVENT | 13 |
| CHAT_SEND_GAG | 14 |
| CHAT_SEND_HIGHLIGHT | 15 |
| CHAT_SEND_LIST | 16 |
| CHAT_SEND_ARRAY | 17 |
| CHAT_SEND_BARITEM | 18 |
| CHAT_VERSION | 19 |
| CHAT_FILE_START | 20 |
| CHAT_FILE_DENY | 21 |
| CHAT_FILE_BLOCK_REQUEST | 22 |
| CHAT_FILE_BLOCK | 23 |
| CHAT_FILE_END | 24 |
| CHAT_FILE_CANCEL | 25 |
| CHAT_PING_REQUEST | 26 |
| CHAT_PING_RESPONSE | 27 |
| CHAT_PEEK_CONNECTIONS | 28 |
| CHAT_PEEK_LIST | 29 |
| CHAT_SNOOP | 30 |
| CHAT_SNOOP_DATA | 31 |
| CHAT_END_OF_COMMAND | 255 |
When a user changes their chat name the new name needs to be broadcast to all of their connections. The data block looks like:
"<CHAT_NAME_CHANGE><new name><CHAT_END_OF_COMMAND>" - "%c%s%c"
Requesting connections from another connection asks to see all the people that person has marked as public, then try to connect to all of those yourself. The request for connections is just:
"<CHAT_REQUEST_CONNECTIONS><CHAT_END_OF_COMMAND>" - "%c%c"
Need to put all the IPs and port numbers of your public connections in a comma delimited string and send them back as a connection list.
"<CHAT_CONNECTION_LIST><ip addresses and ports><CHAT_END_OF_COMMAND>" - "%c%s%c"
The ip/port string looks like
"<ip address>,<port>,<ip address>,<port>, ...>"
If a connection is missing an IP address the address in the string should say:
"<Unknown>"
A sample string might looks something like this:
"28.25.102.48,4050,100.284.27.65,4000,<Unknown>,4050"
This is a result from a connection request. The IP addresses and port numbers need to be parsed out of the string and an attempt made to connect them. See CHAT_REQUEST_CONNECTIONS for the format of the string.
<CHAT_TEXT_EVERYBODY><text to send><CHAT_END_OF_COMMAND>
Used to send some chat text to everybody. All the text you want to be displayed needs to be generated on the sender's side, including the line feeds and the "<chat name> chats to everybody" string. "\n%s chats to everybody, '%s'\n" The reason for sending all the text rather than having the receiving side generate the "<chatname> chats to everybody" is just to be polite and lift some of the burden from the receiver's side. The receiver can just print the string that comes in and isn't having to constantly build all the chat strings. The data should only be sent to chat connections are you not ignoring.
If the chat connection isn't being ignored, you simply print the string. If you have any connections marked as being served you need to echo this string to those connections. Or if this is coming from a connection being served, you need to echo to all your other connections. This allows people who cannot connect directly to each other to connect with a 3rd person who can connect to both and be a server for them.
<CHAT_TEXT_PERSONAL><text to send><CHAT_END_OF_COMMAND>
This works the same way as CHAT_TEXT_EVERYBODY as far as what you need to send. The text should obviously be changed so the person receiving knows this was a personal chat and not broadcast to everybody.
"\n%s chats to you, '%s'\n"
Just print the string that comes in if you aren't ignoring this connection.
<CHAT_TEXT_GROUP><group><text to send><CHAT_END_OF_COMMAND>
Used when you send text to a specific group of connections. Works basically the same as the other text commands. The group name is a 15 character string. If must be 15 characters long - pad it on the right with spaces to fill it out.
"\n%s chats to the group, '%s'\n"
Just print the string that comes in if you aren't ignoring this connection.
<CHAT_MESSAGE><message><CHAT_END_OF_COMMAND>
This is used to send a message to another chat connection. An example of this is when you try to send a command (action, alias, etc. ...) to another chat connection and they don't have you flagged as accepting commands. In that case a chat message is sent back to the sender telling them that command are not being accepted. To let the other side know the message is generated from the chat program it is a good idea to make the string resemble something like:
"\n<CHAT> %s is not allowing commands.\n"
Just print the message string.
<CHAT_VERSION><version string><CHAT_END_OF_COMMAND>
<CHAT_FILE_START><filename,length><CHAT_END_OF_COMMAND>
This is sent to start sending a chat connection a file. The filename should be just the filename and not a path. Length is the size of the file in bytes.
First should check to make sure you are allowing files from this connection. Make sure the filename is valid and that the length was trasnmitted. MM by default won't allow you to overwrite files; which keeps people from messing with file already in your directory. If for any reason the data isn't valid or you don't want to accept files from this person a CHAT_FILE_DENY should be sent back to abort the transfer. If you want to continue with the transfer you need to start it off by requesting a block of data with CHAT_FILE_BLOCK_REQUEST.
<CHAT_FILE_DENY><message><CHAT_END_OF_COMMAND>
This is used when a CHAT_FILE_START has been received and you want to prevent the transfer from continuing. <message> is a string telling the reason it was denied. For example, if the file already existed you might deny it with:
"File already exists."
Print the deny message. Deal with cleaning up any files you opened when you tried to start the transfer.
<CHAT_FILE_BLOCK_REQUEST><CHAT_END_OF_COMMAND>
Sent to request the next block of data in a transfer.
Need to create a file block to be sent back. If the end of file is reached need to send a CHAT_FILE_END close up the files and let the user know it is done sending.
<CHAT_FILE_BLOCK><block of data>
A file block is 500 bytes. A file block is ALWAYS 500 bytes so no CHAT_END_OF_COMMAND is needed.
The receiver needs to keep track of the number of bytes written to properly write the last block of data. If you keep track of the bytes written you know when to expect that last block that probably doesn't have a full 500 bytes to be saved. File transfers are receiver driven, so for each block of data you accept, you need to send another CHAT_FILE_BLOCK_REQUEST back out to get more data.
<CHAT_FILE_END><CHAT_END_OF_COMMAND>
Close up your files and be done with it.
<CHAT_FILE_CANCEL><CHAT_END_OF_COMMAND>
Either side can send this command to abort a file transfer in progress.
<CHAT_PING_REQUEST><timing data><CHAT_END_OF_COMMAND>
The timing data is really up to the ping requester. MM uses the number of clocks ticks elapsed to determine the length of a ping. Your timing data is sent right back to you from the side receiving the ping so you can use anything you want to indicate the length of a ping. The reason for sending the ping data was to keep the code simple. If rather than sending the data you keep track of it in your program, you'd have to build some sort of object that keeps track of all the pings you have sent and their start times. This way you don't have to keep track of the start time, it's given right back to you when the ping is returned.
When a request is received send the timing data right back in a CHAT_PING_RESPONSE block.
<CHAT_PING_RESPONSE><timing data><CHAT_END_OF_COMMAND>
The timing data is just the data that was sent to you with the CHAT_PING_REQUEST.
<CHAT_PEEK_CONNECTIONS><CHAT_END_OF_COMMAND>
This is sent to request all the public connections the other person has. Unlike the Request Connections command, this one just returns a list to be viewed. It doesn't connect you to them all.
Sends back:
<CHAT_PEEK_LIST><address list><CHAT_END_OF_COMMAND>
It's been too long since I wrote this to remember why I changed it, but this uses a slightly different format than the Request Connection list. The address should look like:
"<address>~<port>~<name>"
Like the Request Connections, and unknown address should be returned as "<Unknown>" for consistency. Same address list would look something like:
"204.285.28.18~4050~Omawarisan~<Unknown>~4050~Baalzebul"
This is the result returned from CHAT_PEEK_CONNECTIONS. The format for the data is above.
<CHAT_SNOOP><CHAT_END_OF_COMMAND>
This sends a request to the other client to start/stop snooping.
The receiver needs to be in charge of allowing the snooping. You probably don't want anybody able to snoop you at will. When being snooped, all data seen on the screen should be send to the person snooping. This gives the person requesting the snoop a view through the eyes of the person being snooped. Data is sent with the CHAT_SNOOP_DATA command.
<CHAT_SNOOP_DATA><text><CHAT_END_OF_COMMAND>
The text to send should just be the same text being seen on the person who is being snooped's side. The current text color should probably be embedded at the start of the line to ensure that the snooped text comes out the right color initially on the receiving side.
MM indicates the snooped text by prefacing ">>" at the start of each line printed from the remote side (bright green). This allows the user to tell which lines are coming from the snooped connection, and which are normal text from their connection. This is really up to you how you want it to appear for the user.
(End of MudMaster Chat Spec)