Compare commits

..

No commits in common. "main" and "v1.7.3" have entirely different histories.
main ... v1.7.3

433 changed files with 72353 additions and 250178 deletions

View File

@ -1,32 +0,0 @@
name: Build project
on:
push:
branches:
- main
paths:
- Source/**
pull_request:
branches:
- main
paths:
- Source/**
jobs:
build:
runs-on: windows-latest
steps:
- name: Checkout
uses: actions/checkout@v3
- name: Setup MSBuild.exe
uses: microsoft/setup-msbuild@v1.1.3
- name: Build release
run: msbuild Source/GrasscutterTools.sln /p:Configuration=Release /t:build /restore
- name: Upload build artifacts
uses: actions/upload-artifact@v3
with:
name: GrasscutterTools
path: Source/GrasscutterTools/bin/Release/GrasscutterTools.exe

Binary file not shown.

Before

Width:  |  Height:  |  Size: 37 KiB

After

Width:  |  Height:  |  Size: 28 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 20 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 38 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 48 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 24 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 34 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 38 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 34 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 31 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 49 KiB

After

Width:  |  Height:  |  Size: 36 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 23 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 30 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 16 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 18 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 14 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 24 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 18 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 42 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 54 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 22 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 52 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 57 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 95 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 62 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 48 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 52 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 37 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 7.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 78 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 21 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 40 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 19 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 17 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 50 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 20 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 36 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 14 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 35 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 26 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 17 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 17 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 16 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 22 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 24 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 16 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 15 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 30 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 34 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 41 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 136 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 60 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 36 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 49 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 74 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 40 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 86 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 23 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 44 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 27 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 57 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 24 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 38 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 31 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 42 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 44 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 43 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 50 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 31 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 3.6 MiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 32 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 14 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 821 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 5.3 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 41 KiB

After

Width:  |  Height:  |  Size: 34 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 23 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 55 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 62 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 25 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 41 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 48 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 38 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 32 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 48 KiB

After

Width:  |  Height:  |  Size: 42 KiB

View File

@ -3,69 +3,19 @@
[![GitHub license](https://img.shields.io/github/license/jie65535/GrasscutterCommandGenerator)](https://github.com/jie65535/GrasscutterCommandGenerator/blob/main/LICENSE)
[![GitHub stars](https://img.shields.io/github/stars/jie65535/GrasscutterCommandGenerator)](https://github.com/jie65535/GrasscutterCommandGenerator/stargazers)
[![Github All Releases](https://img.shields.io/github/downloads/jie65535/GrasscutterCommandGenerator/total.svg)](https://github.com/jie65535/GrasscutterCommandGenerator/releases)
[![GitHub release](https://img.shields.io/github/v/release/jie65535/GrasscutterCommandGenerator)](https://github.com/jie65535/GrasscutterCommandGenerator/releases/latest)
[![Build](https://github.com/jie65535/GrasscutterCommandGenerator/actions/workflows/build.yml/badge.svg)](https://github.com/jie65535/GrasscutterCommandGenerator/actions/workflows/build.yml)
[![QQ Group](https://pub.idqqimg.com/wpa/images/group.png)](http://qm.qq.com/cgi-bin/qm/qr?_wv=1027&k=fBizzp6RwJsIY7gFlmd4L-WG0V3aF8X3&authKey=mTjf%2B7jCIZess1HTRi05e5yi%2FHKA1auMwE8%2FJ960PFWk8WMATST654gWPi4OTHTZ&noverify=0&group_code=835489603)
[![QQ Group](https://pub.idqqimg.com/wpa/images/group.png)](https://qm.qq.com/cgi-bin/qm/qr?k=PdS9--b-n8LEAmYjX8fNFXtKDcsp4NHN&jump_from=webapi&authKey=7ty3ZCKYMKLGWLmO8O84qiNAZ0EuCnSGF+acP+74xuDMKYXXNjuPP7iUzffHz4r2)
English | [简体中文](README_zh-cn.md) | [繁體中文](README_zh-tw.md) | [Русский](README_ru-RU.md)
English | [简体中文](README_zh-cn.md) | [Русский](README_ru-RU.md)
## Commands Generator
Please download the latest committed automated build from [Action](https://github.com/jie65535/GrasscutterCommandGenerator/actions/workflows/build.yml), or a release from [Releases](https://github.com/jie65535/GrasscutterCommandGenerator/releases) (may be behind)
Please download the latest version from [Releases](https://github.com/jie65535/GrasscutterCommandGenerator/releases)
Support 简体中文, 繁中文, English and Русский languages.
Support 简体中文, 繁中文, English and Русский languages.
> **Warning**: app look may be different rather than on screenshots. It may also contain translation errors and a lack of certain resources. **We're welcome everyone to contribute to their [improvement](/Source/GrasscutterTools/Resources/en-us)**
## Remote command
The server require [gc-opencommand-plugin](https://github.com/jie65535/gc-opencommand-plugin) support
![OpenCommand](Doc/Screenshots/OpenCommand.gif)
> If you cannot connect to the server, please make sure the server address is correct.
>
> It is recommended to configure the server to HTTP mode, as shown in the figure(config.json):
> ![ConfigHttp](Doc/Screenshots/ConfigHttp.png)
>
> You can visit http://127.0.0.1/status/server with a browser to test whether the service is working properly.
>
> If you are not using port `80`, specify the port number to access in the url: http://127.0.0.1:443
Welcome everyone to improve the [ID Resource](/Source/GrasscutterTools/Resources/en-us).
## Update log
### GrasscutterTools-v1.13
![Proxy](Doc/Screenshots-en/22-Proxy.png)
![SceneTag](Doc/Screenshots-en/23-SceneTag.png)
![Weather](Doc/Screenshots-en/24-Weather.png)
![Settings](Doc/Screenshots-en/25-Settings.png)
### GrasscutterTools-v1.11
![HotKey](Doc/Screenshots-en/21-HotKey.png)
Commandline Usages:
```bash
GcTools.exe -help
GcTools.exe -version
GcTools.exe -c "cmd arg"
GcTools.exe -c "cmd1 arg" && GcTools -c "cmd2 arg1 arg2"
GcTools.exe -host http://127.0.0.1:443 -token 123456 -c "cmd1 arg1 arg2 | cmd2 | cmd3 arg"
```
### GrasscutterTools-v1.10
![Cutscene](Doc/Screenshots-en/7-ChangeScene.png)
![Activity Editor](Doc/Screenshots-en/20-ActivityEditor.png)
### GrasscutterTools-v1.9
![Achievement Page](Doc/Screenshots-en/19-AchievementPage.png)
### GrasscutterTools-v1.8
![Task page](Doc/Screenshots-en/18-TaskPage.png)
### GrasscutterTools-v1.7.3
![Gadget](Doc/Screenshots-en/6-SpawnEntity.png)
@ -87,6 +37,21 @@ Added [AttackModifier](https://github.com/NotThorny/AttackModifier), [AttackInfu
![Mail Editor](Doc/Screenshots-en/16-MailEditor.png)
## Remote command
The server require [gc-opencommand-plugin](https://github.com/jie65535/gc-opencommand-plugin) support
![OpenCommand](Doc/Screenshots/OpenCommand.gif)
> If you cannot connect to the server, please make sure the server address is correct.
>
> It is recommended to configure the server to HTTP mode, as shown in the figure(config.json):
> ![ConfigHttp](Doc/Screenshots/ConfigHttp.png)
>
> You can visit http://127.0.0.1/status/server with a browser to test whether the service is working properly.
>
> If you are not using port `80`, specify the port number to access in the url: http://127.0.0.1:443
---

View File

@ -3,18 +3,39 @@
[![GitHub лицензия](https://img.shields.io/github/license/jie65535/GrasscutterCommandGenerator)](https://github.com/jie65535/GrasscutterCommandGenerator/blob/main/LICENSE)
[![GitHub звёзды](https://img.shields.io/github/stars/jie65535/GrasscutterCommandGenerator)](https://github.com/jie65535/GrasscutterCommandGenerator/stargazers)
[![Github ВСЕ выпуски](https://img.shields.io/github/downloads/jie65535/GrasscutterCommandGenerator/total.svg)](https://github.com/jie65535/GrasscutterCommandGenerator/releases)
[![GitHub release](https://img.shields.io/github/v/release/jie65535/GrasscutterCommandGenerator)](https://github.com/jie65535/GrasscutterCommandGenerator/releases/latest)
[![Build](https://github.com/jie65535/GrasscutterCommandGenerator/actions/workflows/build.yml/badge.svg)](https://github.com/jie65535/GrasscutterCommandGenerator/actions/workflows/build.yml)
[English](README.md) | [简体中文](README_zh-cn.md) | [繁體中文](README_zh-tw.md) | Русский - Перевод [Юрий Дворецкий](https://github.com/yurikenjx) (с исправлениями от [EgorBron](https://github.com/EgorBron))
[English](README.md) | [简体中文](README_zh-cn.md) | Русский - Перевод [Юрий Дворецкий](https://github.com/yurikenjx) (с исправлениями от [EgorBron](https://github.com/EgorBron))
## Генератор команд (GCG)
Пожалуйста, загрузите последнюю подтвержденную автоматизированную сборку из [Action](https://github.com/jie65535/GrasscutterCommandGenerator/actions/workflows/build.yml) или выпуск из [Releases](https://github.com/jie65535/GrasscutterCommandGenerator/releases) (может отставать)
Загрузите последнюю версию из вкладки [Releases](https://github.com/jie65535/GrasscutterCommandGenerator/releases).
GCG поддерживает 简体中文 (китайский упр.), 繁中文 (китайский трад.), English (английский) и Русский языки.
GCG поддерживает 简体中文 (китайский упр.), 繁中文 (китайский трад.), English (английский) и Русский языки.
> **Warning**: вид приложения может отличаться от скриншотов. Также в нём могут присутствовать ошибки в переводе и отсутсвие некоторых ресурсов. **Мы приглашаем всех сделать вклад в их [улучшение](/Source/GrasscutterTools/Resources/ru-ru)**
Приглашаем всех улучшить [ID ресурсов и перевод](/Source/GrasscutterTools/Resources/ru-ru)
## Update log
### GrasscutterTools-v1.7.3
![Gadget](Doc/Screenshots-ru/4-SpawnEntity.png)
Added [AttackModifier](https://github.com/NotThorny/AttackModifier), [AttackInfusedWithItem](https://github.com/snoobi-seggs/AttackInfusedWithItem), [SwitchElementTraveller](https://github.com/Penelopeep/SwitchElementTraveller) plugins command generation
![AttackInfusedWithItem Gif](Doc/Screenshots/AttackMod.gif)
### GrasscutterTools-v1.7.2
![Shop Editor](Doc/Screenshots-ru/17-ShopEditor.png)
### GrasscutterTools-v1.7.1
- Gadgets (CHS Only)
### GrasscutterTools-v1.7.0
![Run Commands](Doc/Screenshots/RunMultipleCommands.png)
![Drop Editor](Doc/Screenshots-ru/15-DropEditor.png)
![Mail Editor](Doc/Screenshots-ru/16-MailEditor.png)
## Удаленная команда (OpenCommand)
@ -32,96 +53,34 @@ GCG поддерживает 简体中文 (китайский упр.), 繁體
>
> Если вы не указали порт `80` в конфиге, вам нужно указать свой порт в URL-адресе (например, http://127.0.0.1:443)
## Лог обновлений
### GrasscutterTools-v1.13
![Proxy](Doc/Screenshots-en/22-Proxy.png)
![SceneTag](Doc/Screenshots-en/23-SceneTag.png)
![Weather](Doc/Screenshots-en/24-Weather.png)
![Settings](Doc/Screenshots-en/25-Settings.png)
### GrasscutterTools-v1.11
![HotKey](Doc/Screenshots-en/21-HotKey.png)
Commandline Usages:
```bash
GcTools.exe -help
GcTools.exe -version
GcTools.exe -c "cmd arg"
GcTools.exe -c "cmd1 arg" && GcTools -c "cmd2 arg1 arg2"
GcTools.exe -host http://127.0.0.1:443 -token 123456 -c "cmd1 arg1 arg2 | cmd2 | cmd3 arg"
```
### GrasscutterTools-v1.10
![Cutscene](Doc/Screenshots-ru/12-Scenes.png)
![Activity Editor](Doc/Screenshots-ru/20-ActivityEditor.png)
### GrasscutterTools-v1.9
![Achievement Page](Doc/Screenshots-ru/19-AchievementPage.png)
### GrasscutterTools-v1.8
![Task page](Doc/Screenshots-ru/18-TaskPage.png)
### GrasscutterTools-v1.7.3
![Улучшенный спавн](Doc/Screenshots-ru/5-Spawn.png)
Добавлена поддержка генерации команд для плагинов [AttackModifier](https://github.com/NotThorny/AttackModifier), [AttackInfusedWithItem](https://github.com/snoobi-seggs/AttackInfusedWithItem), [SwitchElementTraveller](https://github.com/Penelopeep/SwitchElementTraveller).
![AttackInfusedWithItem Gif](Doc/Screenshots/AttackMod.gif)
### GrasscutterTools-v1.7.2
![Редактор магазина](Doc/Screenshots-ru/13-Shop.png)
### GrasscutterTools-v1.7.1
- Гаджеты (пока что только на китайском)
### GrasscutterTools-v1.7.0
![Запуск нескольких команд](Doc/Screenshots/RunMultipleCommands.png)
![Редактор дропа](Doc/Screenshots-ru/15-Drops.png)
![Редактор писем](Doc/Screenshots-ru/10-Mail.png)
---
## Скриншоты
![Логитип](Doc/Screenshots/GrasscutterLogo.png)
![Главная](Doc/Screenshots-ru/1-Home.png)
![Главная](Doc/Screenshots-ru/0-Home.png)
![OpenCommand](Doc/Screenshots-ru/2-Opencommand.png)
![Скриншот пользовательских команд](Doc/Screenshots-ru/1-CustomCommands.png)
![Кастомные команды](Doc/Screenshots-ru/3-Custom.png)
![Скриншот артефактов](Doc/Screenshots-ru/2-CustomArtifact.png)
![Артефакты](Doc/Screenshots-ru/4-Artifacts.png)
![Скриншот квестов](Doc/Screenshots-ru/3-Quest.png)
![Спавн сущностей](Doc/Screenshots-ru/5-Spawn.png)
![Скриншот спавна сущностей](Doc/Screenshots-ru/4-SpawnEntity.png)
![Выдача предметов](Doc/Screenshots-ru/6-Give.png)
![Скриншот выдачи персонажа](Doc/Screenshots-ru/5-GiveAvatar.png)
![Выдача персонажей](Doc/Screenshots-ru/7-Character.png)
![Скриншот выдачи оружия](Doc/Screenshots-ru/7-CustomWeapon.png)
![Выдача оружий](Doc/Screenshots-ru/8-Weapons.png)
![Скриншот выдачи предметов](Doc/Screenshots-ru/8-GiveItem.png)
![Управление аккаунтами](Doc/Screenshots-ru/9-Accounts.png)
![Скриншот смены сцены](Doc/Screenshots-ru/9-ChangeScene.png)
![Почта](Doc/Screenshots-ru/10-Mail.png)
![Скриншот управления аккаунтом](Doc/Screenshots-ru/11-Manage.png)
![Квесты](Doc/Screenshots-ru/11-Quests.png)
![Скриншот OpenCommand](Doc/Screenshots-ru/13-Remote.png)
![Сцены](Doc/Screenshots-ru/12-Scenes.png)
![Редактор баннеров](Doc/Screenshots-ru/14-GachaBannerEditor.png)
![Редактор магазина](Doc/Screenshots-ru/13-Shop.png)
![Редактор баннеров](Doc/Screenshots-ru/14-Gachas.png)
![Редактор дропа](Doc/Screenshots-ru/14-Drops.png)
![Браузер текстов](Doc/Screenshots-ru/16-Textmaps.png)
![Текстоый браузер карт](Doc/Screenshots-ru/15-TextMapBrowser.png)

View File

@ -3,69 +3,19 @@
[![GitHub license](https://img.shields.io/github/license/jie65535/GrasscutterCommandGenerator)](https://github.com/jie65535/GrasscutterCommandGenerator/blob/main/LICENSE)
[![GitHub stars](https://img.shields.io/github/stars/jie65535/GrasscutterCommandGenerator)](https://github.com/jie65535/GrasscutterCommandGenerator/stargazers)
[![Github All Releases](https://img.shields.io/github/downloads/jie65535/GrasscutterCommandGenerator/total.svg)](https://github.com/jie65535/GrasscutterCommandGenerator/releases)
[![GitHub release](https://img.shields.io/github/v/release/jie65535/GrasscutterCommandGenerator)](https://github.com/jie65535/GrasscutterCommandGenerator/releases/latest)
[![Build](https://github.com/jie65535/GrasscutterCommandGenerator/actions/workflows/build.yml/badge.svg)](https://github.com/jie65535/GrasscutterCommandGenerator/actions/workflows/build.yml)
[![QQ Group](https://pub.idqqimg.com/wpa/images/group.png)](http://qm.qq.com/cgi-bin/qm/qr?_wv=1027&k=fBizzp6RwJsIY7gFlmd4L-WG0V3aF8X3&authKey=mTjf%2B7jCIZess1HTRi05e5yi%2FHKA1auMwE8%2FJ960PFWk8WMATST654gWPi4OTHTZ&noverify=0&group_code=835489603)
[![QQ Group](https://pub.idqqimg.com/wpa/images/group.png)](https://qm.qq.com/cgi-bin/qm/qr?k=PdS9--b-n8LEAmYjX8fNFXtKDcsp4NHN&jump_from=webapi&authKey=7ty3ZCKYMKLGWLmO8O84qiNAZ0EuCnSGF+acP+74xuDMKYXXNjuPP7iUzffHz4r2)
[English](README.md) | 简体中文 | [繁體中文](README_zh-tw.md) | [Русский](README_ru-RU.md)
[English](README.md) | 简体中文 | [Русский](README_ru-RU.md)
## Commands Generator
请从 [Action](https://github.com/jie65535/GrasscutterCommandGenerator/actions/workflows/build.yml) 中下载最新提交的自动构建版本,或者从 [Releases](https://github.com/jie65535/GrasscutterCommandGenerator/releases) 中下载发布版本(可能落后)
请从 [Releases](https://github.com/jie65535/GrasscutterCommandGenerator/releases) 中获取最新版本
本工具支持 简体中文, 繁中文, English 与 Русский 语言。
本工具支持 简体中文, 繁中文, English 与 Русский 语言。
> **Warning**: 应用程序的外观可能与截图上的不同。它也可能包含翻译错误和缺乏某些资源。**我们欢迎各位为此工具做出贡献并<a href="./Source/GrasscutterTools/Resources/zh-cn">改进</a> : )**
欢迎大家一起来完善工具的[内置资源](/Source/GrasscutterTools/Resources/zh-cn)。
## 远程执行
服务端需要 [gc-opencommand-plugin](https://github.com/jie65535/gc-opencommand-plugin) 插件支持
![OpenCommand](Doc/Screenshots/OpenCommand.gif)
> 如果你无法连接到服务器,请确认填写的服务器地址是否正确。
>
> 建议配置服务器为HTTP模式如图所示(config.json)
> ![ConfigHttp](Doc/Screenshots/ConfigHttp.png)
>
> 你可以用浏览器访问 http://127.0.0.1/status/server 来测试服务是否正常工作。
>
> 如果使用的不是`80`端口则要在url中指定访问的端口号http://127.0.0.1:443
## 更新概要
### GrasscutterTools-v1.13
![Proxy](Doc/Screenshots/22-Proxy.png)
![SceneTag](Doc/Screenshots/23-SceneTag.png)
![Weather](Doc/Screenshots/24-Weather.png)
![Settings](Doc/Screenshots/25-Settings.png)
### GrasscutterTools-v1.11
![HotKey](Doc/Screenshots/21-HotKey.png)
命令行用法:
```bash
GcTools.exe -help
GcTools.exe -version
GcTools.exe -c "cmd arg"
GcTools.exe -c "cmd1 arg" && GcTools -c "cmd2 arg1 arg2"
GcTools.exe -host http://127.0.0.1:443 -token 123456 -c "cmd1 arg1 arg2 | cmd2 | cmd3 arg"
```
### GrasscutterTools-v1.10
![Cutscene](Doc/Screenshots/7-ChangeScene.png)
![Activity Editor](Doc/Screenshots/20-ActivityEditor.png)
### GrasscutterTools-v1.9
![Achievement Page](Doc/Screenshots/19-AchievementPage.png)
### GrasscutterTools-v1.8
![Task page](Doc/Screenshots/18-TaskPage.png)
## Update log
### GrasscutterTools-v1.7.3
![Spawns](Doc/Screenshots/6-SpawnEntity.png)
@ -92,6 +42,21 @@ GcTools.exe -host http://127.0.0.1:443 -token 123456 -c "cmd1 arg1 arg2 | cmd2 |
![Mail Editor](Doc/Screenshots/16-MailEditor.png)
## 远程执行
服务端需要 [gc-opencommand-plugin](https://github.com/jie65535/gc-opencommand-plugin) 插件支持
![OpenCommand](Doc/Screenshots/OpenCommand.gif)
> 如果你无法连接到服务器,请确认填写的服务器地址是否正确。
>
> 建议配置服务器为HTTP模式如图所示(config.json)
> ![ConfigHttp](Doc/Screenshots/ConfigHttp.png)
>
> 你可以用浏览器访问 http://127.0.0.1/status/server 来测试服务是否正常工作。
>
> 如果使用的不是`80`端口则要在url中指定访问的端口号http://127.0.0.1:443
---
## 软件截图

View File

@ -1,125 +0,0 @@
# Grasscutter Tools
[![GitHub license](https://img.shields.io/github/license/jie65535/GrasscutterCommandGenerator)](https://github.com/jie65535/GrasscutterCommandGenerator/blob/main/LICENSE)
[![GitHub stars](https://img.shields.io/github/stars/jie65535/GrasscutterCommandGenerator)](https://github.com/jie65535/GrasscutterCommandGenerator/stargazers)
[![Github All Releases](https://img.shields.io/github/downloads/jie65535/GrasscutterCommandGenerator/total.svg)](https://github.com/jie65535/GrasscutterCommandGenerator/releases)
[![GitHub release](https://img.shields.io/github/v/release/jie65535/GrasscutterCommandGenerator)](https://github.com/jie65535/GrasscutterCommandGenerator/releases/latest)
[![Build](https://github.com/jie65535/GrasscutterCommandGenerator/actions/workflows/build.yml/badge.svg)](https://github.com/jie65535/GrasscutterCommandGenerator/actions/workflows/build.yml)
[![QQ Group](https://pub.idqqimg.com/wpa/images/group.png)](http://qm.qq.com/cgi-bin/qm/qr?_wv=1027&k=fBizzp6RwJsIY7gFlmd4L-WG0V3aF8X3&authKey=mTjf%2B7jCIZess1HTRi05e5yi%2FHKA1auMwE8%2FJ960PFWk8WMATST654gWPi4OTHTZ&noverify=0&group_code=835489603)
[English](README.md) | [简体中文](README_zh-cn.md) | 繁體中文 | [Русский](README_ru-RU.md)
## 指令產生工具
請從 [Action](https://github.com/jie65535/GrasscutterCommandGenerator/actions/workflows/build.yml) 中下載最新提交的自動構建版本,或者從 [Releases](https://github.com/jie65535/GrasscutterCommandGenerator/releases) 中下載發布版本(可能落後)
本工具支援 简体中文、繁體中文、English 及 Русский 上述語言。
> **Warning**: 程式中的實際外觀可能會與截圖中的內容不同。其中也可能包含翻譯錯誤及缺乏特定資源。**我們歡迎各位為此工具做出貢獻並[改進](/Source/GrasscutterTools/Resources/zh-tw)**
## 遠端控制
伺服器需要安裝 [gc-opencommand-plugin](https://github.com/jie65535/gc-opencommand-plugin) 插件
![OpenCommand](Doc/Screenshots-tw/OpenCommand.gif)
> 如果你無法連接至伺服器,請確認輸入的伺服器位址是否正確。
>
> 建議將伺服器調整為HTTP模式如下圖所示(config.json):
> ![ConfigHttp](Doc/Screenshots-tw/ConfigHttp.png)
>
> 你可藉由任何瀏覽器輸入網址 http://127.0.0.1/status/server 以測試伺服器是否正常運作。
>
> 如果你並非使用`80`端口, 則須在網址後輸入指定端口: http://127.0.0.1:443
## 更新日誌
### GrasscutterTools-v1.13
![Proxy](Doc/Screenshots/22-Proxy.png)
![SceneTag](Doc/Screenshots/23-SceneTag.png)
![Weather](Doc/Screenshots/24-Weather.png)
![Settings](Doc/Screenshots/25-Settings.png)
### GrasscutterTools-v1.11
![HotKey](Doc/Screenshots/21-HotKey.png)
Commandline Usages:
```bash
GcTools.exe -help
GcTools.exe -version
GcTools.exe -c "cmd arg"
GcTools.exe -c "cmd1 arg" && GcTools -c "cmd2 arg1 arg2"
GcTools.exe -host http://127.0.0.1:443 -token 123456 -c "cmd1 arg1 arg2 | cmd2 | cmd3 arg"
```
### GrasscutterTools-v1.10
![Cutscene](Doc/Screenshots-tw/7-ChangeScene.png)
![Activity Editor](Doc/Screenshots-tw/20-ActivityEditor.png)
### GrasscutterTools-v1.9
![Achievement Page](Doc/Screenshots-tw/19-AchievementPage.png)
### GrasscutterTools-v1.8
![Task page](Doc/Screenshots-tw/18-TaskPage.png)
### GrasscutterTools-v1.7.3
![Spawns](Doc/Screenshots-tw/6-SpawnEntity.png)
![AttackMod](Doc/Screenshots-tw/6.1-AttackMod.png)
![AttackInfuse](Doc/Screenshots-tw/6.2-AttackInfuse.png)
新增 [攻擊修改](https://github.com/NotThorny/AttackModifier)、[攻擊注入](https://github.com/snoobi-seggs/AttackInfusedWithItem)、[主角切換元素](https://github.com/Penelopeep/SwitchElementTraveller)等插件指令產生
![AttackInfusedWithItem Gif](Doc/Screenshots-tw/AttackMod.gif)
### GrasscutterTools-v1.7.2
![Shop Editor](Doc/Screenshots-tw/17-ShopEditor.png)
### GrasscutterTools-v1.7.1
- 新增 Gadgets
### GrasscutterTools-v1.7.0
![Run Commands](Doc/Screenshots-tw/RunMultipleCommands.png)
![Drop Editor](Doc/Screenshots-tw/15-DropEditor.png)
![Mail Editor](Doc/Screenshots-tw/16-MailEditor.png)
---
## 工具截圖
![Logo](Doc/Screenshots-tw/GrasscutterLogo.png)
![Home](Doc/Screenshots-tw/0-Home.png)
![Custom Commands Screenshot](Doc/Screenshots-tw/1-CustomCommands.png)
![Custom Artifact Screenshot](Doc/Screenshots-tw/2-CustomArtifact.png)
![Custom Weapon Screenshort](Doc/Screenshots-tw/3-CustomWeapon.png)
![Give Item Screenshort](Doc/Screenshots-tw/4-GiveItem.png)
![Give Avatar Screenshort](Doc/Screenshots-tw/5-GiveAvatar.png)
![Spawn Entity Screenshort](Doc/Screenshots-tw/6-SpawnEntity.png)
![Change Scene Screenshort](Doc/Screenshots-tw/7-ChangeScene.png)
![Management](Doc/Screenshots-tw/9-Manage.png)
![GachaBannerEditor](Doc/Screenshots-tw/10-GachaBannerEditor.png)
![Text Map Browser](Doc/Screenshots-tw/11-TextMapBrowser.png)
![Remote Screenshort](Doc/Screenshots-tw/12-Remote.png)
![Quest Screenshort](Doc/Screenshots-tw/13-Quest.png)

View File

@ -29,7 +29,7 @@
<value>10001</value>
</setting>
<setting name="Host" serializeAs="String">
<value>http://127.0.0.1:443</value>
<value>https://127.0.0.1</value>
</setting>
<setting name="CheckedLastVersion" serializeAs="String">
<value />
@ -58,33 +58,6 @@
<setting name="MainFormSize" serializeAs="String">
<value>0, 0</value>
</setting>
<setting name="BannersJsonPath" serializeAs="String">
<value />
</setting>
<setting name="IsIncludeUID" serializeAs="String">
<value>False</value>
</setting>
<setting name="NavContainerSplitterDistance" serializeAs="String">
<value>0</value>
</setting>
<setting name="ActivityConfigJsonPath" serializeAs="String">
<value />
</setting>
<setting name="ProjectResourcePath" serializeAs="String">
<value />
</setting>
<setting name="IsUpgraded" serializeAs="String">
<value>False</value>
</setting>
<setting name="IsHotkeyEenabled" serializeAs="String">
<value>True</value>
</setting>
<setting name="AutoStartProxy" serializeAs="String">
<value>False</value>
</setting>
<setting name="WindowOpacity" serializeAs="String">
<value>100</value>
</setting>
</GrasscutterTools.Properties.Settings>
</userSettings>
</configuration>

View File

@ -0,0 +1,186 @@
using System;
using System.ComponentModel;
using System.Runtime.InteropServices;
using System.Windows.Forms;
namespace GrasscutterTools.Controls
{
[ToolboxItem(true)]
public class TextBoxXP : TextBox
{
/// <summary>
/// 获得当前进程,以便重绘控件
/// </summary>
/// <param name="hWnd"></param>
/// <returns></returns>
[DllImport("user32.dll")]
private static extern IntPtr GetWindowDC(IntPtr hWnd);
[DllImport("user32.dll")]
private static extern int ReleaseDC(IntPtr hWnd, IntPtr hDC);
private const int EM_SETCUEBANNER = 0x1501;
[DllImport("user32.dll", CharSet = CharSet.Auto)]
private static extern Int32 SendMessage
(IntPtr hWnd, int msg, int wParam, [MarshalAs(UnmanagedType.LPWStr)] string lParam);
/// <summary>
/// 水印文本
/// </summary>
private string _Watermark = "";
private float maximum;
private float minimum;
#region
/// <summary>
/// 是否启用热点效果
/// </summary>
[Category("外观")]
[Browsable(true)]
[Localizable(true)]
[Description("获取或设置输入框水印文本")]
[DefaultValue("")]
public string Watermark
{
get
{
return this._Watermark;
}
set
{
this._Watermark = value;
SendMessage(Handle, EM_SETCUEBANNER, 0, _Watermark);
this.Invalidate();
}
}
/// <summary>
/// 是否只能输入数字
/// </summary>
[Category("行为")]
[Browsable(true)]
[Description("获取或设置TextBox是否只允许输入数字")]
[DefaultValue(false)]
public bool DigitOnly { get; set; }
/// <summary>
/// 转为数值
/// </summary>
public float Number
{
get
{
if (float.TryParse(Text, out float value))
return value;
else
return 0f;
}
}
[Category("数据")]
[Browsable(true)]
[DefaultValue(0)]
[Description("指示小数点后位数")]
public int DecimalPlaces { get; set; }
[Category("数据")]
[Description("获取或设置限制的最大值")]
public float Maximum
{
get
{
return maximum;
}
set
{
maximum = value;
if (minimum > maximum)
{
minimum = maximum;
}
}
}
[Category("数据")]
[Browsable(true)]
[Description("获取或设置限制的最小值")]
public float Minimum
{
get
{
return minimum;
}
set
{
minimum = value;
if (minimum > maximum)
{
maximum = value;
}
}
}
#endregion
/// <summary>
///
/// </summary>
public TextBoxXP()
: base()
{
//BorderStyle = BorderStyle.FixedSingle;
//Font = Styles.StaticResources.DefaultFont;
}
protected override void OnKeyPress(KeyPressEventArgs e)
{
base.OnKeyPress(e);
// 如果只允许输入数字,则判断输入是否为退格或者数字
if (DigitOnly)
{
//IsNumber指定字符串中位于指定位置的字符是否属于数字类别
//IsPunctuation指定字符串中位于指定位置的字符是否属于标点符号类别
//IsControl指定字符串中位于指定位置的字符是否属于控制字符类别
if (!Char.IsNumber(e.KeyChar) && !Char.IsPunctuation(e.KeyChar) && !Char.IsControl(e.KeyChar))
{
e.Handled = true; //获取或设置一个值指示是否处理过System.Windows.Forms.Control.KeyPress事件
}
else if (Char.IsPunctuation(e.KeyChar) && DecimalPlaces > 0)
{
if (e.KeyChar == '.')
{
if (Text.LastIndexOf('.') != -1)
{
e.Handled = true;
}
}
else
{
e.Handled = true;
}
}
}
}
protected override void OnLeave(EventArgs e)
{
base.OnLeave(e);
if (DigitOnly)
{
if (!string.IsNullOrWhiteSpace(Text))
{
if (Number > Maximum)
Text = Maximum.ToString("F" + DecimalPlaces);
if (Number < Minimum)
Text = Minimum.ToString("F" + DecimalPlaces);
}
}
}
}
}

View File

@ -14,9 +14,8 @@
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*
*
**/
using System.Threading.Tasks;
using GrasscutterTools.DispatchServer.Model;
@ -24,7 +23,7 @@ using GrasscutterTools.Utils;
namespace GrasscutterTools.DispatchServer
{
internal static class DispatchServerAPI
public static class DispatchServerAPI
{
public static async Task<ServerStatus> QueryServerStatus(string host)
{

View File

@ -14,14 +14,13 @@
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*
*
**/
using Newtonsoft.Json;
namespace GrasscutterTools.DispatchServer.Model
{
internal class ServerStatus
public class ServerStatus
{
[JsonProperty("playerCount")]
public int PlayerCount { get; set; }
@ -33,7 +32,7 @@ namespace GrasscutterTools.DispatchServer.Model
public string Version { get; set; }
}
internal class ServerStatusResponse
public class ServerStatusResponse
{
[JsonProperty("retcode")]
public int RetCode { get; set; }

View File

@ -1,202 +0,0 @@
#nullable enable
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System;
using System.Security.Cryptography;
using System.Security.Cryptography.X509Certificates;
namespace Eavesdrop;
public sealed class Certifier : IDisposable
{
private readonly X509Store _rootStore, _myStore;
private readonly IDictionary<string, X509Certificate2> _certificateCache;
public string Issuer { get; }
public string CertificateAuthorityName { get; }
public DateTime NotAfter { get; set; }
public DateTime NotBefore { get; set; }
public int KeyLength { get; set; } = 1024;
public bool IsCachingSignedCertificates { get; set; }
public X509Certificate2? Authority { get; private set; }
public Certifier()
: this("Eavesdrop")
{ }
public Certifier(string issuer)
: this(issuer, $"{issuer} Root Certificate Authority", StoreLocation.CurrentUser)
{ }
public Certifier(string issuer, string certificateAuthorityName)
: this(issuer, certificateAuthorityName, StoreLocation.CurrentUser)
{ }
public Certifier(string issuer, string certificateAuthorityName, StoreLocation location)
{
_myStore = new X509Store(StoreName.My, location);
_rootStore = new X509Store(StoreName.Root, location);
_certificateCache = new Dictionary<string, X509Certificate2>();
NotBefore = DateTime.Now;
NotAfter = NotBefore.AddMonths(1);
Issuer = issuer;
CertificateAuthorityName = certificateAuthorityName;
}
public bool CreateTrustedRootCertificate()
{
return (Authority = InstallCertificate(_rootStore, CertificateAuthorityName)) != null;
}
public bool DestroyTrustedRootCertificate()
{
return DestroyCertificates(_rootStore);
}
public bool ExportTrustedRootCertificate(string path)
{
X509Certificate2? rootCertificate = InstallCertificate(_rootStore, CertificateAuthorityName);
path = Path.GetFullPath(path);
if (rootCertificate != null)
{
byte[] data = rootCertificate.Export(X509ContentType.Cert);
File.WriteAllBytes(path, data);
}
return File.Exists(path);
}
public X509Certificate2? GenerateCertificate(string certificateName)
{
return InstallCertificate(_myStore, certificateName);
}
public X509Certificate2 CreateCertificate(string subjectName, string alternateName)
{
using var rsa = RSA.Create(KeyLength);
var certificateRequest = new CertificateRequest(subjectName, rsa, HashAlgorithmName.SHA256, RSASignaturePadding.Pkcs1);
if (Authority == null)
{
certificateRequest.CertificateExtensions.Add(new X509BasicConstraintsExtension(true, false, 0, true));
certificateRequest.CertificateExtensions.Add(new X509SubjectKeyIdentifierExtension(certificateRequest.PublicKey, false));
using X509Certificate2 certificate = certificateRequest.CreateSelfSigned(NotBefore.ToUniversalTime(), NotAfter.ToUniversalTime());
certificate.FriendlyName = alternateName;
return new X509Certificate2(certificate.Export(X509ContentType.Pfx, string.Empty), string.Empty, X509KeyStorageFlags.Exportable | X509KeyStorageFlags.PersistKeySet);
}
else
{
var sanBuilder = new SubjectAlternativeNameBuilder();
sanBuilder.AddDnsName(alternateName);
certificateRequest.CertificateExtensions.Add(sanBuilder.Build());
certificateRequest.CertificateExtensions.Add(new X509BasicConstraintsExtension(false, false, 0, false));
certificateRequest.CertificateExtensions.Add(new X509SubjectKeyIdentifierExtension(certificateRequest.PublicKey, false));
using X509Certificate2 certificate = certificateRequest.Create(Authority, Authority.NotBefore, Authority.NotAfter, Guid.NewGuid().ToByteArray());
using X509Certificate2 certificateWithPrivateKey = certificate.CopyWithPrivateKey(rsa);
certificateWithPrivateKey.FriendlyName = alternateName;
return new X509Certificate2(certificateWithPrivateKey.Export(X509ContentType.Pfx, string.Empty), string.Empty, X509KeyStorageFlags.Exportable | X509KeyStorageFlags.PersistKeySet);
}
}
private X509Certificate2? InstallCertificate(X509Store store, string certificateName)
{
if (_certificateCache.TryGetValue(certificateName, out X509Certificate2? certificate))
{
if (DateTime.Now >= certificate.NotAfter)
{
_certificateCache.Remove(certificateName);
}
else return certificate;
}
lock (store)
{
try
{
store.Open(OpenFlags.ReadWrite);
string subjectName = $"CN={certificateName}, O={Issuer}";
certificate = FindCertificates(store, subjectName)?[0];
if (certificate != null && DateTime.Now >= certificate.NotAfter)
{
if (Authority == null)
{
DestroyCertificates();
store.Open(OpenFlags.ReadWrite);
}
else
{
store.Remove(certificate);
}
certificate = null;
}
if (certificate == null)
{
certificate = CreateCertificate(subjectName, certificateName);
if (certificate != null)
{
if (store == _rootStore || IsCachingSignedCertificates)
{
store.Add(certificate);
}
}
}
return certificate;
}
catch { return certificate = null; }
finally
{
store.Close();
if (certificate != null && !_certificateCache.ContainsKey(certificateName))
{
_certificateCache.Add(certificateName, certificate);
}
}
}
}
public bool DestroyCertificates(X509Store store)
{
lock (store)
{
try
{
store.Open(OpenFlags.ReadWrite);
X509Certificate2Collection certificates = store.Certificates.Find(X509FindType.FindByIssuerName, Issuer, false);
store.RemoveRange(certificates);
IEnumerable<string> subjectNames = certificates.Cast<X509Certificate2>().Select(c => c.GetNameInfo(X509NameType.SimpleName, false));
foreach (string subjectName in subjectNames)
{
if (!_certificateCache.ContainsKey(subjectName)) continue;
_certificateCache.Remove(subjectName);
}
return true;
}
catch { return false; }
finally { store.Close(); }
}
}
public bool DestroyCertificates() => DestroyCertificates(_myStore) && DestroyCertificates(_rootStore);
private static X509Certificate2Collection? FindCertificates(X509Store store, string subjectName)
{
X509Certificate2Collection certificates = store.Certificates
.Find(X509FindType.FindBySubjectDistinguishedName, subjectName, false);
return certificates.Count > 0 ? certificates : null;
}
public void Dispose()
{
_myStore.Close();
_rootStore.Close();
_myStore.Dispose();
_rootStore.Dispose();
}
}

View File

@ -1,200 +0,0 @@
using System;
using System.Net;
using System.Net.Http;
using System.Net.Sockets;
using System.Threading.Tasks;
using System.Collections.Generic;
using Eavesdrop.Network;
namespace Eavesdrop
{
public static class Eavesdropper
{
private static TcpListener _listener;
private static readonly object _stateLock;
public delegate Task AsyncEventHandler<TEventArgs>(object sender, TEventArgs e);
public static event AsyncEventHandler<RequestInterceptedEventArgs> RequestInterceptedAsync;
private static async Task OnRequestInterceptedAsync(RequestInterceptedEventArgs e)
{
Task interceptedTask = RequestInterceptedAsync?.Invoke(null, e);
if (interceptedTask != null)
{
await interceptedTask;
}
}
public static event AsyncEventHandler<ResponseInterceptedEventArgs> ResponseInterceptedAsync;
private static async Task OnResponseInterceptedAsync(ResponseInterceptedEventArgs e)
{
Task interceptedTask = ResponseInterceptedAsync?.Invoke(null, e);
if (interceptedTask != null)
{
await interceptedTask;
}
}
public static List<string> Overrides { get; }
public static bool IsRunning { get; private set; }
public static Certifier Certifier { get; set; }
static Eavesdropper()
{
_stateLock = new object();
ServicePointManager.Expect100Continue = true;
ServicePointManager.SecurityProtocol = SecurityProtocolType.Tls | SecurityProtocolType.Tls11 | SecurityProtocolType.Tls12;
Overrides = new List<string>();
Certifier = new Certifier("Eavesdrop", "Eavesdrop Root Certificate Authority");
}
public static void Terminate()
{
lock (_stateLock)
{
ResetMachineProxy();
IsRunning = false;
if (_listener != null)
{
_listener.Stop();
_listener = null;
}
}
}
public static void Initiate(int port)
{
Initiate(port, Interceptors.Default);
}
public static void Initiate(int port, Interceptors interceptors)
{
Initiate(port, interceptors, true);
}
public static void Initiate(int port, Interceptors interceptors, bool setSystemProxy)
{
lock (_stateLock)
{
Terminate();
_listener = new TcpListener(IPAddress.Any, port);
_listener.Start();
IsRunning = true;
Task.Factory.StartNew(InterceptRequestAsync, TaskCreationOptions.LongRunning);
if (setSystemProxy)
{
SetMachineProxy(port, interceptors);
}
}
}
private static async Task InterceptRequestAsync()
{
while (IsRunning && _listener != null)
{
try
{
TcpClient client = await _listener.AcceptTcpClientAsync().ConfigureAwait(false);
Task handleClientAsync = HandleClientAsync(client);
}
catch (ObjectDisposedException)
{
}
}
}
private static async Task HandleClientAsync(TcpClient client)
{
using var local = new EavesNode(Certifier, client);
WebRequest request = await local.ReadRequestAsync().ConfigureAwait(false);
if (request == null) return;
HttpContent requestContent = null;
var requestArgs = new RequestInterceptedEventArgs(request);
try
{
requestArgs.Content = requestContent = await local.ReadRequestContentAsync(request).ConfigureAwait(false);
await OnRequestInterceptedAsync(requestArgs).ConfigureAwait(false);
if (requestArgs.Cancel) return;
request = requestArgs.Request;
if (requestArgs.Content != null)
{
await local.WriteRequestContentAsync(request, requestArgs.Content).ConfigureAwait(false);
}
}
finally
{
requestContent?.Dispose();
requestArgs.Content?.Dispose();
}
WebResponse response = null;
try { response = await request.GetResponseAsync().ConfigureAwait(false); }
catch (WebException ex) { response = ex.Response; }
catch (ProtocolViolationException)
{
response?.Dispose();
response = null;
}
if (response == null) return;
HttpContent responseContent = null;
var responseArgs = new ResponseInterceptedEventArgs(request, response);
try
{
responseArgs.Content = responseContent = EavesNode.ReadResponseContent(response);
await OnResponseInterceptedAsync(responseArgs).ConfigureAwait(false);
if (responseArgs.Cancel) return;
await local.SendResponseAsync(responseArgs.Response, responseArgs.Content).ConfigureAwait(false);
}
finally
{
response.Dispose();
responseArgs.Response.Dispose();
responseContent?.Dispose();
responseArgs.Content?.Dispose();
}
}
private static void ResetMachineProxy()
{
INETOptions.Overrides.Clear();
INETOptions.IsIgnoringLocalTraffic = false;
INETOptions.HTTPAddress = null;
INETOptions.HTTPSAddress = null;
INETOptions.IsProxyEnabled = false;
INETOptions.Save();
}
private static void SetMachineProxy(int port, Interceptors interceptors)
{
foreach (string @override in Overrides)
{
if (INETOptions.Overrides.Contains(@override)) continue;
INETOptions.Overrides.Add(@override);
}
string address = ("127.0.0.1:" + port);
if (interceptors.HasFlag(Interceptors.HTTP))
{
INETOptions.HTTPAddress = address;
}
if (interceptors.HasFlag(Interceptors.HTTPS))
{
INETOptions.HTTPSAddress = address;
}
INETOptions.IsProxyEnabled = true;
INETOptions.IsIgnoringLocalTraffic = true;
INETOptions.Save();
}
}
}

View File

@ -1,78 +0,0 @@
using System;
using System.Net;
using System.Net.Http;
using System.ComponentModel;
namespace Eavesdrop
{
public class RequestInterceptedEventArgs : CancelEventArgs
{
private HttpWebRequest _httpRequest;
public HttpContent Content { get; set; }
private WebRequest _request;
public WebRequest Request
{
get => _request;
set
{
_request = value;
_httpRequest = (value as HttpWebRequest);
}
}
public Uri Uri => Request?.RequestUri;
public CookieContainer CookieContainer => _httpRequest?.CookieContainer;
public string Method
{
get => Request?.Method;
set
{
if (Request != null)
{
Request.Method = value;
}
}
}
public IWebProxy Proxy
{
get => Request?.Proxy;
set
{
if (Request != null)
{
Request.Proxy = value;
}
}
}
public string ContentType
{
get => Request?.ContentType;
set
{
if (Request != null)
{
Request.ContentType = value;
}
}
}
public WebHeaderCollection Headers
{
get => Request?.Headers;
set
{
if (Request != null)
{
Request.Headers = value;
}
}
}
public RequestInterceptedEventArgs(WebRequest request)
{
Request = request;
}
}
}

View File

@ -1,65 +0,0 @@
using System;
using System.Net;
using System.Net.Http;
using System.ComponentModel;
using System.Threading.Tasks;
using Eavesdrop.Network;
namespace Eavesdrop
{
public class ResponseInterceptedEventArgs : CancelEventArgs
{
private WebResponse _response;
public WebResponse Response
{
get => _response;
set
{
_response = value;
if (value is HttpWebResponse httpResponse)
{
CookieContainer = new CookieContainer();
CookieContainer.Add(httpResponse.Cookies);
}
else CookieContainer = null;
}
}
public WebRequest Request { get; }
public Uri Uri => Response?.ResponseUri;
public HttpContent Content { get; set; }
public CookieContainer CookieContainer { get; private set; }
public string ContentType
{
get => Response?.ContentType;
set
{
if (Response != null)
{
Response.ContentType = value;
}
}
}
public WebHeaderCollection Headers
{
get => Response?.Headers;
set
{
if (Response == null) return;
foreach (string header in value.AllKeys)
{
Response.Headers[header] = value[header];
}
}
}
public ResponseInterceptedEventArgs(WebRequest request, WebResponse response)
{
Request = request;
Response = response;
}
}
}

View File

@ -1,14 +0,0 @@
using System;
namespace Eavesdrop
{
[Flags]
public enum Interceptors
{
None = 0,
HTTP = 1,
HTTPS = 2,
Default = (HTTP | HTTPS)
}
}

View File

@ -1,241 +0,0 @@
using System;
using System.ComponentModel;
using System.Collections.Generic;
using System.Runtime.InteropServices;
using Microsoft.Win32;
namespace Eavesdrop
{
public static class INETOptions
{
private static readonly object _stateLock;
private static readonly int _iNetOptionSize;
private static readonly int _iNetPackageSize;
private static readonly RegistryKey _proxyKey;
public static List<string> Overrides { get; }
public static string HTTPAddress { get; set; }
public static string HTTPSAddress { get; set; }
public static bool IsProxyEnabled { get; set; }
public static bool IsIgnoringLocalTraffic { get; set; }
static INETOptions()
{
_stateLock = new object();
_iNetOptionSize = Marshal.SizeOf(typeof(INETOption));
_iNetPackageSize = Marshal.SizeOf(typeof(INETPackage));
_proxyKey = Registry.CurrentUser.OpenSubKey(@"Software\Microsoft\Windows\CurrentVersion\Internet Settings", true);
Overrides = new List<string>();
Load();
}
public static void Save()
{
lock (_stateLock)
{
var options = new List<INETOption>(3);
string joinedAddresses = (IsProxyEnabled ? GetJoinedAddresses() : null);
string joinedOverrides = (IsProxyEnabled ? GetJoinedOverrides() : null);
var kind = ProxyKind.PROXY_TYPE_DIRECT;
if (!string.IsNullOrWhiteSpace(joinedAddresses))
{
options.Add(new INETOption(OptionKind.INTERNET_PER_CONN_PROXY_SERVER, joinedAddresses));
if (!string.IsNullOrWhiteSpace(joinedOverrides))
{
options.Add(new INETOption(OptionKind.INTERNET_PER_CONN_PROXY_BYPASS, joinedOverrides));
}
kind |= ProxyKind.PROXY_TYPE_PROXY;
}
options.Insert(0, new INETOption(OptionKind.INTERNET_PER_CONN_FLAGS, (int)kind));
var inetPackage = new INETPackage
{
_optionError = 0,
_size = _iNetPackageSize,
_connection = IntPtr.Zero,
_optionCount = options.Count
};
IntPtr optionsPtr = Marshal.AllocCoTaskMem(_iNetOptionSize * options.Count);
for (int i = 0; i < options.Count; ++i)
{
var optionPtr = new IntPtr((IntPtr.Size == 4 ? optionsPtr.ToInt32() : optionsPtr.ToInt64()) + (i * _iNetOptionSize));
Marshal.StructureToPtr(options[i], optionPtr, false);
}
inetPackage._optionsPtr = optionsPtr;
IntPtr iNetPackagePtr = Marshal.AllocCoTaskMem(_iNetPackageSize);
Marshal.StructureToPtr(inetPackage, iNetPackagePtr, false);
int returnvalue = (NativeMethods.InternetSetOption(IntPtr.Zero, 75, iNetPackagePtr, _iNetPackageSize) ? -1 : 0);
if (returnvalue == 0)
{
returnvalue = Marshal.GetLastWin32Error();
}
Marshal.FreeCoTaskMem(optionsPtr);
Marshal.FreeCoTaskMem(iNetPackagePtr);
if (returnvalue > 0)
{
throw new Win32Exception(Marshal.GetLastWin32Error());
}
NativeMethods.InternetSetOption(IntPtr.Zero, 39, iNetPackagePtr, _iNetPackageSize);
NativeMethods.InternetSetOption(IntPtr.Zero, 37, iNetPackagePtr, _iNetPackageSize);
}
}
public static void Load()
{
lock (_stateLock)
{
LoadAddresses();
LoadOverrides();
IsProxyEnabled = (_proxyKey.GetValue("ProxyEnable")?.ToString() == "1");
}
}
private static void LoadOverrides()
{
string proxyOverride = _proxyKey.GetValue("ProxyOverride")?.ToString();
if (string.IsNullOrWhiteSpace(proxyOverride)) return;
string[] overrides = proxyOverride.Split(';');
foreach (string @override in overrides)
{
if (@override == "<local>")
{
IsIgnoringLocalTraffic = true;
}
else if (!Overrides.Contains(@override))
{
Overrides.Add(@override);
}
}
}
private static void LoadAddresses()
{
string proxyServer = _proxyKey.GetValue("ProxyServer")?.ToString();
if (string.IsNullOrWhiteSpace(proxyServer)) return;
string[] values = proxyServer.Split(';');
foreach (string value in values)
{
string[] pair = value.Split('=');
if (pair.Length != 2)
{
HTTPAddress = value;
HTTPSAddress = value;
return;
}
string address = pair[1];
string protocol = pair[0];
switch (protocol)
{
case "http": HTTPAddress = address; break;
case "https": HTTPSAddress = address; break;
}
}
}
private static string GetJoinedAddresses()
{
var addresses = new List<string>(2);
if (!string.IsNullOrWhiteSpace(HTTPAddress))
{
addresses.Add("http=" + HTTPAddress);
}
if (!string.IsNullOrWhiteSpace(HTTPSAddress))
{
addresses.Add("https=" + HTTPSAddress);
}
return string.Join(";", addresses);
}
private static string GetJoinedOverrides()
{
var overrides = new List<string>(Overrides);
if (IsIgnoringLocalTraffic)
{
overrides.Add("<local>");
}
return string.Join(";", overrides);
}
[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Auto)]
private struct INETOption
{
private readonly OptionKind _kind;
private readonly INETOptionValue _value;
public INETOption(OptionKind kind, int value)
{
_kind = kind;
_value = CreateValue(value);
}
public INETOption(OptionKind kind, string value)
{
_kind = kind;
_value = CreateValue(value);
}
private static INETOptionValue CreateValue(int value)
{
return new INETOptionValue
{
_intValue = value
};
}
private static INETOptionValue CreateValue(string value)
{
return new INETOptionValue
{
_stringPointer = Marshal.StringToHGlobalAuto(value)
};
}
[StructLayout(LayoutKind.Explicit)]
private struct INETOptionValue
{
[FieldOffset(0)]
public int _intValue;
[FieldOffset(0)]
public IntPtr _stringPointer;
[FieldOffset(0)]
public System.Runtime.InteropServices.ComTypes.FILETIME _fileTime;
}
}
[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Auto)]
private struct INETPackage
{
public int _size;
public IntPtr _connection;
public int _optionCount;
public int _optionError;
public IntPtr _optionsPtr;
}
[Flags]
private enum ProxyKind
{
PROXY_TYPE_DIRECT = 1,
PROXY_TYPE_PROXY = 2,
PROXY_TYPE_AUTO_PROXY_URL = 4,
PROXY_TYPE_AUTO_DETECT = 8
}
private enum OptionKind
{
INTERNET_PER_CONN_FLAGS = 1,
INTERNET_PER_CONN_PROXY_SERVER = 2,
INTERNET_PER_CONN_PROXY_BYPASS = 3,
INTERNET_PER_CONN_AUTOCONFIG_URL = 4
}
}
}

View File

@ -1,12 +0,0 @@
using System;
using System.Runtime.InteropServices;
namespace Eavesdrop
{
internal static class NativeMethods
{
[DllImport("wininet.dll", SetLastError = true, CharSet = CharSet.Auto)]
[return: MarshalAs(UnmanagedType.Bool)]
public static extern bool InternetSetOption(IntPtr hInternet, int dwOption, IntPtr lpBuffer, int dwBufferLength);
}
}

View File

@ -1,21 +0,0 @@
MIT License
Copyright (c) 2022 ArachisH
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.

Some files were not shown because too many files have changed in this diff Show More