Welcome!

Join our community of MMO enthusiasts and game developers! By registering, you'll gain access to discussions on the latest developments in MMO server files and collaborate with like-minded individuals. Join us today and unlock the potential of MMO server development!

Join Today!

[GUIDE/CODING] The need to know functions of Habbo emulators

◝(⁰▿⁰)◜Smile◝ (⁰▿⁰)◜
Developer
Joined
May 29, 2007
Messages
2,167
Reaction score
898
Cleaner Java B64 Encoding & Decoding:
Code:
import java.nio.charset.Charset;

public class Encoding {

    private static final Charset US_ASCII = Charset.forName("US-ASCII");

    public static int decodeB64(String value) {
        int result = 0;

        byte[] asciiBytes = value.getBytes(US_ASCII);
        for (int i = 0; i < asciiBytes.length; i++) {
            result += ((asciiBytes[i] - 0x40) << 6 * (asciiBytes.length - 1 - i));
        }

        return result;
    }

    public static String encodeB64(int value, int length) {
        int subValue;

        StringBuilder builder = new StringBuilder();
        for (int i = 0; i < length; i++) {
            subValue = (value >> 6 * (length - 1 - i)) & 0x3f;
            builder.append((char) (subValue + 0x40));
        }

        return builder.toString();
    }

    public static String encodeB64(int value) {
        return encodeB64(value, 2);
    }

}

Unit testing:
Code:
import org.junit.Test;
import static org.junit.Assert.*;

public class EncodingTest {

    // Encoding Tests

    @[I][B][URL="http://forum.ragezone.com/members/196303.html"]TesT[/URL][/B][/I]
    public void encoding0ReturnsAtAt() {
        String encodeResult = Encoding.encodeB64(0);

        assertEquals("@@", encodeResult);
    }

    @[I][B][URL="http://forum.ragezone.com/members/196303.html"]TesT[/URL][/B][/I]
    public void encoding1ReturnsAtA() {
        String encodeResult = Encoding.encodeB64(1);

        assertEquals("@A", encodeResult);
    }

    @[I][B][URL="http://forum.ragezone.com/members/196303.html"]TesT[/URL][/B][/I]
    public void encoding2ReturnsAtB() {
        String encodeResult = Encoding.encodeB64(2);

        assertEquals("@B", encodeResult);
    }

    @[I][B][URL="http://forum.ragezone.com/members/196303.html"]TesT[/URL][/B][/I]
    public void encoding128ReturnsBAt(){
        String encodeResult = Encoding.encodeB64(128);

        assertEquals("B@", encodeResult);
    }

    @[I][B][URL="http://forum.ragezone.com/members/196303.html"]TesT[/URL][/B][/I]
    public void encoding312ReturnsDx(){
        String encodeResult = Encoding.encodeB64(312);

        assertEquals("Dx", encodeResult);
    }

    // Decoding Tests

    @[I][B][URL="http://forum.ragezone.com/members/196303.html"]TesT[/URL][/B][/I]
    public void decodeAtAtReturns0() {
        int decodeResult = Encoding.decodeB64("@@");

        assertEquals(0, decodeResult);
    }

    @[I][B][URL="http://forum.ragezone.com/members/196303.html"]TesT[/URL][/B][/I]
    public void decodeAtAReturns1() {
        int decodeResult = Encoding.decodeB64("@A");

        assertEquals(1, decodeResult);
    }

    @[I][B][URL="http://forum.ragezone.com/members/196303.html"]TesT[/URL][/B][/I]
    public void decodeAtBReturns2() {
        int decodeResult = Encoding.decodeB64("@B");

        assertEquals(2, decodeResult);
    }

    @[I][B][URL="http://forum.ragezone.com/members/196303.html"]TesT[/URL][/B][/I]
    public void decodeBAtReturns128(){
        int decodeResult = Encoding.decodeB64("B@");

        assertEquals(128, decodeResult);
    }

    @[I][B][URL="http://forum.ragezone.com/members/196303.html"]TesT[/URL][/B][/I]
    public void decodeDxReturns312(){
        int decodeResult = Encoding.decodeB64("Dx");

        assertEquals(312, decodeResult);
    }

}

