@@ -69,17 +69,8 @@ class GameMemory
69
69
70
70
private SourceSplitSettings _settings ;
71
71
72
- private ValueWatcher < int > _custTimeCountWatcher ;
72
+ private ValueWatcher < long > _custTimeCountWatcher ;
73
73
private IntPtr _custTimeCountPtr ;
74
- private Detour _custTimeInjection ;
75
- private void ResetTimeInjection ( Process game )
76
- {
77
- if ( game != null )
78
- {
79
- _custTimeInjection ? . Restore ( game ) ;
80
- AppData . DeleteData ( "Detours" , "host_runframe" ) ;
81
- }
82
- }
83
74
84
75
public GameState _state ;
85
76
@@ -497,8 +488,6 @@ void MemoryReadThread(CancellationTokenSource cts)
497
488
{
498
489
try
499
490
{
500
- ResetTimeInjection ( game ) ;
501
-
502
491
GameOffsets offsets ;
503
492
while ( ! this . TryGetGameProcess ( out game , out offsets ) )
504
493
{
@@ -513,25 +502,21 @@ void MemoryReadThread(CancellationTokenSource cts)
513
502
if ( cts . IsCancellationRequested )
514
503
goto ret ;
515
504
}
516
- catch ( Win32Exception win32ex )
505
+ catch ( Exception ex ) when ( ex is InvalidOperationException || ex is Win32Exception )
517
506
{
518
- Trace . WriteLine ( win32ex . ToString ( ) ) ;
507
+ Trace . WriteLine ( ex . ToString ( ) ) ;
519
508
Thread . Sleep ( 1000 ) ;
520
509
}
521
- catch ( Exception ex ) // probably a Win32Exception on access denied to a process
510
+ catch ( Exception ex )
522
511
{
523
512
Trace . WriteLine ( ex . ToString ( ) ) ;
513
+ new ErrorDialog ( $ "Main:\n { ex } \n \n Inner:\n { ex . InnerException ? . ToString ( ) } ") ;
524
514
525
- var errorWindow = new ErrorDialog ( $ "Main:\n { ex } \n \n Inner:\n { ex . InnerException ? . ToString ( ) } ") ;
526
- errorWindow . ShowDialog ( ) ;
527
515
Thread . Sleep ( 1000 ) ;
528
-
529
- ResetTimeInjection ( game ) ;
530
516
}
531
517
}
532
518
533
519
ret :
534
- ResetTimeInjection ( game ) ;
535
520
Process . GetCurrentProcess ( ) . PriorityClass = ProcessPriorityClass . Normal ;
536
521
timeEndPeriod ( 1 ) ;
537
522
}
@@ -568,8 +553,8 @@ bool TryGetGameProcess(out Process p, out GameOffsets offsets)
568
553
return false ;
569
554
570
555
// process is up, check if engine and server are both loaded yet
571
- ProcessModuleWow64Safe engine = p . ModulesWow64Safe ( ) . FirstOrDefault ( x => x . ModuleName . ToLower ( ) == "engine.dll" ) ;
572
- ProcessModuleWow64Safe server = p . ModulesWow64Safe ( ) . FirstOrDefault ( x => x . ModuleName . ToLower ( ) == "server.dll" ) ;
556
+ ProcessModuleWow64Safe engine = p . ModulesWow64SafeNoCache ( ) . FirstOrDefault ( x => x . ModuleName . ToLower ( ) == "engine.dll" ) ;
557
+ ProcessModuleWow64Safe server = p . ModulesWow64SafeNoCache ( ) . FirstOrDefault ( x => x . ModuleName . ToLower ( ) == "server.dll" ) ;
573
558
574
559
if ( engine == null || server == null )
575
560
return false ;
@@ -599,34 +584,8 @@ bool scanForPtr(ref IntPtr ptr, SigScanTarget target, SignatureScanner scanner,
599
584
&& ! scanForPtr ( ref offsets . SignOnStatePtr , _signOnStateTarget2 , scanner , "CBaseClientState::m_nSignonState" , "2" ) )
600
585
return false ;
601
586
602
- #region HOST_RUNFRAME TICK COUNT DETOUR
587
+ #region HOST_RUNFRAME TICK COUNT
603
588
604
- const int timeCountPtrOff = 0x8 ;
605
-
606
- string serialzedFilePath = AppData . GetDataPath ( "Detours" , "host_runframe" ) ;
607
- _custTimeCountWatcher = new ValueWatcher < int > ( 0 ) ;
608
- if ( File . Exists ( serialzedFilePath ) )
609
- {
610
- Stream dStream = File . Open ( serialzedFilePath , FileMode . Open ) ;
611
- try
612
- {
613
- BinaryFormatter dBinary = new BinaryFormatter ( ) ;
614
- dBinary . Binder = new SerialBinder ( ) ;
615
- Detour dDetour = ( Detour ) dBinary . Deserialize ( dStream ) ;
616
- bool good = dDetour . VerifyIntegrity ( p ) ;
617
- if ( good )
618
- {
619
- _custTimeInjection = dDetour ;
620
- _custTimeCountPtr = _custTimeInjection . Destination - timeCountPtrOff ;
621
- goto skipTimeHooks ;
622
- }
623
- }
624
- finally
625
- {
626
- dStream . Close ( ) ;
627
- }
628
- }
629
-
630
589
// find the beginning of _host_runframe
631
590
// find string pointer and reference
632
591
SigScanTarget _hrfStringTarg = new SigScanTarget ( "_Host_RunFrame (top): _heapchk() != _HEAPOK\n " . ConvertToHex ( ) + "00" ) ;
@@ -640,82 +599,33 @@ bool scanForPtr(ref IntPtr ptr, SigScanTarget target, SignatureScanner scanner,
640
599
"host_runframe target jump" ) )
641
600
return false ;
642
601
643
- // we can't detour the jl directly due to weird performance issues so back track until we
644
- // found our target cmp instruction
645
-
646
- // find out where the jl goes to
602
+ // find out where the jl goes to, which should be the top of the update loop
647
603
IntPtr loopTo = _hrfStringPtr + p . ReadValue < int > ( _hrfStringPtr + 0x2 ) + 0x6 ;
648
- // find cmp instruction
649
- int i = 0 ;
650
- // should never be more than 15 bytes away...
651
- for ( i = 0 ; i < 15 ; i ++ )
652
- {
653
- byte curByte = p . ReadValue < byte > ( _hrfStringPtr - i ) ;
654
- // we are cmp'ing 2 registeres normally, so use 0x39 and 0x3B
655
- if ( new byte [ ] { 0x39 , 0x3B } . Contains ( curByte ) )
656
- {
657
- byte prev = p . ReadValue < byte > ( _hrfStringPtr - i - 1 ) ;
658
- // ignore this byte if this is part of a mov
659
- if ( ( prev <= 0x8E && prev >= 0x89 ) )
660
- continue ;
661
-
662
- _hrfStringPtr -= i ;
663
- Debug . WriteLine ( $ "host_runframe target cmp at 0x{ _hrfStringPtr . ToString ( "X" ) } ") ;
664
- goto success ;
665
- }
666
- }
667
- return false ;
668
604
669
- success :
670
- // allocate memory for our detour
671
- IntPtr workSpace = p . AllocateMemory ( 0x100 ) ;
672
- // bytes of the ptr to our custom tick count var
673
- byte [ ] timePtrBytes = BitConverter . GetBytes ( workSpace . ToInt64 ( ) ) ;
674
- _custTimeInjection = new Detour (
675
- p ,
676
- _hrfStringPtr ,
677
- workSpace + timeCountPtrOff ,
678
- 6 + i ,
679
- new byte [ ]
680
- {
681
- // inc [tick count val]
682
- 0xFF , 0x05 , timePtrBytes [ 0 ] , timePtrBytes [ 1 ] , timePtrBytes [ 2 ] , timePtrBytes [ 3 ] ,
683
- } ,
684
- true ) ;
685
- // because the detour just simply copies bytes over, we'll have to rewrite the jl instruction
686
- // to correct the offset back to the start of the loop
687
- // bytes of the ptr from the detoured jl to the start of the loop
688
- byte [ ] loopToBytes = BitConverter . GetBytes ( ( int ) loopTo - ( int ) ( workSpace + 0x8 + 0x6 + i + 0x6 ) ) ;
689
- p . WriteBytes ( workSpace + 0x8 + 6 + i , new byte [ ]
690
- {
691
- 0x0F , 0x8C , loopToBytes [ 0 ] , loopToBytes [ 1 ] , loopToBytes [ 2 ] , loopToBytes [ 3 ] ,
692
- } ) ;
693
- // final memory layout should look like this:
694
- // workspace + 0x0 [tick count val]
695
- // workspace + 0x8 inc [tick count val]
696
- // workspace + 0xE (cmp and along with any other bytes in between that and the jl)
697
- // workspace + 0xE + i jl [loop start]
698
- // workspace + 0xE + i + 6 jmp [loop end]
699
- _custTimeCountPtr = workSpace ;
700
-
701
- // serialize data and store it off in case livesplit crashes and we can't restore the original bytes
605
+ while ( ( long ) loopTo <= ( long ) _hrfStringPtr )
702
606
{
703
- Stream s = File . Create ( serialzedFilePath ) ;
704
- try
705
- {
706
- BinaryFormatter bf = new BinaryFormatter ( ) ;
707
- bf . Binder = new SerialBinder ( ) ;
708
- bf . Serialize ( s , _custTimeInjection ) ;
709
- }
710
- finally
607
+ loopTo = loopTo + 1 ;
608
+ uint candidateHostFrameCount = p . ReadValue < uint > ( loopTo ) ;
609
+
610
+ if ( scanner . IsWithin ( candidateHostFrameCount ) )
711
611
{
712
- s . Close ( ) ;
612
+ for ( int i = 1 ; i <= 2 ; i ++ )
613
+ {
614
+ uint candidateNextPtr = p . ReadValue < uint > ( loopTo + 4 + i ) ;
615
+ if ( scanner . IsWithin ( candidateNextPtr ) && candidateNextPtr - candidateHostFrameCount <= 0x8 )
616
+ {
617
+ _custTimeCountPtr = ( IntPtr ) candidateHostFrameCount ;
618
+ Debug . WriteLine ( $ "host_runframe host_tickcount ptr is 0x{ _custTimeCountPtr . ToString ( "X" ) } ") ;
619
+ goto skipTimeHooks ;
620
+ }
621
+ }
713
622
}
714
623
715
624
}
625
+ return false ;
716
626
717
627
skipTimeHooks :
718
-
628
+ _custTimeCountWatcher = new ValueWatcher < long > ( 0 ) ;
719
629
#endregion
720
630
721
631
// get the game dir now to evaluate game-specific stuff
@@ -742,7 +652,7 @@ bool scanForPtr(ref IntPtr ptr, SigScanTarget target, SignatureScanner scanner,
742
652
743
653
#region CLIENT
744
654
// optional client fade list
745
- ProcessModuleWow64Safe client = p . ModulesWow64Safe ( ) . FirstOrDefault ( x => x . ModuleName . ToLower ( ) == "client.dll" ) ;
655
+ ProcessModuleWow64Safe client = p . ModulesWow64SafeNoCache ( ) . FirstOrDefault ( x => x . ModuleName . ToLower ( ) == "client.dll" ) ;
746
656
if ( client != null )
747
657
{
748
658
var clientScanner = new SignatureScanner ( p , client . BaseAddress , client . ModuleMemorySize ) ;
@@ -1109,7 +1019,7 @@ void CheckGameState(GameState state)
1109
1019
{
1110
1020
// note: seems to be slow sometimes. ~3ms
1111
1021
1112
- if ( state . TickCount > 0 )
1022
+ if ( _settings . ServerInitialTicks . Value || state . TickCount > 0 )
1113
1023
{
1114
1024
if ( state . ServerState . Current == ServerState . Paused )
1115
1025
this . SendMiscTimeEvent ( _custTimeCountWatcher . Current - _custTimeCountWatcher . Old , MiscTimeType . PauseTime ) ;
@@ -1194,7 +1104,7 @@ void CheckGameState(GameState state)
1194
1104
this . SendNewGameStartedEvent ( levelName ) ;
1195
1105
1196
1106
if ( ! string . IsNullOrWhiteSpace ( levelName ) &&
1197
- ( levelName == _settings . StartMap
1107
+ ( levelName == _settings . StartMap . Value
1198
1108
|| state . GameSupport . StartOnFirstLoadMaps . Contains ( levelName )
1199
1109
|| state . GameSupport . AdditionalGameSupport . Any ( x => x . StartOnFirstLoadMaps . Contains ( levelName ) ) ) )
1200
1110
{
@@ -1262,13 +1172,13 @@ public void SendMapChangedEvent(string mapName, string prevMapName, bool isGener
1262
1172
1263
1173
public class SessionTicksUpdateEventArgs : EventArgs
1264
1174
{
1265
- public int TickDifference { get ; private set ; }
1266
- public SessionTicksUpdateEventArgs ( int tickDifference )
1175
+ public long TickDifference { get ; private set ; }
1176
+ public SessionTicksUpdateEventArgs ( long tickDifference )
1267
1177
{
1268
1178
this . TickDifference = tickDifference ;
1269
1179
}
1270
1180
}
1271
- public void SendSessionTimeUpdateEvent ( int tickDifference )
1181
+ public void SendSessionTimeUpdateEvent ( long tickDifference )
1272
1182
{
1273
1183
// note: sometimes this takes a few ms
1274
1184
_uiThread . Post ( d => {
@@ -1336,9 +1246,9 @@ public void SendNewGameStartedEvent(string map)
1336
1246
1337
1247
public class MiscTimeEventArgs : EventArgs
1338
1248
{
1339
- public int TickDifference { get ; private set ; }
1249
+ public long TickDifference { get ; private set ; }
1340
1250
public MiscTimeType Type { get ; private set ; }
1341
- public MiscTimeEventArgs ( int tickDiff , MiscTimeType type )
1251
+ public MiscTimeEventArgs ( long tickDiff , MiscTimeType type )
1342
1252
{
1343
1253
this . TickDifference = tickDiff ;
1344
1254
this . Type = type ;
@@ -1351,7 +1261,7 @@ public enum MiscTimeType
1351
1261
PauseTime ,
1352
1262
ClientDisconnectTime
1353
1263
}
1354
- public void SendMiscTimeEvent ( int tickDiff , MiscTimeType type )
1264
+ public void SendMiscTimeEvent ( long tickDiff , MiscTimeType type )
1355
1265
{
1356
1266
_uiThread . Post ( d => {
1357
1267
this . OnMiscTime ? . Invoke ( this , new MiscTimeEventArgs ( tickDiff , type ) ) ;
0 commit comments