Gabriel Capella 1 tahun lalu
induk
melakukan
bfac3dd875

+ 1 - 1
lib/grid.dart

@@ -21,7 +21,7 @@ class _TransformationsDemoState extends State<TransformationsDemo>
   final GlobalKey _targetKey = GlobalKey();
   final Map<Point<int>, Widget> elements = <Point<int>, Widget>{};
   Set<Color> swatches = Colors.primaries.map((e) => Color(e.value)).toSet();
-  final double minScale = 1;
+  final double minScale = 2;
   final ColorRef cc = ColorRef(Colors.blueGrey);
 
   final TransformationController _transformationController =

+ 9 - 5
lib/src/connection/ws.dart

@@ -2,7 +2,7 @@ import 'dart:async';
 import 'dart:convert';
 import 'dart:math';
 
-import 'package:the_paint/src/generated/client.pbgrpc.dart';
+import 'package:the_paint/src/generated/client.pb.dart';
 import 'package:flutter_webrtc/flutter_webrtc.dart';
 import 'package:web_socket_channel/web_socket_channel.dart';
 
@@ -59,8 +59,6 @@ class WebRTCChannels extends Resolver {
     _rtcData = _channel.stream
         .map((event) => RTCData.fromBuffer(event))
         .asBroadcastStream();
-
-    // await waitForData();
   }
 
   Future<RTCData> waitForData(String event /*add client id*/) async {
@@ -92,9 +90,15 @@ class WebRTCChannels extends Resolver {
         print('\ndc2: change state: ${state.toString()}');
       };
       channel.onMessage = (data) {
+        print(data.binary.length);
         dup.streamToUI.add(data);
       };
-      // add future to send message to channel!!!!
+
+      if (channel.id == 1) {
+        dup.streamFromUI.stream.forEach((element) {
+          channel.send(element);
+        });
+      }
     };
     return dup;
   }
