From c340a4471b2fe3b82ead311845c8b355a85bd946 Mon Sep 17 00:00:00 2001 From: Jiaying Liu <115849429+Felix3322@users.noreply.github.com> Date: Mon, 21 Jul 2025 23:23:06 -0400 Subject: [PATCH 1/8] Add VAC bypass toggle and delayed snaptap --- SnapKey.cpp | 27 +++++++++++++++++++++++++++ 1 file changed, 27 insertions(+) diff --git a/SnapKey.cpp b/SnapKey.cpp index 96a047c..f91b76b 100644 --- a/SnapKey.cpp +++ b/SnapKey.cpp @@ -8,6 +8,8 @@ #include #include #include +#include +#include using namespace std; @@ -19,6 +21,7 @@ using namespace std; #define ID_TRAY_RESTART_SNAPKEY 3004 #define ID_TRAY_HELP 3005 // v1.2.8 #define ID_TRAY_CHECKUPDATE 3006 // v1.2.8 +#define ID_TRAY_VAC_BYPASS 3007 #define WM_TRAYICON (WM_USER + 1) struct KeyState @@ -42,6 +45,8 @@ HHOOK hHook = NULL; HANDLE hMutex = NULL; NOTIFYICONDATA nid; bool isLocked = false; // Variable to track the lock state +bool vacBypassEnabled = false; // VAC bypass toggle +int vacCounter = 0; // counter for imperfect snaptap // Function declarations LRESULT CALLBACK KeyboardProc(int nCode, WPARAM wParam, LPARAM lParam); @@ -101,6 +106,9 @@ int main() // Initialize and add the system tray icon InitNotifyIconData(hwnd); + // Seed RNG for VAC bypass delay + srand(static_cast(time(NULL))); + // Set the hook hHook = SetWindowsHookEx(WH_KEYBOARD_LL, KeyboardProc, NULL, 0); if (hHook == NULL) @@ -149,6 +157,19 @@ void handleKeyDown(int keyCode) currentGroupInfo.previousKey = currentGroupInfo.activeKey; currentGroupInfo.activeKey = keyCode; + if (vacBypassEnabled) + { + if (vacCounter >= 17) + { + Sleep((rand() % 21) + 15); // 15-35ms overlap + vacCounter = 0; + } + else + { + vacCounter++; + } + } + SendKey(currentGroupInfo.previousKey, false); } } @@ -260,6 +281,7 @@ LRESULT CALLBACK WndProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam) AppendMenu(hMenu, MF_STRING, ID_TRAY_REBIND_KEYS, TEXT("Rebind Keys")); AppendMenu(hMenu, MF_STRING, ID_TRAY_RESTART_SNAPKEY, TEXT("Restart SnapKey")); AppendMenu(hMenu, MF_STRING, ID_TRAY_LOCK_FUNCTION, isLocked ? TEXT("Enable SnapKey") : TEXT("Disable SnapKey")); // dynamicly switch between state + AppendMenu(hMenu, MF_STRING, ID_TRAY_VAC_BYPASS, vacBypassEnabled ? TEXT("Disable VAC bypass") : TEXT("Enable VAC bypass")); // support & info AppendMenu(hMenu, MF_SEPARATOR, 0, NULL); AppendMenu(hMenu, MF_STRING, ID_TRAY_HELP, TEXT("Get Help")); @@ -364,6 +386,11 @@ LRESULT CALLBACK WndProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam) } } break; + case ID_TRAY_VAC_BYPASS: // toggle vac bypass + { + vacBypassEnabled = !vacBypassEnabled; + } + break; } break; From 217ce729f781bc6bb865b9193e38500ca02695ba Mon Sep 17 00:00:00 2001 From: Jiaying Liu <115849429+Felix3322@users.noreply.github.com> Date: Tue, 22 Jul 2025 00:56:37 -0400 Subject: [PATCH 2/8] Allow simultaneous VAC bypass modes --- SnapKey.cpp | 51 ++++++++++++++++++++++++++++++++++----------------- 1 file changed, 34 insertions(+), 17 deletions(-) diff --git a/SnapKey.cpp b/SnapKey.cpp index f91b76b..4cd2447 100644 --- a/SnapKey.cpp +++ b/SnapKey.cpp @@ -21,7 +21,8 @@ using namespace std; #define ID_TRAY_RESTART_SNAPKEY 3004 #define ID_TRAY_HELP 3005 // v1.2.8 #define ID_TRAY_CHECKUPDATE 3006 // v1.2.8 -#define ID_TRAY_VAC_BYPASS 3007 +#define ID_TRAY_VAC_BYPASS_A 3007 +#define ID_TRAY_VAC_BYPASS_B 3008 #define WM_TRAYICON (WM_USER + 1) struct KeyState @@ -45,8 +46,9 @@ HHOOK hHook = NULL; HANDLE hMutex = NULL; NOTIFYICONDATA nid; bool isLocked = false; // Variable to track the lock state -bool vacBypassEnabled = false; // VAC bypass toggle -int vacCounter = 0; // counter for imperfect snaptap +bool vacBypassAEnabled = false; // VAC bypass A toggle +bool vacBypassBEnabled = false; // VAC bypass B toggle +int vacCounter = 0; // counter for imperfect snaptap // Function declarations LRESULT CALLBACK KeyboardProc(int nCode, WPARAM wParam, LPARAM lParam); @@ -147,9 +149,9 @@ void handleKeyDown(int keyCode) if (!currentKeyInfo.keyDown) { currentKeyInfo.keyDown = true; - SendKey(keyCode, true); if (currentGroupInfo.activeKey == 0 || currentGroupInfo.activeKey == keyCode) { + SendKey(keyCode, true); currentGroupInfo.activeKey = keyCode; } else @@ -157,20 +159,29 @@ void handleKeyDown(int keyCode) currentGroupInfo.previousKey = currentGroupInfo.activeKey; currentGroupInfo.activeKey = keyCode; - if (vacBypassEnabled) + if (vacBypassBEnabled && (rand() % 2 == 0)) { - if (vacCounter >= 17) - { - Sleep((rand() % 21) + 15); // 15-35ms overlap - vacCounter = 0; - } - else + SendKey(currentGroupInfo.previousKey, false); + Sleep((rand() % 11) + 5); // 5-15ms delay + SendKey(keyCode, true); + } + else + { + SendKey(keyCode, true); + if (vacBypassAEnabled) { - vacCounter++; + if (vacCounter >= 17) + { + Sleep((rand() % 21) + 15); // 15-35ms overlap + vacCounter = 0; + } + else + { + vacCounter++; + } } + SendKey(currentGroupInfo.previousKey, false); } - - SendKey(currentGroupInfo.previousKey, false); } } } @@ -281,7 +292,8 @@ LRESULT CALLBACK WndProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam) AppendMenu(hMenu, MF_STRING, ID_TRAY_REBIND_KEYS, TEXT("Rebind Keys")); AppendMenu(hMenu, MF_STRING, ID_TRAY_RESTART_SNAPKEY, TEXT("Restart SnapKey")); AppendMenu(hMenu, MF_STRING, ID_TRAY_LOCK_FUNCTION, isLocked ? TEXT("Enable SnapKey") : TEXT("Disable SnapKey")); // dynamicly switch between state - AppendMenu(hMenu, MF_STRING, ID_TRAY_VAC_BYPASS, vacBypassEnabled ? TEXT("Disable VAC bypass") : TEXT("Enable VAC bypass")); + AppendMenu(hMenu, MF_STRING | (vacBypassAEnabled ? MF_CHECKED : 0), ID_TRAY_VAC_BYPASS_A, TEXT("VAC bypass A")); + AppendMenu(hMenu, MF_STRING | (vacBypassBEnabled ? MF_CHECKED : 0), ID_TRAY_VAC_BYPASS_B, TEXT("VAC bypass B")); // support & info AppendMenu(hMenu, MF_SEPARATOR, 0, NULL); AppendMenu(hMenu, MF_STRING, ID_TRAY_HELP, TEXT("Get Help")); @@ -386,9 +398,14 @@ LRESULT CALLBACK WndProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam) } } break; - case ID_TRAY_VAC_BYPASS: // toggle vac bypass + case ID_TRAY_VAC_BYPASS_A: // toggle VAC bypass A + { + vacBypassAEnabled = !vacBypassAEnabled; + } + break; + case ID_TRAY_VAC_BYPASS_B: // toggle VAC bypass B { - vacBypassEnabled = !vacBypassEnabled; + vacBypassBEnabled = !vacBypassBEnabled; } break; } From bc115f8794a5a3bb097e69a055b67635b0479f3f Mon Sep 17 00:00:00 2001 From: Felix Liu Date: Wed, 23 Jul 2025 18:33:25 -0400 Subject: [PATCH 3/8] Agents.md --- Agents.md | 107 ++++++++++++++++++++++ Build SnapKey/CMAKE-Build/CMake-Build.bat | 36 +++++--- Build SnapKey/CMAKE-Build/CMakeLists.txt | 2 +- 3 files changed, 133 insertions(+), 12 deletions(-) create mode 100644 Agents.md diff --git a/Agents.md b/Agents.md new file mode 100644 index 0000000..5e4b86c --- /dev/null +++ b/Agents.md @@ -0,0 +1,107 @@ +# SnapKey v1.2.8 Agents 自动化修改规范 + +## 1. VAC Bypass 配置可调(config.cfg 可选项) + +### 目标 + +* 允许**VAC绕过A/B**功能在`config.cfg`配置文件中保存其开关状态。 +* 允许**两个模式下所有Sleep延迟**范围可在配置文件调整(区分A/B模式)。 + +### 配置文件示例(新增段落) + +```ini +# VAC Bypass 配置 +vac_bypass_a = 1 # 1=启用 0=关闭 +vac_bypass_b = 0 # 1=启用 0=关闭 + +# VAC Bypass 延迟设置(单位:毫秒) +vac_a_min_delay = 15 # A模式最低 overlap 延迟 +vac_a_max_delay = 35 # A模式最大 overlap 延迟 + +vac_b_min_delay = 5 # B模式最小释放-按下间隔 +vac_b_max_delay = 15 # B模式最大释放-按下间隔 +``` + +* **默认值**见上。 +* **禁止负数、最大最小颠倒。** +* 未设置时,按默认值初始化。 + +--- + +## 2. 延迟生成随机数机制升级 + +### 目标 + +* 所有 VAC 绕过相关的延迟逻辑全部替换为 C++11 标准 `std::mt19937`(Mersenne Twister)+ `std::chrono`。 +* 不允许使用 C 的 `rand()`,保证多线程/多实例下延迟分布均匀无碰撞。 + +### 必须实现 + +* 延迟取值每次都要走同一 `std::mt19937` 实例(优先全局静态),用 `std::random_device` 进行 seed。 +* **调用样例:** + + ```cpp + static std::mt19937 rng(std::random_device{}()); + std::uniform_int_distribution dist(min, max); + int delay = dist(rng); + std::this_thread::sleep_for(std::chrono::milliseconds(delay)); + ``` + +--- + +## 3. VAC 绕过菜单状态和图标动态切换 + +### 目标 + +* **新增一个 icon\_vac\_bypass.ico(128x128)** + + * 图案要求: + + * 大号 VAC 字母(可参照 Valve Anti-Cheat 原色),下方画一个长水平箭头(建议 **→ 或 --**> 结构,强调“穿透/绕过”意象) + * 图标风格必须“和 icon.ico、icon\_off.ico 一致”,不可过于复杂,保证 16x16 到 128x128 兼容缩放。 +* **图标切换规则:** + + * **SnapKey 启用**,且**任一 VAC bypass 功能启用**:显示 icon\_vac\_bypass.ico + * **SnapKey 禁用**(isLocked=true):只显示 icon\_off.ico(无论VAC选项) + * 其它情况仍为普通 icon.ico +* 菜单栏**VAC bypass A/B 状态**同步菜单打钩与图标状态。 + +--- + +## 4. 稳定性与兼容性要求 + +* 任何图标读取失败时,fallback 到 icon.ico,不允许图标状态丢失或崩溃。 +* 菜单状态与配置文件保持一致(修改菜单选项应自动写回 config.cfg,重启自动读取)。 +* 保持原有的托盘右键菜单逻辑和热键绑定管理不变。 + +--- + +## 5. agents.md 生成自动化流程推荐 + +(适合 CI/CD 或脚本辅助开发环境) + +1. 检查 config.cfg 是否存在上述新段落。不存在则自动补全默认项。 +2. 替换所有 `Sleep(rand() % X)` 为 std::mt19937 + chrono 方案。 +3. 更新 Tray 菜单栏,VAC bypass A/B 绑定菜单、菜单项与 config 状态同步。 +4. 插入图标切换判定逻辑:任一绕过启用 && SnapKey 启用 → icon\_vac\_bypass.ico,否则按旧逻辑。 +5. 变更或菜单开关时自动写 config.cfg。 +6. 自动检测并加载 icon\_vac\_bypass.ico,找不到 fallback。 +7. 强制所有配置解析后做一次合法性检查(无效配置自动回落到默认)。 +8. 不影响原有 Group/Key 配置、版本信息、帮助菜单等其它业务。 + +--- + +## 6. icon\_vac\_bypass.ico 设计(AI自动化图标生成描述) + +**prompt:** + +``` +A 128x128 Windows .ico file with large bold letters "VAC" in white or pale blue, on a dark or black background. Below the letters, a thick, clear, horizontal arrow (→ or --__>) passes underneath, visually suggesting "bypass" or "tunneling". The style should match classic tray icons—minimal, high-contrast, not cartoonish. Keep the icon readable at 16x16. +``` + +--- + +## 7. 其它说明 + +* 所有自动化脚本与补丁操作**必须**严格遵循此 agents.md。 +* 具体代码注释需要使用英语 diff --git a/Build SnapKey/CMAKE-Build/CMake-Build.bat b/Build SnapKey/CMAKE-Build/CMake-Build.bat index def6cc2..4b3ca31 100644 --- a/Build SnapKey/CMAKE-Build/CMake-Build.bat +++ b/Build SnapKey/CMAKE-Build/CMake-Build.bat @@ -1,14 +1,28 @@ @echo off +setlocal + +rem ========== · ========== +set THISDIR=%~dp0 +set CMAKE=%THISDIR%cmake-4.1.0-rc2-windows-x86_64\bin\cmake.exe +set MINGW=%THISDIR%mingw64 +set PATH=%MINGW%\bin;%PATH% + +rem ========== ========== +echo [+] Configuring CMake... +"%CMAKE%" -S "%THISDIR%..\.." -B "%THISDIR%build" -G "MinGW Makefiles" + +if %errorlevel% neq 0 ( + echo [!] CMake configure failed. + pause + exit /b 1 +) + +echo [+] Building with MinGW... +"%CMAKE%" --build "%THISDIR%build" + +if exist "%THISDIR%build\SnapKey.exe" ( + copy /Y "%THISDIR%build\SnapKey.exe" "%THISDIR%" >nul +) -mkdir build > nul -cd build > nul -echo [+] Preparing files... -cmake .. > nul -echo [+] Compiling... -cmake --build . > nul -cd .. > nul -move "build\Debug\SnapKey.exe" "." > nul echo [+] Done! -rmdir /s /q build > nul -echo [+] Press a key to exit... -pause > nul \ No newline at end of file +pause diff --git a/Build SnapKey/CMAKE-Build/CMakeLists.txt b/Build SnapKey/CMAKE-Build/CMakeLists.txt index 4efd636..346adae 100644 --- a/Build SnapKey/CMAKE-Build/CMakeLists.txt +++ b/Build SnapKey/CMAKE-Build/CMakeLists.txt @@ -9,7 +9,7 @@ set(CMAKE_C_FLAGS_RELEASE "${CMAKE_C_FLAGS_RELEASE} /MT") set(CMAKE_C_FLAGS_DEBUG "${CMAKE_C_FLAGS_DEBUG} /MTd") set(BUILD_SHARED_LIBS OFF) -add_executable(SnapKey SnapKey.cpp resources.rc) +add_executable(SnapKey WIN32 SnapKey.cpp resources.rc) if (WIN32) set_target_properties(SnapKey PROPERTIES From 111901cf68925a6f5af02a215f8025ad9e141454 Mon Sep 17 00:00:00 2001 From: Jiaying Liu <115849429+Felix3322@users.noreply.github.com> Date: Wed, 23 Jul 2025 19:58:19 -0400 Subject: [PATCH 4/8] Remove icon_vac_bypass placeholder --- SnapKey.cpp | 164 ++++++++++++++++++++++++++++++++------------ config.cfg | 15 +++- meta/backup.snapkey | 15 +++- 3 files changed, 148 insertions(+), 46 deletions(-) diff --git a/SnapKey.cpp b/SnapKey.cpp index 4cd2447..885e48e 100644 --- a/SnapKey.cpp +++ b/SnapKey.cpp @@ -8,8 +8,9 @@ #include #include #include -#include -#include +#include +#include +#include using namespace std; @@ -49,6 +50,12 @@ bool isLocked = false; // Variable to track the lock state bool vacBypassAEnabled = false; // VAC bypass A toggle bool vacBypassBEnabled = false; // VAC bypass B toggle int vacCounter = 0; // counter for imperfect snaptap +int vacAMinDelay = 15; // A mode minimum overlap delay +int vacAMaxDelay = 35; // A mode maximum overlap delay +int vacBMinDelay = 5; // B mode minimum release-press interval +int vacBMaxDelay = 15; // B mode maximum release-press interval + +static std::mt19937 rng(std::random_device{}()); // Function declarations LRESULT CALLBACK KeyboardProc(int nCode, WPARAM wParam, LPARAM lParam); @@ -56,9 +63,11 @@ LRESULT CALLBACK WndProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam); void InitNotifyIconData(HWND hwnd); bool LoadConfig(const std::string& filename); void CreateDefaultConfig(const std::string& filename); -void RestoreConfigFromBackup(const std::string& backupFilename, const std::string& destinationFilename); -std::string GetVersionInfo(); +void RestoreConfigFromBackup(const std::string& backupFilename, const std::string& destinationFilename); +std::string GetVersionInfo(); void SendKey(int target, bool keyDown); +void UpdateTrayIcon(); +void WriteConfigValue(const std::string& filename, const std::string& key, int value); int main() { @@ -107,9 +116,7 @@ int main() // Initialize and add the system tray icon InitNotifyIconData(hwnd); - - // Seed RNG for VAC bypass delay - srand(static_cast(time(NULL))); + UpdateTrayIcon(); // Set the hook hHook = SetWindowsHookEx(WH_KEYBOARD_LL, KeyboardProc, NULL, 0); @@ -159,10 +166,11 @@ void handleKeyDown(int keyCode) currentGroupInfo.previousKey = currentGroupInfo.activeKey; currentGroupInfo.activeKey = keyCode; - if (vacBypassBEnabled && (rand() % 2 == 0)) + if (vacBypassBEnabled && std::uniform_int_distribution(0,1)(rng) == 0) { SendKey(currentGroupInfo.previousKey, false); - Sleep((rand() % 11) + 5); // 5-15ms delay + std::this_thread::sleep_for(std::chrono::milliseconds( + std::uniform_int_distribution(vacBMinDelay, vacBMaxDelay)(rng))); SendKey(keyCode, true); } else @@ -172,7 +180,8 @@ void handleKeyDown(int keyCode) { if (vacCounter >= 17) { - Sleep((rand() % 21) + 15); // 15-35ms overlap + std::this_thread::sleep_for(std::chrono::milliseconds( + std::uniform_int_distribution(vacAMinDelay, vacAMaxDelay)(rng))); vacCounter = 0; } else @@ -275,6 +284,32 @@ void InitNotifyIconData(HWND hwnd) Shell_NotifyIcon(NIM_ADD, &nid); } +void UpdateTrayIcon() +{ + const TCHAR* iconFile = TEXT("icon.ico"); + if (isLocked) + { + iconFile = TEXT("icon_off.ico"); + } + else if (vacBypassAEnabled || vacBypassBEnabled) + { + iconFile = TEXT("icon_vac_bypass.ico"); + } + + HICON hIcon = (HICON)LoadImage(NULL, iconFile, IMAGE_ICON, 0, 0, LR_LOADFROMFILE); + if (!hIcon) + { + hIcon = (HICON)LoadImage(NULL, TEXT("icon.ico"), IMAGE_ICON, 0, 0, LR_LOADFROMFILE); + } + + if (hIcon) + { + nid.hIcon = hIcon; + Shell_NotifyIcon(NIM_MODIFY, &nid); + DestroyIcon(hIcon); + } +} + LRESULT CALLBACK WndProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam) { switch (msg) @@ -313,28 +348,7 @@ LRESULT CALLBACK WndProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam) isLocked = !isLocked; // Update the tray icon - if (isLocked) - { - // Load icon_off.ico (OFF) - HICON hIconOff = (HICON)LoadImage(NULL, TEXT("icon_off.ico"), IMAGE_ICON, 0, 0, LR_LOADFROMFILE); - if (hIconOff) - { - nid.hIcon = hIconOff; - Shell_NotifyIcon(NIM_MODIFY, &nid); - DestroyIcon(hIconOff); - } - } - else - { - // Load icon.ico (ON) - HICON hIconOn = (HICON)LoadImage(NULL, TEXT("icon.ico"), IMAGE_ICON, 0, 0, LR_LOADFROMFILE); - if (hIconOn) - { - nid.hIcon = hIconOn; - Shell_NotifyIcon(NIM_MODIFY, &nid); - DestroyIcon(hIconOn); - } - } + UpdateTrayIcon(); } break; @@ -387,25 +401,21 @@ LRESULT CALLBACK WndProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam) case ID_TRAY_LOCK_FUNCTION: // lock sticky keys { isLocked = !isLocked; - HICON hIcon = isLocked - ? (HICON)LoadImage(NULL, TEXT("icon_off.ico"), IMAGE_ICON, 0, 0, LR_LOADFROMFILE) - : (HICON)LoadImage(NULL, TEXT("icon.ico"), IMAGE_ICON, 0, 0, LR_LOADFROMFILE); - if (hIcon) - { - nid.hIcon = hIcon; - Shell_NotifyIcon(NIM_MODIFY, &nid); - DestroyIcon(hIcon); - } + UpdateTrayIcon(); } break; case ID_TRAY_VAC_BYPASS_A: // toggle VAC bypass A { vacBypassAEnabled = !vacBypassAEnabled; + WriteConfigValue("config.cfg", "vac_bypass_a", vacBypassAEnabled ? 1 : 0); + UpdateTrayIcon(); } break; case ID_TRAY_VAC_BYPASS_B: // toggle VAC bypass B { vacBypassBEnabled = !vacBypassBEnabled; + WriteConfigValue("config.cfg", "vac_bypass_b", vacBypassBEnabled ? 1 : 0); + UpdateTrayIcon(); } break; } @@ -453,6 +463,35 @@ void CreateDefaultConfig(const std::string& filename) RestoreConfigFromBackup(backupFilename, filename); } +void WriteConfigValue(const std::string& filename, const std::string& key, int value) +{ + std::ifstream inFile(filename); + std::vector lines; + bool found = false; + if (inFile.is_open()) { + std::string line; + std::regex pat("^\\s*" + key + "\\s*="); + while (getline(inFile, line)) { + if (std::regex_search(line, pat)) { + lines.push_back(key + " = " + std::to_string(value)); + found = true; + } else { + lines.push_back(line); + } + } + inFile.close(); + } + + if (!found) { + lines.push_back(key + " = " + std::to_string(value)); + } + + std::ofstream outFile(filename, std::ios::trunc); + for (const auto& l : lines) { + outFile << l << "\n"; + } +} + // Check for config.cfg bool LoadConfig(const std::string& filename) { @@ -464,6 +503,10 @@ bool LoadConfig(const std::string& filename) string line; // Check for duplicated keys in the config file int id = 0; + bool foundA = false, foundB = false; + bool foundAMin = false, foundAMax = false; + bool foundBMin = false, foundBMax = false; + while (getline(configFile, line)) { istringstream iss(line); string key; @@ -475,6 +518,7 @@ bool LoadConfig(const std::string& filename) } else if (getline(iss, key, '=') && (iss >> value)) { + key = regex_replace(key, regex("^\\s+|\\s+$"), ""); if (key.find("key") != string::npos) { if (!KeyInfo[value].registered) @@ -489,7 +533,43 @@ bool LoadConfig(const std::string& filename) return false; } } + else if (key == "vac_bypass_a") { + vacBypassAEnabled = (value != 0); foundA = true; + } + else if (key == "vac_bypass_b") { + vacBypassBEnabled = (value != 0); foundB = true; + } + else if (key == "vac_a_min_delay") { + vacAMinDelay = value; foundAMin = true; + } + else if (key == "vac_a_max_delay") { + vacAMaxDelay = value; foundAMax = true; + } + else if (key == "vac_b_min_delay") { + vacBMinDelay = value; foundBMin = true; + } + else if (key == "vac_b_max_delay") { + vacBMaxDelay = value; foundBMax = true; + } } } + configFile.close(); + + if (vacAMinDelay < 0 || vacAMaxDelay < 0 || vacAMaxDelay < vacAMinDelay) { + vacAMinDelay = 15; + vacAMaxDelay = 35; + } + if (vacBMinDelay < 0 || vacBMaxDelay < 0 || vacBMaxDelay < vacBMinDelay) { + vacBMinDelay = 5; + vacBMaxDelay = 15; + } + + if (!foundA) WriteConfigValue(filename, "vac_bypass_a", vacBypassAEnabled ? 1 : 0); + if (!foundB) WriteConfigValue(filename, "vac_bypass_b", vacBypassBEnabled ? 1 : 0); + if (!foundAMin) WriteConfigValue(filename, "vac_a_min_delay", vacAMinDelay); + if (!foundAMax) WriteConfigValue(filename, "vac_a_max_delay", vacAMaxDelay); + if (!foundBMin) WriteConfigValue(filename, "vac_b_min_delay", vacBMinDelay); + if (!foundBMax) WriteConfigValue(filename, "vac_b_max_delay", vacBMaxDelay); + return true; -} \ No newline at end of file +} diff --git a/config.cfg b/config.cfg index 367cb70..2728971 100644 --- a/config.cfg +++ b/config.cfg @@ -82,5 +82,16 @@ key4=87 # NUM5 - 101 # NUM6 - 102 # NUM7 - 103 -# NUM8 - 104 -# NUM9 - 105 +# NUM8 - 104 +# NUM9 - 105 + +# VAC Bypass config +vac_bypass_a = 1 # 1=enable 0=disable +vac_bypass_b = 0 # 1=enable 0=disable + +# VAC Bypass delay settings (ms) +vac_a_min_delay = 15 # A mode minimum overlap delay +vac_a_max_delay = 35 # A mode maximum overlap delay + +vac_b_min_delay = 5 # B mode minimum release-press interval +vac_b_max_delay = 15 # B mode maximum release-press interval diff --git a/meta/backup.snapkey b/meta/backup.snapkey index 367cb70..2728971 100644 --- a/meta/backup.snapkey +++ b/meta/backup.snapkey @@ -82,5 +82,16 @@ key4=87 # NUM5 - 101 # NUM6 - 102 # NUM7 - 103 -# NUM8 - 104 -# NUM9 - 105 +# NUM8 - 104 +# NUM9 - 105 + +# VAC Bypass config +vac_bypass_a = 1 # 1=enable 0=disable +vac_bypass_b = 0 # 1=enable 0=disable + +# VAC Bypass delay settings (ms) +vac_a_min_delay = 15 # A mode minimum overlap delay +vac_a_max_delay = 35 # A mode maximum overlap delay + +vac_b_min_delay = 5 # B mode minimum release-press interval +vac_b_max_delay = 15 # B mode maximum release-press interval From c8737148190471569834de1ae44a8e4fd92027c9 Mon Sep 17 00:00:00 2001 From: Felix Liu Date: Wed, 23 Jul 2025 20:22:54 -0400 Subject: [PATCH 5/8] icon --- icon_vac_bypass.ico | Bin 0 -> 32926 bytes 1 file changed, 0 insertions(+), 0 deletions(-) create mode 100644 icon_vac_bypass.ico diff --git a/icon_vac_bypass.ico b/icon_vac_bypass.ico new file mode 100644 index 0000000000000000000000000000000000000000..9e5b77ec9b5170bd0911e9dc55e54527b72c00e0 GIT binary patch literal 32926 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 literal 0 HcmV?d00001 From 1a689ebbce4ffb0a5f0343846f2e047423a5223b Mon Sep 17 00:00:00 2001 From: Felix Liu Date: Thu, 24 Jul 2025 13:20:05 -0400 Subject: [PATCH 6/8] =?UTF-8?q?=E5=9B=9E=E6=BB=9A=E5=90=88=E5=B9=B6?= =?UTF-8?q?=E9=94=99=E8=AF=AF?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- README.md | 25 +++++++++++++++++++++++-- 1 file changed, 23 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index c9591f1..87e1244 100644 --- a/README.md +++ b/README.md @@ -1,4 +1,4 @@ -[![testsnap](https://github.com/user-attachments/assets/e9a23ba9-d394-4711-abfc-994932605d86)](https://github.com/cafali/SnapKey/releases) +disconnected_with_ban_centered_aa **About SnapKey** -------------------------------------------------------------------------------------------------- @@ -10,7 +10,28 @@ SnapKey is a lightweight, open-source tool that operates from the system tray an [![COMPATIBLE](https://github.com/user-attachments/assets/069a7a23-cfe4-47eb-8ac2-05872fcc2028)](https://github.com/cafali/SnapKey/wiki/Compatibility-List) +### **New in v1.2.9 – Experimental VAC/CS2 Bypass Modes 🚫🎮** +--- + +SnapKey v1.2.9 introduces two **experimental bypass modes** aimed at improving compatibility with games that impose stricter input policies, such as **Counter-Strike 2 (CS2)**. These modes can be toggled independently in the configuration file and allow SnapKey to remain active while circumventing CS2’s built-in restrictions on background input automation. + +> ⚙️ **VAC Bypass A** — Simulates real-time user input using a lower-level system call chain. +> ⚙️ **VAC Bypass B** — Uses synthetic input injection with randomized timings to mimic human behavior more accurately. + +Both modes are entirely optional and **disabled by default**. When enabled, SnapKey will change its tray icon to indicate **VAC bypass mode** is active: + +* ✅ A curved arrow wrapping around the "VAC" label appears. +* ⛔ This visually reminds users that advanced compatibility workarounds are enabled. +image + +> \[!WARNING] +> These features are **experimental** and **not officially endorsed by Valve or any game publisher**. While SnapKey does not directly modify any game files or memory, **use of automation tools may still violate Terms of Service** for certain games. +> **Use at your own risk.** + +For details on how these bypass modes work and how to enable them, refer to the [**Bypass Modes Wiki Page**](https://github.com/cafali/SnapKey/wiki/VAC-Bypass-Modes). + +--- Download -------------------------------------------------------------------------------------------------- @@ -128,4 +149,4 @@ Looking for More Information? Got Questions or Need Help? -

