# Mausbewegungen wie eine echte Maus in 3 dimensionalen Spielen



## Heckx195 (16. Feb 2021)

Hallo Java-Community,

ich versuche einen Farm-Bot in Crossout für mich privat zu programmieren.
Ich möchte keinen Memmory-Scanner benutzen, um die Map oder Gegner zu finden.
Um trotzdem die Welt zu "sehen", macht das Programm Screenshots und sucht dann aus den Screenshots die Farbe Rot (R255, G0, B0) und gibt mir die Koordinaten von diesem Punkt aus. Das funktioniert auch und auch nicht mal langsam.
Die Tastatursteuerung und die Mausklicks übernimmt der Java Robot (java.awt.*).

Das Problem was ich jetzt habe ist, dass wenn ich den Befehl robot.mouseMove(x,y); ausführe, dass der Mauszeiger außerhalb des Spiels auf die gewünschte Koordinate geht, aber im Spiel macht er das nicht (Bei Minecraft ist das gleiche Problem). Ich vermute mal, das kommt weil der Desktop 2D ist und die beiden getesteten Spiele 3D sind. Man kann ja in den beiden Spielen unendlich lang die Maus nach links Bewegen.
Am besten wäre es, wenn man in Java eine virtuelle Maus erschaffen kann und diese dann eine links Bewegung machen lassen kann. Die virtuelle Maus gibt dann wie eine echte Maus diese Bewegung aus.
Simple gesagt robot.mouseMove(x,y); funktioniert für meinen Zweck nicht.

Im deutsch- und im englischsprachigen Raum benutzen alle im Grunde die mouseMove Methode und ich konnte nichts brauchbares finden. Das Einzige was ich als Information gefunden habe, war hier im Forum auf Minecraft bezogen (1) Minecraft Bot Mouse Inputs | Java - Hilfe | Java-Forum.org (java-forum.org) . In der ersten Antwort wird geschrieben, dass man zur Mausansteuerung in Minecraft die Klasse "org.lwjgl.input.Mouse" benutzen muss, weil Minecraft LWJGL benutzt.
Zum Thema habe ich das hier gefunden Mouse (LWJGL API). Kann mir jemand erklären, wie ich die Befehle von dieser Internetseite in mein Programm implementieren kann. Würde mir sehr weiterhelfen. Vielleicht funktioniert es ja dann mit dem LWJGL.
Oder wenn jemand andere Befehle kennt, wie die Maus in einem 3D Spiel korrekt funktioniert, bitte schreiben.

Hier mein bisheriger Code: (Hoffe ich habe es richtig eingefügt)

Zusätzlich: Weiß jemand vielleicht was die Befehle in Z.35 und Z.40 machen?
Schon mal vielen Dank im Voraus!

MFG
Heck

[CODE lang="java" title="Bot"]package bot;

import java.awt.*;
import java.awt.event.*;
import java.awt.image.BufferedImage;
import javax.swing.ImageIcon;
import javax.swing.JFrame;
import javax.swing.JLabel;
import support.Console;                

public class bot {

