void CMUSHclientDoc::ResetOneTimer (CTimer * timer_item) { CmcDateTime tNow = CmcDateTime::GetTimeNow(); CmcDateTimeSpan tsOneDay (1, 0, 0, 0); if (!timer_item->bEnabled) // ignore un-enabled timers return; // so we can see when it is likely to fire next timer_item->tWhenFired = tNow; // for timers that go off "at" a time, find today's date, and move the time in if (timer_item->iType == CTimer::eAtTime) { timer_item->tFireTime = CmcDateTime (tNow.GetYear (), tNow.GetMonth (), tNow.GetDay (), timer_item->iAtHour, timer_item->iAtMinute, timer_item->fAtSecond); // if this time has passed, go onto tomorrow if (timer_item->tFireTime < tNow) timer_item->tFireTime += tsOneDay; } else // for periodic timers, find "now" and add the period to it timer_item->tFireTime = tNow + CmcDateTimeSpan (0, // now plus the interval timer_item->iEveryHour, timer_item->iEveryMinute, timer_item->fEverySecond) - CmcDateTimeSpan (0, // minus the offset timer_item->iOffsetHour, timer_item->iOffsetMinute, timer_item->fOffsetSecond); } // end of CMUSHclientDoc::ResetOneTimer
void CMUSHclientDoc::CheckTimerList (CTimerMap & TimerMap) { CTimer * timer_item; CString strTimerName; CmcDateTime tNow = CmcDateTime::GetTimeNow(); CmcDateTimeSpan tsOneDay (1, 0, 0, 0); // check for deleted chat sessions for (POSITION chatpos = m_ChatList.GetHeadPosition (); chatpos; ) { POSITION oldpos = chatpos; CChatSocket * pSocket = m_ChatList.GetNext (chatpos); if (pSocket->m_bDeleteMe) { m_ChatList.RemoveAt (oldpos); delete pSocket; break; // list is no longer valid } } set <string> firedTimersList; POSITION pos; // iterate through all timers for this document - first build list of them for (pos = TimerMap.GetStartPosition(); pos; ) { TimerMap.GetNextAssoc (pos, strTimerName, timer_item); if (!timer_item->bEnabled) // ignore un-enabled timers continue; // no timer activity whilst closed or in the middle of connecting, or if not enabled if (!timer_item->bActiveWhenClosed) if (m_iConnectPhase != eConnectConnectedToMud) continue; // if not ready to fire yet, ignore it if (timer_item->tFireTime > tNow) continue; firedTimersList.insert ((LPCTSTR) strTimerName); // add to list of fired timers } // now process list, checking timer still exists in case a script deleted one // see: http://www.gammon.com.au/forum/?id=10358 for (set <string>::iterator it = firedTimersList.begin (); it != firedTimersList.end (); it++) { // get next fired timer from list strTimerName = it->c_str (); // check still exists, get pointer if so if (!TimerMap.Lookup (strTimerName, timer_item)) continue; timer_item->nMatched++; // count timer matches timer_item->tWhenFired = tNow; // when it fired m_iTimersFiredCount++; m_iTimersFiredThisSessionCount++; // TRACE1 ("Fired at = %10.8f\n", timer_item->tWhenFired.m_dt); if (timer_item->strLabel.IsEmpty ()) Trace ("Fired unlabelled timer "); else Trace ("Fired timer %s", (LPCTSTR) timer_item->strLabel); // TRACE1 ("Fire time = %10.8f\n", timer_item->tFireTime.m_dt); // update fire time - before calling the script, in case it takes a long time if (timer_item->iType == CTimer::eAtTime) timer_item->tFireTime += tsOneDay; else timer_item->tFireTime += CmcDateTimeSpan (0, // add the interval timer_item->iEveryHour, timer_item->iEveryMinute, timer_item->fEverySecond); // in case clock changes or some such thing, make sure timer will be due to // fire in the future, not the past, or it might go mad and keep firing if (timer_item->tFireTime <= tNow) ResetOneTimer (timer_item); // if one-shot, disable it, so if the timer routine finds it again while // it is still executing (eg. due to a syntax error dialog box) then // it won't fire again. if (timer_item->bOneShot) timer_item->bEnabled = false; // send timer message, if this timer list is "active" CString strExtraOutput; timer_item->bExecutingScript = true; // cannot be deleted now m_iCurrentActionSource = eTimerFired; SendTo (timer_item->iSendTo, timer_item->strContents, timer_item->bOmitFromOutput, // omit from output timer_item->bOmitFromLog, // omit from log TFormat ("Timer: %s", (LPCTSTR) timer_item->strLabel), timer_item->strVariable, strExtraOutput ); m_iCurrentActionSource = eUnknownActionSource; timer_item->bExecutingScript = false; // can be deleted now // display any stuff sent to output window if (!strExtraOutput.IsEmpty ()) DisplayMsg (strExtraOutput, strExtraOutput.GetLength (), COMMENT); // invoke script subroutine, if any if (!timer_item->strProcedure.IsEmpty ()) if (CheckScriptingAvailable ("Timer", timer_item->dispid, timer_item->strProcedure)) continue; if (timer_item->dispid != DISPID_UNKNOWN) // if we have a dispatch id { CString strType = "timer"; CString strReason = TFormat ("processing timer \"%s\"", (LPCTSTR) timer_item->strLabel); // get unlabelled timer's internal name const char * pLabel = timer_item->strLabel; if (pLabel [0] == 0) pLabel = GetTimerRevMap () [timer_item].c_str (); if (GetScriptEngine () && GetScriptEngine ()->IsLua ()) { list<double> nparams; list<string> sparams; sparams.push_back (pLabel); timer_item->bExecutingScript = true; // cannot be deleted now GetScriptEngine ()->ExecuteLua (timer_item->dispid, timer_item->strProcedure, eTimerFired, strType, strReason, nparams, sparams, timer_item->nInvocationCount); timer_item->bExecutingScript = false; // can be deleted now } // end of Lua else { // prepare for the arguments (so far, 1 which is the timer name) // WARNING - arguments should appear in REVERSE order to what the sub expects them! enum { eTimerName, eArgCount, // this MUST be last }; COleVariant args [eArgCount]; DISPPARAMS params = { args, NULL, eArgCount, 0 }; // args [eTimerName] = strTimerName; args [eTimerName] = pLabel; timer_item->bExecutingScript = true; // cannot be deleted now ExecuteScript (timer_item->dispid, timer_item->strProcedure, eTimerFired, strType, strReason, params, timer_item->nInvocationCount); timer_item->bExecutingScript = false; // can be deleted now } // not Lua } // end of having a dispatch ID // If they passed the wrong arguments to the timer routine, the dialog box // might appear, and the timer be deleted, before we get a chance to // do this code, in which case the timer has gone. // Just get it again to be sure ... [#430] if (!TimerMap.Lookup (strTimerName, timer_item)) return; // if one-shot timer, delete from list if (timer_item->bOneShot) { TimerMap.RemoveKey (strTimerName); delete timer_item; SortTimers (); } } // end of processing each timer } // end of CMUSHclientDoc::CheckTimerMap
void CTimerDlg::DoDataExchange(CDataExchange* pDX) { CDialog::DoDataExchange(pDX); if(!pDX->m_bSaveAndValidate) { if (m_current_timer && m_current_timer->bIncluded) m_strIncluded = "(included)"; else m_strIncluded.Empty (); } //{{AFX_DATA_MAP(CTimerDlg) DDX_Control(pDX, IDC_SEND_TO, m_ctlSendTo); DDX_Control(pDX, IDC_SCRIPT_NAME, m_ctlProcedure); DDX_Control(pDX, IDC_TIMER_CONTENTS, m_ctlContents); DDX_Control(pDX, IDC_BUTTON_AT, m_ctlButtonAt); DDX_Check(pDX, IDC_ENABLED, m_bEnabled); DDX_Text(pDX, IDC_TIMER_LABEL, m_strLabel); DDX_Text(pDX, IDC_AT_HOUR, m_iAtHour); DDV_MinMaxInt(pDX, m_iAtHour, 0, 23); DDX_Text(pDX, IDC_AT_MINUTE, m_iAtMinute); DDV_MinMaxInt(pDX, m_iAtMinute, 0, 59); DDX_Text(pDX, IDC_INTERVAL_HOUR, m_iEveryHour); DDV_MinMaxInt(pDX, m_iEveryHour, 0, 23); DDX_Text(pDX, IDC_INTERVAL_MINUTE, m_iEveryMinute); DDV_MinMaxInt(pDX, m_iEveryMinute, 0, 59); DDX_Text(pDX, IDC_OFFSET_HOUR, m_iOffsetHour); DDV_MinMaxInt(pDX, m_iOffsetHour, 0, 23); DDX_Text(pDX, IDC_OFFSET_MINUTE, m_iOffsetMinute); DDV_MinMaxInt(pDX, m_iOffsetMinute, 0, 59); DDX_MinMaxString(pDX, IDC_TIMER_CONTENTS, m_strContents); DDV_MinMaxString(pDX, m_strContents, 0, 32000); DDX_Radio(pDX, IDC_BUTTON_EVERY, m_iType); DDX_Text(pDX, IDC_SCRIPT_NAME, m_strProcedure); DDX_Check(pDX, IDC_ONE_SHOT_TIMER, m_bOneShot); DDX_Check(pDX, IDC_TEMPORARY, m_bTemporary); DDX_Check(pDX, IDC_ACTIVE_WHEN_CLOSED, m_bActiveWhenClosed); DDX_Text(pDX, IDC_GROUP, m_strGroup); DDX_CBIndex(pDX, IDC_SEND_TO, m_iSendTo); DDX_Text(pDX, IDC_VARIABLE, m_strVariable); DDX_Text(pDX, IDC_INCLUDED, m_strIncluded); DDX_Check(pDX, IDC_OMIT_FROM_OUTPUT, m_bOmitFromOutput); DDX_Check(pDX, IDC_OMIT_FROM_LOG, m_bOmitFromLog); DDX_Text(pDX, IDC_AT_SECOND, m_fAtSecond); DDV_MinMaxDouble(pDX, m_fAtSecond, 0., 59.99999); DDX_Text(pDX, IDC_INTERVAL_SECOND, m_fEverySecond); DDV_MinMaxDouble(pDX, m_fEverySecond, 0., 59.99999); DDX_Text(pDX, IDC_OFFSET_SECOND, m_fOffsetSecond); DDV_MinMaxDouble(pDX, m_fOffsetSecond, 0., 59.99999); //}}AFX_DATA_MAP if(pDX->m_bSaveAndValidate) { m_strLabel.TrimLeft (); m_strLabel.TrimRight (); m_strProcedure.TrimLeft (); m_strProcedure.TrimRight (); m_strGroup.TrimLeft (); m_strGroup.TrimRight (); if (m_iType == CTimer::eInterval) { CmcDateTimeSpan ts1 (0, m_iEveryHour, m_iEveryMinute, m_fEverySecond); CmcDateTimeSpan ts2 (0, m_iOffsetHour, m_iOffsetMinute, m_fOffsetSecond); if (ts1 <= CmcDateTimeSpan (0, 0, 0, 0)) { ::TMessageBox("The timer interval must be greater than zero."); DDX_Text(pDX, IDC_INTERVAL_HOUR, m_iEveryHour); pDX->Fail(); } // end of interval <= 0 if(ts2 >= ts1) { ::TMessageBox("The timer offset must be less than the timer period."); DDX_Text(pDX, IDC_OFFSET_HOUR, m_iOffsetHour); pDX->Fail(); } // end of offset >= period } // end of doing a periodical timer CString strTimerName; CTimer * timer_item; POSITION pos; for (pos = m_pTimerMap->GetStartPosition (); pos; ) { m_pTimerMap->GetNextAssoc (pos, strTimerName, timer_item); // don't compare against itself if (timer_item == m_current_timer) continue; // now check the label for duplicates if (!m_strLabel.IsEmpty ()) // we can have duplicate blank names if (m_strLabel.CompareNoCase (timer_item->strLabel) == 0) { CString strMsg; strMsg = TFormat ("The timer label \"%s\" is already in the list of timers.", (LPCTSTR) m_strLabel); ::UMessageBox(strMsg); DDX_Text(pDX, IDC_TIMER_LABEL, m_strLabel); pDX->Fail(); } } // end of checking each Timer if (!m_strLabel.IsEmpty ()) // we can have blank labels { // check label is valid if (CheckLabel (m_strLabel)) { ::TMessageBox ("The label must start with a letter and consist of letters" ", numbers or the underscore character."); DDX_Text(pDX, IDC_TIMER_LABEL, m_strLabel); pDX->Fail(); } } // end of having non-blank label if (m_strVariable.IsEmpty ()) // we can have blank variables { if (m_iSendTo == eSendToVariable) { ::TMessageBox("When sending to a variable you must specify a variable name. ", MB_ICONSTOP); DDX_Text(pDX, IDC_VARIABLE, m_strVariable); pDX->Fail(); } } else { // check variable name is valid if (CheckLabel (m_strVariable)) { ::TMessageBox("The variable name must start with a letter and consist of letters" ", numbers or the underscore character."); DDX_Text(pDX, IDC_VARIABLE, m_strVariable); pDX->Fail(); } } // end of having non-blank variable // check for speed walking OK, unless they are substituting if (m_iSendTo == eSendToSpeedwalk) { CString strResult = m_pDoc->DoEvaluateSpeedwalk (m_strContents); if (!strResult.IsEmpty ()) { if (strResult [0] == '*') // error in speedwalk string? { ::UMessageBox (strResult.Mid (1)); // already translated, I think DDX_Text(pDX, IDC_TIMER_CONTENTS, m_strContents); pDX->Fail(); } // end of error message } // end of non-empty speedwalk } // end of speed walking wanted if(m_strContents.IsEmpty () && m_strProcedure.IsEmpty ()) { ::TMessageBox("The timer contents cannot be blank unless you specify a script subroutine."); DDX_Text(pDX, IDC_TIMER_CONTENTS, m_strContents); pDX->Fail(); } // end of contents being blank if (!m_strProcedure.IsEmpty ()) // blank procedure is OK { // check procedure is valid if (CheckLabel (m_strProcedure, true)) { ::TMessageBox("The script subroutine name must start with a letter and consist of letters" ", numbers or the underscore character."); DDX_Text(pDX, IDC_SCRIPT_NAME, m_strProcedure); pDX->Fail(); } } // end of having non-blank procedure } // end of saving and validating }