-*На данный момент программа находится в предварительной версии, поэтому некоторые функции нестабильны. Подробнее можно узнать [здесь](preview.md).*
-# Minecraft Holy Client
+
A high-performance platform for running stress-testing minecraft bots.
-Высокопроизводительная платформа для запуска стресс-тест ботов Minecraft, написанная на C#.
+
-[Скачайте в релизах](https://github.com/Titlehhhh/Minecraft-Holy-Client/releases) последнюю версию Minecraft Holy Client и запустите её.
-После запуска приложения Вас встретит окно с главной страницей.
-![image](https://github.com/Titlehhhh/Minecraft-Holy-Client/assets/93156853/4d582c49-13bd-44c7-81b4-f6ebd5b924cd)
+
-После открытия навигационного меню Вы увидите несколько пунктов:
-- Главная. Страница где есть основная информация о приложении: GitHub, Наш Discord сервер, а также документация(в разрботке).
-- Бот менеджер. Эта экспериментальная функция. Планируется в программу добавить простых ботов, таких как, боты афк-рыбалки или подобные.
-- Настройки. Вы можете настроить язык. Пока можно настроить только это.
-- Стресс-тест. Основная возможность приложения. Здесь Вы можете протестировать Ваш сервер под высокой нагрузкой.
-- Управление расширениями. Здесь Вы можете добавить свои плагины в программу.
-![image](https://github.com/Titlehhhh/Minecraft-Holy-Client/assets/93156853/3a156e06-bd3c-4882-9c5c-ce123c14b9c2)
+---
-### Запуск стресс-теста
-1) Перейдем на страницу Стресс-тест и введем Ip адрес сервера, который мы хотим нагрузить.
-2) Далее поставить префикс ника для ботов. Пусть будет "Title_"
-3) Затем укажем количество ботов. Это количество, которое система будет стремиться запустить. Например 1000.
-4) Укажем, что нужно в тестировании использовать прокси сервера, а также укажем версию 1.16.5.
-5) В настройках поведения укажем, чтобы боты спамили "Hello from Minecraft Holy Client".
-![image](https://github.com/Titlehhhh/Minecraft-Holy-Client/assets/93156853/fdbab77e-0eed-44ed-bbe3-30e6d100802b)
+
+
+
-Нажимаем на зеленую кнопку видим, что боты зашли на сервер и спамят "Hello from Minecraft Holy Client"
-![image](https://github.com/Titlehhhh/Minecraft-Holy-Client/assets/93156853/51c89585-1fd5-4351-9677-a59322ececd7)
+**Minecraft Holy Client** is a handy, **high-performance**, easily extensible **open-source** application designed to run load and stress-test **Minecraft** bots, written in C#.
+All components of the application have been designed to **maximize performance**, so it works stably on **weak devices**.
-## Roadmap
-![image](https://github.com/Titlehhhh/Minecraft-Holy-Client/assets/93156853/cee54a39-b6e5-4e10-b329-ec0230eb43b4)
+![Minecraft Holy Client Bots](https://github.com/Titlehhhh/Minecraft-Holy-Client/assets/93156853/75c9f0a3-8ae2-4b7c-8ad5-e8b5fa120165)
+
+(Preview 300~ bots)
+
+
+
+
+
+
+## Installation
+
+Minecraft Holy Client is available for **Windows**, **Linux** and **MacOs**. In addition, it is **portable** and is not installed on the system, so it can be easily uninstalled.
+
+[Download](https://github.com/Titlehhhh/Minecraft-Holy-Client/releases) the latest version of HolyClient.Desktop and follow these instructions depending on your platform.
+
+### Windows
+
+1) Open and _extract_ the archive with the program.
+2) Run **HolyClient.Desktop.exe**
+
+### Linux
+
+_Using Ubuntu as an example_
+
+1) Open and _extract_ the archive with the program.
+2) Open a terminal where the HolyClient.Desktop file is located and execute `./HolyClient.Desktop` in the console
+
+### Mac Os
+
+1) Open and _extract_ the archive with the program.
+2) Open a terminal where the HolyClient.Desktop file is located and execute `./HolyClient.Desktop` in the console
+
+
+## Launch
+
+When we have launched the app, then we can safely go to the "Stress Test" tab and we can launch bots.
+
+
+
+![image](https://github.com/Titlehhhh/Minecraft-Holy-Client/assets/93156853/55769ef1-f81d-477d-9027-02dfa0339f80)
+
+
+## Features
+
+- **Custom proxies**. It is possible to load custom proxies from a link and a file.
+- **Multiverse in bots**. Bots can enter servers from 1.12.2 to 1.19.3. Temporarily not all packets are supported, this is being actively worked on.
+- **Using user behavior for stress testing**.
+
+## Contributing
+
+Contributions are always welcome!
+
+## Custom plugin
+
+The default plugin has few features. It just sends `/register ` first and starts spamming immediately.
+Minecraft servers are a large number, with different anti-bot systems and if you want to test your server for bot attacks, it makes sense to write a custom plugin that would bypass it. How to write custom plugins is written here.
diff --git a/ReleaseNotes/2.0.0-preview.1/En.md b/ReleaseNotes/2.0.0-preview.1/En.md
index 9975ba39..8eba07b2 100644
--- a/ReleaseNotes/2.0.0-preview.1/En.md
+++ b/ReleaseNotes/2.0.0-preview.1/En.md
@@ -1,7 +1,7 @@
> [!WARNING]
> Disclaimer: This translation was generated using AI.
-# What's New in Minecraft Holy Client *2.0.0-preview.1*?
+# What's New in Minecraft Holy Client **2.0.0-preview.1.1**?
## Major Fixes
diff --git a/ReleaseNotes/2.0.0-preview.1/Ru.md b/ReleaseNotes/2.0.0-preview.1/Ru.md
index 34017dd4..b251d578 100644
--- a/ReleaseNotes/2.0.0-preview.1/Ru.md
+++ b/ReleaseNotes/2.0.0-preview.1/Ru.md
@@ -1,4 +1,4 @@
-# Что изменилось в Minecraft Holy Client *2.0.0-preview.1*?
+# Что изменилось в Minecraft Holy Client **2.0.0-preview.1.1**?
## Основные исправления
diff --git a/ReleaseNotes/2.0.0-preview.2/En.md b/ReleaseNotes/2.0.0-preview.2/En.md
new file mode 100644
index 00000000..e69de29b
diff --git a/ReleaseNotes/2.0.0-preview.2/Ru.md b/ReleaseNotes/2.0.0-preview.2/Ru.md
new file mode 100644
index 00000000..d885aba8
--- /dev/null
+++ b/ReleaseNotes/2.0.0-preview.2/Ru.md
@@ -0,0 +1,2 @@
+- memory performance
+- default plugin /reg to /register
\ No newline at end of file
diff --git a/build/_build.csproj b/build/_build.csproj
index ece0af80..02de8215 100644
--- a/build/_build.csproj
+++ b/build/_build.csproj
@@ -21,7 +21,7 @@
-
+
diff --git a/preview.md b/preview.md
index 2440d5a1..fd77f593 100644
--- a/preview.md
+++ b/preview.md
@@ -2,22 +2,18 @@
Этот раздел может меняться в течение разработки. Все изменения будут написаны в заметках о выпуске предварительной версии.
-# Функции, которые нужно добавить
-## Плагины
-- Плагин, который будет установлен в программу по умолчанию. Это упростит использование программой простым пользователям.
+# Плагины
- Возможность перезагружать плагины из менеджера расширений, а также из вкладки "Поведения".
- Возможность загружать плагины из NuGet.
- Отображение ошибки, если плагин не найден.
- Отображение ошибки, если поведение не найдено.
-## Прокси
-- Прокси по умолчанию.
-- Возможность загружать прокси из URL источников, например, из открытых репозиториех GitHub с бесплатными списками прокси.
+# Прокси
- Возможность загружать прокси с логином и паролем.
- Отображение количества загруженных прокси в окне с запуском стресс теста.
- Возможность загружать разные типы прокси(HTTP(S), Socks(4/5)) из одного источника.
-## Прочее
+# Прочее
- Система профилей в стресс тесте.
diff --git a/roadmap.png b/roadmap.png
new file mode 100644
index 00000000..3a07a87b
Binary files /dev/null and b/roadmap.png differ
diff --git a/src/CoreLibs/HolyClient.Abstractions/StressTest/IStressTestBot.cs b/src/CoreLibs/HolyClient.Abstractions/StressTest/IStressTestBot.cs
index 7d5da709..544964d5 100644
--- a/src/CoreLibs/HolyClient.Abstractions/StressTest/IStressTestBot.cs
+++ b/src/CoreLibs/HolyClient.Abstractions/StressTest/IStressTestBot.cs
@@ -6,7 +6,7 @@ namespace HolyClient.Abstractions.StressTest
public interface IStressTestBot
{
public Task Restart(bool changeNickAndProxy);
- IObservable OnError { get; }
+
public MinecraftClient Client { get; }
}
diff --git a/src/CoreLibs/HolyClient.Common/HolyClient.Common.csproj b/src/CoreLibs/HolyClient.Common/HolyClient.Common.csproj
index 4d094185..e7c29dbb 100644
--- a/src/CoreLibs/HolyClient.Common/HolyClient.Common.csproj
+++ b/src/CoreLibs/HolyClient.Common/HolyClient.Common.csproj
@@ -11,9 +11,9 @@
-
+
-
+
diff --git a/src/CoreLibs/HolyClient.Core/Helpers/MapDataHelper.cs b/src/CoreLibs/HolyClient.Core/Helpers/MapDataHelper.cs
index 94e62762..fe692d3d 100644
--- a/src/CoreLibs/HolyClient.Core/Helpers/MapDataHelper.cs
+++ b/src/CoreLibs/HolyClient.Core/Helpers/MapDataHelper.cs
@@ -27,7 +27,7 @@ public static Image CreateImage(byte[] colors)
var color = new Rgba32(values[0], values[1], values[2]);
- //Console.WriteLine(color.Name);
+
image[y, x] = color;
}
}
diff --git a/src/CoreLibs/HolyClient.Core/HolyClient.Core.csproj b/src/CoreLibs/HolyClient.Core/HolyClient.Core.csproj
index 9893d896..c4aae8fd 100644
--- a/src/CoreLibs/HolyClient.Core/HolyClient.Core.csproj
+++ b/src/CoreLibs/HolyClient.Core/HolyClient.Core.csproj
@@ -20,9 +20,9 @@
-
+
-
+
diff --git a/src/CoreLibs/HolyClient.Core/Models/BotManager/MinecraftBot.cs b/src/CoreLibs/HolyClient.Core/Models/BotManager/MinecraftBot.cs
index 847056c9..025783de 100644
--- a/src/CoreLibs/HolyClient.Core/Models/BotManager/MinecraftBot.cs
+++ b/src/CoreLibs/HolyClient.Core/Models/BotManager/MinecraftBot.cs
@@ -110,7 +110,7 @@ public async Task Run(ILogger logger, CancellationToken cancellation)
Interlocked.Exchange(ref _cleanUpPlugins, disposables);
- await minecraftClient.Login(logger);
+ await minecraftClient.Start(logger);
}
public Task Stop()
{
diff --git a/src/CoreLibs/HolyClient.StressTest/DefaultBehavior.cs b/src/CoreLibs/HolyClient.StressTest/DefaultBehavior.cs
index 4348a0f4..a2dcc1d6 100644
--- a/src/CoreLibs/HolyClient.StressTest/DefaultBehavior.cs
+++ b/src/CoreLibs/HolyClient.StressTest/DefaultBehavior.cs
@@ -13,7 +13,14 @@ public class DefaultBehavior : IStressTestBehavior
[System.ComponentModel.DisplayName("Spam timeout")]
public int SpamTimeout { get; set; } = 5000;
+ [System.ComponentModel.DisplayName("Reconnect timeout")]
+ public int ReconnectTimeout { get; set; } = 5000;
+ [System.ComponentModel.DisplayName("Reconnect timeout")]
+ public int Reconnects { get; set; } = 1;
+
+ [System.ComponentModel.DisplayName("Spam Nocom")]
+ public bool SpamNocom { get; set; } = false;
private static Regex SayVerifyRegex = new(@"\.say \/verify (\d+)");
@@ -23,9 +30,9 @@ public Task Activate(CompositeDisposable disposables, IEnumerable
- {
+ Action onErr = async (exc) =>
+ {
try
{
if (cts is not null)
@@ -43,13 +50,35 @@ public Task Activate(CompositeDisposable disposables, IEnumerable
+ {
+ bot.Client.OnErrored -= onErr;
+ }));
var d2 = bot.Client.OnJoinGame.Subscribe(async x =>
{
@@ -60,12 +89,12 @@ public Task Activate(CompositeDisposable disposables, IEnumerable x.Message.Contains("verify"))
@@ -76,29 +105,25 @@ public Task Activate(CompositeDisposable disposables, IEnumerable
-
+
\ No newline at end of file
diff --git a/src/CoreLibs/HolyClient.StressTest/HolyClient.StressTest.csproj b/src/CoreLibs/HolyClient.StressTest/HolyClient.StressTest.csproj
index b540ee19..3663c14b 100644
--- a/src/CoreLibs/HolyClient.StressTest/HolyClient.StressTest.csproj
+++ b/src/CoreLibs/HolyClient.StressTest/HolyClient.StressTest.csproj
@@ -9,9 +9,9 @@
-
+
-
+
diff --git a/src/CoreLibs/HolyClient.StressTest/IProxyProvider.cs b/src/CoreLibs/HolyClient.StressTest/IProxyProvider.cs
index 98964bf5..a6ddb828 100644
--- a/src/CoreLibs/HolyClient.StressTest/IProxyProvider.cs
+++ b/src/CoreLibs/HolyClient.StressTest/IProxyProvider.cs
@@ -2,7 +2,7 @@
namespace HolyClient.StressTest
{
- public interface IProxyProvider
+ public interface IProxyProvider : IDisposable
{
public ValueTask GetNextProxy();
}
diff --git a/src/CoreLibs/HolyClient.StressTest/IStressTest.cs b/src/CoreLibs/HolyClient.StressTest/IStressTestProfile.cs
similarity index 59%
rename from src/CoreLibs/HolyClient.StressTest/IStressTest.cs
rename to src/CoreLibs/HolyClient.StressTest/IStressTestProfile.cs
index 7c311d12..0fac2316 100644
--- a/src/CoreLibs/HolyClient.StressTest/IStressTest.cs
+++ b/src/CoreLibs/HolyClient.StressTest/IStressTestProfile.cs
@@ -2,15 +2,28 @@
using HolyClient.Abstractions.StressTest;
using HolyClient.Core.Infrastructure;
using McProtoNet;
+using System.Collections.Concurrent;
using System.ComponentModel;
namespace HolyClient.StressTest
{
+ public class ExceptionThrowCount
+ {
+ public Type TypeException { get; set; }
+
+ public int Count { get; set; }
+
+ public Dictionary Messages { get; set; }
+ }
+
- [MessagePack.Union(0, typeof(StressTest))]
- public interface IStressTest : INotifyPropertyChanged, INotifyPropertyChanging
+ [MessagePack.Union(0, typeof(StressTestProfile))]
+ public interface IStressTestProfile : INotifyPropertyChanged, INotifyPropertyChanging
{
+ Guid Id { get; set; }
+ string Name { get; set; }
+
string Server { get; set; }
@@ -23,17 +36,21 @@ public interface IStressTest : INotifyPropertyChanged, INotifyPropertyChanging
bool UseProxy { get; set; }
MinecraftVersion Version { get; set; }
-
+
ISourceCache Proxies { get; }
IObservable Metrics { get; }
+
+ ConcurrentDictionary, ExceptionCounter> ExceptionCounter { get; }
+
IStressTestBehavior Behavior { get; }
StressTestServiceState CurrentState { get; }
- PluginTypeReference BehaviorRef { get; }
-
+ PluginTypeReference BehaviorRef { get; }
+ bool CheckDNS { get; set; }
+
void SetBehavior(IPluginSource pluginSource);
void DeleteBehavior();
diff --git a/src/CoreLibs/HolyClient.StressTest/ProxyProvider.cs b/src/CoreLibs/HolyClient.StressTest/ProxyProvider.cs
index dfd72aa8..0fe924f5 100644
--- a/src/CoreLibs/HolyClient.StressTest/ProxyProvider.cs
+++ b/src/CoreLibs/HolyClient.StressTest/ProxyProvider.cs
@@ -53,7 +53,19 @@ async ValueTask IProxyProvider.GetNextProxy()
_lockAsync.Release();
}
- }
+ }
+ private bool disposed = false;
+ public void Dispose()
+ {
+ if (disposed)
+ return;
+ disposed = true;
+ foreach (var proxy in _clients)
+ {
+ proxy.Dispose();
+ }
+ GC.SuppressFinalize(this);
+ }
}
}
diff --git a/src/CoreLibs/HolyClient.StressTest/StressTestBot.cs b/src/CoreLibs/HolyClient.StressTest/StressTestBot.cs
index d2bac8a0..954cf1a0 100644
--- a/src/CoreLibs/HolyClient.StressTest/StressTestBot.cs
+++ b/src/CoreLibs/HolyClient.StressTest/StressTestBot.cs
@@ -28,18 +28,17 @@ public StressTestBot(MinecraftClient client, INickProvider nickProvider, IProxyP
this.cancellationToken = cancellationToken;
}
- private Subject _onError = new();
-
- public IObservable OnError => _onError;
public async Task Restart(bool changeNickAndProxy)
{
if (cancellationToken.IsCancellationRequested)
return;
+ Client.Disconnect();
+
+
try
{
- Client.Disconnect();
if (changeNickAndProxy)
{
IProxyClient? proxy = null;
@@ -58,31 +57,18 @@ public async Task Restart(bool changeNickAndProxy)
};
}
- await Client.Login(Logger.None);
-
-
+ await Client.Start(Logger.None);
}
- catch (Exception ex)
+ catch
{
+ throw;
+ }
- _onError.OnNext(ex);
-
- }
}
- private async void WaitClient()
- {
- try
- {
- await Client;
- }
- catch (Exception ex)
- {
- this._onError.OnNext(ex);
- }
- }
+
}
}
diff --git a/src/CoreLibs/HolyClient.StressTest/StressTest.cs b/src/CoreLibs/HolyClient.StressTest/StressTestProfile.cs
similarity index 58%
rename from src/CoreLibs/HolyClient.StressTest/StressTest.cs
rename to src/CoreLibs/HolyClient.StressTest/StressTestProfile.cs
index a9c6fbfa..f8a8bba8 100644
--- a/src/CoreLibs/HolyClient.StressTest/StressTest.cs
+++ b/src/CoreLibs/HolyClient.StressTest/StressTestProfile.cs
@@ -9,18 +9,44 @@
using MessagePack;
using ReactiveUI;
using ReactiveUI.Fody.Helpers;
+using Stateless.Graph;
+using System.Collections.Concurrent;
using System.Diagnostics;
+using System.Diagnostics.Metrics;
+using System.Net.Sockets;
+using System.Net;
using System.Reactive.Disposables;
+using System.Reactive.Linq;
using System.Reactive.Subjects;
+using System.Runtime.CompilerServices;
+using System.Threading;
namespace HolyClient.StressTest
{
+ public class ExceptionCounter
+ {
+ private volatile int _x = 1;
+
+ public int Count => Volatile.Read(ref this._x);
+
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public void Increment()
+ {
+ Interlocked.Increment(ref _x);
+ }
+
+ }
+
[MessagePackObject(keyAsPropertyName: true)]
- public class StressTest : ReactiveObject, IStressTest
+ public class StressTestProfile : ReactiveObject, IStressTestProfile
{
#region Properties
#region Serializable
+ public Guid Id { get; set; } = Guid.NewGuid();
+
+ [Reactive]
+ public string Name { get; set; }
[Reactive]
@@ -42,6 +68,9 @@ public IEnumerable ProxiesState
public PluginTypeReference BehaviorRef { get; set; }
[Reactive]
public bool UseProxy { get; set; } = true;
+
+ [Reactive]
+ public bool CheckDNS { get; set; } = false;
#endregion
#region NonSerializable
@@ -72,7 +101,9 @@ public IStressTestBehavior Behavior
[IgnoreMember]
public StressTestServiceState CurrentState { get; private set; }
-
+
+ [IgnoreMember]
+ public ConcurrentDictionary, ExceptionCounter> ExceptionCounter { get; private set; } = new();
#endregion
@@ -85,13 +116,17 @@ public IStressTestBehavior Behavior
private readonly object _currentInfoLock = new();
private StressTestMetrik currentInfo;
- private volatile int _botsOnlineCounter = 0;
+ private volatile int _botsConnectionCounter = 0;
+ private volatile int _botsHandshakeCounter = 0;
+ private volatile int _botsLoginCounter = 0;
+ private volatile int _botsPlayCounter = 0;
+
private volatile int _cpsCounter = 0;
private IDisposable? _cleanUp;
- public StressTest()
+ public StressTestProfile()
{
}
@@ -102,9 +137,12 @@ public StressTest()
[ConfigureAwait(false)]
public async Task Start(Serilog.ILogger logger)
{
-
+ ExceptionCounter.Clear();
CurrentState = StressTestServiceState.Init;
- _botsOnlineCounter = 0;
+ _botsConnectionCounter = 0;
+ _botsHandshakeCounter = 0;
+ _botsLoginCounter = 0;
+ _botsPlayCounter = 0;
_cpsCounter = 0;
try
{
@@ -124,6 +162,11 @@ public async Task Start(Serilog.ILogger logger)
var proxyProvider = await LoadProxy(logger);
+ if (proxyProvider is not null)
+ {
+ _disposables.Add(proxyProvider);
+ }
+
var bots = new List();
string host = this.Server;
@@ -153,9 +196,26 @@ public async Task Start(Serilog.ILogger logger)
logger.Error($"[STRESS TEST] Ошибка поиска srv для {this.Server}");
}
}
+ logger.Information($"[STRESS TEST] Поиск DNS для {this.Server}");
+ if (CheckDNS)
+ {
+ try
+ {
+ var result = await Dns.GetHostAddressesAsync(host, AddressFamily.InterNetwork, cancellationTokenSource.Token).ConfigureAwait(false);
+
+ host = result[0].ToString();
+ logger.Information($"[STRESS TEST] DNS IP for {this.Server} - {host}");
+ }
+ catch
+ {
+ logger.Error($"[STRESS TEST] Ошибка поиска DNS для {this.Server}");
+ }
+ }
logger.Information($"[STRESS TEST] Запущен стресс тест на {this.NumberOfBots} ботов на сервер {host}:{port}");
+
+
var stressTestBots = new List();
var nickProvider = new NickProvider(this.BotsNickname);
@@ -182,60 +242,125 @@ public async Task Start(Serilog.ILogger logger)
};
- bot.StateChanged += Bot_StateChanged;
- _disposables.Add(Disposable.Create(() =>
- {
- bot.StateChanged -= Bot_StateChanged;
- bot.Dispose();
- }));
- stressTestBots.Add(
- new StressTestBot(
+ var b = new StressTestBot(
bot, nickProvider, proxyProvider,
logger,
i,
- cancellationTokenSource.Token));
-
- }
+ cancellationTokenSource.Token);
- new Thread(() =>
- {
- try
+ Action onState = (state) =>
{
- Stopwatch stopwatch = new();
- while (!cancellationTokenSource.IsCancellationRequested)
+ if (state.NewValue == ClientState.Play)
{
- stopwatch.Start();
- var cps = Interlocked.Exchange(ref _cpsCounter, 0);
+ Interlocked.Increment(ref _cpsCounter);
- var botsOnline = Volatile.Read(ref _botsOnlineCounter);
- _dataPerSecond.OnNext(new StressTestMetrik(cps, botsOnline));
+ Interlocked.Increment(ref _botsPlayCounter);
+ }
+ else if (state.NewValue == ClientState.Connecting)
+ {
+ Interlocked.Increment(ref _botsConnectionCounter);
+ }
+ else if (state.NewValue == ClientState.HandShake)
+ {
+ Interlocked.Increment(ref _botsHandshakeCounter);
+ }
+ else if (state.NewValue == ClientState.Login)
+ {
+ Interlocked.Increment(ref _botsLoginCounter);
+ }
+ };
+ Action onError = (exc) =>
+ {
- stopwatch.Stop();
+ var state = b.Client.CurrentState;
- if (stopwatch.Elapsed.Microseconds < 1000)
- {
- Thread.Sleep(1000 - stopwatch.Elapsed.Microseconds);
- }
- stopwatch.Reset();
+ if (state == ClientState.Play)
+ {
+ Interlocked.Decrement(ref _botsPlayCounter);
}
- }
- catch
+ else if (state == ClientState.Connecting)
+ {
+ Interlocked.Decrement(ref _botsConnectionCounter);
+ }
+ else if (state == ClientState.HandShake)
+ {
+ Interlocked.Decrement(ref _botsHandshakeCounter);
+ }
+ else if (state == ClientState.Login)
+ {
+ Interlocked.Decrement(ref _botsLoginCounter);
+ }
+
+ //Console.WriteLine(ex.GetType().Name);
+ //Console.WriteLine(ex.Message);
+ //Console.WriteLine(ex.StackTrace);
+
+ var key = Tuple.Create(exc.GetType().FullName, exc.Message);
+
+ if (ExceptionCounter.TryGetValue(key, out var counter))
+ {
+ counter.Increment();
+ }
+ else
+ {
+ ExceptionCounter[key] = new ExceptionCounter();
+ }
+ };
+
+ b.Client.OnStateChanged += onState;
+ b.Client.OnErrored += onError;
+
+ _disposables.Add(Disposable.Create(() =>
{
+ b.Client.OnStateChanged -= onState;
+ b.Client.OnErrored -= onError;
+ }));
+
+
+
+ stressTestBots.Add(b);
+ bot.DisposeWith(_disposables);
+
+ }
+
+
+
+ var metricsThread = new Thread(() =>
+ {
+ Stopwatch stopwatch = new();
+
+ while (!cancellationTokenSource.IsCancellationRequested)
+ {
+ stopwatch.Start();
+ var cps = Interlocked.Exchange(ref _cpsCounter, 0);
+
+ var botsOnline = Volatile.Read(ref _botsPlayCounter);
+
+ _dataPerSecond.OnNext(new StressTestMetrik(cps, botsOnline));
+
+
+ stopwatch.Stop();
+ if (stopwatch.Elapsed.Microseconds < 1000)
+ {
+ Thread.Sleep(1000 - stopwatch.Elapsed.Microseconds);
+ }
+ stopwatch.Reset();
}
})
{
Name = "Stress test counter",
-
IsBackground = true
- }.Start();
+ };
+
+
CompositeDisposable disposables = new();
_disposables.Add(disposables);
@@ -243,42 +368,27 @@ public async Task Start(Serilog.ILogger logger)
_cleanUp = _disposables;
-
+ logger.Information("Запуск поведения");
if (Behavior is not null)
{
- logger.Information("Загружено поведение: " + Behavior.GetType().FullName);
await Behavior.Activate(disposables, stressTestBots, cancellationTokenSource.Token);
}
- else
- {
- DefaultBehavior testBehavior = new();
- await testBehavior.Activate(disposables, stressTestBots, cancellationTokenSource.Token);
- }
+ logger.Information("Поведение запущено");
+
+ metricsThread.Start();
+
+ logger.Information("Запущены потоки чтения метрик");
+
CurrentState = StressTestServiceState.Running;
}
- catch
+ catch (Exception ex)
{
+ logger.Error(ex, "Не удалось запустить стресс тест");
+
CurrentState = StressTestServiceState.None;
}
}
- private void Bot_StateChanged(object? sender, McProtoNet.StateChangedEventArgs e)
- {
- //Console.WriteLine(e.NewState);
- if (e.NewState == ClientState.Play)
- {
- //Console.WriteLine("Play");
- Interlocked.Increment(ref _botsOnlineCounter);
- Interlocked.Increment(ref _cpsCounter);
- }
- else if (e.NewState == ClientState.Failed)
- {
- if (e.OldState == ClientState.Play)
- {
- Interlocked.Decrement(ref _botsOnlineCounter);
- }
- }
- }
private async Task LoadProxy(Serilog.ILogger logger)
@@ -289,30 +399,31 @@ private void Bot_StateChanged(object? sender, McProtoNet.StateChangedEventArgs e
return null;
}
- logger.Information("Загрузка прокси");
- var sources = this.Proxies.Items.ToList();
+ var sources = this.Proxies.Items.ToList();
+ logger.Information("Загрузка прокси");
if (sources.Count() == 0)
{
sources.Add(new UrlProxySource(QuickProxyNet.ProxyType.HTTP, "https://raw.githubusercontent.com/TheSpeedX/PROXY-List/master/http.txt"));
sources.Add(new UrlProxySource(QuickProxyNet.ProxyType.SOCKS4, "https://raw.githubusercontent.com/TheSpeedX/PROXY-List/master/socks4.txt"));
sources.Add(new UrlProxySource(QuickProxyNet.ProxyType.SOCKS5, "https://raw.githubusercontent.com/TheSpeedX/PROXY-List/master/socks5.txt"));
}
+
List>> tasks = new();
foreach (var s in sources)
{
tasks.Add(s.GetProxiesAsync());
}
-
+
var result = await Task.WhenAll(tasks);
var proxies = result.SelectMany(x => x).ToList();
-
+
var provider = new ProxyProvider(proxies);
var group = proxies.GroupBy(x => x.Type).Select(x => $"{x.Key} - {x.Count()}");
-
+
logger.Information($"Загружено {proxies.Count} прокси. {string.Join(", ", group)}");
return provider;
diff --git a/src/CoreLibs/HolyClient.StressTest/TidePVPBehaviorAttack.cs b/src/CoreLibs/HolyClient.StressTest/TidePVPBehaviorAttack.cs
new file mode 100644
index 00000000..459d1d14
--- /dev/null
+++ b/src/CoreLibs/HolyClient.StressTest/TidePVPBehaviorAttack.cs
@@ -0,0 +1,14 @@
+using HolyClient.Abstractions.StressTest;
+using System.Reactive.Disposables;
+
+namespace HolyClient.StressTest
+{
+ public class TidePVPBehaviorAttack : IStressTestBehavior
+ {
+ public Task Activate(CompositeDisposable disposables, IEnumerable bots, CancellationToken cancellationToken)
+ {
+ return Task.CompletedTask;
+ }
+ }
+
+}
diff --git a/src/HolyClient/App.axaml.cs b/src/HolyClient/App.axaml.cs
index ec8e7813..14d453dc 100644
--- a/src/HolyClient/App.axaml.cs
+++ b/src/HolyClient/App.axaml.cs
@@ -6,10 +6,14 @@
using Avalonia.Styling;
using HolyClient.AppState;
using HolyClient.Localization;
+using HolyClient.Models;
using HolyClient.ViewModels;
using HolyClient.Views;
+using ReactiveUI;
using Splat;
using System;
+using System.Reflection;
+using System.Threading;
[assembly: XmlnsDefinition("https://github.com/avaloniaui", "HolyClient.Assets.Fonts.Roboto")]
[assembly: XmlnsDefinition("https://github.com/avaloniaui", "HolyClient.Localization")]
@@ -35,28 +39,34 @@ public override void Initialize()
public override void OnFrameworkInitializationCompleted()
{
+ ThreadPool.GetMinThreads(out var min, out var cpt);
+ ThreadPool.SetMinThreads(1, cpt);
- MainViewModel mainViewModel = new();
- Locator.CurrentMutable.RegisterConstant(mainViewModel);
+
+
+
+ RootViewModel root = new();
+ Locator.CurrentMutable.RegisterConstant(root, "Root");
+
+ Locator.CurrentMutable.RegisterViewsForViewModels(Assembly.GetExecutingAssembly());
+
+
try
{
- MainView view = new MainView
+
+
+ RootView rootView = new()
{
- DataContext = mainViewModel
+ DataContext = root
};
- Locator.CurrentMutable.RegisterConstant(view);
+
if (ApplicationLifetime is IClassicDesktopStyleApplicationLifetime desktop)
{
var wnd = new MainWindow()
{
- Content = view
- };
- wnd.Opened += (s, e) =>
- {
- var state = Locator.Current.GetService();
- mainViewModel.OnLoadState(state);
- };
+ Content = rootView
+ };
desktop.MainWindow = wnd;
}
@@ -72,7 +82,7 @@ public override void OnFrameworkInitializationCompleted()
}
catch (Exception e)
{
- Console.WriteLine(e);
+
if (ApplicationLifetime is ISingleViewApplicationLifetime single)
{
single.MainView = new TextBlock()
diff --git a/src/HolyClient/AppState/BehaviorKey.cs b/src/HolyClient/AppState/BehaviorKey.cs
new file mode 100644
index 00000000..d3b02dd4
--- /dev/null
+++ b/src/HolyClient/AppState/BehaviorKey.cs
@@ -0,0 +1,40 @@
+using MessagePack;
+using System;
+
+namespace HolyClient.AppState;
+
+[MessagePackObject]
+public struct BehaviorKey
+{
+ [Key(0)]
+ public readonly string Name;
+ [Key(1)]
+ public readonly string Assembly;
+
+ public BehaviorKey(string name, string assembly)
+ {
+ Name = name;
+ Assembly = assembly;
+ }
+
+
+ public bool Equals(BehaviorKey c)
+ => c is BehaviorKey
+ && (Name, Assembly)
+ == (c.Name, c.Assembly);
+
+ public override bool Equals(object o)
+ => (o is BehaviorKey c) && Equals(c);
+
+ public static bool operator ==(in BehaviorKey c1, in BehaviorKey c2)
+ => Equals(c1, c2);
+
+ public static bool operator !=(in BehaviorKey c1, in BehaviorKey c2)
+ => !Equals(c1, c2);
+
+
+
+ public override int GetHashCode()
+ => HashCode.Combine(Name, Assembly);
+
+}
diff --git a/src/HolyClient/AppState/MainState.cs b/src/HolyClient/AppState/MainState.cs
index 2fded914..0de2dc81 100644
--- a/src/HolyClient/AppState/MainState.cs
+++ b/src/HolyClient/AppState/MainState.cs
@@ -4,6 +4,7 @@
using MessagePack;
using ReactiveUI;
using ReactiveUI.Fody.Helpers;
+using System.Collections;
namespace HolyClient.AppState;
@@ -18,19 +19,13 @@ public class MainState : ReactiveObject
[Key(1)]
public Page SelectedPage { get; set; }
-
[Reactive]
- [Key(2)]
- public IBotManager BotManagerState { get; set; } = new BotManager();
-
+ [Key(5)]
+ public StressTestState StressTest { get; set; } = new();
- [Reactive]
- [Key(3)]
- public IStressTest StressTestState { get; set; } = new HolyClient.StressTest.StressTest();
[Reactive]
[Key(4)]
public ExtensionManagerState ExtensionManagerState { get; set; } = new();
}
-
diff --git a/src/HolyClient/AppState/StressTestState.cs b/src/HolyClient/AppState/StressTestState.cs
new file mode 100644
index 00000000..49a3dd5b
--- /dev/null
+++ b/src/HolyClient/AppState/StressTestState.cs
@@ -0,0 +1,33 @@
+using DynamicData;
+using HolyClient.Core.Infrastructure;
+using HolyClient.StressTest;
+using MessagePack;
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Threading.Tasks;
+
+namespace HolyClient.AppState;
+
+[MessagePackObject(keyAsPropertyName: true)]
+public sealed class StressTestState
+{
+ [IgnoreMember]
+ public SourceCache Profiles { get; } = new(x => x.Id);
+
+ public Guid SelectedProfileId { get; set; }
+
+ public IEnumerable ProfilesStates
+ {
+ get => Profiles.Items.ToList();
+ set => Profiles.AddOrUpdate(value);
+ }
+
+ internal async Task Initialization(IPluginProvider? pluginProvider)
+ {
+ foreach (var p in ProfilesStates)
+ {
+ await p.Initialization(pluginProvider);
+ }
+ }
+}
diff --git a/src/HolyClient/BootStrap.cs b/src/HolyClient/BootStrap.cs
index ca84e8bd..2517e5ab 100644
--- a/src/HolyClient/BootStrap.cs
+++ b/src/HolyClient/BootStrap.cs
@@ -47,6 +47,11 @@ await Task.Run(async () =>
var state = RxApp.SuspensionHost.GetAppState();
+
+
+
+
+
Loc.Instance.CurrentLanguage = state.SettingsState.Language;
progress.OnNext("Bootstrap.LoadingState.LoadPlugins");
@@ -67,11 +72,11 @@ await Task.Run(async () =>
Locator.CurrentMutable.RegisterConstant(new PluginProvider());
- await state.BotManagerState.Initialization();
- await state.StressTestState.Initialization(Locator.Current.GetService());
+
+ await state.StressTest.Initialization(Locator.Current.GetService());
progress.OnNext("Bootstrap.LoadingState.AlmostDone");
@@ -124,12 +129,23 @@ await Dispatcher.UIThread.InvokeAsync(() =>
progress.OnCompleted();
});
+
+ var mainState = Locator.Current.GetService();
+
+ var mainViewModel = new MainViewModel(mainState);
+
+ Locator.CurrentMutable.RegisterConstant(mainViewModel, "Main");
+ Locator.CurrentMutable.RegisterConstant(mainViewModel);
+
+ var root = Locator.Current.GetService("Root");
+
+ await root.Router.Navigate.Execute(mainViewModel);
}
private static void RegisterViewModels()
{
- Locator.CurrentMutable.RegisterViewsForViewModels(Assembly.GetExecutingAssembly());
+
}
@@ -137,16 +153,14 @@ private static void RegisterViewModels()
private static void RegisterStates(MainState state)
{
Locator.CurrentMutable.RegisterConstant(state);
- Locator.CurrentMutable.RegisterConstant(state.BotManagerState);
Locator.CurrentMutable.RegisterConstant(state.SettingsState);
- Locator.CurrentMutable.RegisterConstant(state.StressTestState);
+ Locator.CurrentMutable.RegisterConstant(state.StressTest);
}
private static void RegisterPages()
{
Locator.CurrentMutable.RegisterConstant(new HomeViewModel(), nameof(Page.Home));
- Locator.CurrentMutable.RegisterConstant(new BotManagerViewModel(), nameof(Page.BotManager));
Locator.CurrentMutable.RegisterConstant(new SettingsViewModel(), nameof(Page.Settings));
Locator.CurrentMutable.RegisterConstant(new StressTestViewModel(), nameof(Page.StressTest));
Locator.CurrentMutable.RegisterConstant(new ManagingExtensionsViewModel(), nameof(Page.ManagingExtensions));
diff --git a/src/HolyClient/Commands/StartStopBotCommand.cs b/src/HolyClient/Commands/StartStopBotCommand.cs
deleted file mode 100644
index 0efd1548..00000000
--- a/src/HolyClient/Commands/StartStopBotCommand.cs
+++ /dev/null
@@ -1,50 +0,0 @@
-using HolyClient.Contracts.Models;
-using ReactiveUI;
-using ReactiveUI.Fody.Helpers;
-using System;
-using System.Windows.Input;
-
-namespace HolyClient.Commands
-{
- public class StartStopBotCommand : ReactiveObject, ICommand
- {
-
- private IBotProfile _profile;
- private Serilog.ILogger _logger;
- [Reactive]
- public bool IsActivate { get; private set; } = false;
-
-
- public StartStopBotCommand(IBotProfile profile, Serilog.ILogger logger)
- {
- _logger = logger;
- _profile = profile;
- }
- private bool _canExectute;
-
- public event EventHandler? CanExecuteChanged;
-
- private void RaiseCanExecute()
- {
-
- }
-
- public bool CanExecute(object? parameter)
- {
- return true;
- }
-
- public void Execute(object? parameter)
- {
- if (IsActivate)
- {
- _profile.Stop();
- }
- else
- {
- _profile.Start(this._logger);
- }
- IsActivate = !IsActivate;
- }
- }
-}
diff --git a/src/HolyClient/Commands/StartStressTestCommand.cs b/src/HolyClient/Commands/StartStressTestCommand.cs
deleted file mode 100644
index adad22ac..00000000
--- a/src/HolyClient/Commands/StartStressTestCommand.cs
+++ /dev/null
@@ -1,189 +0,0 @@
-using HolyClient.StressTest;
-using HolyClient.ViewModels;
-using ReactiveUI;
-using Serilog;
-using System;
-using System.Collections.Concurrent;
-using System.Collections.Generic;
-using System.Linq;
-using System.Reactive.Disposables;
-using System.Reactive.Linq;
-using System.Threading;
-using System.Threading.Tasks;
-using System.Windows.Input;
-
-namespace HolyClient.Commands
-{
- public class StartStressTestCommand : ICommand, IDisposable
- {
- public event EventHandler? CanExecuteChanged;
-
- private IScreen screen;
- private IStressTest _model;
- private IDisposable? _cleanUp = null;
- private bool _canExecute;
- public StartStressTestCommand(IScreen screen, IStressTest model, IObservable canExecute)
- {
- CompositeDisposable d = new();
-
- this.screen = screen;
- _model = model;
-
- canExecute.Subscribe(x =>
- {
- Console.WriteLine("CanExecute: " + _canExecute);
- _canExecute = x;
- this.CanExecuteChanged?.Invoke(this, new EventArgs());
- }).DisposeWith(d);
-
- _cleanUp = d;
- }
-
- public bool CanExecute(object? parameter)
- {
- return _canExecute;
- }
- public async void Execute(object? parameter)
- {
- if (!_canExecute)
- return;
-
- Thread.CurrentThread.Priority = ThreadPriority.Highest;
- LoggerWrapper loggerWrapper = new LoggerWrapper();
-
- ILogger logger = loggerWrapper;
-
- try
- {
-
-
-
- StressTestLoadingViewModel loadingVM = new StressTestLoadingViewModel(this.screen, _model);
-
- StressTestProcessViewModel proccess = new StressTestProcessViewModel(this.screen, _model, loggerWrapper);
- await screen.Router.Navigate.Execute(loadingVM);
-
-
-
- await _model.Start(logger);
- //await Task.Factory.StartNew(() =>, default, TaskCreationOptions.LongRunning, StaScheduler).Unwrap();
-
-
- await screen.Router.Navigate.Execute(proccess);
-
- }
- catch (TaskCanceledException)
- {
- logger.Information("[STRESS TEST] Завершился из-за отмены");
- }
- catch (Exception ex)
- {
- logger.Error(ex, "[STRESS TEST] завершился с ошибкой");
- }
- finally
- {
-
- }
-
- }
-
- public void Dispose()
- {
- Interlocked.Exchange(ref _cleanUp, null)?.Dispose();
- }
- }
- public sealed class StaTaskScheduler : TaskScheduler, IDisposable
- {
- /// Stores the queued tasks to be executed by our pool of STA threads.
- private BlockingCollection _tasks;
- /// The STA threads used by the scheduler.
- private readonly List _threads;
-
- /// Initializes a new instance of the StaTaskScheduler class with the specified concurrency level.
- /// The number of threads that should be created and used by this scheduler.
- public StaTaskScheduler(int numberOfThreads)
- {
- // Validate arguments
- if (numberOfThreads < 1) throw new ArgumentOutOfRangeException("concurrencyLevel");
-
- // Initialize the tasks collection
- _tasks = new BlockingCollection();
-
- // Create the threads to be used by this scheduler
- _threads = Enumerable.Range(0, numberOfThreads).Select(i =>
- {
- var thread = new Thread(() =>
- {
- // Continually get the next task and try to execute it.
- // This will continue until the scheduler is disposed and no more tasks remain.
- foreach (var t in _tasks.GetConsumingEnumerable())
- {
- TryExecuteTask(t);
- }
- });
- thread.Name = "STA THREAD";
- thread.IsBackground = true;
- // thread.SetApartmentState(ApartmentState.STA);
- return thread;
- }).ToList();
-
- // Start all of the threads
- _threads.ForEach(t => t.Start());
- }
-
- /// Queues a Task to be executed by this scheduler.
- /// The task to be executed.
- protected override void QueueTask(Task task)
- {
- // Push it into the blocking collection of tasks
- _tasks.Add(task);
- }
-
- /// Provides a list of the scheduled tasks for the debugger to consume.
- /// An enumerable of all tasks currently scheduled.
- protected override IEnumerable GetScheduledTasks()
- {
- // Serialize the contents of the blocking collection of tasks for the debugger
- return _tasks.ToArray();
- }
-
- /// Determines whether a Task may be inlined.
- /// The task to be executed.
- /// Whether the task was previously queued.
- /// true if the task was successfully inlined; otherwise, false.
- protected override bool TryExecuteTaskInline(Task task, bool taskWasPreviouslyQueued)
- {
- // Try to inline if the current thread is STA
- return
- Thread.CurrentThread.GetApartmentState() == ApartmentState.STA &&
- TryExecuteTask(task);
- }
-
- /// Gets the maximum concurrency level supported by this scheduler.
- public override int MaximumConcurrencyLevel
- {
- get { return _threads.Count; }
- }
-
- ///
- /// Cleans up the scheduler by indicating that no more tasks will be queued.
- /// This method blocks until all threads successfully shutdown.
- ///
- public void Dispose()
- {
- if (_tasks != null)
- {
- // Indicate that no new tasks will be coming in
- _tasks.CompleteAdding();
-
- // Wait for all threads to finish processing tasks
- foreach (var thread in _threads) thread.Join();
-
- // Cleanup
- _tasks.Dispose();
- _tasks = null;
- }
- }
- }
-
-}
diff --git a/src/HolyClient/Commands/StopStressTestCommand.cs b/src/HolyClient/Commands/StopStressTestCommand.cs
deleted file mode 100644
index f3a8bba0..00000000
--- a/src/HolyClient/Commands/StopStressTestCommand.cs
+++ /dev/null
@@ -1,32 +0,0 @@
-using HolyClient.StressTest;
-using HolyClient.ViewModels;
-using ReactiveUI;
-using System;
-using System.Reactive.Linq;
-using System.Windows.Input;
-
-namespace HolyClient.Commands
-{
- public class StopStressTestCommand : ICommand
- {
- public event EventHandler? CanExecuteChanged;
- private readonly IScreen screen;
- private readonly IStressTest stressTest;
- public StopStressTestCommand(IScreen screen, IStressTest stressTest)
- {
- this.screen = screen;
- this.stressTest = stressTest;
- }
-
- public bool CanExecute(object? parameter)
- {
- return true;
- }
-
- public async void Execute(object? parameter)
- {
- await this.stressTest.Stop();
- await screen.Router.NavigateAndReset.Execute(new StressTestConfigurationViewModel(screen, stressTest));
- }
- }
-}
diff --git a/src/HolyClient/DesignTime/DesignBotManagerViewModel.cs b/src/HolyClient/DesignTime/DesignBotManagerViewModel.cs
deleted file mode 100644
index 67656add..00000000
--- a/src/HolyClient/DesignTime/DesignBotManagerViewModel.cs
+++ /dev/null
@@ -1,59 +0,0 @@
-using HolyClient.ViewModels;
-using ReactiveUI;
-using System;
-using System.Collections.ObjectModel;
-using System.ComponentModel;
-using System.Linq;
-using System.Reactive;
-
-namespace HolyClient.DesignTime
-{
- public class DesignBotManagerViewModel : IBotManagerViewModel
- {
- public DesignBotManagerViewModel()
- {
- ObservableCollection profiles = new()
- {
- new DesignBotProfileViewModel()
- {
- Name = "New Profile"
- }
- };
- Profiles = new(profiles);
- SelectedProfile = Profiles.First();
-
- }
-
- public ViewModelActivator Activator { get; } = new();
-
- public ReactiveCommand CreateProfileCommand { get; }
-
- public IScreen HostScreen => null;
-
- public ReadOnlyObservableCollection Profiles { get; }
-
- public RoutingState Router { get; set; }
-
-
- public IBotProfileViewModel SelectedProfile { get; set; }
-
- public string? UrlPathSegment => "botManager";
-
- public Interaction Dialog => throw new NotImplementedException();
-
- public ReactiveCommand RemoveProfileCommand => throw new NotImplementedException();
-
- public event PropertyChangedEventHandler? PropertyChanged;
- public event PropertyChangingEventHandler? PropertyChanging;
-
- public void RaisePropertyChanged(PropertyChangedEventArgs args)
- {
- throw new NotImplementedException();
- }
-
- public void RaisePropertyChanging(PropertyChangingEventArgs args)
- {
- throw new NotImplementedException();
- }
- }
-}
diff --git a/src/HolyClient/DesignTime/DesignBottingProfileViewModel.cs b/src/HolyClient/DesignTime/DesignBottingProfileViewModel.cs
deleted file mode 100644
index 3f39d06f..00000000
--- a/src/HolyClient/DesignTime/DesignBottingProfileViewModel.cs
+++ /dev/null
@@ -1,40 +0,0 @@
-using HolyClient.ViewModels;
-using McProtoNet;
-using ReactiveUI;
-using System;
-using System.Collections.ObjectModel;
-using System.Windows.Input;
-
-namespace HolyClient.DesignTime
-{
- public class DesignBotProfileViewModel : IBotProfileViewModel
- {
- public ViewModelActivator Activator { get; }
-
- public Guid Id => Guid.NewGuid();
-
- public string Name { get; set; }
-
- public ICommand StartBotCommand => throw new NotImplementedException();
-
- public ICommand StopBotCommand => throw new NotImplementedException();
-
- public RoutingState Router => throw new NotImplementedException();
-
-
-
- public ObservableCollection LogItems => throw new NotImplementedException();
-
- public ConsoleViewModel Console => throw new NotImplementedException();
-
- public string Server { get => throw new NotImplementedException(); set => throw new NotImplementedException(); }
- public string Nickname { get => throw new NotImplementedException(); set => throw new NotImplementedException(); }
- public MinecraftVersion Version { get => throw new NotImplementedException(); set => throw new NotImplementedException(); }
- public int SelectedTab { get => throw new NotImplementedException(); set => throw new NotImplementedException(); }
-
- public void Dispose()
- {
-
- }
- }
-}
diff --git a/src/HolyClient/Designer/CustomStyles.axaml b/src/HolyClient/Designer/CustomStyles.axaml
index 5138b575..cf7ecc58 100644
--- a/src/HolyClient/Designer/CustomStyles.axaml
+++ b/src/HolyClient/Designer/CustomStyles.axaml
@@ -12,7 +12,7 @@
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
diff --git a/src/HolyClient/Views/Pages/BotManager/BotProfileView.axaml.cs b/src/HolyClient/Views/Pages/BotManager/BotProfileView.axaml.cs
deleted file mode 100644
index d7d02c75..00000000
--- a/src/HolyClient/Views/Pages/BotManager/BotProfileView.axaml.cs
+++ /dev/null
@@ -1,14 +0,0 @@
-using Avalonia.ReactiveUI;
-using HolyClient.ViewModels;
-using ReactiveUI;
-
-namespace HolyClient.Views;
-
-public partial class BotProfileView : ReactiveUserControl
-{
- public BotProfileView()
- {
- InitializeComponent();
- this.WhenActivated(x => { });
- }
-}
\ No newline at end of file
diff --git a/src/HolyClient/Views/Pages/BotManager/EmptyProfilesView.axaml.cs b/src/HolyClient/Views/Pages/BotManager/EmptyProfilesView.axaml.cs
deleted file mode 100644
index 0eb933c4..00000000
--- a/src/HolyClient/Views/Pages/BotManager/EmptyProfilesView.axaml.cs
+++ /dev/null
@@ -1,12 +0,0 @@
-using Avalonia.Controls;
-
-namespace HolyClient.Views
-{
- public partial class EmptyProfilesView : UserControl
- {
- public EmptyProfilesView()
- {
- InitializeComponent();
- }
- }
-}
diff --git a/src/HolyClient/Views/Pages/StressTest/StressTestLoadingView.axaml b/src/HolyClient/Views/Pages/StressTest/StressTestLoadingView.axaml
deleted file mode 100644
index a0d0cf1a..00000000
--- a/src/HolyClient/Views/Pages/StressTest/StressTestLoadingView.axaml
+++ /dev/null
@@ -1,14 +0,0 @@
-
-
-
-
-
-
-
-
-
diff --git a/src/HolyClient/Views/Pages/StressTest/StressTestLoadingView.axaml.cs b/src/HolyClient/Views/Pages/StressTest/StressTestLoadingView.axaml.cs
deleted file mode 100644
index e2ae7952..00000000
--- a/src/HolyClient/Views/Pages/StressTest/StressTestLoadingView.axaml.cs
+++ /dev/null
@@ -1,12 +0,0 @@
-using Avalonia.ReactiveUI;
-using HolyClient.ViewModels;
-
-namespace HolyClient.Views;
-
-public partial class StressTestLoadingView : ReactiveUserControl
-{
- public StressTestLoadingView()
- {
- InitializeComponent();
- }
-}
\ No newline at end of file
diff --git a/src/HolyClient/Views/Pages/StressTest/StressTestProcessView.axaml b/src/HolyClient/Views/Pages/StressTest/StressTestProcessView.axaml
index 0cfaceb3..0d8d9e92 100644
--- a/src/HolyClient/Views/Pages/StressTest/StressTestProcessView.axaml
+++ b/src/HolyClient/Views/Pages/StressTest/StressTestProcessView.axaml
@@ -5,6 +5,7 @@
xmlns:lvc="using:LiveChartsCore.SkiaSharpView.Avalonia"
xmlns:designTime="using:HolyClient.DesignTime"
x:CompileBindings="True"
+
xmlns:sg="using:SpacedGridControl.Avalonia"
xmlns:vm="using:HolyClient.ViewModels"
x:DataType="vm:StressTestProcessViewModel"
@@ -16,44 +17,99 @@
+
-
+
-
+
-
+
-
+
-
-
-
-
-
-
-
-
-
-
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/src/HolyClient/Views/Pages/StressTest/StressTestConfigurationView.axaml b/src/HolyClient/Views/Pages/StressTest/StressTestProfileView.axaml
similarity index 88%
rename from src/HolyClient/Views/Pages/StressTest/StressTestConfigurationView.axaml
rename to src/HolyClient/Views/Pages/StressTest/StressTestProfileView.axaml
index 550490a2..f6845537 100644
--- a/src/HolyClient/Views/Pages/StressTest/StressTestConfigurationView.axaml
+++ b/src/HolyClient/Views/Pages/StressTest/StressTestProfileView.axaml
@@ -1,319 +1,341 @@
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/src/HolyClient/Views/Pages/StressTest/StressTestConfigurationView.axaml.cs b/src/HolyClient/Views/Pages/StressTest/StressTestProfileView.axaml.cs
similarity index 86%
rename from src/HolyClient/Views/Pages/StressTest/StressTestConfigurationView.axaml.cs
rename to src/HolyClient/Views/Pages/StressTest/StressTestProfileView.axaml.cs
index a89b56e3..3da644a1 100644
--- a/src/HolyClient/Views/Pages/StressTest/StressTestConfigurationView.axaml.cs
+++ b/src/HolyClient/Views/Pages/StressTest/StressTestProfileView.axaml.cs
@@ -1,98 +1,87 @@
-using Avalonia;
-using Avalonia.Controls;
-using Avalonia.ReactiveUI;
-using FluentAvalonia.UI.Controls;
-using HolyClient.Localization;
-using HolyClient.ViewModels;
-using HolyClient.Views;
-using ReactiveUI;
-using System.Reactive;
-using System.Reactive.Disposables;
-
-namespace HolyClient.Views;
-
-public partial class StressTestConfigurationView : ReactiveUserControl
-{
-
- public StressTestConfigurationView()
- {
+using Avalonia;
+using Avalonia.Controls;
+using Avalonia.Markup.Xaml;
+using Avalonia.ReactiveUI;
+using FluentAvalonia.UI.Controls;
+using HolyClient.Localization;
+using HolyClient.ViewModels;
+using ReactiveUI;
+using System.Reactive.Disposables;
+
+namespace HolyClient.Views;
+
+public partial class StressTestProfileView : ReactiveUserControl
+{
+ public StressTestProfileView()
+ {
InitializeComponent();
+ this.WhenActivated(d =>
+ {
+
+ var NotificationManager = new Avalonia.Controls.Notifications.WindowNotificationManager(TopLevel.GetTopLevel(this));
+
+ NotificationManager.Position = Avalonia.Controls.Notifications.NotificationPosition.BottomRight;
+
+ this.ViewModel.SelectProxyImportSourceDialog.RegisterHandler(async x =>
+ {
+ ContentDialog dialog = new ContentDialog()
+ {
+ Title = Loc.Tr("StressTest.Configuration.Proxy.Dialog.SelectSource"),
+ PrimaryButtonText = Loc.Tr("Next"),
+ IsSecondaryButtonEnabled = false,
+ CloseButtonText = Loc.Tr("Cancel"),
+ Content = new SelectImportSourceProxyDialogContent()
+ {
+ DataContext = x.Input
+ }
+ };
+ var result = await dialog.ShowAsync();
+
+
+ x.SetOutput(result == ContentDialogResult.Primary);
+ }).DisposeWith(d);
+ this.ViewModel.ImportProxyDialog.RegisterHandler(async x =>
+ {
+
+ ContentDialog dialog = new ContentDialog()
+ {
+ Title = Loc.Tr($"StressTest.Configuration.Proxy.Dialog.SelectSource.{x.Input.Title}"),
+ PrimaryButtonText = Loc.Tr("Add"),
+ IsSecondaryButtonEnabled = false,
+ CloseButtonText = Loc.Tr("Cancel"),
+ Content = x.Input
+
+ };
+ var result = await dialog.ShowAsync();
+
+
+ x.SetOutput(result == ContentDialogResult.Primary);
+ }).DisposeWith(d);
+
+
+
+
+ this.ViewModel.ConfirmDeleteProxyDialog.RegisterHandler(async x =>
+ {
+ ContentDialog dialog = new ContentDialog()
+ {
+ Title = Loc.Tr("StressTest.Configuration.Proxy.Dialog.ConfirmDeleteQuestion"),
+ PrimaryButtonText = Loc.Tr("Yes"),
+ IsSecondaryButtonEnabled = false,
+ CloseButtonText = Loc.Tr("No")
+
+ };
+
+
+
+ var result = await dialog.ShowAsync();
+
+ x.SetOutput(result == ContentDialogResult.Primary);
+ });
+
- this.WhenActivated(d =>
- {
-
- var NotificationManager = new Avalonia.Controls.Notifications.WindowNotificationManager(TopLevel.GetTopLevel(this));
-
- NotificationManager.Position = Avalonia.Controls.Notifications.NotificationPosition.BottomRight;
-
- this.ViewModel.SelectProxyImportSourceDialog.RegisterHandler(async x =>
- {
- ContentDialog dialog = new ContentDialog()
- {
- Title = Loc.Tr("StressTest.Configuration.Proxy.Dialog.SelectSource"),
- PrimaryButtonText = Loc.Tr("Next"),
- IsSecondaryButtonEnabled = false,
- CloseButtonText = Loc.Tr("Cancel"),
- Content = new SelectImportSourceProxyDialogContent()
- {
- DataContext = x.Input
- }
- };
- var result = await dialog.ShowAsync();
-
-
- x.SetOutput(result == ContentDialogResult.Primary);
- }).DisposeWith(d);
- this.ViewModel.ImportProxyDialog.RegisterHandler(async x =>
- {
-
- ContentDialog dialog = new ContentDialog()
- {
- Title = Loc.Tr($"StressTest.Configuration.Proxy.Dialog.SelectSource.{x.Input.Title}"),
- PrimaryButtonText = Loc.Tr("Add"),
- IsSecondaryButtonEnabled = false,
- CloseButtonText = Loc.Tr("Cancel"),
- Content = x.Input
-
- };
- var result = await dialog.ShowAsync();
-
-
- x.SetOutput(result == ContentDialogResult.Primary);
- }).DisposeWith(d);
-
-
-
-
- this.ViewModel.ConfirmDeleteProxyDialog.RegisterHandler(async x =>
- {
- ContentDialog dialog = new ContentDialog()
- {
- Title = Loc.Tr("StressTest.Configuration.Proxy.Dialog.ConfirmDeleteQuestion"),
- PrimaryButtonText = Loc.Tr("Yes"),
- IsSecondaryButtonEnabled = false,
- CloseButtonText = Loc.Tr("No")
-
- };
-
-
-
- var result = await dialog.ShowAsync();
-
- x.SetOutput(result == ContentDialogResult.Primary);
- });
-
-
-
- });
- }
- protected override void OnAttachedToVisualTree(VisualTreeAttachmentEventArgs e)
- {
- base.OnAttachedToVisualTree(e);
-
-
-
- }
+ });
+ }
}
\ No newline at end of file
diff --git a/src/HolyClient/Views/Pages/StressTest/StressTestView.axaml b/src/HolyClient/Views/Pages/StressTest/StressTestView.axaml
index 7a09688b..4d495e62 100644
--- a/src/HolyClient/Views/Pages/StressTest/StressTestView.axaml
+++ b/src/HolyClient/Views/Pages/StressTest/StressTestView.axaml
@@ -3,7 +3,57 @@
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
mc:Ignorable="d" d:DesignWidth="800" d:DesignHeight="450"
+ xmlns:ui="using:FluentAvalonia.UI.Controls"
xmlns:reactive="http://reactiveui.net"
+ xmlns:views="using:HolyClient.Views"
x:Class="HolyClient.Views.StressTestView">
-
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/src/HolyClient/Views/Pages/StressTest/StressTestView.axaml.cs b/src/HolyClient/Views/Pages/StressTest/StressTestView.axaml.cs
index e175a5c0..f9a43fc8 100644
--- a/src/HolyClient/Views/Pages/StressTest/StressTestView.axaml.cs
+++ b/src/HolyClient/Views/Pages/StressTest/StressTestView.axaml.cs
@@ -1,5 +1,8 @@
using Avalonia.ReactiveUI;
+using FluentAvalonia.UI.Controls;
+using HolyClient.Localization;
using HolyClient.ViewModels;
+using ReactiveUI;
namespace HolyClient.Views;
@@ -8,5 +11,32 @@ public partial class StressTestView : ReactiveUserControl
public StressTestView()
{
InitializeComponent();
+
+ this.WhenActivated(d =>
+ {
+
+
+
+
+
+ this.ViewModel.ConfirmRemoveDialog.RegisterHandler(async x =>
+ {
+ ContentDialog dialog = new ContentDialog()
+ {
+ Title = "Вы точно хотите удалить?",
+ PrimaryButtonText = Loc.Tr("Yes"),
+ IsSecondaryButtonEnabled = false,
+ CloseButtonText = Loc.Tr("No")
+
+ };
+
+ var result = await dialog.ShowAsync();
+
+ x.SetOutput(result == ContentDialogResult.Primary);
+ });
+
+
+
+ });
}
}
\ No newline at end of file
diff --git a/src/HolyClient/Views/Pages/BotManager/EmptyProfilesView.axaml b/src/HolyClient/Views/RootView.axaml
similarity index 53%
rename from src/HolyClient/Views/Pages/BotManager/EmptyProfilesView.axaml
rename to src/HolyClient/Views/RootView.axaml
index 10ef412e..5c3fa2cc 100644
--- a/src/HolyClient/Views/Pages/BotManager/EmptyProfilesView.axaml
+++ b/src/HolyClient/Views/RootView.axaml
@@ -1,12 +1,9 @@
-
-
-
-
-
-
+
+
+
diff --git a/src/HolyClient/Views/RootView.axaml.cs b/src/HolyClient/Views/RootView.axaml.cs
new file mode 100644
index 00000000..acb48bb3
--- /dev/null
+++ b/src/HolyClient/Views/RootView.axaml.cs
@@ -0,0 +1,15 @@
+using Avalonia;
+using Avalonia.Controls;
+using Avalonia.Markup.Xaml;
+using Avalonia.ReactiveUI;
+using HolyClient.ViewModels;
+
+namespace HolyClient;
+
+public partial class RootView : ReactiveUserControl
+{
+ public RootView()
+ {
+ InitializeComponent();
+ }
+}
\ No newline at end of file
diff --git a/src/HolyClient/lang/words.loc.json b/src/HolyClient/lang/words.loc.json
index 2aab6191..c10b7f87 100644
--- a/src/HolyClient/lang/words.loc.json
+++ b/src/HolyClient/lang/words.loc.json
@@ -59,107 +59,7 @@
}
}
},
- "BotManager": {
- "en": "Bot Manager",
- "ru": "Менеджер ботов",
- "ProfilesAbsent": {
- "en": "You do not have any profiles.",
- "ru": "У вас отсутсвуют профили."
- },
- "CreateProfile": {
- "en": "Create profile",
- "ru": "Создать профиль"
- },
- "RemoveProfile": {
- "en": "Remove profile",
- "ru": "Удалить профиль"
- },
- "DialogRemoveConfirmRequest": {
- "en": "Are you sure you want to remove it?",
- "ru": "Вы точно хотите удалить?"
- },
- "Process": {
- "BotsCount": {
- "en": "Number of bots",
- "ru": "Количество ботов"
- }
- },
- "Validation": {
- "Enter": {
- "Address": {
- "en": "Enter address",
- "ru": "Введите адрес"
- },
- "Port": {
- "en": "Enter port",
- "ru": "Введите порт"
- }
- },
- "InvalidFormat": {
- "en": "Invalid format",
- "ru": "Неверный формат"
- },
- "LongPort": {
- "en": "Port is too long",
- "ru": "Порт длинный"
- },
- "UnkownVersion": {
- "en": "Unknown version",
- "ru": "Неизвестная версия"
- },
- "NicknameLong": {
- "en": "The nickname is too long",
- "ru": "Ник длинный"
- }
-
- },
- "PreVersion": {
- "en": "Preview version",
- "ru": "Предварительная версия"
- },
- "Address": {
- "en": "Address",
- "ru": "Адрес"
- },
- "ThreadCount": {
- "en": "Number of threads",
- "ru": "Количество потоков"
- },
- "Version": {
- "en": "Version",
- "ru": "Версия"
- },
- "Nickname": {
- "en": "Nickname",
- "ru": "Никнейм"
- },
- "Start": {
- "en": "Start",
- "ru": "Запустить"
- },
- "Stop": {
- "en": "Stop",
- "ru": "Остановить"
- },
- "Error": {
- "en": "An error occurred during startup:",
- "ru": "Во время запуска произошла ошибка:"
- },
- "Tabs": {
- "GeneralSettings": {
- "en": "General settings",
- "ru": "Основные настройки"
- },
- "Plugins": {
- "en": "Plugins",
- "ru": "Плагины"
- },
- "Console": {
- "en": "Console",
- "ru": "Консоль"
- }
- }
- },
+
"StressTest": {
"en": "Stress test",
"ru": "Стресс тест",
diff --git a/src/McProtoNet/McProtoNet.Core/IO/MinecraftPrimitiveReader.cs b/src/McProtoNet/McProtoNet.Core/IO/MinecraftPrimitiveReader.cs
index 9e67f955..65680494 100644
--- a/src/McProtoNet/McProtoNet.Core/IO/MinecraftPrimitiveReader.cs
+++ b/src/McProtoNet/McProtoNet.Core/IO/MinecraftPrimitiveReader.cs
@@ -209,10 +209,10 @@ public virtual long ReadVarLong()
return result;
}
- static RecyclableMemoryStreamManager streamManager = new();
+
public virtual byte[] ReadToEnd()
{
- using (var ms = streamManager.GetStream())
+ using (var ms = StaticResources.MSmanager.GetStream())
{
BaseStream.CopyTo(ms);
diff --git a/src/McProtoNet/McProtoNet.Core/Protocol/Contracts/IMinecraftPacketReader.cs b/src/McProtoNet/McProtoNet.Core/Protocol/Contracts/IMinecraftPacketReader.cs
index 8fee36cd..b1c28c88 100644
--- a/src/McProtoNet/McProtoNet.Core/Protocol/Contracts/IMinecraftPacketReader.cs
+++ b/src/McProtoNet/McProtoNet.Core/Protocol/Contracts/IMinecraftPacketReader.cs
@@ -1,6 +1,6 @@
namespace McProtoNet.Core.Protocol
{
- public interface IMinecraftPacketReader : ISwitchCompression, IDisposable, IAsyncDisposable
+ public interface IMinecraftPacketReader : ISwitchCompression
{
Packet ReadNextPacket();
ValueTask ReadNextPacketAsync(CancellationToken cancellationToken = default);
diff --git a/src/McProtoNet/McProtoNet.Core/Protocol/Contracts/IMinecraftPacketSender.cs b/src/McProtoNet/McProtoNet.Core/Protocol/Contracts/IMinecraftPacketSender.cs
index ecd99e2e..ee6453ad 100644
--- a/src/McProtoNet/McProtoNet.Core/Protocol/Contracts/IMinecraftPacketSender.cs
+++ b/src/McProtoNet/McProtoNet.Core/Protocol/Contracts/IMinecraftPacketSender.cs
@@ -1,6 +1,6 @@
namespace McProtoNet.Core.Protocol
{
- public interface IMinecraftPacketSender : ISwitchCompression, IDisposable, IAsyncDisposable
+ public interface IMinecraftPacketSender : ISwitchCompression
{
void SendPacket(Packet packet);
ValueTask SendPacketAsync(Packet packet, CancellationToken cancellationToken = default);
diff --git a/src/McProtoNet/McProtoNet.Core/Protocol/Extensions.cs b/src/McProtoNet/McProtoNet.Core/Protocol/Extensions.cs
index a15b6098..87f5083c 100644
--- a/src/McProtoNet/McProtoNet.Core/Protocol/Extensions.cs
+++ b/src/McProtoNet/McProtoNet.Core/Protocol/Extensions.cs
@@ -2,6 +2,7 @@
using McProtoNet.Core.Protocol;
using Microsoft.IO;
using System.Buffers;
+using System.Threading;
namespace McProtoNet.Core
{
@@ -38,6 +39,8 @@ public static int GetVarIntLength(this int value, byte[] data)
return len;
}
+
+
public static int GetVarIntLength(this int value, Span data)
{
var unsigned = (uint)value;
@@ -60,43 +63,40 @@ public static int GetVarIntLength(this int value, Span data)
private static int CONTINUE_BIT = 0x80;
public static int ReadVarInt(this Stream stream)
{
- byte[] buff = ArrayPool.Shared.Rent(1);
- try
+ using var memory = MemoryPool.Shared.Rent(1);
+
+ var buff = memory.Memory.Slice(0, 1).Span;
+
+ int numRead = 0;
+ int result = 0;
+ byte read;
+ do
{
- int numRead = 0;
- int result = 0;
- byte read;
- do
+ if (stream.Read(buff) <= 0)
{
- if (stream.Read(buff, 0, 1) <= 0)
- {
- throw new EndOfStreamException();
- }
- read = buff[0];
+ throw new EndOfStreamException();
+ }
+ read = buff[0];
- int value = read & 0b01111111;
- result |= value << 7 * numRead;
+ int value = read & 0b01111111;
+ result |= value << 7 * numRead;
- numRead++;
- if (numRead > 5)
- {
- throw new InvalidOperationException("VarInt is too big");
- }
- } while ((read & 0b10000000) != 0);
+ numRead++;
+ if (numRead > 5)
+ {
+ throw new InvalidOperationException("VarInt is too big");
+ }
+ } while ((read & 0b10000000) != 0);
+
+ return result;
- return result;
- }
- finally
- {
- ArrayPool.Shared.Return(buff);
- }
}
public static async ValueTask ReadVarIntAsync(this Stream stream, CancellationToken token = default)
{
- byte[] buff = new byte[1];
-
+ using var memory = MemoryPool.Shared.Rent(1);
+ var buff = memory.Memory.Slice(0, 1);
int numRead = 0;
int result = 0;
byte read;
@@ -106,7 +106,7 @@ public static async ValueTask ReadVarIntAsync(this Stream stream, Cancellat
{
throw new EndOfStreamException();
}
- read = buff[0];
+ read = buff.Span[0];
int value = read & 0b01111111;
@@ -121,6 +121,9 @@ public static async ValueTask ReadVarIntAsync(this Stream stream, Cancellat
return result;
}
+
+
+
public static int ReadVarInt(this Stream stream, out int len)
{
byte[] buff = new byte[1];
@@ -216,12 +219,12 @@ public static async ValueTask ReadToEndAsync(this Stream stream, Memory.Shared.Rent(64);
+ try
+ {
+ int bytesRead;
+ while ((bytesRead = await source.ReadAsync(new Memory(buffer), cancellationToken).ConfigureAwait(false)) != 0)
+ {
+ await destination.WriteAsync(new ReadOnlyMemory(buffer, 0, bytesRead), cancellationToken).ConfigureAwait(false);
+ }
+ }
+ finally
+ {
+ ArrayPool.Shared.Return(buffer);
}
}
}
diff --git a/src/McProtoNet/McProtoNet.Core/Protocol/MinecraftPacketReader.cs b/src/McProtoNet/McProtoNet.Core/Protocol/MinecraftPacketReader.cs
index e8dcda33..dd7fe0d8 100644
--- a/src/McProtoNet/McProtoNet.Core/Protocol/MinecraftPacketReader.cs
+++ b/src/McProtoNet/McProtoNet.Core/Protocol/MinecraftPacketReader.cs
@@ -1,78 +1,74 @@
using Microsoft.IO;
using System.Buffers;
using System.IO.Compression;
+using System.Net.Sockets;
+using System.Runtime.CompilerServices;
namespace McProtoNet.Core.Protocol
{
public class MinecraftPacketReader : IMinecraftPacketReader
{
- private static readonly RecyclableMemoryStreamManager MSmanager = new RecyclableMemoryStreamManager();
- private readonly bool disposeStream;
- private Stream _baseStream;
- private RecyclableMemoryStream fastStream = MSmanager.GetStream() as RecyclableMemoryStream;
+ public Stream BaseStream { get; set; }
- public MinecraftPacketReader(Stream baseStream, bool disposeStream)
+ public MinecraftPacketReader(Stream baseStream)
{
- _baseStream = baseStream;
+ BaseStream = baseStream;
}
- public MinecraftPacketReader(Stream baseStream) : this(baseStream, true)
+ public MinecraftPacketReader()
{
}
+
+ [MethodImpl(MethodImplOptions.AggressiveOptimization)]
public Packet ReadNextPacket()
{
- ThrowIfDisposed();
+ //ThrowIfDisposed();
- int len = _baseStream.ReadVarInt();
+ int len = BaseStream.ReadVarInt();
if (_compressionThreshold <= 0)
{
- int id = _baseStream.ReadVarInt();
+ int id = BaseStream.ReadVarInt();
len -= id.GetVarIntLength();
var memory = MemoryPool.Shared.Rent(len);
- _baseStream.ReadExactly(memory.Memory.Slice(0, len).Span);
+ BaseStream.ReadExactly(memory.Memory.Slice(0, len).Span);
return new(
id,
- MSmanager.GetStream(memory.Memory.Slice(0, len).Span),
- memory);
+ StaticResources.MSmanager.GetStream(memory.Memory.Slice(0, len).Span));
}
- int sizeUncompressed = _baseStream.ReadVarInt();
+ int sizeUncompressed = BaseStream.ReadVarInt();
if (sizeUncompressed > 0)
{
len -= sizeUncompressed.GetVarIntLength();
var memory = MemoryPool.Shared.Rent(sizeUncompressed);
- _baseStream.ReadExactly(memory.Memory.Slice(0, len).Span);
+ BaseStream.ReadExactly(memory.Memory.Slice(0, len).Span);
Memory compressedData = memory.Memory.Slice(0, len);
- fastStream.Position = 0;
- fastStream.SetLength(len);
- var destMemory = fastStream.GetMemory(len);
-
- compressedData.CopyTo(destMemory);
-
- using (var ReadZlib = new ZLibStream(fastStream, CompressionMode.Decompress, true))
+ using (var fastStream = StaticResources.MSmanager.GetStream(compressedData.Span))
{
- int id = ReadZlib.ReadVarInt();
+ using (var ReadZlib = new ZLibStream(fastStream, CompressionMode.Decompress, true))
+ {
+ int id = ReadZlib.ReadVarInt();
- sizeUncompressed -= id.GetVarIntLength();
+ sizeUncompressed -= id.GetVarIntLength();
- ReadZlib.ReadExactly(memory.Memory.Slice(0, sizeUncompressed).Span);
+ ReadZlib.ReadExactly(memory.Memory.Slice(0, sizeUncompressed).Span);
- return new Packet(
- id,
- MSmanager.GetStream(memory.Memory.Slice(0, sizeUncompressed).Span),
- memory);
+ return new Packet(
+ id,
+ StaticResources.MSmanager.GetStream(memory.Memory.Slice(0, sizeUncompressed).Span));
+ }
}
@@ -80,70 +76,80 @@ public Packet ReadNextPacket()
}
{
- int id = _baseStream.ReadVarInt();
+ int id = BaseStream.ReadVarInt();
len -= id.GetVarIntLength() + 1;
var memory = MemoryPool.Shared.Rent(len);
- _baseStream.ReadExactly(memory.Memory.Slice(0, len).Span);
+ BaseStream.ReadExactly(memory.Memory.Slice(0, len).Span);
return new(
id,
- MSmanager.GetStream(memory.Memory.Slice(0, len).Span),
- memory);
+ StaticResources.MSmanager.GetStream(memory.Memory.Slice(0, len).Span));
}
}
+ [MethodImpl(MethodImplOptions.AggressiveOptimization)]
public async ValueTask ReadNextPacketAsync(CancellationToken token)
{
- ThrowIfDisposed();
+ //ThrowIfDisposed();
- int len = await _baseStream.ReadVarIntAsync(token);
+ int len = await BaseStream.ReadVarIntAsync(token);
if (_compressionThreshold <= 0)
{
- int id = await _baseStream.ReadVarIntAsync(token);
+ int id = await BaseStream.ReadVarIntAsync(token);
len -= id.GetVarIntLength();
var memory = MemoryPool.Shared.Rent(len);
+ try
+ {
+ await BaseStream.ReadExactlyAsync(memory.Memory.Slice(0, len), token);
- await _baseStream.ReadExactlyAsync(memory.Memory.Slice(0, len), token);
-
- return new(
- id,
- MSmanager.GetStream(memory.Memory.Span.Slice(0, len)),
- memory);
+ return new(
+ id,
+ StaticResources.MSmanager.GetStream(memory.Memory.Span.Slice(0, len)),
+ memory);
+ }
+ catch
+ {
+ memory.Dispose();
+ throw;
+ }
}
- int sizeUncompressed = await _baseStream.ReadVarIntAsync(token);
+ int sizeUncompressed = await BaseStream.ReadVarIntAsync(token);
if (sizeUncompressed > 0)
{
len -= sizeUncompressed.GetVarIntLength();
var memory = MemoryPool.Shared.Rent(sizeUncompressed);
- await _baseStream.ReadExactlyAsync(memory.Memory.Slice(0, len), token);
-
- Memory compressedData = memory.Memory.Slice(0, len);
-
- fastStream.Position = 0;
- fastStream.SetLength(len);
- var destMemory = fastStream.GetMemory(len);
+ try
+ {
+ await BaseStream.ReadExactlyAsync(memory.Memory.Slice(0, len), token);
- compressedData.CopyTo(destMemory);
+ Memory compressedData = memory.Memory.Slice(0, len);
- using (var ReadZlib = new ZLibStream(fastStream, CompressionMode.Decompress, true))
- {
- int id = await ReadZlib.ReadVarIntAsync(token);
+ using (var fastStream = StaticResources.MSmanager.GetStream(compressedData.Span))
+ using (var ReadZlib = new ZLibStream(fastStream, CompressionMode.Decompress, true))
+ {
+ int id = await ReadZlib.ReadVarIntAsync(token);
- sizeUncompressed -= id.GetVarIntLength();
+ sizeUncompressed -= id.GetVarIntLength();
- await ReadZlib.ReadExactlyAsync(memory.Memory.Slice(0, sizeUncompressed), token);
+ await ReadZlib.ReadExactlyAsync(memory.Memory.Slice(0, sizeUncompressed), token);
- return new Packet(
- id,
- MSmanager.GetStream(memory.Memory.Slice(0, sizeUncompressed).Span),
- memory);
+ return new Packet(
+ id,
+ StaticResources.MSmanager.GetStream(memory.Memory.Slice(0, sizeUncompressed).Span),
+ memory);
+ }
+ }
+ catch
+ {
+ memory.Dispose();
+ throw;
}
@@ -151,16 +157,23 @@ public async ValueTask ReadNextPacketAsync(CancellationToken token)
}
{
- int id = await _baseStream.ReadVarIntAsync(token);
+ int id = await BaseStream.ReadVarIntAsync(token);
len -= id.GetVarIntLength() + 1;
var memory = MemoryPool.Shared.Rent(len);
-
- await _baseStream.ReadExactlyAsync(memory.Memory.Slice(0, len), token);
- return new(
- id,
- MSmanager.GetStream(memory.Memory.Slice(0, len).Span),
- memory);
+ try
+ {
+ await BaseStream.ReadExactlyAsync(memory.Memory.Slice(0, len), token);
+ return new(
+ id,
+ StaticResources.MSmanager.GetStream(memory.Memory.Slice(0, len).Span),
+ memory);
+ }
+ catch
+ {
+ memory.Dispose();
+ throw;
+ }
}
}
@@ -171,54 +184,28 @@ public void SwitchCompression(int threshold)
}
private bool _disposed = false;
- private void ThrowIfDisposed()
- {
- if (_disposed)
- throw new ObjectDisposedException(nameof(MinecraftPacketReader));
- }
- ~MinecraftPacketReader()
- {
- Dispose();
- }
- public void Dispose()
- {
- if (_disposed)
- return;
+ //private void ThrowIfDisposed()
+ //{
+ // if (_disposed)
+ // throw new ObjectDisposedException(nameof(MinecraftPacketReader));
+ //}
+ //~MinecraftPacketReader()
+ //{
+ // Dispose();
+ //}
+ //public void Dispose()
+ //{
+ // if (_disposed)
+ // return;
+
+ // //fastStream?.Dispose();
+ // //fastStream = null;
+
+ // _disposed = true;
+ // GC.SuppressFinalize(this);
+ //}
- fastStream?.Dispose();
- fastStream = null;
- if (disposeStream)
- {
- if (_baseStream is not null)
- {
- _baseStream.Dispose();
- _baseStream = null;
- }
- }
- _disposed = true;
- GC.SuppressFinalize(this);
- }
- public async ValueTask DisposeAsync()
- {
- if (_disposed)
- return;
- _disposed = true;
- if (fastStream is not null)
- {
- await fastStream.DisposeAsync();
- fastStream = null;
- }
- if (disposeStream)
- {
- if (_baseStream is not null)
- {
- await _baseStream.DisposeAsync();
- _baseStream = null;
- }
- }
- GC.SuppressFinalize(this);
- }
}
diff --git a/src/McProtoNet/McProtoNet.Core/Protocol/MinecraftPacketSender.cs b/src/McProtoNet/McProtoNet.Core/Protocol/MinecraftPacketSender.cs
index e84bb799..d798745e 100644
--- a/src/McProtoNet/McProtoNet.Core/Protocol/MinecraftPacketSender.cs
+++ b/src/McProtoNet/McProtoNet.Core/Protocol/MinecraftPacketSender.cs
@@ -1,4 +1,5 @@
using Microsoft.IO;
+using System.Buffers;
using System.IO.Compression;
using System.Runtime.CompilerServices;
@@ -6,23 +7,23 @@ namespace McProtoNet.Core.Protocol
{
public class MinecraftPacketSender : IMinecraftPacketSender
{
- private Stream _baseStream;
- private readonly bool disposedStream;
- public MinecraftPacketSender(Stream baseStream, bool disposedStream)
+ public Stream BaseStream { get; set; }
+
+ public MinecraftPacketSender(Stream baseStream)
{
- _baseStream = baseStream;
- this.disposedStream = disposedStream;
+ BaseStream = baseStream;
}
- public MinecraftPacketSender(Stream baseStream) : this(baseStream, true)
+ public MinecraftPacketSender()
{
}
+
private const int ZERO_VARLENGTH = 1;//default(int).GetVarIntLength();
private readonly byte[] ZERO_VARINT = { 0 };
private SemaphoreSlim semaphore = new SemaphoreSlim(1, 1);
- static RecyclableMemoryStreamManager streamManager = new();
+
public void SendPacket(Packet packet)
{
semaphore.Wait();
@@ -30,7 +31,7 @@ public void SendPacket(Packet packet)
var data = packet.Data;
try
{
- ThrowIfDisposed();
+ //ThrowIfDisposed();
@@ -47,7 +48,7 @@ public void SendPacket(Packet packet)
if (uncompressedSize >= _compressionThreshold)
{
- using (var compressedPacket = streamManager.GetStream())
+ using (var compressedPacket = StaticResources.MSmanager.GetStream())
{
using (var zlibStream = new ZLibStream(compressedPacket, CompressionMode.Compress, true))
{
@@ -58,12 +59,12 @@ public void SendPacket(Packet packet)
int fullSize = uncompressedSizeLength + (int)compressedPacket.Length;
- _baseStream.WriteVarInt(fullSize);
+ BaseStream.WriteVarInt(fullSize);
- _baseStream.WriteVarInt(uncompressedSize);
+ BaseStream.WriteVarInt(uncompressedSize);
compressedPacket.Position = 0;
- compressedPacket.CopyTo(_baseStream);
+ compressedPacket.CopyTo(BaseStream);
}
@@ -73,13 +74,13 @@ public void SendPacket(Packet packet)
#region Short
uncompressedSize++;
- _baseStream.WriteVarInt(uncompressedSize);
+ BaseStream.WriteVarInt(uncompressedSize);
- _baseStream.Write(ZERO_VARINT);
+ BaseStream.Write(ZERO_VARINT);
- _baseStream.Write(idData.Slice(0, idLen));
+ BaseStream.Write(idData.Slice(0, idLen));
- data.CopyTo(_baseStream);
+ data.CopyTo(BaseStream);
#endregion
}
}
@@ -87,7 +88,7 @@ public void SendPacket(Packet packet)
{
SendPacketWithoutCompression(packet.Data, id);
}
- _baseStream.Flush();
+ BaseStream.Flush();
}
finally
{
@@ -98,7 +99,7 @@ public void SendPacket(Packet packet)
private void SendPacketWithoutCompression(MemoryStream packet, int id)
{
- ThrowIfDisposed();
+ //ThrowIfDisposed();
// packet.Write(writer);
int Packetlength = (int)packet.Length;
@@ -106,12 +107,12 @@ private void SendPacketWithoutCompression(MemoryStream packet, int id)
Packetlength += id.GetVarIntLength();
//Записываем длину всего пакета
- _baseStream.WriteVarInt(Packetlength);
+ BaseStream.WriteVarInt(Packetlength);
//Записываем ID пакета
- _baseStream.WriteVarInt(id);
+ BaseStream.WriteVarInt(id);
//Все данные пакета перекидваем в интернет
- packet.CopyTo(_baseStream);
+ packet.CopyTo(BaseStream);
}
@@ -120,7 +121,7 @@ private void SendPacketWithoutCompression(MemoryStream packet, int id)
public async ValueTask SendPacketAsync(Packet packet, CancellationToken token = default)
{
- ThrowIfDisposed();
+ //ThrowIfDisposed();
int id = packet.Id;
var data = packet.Data;
//await semaphore.WaitAsync(token);
@@ -141,12 +142,12 @@ public async ValueTask SendPacketAsync(Packet packet, CancellationToken token =
if (uncompressedSize >= _compressionThreshold)
{
- using (var compressedPacket = streamManager.GetStream())
+ using (var compressedPacket = StaticResources.MSmanager.GetStream())
{
using (var zlibStream = new ZLibStream(compressedPacket, CompressionMode.Compress, true))
{
- await zlibStream.WriteVarIntAsync(id);
- await data.CopyToAsync(zlibStream, token);
+ await zlibStream.WriteVarIntAsync(id, token);
+ await data.CopyToFromMemoryStreamAsync(zlibStream, token);
}
int uncompressedSizeLength = uncompressedSize.GetVarIntLength();
@@ -154,12 +155,12 @@ public async ValueTask SendPacketAsync(Packet packet, CancellationToken token =
- await _baseStream.WriteVarIntAsync(fullSize, token);
+ await BaseStream.WriteVarIntAsync(fullSize, token);
- await _baseStream.WriteVarIntAsync(uncompressedSize, token);
+ await BaseStream.WriteVarIntAsync(uncompressedSize, token);
compressedPacket.Position = 0;
- await compressedPacket.CopyToAsync(_baseStream, token);
+ await compressedPacket.CopyToFromMemoryStreamAsync(BaseStream, token);
}
}
@@ -167,11 +168,11 @@ public async ValueTask SendPacketAsync(Packet packet, CancellationToken token =
{
uncompressedSize++;
- await _baseStream.WriteVarIntAsync(uncompressedSize, token);
- await _baseStream.WriteAsync(ZERO_VARINT, token);
- await _baseStream.WriteAsync(idData.AsMemory(0, idLen), token);
+ await BaseStream.WriteVarIntAsync(uncompressedSize, token);
+ await BaseStream.WriteAsync(ZERO_VARINT, token);
+ await BaseStream.WriteAsync(idData.AsMemory(0, idLen), token);
- await data.CopyToAsync(_baseStream);
+ await data.CopyToAsync(BaseStream, token);
}
@@ -180,7 +181,7 @@ public async ValueTask SendPacketAsync(Packet packet, CancellationToken token =
{
await SendPacketWithoutCompressionAsync(packet.Data, id, token);
}
- await _baseStream.FlushAsync(token);
+ await BaseStream.FlushAsync(token);
}
finally
{
@@ -188,84 +189,86 @@ public async ValueTask SendPacketAsync(Packet packet, CancellationToken token =
}
}
- private async Task SendPacketWithoutCompressionAsync(MemoryStream packet, int id, CancellationToken token)
+ private async ValueTask SendPacketWithoutCompressionAsync(MemoryStream packet, int id, CancellationToken token)
{
- ThrowIfDisposed();
+ //ThrowIfDisposed();
// packet.Write(writer);
int Packetlength = (int)packet.Length;
- byte[] idData = new byte[5];
- int len = id.GetVarIntLength(idData);
+ using var idDataMemory = MemoryPool.Shared.Rent(5);
+
+
+ int len = id.GetVarIntLength(idDataMemory.Memory.Span);
//Записываем длину всего пакета
- await _baseStream.WriteVarIntAsync(Packetlength + len, token);
+ await BaseStream.WriteVarIntAsync(Packetlength + len, token);
//Записываем ID пакета
- await _baseStream.WriteAsync(idData, 0, len, token);
+ await BaseStream.WriteAsync(idDataMemory.Memory.Slice(0, len), token);
- //Все данные пакета перекидваем в интернет
- await packet.CopyToAsync(_baseStream, token);
+ //Все данные пакета перекидываем в интернет
+ await packet.CopyToFromMemoryStreamAsync(BaseStream, token);
}
#endregion
- ~MinecraftPacketSender()
- {
- Dispose();
- }
+ //~MinecraftPacketSender()
+ //{
+ // Dispose();
+ //}
private int _compressionThreshold;
public void SwitchCompression(int threshold)
{
_compressionThreshold = threshold;
}
- private bool _disposed;
- private void ThrowIfDisposed()
- {
- if (_disposed)
- throw new ObjectDisposedException(nameof(MinecraftPacketSender));
- }
-
- public void Dispose()
- {
-
- if (_disposed)
- return;
- if (semaphore is not null)
- {
- semaphore.Dispose();
- semaphore = null;
- }
- if (disposedStream)
- {
- if (_baseStream is { })
- {
- _baseStream.Dispose();
- _baseStream = null;
- }
- }
- _disposed = true;
- GC.SuppressFinalize(this);
- }
-
- public async ValueTask DisposeAsync()
- {
- if (_disposed)
- _disposed = true;
- if (semaphore is not null)
- {
- semaphore.Dispose();
- semaphore = null;
- }
- if (disposedStream)
- {
- if (_baseStream is not null)
- {
- await _baseStream.DisposeAsync();
- _baseStream = null;
- }
- }
- GC.SuppressFinalize(this);
- }
+ //private bool _disposed;
+ //private void ThrowIfDisposed()
+ //{
+ // if (_disposed)
+ // throw new ObjectDisposedException(nameof(MinecraftPacketSender));
+ //}
+
+ //public void Dispose()
+ //{
+
+ // if (_disposed)
+ // return;
+ // if (semaphore is not null)
+ // {
+ // semaphore.Dispose();
+ // semaphore = null;
+ // }
+ // //if (disposedStream)
+ // //{
+ // // if (BaseStream is { })
+ // // {
+ // // BaseStream.Dispose();
+ // // BaseStream = null;
+ // // }
+ // //}
+ // _disposed = true;
+ // GC.SuppressFinalize(this);
+ //}
+
+ //public async ValueTask DisposeAsync()
+ //{
+ // if (_disposed)
+ // _disposed = true;
+ // if (semaphore is not null)
+ // {
+ // semaphore.Dispose();
+ // semaphore = null;
+ // }
+ // //if (disposedStream)
+ // //{
+ // // if (BaseStream is not null)
+ // // {
+ // // await BaseStream.DisposeAsync();
+ // // BaseStream = null;
+ // // }
+ // //}
+ // GC.SuppressFinalize(this);
+ //}
}
}
diff --git a/src/McProtoNet/McProtoNet.Core/Protocol/MinecraftProtocol.cs b/src/McProtoNet/McProtoNet.Core/Protocol/MinecraftProtocol.cs
index 13b054ad..c00a5376 100644
--- a/src/McProtoNet/McProtoNet.Core/Protocol/MinecraftProtocol.cs
+++ b/src/McProtoNet/McProtoNet.Core/Protocol/MinecraftProtocol.cs
@@ -19,8 +19,8 @@ public MinecraftProtocol(TcpClient tcpClient)
public MinecraftProtocol(Stream baseStream, bool disposeStream)
{
_baseStream = baseStream;
- Reader = new MinecraftPacketReader(_baseStream, disposeStream);
- Sender = new MinecraftPacketSender(_baseStream, disposeStream);
+ Reader = new MinecraftPacketReader(_baseStream );
+ Sender = new MinecraftPacketSender(_baseStream);
}
~MinecraftProtocol()
@@ -35,8 +35,8 @@ public void Dispose()
return;
_disposed = true;
- Reader?.Dispose();
- Sender?.Dispose();
+ //Reader?.Dispose();
+ //Sender?.Dispose();
Reader = null;
Sender = null;
diff --git a/src/McProtoNet/McProtoNet.Core/Protocol/MinecraftStream.cs b/src/McProtoNet/McProtoNet.Core/Protocol/MinecraftStream.cs
index 3837de34..92f36a36 100644
--- a/src/McProtoNet/McProtoNet.Core/Protocol/MinecraftStream.cs
+++ b/src/McProtoNet/McProtoNet.Core/Protocol/MinecraftStream.cs
@@ -22,8 +22,7 @@ public sealed class MinecraftStream : Stream, IDisposable
public override long Length => BaseStream.Length;
public override long Position { get => BaseStream.Position; set => BaseStream.Position = value; }
- public SemaphoreSlim Lock { get; } = new SemaphoreSlim(1, 1);
-
+
public MinecraftStream(Stream stream)
{
@@ -247,7 +246,7 @@ protected override void Dispose(bool disposing)
if (_disposed)
return;
BaseStream.Dispose();
- Lock.Dispose();
+
_disposed = true;
}
diff --git a/src/McProtoNet/McProtoNet.Core/Protocol/PacketReaderWriter.cs b/src/McProtoNet/McProtoNet.Core/Protocol/PacketReaderWriter.cs
index 60b0de3a..4e6f9725 100644
--- a/src/McProtoNet/McProtoNet.Core/Protocol/PacketReaderWriter.cs
+++ b/src/McProtoNet/McProtoNet.Core/Protocol/PacketReaderWriter.cs
@@ -57,7 +57,7 @@ public MinecraftPacket ReadNextPacket()
}
}
- static RecyclableMemoryStreamManager streamManager = new();
+
public async Task SendPacketAsync(MinecraftPacket packet, CancellationToken cancellationToken = default)
{
cancellationToken.ThrowIfCancellationRequested();
@@ -65,7 +65,7 @@ public async Task SendPacketAsync(MinecraftPacket packet, CancellationToken canc
if (ok)
{
- using (MemoryStream ms = streamManager.GetStream())
+ using (MemoryStream ms = StaticResources.MSmanager.GetStream())
{
IMinecraftPrimitiveWriter writer = new MinecraftPrimitiveWriter(ms);
packet.Write(writer);
diff --git a/src/McProtoNet/McProtoNet.Core/SessionToken.cs b/src/McProtoNet/McProtoNet.Core/SessionToken.cs
index 3d5c169d..6e113689 100644
--- a/src/McProtoNet/McProtoNet.Core/SessionToken.cs
+++ b/src/McProtoNet/McProtoNet.Core/SessionToken.cs
@@ -1,7 +1,6 @@
-
-
-namespace McProtoNet
+namespace McProtoNet
{
+
public struct SessionToken
{
public string UUID { get; private set; }
diff --git a/src/McProtoNet/McProtoNet.Core/StaticResources.cs b/src/McProtoNet/McProtoNet.Core/StaticResources.cs
new file mode 100644
index 00000000..29622b4b
--- /dev/null
+++ b/src/McProtoNet/McProtoNet.Core/StaticResources.cs
@@ -0,0 +1,16 @@
+
+
+using Microsoft.IO;
+
+namespace McProtoNet
+{
+ public static class StaticResources
+ {
+ static StaticResources()
+ {
+
+ }
+
+ public static readonly RecyclableMemoryStreamManager MSmanager = new RecyclableMemoryStreamManager();
+ }
+}
diff --git a/src/McProtoNet/McProtoNet/Client/ClientState.cs b/src/McProtoNet/McProtoNet/Client/ClientState.cs
index f62ee19f..59e73ee4 100644
--- a/src/McProtoNet/McProtoNet/Client/ClientState.cs
+++ b/src/McProtoNet/McProtoNet/Client/ClientState.cs
@@ -6,7 +6,17 @@ public enum ClientState
Connecting,
HandShake,
Login,
- Play,
- Failed
+ Play
+ }
+ public struct ClientStateChanged
+ {
+ public ClientState OldValue { get; }
+ public ClientState NewValue { get; }
+
+ public ClientStateChanged(ClientState oldValue, ClientState newValue)
+ {
+ OldValue = oldValue;
+ NewValue = newValue;
+ }
}
}
\ No newline at end of file
diff --git a/src/McProtoNet/McProtoNet/Client/MinecraftClient.Read.cs b/src/McProtoNet/McProtoNet/Client/MinecraftClient.Read.cs
index 1be1ea18..70f89090 100644
--- a/src/McProtoNet/McProtoNet/Client/MinecraftClient.Read.cs
+++ b/src/McProtoNet/McProtoNet/Client/MinecraftClient.Read.cs
@@ -63,11 +63,11 @@ private void CreateEvents()
public IObservable OnSpawnPlayer => _spawnPlayerEvent;
- private void OnPacket(IMinecraftPrimitiveReader reader, PacketIn id, CancellationToken cancellation)
+ private void OnPacket(IMinecraftPrimitiveReader reader, PacketIn id)
{
- if (id == PacketIn.Disconnect)
+ if (_disconnectEvent.HasObservers && id == PacketIn.Disconnect)
{
string reason = reader.ReadString();
var dis = PacketPool.DisconnectEventPool.Get();
@@ -83,7 +83,7 @@ private void OnPacket(IMinecraftPrimitiveReader reader, PacketIn id, Cancellatio
}
throw new DisconnectException(reason);
}
- if (id == PacketIn.JoinGame)
+ if (_joinGameEvent.HasObservers && id == PacketIn.JoinGame)
{
var join = PacketPool.JoinGamePacketPool.Get();
@@ -98,6 +98,7 @@ private void OnPacket(IMinecraftPrimitiveReader reader, PacketIn id, Cancellatio
}
else if (id == PacketIn.MapData)
{
+ return;
int mapid = reader.ReadVarInt();
byte scale = reader.ReadUnsignedByte();
// 1.9 +
@@ -203,7 +204,7 @@ private void OnPacket(IMinecraftPrimitiveReader reader, PacketIn id, Cancellatio
long pingId = reader.ReadLong();
SendPacket(w => w.WriteLong(pingId), PacketOut.KeepAlive);
}
- else if (id == PacketIn.PlayerPositionRotation)
+ else if (_playerPositionRotationEvent.HasObservers && id == PacketIn.PlayerPositionRotation)
{
var x = reader.ReadDouble();
var y = reader.ReadDouble();
@@ -239,7 +240,7 @@ private void OnPacket(IMinecraftPrimitiveReader reader, PacketIn id, Cancellatio
}
- else if (id == PacketIn.Respawn)
+ else if (_respawnEvent.HasObservers && id == PacketIn.Respawn)
{
var respawn = PacketPool.RespawnPacketPool.Get();
@@ -254,7 +255,7 @@ private void OnPacket(IMinecraftPrimitiveReader reader, PacketIn id, Cancellatio
PacketPool.RespawnPacketPool.Return(respawn);
}
}
- else if (id == PacketIn.ChatMessage)
+ else if (_chatEvent.HasObservers && id == PacketIn.ChatMessage)
{
string message = reader.ReadString();
@@ -306,11 +307,11 @@ private void OnPacket(IMinecraftPrimitiveReader reader, PacketIn id, Cancellatio
reader.ReadShort();
reader.ReadShort();
- var spawnEntity = PacketPool.SpawnEntityPacketPool.Get();
+ //var spawnEntity = PacketPool.SpawnEntityPacketPool.Get();
try
{
- spawnEntity.Id = entityID;
- spawnEntity.UUID = entityUUID;
+ //spawnEntity.Id = entityID;
+ //spawnEntity.UUID = entityUUID;
}
catch
{
diff --git a/src/McProtoNet/McProtoNet/Client/MinecraftClient.Send.cs b/src/McProtoNet/McProtoNet/Client/MinecraftClient.Send.cs
index 5d42d193..9f47aded 100644
--- a/src/McProtoNet/McProtoNet/Client/MinecraftClient.Send.cs
+++ b/src/McProtoNet/McProtoNet/Client/MinecraftClient.Send.cs
@@ -6,29 +6,88 @@ namespace McProtoNet
{
public partial class MinecraftClient : IMinecraftClientEvents, IMinecraftClientActions
{
+ private SemaphoreSlim semaphore = new SemaphoreSlim(1, 1);
public ValueTask SendPacket(Action action, PacketOut id)
{
- if (_core is null)
- return ValueTask.CompletedTask;
+ return SendPacket(action, _packetPallete.GetOut(id));
-
- return _core.SendPacket(action, id);
}
- public ValueTask SendPacket(Action action, int id)
+ public async ValueTask SendPacket(Action action, int id)
{
- if (_core is null)
- return ValueTask.CompletedTask;
- return _core.SendPacket(action, id);
+ try
+ {
+ await semaphore.WaitAsync(CTS.Token);
+ using (MemoryStream ms = StaticResources.MSmanager.GetStream())
+ {
+ var writer = Performance.Writers.Get();
+ try
+ {
+
+
+ writer.BaseStream = ms;
+ action(writer);
+ ms.Position = 0;
+ }
+ finally
+ {
+ Performance.Writers.Return(writer);
+ }
+ await PacketSender.SendPacketAsync(new(id, ms), CTS.Token);
+
+ }
+ }
+ catch (Exception ex)
+ {
+ CancelAll(ex);
+ throw;
+ }
+ finally
+ {
+ semaphore.Release();
+ }
+
+
+
+
}
- private ValueTask SendPacketAsync(IOutputPacket packet, int id)
+ public async ValueTask SendPacketAsync(IOutputPacket packet, int id)
{
- if (_core is null)
- return ValueTask.CompletedTask;
- return _core.SendPacketAsync(packet, id);
+ try
+ {
+ await semaphore.WaitAsync(CTS.Token);
+ using (MemoryStream ms = StaticResources.MSmanager.GetStream())
+ {
+ var writer = Performance.Writers.Get();
+ try
+ {
+
+ writer.BaseStream = ms;
+
+ packet.Write(writer);
+
+ }
+ finally
+ {
+ Performance.Writers.Return(writer);
+ }
+ ms.Position = 0;
+ await PacketSender.SendPacketAsync(new(id, ms), CTS.Token);
+ }
+ }
+ catch (Exception ex)
+ {
+ CancelAll(ex);
+ throw;
+ }
+ finally
+ {
+ semaphore.Release();
+ }
+
}
public ValueTask SendChat(string text)
diff --git a/src/McProtoNet/McProtoNet/Client/MinecraftClient.cs b/src/McProtoNet/McProtoNet/Client/MinecraftClient.cs
index 9a453a41..c535e7f8 100644
--- a/src/McProtoNet/McProtoNet/Client/MinecraftClient.cs
+++ b/src/McProtoNet/McProtoNet/Client/MinecraftClient.cs
@@ -1,7 +1,11 @@
-using Serilog;
+using McProtoNet.Core;
+using McProtoNet.Core.IO;
+using McProtoNet.Core.Protocol;
+using McProtoNet.Utils;
+using Serilog;
using System.ComponentModel.DataAnnotations;
using System.IO.Pipelines;
-using System.Runtime.CompilerServices;
+using System.Net.Sockets;
namespace McProtoNet
{
@@ -9,31 +13,47 @@ namespace McProtoNet
public partial class MinecraftClient : IDisposable
{
+
+ #region Fields
private ILogger _logger;
- private enum Trigger
- {
- Starting,
- Stop,
- Restart
- }
- public event EventHandler StateChanged;
+ private Pipe pipe;
- public ClientState State
- {
- get => state;
- private set
- {
- var old = this.state;
- this.state = value;
- StateChanged?.Invoke(this, new(old, value));
- }
- }
- public ClientConfig Config { get; set; } = new();
+ private CancellationTokenSource CTS;
+ private IPacketPallete _packetPallete;
+
+ private int _currentState = 0;
+
+
+ private Stream mainStream;
+ private MinecraftStream minecraftStream;
+ private MinecraftPacketReader PacketReader;
+ private MinecraftPacketSender PacketSender;
+
+
+
+ private int _isActive = 0;
private MinecraftVersion _protocol;
- private volatile MinecraftClientCore _core;
- Pipe pipe;
+ #endregion
+ #region Properties
+ public event Action OnStateChanged;
+ public event Action OnErrored;
+
+ public ClientState CurrentState => (ClientState)_currentState;
+
+ public bool IsActive => _isActive == 1;
+
+
+ public ClientConfig Config { get; set; } = new();
+ #endregion
+
+
+
+
+
+
+
public MinecraftClient()
@@ -42,33 +62,44 @@ public MinecraftClient()
{
});
+
+
+
+ PacketReader = new();
+ PacketSender = new();
+
+
+
CreateEvents();
}
- private static IPacketPallete _cache754 = new PacketPalette_1_16();
+
+
+
+
private IPacketPallete CreatePallete()
{
IPacketPallete? packetPallete = null;
if (Config.Version <= MinecraftVersion.MC_1_12_2_Version)
- packetPallete = new PacketPalette_1_12_2();
+ packetPallete = PacketPalette_1_12_2.Instance;
else if (Config.Version < MinecraftVersion.MC_1_14_Version)
- packetPallete = new PacketPalette_1_13();
+ packetPallete = PacketPalette_1_13.Instance;
else if (Config.Version <= MinecraftVersion.MC_1_15_Version)
- packetPallete = new PacketPalette_1_14();
+ packetPallete = PacketPalette_1_14.Instance;
else if (Config.Version <= MinecraftVersion.MC_1_15_2_Version)
- packetPallete = new PacketPalette_1_15();
+ packetPallete = PacketPalette_1_15.Instance;
else if (Config.Version <= MinecraftVersion.MC_1_16_1_Version)
{
- packetPallete = _cache754;
+ packetPallete = PacketPalette_1_16.Instance;
}
else if (Config.Version <= MinecraftVersion.MC_1_16_5_Version)
- packetPallete = new PacketPalette_1_16_2();
+ packetPallete = PacketPalette_1_16_2.Instance;
else if (Config.Version <= MinecraftVersion.MC_1_17_1_Version)
- packetPallete = new PacketPalette_1_17();
+ packetPallete = PacketPalette_1_17.Instance;
else if (Config.Version <= MinecraftVersion.MC_1_18_2_Version)
- packetPallete = new PacketPalette_1_18();
+ packetPallete = PacketPalette_1_18.Instance;
// else if (protocol <= MC_1_19_Version)
// packetPallete = new PacketPalette_1_19();
// else if (protocol <= MC_1_19_2_Version)
@@ -77,34 +108,7 @@ private IPacketPallete CreatePallete()
// packetPallete = new PacketPalette1193();
return packetPallete;
}
- private void CreateNewCore()
- {
- RemoveCore();
-
- try
- {
- pipe.Reset();
- }
- catch
- {
-
- }
- // throw new Exception("CTOR");
- _core = new MinecraftClientCore(
- Config.Version,
- Config.Username,
- Config.Host,
- Config.Port,
- Config.Proxy,
- CreatePallete(),
- this.pipe,
- this._logger);
- }
- private async void RemoveCore()
- {
- Interlocked.Exchange(ref _core, null)?.DisposeAsync();
- }
private void ValidateConfig()
{
if (string.IsNullOrWhiteSpace(Config.Username))
@@ -121,86 +125,170 @@ private void ValidateConfig()
//ToDO login
}
}
- private TaskCompletionSource workTask;
- public TaskAwaiter GetAwaiter()
+
+
+ private void ResetFields()
{
- if (workTask is null)
- {
- return Task.CompletedTask.GetAwaiter();
- }
- return workTask.Task.GetAwaiter();
+ if (CTS is not null)
+ if (!CTS.IsCancellationRequested)
+ {
+ CTS.Cancel();
+ }
+ CTS = new();
+ _canceling = 0;
+ _isActive = 1;
+ _currentState = 0;
+ _errorInvoked = 0;
+
+ PacketReader.SwitchCompression(0);
+ PacketSender.SwitchCompression(0);
+
+ PacketSender.BaseStream = null;
+ PacketReader.BaseStream = null;
}
- public async Task Login(Serilog.ILogger logger)
+ public async Task Start(Serilog.ILogger logger)
{
- if (workTask is not null)
- {
- workTask.TrySetResult();
- }
- workTask = new();
+ ResetFields();
+
_logger = logger;
+ _packetPallete = CreatePallete();
- _startDisconnect = false;
ValidateConfig();
- CreateNewCore();
+
_protocol = Config.Version;
+ bool runLoop = false;
try
{
- State = ClientState.Connecting;
- await _core.Connect();
+ ChangeState(ClientState.Connecting);
+ await Connect();
+
+ ChangeState(ClientState.HandShake);
+ await HandShake();
+
+ ChangeState(ClientState.Login);
+
+ await this.SendPacket(w =>
+ {
+ w.WriteString(this.Config.Username);
+ }, 0x00);
+
+
+ await LoginCore(CTS.Token);
- State = ClientState.HandShake;
- await _core.HandShake();
- State = ClientState.Login;
- var t = await _core.Login(OnPacket);
- State = ClientState.Play;
+ var readStream = pipe.Reader.AsStream();
+ PacketReader.BaseStream = readStream;
- WaitTask(t);
+ var fill = FillPipeAsync();
+ var read = ReadPacketLoop();
+ var combined = Task.WhenAll(fill, read);
+
+ _ = combined.ContinueWith(t =>
+ {
+ try
+ {
+ if (t.IsFaulted)
+ {
+ CancelAll(t.Exception);
+ }
+ else
+ {
+ CancelAll(new TaskCanceledException());
+ }
+ this.pipe.Reset();
+ this.InvokeError();
+ }
+ catch
+ {
+ //new Thread(() =>
+ //{
+ // throw new Exception("Fatal");
+ //}).Start();
+ }
+ finally
+ {
+ //Console.WriteLine("ok");
+ }
+
+
+ });
+ runLoop = true;
}
catch (Exception e)
{
- workTask.TrySetException(e);
- State = ClientState.Failed;
+
+ CancelAll(e);
+ if (!runLoop)
+ {
+ InvokeError();
+ }
_logger.Error(e, "Во время запуска клиента произошла ошибка");
- throw e;
+ //throw e;
}
}
- private async void WaitTask(Task t)
+
+
+
+
+ private void ChangeState(ClientState state)
{
- try
- {
- await t;
- }
- catch (Exception e)
- {
- this.State = ClientState.Failed;
- workTask.TrySetException(e);
- }
- finally
+
+ var old = Interlocked.Exchange(ref _currentState, (int)state);
+
+ ClientStateChanged changed = new((ClientState)old, state);
+
+ OnStateChanged?.Invoke(changed);
+ }
+ private int _canceling = 0;
+ private Exception _error;
+ private void CancelAll(Exception exception)
+ {
+ if (Interlocked.CompareExchange(ref _canceling, 1, 0) == 0)
{
+ _error = exception;
+ if (CTS is not null)
+ {
+ CTS.Cancel();
+ CTS.Dispose();
+ CTS = null;
+ }
+
+ tcpClient?.Dispose();
+ tcpClient = null;
+
+ this.mainStream?.Dispose();
+ this.mainStream = null;
+
+ minecraftStream?.Dispose();
+ minecraftStream = null;
+
+
}
}
+ private int _errorInvoked = 0;
+ private void InvokeError()
+ {
+ OnErrored?.Invoke(this._error);
+ }
-
- private bool _startDisconnect = false;
- public async void Disconnect()
+ public void Disconnect()
{
- if (_startDisconnect)
- return;
- workTask?.TrySetCanceled();
- _startDisconnect = true;
- if (_core is not null)
- await _core.DisposeAsync();
+ this.mainStream?.Dispose();
+ this.mainStream = null;
+ minecraftStream?.Dispose();
+ minecraftStream = null;
+
+
}
@@ -211,39 +299,337 @@ public async void Disconnect()
}
private bool _disposed = false;
- private ClientState state;
-
public void Dispose()
{
if (_disposed) return;
+ _disposed = true;
-
- if (_core is { })
+ if (CTS is not null)
{
- _core.Dispose();
+
+ CTS.Dispose();
+ CTS = null;
}
+
+ tcpClient?.Dispose();
+ tcpClient = null;
+ semaphore?.Dispose();
+ semaphore = null;
+ mainStream?.Dispose();
+ mainStream = null;
+
+ minecraftStream?.Dispose();
+ minecraftStream = null;
+
try
{
+ pipe.Reader.Complete();
+ pipe.Writer.Complete();
pipe.Reset();
}
catch
{
}
-
pipe = null;
- _disposed = true;
GC.SuppressFinalize(this);
}
- public ValueTask DisposeAsync()
+
+
+
+ #region Core
+
+
+
+
+ //private int threshold;
+
+
+
+
+
+
+
+
+
+
+ private TcpClient tcpClient;
+
+ private async ValueTask CreateTcp(CancellationToken token)
{
+ token.ThrowIfCancellationRequested();
+ if (Config.Proxy is null)
+ {
+
+ tcpClient = new TcpClient();
+
+ _logger.Information("Подключение");
+ await tcpClient.ConnectAsync(Config.Host, Config.Port, token);
+ return tcpClient.GetStream();
+ }
+ _logger.Information($"Подключение к {Config.Proxy.Type} прокси {Config.Proxy.ProxyHost}:{Config.Proxy.ProxyPort}");
+
+
+ tcpClient = new();
+ try
+ {
+
+ await tcpClient.ConnectAsync(Config.Proxy.ProxyHost, Config.Proxy.ProxyPort, token);
+
+ var tcpStream = tcpClient.GetStream();
+
+ using (var timeout = new CancellationTokenSource(TimeSpan.FromSeconds(5)))
+ {
+
+ using var combined = CancellationTokenSource.CreateLinkedTokenSource(timeout.Token, token);
+
+ using var reg = combined.Token.Register(() => tcpStream.Dispose());
+
+ var stream = await Config.Proxy.ConnectAsync(
+ tcpStream,
+ Config.Host,
+ Config.Port,
+ combined.Token);
+
+ return stream;
+ }
+
+ }
+ catch
+ {
+ tcpClient.Dispose();
+ throw;
+ }
+
+
+ }
+
+ public async ValueTask Connect()
+ {
+ CTS = new();
+ mainStream = await CreateTcp(CTS.Token);
+
+ minecraftStream = new MinecraftStream(mainStream);
+
+ PacketSender.BaseStream = minecraftStream;
+ PacketReader.BaseStream = minecraftStream;
+
+ }
+ public ValueTask HandShake()
+ {
+
+ _logger.Information("Рукопожатие");
+ return SendPacketAsync(
+ new HandShakePacket(
+ HandShakeIntent.LOGIN,
+ (int)_protocol,
+ Config.Host,
+ Config.Port),
+ 0x00);
+
+
+ }
+
+
+
+
+ private async ValueTask LoginCore(CancellationToken cancellation)
+ {
+ bool ok = false;
+ do
+ {
+ using (Packet readData = await PacketReader.ReadNextPacketAsync(cancellation))
+ {
+ var reader = Performance.Readers.Get();
+ try
+ {
+ reader.BaseStream = readData.Data;
+
+
+ ok = await HandleLogin(reader, readData.Id);
+ }
+ finally
+ {
+ Performance.Readers.Return(reader);
+ }
+ }
+
+ } while (!ok);
+
+ }
+ private async Task HandleLogin(MinecraftPrimitiveReader reader, int id)
+ {
+
+ if (id == 0x02)
+ {
+ ChangeState(ClientState.Play);
+ _logger.Information("Переход в Play режим");
+ return true;
+ }
+ else if (id == 0x03)
+ {
+ var threshold = reader.ReadVarInt();
+ _logger.Information($"Включаем сжатие: {threshold}");
+ PacketReader.SwitchCompression(threshold);
+ PacketSender.SwitchCompression(threshold);
+ }
+ else if (id == 0x01)
+ {
+ reader.ReadString();
+ byte[] publicKey = reader.ReadByteArray();
+ byte[] verifyToken = reader.ReadByteArray();
+ var RSAService = CryptoHandler.DecodeRSAPublicKey(publicKey);
+ byte[] secretKey = CryptoHandler.GenerateAESPrivateKey();
+
+ byte[] g = RSAService.Encrypt(secretKey, false);
+ byte[] token = RSAService.Encrypt(verifyToken, false);
+ _logger.Information("Включаем шифрование");
+ await SendEncrypt(g, token);
+ minecraftStream.SwitchEncryption(secretKey);
+
+ }
+ else if (id == 0x00)
+ {
+ var r = reader.ReadString();
+
+ throw new LoginRejectedException(r);
+ }
+ else
+ {
+ throw new Exception("Unkown packet: " + id);
+ }
+
+ return false;
+ }
+
+ private async Task ReadPacketLoop()
+ {
+
+ try
+ {
+ while (!CTS.IsCancellationRequested)
+ {
+ using (var packet = await PacketReader.ReadNextPacketAsync(CTS.Token))
+ {
+ if (_packetPallete.TryGetIn(packet.Id, out var packetIn))
+ {
+ packet.Data.Position = 0;
+
+ var reader = Performance.Readers.Get();
+
+ try
+ {
+
+
+
+ reader.BaseStream = packet.Data;
+ OnPacket(reader, packetIn);
+
+ }
+ finally
+ {
+ Performance.Readers.Return(reader);
+ }
+ }
+ }
+ }
+
+ }
+ catch (Exception ex)
+ {
+
+ await pipe.Reader.CompleteAsync(ex);
+
+
+ CancelAll(ex);
+
+ throw;
+ //OnError?.Invoke(ex);
+ //throw;
+ }
+ finally
+ {
+
+ }
+
+ await pipe.Reader.CompleteAsync();
+
- return ValueTask.CompletedTask;
}
+ private async Task FillPipeAsync()
+ {
+ try
+ {
+ const int minimumBufferSize = 128;
+ while (!CTS.IsCancellationRequested)
+ {
+ Memory memory = pipe.Writer.GetMemory(minimumBufferSize);
+ int bytesRead = await minecraftStream.ReadAsync(memory, CTS.Token);
+
+
+
+
+ pipe.Writer.Advance(bytesRead);
+
+
+ FlushResult result = await pipe.Writer.FlushAsync(CTS.Token);
+ if (result.IsCompleted)
+ {
+ break;
+ }
+
+ if (bytesRead <= 0)
+ {
+ throw new EndOfStreamException();
+ }
+ }
+ }
+ catch (Exception ex)
+ {
+
+ await pipe.Writer.CompleteAsync(ex);
+
+ CancelAll(ex);
+ throw;
+ }
+ finally
+ {
+
+ }
+
+ await pipe.Writer.CompleteAsync();
+
+ }
+
+
+
+
+
+
+
+
+
+
+ #region Send
+
+
+
+ private ValueTask SendEncrypt(byte[] key, byte[] token)
+ {
+ return SendPacket(w =>
+ {
+ w.WriteByteArray(key);
+ w.WriteByteArray(token);
+ }, 0x01);
+ }
+
+ #endregion
+
+ #endregion
diff --git a/src/McProtoNet/McProtoNet/Client/MinecraftClientCore.cs b/src/McProtoNet/McProtoNet/Client/MinecraftClientCore.cs
index 81e45a99..994c09c0 100644
--- a/src/McProtoNet/McProtoNet/Client/MinecraftClientCore.cs
+++ b/src/McProtoNet/McProtoNet/Client/MinecraftClientCore.cs
@@ -1,478 +1,5 @@
-using McProtoNet.Core;
-using McProtoNet.Core.IO;
-using McProtoNet.Core.Protocol;
-using McProtoNet.Utils;
-using Microsoft.IO;
-using QuickProxyNet;
-using Serilog;
-using System.IO.Pipelines;
-using System.Net.Sockets;
-
-namespace McProtoNet
+namespace McProtoNet
{
- public delegate void OnPacketReceived(MinecraftPrimitiveReader reader, PacketIn id, CancellationToken cancellation);
-
- public class MinecraftClientCore : IDisposable, IAsyncDisposable
- {
- #region ReadOnlyFields
-
-
- private MinecraftVersion _protocol;
- private string _nick;
- private string _host;
- private ushort _port;
-
- private IProxyClient? _proxy;
- private Pipe pipe;
- private IPacketPallete _packetPallete;
- private ILogger _logger;
-
- private CancellationTokenSource CTS = new();
-
- public MinecraftClientCore(MinecraftVersion protocol, string nick, string host, ushort port, IProxyClient? proxy, IPacketPallete packetPallete, Pipe pipe, ILogger logger)
- {
- _protocol = protocol;
- _nick = nick;
- _host = host;
- _port = port;
- _proxy = proxy;
- _packetPallete = packetPallete;
- this.pipe = pipe;
- _logger = logger;
- }
-
-
- private int threshold;
- private SubProtocol _subProtocol;
-
-
-
- #endregion
-
- #region StateFields
- Stream mainStream;
- private MinecraftStream minecraftStream;
- public IMinecraftPacketReader PacketReader;
- public IMinecraftPacketSender PacketSender;
- #endregion
-
-
-
- public async Task Connect()
- {
-
-
- mainStream = await CreateTcp(CTS.Token);
-
-
-
- minecraftStream = new MinecraftStream(mainStream);
- PacketSender = new MinecraftPacketSender(minecraftStream, true);
-
-
- }
- public async Task HandShake()
- {
- _subProtocol = SubProtocol.HandShake;
- _logger.Information("Рукопожатие");
- await PacketSender.SendPacketAsync(
- new HandShakePacket(
- HandShakeIntent.LOGIN,
- (int)_protocol,
- _host,
- _port),
- 0x00, CTS.Token);
-
-
- }
- public async Task Login(OnPacketReceived packetReceived)
- {
- CTS.Token.ThrowIfCancellationRequested();
-
- _subProtocol = SubProtocol.Login;
- _logger.Information("Логинизация");
- await this.SendPacket(w =>
- {
- w.WriteString(this._nick);
- }, 0x00);
-
-
- await using (PacketReader = new MinecraftPacketReader(minecraftStream, false))
- {
- await LoginCore(CTS.Token);
- }
-
- Task fill = null;
-
- //if (Pipelines)
- {
- var readStream = pipe.Reader.AsStream();
-
-
- fill = FillPipeAsync(minecraftStream, CTS.Token);
-
- PacketReader = new MinecraftPacketReader(readStream, false);
- PacketReader.SwitchCompression(threshold);
-
-
-
- }
- //else
- //{
-
-
-
- // fill = Task.CompletedTask;
-
- // PacketReader = new MinecraftPacketReader(minecraftStream, false);
- // PacketReader.SwitchCompression(threshold);
- //}
- var read = ReadPacketLoop(CTS.Token, packetReceived);
-
- return Task.WhenAll(read, fill);
- }
-
- //public static bool Pipelines { get; set; } = true;
-
- private async ValueTask LoginCore(CancellationToken cancellation)
- {
- bool ok = false;
- do
- {
- using (Packet readData = await PacketReader.ReadNextPacketAsync(cancellation))
- {
- var reader = Performance.Readers.Get();
- try
- {
- reader.BaseStream = readData.Data;
-
-
- ok = await HandleLogin(reader, readData.Id);
- }
- finally
- {
- Performance.Readers.Return(reader);
- }
- }
-
- } while (!ok);
-
- }
- private async Task HandleLogin(MinecraftPrimitiveReader reader, int id)
- {
-
- if (id == 0x02)
- {
- _subProtocol = SubProtocol.Game;
- _logger.Information("Переход в Play режим");
- return true;
- }
- else if (id == 0x03)
- {
- threshold = reader.ReadVarInt();
- _logger.Information($"Включаем сжатие: {threshold}");
- PacketReader.SwitchCompression(threshold);
- PacketSender.SwitchCompression(threshold);
- }
- else if (id == 0x01)
- {
- reader.ReadString();
- byte[] publicKey = reader.ReadByteArray();
- byte[] verifyToken = reader.ReadByteArray();
- var RSAService = CryptoHandler.DecodeRSAPublicKey(publicKey);
- byte[] secretKey = CryptoHandler.GenerateAESPrivateKey();
-
- byte[] g = RSAService.Encrypt(secretKey, false);
- byte[] token = RSAService.Encrypt(verifyToken, false);
- _logger.Information("Включаем шифрование");
- await SendEncrypt(g, token);
- minecraftStream.SwitchEncryption(secretKey);
-
- }
- else if (id == 0x00)
- {
- var r = reader.ReadString();
-
- throw new LoginRejectedException(r);
- }
- else
- {
- throw new Exception("Unkown packet: " + id);
- }
-
- return false;
- }
-
-
- private async Task ReadPacketLoop(CancellationToken cancellationToken, OnPacketReceived packetReceived)
- {
-
- try
- {
-
-
-
- while (!cancellationToken.IsCancellationRequested)
- {
- using (var packet = await PacketReader.ReadNextPacketAsync(cancellationToken))
- {
- if (_packetPallete.TryGetIn(packet.Id, out var packetIn))
- {
- packet.Data.Position = 0;
-
- var reader = Performance.Readers.Get();
-
- try
- {
-
-
-
- reader.BaseStream = packet.Data;
-
- packetReceived.Invoke(reader, packetIn, cancellationToken);
- }
- finally
- {
- Performance.Readers.Return(reader);
- }
- }
- }
- }
- }
- finally
- {
- await pipe.Reader.CompleteAsync();
- }
-
- }
- private async Task FillPipeAsync(Stream stream, CancellationToken cancellationToken)
- {
- try
- {
- const int minimumBufferSize = 128;
- while (!cancellationToken.IsCancellationRequested)
- {
- Memory memory = pipe.Writer.GetMemory(minimumBufferSize);
- int bytesRead = await stream.ReadAsync(memory, cancellationToken);
- //Console.WriteLine("Read Block: " + bytesRead);
-
-
-
- pipe.Writer.Advance(bytesRead);
-
-
- FlushResult result = await pipe.Writer.FlushAsync();
- if (result.IsCompleted)
- {
- break;
- }
- if (bytesRead <= 0)
- {
- throw new EndOfStreamException();
- }
- }
- }
- finally
- {
- await pipe.Writer.CompleteAsync();
- }
- }
-
-
- public Task FillTask { get; private set; }
- public ValueTask ReadPacketsTask { get; private set; }
-
- private async ValueTask CreateTcp(CancellationToken token)
- {
- token.ThrowIfCancellationRequested();
- if (_proxy is null)
- {
-
- TcpClient tcp = new TcpClient();
-
- _logger.Information("Подключение");
- await tcp.ConnectAsync(_host, _port, token);
- return tcp.GetStream();
- }
- _logger.Information($"Подключение к {_proxy.Type} прокси {_proxy.ProxyHost}:{_proxy.ProxyPort}");
-
-
-
-
- return await _proxy.ConnectAsync(_host, _port, 5000, token);
-
-
- }
-
-
-
-
- bool _disposed;
-
-
-
- #region Send
-
-
-
- private ValueTask SendEncrypt(byte[] key, byte[] token)
- {
- return SendPacket(w =>
- {
- w.WriteByteArray(key);
- w.WriteByteArray(token);
- }, 0x01);
- }
- public ValueTask SendPacket(Action action, PacketOut id)
- {
- int _id = _packetPallete.GetOut(id);
- return SendPacket(action, _id);
- }
- private SemaphoreSlim semaphore = new SemaphoreSlim(1, 1);
- static RecyclableMemoryStreamManager streamManager = new();
-
-
- public async ValueTask SendPacket(Action action, int id)
- {
- await semaphore.WaitAsync();
- try
- {
- using (MemoryStream ms = streamManager.GetStream())
- {
- var writer = Performance.Writers.Get();
- try
- {
-
-
- writer.BaseStream = ms;
- action(writer);
- ms.Position = 0;
- }
- finally
- {
- Performance.Writers.Return(writer);
- }
- await PacketSender.SendPacketAsync(new(id, ms), CTS.Token);
-
- }
- }
- catch { }
- finally
- {
- semaphore.Release();
- }
- }
- public async ValueTask SendPacketAsync(IOutputPacket packet, int id)
- {
- await semaphore.WaitAsync();
- try
- {
- using (MemoryStream ms = streamManager.GetStream())
- {
- var writer = Performance.Writers.Get();
- try
- {
-
- writer.BaseStream = ms;
-
- packet.Write(writer);
-
- }
- finally
- {
- Performance.Writers.Return(writer);
- }
- ms.Position = 0;
- await PacketSender.SendPacketAsync(new(id, ms), CTS.Token);
- }
- }
- catch { }
- finally
- {
- semaphore.Release();
- }
- }
- #endregion
-
- #region Dispose
-
-
- public void Dispose()
- {
- if (_disposed) return;
-
- if (pipe is { })
- {
- pipe = null;
- }
- if (PacketSender is { })
- {
- PacketSender.Dispose();
- }
- PacketSender = null;
- if (PacketReader is { })
- {
- PacketReader.Dispose();
- }
- PacketReader = null;
-
- _proxy = null;
- _packetPallete = null;
- _disposed = true;
-
- _logger = null;
-
- if (CTS is { })
- {
- if (!CTS.IsCancellationRequested)
- {
- CTS.Cancel();
- }
- CTS.Dispose();
- }
- CTS = null;
-
- GC.SuppressFinalize(this);
-
- }
- public async ValueTask DisposeAsync()
- {
- if (_disposed) return;
-
-
- pipe = null;
-
- if (PacketSender is { })
- {
- await PacketSender.DisposeAsync();
- }
- PacketSender = null;
- if (PacketReader is { })
- {
- await PacketReader.DisposeAsync();
- }
- PacketReader = null;
- _proxy = null;
- _packetPallete = null;
- _disposed = true;
-
- _logger = null;
-
-
- if (!CTS.IsCancellationRequested)
- {
- await CTS.CancelAsync();
- }
- CTS.Dispose();
-
-
-
- GC.SuppressFinalize(this);
- }
-
- #endregion
- }
-
public class ReadWriteStream : Stream
{
diff --git a/src/McProtoNet/McProtoNet/McProtoNet.csproj b/src/McProtoNet/McProtoNet/McProtoNet.csproj
index 2cfd80ea..eac98c0b 100644
--- a/src/McProtoNet/McProtoNet/McProtoNet.csproj
+++ b/src/McProtoNet/McProtoNet/McProtoNet.csproj
@@ -24,7 +24,7 @@
-
+
diff --git a/src/McProtoNet/McProtoNet/PacketPalletes/PacketPalette_1_12_2.cs b/src/McProtoNet/McProtoNet/PacketPalletes/PacketPalette_1_12_2.cs
index 0fa46cde..a303164a 100644
--- a/src/McProtoNet/McProtoNet/PacketPalletes/PacketPalette_1_12_2.cs
+++ b/src/McProtoNet/McProtoNet/PacketPalletes/PacketPalette_1_12_2.cs
@@ -1,8 +1,12 @@
-namespace McProtoNet
+using System.Collections.Frozen;
+
+namespace McProtoNet
{
public class PacketPalette_1_12_2 : IPacketPallete
{
- private readonly Dictionary typeIn = new()
+ public static PacketPalette_1_12_2 Instance { get; } = new();
+
+ private readonly FrozenDictionary typeIn = FrozenDictionary.ToFrozenDictionary(new Dictionary()
{
{ 0x00, PacketIn.SpawnEntity },
{ 0x01, PacketIn.SpawnExperienceOrb },
@@ -84,9 +88,9 @@ public class PacketPalette_1_12_2 : IPacketPallete
{ 0x4D, PacketIn.Advancements },
{ 0x4E, PacketIn.EntityProperties },
{ 0x4F, PacketIn.EntityEffect },
- };
+ });
- private readonly Dictionary typeOut = new()
+ private readonly FrozenDictionary typeOut = FrozenDictionary.ToFrozenDictionary(new Dictionary()
{
{ PacketOut.TeleportConfirm, 0x00 },
{ PacketOut.TabComplete, 0x01 },
@@ -121,7 +125,7 @@ public class PacketPalette_1_12_2 : IPacketPallete
{ PacketOut.Spectate, 0x1E },
{ PacketOut.PlayerBlockPlacement, 0x1F },
{ PacketOut.UseItem, 0x20 },
- };
+ });
public int GetOut(PacketOut packet)
{
diff --git a/src/McProtoNet/McProtoNet/PacketPalletes/PacketPalette_1_13.cs b/src/McProtoNet/McProtoNet/PacketPalletes/PacketPalette_1_13.cs
index b2d655ae..69f47cd8 100644
--- a/src/McProtoNet/McProtoNet/PacketPalletes/PacketPalette_1_13.cs
+++ b/src/McProtoNet/McProtoNet/PacketPalletes/PacketPalette_1_13.cs
@@ -1,8 +1,12 @@
-namespace McProtoNet
+using System.Collections.Frozen;
+
+namespace McProtoNet
{
public class PacketPalette_1_13 : IPacketPallete
{
- private readonly Dictionary typeIn = new()
+ public static PacketPalette_1_13 Instance { get; } = new();
+
+ private readonly FrozenDictionary typeIn = FrozenDictionary.ToFrozenDictionary(new Dictionary()
{
{ 0x00, PacketIn.SpawnEntity },
{ 0x01, PacketIn.SpawnExperienceOrb },
@@ -90,9 +94,9 @@ public class PacketPalette_1_13 : IPacketPallete
{ 0x53, PacketIn.EntityEffect },
{ 0x54, PacketIn.DeclareRecipes },
{ 0x55, PacketIn.Tags },
- };
+ });
- private readonly Dictionary typeOut = new()
+ private readonly FrozenDictionary typeOut = FrozenDictionary.ToFrozenDictionary(new Dictionary()
{
{ PacketOut.TeleportConfirm, 0x00 },
{ PacketOut.QueryBlockNBT, 0x01 },
@@ -137,7 +141,7 @@ public class PacketPalette_1_13 : IPacketPallete
{ PacketOut.Spectate, 0x28 },
{ PacketOut.PlayerBlockPlacement, 0x29 },
{ PacketOut.UseItem, 0x2A },
- };
+ });
public int GetOut(PacketOut packet)
{
diff --git a/src/McProtoNet/McProtoNet/PacketPalletes/PacketPalette_1_14.cs b/src/McProtoNet/McProtoNet/PacketPalletes/PacketPalette_1_14.cs
index 6d7ee5b4..f31a035c 100644
--- a/src/McProtoNet/McProtoNet/PacketPalletes/PacketPalette_1_14.cs
+++ b/src/McProtoNet/McProtoNet/PacketPalletes/PacketPalette_1_14.cs
@@ -1,8 +1,12 @@
-namespace McProtoNet
+using System.Collections.Frozen;
+
+namespace McProtoNet
{
public class PacketPalette_1_14 : IPacketPallete
{
- private readonly Dictionary typeIn = new()
+ public static PacketPalette_1_14 Instance { get; } = new();
+
+ private readonly FrozenDictionary typeIn = FrozenDictionary.ToFrozenDictionary(new Dictionary()
{
{ 0x00, PacketIn.SpawnEntity },
{ 0x01, PacketIn.SpawnExperienceOrb },
@@ -97,9 +101,9 @@ public class PacketPalette_1_14 : IPacketPallete
{ 0x5A, PacketIn.DeclareRecipes },
{ 0x5B, PacketIn.Tags },
{ 0x5C, PacketIn.AcknowledgePlayerAction },
- };
+ });
- private readonly Dictionary typeOut = new()
+ private readonly FrozenDictionary typeOut = FrozenDictionary.ToFrozenDictionary(new Dictionary()
{
{ PacketOut.TeleportConfirm, 0x00 },
{ PacketOut.QueryBlockNBT, 0x01 },
@@ -147,7 +151,7 @@ public class PacketPalette_1_14 : IPacketPallete
{ PacketOut.Spectate, 0x2B },
{ PacketOut.PlayerBlockPlacement, 0x2C },
{ PacketOut.UseItem, 0x2D },
- };
+ });
public int GetOut(PacketOut packet)
{
diff --git a/src/McProtoNet/McProtoNet/PacketPalletes/PacketPalette_1_15.cs b/src/McProtoNet/McProtoNet/PacketPalletes/PacketPalette_1_15.cs
index 39f66203..37507473 100644
--- a/src/McProtoNet/McProtoNet/PacketPalletes/PacketPalette_1_15.cs
+++ b/src/McProtoNet/McProtoNet/PacketPalletes/PacketPalette_1_15.cs
@@ -1,8 +1,12 @@
-namespace McProtoNet
+using System.Collections.Frozen;
+
+namespace McProtoNet
{
public class PacketPalette_1_15 : IPacketPallete
{
- private readonly Dictionary typeIn = new()
+ public static PacketPalette_1_15 Instance { get; } = new();
+
+ private readonly FrozenDictionary typeIn = FrozenDictionary.ToFrozenDictionary(new Dictionary()
{
{ 0x00, PacketIn.SpawnEntity },
{ 0x01, PacketIn.SpawnExperienceOrb },
@@ -97,9 +101,9 @@ public class PacketPalette_1_15 : IPacketPallete
{ 0x5A, PacketIn.EntityEffect },
{ 0x5B, PacketIn.DeclareRecipes },
{ 0x5C, PacketIn.Tags },
- };
+ });
- private readonly Dictionary typeOut = new()
+ private readonly FrozenDictionary typeOut = FrozenDictionary.ToFrozenDictionary(new Dictionary()
{
{ PacketOut.TeleportConfirm, 0x00 },
{ PacketOut.QueryBlockNBT, 0x01 },
@@ -147,7 +151,7 @@ public class PacketPalette_1_15 : IPacketPallete
{ PacketOut.Spectate, 0x2B },
{ PacketOut.PlayerBlockPlacement, 0x2C },
{ PacketOut.UseItem, 0x2D },
- };
+ });
public int GetOut(PacketOut packet)
{
diff --git a/src/McProtoNet/McProtoNet/PacketPalletes/PacketPalette_1_16.cs b/src/McProtoNet/McProtoNet/PacketPalletes/PacketPalette_1_16.cs
index 7e664f1e..96aa5d19 100644
--- a/src/McProtoNet/McProtoNet/PacketPalletes/PacketPalette_1_16.cs
+++ b/src/McProtoNet/McProtoNet/PacketPalletes/PacketPalette_1_16.cs
@@ -4,6 +4,9 @@ namespace McProtoNet
{
public class PacketPalette_1_16 : IPacketPallete
{
+
+ public static PacketPalette_1_16 Instance { get; } = new();
+
private readonly FrozenDictionary typeIn = FrozenDictionary.ToFrozenDictionary(new Dictionary()
{
{ 0x00, PacketIn.SpawnEntity },
diff --git a/src/McProtoNet/McProtoNet/PacketPalletes/PacketPalette_1_16_2.cs b/src/McProtoNet/McProtoNet/PacketPalletes/PacketPalette_1_16_2.cs
index 80c06b0c..f11030aa 100644
--- a/src/McProtoNet/McProtoNet/PacketPalletes/PacketPalette_1_16_2.cs
+++ b/src/McProtoNet/McProtoNet/PacketPalletes/PacketPalette_1_16_2.cs
@@ -1,8 +1,11 @@
-namespace McProtoNet
+using System.Collections.Frozen;
+
+namespace McProtoNet
{
public class PacketPalette_1_16_2 : IPacketPallete
{
- private readonly Dictionary typeIn = new()
+ public static PacketPalette_1_16_2 Instance { get; } = new();
+ private readonly FrozenDictionary typeIn = FrozenDictionary.ToFrozenDictionary(new Dictionary()
{
{ 0x00, PacketIn.SpawnEntity },
{ 0x01, PacketIn.SpawnExperienceOrb },
@@ -96,9 +99,9 @@ public class PacketPalette_1_16_2 : IPacketPallete
{ 0x59, PacketIn.EntityEffect },
{ 0x5A, PacketIn.DeclareRecipes },
{ 0x5B, PacketIn.Tags },
- };
+ });
- private readonly Dictionary typeOut = new()
+ private readonly FrozenDictionary typeOut = FrozenDictionary.ToFrozenDictionary(new Dictionary()
{
{ PacketOut.TeleportConfirm, 0x00 },
{ PacketOut.QueryBlockNBT, 0x01 },
@@ -148,7 +151,7 @@ public class PacketPalette_1_16_2 : IPacketPallete
{ PacketOut.Spectate, 0x2D },
{ PacketOut.PlayerBlockPlacement, 0x2E },
{ PacketOut.UseItem, 0x2F },
- };
+ });
public int GetOut(PacketOut packet)
{
diff --git a/src/McProtoNet/McProtoNet/PacketPalletes/PacketPalette_1_17.cs b/src/McProtoNet/McProtoNet/PacketPalletes/PacketPalette_1_17.cs
index 6d12d570..648b31e4 100644
--- a/src/McProtoNet/McProtoNet/PacketPalletes/PacketPalette_1_17.cs
+++ b/src/McProtoNet/McProtoNet/PacketPalletes/PacketPalette_1_17.cs
@@ -1,8 +1,12 @@
-namespace McProtoNet
+using System.Collections.Frozen;
+
+namespace McProtoNet
{
public class PacketPalette_1_17 : IPacketPallete
{
- private readonly Dictionary typeIn = new()
+ public static PacketPalette_1_17 Instance { get; } = new();
+
+ private readonly FrozenDictionary typeIn = FrozenDictionary.ToFrozenDictionary(new Dictionary()
{
{ 0x00, PacketIn.SpawnEntity },
{ 0x01, PacketIn.SpawnExperienceOrb },
@@ -107,9 +111,9 @@ public class PacketPalette_1_17 : IPacketPallete
{ 0x64, PacketIn.EntityEffect },
{ 0x65, PacketIn.DeclareRecipes },
{ 0x66, PacketIn.Tags },
- };
+ });
- private readonly Dictionary typeOut = new()
+ private readonly FrozenDictionary typeOut = FrozenDictionary.ToFrozenDictionary(new Dictionary()
{
{ PacketOut.TeleportConfirm, 0x00 },
{ PacketOut.QueryBlockNBT, 0x01 },
@@ -159,7 +163,7 @@ public class PacketPalette_1_17 : IPacketPallete
{ PacketOut.Spectate, 0x2D },
{ PacketOut.PlayerBlockPlacement, 0x2E },
{ PacketOut.UseItem, 0x2F },
- };
+ });
public int GetOut(PacketOut packet)
{
diff --git a/src/McProtoNet/McProtoNet/PacketPalletes/PacketPalette_1_18.cs b/src/McProtoNet/McProtoNet/PacketPalletes/PacketPalette_1_18.cs
index 260b61de..c6feeb0b 100644
--- a/src/McProtoNet/McProtoNet/PacketPalletes/PacketPalette_1_18.cs
+++ b/src/McProtoNet/McProtoNet/PacketPalletes/PacketPalette_1_18.cs
@@ -1,8 +1,11 @@
-namespace McProtoNet
+using System.Collections.Frozen;
+
+namespace McProtoNet
{
public class PacketPalette_1_18 : IPacketPallete
{
- private readonly Dictionary typeIn = new()
+ public static PacketPalette_1_18 Instance { get; } = new();
+ private readonly FrozenDictionary typeIn = FrozenDictionary.ToFrozenDictionary(new Dictionary()
{
{ 0x00, PacketIn.SpawnEntity },
{ 0x01, PacketIn.SpawnExperienceOrb },
@@ -108,9 +111,9 @@ public class PacketPalette_1_18 : IPacketPallete
{ 0x65, PacketIn.EntityEffect },
{ 0x66, PacketIn.DeclareRecipes },
{ 0x67, PacketIn.Tags },
- };
+ });
- private readonly Dictionary typeOut = new()
+ private readonly FrozenDictionary typeOut = FrozenDictionary.ToFrozenDictionary(new Dictionary()
{
{ PacketOut.TeleportConfirm, 0x00 },
{ PacketOut.QueryBlockNBT, 0x01 },
@@ -160,7 +163,7 @@ public class PacketPalette_1_18 : IPacketPallete
{ PacketOut.Spectate, 0x2D },
{ PacketOut.PlayerBlockPlacement, 0x2E },
{ PacketOut.UseItem, 0x2F },
- };
+ });
public int GetOut(PacketOut packet)
{
diff --git a/src/Platforms/HolyClient.Desktop/HolyClient.Desktop.csproj b/src/Platforms/HolyClient.Desktop/HolyClient.Desktop.csproj
index c3800dd2..4f32f8bd 100644
--- a/src/Platforms/HolyClient.Desktop/HolyClient.Desktop.csproj
+++ b/src/Platforms/HolyClient.Desktop/HolyClient.Desktop.csproj
@@ -12,7 +12,7 @@
-
+ Exe
@@ -28,7 +28,7 @@
-
+
diff --git a/src/QuickProxyNet/Clients/ProxyClient.cs b/src/QuickProxyNet/Clients/ProxyClient.cs
index 23e6a280..dff6e467 100644
--- a/src/QuickProxyNet/Clients/ProxyClient.cs
+++ b/src/QuickProxyNet/Clients/ProxyClient.cs
@@ -1,8 +1,11 @@
-using System.Net;
+using System.Diagnostics;
+using System.Net;
using System.Net.Sockets;
namespace QuickProxyNet
{
+
+
public abstract class ProxyClient : IProxyClient
{
public abstract ProxyType Type { get; }
@@ -20,8 +23,16 @@ protected ProxyClient(string host, int port)
ProxyHost = host;
ProxyPort = port == 0 ? 1080 : port;
- }
+ socket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp)
+ {
+ NoDelay = true,
+ LingerState = new LingerOption(true, 0),
+ SendTimeout = 10000,
+ ReceiveTimeout = 10000
+ };
+ }
+ private Socket socket;
protected ProxyClient(string host, int port, NetworkCredential credentials) : this(host, port)
{
if (credentials == null)
@@ -70,27 +81,28 @@ static void ValidateArguments(string host, int port, int timeout)
throw new ArgumentOutOfRangeException(nameof(timeout));
}
- public async ValueTask ConnectAsync(string host, int port, CancellationToken cancellationToken = default(CancellationToken))
+ public async Task ConnectAsync(string host, int port, CancellationToken cancellationToken = default(CancellationToken))
{
- var socket = SocketHelper.CreateSocket();
-
+ var socket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp)
+ {
+ NoDelay = true,
+ LingerState = new LingerOption(true, 0),
+ SendTimeout = 10000,
+ ReceiveTimeout = 10000
+ };
try
{
await socket.ConnectAsync(ProxyHost, ProxyPort, cancellationToken);
- }
- catch
- {
- socket.Dispose();
- throw;
- }
- var stream = new NetworkStream(socket, true);
- using (cancellationToken.Register(() => stream.Dispose()))
- {
+ var stream = new NetworkStream(socket, true);
try
{
+
+
+ using var reg = cancellationToken.Register(() => stream.Dispose());
+
return await ConnectAsync(stream, host, port, cancellationToken);
}
catch
@@ -98,14 +110,24 @@ static void ValidateArguments(string host, int port, int timeout)
stream.Dispose();
throw;
}
+
+ }
+ catch
+ {
+
+
+ throw;
}
}
- public async virtual ValueTask ConnectAsync(string host, int port, int timeout, CancellationToken cancellationToken = default(CancellationToken))
+ public async virtual Task ConnectAsync(string host, int port, int timeout, CancellationToken cancellationToken = default(CancellationToken))
{
ValidateArguments(host, port, timeout);
+ cancellationToken.ThrowIfCancellationRequested();
+
+
using (var ts = new CancellationTokenSource(timeout))
{
@@ -116,14 +138,17 @@ static void ValidateArguments(string host, int port, int timeout)
}
}
- //throw new ProxyProtocolException("Deadline Exception");
+
}
public abstract ValueTask ConnectAsync(Stream source, string host, int port, CancellationToken cancellationToken = default);
- public async ValueTask EstablishTCPConnectionAsync(CancellationToken token)
+ public async Task EstablishTCPConnectionAsync(CancellationToken token)
{
- var socket = SocketHelper.CreateSocket();
+
+ throw new NotImplementedException();
+ //TcpClient tcpClient = new();
+
try
{
await socket.ConnectAsync(ProxyHost, ProxyPort, token);
@@ -138,5 +163,18 @@ public async ValueTask EstablishTCPConnectionAsync(CancellationToken tok
return new NetworkStream(socket, true);
}
+ private bool disposed = false;
+ public void Dispose()
+ {
+ if (disposed)
+ return;
+
+ disposed = true;
+ Interlocked.Exchange(ref socket, null)?.Dispose();
+
+
+ GC.SuppressFinalize(this);
+
+ }
}
}
diff --git a/src/QuickProxyNet/IProxyClient.cs b/src/QuickProxyNet/IProxyClient.cs
index f1f58258..1ef8e69c 100644
--- a/src/QuickProxyNet/IProxyClient.cs
+++ b/src/QuickProxyNet/IProxyClient.cs
@@ -1,25 +1,9 @@
using System.Net;
-using System.Net.Sockets;
namespace QuickProxyNet
{
- public static class SocketHelper
- {
-
- public static Socket CreateSocket()
- {
- var socket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp)
- {
- NoDelay = true,
- LingerState = new LingerOption(true, 0),
- SendTimeout = 10000,
- ReceiveTimeout = 10000
- };
- return socket;
- }
- }
- public interface IProxyClient
+ public interface IProxyClient : IDisposable
{
NetworkCredential ProxyCredentials { get; }
@@ -31,11 +15,11 @@ public interface IProxyClient
IPEndPoint LocalEndPoint { get; set; }
- ValueTask EstablishTCPConnectionAsync(CancellationToken token);
+ Task EstablishTCPConnectionAsync(CancellationToken token);
- ValueTask ConnectAsync(string host, int port, CancellationToken cancellationToken = default(CancellationToken));
+ Task ConnectAsync(string host, int port, CancellationToken cancellationToken = default(CancellationToken));
ValueTask ConnectAsync(Stream source, string host, int port, CancellationToken cancellationToken = default(CancellationToken));
- ValueTask ConnectAsync(string host, int port, int timeout, CancellationToken cancellationToken = default(CancellationToken));
+ Task ConnectAsync(string host, int port, int timeout, CancellationToken cancellationToken = default(CancellationToken));
}
}
diff --git a/src/QuickProxyNet/SocketHelper.cs b/src/QuickProxyNet/SocketHelper.cs
new file mode 100644
index 00000000..cc39fba0
--- /dev/null
+++ b/src/QuickProxyNet/SocketHelper.cs
@@ -0,0 +1,11 @@
+using System.Net.Sockets;
+
+namespace QuickProxyNet
+{
+ public static class SocketHelper
+ {
+
+
+
+ }
+}
diff --git a/src/Samples/StressTest/TestStressTestBehavior/SpammerHelloBehavior.cs b/src/Samples/StressTest/TestStressTestBehavior/SpammerHelloBehavior.cs
index 38a7f03b..ac8e94db 100644
--- a/src/Samples/StressTest/TestStressTestBehavior/SpammerHelloBehavior.cs
+++ b/src/Samples/StressTest/TestStressTestBehavior/SpammerHelloBehavior.cs
@@ -13,15 +13,15 @@ public Task Activate(CompositeDisposable disposables, IEnumerable
- {
- Console.WriteLine(x.Message);
+ //var d = bot.OnError.Subscribe(async x =>
+ //{
+
- await Task.Delay(1500);
- await bot.Restart(true);
- });
+ // await Task.Delay(1500);
+ // await bot.Restart(true);
+ //});
- disposables.Add(d);
+ //disposables.Add(d);
var d2 = bot.Client.OnJoinGame.Subscribe(async x =>
{
diff --git a/tasks.md b/tasks.md
new file mode 100644
index 00000000..36c4b1b7
--- /dev/null
+++ b/tasks.md
@@ -0,0 +1,70 @@
+
+
+
+
+# Current
+
+- ✔️ Сделать систему профилей
+- ✔️ Вырезать бот менеджер
+
+- ✔️ В процессе добавить две вкладки:
+ - ✔️ Графики с метриками
+ - ✔️ Популярные исключения
+
+- **Разделение чекинга прокси и стресс-теста**
+- **Добавить логгер в плагины**
+
+- **Добавить возможность плагинам делать свой UI**
+
+
+- Сделать возможность загрузки прокси с логином и паролем
+- Ввод команд в консоли стресс теста
+- на главную страницу добавить ссылку на Release Notes
+
+# General
+
+- Ники из файла
+- Сделать систему профилей
+- Number of bots переименовать во что то связанное с паралелностью
+
+- сделать возможность загрузки разных прокси из одного источника
+- Добавить флажок "Загрузка офлайн" у файла и ссылки
+- добавить больше настроек в поведение
+
+- В процессе кнопку отмены вынести отдельно и красиво и перекрасить в красный
+
+
+- Генерация отчетов на основе стресс-теста
+
+- Добавить вкладку с атакой разными методами
+- Динамическая перезагрузка плагинов
+- Загрузка плагинов из NuGet
+- В настройках сделать красиво
+- Добавить автообновление программы
+
+
+- Добавить логгер в программу
+- Добавить дилоговое окно с фатальной ошибкой
+- Протестить minver перед релизом
+
+- Отображение версии в настройках
+- Изменить описание в библиотеках
+
+# Documentation
+
+Сделать документацию по созданию плагинов
+
+# Future
+
+Сделать генерацию пакетов с помощью codegen
+
+
+# Readme
+
+- добавить RoadMap
+- Добавить release notes
+- Disclaimer
+
+# Discord
+
+- Уведомление о новой версии в дискорд