public bool ChangeReceived(string sessionKey, int clientRevisionId, string selStr, string changeStr) { try { lock (lockObject) { var sess = sessions.Find(x => x.SessionKey == sessionKey); if (sess == null) { return(false); } sess.LastActiveUtc = DateTime.UtcNow; ensureDocLoaded(sess.DocId); var doc = docs.Find(x => x.DocId == sess.DocId); if (doc == null) { return(false); } var sel = JsonConvert.DeserializeObject <Selection>(selStr); ChangeSet cs = changeStr != null?ChangeSet.FromJson(changeStr) : null; logger.Verbose("Change received from session {sessionKey}: client rev {clientRevisionId}, sel: {sel} , change: \n{change}", sessionKey, clientRevisionId, selStr, changeStr); // Who are we broadcasting to? List <string> receivers = new List <string>(); foreach (var x in sessions) { if (x.RequestedUtc == DateTime.MinValue && x.DocId == doc.DocId) { receivers.Add(x.SessionKey); } } // What are we broadcasting? ChangeToBroadcast ctb = new ChangeToBroadcast { SourceSessionKey = sessionKey, SourceBaseDocRevisionId = clientRevisionId, NewDocRevisionId = doc.Revisions.Count - 1, ReceiverSessionKeys = receivers, }; // This is only about a changed selection if (cs == null) { sess.Selection = doc.ForwardSelection(sel, clientRevisionId); ctb.SelJson = JsonConvert.SerializeObject(getDocSels(sess.DocId)); logger.Verbose("Propagating selection update: {sels}", ctb.SelJson); } // We got us a real change set else { if (!cs.IsValid()) { logger.Warning("Change is invalid. Ending this session."); return(false); } ChangeSet csToProp; doc.ApplyChange(cs, sel, clientRevisionId, out csToProp, out sess.Selection); ctb.NewDocRevisionId = doc.Revisions.Count - 1; ctb.SelJson = JsonConvert.SerializeObject(getDocSels(sess.DocId)); ctb.ChangeJson = csToProp.SerializeJson(); logger.Verbose("Propagating changeset and selection update:\n{change}\n{sels}", ctb.ChangeJson, ctb.SelJson); } // Showtime! Broadcaster.EnqueueChangeForBroadcast(ctb); } return(true); } catch (Exception ex) { logger.Error(ex, "Error in ChangeReceived"); throw; } }