@@ -125,7 +129,7 @@ class Duplex<T> {
   StreamController<T> streamFromUI = StreamController.broadcast();
 
   void send(T serialize) {
-    streamFromUI.add(serialize);
+    streamFromUI.sink.add(serialize);
   }
 
   Stream<T> getStream() {

+ 343 - 29
lib/src/generated/client.pb.dart

@@ -7,49 +7,50 @@
 
 import 'dart:core' as $core;
 
+import 'package:fixnum/fixnum.dart' as $fixnum;
 import 'package:protobuf/protobuf.dart' as $pb;
 
-class MonitorSub extends $pb.GeneratedMessage {
-  static final $pb.BuilderInfo _i = $pb.BuilderInfo(const $core.bool.fromEnvironment('protobuf.omit_message_names') ? '' : 'MonitorSub', createEmptyInstance: create)
+class RequestData extends $pb.GeneratedMessage {
+  static final $pb.BuilderInfo _i = $pb.BuilderInfo(const $core.bool.fromEnvironment('protobuf.omit_message_names') ? '' : 'RequestData', createEmptyInstance: create)
     ..aOM<BPoint>(1, const $core.bool.fromEnvironment('protobuf.omit_field_names') ? '' : 'tile', subBuilder: BPoint.create)
-    ..aOB(2, const $core.bool.fromEnvironment('protobuf.omit_field_names') ? '' : 'susbscribed')
+    ..a<$fixnum.Int64>(2, const $core.bool.fromEnvironment('protobuf.omit_field_names') ? '' : 'from', $pb.PbFieldType.OU6, defaultOrMaker: $fixnum.Int64.ZERO)
     ..hasRequiredFields = false
   ;
 
-  MonitorSub._() : super();
-  factory MonitorSub({
+  RequestData._() : super();
+  factory RequestData({
     BPoint? tile,
-    $core.bool? susbscribed,
+    $fixnum.Int64? from,
   }) {
     final _result = create();
     if (tile != null) {
       _result.tile = tile;
     }
-    if (susbscribed != null) {
-      _result.susbscribed = susbscribed;
+    if (from != null) {
+      _result.from = from;
     }
     return _result;
   }
-  factory MonitorSub.fromBuffer($core.List<$core.int> i, [$pb.ExtensionRegistry r = $pb.ExtensionRegistry.EMPTY]) => create()..mergeFromBuffer(i, r);
-  factory MonitorSub.fromJson($core.String i, [$pb.ExtensionRegistry r = $pb.ExtensionRegistry.EMPTY]) => create()..mergeFromJson(i, r);
+  factory RequestData.fromBuffer($core.List<$core.int> i, [$pb.ExtensionRegistry r = $pb.ExtensionRegistry.EMPTY]) => create()..mergeFromBuffer(i, r);
+  factory RequestData.fromJson($core.String i, [$pb.ExtensionRegistry r = $pb.ExtensionRegistry.EMPTY]) => create()..mergeFromJson(i, r);
   @$core.Deprecated(
   'Using this can add significant overhead to your binary. '
   'Use [GeneratedMessageGenericExtensions.deepCopy] instead. '
   'Will be removed in next major version')
-  MonitorSub clone() => MonitorSub()..mergeFromMessage(this);
+  RequestData clone() => RequestData()..mergeFromMessage(this);
   @$core.Deprecated(
   'Using this can add significant overhead to your binary. '
   'Use [GeneratedMessageGenericExtensions.rebuild] instead. '
   'Will be removed in next major version')
-  MonitorSub copyWith(void Function(MonitorSub) updates) => super.copyWith((message) => updates(message as MonitorSub)) as MonitorSub; // ignore: deprecated_member_use
+  RequestData copyWith(void Function(RequestData) updates) => super.copyWith((message) => updates(message as RequestData)) as RequestData; // ignore: deprecated_member_use
   $pb.BuilderInfo get info_ => _i;
   @$core.pragma('dart2js:noInline')
-  static MonitorSub create() => MonitorSub._();
-  MonitorSub createEmptyInstance() => create();
-  static $pb.PbList<MonitorSub> createRepeated() => $pb.PbList<MonitorSub>();
+  static RequestData create() => RequestData._();
+  RequestData createEmptyInstance() => create();
+  static $pb.PbList<RequestData> createRepeated() => $pb.PbList<RequestData>();
   @$core.pragma('dart2js:noInline')
-  static MonitorSub getDefault() => _defaultInstance ??= $pb.GeneratedMessage.$_defaultFor<MonitorSub>(create);
-  static MonitorSub? _defaultInstance;
+  static RequestData getDefault() => _defaultInstance ??= $pb.GeneratedMessage.$_defaultFor<RequestData>(create);
+  static RequestData? _defaultInstance;
 
   @$pb.TagNumber(1)
   BPoint get tile => $_getN(0);
@@ -63,13 +64,261 @@ class MonitorSub extends $pb.GeneratedMessage {
   BPoint ensureTile() => $_ensure(0);
 
   @$pb.TagNumber(2)
-  $core.bool get susbscribed => $_getBF(1);
+  $fixnum.Int64 get from => $_getI64(1);
   @$pb.TagNumber(2)
-  set susbscribed($core.bool v) { $_setBool(1, v); }
+  set from($fixnum.Int64 v) { $_setInt64(1, v); }
   @$pb.TagNumber(2)
-  $core.bool hasSusbscribed() => $_has(1);
+  $core.bool hasFrom() => $_has(1);
   @$pb.TagNumber(2)
-  void clearSusbscribed() => clearField(2);
+  void clearFrom() => clearField(2);
+}
+
+enum WebRTCRequest_WebRTCRequestOneOf {
+  requestData, 
+  notSet
+}
+
+class WebRTCRequest extends $pb.GeneratedMessage {
+  static const $core.Map<$core.int, WebRTCRequest_WebRTCRequestOneOf> _WebRTCRequest_WebRTCRequestOneOfByTag = {
+    1 : WebRTCRequest_WebRTCRequestOneOf.requestData,
+    0 : WebRTCRequest_WebRTCRequestOneOf.notSet
+  };
+  static final $pb.BuilderInfo _i = $pb.BuilderInfo(const $core.bool.fromEnvironment('protobuf.omit_message_names') ? '' : 'WebRTCRequest', createEmptyInstance: create)
+    ..oo(0, [1])
+    ..aOM<RequestData>(1, const $core.bool.fromEnvironment('protobuf.omit_field_names') ? '' : 'requestData', protoName: 'requestData', subBuilder: RequestData.create)
+    ..hasRequiredFields = false
+  ;
+
+  WebRTCRequest._() : super();
+  factory WebRTCRequest({
+    RequestData? requestData,
+  }) {
+    final _result = create();
+    if (requestData != null) {
+      _result.requestData = requestData;
+    }
+    return _result;
+  }
+  factory WebRTCRequest.fromBuffer($core.List<$core.int> i, [$pb.ExtensionRegistry r = $pb.ExtensionRegistry.EMPTY]) => create()..mergeFromBuffer(i, r);
+  factory WebRTCRequest.fromJson($core.String i, [$pb.ExtensionRegistry r = $pb.ExtensionRegistry.EMPTY]) => create()..mergeFromJson(i, r);
+  @$core.Deprecated(
+  'Using this can add significant overhead to your binary. '
+  'Use [GeneratedMessageGenericExtensions.deepCopy] instead. '
+  'Will be removed in next major version')
+  WebRTCRequest clone() => WebRTCRequest()..mergeFromMessage(this);
+  @$core.Deprecated(
+  'Using this can add significant overhead to your binary. '
+  'Use [GeneratedMessageGenericExtensions.rebuild] instead. '
+  'Will be removed in next major version')
+  WebRTCRequest copyWith(void Function(WebRTCRequest) updates) => super.copyWith((message) => updates(message as WebRTCRequest)) as WebRTCRequest; // ignore: deprecated_member_use
+  $pb.BuilderInfo get info_ => _i;
+  @$core.pragma('dart2js:noInline')
+  static WebRTCRequest create() => WebRTCRequest._();
+  WebRTCRequest createEmptyInstance() => create();
+  static $pb.PbList<WebRTCRequest> createRepeated() => $pb.PbList<WebRTCRequest>();
+  @$core.pragma('dart2js:noInline')
+  static WebRTCRequest getDefault() => _defaultInstance ??= $pb.GeneratedMessage.$_defaultFor<WebRTCRequest>(create);
+  static WebRTCRequest? _defaultInstance;
+
+  WebRTCRequest_WebRTCRequestOneOf whichWebRTCRequestOneOf() => _WebRTCRequest_WebRTCRequestOneOfByTag[$_whichOneof(0)]!;
+  void clearWebRTCRequestOneOf() => clearField($_whichOneof(0));
+
+  @$pb.TagNumber(1)
+  RequestData get requestData => $_getN(0);
+  @$pb.TagNumber(1)
+  set requestData(RequestData v) { setField(1, v); }
+  @$pb.TagNumber(1)
+  $core.bool hasRequestData() => $_has(0);
+  @$pb.TagNumber(1)
+  void clearRequestData() => clearField(1);
+  @$pb.TagNumber(1)
+  RequestData ensureRequestData() => $_ensure(0);
+}
+
+enum WebRTCReply_WebRTCReplyOneOf {
+  requestData, 
+  notSet
+}
+
+class WebRTCReply extends $pb.GeneratedMessage {
+  static const $core.Map<$core.int, WebRTCReply_WebRTCReplyOneOf> _WebRTCReply_WebRTCReplyOneOfByTag = {
+    1 : WebRTCReply_WebRTCReplyOneOf.requestData,
+    0 : WebRTCReply_WebRTCReplyOneOf.notSet
+  };
+  static final $pb.BuilderInfo _i = $pb.BuilderInfo(const $core.bool.fromEnvironment('protobuf.omit_message_names') ? '' : 'WebRTCReply', createEmptyInstance: create)
+    ..oo(0, [1])
+    ..aOM<RequestData>(1, const $core.bool.fromEnvironment('protobuf.omit_field_names') ? '' : 'requestData', protoName: 'requestData', subBuilder: RequestData.create)
+    ..hasRequiredFields = false
+  ;
+
+  WebRTCReply._() : super();
+  factory WebRTCReply({
+    RequestData? requestData,
+  }) {
+    final _result = create();
+    if (requestData != null) {
+      _result.requestData = requestData;
+    }
+    return _result;
+  }
+  factory WebRTCReply.fromBuffer($core.List<$core.int> i, [$pb.ExtensionRegistry r = $pb.ExtensionRegistry.EMPTY]) => create()..mergeFromBuffer(i, r);
+  factory WebRTCReply.fromJson($core.String i, [$pb.ExtensionRegistry r = $pb.ExtensionRegistry.EMPTY]) => create()..mergeFromJson(i, r);
+  @$core.Deprecated(
+  'Using this can add significant overhead to your binary. '
+  'Use [GeneratedMessageGenericExtensions.deepCopy] instead. '
+  'Will be removed in next major version')
+  WebRTCReply clone() => WebRTCReply()..mergeFromMessage(this);
+  @$core.Deprecated(
+  'Using this can add significant overhead to your binary. '
+  'Use [GeneratedMessageGenericExtensions.rebuild] instead. '
+  'Will be removed in next major version')
+  WebRTCReply copyWith(void Function(WebRTCReply) updates) => super.copyWith((message) => updates(message as WebRTCReply)) as WebRTCReply; // ignore: deprecated_member_use
+  $pb.BuilderInfo get info_ => _i;
+  @$core.pragma('dart2js:noInline')
+  static WebRTCReply create() => WebRTCReply._();
+  WebRTCReply createEmptyInstance() => create();
+  static $pb.PbList<WebRTCReply> createRepeated() => $pb.PbList<WebRTCReply>();
+  @$core.pragma('dart2js:noInline')
+  static WebRTCReply getDefault() => _defaultInstance ??= $pb.GeneratedMessage.$_defaultFor<WebRTCReply>(create);
+  static WebRTCReply? _defaultInstance;
+
+  WebRTCReply_WebRTCReplyOneOf whichWebRTCReplyOneOf() => _WebRTCReply_WebRTCReplyOneOfByTag[$_whichOneof(0)]!;
+  void clearWebRTCReplyOneOf() => clearField($_whichOneof(0));
+
+  @$pb.TagNumber(1)
+  RequestData get requestData => $_getN(0);
+  @$pb.TagNumber(1)
+  set requestData(RequestData v) { setField(1, v); }
+  @$pb.TagNumber(1)
+  $core.bool hasRequestData() => $_has(0);
+  @$pb.TagNumber(1)
+  void clearRequestData() => clearField(1);
+  @$pb.TagNumber(1)
+  RequestData ensureRequestData() => $_ensure(0);
+}
+
+enum WebSocketRequest_WebSocketRequestOneOf {
+  requestData, 
+  notSet
+}
+
+class WebSocketRequest extends $pb.GeneratedMessage {
+  static const $core.Map<$core.int, WebSocketRequest_WebSocketRequestOneOf> _WebSocketRequest_WebSocketRequestOneOfByTag = {
+    1 : WebSocketRequest_WebSocketRequestOneOf.requestData,
+    0 : WebSocketRequest_WebSocketRequestOneOf.notSet
+  };
+  static final $pb.BuilderInfo _i = $pb.BuilderInfo(const $core.bool.fromEnvironment('protobuf.omit_message_names') ? '' : 'WebSocketRequest', createEmptyInstance: create)
+    ..oo(0, [1])
+    ..aOM<RequestData>(1, const $core.bool.fromEnvironment('protobuf.omit_field_names') ? '' : 'requestData', protoName: 'requestData', subBuilder: RequestData.create)
+    ..hasRequiredFields = false
+  ;
+
+  WebSocketRequest._() : super();
+  factory WebSocketRequest({
+    RequestData? requestData,
+  }) {
+    final _result = create();
+    if (requestData != null) {
+      _result.requestData = requestData;
+    }
+    return _result;
+  }
+  factory WebSocketRequest.fromBuffer($core.List<$core.int> i, [$pb.ExtensionRegistry r = $pb.ExtensionRegistry.EMPTY]) => create()..mergeFromBuffer(i, r);
+  factory WebSocketRequest.fromJson($core.String i, [$pb.ExtensionRegistry r = $pb.ExtensionRegistry.EMPTY]) => create()..mergeFromJson(i, r);
+  @$core.Deprecated(
+  'Using this can add significant overhead to your binary. '
+  'Use [GeneratedMessageGenericExtensions.deepCopy] instead. '
+  'Will be removed in next major version')
+  WebSocketRequest clone() => WebSocketRequest()..mergeFromMessage(this);
+  @$core.Deprecated(
+  'Using this can add significant overhead to your binary. '
+  'Use [GeneratedMessageGenericExtensions.rebuild] instead. '
+  'Will be removed in next major version')
+  WebSocketRequest copyWith(void Function(WebSocketRequest) updates) => super.copyWith((message) => updates(message as WebSocketRequest)) as WebSocketRequest; // ignore: deprecated_member_use
+  $pb.BuilderInfo get info_ => _i;
+  @$core.pragma('dart2js:noInline')
+  static WebSocketRequest create() => WebSocketRequest._();
+  WebSocketRequest createEmptyInstance() => create();
+  static $pb.PbList<WebSocketRequest> createRepeated() => $pb.PbList<WebSocketRequest>();
+  @$core.pragma('dart2js:noInline')
+  static WebSocketRequest getDefault() => _defaultInstance ??= $pb.GeneratedMessage.$_defaultFor<WebSocketRequest>(create);
+  static WebSocketRequest? _defaultInstance;
+
+  WebSocketRequest_WebSocketRequestOneOf whichWebSocketRequestOneOf() => _WebSocketRequest_WebSocketRequestOneOfByTag[$_whichOneof(0)]!;
+  void clearWebSocketRequestOneOf() => clearField($_whichOneof(0));
+
+  @$pb.TagNumber(1)
+  RequestData get requestData => $_getN(0);
+  @$pb.TagNumber(1)
+  set requestData(RequestData v) { setField(1, v); }
+  @$pb.TagNumber(1)
+  $core.bool hasRequestData() => $_has(0);
+  @$pb.TagNumber(1)
+  void clearRequestData() => clearField(1);
+  @$pb.TagNumber(1)
+  RequestData ensureRequestData() => $_ensure(0);
+}
+
+enum WebSocketReply_WebSocketReplyOneOf {
+  requestData, 
+  notSet
+}
+
+class WebSocketReply extends $pb.GeneratedMessage {
+  static const $core.Map<$core.int, WebSocketReply_WebSocketReplyOneOf> _WebSocketReply_WebSocketReplyOneOfByTag = {
+    1 : WebSocketReply_WebSocketReplyOneOf.requestData,
+    0 : WebSocketReply_WebSocketReplyOneOf.notSet
+  };
+  static final $pb.BuilderInfo _i = $pb.BuilderInfo(const $core.bool.fromEnvironment('protobuf.omit_message_names') ? '' : 'WebSocketReply', createEmptyInstance: create)
+    ..oo(0, [1])
+    ..aOM<RequestData>(1, const $core.bool.fromEnvironment('protobuf.omit_field_names') ? '' : 'requestData', protoName: 'requestData', subBuilder: RequestData.create)
+    ..hasRequiredFields = false
+  ;
+
+  WebSocketReply._() : super();
+  factory WebSocketReply({
+    RequestData? requestData,
+  }) {
+    final _result = create();
+    if (requestData != null) {
+      _result.requestData = requestData;
+    }
+    return _result;
+  }
+  factory WebSocketReply.fromBuffer($core.List<$core.int> i, [$pb.ExtensionRegistry r = $pb.ExtensionRegistry.EMPTY]) => create()..mergeFromBuffer(i, r);
+  factory WebSocketReply.fromJson($core.String i, [$pb.ExtensionRegistry r = $pb.ExtensionRegistry.EMPTY]) => create()..mergeFromJson(i, r);
+  @$core.Deprecated(
+  'Using this can add significant overhead to your binary. '
+  'Use [GeneratedMessageGenericExtensions.deepCopy] instead. '
+  'Will be removed in next major version')
+  WebSocketReply clone() => WebSocketReply()..mergeFromMessage(this);
+  @$core.Deprecated(
+  'Using this can add significant overhead to your binary. '
+  'Use [GeneratedMessageGenericExtensions.rebuild] instead. '
+  'Will be removed in next major version')
+  WebSocketReply copyWith(void Function(WebSocketReply) updates) => super.copyWith((message) => updates(message as WebSocketReply)) as WebSocketReply; // ignore: deprecated_member_use
+  $pb.BuilderInfo get info_ => _i;
+  @$core.pragma('dart2js:noInline')
+  static WebSocketReply create() => WebSocketReply._();
+  WebSocketReply createEmptyInstance() => create();
+  static $pb.PbList<WebSocketReply> createRepeated() => $pb.PbList<WebSocketReply>();
+  @$core.pragma('dart2js:noInline')
+  static WebSocketReply getDefault() => _defaultInstance ??= $pb.GeneratedMessage.$_defaultFor<WebSocketReply>(create);
+  static WebSocketReply? _defaultInstance;
+
+  WebSocketReply_WebSocketReplyOneOf whichWebSocketReplyOneOf() => _WebSocketReply_WebSocketReplyOneOfByTag[$_whichOneof(0)]!;
+  void clearWebSocketReplyOneOf() => clearField($_whichOneof(0));
+
+  @$pb.TagNumber(1)
+  RequestData get requestData => $_getN(0);
+  @$pb.TagNumber(1)
+  set requestData(RequestData v) { setField(1, v); }
+  @$pb.TagNumber(1)
+  $core.bool hasRequestData() => $_has(0);
+  @$pb.TagNumber(1)
+  void clearRequestData() => clearField(1);
+  @$pb.TagNumber(1)
+  RequestData ensureRequestData() => $_ensure(0);
 }
 
 class MonitorPoint extends $pb.GeneratedMessage {
@@ -140,21 +389,21 @@ class MonitorPoint extends $pb.GeneratedMessage {
 class MonitorReply extends $pb.GeneratedMessage {
   static final $pb.BuilderInfo _i = $pb.BuilderInfo(const $core.bool.fromEnvironment('protobuf.omit_message_names') ? '' : 'MonitorReply', createEmptyInstance: create)
     ..pc<MonitorPoint>(1, const $core.bool.fromEnvironment('protobuf.omit_field_names') ? '' : 'points', $pb.PbFieldType.PM, subBuilder: MonitorPoint.create)
-    ..a<$core.List<$core.int>>(2, const $core.bool.fromEnvironment('protobuf.omit_field_names') ? '' : 'bytesPoints', $pb.PbFieldType.OY)
+    ..aOM<BPoint>(2, const $core.bool.fromEnvironment('protobuf.omit_field_names') ? '' : 'tile', subBuilder: BPoint.create)
     ..hasRequiredFields = false
   ;
 
   MonitorReply._() : super();
   factory MonitorReply({
     $core.Iterable<MonitorPoint>? points,
-    $core.List<$core.int>? bytesPoints,
+    BPoint? tile,
   }) {
     final _result = create();
     if (points != null) {
       _result.points.addAll(points);
     }
-    if (bytesPoints != null) {
-      _result.bytesPoints = bytesPoints;
+    if (tile != null) {
+      _result.tile = tile;
     }
     return _result;
   }
@@ -183,13 +432,78 @@ class MonitorReply extends $pb.GeneratedMessage {
   $core.List<MonitorPoint> get points => $_getList(0);
 
   @$pb.TagNumber(2)
-  $core.List<$core.int> get bytesPoints => $_getN(1);
+  BPoint get tile => $_getN(1);
+  @$pb.TagNumber(2)
+  set tile(BPoint v) { setField(2, v); }
+  @$pb.TagNumber(2)
+  $core.bool hasTile() => $_has(1);
+  @$pb.TagNumber(2)
+  void clearTile() => clearField(2);
+  @$pb.TagNumber(2)
+  BPoint ensureTile() => $_ensure(1);
+}
+
+class MonitorRequest extends $pb.GeneratedMessage {
+  static final $pb.BuilderInfo _i = $pb.BuilderInfo(const $core.bool.fromEnvironment('protobuf.omit_message_names') ? '' : 'MonitorRequest', createEmptyInstance: create)
+    ..aInt64(1, const $core.bool.fromEnvironment('protobuf.omit_field_names') ? '' : 'last')
+    ..aOM<BPoint>(2, const $core.bool.fromEnvironment('protobuf.omit_field_names') ? '' : 'tile', subBuilder: BPoint.create)
+    ..hasRequiredFields = false
+  ;
+
+  MonitorRequest._() : super();
+  factory MonitorRequest({
+    $fixnum.Int64? last,
+    BPoint? tile,
+  }) {
+    final _result = create();
+    if (last != null) {
+      _result.last = last;
+    }
+    if (tile != null) {
+      _result.tile = tile;
+    }
+    return _result;
+  }
+  factory MonitorRequest.fromBuffer($core.List<$core.int> i, [$pb.ExtensionRegistry r = $pb.ExtensionRegistry.EMPTY]) => create()..mergeFromBuffer(i, r);
+  factory MonitorRequest.fromJson($core.String i, [$pb.ExtensionRegistry r = $pb.ExtensionRegistry.EMPTY]) => create()..mergeFromJson(i, r);
+  @$core.Deprecated(
+  'Using this can add significant overhead to your binary. '
+  'Use [GeneratedMessageGenericExtensions.deepCopy] instead. '
+  'Will be removed in next major version')
+  MonitorRequest clone() => MonitorRequest()..mergeFromMessage(this);
+  @$core.Deprecated(
+  'Using this can add significant overhead to your binary. '
+  'Use [GeneratedMessageGenericExtensions.rebuild] instead. '
+  'Will be removed in next major version')
+  MonitorRequest copyWith(void Function(MonitorRequest) updates) => super.copyWith((message) => updates(message as MonitorRequest)) as MonitorRequest; // ignore: deprecated_member_use
+  $pb.BuilderInfo get info_ => _i;
+  @$core.pragma('dart2js:noInline')
+  static MonitorRequest create() => MonitorRequest._();
+  MonitorRequest createEmptyInstance() => create();
+  static $pb.PbList<MonitorRequest> createRepeated() => $pb.PbList<MonitorRequest>();
+  @$core.pragma('dart2js:noInline')
+  static MonitorRequest getDefault() => _defaultInstance ??= $pb.GeneratedMessage.$_defaultFor<MonitorRequest>(create);
+  static MonitorRequest? _defaultInstance;
+
+  @$pb.TagNumber(1)
+  $fixnum.Int64 get last => $_getI64(0);
+  @$pb.TagNumber(1)
+  set last($fixnum.Int64 v) { $_setInt64(0, v); }
+  @$pb.TagNumber(1)
+  $core.bool hasLast() => $_has(0);
+  @$pb.TagNumber(1)
+  void clearLast() => clearField(1);
+
+  @$pb.TagNumber(2)
+  BPoint get tile => $_getN(1);
+  @$pb.TagNumber(2)
+  set tile(BPoint v) { setField(2, v); }
   @$pb.TagNumber(2)
-  set bytesPoints($core.List<$core.int> v) { $_setBytes(1, v); }
+  $core.bool hasTile() => $_has(1);
   @$pb.TagNumber(2)
-  $core.bool hasBytesPoints() => $_has(1);
+  void clearTile() => clearField(2);
   @$pb.TagNumber(2)
-  void clearBytesPoints() => clearField(2);
+  BPoint ensureTile() => $_ensure(1);
 }
 
 class MonitorPointList extends $pb.GeneratedMessage {

+ 0 - 96
lib/src/generated/client.pbgrpc.dart

@@ -1,96 +0,0 @@
-///
-//  Generated code. Do not modify.
-//  source: client.proto
-//
-// @dart = 2.12
-// ignore_for_file: annotate_overrides,camel_case_types,constant_identifier_names,directives_ordering,library_prefixes,non_constant_identifier_names,prefer_final_fields,return_of_invalid_type,unnecessary_const,unnecessary_import,unnecessary_this,unused_import,unused_shown_name
-
-import 'dart:async' as $async;
-
-import 'dart:core' as $core;
-
-import 'package:grpc/service_api.dart' as $grpc;
-import 'client.pb.dart' as $0;
-export 'client.pb.dart';
-
-class PaintClient extends $grpc.Client {
-  static final _$paint = $grpc.ClientMethod<$0.PaintRequest, $0.PaintReply>(
-      '/PaintClient/Paint',
-      ($0.PaintRequest value) => value.writeToBuffer(),
-      ($core.List<$core.int> value) => $0.PaintReply.fromBuffer(value));
-  static final _$monitor = $grpc.ClientMethod<$0.MonitorSub, $0.MonitorReply>(
-      '/PaintClient/Monitor',
-      ($0.MonitorSub value) => value.writeToBuffer(),
-      ($core.List<$core.int> value) => $0.MonitorReply.fromBuffer(value));
-  static final _$requestRTC = $grpc.ClientMethod<$0.RTCData, $0.RTCData>(
-      '/PaintClient/RequestRTC',
-      ($0.RTCData value) => value.writeToBuffer(),
-      ($core.List<$core.int> value) => $0.RTCData.fromBuffer(value));
-
-  PaintClient($grpc.ClientChannel channel,
-      {$grpc.CallOptions? options,
-      $core.Iterable<$grpc.ClientInterceptor>? interceptors})
-      : super(channel, options: options, interceptors: interceptors);
-
-  $grpc.ResponseFuture<$0.PaintReply> paint($0.PaintRequest request,
-      {$grpc.CallOptions? options}) {
-    return $createUnaryCall(_$paint, request, options: options);
-  }
-
-  $grpc.ResponseStream<$0.MonitorReply> monitor($0.MonitorSub request,
-      {$grpc.CallOptions? options}) {
-    return $createStreamingCall(
-        _$monitor, $async.Stream.fromIterable([request]),
-        options: options);
-  }
-
-  $grpc.ResponseStream<$0.RTCData> requestRTC($async.Stream<$0.RTCData> request,
-      {$grpc.CallOptions? options}) {
-    return $createStreamingCall(_$requestRTC, request, options: options);
-  }
-}
-
-abstract class PaintClientServiceBase extends $grpc.Service {
-  $core.String get $name => 'PaintClient';
-
-  PaintClientServiceBase() {
-    $addMethod($grpc.ServiceMethod<$0.PaintRequest, $0.PaintReply>(
-        'Paint',
-        paint_Pre,
-        false,
-        false,
-        ($core.List<$core.int> value) => $0.PaintRequest.fromBuffer(value),
-        ($0.PaintReply value) => value.writeToBuffer()));
-    $addMethod($grpc.ServiceMethod<$0.MonitorSub, $0.MonitorReply>(
-        'Monitor',
-        monitor_Pre,
-        false,
-        true,
-        ($core.List<$core.int> value) => $0.MonitorSub.fromBuffer(value),
-        ($0.MonitorReply value) => value.writeToBuffer()));
-    $addMethod($grpc.ServiceMethod<$0.RTCData, $0.RTCData>(
-        'RequestRTC',
-        requestRTC,
-        true,
-        true,
-        ($core.List<$core.int> value) => $0.RTCData.fromBuffer(value),
-        ($0.RTCData value) => value.writeToBuffer()));
-  }
-
-  $async.Future<$0.PaintReply> paint_Pre(
-      $grpc.ServiceCall call, $async.Future<$0.PaintRequest> request) async {
-    return paint(call, await request);
-  }
-
-  $async.Stream<$0.MonitorReply> monitor_Pre(
-      $grpc.ServiceCall call, $async.Future<$0.MonitorSub> request) async* {
-    yield* monitor(call, await request);
-  }
-
-  $async.Future<$0.PaintReply> paint(
-      $grpc.ServiceCall call, $0.PaintRequest request);
-  $async.Stream<$0.MonitorReply> monitor(
-      $grpc.ServiceCall call, $0.MonitorSub request);
-  $async.Stream<$0.RTCData> requestRTC(
-      $grpc.ServiceCall call, $async.Stream<$0.RTCData> request);
-}

+ 71 - 8
lib/src/generated/client.pbjson.dart

@@ -8,17 +8,69 @@
 import 'dart:core' as $core;
 import 'dart:convert' as $convert;
 import 'dart:typed_data' as $typed_data;
-@$core.Deprecated('Use monitorSubDescriptor instead')
-const MonitorSub$json = const {
-  '1': 'MonitorSub',
+@$core.Deprecated('Use requestDataDescriptor instead')
+const RequestData$json = const {
+  '1': 'RequestData',
   '2': const [
     const {'1': 'tile', '3': 1, '4': 1, '5': 11, '6': '.BPoint', '10': 'tile'},
-    const {'1': 'susbscribed', '3': 2, '4': 1, '5': 8, '10': 'susbscribed'},
+    const {'1': 'from', '3': 2, '4': 1, '5': 4, '10': 'from'},
   ],
 };
 
-/// Descriptor for `MonitorSub`. Decode as a `google.protobuf.DescriptorProto`.
-final $typed_data.Uint8List monitorSubDescriptor = $convert.base64Decode('CgpNb25pdG9yU3ViEhsKBHRpbGUYASABKAsyBy5CUG9pbnRSBHRpbGUSIAoLc3VzYnNjcmliZWQYAiABKAhSC3N1c2JzY3JpYmVk');
+/// Descriptor for `RequestData`. Decode as a `google.protobuf.DescriptorProto`.
+final $typed_data.Uint8List requestDataDescriptor = $convert.base64Decode('CgtSZXF1ZXN0RGF0YRIbCgR0aWxlGAEgASgLMgcuQlBvaW50UgR0aWxlEhIKBGZyb20YAiABKARSBGZyb20=');
+@$core.Deprecated('Use webRTCRequestDescriptor instead')
+const WebRTCRequest$json = const {
+  '1': 'WebRTCRequest',
+  '2': const [
+    const {'1': 'requestData', '3': 1, '4': 1, '5': 11, '6': '.RequestData', '9': 0, '10': 'requestData'},
+  ],
+  '8': const [
+    const {'1': 'WebRTCRequestOneOf'},
+  ],
+};
+
+/// Descriptor for `WebRTCRequest`. Decode as a `google.protobuf.DescriptorProto`.
+final $typed_data.Uint8List webRTCRequestDescriptor = $convert.base64Decode('Cg1XZWJSVENSZXF1ZXN0EjAKC3JlcXVlc3REYXRhGAEgASgLMgwuUmVxdWVzdERhdGFIAFILcmVxdWVzdERhdGFCFAoSV2ViUlRDUmVxdWVzdE9uZU9m');
+@$core.Deprecated('Use webRTCReplyDescriptor instead')
+const WebRTCReply$json = const {
+  '1': 'WebRTCReply',
+  '2': const [
+    const {'1': 'requestData', '3': 1, '4': 1, '5': 11, '6': '.RequestData', '9': 0, '10': 'requestData'},
+  ],
+  '8': const [
+    const {'1': 'WebRTCReplyOneOf'},
+  ],
+};
+
+/// Descriptor for `WebRTCReply`. Decode as a `google.protobuf.DescriptorProto`.
+final $typed_data.Uint8List webRTCReplyDescriptor = $convert.base64Decode('CgtXZWJSVENSZXBseRIwCgtyZXF1ZXN0RGF0YRgBIAEoCzIMLlJlcXVlc3REYXRhSABSC3JlcXVlc3REYXRhQhIKEFdlYlJUQ1JlcGx5T25lT2Y=');
+@$core.Deprecated('Use webSocketRequestDescriptor instead')
+const WebSocketRequest$json = const {
+  '1': 'WebSocketRequest',
+  '2': const [
+    const {'1': 'requestData', '3': 1, '4': 1, '5': 11, '6': '.RequestData', '9': 0, '10': 'requestData'},
+  ],
+  '8': const [
+    const {'1': 'WebSocketRequestOneOf'},
+  ],
+};
+
+/// Descriptor for `WebSocketRequest`. Decode as a `google.protobuf.DescriptorProto`.
+final $typed_data.Uint8List webSocketRequestDescriptor = $convert.base64Decode('ChBXZWJTb2NrZXRSZXF1ZXN0EjAKC3JlcXVlc3REYXRhGAEgASgLMgwuUmVxdWVzdERhdGFIAFILcmVxdWVzdERhdGFCFwoVV2ViU29ja2V0UmVxdWVzdE9uZU9m');
+@$core.Deprecated('Use webSocketReplyDescriptor instead')
+const WebSocketReply$json = const {
+  '1': 'WebSocketReply',
+  '2': const [
+    const {'1': 'requestData', '3': 1, '4': 1, '5': 11, '6': '.RequestData', '9': 0, '10': 'requestData'},
+  ],
+  '8': const [
+    const {'1': 'WebSocketReplyOneOf'},
+  ],
+};
+
+/// Descriptor for `WebSocketReply`. Decode as a `google.protobuf.DescriptorProto`.
+final $typed_data.Uint8List webSocketReplyDescriptor = $convert.base64Decode('Cg5XZWJTb2NrZXRSZXBseRIwCgtyZXF1ZXN0RGF0YRgBIAEoCzIMLlJlcXVlc3REYXRhSABSC3JlcXVlc3REYXRhQhUKE1dlYlNvY2tldFJlcGx5T25lT2Y=');
 @$core.Deprecated('Use monitorPointDescriptor instead')
 const MonitorPoint$json = const {
   '1': 'MonitorPoint',
@@ -35,12 +87,23 @@ const MonitorReply$json = const {
   '1': 'MonitorReply',
   '2': const [
     const {'1': 'points', '3': 1, '4': 3, '5': 11, '6': '.MonitorPoint', '10': 'points'},
-    const {'1': 'bytes_points', '3': 2, '4': 1, '5': 12, '10': 'bytesPoints'},
+    const {'1': 'tile', '3': 2, '4': 1, '5': 11, '6': '.BPoint', '10': 'tile'},
   ],
 };
 
 /// Descriptor for `MonitorReply`. Decode as a `google.protobuf.DescriptorProto`.
-final $typed_data.Uint8List monitorReplyDescriptor = $convert.base64Decode('CgxNb25pdG9yUmVwbHkSJQoGcG9pbnRzGAEgAygLMg0uTW9uaXRvclBvaW50UgZwb2ludHMSIQoMYnl0ZXNfcG9pbnRzGAIgASgMUgtieXRlc1BvaW50cw==');
+final $typed_data.Uint8List monitorReplyDescriptor = $convert.base64Decode('CgxNb25pdG9yUmVwbHkSJQoGcG9pbnRzGAEgAygLMg0uTW9uaXRvclBvaW50UgZwb2ludHMSGwoEdGlsZRgCIAEoCzIHLkJQb2ludFIEdGlsZQ==');
+@$core.Deprecated('Use monitorRequestDescriptor instead')
+const MonitorRequest$json = const {
+  '1': 'MonitorRequest',
+  '2': const [
+    const {'1': 'last', '3': 1, '4': 1, '5': 3, '10': 'last'},
+    const {'1': 'tile', '3': 2, '4': 1, '5': 11, '6': '.BPoint', '10': 'tile'},
+  ],
+};
+
+/// Descriptor for `MonitorRequest`. Decode as a `google.protobuf.DescriptorProto`.
+final $typed_data.Uint8List monitorRequestDescriptor = $convert.base64Decode('Cg5Nb25pdG9yUmVxdWVzdBISCgRsYXN0GAEgASgDUgRsYXN0EhsKBHRpbGUYAiABKAsyBy5CUG9pbnRSBHRpbGU=');
 @$core.Deprecated('Use monitorPointListDescriptor instead')
 const MonitorPointList$json = const {
   '1': 'MonitorPointList',

+ 27 - 10
lib/src/square/rtc.dart

@@ -13,7 +13,7 @@ class WebRTCSquareData extends SquereData {
   final Resolver resolver = WebRTCChannels();
   final StreamController<MonitorReply> streamChannel =
       StreamController<MonitorReply>();
-      
+
   Duplex? _dc;
 
   WebRTCSquareData(super.tile);
@@ -23,23 +23,40 @@ class WebRTCSquareData extends SquereData {
     resolver.dataChannel(super.tile).then((dc) {
       _dc = dc;
       streamChannel.addStream(dc.getStream().map((event) {
-        var point = Point(1, 1);
-        var color = Colors.blueGrey;
-        return MonitorReply(points: [
-          MonitorPoint(
-              point: BPoint(x: point.x, y: point.y), color: fromColor(color))
-        ]);
-      }));
+        return MonitorReply.fromBuffer(event.binary);
+      }).where((event) => event.tile.x == tile.x && event.tile.y == tile.y));
+
+      // request
+      var req = WebRTCRequest(
+        requestData: RequestData(tile: BPoint(x: tile.x, y: tile.y)),
+      );
+
+      _dc!.send(
+        RTCDataChannelMessage.fromBinary(
+          req.writeToBuffer(),
+        ),
+      );
     });
     return streamChannel.stream;
   }
 
   @override
   void paint(Point<int> point, Color color) {
-    if (_dc == null ) {
+    if (_dc == null) {
       return;
     }
-    _dc!.send(serialize(point, color));
+    // request
+    var req = WebRTCRequest(
+      requestData: RequestData(tile: BPoint(x: tile.x, y: tile.y)),
+    );
+
+    _dc!.send(
+      RTCDataChannelMessage.fromBinary(
+        req.writeToBuffer(),
+      ),
+    );
+
+    // _dc!.send(serialize(point, color));
   }
 
   RTCDataChannelMessage serialize(Point<int> point, Color color) {

+ 3 - 2
lib/src/widget/square.dart

@@ -4,7 +4,7 @@ import 'dart:math';
 import 'dart:typed_data';
 import 'package:flutter/material.dart';
 import 'package:flutter/services.dart';
-import 'package:the_paint/src/generated/client.pbgrpc.dart';
+import 'package:the_paint/src/generated/client.pb.dart';
 import 'dart:ui' as ui;
 import '../color_ref.dart';
 import '../square/in_memory.dart';
@@ -65,8 +65,9 @@ class SquareComponentState extends State<SquareComponent> {
   }
 
   Future<void> streamSub() async {
-    await for (var event in __serverSquareData.getSteam()) {
+    await for (MonitorReply event in __serverSquareData.getSteam()) {
       setState(() {
+        print(event.points.length);
         storePoint(event);
       });
     }

+ 4 - 0
linux/flutter/generated_plugin_registrant.cc

@@ -6,6 +6,10 @@
 
 #include "generated_plugin_registrant.h"
 
+#include <flutter_webrtc/flutter_web_r_t_c_plugin.h>
 
 void fl_register_plugins(FlPluginRegistry* registry) {
+  g_autoptr(FlPluginRegistrar) flutter_webrtc_registrar =
+      fl_plugin_registry_get_registrar_for_plugin(registry, "FlutterWebRTCPlugin");
+  flutter_web_r_t_c_plugin_register_with_registrar(flutter_webrtc_registrar);
 }

+ 1 - 0
linux/flutter/generated_plugins.cmake

@@ -3,6 +3,7 @@
 #
 
 list(APPEND FLUTTER_PLUGIN_LIST
+  flutter_webrtc
 )
 
 list(APPEND FLUTTER_FFI_PLUGIN_LIST

+ 1 - 1
macos/Flutter/GeneratedPluginRegistrant.swift

@@ -6,7 +6,7 @@ import FlutterMacOS
 import Foundation
 
 import flutter_webrtc
-import path_provider_macos
+import path_provider_foundation
 
 func RegisterGeneratedPlugins(registry: FlutterPluginRegistry) {
   FlutterWebRTCPlugin.register(with: registry.registrar(forPlugin: "FlutterWebRTCPlugin"))

+ 5 - 4
macos/Runner.xcodeproj/project.pbxproj

@@ -3,7 +3,7 @@
 	archiveVersion = 1;
 	classes = {
 	};
-	objectVersion = 51;
+	objectVersion = 54;
 	objects = {
 
 /* Begin PBXAggregateTarget section */
@@ -235,6 +235,7 @@
 /* Begin PBXShellScriptBuildPhase section */
 		3399D490228B24CF009A79C7 /* ShellScript */ = {
 			isa = PBXShellScriptBuildPhase;
+			alwaysOutOfDate = 1;
 			buildActionMask = 2147483647;
 			files = (
 			);
@@ -344,7 +345,7 @@
 				GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
 				GCC_WARN_UNUSED_FUNCTION = YES;
 				GCC_WARN_UNUSED_VARIABLE = YES;
-				MACOSX_DEPLOYMENT_TARGET = 10.11;
+				MACOSX_DEPLOYMENT_TARGET = 10.14;
 				MTL_ENABLE_DEBUG_INFO = NO;
 				SDKROOT = macosx;
 				SWIFT_COMPILATION_MODE = wholemodule;
@@ -423,7 +424,7 @@
 				GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
 				GCC_WARN_UNUSED_FUNCTION = YES;
 				GCC_WARN_UNUSED_VARIABLE = YES;
-				MACOSX_DEPLOYMENT_TARGET = 10.11;
+				MACOSX_DEPLOYMENT_TARGET = 10.14;
 				MTL_ENABLE_DEBUG_INFO = YES;
 				ONLY_ACTIVE_ARCH = YES;
 				SDKROOT = macosx;
@@ -470,7 +471,7 @@
 				GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
 				GCC_WARN_UNUSED_FUNCTION = YES;
 				GCC_WARN_UNUSED_VARIABLE = YES;
-				MACOSX_DEPLOYMENT_TARGET = 10.11;
+				MACOSX_DEPLOYMENT_TARGET = 10.14;
 				MTL_ENABLE_DEBUG_INFO = NO;
 				SDKROOT = macosx;
 				SWIFT_COMPILATION_MODE = wholemodule;

+ 208 - 126
pubspec.lock

@@ -5,149 +5,170 @@ packages:
     dependency: transitive
     description:
       name: _fe_analyzer_shared
-      url: "https://pub.dartlang.org"
+      sha256: ae92f5d747aee634b87f89d9946000c2de774be1d6ac3e58268224348cd0101a
+      url: "https://pub.dev"
     source: hosted
-    version: "50.0.0"
+    version: "61.0.0"
   analyzer:
     dependency: transitive
     description:
       name: analyzer
-      url: "https://pub.dartlang.org"
+      sha256: ea3d8652bda62982addfd92fdc2d0214e5f82e43325104990d4f4c4a2a313562
+      url: "https://pub.dev"
     source: hosted
-    version: "5.2.0"
+    version: "5.13.0"
   archive:
     dependency: transitive
     description:
       name: archive
-      url: "https://pub.dartlang.org"
+      sha256: "0c8368c9b3f0abbc193b9d6133649a614204b528982bebc7026372d61677ce3a"
+      url: "https://pub.dev"
     source: hosted
-    version: "3.3.2"
+    version: "3.3.7"
   args:
     dependency: transitive
     description:
       name: args
-      url: "https://pub.dartlang.org"
+      sha256: c372bb384f273f0c2a8aaaa226dad84dc27c8519a691b888725dec59518ad53a
+      url: "https://pub.dev"
     source: hosted
-    version: "2.3.1"
+    version: "2.4.1"
   async:
     dependency: transitive
     description:
       name: async
-      url: "https://pub.dartlang.org"
+      sha256: "947bfcf187f74dbc5e146c9eb9c0f10c9f8b30743e341481c1e2ed3ecc18c20c"
+      url: "https://pub.dev"
     source: hosted
-    version: "2.9.0"
+    version: "2.11.0"
   basics:
     dependency: transitive
     description:
       name: basics
-      url: "https://pub.dartlang.org"
+      sha256: "41ff8aded84ae174d1df5cce0bcac3ab9070caac9f7da35fd2cc638dfee6163f"
+      url: "https://pub.dev"
     source: hosted
-    version: "0.6.0"
+    version: "0.10.0"
   bitmap:
     dependency: "direct main"
     description:
       name: bitmap
-      url: "https://pub.dartlang.org"
+      sha256: d4ec0147d64eff00efbbeead5c04d517ea4fbb528022adaa3551e3586e80f4d4
+      url: "https://pub.dev"
     source: hosted
-    version: "0.1.2"
+    version: "0.1.3"
   boolean_selector:
     dependency: transitive
     description:
       name: boolean_selector
-      url: "https://pub.dartlang.org"
+      sha256: "6cfb5af12253eaf2b368f07bacc5a80d1301a071c73360d746b7f2e32d762c66"
+      url: "https://pub.dev"
     source: hosted
-    version: "2.1.0"
+    version: "2.1.1"
   characters:
     dependency: transitive
     description:
       name: characters
-      url: "https://pub.dartlang.org"
+      sha256: "04a925763edad70e8443c99234dc3328f442e811f1d8fd1a72f1c8ad0f69a605"
+      url: "https://pub.dev"
     source: hosted
-    version: "1.2.1"
+    version: "1.3.0"
   clock:
     dependency: transitive
     description:
       name: clock
-      url: "https://pub.dartlang.org"
+      sha256: cb6d7f03e1de671e34607e909a7213e31d7752be4fb66a86d29fe1eb14bfb5cf
+      url: "https://pub.dev"
     source: hosted
     version: "1.1.1"
   collection:
     dependency: transitive
     description:
       name: collection
-      url: "https://pub.dartlang.org"
+      sha256: "4a07be6cb69c84d677a6c3096fcf960cc3285a8330b4603e0d463d15d9bd934c"
+      url: "https://pub.dev"
     source: hosted
-    version: "1.16.0"
+    version: "1.17.1"
   convert:
     dependency: transitive
     description:
       name: convert
-      url: "https://pub.dartlang.org"
+      sha256: "0f08b14755d163f6e2134cb58222dd25ea2a2ee8a195e53983d57c075324d592"
+      url: "https://pub.dev"
     source: hosted
     version: "3.1.1"
   crypto:
     dependency: transitive
     description:
       name: crypto
-      url: "https://pub.dartlang.org"
+      sha256: ff625774173754681d66daaf4a448684fb04b78f902da9cb3d308c19cc5e8bab
+      url: "https://pub.dev"
     source: hosted
-    version: "3.0.2"
+    version: "3.0.3"
   cupertino_icons:
     dependency: "direct main"
     description:
       name: cupertino_icons
-      url: "https://pub.dartlang.org"
+      sha256: e35129dc44c9118cee2a5603506d823bab99c68393879edb440e0090d07586be
+      url: "https://pub.dev"
     source: hosted
     version: "1.0.5"
   cyclop:
     dependency: "direct main"
     description:
       name: cyclop
-      url: "https://pub.dartlang.org"
+      sha256: b8e6f0356d87ad6a719c24763bf40b25f8a8554caef4c33056336e54b9fa64c1
+      url: "https://pub.dev"
     source: hosted
-    version: "0.6.0"
+    version: "0.6.2"
   dart_style:
     dependency: transitive
     description:
       name: dart_style
-      url: "https://pub.dartlang.org"
+      sha256: f4f1f73ab3fd2afcbcca165ee601fe980d966af6a21b5970c6c9376955c528ad
+      url: "https://pub.dev"
     source: hosted
-    version: "2.2.4"
+    version: "2.3.1"
   dart_webrtc:
     dependency: transitive
     description:
       name: dart_webrtc
-      url: "https://pub.dartlang.org"
+      sha256: a34e59ac1559cac954e48c9fe156164992163d2f4b7e75d5b0e927ee2f1e4922
+      url: "https://pub.dev"
     source: hosted
-    version: "1.0.9"
+    version: "1.0.16"
   fake_async:
     dependency: transitive
     description:
       name: fake_async
-      url: "https://pub.dartlang.org"
+      sha256: "511392330127add0b769b75a987850d136345d9227c6b94c96a04cf4a391bf78"
+      url: "https://pub.dev"
     source: hosted
     version: "1.3.1"
   ffi:
     dependency: transitive
     description:
       name: ffi
-      url: "https://pub.dartlang.org"
+      sha256: ed5337a5660c506388a9f012be0288fb38b49020ce2b45fe1f8b8323fe429f99
+      url: "https://pub.dev"
     source: hosted
-    version: "1.2.1"
+    version: "2.0.2"
   file:
     dependency: transitive
     description:
       name: file
-      url: "https://pub.dartlang.org"
+      sha256: "1b92bec4fc2a72f59a8e15af5f52cd441e4a7860b49499d69dfa817af20e925d"
+      url: "https://pub.dev"
     source: hosted
     version: "6.1.4"
   fixnum:
     dependency: transitive
     description:
       name: fixnum
-      url: "https://pub.dartlang.org"
+      sha256: "25517a4deb0c03aa0f32fd12db525856438902d9c16536311e76cdc57b31d7d1"
+      url: "https://pub.dev"
     source: hosted
-    version: "1.0.1"
+    version: "1.1.0"
   flutter:
     dependency: "direct main"
     description: flutter
@@ -157,7 +178,8 @@ packages:
     dependency: "direct dev"
     description:
       name: flutter_lints
-      url: "https://pub.dartlang.org"
+      sha256: aeb0b80a8b3709709c9cc496cdc027c5b3216796bc0af0ce1007eaf24464fd4c
+      url: "https://pub.dev"
     source: hosted
     version: "2.0.1"
   flutter_test:
@@ -169,205 +191,250 @@ packages:
     dependency: "direct main"
     description:
       name: flutter_webrtc
-      url: "https://pub.dartlang.org"
+      sha256: "02625651b6b46be39ba1ff26219de704012df571305aef3c7cdafb94f7c50dc6"
+      url: "https://pub.dev"
     source: hosted
-    version: "0.9.11"
+    version: "0.9.31"
   glob:
     dependency: transitive
     description:
       name: glob
-      url: "https://pub.dartlang.org"
+      sha256: "0e7014b3b7d4dac1ca4d6114f82bf1782ee86745b9b42a92c9289c23d8a0ab63"
+      url: "https://pub.dev"
     source: hosted
-    version: "2.1.0"
+    version: "2.1.2"
   googleapis_auth:
     dependency: transitive
     description:
       name: googleapis_auth
-      url: "https://pub.dartlang.org"
+      sha256: af7c3a3edf9d0de2e1e0a77e994fae0a581c525fa7012af4fa0d4a52ed9484da
+      url: "https://pub.dev"
     source: hosted
-    version: "1.3.1"
+    version: "1.4.1"
   grpc:
     dependency: "direct main"
     description:
       name: grpc
-      url: "https://pub.dartlang.org"
+      sha256: a73c16e4f6a4a819be892bb2c73cc1d0b00e36095f69b0738cc91a733e3d27ba
+      url: "https://pub.dev"
     source: hosted
     version: "3.1.0"
   http:
     dependency: transitive
     description:
       name: http
-      url: "https://pub.dartlang.org"
+      sha256: "5895291c13fa8a3bd82e76d5627f69e0d85ca6a30dcac95c4ea19a5d555879c2"
+      url: "https://pub.dev"
     source: hosted
-    version: "0.13.5"
+    version: "0.13.6"
   http2:
     dependency: transitive
     description:
       name: http2
-      url: "https://pub.dartlang.org"
+      sha256: "58805ebc6513eed3b98ee0a455a8357e61d187bf2e0fdc1e53120770f78de258"
+      url: "https://pub.dev"
     source: hosted
     version: "2.0.1"
   http_parser:
     dependency: transitive
     description:
       name: http_parser
-      url: "https://pub.dartlang.org"
+      sha256: "2aa08ce0341cc9b354a498388e30986515406668dbcc4f7c950c3e715496693b"
+      url: "https://pub.dev"
     source: hosted
     version: "4.0.2"
   image:
     dependency: transitive
     description:
       name: image
-      url: "https://pub.dartlang.org"
+      sha256: a72242c9a0ffb65d03de1b7113bc4e189686fc07c7147b8b41811d0dd0e0d9bf
+      url: "https://pub.dev"
     source: hosted
-    version: "3.2.2"
+    version: "4.0.17"
+  js:
+    dependency: transitive
+    description:
+      name: js
+      sha256: f2c445dce49627136094980615a031419f7f3eb393237e4ecd97ac15dea343f3
+      url: "https://pub.dev"
+    source: hosted
+    version: "0.6.7"
   lints:
     dependency: transitive
     description:
       name: lints
-      url: "https://pub.dartlang.org"
+      sha256: "6b0206b0bf4f04961fc5438198ccb3a885685cd67d4d4a32cc20ad7f8adbe015"
+      url: "https://pub.dev"
     source: hosted
-    version: "2.0.1"
+    version: "2.1.0"
   matcher:
     dependency: transitive
     description:
       name: matcher
-      url: "https://pub.dartlang.org"
+      sha256: "6501fbd55da300384b768785b83e5ce66991266cec21af89ab9ae7f5ce1c4cbb"
+      url: "https://pub.dev"
     source: hosted
-    version: "0.12.12"
+    version: "0.12.15"
   material_color_utilities:
     dependency: transitive
     description:
       name: material_color_utilities
-      url: "https://pub.dartlang.org"
+      sha256: d92141dc6fe1dad30722f9aa826c7fbc896d021d792f80678280601aff8cf724
+      url: "https://pub.dev"
     source: hosted
-    version: "0.1.5"
+    version: "0.2.0"
   meta:
     dependency: transitive
     description:
       name: meta
-      url: "https://pub.dartlang.org"
+      sha256: "3c74dbf8763d36539f114c799d8a2d87343b5067e9d796ca22b5eb8437090ee3"
+      url: "https://pub.dev"
     source: hosted
-    version: "1.8.0"
+    version: "1.9.1"
   package_config:
     dependency: transitive
     description:
       name: package_config
-      url: "https://pub.dartlang.org"
+      sha256: "1c5b77ccc91e4823a5af61ee74e6b972db1ef98c2ff5a18d3161c982a55448bd"
+      url: "https://pub.dev"
     source: hosted
     version: "2.1.0"
   path:
     dependency: transitive
     description:
       name: path
-      url: "https://pub.dartlang.org"
+      sha256: "8829d8a55c13fc0e37127c29fedf290c102f4e40ae94ada574091fe0ff96c917"
+      url: "https://pub.dev"
     source: hosted
-    version: "1.8.2"
+    version: "1.8.3"
   path_provider:
     dependency: transitive
     description:
       name: path_provider
-      url: "https://pub.dartlang.org"
+      sha256: "3087813781ab814e4157b172f1a11c46be20179fcc9bea043e0fba36bc0acaa2"
+      url: "https://pub.dev"
     source: hosted
-    version: "2.0.11"
+    version: "2.0.15"
   path_provider_android:
     dependency: transitive
     description:
       name: path_provider_android
-      url: "https://pub.dartlang.org"
+      sha256: "2cec049d282c7f13c594b4a73976b0b4f2d7a1838a6dd5aaf7bd9719196bee86"
+      url: "https://pub.dev"
     source: hosted
-    version: "2.0.20"
-  path_provider_ios:
+    version: "2.0.27"
+  path_provider_foundation:
     dependency: transitive
     description:
-      name: path_provider_ios
-      url: "https://pub.dartlang.org"
+      name: path_provider_foundation
+      sha256: "1995d88ec2948dac43edf8fe58eb434d35d22a2940ecee1a9fefcd62beee6eb3"
+      url: "https://pub.dev"
     source: hosted
-    version: "2.0.11"
+    version: "2.2.3"
   path_provider_linux:
     dependency: transitive
     description:
       name: path_provider_linux
-      url: "https://pub.dartlang.org"
+      sha256: ffbb8cc9ed2c9ec0e4b7a541e56fd79b138e8f47d2fb86815f15358a349b3b57
+      url: "https://pub.dev"
     source: hosted
-    version: "2.1.7"
-  path_provider_macos:
-    dependency: transitive
-    description:
-      name: path_provider_macos
-      url: "https://pub.dartlang.org"
-    source: hosted
-    version: "2.0.6"
+    version: "2.1.11"
   path_provider_platform_interface:
     dependency: transitive
     description:
       name: path_provider_platform_interface
-      url: "https://pub.dartlang.org"
+      sha256: "57585299a729335f1298b43245842678cb9f43a6310351b18fb577d6e33165ec"
+      url: "https://pub.dev"
     source: hosted
-    version: "2.0.5"
+    version: "2.0.6"
   path_provider_windows:
     dependency: transitive
     description:
       name: path_provider_windows
-      url: "https://pub.dartlang.org"
+      sha256: d3f80b32e83ec208ac95253e0cd4d298e104fbc63cb29c5c69edaed43b0c69d6
+      url: "https://pub.dev"
     source: hosted
-    version: "2.0.7"
+    version: "2.1.6"
   petitparser:
     dependency: transitive
     description:
       name: petitparser
-      url: "https://pub.dartlang.org"
+      sha256: cb3798bef7fc021ac45b308f4b51208a152792445cce0448c9a4ba5879dd8750
+      url: "https://pub.dev"
     source: hosted
-    version: "5.1.0"
+    version: "5.4.0"
   platform:
     dependency: transitive
     description:
       name: platform
-      url: "https://pub.dartlang.org"
+      sha256: "4a451831508d7d6ca779f7ac6e212b4023dd5a7d08a27a63da33756410e32b76"
+      url: "https://pub.dev"
     source: hosted
     version: "3.1.0"
+  platform_detect:
+    dependency: transitive
+    description:
+      name: platform_detect
+      sha256: "14afcb6ffcd93745e39a288db53d1d6522ea25d71f7993c13a367a86c437b54d"
+      url: "https://pub.dev"
+    source: hosted
+    version: "2.0.7"
   plugin_platform_interface:
     dependency: transitive
     description:
       name: plugin_platform_interface
-      url: "https://pub.dartlang.org"
+      sha256: "6a2128648c854906c53fa8e33986fc0247a1116122f9534dd20e3ab9e16a32bc"
+      url: "https://pub.dev"
+    source: hosted
+    version: "2.1.4"
+  pointycastle:
+    dependency: transitive
+    description:
+      name: pointycastle
+      sha256: "7c1e5f0d23c9016c5bbd8b1473d0d3fb3fc851b876046039509e18e0c7485f2c"
+      url: "https://pub.dev"
     source: hosted
-    version: "2.1.3"
+    version: "3.7.3"
   process:
     dependency: transitive
     description:
       name: process
-      url: "https://pub.dartlang.org"
+      sha256: "53fd8db9cec1d37b0574e12f07520d582019cb6c44abf5479a01505099a34a09"
+      url: "https://pub.dev"
     source: hosted
     version: "4.2.4"
   protobuf:
     dependency: transitive
     description:
       name: protobuf
-      url: "https://pub.dartlang.org"
+      sha256: "01dd9bd0fa02548bf2ceee13545d4a0ec6046459d847b6b061d8a27237108a08"
+      url: "https://pub.dev"
     source: hosted
     version: "2.1.0"
   protoc_plugin:
     dependency: "direct main"
     description:
       name: protoc_plugin
-      url: "https://pub.dartlang.org"
+      sha256: e2be5014ba145dc0f8de20ac425afa2a513aff64fe350d338e481d40de0573df
+      url: "https://pub.dev"
     source: hosted
     version: "20.0.1"
   pub_semver:
     dependency: transitive
     description:
       name: pub_semver
-      url: "https://pub.dartlang.org"
+      sha256: "40d3ab1bbd474c4c2328c91e3a7df8c6dd629b79ece4c4bd04bee496a224fb0c"
+      url: "https://pub.dev"
     source: hosted
-    version: "2.1.2"
+    version: "2.1.4"
   quiver:
     dependency: transitive
     description:
       name: quiver
-      url: "https://pub.dartlang.org"
+      sha256: b1c1ac5ce6688d77f65f3375a9abb9319b3cb32486bdc7a1e0fdf004d7ba4e47
+      url: "https://pub.dev"
     source: hosted
-    version: "3.1.0"
+    version: "3.2.1"
   sky_engine:
     dependency: transitive
     description: flutter
@@ -377,107 +444,122 @@ packages:
     dependency: transitive
     description:
       name: source_span
-      url: "https://pub.dartlang.org"
+      sha256: dd904f795d4b4f3b870833847c461801f6750a9fa8e61ea5ac53f9422b31f250
+      url: "https://pub.dev"
     source: hosted
-    version: "1.9.0"
+    version: "1.9.1"
   stack_trace:
     dependency: transitive
     description:
       name: stack_trace
-      url: "https://pub.dartlang.org"
+      sha256: c3c7d8edb15bee7f0f74debd4b9c5f3c2ea86766fe4178eb2a18eb30a0bdaed5
+      url: "https://pub.dev"
     source: hosted
-    version: "1.10.0"
+    version: "1.11.0"
   stream_channel:
     dependency: transitive
     description:
       name: stream_channel
-      url: "https://pub.dartlang.org"
+      sha256: "83615bee9045c1d322bbbd1ba209b7a749c2cbcdcb3fdd1df8eb488b3279c1c8"
+      url: "https://pub.dev"
     source: hosted
-    version: "2.1.0"
+    version: "2.1.1"
   string_scanner:
     dependency: transitive
     description:
       name: string_scanner
-      url: "https://pub.dartlang.org"
+      sha256: "556692adab6cfa87322a115640c11f13cb77b3f076ddcc5d6ae3c20242bedcde"
+      url: "https://pub.dev"
     source: hosted
-    version: "1.1.1"
+    version: "1.2.0"
   term_glyph:
     dependency: transitive
     description:
       name: term_glyph
-      url: "https://pub.dartlang.org"
+      sha256: a29248a84fbb7c79282b40b8c72a1209db169a2e0542bce341da992fe1bc7e84
+      url: "https://pub.dev"
     source: hosted
     version: "1.2.1"
   test_api:
     dependency: transitive
     description:
       name: test_api
-      url: "https://pub.dartlang.org"
+      sha256: eb6ac1540b26de412b3403a163d919ba86f6a973fe6cc50ae3541b80092fdcfb
+      url: "https://pub.dev"
     source: hosted
-    version: "0.4.12"
+    version: "0.5.1"
   typed_data:
     dependency: transitive
     description:
       name: typed_data
-      url: "https://pub.dartlang.org"
+      sha256: facc8d6582f16042dd49f2463ff1bd6e2c9ef9f3d5da3d9b087e244a7b564b3c
+      url: "https://pub.dev"
     source: hosted
-    version: "1.3.1"
+    version: "1.3.2"
   vector_math:
     dependency: "direct main"
     description:
       name: vector_math
-      url: "https://pub.dartlang.org"
+      sha256: "80b3257d1492ce4d091729e3a67a60407d227c27241d6927be0130c98e741803"
+      url: "https://pub.dev"
     source: hosted
-    version: "2.1.2"
+    version: "2.1.4"
   watcher:
     dependency: transitive
     description:
       name: watcher
-      url: "https://pub.dartlang.org"
+      sha256: "3d2ad6751b3c16cf07c7fca317a1413b3f26530319181b37e3b9039b84fc01d8"
+      url: "https://pub.dev"
     source: hosted
-    version: "1.0.2"
+    version: "1.1.0"
   web_socket_channel:
     dependency: "direct main"
     description:
       name: web_socket_channel
-      url: "https://pub.dartlang.org"
+      sha256: d88238e5eac9a42bb43ca4e721edba3c08c6354d4a53063afaa568516217621b
+      url: "https://pub.dev"
     source: hosted
-    version: "2.2.0"
+    version: "2.4.0"
   webrtc_interface:
     dependency: transitive
     description:
       name: webrtc_interface
-      url: "https://pub.dartlang.org"
+      sha256: "0ac4693f921c81005edefd2f43b9fe84b0ed54481474fe1ee16b789b0c84a77c"
+      url: "https://pub.dev"
     source: hosted
-    version: "1.0.8"
+    version: "1.0.13"
   win32:
     dependency: transitive
     description:
       name: win32
-      url: "https://pub.dartlang.org"
+      sha256: "5a751eddf9db89b3e5f9d50c20ab8612296e4e8db69009788d6c8b060a84191c"
+      url: "https://pub.dev"
     source: hosted
-    version: "2.6.1"
+    version: "4.1.4"
   xdg_directories:
     dependency: transitive
     description:
       name: xdg_directories
-      url: "https://pub.dartlang.org"
+      sha256: ee1505df1426458f7f60aac270645098d318a8b4766d85fde75f76f2e21807d1
+      url: "https://pub.dev"
     source: hosted
-    version: "0.2.0+2"
+    version: "1.0.0"
   xml:
     dependency: transitive
     description:
       name: xml
-      url: "https://pub.dartlang.org"
+      sha256: "5bc72e1e45e941d825fd7468b9b4cc3b9327942649aeb6fc5cdbf135f0a86e84"
+      url: "https://pub.dev"
     source: hosted
-    version: "6.1.0"
+    version: "6.3.0"
   yaml:
     dependency: transitive
     description:
       name: yaml
-      url: "https://pub.dartlang.org"
+      sha256: "75769501ea3489fca56601ff33454fe45507ea3bfb014161abc3b43ae25989d5"
+      url: "https://pub.dev"
     source: hosted
-    version: "3.1.1"
+    version: "3.1.2"
 sdks:
-  dart: ">=2.18.0 <3.0.0"
-  flutter: ">=2.10.0"
+  dart: ">=3.0.0 <4.0.0"
+  flutter: ">=3.3.0"

+ 0 - 1
server/go.mod

@@ -33,6 +33,5 @@ require (
 	golang.org/x/net v0.0.0-20221004154528-8021a29435af // indirect
 	golang.org/x/sys v0.0.0-20221010170243-090e33056c14 // indirect
 	golang.org/x/text v0.3.7 // indirect
-	google.golang.org/genproto v0.0.0-20210126160654-44e461bb6506 // indirect
 	gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b // indirect
 )

+ 44 - 75
server/main.go

@@ -1,8 +1,6 @@
 package main
 
 import (
-	"context"
-	"flag"
 	"log"
 	"math/rand"
 	"net/http"
@@ -23,69 +21,16 @@ import (
 	"github.com/sirupsen/logrus"
 )
 
-var (
-	tls        = flag.Bool("tls", false, "Connection uses TLS if true, else plain TCP")
-	certFile   = flag.String("cert_file", "", "The TLS cert file")
-	keyFile    = flag.String("key_file", "", "The TLS key file")
-	jsonDBFile = flag.String("json_db_file", "", "A json file containing a list of features")
-	port       = flag.Int("port", 8080, "The server port")
-)
-
 var upgrader = websocket.Upgrader{
 	CheckOrigin: func(r *http.Request) bool { return true },
 }
 
-type server struct {
-	pb.UnimplementedPaintClientServer
-	// savedFeatures []*pb.Feature // read-only after initialized
-	//
-	// mu         sync.Mutex // protects routeNotes
-	// routeNotes map[string][]*pb.RouteNote
-}
-
 func main() {
 	http.HandleFunc("/ws", websocketHandler)
 	logrus.Fatal(http.ListenAndServe(":8081", nil))
 
 }
 
-func (s *server) Paint(ctx context.Context, request *pb.PaintRequest) (*pb.PaintReply, error) {
-	panic("implement me")
-}
-
-func (s *server) Monitor(sub *pb.MonitorSub, monitorServer pb.PaintClient_MonitorServer) error {
-
-	// for k := 0; k < 4; k++ {
-	// 	points := []*pb.MonitorPoint{}
-	// 	for i := 64*k; i < 64*(k+1); i++ {
-	// 		for j := 0; j < 256; j++ {
-	// 			points = append(points, &pb.MonitorPoint{
-	// 				Point: &pb.BPoint{
-	// 					X: int32(i),
-	// 					Y: int32(j),
-	// 				},
-	// 				Color: &pb.BColor{
-	// 					Rgba: uint32(rand.Intn(256)) | uint32(rand.Intn(256))<<8 | uint32(rand.Intn(256))<<16 | 255<<24,
-	// 				},
-	// 			})
-	// 		}
-	// 	}
-	//
-	// 	monitorServer.Send(&pb.MonitorReply{
-	// 		Points: points,
-	// 		// BytesPoints: b.Bytes(),
-	// 	})
-	// }
-	points := Getp(0, 0)
-
-	monitorServer.Send(&pb.MonitorReply{
-		Points: points,
-		// BytesPoints: b.Bytes(),
-	})
-
-	return nil
-}
-
 func Getp(ii int, jj int) []*pb.MonitorPoint {
 	points := []*pb.MonitorPoint{}
 	for i := ii * 32; i < 32*(1+ii); i++ {
@@ -141,39 +86,63 @@ func websocketHandler(w http.ResponseWriter, r *http.Request) {
 		logrus.Error(err)
 	}
 	// Create DataChannel.
-	sendChannel, err := pc.CreateDataChannel("data", nil)
+	f := false
+	sendChannel, err := pc.CreateDataChannel("data", &webrtc.DataChannelInit{
+		Ordered: &f,
+	})
 	if err != nil {
 		logrus.Error(err)
 	}
+
 	sendChannel.OnClose(func() {
 		logrus.Println("sendChannel has closed")
 	})
 	sendChannel.OnOpen(func() {
 		logrus.Println("sendChannel has opened")
+	})
+	sendChannel.OnMessage(func(msg webrtc.DataChannelMessage) {
+		data := &pb.WebRTCRequest{}
+		err := proto.Unmarshal(msg.Data, data)
+		if err != nil {
+			logrus.WithField("data", data).Error(err)
+			return
+		}
+		logrus.WithField("data", data).Info("req")
+
+		if data.GetRequestData() != nil {
+			for i := 0; i < 8; i++ {
+				for j := 0; j < 8; j++ {
+					data := &pb.MonitorReply{
+						Points: Getp(i, j),
+					}
+					md, err := proto.Marshal(data)
+					if err != nil {
+						logrus.Error(err)
+						continue
+					}
+
+					logrus.Info(len(md))
+					sendChannel.Send(md)
+					// err = sendChannel.Send(md)
+					// if err != nil {
+					// 	logrus.Error(err)
+					// 	continue
+					// }
 
-		for i := 0; i < 8; i++ {
-			for j := 0; j < 8; j++ {
-				data := &pb.MonitorReply{
-					Points: Getp(i, j),
-				}
-				md, err := proto.Marshal(data)
-				if err != nil {
-					logrus.Error(err)
-					continue
-				}
-
-				err = sendChannel.Send(md)
-				if err != nil {
-					logrus.Error(err)
-					continue
 				}
-
 			}
 		}
 	})
-	sendChannel.OnMessage(func(msg webrtc.DataChannelMessage) {
-		logrus.Println(string(msg.Data))
-	})
+
+	// Create a video track
+	videoTrack, err := webrtc.NewTrackLocalStaticRTP(webrtc.RTPCodecCapability{MimeType: webrtc.MimeTypeVP8}, "video", "pion")
+	if err != nil {
+		panic(err)
+	}
+	_, err = pc.AddTrack(videoTrack)
+	if err != nil {
+		panic(err)
+	}
 
 	// Create offer
 	offer, err := pc.CreateOffer(nil)

+ 538 - 125
server/protos/client.pb.go

@@ -20,17 +20,17 @@ const (
 	_ = protoimpl.EnforceVersion(protoimpl.MaxVersion - 20)
 )
 
-type MonitorSub struct {
+type RequestData struct {
 	state         protoimpl.MessageState
 	sizeCache     protoimpl.SizeCache
 	unknownFields protoimpl.UnknownFields
 
-	Tile        *BPoint `protobuf:"bytes,1,opt,name=tile,proto3" json:"tile,omitempty"`
-	Susbscribed bool    `protobuf:"varint,2,opt,name=susbscribed,proto3" json:"susbscribed,omitempty"`
+	Tile *BPoint `protobuf:"bytes,1,opt,name=tile,proto3" json:"tile,omitempty"`
+	From uint64  `protobuf:"varint,2,opt,name=from,proto3" json:"from,omitempty"`
 }
 
-func (x *MonitorSub) Reset() {
-	*x = MonitorSub{}
+func (x *RequestData) Reset() {
+	*x = RequestData{}
 	if protoimpl.UnsafeEnabled {
 		mi := &file_protos_client_proto_msgTypes[0]
 		ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
@@ -38,13 +38,13 @@ func (x *MonitorSub) Reset() {
 	}
 }
 
-func (x *MonitorSub) String() string {
+func (x *RequestData) String() string {
 	return protoimpl.X.MessageStringOf(x)
 }
 
-func (*MonitorSub) ProtoMessage() {}
+func (*RequestData) ProtoMessage() {}
 
-func (x *MonitorSub) ProtoReflect() protoreflect.Message {
+func (x *RequestData) ProtoReflect() protoreflect.Message {
 	mi := &file_protos_client_proto_msgTypes[0]
 	if protoimpl.UnsafeEnabled && x != nil {
 		ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
@@ -56,25 +56,289 @@ func (x *MonitorSub) ProtoReflect() protoreflect.Message {
 	return mi.MessageOf(x)
 }
 
-// Deprecated: Use MonitorSub.ProtoReflect.Descriptor instead.
-func (*MonitorSub) Descriptor() ([]byte, []int) {
+// Deprecated: Use RequestData.ProtoReflect.Descriptor instead.
+func (*RequestData) Descriptor() ([]byte, []int) {
 	return file_protos_client_proto_rawDescGZIP(), []int{0}
 }
 
-func (x *MonitorSub) GetTile() *BPoint {
+func (x *RequestData) GetTile() *BPoint {
 	if x != nil {
 		return x.Tile
 	}
 	return nil
 }
 
-func (x *MonitorSub) GetSusbscribed() bool {
+func (x *RequestData) GetFrom() uint64 {
 	if x != nil {
-		return x.Susbscribed
+		return x.From
 	}
-	return false
+	return 0
+}
+
+type WebRTCRequest struct {
+	state         protoimpl.MessageState
+	sizeCache     protoimpl.SizeCache
+	unknownFields protoimpl.UnknownFields
+
+	// Types that are assignable to WebRTCRequestOneOf:
+	//	*WebRTCRequest_RequestData
+	WebRTCRequestOneOf isWebRTCRequest_WebRTCRequestOneOf `protobuf_oneof:"WebRTCRequestOneOf"`
+}
+
+func (x *WebRTCRequest) Reset() {
+	*x = WebRTCRequest{}
+	if protoimpl.UnsafeEnabled {
+		mi := &file_protos_client_proto_msgTypes[1]
+		ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
+		ms.StoreMessageInfo(mi)
+	}
+}
+
+func (x *WebRTCRequest) String() string {
+	return protoimpl.X.MessageStringOf(x)
+}
+
+func (*WebRTCRequest) ProtoMessage() {}
+
+func (x *WebRTCRequest) ProtoReflect() protoreflect.Message {
+	mi := &file_protos_client_proto_msgTypes[1]
+	if protoimpl.UnsafeEnabled && x != nil {
+		ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
+		if ms.LoadMessageInfo() == nil {
+			ms.StoreMessageInfo(mi)
+		}
+		return ms
+	}
+	return mi.MessageOf(x)
+}
+
+// Deprecated: Use WebRTCRequest.ProtoReflect.Descriptor instead.
+func (*WebRTCRequest) Descriptor() ([]byte, []int) {
+	return file_protos_client_proto_rawDescGZIP(), []int{1}
+}
+
+func (m *WebRTCRequest) GetWebRTCRequestOneOf() isWebRTCRequest_WebRTCRequestOneOf {
+	if m != nil {
+		return m.WebRTCRequestOneOf
+	}
+	return nil
+}
+
+func (x *WebRTCRequest) GetRequestData() *RequestData {
+	if x, ok := x.GetWebRTCRequestOneOf().(*WebRTCRequest_RequestData); ok {
+		return x.RequestData
+	}
+	return nil
+}
+
+type isWebRTCRequest_WebRTCRequestOneOf interface {
+	isWebRTCRequest_WebRTCRequestOneOf()
+}
+
+type WebRTCRequest_RequestData struct {
+	RequestData *RequestData `protobuf:"bytes,1,opt,name=requestData,proto3,oneof"`
+}
+
+func (*WebRTCRequest_RequestData) isWebRTCRequest_WebRTCRequestOneOf() {}
+
+type WebRTCReply struct {
+	state         protoimpl.MessageState
+	sizeCache     protoimpl.SizeCache
+	unknownFields protoimpl.UnknownFields
+
+	// Types that are assignable to WebRTCReplyOneOf:
+	//	*WebRTCReply_RequestData
+	WebRTCReplyOneOf isWebRTCReply_WebRTCReplyOneOf `protobuf_oneof:"WebRTCReplyOneOf"`
+}
+
+func (x *WebRTCReply) Reset() {
+	*x = WebRTCReply{}
+	if protoimpl.UnsafeEnabled {
+		mi := &file_protos_client_proto_msgTypes[2]
+		ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
+		ms.StoreMessageInfo(mi)
+	}
+}
+
+func (x *WebRTCReply) String() string {
+	return protoimpl.X.MessageStringOf(x)
+}
+
+func (*WebRTCReply) ProtoMessage() {}
+
+func (x *WebRTCReply) ProtoReflect() protoreflect.Message {
+	mi := &file_protos_client_proto_msgTypes[2]
+	if protoimpl.UnsafeEnabled && x != nil {
+		ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
+		if ms.LoadMessageInfo() == nil {
+			ms.StoreMessageInfo(mi)
+		}
+		return ms
+	}
+	return mi.MessageOf(x)
+}
+
+// Deprecated: Use WebRTCReply.ProtoReflect.Descriptor instead.
+func (*WebRTCReply) Descriptor() ([]byte, []int) {
+	return file_protos_client_proto_rawDescGZIP(), []int{2}
+}
+
+func (m *WebRTCReply) GetWebRTCReplyOneOf() isWebRTCReply_WebRTCReplyOneOf {
+	if m != nil {
+		return m.WebRTCReplyOneOf
+	}
+	return nil
+}
+
+func (x *WebRTCReply) GetRequestData() *RequestData {
+	if x, ok := x.GetWebRTCReplyOneOf().(*WebRTCReply_RequestData); ok {
+		return x.RequestData
+	}
+	return nil
+}
+
+type isWebRTCReply_WebRTCReplyOneOf interface {
+	isWebRTCReply_WebRTCReplyOneOf()
+}
+
+type WebRTCReply_RequestData struct {
+	RequestData *RequestData `protobuf:"bytes,1,opt,name=requestData,proto3,oneof"`
+}
+
+func (*WebRTCReply_RequestData) isWebRTCReply_WebRTCReplyOneOf() {}
+
+type WebSocketRequest struct {
+	state         protoimpl.MessageState
+	sizeCache     protoimpl.SizeCache
+	unknownFields protoimpl.UnknownFields
+
+	// Types that are assignable to WebSocketRequestOneOf:
+	//	*WebSocketRequest_RequestData
+	WebSocketRequestOneOf isWebSocketRequest_WebSocketRequestOneOf `protobuf_oneof:"WebSocketRequestOneOf"`
+}
+
+func (x *WebSocketRequest) Reset() {
+	*x = WebSocketRequest{}
+	if protoimpl.UnsafeEnabled {
+		mi := &file_protos_client_proto_msgTypes[3]
+		ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
+		ms.StoreMessageInfo(mi)
+	}
+}
+
+func (x *WebSocketRequest) String() string {
+	return protoimpl.X.MessageStringOf(x)
+}
+
+func (*WebSocketRequest) ProtoMessage() {}
+
+func (x *WebSocketRequest) ProtoReflect() protoreflect.Message {
+	mi := &file_protos_client_proto_msgTypes[3]
+	if protoimpl.UnsafeEnabled && x != nil {
+		ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
+		if ms.LoadMessageInfo() == nil {
+			ms.StoreMessageInfo(mi)
+		}
+		return ms
+	}
+	return mi.MessageOf(x)
+}
+
+// Deprecated: Use WebSocketRequest.ProtoReflect.Descriptor instead.
+func (*WebSocketRequest) Descriptor() ([]byte, []int) {
+	return file_protos_client_proto_rawDescGZIP(), []int{3}
+}
+
+func (m *WebSocketRequest) GetWebSocketRequestOneOf() isWebSocketRequest_WebSocketRequestOneOf {
+	if m != nil {
+		return m.WebSocketRequestOneOf
+	}
+	return nil
+}
+
+func (x *WebSocketRequest) GetRequestData() *RequestData {
+	if x, ok := x.GetWebSocketRequestOneOf().(*WebSocketRequest_RequestData); ok {
+		return x.RequestData
+	}
+	return nil
+}
+
+type isWebSocketRequest_WebSocketRequestOneOf interface {
+	isWebSocketRequest_WebSocketRequestOneOf()
+}
+
+type WebSocketRequest_RequestData struct {
+	RequestData *RequestData `protobuf:"bytes,1,opt,name=requestData,proto3,oneof"`
+}
+
+func (*WebSocketRequest_RequestData) isWebSocketRequest_WebSocketRequestOneOf() {}
+
+type WebSocketReply struct {
+	state         protoimpl.MessageState
+	sizeCache     protoimpl.SizeCache
+	unknownFields protoimpl.UnknownFields
+
+	// Types that are assignable to WebSocketReplyOneOf:
+	//	*WebSocketReply_RequestData
+	WebSocketReplyOneOf isWebSocketReply_WebSocketReplyOneOf `protobuf_oneof:"WebSocketReplyOneOf"`
+}
+
+func (x *WebSocketReply) Reset() {
+	*x = WebSocketReply{}
+	if protoimpl.UnsafeEnabled {
+		mi := &file_protos_client_proto_msgTypes[4]
+		ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
+		ms.StoreMessageInfo(mi)
+	}
+}
+
+func (x *WebSocketReply) String() string {
+	return protoimpl.X.MessageStringOf(x)
+}
+
+func (*WebSocketReply) ProtoMessage() {}
+
+func (x *WebSocketReply) ProtoReflect() protoreflect.Message {
+	mi := &file_protos_client_proto_msgTypes[4]
+	if protoimpl.UnsafeEnabled && x != nil {
+		ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
+		if ms.LoadMessageInfo() == nil {
+			ms.StoreMessageInfo(mi)
+		}
+		return ms
+	}
+	return mi.MessageOf(x)
 }
 
+// Deprecated: Use WebSocketReply.ProtoReflect.Descriptor instead.
+func (*WebSocketReply) Descriptor() ([]byte, []int) {
+	return file_protos_client_proto_rawDescGZIP(), []int{4}
+}
+
+func (m *WebSocketReply) GetWebSocketReplyOneOf() isWebSocketReply_WebSocketReplyOneOf {
+	if m != nil {
+		return m.WebSocketReplyOneOf
+	}
+	return nil
+}
+
+func (x *WebSocketReply) GetRequestData() *RequestData {
+	if x, ok := x.GetWebSocketReplyOneOf().(*WebSocketReply_RequestData); ok {
+		return x.RequestData
+	}
+	return nil
+}
+
+type isWebSocketReply_WebSocketReplyOneOf interface {
+	isWebSocketReply_WebSocketReplyOneOf()
+}
+
+type WebSocketReply_RequestData struct {
+	RequestData *RequestData `protobuf:"bytes,1,opt,name=requestData,proto3,oneof"`
+}
+
+func (*WebSocketReply_RequestData) isWebSocketReply_WebSocketReplyOneOf() {}
+
 type MonitorPoint struct {
 	state         protoimpl.MessageState
 	sizeCache     protoimpl.SizeCache
@@ -87,7 +351,7 @@ type MonitorPoint struct {
 func (x *MonitorPoint) Reset() {
 	*x = MonitorPoint{}
 	if protoimpl.UnsafeEnabled {
-		mi := &file_protos_client_proto_msgTypes[1]
+		mi := &file_protos_client_proto_msgTypes[5]
 		ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
 		ms.StoreMessageInfo(mi)
 	}
@@ -100,7 +364,7 @@ func (x *MonitorPoint) String() string {
 func (*MonitorPoint) ProtoMessage() {}
 
 func (x *MonitorPoint) ProtoReflect() protoreflect.Message {
-	mi := &file_protos_client_proto_msgTypes[1]
+	mi := &file_protos_client_proto_msgTypes[5]
 	if protoimpl.UnsafeEnabled && x != nil {
 		ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
 		if ms.LoadMessageInfo() == nil {
@@ -113,7 +377,7 @@ func (x *MonitorPoint) ProtoReflect() protoreflect.Message {
 
 // Deprecated: Use MonitorPoint.ProtoReflect.Descriptor instead.
 func (*MonitorPoint) Descriptor() ([]byte, []int) {
-	return file_protos_client_proto_rawDescGZIP(), []int{1}
+	return file_protos_client_proto_rawDescGZIP(), []int{5}
 }
 
 func (x *MonitorPoint) GetPoint() *BPoint {
@@ -135,14 +399,14 @@ type MonitorReply struct {
 	sizeCache     protoimpl.SizeCache
 	unknownFields protoimpl.UnknownFields
 
-	Points      []*MonitorPoint `protobuf:"bytes,1,rep,name=points,proto3" json:"points,omitempty"`
-	BytesPoints []byte          `protobuf:"bytes,2,opt,name=bytes_points,json=bytesPoints,proto3" json:"bytes_points,omitempty"`
+	Points []*MonitorPoint `protobuf:"bytes,1,rep,name=points,proto3" json:"points,omitempty"`
+	Tile   *BPoint         `protobuf:"bytes,2,opt,name=tile,proto3" json:"tile,omitempty"`
 }
 
 func (x *MonitorReply) Reset() {
 	*x = MonitorReply{}
 	if protoimpl.UnsafeEnabled {
-		mi := &file_protos_client_proto_msgTypes[2]
+		mi := &file_protos_client_proto_msgTypes[6]
 		ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
 		ms.StoreMessageInfo(mi)
 	}
@@ -155,7 +419,7 @@ func (x *MonitorReply) String() string {
 func (*MonitorReply) ProtoMessage() {}
 
 func (x *MonitorReply) ProtoReflect() protoreflect.Message {
-	mi := &file_protos_client_proto_msgTypes[2]
+	mi := &file_protos_client_proto_msgTypes[6]
 	if protoimpl.UnsafeEnabled && x != nil {
 		ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
 		if ms.LoadMessageInfo() == nil {
@@ -168,7 +432,7 @@ func (x *MonitorReply) ProtoReflect() protoreflect.Message {
 
 // Deprecated: Use MonitorReply.ProtoReflect.Descriptor instead.
 func (*MonitorReply) Descriptor() ([]byte, []int) {
-	return file_protos_client_proto_rawDescGZIP(), []int{2}
+	return file_protos_client_proto_rawDescGZIP(), []int{6}
 }
 
 func (x *MonitorReply) GetPoints() []*MonitorPoint {
@@ -178,9 +442,64 @@ func (x *MonitorReply) GetPoints() []*MonitorPoint {
 	return nil
 }
 
-func (x *MonitorReply) GetBytesPoints() []byte {
+func (x *MonitorReply) GetTile() *BPoint {
 	if x != nil {
-		return x.BytesPoints
+		return x.Tile
+	}
+	return nil
+}
+
+type MonitorRequest struct {
+	state         protoimpl.MessageState
+	sizeCache     protoimpl.SizeCache
+	unknownFields protoimpl.UnknownFields
+
+	Last int64   `protobuf:"varint,1,opt,name=last,proto3" json:"last,omitempty"`
+	Tile *BPoint `protobuf:"bytes,2,opt,name=tile,proto3" json:"tile,omitempty"`
+}
+
+func (x *MonitorRequest) Reset() {
+	*x = MonitorRequest{}
+	if protoimpl.UnsafeEnabled {
+		mi := &file_protos_client_proto_msgTypes[7]
+		ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
+		ms.StoreMessageInfo(mi)
+	}
+}
+
+func (x *MonitorRequest) String() string {
+	return protoimpl.X.MessageStringOf(x)
+}
+
+func (*MonitorRequest) ProtoMessage() {}
+
+func (x *MonitorRequest) ProtoReflect() protoreflect.Message {
+	mi := &file_protos_client_proto_msgTypes[7]
+	if protoimpl.UnsafeEnabled && x != nil {
+		ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
+		if ms.LoadMessageInfo() == nil {
+			ms.StoreMessageInfo(mi)
+		}
+		return ms
+	}
+	return mi.MessageOf(x)
+}
+
+// Deprecated: Use MonitorRequest.ProtoReflect.Descriptor instead.
+func (*MonitorRequest) Descriptor() ([]byte, []int) {
+	return file_protos_client_proto_rawDescGZIP(), []int{7}
+}
+
+func (x *MonitorRequest) GetLast() int64 {
+	if x != nil {
+		return x.Last
+	}
+	return 0
+}
+
+func (x *MonitorRequest) GetTile() *BPoint {
+	if x != nil {
+		return x.Tile
 	}
 	return nil
 }
@@ -196,7 +515,7 @@ type MonitorPointList struct {
 func (x *MonitorPointList) Reset() {
 	*x = MonitorPointList{}
 	if protoimpl.UnsafeEnabled {
-		mi := &file_protos_client_proto_msgTypes[3]
+		mi := &file_protos_client_proto_msgTypes[8]
 		ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
 		ms.StoreMessageInfo(mi)
 	}
@@ -209,7 +528,7 @@ func (x *MonitorPointList) String() string {
 func (*MonitorPointList) ProtoMessage() {}
 
 func (x *MonitorPointList) ProtoReflect() protoreflect.Message {
-	mi := &file_protos_client_proto_msgTypes[3]
+	mi := &file_protos_client_proto_msgTypes[8]
 	if protoimpl.UnsafeEnabled && x != nil {
 		ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
 		if ms.LoadMessageInfo() == nil {
@@ -222,7 +541,7 @@ func (x *MonitorPointList) ProtoReflect() protoreflect.Message {
 
 // Deprecated: Use MonitorPointList.ProtoReflect.Descriptor instead.
 func (*MonitorPointList) Descriptor() ([]byte, []int) {
-	return file_protos_client_proto_rawDescGZIP(), []int{3}
+	return file_protos_client_proto_rawDescGZIP(), []int{8}
 }
 
 func (x *MonitorPointList) GetPoints() []*MonitorPoint {
@@ -244,7 +563,7 @@ type BPoint struct {
 func (x *BPoint) Reset() {
 	*x = BPoint{}
 	if protoimpl.UnsafeEnabled {
-		mi := &file_protos_client_proto_msgTypes[4]
+		mi := &file_protos_client_proto_msgTypes[9]
 		ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
 		ms.StoreMessageInfo(mi)
 	}
@@ -257,7 +576,7 @@ func (x *BPoint) String() string {
 func (*BPoint) ProtoMessage() {}
 
 func (x *BPoint) ProtoReflect() protoreflect.Message {
-	mi := &file_protos_client_proto_msgTypes[4]
+	mi := &file_protos_client_proto_msgTypes[9]
 	if protoimpl.UnsafeEnabled && x != nil {
 		ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
 		if ms.LoadMessageInfo() == nil {
@@ -270,7 +589,7 @@ func (x *BPoint) ProtoReflect() protoreflect.Message {
 
 // Deprecated: Use BPoint.ProtoReflect.Descriptor instead.
 func (*BPoint) Descriptor() ([]byte, []int) {
-	return file_protos_client_proto_rawDescGZIP(), []int{4}
+	return file_protos_client_proto_rawDescGZIP(), []int{9}
 }
 
 func (x *BPoint) GetX() int32 {
@@ -298,7 +617,7 @@ type BColor struct {
 func (x *BColor) Reset() {
 	*x = BColor{}
 	if protoimpl.UnsafeEnabled {
-		mi := &file_protos_client_proto_msgTypes[5]
+		mi := &file_protos_client_proto_msgTypes[10]
 		ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
 		ms.StoreMessageInfo(mi)
 	}
@@ -311,7 +630,7 @@ func (x *BColor) String() string {
 func (*BColor) ProtoMessage() {}
 
 func (x *BColor) ProtoReflect() protoreflect.Message {
-	mi := &file_protos_client_proto_msgTypes[5]
+	mi := &file_protos_client_proto_msgTypes[10]
 	if protoimpl.UnsafeEnabled && x != nil {
 		ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
 		if ms.LoadMessageInfo() == nil {
@@ -324,7 +643,7 @@ func (x *BColor) ProtoReflect() protoreflect.Message {
 
 // Deprecated: Use BColor.ProtoReflect.Descriptor instead.
 func (*BColor) Descriptor() ([]byte, []int) {
-	return file_protos_client_proto_rawDescGZIP(), []int{5}
+	return file_protos_client_proto_rawDescGZIP(), []int{10}
 }
 
 func (x *BColor) GetRgba() uint32 {
@@ -346,7 +665,7 @@ type PaintRequest struct {
 func (x *PaintRequest) Reset() {
 	*x = PaintRequest{}
 	if protoimpl.UnsafeEnabled {
-		mi := &file_protos_client_proto_msgTypes[6]
+		mi := &file_protos_client_proto_msgTypes[11]
 		ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
 		ms.StoreMessageInfo(mi)
 	}
@@ -359,7 +678,7 @@ func (x *PaintRequest) String() string {
 func (*PaintRequest) ProtoMessage() {}
 
 func (x *PaintRequest) ProtoReflect() protoreflect.Message {
-	mi := &file_protos_client_proto_msgTypes[6]
+	mi := &file_protos_client_proto_msgTypes[11]
 	if protoimpl.UnsafeEnabled && x != nil {
 		ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
 		if ms.LoadMessageInfo() == nil {
@@ -372,7 +691,7 @@ func (x *PaintRequest) ProtoReflect() protoreflect.Message {
 
 // Deprecated: Use PaintRequest.ProtoReflect.Descriptor instead.
 func (*PaintRequest) Descriptor() ([]byte, []int) {
-	return file_protos_client_proto_rawDescGZIP(), []int{6}
+	return file_protos_client_proto_rawDescGZIP(), []int{11}
 }
 
 func (x *PaintRequest) GetPoint() *BPoint {
@@ -401,7 +720,7 @@ type PaintReply struct {
 func (x *PaintReply) Reset() {
 	*x = PaintReply{}
 	if protoimpl.UnsafeEnabled {
-		mi := &file_protos_client_proto_msgTypes[7]
+		mi := &file_protos_client_proto_msgTypes[12]
 		ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
 		ms.StoreMessageInfo(mi)
 	}
@@ -414,7 +733,7 @@ func (x *PaintReply) String() string {
 func (*PaintReply) ProtoMessage() {}
 
 func (x *PaintReply) ProtoReflect() protoreflect.Message {
-	mi := &file_protos_client_proto_msgTypes[7]
+	mi := &file_protos_client_proto_msgTypes[12]
 	if protoimpl.UnsafeEnabled && x != nil {
 		ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
 		if ms.LoadMessageInfo() == nil {
@@ -427,7 +746,7 @@ func (x *PaintReply) ProtoReflect() protoreflect.Message {
 
 // Deprecated: Use PaintReply.ProtoReflect.Descriptor instead.
 func (*PaintReply) Descriptor() ([]byte, []int) {
-	return file_protos_client_proto_rawDescGZIP(), []int{7}
+	return file_protos_client_proto_rawDescGZIP(), []int{12}
 }
 
 func (x *PaintReply) GetMessage() string {
@@ -449,7 +768,7 @@ type RTCData struct {
 func (x *RTCData) Reset() {
 	*x = RTCData{}
 	if protoimpl.UnsafeEnabled {
-		mi := &file_protos_client_proto_msgTypes[8]
+		mi := &file_protos_client_proto_msgTypes[13]
 		ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
 		ms.StoreMessageInfo(mi)
 	}
@@ -462,7 +781,7 @@ func (x *RTCData) String() string {
 func (*RTCData) ProtoMessage() {}
 
 func (x *RTCData) ProtoReflect() protoreflect.Message {
-	mi := &file_protos_client_proto_msgTypes[8]
+	mi := &file_protos_client_proto_msgTypes[13]
 	if protoimpl.UnsafeEnabled && x != nil {
 		ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
 		if ms.LoadMessageInfo() == nil {
@@ -475,7 +794,7 @@ func (x *RTCData) ProtoReflect() protoreflect.Message {
 
 // Deprecated: Use RTCData.ProtoReflect.Descriptor instead.
 func (*RTCData) Descriptor() ([]byte, []int) {
-	return file_protos_client_proto_rawDescGZIP(), []int{8}
+	return file_protos_client_proto_rawDescGZIP(), []int{13}
 }
 
 func (x *RTCData) GetEvent() string {
@@ -496,51 +815,68 @@ var File_protos_client_proto protoreflect.FileDescriptor
 
 var file_protos_client_proto_rawDesc = []byte{
 	0x0a, 0x13, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x73, 0x2f, 0x63, 0x6c, 0x69, 0x65, 0x6e, 0x74, 0x2e,
-	0x70, 0x72, 0x6f, 0x74, 0x6f, 0x22, 0x4b, 0x0a, 0x0a, 0x4d, 0x6f, 0x6e, 0x69, 0x74, 0x6f, 0x72,
-	0x53, 0x75, 0x62, 0x12, 0x1b, 0x0a, 0x04, 0x74, 0x69, 0x6c, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28,
-	0x0b, 0x32, 0x07, 0x2e, 0x42, 0x50, 0x6f, 0x69, 0x6e, 0x74, 0x52, 0x04, 0x74, 0x69, 0x6c, 0x65,
-	0x12, 0x20, 0x0a, 0x0b, 0x73, 0x75, 0x73, 0x62, 0x73, 0x63, 0x72, 0x69, 0x62, 0x65, 0x64, 0x18,
-	0x02, 0x20, 0x01, 0x28, 0x08, 0x52, 0x0b, 0x73, 0x75, 0x73, 0x62, 0x73, 0x63, 0x72, 0x69, 0x62,
-	0x65, 0x64, 0x22, 0x4c, 0x0a, 0x0c, 0x4d, 0x6f, 0x6e, 0x69, 0x74, 0x6f, 0x72, 0x50, 0x6f, 0x69,
-	0x6e, 0x74, 0x12, 0x1d, 0x0a, 0x05, 0x70, 0x6f, 0x69, 0x6e, 0x74, 0x18, 0x01, 0x20, 0x01, 0x28,
-	0x0b, 0x32, 0x07, 0x2e, 0x42, 0x50, 0x6f, 0x69, 0x6e, 0x74, 0x52, 0x05, 0x70, 0x6f, 0x69, 0x6e,
-	0x74, 0x12, 0x1d, 0x0a, 0x05, 0x63, 0x6f, 0x6c, 0x6f, 0x72, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b,
-	0x32, 0x07, 0x2e, 0x42, 0x43, 0x6f, 0x6c, 0x6f, 0x72, 0x52, 0x05, 0x63, 0x6f, 0x6c, 0x6f, 0x72,
-	0x22, 0x58, 0x0a, 0x0c, 0x4d, 0x6f, 0x6e, 0x69, 0x74, 0x6f, 0x72, 0x52, 0x65, 0x70, 0x6c, 0x79,
-	0x12, 0x25, 0x0a, 0x06, 0x70, 0x6f, 0x69, 0x6e, 0x74, 0x73, 0x18, 0x01, 0x20, 0x03, 0x28, 0x0b,
-	0x32, 0x0d, 0x2e, 0x4d, 0x6f, 0x6e, 0x69, 0x74, 0x6f, 0x72, 0x50, 0x6f, 0x69, 0x6e, 0x74, 0x52,
-	0x06, 0x70, 0x6f, 0x69, 0x6e, 0x74, 0x73, 0x12, 0x21, 0x0a, 0x0c, 0x62, 0x79, 0x74, 0x65, 0x73,
-	0x5f, 0x70, 0x6f, 0x69, 0x6e, 0x74, 0x73, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x0b, 0x62,
-	0x79, 0x74, 0x65, 0x73, 0x50, 0x6f, 0x69, 0x6e, 0x74, 0x73, 0x22, 0x39, 0x0a, 0x10, 0x4d, 0x6f,
-	0x6e, 0x69, 0x74, 0x6f, 0x72, 0x50, 0x6f, 0x69, 0x6e, 0x74, 0x4c, 0x69, 0x73, 0x74, 0x12, 0x25,
-	0x0a, 0x06, 0x70, 0x6f, 0x69, 0x6e, 0x74, 0x73, 0x18, 0x01, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x0d,
-	0x2e, 0x4d, 0x6f, 0x6e, 0x69, 0x74, 0x6f, 0x72, 0x50, 0x6f, 0x69, 0x6e, 0x74, 0x52, 0x06, 0x70,
-	0x6f, 0x69, 0x6e, 0x74, 0x73, 0x22, 0x24, 0x0a, 0x06, 0x42, 0x50, 0x6f, 0x69, 0x6e, 0x74, 0x12,
-	0x0c, 0x0a, 0x01, 0x78, 0x18, 0x01, 0x20, 0x01, 0x28, 0x05, 0x52, 0x01, 0x78, 0x12, 0x0c, 0x0a,
-	0x01, 0x79, 0x18, 0x02, 0x20, 0x01, 0x28, 0x05, 0x52, 0x01, 0x79, 0x22, 0x1c, 0x0a, 0x06, 0x42,
-	0x43, 0x6f, 0x6c, 0x6f, 0x72, 0x12, 0x12, 0x0a, 0x04, 0x72, 0x67, 0x62, 0x61, 0x18, 0x01, 0x20,
-	0x01, 0x28, 0x0d, 0x52, 0x04, 0x72, 0x67, 0x62, 0x61, 0x22, 0x4c, 0x0a, 0x0c, 0x50, 0x61, 0x69,
-	0x6e, 0x74, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x1d, 0x0a, 0x05, 0x70, 0x6f, 0x69,
-	0x6e, 0x74, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x07, 0x2e, 0x42, 0x50, 0x6f, 0x69, 0x6e,
-	0x74, 0x52, 0x05, 0x70, 0x6f, 0x69, 0x6e, 0x74, 0x12, 0x1d, 0x0a, 0x05, 0x63, 0x6f, 0x6c, 0x6f,
-	0x72, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x07, 0x2e, 0x42, 0x43, 0x6f, 0x6c, 0x6f, 0x72,
-	0x52, 0x05, 0x63, 0x6f, 0x6c, 0x6f, 0x72, 0x22, 0x26, 0x0a, 0x0a, 0x50, 0x61, 0x69, 0x6e, 0x74,
-	0x52, 0x65, 0x70, 0x6c, 0x79, 0x12, 0x18, 0x0a, 0x07, 0x6d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65,
-	0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x07, 0x6d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x22,
-	0x33, 0x0a, 0x07, 0x52, 0x54, 0x43, 0x44, 0x61, 0x74, 0x61, 0x12, 0x14, 0x0a, 0x05, 0x65, 0x76,
-	0x65, 0x6e, 0x74, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x05, 0x65, 0x76, 0x65, 0x6e, 0x74,
-	0x12, 0x12, 0x0a, 0x04, 0x64, 0x61, 0x74, 0x61, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04,
-	0x64, 0x61, 0x74, 0x61, 0x32, 0x87, 0x01, 0x0a, 0x0b, 0x50, 0x61, 0x69, 0x6e, 0x74, 0x43, 0x6c,
-	0x69, 0x65, 0x6e, 0x74, 0x12, 0x25, 0x0a, 0x05, 0x50, 0x61, 0x69, 0x6e, 0x74, 0x12, 0x0d, 0x2e,
-	0x50, 0x61, 0x69, 0x6e, 0x74, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x0b, 0x2e, 0x50,
-	0x61, 0x69, 0x6e, 0x74, 0x52, 0x65, 0x70, 0x6c, 0x79, 0x22, 0x00, 0x12, 0x29, 0x0a, 0x07, 0x4d,
-	0x6f, 0x6e, 0x69, 0x74, 0x6f, 0x72, 0x12, 0x0b, 0x2e, 0x4d, 0x6f, 0x6e, 0x69, 0x74, 0x6f, 0x72,
-	0x53, 0x75, 0x62, 0x1a, 0x0d, 0x2e, 0x4d, 0x6f, 0x6e, 0x69, 0x74, 0x6f, 0x72, 0x52, 0x65, 0x70,
-	0x6c, 0x79, 0x22, 0x00, 0x30, 0x01, 0x12, 0x26, 0x0a, 0x0a, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73,
-	0x74, 0x52, 0x54, 0x43, 0x12, 0x08, 0x2e, 0x52, 0x54, 0x43, 0x44, 0x61, 0x74, 0x61, 0x1a, 0x08,
-	0x2e, 0x52, 0x54, 0x43, 0x44, 0x61, 0x74, 0x61, 0x22, 0x00, 0x28, 0x01, 0x30, 0x01, 0x42, 0x11,
-	0x5a, 0x0f, 0x2e, 0x2f, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x73, 0x2f, 0x63, 0x6c, 0x69, 0x65, 0x6e,
-	0x74, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33,
+	0x70, 0x72, 0x6f, 0x74, 0x6f, 0x22, 0x3e, 0x0a, 0x0b, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74,
+	0x44, 0x61, 0x74, 0x61, 0x12, 0x1b, 0x0a, 0x04, 0x74, 0x69, 0x6c, 0x65, 0x18, 0x01, 0x20, 0x01,
+	0x28, 0x0b, 0x32, 0x07, 0x2e, 0x42, 0x50, 0x6f, 0x69, 0x6e, 0x74, 0x52, 0x04, 0x74, 0x69, 0x6c,
+	0x65, 0x12, 0x12, 0x0a, 0x04, 0x66, 0x72, 0x6f, 0x6d, 0x18, 0x02, 0x20, 0x01, 0x28, 0x04, 0x52,
+	0x04, 0x66, 0x72, 0x6f, 0x6d, 0x22, 0x57, 0x0a, 0x0d, 0x57, 0x65, 0x62, 0x52, 0x54, 0x43, 0x52,
+	0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x30, 0x0a, 0x0b, 0x72, 0x65, 0x71, 0x75, 0x65, 0x73,
+	0x74, 0x44, 0x61, 0x74, 0x61, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x0c, 0x2e, 0x52, 0x65,
+	0x71, 0x75, 0x65, 0x73, 0x74, 0x44, 0x61, 0x74, 0x61, 0x48, 0x00, 0x52, 0x0b, 0x72, 0x65, 0x71,
+	0x75, 0x65, 0x73, 0x74, 0x44, 0x61, 0x74, 0x61, 0x42, 0x14, 0x0a, 0x12, 0x57, 0x65, 0x62, 0x52,
+	0x54, 0x43, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x4f, 0x6e, 0x65, 0x4f, 0x66, 0x22, 0x53,
+	0x0a, 0x0b, 0x57, 0x65, 0x62, 0x52, 0x54, 0x43, 0x52, 0x65, 0x70, 0x6c, 0x79, 0x12, 0x30, 0x0a,
+	0x0b, 0x72, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x44, 0x61, 0x74, 0x61, 0x18, 0x01, 0x20, 0x01,
+	0x28, 0x0b, 0x32, 0x0c, 0x2e, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x44, 0x61, 0x74, 0x61,
+	0x48, 0x00, 0x52, 0x0b, 0x72, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x44, 0x61, 0x74, 0x61, 0x42,
+	0x12, 0x0a, 0x10, 0x57, 0x65, 0x62, 0x52, 0x54, 0x43, 0x52, 0x65, 0x70, 0x6c, 0x79, 0x4f, 0x6e,
+	0x65, 0x4f, 0x66, 0x22, 0x5d, 0x0a, 0x10, 0x57, 0x65, 0x62, 0x53, 0x6f, 0x63, 0x6b, 0x65, 0x74,
+	0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x30, 0x0a, 0x0b, 0x72, 0x65, 0x71, 0x75, 0x65,
+	0x73, 0x74, 0x44, 0x61, 0x74, 0x61, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x0c, 0x2e, 0x52,
+	0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x44, 0x61, 0x74, 0x61, 0x48, 0x00, 0x52, 0x0b, 0x72, 0x65,
+	0x71, 0x75, 0x65, 0x73, 0x74, 0x44, 0x61, 0x74, 0x61, 0x42, 0x17, 0x0a, 0x15, 0x57, 0x65, 0x62,
+	0x53, 0x6f, 0x63, 0x6b, 0x65, 0x74, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x4f, 0x6e, 0x65,
+	0x4f, 0x66, 0x22, 0x59, 0x0a, 0x0e, 0x57, 0x65, 0x62, 0x53, 0x6f, 0x63, 0x6b, 0x65, 0x74, 0x52,
+	0x65, 0x70, 0x6c, 0x79, 0x12, 0x30, 0x0a, 0x0b, 0x72, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x44,
+	0x61, 0x74, 0x61, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x0c, 0x2e, 0x52, 0x65, 0x71, 0x75,
+	0x65, 0x73, 0x74, 0x44, 0x61, 0x74, 0x61, 0x48, 0x00, 0x52, 0x0b, 0x72, 0x65, 0x71, 0x75, 0x65,
+	0x73, 0x74, 0x44, 0x61, 0x74, 0x61, 0x42, 0x15, 0x0a, 0x13, 0x57, 0x65, 0x62, 0x53, 0x6f, 0x63,
+	0x6b, 0x65, 0x74, 0x52, 0x65, 0x70, 0x6c, 0x79, 0x4f, 0x6e, 0x65, 0x4f, 0x66, 0x22, 0x4c, 0x0a,
+	0x0c, 0x4d, 0x6f, 0x6e, 0x69, 0x74, 0x6f, 0x72, 0x50, 0x6f, 0x69, 0x6e, 0x74, 0x12, 0x1d, 0x0a,
+	0x05, 0x70, 0x6f, 0x69, 0x6e, 0x74, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x07, 0x2e, 0x42,
+	0x50, 0x6f, 0x69, 0x6e, 0x74, 0x52, 0x05, 0x70, 0x6f, 0x69, 0x6e, 0x74, 0x12, 0x1d, 0x0a, 0x05,
+	0x63, 0x6f, 0x6c, 0x6f, 0x72, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x07, 0x2e, 0x42, 0x43,
+	0x6f, 0x6c, 0x6f, 0x72, 0x52, 0x05, 0x63, 0x6f, 0x6c, 0x6f, 0x72, 0x22, 0x52, 0x0a, 0x0c, 0x4d,
+	0x6f, 0x6e, 0x69, 0x74, 0x6f, 0x72, 0x52, 0x65, 0x70, 0x6c, 0x79, 0x12, 0x25, 0x0a, 0x06, 0x70,
+	0x6f, 0x69, 0x6e, 0x74, 0x73, 0x18, 0x01, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x0d, 0x2e, 0x4d, 0x6f,
+	0x6e, 0x69, 0x74, 0x6f, 0x72, 0x50, 0x6f, 0x69, 0x6e, 0x74, 0x52, 0x06, 0x70, 0x6f, 0x69, 0x6e,
+	0x74, 0x73, 0x12, 0x1b, 0x0a, 0x04, 0x74, 0x69, 0x6c, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b,
+	0x32, 0x07, 0x2e, 0x42, 0x50, 0x6f, 0x69, 0x6e, 0x74, 0x52, 0x04, 0x74, 0x69, 0x6c, 0x65, 0x22,
+	0x41, 0x0a, 0x0e, 0x4d, 0x6f, 0x6e, 0x69, 0x74, 0x6f, 0x72, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73,
+	0x74, 0x12, 0x12, 0x0a, 0x04, 0x6c, 0x61, 0x73, 0x74, 0x18, 0x01, 0x20, 0x01, 0x28, 0x03, 0x52,
+	0x04, 0x6c, 0x61, 0x73, 0x74, 0x12, 0x1b, 0x0a, 0x04, 0x74, 0x69, 0x6c, 0x65, 0x18, 0x02, 0x20,
+	0x01, 0x28, 0x0b, 0x32, 0x07, 0x2e, 0x42, 0x50, 0x6f, 0x69, 0x6e, 0x74, 0x52, 0x04, 0x74, 0x69,
+	0x6c, 0x65, 0x22, 0x39, 0x0a, 0x10, 0x4d, 0x6f, 0x6e, 0x69, 0x74, 0x6f, 0x72, 0x50, 0x6f, 0x69,
+	0x6e, 0x74, 0x4c, 0x69, 0x73, 0x74, 0x12, 0x25, 0x0a, 0x06, 0x70, 0x6f, 0x69, 0x6e, 0x74, 0x73,
+	0x18, 0x01, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x0d, 0x2e, 0x4d, 0x6f, 0x6e, 0x69, 0x74, 0x6f, 0x72,
+	0x50, 0x6f, 0x69, 0x6e, 0x74, 0x52, 0x06, 0x70, 0x6f, 0x69, 0x6e, 0x74, 0x73, 0x22, 0x24, 0x0a,
+	0x06, 0x42, 0x50, 0x6f, 0x69, 0x6e, 0x74, 0x12, 0x0c, 0x0a, 0x01, 0x78, 0x18, 0x01, 0x20, 0x01,
+	0x28, 0x05, 0x52, 0x01, 0x78, 0x12, 0x0c, 0x0a, 0x01, 0x79, 0x18, 0x02, 0x20, 0x01, 0x28, 0x05,
+	0x52, 0x01, 0x79, 0x22, 0x1c, 0x0a, 0x06, 0x42, 0x43, 0x6f, 0x6c, 0x6f, 0x72, 0x12, 0x12, 0x0a,
+	0x04, 0x72, 0x67, 0x62, 0x61, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x04, 0x72, 0x67, 0x62,
+	0x61, 0x22, 0x4c, 0x0a, 0x0c, 0x50, 0x61, 0x69, 0x6e, 0x74, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73,
+	0x74, 0x12, 0x1d, 0x0a, 0x05, 0x70, 0x6f, 0x69, 0x6e, 0x74, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b,
+	0x32, 0x07, 0x2e, 0x42, 0x50, 0x6f, 0x69, 0x6e, 0x74, 0x52, 0x05, 0x70, 0x6f, 0x69, 0x6e, 0x74,
+	0x12, 0x1d, 0x0a, 0x05, 0x63, 0x6f, 0x6c, 0x6f, 0x72, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32,
+	0x07, 0x2e, 0x42, 0x43, 0x6f, 0x6c, 0x6f, 0x72, 0x52, 0x05, 0x63, 0x6f, 0x6c, 0x6f, 0x72, 0x22,
+	0x26, 0x0a, 0x0a, 0x50, 0x61, 0x69, 0x6e, 0x74, 0x52, 0x65, 0x70, 0x6c, 0x79, 0x12, 0x18, 0x0a,
+	0x07, 0x6d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x07,
+	0x6d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x22, 0x33, 0x0a, 0x07, 0x52, 0x54, 0x43, 0x44, 0x61,
+	0x74, 0x61, 0x12, 0x14, 0x0a, 0x05, 0x65, 0x76, 0x65, 0x6e, 0x74, 0x18, 0x01, 0x20, 0x01, 0x28,
+	0x09, 0x52, 0x05, 0x65, 0x76, 0x65, 0x6e, 0x74, 0x12, 0x12, 0x0a, 0x04, 0x64, 0x61, 0x74, 0x61,
+	0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x64, 0x61, 0x74, 0x61, 0x42, 0x11, 0x5a, 0x0f,
+	0x2e, 0x2f, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x73, 0x2f, 0x63, 0x6c, 0x69, 0x65, 0x6e, 0x74, 0x62,
+	0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33,
 }
 
 var (
@@ -555,37 +891,42 @@ func file_protos_client_proto_rawDescGZIP() []byte {
 	return file_protos_client_proto_rawDescData
 }
 
-var file_protos_client_proto_msgTypes = make([]protoimpl.MessageInfo, 9)
+var file_protos_client_proto_msgTypes = make([]protoimpl.MessageInfo, 14)
 var file_protos_client_proto_goTypes = []interface{}{
-	(*MonitorSub)(nil),       // 0: MonitorSub
-	(*MonitorPoint)(nil),     // 1: MonitorPoint
-	(*MonitorReply)(nil),     // 2: MonitorReply
-	(*MonitorPointList)(nil), // 3: MonitorPointList
-	(*BPoint)(nil),           // 4: BPoint
-	(*BColor)(nil),           // 5: BColor
-	(*PaintRequest)(nil),     // 6: PaintRequest
-	(*PaintReply)(nil),       // 7: PaintReply
-	(*RTCData)(nil),          // 8: RTCData
+	(*RequestData)(nil),      // 0: RequestData
+	(*WebRTCRequest)(nil),    // 1: WebRTCRequest
+	(*WebRTCReply)(nil),      // 2: WebRTCReply
+	(*WebSocketRequest)(nil), // 3: WebSocketRequest
+	(*WebSocketReply)(nil),   // 4: WebSocketReply
+	(*MonitorPoint)(nil),     // 5: MonitorPoint
+	(*MonitorReply)(nil),     // 6: MonitorReply
+	(*MonitorRequest)(nil),   // 7: MonitorRequest
+	(*MonitorPointList)(nil), // 8: MonitorPointList
+	(*BPoint)(nil),           // 9: BPoint
+	(*BColor)(nil),           // 10: BColor
+	(*PaintRequest)(nil),     // 11: PaintRequest
+	(*PaintReply)(nil),       // 12: PaintReply
+	(*RTCData)(nil),          // 13: RTCData
 }
 var file_protos_client_proto_depIdxs = []int32{
-	4,  // 0: MonitorSub.tile:type_name -> BPoint
-	4,  // 1: MonitorPoint.point:type_name -> BPoint
-	5,  // 2: MonitorPoint.color:type_name -> BColor
-	1,  // 3: MonitorReply.points:type_name -> MonitorPoint
-	1,  // 4: MonitorPointList.points:type_name -> MonitorPoint
-	4,  // 5: PaintRequest.point:type_name -> BPoint
-	5,  // 6: PaintRequest.color:type_name -> BColor
-	6,  // 7: PaintClient.Paint:input_type -> PaintRequest
-	0,  // 8: PaintClient.Monitor:input_type -> MonitorSub
-	8,  // 9: PaintClient.RequestRTC:input_type -> RTCData
-	7,  // 10: PaintClient.Paint:output_type -> PaintReply
-	2,  // 11: PaintClient.Monitor:output_type -> MonitorReply
-	8,  // 12: PaintClient.RequestRTC:output_type -> RTCData
-	10, // [10:13] is the sub-list for method output_type
-	7,  // [7:10] is the sub-list for method input_type
-	7,  // [7:7] is the sub-list for extension type_name
-	7,  // [7:7] is the sub-list for extension extendee
-	0,  // [0:7] is the sub-list for field type_name
+	9,  // 0: RequestData.tile:type_name -> BPoint
+	0,  // 1: WebRTCRequest.requestData:type_name -> RequestData
+	0,  // 2: WebRTCReply.requestData:type_name -> RequestData
+	0,  // 3: WebSocketRequest.requestData:type_name -> RequestData
+	0,  // 4: WebSocketReply.requestData:type_name -> RequestData
+	9,  // 5: MonitorPoint.point:type_name -> BPoint
+	10, // 6: MonitorPoint.color:type_name -> BColor
+	5,  // 7: MonitorReply.points:type_name -> MonitorPoint
+	9,  // 8: MonitorReply.tile:type_name -> BPoint
+	9,  // 9: MonitorRequest.tile:type_name -> BPoint
+	5,  // 10: MonitorPointList.points:type_name -> MonitorPoint
+	9,  // 11: PaintRequest.point:type_name -> BPoint
+	10, // 12: PaintRequest.color:type_name -> BColor
+	13, // [13:13] is the sub-list for method output_type
+	13, // [13:13] is the sub-list for method input_type
+	13, // [13:13] is the sub-list for extension type_name
+	13, // [13:13] is the sub-list for extension extendee
+	0,  // [0:13] is the sub-list for field type_name
 }
 
 func init() { file_protos_client_proto_init() }
@@ -595,7 +936,7 @@ func file_protos_client_proto_init() {
 	}
 	if !protoimpl.UnsafeEnabled {
 		file_protos_client_proto_msgTypes[0].Exporter = func(v interface{}, i int) interface{} {
-			switch v := v.(*MonitorSub); i {
+			switch v := v.(*RequestData); i {
 			case 0:
 				return &v.state
 			case 1:
@@ -607,7 +948,7 @@ func file_protos_client_proto_init() {
 			}
 		}
 		file_protos_client_proto_msgTypes[1].Exporter = func(v interface{}, i int) interface{} {
-			switch v := v.(*MonitorPoint); i {
+			switch v := v.(*WebRTCRequest); i {
 			case 0:
 				return &v.state
 			case 1:
@@ -619,7 +960,7 @@ func file_protos_client_proto_init() {
 			}
 		}
 		file_protos_client_proto_msgTypes[2].Exporter = func(v interface{}, i int) interface{} {
-			switch v := v.(*MonitorReply); i {
+			switch v := v.(*WebRTCReply); i {
 			case 0:
 				return &v.state
 			case 1:
@@ -631,7 +972,7 @@ func file_protos_client_proto_init() {
 			}
 		}
 		file_protos_client_proto_msgTypes[3].Exporter = func(v interface{}, i int) interface{} {
-			switch v := v.(*MonitorPointList); i {
+			switch v := v.(*WebSocketRequest); i {
 			case 0:
 				return &v.state
 			case 1:
@@ -643,7 +984,7 @@ func file_protos_client_proto_init() {
 			}
 		}
 		file_protos_client_proto_msgTypes[4].Exporter = func(v interface{}, i int) interface{} {
-			switch v := v.(*BPoint); i {
+			switch v := v.(*WebSocketReply); i {
 			case 0:
 				return &v.state
 			case 1:
@@ -655,7 +996,7 @@ func file_protos_client_proto_init() {
 			}
 		}
 		file_protos_client_proto_msgTypes[5].Exporter = func(v interface{}, i int) interface{} {
-			switch v := v.(*BColor); i {
+			switch v := v.(*MonitorPoint); i {
 			case 0:
 				return &v.state
 			case 1:
@@ -667,7 +1008,7 @@ func file_protos_client_proto_init() {
 			}
 		}
 		file_protos_client_proto_msgTypes[6].Exporter = func(v interface{}, i int) interface{} {
-			switch v := v.(*PaintRequest); i {
+			switch v := v.(*MonitorReply); i {
 			case 0:
 				return &v.state
 			case 1:
@@ -679,7 +1020,7 @@ func file_protos_client_proto_init() {
 			}
 		}
 		file_protos_client_proto_msgTypes[7].Exporter = func(v interface{}, i int) interface{} {
-			switch v := v.(*PaintReply); i {
+			switch v := v.(*MonitorRequest); i {
 			case 0:
 				return &v.state
 			case 1:
@@ -691,6 +1032,66 @@ func file_protos_client_proto_init() {
 			}
 		}
 		file_protos_client_proto_msgTypes[8].Exporter = func(v interface{}, i int) interface{} {
+			switch v := v.(*MonitorPointList); i {
+			case 0:
+				return &v.state
+			case 1:
+				return &v.sizeCache
+			case 2:
+				return &v.unknownFields
+			default:
+				return nil
+			}
+		}
+		file_protos_client_proto_msgTypes[9].Exporter = func(v interface{}, i int) interface{} {
+			switch v := v.(*BPoint); i {
+			case 0:
+				return &v.state
+			case 1:
+				return &v.sizeCache
+			case 2:
+				return &v.unknownFields
+			default:
+				return nil
+			}
+		}
+		file_protos_client_proto_msgTypes[10].Exporter = func(v interface{}, i int) interface{} {
+			switch v := v.(*BColor); i {
+			case 0:
+				return &v.state
+			case 1:
+				return &v.sizeCache
+			case 2:
+				return &v.unknownFields
+			default:
+				return nil
+			}
+		}
+		file_protos_client_proto_msgTypes[11].Exporter = func(v interface{}, i int) interface{} {
+			switch v := v.(*PaintRequest); i {
+			case 0:
+				return &v.state
+			case 1:
+				return &v.sizeCache
+			case 2:
+				return &v.unknownFields
+			default:
+				return nil
+			}
+		}
+		file_protos_client_proto_msgTypes[12].Exporter = func(v interface{}, i int) interface{} {
+			switch v := v.(*PaintReply); i {
+			case 0:
+				return &v.state
+			case 1:
+				return &v.sizeCache
+			case 2:
+				return &v.unknownFields
+			default:
+				return nil
+			}
+		}
+		file_protos_client_proto_msgTypes[13].Exporter = func(v interface{}, i int) interface{} {
 			switch v := v.(*RTCData); i {
 			case 0:
 				return &v.state
@@ -703,15 +1104,27 @@ func file_protos_client_proto_init() {
 			}
 		}
 	}
+	file_protos_client_proto_msgTypes[1].OneofWrappers = []interface{}{
+		(*WebRTCRequest_RequestData)(nil),
+	}
+	file_protos_client_proto_msgTypes[2].OneofWrappers = []interface{}{
+		(*WebRTCReply_RequestData)(nil),
+	}
+	file_protos_client_proto_msgTypes[3].OneofWrappers = []interface{}{
+		(*WebSocketRequest_RequestData)(nil),
+	}
+	file_protos_client_proto_msgTypes[4].OneofWrappers = []interface{}{
+		(*WebSocketReply_RequestData)(nil),
+	}
 	type x struct{}
 	out := protoimpl.TypeBuilder{
 		File: protoimpl.DescBuilder{
 			GoPackagePath: reflect.TypeOf(x{}).PkgPath(),
 			RawDescriptor: file_protos_client_proto_rawDesc,
 			NumEnums:      0,
-			NumMessages:   9,
+			NumMessages:   14,
 			NumExtensions: 0,
-			NumServices:   1,
+			NumServices:   0,
 		},
 		GoTypes:           file_protos_client_proto_goTypes,
 		DependencyIndexes: file_protos_client_proto_depIdxs,

+ 45 - 10
server/protos/client.proto

@@ -7,17 +7,47 @@ option go_package = "./protos/client";
 // protoc --go_out=. --go_opt=paths=source_relative  --go-grpc_out=.
 // --go-grpc_opt=paths=source_relative protos/client.proto
 
-// The greeting service definition.
-service PaintClient {
-  // Sends a greeting
-  rpc Paint(PaintRequest) returns (PaintReply) {}
-  rpc Monitor(MonitorSub) returns (stream MonitorReply) {}
-  rpc RequestRTC(stream RTCData) returns (stream RTCData) {}
-}
+// // The greeting service definition.
+// service PaintClient {
+//   // Sends a greeting
+//   rpc Paint(PaintRequest) returns (PaintReply) {}
+//   rpc Monitor(MonitorSub) returns (stream MonitorReply) {}
+//   rpc RequestRTC(stream RTCData) returns (stream RTCData) {}
+// }
+
+// message MonitorSub {
+//   BPoint tile = 1;
+//   bool susbscribed = 2;
+// }
 
-message MonitorSub {
+message RequestData {
   BPoint tile = 1;
-  bool susbscribed = 2;
+  uint64 from = 2;
+}
+
+message WebRTCRequest {
+  oneof WebRTCRequestOneOf {
+    RequestData requestData = 1;
+  }
+}
+
+
+message WebRTCReply {
+  oneof WebRTCReplyOneOf {
+    RequestData requestData = 1;
+  }
+}
+
+message WebSocketRequest {
+  oneof WebSocketRequestOneOf {
+    RequestData requestData = 1;
+  }
+}
+
+message WebSocketReply {
+  oneof WebSocketReplyOneOf {
+    RequestData requestData = 1;
+  }
 }
 
 message MonitorPoint {
@@ -27,7 +57,12 @@ message MonitorPoint {
 
 message MonitorReply {
   repeated MonitorPoint points = 1;
-  bytes bytes_points = 2;
+  BPoint tile = 2;
+}
+
+message MonitorRequest {
+  int64 last = 1;
+  BPoint tile = 2;
 }
 
 message MonitorPointList {

+ 0 - 239
server/protos/client_grpc.pb.go

@@ -1,239 +0,0 @@
-// Code generated by protoc-gen-go-grpc. DO NOT EDIT.
-// versions:
-// - protoc-gen-go-grpc v1.2.0
-// - protoc             v3.19.4
-// source: protos/client.proto
-
-package client
-
-import (
-	context "context"
-	grpc "google.golang.org/grpc"
-	codes "google.golang.org/grpc/codes"
-	status "google.golang.org/grpc/status"
-)
-
-// This is a compile-time assertion to ensure that this generated file
-// is compatible with the grpc package it is being compiled against.
-// Requires gRPC-Go v1.32.0 or later.
-const _ = grpc.SupportPackageIsVersion7
-
-// PaintClientClient is the client API for PaintClient service.
-//
-// For semantics around ctx use and closing/ending streaming RPCs, please refer to https://pkg.go.dev/google.golang.org/grpc/?tab=doc#ClientConn.NewStream.
-type PaintClientClient interface {
-	// Sends a greeting
-	Paint(ctx context.Context, in *PaintRequest, opts ...grpc.CallOption) (*PaintReply, error)
-	Monitor(ctx context.Context, in *MonitorSub, opts ...grpc.CallOption) (PaintClient_MonitorClient, error)
-	RequestRTC(ctx context.Context, opts ...grpc.CallOption) (PaintClient_RequestRTCClient, error)
-}
-
-type paintClientClient struct {
-	cc grpc.ClientConnInterface
-}
-
-func NewPaintClientClient(cc grpc.ClientConnInterface) PaintClientClient {
-	return &paintClientClient{cc}
-}
-
-func (c *paintClientClient) Paint(ctx context.Context, in *PaintRequest, opts ...grpc.CallOption) (*PaintReply, error) {
-	out := new(PaintReply)
-	err := c.cc.Invoke(ctx, "/PaintClient/Paint", in, out, opts...)
-	if err != nil {
-		return nil, err
-	}
-	return out, nil
-}
-
-func (c *paintClientClient) Monitor(ctx context.Context, in *MonitorSub, opts ...grpc.CallOption) (PaintClient_MonitorClient, error) {
-	stream, err := c.cc.NewStream(ctx, &PaintClient_ServiceDesc.Streams[0], "/PaintClient/Monitor", opts...)
-	if err != nil {
-		return nil, err
-	}
-	x := &paintClientMonitorClient{stream}
-	if err := x.ClientStream.SendMsg(in); err != nil {
-		return nil, err
-	}
-	if err := x.ClientStream.CloseSend(); err != nil {
-		return nil, err
-	}
-	return x, nil
-}
-
-type PaintClient_MonitorClient interface {
-	Recv() (*MonitorReply, error)
-	grpc.ClientStream
-}
-
-type paintClientMonitorClient struct {
-	grpc.ClientStream
-}
-
-func (x *paintClientMonitorClient) Recv() (*MonitorReply, error) {
-	m := new(MonitorReply)
-	if err := x.ClientStream.RecvMsg(m); err != nil {
-		return nil, err
-	}
-	return m, nil
-}
-
-func (c *paintClientClient) RequestRTC(ctx context.Context, opts ...grpc.CallOption) (PaintClient_RequestRTCClient, error) {
-	stream, err := c.cc.NewStream(ctx, &PaintClient_ServiceDesc.Streams[1], "/PaintClient/RequestRTC", opts...)
-	if err != nil {
-		return nil, err
-	}
-	x := &paintClientRequestRTCClient{stream}
-	return x, nil
-}
-
-type PaintClient_RequestRTCClient interface {
-	Send(*RTCData) error
-	Recv() (*RTCData, error)
-	grpc.ClientStream
-}
-
-type paintClientRequestRTCClient struct {
-	grpc.ClientStream
-}
-
-func (x *paintClientRequestRTCClient) Send(m *RTCData) error {
-	return x.ClientStream.SendMsg(m)
-}
-
-func (x *paintClientRequestRTCClient) Recv() (*RTCData, error) {
-	m := new(RTCData)
-	if err := x.ClientStream.RecvMsg(m); err != nil {
-		return nil, err
-	}
-	return m, nil
-}
-
-// PaintClientServer is the server API for PaintClient service.
-// All implementations must embed UnimplementedPaintClientServer
-// for forward compatibility
-type PaintClientServer interface {
-	// Sends a greeting
-	Paint(context.Context, *PaintRequest) (*PaintReply, error)
-	Monitor(*MonitorSub, PaintClient_MonitorServer) error
-	RequestRTC(PaintClient_RequestRTCServer) error
-	mustEmbedUnimplementedPaintClientServer()
-}
-
-// UnimplementedPaintClientServer must be embedded to have forward compatible implementations.
-type UnimplementedPaintClientServer struct {
-}
-
-func (UnimplementedPaintClientServer) Paint(context.Context, *PaintRequest) (*PaintReply, error) {
-	return nil, status.Errorf(codes.Unimplemented, "method Paint not implemented")
-}
-func (UnimplementedPaintClientServer) Monitor(*MonitorSub, PaintClient_MonitorServer) error {
-	return status.Errorf(codes.Unimplemented, "method Monitor not implemented")
-}
-func (UnimplementedPaintClientServer) RequestRTC(PaintClient_RequestRTCServer) error {
-	return status.Errorf(codes.Unimplemented, "method RequestRTC not implemented")
-}
-func (UnimplementedPaintClientServer) mustEmbedUnimplementedPaintClientServer() {}
-
-// UnsafePaintClientServer may be embedded to opt out of forward compatibility for this service.
-// Use of this interface is not recommended, as added methods to PaintClientServer will
-// result in compilation errors.
-type UnsafePaintClientServer interface {
-	mustEmbedUnimplementedPaintClientServer()
-}
-
-func RegisterPaintClientServer(s grpc.ServiceRegistrar, srv PaintClientServer) {
-	s.RegisterService(&PaintClient_ServiceDesc, srv)
-}
-
-func _PaintClient_Paint_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {
-	in := new(PaintRequest)
-	if err := dec(in); err != nil {
-		return nil, err
-	}
-	if interceptor == nil {
-		return srv.(PaintClientServer).Paint(ctx, in)
-	}
-	info := &grpc.UnaryServerInfo{
-		Server:     srv,
-		FullMethod: "/PaintClient/Paint",
-	}
-	handler := func(ctx context.Context, req interface{}) (interface{}, error) {
-		return srv.(PaintClientServer).Paint(ctx, req.(*PaintRequest))
-	}
-	return interceptor(ctx, in, info, handler)
-}
-
-func _PaintClient_Monitor_Handler(srv interface{}, stream grpc.ServerStream) error {
-	m := new(MonitorSub)
-	if err := stream.RecvMsg(m); err != nil {
-		return err
-	}
-	return srv.(PaintClientServer).Monitor(m, &paintClientMonitorServer{stream})
-}
-
-type PaintClient_MonitorServer interface {
-	Send(*MonitorReply) error
-	grpc.ServerStream
-}
-
-type paintClientMonitorServer struct {
-	grpc.ServerStream
-}
-
-func (x *paintClientMonitorServer) Send(m *MonitorReply) error {
-	return x.ServerStream.SendMsg(m)
-}
-
-func _PaintClient_RequestRTC_Handler(srv interface{}, stream grpc.ServerStream) error {
-	return srv.(PaintClientServer).RequestRTC(&paintClientRequestRTCServer{stream})
-}
-
-type PaintClient_RequestRTCServer interface {
-	Send(*RTCData) error
-	Recv() (*RTCData, error)
-	grpc.ServerStream
-}
-
-type paintClientRequestRTCServer struct {
-	grpc.ServerStream
-}
-
-func (x *paintClientRequestRTCServer) Send(m *RTCData) error {
-	return x.ServerStream.SendMsg(m)
-}
-
-func (x *paintClientRequestRTCServer) Recv() (*RTCData, error) {
-	m := new(RTCData)
-	if err := x.ServerStream.RecvMsg(m); err != nil {
-		return nil, err
-	}
-	return m, nil
-}
-
-// PaintClient_ServiceDesc is the grpc.ServiceDesc for PaintClient service.
-// It's only intended for direct use with grpc.RegisterService,
-// and not to be introspected or modified (even as a copy)
-var PaintClient_ServiceDesc = grpc.ServiceDesc{
-	ServiceName: "PaintClient",
-	HandlerType: (*PaintClientServer)(nil),
-	Methods: []grpc.MethodDesc{
-		{
-			MethodName: "Paint",
-			Handler:    _PaintClient_Paint_Handler,
-		},
-	},
-	Streams: []grpc.StreamDesc{
-		{
-			StreamName:    "Monitor",
-			Handler:       _PaintClient_Monitor_Handler,
-			ServerStreams: true,
-		},
-		{
-			StreamName:    "RequestRTC",
-			Handler:       _PaintClient_RequestRTC_Handler,
-			ServerStreams: true,
-			ClientStreams: true,
-		},
-	},
-	Metadata: "protos/client.proto",
-}