AFK Macro

Idle Mafia Macro/Source/src/linux/LinuxNative.cs

LinuxNative.cs

Linux: the screen and the mouse · 484 lines · 18 KB

using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Drawing;
using System.Drawing.Imaging;
using System.IO;
using System.Linq;
using System.Net.Sockets;
using System.Runtime.InteropServices;
using System.Threading;
namespace IdleMafiaBot
{
static class Native
{
public struct POINT { public int X, Y; }
public struct RECT { public int Left, Top, Right, Bottom; }
public static uint Now { get { return unchecked((uint)Environment.TickCount); } }
public static uint LastInputTick() { return 0; }
public static IntPtr RealPixels() { return IntPtr.Zero; }
public static void RestorePixels(IntPtr before) { }
public static bool GetCursorPos(out POINT p) { p = new POINT { X = Rfb.X, Y = Rfb.Y }; return true; }
public static IntPtr GetForegroundWindow() { return RobloxWindow.One; }
static long Meminfo(string[] lines, string key)
{
foreach (var l in lines)
if (l.StartsWith(key + ":", StringComparison.Ordinal))
{
var p = l.Substring(key.Length + 1).Trim().Split(' ');
long kb;
if (long.TryParse(p[0], out kb)) return kb * 1024;
}
return -1;
}
public static long BotMemory()
{
try { using (var p = Process.GetCurrentProcess()) return p.WorkingSet64; }
catch (Exception) { return -1; }
}
public static string Size(long bytes)
{
var ci = System.Globalization.CultureInfo.InvariantCulture;
double gb = bytes / (1024.0 * 1024 * 1024);
if (gb < 1) return (bytes >> 20).ToString(ci) + " MB";
return gb.ToString(gb >= 10 ? "0" : "0.0", ci) + " GB";
}
public static string MemoryText()
{
long own = BotMemory();
string bot = own >= 0 ? "the bot uses " + Size(own) : "the bot's own memory isn't known";
try
{
var m = File.ReadAllLines("/proc/meminfo");
return bot + "; this machine has " + Size(Meminfo(m, "MemAvailable")) + " of " + Size(Meminfo(m, "MemTotal")) + " free";
}
catch (Exception) { return bot; }
}
}
sealed class OutOfMemoryPause : Exception { public OutOfMemoryPause(Exception e) : base("the machine ran out of memory", e) { } }
sealed class WindowChangedException : Exception { public WindowChangedException(string m) : base(m) { } }
sealed class UserBusyException : Exception { public UserBusyException() : base("you started using the PC") { } }
static class Rfb
{
const int Port = 5901;
static readonly object gate = new object();
static TcpClient client;
static NetworkStream net;
public static volatile int X, Y;
static byte[] Need(int n)
{
var b = new byte[n];
for (int got = 0; got < n;)
{
int c = net.Read(b, got, n - got);
if (c <= 0) throw new IOException("the desktop closed the connection");
got += c;
}
return b;
}
static int BigEnd(byte[] b, int at, int n)
{
int v = 0;
for (int i = 0; i < n; i++) v = (v << 8) | b[at + i];
return v;
}
static void Connect()
{
client = new TcpClient("127.0.0.1", Port) { NoDelay = true };
client.ReceiveTimeout = 5000;
net = client.GetStream();
var version = Need(12);
net.Write(version, 0, 12);
var types = Need(Need(1)[0]);
if (Array.IndexOf(types, (byte)1) < 0) throw new IOException("the desktop wants a password: a wayvnc of your own on port " + Port + ", or ~/.config/wayvnc/config");
net.WriteByte(1);
if (BigEnd(Need(4), 0, 4) != 0) throw new IOException("the desktop refused the connection");
net.WriteByte(1);
Need(20);
Need(BigEnd(Need(4), 0, 4));
client.ReceiveTimeout = 0;
var drain = new Thread(Drain) { IsBackground = true, Name = "rfb" };
drain.Start(net);
}
static void Drain(object stream)
{
var b = new byte[4096];
try { while (((NetworkStream)stream).Read(b, 0, b.Length) > 0) { } }
catch (Exception) { }
}
static void Send(byte[] m)
{
lock (gate)
{
try
{
if (client == null) Connect();
net.Write(m, 0, m.Length);
}
catch (Exception e)
{
try { if (client != null) client.Close(); } catch (Exception) { }
client = null;
throw new WindowChangedException("the desktop's mouse didn't answer (" + e.Message + ")");
}
}
}
public static void Pointer(int x, int y, int mask)
{
X = x;
Y = y;
Send(new[] { (byte)5, (byte)mask, (byte)(x >> 8), (byte)x, (byte)(y >> 8), (byte)y });
}
public static void Key(uint sym, bool down)
{
Send(new[] { (byte)4, (byte)(down ? 1 : 0), (byte)0, (byte)0, (byte)(sym >> 24), (byte)(sym >> 16), (byte)(sym >> 8), (byte)sym });
}
}
sealed class RobloxWindow
{
public static readonly IntPtr One = new IntPtr(1);
public IntPtr Handle { get; private set; }
public int Width { get; private set; }
public int Height { get; private set; }
public IntPtr Pinned;
public bool NoFallback;
public static volatile IntPtr[] Taken = new IntPtr[0];
static int checkedAt = Environment.TickCount - 60000;
static bool alive;
static bool Alive()
{
if (unchecked(Environment.TickCount - checkedAt) < 2000) return alive;
checkedAt = Environment.TickCount;
bool found = false;
foreach (var d in Directory.GetDirectories("/proc"))
{
string pid = Path.GetFileName(d);
if (pid.Length == 0 || !char.IsDigit(pid[0])) continue;
try { if (File.ReadAllText(d + "/comm").Trim() == "sober") { found = true; break; } }
catch (Exception) { }
}
return alive = found;
}
public static List<IntPtr> All()
{
var all = new List<IntPtr>();
if (Alive()) all.Add(One);
return all;
}
public bool Find()
{
Handle = Alive() ? One : IntPtr.Zero;
if (Handle == IntPtr.Zero) return false;
if (Width == 0) using (var f = Grab()) { }
return true;
}
public bool Minimized { get { return false; } }
public static volatile bool ScreenFirst;
public bool ScreenOnly;
public static int EmptyPictures, ScreenCopies, Asked, HalfDrawn;
internal static bool TestWhite = false;
public static volatile string CoveredBy;
public Frame Capture()
{
if (Handle == IntPtr.Zero || !Alive()) throw new WindowChangedException("Roblox was closed");
return Grab();
}
Frame Grab()
{
byte[] ppm;
var start = new ProcessStartInfo("grim", "-t ppm -") { UseShellExecute = false, RedirectStandardOutput = true, CreateNoWindow = true };
using (var p = Process.Start(start))
using (var mem = new MemoryStream(7 * 1024 * 1024))
{
p.StandardOutput.BaseStream.CopyTo(mem);
p.WaitForExit();
if (mem.Length < 32) throw new WindowChangedException("the desktop gave no picture");
ppm = mem.ToArray();
}
int at = 0, w = Number(ppm, ref at, true), h = Number(ppm, ref at, false);
Number(ppm, ref at, false);
at++;
var bmp = new Bitmap(w, h, PixelFormat.Format32bppArgb);
var data = bmp.LockBits(new Rectangle(0, 0, w, h), ImageLockMode.WriteOnly, PixelFormat.Format32bppArgb);
var row = new byte[data.Stride];
for (int y = 0; y < h; y++)
{
for (int x = 0; x < w; x++, at += 3)
{
row[x * 4] = ppm[at + 2];
row[x * 4 + 1] = ppm[at + 1];
row[x * 4 + 2] = ppm[at];
row[x * 4 + 3] = 255;
}
Marshal.Copy(row, 0, data.Scan0 + y * data.Stride, data.Stride);
}
bmp.UnlockBits(data);
Width = w;
Height = h;
return new Frame(bmp, true);
}
static int Number(byte[] b, ref int at, bool first)
{
if (first) at = 2;
while (b[at] == 32 || b[at] == 10 || b[at] == 13 || b[at] == 9) at++;
int v = 0;
while (b[at] >= 48 && b[at] <= 57) v = v * 10 + b[at++] - 48;
return v;
}
static int otherAt = Environment.TickCount - 60000;
static string other;
static readonly System.Text.RegularExpressions.Regex WindowRx = new System.Text.RegularExpressions.Regex(
@"""name"":\s*""((?:[^""\\]|\\.)*)""(?:(?!""name"").)*?""pid"":\s*\d+", System.Text.RegularExpressions.RegexOptions.Singleline);
public static string OtherWindow()
{
if (unchecked(Environment.TickCount - otherAt) < 15000) return other;
otherAt = Environment.TickCount;
string found = null, blind = null;
try
{
var start = new ProcessStartInfo("swaymsg", "-t get_tree -r") { UseShellExecute = false, RedirectStandardOutput = true, RedirectStandardError = true, CreateNoWindow = true };
string sock = SwaySocket();
if (sock == null) blind = "the bot's desktop (sway) wasn't found";
else
{
start.EnvironmentVariables["SWAYSOCK"] = sock;
string tree;
using (var p = Process.Start(start))
{
tree = p.StandardOutput.ReadToEnd();
p.WaitForExit(5000);
}
if (tree.Trim().Length == 0) blind = "its desktop (sway) didn't answer";
var names = new List<string>();
foreach (System.Text.RegularExpressions.Match m in WindowRx.Matches(tree)) names.Add(m.Groups[1].Value);
if (names.Count > 1) found = names.FirstOrDefault(n => n != "Sober") ?? "a second Sober";
}
}
catch (Exception e) { blind = "swaymsg: " + e.Message; }
if (blind != null && saidBlind == null) { var note = Note; if (note != null) note("Roblox's own windows (its Security check) can't be seen: " + blind); }
saidBlind = blind;
return other = found;
}
static string saidBlind;
public static Action<string> Note;
static string SwaySocket()
{
string up = Path.GetDirectoryName(AppDomain.CurrentDomain.BaseDirectory.TrimEnd('/'));
if (string.IsNullOrEmpty(up)) return null;
try
{
string[] note = File.ReadAllText(Path.Combine(up, ".desktop")).Split((char[])null, StringSplitOptions.RemoveEmptyEntries);
if (note.Length >= 2 && OurSway(note[1], up)) return note[1];
}
catch (Exception) { }
string run = Environment.GetEnvironmentVariable("XDG_RUNTIME_DIR") ?? "";
if (run.Length == 0 || !Directory.Exists(run)) return null;
return Directory.GetFiles(run, "sway-ipc.*.sock").Where(f => OurSway(f, up)).OrderByDescending(f => File.GetLastWriteTimeUtc(f)).FirstOrDefault();
}
static bool OurSway(string sock, string up)
{
try
{
var m = System.Text.RegularExpressions.Regex.Match(Path.GetFileName(sock), @"^sway-ipc\.\d+\.(\d+)\.sock$");
if (!m.Success) return false;
string proc = "/proc/" + m.Groups[1].Value + "/";
if (File.ReadAllText(proc + "comm").Trim() != "sway") return false;
return File.ReadAllText(proc + "cmdline").Replace('\0', ' ').Contains(Path.Combine(up, "sway.conf"));
}
catch (Exception) { return false; }
}
public bool InSight() { return true; }
public static void NoteUser() { }
public bool UserActiveWithin(int ms) { return false; }
public bool UserInputSinceBot() { return false; }
public bool BeginAct() { return true; }
public bool Front() { return Handle != IntPtr.Zero; }
public void EndAct(bool giveFocusBack, bool park = true) { if (park) Park(); }
public bool IsForeground { get { return Handle != IntPtr.Zero; } }
public void GiveFocusBack(IntPtr w) { }
public string FrontWindow() { return "Roblox"; }
public const string PlaceId = "73897506680154";
public static void StartGame()
{
string script = Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "game.sh");
using (Process.Start(new ProcessStartInfo("/bin/sh", "\"" + script + "\" " + PlaceId + " again") { UseShellExecute = false })) { }
}
public DateTime LastInput { get; private set; }
public Point ParkAt = new Point(600, 170);
void Live()
{
if (Handle == IntPtr.Zero || !Alive()) throw new WindowChangedException("Roblox was closed");
}
void MoveTo(int x, int y)
{
Live();
x = Math.Max(0, Math.Min(Width - 1, x));
y = Math.Max(0, Math.Min(Height - 1, y));
Rfb.Pointer(Math.Max(0, x - 4), Math.Max(0, y - 4), 0);
Thread.Sleep(60);
Rfb.Pointer(x, y, 0);
Thread.Sleep(120);
}
int PastChat(int x, int y)
{
return x < 250 && y > 130 && y < 177 ? 178 : y;
}
public void Click(int x, int y)
{
y = PastChat(x, y);
MoveTo(x, y);
Rfb.Pointer(Rfb.X, Rfb.Y, 1);
Thread.Sleep(70);
Rfb.Pointer(Rfb.X, Rfb.Y, 0);
LastInput = DateTime.UtcNow;
Thread.Sleep(60);
Park();
}
public void SlowClick(int x, int y)
{
Live();
y = PastChat(x, y);
for (int i = 0; i <= 8; i++)
{
Rfb.Pointer(x + (8 - i) * 6, y, 0);
Thread.Sleep(25);
}
Thread.Sleep(150);
Rfb.Pointer(x, y, 1);
Thread.Sleep(120);
Rfb.Pointer(x, y, 0);
LastInput = DateTime.UtcNow;
Thread.Sleep(60);
Park();
}
static uint Sym(byte vk)
{
switch (vk)
{
case 0x08: return 0xff08;
case 0x0D: return 0xff0d;
case 0x1B: return 0xff1b;
case 0x20: return 0x20;
}
if (vk >= 0x70 && vk <= 0x7B) return 0xffbeu + (uint)(vk - 0x70);
return char.ToLowerInvariant((char)vk);
}
void Press(uint sym)
{
Rfb.Key(sym, true);
Thread.Sleep(30);
Rfb.Key(sym, false);
LastInput = DateTime.UtcNow;
Thread.Sleep(50);
}
public void TypeKeys(string keys)
{
Live();
foreach (char ch in keys ?? "")
{
byte vk = ch == (char)8 ? (byte)0x08 : ch == (char)13 ? (byte)0x0D : ch == ' ' ? (byte)0x20
: char.IsLetterOrDigit(ch) && ch < 128 ? (byte)char.ToUpperInvariant(ch) : (byte)0;
if (vk != 0) Press(Sym(vk));
}
}
public void PressKey(byte vk)
{
Live();
Press(Sym(vk));
Thread.Sleep(250);
}
public void Hover(int x, int y) { MoveTo(x, y); }
public void LeaveToPark()
{
Live();
int fx = Rfb.X, fy = Rfb.Y;
for (int i = 1; i <= 10; i++)
{
Rfb.Pointer(fx + (ParkAt.X - fx) * i / 10, fy + (ParkAt.Y - fy) * i / 10, 0);
Thread.Sleep(30);
}
}
public void SweepFromTop() { LeaveToPark(); }
public void Scroll(int x, int y, int notches)
{
MoveTo(x, y);
int mask = notches > 0 ? 8 : 16;
for (int i = 0; i < Math.Abs(notches); i++)
{
Rfb.Pointer(Rfb.X, Rfb.Y, mask);
Rfb.Pointer(Rfb.X, Rfb.Y, 0);
LastInput = DateTime.UtcNow;
Thread.Sleep(12);
}
Thread.Sleep(350);
Park();
}
public void Park()
{
if (Handle == IntPtr.Zero) return;
Rfb.Pointer(Math.Max(0, ParkAt.X - 2), ParkAt.Y, 0);
Thread.Sleep(10);
Rfb.Pointer(ParkAt.X, ParkAt.Y, 0);
}
}
}