mirror of
https://github.com/1dot13/source.git
synced 2026-08-26 14:30:26 +02:00
NETWORKED, BOUNDS_CHECKER and UBMODSHADYJOB are not defined in any source file, in CMakeLists.txt, or on any command line, so all 26 guarded sites were dead. NETWORKED is the most thoroughly dead of the three: its blocks include Networking.h, NetworkEvent.h, Communication.h and Application Msg.h, none of which exist in this repository, so that code could not have compiled since long before the CMake build. Multiplayer went through Multiplayer/ and RakNet instead. UBMODSHADYJOB was referenced by a stale comment pointing at builddefines.h, which is how it turned up. Removed with unifdef, so the surviving branch of each conditional is kept: the #ifndef BOUNDS_CHECKER bodies stay, and so does the #else of UBMODSHADYJOB. The compiler never saw any of this, so nothing shrinks: no object file's section sizes change, and the only difference in the executables is 24 to 64 assert line numbers shifting by the number of lines removed above them. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
853 lines
23 KiB
C++
853 lines
23 KiB
C++
#include <windows.h>
|
|
#include <mmsystem.h>
|
|
#include <string.h>
|
|
#include "stdlib.h"
|
|
#include "DEBUG.H"
|
|
#include "Timer Control.h"
|
|
#include "Overhead.h"
|
|
#include "Handle Items.h"
|
|
#include "worlddef.h"
|
|
#include "renderworld.h"
|
|
#include "Interface Control.h"
|
|
#include "KeyMap.h"
|
|
|
|
#ifndef WIN32_LEAN_AND_MEAN
|
|
#define WIN32_LEAN_AND_MEAN
|
|
#endif
|
|
|
|
#include "Soldier Control.h"
|
|
#include "connect.h"
|
|
|
|
// Base resolution of callback timer
|
|
static INT32 BASETIMESLICE = 10;
|
|
const INT32 FASTFORWARDTIMESLICE = 1000;
|
|
const LONGLONG FREQUENCY_CONST = 1000000;
|
|
static INT32 MIN_NOTIFY_TIME = 16000;
|
|
static INT32 UPDATETIMESLICE = 10000;
|
|
|
|
|
|
INT32 giClockTimer = -1;
|
|
INT32 giTimerDiag = 0;
|
|
|
|
UINT32 guiBaseJA2Clock = 0;
|
|
UINT32 guiBaseJA2NoPauseClock = 0;
|
|
|
|
BOOLEAN gfPauseClock = FALSE;
|
|
|
|
BOOLEAN gfHispeedClockMode = FALSE;
|
|
|
|
const inline UINT32 TIME_US_TO_MS(UINT32 value) { return value / 1000; }
|
|
const inline UINT32 TIME_MS_TO_US(UINT32 value) { return value * 1000; }
|
|
|
|
UINT32 giFastForwardPeriod = FASTFORWARDTIMESLICE;
|
|
BOOLEAN giFastForwardMode = FALSE;
|
|
INT32 giFastForwardKey = 0;
|
|
UINT32 guiTimeSlice = 0;
|
|
FLOAT gfClockSpeedPercent = 1.0;
|
|
LARGE_INTEGER gliPerfFreq = {0};
|
|
LARGE_INTEGER gliPerfCount = {0};
|
|
LARGE_INTEGER gliPerfCountNext = {0};
|
|
|
|
|
|
INT32 giTimerIntervals[ NUMTIMERS ] =
|
|
{
|
|
5, // Tactical Overhead
|
|
20, // NEXTSCROLL
|
|
200, // Start Scroll
|
|
200, // Animate tiles
|
|
1000, // FPS Counter
|
|
80, // PATH FIND COUNTER
|
|
150, // CURSOR TIMER
|
|
250, // RIGHT CLICK FOR MENU
|
|
300, // LEFT
|
|
30, // SLIDING TEXT
|
|
200, // TARGET REFINE TIMER
|
|
150, // CURSOR/AP FLASH
|
|
60, // FADE MERCS OUT
|
|
160, // PANEL SLIDE
|
|
1000, // CLOCK UPDATE DELAY
|
|
20, // PHYSICS UPDATE
|
|
100, // FADE ENEMYS
|
|
20, // STRATEGIC OVERHEAD
|
|
40,
|
|
500, // NON GUN TARGET REFINE TIMER
|
|
250, // IMPROVED CURSOR FLASH
|
|
500, // 2nd CURSOR FLASH
|
|
400, // RADARMAP BLINK AND OVERHEAD MAP BLINK SHOUDL BE THE SAME
|
|
400,
|
|
10, // Music Overhead
|
|
100, // Rubber band start delay
|
|
};
|
|
|
|
// TIMER COUNTERS
|
|
INT32 giTimerCounters[ NUMTIMERS ];
|
|
|
|
INT32 giTimerAirRaidQuote = 0;
|
|
INT32 giTimerAirRaidDiveStarted = 0;
|
|
INT32 giTimerAirRaidUpdate = 0;
|
|
INT32 giTimerCustomizable = 0;
|
|
INT32 giTimerTeamTurnUpdate = 0;
|
|
|
|
CUSTOMIZABLE_TIMER_CALLBACK gpCustomizableTimerCallback = NULL;
|
|
|
|
// Clock Callback event ID
|
|
MMRESULT gTimerID;
|
|
|
|
TIMECAPS gtc;
|
|
|
|
HANDLE ghClockThread;
|
|
DWORD gdwClockThreadId;
|
|
HANDLE ghClockThreadShutdown;
|
|
|
|
HANDLE ghNotifyThread;
|
|
DWORD gdwNotifyThreadId;
|
|
HANDLE ghNotifyThreadEvent;
|
|
HANDLE ghNotifyThreadShutdownComplete;
|
|
|
|
// GLOBALS FOR CALLBACK
|
|
UINT32 gCNT;
|
|
SOLDIERTYPE *gPSOLDIER;
|
|
|
|
// GLobal for displaying time diff ( DIAG )
|
|
UINT32 guiClockDiff = 0;
|
|
UINT32 guiClockStart = 0;
|
|
|
|
// BOB: made global to help track freeze issue
|
|
LONGLONG gliTimestampDiff = 0;
|
|
LONGLONG gliWaitTime = 0;
|
|
LONGLONG giIncrement = 0;
|
|
UINT32 giSleepTime = 0;
|
|
|
|
|
|
extern UINT32 guiCompressionStringBaseTime;
|
|
extern INT32 giFlashHighlightedItemBaseTime;
|
|
//extern INT32 giCompatibleItemBaseTime;//Moa:removed (see HandleMouseInCompatableItemForMapSectorInventory)
|
|
extern INT32 giAnimateRouteBaseTime;
|
|
extern INT32 giPotHeliPathBaseTime;
|
|
extern INT32 giPotMilitiaPathBaseTime;
|
|
extern INT32 giClickHeliIconBaseTime;
|
|
extern INT32 giExitToTactBaseTime;
|
|
extern UINT32 guiSectorLocatorBaseTime;
|
|
extern INT32 giCommonGlowBaseTime;
|
|
extern INT32 giFlashAssignBaseTime;
|
|
extern INT32 giFlashContractBaseTime;
|
|
extern UINT32 guiFlashCursorBaseTime;
|
|
extern INT32 giPotCharPathBaseTime;
|
|
|
|
// sevenfm: display overflow detection
|
|
extern void MapScreenMessage(UINT16 usColor, UINT8 ubPriority, STR16 pStringA, ...);
|
|
|
|
typedef void (*TIMER_NOTIFY_CALLBACK) ( INT32 timer, PTR state );
|
|
struct TIMER_NOTIFY_ITEM
|
|
{
|
|
TIMER_NOTIFY_CALLBACK callback;
|
|
PTR state;
|
|
};
|
|
typedef std::list<TIMER_NOTIFY_ITEM> TIMER_NOTIFY_ITEM_LIST;
|
|
typedef TIMER_NOTIFY_ITEM_LIST::iterator TIMER_NOTIFY_ITEM_ITERATOR;
|
|
static TIMER_NOTIFY_ITEM_LIST glNotifyCallbacks;
|
|
static CRITICAL_SECTION gcsNotifyLock;
|
|
|
|
static bool HasTimerNotifyCallbacks( );
|
|
static void BroadcastTimerNotify(INT32 );
|
|
static BOOLEAN UpdateTimeCounter( INT32 &counter, INT32 &iTimeLeft );
|
|
static BOOLEAN UpdateCounter( INT32 counter, INT32 &iTimeLeft);
|
|
static void UpdateTimer();
|
|
void ResetJA2ClockGlobalTimers(void);
|
|
|
|
UINT32 InitializeJA2TimerCallback( UINT32 uiDelay, LPTIMECALLBACK TimerProc, UINT32 uiUser );
|
|
|
|
// CALLBACKS
|
|
void CALLBACK FlashItem( UINT uiID, UINT uiMsg, DWORD uiUser, DWORD uiDw1, DWORD uiDw2 );
|
|
|
|
void CALLBACK TimeProc( UINT uID, UINT uMsg, DWORD dwUser, DWORD dw1, DWORD dw2 )
|
|
{
|
|
static BOOLEAN fInFunction = FALSE;
|
|
//SOLDIERTYPE *pSoldier;
|
|
|
|
if ( !fInFunction )
|
|
{
|
|
fInFunction = TRUE;
|
|
|
|
BOOLEAN timerDone = FALSE;
|
|
BOOLEAN tickTime = FALSE;
|
|
INT32 iTimeLeft = 0;
|
|
|
|
// Use QPC to check if BASETIMESLICE (in ms) has passed
|
|
if (IsHiSpeedClockMode())
|
|
{
|
|
// Only advance time when sufficient time has passed to exceed next time
|
|
QueryPerformanceCounter(&gliPerfCount);
|
|
if (gliPerfCount.QuadPart > gliPerfCountNext.QuadPart)
|
|
{
|
|
INT32 iNext = IsFastForwardMode() ? giFastForwardPeriod : UPDATETIMESLICE;
|
|
giIncrement = (iNext * gliPerfFreq.QuadPart) / FREQUENCY_CONST;
|
|
gliPerfCountNext.QuadPart = gliPerfCount.QuadPart + giIncrement;
|
|
iTimeLeft = iNext;
|
|
timerDone = IsFastForwardMode();
|
|
tickTime = TRUE;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
// When using millisecond timer, advance time everytime this function is called
|
|
tickTime = TRUE;
|
|
timerDone = !IsFastForwardMode();
|
|
}
|
|
if (tickTime)
|
|
{
|
|
guiBaseJA2NoPauseClock += BASETIMESLICE;
|
|
|
|
if ( !gfPauseClock )
|
|
{
|
|
UINT32 uiOldClock = guiBaseJA2Clock;
|
|
|
|
guiBaseJA2Clock += BASETIMESLICE;
|
|
|
|
// Terapevt suggested fix
|
|
if ((INT32)guiBaseJA2Clock < 0)
|
|
guiBaseJA2Clock = 0;
|
|
|
|
// detect overflow
|
|
if (uiOldClock > guiBaseJA2Clock)
|
|
{
|
|
MapScreenMessage(162, 0, L"guiBaseJA2Clock overflow detected!");
|
|
for (gCNT = 0; gCNT < TOTAL_SOLDIERS; gCNT++)
|
|
{
|
|
if (MercPtrs[gCNT])
|
|
MercPtrs[gCNT]->ResetSoldierChangeStatTimer();
|
|
}
|
|
}
|
|
|
|
for ( gCNT = 0; gCNT < NUMTIMERS; gCNT++ )
|
|
{
|
|
timerDone |= UpdateCounter( gCNT, iTimeLeft );
|
|
}
|
|
|
|
// Update some specialized countdown timers...
|
|
timerDone |= UpdateTimeCounter( giTimerAirRaidQuote, iTimeLeft );
|
|
timerDone |= UpdateTimeCounter( giTimerAirRaidDiveStarted, iTimeLeft );
|
|
timerDone |= UpdateTimeCounter( giTimerAirRaidUpdate, iTimeLeft );
|
|
timerDone |= UpdateTimeCounter( giTimerTeamTurnUpdate, iTimeLeft );
|
|
|
|
if ( gpCustomizableTimerCallback )
|
|
{
|
|
timerDone |= UpdateTimeCounter( giTimerCustomizable, iTimeLeft );
|
|
}
|
|
|
|
|
|
// If mapscreen...
|
|
if( guiTacticalInterfaceFlags & INTERFACE_MAPSCREEN )
|
|
{
|
|
// IN Mapscreen, loop through player's team.....
|
|
for ( gCNT = gTacticalStatus.Team[ gbPlayerNum ].bFirstID; gCNT <= gTacticalStatus.Team[ gbPlayerNum ].bLastID; gCNT++ )
|
|
{
|
|
gPSOLDIER = MercPtrs[ gCNT ];
|
|
timerDone |= UpdateTimeCounter( gPSOLDIER->timeCounters.PortraitFlashCounter, iTimeLeft );
|
|
timerDone |= UpdateTimeCounter( gPSOLDIER->timeCounters.PanelAnimateCounter, iTimeLeft );
|
|
}
|
|
}
|
|
else
|
|
{
|
|
// Set update flags for soldiers
|
|
////////////////////////////
|
|
for ( gCNT = 0; gCNT < guiNumMercSlots; gCNT++ )
|
|
{
|
|
gPSOLDIER = MercSlots[ gCNT ];
|
|
|
|
if ( gPSOLDIER != NULL )
|
|
{
|
|
timerDone |= UpdateTimeCounter( gPSOLDIER->timeCounters.UpdateCounter, iTimeLeft );
|
|
timerDone |= UpdateTimeCounter( gPSOLDIER->timeCounters.DamageCounter, iTimeLeft );
|
|
timerDone |= UpdateTimeCounter( gPSOLDIER->timeCounters.ReloadCounter, iTimeLeft );
|
|
timerDone |= UpdateTimeCounter( gPSOLDIER->timeCounters.FlashSelCounter, iTimeLeft );
|
|
timerDone |= UpdateTimeCounter( gPSOLDIER->timeCounters.BlinkSelCounter, iTimeLeft );
|
|
timerDone |= UpdateTimeCounter( gPSOLDIER->timeCounters.PortraitFlashCounter, iTimeLeft );
|
|
timerDone |= UpdateTimeCounter( gPSOLDIER->timeCounters.AICounter, iTimeLeft );
|
|
timerDone |= UpdateTimeCounter( gPSOLDIER->timeCounters.FadeCounter, iTimeLeft );
|
|
timerDone |= UpdateTimeCounter( gPSOLDIER->timeCounters.NextTileCounter, iTimeLeft );
|
|
timerDone |= UpdateTimeCounter( gPSOLDIER->timeCounters.PanelAnimateCounter, iTimeLeft );
|
|
#ifdef JA2UB
|
|
timerDone |= UpdateTimeCounter( gPSOLDIER->GetupFromJA25StartCounter, iTimeLeft );
|
|
#endif
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
if (timerDone)
|
|
SetEvent(ghNotifyThreadEvent);
|
|
fInFunction = FALSE;
|
|
}
|
|
}
|
|
|
|
static UINT32 MIN_TIMER(UINT32 timer, UINT32 other)
|
|
{
|
|
UINT32 value = TIME_MS_TO_US(timer);
|
|
return ( value && value < other ? value : other );
|
|
}
|
|
|
|
// checks if the clock based on QueryPerformanceCounter is using sane values
|
|
static inline bool TimerSanityCheck() {
|
|
// quick and drity check for messed up gliPerfCountNext - if the high part is ahead by more than 2, something very bad is going on.
|
|
return !( gliPerfCountNext.HighPart > gliPerfCount.HighPart + 1L );
|
|
}
|
|
|
|
// Returns the smallest time interval for a counter currently in use
|
|
UINT32 GetNextCounterDoneTime(void)
|
|
{
|
|
QueryPerformanceCounter(&gliPerfCount);
|
|
gliTimestampDiff = gliPerfCountNext.QuadPart - gliPerfCount.QuadPart;
|
|
gliWaitTime = (gliTimestampDiff * FREQUENCY_CONST) / gliPerfFreq.QuadPart;
|
|
|
|
// if the wait time is too long, or the "missed" step gets too long, re-evaluate the timer and try waiting 125ms
|
|
if (gliWaitTime > 15000 || gliWaitTime < -15000) {
|
|
gliWaitTime = 125; // in mili-seconds
|
|
|
|
QueryPerformanceFrequency(&gliPerfFreq);
|
|
gliPerfCountNext.QuadPart = gliPerfCount.QuadPart + ((125 * gliPerfFreq.QuadPart) / FREQUENCY_CONST);
|
|
}
|
|
|
|
return (UINT32)((gliWaitTime > 0) ? gliWaitTime : 0);
|
|
}
|
|
|
|
// Function to test if there are any outstanding timers. Used in fast forward routines
|
|
BOOLEAN IsTimerActive(void)
|
|
{
|
|
return GetNextCounterDoneTime() <= FASTFORWARDTIMESLICE ? TRUE : FALSE;
|
|
}
|
|
|
|
DWORD WINAPI JA2ClockThread( LPVOID lpParam )
|
|
{
|
|
__try
|
|
{
|
|
for(;;)
|
|
{
|
|
TimeProc(0, 0, 0, 0, 0);
|
|
|
|
DWORD dwResult = WaitForSingleObject(ghClockThreadShutdown, 0);
|
|
if (dwResult == WAIT_OBJECT_0 || dwResult == WAIT_ABANDONED)
|
|
break;
|
|
YieldProcessor();
|
|
|
|
// Sleep for a couple of milliseconds if not in fast forward mode
|
|
if (!IsFastForwardMode()) {
|
|
|
|
giSleepTime = TIME_US_TO_MS(GetNextCounterDoneTime());
|
|
|
|
// monitor the returned sleep times, if we try to sleep for more than 2 secs then somehing must be very wrong.
|
|
if (giSleepTime > 2000) {
|
|
giSleepTime = 250;
|
|
}
|
|
|
|
Sleep(giSleepTime);
|
|
}
|
|
}
|
|
}
|
|
__except( EXCEPTION_EXECUTE_HANDLER )
|
|
{
|
|
// Unhandled exception just exit
|
|
__debugbreak();
|
|
}
|
|
return 0L;
|
|
}
|
|
|
|
DWORD WINAPI JA2NotifyThread( LPVOID lpParam )
|
|
{
|
|
HANDLE waitHandles[] = {ghClockThreadShutdown, ghNotifyThreadEvent};
|
|
for(;;)
|
|
{
|
|
DWORD dwResult = WaitForSingleObject(ghClockThreadShutdown, 0);
|
|
if (dwResult == WAIT_OBJECT_0 || dwResult == WAIT_ABANDONED)
|
|
break;
|
|
|
|
DWORD waitTime = (!IsFastForwardMode()) ? max(TIME_US_TO_MS(MIN_NOTIFY_TIME), TIME_US_TO_MS( GetNextCounterDoneTime() ) ) : 0;
|
|
dwResult = WaitForMultipleObjectsEx(_countof(waitHandles), waitHandles, FALSE, waitTime, FALSE);
|
|
if (dwResult == WAIT_OBJECT_0)
|
|
break;
|
|
if (dwResult >= WAIT_ABANDONED_0 && dwResult <= (WAIT_ABANDONED_0 + _countof(waitHandles)))
|
|
break;
|
|
if ( dwResult == WAIT_FAILED || dwResult == WAIT_TIMEOUT || (dwResult-WAIT_OBJECT_0) == 1)
|
|
{
|
|
if (HasTimerNotifyCallbacks())
|
|
{
|
|
BroadcastTimerNotify(-1);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
// unexpected failure
|
|
DebugMsg(TOPIC_JA2, DBG_LEVEL_3, "JA2NotifyThread failed!");
|
|
__debugbreak();
|
|
}
|
|
}
|
|
SetEvent(ghNotifyThreadShutdownComplete);
|
|
return 0L;
|
|
}
|
|
|
|
|
|
|
|
BOOLEAN InitializeJA2Clock()
|
|
{
|
|
#ifdef CALLBACKTIMER
|
|
MMRESULT mmResult;
|
|
INT32 cnt;
|
|
|
|
// Init timer delays
|
|
for ( cnt = 0; cnt < NUMTIMERS; cnt++ )
|
|
{
|
|
giTimerCounters[ cnt ] = giTimerIntervals[ cnt ];
|
|
}
|
|
|
|
|
|
// First get timer resolutions
|
|
mmResult = timeGetDevCaps( >c, sizeof( gtc ) );
|
|
|
|
if ( mmResult != TIMERR_NOERROR )
|
|
{
|
|
DebugMsg( TOPIC_JA2, DBG_LEVEL_3, "Could not get timer properties");
|
|
}
|
|
|
|
if ( !QueryPerformanceFrequency(&gliPerfFreq) )
|
|
{
|
|
DebugMsg( TOPIC_JA2, DBG_LEVEL_3, "Could not get performance frequency");
|
|
}
|
|
if ( !QueryPerformanceCounter(&gliPerfCount) )
|
|
{
|
|
DebugMsg( TOPIC_JA2, DBG_LEVEL_3, "Could not get performance frequency");
|
|
}
|
|
|
|
timeBeginPeriod(gtc.wPeriodMin);
|
|
|
|
InitializeCriticalSection(&gcsNotifyLock);
|
|
|
|
if (IsHiSpeedClockMode())
|
|
{
|
|
ghClockThreadShutdown = CreateEvent(NULL, TRUE, FALSE, NULL);
|
|
ghNotifyThreadEvent = CreateEvent(NULL, FALSE, FALSE, NULL);
|
|
ghNotifyThreadShutdownComplete = CreateEvent(NULL, TRUE, FALSE, NULL);
|
|
ghClockThread = CreateThread(
|
|
NULL, // default security attributes
|
|
0, // use default stack size
|
|
JA2ClockThread, // thread function
|
|
NULL, // argument to thread function
|
|
0, // use default creation flags
|
|
&gdwClockThreadId);// returns the thread identifier
|
|
ghNotifyThread = CreateThread(
|
|
NULL, // default security attributes
|
|
0, // use default stack size
|
|
JA2NotifyThread, // thread function
|
|
NULL, // argument to thread function
|
|
0, // use default creation flags
|
|
&gdwNotifyThreadId);// returns the thread identifier
|
|
}
|
|
else
|
|
{
|
|
UpdateTimer();
|
|
}
|
|
#endif
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
|
|
void ShutdownJA2Clock(void)
|
|
{
|
|
if (IsHiSpeedClockMode())
|
|
{
|
|
SetEvent(ghClockThreadShutdown);
|
|
WaitForSingleObject(ghNotifyThreadShutdownComplete, 2000);
|
|
HANDLE waitHandles[] = {ghClockThread, ghNotifyThread};
|
|
WaitForMultipleObjects(_countof(waitHandles), waitHandles, TRUE, 1000);
|
|
CloseHandle(ghClockThreadShutdown);
|
|
CloseHandle(ghClockThread);
|
|
CloseHandle(ghNotifyThreadEvent);
|
|
CloseHandle(ghNotifyThread);
|
|
// During ungraceful shutdowns notify lock may be in use in notify thread
|
|
if (TryEnterCriticalSection(&gcsNotifyLock))
|
|
{
|
|
LeaveCriticalSection(&gcsNotifyLock);
|
|
DeleteCriticalSection(&gcsNotifyLock);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
// Make sure we kill the timer
|
|
#ifdef CALLBACKTIMER
|
|
timeKillEvent( gTimerID );
|
|
#endif
|
|
}
|
|
|
|
timeEndPeriod(gtc.wPeriodMin);
|
|
}
|
|
|
|
|
|
UINT32 InitializeJA2TimerCallback( UINT32 uiDelay, LPTIMECALLBACK TimerProc, UINT32 uiUser )
|
|
{
|
|
MMRESULT mmResult;
|
|
MMRESULT TimerID;
|
|
|
|
|
|
// First get timer resolutions
|
|
mmResult = timeGetDevCaps( >c, sizeof( gtc ) );
|
|
|
|
if ( mmResult != TIMERR_NOERROR )
|
|
{
|
|
__debugbreak();
|
|
DebugMsg( TOPIC_JA2, DBG_LEVEL_3, "Could not get timer properties");
|
|
}
|
|
|
|
// Set timer at lowest resolution. Could use middle of lowest/highest, we'll see how this performs first
|
|
TimerID = timeSetEvent( (UINT)uiDelay, (UINT)uiDelay, TimerProc, (DWORD)uiUser, TIME_PERIODIC );
|
|
|
|
if ( !TimerID )
|
|
{
|
|
__debugbreak();
|
|
DebugMsg( TOPIC_JA2, DBG_LEVEL_3, "Could not create timer callback");
|
|
}
|
|
|
|
return ( (UINT32)TimerID );
|
|
}
|
|
|
|
void RemoveJA2TimerCallback( UINT32 uiTimer )
|
|
{
|
|
timeKillEvent( uiTimer );
|
|
}
|
|
|
|
|
|
UINT32 InitializeJA2TimerID( UINT32 uiDelay, UINT32 uiCallbackID, UINT32 uiUser )
|
|
{
|
|
switch( uiCallbackID )
|
|
{
|
|
case ITEM_LOCATOR_CALLBACK:
|
|
|
|
return( InitializeJA2TimerCallback( uiDelay, FlashItem, uiUser ) );
|
|
break;
|
|
|
|
}
|
|
|
|
// invalid callback id
|
|
Assert( FALSE );
|
|
return( 0 );
|
|
}
|
|
|
|
|
|
//////////////////////////////////////////////////////////////////////////////////////////////
|
|
// TIMER CALLBACK S
|
|
//////////////////////////////////////////////////////////////////////////////////////////////
|
|
void CALLBACK FlashItem( UINT uiID, UINT uiMsg, DWORD uiUser, DWORD uiDw1, DWORD uiDw2 )
|
|
{
|
|
|
|
}
|
|
|
|
|
|
void PauseTime( BOOLEAN fPaused )
|
|
{
|
|
gfPauseClock = fPaused;
|
|
}
|
|
|
|
void SetCustomizableTimerCallbackAndDelay( INT32 iDelay, CUSTOMIZABLE_TIMER_CALLBACK pCallback, BOOLEAN fReplace )
|
|
{
|
|
if ( gpCustomizableTimerCallback )
|
|
{
|
|
if ( !fReplace )
|
|
{
|
|
// replace callback but call the current callback first
|
|
gpCustomizableTimerCallback();
|
|
}
|
|
}
|
|
|
|
RESETTIMECOUNTER( giTimerCustomizable, iDelay );
|
|
gpCustomizableTimerCallback = pCallback;
|
|
}
|
|
|
|
void CheckCustomizableTimer( void )
|
|
{
|
|
if ( gpCustomizableTimerCallback )
|
|
{
|
|
if ( TIMECOUNTERDONE( giTimerCustomizable, 0 ) )
|
|
{
|
|
// set the callback to a temp variable so we can reset the global variable
|
|
// before calling the callback, so that if the callback sets up another
|
|
// instance of the timer, we don't reset it afterwards
|
|
CUSTOMIZABLE_TIMER_CALLBACK pTempCallback;
|
|
|
|
pTempCallback = gpCustomizableTimerCallback;
|
|
gpCustomizableTimerCallback = NULL;
|
|
pTempCallback();
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
|
|
void ResetJA2ClockGlobalTimers( void )
|
|
{
|
|
UINT32 uiCurrentTime = GetJA2Clock();
|
|
|
|
guiCompressionStringBaseTime = uiCurrentTime;
|
|
giFlashHighlightedItemBaseTime = uiCurrentTime;
|
|
//giCompatibleItemBaseTime = uiCurrentTime;//Moa: removed (see HandleMouseInCompatableItemForMapSectorInventory)
|
|
giAnimateRouteBaseTime = uiCurrentTime;
|
|
giPotHeliPathBaseTime = uiCurrentTime;
|
|
giPotMilitiaPathBaseTime = uiCurrentTime;
|
|
giClickHeliIconBaseTime = uiCurrentTime;
|
|
giExitToTactBaseTime = uiCurrentTime;
|
|
guiSectorLocatorBaseTime = uiCurrentTime;
|
|
|
|
giCommonGlowBaseTime = uiCurrentTime;
|
|
giFlashAssignBaseTime = uiCurrentTime;
|
|
giFlashContractBaseTime = uiCurrentTime;
|
|
guiFlashCursorBaseTime = uiCurrentTime;
|
|
giPotCharPathBaseTime = uiCurrentTime;
|
|
}
|
|
|
|
void SetTileAnimCounter( INT32 iTime )
|
|
{
|
|
giTimerIntervals[ ANIMATETILES ] = iTime;
|
|
}
|
|
|
|
void SetFastForwardPeriod(DOUBLE value)
|
|
{
|
|
giFastForwardPeriod = (UINT32)(value);
|
|
if (giFastForwardPeriod <= 1)
|
|
giFastForwardPeriod = 1;
|
|
}
|
|
|
|
void SetFastForwardKey(INT32 key)
|
|
{
|
|
giFastForwardKey = key;
|
|
}
|
|
|
|
BOOLEAN IsFastForwardKeyPressed()
|
|
{
|
|
// WANNE: In a multiplayer game it is not allowed for the "pure" client to do fast forward
|
|
// Only the server is allowed to do, because the AI is generated on the server
|
|
if (is_networked)
|
|
{
|
|
if (!is_server) // It is not allowed when we are not the server
|
|
return false;
|
|
else if (gTacticalStatus.ubCurrentTeam != 1) // It is not allowed, when it is not the enemy turn!
|
|
return false;
|
|
}
|
|
|
|
return giFastForwardKey && IsKeyPressed(giFastForwardKey);
|
|
}
|
|
|
|
void SetFastForwardMode(BOOLEAN enable)
|
|
{
|
|
giFastForwardMode = enable;
|
|
UpdateTimer();
|
|
}
|
|
|
|
BOOLEAN IsFastForwardMode()
|
|
{
|
|
return giFastForwardMode || IsFastForwardKeyPressed();
|
|
}
|
|
|
|
LONGLONG GetJA2Microseconds()
|
|
{
|
|
return gliPerfCount.QuadPart * FREQUENCY_CONST / gliPerfFreq.QuadPart;
|
|
}
|
|
|
|
void AddTimerNotifyCallback( TIMER_NOTIFY_CALLBACK callback, PTR state )
|
|
{
|
|
EnterCriticalSection(&gcsNotifyLock);
|
|
BOOL addItem = TRUE;
|
|
for (TIMER_NOTIFY_ITEM_ITERATOR itr = glNotifyCallbacks.begin(); itr != glNotifyCallbacks.end(); ++itr) {
|
|
if ( callback == (*itr).callback && state == (*itr).state ){
|
|
addItem = FALSE;
|
|
break;
|
|
}
|
|
}
|
|
if (addItem)
|
|
{
|
|
TIMER_NOTIFY_ITEM item;
|
|
item.callback = callback;
|
|
item.state = state;
|
|
glNotifyCallbacks.push_back(item);
|
|
}
|
|
LeaveCriticalSection(&gcsNotifyLock);
|
|
}
|
|
|
|
void RemoveTimerNotifyCallback( TIMER_NOTIFY_CALLBACK callback, PTR state )
|
|
{
|
|
EnterCriticalSection(&gcsNotifyLock);
|
|
for ( TIMER_NOTIFY_ITEM_ITERATOR itr = glNotifyCallbacks.begin(); itr != glNotifyCallbacks.end(); )
|
|
{
|
|
if ( callback == (*itr).callback && state == (*itr).state )
|
|
itr = glNotifyCallbacks.erase(itr);
|
|
else
|
|
++itr;
|
|
}
|
|
LeaveCriticalSection(&gcsNotifyLock);
|
|
}
|
|
|
|
void ClearTimerNotifyCallbacks()
|
|
{
|
|
// If we cannot get the lock it is likely due to exception while handling notification and we are shutting down
|
|
if ( TryEnterCriticalSection(&gcsNotifyLock) )
|
|
{
|
|
glNotifyCallbacks.clear();
|
|
LeaveCriticalSection(&gcsNotifyLock);
|
|
}
|
|
}
|
|
|
|
static bool HasTimerNotifyCallbacks( )
|
|
{
|
|
return !glNotifyCallbacks.empty();
|
|
}
|
|
|
|
|
|
// Call timer notify routine
|
|
// Separate the callback notifies with normal try/catch
|
|
// as SEH __try/__except are incompatible with C++ exceptions
|
|
static void InnerTimerNotify(INT32 timer)
|
|
{
|
|
try
|
|
{
|
|
for (TIMER_NOTIFY_ITEM_ITERATOR itr = glNotifyCallbacks.begin(); itr != glNotifyCallbacks.end(); ++itr)
|
|
{
|
|
if ( NULL != (*itr).callback)
|
|
(*itr).callback( timer, (*itr).state );
|
|
}
|
|
}
|
|
catch (...) {
|
|
__debugbreak();
|
|
DebugMsg(TOPIC_JA2, DBG_LEVEL_3, "InnerTimerNotify Failed!");
|
|
}
|
|
}
|
|
static void BroadcastTimerNotify(INT32 timer)
|
|
{
|
|
EnterCriticalSection(&gcsNotifyLock);
|
|
__try
|
|
{
|
|
__try { InnerTimerNotify(timer); }
|
|
__except( EXCEPTION_EXECUTE_HANDLER )
|
|
{ /* Not sure. exit? */
|
|
__debugbreak();
|
|
DebugMsg(TOPIC_JA2, DBG_LEVEL_3, "BroadcastTimerNotify Failed!");
|
|
}
|
|
}
|
|
__finally
|
|
{
|
|
LeaveCriticalSection(&gcsNotifyLock);
|
|
}
|
|
}
|
|
|
|
BOOLEAN UpdateTimeCounter( INT32 &counter, INT32 &iTimeLeft)
|
|
{
|
|
if (counter == 0) {
|
|
return FALSE;
|
|
} else if ( ( counter - BASETIMESLICE ) < 0 ) {
|
|
counter = 0;
|
|
return TRUE;
|
|
} else {
|
|
counter -= BASETIMESLICE;
|
|
if ( counter < iTimeLeft )
|
|
iTimeLeft = counter;
|
|
return FALSE;
|
|
}
|
|
return FALSE;
|
|
}
|
|
|
|
BOOLEAN UpdateCounter( INT32 counterIdx, INT32 &iTimeLeft )
|
|
{
|
|
INT32& counter = giTimerCounters[ counterIdx ];
|
|
return UpdateTimeCounter(counter, iTimeLeft);
|
|
}
|
|
|
|
BOOLEAN UpdateCounter( INT32 counterIdx )
|
|
{
|
|
INT32 iDummy = 0;
|
|
return UpdateCounter(counterIdx, iDummy);
|
|
}
|
|
|
|
void ResetCounter(INT32 counterIdx)
|
|
{
|
|
giTimerCounters[ counterIdx ] = giTimerIntervals[ counterIdx ];
|
|
}
|
|
|
|
BOOLEAN CounterDone(INT32 counterIdx)
|
|
{
|
|
return ( giTimerCounters[ counterIdx ] == 0 ) ? TRUE : FALSE;
|
|
}
|
|
|
|
void ResetTimerCounter(INT32 &timer, INT32 value)
|
|
{
|
|
timer = value;
|
|
}
|
|
|
|
BOOLEAN TimeCounterDone(INT32 timer)
|
|
{
|
|
return ( timer == 0 ) ? TRUE : FALSE;
|
|
}
|
|
|
|
void ZeroTimeCounter(INT32& timer)
|
|
{
|
|
timer = 0;
|
|
}
|
|
|
|
BOOLEAN IsJA2TimerThread()
|
|
{
|
|
return (GetCurrentThreadId() == gdwClockThreadId);
|
|
}
|
|
|
|
#ifndef GetJA2Clock
|
|
UINT32 GetJA2Clock()
|
|
{
|
|
return guiBaseJA2Clock;
|
|
}
|
|
#endif
|
|
|
|
#ifndef GetJA2NoPauseClock
|
|
UINT32 GetJA2NoPauseClock()
|
|
{
|
|
return guiBaseJA2NoPauseClock;
|
|
}
|
|
#endif
|
|
|
|
void SetHiSpeedClockMode(BOOLEAN enable)
|
|
{
|
|
gfHispeedClockMode = enable;
|
|
}
|
|
|
|
BOOLEAN IsHiSpeedClockMode()
|
|
{
|
|
return gfHispeedClockMode;
|
|
}
|
|
|
|
void SetNotifyFrequencyKey(INT32 value)
|
|
{
|
|
MIN_NOTIFY_TIME = value;
|
|
}
|
|
|
|
void SetClockSpeedPercent(FLOAT value)
|
|
{
|
|
gfClockSpeedPercent = value;
|
|
UPDATETIMESLICE = (UINT32)((FLOAT)TIME_MS_TO_US(BASETIMESLICE) * 100.0f / value);
|
|
UpdateTimer();
|
|
}
|
|
|
|
void UpdateTimer()
|
|
{
|
|
// Set timer at lowest resolution. Could use middle of lowest/highest, we'll see how this performs first
|
|
if (!IsHiSpeedClockMode())
|
|
{
|
|
UINT uiTimeSlice = giFastForwardMode ? gtc.wPeriodMin : max(gtc.wPeriodMin, TIME_US_TO_MS(UPDATETIMESLICE));
|
|
if (uiTimeSlice != guiTimeSlice)
|
|
{
|
|
guiTimeSlice = uiTimeSlice;
|
|
if (gTimerID != 0) timeKillEvent(gTimerID);
|
|
gTimerID = timeSetEvent( uiTimeSlice, uiTimeSlice, TimeProc, (DWORD)0, TIME_PERIODIC );
|
|
if ( !gTimerID )
|
|
{
|
|
__debugbreak();
|
|
DebugMsg( TOPIC_JA2, DBG_LEVEL_3, "Could not create timer callback");
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|