Advertisement
Not a member of Pastebin yet?
Sign Up,
it unlocks many cool features!
- package com.runematescript
- import com.runemate.game.api.hybrid.Environment
- import com.runemate.game.api.hybrid.entities.details.Renderable
- import com.runemate.game.api.hybrid.local.Screen
- import com.runemate.game.api.script.framework.AbstractBot
- import com.runemate.game.api.script.framework.listeners.EngineListener
- import com.sun.jna.Pointer
- import com.sun.jna.platform.win32.*
- import com.sun.jna.platform.win32.WinUser.*
- import com.sun.jna.ptr.IntByReference
- import javafx.application.Platform
- import javafx.beans.property.BooleanProperty
- import javafx.beans.property.SimpleBooleanProperty
- import javafx.scene.Scene
- import javafx.scene.canvas.Canvas
- import javafx.scene.layout.Background
- import javafx.scene.layout.Pane
- import javafx.scene.paint.Color
- import javafx.stage.Stage
- import javafx.stage.StageStyle
- import java.util.*
- import java.util.concurrent.Semaphore
- import java.util.concurrent.TimeUnit
- import kotlin.concurrent.thread
- interface GameOverlay {
- var enableDoubleBuffering: Boolean
- val state: State
- fun initialize()
- fun destroy()
- fun addRenderable(renderable: Renderable)
- fun removeRenderable(renderable: Renderable)
- companion object {
- @JvmStatic
- fun newOverlay(bot: AbstractBot): GameOverlay = WindowsGameOverlay(bot)
- }
- enum class State {
- READY, INITIALIZED, DESTROYED
- }
- }
- private abstract class GameOverlayBase : GameOverlay, EngineListener {
- private val renderables: MutableList<Renderable> = Collections.synchronizedList(mutableListOf())
- val renderLock = Semaphore(1)
- override var enableDoubleBuffering = false
- override var state = GameOverlay.State.READY;
- abstract val bot: AbstractBot
- lateinit var stage: OverlayStage
- lateinit var gamePID: String
- open fun prepareStage() {}
- open fun setup() {}
- open fun overlayMain() {}
- open fun onDestroy() {}
- final override fun initialize() {
- when (state) {
- GameOverlay.State.READY -> state = GameOverlay.State.INITIALIZED
- GameOverlay.State.INITIALIZED -> return
- GameOverlay.State.DESTROYED -> return
- }
- gamePID = Environment.getRuneScapeProcessId() ?: throw IllegalStateException("Couldn't get game process id")
- Platform.runLater {
- stage = OverlayStage("GameOverlay$gamePID%")
- prepareStage()
- stage.show()
- bot.platform.invokeLater {
- setup()
- bot.eventDispatcher.addListener(this)
- overlayMain()
- }
- }
- }
- final override fun destroy() {
- when (state) {
- GameOverlay.State.READY -> {
- state = GameOverlay.State.DESTROYED
- return
- }
- GameOverlay.State.INITIALIZED -> state = GameOverlay.State.DESTROYED
- GameOverlay.State.DESTROYED -> return
- }
- try {
- onDestroy()
- bot.eventDispatcher.removeListener(this)
- Platform.runLater {
- stage.close()
- }
- } catch (e: Exception) {
- e.printStackTrace(bot.logger.printStream)
- }
- }
- final override fun addRenderable(renderable: Renderable) {
- renderables.add(renderable)
- }
- final override fun removeRenderable(renderable: Renderable) {
- renderables.remove(renderable)
- }
- final override fun onCycleStart() {
- if (renderLock.tryAcquire()) {
- thread {
- try {
- if (enableDoubleBuffering) {
- stage.renderDoubleBuffered(renderables)
- } else {
- stage.render(renderables)
- }
- } finally {
- renderLock.release()
- }
- }
- }
- }
- }
- private class OverlayStage(title: String) : Stage(StageStyle.TRANSPARENT) {
- val root = Pane()
- private val frontCanvas = Canvas()
- private val backCanvas = Canvas()
- private val doubleBufferingFlag = SimpleBooleanProperty()
- init {
- setTitle(title)
- scene = Scene(root, 1.0, 1.0, Color.TRANSPARENT)
- root.background = Background.EMPTY
- frontCanvas.widthProperty().bind(widthProperty())
- frontCanvas.heightProperty().bind(heightProperty())
- backCanvas.widthProperty().bind(widthProperty())
- backCanvas.heightProperty().bind(heightProperty())
- frontCanvas.visibleProperty().bind(doubleBufferingFlag)
- backCanvas.visibleProperty().bind(doubleBufferingFlag.not())
- root.children.setAll(frontCanvas, backCanvas)
- root.setOnMouseMoved { event ->
- println("${event.x}, ${event.y}")
- }
- }
- fun render(renderables: List<Renderable>) {
- val canvas = if (doubleBufferingFlag.get()) frontCanvas else backCanvas
- val graphics = canvas.graphicsContext2D
- graphics.clearRect(0.0, 0.0, width, height)
- renderables.forEach {
- it.render(graphics)
- }
- }
- fun renderDoubleBuffered(renderables: List<Renderable>) {
- val flag = doubleBufferingFlag.get()
- val canvas = if (flag) backCanvas else frontCanvas
- val graphics = canvas.graphicsContext2D
- graphics.clearRect(0.0, 0.0, width, height)
- renderables.forEach {
- it.render(graphics)
- }
- Platform.runLater {
- doubleBufferingFlag.set(!flag)
- }
- }
- }
- private class WindowsGameOverlay(override val bot: AbstractBot) : GameOverlayBase() {
- private val user32 = User32.INSTANCE
- private val kernel32 = Kernel32.INSTANCE
- private val hooks = mutableListOf<WinNT.HANDLE>()
- private val procs = mutableListOf<WinEventProc>()
- private var threadID: Int = -1
- override fun prepareStage() {
- }
- override fun setup() {
- threadID = kernel32.GetCurrentThreadId()
- val (gameHWND, overlayHWND) = findWindowHandles()
- val localPID = getProcessIdOf(overlayHWND)
- val gamePID = gamePID.toInt()
- val style = user32.GetWindowLong(overlayHWND, GWL_EXSTYLE)
- user32.ShowWindow(overlayHWND, SW_HIDE)
- user32.SetWindowLong(overlayHWND, GWL_EXSTYLE, style or WS_EX_TRANSPARENT or WS_EX_TOOLWINDOW)
- user32.ShowWindow(overlayHWND, SW_SHOW)
- GameRelocateProc.adjustPosition(overlayHWND)
- GameFocusProc.adjustZOrder(gameHWND, overlayHWND)
- val gameFocusProc = GameFocusProc(gameHWND, overlayHWND)
- val overlayFocusProc = OverlayFocusProc(gameHWND, overlayHWND)
- val gameRelocateProc = GameRelocateProc(overlayHWND)
- val gameDestroyProc = GameDestroyProc(this, gameHWND)
- val gameMinimizeProc = GameMinimizeProc(this, gameHWND, overlayHWND)
- Collections.addAll(procs,
- gameFocusProc,
- overlayFocusProc,
- gameRelocateProc,
- gameDestroyProc,
- gameMinimizeProc
- )
- Collections.addAll(hooks,
- user32.SetWinEventHook(EVENT_OBJECT_REORDER, EVENT_OBJECT_REORDER, null, gameFocusProc, gamePID, 0, WINEVENT_OUTOFCONTEXT or WINEVENT_SKIPOWNPROCESS),
- user32.SetWinEventHook(EVENT_SYSTEM_FOREGROUND, EVENT_SYSTEM_FOREGROUND, null, gameFocusProc, gamePID, 0, WINEVENT_OUTOFCONTEXT or WINEVENT_SKIPOWNPROCESS),
- user32.SetWinEventHook(EVENT_OBJECT_REORDER, EVENT_OBJECT_REORDER, null, overlayFocusProc, localPID, 0, WINEVENT_OUTOFCONTEXT or WINEVENT_SKIPOWNTHREAD),
- user32.SetWinEventHook(EVENT_SYSTEM_FOREGROUND, EVENT_SYSTEM_FOREGROUND, null, overlayFocusProc, localPID, 0, WINEVENT_OUTOFCONTEXT or WINEVENT_SKIPOWNTHREAD),
- user32.SetWinEventHook(EVENT_OBJECT_LOCATIONCHANGE, EVENT_OBJECT_LOCATIONCHANGE, null, gameRelocateProc, gamePID, 0, WINEVENT_OUTOFCONTEXT or WINEVENT_SKIPOWNPROCESS),
- user32.SetWinEventHook(EVENT_OBJECT_DESTROY, EVENT_OBJECT_DESTROY, null, gameDestroyProc, gamePID, 0, WINEVENT_OUTOFCONTEXT or WINEVENT_SKIPOWNPROCESS),
- user32.SetWinEventHook(EVENT_SYSTEM_MINIMIZESTART, EVENT_SYSTEM_MINIMIZEEND, null, gameMinimizeProc, gamePID, 0, WINEVENT_OUTOFCONTEXT or WINEVENT_SKIPOWNPROCESS)
- )
- }
- override fun overlayMain() {
- val msg = WinUser.MSG()
- while (user32.GetMessage(msg, null, 0, 0) > 0) {
- user32.TranslateMessage(msg)
- user32.DispatchMessage(msg)
- }
- }
- override fun onDestroy() {
- user32.PostThreadMessage(threadID, WM_QUIT, null, null)
- hooks.forEach {
- user32.UnhookWinEvent(it)
- }
- }
- private fun getProcessIdOf(overlayHWND: WinDef.HWND): Int {
- val ref = IntByReference()
- user32.GetWindowThreadProcessId(overlayHWND, ref)
- return ref.value
- }
- private fun findWindowHandles(): Pair<WinDef.HWND, WinDef.HWND> {
- var gameHWND: WinDef.HWND? = null
- var overlayHWND: WinDef.HWND? = null
- val proc = proc@{ hwnd: WinDef.HWND, _: Pointer? ->
- val buffer = CharArray(100)
- user32.GetWindowText(hwnd, buffer, 100)
- val title = String(buffer)
- if (overlayHWND == null && title.startsWith(stage.title)) {
- overlayHWND = hwnd
- } else if (gameHWND == null && title.contains("RuneScape")) {
- val pid = IntByReference()
- user32.GetWindowThreadProcessId(hwnd, pid)
- if (pid.value == gamePID.toInt()) {
- gameHWND = hwnd
- }
- }
- true
- }
- user32.EnumWindows(proc, null);
- return Pair(
- gameHWND ?: throw IllegalStateException("Couldn't find game window"),
- overlayHWND ?: throw IllegalStateException("Couldn't find overlay window")
- )
- }
- }
- private class GameMinimizeProc(private val overlay: WindowsGameOverlay, private val gameHWND: WinDef.HWND, private val overlayHWND: WinDef.HWND) : WinUser.WinEventProc {
- override fun callback(hWinEventHook: WinNT.HANDLE, event: WinDef.DWORD, hwnd: WinDef.HWND?, idObject: WinDef.LONG, idChild: WinDef.LONG, idEventThread: WinDef.DWORD, dwmsEventTime: WinDef.DWORD) {
- if (hwnd == gameHWND) {
- when (event.toInt()) {
- EVENT_SYSTEM_MINIMIZESTART -> {
- User32.INSTANCE.ShowWindow(overlayHWND, SW_HIDE)
- overlay.renderLock.acquire()
- }
- EVENT_SYSTEM_MINIMIZEEND -> {
- User32.INSTANCE.ShowWindow(overlayHWND, SW_SHOW)
- overlay.renderLock.release()
- }
- }
- }
- }
- }
- private class GameDestroyProc(private val overlay: GameOverlay, private val gameHWND: WinDef.HWND) : WinUser.WinEventProc {
- override fun callback(hWinEventHook: WinNT.HANDLE, event: WinDef.DWORD, hwnd: WinDef.HWND?, idObject: WinDef.LONG, idChild: WinDef.LONG, idEventThread: WinDef.DWORD, dwmsEventTime: WinDef.DWORD) {
- if (hwnd == gameHWND) {
- overlay.destroy()
- }
- }
- }
- private class GameFocusProc(private val gameHWND: WinDef.HWND, private val overlayHWND: WinDef.HWND) : WinUser.WinEventProc {
- override fun callback(hWinEventHook: WinNT.HANDLE, event: WinDef.DWORD, hwnd: WinDef.HWND?, idObject: WinDef.LONG, idChild: WinDef.LONG, idEventThread: WinDef.DWORD, dwmsEventTime: WinDef.DWORD) {
- adjustZOrder(gameHWND, overlayHWND)
- }
- companion object {
- fun adjustZOrder(gameHWND: WinDef.HWND, overlayHWND: WinDef.HWND) {
- User32.INSTANCE.SetWindowPos(overlayHWND, gameHWND, 0, 0, 0, 0, SWP_NOSIZE or SWP_NOMOVE or SWP_NOACTIVATE)
- User32.INSTANCE.SetWindowPos(gameHWND, overlayHWND, 0, 0, 0, 0, SWP_NOSIZE or SWP_NOMOVE or SWP_NOACTIVATE)
- }
- }
- }
- private class OverlayFocusProc(private val gameHWND: WinDef.HWND, private val overlayHWND: WinDef.HWND) : WinUser.WinEventProc {
- override fun callback(hWinEventHook: WinNT.HANDLE, event: WinDef.DWORD, hwnd: WinDef.HWND?, idObject: WinDef.LONG, idChild: WinDef.LONG, idEventThread: WinDef.DWORD, dwmsEventTime: WinDef.DWORD) {
- User32.INSTANCE.SetWindowPos(gameHWND, overlayHWND, 0, 0, 0, 0, SWP_NOSIZE or SWP_NOMOVE or SWP_NOACTIVATE)
- }
- }
- private class GameRelocateProc(private val overlayHWND: WinDef.HWND) : WinUser.WinEventProc {
- override fun callback(hWinEventHook: WinNT.HANDLE, event: WinDef.DWORD, hwnd: WinDef.HWND?, idObject: WinDef.LONG, idChild: WinDef.LONG, idEventThread: WinDef.DWORD, dwmsEventTime: WinDef.DWORD) {
- if (hwnd != null) {
- adjustPosition(overlayHWND)
- }
- }
- companion object {
- fun adjustPosition(overlayHWND: WinDef.HWND) {
- val location = Screen.getLocation() ?: return
- val bounds = Screen.getBounds() ?: return
- User32.INSTANCE.SetWindowPos(overlayHWND, null, location.x, location.y, bounds.width, bounds.height, SWP_NOZORDER)
- }
- }
- }
- private const val WINEVENT_OUTOFCONTEXT = 0x0000
- private const val WINEVENT_SKIPOWNTHREAD = 0x0001
- private const val WINEVENT_SKIPOWNPROCESS = 0x0002
- private const val WINEVENT_INCONTEXT = 0x0004
- private const val EVENT_SYSTEM_FOREGROUND = 0x0003
- private const val EVENT_SYSTEM_MINIMIZESTART = 0x0016
- private const val EVENT_SYSTEM_MINIMIZEEND = 0x0017
- private const val EVENT_OBJECT_DESTROY = 0x8001
- private const val EVENT_OBJECT_REORDER = 0x8004
- private const val EVENT_OBJECT_LOCATIONCHANGE = 0x800B
- private const val WS_EX_TOOLWINDOW = 0x0080;
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement