Flaming Idiots
Active Member
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.
Edit: Added an IsGameLoading property and GameLoading event to notify if the game is still loading, instead of some unknown state.
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

