RuneScape Loader for VB2010

Discussion in 'Programming General' started by Flaming Idiots, Apr 21, 2011.

RuneScape Loader for VB2010
  1. Unread #1 - Apr 21, 2011 at 8:06 PM
  2. Flaming Idiots
    Joined:
    Dec 22, 2005
    Posts:
    235
    Referrals:
    1
    Sythe Gold:
    0
    Two Factor Authentication User

    Flaming Idiots Active Member
    Visual Basic Programmers

    RuneScape Loader for VB2010

    Due to some issues with an older RuneScape loader, I decided to rebuild it.

    It supports hiding or showing the ads, taking snapshots, multiple game languages and loading RuneScape classic. Also if the X button at the login screen is pressed, it will automatically unload the game.

    To use it, add the code below into a new code file, build your projects, add it to your form/usercontrol, the call the LoadGame function.

    Code:
    Imports System.Runtime.InteropServices
    
    ''' <summary>
    ''' A control for loading RuneScape.
    ''' </summary> 
    <System.Windows.Forms.Docking(DockingBehavior.AutoDock)>
    Public Class RuneScapeLoader
        Inherits Control
    
        Private Const classicUrl As String = "http://www.runescape.com/classicapplet/classicgame.ws?f={0}"
        Private Const gameUrl As String = "http://www.runescape.com{0}/game.ws"
    
        Public Event GameLoaded As EventHandler
        Public Event GameUnloaded As EventHandler
        Public Event GameLoading As EventHandler
    
        Private WithEvents Loader As RuneScapeLoaderBrowser
        Private _gameHandle As IntPtr
        Private _hideAds As Boolean = True
    
        Private Sub Loader_Disposed(sender As Object, e As System.EventArgs) Handles Loader.Disposed
            Me.Loader = Nothing
            RaiseEvent GameUnloaded(Me, EventArgs.Empty)
        End Sub
    
        Private Sub SetLoaded(gameHandle As IntPtr?)
            Dim gh = If(gameHandle, IntPtr.Zero)
            Me._gameHandle = gh
            If gameHandle.HasValue Then
                RaiseEvent GameLoaded(Me, EventArgs.Empty)
            End If
        End Sub 
    
        ''' <summary>
        ''' Gets if the RuneScape is currently loaded.
        ''' </summary> 
        <System.ComponentModel.Browsable(False)>
        Public ReadOnly Property IsGameLoaded() As Boolean
            Get
                Return _gameHandle <> IntPtr.Zero
            End Get
        End Property
    
        ''' <summary>
        ''' Gets if the RuneScape is currently loading.
        ''' </summary> 
        <System.ComponentModel.Browsable(False)>
        Public ReadOnly Property IsGameLoading() As Boolean
            Get
                Return Loader IsNot Nothing AndAlso _gameHandle = IntPtr.Zero
            End Get
        End Property
    
        ''' <summary>
        ''' Gets the current handle for RuneScape if it is loaded. Otherwise returns nothing. 
        ''' </summary> 
        <System.ComponentModel.Browsable(False)>
        Public ReadOnly Property GameHandle As IntPtr?
            Get
                Return If(_gameHandle = IntPtr.Zero, Nothing, _gameHandle)
            End Get
        End Property
    
        ''' <summary>
        ''' Gets or sets if the the game loader should hide everything but the game. Set to null, it will only hide the bar at the top for members.
        ''' </summary> 
        Public Property HideAds() As Boolean
            Get
                Return _hideAds
            End Get
            Set(ByVal value As Boolean)
                _hideAds = value
                If IsGameLoaded Then Loader.SetGameBounds(Loader.Url)
            End Set
        End Property
    
        Private Sub InitLoader()
            If Loader IsNot Nothing Then Throw New InvalidOperationException("RuneScape must be unloaded before it can be reloaded.")
            Me.Loader = New RuneScapeLoaderBrowser(Me)
            Me.Loader.Size = New Size(1000, 1000)
            Me.Controls.Add(Loader)
        End Sub
    
        ''' <summary>
        ''' Loads RuneScape using the specified language. Defaults to English.
        ''' </summary>
        Public Sub LoadGame(Optional language As GameLanguage = GameLanguage.English)
            InitLoader()
            Loader.Navigate(String.Format(gameUrl, If(language = GameLanguage.English, "", "/l=" + CInt(language))))
            RaiseEvent GameLoading(Me, EventArgs.Empty)
        End Sub
    
        ''' <summary>
        ''' Loads RuneScape Classic using the specified world. Defaults to the best world.
        ''' </summary> 
        Public Sub LoadClassic(Optional world As Integer? = Nothing)
            InitLoader()
            Loader.Navigate(String.Format(classicUrl, If(world, "best")))
            RaiseEvent GameLoading(Me, EventArgs.Empty)
        End Sub
    
        ''' <summary>
        ''' Unloads RuneScape.
        ''' </summary> 
        Public Sub UnloadGame()
            If Loader Is Nothing Then Throw New InvalidOperationException("RuneScape must be loaded before it can be unloaded.")
            Loader.Navigate("about:dispose")
        End Sub
    
        ''' <summary>
        ''' Takes a snapshot of RuneScape and returns an image. If the game is not loaded or there was another problem it will return nothing.
        ''' </summary> 
        Public Function TakeSnapshot() As Image
            If Not IsGameLoaded Then Return Nothing
            Dim lr = New RuneScapeLoaderBrowser.LRTBRect
            RuneScapeLoaderBrowser.GetWindowRect(_gameHandle, lr)
            Dim r = lr.ToRectangle()
            If r.Width = 0 OrElse r.Height = 0 Then Return Nothing
            Dim bmp = New Bitmap(r.Width, r.Height, Imaging.PixelFormat.Format24bppRgb)
            Using g = Graphics.FromImage(bmp)
                g.CopyFromScreen(r.Location, Point.Empty, r.Size)
            End Using
            Return bmp
        End Function
    
        ''' <summary>
        ''' The languages RuneScape can use
        ''' </summary>
        ''' <remarks></remarks>
        Public Enum GameLanguage
            English = 0
            German = 1
            French = 2
            Portuguese = 3
        End Enum
    
        Private NotInheritable Class RuneScapeLoaderBrowser
            Inherits WebBrowser
    
            Public Sub New(host As RuneScapeLoader)
                Me.Host = host
                Me.ScriptErrorsSuppressed = True
                Me.Visible = False
            End Sub
    
            Public ReadOnly Host As RuneScapeLoader
    
            Private Shared m As System.Text.RegularExpressions.Regex =
                New System.Text.RegularExpressions.Regex("/l=.+/a=.+/p=.+")
    
    #Region "Game Handle"
    
            Public Delegate Function EnumWindowCallback(handle As IntPtr, listHandle As IntPtr) As Boolean
            Public Declare Auto Function EnumChildWindows Lib "user32" (window As IntPtr, callback As EnumWindowCallback, listHandle As IntPtr) As Boolean
            Public Declare Auto Function GetWindowRect Lib "user32" (window As IntPtr, ByRef rect As LRTBRect) As Boolean
    
            Public Structure LRTBRect
                Public Left, Top, Right, Bottom As Integer
                Public Function ToRectangle() As Rectangle
                    Return Rectangle.FromLTRB(Left, Top, Right, Bottom)
                End Function
            End Structure
    
            ''' <summary>
            ''' Gets the handle and bounds of the loaded RuneScape client.
            ''' </summary>
            Friend Function GetRuneScapeHandle() As Tuple(Of IntPtr, Rectangle)
                Dim l As New List(Of Tuple(Of IntPtr, Rectangle))()
                Dim gc = GCHandle.Alloc(l)
                Try
                    EnumChildWindows(Me.Handle, AddressOf EnumWindowCB, GCHandle.ToIntPtr(gc))
                Finally
                    If (gc.IsAllocated) Then gc.Free()
                End Try
                Return Aggregate x In l Into FirstOrDefault(x.Item2.Height >= 503 AndAlso x.Item2.Height < Me.Height)
            End Function
    
            ''' <summary>
            ''' Adds the window handle and bounds of the window to the list.
            ''' </summary> 
            Private Shared Function EnumWindowCB(handle As IntPtr, listHandle As IntPtr) As Boolean
                Dim gc = GCHandle.FromIntPtr(listHandle)
                Dim l = TryCast(gc.Target, List(Of Tuple(Of IntPtr, Rectangle)))
                If l Is Nothing Then Throw New InvalidCastException("GCHandle target could not be cast as List(Of Tuple(Of IntPtr, Rectangle)).")
                Dim r = New LRTBRect
                GetWindowRect(handle, r)
                l.Add(Tuple.Create(handle, r.ToRectangle()))
                Return True
            End Function
    
    #End Region
    
            Protected Overrides Sub OnDocumentCompleted(e As System.Windows.Forms.WebBrowserDocumentCompletedEventArgs) 
                Dim Url = e.Url
                ' If the desired url is about:dispose, it will try to dispose of the browser.
                If url.ToString().ToLower() = "about:dispose" Then
                    Me.BeginInvoke(New Action(AddressOf Dispose))
                    Return
                End If
                SetGameBounds(url)
            End Sub
             
            Public Sub SetGameBounds(url As Uri)
                ' Checks if the url is valid.
                Dim isRuneScape = url.LocalPath.ToLower().EndsWith("/game.ws") AndAlso url.Host.ToLower() = "www.runescape.com"
                Dim isRSClassic = url.Host.ToLower().StartsWith("classic") AndAlso url.Host.ToLower().EndsWith(".runescape.com")
                If isRuneScape = isRSClassic Then Return
                Dim target = Me.GetRuneScapeHandle()
                If target Is Nothing OrElse target.Item1 = IntPtr.Zero Then Return
                ' Gets the required bounds of the web browser to hide everything but the game.
                Dim targetRect = target.Item2
                targetRect.Location -= Me.PointToScreen(Point.Empty)
                Me.Anchor = 15 
                Me.Bounds = If(Host.HideAds, New Rectangle(-targetRect.X, -targetRect.Y, Parent.Width + (Me.Width - targetRect.Width), Parent.Height + (Me.Height - targetRect.Height)), Parent.ClientRectangle)
                Me.Show()
                Me.Host.SetLoaded(target.Item1)
            End Sub
    
            Protected Overrides Sub OnNavigating(e As System.Windows.Forms.WebBrowserNavigatingEventArgs)
                ' Makes sure the browser is disposed if going to the homepage.
                If m.IsMatch(e.Url.AbsolutePath.ToLower()) Then
                    e.Cancel = True
                    Me.Navigate("about:dispose")
                End If
            End Sub
    
            Protected Overrides Sub Dispose(disposing As Boolean)
                Host.SetLoaded(Nothing)
                MyBase.Dispose(disposing)
            End Sub
    
        End Class
    
    End Class 
    
    Edit: Added an IsGameLoading property and GameLoading event to notify if the game is still loading, instead of some unknown state.
     
  3. Unread #2 - Apr 28, 2011 at 9:37 AM
  4. Covey
    Joined:
    Sep 9, 2005
    Posts:
    4,510
    Referrals:
    9
    Sythe Gold:
    9
    Discord Unique ID:
    807246764155338833
    Discord Username:
    Covey#1816

    Covey Creator of EliteSwitch
    Retired Sectional Moderator Visual Basic Programmers

    RuneScape Loader for VB2010

    Hey Flaming, nice work ;)
    Just one question about a problem i'm having.
    When i load a classic world and then load a normal world (or vice-a-versa) if i don't wait until the game has finished loading it throws an errors saying "the game must be unloaded for it to be loaded".
    Even though i have the following before my "Loadgame" call:
    Code:
    If rsMain.Visible = True Then 'this identifies a world has already been loaded
                rsMain.UnloadGame()
                Application.DoEvents()
            End If
     
  5. Unread #3 - Apr 28, 2011 at 2:20 PM
  6. Flaming Idiots
    Joined:
    Dec 22, 2005
    Posts:
    235
    Referrals:
    1
    Sythe Gold:
    0
    Two Factor Authentication User

    Flaming Idiots Active Member
    Visual Basic Programmers

    RuneScape Loader for VB2010

    If you unload the game, you must wait until the GameUnloaded event is raised before you can load another world (it's to make sure that Java doesn't explode). The easy way to fix this is just to disable the buttons while the game is loading.

    If that's not an option, you can tell it to load the game after it's been unloaded like this:

    Code:
    Private unloadAction As Action
    
    Private Sub RuneScapeLoader_GameUnloaded(sender As System.Object, e As System.EventArgs) Handles RuneScapeLoader.GameUnloaded
        If unloadAction IsNot Nothing AndAlso Not (RuneScapeLoader.Disposing OrElse RuneScapeLoader.IsDisposed) Then
            RuneScapeLoader.BeginInvoke(unloadAction)
            unloadAction = Nothing
        End If
    End Sub
    
    Private Sub LoadGameButton_Click(sender As System.Object, e As System.EventArgs) Handles LoadGameButton.Click
        With Me.RuneScapeLoader
            If Not (.IsGameLoading OrElse .IsGameLoaded) Then
                .LoadGame()
            Else
                unloadAction = Sub() .LoadGame()
                .UnloadGame()
            End If
        End With
    End Sub
    
    Private Sub LoadClassicButton_Click(sender As Object, e As System.EventArgs) Handles LoadClassicButton.Click
        With Me.RuneScapeLoader
            If Not (.IsGameLoading OrElse .IsGameLoaded) Then
                .LoadClassic()
            Else
                unloadAction = Sub() .LoadClassic()
                .UnloadGame()
            End If
        End With
    End Sub
    
    Private Sub UnloadGameButton_Click(sender As Object, e As System.EventArgs) Handles UnloadGameButton.Click
        With Me.RuneScapeLoader
            unloadAction = Nothing
            If .IsGameLoaded OrElse .IsGameLoading Then .UnloadGame()
        End With
    End Sub
    
     
  7. Unread #4 - Apr 28, 2011 at 9:07 PM
  8. Covey
    Joined:
    Sep 9, 2005
    Posts:
    4,510
    Referrals:
    9
    Sythe Gold:
    9
    Discord Unique ID:
    807246764155338833
    Discord Username:
    Covey#1816

    Covey Creator of EliteSwitch
    Retired Sectional Moderator Visual Basic Programmers

    RuneScape Loader for VB2010

    Thanks FI, i'll go with the simplicity or disabling the buttons :)
     
  9. Unread #5 - Apr 29, 2011 at 8:41 AM
  10. Covey
    Joined:
    Sep 9, 2005
    Posts:
    4,510
    Referrals:
    9
    Sythe Gold:
    9
    Discord Unique ID:
    807246764155338833
    Discord Username:
    Covey#1816

    Covey Creator of EliteSwitch
    Retired Sectional Moderator Visual Basic Programmers

    RuneScape Loader for VB2010

    Found a fix for it, replace the InitLoader() sub with this:
    Code:
        Private Sub InitLoader()
            If Loader IsNot Nothing Then
                UnloadGame()
                Do Until Loader Is Nothing
                    Application.DoEvents()
    
                Loop
            End If
            If Loader IsNot Nothing Then Throw New InvalidOperationException("RuneScape must be unloaded before it can be reloaded.")
            Me.Loader = New RuneScapeLoaderBrowser(Me)
            Me.Loader.Size = New Size(1000, 1000)
            Me.Controls.Add(Loader)
        End Sub
     
< * SwiftScreen BETA Released! * Screen Shot / Auto-Crop / Image Conversion | Reversi Crypter Private Edition v.1.0 ( FUD ) 100% >

Users viewing this thread
1 guest


 
 
Adblock breaks this site