TypedMessages

A simple piece of data with encoder and decoder

Messages

Message is a piece of information can be shared between node and peer

Lets define a message

class ClientInfoMessage extends Message {
  const ClientInfoMessage(this.name, this.avatar, this.clientId)
      : assert(
          name.length <= 20,
          'client info name can not exceed 20bytes : ${name.length}',
        );

  final int clientId; // 20 bytes Uint
  final String name; // bytes length in range [0, 20]
  final int avatar; // 2 bytes Uint
}

Prototypes

Prototypes are used to encode messages before send also validate and decode incoming messages

const int kClientInfoMessageId = 0;

// define or assign the prototype into the __MessagesPrototypes__ singleton
IMessagePrototype.definePrototype(const ClientInfoMessagePrototype());

class ClientInfoMessagePrototype implements IMessagePrototype<ClientInfoMessage> {
  const ClientInfoMessagePrototype();

  @override
  Uint8List encode(ClientInfoMessage message) {
    final writer = BytesWriter(22 + message.name.length);
    writer.writeSingleByte(kClientInfoMessageId);
    writer.writeSingleByte(message.name.length);
    writer.writeText(message.name);
    writer.writeUint(message.avatar, 2);
    writer.writeUint(message.clientId, 20);
    return writer.toBuffer();
  }

  @override
  ClientInfoMessage decode(Uint8List bytes) {
    final reader = BytesReader(bytes);
    final int nameLength = reader.readSingleByte(1);
    final String name = reader.readText(2, nameLength);
    final int avatar = reader.readUint(2 + nameLength, 2);
    final int id = reader.readUint(4 + nameLength, 20);
    return ClientInfoMessage(name, avatar, id);
  }

  @override
  bool validate(Uint8List bytes) {
    final reader = BytesReader(bytes);

    if (reader.length < 22 || reader.length > 42) return false;

    final int id = reader.readSingleByte(0);
    final int nameLength = reader.readSingleByte(1);

    return id == kClientInfoMessageId && nameLength >= 0 && nameLength <= 20;
  }
}

BytesWriter / BytesReader

BytesWriter and BytesReader are util classes

Usage

Now after creating ClientInfoMessage class and defining its prototype ClientInfoMessagePrototype using IMessagePrototype.defineProtoype

We have 2 ways to encode and decode data

Method1 Message abstract class

final message = ClientInfoMessage("My Client", 0, 568978596);

final Uint8List bytes = Message.encode(message);

final ClientInfoMessage? decodedMessage = Message.decode(bytes);

Method2 using MessagesPrototypes singleton and prototypes directly

final instance = MessagesPrototypes.instance;
final message = ClientInfoMessage("My Client", 0, 568978596);

// encode
final Uint8List? bytes = instance.encodeMessage(message);

// decode
final ClientInfoMessage? decodedMessage = instance.decodeBytes(bytes!);

// use defined prototype
final IMessagePrototype<ClientInfoMessage>? prototype = instance.findPrototypeByMessageType<ClientInfoMessage>();

prototype.encode(message);
prototype.decode(bytes!);
prototype.validate(bytes!);

Libraries

typed_messages