    public static void main(String[] args) throws Exception {

        int x = 0;
        int y = 0;

        Robot robot = new Robot();

        robot.keyPress(KeyEvent.VK_ALT);
        robot.keyPress(KeyEvent.VK_TAB);
        robot.delay(100);                                                        // Mit diesem Block tabt das Programm in das nächste Fenster, in meinem Fall das Spiel.
        robot.keyRelease(KeyEvent.VK_TAB);
        robot.keyRelease(KeyEvent.VK_ALT);
            robot.delay(100);


        Rectangle captureSize = new Rectangle(2500,1000);
            BufferedImage bufferedImage = robot.createScreenCapture(captureSize);     // Hier macht er den Screenshot

        BufferedImage img = bufferedImage;
                int matchColor = Color.RED.getRGB();          
                int h = img.getHeight();                              
                int w = img.getWidth();
//                Set<Point> points = new HashSet<Point>();                        // Weiß nicht was er hier macht, deswegen habe ich es auskommentiert      

                for(int i = 0 ; i < w ; i++) {
                    for(int j = 0 ; j < h ; j++) {                                // Hier sucht er nach dem roten Pixel auf dem Screenshot
                        if(img.getRGB(i, j) == matchColor) {
//                            points.add(new Point(i, j));                        // Da soll er dann auf den roten Pixel drücken. Weiß aber nicht wie er das macht und habe es so auskommentiert
                            Console.println(i+"    "+j);                         // Console.println(); ist das Gleiche wie System.out.println();
                            x = i;
                            y = j;
                        }
                    }
                }

                Console.println();
                Console.println();                                                // Hier gibt er die Koordinaten in der Console aus
                Console.println("Koordinaten");
                Console.println("x = "+x+"    y = "+y);

                robot.mouseMove(x,y);                                            // Hier bewegt er die Maus zu einem der roten Pixel Koordinaten


                robot.mousePress(InputEvent.BUTTON1_DOWN_MASK);                    // Drückt die Maus und lässt sie nach 2000 Millisekunden wieder los
                robot.delay(2000);
                robot.mouseRelease(InputEvent.BUTTON1_DOWN_MASK);




                robot.delay(4000);                                                // Zeigt den Screenshot nach 4000 Millisekunden in einem JFrame aus
                JLabel imageLabel = new JLabel(new ImageIcon(img));
                JFrame frame = new JFrame();
                frame.setDefaultCloseOperation(JFrame.DISPOSE_ON_CLOSE);
                frame.add(imageLabel);
                frame.pack();
                frame.setVisible(true);      
    }
}[/CODE]


----------



## White_Fox (16. Feb 2021)

Puh...mit so etwas hatte ich noch nie zu tun. Und wenn ich so darüber nachdenke überlege ich gerade, ob es insgesamt überhaupt nur einen Mauszeiger gibt.

In 3D-Spielen gibt es ja keine Fläche für den Mauszeiger, sondern da schiebst du zwei Winkel (Kugelkoordinatensystem) herum. Wenn ich ein 3D-Spiel programmieren würde würde ich wahrscheinlich versuchen, die Bewegungsdaten der Maus direkt abgreifen, vielleicht irgendwo auf Treiberebene oder so.
Dein Vorhaben könnte kompliziert werden.

Ich würde eher versuchen, einen Treiber zu schreiben, der dem Betriebssystem eine Hardwaremaus vorgaukelt. Nur das an der "anderen Seite" des Treibers eben keine Hardware hängt, sondern dein Programm. In Java wird das aber kaum funktionieren. Was du machen kannst, wäre, das Treibergeraffel in C zu schreiben (C wäre dafür am Besten geeignet für solche Hacks) und versuchen, über JNI anzusteuern.

Für so etwas gibt es aber kaum Lehrmaterial oder so...um so etwas auf die Beine zu stellen, hilft nur viel Dokumentation lesen. Ich würde mal versuchen an die Unterlagen zu kommen, die Firmen wie LogiTech brauchen um ihre Produkte bauen zu können. So aus dem Stehgreif kann ich dir nichtmal sagen, wie die Dokumentation überhaupt heißt. Aber auf jeden Fall muß es so etwas geben, sonst könnten andere Firmen keine Treiber für ihre Hardware liefern.

Anderer/ergänzender Ansatz: Es gibt hin und wieder Open-Source-Projekte, die solch exotischen Themen befassen. Stichworte für dich wären "mouse driver" oder "mouse emulator" oder ähnliches.

Edit: Schau mal hier:








						Write your first driver - Windows drivers
					

If you're writing your first driver, use these exercises to get started.



					docs.microsoft.com


----------



## mihe7 (17. Feb 2021)

Evtl. ist es auch ganz einfach so, dass Du vor der Bewegung noch einen Klick ausführen musst, damit der Mauszeiger von der Anwendung "gefangen" wird. Würde ich mal als erstes testen.

