protected override void OnMessage(MessageEventArgs e) { lock (_lock) { if (e.Type == Opcode.Binary) { if (!CanSend) { Send("error: readonly connection"); Logger.Warn(_channel.Endpoint + " | received transaction on a readonly connection"); return; } if (!IsLive) { Send("error: past situation cannot be edited"); Logger.Warn(_channel.Endpoint + " | received transaction on a past situation connection"); return; } _protoModelObserver.Reset(); string senderID; try { senderID = _fromProtobuf.Convert(e.RawData, _strictServer); } catch (Exception ex) { Logger.Warn(ex.Message); return; } if (senderID == "undefined") { Logger.Warn(_channel.Endpoint + " | undefined senderID detected. Ignores"); return; } Logger.Info(_channel.Endpoint + " | " + senderID + " | transaction received | " + e.RawData.Length + " bytes"); Logger.Debug(Convert.ToBase64String(e.RawData)); LastSenderID = senderID; _toProtobuf.ApplyThingsSuppressions(_protoModelObserver.Deletions); // Broadcast to other clients if (_protoModelObserver.SomethingChanged()) { foreach (var session in Sessions.Sessions) { if (session != this) { var s = session as BroadcastService; if (s != null) { var transaction = _protoModelObserver.GetTransaction(s._toProtobuf, senderID, false, !s.CanReceive || !s.IsLive); //s.Send(s._toProtobuf.Convert(transaction)); s.Send(transaction); } } } } } else if (e.Type == Opcode.Text) { if ("live" == e.Data) { IsLive = true; if (CurrentWarehouse != null && CanReceive) { _currentProtoModelObserver.Reset(); CurrentWarehouse.RegisterObserver(_currentProtoModelObserver); TimeMachine.SynchronizeWarehouse(LiveWarehouse, CurrentWarehouse); if (_currentProtoModelObserver.SomethingChanged()) { var transaction = _currentProtoModelObserver.GetTransaction(_toProtobuf, Configuration.TimeMachineSenderName); Send(transaction); } CurrentWarehouse = null; } Send("live"); Logger.Info(_channel.Endpoint + " | " + LastSenderID + " | live"); } else if ("pause" == e.Data) { IsLive = false; Send("pause"); CurrentWarehouse = new Warehouse(); CurrentWarehouse.RegisterCollection(LiveWarehouse.Things); Logger.Info(_channel.Endpoint + " | " + LastSenderID + " | pause"); } else { Match match; if ((match = _loadRegex.Match(e.Data)).Success) { IsLive = false; Logger.Info(_channel.Endpoint + " | " + LastSenderID + " | load"); var timestamp = match.Groups[1].Value; long parsedTimestamp; if (!long.TryParse(timestamp, out parsedTimestamp)) { DateTime parsedDateTime; if (!DateTime.TryParse(timestamp, out parsedDateTime)) { Logger.Info(_channel.Endpoint + " | " + LastSenderID + " | unable to parse the timestamp"); Send("error: unable to parse the timestamp"); } parsedTimestamp = parsedDateTime.Subtract(DateTimeEpoch).Ticks / 10000; } var oldSituation = TimeMachine.RetrieveWarehouse(parsedTimestamp); if (oldSituation == null) { Send("error: no result found"); return; } if (CurrentWarehouse == null) { CurrentWarehouse = new Warehouse(); CurrentWarehouse.RegisterCollection(LiveWarehouse.Things); } _currentProtoModelObserver.Reset(); CurrentWarehouse.RegisterObserver(_currentProtoModelObserver); TimeMachine.SynchronizeWarehouse(oldSituation, CurrentWarehouse); if (_currentProtoModelObserver.SomethingChanged()) { var transaction = _currentProtoModelObserver.GetTransaction(_toProtobuf, Configuration.TimeMachineSenderName); var protoData = _toProtobuf.Convert(transaction); Send(protoData); } } else { Send("error: instruction unknown"); Logger.Info(_channel.Endpoint + " | " + LastSenderID + " | instruction unknown"); } } } } }