/// <summary> /// 发送房间同步消息 /// </summary> /// <param name="msgEntries"></param> public bool SendMessage(SyncMsg msg) { if (syncClient == null) { return(false); } if (!syncClient.Running) { return(false); } SyncProto syncProto = new SyncProto(); syncProto.SyncMsg = msg; if (messageType == CollaborationMessageType.JSON) { string msgData = JsonUtil.Serialize(syncProto, false, false); syncClient.SendMessage(msgData); } else if (messageType == CollaborationMessageType.ProtoBuf) { syncClient.SendMessage(syncProto.ToByteArray()); } //Debug.Log("Send Sync: " + syncProto); return(true); }
// Dequeue an update. Block if there are no updates in the queue. public SyncMsg Dequeue() { SyncMsg update = null; lock (m_syncRoot) { // If the queue is empty, wait for it to contain something if (m_queue.Count < 1 && m_firstQueue.Count < 1) { Monitor.Wait(m_syncRoot); } if (m_firstQueue.Count > 0) { update = m_firstQueue.Dequeue(); } else { if (m_queue.Count > 0) { UUID id = m_queue.Dequeue(); if (m_updates.TryGetValue(id, out update)) { m_updates.Remove(id); } } } } return(update); }
/// <summary> /// creates the message to be sent to the server /// </summary> void CreateMessage() { ObjectInfo info = new ObjectInfo(); info.ObjType = focused.name; //create message entry MsgEntry entry = new MsgEntry(); entry.OpType = MsgEntry.Types.OP_TYPE.Upd; entry.ShowId = showObject.ShowId; //add the camera's current position entry.Pr.Add(mainCamera.transform.position.x); entry.Pr.Add(mainCamera.transform.position.y); entry.Pr.Add(mainCamera.transform.position.z); //add the focused object position entry.Pr.Add(focused.transform.position.x); entry.Pr.Add(focused.transform.position.y); entry.Pr.Add(focused.transform.position.z); //add the name of the focusedObject entry.Info = info; //create sync message SyncMsg msg = new SyncMsg(); msg.MsgEntry.Add(entry); //send message cm.SendMessage(msg); }
/////////////////////////////////////////////////////////// // Sending messages out to the other side of the connection /////////////////////////////////////////////////////////// // Send messages from the update Q as fast as we can DeQueue them // *** This is the main send loop thread for each connected client private void SendLoop() { try { //while (true) while (!_shouldStopSend && m_tcpConnection.Connected) { SyncMsg msg = m_outQ.Dequeue(); if (msg != null) { if (m_collectingStats) { currentQueue.Event(-1); } // Do any conversion if it was not done earlier (on a friendlier thread) if (msg.ConvertOut(m_regionSyncModule)) { Send(msg); } } } } catch (Exception e) { m_log.ErrorFormat("{0} has disconnected: {1} (SendLoop)", description, e); } Shutdown(); }
/////////////////////////////////////////////////////////// // Receiving messages from the other side ofthe connection /////////////////////////////////////////////////////////// private void ReceiveLoop() { m_log.WarnFormat("{0} Thread running: {1}", LogHeader, m_rcvLoop.Name); //while (true && m_tcpConnection.Connected) while (!_shouldStopRecv && m_tcpConnection.Connected) { SyncMsg msg; // Try to get the message from the network stream try { msg = SyncMsg.SyncMsgFactory(m_tcpConnection.GetStream(), this); // m_log.WarnFormat("{0} Recv msg: {1}", LogHeader, msg.ToString()); } // If there is a problem reading from the client, shut 'er down. catch (Exception e) { //ShutdownClient(); m_log.ErrorFormat("{0}: ReceiveLoop error. Connector {1} disconnected: {2}.", LogHeader, m_connectorNum, e); Shutdown(); return; } // Handle message HandleMessage(msg); } }
// Enqueue an update // Note that only one update for each id is queued so it is possible that this particular // update will not get queued if there is already one queued for that id. // Returns 'true' if the object was actually enqueued. public bool Enqueue(UUID id, SyncMsg update) { bool ret = false; lock (m_syncRoot) { if (!m_updates.ContainsKey(id)) { m_queue.Enqueue(id); m_updates[id] = update; ret = true; } else { OverWrittenUpdates++; // If this is an update, we merge our update flags SyncMsgUpdatedProperties updatedPropMsg = update as SyncMsgUpdatedProperties; SyncMsgUpdatedProperties existingUpdatedPropMsg = m_updates[id] as SyncMsgUpdatedProperties; if (updatedPropMsg != null && existingUpdatedPropMsg != null) { existingUpdatedPropMsg.AddUpdates(updatedPropMsg.SyncableProperties); } else { // It is very odd that it is not one of ours. Don't know how another type got into the list. } } Monitor.Pulse(m_syncRoot); } return(ret); }
public IEnumerator UpdateContainerData() { if (!connectToServer) { yield break; } while (true) { if (extraOperating) { SyncMsg sync = new SyncMsg(); sync.MsgEntry.Add(new MsgEntry[1] { cv_originContainer.co.CreateMsgEntry() }); cm.SendMessage(sync); if (Time.realtimeSinceStartup > extraOperatingEndTime) { extraOperating = false; } } yield return(new WaitForSecondsRealtime(0.03f)); } }
// Add a message to the first of the queue. public void QueueMessageFirst(SyncMsg update) { lock (m_syncRoot) { m_firstQueue.Enqueue(update); Monitor.Pulse(m_syncRoot); } }
private void HandleMessage(SyncMsg msg) { // m_log.DebugFormat("{0} Recv msg: {1}: {2}", LogHeader, this.Description, msg.ToString()); CollectReceiveStat(msg.MType.ToString(), msg.DataLength); // TODO: Consider doing the data unpacking on a different thread than the input reader thread try { msg.ConvertIn(m_regionSyncModule); } catch (Exception e) { if (msg == null) { m_log.ErrorFormat("{0} Exception converting msg: NULL MESSAGE: {1}", LogHeader, e); } else { m_log.ErrorFormat("{0} Exception converting msg: type={1},len={2}: {3}", LogHeader, msg.MType, msg.DataLength, e); } } // TODO: Consider doing the message processing on a different thread than the input reader thread try { msg.HandleIn(m_regionSyncModule); } catch (Exception e) { if (msg == null) { m_log.ErrorFormat("{0} Exception handling msg: NULL MESSAGE: {1}", LogHeader, e); } else { m_log.ErrorFormat("{0} Exception handling msg: type={1},len={2}: {3}", LogHeader, msg.MType, msg.DataLength, e); } } // If this is an initialization message, print out info and start stats gathering if initialized enough if (msg.MType == SyncMsg.MsgType.RegionName || msg.MType == SyncMsg.MsgType.ActorID) { switch (msg.MType) { case SyncMsg.MsgType.RegionName: m_log.DebugFormat("Syncing to region \"{0}\"", otherSideRegionName); break; case SyncMsg.MsgType.ActorID: m_log.DebugFormat("Syncing to actor \"{0}\"", otherSideActorID); break; } if (otherSideRegionName != null && otherSideActorID != null) { StartCollectingStats(); } } }
public override bool Synchronize(SyncMsg sync) { CheckDisposed(); if (sync == SyncMsg.ksyncUndoRedo || sync == SyncMsg.ksyncFullRefresh) { OnMasterRefresh(null); return true; } return base.Synchronize (sync); }
public override bool Synchronize(SyncMsg sync) { CheckDisposed(); if (sync == SyncMsg.ksyncUndoRedo || sync == SyncMsg.ksyncFullRefresh) { OnMasterRefresh(null); return(true); } return(base.Synchronize(sync)); }
// Queue the outgoing message so it it sent "now" and before update messages. public void ImmediateOutgoingMsg(SyncMsg msg) { msg.LogTransmission(this); // The new way is to add a first queue and to place this message at the front. m_outQ.QueueMessageFirst(msg); if (m_collectingStats) { currentQueue.Event(1); } }
public bool KeepAlive(SyncMsg msg) { TimeSpan timePassed = DateTime.Now - m_lastSendTime; if (timePassed.TotalMilliseconds > KeepAliveMaxInterval) { ImmediateOutgoingMsg(msg); return(true); } else { return(false); } }
/// <summary> /// creates the message to be sent to the server. /// Content: device Ip /// </summary> void PlayAction() { MsgEntry msgEntry = new MsgEntry(); msgEntry.OpType = MsgEntry.Types.OP_TYPE.Upd; msgEntry.ShowId = "000"; ObjectInfo info = new ObjectInfo(); info.ObjType = collaborationManager.clientId; msgEntry.Info = info; SyncMsg msg = new SyncMsg(); msg.MsgEntry.Add(msgEntry); collaborationManager.SendMessage(msg); }
/// <summary> /// creates a message that contains /// the waiting player id /// </summary> void WaitingPlayerMessage(string currPlayer) { MsgEntry msgEntry = new MsgEntry(); msgEntry.OpType = MsgEntry.Types.OP_TYPE.Upd; msgEntry.ShowId = "001"; ObjectInfo info = new ObjectInfo(); info.ObjType = currPlayer; msgEntry.Info = info; SyncMsg msg = new SyncMsg(); msg.MsgEntry.Add(msgEntry); collaborationManager.SendMessage(msg); }
/// <summary> /// manages the color of the cell according to the player that clicks it /// </summary> /// <param name="ip"></param> private void ClickCube(string ip, string cellname, float cellcount) { if (ip == playerA.id) { CurrentColor = 0; blueCounter += 1; if (blueCounter > 3) { blueSelection.Clear(); blueCounter = 1; } counter = blueCounter; } if (ip == playerB.id) { CurrentColor = CType.green; greenCounter += 1; if (greenCounter > 3) { greenSelection.Clear(); greenCounter = 1; } counter = greenCounter; } MsgEntry entry = new MsgEntry(); entry.OpType = MsgEntry.Types.OP_TYPE.Upd; entry.ShowId = ip; ObjectInfo info = new ObjectInfo(); info.ObjType = cellname; entry.Info = info; entry.Pr.Add(counter); entry.Pr.Add(cellcount); entry.Vec.Add((long)CurrentColor); SyncMsg msg = new SyncMsg(); msg.MsgEntry.Add(entry); collaborationManager.SendMessage(msg); }
void Update() { if (collaborationManager != null) { if (collaborationManager.enterRoomResult == EnterRoomResult.EnterRoomSuccess) { MsgEntry entry = new MsgEntry(); entry.OpType = MsgEntry.Types.OP_TYPE.Upd; entry.ShowId = "Test"; GetTransformFloat(cube.transform, entry); SyncMsg msg = new SyncMsg(); msg.MsgEntry.Add(entry); collaborationManager.SendMessage(msg); } } }
/// <summary> /// creates a message that is bound to be use only /// when trash button is selected /// </summary> void CreateTrashMessage() { ObjectInfo info = new ObjectInfo(); info.ObjType = focused.name; //create message entry MsgEntry entry = new MsgEntry(); entry.OpType = MsgEntry.Types.OP_TYPE.Upd; entry.ShowId = "showId002"; //add the name of the focused Object entry.Info = info; //create Sync message SyncMsg msg = new SyncMsg(); msg.MsgEntry.Add(entry); cm.SendMessage(msg); }
private void ClickCube() { CurrentColor += 1; if ((int)CurrentColor > 2) { CurrentColor = 0; } MsgEntry entry = new MsgEntry(); entry.OpType = MsgEntry.Types.OP_TYPE.Upd; entry.ShowId = showObject.ShowId; entry.Vec.Add((long)CurrentColor); SyncMsg msg = new SyncMsg(); msg.MsgEntry.Add(entry); cm.SendMessage(msg); }
/// <summary> /// Enqueue update of an object/avatar into the outgoing queue, and return right away /// </summary> /// <param name="id">UUID of the object/avatar</param> /// <param name="update">the update infomation in byte format</param> public void EnqueueOutgoingUpdate(UUID id, SyncMsg update) { // m_log.DebugFormat("{0} Enqueue msg {1}", LogHeader, update.ToString()); update.LogTransmission(this); // Enqueue is thread safe bool actuallyQueued = m_outQ.Enqueue(id, update); /* BEGIN DEBUG * if (!actuallyQueued) * { * SyncMsgUpdatedProperties upd = update as SyncMsgUpdatedProperties; * m_log.DebugFormat("{0} EnqueueOutgoingUpdate. Multiple update. UUID={1}, prop={2}", * LogHeader, upd.Uuid, upd.PropsAsString()); * } * END DEBUG */ if (actuallyQueued && m_collectingStats) { currentQueue.Event(1); } }
public void SelectOnePlanet(string name, System.Action cb = null, bool sendMsg = true, bool animating = true, float animationStartUniverseTime = -1) { if (selected) { //Debug.Log("Select Blocked!!!!!!!!!!!"); return; } if (planetMap.ContainsKey(name)) { PlanetObject sel = planetMap[name]; if (sendMsg && connectToServer) { MsgEntry me_toSend = sel.CreateMsgEntry(); SyncMsg sync = new SyncMsg(); sync.MsgEntry.Add(me_toSend); Debug.Log("Send select plaent![" + name + "]"); cm.SendMessage(sync); } } }
private void RequestToServer() { if (cm != null) { if (cm.enterRoomResult == EnterRoomResult.EnterRoomSuccess) { MsgEntry entry = new MsgEntry(); entry.OpType = MsgEntry.Types.OP_TYPE.Upd; entry.ShowId = m_showObject.ShowId; entry.Vec.Add((long)m_behaviourType); if (m_behaviourType == BehaviourType.FingerDrag) { GetTransformFloat(this.transform, entry); } SyncMsg msg = new SyncMsg(); msg.MsgEntry.Add(entry); cm.SendMessage(msg); } } }
//Send out a messge directly. This should only by called for short messages that are not sent frequently. //Don't call this function for sending out updates. Call EnqueueOutgoingUpdate instead private void Send(SyncMsg msg) { // m_log.DebugFormat("{0} Send msg: {1}: {2}", LogHeader, this.Description, msg.ToString()); if (m_tcpConnection.Connected) { try { byte[] data = msg.GetWireBytes(); CollectSendStat(msg.MType.ToString(), msg.DataLength); // Rather than async write, use the TCP flow control to stop this thread if the // receiver cannot consume the data quick enough. m_tcpConnection.GetStream().Write(data, 0, data.Length); m_lastSendTime = DateTime.Now; /* * m_tcpConnection.GetStream().BeginWrite(data, 0, data.Length, ar => * { * if (m_tcpConnection.Connected) * { * try * { * m_tcpConnection.GetStream().EndWrite(ar); * } * catch (Exception) * { } * } * }, null); */ } catch (Exception e) { m_log.ErrorFormat("{0}:Error in Send() {1}/{2} has disconnected: connector={3}, msgType={4}. e={5}", LogHeader, otherSideActorID, otherSideRegionName, m_connectorNum, msg.MType.ToString(), e); Shutdown(); } } }
/// <summary> /// Handle a change by adding a record to the database (only if the /// change does not arise directly out of processing a sync record from elsewhere). /// </summary> /// <param name="hvo"></param> /// <param name="tag"></param> /// <param name="ivMin"></param> /// <param name="cvIns"></param> /// <param name="cvDel"></param> public void PropChanged(int hvo, int tag, int ivMin, int cvIns, int cvDel) { CheckDisposed(); if (hvo == m_hvoIgnore && tag == m_flidIgnore) { return; } if (tag < 0) { return; // common for fake properties } // If it's a virtual property we don't want to make a sync record. IVwCacheDa cda = m_cache.MainCacheAccessor as IVwCacheDa; IVwVirtualHandler vh = cda.GetVirtualHandlerId(tag); if (vh != null) { return; } // (CLE-76) Topics List Editor needs to be sync'd with the database when // we make certain changes to possibility lists, especially structural ones // like adding/removing/moving/promoting possibilities. The simplest thing // to do for now, is to indicate a full refresh is needed when any of these // properties are being altered (even if it turns out to be minor). SyncMsg msg = SyncMsg.ksyncSimpleEdit; if (tag == (int)CmPossibility.CmPossibilityTags.kflidName || tag == (int)CmPossibility.CmPossibilityTags.kflidAbbreviation || tag == (int)CmPossibilityList.CmPossibilityListTags.kflidPossibilities || tag == (int)CmPossibility.CmPossibilityTags.kflidSubPossibilities) { // NOTE: assume that apps that watch for ksyncFullRefresh will only // refresh once, and not multiple times if multiple refreshes get posted. // This is how Topic List Editor currently behaves. msg = SyncMsg.ksyncFullRefresh; } m_cache.StoreSync(m_appGuid, new SyncInfo(msg, hvo, tag)); }
public void PutBackPlanet(bool animate = true) { if (!selected) { //Debug.Log("Back Blocked!!!!!!!!!!!!"); return; } if (connectToServer) { //Debug.Log("Send back planet!!!"); SyncMsg sync = new SyncMsg(); sync.MsgEntry.Add(new MsgEntry[1] { planetObject_notSelected.CreateMsgEntry() }); cm.SendMessage(sync); } else { // 单机版再说吧!! } }
/// ------------------------------------------------------------------------------------ /// <summary> /// Called just before a window syncronizes it's views with DB changes (e.g. when an /// undo or redo command is issued). /// </summary> /// <param name="sync">syncronization message</param> /// ------------------------------------------------------------------------------------ public override void PreSynchronize(SyncMsg sync) { throw new NotImplementedException(); }
/// ------------------------------------------------------------------------------------ /// <summary> /// Called when a window syncronizes it's views with DB changes (e.g. when an undo or /// redo command is issued). /// </summary> /// <param name="sync">syncronization message</param> /// <returns>True if the sync message was handled; false, indicating that the /// application should refresh all windows. </returns> /// ------------------------------------------------------------------------------------ public virtual bool Synchronize(SyncMsg sync) { CheckDisposed(); if (sync == SyncMsg.ksyncStyle) { // force our stylesheet to resync (LT-7382). ResyncStylesheet(); ResyncRootboxStyles(); return true; } return false; }
/// ------------------------------------------------------------------------------------ /// <summary> /// /// </summary> /// <param name="sync"></param> /// <returns></returns> /// ------------------------------------------------------------------------------------ public bool Synchronize(SyncMsg sync) { CheckDisposed(); return false; }
public bool Synchronize(SyncMsg sync) { throw new NotImplementedException(); }
/// ------------------------------------------------------------------------------------ /// <summary> /// Called just before a window syncronizes it's views with DB changes (e.g. when an /// undo or redo command is issued). /// </summary> /// <param name="sync">syncronization message</param> /// ------------------------------------------------------------------------------------ public virtual void PreSynchronize(SyncMsg sync) { CheckDisposed(); // TODO: Implement it. This is copied from TE. }
/// ------------------------------------------------------------------------------------ /// <summary> /// Cycle through the applications main windows and synchronize them with database /// changes. /// </summary> /// <param name="sync">synchronization information record</param> /// <returns>false if a RefreshAllViews was performed or presync failed; this suppresses /// subsequent sync messages. True to continue processing.</returns> /// ------------------------------------------------------------------------------------ public virtual bool Synchronize(SyncMsg sync) { CheckDisposed(); if (m_suppressedCacheInfo != null) { Queue<SyncMsg> messages = m_suppressedCacheInfo.Queue; if (!messages.Contains(sync)) messages.Enqueue(sync); return true; } if (sync == SyncMsg.ksyncFullRefresh) { RefreshAllViews(); return false; } foreach (IFwMainWnd wnd in MainWindows) wnd.PreSynchronize(sync); if (sync == SyncMsg.ksyncWs) { // REVIEW TeTeam: AfLpInfo::Synchronize calls AfLpInfo::FullRefresh, which // clears the cache, loads the styles, loads ws and updates wsf, load project // basics, updates LinkedFiles root, load overlays and refreshes possibility // lists. I don't think we need to do any of these here. RefreshAllViews(); return false; } foreach (IFwMainWnd wnd in MainWindows) { if (!wnd.Synchronize(sync)) { // The window itself was not able to process the message successfully; // play safe and refresh everything RefreshAllViews(); return false; } } return true; }
/// ------------------------------------------------------------------------------------ /// <summary> /// Called just before a window syncronizes it's views with DB changes (e.g. when an /// undo or redo command is issued). /// </summary> /// <param name="sync">syncronization message</param> /// ------------------------------------------------------------------------------------ public abstract void PreSynchronize(SyncMsg sync);
/// ------------------------------------------------------------------------------------ /// <summary> /// Called just before a window synchronizes it's views with DB changes (e.g. when an /// undo or redo command is issued). /// </summary> /// <param name="sync">syncronization message</param> /// ------------------------------------------------------------------------------------ public override void PreSynchronize(SyncMsg sync) { CheckDisposed(); if (sync == SyncMsg.ksyncWs) { // Moved the call to UpdateWritingSystemsInCombobox() from OnFileProjectProperties() // to here. This fixes the crash from TE-5337. // We have to reload the writing systems that are displayed in the combo box UpdateWritingSystemsInCombobox(); } }
/// ------------------------------------------------------------------------------------ /// <summary> /// /// </summary> /// <param name="cache"></param> /// <param name="msg"></param> /// ------------------------------------------------------------------------------------ public UndoWithSyncAction(FdoCache cache, SyncMsg msg) { m_cache = cache; m_syncMsg = msg; }
/// ------------------------------------------------------------------------------------ /// <summary> /// Cycle through the applications main windows and synchronize them with database /// changes /// </summary> /// <param name="sync">synchronization information record</param> /// <param name="cache">database cache</param> /// <returns> /// false if a refreshall was performed or presync failed; this suppresses /// subsequent sync messages. True to continue processing. /// </returns> /// ------------------------------------------------------------------------------------ public override bool Synchronize(SyncMsg sync, FdoCache cache) { m_syncs.Add(sync); m_cacheFromSynchronize = cache; return true; }
/// ------------------------------------------------------------------------------------ /// <summary> /// /// </summary> /// <param name="sync"></param> /// <returns></returns> /// ------------------------------------------------------------------------------------ public bool Synchronize(SyncMsg sync) { CheckDisposed(); return(false); }
/// ------------------------------------------------------------------------------------ /// <summary> /// /// </summary> /// <param name="sync"></param> /// <returns></returns> /// ------------------------------------------------------------------------------------ public void PreSynchronize(SyncMsg sync) { }
/// ------------------------------------------------------------------------------------ /// <summary> /// Called when a window syncronizes it's views with DB changes (e.g. when an undo or /// redo command is issued). /// </summary> /// <param name="sync">syncronization message</param> /// <returns>true if successful (false results in RefreshAll)</returns> /// ------------------------------------------------------------------------------------ public abstract bool Synchronize(SyncMsg sync);
/// ------------------------------------------------------------------------------------ /// <summary> /// Initializes a new instance of the <see cref="SyncUndoAction"/> class. /// </summary> /// <param name="app">The application.</param> /// <param name="syncMsg">The sync message.</param> /// ------------------------------------------------------------------------------------ public SyncUndoAction(IApp app, SyncMsg syncMsg) { m_app = app; m_syncMsg = syncMsg; }
/// ------------------------------------------------------------------------------------ /// <summary> /// Called just before a window synchronizes it's views with DB changes (e.g. when an /// undo or redo command is issued). /// </summary> /// <param name="sync">syncronization message</param> /// ------------------------------------------------------------------------------------ public override void PreSynchronize(SyncMsg sync) { CheckDisposed(); }
/// ------------------------------------------------------------------------------------ /// <summary> /// Handle import sync messages. /// </summary> /// <param name="sync"></param> /// ------------------------------------------------------------------------------------ public override bool Synchronize(SyncMsg sync) { CheckDisposed(); switch (sync) { case SyncMsg.ksyncStyle: ParaStyleListHelper.IgnoreListRefresh = true; CharStyleListHelper.IgnoreListRefresh = true; ReSynchStyleSheet(); // REVIEW: can we skip the rest of this, if we know that a ksynchUndoRedo will // do it for us later? RefreshAllViews(); ParaStyleListHelper.IgnoreListRefresh = false; CharStyleListHelper.IgnoreListRefresh = false; InitStyleComboBox(); return true; case SyncMsg.ksyncUndoRedo: ParaStyleListHelper.IgnoreListRefresh = true; CharStyleListHelper.IgnoreListRefresh = true; RefreshAllViews(); ParaStyleListHelper.IgnoreListRefresh = false; CharStyleListHelper.IgnoreListRefresh = false; InitStyleComboBox(); return true; case SyncMsg.ksyncWs: return true; // Don't care -- do nothing } // Updating views in all windows. IHelpTopicProvider.App should never be null unless // running from a test. if (m_app != null) return false; // causes a RefreshAllViews, and allows caller to notify its callers. RefreshAllViews(); // special case for testing. return true; }
/// ------------------------------------------------------------------------------------ /// <summary> /// Verifies the synch message exists. /// </summary> /// <param name="cache">The cache.</param> /// <param name="msg">The MSG.</param> /// <param name="hvo">The hvo.</param> /// <param name="tag">The tag.</param> /// ------------------------------------------------------------------------------------ internal void VerifySynchMessageExists(FdoCache cache, SyncMsg msg, int hvo, int tag) { Assert.AreEqual(m_cacheFromSynchronize, cache); bool foundMessage = false; foreach (SyncMsg info in m_syncs) { if (info == msg) foundMessage = true; } m_syncs.Clear(); Assert.IsTrue(foundMessage, "Should find the synch message"); }