VL64 With comments and explained.
Code:
    public static int decodeVL64(String value) {
        char[] charArray = value.toCharArray();

        int result;
        boolean isNegative = (charArray[0] & 4) == 4; // (charArray[0] >> 2 & 1) == 1
        int totalBytes = (charArray[0] >> 3) & 7; // 3 Bits of length data. -> VL64 Max size = 6 bytes. [0 excluded]

        // Value in byte 1 -> It contains 2 bits of data. (Last 2 bits)
        result = charArray[0] & 3;

        int shiftAmount = 2;
        for (int i = 1; i < totalBytes; i++) {
            result |= (charArray[i] & 0x3f) << shiftAmount; // Mask with 63 [0-63] & Shift by 6 bits (64).
            shiftAmount += 6; // Add 6 bits to the shift
        }

        if (isNegative) {
            result = -result; // Invert if negative.
        }

        return result;
    }

    public static String encodeVL64(int value) {
        byte[] vlEncoded = new byte[6];
        byte byteCount = 1; // We always have 1 byte.
        int absoluteValue = Math.abs(value);

        vlEncoded[0] = (byte) (0x40 + (absoluteValue & 3)); // Move 2 bits inside the first byte.
        vlEncoded[0] |= value < 0 ? 4 : 0; // Negative mask -> 3rd right bit = negative 1 = yes - 0 = no.

        // Shift 2 bits at start, then shift 6 until the value becomes 0.
        for (absoluteValue >>= 2; absoluteValue != 0; absoluteValue >>= 6) {
            vlEncoded[byteCount++] = (byte) (0x40 + (absoluteValue & 0x3f)); // Shift value inside the byte.
        }

        // Put the length inside the byte
        vlEncoded[0] |= byteCount << 3; // Shift to the left by 3 bits so we keep the negative mask & 2 bits.

        return new String(vlEncoded, 0, byteCount, US_ASCII);
    }
 
Last edited:
Developer
Developer
Joined
Dec 11, 2010
Messages
2,955
Reaction score
2,685
Habbo 2001 "v1" client

This version of Habbo's protocol is using FUSE 0.1.0 standards, for those wondering, the protocol with the B64 and VL64 encoding is called FUSE 0.2.0.

So with the 0.1.0 protocol, the main packets built like this:

Code:
# [HEADER] + [CHAR 13] + [DATA] + [CHAR 13]##

The prefixed # and suffixed [CHAR 13]## is what tells the client the start and the end of a packet, much like how all FUSE 0.2.0 packets end with [CHAR 1], because they don't send back a length as a prefix (modern Habbo now sends a length of the packet to the client).

Example taken from fuselight source (written by Aapo Kyrola while working at Sulake):

Code:
/**
  * Processes and sends a fuse message to the client:
  * # @type \r data\r##
  */
public void processFuseMessage(String type, String data) {
	try {
		StringBuffer sb = new StringBuffer(7 + type.length() + data.length());
		/* For security reasons don't accept # inside messages */
		data = data.replace('#', '*');
		type = type.replace('#', '*');
		
		/* Compose fuse-protocol-compatible message */
		sb.append("#");
		sb.append(type);
		sb.append("\r");
		sb.append(data);
		sb.append("\r##");
		out.println(sb.toString()); 
	} catch (Exception e) {
		Log.error(e);    
	}
}

Protocol tips for Habbo Hotel 2001

Loading rooms

So since I joined the scene I had a bug where trying to load rooms would always cause an infinite loading screen, and it didn't occur to me until two months ago that the packet OBJECTS which is responsible for this needs a space before the header.

So this is what needs to be sent:

Code:
 OBJECTS WOLRLD 0 model_a

There needs to be a space before OBJECTS otherwise it will not work, and the loading screen for rooms will be infinite.

Instant messaging

The instant messaging packet on the Habbo console for the 2001 version is ever so slightly different to Blunk v5 that if you tried to sent the structure from Blunk on login while using the v1 client, your client would break and your login will be stuck, and you'll receive a Shockwave error.

Basically the version 1 structure for messenger console is:

Code:
"MESSENGER_MSG"
[13]
message ID
[13]
from user ID
[13]
"[]"
[13]
"dd-MM-yyyy hh:MM:ss" (the time when message was sent format) +
[13] 
Message contents
[13]
Figure code of person who sent message
[13]
""

The difference from the v5 structure is that the [] was required in the third parameter, which contains the list of ID's who are receiving the message (because remember you could send a message to multiple friends).

Example:

Code:
[13, 732, 93, 23]

Figure codes

Here's the default figure of a new user:

Code:
sd=001/0&hr=001/255,255,255&hd=002/255,204,153&ey=001/0&fc=001/255,204,153&bd=001/255,204,153&lh=001/255,204,153&rh=001/255,204,153&ch=001/232,177,55&ls=001/232,177,55&rs=001/232,177,55&lg=001/119,159,187&sh=001/175,220,223

As you can see, it contains the typical hr and ey which is what can be found in the modern Habbo figure codes, the difference is the next part.

Take a look at this:

Code:
hr=001/255,255,255

I'll break it down for you:

hr = hair (so it's the part of the body)
001 = the hair style chosen
255,255,255 = the rgb color format
 
Back
Top