Jump to content
Sign in to follow this  
jvanegmond

Source Dedicator Server Monitor for Windows

Recommended Posts

Source Dedicator Server Monitor for Windows

Introduction

After trying a few SRCDS (Source Dedicated Server) server restarters, I wasn't satisfied with all the features. Most of them do not even properly detect crashes because they use conventional methods of detecting a crash. I've decided to write my own that does the job better for everyone.

This uses a new system of simulating a player trying to connect to the server. If the server for some reason is not answering to the query, it is restarted. Whether this be a crash or a freeze.

Features

  • Easy to set up.

    A standard windows form with a few controls provides easy access to all the available settings.

  • Easy to manage.

    Your preferences are automatically remembered. So next time you start the server, you won't have to set it up again.

  • Flexibility.

    Your server will keep running, no matter what error you get that causes it to crash.

  • Reliability.

    The monitor is built with long-term reliability in mind. Once you have this program, you will never have to restart your server manually again.

Download

Currently only source is available. Ask me for a compiled package with or without installer should you need one.

Source Dedicated Server Monitor for Windows:

File: ServerMonitor.au3

Opt("GUIOnEventMode", 1)
Opt("WinTitleMatchMode", 2)

#include <GUIConstantsEx.au3>
#include <WindowsConstants.au3>

Dim $DEBUG = False

Dim $mRunning = False
Dim $mPath = "C:\scrds\server\orangebox\srcds.exe"
Dim $mParameter = "-console -game GAMENAME_HERE -maxplayers 12 -autoupdate"
Dim $mTimeStart
Dim $mProcessID = 0
Dim $mConsoleText = ""
Dim $mIPAdress = @IPAddress1
Dim $mPort = 27015
Dim $mServerRunning = False
Dim $mDBPath = @UserProfileDir & "\" & "serverdata.ini"

$mIPAdress = IniRead($mDBPath, "Data", "IP", $mIPAdress)
$mPort = IniRead($mDBPath, "Data", "Port", $mPort)
$mPath = IniRead($mDBPath, "Data", "Path", $mPath)
$mParameter = IniRead($mDBPath, "Data", "Parameter", $mParameter)

#Region ### START Koda GUI section ### Form=
$Form1 = GUICreate("Source Dedicated Server Monitor by Manadar.com", 378, 444)
GUISetOnEvent($GUI_EVENT_CLOSE,"_Event_Close")
GUISetFont(8,400,0,"Tahoma")
$Group1 = GUICtrlCreateGroup("Status", 8, 8, 361, 217)
$Label1 = GUICtrlCreateEdit("", 16, 24, 345, 193)
$Menu1 = GUICtrlCreateContextMenu($Label1)
$MenuItem = GUICtrlCreateMenuItem("Copy all", $Menu1)
GUICtrlSetOnEvent($MenuItem, "_Console_Copy")
GUICtrlCreateGroup("", -99, -99, 1, 1)
$Group4 = GUICtrlCreateGroup("Server address", 8, 232, 361, 57)
$Input4 = GUICtrlCreateInput($mIPAdress, 16, 248, 225, 21)
$Label2 = GUICtrlCreateLabel(":", 247, 250, 7, 17)
$Input5 = GUICtrlCreateInput($mPort, 256, 248, 105, 21)
GUICtrlCreateGroup("", -99, -99, 1, 1)
$Group2 = GUICtrlCreateGroup("Path to server", 8, 288, 361, 49)
$Input2 = GUICtrlCreateInput($mPath, 16, 304, 249, 21)
$Button1 = GUICtrlCreateButton("Browse", 272, 304, 91, 25)
GUICtrlSetOnEvent($Button1, "_Event_Browse")
GUICtrlCreateGroup("", -99, -99, 1, 1)
$Group3 = GUICtrlCreateGroup("Parameters for restart", 8, 344, 361, 49)
$Input3 = GUICtrlCreateInput($mParameter, 16, 360, 345, 21)
GUICtrlCreateGroup("", -99, -99, 1, 1)
$Button2 = GUICtrlCreateButton("Monitor my server", 8, 400, 179, 33)
GUICtrlSetOnEvent($Button2, "_Event_Monitor")
$Button3 = GUICtrlCreateButton("About", 200, 400, 171, 33)
GUICtrlSetOnEvent($Button3, "_Event_About")