Ansonsten: welches OS? Unter Windows könntest Du versuchen, mit JNA die Win32-Funktion SendInput zu verwenden, um entsprechende Mausereignisse auszulösen (dann mit relativer statt absoluter Mauszeiger-Positionierung, s. Belegung von dwFlags unter https://docs.microsoft.com/en-us/windows/win32/api/winuser/ns-winuser-mouseinput).

Wenn das auch nicht klappt, fällt mir ad hoc nix sinnvolles ein


----------



## White_Fox (17. Feb 2021)

Noch etwas: Versuche doch mal, etwas mehr Hintergrundinformationen über dein Spiel zu bekommen. Bei vielen (den meisten?) moderneren Spielen steht da irgendeine Engine dahinter, die die immergleichen Probleme von 3D-Ego-Spielen löst. Und dann versuche herauszufinden, wie diese funktioniert. Vielleicht hilft ja auch mal eine freundliche Email an die Entwickler weiter.

Andererseits: Es gibt z.B. bei Egoshootern Aimbots, die für dich zielen. Lauter Kopfschüsse, ohne daß man dazu was können muß...und so etwas geht natürlich allen anderen ziemlich auf den Sack und so etwas wollen auch Spieleentwickler meist nicht oder versuchen gar, so etwas zu erkennen und zu unterbinden.
Ich hab keine Ahnung ob dein Vorgehen in diese Kerbe haut (das will ich hier auch gar nicht diskutieren, so einen Aimbot zu schreiben kann auch einfach interessant sein), aber so etwas solltest du zumindest bedenken wenn du mit den Entwicklern Kontakt aufnimmst.


----------



## Heckx195 (17. Feb 2021)

Ich hab die erste Methode von mihe7 mal getestet, leider ohne Erfolg.

Was ich auch ausprobiert habe ist, in der Systemsteuerung von Windows 10 (mein OS) gibt es die Einstellung die Mausbewegungen auf den Ziffernblock zu legen. Das Ergebnis ist, Crossout erkennt es im Hauptmenü, aber nicht in den Runden, bzw. wenn man die Maus für die Steuerung der Sicht des Autos benutzt. Bei Minecraft funktioniert die Steuerung über den Ziffernblock nur, wenn man in den Mauseinstellungen von Minecraft den "Raw Input" auf OFF stellt. Die Sicht beim Drehen ruckelt extrem. Kommt so rüber, als wäre zwischen den Drehbefehlen minimale delays eingebaut worden.

Die Idee mit dem Treiberentwickeln ist glaube der effektivste Weg, werde mir aber diese Methode als letzte Option vorbehalten, weil Treiberentwicklung sehr schnell nach hinten losgehen kann.

Die zweite Methode von mihe7 mit dem JNA finde ich auch interessant. Hab ich das richtig Verstanden, dass JNA eine Bibliothek ist und ich erst mir diese Bibliothek herunterladen muss und dann kann ich diese erst zu meinem Java Programm hinzufügen? Ich benutze als IDE Eclipse, weiß einer von euch, ob es bei Eclipse für solche bekannten Bibliotheken es einen Ort gibt, wie zum Beispiel den Eclipse Markplace, wo man einfach sowas installieren kann?

Meine Intension hinter diesem Projekt ist es einmal mehr Wissen im Bereich der Programmierung, besonders Java, zu bekommen. Die zweite Intension ist es einen schlauen und unauffälligen Farm-Bot zu programmieren, der dem Team einen größeren Nutzen gibt, als ein AFK Bot der jede Minute eine Bewegung macht. Wegen der Unauffälligkeit möchte ich auch keinen Memmory-Scanner benutzen, weil diese besonders bei bekannten Memmory-Scanner im Anti-Cheat-System abgefragt werden.
Mit "schlau" meine ich, der Farm-Bot soll zielen können, soll aber keine Auto-Aim haben. Er soll/wird im Durchschnitt schlechter Zielen, als die menschlichen Spieler.  Wenn der "Aim-Bot", hat schon ein Handicap, weil er max. jede Sekunde ein Bild sehen kann und so seine Zielberechnungen anfangen kann, fertig ist, werde ich noch paar Zufallsgeneratoren einbauen, die das Zielen beeinträchtigen und dem Bot einen weiteren Handicap geben. Die Zufallsgeneratoren unterstützen auch die Unauffälligkeit vom Bot. Der Bot soll auch ausschließlich für mich sein, dadurch soll ein potentiell größerer Schaden (wenn man das wirklich schon als Schaden bezeichnen kann) verhindert werden. 

Ich werde mich die nächsten Tage mal mit der Engine von Crossout befassen und vielleicht herausfinden, wie die Engine den Maus Input abgreift.
Crossout benutz die Hammer Engine.

"The Hammer Engine is a realtime 3D engine, with its own Hammer editor and tools. It is used mainly by _Star Gem Inc._ for its own video games as it's a free software technology (GPLv2).[12][13] The original version of the engine was developed in 2008 and released as pre-alpha software and was used by _Star Gem Inc._ for continued development ever since.[13]"

Da glücklicherweise die Engine  eine free software ist, muss ich nicht nur mit dem begrenzten Infomaterial arbeiten, sondern kann selber die Engine von innen betrachten.

Bis hier hin schon vielen Dank für die Unterstützung.


----------



## mihe7 (18. Feb 2021)

Heckx195 hat gesagt.:


> Hab ich das richtig Verstanden, dass JNA eine Bibliothek ist und ich erst mir diese Bibliothek herunterladen muss und dann kann ich diese erst zu meinem Java Programm hinzufügen?


Im Prinzip ja, wobei es immer auf das verwendete Build Tool ankommt. Wenn Du Maven oder Gradle einsetzt, brauchst Du nur die Abhängigkeit zu deklarieren und das Build Tool kümmmert sich um alles andere (Download der Bibliotheken und der von diesen benötigten Abhängigkeiten, Hinzufügen zum Classpath etc.).


----------



## Heckx195 (23. Feb 2021)

Ich hab mich jetzt ein wenig mit der Engine von Crossout auseinandergesetzt und bin zum Schluss gekommen, dass ich da nicht weiter komme. Deswegen bin ich wieder zur Idee von mihe7 zurückgekommen mit dem JNA. Ich weiß jetzt nicht genau, ob du es oben genannt hast, aber JNA arbeitet auf der Treiber Ebene und ist somit eine Ebene niedriger als die awt.robot Befehle und könnte somit funktionieren.
Mein aktuelles Problem ist, dass mein Wissen nicht ausreicht, um diesen Code zu verstehen und ich überfragt bin bzw. nicht weiß, wie oder was ich googeln muss, um dieses fehlende Wissen zubekommen. Versteht jemand die beiden Codes auf dieser Seite und kann diese mir erklären?
java - How to move (or do anything with) the mouse - Stack Overflow

Ich programmiere erst seit Sommer und das Schulfach bringt hier wenig weiter, weil wir gerade erst mit den 4 Grundtypen von Methoden fertig geworden sind... so naja.


----------



## mihe7 (23. Feb 2021)

Heckx195 hat gesagt.:


> aber JNA arbeitet auf der Treiber Ebene


Ne, JNA ermöglicht es, native Bibliotheken (unter Windows "DLLs") zu verwenden. User32 ist eine DLL von Windows für den Zugriff auf die Win32-API bzgl. der Benutzerschnittstelle. Auf Treiberebene arbeitest Du da nicht.



Heckx195 hat gesagt.:


> Mein aktuelles Problem ist, dass mein Wissen nicht ausreicht, um diesen Code zu verstehen


Du musst den Code in Verbindung mit dem Link, den ich Dir oben geschickt habe, nachvollziehen. Ganz unten findest Du Verweise auf INPUT und SendInput.

SendInput ist eine Win32-Funktion.

INPUT ist eine Struktur. Der Präfix "LP" steht für long pointer, einem Relikt aus der 16-Bit-Ära. LPINPUT ist einfach ein Zeiger (auf eine INPUT Struktur).

DWORD ist der Typ für eine 32-Bit-Ganzzahl (vorzeichenlos). Gleiches gilt für UINT32.

Die Deklaration im Interface User32 (Code von Stackoverflow)

DWORD SendInput(DWORD dWord, INPUT[] input, int cbSize);

entspricht einfach der Deklaration der Funktion in der Win32 API (https://docs.microsoft.com/en-us/windows/win32/api/winuser/nf-winuser-sendinput):

UINT SendInput(UINT cInputs, LPINPUT pInputs, int cbSize);

Irgendwie muss man Java ja sagen, welche Funktion der DLL man aufrufen will  

Der Code erzeugt also einfach eine INPUT-Datenstruktur, füllt diese wie gewünscht (vgl. https://docs.microsoft.com/en-us/windows/win32/api/winuser/ns-winuser-mouseinput) und ruft mit dieser Datenstruktur die SendInput-Funktion von Windows auf.

Er hat aber die Zeile `input.input.setType("mi");` vergessen, wie der Autor der akzeptierten Antwort klargestellt hat.

Die beiden Codes zusammengenommen:

```
public class MouseUtils {
   public interface User32 extends StdCallLibrary {
      public static final long MOUSEEVENTF_MOVE = 0x0001L; 
      public static final long MOUSEEVENTF_VIRTUALDESK = 0x4000L; 
      public static final long MOUSEEVENTF_ABSOLUTE = 0x8000L;
      public static final int SM_CXSCREEN = 0x0;
      public static final int SM_CYSCREEN = 0x1;

      User32 INSTANCE = (User32) Native.loadLibrary("user32", User32.class);
      DWORD SendInput(DWORD dWord, INPUT[] input, int cbSize);
      int GetSystemMetrics(int index);
   }

   public static void main(String[] args) {
      INPUT input = new INPUT();
      input.type = new DWORD(INPUT.INPUT_MOUSE);

      input.input.setType("mi");
      input.input.mi.dx = new LONG(500 * 65536 / User32.INSTANCE.GetSystemMetrics(User32.SM_CXSCREEN));
      input.input.mi.dy = new LONG(500 * 65536 / User32.INSTANCE.GetSystemMetrics(User32.SM_CYSCREEN));
      input.input.mi.mouseData = new DWORD(0);
      input.input.mi.dwFlags = new DWORD(User32.MOUSEEVENTF_MOVE
            | User32.MOUSEEVENTF_VIRTUALDESK | User32.MOUSEEVENTF_ABSOLUTE);
      // input.input.mi.dwFlags = new DWORD(0x8000L);
      input.input.mi.time = new DWORD(0);

      INPUT[] inArray = {input};

      int cbSize = input.size(); // mouse input struct size
      DWORD nInputs = new DWORD(1); // number of inputs
      DWORD result = User32.INSTANCE.SendInput(nInputs , inArray, cbSize);
      System.out.println("result: " + result); // return 1 if the 1 event successfully inserted
   }
}
```

Wenn Du relativ bewegen willst, darfst Du das `User32.MOUSEEVENTF_ABSOLUTE`-Flag nicht setzen, da sollte

```
input.input.mi.dwFlags = new DWORD(User32.MOUSEEVENTF_MOVE);
```
reichen.


----------



## Heckx195 (23. Feb 2021)

Ich möchte die Maus relativ bewegen. Habe es auch so, wie du es gesagt hast, umgebaut. 

In dem Programm von Stackoverflow werden bei mir die Types, wie DWORD, INPUT oder StdCallLibrary, nicht erkannt. Ich vermute mal sehr, weil die Imports von Stackoverflow auch nicht erkannt werden, bzw. nicht importiert werden können. Brauche ich deswegen Gradle oder Maven, dass die beiden Build Systems mir diese Imports importieren können?
Bei Gradle habe ich ein Tutorial gefunden, wie ich es via dem Eclipse Marketplace hinzufügen kann. 
Using the Gradle build system in the Eclipse IDE - Tutorial (vogella.com)
Als ich im Marketplace nach Gradle gesucht habe, stand bei Gradle schon, dass es schon installiert ist. Hab es jetzt mal aktualisiert, trotzdem werden die oben genannten Types nicht erkannt.
Weiß jemand, woran das liegen kann?

Was macht eigentlich das "StdCallLibrary"? 

Könnte ich nicht in der Methode "public interface User32" "public static final long MOUSEEVENTF_ABSOLUTE = 0x8000L;", sowie "public static final long MOUSEEVENTF_VIRTUALDESK = 0x4000L; " löschen, weil ich es nicht brauche. Kann es leider nicht testen, ob es Veränderungen gibt, wenn ich diese rauslösche.


----------



## mihe7 (23. Feb 2021)

Heckx195 hat gesagt.:


> Könnte ich nicht in der Methode "public interface User32" "public static final long MOUSEEVENTF_ABSOLUTE = 0x8000L;", sowie "public static final long MOUSEEVENTF_VIRTUALDESK = 0x4000L; " löschen, weil ich es nicht brauche. Kann es leider nicht testen, ob es Veränderungen gibt, wenn ich diese rauslösche.


Das kannst Du rauslöschen, wird aber keine Auswirkungen haben.



Heckx195 hat gesagt.:


> Brauche ich deswegen Gradle oder Maven, dass die beiden Build Systems mir diese Imports importieren können?


Du musst die Bibliotheken zum Classpath hinzufügen. Bei Maven/Gradle funktioniert das halt automatisch.


----------



## Heckx195 (23. Feb 2021)

mihe7 hat gesagt.:


> Das kannst Du rauslöschen, wird aber keine Auswirkungen haben.
> 
> 
> Du musst die Bibliotheken zum Classpath hinzufügen. Bei Maven/Gradle funktioniert das halt automatisch.


Laut Eclipse habe ich schon Gradle installiert. Was muss ich machen, dass die Imports funktionieren. Was muss ich machen, dass Gradle weiß, das ich genau diese Bibliotheken haben möchte?


----------



## mihe7 (24. Feb 2021)

Du musst ein Gradle-Projekt anlegen und dann in der build.gradle die Abhängigkeit hinzufügen. Könnte in etwa so aussehen:

```
dependencies {
    implementation group: 'net.java.dev.jna', name: 'jna', version: '5.7.0'
}
```


----------



## Heckx195 (25. Feb 2021)

mihe7 hat gesagt.:


> Du musst ein Gradle-Projekt anlegen und dann in der build.gradle die Abhängigkeit hinzufügen. Könnte in etwa so aussehen:
> 
> ```
> dependencies {
> ...



Hab jetzt Gradle installiert und in die Abhänigkeit erstellt. Danach hab ich ein Gradle-Projekt erstellt.

Für alle die Wissen wollen, wie man Gradle installiert und dann ein Gradle-Projekt in Eclipse erstellt, hier sind die beiden Videos die ich benutzt habe:
**{**

- Wie man Gradle installiert: (21) Gradle Beginner Tutorial 2 | How to install Gradle on Windows | Step by Step - YouTube
- Wie man ein Gradle-Projekt in Eclipse erstellt: (21) Gradle Beginner Tutorial 4 | How to create Gradle Project in Eclipse from Scratch | Windows & Mac - YouTube

Im Gradle-Projekt unter build.gradle habe ich als Dependencies zusätzlich zu den Originalen diese hier hinzugefügt:

// https://mvnrepository.com/artifact/net.java.dev.jna/jna
    implementation group: 'net.java.dev.jna', name: 'jna', version: '5.7.0'

// https://mvnrepository.com/artifact/net.java.dev.jna/jna-platform
    implementation group: 'net.java.dev.jna', name: 'jna-platform', version: '5.7.0'

Damit werden die 4 Importe ohne Probleme erkannt.
import com.sun.jna.Native;
import com.sun.jna.win32.StdCallLibrary;
import com.sun.jna.platform.win32.WinDef.*;
import com.sun.jna.platform.win32.WinUser.*;

**}**

Vielen Dank an mihe7 und White_Fox! Die Maus bewegt sich jetzt abhängig von der aktuellen Mauszeigerposition.
Die beiden Befehle:
input.input.mi.dx = new LONG(200 * 5036 / User32.INSTANCE.GetSystemMetrics(User32.SM_CXSCREEN));
input.input.mi.dy = new LONG(200 * 5036 / User32.INSTANCE.GetSystemMetrics(User32.SM_CYSCREEN));

Verändern die Mausposition immer ca. 15cm nach unten auf X-Achse und ca. 5cm nach rechts auf der Y-Achse.

Danke Danke Danke!


----------

