813 lines
29 KiB
C#
813 lines
29 KiB
C#
// ReSharper disable once RedundantUsingDirective
|
|
|
|
using System;
|
|
using System.Collections.Concurrent;
|
|
using System.Collections.Generic;
|
|
using System.IO;
|
|
using System.Linq;
|
|
using System.Reflection;
|
|
using System.Runtime.CompilerServices;
|
|
using System.Runtime.InteropServices;
|
|
using System.Threading;
|
|
using System.Threading.Tasks;
|
|
using ClassicAssist.Data;
|
|
using ClassicAssist.Data.Abilities;
|
|
using ClassicAssist.Data.Commands;
|
|
using ClassicAssist.Data.Hotkeys;
|
|
using ClassicAssist.Data.Macros;
|
|
using ClassicAssist.Data.Scavenger;
|
|
using ClassicAssist.Data.Targeting;
|
|
using ClassicAssist.Misc;
|
|
using ClassicAssist.UO.Data;
|
|
using ClassicAssist.UO.Network;
|
|
using ClassicAssist.UO.Network.PacketFilter;
|
|
using ClassicAssist.UO.Network.Packets;
|
|
using ClassicAssist.UO.Objects;
|
|
using CUO_API;
|
|
using Sentry;
|
|
|
|
[assembly: InternalsVisibleTo( "ClassicAssist.Tests" )]
|
|
|
|
// ReSharper disable once CheckNamespace
|
|
namespace ClassicAssist.Shared
|
|
{
|
|
public static partial class Engine
|
|
{
|
|
public delegate void dConnected();
|
|
|
|
public delegate void dDisconnected();
|
|
|
|
public delegate void dPlayerInitialized( PlayerMobile player );
|
|
|
|
public delegate void dSendRecvPacket( byte[] data, int length );
|
|
|
|
public delegate void dUpdateWindowTitle();
|
|
|
|
private const int MAX_DISTANCE = 32;
|
|
|
|
private static OnConnected _onConnected;
|
|
private static OnDisconnected _onDisconnected;
|
|
private static OnPacketSendRecv _onReceive;
|
|
private static OnPacketSendRecv _onSend;
|
|
private static OnGetUOFilePath _getUOFilePath;
|
|
private static OnPacketSendRecv _sendToClient;
|
|
private static OnPacketSendRecv _sendToServer;
|
|
private static OnGetPacketLength _getPacketLength;
|
|
private static OnUpdatePlayerPosition _onPlayerPositionChanged;
|
|
private static OnClientClose _onClientClosing;
|
|
private static OnFocusGained _onFocusGained;
|
|
private static OnFocusLost _onFocusLost;
|
|
private static readonly PacketFilter _incomingPacketFilter = new PacketFilter();
|
|
private static readonly PacketFilter _outgoingPacketPreFilter = new PacketFilter();
|
|
private static readonly PacketFilter _outgoingPacketPostFilter = new PacketFilter();
|
|
private static OnHotkey _onHotkeyPressed;
|
|
private static RequestMove _requestMove;
|
|
|
|
private static readonly int[] _sequenceList = new int[256];
|
|
private static OnMouse _onMouse;
|
|
|
|
private static readonly DateTime[] _lastMouseAction = new DateTime[(int)MouseOptions.None];
|
|
private static readonly object _clientSendLock = new object();
|
|
private static DateTime _nextPacketRecvTime;
|
|
|
|
private static readonly TimeSpan PACKET_RECV_DELAY = TimeSpan.FromMilliseconds( 5 );
|
|
private static readonly object _serverSendLock = new object();
|
|
|
|
private static readonly TimeSpan PACKET_SEND_DELAY = TimeSpan.FromMilliseconds( 5 );
|
|
private static DateTime _nextPacketSendTime;
|
|
private static unsafe PluginHeader* _plugin;
|
|
public static int LastSpellID;
|
|
|
|
public static Assembly ClassicAssembly { get; set; }
|
|
|
|
public static string ClientPath { get; set; }
|
|
public static Version ClientVersion { get; set; }
|
|
public static bool Connected { get; set; }
|
|
public static ShardEntry CurrentShard { get; set; }
|
|
public static IDispatcher Dispatcher { get; set; }
|
|
public static FeatureFlags Features { get; set; }
|
|
public static GumpCollection Gumps { get; set; } = new GumpCollection();
|
|
public static bool IsClientFocused { get; set; }
|
|
public static ItemCollection Items { get; set; } = new ItemCollection( 0 );
|
|
public static CircularBuffer<JournalEntry> Journal { get; set; } = new CircularBuffer<JournalEntry>( 1024 );
|
|
|
|
public static DateTime LastActionPacket { get; set; }
|
|
public static int LastPromptID { get; set; }
|
|
public static int LastPromptSerial { get; set; }
|
|
public static TargetQueue<object> LastTargetQueue { get; set; } = new TargetQueue<object>();
|
|
public static MenuCollection Menus { get; set; } = new MenuCollection();
|
|
public static MobileCollection Mobiles { get; set; } = new MobileCollection( Items );
|
|
public static PacketWaitEntries PacketWaitEntries { get; set; }
|
|
public static PlayerMobile Player { get; set; }
|
|
public static QuestPointerList QuestPointers { get; set; } = new QuestPointerList();
|
|
public static RehueList RehueList { get; set; } = new RehueList();
|
|
public static List<ShardEntry> Shards { get; set; }
|
|
public static string StartupPath { get; set; }
|
|
public static bool TargetExists { get; set; }
|
|
public static TargetFlags TargetFlags { get; set; }
|
|
public static int TargetSerial { get; set; }
|
|
public static TargetType TargetType { get; set; }
|
|
public static IUIInvoker UIInvoker { get; set; }
|
|
public static bool WaitingForTarget { get; set; }
|
|
internal static ConcurrentDictionary<uint, int> GumpList { get; set; } = new ConcurrentDictionary<uint, int>();
|
|
public static IMessageBoxProvider MessageBoxProvider { get; private set; }
|
|
|
|
public static event dUpdateWindowTitle UpdateWindowTitleEvent;
|
|
|
|
public static event dSendRecvPacket InternalPacketSentEvent;
|
|
public static event dSendRecvPacket InternalPacketReceivedEvent;
|
|
|
|
public static event dSendRecvPacket PacketReceivedEvent;
|
|
public static event dSendRecvPacket PacketSentEvent;
|
|
public static event dSendRecvPacket SentPacketFilteredEvent;
|
|
public static event dSendRecvPacket ReceivedPacketFilteredEvent;
|
|
public static event dConnected ConnectedEvent;
|
|
public static event dDisconnected DisconnectedEvent;
|
|
public static event dPlayerInitialized PlayerInitializedEvent;
|
|
|
|
public static unsafe void Install( PluginHeader* plugin, IMessageBoxProvider provider )
|
|
{
|
|
_plugin = plugin;
|
|
MessageBoxProvider = provider;
|
|
|
|
Initialize();
|
|
|
|
InitializePlugin( plugin );
|
|
}
|
|
|
|
internal static unsafe void InitializePlugin( PluginHeader* plugin )
|
|
{
|
|
Console.WriteLine($"[CA] InitializePlugin called, plugin ptr={(IntPtr)plugin}");
|
|
try
|
|
{
|
|
_onConnected = OnConnected;
|
|
_onDisconnected = OnDisconnected;
|
|
_onReceive = OnPacketReceive;
|
|
_onSend = OnPacketSend;
|
|
_onPlayerPositionChanged = OnPlayerPositionChanged;
|
|
_onClientClosing = OnClientClosing;
|
|
_onHotkeyPressed = OnHotkeyPressed;
|
|
_onMouse = OnMouse;
|
|
_onFocusGained = OnFocusGained;
|
|
_onFocusLost = OnFocusLost;
|
|
Console.WriteLine($"[CA] Delegates assigned");
|
|
WindowHandle = plugin->HWND;
|
|
Console.WriteLine($"[CA] WindowHandle={WindowHandle}");
|
|
|
|
plugin->OnConnected = Marshal.GetFunctionPointerForDelegate( _onConnected );
|
|
plugin->OnDisconnected = Marshal.GetFunctionPointerForDelegate( _onDisconnected );
|
|
plugin->OnRecv = Marshal.GetFunctionPointerForDelegate( _onReceive );
|
|
plugin->OnSend = Marshal.GetFunctionPointerForDelegate( _onSend );
|
|
plugin->OnPlayerPositionChanged = Marshal.GetFunctionPointerForDelegate( _onPlayerPositionChanged );
|
|
plugin->OnClientClosing = Marshal.GetFunctionPointerForDelegate( _onClientClosing );
|
|
plugin->OnHotkeyPressed = Marshal.GetFunctionPointerForDelegate( _onHotkeyPressed );
|
|
plugin->OnMouse = Marshal.GetFunctionPointerForDelegate( _onMouse );
|
|
plugin->OnFocusGained = Marshal.GetFunctionPointerForDelegate( _onFocusGained );
|
|
plugin->OnFocusLost = Marshal.GetFunctionPointerForDelegate( _onFocusLost );
|
|
|
|
_getPacketLength = Marshal.GetDelegateForFunctionPointer<OnGetPacketLength>( plugin->GetPacketLength );
|
|
_getUOFilePath = Marshal.GetDelegateForFunctionPointer<OnGetUOFilePath>( plugin->GetUOFilePath );
|
|
_sendToClient = Marshal.GetDelegateForFunctionPointer<OnPacketSendRecv>( plugin->Recv );
|
|
_sendToServer = Marshal.GetDelegateForFunctionPointer<OnPacketSendRecv>( plugin->Send );
|
|
_requestMove = Marshal.GetDelegateForFunctionPointer<RequestMove>( plugin->RequestMove );
|
|
|
|
ClientPath = _getUOFilePath();
|
|
ClientVersion = new Version( (byte)(plugin->ClientVersion >> 24), (byte)(plugin->ClientVersion >> 16),
|
|
(byte)(plugin->ClientVersion >> 8), (byte)plugin->ClientVersion );
|
|
|
|
if (!Path.IsPathRooted( ClientPath ))
|
|
{
|
|
ClientPath = Path.GetFullPath( ClientPath );
|
|
}
|
|
|
|
Art.Initialize( ClientPath );
|
|
Hues.Initialize( ClientPath );
|
|
Cliloc.Initialize( ClientPath );
|
|
Skills.Initialize( ClientPath );
|
|
Speech.Initialize( ClientPath );
|
|
TileData.Initialize( ClientPath );
|
|
Statics.Initialize( ClientPath );
|
|
MapInfo.Initialize( ClientPath );
|
|
|
|
ClassicAssembly = AppDomain.CurrentDomain.GetAssemblies()
|
|
.FirstOrDefault( a => a.FullName.StartsWith( "ClassicUO," ) );
|
|
|
|
InitializeExtensions();
|
|
Console.WriteLine("[CA] InitializePlugin DONE");
|
|
}
|
|
catch (Exception ex)
|
|
{
|
|
Console.WriteLine($"[CA] InitializePlugin ERROR: {ex}");
|
|
throw;
|
|
}
|
|
}
|
|
|
|
private static void InitializeExtensions()
|
|
{
|
|
IEnumerable<Type> types = Assembly.GetExecutingAssembly().GetTypes()
|
|
.Where( t => typeof( IExtension ).IsAssignableFrom( t ) && t.IsClass );
|
|
|
|
foreach (Type type in types)
|
|
{
|
|
try
|
|
{
|
|
IExtension instance = (IExtension)Activator.CreateInstance( type );
|
|
instance?.Initialize();
|
|
}
|
|
catch (Exception e)
|
|
{
|
|
Console.WriteLine( e.ToString() );
|
|
}
|
|
}
|
|
}
|
|
|
|
private static void OnMouse( int button, int wheel )
|
|
{
|
|
MouseOptions mouse = MouseOptions.None;
|
|
|
|
if (button > 0)
|
|
{
|
|
mouse = SDLKeys.MouseButtonToMouseOptions( button );
|
|
}
|
|
|
|
if (wheel != 0)
|
|
{
|
|
mouse = wheel < 0 ? MouseOptions.MouseWheelDown : MouseOptions.MouseWheelUp;
|
|
|
|
if (Options.CurrentOptions.LimitMouseWheelTrigger)
|
|
{
|
|
TimeSpan diff = DateTime.Now - _lastMouseAction[(int)mouse];
|
|
|
|
if (diff < TimeSpan.FromMilliseconds( Options.CurrentOptions.LimitMouseWheelTriggerMS ))
|
|
{
|
|
return;
|
|
}
|
|
}
|
|
|
|
_lastMouseAction[(int)mouse] = DateTime.Now;
|
|
}
|
|
|
|
HotkeyManager.GetInstance().OnMouseAction( mouse );
|
|
}
|
|
|
|
private static bool OnHotkeyPressed( int key, int mod, bool pressed )
|
|
{
|
|
if ( !IsClientFocused )
|
|
{
|
|
return false;
|
|
}
|
|
|
|
Key keys = SDLKeys.SDLKeyToKeys( key );
|
|
|
|
bool pass = HotkeyManager.GetInstance().OnHotkeyPressed( keys );
|
|
|
|
return !pass;
|
|
}
|
|
|
|
private static void OnClientClosing()
|
|
{
|
|
if ( Options.CurrentOptions != null )
|
|
{
|
|
Options.Save( Options.CurrentOptions );
|
|
}
|
|
|
|
try { AssistantOptions.Save(); } catch { /* ignored */ }
|
|
try { SentrySdk.Close(); } catch { /* ignored */ }
|
|
}
|
|
|
|
private static void OnPlayerPositionChanged( int x, int y, int z )
|
|
{
|
|
if (Player != null)
|
|
{
|
|
Player.X = x;
|
|
Player.Y = y;
|
|
Player.Z = z;
|
|
}
|
|
|
|
Items.RemoveByDistance( MAX_DISTANCE, x, y );
|
|
Mobiles.RemoveByDistance( MAX_DISTANCE, x, y );
|
|
ScavengerManager.GetInstance().CheckArea?.Invoke();
|
|
}
|
|
|
|
public static Item GetOrCreateItem( int serial, int containerSerial = -1 )
|
|
{
|
|
Item item = Items.GetItem( serial );
|
|
|
|
if (item != null)
|
|
{
|
|
return item;
|
|
}
|
|
|
|
item = new Item( serial, containerSerial );
|
|
|
|
if (IncomingPacketHandlers.PropertyCache.TryGetValue( serial, out Property[] properties ))
|
|
{
|
|
item.Properties = properties;
|
|
}
|
|
|
|
return item;
|
|
}
|
|
|
|
public static Mobile GetOrCreateMobile( int serial )
|
|
{
|
|
if (Player?.Serial == serial)
|
|
{
|
|
return Player;
|
|
}
|
|
|
|
if (Mobiles.GetMobile( serial, out Mobile mobile ))
|
|
{
|
|
return mobile;
|
|
}
|
|
|
|
mobile = new Mobile( serial );
|
|
|
|
if (IncomingPacketHandlers.PropertyCache.TryGetValue( serial, out Property[] properties ))
|
|
{
|
|
mobile.Properties = properties;
|
|
}
|
|
|
|
return mobile;
|
|
}
|
|
|
|
private static void Initialize()
|
|
{
|
|
StartupPath = Path.GetDirectoryName( Assembly.GetExecutingAssembly().Location );
|
|
|
|
if (StartupPath == null)
|
|
{
|
|
throw new InvalidOperationException();
|
|
}
|
|
|
|
AppDomain.CurrentDomain.AssemblyResolve += OnAssemblyResolve;
|
|
|
|
PacketWaitEntries = new PacketWaitEntries();
|
|
|
|
IncomingQueue = new ThreadQueue<Packet>( ProcessIncomingQueue );
|
|
OutgoingQueue = new ThreadQueue<Packet>( ProcessOutgoingQueue );
|
|
|
|
IncomingPacketHandlers.Initialize();
|
|
OutgoingPacketHandlers.Initialize();
|
|
|
|
IncomingPacketFilters.Initialize();
|
|
OutgoingPacketFilters.Initialize();
|
|
|
|
CommandsManager.Initialize();
|
|
|
|
AssistantOptions.Load();
|
|
}
|
|
|
|
private static void ProcessIncomingQueue( Packet packet )
|
|
{
|
|
try
|
|
{
|
|
PacketReceivedEvent?.Invoke( packet.GetPacket(), packet.GetLength() );
|
|
|
|
PacketHandler handler = IncomingPacketHandlers.GetHandler( packet.GetPacketID() );
|
|
|
|
int length = _getPacketLength( packet.GetPacketID() );
|
|
|
|
handler?.OnReceive?.Invoke( new PacketReader( packet.GetPacket(), packet.GetLength(), length > 0 ) );
|
|
|
|
PacketWaitEntries?.CheckWait( packet.GetPacket(), PacketDirection.Incoming );
|
|
}
|
|
catch (Exception e)
|
|
{
|
|
SentrySdk.WithScope( scope =>
|
|
{
|
|
scope.SetExtra( "Packet", packet.GetPacket() );
|
|
scope.SetExtra( "Player", Player.ToString() );
|
|
scope.SetExtra( "WorldItemCount", Items.Count() );
|
|
scope.SetExtra( "WorldMobileCount", Mobiles.Count() );
|
|
SentrySdk.CaptureException( e );
|
|
} );
|
|
}
|
|
}
|
|
|
|
private static void ProcessOutgoingQueue( Packet packet )
|
|
{
|
|
try
|
|
{
|
|
PacketSentEvent?.Invoke( packet.GetPacket(), packet.GetLength() );
|
|
|
|
PacketHandler handler = OutgoingPacketHandlers.GetHandler( packet.GetPacketID() );
|
|
|
|
int length = _getPacketLength( packet.GetPacketID() );
|
|
|
|
handler?.OnReceive?.Invoke( new PacketReader( packet.GetPacket(), packet.GetLength(), length > 0 ) );
|
|
|
|
PacketWaitEntries?.CheckWait( packet.GetPacket(), PacketDirection.Outgoing );
|
|
}
|
|
catch (Exception e)
|
|
{
|
|
SentrySdk.WithScope( scope =>
|
|
{
|
|
scope.SetExtra( "Packet", packet.GetPacket() );
|
|
scope.SetExtra( "Player", Player.ToString() );
|
|
scope.SetExtra( "WorldItemCount", Items.Count() );
|
|
scope.SetExtra( "WorldMobileCount", Mobiles.Count() );
|
|
SentrySdk.CaptureException( e );
|
|
} );
|
|
}
|
|
}
|
|
|
|
private static Assembly OnAssemblyResolve( object sender, ResolveEventArgs args )
|
|
{
|
|
string assemblyname = new AssemblyName( args.Name ).Name;
|
|
|
|
string[] searchPaths = { StartupPath, RuntimeEnvironment.GetRuntimeDirectory() };
|
|
|
|
if (assemblyname.Contains( "Colletions" ))
|
|
{
|
|
assemblyname = "System.Collections";
|
|
}
|
|
|
|
foreach (string searchPath in searchPaths)
|
|
{
|
|
string fullPath = Path.Combine( searchPath, assemblyname + ".dll" );
|
|
|
|
string culture = new AssemblyName( args.Name ).CultureName;
|
|
|
|
if (!File.Exists( fullPath ))
|
|
{
|
|
string culturePath = Path.Combine( searchPath, culture, assemblyname + ".dll" );
|
|
|
|
if (File.Exists( culturePath ))
|
|
{
|
|
fullPath = culturePath;
|
|
}
|
|
else
|
|
{
|
|
continue;
|
|
}
|
|
}
|
|
|
|
Assembly assembly = Assembly.LoadFrom( fullPath );
|
|
|
|
return assembly;
|
|
}
|
|
|
|
return null;
|
|
}
|
|
|
|
public static void SetPlayer( PlayerMobile mobile )
|
|
{
|
|
Player = mobile;
|
|
|
|
PlayerInitializedEvent?.Invoke( mobile );
|
|
|
|
mobile.MobileStatusUpdated += ( status, newStatus ) =>
|
|
{
|
|
if (!Options.CurrentOptions.UseDeathScreenWhilstHidden)
|
|
{
|
|
return;
|
|
}
|
|
|
|
if (newStatus.HasFlag( MobileStatus.Hidden ))
|
|
{
|
|
SendPacketToClient( new MobileUpdate( mobile.Serial, mobile.ID == 0x191 ? 0x193 : 0x192, mobile.Hue,
|
|
newStatus, mobile.X, mobile.Y, mobile.Z, mobile.Direction ) );
|
|
}
|
|
};
|
|
|
|
//TODO
|
|
//Task.Run( async () =>
|
|
//{
|
|
// try
|
|
// {
|
|
// GitHubClient client = new GitHubClient( new ProductHeaderValue( "ClassicAssist" ) );
|
|
|
|
// IReadOnlyList<Release> releases =
|
|
// await client.Repository.Release.GetAll( "Reetus", "ClassicAssist" );
|
|
|
|
// Release latestRelease = releases.FirstOrDefault();
|
|
|
|
// if ( latestRelease == null )
|
|
// {
|
|
// return;
|
|
// }
|
|
|
|
// Version latestVersion = Version.Parse( latestRelease.TagName );
|
|
|
|
// if ( !Version.TryParse(
|
|
// FileVersionInfo.GetVersionInfo( Path.Combine( StartupPath, "ClassicAssist.dll" ) )
|
|
// .ProductVersion, out Version localVersion ) )
|
|
// {
|
|
// return;
|
|
// }
|
|
|
|
// if ( latestVersion > localVersion && AssistantOptions.UpdateGumpVersion < latestVersion )
|
|
// {
|
|
// IReadOnlyList<GitHubCommit> commits =
|
|
// await client.Repository.Commit.GetAll( "Reetus", "ClassicAssist" );
|
|
|
|
// IEnumerable<GitHubCommit> latestCommits =
|
|
// commits.OrderByDescending( c => c.Commit.Author.Date ).Take( 15 );
|
|
|
|
// StringBuilder commitMessage = new StringBuilder();
|
|
|
|
// foreach ( GitHubCommit gitHubCommit in latestCommits )
|
|
// {
|
|
// commitMessage.AppendLine( $"{gitHubCommit.Commit.Author.Date.Date.ToShortDateString()}:" );
|
|
// commitMessage.AppendLine();
|
|
// commitMessage.AppendLine( gitHubCommit.Commit.Message );
|
|
// commitMessage.AppendLine();
|
|
// }
|
|
|
|
// StringBuilder message = new StringBuilder();
|
|
// message.AppendLine( Strings.ProductName );
|
|
// message.AppendLine(
|
|
// $"{Strings.New_version_available_} <A HREF=\"https://github.com/Reetus/ClassicAssist/releases/tag/{latestVersion}\">{latestVersion}</A>" );
|
|
// message.AppendLine();
|
|
// message.AppendLine( commitMessage.ToString() );
|
|
// message.AppendLine(
|
|
// $"<A HREF=\"https://github.com/Reetus/ClassicAssist/commits/master\">{Strings.See_More}</A>" );
|
|
|
|
// UpdateMessageGump gump =
|
|
// new UpdateMessageGump( WindowHandle, message.ToString(), latestVersion );
|
|
// gump.SendGump();
|
|
// }
|
|
// }
|
|
// catch ( Exception )
|
|
// {
|
|
// // Squash all
|
|
// }
|
|
//} );
|
|
|
|
AbilitiesManager.GetInstance().Enabled = AbilityType.None;
|
|
AbilitiesManager.GetInstance().ResendGump( AbilityType.None );
|
|
|
|
Task.Run( async () =>
|
|
{
|
|
await Task.Delay( 3000 );
|
|
MacroManager.GetInstance().Autostart();
|
|
} );
|
|
}
|
|
|
|
public static void SendPacketToServer( byte[] packet, int length )
|
|
{
|
|
lock (_serverSendLock)
|
|
{
|
|
while (DateTime.Now < _nextPacketSendTime)
|
|
{
|
|
Thread.Sleep( 1 );
|
|
}
|
|
|
|
InternalPacketSentEvent?.Invoke( packet, length );
|
|
|
|
PacketWaitEntries?.CheckWait( packet, PacketDirection.Outgoing, true );
|
|
|
|
(byte[] data, int dataLength) = Utility.CopyBuffer( packet, length );
|
|
|
|
_sendToServer?.Invoke( ref data, ref dataLength );
|
|
|
|
_nextPacketSendTime = DateTime.Now + PACKET_SEND_DELAY;
|
|
}
|
|
}
|
|
|
|
public static void SendPacketToClient( byte[] packet, int length, bool delay = true )
|
|
{
|
|
try
|
|
{
|
|
lock (_clientSendLock)
|
|
{
|
|
if (delay)
|
|
{
|
|
while (DateTime.Now < _nextPacketRecvTime)
|
|
{
|
|
Thread.Sleep( 1 );
|
|
}
|
|
}
|
|
|
|
InternalPacketReceivedEvent?.Invoke( packet, length );
|
|
|
|
_sendToClient?.Invoke( ref packet, ref length );
|
|
|
|
_nextPacketRecvTime = DateTime.Now + PACKET_RECV_DELAY;
|
|
}
|
|
}
|
|
catch (ThreadInterruptedException)
|
|
{
|
|
// Macro was interupted whilst we were waiting...
|
|
}
|
|
}
|
|
|
|
public static void SendPacketToClient( PacketWriter packet )
|
|
{
|
|
byte[] data = packet.ToArray();
|
|
|
|
SendPacketToClient( data, data.Length );
|
|
}
|
|
|
|
public static void SendPacketToClient( BasePacket basePacket, bool delay = true )
|
|
{
|
|
if (basePacket.Direction != PacketDirection.Any && basePacket.Direction != PacketDirection.Incoming)
|
|
{
|
|
throw new InvalidOperationException( "Send packet wrong direction." );
|
|
}
|
|
|
|
byte[] data = basePacket.ToArray();
|
|
|
|
SendPacketToClient( data, data.Length, delay );
|
|
}
|
|
|
|
public static void SendPacketToServer( PacketWriter packet )
|
|
{
|
|
byte[] data = packet.ToArray();
|
|
|
|
SendPacketToServer( data, data.Length );
|
|
}
|
|
|
|
public static void SendPacketToServer( BasePacket basePacket )
|
|
{
|
|
if (basePacket.Direction != PacketDirection.Any && basePacket.Direction != PacketDirection.Outgoing)
|
|
{
|
|
throw new InvalidOperationException( "Send packet wrong direction." );
|
|
}
|
|
|
|
byte[] data = basePacket.ToArray();
|
|
|
|
if (data == null)
|
|
{
|
|
return;
|
|
}
|
|
|
|
SendPacketToServer( data, data.Length );
|
|
}
|
|
|
|
public static bool Move( Direction direction, bool run )
|
|
{
|
|
return _requestMove?.Invoke( (int)direction, run ) ?? false;
|
|
}
|
|
|
|
public static void UpdateWindowTitle()
|
|
{
|
|
UpdateWindowTitleEvent?.Invoke();
|
|
}
|
|
|
|
public static void GetMapZ( int x, int y, out sbyte groundZ, out sbyte staticZ )
|
|
{
|
|
groundZ = staticZ = (sbyte)(Player?.Z ?? 0);
|
|
|
|
if (ClassicAssembly == null)
|
|
{
|
|
return;
|
|
}
|
|
|
|
PropertyInfo mapProperty = ClassicAssembly.GetType( "ClassicUO.Game.World" )?.GetProperty( "Map" );
|
|
|
|
if (mapProperty == null)
|
|
{
|
|
return;
|
|
}
|
|
|
|
object mapInstance = mapProperty.GetMethod.Invoke( mapProperty, null );
|
|
|
|
MethodInfo getMapZMethod = mapInstance?.GetType().GetMethod( "GetMapZ" );
|
|
|
|
if (getMapZMethod == null)
|
|
{
|
|
return;
|
|
}
|
|
|
|
object[] parameters = { x, y, null, null };
|
|
|
|
getMapZMethod.Invoke( mapInstance, parameters );
|
|
|
|
groundZ = (sbyte)parameters[2];
|
|
staticZ = (sbyte)parameters[3];
|
|
}
|
|
|
|
public static Stream GetResourceStream( string name )
|
|
{
|
|
return Assembly.GetAssembly( typeof( Engine ) )
|
|
.GetManifestResourceStream( $"ClassicAssist.Shared.Resources.{name}" );
|
|
}
|
|
|
|
#region ClassicUO Events
|
|
|
|
private static bool OnPacketSend( ref byte[] data, ref int length )
|
|
{
|
|
bool filter = false;
|
|
|
|
if (CommandsManager.IsSpeechPacket( data[0] ))
|
|
{
|
|
filter = CommandsManager.CheckCommand( data, length );
|
|
}
|
|
|
|
if (_outgoingPacketPreFilter.MatchFilterAll( data, out PacketFilterInfo[] pfis ) > 0)
|
|
{
|
|
foreach (PacketFilterInfo pfi in pfis)
|
|
{
|
|
pfi.Action?.Invoke( data, pfi );
|
|
}
|
|
|
|
SentPacketFilteredEvent?.Invoke( data, data.Length );
|
|
|
|
PacketWaitEntries.CheckWait( data, PacketDirection.Outgoing, true );
|
|
|
|
return false;
|
|
}
|
|
|
|
if (OutgoingPacketFilters.CheckPacket( ref data, ref length ))
|
|
{
|
|
SentPacketFilteredEvent?.Invoke( data, data.Length );
|
|
|
|
return false;
|
|
}
|
|
|
|
OutgoingQueue.Enqueue( new Packet( data, length ) );
|
|
|
|
// ReSharper disable once InvertIf
|
|
if (_outgoingPacketPostFilter.MatchFilterAll( data, out PacketFilterInfo[] pfisPost ) > 0)
|
|
{
|
|
foreach (PacketFilterInfo pfi in pfisPost)
|
|
{
|
|
pfi.Action?.Invoke( data, pfi );
|
|
}
|
|
|
|
SentPacketFilteredEvent?.Invoke( data, data.Length );
|
|
|
|
PacketWaitEntries.CheckWait( data, PacketDirection.Outgoing, true );
|
|
|
|
return false;
|
|
}
|
|
|
|
return !filter;
|
|
}
|
|
|
|
public static IntPtr WindowHandle { get; private set; }
|
|
|
|
public static ThreadQueue<Packet> IncomingQueue { get; set; }
|
|
|
|
public static ThreadQueue<Packet> OutgoingQueue { get; set; }
|
|
|
|
private static bool OnPacketReceive( ref byte[] data, ref int length )
|
|
{
|
|
if (_incomingPacketFilter.MatchFilterAll( data, out PacketFilterInfo[] pfis ) > 0)
|
|
{
|
|
foreach (PacketFilterInfo pfi in pfis)
|
|
{
|
|
pfi.Action?.Invoke( data, pfi );
|
|
}
|
|
|
|
ReceivedPacketFilteredEvent?.Invoke( data, length );
|
|
|
|
PacketWaitEntries.CheckWait( data, PacketDirection.Incoming, true );
|
|
|
|
return false;
|
|
}
|
|
|
|
if (IncomingPacketFilters.CheckPacket( ref data, ref length ))
|
|
{
|
|
ReceivedPacketFilteredEvent?.Invoke( data, length );
|
|
|
|
return false;
|
|
}
|
|
|
|
IncomingQueue.Enqueue( new Packet( data, length ) );
|
|
|
|
return true;
|
|
}
|
|
|
|
public static Direction GetSequence( int sequence )
|
|
{
|
|
return (Direction)Thread.VolatileRead( ref _sequenceList[sequence] );
|
|
}
|
|
|
|
public static void SetSequence( int sequence, Direction direction )
|
|
{
|
|
_sequenceList[sequence] = (int)direction;
|
|
}
|
|
|
|
private static void OnConnected()
|
|
{
|
|
Connected = true;
|
|
|
|
ConnectedEvent?.Invoke();
|
|
}
|
|
|
|
private static void OnDisconnected()
|
|
{
|
|
Connected = false;
|
|
|
|
Items.Clear();
|
|
Mobiles.Clear();
|
|
Player = null;
|
|
|
|
DisconnectedEvent?.Invoke();
|
|
}
|
|
|
|
private static void OnFocusGained()
|
|
{
|
|
IsClientFocused = true;
|
|
}
|
|
|
|
private static void OnFocusLost()
|
|
{
|
|
IsClientFocused = false;
|
|
}
|
|
|
|
#endregion
|
|
}
|
|
} |