GUICtrlSetState($Button2,$GUI_FOCUS)

GUISetState(@SW_SHOW)
#EndRegion ### END Koda GUI section ###

UDPStartup()

_Console_AddLine("Source Dedicated Server Monitor. © Manadar.com 2008")

_Check_Handler()
$mTimeStart = TimerInit()
While 1
    If TimerDiff($mTimeStart) > 3000 Then
        $mTimeStart = TimerInit()
        _Check_Handler()
    EndIf
    Sleep(100)
WEnd

Func _DEBUG($pText)
    If Not $DEBUG Then Return
    
    $pText = StringReplace(StringStripCR($pText),@LF,"")
    $lLog = _Console_PrintTime() & $pText & @CRLF
    ConsoleWrite($lLog)
    
    FileWrite("debug.log", $lLog)
EndFunc

Func _Check_Handler()
    _DEBUG("Function: _Check_Handler()")
    _DEBUG("$mRunning = " & $mRunning)
    If Not $mRunning Then Return
    
    _DEBUG("$mProcessID = " & $mProcessID)
    If Not $mProcessID Then
        
        _Console_AddLine(_Console_PrintTime() & "Trying to attach to srcds.exe")
        $mProcessID = ProcessExists("srcds.exe")
        _DEBUG("$mProcessID = " & $mProcessID)
        If $mProcessID <> 0 Then
            _Console_AddLine(_Console_PrintTime() & "srcds.exe found. Attached to process.")
        Else
            _Server_Start()
        EndIf
        
    EndIf
    
    If $mProcessID Then
        _Check_Server()
    EndIf
EndFunc

Func _Check_Server()
    _DEBUG("Function: _Check_Server()")
    _DEBUG("$mProcessID = " & $mProcessID)
    If Not $mProcessID Then Return
    
    If Not ProcessExists($mProcessID) Then
        _Event_RestartNow()
        Return
    EndIf
    
    $lUDPOpen = UDPOpen($mIPAdress, $mPort)
    $lUDPSend = UDPSend($lUDPOpen, _Query_String() )
    
    $lStartWait = TimerInit()
    While ProcessExists($mProcessID)
        $lReturnData = UDPRecv($lUDPOpen,200)
        If $lReturnData Then
            If $mServerRunning = False Then
                $mServerRunning = True
                _Console_AddLine(_Console_PrintTime() & "Server is up and running.")
                $mRunning = True
                $mProcessID = 0
                _Check_Handler()
            EndIf
            ExitLoop
        EndIf
        If TimerDiff($lStartWait) > 30000 Then
            _Event_RestartNow()
            UDPCloseSocket($lUDPOpen)
            UDPCloseSocket($lUDPOpen)
            UDPCloseSocket($lUDPOpen)
            Return
        EndIf
        Sleep(10)
    WEnd
    
    UDPCloseSocket($lUDPOpen)
    UDPCloseSocket($lUDPOpen)
    UDPCloseSocket($lUDPOpen)
EndFunc

Func _Console_PrintTime()
    Return "[" & @HOUR & ":" & @MIN & ":" & @SEC & "." & _Time_Milliseconds() & "] "
EndFunc

Func _Console_AddLine($pText)
    _Console_Add($pText & @CRLF)
EndFunc

Func _Console_Copy()
    ClipPut($mConsoleText)
EndFunc

Func _Console_Add($pText)
    _DEBUG("Console: " & $pText)
    
    $mConsoleText &= $pText
    
    FileWrite("console.log", $pText)
    
    GUICtrlSetData($Label1, $mConsoleText)
    ControlSend($Form1, "", $Label1, "^{END}")
EndFunc

Func _Console_Clear()
    $mConsoleText = ""
    GUICtrlSetData($Label1,"")
EndFunc

Func _Event_Browse()
    $lFileOpenDialog = FileOpenDialog("Source Dedicated Server Monitor", GUICtrlRead($Input2), "srcds.exe (srcds.exe)|Executables (*.exe)|All (*.*)",3,"srcds.exe",$Form1)
    If @error Then Return
    
    If Not FileExists($lFileOpenDialog) Then Return
    
    GUICtrlSetData($Input2, $lFileOpenDialog)