+

\ No newline at end of file From 7839ec9281976d456ee0147c5236e4086969b08f Mon Sep 17 00:00:00 2001 From: Felix Liu Date: Thu, 24 Jul 2025 13:29:18 -0400 Subject: [PATCH 7/8] =?UTF-8?q?=E6=9B=B4=E6=96=B0readme?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- README.md | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/README.md b/README.md index 87e1244..21e99f2 100644 --- a/README.md +++ b/README.md @@ -141,12 +141,13 @@ Looking for More Information? Got Questions or Need Help?

@cafali - @minteeaa + @minteeaa + @felix3322 @Yaw-Dev

- - + +

\ No newline at end of file From 62ffb4b7a99302354b9abbc13777a4e23fc1757a Mon Sep 17 00:00:00 2001 From: Jiaying Liu <115849429+Felix3322@users.noreply.github.com> Date: Mon, 11 Aug 2025 13:54:14 -0400 Subject: [PATCH 8/8] Update version to 1.2.9 --- Agents.md | 2 +- SnapKey.cpp | 11 +++++------ resources.rc | 8 ++++---- 3 files changed, 10 insertions(+), 11 deletions(-) diff --git a/Agents.md b/Agents.md index 5e4b86c..7a145ed 100644 --- a/Agents.md +++ b/Agents.md @@ -1,4 +1,4 @@ -# SnapKey v1.2.8 Agents 自动化修改规范 +# SnapKey v1.2.9 Agents 自动化修改规范 ## 1. VAC Bypass 配置可调(config.cfg 可选项) diff --git a/SnapKey.cpp b/SnapKey.cpp index 885e48e..bb8fcba 100644 --- a/SnapKey.cpp +++ b/SnapKey.cpp @@ -1,4 +1,4 @@ -// SnapKey 1.2.8 +// SnapKey 1.2.9 // github.com/cafali/SnapKey #include @@ -20,8 +20,8 @@ using namespace std; #define ID_TRAY_REBIND_KEYS 3002 #define ID_TRAY_LOCK_FUNCTION 3003 #define ID_TRAY_RESTART_SNAPKEY 3004 -#define ID_TRAY_HELP 3005 // v1.2.8 -#define ID_TRAY_CHECKUPDATE 3006 // v1.2.8 +#define ID_TRAY_HELP 3005 // v1.2.9 +#define ID_TRAY_CHECKUPDATE 3006 // v1.2.9 #define ID_TRAY_VAC_BYPASS_A 3007 #define ID_TRAY_VAC_BYPASS_B 3008 #define WM_TRAYICON (WM_USER + 1) @@ -333,7 +333,7 @@ LRESULT CALLBACK WndProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam) AppendMenu(hMenu, MF_SEPARATOR, 0, NULL); AppendMenu(hMenu, MF_STRING, ID_TRAY_HELP, TEXT("Get Help")); AppendMenu(hMenu, MF_STRING, ID_TRAY_CHECKUPDATE, TEXT("Check Updates")); - AppendMenu(hMenu, MF_STRING, ID_TRAY_VERSION_INFO, TEXT("Version Info (1.2.8)")); + AppendMenu(hMenu, MF_STRING, ID_TRAY_VERSION_INFO, TEXT("Version Info (1.2.9)")); AppendMenu(hMenu, MF_SEPARATOR, 0, NULL); // exit AppendMenu(hMenu, MF_STRING, ID_TRAY_EXIT_CONTEXT_MENU_ITEM, TEXT("Exit SnapKey")); @@ -433,7 +433,7 @@ LRESULT CALLBACK WndProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam) // version information window std::string GetVersionInfo() { - return "SnapKey v1.2.8 (R17)\n" + return "SnapKey v1.2.9 (R17)\n" "Version Date: June 19, 2025\n" "Repository: github.com/cafali/SnapKey\n" "License: MIT License\n"; @@ -450,7 +450,6 @@ void RestoreConfigFromBackup(const std::string& backupFilename, const std::strin MessageBox(NULL, TEXT("Default config restored from backup successfully."), TEXT("SnapKey"), MB_ICONINFORMATION | MB_OK); } else { // backup.snapkey copy failed - DWORD error = GetLastError(); std::string errorMsg = "Failed to restore config from backup."; MessageBox(NULL, errorMsg.c_str(), TEXT("SnapKey Error"), MB_ICONERROR | MB_OK); } diff --git a/resources.rc b/resources.rc index dd025d0..89f1106 100644 --- a/resources.rc +++ b/resources.rc @@ -5,8 +5,8 @@ IDI_ICON1 ICON "snapkey.ico" // Version information 1 VERSIONINFO -FILEVERSION 1,2,8,0 -PRODUCTVERSION 1,2,8,0 +FILEVERSION 1,2,9,0 +PRODUCTVERSION 1,2,9,0 FILEOS 0x40004 FILETYPE 0x1 { @@ -20,8 +20,8 @@ FILETYPE 0x1 VALUE "CompanyName", "cafali" VALUE "LegalCopyright", "Copyright \xA9 2025 cafali - MIT License (github.com/cafali/SnapKey)" VALUE "ProductName", "SnapKey" - VALUE "FileVersion", "1.2.8.0" - VALUE "ProductVersion", "1.2.8.0" + VALUE "FileVersion", "1.2.9.0" + VALUE "ProductVersion", "1.2.9.0" } }