EndFunc

Func _Event_RestartNow()
    _DEBUG("Function: _Event_RestartNow()")
    
    $mRunning = False
    $mProcessID = 0
    _Console_AddLine(_Console_PrintTime() & "Restart server sequence started.")
    
    _Server_Force_Close()
    _Console_AddLine(_Console_PrintTime() & "Server process killed.")
    
    _Server_Start()
EndFunc

Func _Server_Force_Close()
    For $i = 0 to 10
        ProcessClose($mProcessID)
        ProcessClose("srcds.exe")
        If WinExists("srcds") Then
            $hWnd = WinGetHandle("srcds")
            WinClose($hWnd)
        EndIf
        Sleep(10)
    Next
EndFunc

Func _Server_Start()
    _DEBUG("Function: _Server_Start()")
    
    Dim $lConsecutiveFail = 0
    
    _Console_AddLine(_Console_PrintTime() & "Starting server: """ & $mPath & " " & $mParameter & '"')
    Run($mPath & " " & $mParameter)
    
    $lStartWait = TimerInit()
    While ProcessExists("srcds.exe")
        $lUDPOpen = UDPOpen($mIPAdress, $mPort)
        $lUDPSend = UDPSend($lUDPOpen, _Query_String() )
        
        For $x = 0 to 10
            $lReturnData = UDPRecv($lUDPOpen,200)
            If $lReturnData Then
                
                $mRunning = True
                $mServerRunning = True
                _Console_AddLine(_Console_PrintTime() & "Server up and running.")
                
                Return
            EndIf
            Sleep(50)
        Next
        
        UDPCloseSocket($lUDPOpen)
        UDPCloseSocket($lUDPOpen)
        UDPCloseSocket($lUDPOpen)
        
        If TimerDiff($lStartWait) > 5000 Then
            $lConsecutiveFail += 1
            If $lConsecutiveFail > 40 Then ; 2 minutes start up time max
                _Console_AddLine(_Console_PrintTime() & "Unable to start new server (Incorrect parameters?).")
                _Event_Monitor_Stop()
                ExitLoop
            EndIf
        EndIf
        Sleep(100)
    WEnd
    
    _Console_AddLine(_Console_PrintTime() & "Unable to start new server (Incorrect parameters?).")
    _Event_Monitor_Stop()
EndFunc

Func _Event_Close()
    $mPath = GUICtrlRead($Input2)
    $mParameter = GUICtrlRead($Input3)
    $mIPAdress = GUICtrlRead($Input4)
    $mPort = GUICtrlRead($Input5)
    
    IniWrite($mDBPath, "Data", "IP", $mIPAdress)
    IniWrite($mDBPath, "Data", "Port", $mPort)
    IniWrite($mDBPath, "Data", "Path", $mPath)
    IniWrite($mDBPath, "Data", "Parameter", $mParameter)
    
    Exit
EndFunc

Func _Event_About()
    $lMessageBoxResult = MsgBox(0x44, "Source Dedicated Server Monitor by Manadar.com", "Source Dedicated Server Monitor is copyrighted by Jos van Egmond" & @CRLF & _ 
    "Would you like to visit www.manadar.com?")
    If $lMessageBoxResult = 6 Then
        ShellExecute("http://www.manadar.com")
    EndIf
EndFunc

Func _Event_Monitor()
    GUICtrlSetState($Button1, $GUI_DISABLE)
    GUICtrlSetState($Input2, $GUI_DISABLE)
    GUICtrlSetState($Input3, $GUI_DISABLE)
    GUICtrlSetState($Button2, $GUI_DISABLE)
    GUICtrlSetState($Input4, $GUI_DISABLE)
    GUICtrlSetState($Input5, $GUI_DISABLE)
    
    $mPath = GUICtrlRead($Input2)
    $mParameter = GUICtrlRead($Input3)
    $mIPAdress = GUICtrlRead($Input4)
    $mPort = GUICtrlRead($Input5)
    
    $mRunning = True
    _Check_Handler()
EndFunc

Func _Event_Monitor_Stop()
    GUICtrlSetState($Button1, $GUI_ENABLE)
    GUICtrlSetState($Input2, $GUI_ENABLE)
    GUICtrlSetState($Input3, $GUI_ENABLE)
    GUICtrlSetState($Button2, $GUI_ENABLE)
    GUICtrlSetState($Input4, $GUI_ENABLE)
    GUICtrlSetState($Input5, $GUI_ENABLE)
    
    $mRunning = False
EndFunc

Func _Time_Milliseconds()
    Local $stSystemTime = DllStructCreate('ushort;ushort;ushort;ushort;ushort;ushort;ushort;ushort')
    
    DllCall('kernel32.dll', 'none', 'GetSystemTime', 'ptr', DllStructGetPtr($stSystemTime))
    
    $sMilliSeconds = StringFormat('%03d', DllStructGetData($stSystemTime, 8))
    $stSystemTime = 0
    
    Return $sMilliSeconds
EndFunc

Func _Query_String()
    $lHexQuery = "FF FF FF FF 54 53 6F 75 72 63 65 20 45 6E 67 69 6E 65 20 51 75 65 72 79 00 "
    
    $lSplit = StringSplit($lHexQuery," ")
    $lHexQuery = ""
    
    For $x = 1 to $lSplit[0]
        If $lSplit[$x] Then
            $lHexQuery &= Chr((Dec($lSplit[$x])))
        EndIf
    Next
    
    Return $lHexQuery
EndFunc

Bonus Crash Utility.

File: CrashTest.au3

#include <GUIConstants.au3>

#Region ### START Koda GUI section ### Form=
$Form1 = GUICreate("serverCrash Test Utility", 194, 82, 193, 115)
$Button1 = GUICtrlCreateButton("Crash", 8, 8, 179, 25, 0)
$Button2 = GUICtrlCreateButton("Uncrash", 8, 40, 179, 25, 0)
GUISetState(@SW_SHOW)
#EndRegion ### END Koda GUI section ###

While 1
    $nMsg = GUIGetMsg()
    Switch $nMsg
        Case $GUI_EVENT_CLOSE
            Exit
        Case $Button1
            _ProcessSuspend("srcds.exe")
        Case $Button2
            _ProcessResume("srcds.exe")
    EndSwitch
WEnd


Func _ProcessSuspend($process)
    $processid = ProcessExists($process)
    If $processid Then
        $ai_Handle = DllCall("kernel32.dll", 'int', 'OpenProcess', 'int', 0x1f0fff, 'int', False, 'int', $processid)
        $i_sucess = DllCall("ntdll.dll", "int", "NtSuspendProcess", "int", $ai_Handle[0])
        DllCall('kernel32.dll', 'ptr', 'CloseHandle', 'ptr', $ai_Handle)
        If IsArray($i_sucess) Then
            Return 1
        Else
            SetError(1)
            Return 0
        EndIf
    Else
        SetError(2)
        Return 0
    EndIf
EndFunc   ;==>_ProcessSuspend

Func _ProcessResume($process)
    $processid = ProcessExists($process)
    If $processid Then
        $ai_Handle = DllCall("kernel32.dll", 'int', 'OpenProcess', 'int', 0x1f0fff, 'int', False, 'int', $processid)
        $i_sucess = DllCall("ntdll.dll", "int", "NtResumeProcess", "int", $ai_Handle[0])
        DllCall('kernel32.dll', 'ptr', 'CloseHandle', 'ptr', $ai_Handle)
        If IsArray($i_sucess) Then
            Return 1
        Else
            SetError(1)
            Return 0
        EndIf
    Else
        SetError(2)
        Return 0
    EndIf
EndFunc   ;==>_ProcessResume
Edited by Manadar

Share this post


Link to post
Share on other sites

Updated to version 1.1

Changes:

- Perfect crash detection.

- Keeps a log of everything that happens.

- Added the ability to keep a debugging log.

Bugs fixed:

- Can no longer spaz out and start the server a lot.

I'm working on a 2.0 version with an improved GUI, web interface and generally more features. No, I am not.

Edited by Manadar

Share this post


Link to post
Share on other sites

Create an account or sign in to comment

You need to be a member in order to leave a comment

Create an account

Sign up for a new account in our community. It's easy!

Register a new account

Sign in

Already have an account? Sign in here.

Sign In Now
Sign in to follow this  

×
×
  • Create New...