first commit

This commit is contained in:
Beyhan Oğur
2026-09-03 04:45:36 +03:00
commit d5f52cc3d3
1038 changed files with 169579 additions and 0 deletions
+998
View File
@@ -0,0 +1,998 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
using ClassicAssist.Data;
using ClassicAssist.Data.Skills;
using ClassicAssist.Data.Vendors;
using ClassicAssist.Misc;
using ClassicAssist.Shared.Resources;
using ClassicAssist.UI.ViewModels;
using ClassicAssist.UO;
using ClassicAssist.UO.Data;
using ClassicAssist.UO.Network;
using ClassicAssist.UO.Network.PacketFilter;
using ClassicAssist.UO.Network.Packets;
using ClassicAssist.UO.Objects;
using ClassicAssist.UO.Objects.Gumps;
namespace ClassicAssist.Shared.UO
{
public class Commands
{
private static readonly object _dragDropLock = new object();
public static void DragItem( int serial, int amount )
{
Engine.SendPacketToServer( new DragItem( serial, amount ) );
}
public static void DropItem( int serial, int containerSerial, int x = -1, int y = -1, int z = 0 )
{
Engine.SendPacketToServer( new DropItem( serial, containerSerial, x, y, z ) );
}
public static async Task DragDropAsync( int serial, int amount, int containerSerial, int x = -1, int y = -1,
int z = 0 )
{
if ( amount == -1 )
{
Item i = Engine.Items.GetItem( serial );
amount = i?.Count ?? 1;
}
lock ( _dragDropLock )
{
DragItem( serial, amount );
Thread.Sleep( Options.CurrentOptions.ActionDelayMS );
DropItem( serial, containerSerial, x, y, z );
}
await Task.CompletedTask;
}
public static Task EquipType( int id, Layer layer )
{
if ( id <= -1 )
{
SystemMessage( Strings.Invalid_type___ );
return Task.CompletedTask;
}
int containerSerial = Engine.Player?.Serial ?? 0;
if ( containerSerial == 0 || containerSerial == -1 )
{
return Task.CompletedTask;
}
Item backpack = Engine.Player?.Backpack;
Item item = backpack?.Container?.SelectEntity( i => i.ID == id );
if ( item == null )
{
return Task.CompletedTask;
}
if ( layer == Layer.Invalid )
{
StaticTile tileData = TileData.GetStaticTile( item.ID );
layer = (Layer) tileData.Quality;
}
if ( layer == Layer.Invalid )
{
throw new ArgumentException( "EquipItem: Layer is invalid" );
}
return ActionPacketQueue.EnqueueActionPackets(
new BasePacket[]
{
new DragItem( item.Serial, 1 ), new EquipRequest( item.Serial, layer, containerSerial )
}, QueuePriority.Medium );
}
public static Task EquipItem( Item item, Layer layer )
{
int containerSerial = Engine.Player?.Serial ?? 0;
if ( containerSerial == 0 || containerSerial == -1 )
{
return Task.CompletedTask;
}
if ( layer == Layer.Invalid )
{
StaticTile tileData = TileData.GetStaticTile( item.ID );
layer = (Layer) tileData.Quality;
}
if ( layer == Layer.Invalid )
{
throw new ArgumentException( "EquipItem: Layer is invalid" );
}
return ActionPacketQueue.EnqueueActionPackets(
new BasePacket[]
{
new DragItem( item.Serial, 1 ), new EquipRequest( item.Serial, layer, containerSerial )
}, QueuePriority.Medium );
}
public static void SystemMessage( string text, int hue = 0x03b2 )
{
//TODO
byte[] textBytes = Encoding.BigEndianUnicode.GetBytes( text + '\0' );
int length = 48 + textBytes.Length;
PacketWriter pw = new PacketWriter( length );
pw.Write( (byte) 0xAE );
pw.Write( (short) length );
pw.Write( 0xFFFFFFFF );
pw.Write( (ushort) 0xFFFF );
pw.Write( (byte) 0 );
pw.Write( (short) hue );
pw.Write( (short) 0x03 );
pw.WriteAsciiFixed( Strings.UO_LOCALE, 4 );
pw.WriteAsciiFixed( "System\0", 30 );
pw.Write( textBytes, 0, textBytes.Length );
Engine.SendPacketToClient( pw );
}
public static void MobileQuery( int serial, MobileQueryType queryType = MobileQueryType.StatsRequest )
{
Engine.SendPacketToServer( new MobileQuery( serial, queryType ) );
}
public static async Task<int> GetTargeSerialAsync( string message = "", int timeout = 30000 )
{
if ( string.IsNullOrEmpty( message ) )
{
message = Strings.Target_object___;
}
SystemMessage( message );
Random random = new Random();
return await Task.Run( () =>
{
bool wasTargetting = Engine.TargetExists;
uint value = (uint) random.Next( 1, int.MaxValue );
//TODO
PacketWriter pw = new PacketWriter( 19 );
pw.Write( (byte) 0x6C );
pw.Write( (byte) 0 );
pw.Write( value );
pw.Write( (byte) 0 );
pw.Fill();
AutoResetEvent are = new AutoResetEvent( false );
int serial = -1;
PacketFilterInfo pfi = new PacketFilterInfo( 0x6C,
new[] { PacketFilterConditions.UIntAtPositionCondition( value, 2 ) }, ( packet, info ) =>
{
serial = ( packet[7] << 24 ) | ( packet[8] << 16 ) | ( packet[9] << 8 ) | packet[10];
are.Set();
} );
try
{
Engine.AddSendPreFilter( pfi );
Engine.SendPacketToClient( pw );
bool result = are.WaitOne( timeout );
if ( result )
{
return serial;
}
Engine.SendPacketToClient( new CancelTargetCursor( value ) );
SystemMessage( Strings.Timeout___ );
return serial;
}
finally
{
Engine.RemoveSendPreFilter( pfi );
if ( wasTargetting )
{
ResendTargetToClient();
}
}
} );
}
public static async Task<(TargetType, TargetFlags, int, int, int, int, int)> GetTargeInfoAsync(
string message = "", int timeout = 30000 )
{
if ( string.IsNullOrEmpty( message ) )
{
message = Strings.Target_object___;
}
SystemMessage( message );
Random random = new Random();
return await Task.Run( () =>
{
bool wasTargetting = Engine.TargetExists;
uint value = (uint) random.Next( 1, int.MaxValue );
//TODO
PacketWriter pw = new PacketWriter( 19 );
pw.Write( (byte) 0x6C );
pw.Write( (byte) 1 );
pw.Write( value );
pw.Write( (byte) 0 );
pw.Fill();
AutoResetEvent are = new AutoResetEvent( false );
TargetType targetType = TargetType.Object;
TargetFlags targetFlags = TargetFlags.None;
int serial = 0;
int x = 0;
int y = 0;
int z = 0;
int itemID = 0;
PacketFilterInfo pfi = new PacketFilterInfo( 0x6C,
new[] { PacketFilterConditions.UIntAtPositionCondition( value, 2 ) }, ( packet, info ) =>
{
targetType = (TargetType) packet[1];
targetFlags = (TargetFlags) packet[6];
serial = ( packet[7] << 24 ) | ( packet[8] << 16 ) | ( packet[9] << 8 ) | packet[10];
x = ( packet[11] << 8 ) | packet[12];
y = ( packet[13] << 8 ) | packet[14];
z = ( packet[15] << 8 ) | packet[16];
itemID = ( packet[17] << 8 ) | packet[18];
are.Set();
} );
try
{
Engine.AddSendPreFilter( pfi );
Engine.SendPacketToClient( pw );
bool result = are.WaitOne( timeout );
if ( result )
{
return ( targetType, targetFlags, serial, x, y, z, itemID );
}
Engine.SendPacketToClient( new CancelTargetCursor( value ) );
SystemMessage( Strings.Timeout___ );
return ( targetType, targetFlags, serial, x, y, z, itemID );
}
finally
{
Engine.RemoveSendPreFilter( pfi );
if ( wasTargetting )
{
ResendTargetToClient();
}
}
} );
}
public static bool GumpButtonClick( uint gumpID, int buttonID, int[] switches = null )
{
if ( !Engine.GumpList.TryGetValue( gumpID, out int serial ) )
{
return false;
}
Engine.SendPacketToServer( new GumpButtonClick( (int) gumpID, serial, buttonID, switches ) );
Engine.GumpList.TryRemove( gumpID, out _ );
CloseClientGump( gumpID );
return true;
}
public static void CloseClientGump( Type gumpType )
{
Engine.Gumps.GetGumps( out Gump[] gumps );
IEnumerable<Gump> closeGumps = gumps.Where( t => t.GetType() == gumpType );
foreach ( Gump closeGump in closeGumps )
{
CloseClientGump( closeGump.ID );
}
}
public static void CloseClientGump( uint gumpID )
{
Engine.Gumps.Remove( gumpID );
Engine.SendPacketToClient( new CloseClientGump( gumpID ) );
}
public static bool WaitForMenu( int gumpId, int timeout = 30000 )
{
PacketFilterInfo pfi = new PacketFilterInfo( 0x7C );
if ( gumpId != 0 )
{
pfi = new PacketFilterInfo( 0x7C,
new[] { PacketFilterConditions.ShortAtPositionCondition( gumpId, 7 ) } );
}
PacketWaitEntry packetWaitEntry = Engine.PacketWaitEntries.Add( pfi, PacketDirection.Incoming, true );
try
{
bool result = packetWaitEntry.Lock.WaitOne( timeout );
return result;
}
finally
{
Engine.PacketWaitEntries.Remove( packetWaitEntry );
}
}
public static bool WaitForGump( uint gumpId, int timeout = 30000 )
{
PacketFilterInfo pfi = new PacketFilterInfo( 0xDD );
if ( gumpId != 0 )
{
pfi = new PacketFilterInfo( 0xDD,
new[] { PacketFilterConditions.UIntAtPositionCondition( gumpId, 7 ) } );
}
PacketWaitEntry packetWaitEntry = Engine.PacketWaitEntries.Add( pfi, PacketDirection.Incoming, true );
try
{
bool result = packetWaitEntry.Lock.WaitOne( timeout );
return result;
}
finally
{
Engine.PacketWaitEntries.Remove( packetWaitEntry );
}
}
public static void ChangeSkillLock( SkillEntry skill, LockStatus lockStatus, bool requery = true )
{
Engine.SendPacketToServer( new ChangeSkillLock( skill, lockStatus ) );
if ( requery )
{
Engine.SendPacketToServer(
new MobileQuery( Engine.Player?.Serial ?? 0, MobileQueryType.SkillsRequest ) );
}
}
public static void ChangeStatLock( StatType stat, LockStatus lockStatus )
{
Engine.SendPacketToServer( new ChangeStatLock( stat, lockStatus ) );
}
public static (int, bool) WaitForTargetOrFizzle( int timeout )
{
PacketWaitEntry targetWe = CreateWaitEntry( new PacketFilterInfo( 0x6C ) );
PacketWaitEntry fizzWe = CreateWaitEntry( new PacketFilterInfo( 0xC0,
new[]
{
PacketFilterConditions.IntAtPositionCondition( Engine.Player.Serial, 2 ),
PacketFilterConditions.ShortAtPositionCondition( 0x3735, 10 )
} ) );
PacketWaitEntry fizzMessageWe = CreateWaitEntry( new PacketFilterInfo( 0xC1,
new[] { PacketFilterConditions.IntAtPositionCondition( 502632, 14 ) /* The spell fizzles. */ } ) );
PacketWaitEntry recoveredMessageWe = CreateWaitEntry( new PacketFilterInfo( 0xC1,
new[]
{
PacketFilterConditions.IntAtPositionCondition( 502644,
14 ) /* You have not yet recovered from casting a spell. */
} ) );
PacketWaitEntry alreadyCastingWe = CreateWaitEntry( new PacketFilterInfo( 0xC1,
new[]
{
PacketFilterConditions.IntAtPositionCondition( 502642,
14 ) /* You are already casting a spell. */
} ) );
PacketWaitEntry alreadyCasting2We = CreateWaitEntry( new PacketFilterInfo( 0xC1,
new[]
{
PacketFilterConditions.IntAtPositionCondition( 502645,
14 ) /* You are already casting a spell. */
} ) );
PacketWaitEntry concentrationWe = CreateWaitEntry( new PacketFilterInfo( 0xC1,
new[]
{
PacketFilterConditions.IntAtPositionCondition( 500641,
14 ) /* Your concentration is disturbed, thus ruining thy spell. */
} ) );
PacketWaitEntry noManaWe = CreateWaitEntry( new PacketFilterInfo( 0xC1,
new[]
{
PacketFilterConditions.IntAtPositionCondition( 502625, 14 ) /* Insufficient mana etc... */
} ) );
PacketWaitEntry fizzChivWe = CreateWaitEntry( new PacketFilterInfo( 0x54,
new[]
{
PacketFilterConditions.ShortAtPositionCondition( 0x1D6, 2 ),
PacketFilterConditions.ShortAtPositionCondition( Engine.Player.X, 6 ),
PacketFilterConditions.ShortAtPositionCondition( Engine.Player.Y, 8 ),
PacketFilterConditions.ShortAtPositionCondition( Engine.Player.Z, 10 )
} ) );
Engine.WaitingForTarget = true;
List<Task> tasks = new List<Task>();
try
{
Task<bool> targetTask = Task.Run( () =>
{
do
{
bool result = targetWe.Lock.WaitOne( timeout );
if ( !result )
{
return false;
}
if ( targetWe.Packet[6] == 0x03 )
{
continue;
}
return true;
}
while ( true );
} );
Task fizzTask = Task.Factory.StartNew( () => fizzWe.Lock.WaitOne( timeout ),
TaskCreationOptions.LongRunning );
Task fizzMessageTask = Task.Factory.StartNew( () => fizzMessageWe.Lock.WaitOne( timeout + 100 ),
TaskCreationOptions.LongRunning );
Task recoveredMessageTask =
Task.Factory.StartNew( () => recoveredMessageWe.Lock.WaitOne( timeout + 100 ),
TaskCreationOptions.LongRunning );
Task alreadyCastingTask = Task.Factory.StartNew( () => alreadyCastingWe.Lock.WaitOne( timeout + 100 ),
TaskCreationOptions.LongRunning );
Task alreadyCasting2Task = Task.Factory.StartNew( () => alreadyCasting2We.Lock.WaitOne( timeout + 100 ),
TaskCreationOptions.LongRunning );
Task concentrationTask = Task.Factory.StartNew( () => concentrationWe.Lock.WaitOne( timeout + 100 ),
TaskCreationOptions.LongRunning );
Task noManaTask = Task.Factory.StartNew( () => noManaWe.Lock.WaitOne( timeout + 100 ),
TaskCreationOptions.LongRunning );
Task fizzChivTask = Task.Factory.StartNew( () => fizzChivWe.Lock.WaitOne( timeout + 100 ),
TaskCreationOptions.LongRunning );
int index;
tasks.AddRange( new[]
{
targetTask, fizzTask, fizzMessageTask, recoveredMessageTask, alreadyCastingTask,
alreadyCasting2Task, concentrationTask, noManaTask, fizzChivTask
} );
try
{
index = Task.WaitAny( tasks.ToArray() );
}
catch ( OperationCanceledException )
{
return ( -1, false );
}
catch ( ThreadInterruptedException )
{
return ( -1, false );
}
return ( index, index == 0 && targetTask.Result );
}
finally
{
Engine.PacketWaitEntries.Remove( targetWe );
Engine.PacketWaitEntries.Remove( fizzWe );
Engine.PacketWaitEntries.Remove( fizzMessageWe );
Engine.PacketWaitEntries.Remove( recoveredMessageWe );
Engine.PacketWaitEntries.Remove( alreadyCastingWe );
Engine.PacketWaitEntries.Remove( alreadyCasting2We );
Engine.PacketWaitEntries.Remove( concentrationWe );
Engine.PacketWaitEntries.Remove( noManaWe );
Engine.PacketWaitEntries.Remove( fizzChivWe );
Engine.WaitingForTarget = false;
}
}
private static PacketWaitEntry CreateWaitEntry( PacketFilterInfo packetFilterInfo,
PacketDirection direction = PacketDirection.Incoming )
{
return Engine.PacketWaitEntries.Add( packetFilterInfo, direction );
}
public static bool WaitForTarget( int timeout )
{
PacketFilterInfo pfi = new PacketFilterInfo( 0x6C );
Engine.WaitingForTarget = true;
PacketWaitEntry we = Engine.PacketWaitEntries.Add( pfi, PacketDirection.Incoming );
try
{
do
{
bool result = we.Lock.WaitOne( timeout );
if ( !result )
{
return false;
}
if ( we.Packet[6] == 0x03 )
{
continue;
}
return true;
}
while ( true );
}
finally
{
Engine.PacketWaitEntries.Remove( we );
Engine.WaitingForTarget = false;
}
}
public static async Task<bool> WaitForIncomingPacketFilterAsync( PacketFilterInfo pfi, int timeout,
bool invokeHandler = true, bool fixedSize = false )
{
AutoResetEvent are = new AutoResetEvent( false );
byte[] packet = null;
Engine.AddReceiveFilter( new PacketFilterInfo( pfi.PacketID, pfi.GetConditions(), ( data, info ) =>
{
packet = data;
are.Set();
} ) );
Task filterTask = are.ToTask();
if ( await Task.WhenAny( filterTask, Task.Delay( timeout ) ) == filterTask )
{
if ( invokeHandler )
{
PacketHandler handler = IncomingPacketHandlers.GetHandler( packet[0] );
handler?.OnReceive( new PacketReader( packet, packet.Length, fixedSize ) );
}
}
Engine.RemoveReceiveFilter( pfi );
return packet != null;
}
public static void Resync()
{
Engine.SendPacketToServer( new Resync() );
}
public static void ToggleGargoyleFlying()
{
Engine.SendPacketToServer( new ToggleGargoyleFlying() );
}
public static bool WaitForIncomingPacket( PacketFilterInfo pfi, int timeout, Action beforeWait = null )
{
PacketWaitEntry we = Engine.PacketWaitEntries.Add( pfi, PacketDirection.Incoming, true );
bool result;
beforeWait?.Invoke();
try
{
result = we.Lock.WaitOne( timeout );
}
finally
{
Engine.PacketWaitEntries.Remove( we );
}
return result;
}
public static void SetWeaponAbility( int abilityIndex )
{
Engine.SendPacketToServer( new SetWeaponAbility( abilityIndex ) );
}
public static void ClearWeaponAbility()
{
Engine.SendPacketToServer( new ClearWeaponAbility() );
}
public static void RenameRequest( int serial, string name )
{
Engine.SendPacketToServer( new RenameRequest( serial, name ) );
}
public static bool WaitForContainerContents( int serial, int timeout )
{
PacketFilterInfo pfi = new PacketFilterInfo( 0x3C,
new[] { PacketFilterConditions.IntAtPositionCondition( serial, 19 ) } );
PacketWaitEntry we = Engine.PacketWaitEntries.Add( pfi, PacketDirection.Incoming, true );
Engine.SendPacketToServer( new UseObject( serial ) );
try
{
bool result = we.Lock.WaitOne( timeout );
return result;
}
finally
{
Engine.PacketWaitEntries.Remove( we );
}
}
private static byte[] GetKeywordBytes( IReadOnlyList<int> keywords )
{
int length = keywords.Count;
List<byte> codeBytes = new List<byte> { (byte) ( length >> 4 ) };
int num3 = length & 15;
bool flag = false;
int index = 0;
while ( index < length )
{
int keywordID = keywords[index];
if ( flag )
{
codeBytes.Add( (byte) ( keywordID >> 4 ) );
num3 = keywordID & 15;
}
else
{
codeBytes.Add( (byte) ( ( num3 << 4 ) | ( ( keywordID >> 8 ) & 15 ) ) );
codeBytes.Add( (byte) keywordID );
}
index++;
flag = !flag;
}
if ( !flag )
{
codeBytes.Add( (byte) ( num3 << 4 ) );
}
return codeBytes.ToArray();
}
public static void Speak( string text, int hue = 34, JournalSpeech speechType = JournalSpeech.Say )
{
int[] keywords = new int[0];
if ( speechType == JournalSpeech.Say )
{
keywords = Speech.GetKeywords( text.ToLower() );
}
PacketWriter writer = new PacketWriter();
writer.Write( (byte) 0xAD );
writer.Write( (short) 0 ); // len;
writer.Write( keywords.Length > 0 ? (byte) 0xC0 : (byte) speechType );
writer.Write( (short) hue );
writer.Write( (short) 0x3 );
writer.WriteAsciiFixed( Strings.UO_LOCALE, 4 );
if ( keywords.Length > 0 )
{
byte[] bytes = GetKeywordBytes( keywords );
writer.Write( bytes, 0, bytes.Length );
writer.WriteAsciiNull( text );
}
else
{
writer.WriteBigUniNull( text );
}
byte[] packet = writer.ToArray();
packet[1] = (byte) ( packet.Length << 8 );
packet[2] = (byte) packet.Length;
Engine.SendPacketToServer( packet, packet.Length );
}
public static void PartyMessage( string message )
{
int len = 6 + ( message.Length + 1 ) * 2;
PacketWriter writer = new PacketWriter( len );
writer.Write( (byte) 0xBF );
writer.Write( (short) len );
writer.Write( (short) 6 );
writer.Write( (byte) 4 );
writer.WriteBigUniNull( message );
Engine.SendPacketToServer( writer );
}
public static void OverheadMessage( string message, int hue, int serial )
{
byte[] textBytes = Encoding.BigEndianUnicode.GetBytes( message + '\0' );
int length = 48 + textBytes.Length;
Entity entity = (Entity) Engine.Mobiles.GetMobile( serial ) ?? Engine.Items.GetItem( serial );
if ( entity == null )
{
return;
}
PacketWriter pw = new PacketWriter( length );
pw.Write( (byte) 0xAE );
pw.Write( (short) length );
pw.Write( entity.Serial );
pw.Write( (ushort) entity.ID );
pw.Write( (byte) JournalSpeech.Say );
pw.Write( (short) hue );
pw.Write( (short) 0x03 );
pw.WriteAsciiFixed( Strings.UO_LOCALE, 4 );
pw.WriteAsciiFixed( entity.Name ?? "Unknown", 30 );
pw.Write( textBytes, 0, textBytes.Length );
Engine.SendPacketToClient( pw );
}
public static void ResendTargetToClient()
{
PacketWriter pw = new PacketWriter( 19 );
pw.Write( (byte) 0x6C );
pw.Write( (byte) TargetType.Object );
pw.Write( Engine.TargetSerial );
pw.Write( (byte) Engine.TargetFlags );
pw.Write( 0 );
pw.Write( (short) 0 );
pw.Write( (short) 0 );
pw.Write( (short) 0 );
pw.Write( (short) 0 );
Engine.TargetExists = true;
Engine.SendPacketToClient( pw );
}
public static void VendorBuy( int serial, ShopListEntry[] shopListEntries )
{
int len = 8 + shopListEntries.Length * 7;
PacketWriter pw = new PacketWriter( len );
pw.Write( (byte) 0x3B );
pw.Write( (short) len ); // length
pw.Write( serial );
pw.Write( (byte) 2 ); //item list
foreach ( ShopListEntry entry in shopListEntries )
{
if ( entry == null )
{
continue;
}
pw.Write( (byte) Layer.ShopBuy );
pw.Write( entry.Item.Serial );
pw.Write( (short) entry.Amount );
}
Engine.SendPacketToServer( pw );
}
public static void SetForceWalk( bool force )
{
PacketWriter pw = new PacketWriter( 6 );
pw.Write( (byte) 0xBF );
pw.Write( (short) 0x06 );
pw.Write( (short) 0x26 );
pw.Write( (byte) ( force ? 2 : 0 ) );
Engine.SendPacketToClient( pw );
}
public static void UO3DEquipItems( int[] serials )
{
if ( serials == null || serials.Length == 0 )
{
return;
}
int len = 4 + serials.Length * 4;
PacketWriter pw = new PacketWriter( len );
pw.Write( (byte) 0xEC );
pw.Write( (short) len ); //size
pw.Write( (byte) serials.Length );
foreach ( int serial in serials )
{
pw.Write( serial );
}
Engine.SendPacketToServer( pw );
}
public static void UO3DUnequipItems( int[] layers )
{
if ( layers == null || layers.Length == 0 )
{
return;
}
PacketWriter pw = new PacketWriter( 4 + layers.Length * 2 );
pw.Write( (byte) 0xED );
pw.Write( (short) ( 4 + layers.Length * 2 ) );
pw.Write( (byte) layers.Length );
foreach ( int layer in layers )
{
pw.Write( (short) layer );
}
Engine.SendPacketToServer( pw );
}
public static void ChatMsg( string text )
{
PacketWriter pw = new PacketWriter( 11 + text.Length * 2 );
pw.Write( (byte) 0xB3 );
pw.Write( (short) ( 11 + text.Length * 2 ) );
pw.WriteAsciiFixed( Strings.UO_LOCALE, 4 );
pw.Write( (short) 0x61 );
pw.WriteBigUniNull( text );
Engine.SendPacketToServer( pw );
}
public static void JoinChatChannel( string channel )
{
/*
* Text 0x62 (Join Conference):
BYTE[2] Holder (0x0022)
BYTE[?] Unicode conference name
BYTE[2] Holder (0x0022)
BYTE[2] Holder (0x0020)
BYTE[?] Unicode password if pass req'd
BYTE[2] Null Teriminator (0x0000)
*/
PacketWriter pw = new PacketWriter();
pw.Write( (byte) 0xB3 );
pw.Write( (short) ( 17 + channel.Length * 2 ) );
pw.WriteAsciiFixed( Strings.UO_LOCALE, 4 );
pw.Write( (short) 0x62 );
pw.WriteBigUniNull( channel );
pw.Write( (short) 0x22 );
pw.Write( (short) 0x20 );
pw.Write( (short) 0 );
Engine.SendPacketToServer( pw );
}
public static void RemoveObject( int serial )
{
Engine.SendPacketToClient( new RemoveObject( serial ) );
}
public static async Task InspectObjectAsync()
{
( TargetType targetType, TargetFlags _, int serial, int x, int y, int z, int itemID ) =
await GetTargeInfoAsync( Strings.Target_object___ );
if ( targetType == TargetType.Object && serial != 0 )
{
Entity entity = UOMath.IsMobile( serial )
? (Entity) Engine.Mobiles.GetMobile( serial )
: Engine.Items.GetItem( serial );
if ( entity == null )
{
return;
}
Engine.UIInvoker.Invoke( "ObjectInspectorWindow", null, typeof( ObjectInspectorViewModel ),
new[] { entity } );
}
else
{
if ( itemID == 0 )
{
if ( x == 65535 && y == 65535 )
{
return;
}
LandTile landTile = MapInfo.GetLandTile( (int) Engine.Player.Map, x, y );
Engine.UIInvoker.Invoke( "ObjectInspectorWindow", null, typeof( ObjectInspectorViewModel ),
new[] { (object) landTile } );
}
else
{
StaticTile[] statics = Statics.GetStatics( (int) Engine.Player.Map, x, y );
if ( statics == null )
{
return;
}
StaticTile selectedStatic = statics.FirstOrDefault( i => i.ID == itemID );
if ( selectedStatic.ID == 0 )
{
selectedStatic = TileData.GetStaticTile( itemID );
selectedStatic.X = x;
selectedStatic.Y = y;
selectedStatic.Z = z;
}
Engine.UIInvoker.Invoke( "ObjectInspectorWindow", null, typeof( ObjectInspectorViewModel ),
new[] { (object) selectedStatic } );
}
}
}
}
}
+49
View File
@@ -0,0 +1,49 @@
using System;
using System.Runtime.InteropServices;
namespace ClassicAssist.UO
{
public static class Compression
{
[DllImport( "zlib32.dll", EntryPoint = "uncompress" )]
private static extern int Uncompress32( byte[] dest, ref int destLen, byte[] source, int sourceLen );
[DllImport( "zlib64.dll", EntryPoint = "uncompress" )]
private static extern int Uncompress64( byte[] dest, ref int destLen, byte[] source, int sourceLen );
[DllImport( "zlib32.dll", EntryPoint = "compress" )]
private static extern int Compress32( byte[] dest, ref int destLen, byte[] source, int sourceLen );
[DllImport( "zlib64.dll", EntryPoint = "compress" )]
private static extern int Compress64( byte[] dest, ref int destLen, byte[] source, int sourceLen );
public static bool Uncompress( ref byte[] destBuffer, ref int destLength, byte[] sourceBuffer, int sourceLen )
{
int success = Environment.Is64BitProcess
? Uncompress64( destBuffer, ref destLength, sourceBuffer, sourceLen )
: Uncompress32( destBuffer, ref destLength, sourceBuffer, sourceLen );
return success == 0;
}
public static byte[] Compress( byte[] bytes )
{
byte[] compressBytes = new byte[(int) ( bytes.Length * 1.001 ) + 12];
int length = compressBytes.Length;
if ( Environment.Is64BitProcess )
{
Compress64( compressBytes, ref length, bytes, bytes.Length );
}
else
{
Compress32( compressBytes, ref length, bytes, bytes.Length );
}
Array.Resize( ref compressBytes, length );
return compressBytes;
}
}
}
+382
View File
@@ -0,0 +1,382 @@
using System;
using System.Collections.Generic;
using System.Drawing;
using System.Drawing.Imaging;
using System.IO;
using System.Runtime.InteropServices;
using ClassicAssist.Misc;
namespace ClassicAssist.UO.Data
{
public static class Art
{
private static string _dataPath;
private static bool _isUOPFormat;
private static Lazy<Dictionary<int, Entry3D>> _lazyIndex;
public static bool Initialize( string dataPath )
{
_dataPath = dataPath;
if ( File.Exists( Path.Combine( dataPath, "artLegacyMUL.uop" ) ) )
{
_isUOPFormat = true;
}
_lazyIndex = new Lazy<Dictionary<int, Entry3D>>( () => _isUOPFormat ? LoadUOPIndex() : LoadMULIndex() );
return true;
}
private static Dictionary<int, Entry3D> LoadMULIndex()
{
int entrySize = Marshal.SizeOf( typeof( Entry3D ) );
byte[] buffer = new byte[entrySize];
GCHandle pinnedBuffer = GCHandle.Alloc( buffer, GCHandleType.Pinned );
int index = 0;
Dictionary<int, Entry3D> indexes = new Dictionary<int, Entry3D>();
using ( FileStream reader = File.Open( Path.Combine( _dataPath, "artidx.mul" ), FileMode.Open,
FileAccess.Read, FileShare.ReadWrite ) )
{
int bytesRead;
do
{
bytesRead = reader.Read( buffer, 0, entrySize );
indexes[index++] =
(Entry3D) Marshal.PtrToStructure( pinnedBuffer.AddrOfPinnedObject(), typeof( Entry3D ) );
}
while ( bytesRead > 0 );
}
return indexes;
}
private static Dictionary<int, Entry3D> LoadUOPIndex()
{
Dictionary<ulong, int> hashes = new Dictionary<ulong, int>();
Dictionary<int, Entry3D> indexes = new Dictionary<int, Entry3D>();
using ( FileStream reader = File.Open( Path.Combine( _dataPath, "artLegacyMUL.uop" ), FileMode.Open,
FileAccess.Read, FileShare.ReadWrite ) )
{
using ( BinaryReader bin = new BinaryReader( reader ) )
{
reader.Seek( 12, SeekOrigin.Current );
int firstAddress = bin.ReadInt32();
for ( int i = 0; i < 0x10000 /*formatHeader.NumberOfFiles*/; i++ )
{
string entryName = $"build/artlegacymul/{i:D8}.tga";
ulong hash = HashFileName( entryName );
if ( !hashes.ContainsKey( hash ) )
{
hashes.Add( hash, i );
}
}
long nextAddress = firstAddress;
do
{
reader.Seek( nextAddress, SeekOrigin.Begin );
UOPBlockHeader blockHeader = reader.ReadStruct<UOPBlockHeader>();
nextAddress = blockHeader.NextAddress;
for ( int i = 0; i < blockHeader.NumberOfFiles; i++ )
{
UOPFileHeader fileHeader = reader.ReadStruct<UOPFileHeader>();
if ( fileHeader.DataHeaderAddress == 0 )
{
continue;
}
if ( hashes.ContainsKey( fileHeader.Hash ) )
{
int index = hashes[fileHeader.Hash];
indexes[index] = new Entry3D( (int) fileHeader.DataHeaderAddress + fileHeader.Length,
fileHeader.IsCompressed == 1
? fileHeader.CompressedSize
: fileHeader.DecompressedSize, 0 );
}
}
}
while ( nextAddress > 0 );
}
}
return indexes;
}
public static Bitmap GetStatic( int itemID, int hue )
{
Bitmap bmp = GetStatic( itemID );
if ( hue == 0 || bmp == null )
{
return bmp;
}
StaticTile tileData = TileData.GetStaticTile( itemID );
Hues.ApplyHue( hue, bmp, tileData.Flags.HasFlag( TileFlags.PartialHue ) );
return bmp;
}
public static unsafe Bitmap GetStatic( int itemId )
{
itemId += 0x4000;
string fileName = Path.Combine( _dataPath, "art.mul" );
if ( _isUOPFormat )
{
fileName = Path.Combine( _dataPath, "artLegacyMUL.uop" );
}
FileStream artFile = File.Open( fileName, FileMode.Open, FileAccess.Read, FileShare.ReadWrite );
Entry3D entry;
try
{
entry = _lazyIndex.Value[itemId];
}
catch ( KeyNotFoundException )
{
entry = _lazyIndex.Value[0x4000];
}
artFile.Seek( entry.Lookup, SeekOrigin.Begin );
using ( BinaryReader reader = new BinaryReader( artFile ) )
{
reader.ReadInt32();
int width = reader.ReadInt16();
int height = reader.ReadInt16();
if ( width <= 0 || height <= 0 )
{
return null;
}
int[] lookups = new int[height];
int start = (int) reader.BaseStream.Position + height * 2;
for ( int i = 0; i < height; i++ )
{
lookups[i] = start + reader.ReadUInt16() * 2;
}
Bitmap bmp = new Bitmap( width, height, PixelFormat.Format16bppArgb1555 );
BitmapData bd = bmp.LockBits( new Rectangle( 0, 0, width, height ), ImageLockMode.WriteOnly,
PixelFormat.Format16bppArgb1555 );
ushort* line = (ushort*) bd.Scan0;
int delta = bd.Stride >> 1;
int largestX = 0;
int smallestX = width;
int largestY = 0;
int smallestY = height;
for ( int y = 0; y < height; y++, line += delta )
{
reader.BaseStream.Seek( lookups[y], SeekOrigin.Begin );
ushort* cur = line;
int xOffset, xRun;
int x = 0;
if ( y > largestY )
{
largestY = y + 1;
}
if ( y < smallestY )
{
smallestY = y;
}
while ( ( xOffset = reader.ReadUInt16() ) + ( xRun = reader.ReadUInt16() ) != 0 )
{
cur += xOffset;
ushort* end = cur + xRun;
x += xOffset;
if ( x < smallestX )
{
smallestX = x;
}
while ( cur < end )
{
*cur++ = (ushort) ( reader.ReadUInt16() ^ 0x8000 );
x++;
}
if ( x > largestX )
{
largestX = x;
}
}
}
bmp.UnlockBits( bd );
return bmp;
}
}
private static ulong HashFileName( string s )
{
uint ecx, edx, ebx, esi, edi;
uint eax = ecx = edx = ebx = esi = edi = 0;
ebx = edi = esi = (uint) s.Length + 0xDEADBEEF;
int i = 0;
for ( i = 0; i + 12 < s.Length; i += 12 )
{
edi = (uint) ( ( s[i + 7] << 24 ) | ( s[i + 6] << 16 ) | ( s[i + 5] << 8 ) | s[i + 4] ) + edi;
esi = (uint) ( ( s[i + 11] << 24 ) | ( s[i + 10] << 16 ) | ( s[i + 9] << 8 ) | s[i + 8] ) + esi;
edx = (uint) ( ( s[i + 3] << 24 ) | ( s[i + 2] << 16 ) | ( s[i + 1] << 8 ) | s[i] ) - esi;
edx = ( edx + ebx ) ^ ( esi >> 28 ) ^ ( esi << 4 );
esi += edi;
edi = ( edi - edx ) ^ ( edx >> 26 ) ^ ( edx << 6 );
edx += esi;
esi = ( esi - edi ) ^ ( edi >> 24 ) ^ ( edi << 8 );
edi += edx;
ebx = ( edx - esi ) ^ ( esi >> 16 ) ^ ( esi << 16 );
esi += edi;
edi = ( edi - ebx ) ^ ( ebx >> 13 ) ^ ( ebx << 19 );
ebx += esi;
esi = ( esi - edi ) ^ ( edi >> 28 ) ^ ( edi << 4 );
edi += ebx;
}
if ( s.Length - i > 0 )
{
switch ( s.Length - i )
{
case 12:
esi += (uint) s[i + 11] << 24;
goto case 11;
case 11:
esi += (uint) s[i + 10] << 16;
goto case 10;
case 10:
esi += (uint) s[i + 9] << 8;
goto case 9;
case 9:
esi += s[i + 8];
goto case 8;
case 8:
edi += (uint) s[i + 7] << 24;
goto case 7;
case 7:
edi += (uint) s[i + 6] << 16;
goto case 6;
case 6:
edi += (uint) s[i + 5] << 8;
goto case 5;
case 5:
edi += s[i + 4];
goto case 4;
case 4:
ebx += (uint) s[i + 3] << 24;
goto case 3;
case 3:
ebx += (uint) s[i + 2] << 16;
goto case 2;
case 2:
ebx += (uint) s[i + 1] << 8;
goto case 1;
case 1:
ebx += s[i];
break;
}
esi = ( esi ^ edi ) - ( ( edi >> 18 ) ^ ( edi << 14 ) );
ecx = ( esi ^ ebx ) - ( ( esi >> 21 ) ^ ( esi << 11 ) );
edi = ( edi ^ ecx ) - ( ( ecx >> 7 ) ^ ( ecx << 25 ) );
esi = ( esi ^ edi ) - ( ( edi >> 16 ) ^ ( edi << 16 ) );
edx = ( esi ^ ecx ) - ( ( esi >> 28 ) ^ ( esi << 4 ) );
edi = ( edi ^ edx ) - ( ( edx >> 18 ) ^ ( edx << 14 ) );
eax = ( esi ^ edi ) - ( ( edi >> 8 ) ^ ( edi << 24 ) );
return ( (ulong) edi << 32 ) | eax;
}
return ( (ulong) esi << 32 ) | eax;
}
#region Structures
[StructLayout( LayoutKind.Explicit )]
private struct Entry3D
{
[FieldOffset( 0 )]
public readonly int Lookup;
[FieldOffset( 4 )]
public readonly int Length;
[FieldOffset( 8 )]
public readonly int Extra;
public Entry3D( int lookup, int length, int extra )
{
Lookup = lookup;
Length = length;
Extra = extra;
}
}
[StructLayout( LayoutKind.Explicit, Size = 12 )]
private struct UOPBlockHeader
{
[FieldOffset( 0 )]
public readonly int NumberOfFiles;
[FieldOffset( 4 )]
public readonly long NextAddress;
}
[StructLayout( LayoutKind.Explicit, Pack = 0, Size = 34 )]
private struct UOPFileHeader
{
[FieldOffset( 0 )]
public readonly long DataHeaderAddress;
[FieldOffset( 8 )]
public readonly int Length;
[FieldOffset( 12 )]
public readonly int CompressedSize;
[FieldOffset( 16 )]
public readonly int DecompressedSize;
[FieldOffset( 20 )]
public readonly ulong Hash;
[FieldOffset( 28 )]
public readonly int Unknown;
[FieldOffset( 32 )]
public readonly short IsCompressed;
}
#endregion
}
}
+174
View File
@@ -0,0 +1,174 @@
using System;
using System.Collections.Generic;
using System.IO;
using System.Text;
namespace ClassicAssist.UO.Data
{
public static class Cliloc
{
private static readonly Lazy<Dictionary<int, string>> _lazyClilocList =
new Lazy<Dictionary<int, string>>( LoadClilocs );
private static string _dataPath;
private static Dictionary<int, string> LoadClilocs()
{
string filename = Path.Combine( _dataPath, "Cliloc.enu" );
if ( !File.Exists( filename ) )
{
throw new FileNotFoundException( "File not found.", filename );
}
byte[] fileBytes = File.ReadAllBytes( filename );
Dictionary<int, string> clilocList = new Dictionary<int, string>( 100000 );
ushort len;
for ( int x = 6; x + 7 <= fileBytes.Length; x += 7 + len )
{
len = BitConverter.ToUInt16( fileBytes, x + 5 );
if ( x + 7 + len > fileBytes.Length )
{
break;
}
try
{
int key = BitConverter.ToInt32( fileBytes, x );
string value = Encoding.UTF8.GetString( fileBytes, x + 7, len );
clilocList[key] = value;
}
catch
{
break;
}
}
return clilocList;
}
public static string GetLocalString( string tokenizedString )
{
if ( tokenizedString.ToLower().Contains( "http://" ) || tokenizedString.ToLower().Contains( "https://" ) )
{
return tokenizedString;
}
while ( tokenizedString.Contains( "#" ) )
{
for ( int x = 0; x < tokenizedString.Length; x++ )
{
if ( tokenizedString[x] != '#' || x >= tokenizedString.Length - 1 )
{
continue;
}
if ( !char.IsNumber( tokenizedString[x + 1] ) )
{
return tokenizedString;
}
int y;
for ( y = x + 1; y < tokenizedString.Length; y++ )
{
if ( !char.IsNumber( tokenizedString[y] ) )
{
break;
}
}
string token = tokenizedString.Substring( x, y - x );
string tokenNum = tokenizedString.Substring( x + 1, y - x - 1 );
if ( tokenNum.Length <= 0 )
{
continue;
}
int propertyNum;
try
{
propertyNum = Convert.ToInt32( tokenNum );
}
catch
{
return tokenizedString;
}
string property = GetProperty( propertyNum );
tokenizedString = tokenizedString.Replace( token, property );
}
}
return tokenizedString;
}
public static string GetLocalString( int property, string[] arguments )
{
string propertyString = GetProperty( property );
if ( arguments == null )
{
return propertyString;
}
//foreach (string s in arguments)
for ( int x = 0; x < arguments.Length; x++ )
{
arguments[x] = GetLocalString( arguments[x] );
bool found = false;
int start = 0;
int index = 0;
foreach ( char c in propertyString )
{
if ( c == '~' )
{
if ( found )
{
string subString = propertyString.Substring( start, index - start + 1 );
propertyString = propertyString.Replace( subString, arguments[x] );
break;
}
start = index;
found = true;
}
index++;
}
if ( !found )
{
return propertyString;
}
}
return propertyString;
}
public static void Initialize( string dataPath )
{
_dataPath = dataPath;
}
public static string GetProperty( int property )
{
return _lazyClilocList.Value.TryGetValue( property, out string propertyString )
? propertyString
: $"Localized string {property} not found!";
}
public static Dictionary<int, string> GetItems()
{
return new Dictionary<int, string>( _lazyClilocList.Value );
}
}
}
+15
View File
@@ -0,0 +1,15 @@
namespace ClassicAssist.UO.Data
{
public enum Direction : byte
{
North,
Northeast,
East,
Southeast,
South,
Southwest,
West,
Northwest,
Invalid
}
}
+139
View File
@@ -0,0 +1,139 @@
using System;
namespace ClassicAssist.UO.Data
{
[Flags]
public enum MessageAffixType
{
Append,
Prepend,
System
}
[Flags]
public enum HealthbarColour
{
None = 0x00,
Green = 0x01,
Yellow = 0x02,
Red = 0x04
}
public enum TargetFlags : byte
{
None,
Harmful,
Beneficial,
Cancel
}
public enum Virtues
{
None = '0',
Honor,
Sacrafice,
Valor,
Compassion,
Honesty,
Humility,
Justice,
Spirituality
}
public enum MobileRace : byte
{
Unknown,
Human,
Elf,
Gargoyle
}
public enum JournalSpeech : byte
{
Say,
System,
Emote,
Unknown1,
Unknown2,
Unknown3,
Label,
Focus,
Whisper,
Yell,
Spell,
Unknown4,
Unknown5,
Guild,
Alliance,
GM
}
public enum MobileQueryType : byte
{
StatsRequest = 4,
SkillsRequest = 5
}
public enum TargetType : byte
{
Object,
Tile
}
[Flags]
public enum FeatureFlags
{
None = 0x00000000,
T2A = 0x00000001,
UOR = 0x00000002,
UOTD = 0x00000004,
LBR = 0x00000008,
AOS = 0x00000010,
SixthCharacterSlot = 0x00000020,
SE = 0x00000040,
ML = 0x00000080,
EigthAge = 0x00000100,
NinthAge = 0x00000200, /* Crystal/Shadow Custom House Tiles */
TenthAge = 0x00000400,
IncreasedStorage = 0x00000800, /* Increased Housing/Bank Storage */
SeventhCharacterSlot = 0x00001000,
RoleplayFaces = 0x00002000,
TrialAccount = 0x00004000,
LiveAccount = 0x00008000,
SA = 0x00010000,
HS = 0x00020000,
Gothic = 0x00040000,
Rustic = 0x00080000,
Jungle = 0x00100000,
Shadowguard = 0x00200000,
TOL = 0x00400000,
EJ = 0x00800000
}
public enum Notoriety : byte
{
Invalid,
Innocent,
Ally,
Attackable,
Criminal,
Enemy,
Murderer,
Invulnerable,
Unknown
}
[Flags]
public enum MobileStatus : byte
{
None = 0x00,
Frozen = 0x01,
Female = 0x02,
Flying = 0x04,
Invulnerable = 0x08,
IgnoreMobiles = 0x10,
Unknown = 0x20,
WarMode = 0x40,
Hidden = 0x80
}
}
@@ -0,0 +1,106 @@
#region License
// Copyright (C) 2020 Reetus
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program. If not, see <https://www.gnu.org/licenses/>.
#endregion
using System.Linq;
using ClassicAssist.Data;
using ClassicAssist.Shared;
using ClassicAssist.UO.Network;
using ClassicAssist.UO.Network.Packets;
using ClassicAssist.UO.Objects.Gumps;
namespace ClassicAssist.UO.Data.Gumps
{
public class BoatGumpExtension : IExtension
{
public void Initialize()
{
IncomingPacketHandlers.JournalEntryAddedEvent += OnJournalEntryAddedEvent;
}
private static void OnJournalEntryAddedEvent( JournalEntry je )
{
switch ( je.Cliloc )
{
/* You are now piloting this vessel. */
case 1116727:
new BoatMovementGump().SendGump();
break;
/* You are no longer piloting this vessel. */
case 1149592:
Shared.UO.Commands.CloseClientGump( typeof( BoatMovementGump ) );
break;
}
}
}
public class BoatMovementGump : Gump
{
private static BoatSpeed _lastSpeed = BoatSpeed.OneTile;
public BoatMovementGump() : base( 100, 100 )
{
Movable = true;
Closable = true;
Resizable = false;
Disposable = false;
AddPage( 0 );
AddBackground( 0, 0, 150, 175, 9200 );
AddButton( 0, 0, 4507, 4507, (int) Direction.Northwest + 1, GumpButtonType.Reply, 0 );
AddButton( 50, 0, 4500, 4500, (int) Direction.North + 1, GumpButtonType.Reply, 0 );
AddButton( 100, 0, 4501, 4501, (int) Direction.Northeast + 1, GumpButtonType.Reply, 0 );
AddButton( 10, 50, 4506, 4506, (int) Direction.West + 1, GumpButtonType.Reply, 0 );
AddButton( 90, 50, 4502, 4502, (int) Direction.East + 1, GumpButtonType.Reply, 0 );
AddButton( 0, 100, 4505, 4505, (int) Direction.Southwest + 1, GumpButtonType.Reply, 0 );
AddButton( 50, 100, 4504, 4504, (int) Direction.South + 1, GumpButtonType.Reply, 0 );
AddButton( 100, 100, 4503, 4503, (int) Direction.Southeast + 1, GumpButtonType.Reply, 0 );
AddRadio( 10, 150, 208, 209, _lastSpeed == BoatSpeed.Normal, 1 );
AddLabel( 40, 150, 0, "Fast" );
AddButton( 60, 60, 2151, 2151, 10, GumpButtonType.Reply, 0 );
}
public override void OnResponse( int buttonID, int[] switches )
{
if ( buttonID == 0 )
{
return;
}
BoatSpeed speed = BoatSpeed.OneTile;
if ( switches?.Any( e => e == 1 ) ?? false )
{
speed = BoatSpeed.Normal;
}
_lastSpeed = speed;
if ( buttonID < 9 )
{
Engine.SendPacketToServer( new WheelBoatMoving( (Direction) buttonID - 1, speed ) );
}
if ( buttonID == 10 )
{
Engine.SendPacketToServer( new WheelBoatMoving( Direction.North, BoatSpeed.Stop ) );
}
new BoatMovementGump().SendGump();
}
}
}
@@ -0,0 +1,132 @@
#region License
// Copyright (C) 2020 Reetus
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program. If not, see <https://www.gnu.org/licenses/>.
#endregion
using System.Drawing;
using System.Threading;
using ClassicAssist.Shared;
using ClassicAssist.Helpers;
using ClassicAssist.Misc;
using ClassicAssist.UO.Network.PacketFilter;
using ClassicAssist.UO.Objects.Gumps;
namespace ClassicAssist.UO.Gumps
{
public class ConfirmPromptGump : Gump
{
public ConfirmPromptGump( string message, bool closable = false ) : base( 500, 500 )
{
const int width = 300;
const int height = 200;
SetCenterPosition( width, height );
Closable = closable;
Resizable = false;
Disposable = false;
Movable = true;
AddPage( 0 );
AddBackground( 0, 0, width, height, 9200 );
AddHtml( 10, 10, width - 20, height - 50, message, true, true );
AddButton( width - 140, height - 30, 247, 248, 1, GumpButtonType.Reply, 0 );
AddButton( width - 70, height - 30, 241, 242, 2, GumpButtonType.Reply, 0 );
}
public AutoResetEvent AutoResetEvent { get; set; } = new AutoResetEvent( false );
public bool Result { get; set; }
public override void OnResponse( int buttonID, int[] switches )
{
Result = buttonID == 1;
AutoResetEvent.Set();
}
public static bool ConfirmPrompt( string message, bool closable = false )
{
ConfirmPromptGump gump = new ConfirmPromptGump( message, closable );
PacketFilterInfo pfi = new PacketFilterInfo( 0xB1,
new[] { PacketFilterConditions.UIntAtPositionCondition( gump.ID, 7 ) } );
Engine.AddSendPostFilter( pfi );
gump.SendGump();
gump.AutoResetEvent.WaitOne();
Engine.RemoveSendPostFilter( pfi );
return gump.Result;
}
private static Point GetWindowSize()
{
if ( !NativeMethods.GetWindowRect( Engine.WindowHandle, out NativeMethods.RECT rect ) )
{
return Point.Empty;
}
int windowWidth = rect.Right - rect.Left;
int windowHeight = rect.Bottom - rect.Top;
return new Point( windowWidth, windowHeight );
}
public static Point GetGameWindowCenter()
{
dynamic currentProfile =
Reflection.GetTypePropertyValue<dynamic>( "ClassicUO.Configuration.ProfileManager", "Current", null );
if ( currentProfile == null )
{
return Point.Empty;
}
dynamic gameWindowSize =
Reflection.GetTypePropertyValue<dynamic>( currentProfile.GetType(), "GameWindowSize", currentProfile );
dynamic gameWindowPosition = Reflection.GetTypePropertyValue<dynamic>( currentProfile.GetType(),
"GameWindowPosition", currentProfile );
if ( gameWindowSize == null || gameWindowPosition == null )
{
return Point.Empty;
}
return new Point( gameWindowPosition.X + ( gameWindowSize.X >> 1 ),
gameWindowPosition.Y + ( gameWindowSize.Y >> 1 ) );
}
private void SetCenterPosition( int width, int height )
{
Point gameCenterPosition = GetGameWindowCenter();
if ( gameCenterPosition == Point.Empty )
{
Point size = GetWindowSize();
X = ( size.X - ( width ) ) >> 1;
Y = ( size.Y - ( height ) ) >> 1;
return;
}
X = gameCenterPosition.X - width / 2;
Y = gameCenterPosition.Y - height / 2;
}
}
}
@@ -0,0 +1,112 @@
#region License
// Copyright (C) 2020 Reetus
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program. If not, see <https://www.gnu.org/licenses/>.
#endregion
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading;
using ClassicAssist.Shared;
using ClassicAssist.Data;
using ClassicAssist.Data.Macros;
using ClassicAssist.UO.Objects.Gumps;
namespace ClassicAssist.UO.Gumps
{
public class MacrosGump : RepositionableGump
{
private static Timer _timer;
private static string _lastList;
private static int _serial = 0x0fe00000;
public MacrosGump( string html ) : base( 190, 180, _serial++, (uint) _serial++ )
{
GumpX = Options.CurrentOptions.MacrosGumpX;
GumpY = Options.CurrentOptions.MacrosGumpY;
Movable = false;
Closable = false;
Resizable = false;
Disposable = false;
AddPage( 0 );
AddBackground( 0, 0, 190, 180, 3500 );
AddHtml( 20, 20, 150, 140, html, false, true );
}
public static void ResendGump( bool force = false )
{
try
{
MacroManager _macroManager = MacroManager.GetInstance();
IEnumerable<MacroEntry> macro = _macroManager.Items.Where( e => e.IsRunning );
string html = string.Empty;
foreach ( MacroEntry entry in macro )
{
if ( entry.IsBackground )
{
html += $"<BASEFONT COLOR=#000000><I>{entry.Name}</I></BASEFONT>\n";
}
else
{
html += $"<BASEFONT COLOR=#000000>{entry.Name}</BASEFONT>\n";
}
}
if ( html.Equals( _lastList ) && !force )
{
return;
}
if ( Engine.Gumps.GetGumps( out Gump[] gumps ) )
{
foreach ( Gump macrosGump in gumps.Where( g => g is MacrosGump ) )
{
Shared.UO.Commands.CloseClientGump( macrosGump.ID );
}
}
MacrosGump gump = new MacrosGump( html );
gump.SendGump();
_lastList = html;
}
catch ( InvalidOperationException e )
{
Console.WriteLine( e.ToString() );
}
}
public static void Initialize()
{
_timer = new Timer( o => ResendGump(), null, 1000, 250 );
}
public override void SetPosition( int x, int y )
{
base.SetPosition( x, y );
Options.CurrentOptions.MacrosGumpX = x;
Options.CurrentOptions.MacrosGumpY = y;
ResendGump( true );
}
}
}
@@ -0,0 +1,33 @@
#region License
// Copyright (C) 2020 Reetus
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program. If not, see <https://www.gnu.org/licenses/>.
#endregion
using ClassicAssist.UO.Data;
namespace ClassicAssist.UO.Network.Packets
{
public class Pathfind : BasePacket
{
public Pathfind(int x, int y, int z)
{
_writer = new PacketWriter(7);
_writer.Write((byte)0x38);
_writer.Write((short)x);
_writer.Write((short)y);
_writer.Write((short)z);
}
}
}
@@ -0,0 +1,84 @@
#region License
// Copyright (C) 2020 Reetus
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program. If not, see <https://www.gnu.org/licenses/>.
#endregion
using System.Threading;
using ClassicAssist.Shared;
using ClassicAssist.UI.ViewModels;
using ClassicAssist.UO.Objects.Gumps;
namespace ClassicAssist.UO.Gumps
{
public abstract class RepositionableGump : Gump
{
private const int REPOSITION_BUTTON_ID = 10;
private readonly int _height;
private readonly int _width;
protected RepositionableGump( int width, int height, int serial, uint gumpID ) : base( 0, 0, serial,
gumpID )
{
_width = width;
_height = height;
}
public int GumpX { get; set; } = 100;
public int GumpY { get; set; } = 100;
public override void SendGump()
{
X = GumpX;
Y = GumpY;
AddButton( _width - 15, 5, 0x82C, 0x82C, REPOSITION_BUTTON_ID, GumpButtonType.Reply, 0 );
base.SendGump();
}
public override void OnResponse( int buttonID, int[] switches )
{
if ( buttonID == REPOSITION_BUTTON_ID )
{
Engine.Dispatcher.Invoke( () =>
{
Thread t = new Thread( () =>
{
SetPosition( GumpX, GumpY );
RepositionableGumpViewModel vm = new RepositionableGumpViewModel( this, GumpX, GumpY );
//TODO UI
//RepositionableGumpWindow window = new RepositionableGumpWindow { DataContext = vm };
//WindowInteropHelper helper = new WindowInteropHelper( window ) { Owner = Engine.WindowHandle };
//window.ShowInTaskbar = false;
//window.ShowDialog();
} )
{ IsBackground = true };
t.SetApartmentState( ApartmentState.STA );
t.Start();
} );
}
base.OnResponse( buttonID, switches );
}
public virtual void SetPosition( int x, int y )
{
GumpX = x;
GumpY = y;
}
}
}
@@ -0,0 +1,39 @@
using System;
using ClassicAssist.Data;
using ClassicAssist.Misc;
using ClassicAssist.UO.Objects.Gumps;
namespace ClassicAssist.UO.Gumps
{
public sealed class UpdateMessageGump : Gump
{
private readonly Version _version;
public UpdateMessageGump( IntPtr hWnd, string message, Version version ) : base( 500, 250, -1 )
{
if ( NativeMethods.GetWindowRect( hWnd, out NativeMethods.RECT rect ) )
{
int windowWidth = rect.Right - rect.Left;
int windowHeight = rect.Bottom - rect.Top;
X = ( windowWidth - 600 ) / 2;
Y = ( windowHeight - 400 ) / 2;
}
_version = version;
Closable = true;
Disposable = false;
AddPage( 0 );
AddBackground( 0, 0, 500, 400, 9270 );
AddHtml( 20, 20, 460, 330, message, true, true );
AddButton( 420, 360, 247, 248, 0, GumpButtonType.Reply, 0 );
}
public override void OnResponse( int buttonID, int[] switches )
{
AssistantOptions.UpdateGumpVersion = _version;
base.OnResponse( buttonID, switches );
}
}
}
@@ -0,0 +1,81 @@
using ClassicAssist.Shared;
using ClassicAssist.Data;
using ClassicAssist.Data.Macros.Commands;
using ClassicAssist.UO.Gumps;
using ClassicAssist.UO.Objects.Gumps;
namespace ClassicAssist.UO.Data.Gumps
{
public class WeaponAbilitiesGump : RepositionableGump
{
private readonly bool _primaryEnable;
private readonly int _primaryId;
private readonly bool _secondaryEnable;
private readonly int _secondaryId;
public WeaponAbilitiesGump( int primaryId, bool primaryEnable, int secondaryId, bool secondaryEnable ) : base(
90, 40, GumpSerial++, (uint) GumpSerial++ )
{
if ( Engine.Gumps.GetGumps( out Gump[] gumps ) )
{
foreach ( Gump gump in gumps )
{
if ( gump is WeaponAbilitiesGump )
{
Shared.UO.Commands.CloseClientGump( gump.ID );
}
}
}
GumpX = Options.CurrentOptions.AbilitiesGumpX;
GumpY = Options.CurrentOptions.AbilitiesGumpY;
_primaryId = primaryId;
_secondaryId = secondaryId;
_primaryEnable = primaryEnable;
_secondaryEnable = secondaryEnable;
Movable = false;
Closable = false;
AddPage( 0 );
AddImage( 0, 0, 0x5200 + ( primaryId - 1 ), primaryEnable ? 37 : 0 );
AddImage( 45, 0, 0x5200 + ( secondaryId - 1 ), secondaryEnable ? 37 : 0 );
AddButton( 15, 15, 1209, 1210, 1, GumpButtonType.Reply, 0 );
AddButton( 60, 15, 1209, 1210, 2, GumpButtonType.Reply, 0 );
}
public static int GumpSerial { get; set; } = 0x0fff0000;
public override void OnResponse( int buttonID, int[] switches )
{
switch ( buttonID )
{
case 1:
AbilitiesCommands.SetAbility( "primary" );
break;
case 2:
AbilitiesCommands.SetAbility( "secondary" );
break;
}
base.OnResponse( buttonID, switches );
}
private void ResendGump()
{
WeaponAbilitiesGump gump =
new WeaponAbilitiesGump( _primaryId, _primaryEnable, _secondaryId, _secondaryEnable );
gump.SendGump();
}
public override void SetPosition( int x, int y )
{
base.SetPosition( x, y );
Options.CurrentOptions.AbilitiesGumpX = x;
Options.CurrentOptions.AbilitiesGumpY = y;
ResendGump();
}
}
}
+152
View File
@@ -0,0 +1,152 @@
using System;
using System.Drawing;
using System.Drawing.Imaging;
using System.IO;
using System.Runtime.InteropServices;
using System.Text;
namespace ClassicAssist.UO.Data
{
public static class Hues
{
private const int BlockCount = 375;
private static string _dataPath;
public static Lazy<HueEntry[]> _lazyHueEntries = new Lazy<HueEntry[]>( LoadHueIndex );
public static bool Initialize( string dataPath )
{
_dataPath = dataPath;
return true;
}
private static HueEntry[] LoadHueIndex()
{
HueEntry[] entries = new HueEntry[3000];
if ( !File.Exists( Path.Combine( _dataPath, "hues.mul" ) ) )
{
throw new FileNotFoundException( "File not found", "hues.mul" );
}
using ( FileStream reader = File.Open( Path.Combine( _dataPath, "hues.mul" ), FileMode.Open,
FileAccess.Read, FileShare.ReadWrite ) )
{
BinaryReader binaryReader = new BinaryReader( reader );
int total = 0;
for ( int i = 0; i < BlockCount; i++ )
{
binaryReader.ReadInt32();
for ( int j = 0; j < 8; ++j, ++total )
{
entries[total] = HueEntry.Read( binaryReader );
}
}
}
return entries;
}
public static unsafe void ApplyHue( int hue, Bitmap bmp, bool onlyHueGrayPixels )
{
BitmapData bd = bmp.LockBits( new Rectangle( 0, 0, bmp.Width, bmp.Height ), ImageLockMode.ReadWrite,
PixelFormat.Format16bppArgb1555 );
hue = ( hue & 0x3FFF ) - 1;
int stride = bd.Stride >> 1;
int width = bd.Width;
int height = bd.Height;
int delta = stride - width;
ushort* pBuffer = (ushort*) bd.Scan0;
ushort* pLineEnd = pBuffer + width;
ushort* pImageEnd = pBuffer + stride * height;
if ( onlyHueGrayPixels )
{
while ( pBuffer < pImageEnd )
{
while ( pBuffer < pLineEnd )
{
int c = *pBuffer;
if ( c != 0 )
{
int r = ( c >> 10 ) & 0x1F;
int g = ( c >> 5 ) & 0x1F;
int b = c & 0x1F;
if ( r == g && r == b )
{
*pBuffer = (ushort) _lazyHueEntries.Value[hue].Colors[( c >> 10 ) & 0x1F];
}
}
++pBuffer;
}
pBuffer += delta;
pLineEnd += stride;
}
}
else
{
while ( pBuffer < pImageEnd )
{
while ( pBuffer < pLineEnd )
{
if ( *pBuffer != 0 )
{
int index = ( *pBuffer >> 10 ) & 0x1F;
HueEntry hueEntry = _lazyHueEntries.Value[hue];
*pBuffer = (ushort) hueEntry.Colors[index];
}
++pBuffer;
}
pBuffer += delta;
pLineEnd += stride;
}
}
bmp.UnlockBits( bd );
}
}
[StructLayout( LayoutKind.Sequential, Pack = 1 )]
public struct HueEntry
{
[MarshalAs( UnmanagedType.ByValArray, SizeConst = 32 )]
public short[] Colors;
public short TableStart;
public short TableEnd;
[MarshalAs( UnmanagedType.ByValArray, SizeConst = 20 )]
public string Name;
public static HueEntry Read( BinaryReader reader )
{
// ReSharper disable once UseObjectOrCollectionInitializer
HueEntry entry = new HueEntry();
entry.Colors = new short[32];
for ( int i = 0; i < 32; ++i )
{
entry.Colors[i] = (short) ( reader.ReadUInt16() | 0x8000 );
}
entry.TableStart = (short) ( reader.ReadUInt16() | 0x8000 );
entry.TableEnd = (short) ( reader.ReadUInt16() | 0x8000 );
entry.Name = Encoding.ASCII.GetString( reader.ReadBytes( 20 ) ).TrimEnd( '\0' );
return entry;
}
}
}
+12
View File
@@ -0,0 +1,12 @@
namespace ClassicAssist.UO.Data
{
public struct LandTile
{
public TileFlags Flags { get; set; }
public int ID { get; set; }
public string Name { get; set; }
public int X { get; set; }
public int Y { get; set; }
public sbyte Z { get; set; }
}
}
+37
View File
@@ -0,0 +1,37 @@
namespace ClassicAssist.UO.Data
{
public enum Layer : byte
{
Invalid = 0,
OneHanded = 1,
TwoHanded = 2,
Shoes = 3,
Pants = 4,
Shirt = 5,
Helm = 6,
Gloves = 7,
Ring = 8,
Talisman = 9,
Neck = 10, // 0x0A
Hair = 11, // 0x0B
Waist = 12, // 0x0C
InnerTorso = 13, // 0x0D
Bracelet = 14, // 0x0E
Unused_xF = 15, // 0x0F
FacialHair = 16, // 0x10
MiddleTorso = 17, // 0x11
Earrings = 18, // 0x12
Arms = 19, // 0x13
Cloak = 20, // 0x14
Backpack = 21, // 0x15
OuterTorso = 22, // 0x16
OuterLegs = 23, // 0x17
InnerLegs = 24, // 0x18
Mount = 25, // 0x19
ShopBuy = 26, // 0x1A
ShopResale = 27, // 0x1B
ShopSell = 28, // 0x1C
Bank = 29, // 0x1D
LastValid = 29 // 0x1D
}
}
+131
View File
@@ -0,0 +1,131 @@
using System;
using System.IO;
using System.Linq;
using ClassicAssist.Shared;
namespace ClassicAssist.UO.Data
{
public enum Map
{
Felucca,
Trammel,
Ilshenar,
Malas,
Tokuno,
Ter_Mur
}
public static class MapInfo
{
private static string _dataPath;
private static readonly UOPIndex[] _uopIndex = new UOPIndex[6];
private static int[,] _defaultMapSize { get; } =
{
{ 7168, 4096 }, { 7168, 4096 }, { 2304, 1600 }, { 2560, 2048 }, { 1448, 1448 }, { 1280, 4096 }
};
public static void Initialize( string dataPath )
{
_dataPath = dataPath;
}
public static LandTile GetLandTile( int map, int x, int y )
{
int blockIndex = x / 8 * ( _defaultMapSize[map, 1] / 8 ) + y / 8;
int cellX = x % 8;
int cellY = y % 8;
string fileName = GetMapFilename( map );
if ( string.IsNullOrEmpty( fileName ) )
{
return new LandTile();
}
using ( FileStream fileStream = File.OpenRead( fileName ) )
{
if ( fileName.EndsWith( ".uop", StringComparison.InvariantCultureIgnoreCase ) &&
_uopIndex[map] == null )
{
_uopIndex[map] = new UOPIndex( File.OpenRead( fileName ) );
}
int offset = blockIndex * 196 + 4 + cellY * 24 + cellX * 3;
if ( _uopIndex[map] != null )
{
offset = _uopIndex[map].Lookup( offset );
}
fileStream.Seek( offset, SeekOrigin.Begin );
using ( BinaryReader binaryReader = new BinaryReader( fileStream ) )
{
int id = binaryReader.ReadUInt16();
LandTile landTile = TileData.GetLandTile( id );
landTile.ID = id;
landTile.X = x;
landTile.Y = y;
landTile.Z = binaryReader.ReadSByte();
return landTile;
}
}
}
public static (int, int) GetMapSurface( int map, int x, int y )
{
LandTile landTile = GetLandTile( map, x, y );
StaticTile[] staticTiles = Statics.GetStatics( map, x, y );
if ( staticTiles == null || staticTiles.Length == 0 )
{
return ( landTile.Z, 0 );
}
StaticTile staticTile = staticTiles
.Where( i => i.Flags.HasFlag( TileFlags.Surface ) && Math.Abs( Engine.Player.Z - i.Z ) < 20 )
.OrderByDescending( i => i.Z ).FirstOrDefault();
if ( staticTile.ID == 0 && landTile.Flags.HasFlag( TileFlags.Impassable ) )
{
staticTile = staticTiles
.Where( i => i.Flags.HasFlag( TileFlags.Wet ) && Math.Abs( Engine.Player.Z - i.Z ) < 20 )
.OrderByDescending( i => i.Z ).FirstOrDefault();
}
if ( staticTile.ID == 0 )
{
return ( landTile.Z, 0 );
}
return ( staticTile.Z + staticTile.Height, staticTile.ID );
}
private static string GetMapFilename( int map )
{
if ( string.IsNullOrEmpty( _dataPath ) )
{
return null;
}
string uopFilename = Path.Combine( _dataPath, $"map{map}LegacyMUL.uop" );
if ( File.Exists( uopFilename ) )
{
return uopFilename;
}
string mulFilename = Path.Combine( _dataPath, $"map{map}.mul" );
if ( File.Exists( mulFilename ) )
{
return mulFilename;
}
return null;
}
}
}
@@ -0,0 +1,9 @@
namespace ClassicAssist.UO.Network.PacketFilter
{
public enum PacketDirection : byte
{
Incoming,
Outgoing,
Any
}
}
@@ -0,0 +1,197 @@
using System;
using System.IO;
using System.Text;
namespace ClassicAssist.UO.Data
{
public class PacketReader
{
private readonly MemoryStream _stream;
public PacketReader( byte[] data, int size, bool fixedSize )
{
_stream = new MemoryStream( data, 0, size, false );
_stream.Seek( fixedSize ? 1 : 3, SeekOrigin.Current );
}
public long Index => _stream.Position;
public long Size => _stream.Length;
public byte[] GetData()
{
return _stream.ToArray();
}
public static bool IsSafeChar( int c )
{
return c >= 0x20 && c < 0xFFFE;
}
public bool ReadBoolean()
{
return _stream.ReadByte() != 0;
}
public byte ReadByte()
{
return (byte) _stream.ReadByte();
}
public byte[] ReadByteArray( int length )
{
byte[] bytes = new byte[length];
for ( int i = 0; i < length; i++ )
{
bytes[i] = ReadByte();
}
return bytes;
}
public short ReadInt16()
{
byte[] buffer = new byte[2];
_stream.Read( buffer, 0, 2 );
return (short) ( ( buffer[0] << 8 ) | buffer[1] );
}
public int ReadInt32()
{
byte[] buffer = new byte[4];
_stream.Read( buffer, 0, 4 );
return ( buffer[0] << 24 ) | ( buffer[1] << 16 ) | ( buffer[2] << 8 ) | buffer[3];
}
public sbyte ReadSByte()
{
return (sbyte) ReadByte();
}
public string ReadString()
{
StringBuilder sb = new StringBuilder();
int c;
while ( Index + 1 < Size && ( c = ReadByte() ) != 0 )
{
sb.Append( (char) c );
}
return sb.ToString();
}
public string ReadString( int fixedLength )
{
byte[] buffer = new byte[fixedLength];
_stream.Read( buffer, 0, fixedLength );
return Encoding.ASCII.GetString( buffer ).TrimEnd( '\0' );
}
public string ReadStringSafe()
{
throw new NotImplementedException();
}
public string ReadStringSafe( int fixedLength )
{
StringBuilder output = new StringBuilder();
for ( int i = 0; i < fixedLength; i++ )
{
char c = (char) ReadByte();
if ( c == '\0' )
{
ReadByteArray( fixedLength - i - 1 );
break;
}
if ( IsSafeChar( c ) )
{
output.Append( c );
}
}
return output.ToString();
}
public ushort ReadUInt16()
{
return (ushort) ReadInt16();
}
public uint ReadUInt32()
{
return (uint) ReadInt32();
}
public string ReadUnicodeString()
{
StringBuilder sb = new StringBuilder();
int c;
while ( Index + 1 < Size && ( c = ( ReadByte() << 8 ) | ReadByte() ) != 0 )
{
sb.Append( (char) c );
}
return sb.ToString();
}
public string ReadUnicodeString( int fixedLength )
{
byte[] buffer = new byte[fixedLength];
_stream.Read( buffer, 0, fixedLength );
return Encoding.Unicode.GetString( buffer );
}
public string ReadUnicodeStringLE()
{
StringBuilder sb = new StringBuilder();
int c;
while ( Index + 1 < Size && ( c = ReadByte() | ( ReadByte() << 8 ) ) != 0 )
{
sb.Append( (char) c );
}
return sb.ToString();
}
public string ReadUnicodeStringLESafe( int fixedLength )
{
throw new NotImplementedException();
}
public string ReadUnicodeStringLESafe()
{
throw new NotImplementedException();
}
public string ReadUnicodeStringSafe()
{
throw new NotImplementedException();
}
public string ReadUnicodeStringSafe( int fixedLength )
{
throw new NotImplementedException();
}
public long Seek( int offset, SeekOrigin origin )
{
return _stream.Seek( offset, origin );
}
}
}
@@ -0,0 +1,480 @@
/***************************************************************************
* PacketWriter.cs
* -------------------
* begin : May 1, 2002
* copyright : (C) The RunUO Software Team
* email : info@runuo.com
*
* $Id$
*
***************************************************************************/
/***************************************************************************
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
***************************************************************************/
using System;
using System.Collections.Generic;
using System.IO;
using System.Text;
namespace ClassicAssist.UO.Data
{
/// <summary>
/// Provides functionality for writing primitive binary data.
/// </summary>
public class PacketWriter : IDisposable
{
private static readonly Stack<PacketWriter> Pool = new Stack<PacketWriter>();
/// <summary>
/// Internal format buffer.
/// </summary>
private readonly byte[] _buffer = new byte[4];
/// <summary>
/// Internal stream which holds the entire packet.
/// </summary>
private readonly MemoryStream _stream;
private int m_Capacity;
/// <summary>
/// Instantiates a new PacketWriter instance with the default capacity of 4 bytes.
/// </summary>
public PacketWriter() : this( 32 )
{
}
/// <summary>
/// Instantiates a new PacketWriter instance with a given capacity.
/// </summary>
/// <param name="capacity">Initial capacity for the internal stream.</param>
public PacketWriter( int capacity )
{
_stream = new MemoryStream( capacity );
m_Capacity = capacity;
}
/// <summary>
/// Gets the total stream length.
/// </summary>
public long Length => _stream.Length;
/// <summary>
/// Gets or sets the current stream position.
/// </summary>
public long Position
{
get => _stream.Position;
set => _stream.Position = value;
}
/// <summary>
/// The internal stream used by this PacketWriter instance.
/// </summary>
public MemoryStream UnderlyingStream => _stream;
public void Dispose()
{
Dispose( true );
GC.SuppressFinalize( this );
}
public static PacketWriter CreateInstance()
{
return CreateInstance( 32 );
}
public static PacketWriter CreateInstance( int capacity )
{
PacketWriter pw = null;
lock ( Pool )
{
if ( Pool.Count > 0 )
{
pw = Pool.Pop();
if ( pw != null )
{
pw.m_Capacity = capacity;
pw._stream.SetLength( 0 );
}
}
}
return pw ?? new PacketWriter( capacity );
}
public static void ReleaseInstance( PacketWriter pw )
{
lock ( Pool )
{
if ( !Pool.Contains( pw ) )
{
Pool.Push( pw );
}
else
{
try
{
using ( StreamWriter op = new StreamWriter( "neterr.log" ) )
{
op.WriteLine( "{0}\tInstance pool contains writer", DateTime.UtcNow );
}
}
catch
{
Console.WriteLine( @"net error" );
}
}
}
}
/// <summary>
/// Writes a 1-byte boolean value to the underlying stream. False is represented by 0, true by 1.
/// </summary>
public void Write( bool value )
{
_stream.WriteByte( (byte) ( value ? 1 : 0 ) );
}
/// <summary>
/// Writes a 1-byte unsigned integer value to the underlying stream.
/// </summary>
public void Write( byte value )
{
_stream.WriteByte( value );
}
/// <summary>
/// Writes a 1-byte signed integer value to the underlying stream.
/// </summary>
public void Write( sbyte value )
{
_stream.WriteByte( (byte) value );
}
/// <summary>
/// Writes a 2-byte signed integer value to the underlying stream.
/// </summary>
public void Write( short value )
{
_buffer[0] = (byte) ( value >> 8 );
_buffer[1] = (byte) value;
_stream.Write( _buffer, 0, 2 );
}
/// <summary>
/// Writes a 2-byte unsigned integer value to the underlying stream.
/// </summary>
public void Write( ushort value )
{
_buffer[0] = (byte) ( value >> 8 );
_buffer[1] = (byte) value;
_stream.Write( _buffer, 0, 2 );
}
/// <summary>
/// Writes a 4-byte signed integer value to the underlying stream.
/// </summary>
public void Write( int value )
{
_buffer[0] = (byte) ( value >> 24 );
_buffer[1] = (byte) ( value >> 16 );
_buffer[2] = (byte) ( value >> 8 );
_buffer[3] = (byte) value;
_stream.Write( _buffer, 0, 4 );
}
/// <summary>
/// Writes a 4-byte unsigned integer value to the underlying stream.
/// </summary>
public void Write( uint value )
{
_buffer[0] = (byte) ( value >> 24 );
_buffer[1] = (byte) ( value >> 16 );
_buffer[2] = (byte) ( value >> 8 );
_buffer[3] = (byte) value;
_stream.Write( _buffer, 0, 4 );
}
/// <summary>
/// Writes a sequence of bytes to the underlying stream
/// </summary>
public void Write( byte[] buffer, int offset, int size )
{
_stream.Write( buffer, offset, size );
}
/// <summary>
/// Writes a fixed-length ASCII-encoded string value to the underlying stream. To fit (size), the string content is
/// either truncated or padded with null characters.
/// </summary>
public void WriteAsciiFixed( string value, int size )
{
if ( value == null )
{
Console.WriteLine( @"Network: Attempted to WriteAsciiFixed() with null value" );
value = string.Empty;
}
int length = value.Length;
_stream.SetLength( _stream.Length + size );
if ( length >= size )
{
_stream.Position +=
Encoding.ASCII.GetBytes( value, 0, size, _stream.GetBuffer(), (int) _stream.Position );
}
else
{
Encoding.ASCII.GetBytes( value, 0, length, _stream.GetBuffer(), (int) _stream.Position );
_stream.Position += size;
}
/*byte[] buffer = Encoding.ASCII.GetBytes( value );
if ( buffer.Length >= size )
{
m_Stream.Write( buffer, 0, size );
}
else
{
m_Stream.Write( buffer, 0, buffer.Length );
Fill( size - buffer.Length );
}*/
}
/// <summary>
/// Writes a dynamic-length ASCII-encoded string value to the underlying stream, followed by a 1-byte null character.
/// </summary>
public void WriteAsciiNull( string value )
{
if ( value == null )
{
Console.WriteLine( @"Network: Attempted to WriteAsciiNull() with null value" );
value = string.Empty;
}
int length = value.Length;
_stream.SetLength( _stream.Length + length + 1 );
Encoding.ASCII.GetBytes( value, 0, length, _stream.GetBuffer(), (int) _stream.Position );
_stream.Position += length + 1;
/*byte[] buffer = Encoding.ASCII.GetBytes( value );
m_Stream.Write( buffer, 0, buffer.Length );
m_Stream.WriteByte( 0 );*/
}
/// <summary>
/// Writes a dynamic-length little-endian unicode string value to the underlying stream, followed by a 2-byte null
/// character.
/// </summary>
public void WriteLittleUniNull( string value )
{
if ( value == null )
{
Console.WriteLine( @"Network: Attempted to WriteLittleUniNull() with null value" );
value = string.Empty;
}
int length = value.Length;
_stream.SetLength( _stream.Length + ( length + 1 ) * 2 );
_stream.Position +=
Encoding.Unicode.GetBytes( value, 0, length, _stream.GetBuffer(), (int) _stream.Position );
_stream.Position += 2;
/*byte[] buffer = Encoding.Unicode.GetBytes( value );
m_Stream.Write( buffer, 0, buffer.Length );
m_Buffer[0] = 0;
m_Buffer[1] = 0;
m_Stream.Write( m_Buffer, 0, 2 );*/
}
/// <summary>
/// Writes a fixed-length little-endian unicode string value to the underlying stream. To fit (size), the string
/// content is either truncated or padded with null characters.
/// </summary>
public void WriteLittleUniFixed( string value, int size )
{
if ( value == null )
{
Console.WriteLine( @"Network: Attempted to WriteLittleUniFixed() with null value" );
value = string.Empty;
}
size *= 2;
int length = value.Length;
_stream.SetLength( _stream.Length + size );
if ( length * 2 >= size )
{
_stream.Position +=
Encoding.Unicode.GetBytes( value, 0, length, _stream.GetBuffer(), (int) _stream.Position );
}
else
{
Encoding.Unicode.GetBytes( value, 0, length, _stream.GetBuffer(), (int) _stream.Position );
_stream.Position += size;
}
/*size *= 2;
byte[] buffer = Encoding.Unicode.GetBytes( value );
if ( buffer.Length >= size )
{
m_Stream.Write( buffer, 0, size );
}
else
{
m_Stream.Write( buffer, 0, buffer.Length );
Fill( size - buffer.Length );
}*/
}
/// <summary>
/// Writes a dynamic-length big-endian unicode string value to the underlying stream, followed by a 2-byte null
/// character.
/// </summary>
public void WriteBigUniNull( string value )
{
if ( value == null )
{
Console.WriteLine( @"Network: Attempted to WriteBigUniNull() with null value" );
value = string.Empty;
}
int length = value.Length;
_stream.SetLength( _stream.Length + ( length + 1 ) * 2 );
_stream.Position +=
Encoding.BigEndianUnicode.GetBytes( value, 0, length, _stream.GetBuffer(), (int) _stream.Position );
_stream.Position += 2;
/*byte[] buffer = Encoding.BigEndianUnicode.GetBytes( value );
m_Stream.Write( buffer, 0, buffer.Length );
m_Buffer[0] = 0;
m_Buffer[1] = 0;
m_Stream.Write( m_Buffer, 0, 2 );*/
}
/// <summary>
/// Writes a fixed-length big-endian unicode string value to the underlying stream. To fit (size), the string content
/// is either truncated or padded with null characters.
/// </summary>
public void WriteBigUniFixed( string value, int size )
{
if ( value == null )
{
Console.WriteLine( @"Network: Attempted to WriteBigUniFixed() with null value" );
value = string.Empty;
}
size *= 2;
int length = value.Length;
_stream.SetLength( _stream.Length + size );
if ( length * 2 >= size )
{
_stream.Position +=
Encoding.BigEndianUnicode.GetBytes( value, 0, length, _stream.GetBuffer(), (int) _stream.Position );
}
else
{
Encoding.BigEndianUnicode.GetBytes( value, 0, length, _stream.GetBuffer(), (int) _stream.Position );
_stream.Position += size;
}
/*size *= 2;
byte[] buffer = Encoding.BigEndianUnicode.GetBytes( value );
if ( buffer.Length >= size )
{
m_Stream.Write( buffer, 0, size );
}
else
{
m_Stream.Write( buffer, 0, buffer.Length );
Fill( size - buffer.Length );
}*/
}
/// <summary>
/// Fills the stream from the current position up to (capacity) with 0x00's
/// </summary>
public void Fill()
{
Fill( (int) ( m_Capacity - _stream.Length ) );
}
/// <summary>
/// Writes a number of 0x00 byte values to the underlying stream.
/// </summary>
public void Fill( int length )
{
if ( _stream.Position == _stream.Length )
{
_stream.SetLength( _stream.Length + length );
_stream.Seek( 0, SeekOrigin.End );
}
else
{
_stream.Write( new byte[length], 0, length );
}
}
/// <summary>
/// Offsets the current position from an origin.
/// </summary>
public long Seek( long offset, SeekOrigin origin )
{
return _stream.Seek( offset, origin );
}
/// <summary>
/// Gets the entire stream content as a byte array.
/// </summary>
public byte[] ToArray()
{
return _stream.ToArray();
}
protected virtual void Dispose( bool isDisposing )
{
if ( isDisposing )
{
_stream.Dispose();
}
GC.SuppressFinalize( this );
}
}
}
@@ -0,0 +1,48 @@
namespace ClassicAssist.UO.Data
{
public class PlayerStatus
{
public int ColdResist { get; set; }
public int DamageIncrease { get; set; }
public int DefenseChanceIncrease { get; set; }
public int Dex { get; set; }
public int EnergyResist { get; set; }
public int FasterCasting { get; set; }
public int FasterCastRecovery { get; set; }
public int Features { get; set; }
public int FireResist { get; set; }
public int Followers { get; set; }
public int Gold { get; set; }
public int Health { get; set; }
public int HitChanceIncrease { get; set; }
public int Int { get; set; }
public int LowerManaCost { get; set; }
public int LowerReagentCost { get; set; }
public int Luck { get; set; }
public int Mana { get; set; }
public int MaxColdResist { get; set; }
public int MaxDamage { get; set; }
public int MaxEnergyResist { get; set; }
public int MaxFireResist { get; set; }
public int MaxFollowers { get; set; }
public int MaxHealth { get; set; }
public int MaxMana { get; set; }
public int MaxPhysicalResist { get; set; }
public int MaxPoisonResist { get; set; }
public int MaxStamina { get; set; }
public int MaxWeight { get; set; }
public int MinDamage { get; set; }
public string Name { get; set; }
public int PhysicalResist { get; set; }
public int PoisonResist { get; set; }
public int Race { get; set; }
public int Sex { get; set; }
public int SpellDamageIncrease { get; set; }
public int Stamina { get; set; }
public int StatCap { get; set; }
public int Str { get; set; }
public int SwingSpeedIncrease { get; set; }
public int TithingPoints { get; set; }
public int Weight { get; set; }
}
}
@@ -0,0 +1,100 @@
#region License
// Copyright (C) 2020 Reetus
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program. If not, see <https://www.gnu.org/licenses/>.
#endregion
using System.Collections;
using System.Collections.Generic;
using System.Linq;
using ClassicAssist.Shared;
using ClassicAssist.UO.Network.Packets;
namespace ClassicAssist.UO.Data
{
public enum QuestPointerType
{
Resurrection,
Corpse,
Other
}
public class QuestPointerList : IEnumerable<QuestPointer>
{
private readonly object _lock = new object();
private readonly List<QuestPointer> _questPointers = new List<QuestPointer>();
public IEnumerator<QuestPointer> GetEnumerator()
{
return _questPointers.GetEnumerator();
}
IEnumerator IEnumerable.GetEnumerator()
{
return GetEnumerator();
}
public void Add( QuestPointer questPointer )
{
lock ( _lock )
{
_questPointers.Add( questPointer );
}
Engine.SendPacketToClient( new DisplayQuestPointer( true, questPointer.X, questPointer.Y,
questPointer.Serial ) );
}
public void Remove( QuestPointer questPointer )
{
lock ( _lock )
{
_questPointers.Remove( questPointer );
}
Engine.SendPacketToClient( new DisplayQuestPointer( false, questPointer.X, questPointer.Y,
questPointer.Serial ) );
}
public void Clear()
{
Remove( _questPointers );
}
public void Remove( IEnumerable<QuestPointer> questPointers )
{
foreach ( QuestPointer questPointer in questPointers.ToArray() )
{
Remove( questPointer );
}
}
public void RemoveByType( QuestPointerType type )
{
IEnumerable<QuestPointer> pointers = _questPointers.Where( p => p.Type == type );
Remove( pointers );
}
}
public class QuestPointer
{
public int Serial { get; set; }
public QuestPointerType Type { get; set; }
public int X { get; set; }
public int Y { get; set; }
}
}
+167
View File
@@ -0,0 +1,167 @@
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.Linq;
using ClassicAssist.Shared;
using ClassicAssist.Data;
using ClassicAssist.Shared.UO;
using ClassicAssist.UO.Network.Packets;
using ClassicAssist.UO.Objects;
namespace ClassicAssist.UO.Data
{
public enum RehueType
{
Custom,
Friends,
Enemies
}
public class RehueEntry
{
public int Hue { get; set; }
public int Serial { get; set; }
public RehueType Type { get; set; }
}
public class RehueList
{
private readonly ConcurrentDictionary<int, RehueEntry> _rehueList = new ConcurrentDictionary<int, RehueEntry>();
public void Add( int serial, int hue, RehueType type = RehueType.Custom )
{
RehueEntry entry = new RehueEntry { Serial = serial, Hue = hue, Type = type };
_rehueList.AddOrUpdate( serial, i => entry, ( i, rehueEntry ) => entry );
}
public bool Remove( int serial )
{
return _rehueList.TryRemove( serial, out _ );
}
public bool Contains( int serial )
{
return _rehueList.ContainsKey( serial );
}
public void RemoveByType( RehueType type )
{
IEnumerable<int> keys = _rehueList.Where( kvp => kvp.Value.Type == type ).Select( kvp => kvp.Key );
foreach ( int key in keys )
{
Remove( key );
}
}
public void CheckItem( Item item )
{
if ( !_rehueList.TryGetValue( item.Serial, out RehueEntry entry ) )
{
return;
}
if ( item.Owner != 0 && !UOMath.IsMobile( item.Serial ) )
{
Engine.SendPacketToClient( new ContainerContentUpdate( item.Serial, item.ID, item.Direction, item.Count,
item.X, item.Y, item.Grid, item.Owner, entry.Hue ) );
}
else
{
// TODO
Commands.Resync();
}
}
public bool CheckSAWorldItem( ref byte[] packet, ref int length )
{
int serial = ( packet[4] << 24 ) | ( packet[5] << 16 ) | ( packet[6] << 8 ) | packet[7];
if ( !_rehueList.TryGetValue( serial, out RehueEntry entry ) )
{
return false;
}
Engine.SendPacketToClient( new SAWorldItem( packet, length, entry.Hue ) );
return true;
}
public bool CheckMobileIncoming( Mobile mobile, ItemCollection equipment )
{
bool result = _rehueList.TryGetValue( mobile.Serial, out RehueEntry entry );
if ( result )
{
Engine.SendPacketToClient( new MobileIncoming( mobile, equipment, entry.Hue ) );
return true;
}
if ( Options.CurrentOptions.RehueFriends &&
Options.CurrentOptions.Friends.Any( e => e.Serial == mobile.Serial ) )
{
Engine.SendPacketToClient( new MobileIncoming( mobile, equipment,
Options.CurrentOptions.RehueFriendsHue ) );
return true;
}
return false;
}
public bool CheckMobileUpdate( Mobile mobile )
{
bool result = _rehueList.TryGetValue( mobile.Serial, out RehueEntry entry );
if ( result )
{
Engine.SendPacketToClient( new MobileUpdate( mobile.Serial, mobile.ID,
entry.Hue > 0 ? entry.Hue : mobile.Hue, mobile.Status, mobile.X, mobile.Y, mobile.Z,
mobile.Direction ) );
return true;
}
if ( !Options.CurrentOptions.RehueFriends ||
Options.CurrentOptions.Friends.All( e => e.Serial != mobile.Serial ) )
{
return false;
}
Engine.SendPacketToClient( new MobileUpdate( mobile.Serial, mobile.ID,
Options.CurrentOptions.RehueFriendsHue, mobile.Status, mobile.X, mobile.Y, mobile.Z,
mobile.Direction ) );
return true;
}
public void CheckContainer( ItemCollection collection )
{
int backpack = Engine.Player?.Backpack?.Serial ?? 0;
foreach ( Item item in collection.GetItems() )
{
if ( item.IsDescendantOf( backpack ) )
{
CheckItem( item );
}
}
}
public bool CheckMobileMoving( Mobile mobile )
{
bool result = _rehueList.TryGetValue( mobile.Serial, out RehueEntry entry );
if ( result )
{
Engine.SendPacketToClient( new MobileMoving( mobile, entry.Hue ) );
return true;
}
if ( !Options.CurrentOptions.RehueFriends ||
Options.CurrentOptions.Friends.All( e => e.Serial != mobile.Serial ) )
{
return false;
}
Engine.SendPacketToClient( new MobileMoving( mobile, Options.CurrentOptions.RehueFriendsHue ) );
return true;
}
}
}
@@ -0,0 +1,30 @@
#region License
// Copyright (C) 2020 Reetus
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program. If not, see <https://www.gnu.org/licenses/>.
#endregion
namespace ClassicAssist.UO.Data
{
public class ShardEntry
{
public int Index { get; set; }
public int IP { get; set; }
public string Name { get; set; }
public int PercentFull { get; set; }
public int Timezone { get; set; }
}
}
+117
View File
@@ -0,0 +1,117 @@
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Text;
namespace ClassicAssist.UO.Data
{
public enum LockStatus : byte
{
Up,
Down,
Locked
}
public enum StatType : byte
{
Str,
Dex,
Int
}
public class SkillData
{
public bool Invokable { get; internal set; }
public string Name { get; internal set; }
}
public static class Skills
{
private static Lazy<Dictionary<int, SkillData>> _lazySkillData;
private static string _dataPath;
internal static void Initialize( string dataPath )
{
_dataPath = dataPath;
_lazySkillData = new Lazy<Dictionary<int, SkillData>>( LoadSkills );
}
public static string GetSkillName( int skillID )
{
return _lazySkillData.Value.ContainsKey( skillID ) ? _lazySkillData.Value[skillID].Name : string.Empty;
}
public static bool IsInvokable( int skillID )
{
if ( skillID >= 0 && skillID <= _lazySkillData.Value.Count - 1 )
{
return _lazySkillData.Value[skillID].Invokable;
}
return false;
}
/// <summary>
/// Get string array of all skill names for UO installation defined in options.
/// Array returned will be indexed by skill ID.
/// </summary>
public static string[] GetSkillNames()
{
string[] skillNames = (string[]) _lazySkillData.Value.Values.Select( s => s.Name );
return skillNames;
}
public static SkillData[] GetSkillsArray()
{
return _lazySkillData?.Value.Values.ToArray();
}
internal static Dictionary<int, SkillData> GetSkills()
{
return _lazySkillData.Value;
}
internal static Dictionary<int, SkillData> LoadSkills()
{
string skillIndexFile = Path.Combine( _dataPath, "Skills.idx" );
string skillMulFile = Path.Combine( _dataPath, "skills.mul" );
Dictionary<int, SkillData> skills = new Dictionary<int, SkillData>();
if ( !File.Exists( skillIndexFile ) )
{
throw new FileNotFoundException( "File not found!", skillIndexFile );
}
if ( !File.Exists( skillMulFile ) )
{
throw new FileNotFoundException( "File not found!", skillMulFile );
}
byte[] indexBytes = File.ReadAllBytes( skillIndexFile );
byte[] mulBytes = File.ReadAllBytes( skillMulFile );
for ( int x = 0; x < indexBytes.Length / 12; x++ )
{
int offset = x * 12;
int start = BitConverter.ToInt32( indexBytes, offset );
int length = BitConverter.ToInt32( indexBytes, offset + 4 );
if ( length == 0 )
{
break;
}
skills.Add( x,
new SkillData
{
Invokable = mulBytes[start] == 1,
Name = Encoding.ASCII.GetString( mulBytes, start + 1, length - 2 )
} );
}
return skills;
}
}
}
+74
View File
@@ -0,0 +1,74 @@
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Text;
using System.Text.RegularExpressions;
namespace ClassicAssist.UO.Data
{
public static class Speech
{
private static readonly Lazy<SpeechEntry[]> _entries = new Lazy<SpeechEntry[]>( LoadEntries );
private static string _dataPath;
public static int[] GetKeywords( string input )
{
IEnumerable<SpeechEntry> matches =
_entries.Value.Where( e => Regex.IsMatch( input, WildCardToRegular( e.Keywords ) ) );
return matches.Select( e => e.Id ).Distinct().ToArray();
}
public static void Initialize( string dataPath )
{
_dataPath = dataPath;
}
private static SpeechEntry[] LoadEntries()
{
string fullPath = Path.Combine( _dataPath, "speech.mul" );
using ( FileStream reader =
new FileStream( fullPath, FileMode.Open, FileAccess.Read, FileShare.ReadWrite ) )
{
using ( BinaryReader binaryReader = new BinaryReader( reader ) )
{
List<SpeechEntry> entries = new List<SpeechEntry>();
while ( reader.Position < reader.Length )
{
int id = ( binaryReader.ReadByte() << 8 ) | binaryReader.ReadByte();
int length = ( binaryReader.ReadByte() << 8 ) | binaryReader.ReadByte();
byte[] buffer = new byte[length];
binaryReader.Read( buffer, 0, length );
string text = Encoding.UTF8.GetString( buffer );
if ( text.Contains( "guards" ) )
{
}
entries.Add( new SpeechEntry { Id = id, Keywords = text } );
}
return entries.ToArray();
}
}
}
private static string WildCardToRegular( string value )
{
return "^" + Regex.Escape( value ).Replace( "\\*", ".*" ) + "$";
}
internal struct SpeechEntry
{
public int Id { get; set; }
public string Keywords { get; set; }
}
}
}
@@ -0,0 +1,17 @@
namespace ClassicAssist.UO.Data
{
public struct StaticTile
{
public TileFlags Flags { get; set; }
public byte Weight { get; set; }
public byte Quality { get; set; }
public byte Quantity { get; set; }
public ushort ID { get; set; }
public string Name { get; set; }
public int X { get; set; }
public int Y { get; set; }
public int Z { get; set; }
public int Hue { get; set; }
public int Height { get; set; }
}
}
+149
View File
@@ -0,0 +1,149 @@
#region License
// Copyright (C) 2020 Reetus
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program. If not, see <https://www.gnu.org/licenses/>.
#endregion
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
namespace ClassicAssist.UO.Data
{
public class Statics
{
private static string _dataPath;
private static readonly Lazy<StaticRecord[][]>[] _staticData = new Lazy<StaticRecord[][]>[6];
private static int[,] _defaultMapSize { get; } =
{
{ 7168, 4096 }, { 7168, 4096 }, { 2304, 1600 }, { 2560, 2048 }, { 1448, 1448 }, { 1280, 4096 }
};
internal static void Initialize( string dataPath )
{
_dataPath = dataPath;
for ( int i = 0; i < 6; i++ )
{
int map = i;
_staticData[i] = new Lazy<StaticRecord[][]>( () => LoadStatics( map ) );
}
}
private static StaticRecord[][] LoadStatics( int map )
{
string staticIndexFile = Path.Combine( _dataPath, $"staidx{map}.mul" );
string staticMulFile = Path.Combine( _dataPath, $"statics{map}.mul" );
if ( !File.Exists( staticIndexFile ) )
{
return null;
}
if ( !File.Exists( staticMulFile ) )
{
return null;
}
byte[] indexBytes = File.ReadAllBytes( staticIndexFile );
StaticRecord[][] staticItems = new StaticRecord[indexBytes.Length / 12][];
using ( FileStream fileStream =
new FileStream( staticMulFile, FileMode.Open, FileAccess.Read, FileShare.ReadWrite ) )
{
using ( BinaryReader binaryReader = new BinaryReader( fileStream ) )
{
for ( int x = 0; x < indexBytes.Length / 12; x++ )
{
int offset = x * 12;
int start = BitConverter.ToInt32( indexBytes, offset );
int length = BitConverter.ToInt32( indexBytes, offset + 4 );
if ( start == -1 )
{
continue;
}
fileStream.Seek( start, SeekOrigin.Begin );
staticItems[x] = new StaticRecord[length / 7];
for ( int y = 0; y < length / 7; y++ )
{
staticItems[x][y].ID = binaryReader.ReadUInt16();
staticItems[x][y].X = binaryReader.ReadByte();
staticItems[x][y].Y = binaryReader.ReadByte();
staticItems[x][y].Z = binaryReader.ReadSByte();
staticItems[x][y].Hue = binaryReader.ReadUInt16();
}
}
}
}
return staticItems;
}
public static StaticTile[] GetStatics( int map, int x, int y )
{
if ( _staticData?[map]?.Value == null )
{
return null;
}
int blockIndex = x / 8 * ( _defaultMapSize[map, 1] / 8 ) + y / 8;
int cellX = x % 8;
int cellY = y % 8;
StaticRecord[] blockStatics = _staticData[map].Value[blockIndex];
if ( blockStatics == null )
{
return null;
}
IEnumerable<StaticRecord> statics = blockStatics.Where( i => i.X == cellX && i.Y == cellY );
List<StaticTile> tiles = new List<StaticTile>();
foreach ( StaticRecord staticRecord in statics )
{
StaticTile tile = TileData.GetStaticTile( staticRecord.ID );
tile.X = x;
tile.Y = y;
tile.Z = staticRecord.Z;
tile.Hue = staticRecord.Hue;
tiles.Add( tile );
}
return tiles.ToArray();
}
private struct StaticRecord
{
public ushort ID { get; set; }
public byte X { get; set; }
public byte Y { get; set; }
public sbyte Z { get; set; }
public int Hue { get; set; }
}
}
}
+186
View File
@@ -0,0 +1,186 @@
using System;
using System.IO;
using System.Text;
namespace ClassicAssist.UO.Data
{
public static class TileData
{
private static string _dataPath;
private static readonly Lazy<LandTile[]> _landTiles = new Lazy<LandTile[]>( LoadLandTiles );
private static readonly Lazy<StaticTile[]> _staticTiles = new Lazy<StaticTile[]>( LoadStaticTiles );
private static bool _oldFormat;
public static void Initialize( string dataPath )
{
_dataPath = dataPath;
}
private static StaticTile[] LoadStaticTiles()
{
string fileName = Path.Combine( _dataPath, "tiledata.mul" );
if ( !File.Exists( fileName ) )
{
throw new FileNotFoundException( "File not found.", fileName );
}
byte[] fileBytes = File.ReadAllBytes( fileName );
StaticTile[] staticTiles = new StaticTile[( fileBytes.Length - 428032 ) / 1188 * 32];
MemoryStream ms = new MemoryStream( fileBytes, false );
using ( BinaryReader reader = new BinaryReader( ms ) )
{
ms.Seek( 36, SeekOrigin.Begin );
string name = Encoding.ASCII.GetString( reader.ReadBytes( 20 ) ).TrimEnd( '\0' );
if ( name == "VOID!!!!!!" )
{
_oldFormat = true;
}
if ( _oldFormat )
{
const int offset = 428032;
ms.Seek( offset, SeekOrigin.Begin );
for ( int i = 0; i < 0x4000; ++i )
{
if ( ( i & 0x1F ) == 0 )
{
reader.ReadInt32(); // header
}
staticTiles[i].ID = (ushort) i;
staticTiles[i].Flags = (TileFlags) reader.ReadInt32();
staticTiles[i].Weight = reader.ReadByte();
staticTiles[i].Quality = reader.ReadByte();
reader.ReadInt16();
reader.ReadByte();
staticTiles[i].Quantity = reader.ReadByte();
reader.ReadInt16();
reader.ReadByte();
reader.ReadByte();
reader.ReadInt16();
staticTiles[i].Height = reader.ReadByte();
staticTiles[i].Name = Encoding.ASCII.GetString( reader.ReadBytes( 20 ) ).TrimEnd( '\0' );
}
}
else
{
const int offset = 493568;
ms.Seek( offset, SeekOrigin.Begin );
for ( int i = 0; i < 0x10000; ++i )
{
if ( ( i & 0x1F ) == 0 )
{
reader.ReadInt32(); // header
}
staticTiles[i].ID = (ushort) i;
staticTiles[i].Flags = (TileFlags) reader.ReadInt64();
staticTiles[i].Weight = reader.ReadByte();
staticTiles[i].Quality = reader.ReadByte();
reader.ReadInt16();
reader.ReadByte();
staticTiles[i].Quantity = reader.ReadByte();
reader.ReadInt32();
reader.ReadByte();
/*int value = */
reader.ReadByte();
staticTiles[i].Height = reader.ReadByte();
staticTiles[i].Name = Encoding.ASCII.GetString( reader.ReadBytes( 20 ) ).TrimEnd( '\0' );
}
}
}
ms.Close();
return staticTiles;
}
private static LandTile[] LoadLandTiles()
{
string fileName = Path.Combine( _dataPath, "tiledata.mul" );
if ( !File.Exists( fileName ) )
{
throw new FileNotFoundException( "File not found.", fileName );
}
byte[] fileBytes = File.ReadAllBytes( fileName );
LandTile[] landTiles = new LandTile[16384];
MemoryStream ms = new MemoryStream( fileBytes, false );
using ( BinaryReader bin = new BinaryReader( ms ) )
{
ms.Seek( 36, SeekOrigin.Begin );
string name = Encoding.ASCII.GetString( bin.ReadBytes( 20 ) ).TrimEnd( '\0' );
if ( name == "VOID!!!!!!" )
{
_oldFormat = true;
}
ms.Seek( 0, SeekOrigin.Begin );
if ( _oldFormat )
{
for ( int i = 0; i < 0x4000; ++i )
{
if ( i == 0 || i > 0 && ( i & 0x1f ) == 0 )
{
bin.ReadInt32(); // block header
}
landTiles[i].Flags = (TileFlags) bin.ReadInt32();
landTiles[i].ID = bin.ReadInt16();
landTiles[i].Name = Encoding.ASCII.GetString( bin.ReadBytes( 20 ) ).TrimEnd( '\0' );
}
}
else
{
for ( int i = 0; i < 0x4000; ++i )
{
if ( i == 1 || i > 0 && ( i & 0x1f ) == 0 )
{
bin.ReadInt32(); // block header
}
landTiles[i].Flags = (TileFlags) bin.ReadInt64();
landTiles[i].ID = bin.ReadInt16();
landTiles[i].Name = Encoding.ASCII.GetString( bin.ReadBytes( 20 ) ).TrimEnd( '\0' );
}
}
}
ms.Close();
return landTiles;
}
public static LandTile GetLandTile( int index )
{
return _landTiles.Value[index];
}
public static StaticTile GetStaticTile( int index )
{
try
{
return _staticTiles.Value[index];
}
catch ( IndexOutOfRangeException )
{
return _staticTiles.Value[0];
}
}
}
}
+41
View File
@@ -0,0 +1,41 @@
using System;
namespace ClassicAssist.UO.Data
{
[Flags]
public enum TileFlags : ulong
{
Background = 0x00000001,
Weapon = 0x00000002,
Transparent = 0x00000004,
Translucent = 0x00000008,
Wall = 0x00000010,
Damaging = 0x00000020,
Impassable = 0x00000040,
Wet = 0x00000080,
Unknown = 0x00000100,
Surface = 0x00000200,
Bridge = 0x00000400,
Stackable = 0x00000800,
Window = 0x00001000,
NoShoot = 0x00002000,
PrefixA = 0x00004000,
PrefixAn = 0x00008000,
Internal = 0x00010000,
Foliage = 0x00020000,
PartialHue = 0x00040000,
Unknown1 = 0x00080000,
Map = 0x00100000,
Container = 0x00200000,
Wearable = 0x00400000,
LightSource = 0x00800000,
Animated = 0x01000000,
NoDiagonal = 0x02000000,
Unknown2 = 0x04000000,
Armor = 0x08000000,
Roof = 0x10000000,
Door = 0x20000000,
StairBack = 0x40000000,
StairRight = 0x80000000
}
}
+160
View File
@@ -0,0 +1,160 @@
using System;
using System.Collections.Generic;
using System.IO;
namespace ClassicAssist.UO.Data
{
public class UOPIndex : IDisposable
{
private readonly UOPEntry[] _entries;
private readonly int _length;
private readonly BinaryReader _reader;
public UOPIndex( Stream stream )
{
_reader = new BinaryReader( stream );
_length = (int) stream.Length;
if ( _reader.ReadInt32() != 0x50594D )
{
throw new ArgumentException( "Invalid UOP file." );
}
Version = _reader.ReadInt32();
_reader.ReadInt32();
int nextTable = _reader.ReadInt32();
List<UOPEntry> entries = new List<UOPEntry>();
do
{
stream.Seek( nextTable, SeekOrigin.Begin );
int count = _reader.ReadInt32();
nextTable = _reader.ReadInt32();
_reader.ReadInt32();
for ( int i = 0; i < count; ++i )
{
int offset = _reader.ReadInt32();
if ( offset == 0 )
{
stream.Seek( 30, SeekOrigin.Current );
continue;
}
_reader.ReadInt64();
int length = _reader.ReadInt32();
entries.Add( new UOPEntry( offset, length ) );
stream.Seek( 18, SeekOrigin.Current );
}
}
while ( nextTable != 0 && nextTable < _length );
entries.Sort( OffsetComparer.Instance );
foreach ( UOPEntry t in entries )
{
stream.Seek( t.Offset + 2, SeekOrigin.Begin );
int dataOffset = _reader.ReadInt16();
t.Offset += 4 + dataOffset;
stream.Seek( dataOffset, SeekOrigin.Current );
t.Order = _reader.ReadInt32();
}
entries.Sort();
_entries = entries.ToArray();
}
public int Version { get; }
public int Lookup( int offset )
{
int total = 0;
foreach ( UOPEntry t in _entries )
{
int newTotal = total + t.Length;
if ( offset < newTotal )
{
return t.Offset + ( offset - total );
}
total = newTotal;
}
return _length;
}
public void Close()
{
_reader.Close();
}
private class OffsetComparer : IComparer<UOPEntry>
{
public static readonly IComparer<UOPEntry> Instance = new OffsetComparer();
public int Compare( UOPEntry x, UOPEntry y )
{
if ( x == null || y == null )
{
return -1;
}
return x.Offset.CompareTo( y.Offset );
}
}
private class UOPEntry : IComparable<UOPEntry>
{
public readonly int Length;
public int Offset;
public int Order;
public UOPEntry( int offset, int length )
{
Offset = offset;
Length = length;
Order = 0;
}
public int CompareTo( UOPEntry other )
{
return Order.CompareTo( other.Order );
}
}
#region IDisposable Support
private bool disposedValue; // To detect redundant calls
protected virtual void Dispose( bool disposing )
{
if ( disposedValue )
{
return;
}
if ( disposing )
{
_reader.Dispose();
}
disposedValue = true;
}
public void Dispose()
{
Dispose( true );
GC.SuppressFinalize( this );
}
#endregion
}
}
@@ -0,0 +1,211 @@
#region License
// Copyright (C) 2020 Reetus
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program. If not, see <https://www.gnu.org/licenses/>.
#endregion
using System;
using System.Collections.Generic;
using System.Threading;
using System.Threading.Tasks;
using ClassicAssist.Shared;
using ClassicAssist.Data;
using ClassicAssist.Data.Macros;
using ClassicAssist.Misc;
using ClassicAssist.Shared.Resources;
using ClassicAssist.Shared.UO;
using ClassicAssist.UO.Network.Packets;
using ClassicAssist.UO.Objects;
namespace ClassicAssist.UO.Network
{
public static class ActionPacketQueue
{
private const int DRAG_DROP_DISTANCE = 3;
private static readonly ThreadPriorityQueue<BaseQueueItem> _actionPacketQueue =
new ThreadPriorityQueue<BaseQueueItem>( ProcessActionPacketQueue );
private static readonly object _actionPacketQueueLock = new object();
private static void ProcessActionPacketQueue( BaseQueueItem queueItem )
{
// Lock here so wait don't set the WaitHandle before we've called ToTask(?)
lock ( _actionPacketQueueLock )
{
if ( queueItem is PacketQueueItem actionQueueItem )
{
if ( actionQueueItem.DelaySend )
{
while ( Engine.LastActionPacket +
TimeSpan.FromMilliseconds( Options.CurrentOptions.ActionDelayMS ) > DateTime.Now )
{
Thread.Sleep( 1 );
}
}
byte[] data = actionQueueItem.Packet;
int length = actionQueueItem.Length;
Engine.LastActionPacket = DateTime.Now;
Engine.SendPacketToServer( data, length );
actionQueueItem.WaitHandle.Set();
}
else if ( queueItem is ActionQueueItem actionItem )
{
if ( actionItem.DelaySend )
{
while ( Engine.LastActionPacket +
TimeSpan.FromMilliseconds( Options.CurrentOptions.ActionDelayMS ) > DateTime.Now )
{
Thread.Sleep( 1 );
}
}
bool? result = actionItem.Action?.Invoke( actionItem.CheckRange );
if ( result.HasValue && result.Value )
{
Engine.LastActionPacket = DateTime.Now;
}
actionItem.WaitHandle.Set();
}
}
}
// ReSharper disable once UnusedMember.Global
public static Task EnqueueActionPacket( byte[] packet, int length, QueuePriority priority, bool delaySend )
{
return EnqueueActionPacket( new PacketQueueItem( packet, length, delaySend ), priority );
}
public static Task EnqueueActionPacket( BasePacket packet, QueuePriority priority = QueuePriority.Low,
bool delaySend = true )
{
byte[] data = packet.ToArray();
return EnqueueActionPacket( new PacketQueueItem( data, data.Length, delaySend ), priority );
}
public static Task EnqueueActionPacket( PacketQueueItem packetQueueItem, QueuePriority priority )
{
lock ( _actionPacketQueueLock )
{
_actionPacketQueue.Enqueue( packetQueueItem, priority );
return packetQueueItem.WaitHandle.ToTask();
}
}
public static Task EnqueueDragDropGround( int serial, int amount, int x, int y, int z,
QueuePriority priority = QueuePriority.Low, bool delaySend = true )
{
lock ( _actionPacketQueueLock )
{
byte[] dragPacket = new DragItem( serial, amount ).ToArray();
byte[] dropItem = new DropItem( serial, -1, x, y, z ).ToArray();
PacketQueueItem dragPacketQueueItem = new PacketQueueItem( dragPacket, dragPacket.Length, delaySend );
_actionPacketQueue.Enqueue( dragPacketQueueItem, priority );
PacketQueueItem dropPacketQueueItem = new PacketQueueItem( dropItem, dropItem.Length, delaySend );
_actionPacketQueue.Enqueue( dropPacketQueueItem, priority );
return new[] { dragPacketQueueItem.WaitHandle, dropPacketQueueItem.WaitHandle }.ToTask();
}
}
public static Task EnqueueActionPackets( IEnumerable<BasePacket> packets,
QueuePriority priority = QueuePriority.Low, bool delaySend = true )
{
lock ( _actionPacketQueueLock )
{
List<EventWaitHandle> handles = new List<EventWaitHandle>();
foreach ( BasePacket packet in packets )
{
byte[] data = packet.ToArray();
PacketQueueItem packetQueueItem = new PacketQueueItem( data, data.Length, delaySend );
handles.Add( packetQueueItem.WaitHandle );
_actionPacketQueue.Enqueue( packetQueueItem, priority );
}
return handles.ToTask();
}
}
public static Task EnqueueDragDrop( int serial, int amount, int containerSerial,
QueuePriority priority = QueuePriority.Low, bool checkRange = false, bool checkExisting = false, bool delaySend = true, int x = -1,
int y = -1 )
{
lock ( _actionPacketQueueLock )
{
if ( checkExisting && _actionPacketQueue.Contains( e => e is ActionQueueItem aqi && aqi.Serial == serial ) )
{
return Task.CompletedTask;
}
ActionQueueItem actionQueueItem = new ActionQueueItem( check =>
{
if ( check )
{
Item item = Engine.Items.GetItem( serial );
if ( item == null || item.Distance >= DRAG_DROP_DISTANCE )
{
if ( !MacroManager.QuietMode )
{
Commands.SystemMessage( Strings.Item_out_of_range___ );
}
return false;
}
}
Engine.SendPacketToServer( new DragItem( serial, amount ) );
Thread.Sleep( 50 );
Engine.SendPacketToServer( new DropItem( serial, containerSerial, x, y, 0 ) );
return true;
} ) { CheckRange = checkRange, DelaySend = delaySend, Serial = serial };
_actionPacketQueue.Enqueue( actionQueueItem, priority );
return actionQueueItem.WaitHandle.ToTask();
}
}
public static int Count()
{
lock ( _actionPacketQueueLock )
{
return _actionPacketQueue.Count();
}
}
public static void Clear()
{
lock ( _actionPacketQueueLock )
{
_actionPacketQueue.Clear();
}
}
}
}
@@ -0,0 +1,60 @@
#region License
// Copyright (C) 2020 Reetus
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program. If not, see <https://www.gnu.org/licenses/>.
#endregion
using System;
using System.Threading;
namespace ClassicAssist.UO.Network
{
public abstract class BaseQueueItem
{
protected BaseQueueItem()
{
WaitHandle = new AutoResetEvent( false );
}
public bool DelaySend { get; set; }
public EventWaitHandle WaitHandle { get; set; }
}
public class PacketQueueItem : BaseQueueItem
{
public PacketQueueItem( byte[] packet, int length, bool delaySend )
{
Packet = packet;
Length = length;
DelaySend = delaySend;
}
public int Length { get; set; }
public byte[] Packet { get; set; }
}
public class ActionQueueItem : BaseQueueItem
{
public ActionQueueItem( Func<bool, bool> action )
{
Action = action;
}
public Func<bool, bool> Action { get; set; }
public int Serial { get; set; }
public bool CheckRange { get; set; }
}
}
@@ -0,0 +1,311 @@
using System;
using System.Collections.Generic;
using System.Linq;
using ClassicAssist.Shared;
using ClassicAssist.Data;
using ClassicAssist.Data.Filters;
using ClassicAssist.UO.Data;
using ClassicAssist.UO.Network.PacketFilter;
using ClassicAssist.UO.Objects;
namespace ClassicAssist.UO.Network
{
public static class IncomingPacketFilters
{
public delegate bool OnReceive( ref byte[] packet, ref int length );
private static readonly Dictionary<byte, OnReceive> _filters = new Dictionary<byte, OnReceive>();
public static void Initialize()
{
Register( 0x1C, OnASCIIMessage );
Register( 0x20, OnMobileUpdate );
Register( 0x77, OnMobileMoving );
Register( 0x78, OnMobileIncoming );
Register( 0xC1, OnLocalizedMessage );
Register( 0xCC, OnLocalizedMessageAffix );
Register( 0xF3, OnSAWorldItem );
}
private static bool OnASCIIMessage( ref byte[] packet, ref int length )
{
PacketReader reader = new PacketReader( packet, length, false );
JournalEntry journalEntry = new JournalEntry
{
Serial = reader.ReadInt32(),
ID = reader.ReadInt16(),
SpeechType = (JournalSpeech) reader.ReadByte(),
SpeechHue = reader.ReadInt16(),
SpeechFont = reader.ReadInt16(),
Name = reader.ReadString( 30 ),
Text = reader.ReadString()
};
bool block = RepeatedMessagesFilter.CheckMessage( journalEntry );
if ( block && RepeatedMessagesFilter.FilterOptions.SendToJournal )
{
IncomingPacketHandlers.AddToJournal( journalEntry );
}
return block;
}
private static bool OnMobileUpdate( ref byte[] packet, ref int length )
{
PacketReader reader = new PacketReader( packet, length, true );
int serial = reader.ReadInt32();
int id = reader.ReadInt16();
reader.ReadByte(); // BYTE 0x00;
int hue = reader.ReadUInt16();
int status = reader.ReadByte();
int x = reader.ReadInt16();
int y = reader.ReadInt16();
reader.ReadInt16(); // WORD 0x00;
//TODO Removed & 0x07 to not strip running flag, think of better solution
int direction = reader.ReadByte();
int z = reader.ReadSByte();
Mobile mobile = new Mobile( serial )
{
ID = id,
Hue = hue,
Status = (MobileStatus) status,
X = x,
Y = y,
Direction = (Direction) direction,
Z = z
};
if ( !Engine.RehueList.CheckMobileUpdate( mobile ) )
{
return false;
}
Engine.IncomingQueue.Enqueue( new Packet( packet, length ) );
return true;
}
private static bool OnSAWorldItem( ref byte[] packet, ref int length )
{
if ( !Engine.RehueList.CheckSAWorldItem( ref packet, ref length ) )
{
return false;
}
// Will need to send to handler ourselves because we won't see packet again
PacketHandler handler = IncomingPacketHandlers.GetHandler( 0xF3 );
handler?.OnReceive( new PacketReader( packet, length, true ) );
return true;
}
private static bool OnMobileMoving( ref byte[] packet, ref int length )
{
PacketReader reader = new PacketReader( packet, length, true );
int serial = reader.ReadInt32();
int id = reader.ReadInt16();
int x = reader.ReadInt16();
int y = reader.ReadInt16();
int z = reader.ReadSByte();
//TODO Removed & 0x07 to not strip running flag, think of better solution
int direction = reader.ReadByte();
int hue = reader.ReadUInt16();
int status = reader.ReadByte();
int notoriety = reader.ReadByte();
Mobile mobile = new Mobile( serial )
{
ID = id,
X = x,
Y = y,
Z = z,
Direction = (Direction) direction,
Hue = hue,
Status = (MobileStatus) status,
Notoriety = (Notoriety) notoriety
};
if ( !Engine.RehueList.CheckMobileMoving( mobile ) )
{
return false;
}
Engine.IncomingQueue.Enqueue( new Packet( packet, length ) );
return true;
}
private static bool OnMobileIncoming( ref byte[] packet, ref int length )
{
PacketReader reader = new PacketReader( packet, length, false );
int serial = reader.ReadInt32();
ItemCollection container = new ItemCollection( serial );
Mobile mobile = serial == Engine.Player?.Serial ? Engine.Player : Engine.GetOrCreateMobile( serial );
mobile.ID = reader.ReadInt16();
mobile.X = reader.ReadInt16();
mobile.Y = reader.ReadInt16();
mobile.Z = reader.ReadSByte();
//TODO Removed & 0x07 to not strip running flag, think of better solution
mobile.Direction = (Direction) reader.ReadByte();
mobile.Hue = reader.ReadUInt16();
mobile.Status = (MobileStatus) reader.ReadByte();
mobile.Notoriety = (Notoriety) reader.ReadByte();
bool useNewIncoming = Engine.ClientVersion == null || Engine.ClientVersion >= new Version( 7, 0, 33, 1 );
for ( ;; )
{
int itemSerial = reader.ReadInt32();
if ( itemSerial == 0 )
{
break;
}
Item item = Engine.GetOrCreateItem( itemSerial );
item.Owner = serial;
item.ID = reader.ReadUInt16();
item.Layer = (Layer) reader.ReadByte();
if ( useNewIncoming )
{
item.Hue = reader.ReadUInt16();
}
else
{
if ( ( item.ID & 0x8000 ) != 0 )
{
item.ID ^= 0x8000;
item.Hue = reader.ReadUInt16();
}
}
container.Add( item );
}
mobile.Equipment.Clear();
mobile.Equipment.Add( container.GetItems() );
foreach ( Item item in container.GetItems() )
{
mobile.SetLayer( item.Layer, item.Serial );
}
if ( !Engine.RehueList.CheckMobileIncoming( mobile, container ) )
{
return false;
}
Engine.IncomingQueue.Enqueue( new Packet( packet, length ) );
return true;
}
private static bool OnLocalizedMessage( ref byte[] packet, ref int length )
{
PacketReader reader = new PacketReader( packet, length, false );
JournalEntry journalEntry = new JournalEntry
{
Serial = reader.ReadInt32(),
ID = reader.ReadInt16(),
SpeechType = (JournalSpeech) reader.ReadByte(),
SpeechHue = reader.ReadInt16(),
SpeechFont = reader.ReadInt16(),
Cliloc = reader.ReadInt32(),
Name = reader.ReadString( 30 ),
Arguments = reader.ReadUnicodeString( (int) reader.Size - 50 ).Split( '\t' )
};
journalEntry.Text = Cliloc.GetLocalString( journalEntry.Cliloc, journalEntry.Arguments );
bool block = RepeatedMessagesFilter.CheckMessage( journalEntry );
if ( block && RepeatedMessagesFilter.FilterOptions.SendToJournal )
{
IncomingPacketHandlers.AddToJournal( journalEntry );
}
return block || ClilocFilter.CheckMessage( journalEntry );
}
private static bool OnLocalizedMessageAffix( ref byte[] packet, ref int length )
{
PacketReader reader = new PacketReader( packet, length, false );
int serial = reader.ReadInt32();
int graphic = reader.ReadInt16();
JournalSpeech messageType = (JournalSpeech) reader.ReadByte();
int hue = reader.ReadInt16();
int font = reader.ReadInt16();
int cliloc = reader.ReadInt32();
MessageAffixType affixType = (MessageAffixType) reader.ReadByte();
string name = reader.ReadString( 30 );
string affix = reader.ReadString();
string[] arguments = reader.ReadUnicodeString()
.Split( new[] { '\t' }, StringSplitOptions.RemoveEmptyEntries );
JournalEntry journalEntry = new JournalEntry
{
Serial = serial,
ID = graphic,
SpeechType = messageType,
SpeechHue = hue,
SpeechFont = font,
Cliloc = cliloc,
Name = name,
Arguments = arguments
};
string text = Cliloc.GetLocalString( journalEntry.Cliloc, journalEntry.Arguments );
if ( affixType.HasFlag( MessageAffixType.Prepend ) )
{
journalEntry.Text = $"{affix}{text}";
}
else if ( affixType.HasFlag( MessageAffixType.Append ) )
{
journalEntry.Text = $"{text}{affix}";
}
bool block = RepeatedMessagesFilter.CheckMessage( journalEntry );
if ( block && RepeatedMessagesFilter.FilterOptions.SendToJournal )
{
IncomingPacketHandlers.AddToJournal( journalEntry );
}
return block || ClilocFilter.CheckMessageAffix( journalEntry, affixType, affix );
}
private static void Register( byte packetId, OnReceive action )
{
if ( !_filters.ContainsKey( packetId ) )
{
_filters.Add( packetId, action );
}
}
public static bool CheckPacket( ref byte[] data, ref int length )
{
if ( _filters.ContainsKey( data[0] ) && _filters.TryGetValue( data[0], out OnReceive onReceive ) )
{
return onReceive.Invoke( ref data, ref length );
}
foreach ( DynamicFilterEntry dynamicFilterEntry in DynamicFilterEntry.Filters.Where( e => e.Enabled ) )
{
bool result = dynamicFilterEntry.CheckPacket( ref data, ref length, PacketDirection.Incoming );
if ( result )
{
return true;
}
}
return false;
}
}
}
File diff suppressed because it is too large Load Diff
@@ -0,0 +1,144 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using ClassicAssist.Data;
using ClassicAssist.Data.Abilities;
using ClassicAssist.Shared.Resources;
using ClassicAssist.UO.Data;
using ClassicAssist.UO.Objects;
using UOC = ClassicAssist.Shared.UO.Commands;
namespace ClassicAssist.UO.Network
{
public static class OutgoingPacketFilters
{
public delegate bool OnReceive( ref byte[] packet, ref int length );
private static readonly Dictionary<byte, OnReceive> _filters = new Dictionary<byte, OnReceive>();
public static void Initialize()
{
Register( 0x05, OnAttackRequested );
Register( 0x06, OnUseRequest );
Register( 0x80, OnAccountLoginRequest );
Register( 0x91, OnGameServerLogin );
}
private static bool OnUseRequest( ref byte[] packet, ref int length )
{
if ( !Options.CurrentOptions.CheckHandsPotions )
{
return false;
}
int serial = ( packet[1] << 24 ) | ( packet[2] << 16 ) | ( packet[3] << 8 ) | packet[4];
return AbilitiesManager.GetInstance().CheckHands( serial );
}
private static bool OnGameServerLogin( ref byte[] packet, ref int length )
{
PacketReader reader = new PacketReader( packet, length, true );
_ = reader.ReadInt32();
string username = reader.ReadString( 30 );
string password = reader.ReadString( 30 );
if ( !AssistantOptions.SavePasswords )
{
return false;
}
if ( !AssistantOptions.SavedPasswords.ContainsKey( username ) ||
AssistantOptions.SavePasswordsOnlyBlank && !string.IsNullOrEmpty( password ) )
{
return false;
}
string storedPassword = AssistantOptions.SavedPasswords[username];
byte[] passwordBytes = Encoding.ASCII.GetBytes( storedPassword );
byte[] buffer = new byte[30];
Buffer.BlockCopy( passwordBytes, 0, buffer, 0, passwordBytes.Length );
Buffer.BlockCopy( buffer, 0, packet, 35, buffer.Length );
return false;
}
private static bool OnAccountLoginRequest( ref byte[] packet, ref int packetLength )
{
PacketReader reader = new PacketReader( packet, packetLength, true );
string username = reader.ReadString( 30 );
string password = reader.ReadString( 30 );
if ( !AssistantOptions.SavePasswords )
{
return false;
}
if ( AssistantOptions.SavedPasswords.ContainsKey( username ) &&
( !AssistantOptions.SavePasswordsOnlyBlank || string.IsNullOrEmpty( password ) ) )
{
string storedPassword = AssistantOptions.SavedPasswords[username];
byte[] passwordBytes = Encoding.ASCII.GetBytes( storedPassword );
byte[] buffer = new byte[30];
Buffer.BlockCopy( passwordBytes, 0, buffer, 0, passwordBytes.Length );
Buffer.BlockCopy( buffer, 0, packet, 31, buffer.Length );
return false;
}
if ( string.IsNullOrEmpty( password ) )
{
return false;
}
if ( AssistantOptions.SavedPasswords.ContainsKey( username ) )
{
AssistantOptions.SavedPasswords.Remove( username );
}
AssistantOptions.SavedPasswords.Add( username, password );
AssistantOptions.OnPasswordsChanged();
return false;
}
private static bool OnAttackRequested( ref byte[] packet, ref int length )
{
int serial = ( packet[1] << 24 ) | ( packet[2] << 16 ) | ( packet[3] << 8 ) | packet[4];
bool block = Options.CurrentOptions.PreventAttackingFriendsInWarMode &&
Options.CurrentOptions.Friends.Any( fe => fe.Serial == serial );
if ( block )
{
UOC.SystemMessage( Strings.Attack_request_blocked___ );
}
return block;
}
private static void Register( byte packetId, OnReceive action )
{
if ( !_filters.ContainsKey( packetId ) )
{
_filters.Add( packetId, action );
}
}
public static bool CheckPacket( ref byte[] data, ref int length )
{
if ( _filters.ContainsKey( data[0] ) && _filters.TryGetValue( data[0], out OnReceive onReceive ) )
{
return onReceive.Invoke( ref data, ref length );
}
return false;
}
}
}
@@ -0,0 +1,245 @@
using System;
using System.IO;
using System.Linq;
using ClassicAssist.Shared;
using ClassicAssist.Data.Abilities;
using ClassicAssist.Data.Counters;
using ClassicAssist.Data.Macros.Commands;
using ClassicAssist.Data.Targeting;
using ClassicAssist.UO.Data;
using ClassicAssist.UO.Objects;
using ClassicAssist.UO.Objects.Gumps;
namespace ClassicAssist.UO.Network
{
public static class OutgoingPacketHandlers
{
public delegate void dGump( uint gumpId, int serial, Gump gump );
public delegate void dMenuClick( int serial, int gumpId, int index, int id, int hue );
public delegate void dTargetSentEvent( TargetType targetType, int senderSerial, int flags, int serial, int x,
int y, int z, int id );
private static PacketHandler[] _handlers;
private static PacketHandler[] _extendedHandlers;
public static event dTargetSentEvent TargetSentEvent;
public static event dGump GumpEvent;
public static event dMenuClick MenuClickedEvent;
public static void Initialize()
{
_handlers = new PacketHandler[0x100];
_extendedHandlers = new PacketHandler[0x100];
Register( 0x02, 7, OnMoveRequested );
Register( 0x06, 5, OnUseRequest );
Register( 0x07, 7, OnLiftRequest );
Register( 0x08, 15, OnDropRequest );
Register( 0x13, 10, OnEquipRequest );
Register( 0x6C, 19, OnTargetSent );
Register( 0x7D, 13, OnMenuResponse );
Register( 0xA0, 3, OnPlayServer );
Register( 0xB1, 0, OnGumpButtonPressed );
Register( 0xBD, 0, OnClientVersion );
Register( 0xBF, 0, OnExtendedCommand );
Register( 0xD7, 0, OnEncodedCommand );
Register( 0xEF, 31, OnNewClientVersion );
RegisterExtended( 0x1C, 0, OnSpellCast );
}
private static void OnSpellCast( PacketReader reader )
{
int type = reader.ReadInt16();
if ( type == 0 )
{
reader.Seek( 4, SeekOrigin.Current );
}
Engine.LastSpellID = reader.ReadInt16();
}
private static void OnExtendedCommand( PacketReader reader )
{
int command = reader.ReadInt16();
PacketHandler handler = GetExtendedHandler( command );
handler?.OnReceive( reader );
}
private static void OnMenuResponse( PacketReader reader )
{
int serial = reader.ReadInt32();
int gumpId = reader.ReadInt16();
int index = reader.ReadInt16();
int id = reader.ReadInt16();
int hue = reader.ReadInt16();
Engine.Menus.Remove( gumpId );
MenuClickedEvent?.Invoke( serial, gumpId, index, id, hue );
}
private static void OnPlayServer( PacketReader reader )
{
int index = reader.ReadInt16();
Engine.CurrentShard = Engine.Shards.FirstOrDefault( i => i.Index == index );
}
private static void OnClientVersion( PacketReader reader )
{
string version = reader.ReadString();
Engine.ClientVersion = Version.Parse( version );
}
private static void OnEquipRequest( PacketReader reader )
{
AbilitiesManager manager = AbilitiesManager.GetInstance();
manager.ResendGump( manager.Enabled );
}
private static void OnLiftRequest( PacketReader reader )
{
Engine.LastActionPacket = DateTime.Now;
Engine.Player.Holding = reader.ReadInt32();
Engine.Player.HoldingAmount = reader.ReadUInt16();
CountersManager.GetInstance().RecountAll?.Invoke();
}
private static void OnDropRequest( PacketReader reader )
{
Engine.LastActionPacket = DateTime.Now;
}
private static void OnEncodedCommand( PacketReader reader )
{
int serial = reader.ReadInt32();
int command = reader.ReadInt16();
switch ( command )
{
case 0x19:
{
reader.ReadByte();
int abilityIndex = reader.ReadInt32();
AbilitiesManager.GetInstance().CheckAbility( abilityIndex );
break;
}
}
}
private static void OnUseRequest( PacketReader reader )
{
Engine.LastActionPacket = DateTime.Now;
int serial = reader.ReadInt32();
if ( Engine.Player != null )
{
Engine.Player.LastObjectSerial = serial;
}
}
private static void OnMoveRequested( PacketReader reader )
{
Direction direction = (Direction) ( reader.ReadByte() & 0x07 );
int sequence = reader.ReadByte();
Engine.SetSequence( sequence, direction );
}
private static void OnGumpButtonPressed( PacketReader reader )
{
int senderSerial = reader.ReadInt32();
uint gumpId = reader.ReadUInt32();
int buttonId = reader.ReadInt32();
Engine.GumpList.TryRemove( gumpId, out _ );
if ( Engine.Gumps.GetGump( gumpId, out Gump gump ) )
{
GumpEvent?.Invoke( gumpId, senderSerial, gump );
}
Engine.Gumps.Remove( gumpId, buttonId );
}
private static void OnTargetSent( PacketReader reader )
{
if ( Engine.Player == null )
{
return;
}
TargetType targetType = (TargetType) reader.ReadByte();
Engine.Player.LastTargetType = targetType;
int senderSerial = reader.ReadInt32(); // sender serial
int flags = reader.ReadByte();
int serial = reader.ReadInt32();
int x = reader.ReadInt16();
int y = reader.ReadInt16();
int z = reader.ReadInt16();
int id = reader.ReadInt16();
if ( targetType == TargetType.Object && flags != 0x03 && serial != 0 )
{
Engine.Player.LastTargetSerial = serial;
Engine.Player.LastTargetType = targetType;
switch ( (TargetFlags) flags )
{
case TargetFlags.Harmful when Engine.Mobiles.GetMobile( serial, out Mobile enemyMobile ) &&
AliasCommands.GetAlias( "enemy" ) != serial:
TargetManager.GetInstance().SetEnemy( enemyMobile );
break;
case TargetFlags.Beneficial when Engine.Mobiles.GetMobile( serial, out Mobile friendMobile ) &&
MobileCommands.InFriendList( serial ) &&
AliasCommands.GetAlias( "friend" ) != serial:
TargetManager.GetInstance().SetFriend( friendMobile );
break;
}
}
Engine.TargetExists = false;
TargetSentEvent?.Invoke( targetType, senderSerial, flags, serial, x, y, z, id );
}
private static void OnNewClientVersion( PacketReader reader )
{
reader.ReadInt32();
Engine.ClientVersion = new Version( reader.ReadInt32(), reader.ReadInt32(), reader.ReadInt32(),
reader.ReadInt32() );
}
private static void Register( int packetId, int length, OnPacketReceive onReceive )
{
_handlers[packetId] = new PacketHandler( packetId, length, onReceive );
}
private static void RegisterExtended( int packetId, int length, OnPacketReceive onReceive )
{
_extendedHandlers[packetId] = new PacketHandler( packetId, length, onReceive );
}
internal static PacketHandler GetHandler( int packetId )
{
return _handlers[packetId];
}
private static PacketHandler GetExtendedHandler( int packetId )
{
return _extendedHandlers[packetId];
}
}
}
@@ -0,0 +1,216 @@
using System;
using System.Collections.Generic;
using System.Linq;
namespace ClassicAssist.UO.Network.PacketFilter
{
public class PacketFilter
{
private List<PacketFilterInfo> _filters = new List<PacketFilterInfo>();
public void Add( byte packet, PacketFilterCondition[] constraints )
{
Add( new PacketFilterInfo( packet, constraints ) );
}
public void Add( PacketFilterInfo pfi )
{
if ( _filters == null )
{
_filters = new List<PacketFilterInfo>();
}
_filters.Add( pfi );
}
public void Remove( PacketFilterInfo pfi )
{
if ( _filters == null )
{
_filters = new List<PacketFilterInfo>();
}
if ( _filters.Contains( pfi ) )
{
_filters.Remove( pfi );
}
}
public bool Remove( byte packet, PacketFilterCondition[] constraints )
{
if ( _filters == null )
{
_filters = new List<PacketFilterInfo>();
}
for ( int i = 0; i < _filters.Count; i++ )
{
if ( _filters[i].PacketID != packet )
{
continue;
}
if ( constraints == null && _filters[i].GetConditions() == null )
{
_filters.Remove( _filters[i] );
return true;
}
bool remove = true;
if ( constraints == null || _filters[i].GetConditions() == null ||
_filters[i].GetConditions().Length != constraints.Length )
{
continue;
}
for ( int x = 0; x < _filters[i].GetConditions().Length; x++ )
{
if ( !_filters[i].GetConditions()[x].Equals( constraints[x] ) )
{
remove = false;
}
}
if ( !remove )
{
continue;
}
_filters.Remove( _filters[i] );
return true;
}
return false;
}
public bool Contains( PacketFilterInfo pfi )
{
return _filters != null && _filters.Contains( pfi );
}
public int Count()
{
return _filters?.Count ?? 0;
}
public void Clear()
{
_filters?.Clear();
}
public bool MatchFilter( byte[] packet )
{
return MatchFilter( packet, out PacketFilterInfo _ );
}
public bool MatchFilter( byte[] packet, out PacketFilterInfo pfi )
{
pfi = null;
if ( _filters == null )
{
return false;
}
bool result = false;
for ( int i = 0; i < _filters.Count; i++ )
{
if ( packet[0] == _filters[i].PacketID )
{
if ( _filters[i].GetConditions() == null )
{
// No condition so just match packetid
result = true;
pfi = _filters[i];
}
else
{
foreach ( PacketFilterCondition fc in _filters[i].GetConditions() )
{
if ( fc.Position + fc.Length > packet.Length )
{
result = false;
continue;
}
byte[] tmp = new byte[fc.Length];
Buffer.BlockCopy( packet, fc.Position, tmp, 0, fc.Length );
if ( !tmp.SequenceEqual( fc.GetBytes() ) )
{
result = false;
break;
}
result = true;
pfi = _filters?[i] ?? null;
}
}
}
if ( result )
{
break;
}
}
if ( !result )
{
pfi = null;
}
return result;
}
public int MatchFilterAll( byte[] packet, out PacketFilterInfo[] pfis )
{
List<PacketFilterInfo> pfiList = new List<PacketFilterInfo>();
pfis = null;
if ( _filters == null )
{
return 0;
}
for ( int i = 0; i < _filters.Count; i++ )
{
if ( packet[0] != _filters[i].PacketID )
{
continue;
}
if ( _filters[i].GetConditions() == null )
{
// No condition so just match packetid
pfiList.Add( _filters[i] );
}
else
{
foreach ( PacketFilterCondition fc in _filters[i].GetConditions() )
{
if ( fc.Position + fc.Length > packet.Length )
{
continue;
}
byte[] tmp = new byte[fc.Length];
Buffer.BlockCopy( packet, fc.Position, tmp, 0, fc.Length );
if ( !tmp.SequenceEqual( fc.GetBytes() ) )
{
break;
}
pfiList.Add( _filters[i] );
}
}
}
pfis = pfiList.ToArray();
return pfiList.Count;
}
}
}
@@ -0,0 +1,45 @@
using System.Collections.Generic;
using System.Linq;
namespace ClassicAssist.UO.Network.PacketFilter
{
public class PacketFilterCondition
{
private readonly byte[] _bytes;
public PacketFilterCondition( int position, byte[] bytes, int length )
{
Position = position;
_bytes = bytes;
Length = length;
}
public int Length { get; }
public int Position { get; }
public override bool Equals( object obj )
{
if ( !( obj is PacketFilterCondition pfc ) )
{
return false;
}
return Position == pfc.Position && Length == pfc.Length && _bytes.SequenceEqual( pfc.GetBytes() );
}
public byte[] GetBytes()
{
return _bytes;
}
public override int GetHashCode()
{
int hashCode = -285373213;
hashCode = hashCode * -1521134295 + Position.GetHashCode();
hashCode = hashCode * -1521134295 + EqualityComparer<byte[]>.Default.GetHashCode( _bytes );
hashCode = hashCode * -1521134295 + Length.GetHashCode();
return hashCode;
}
}
}
@@ -0,0 +1,33 @@
namespace ClassicAssist.UO.Network.PacketFilter
{
public class PacketFilterConditions
{
public static PacketFilterCondition IntAtPositionCondition( int value, int position )
{
byte[] valueBytes = { (byte) ( value >> 24 ), (byte) ( value >> 16 ), (byte) ( value >> 8 ), (byte) value };
return new PacketFilterCondition( position, valueBytes, 4 );
}
public static PacketFilterCondition UIntAtPositionCondition( uint value, int position )
{
byte[] valueBytes = { (byte) ( value >> 24 ), (byte) ( value >> 16 ), (byte) ( value >> 8 ), (byte) value };
return new PacketFilterCondition( position, valueBytes, 4 );
}
public static PacketFilterCondition ShortAtPositionCondition( int value, int position )
{
byte[] valueBytes = { (byte) ( value >> 8 ), (byte) value };
return new PacketFilterCondition( position, valueBytes, 2 );
}
public static PacketFilterCondition ByteAtPositionCondition( int value, int position )
{
byte[] valueBytes = { (byte) value };
return new PacketFilterCondition( position, valueBytes, 1 );
}
}
}
@@ -0,0 +1,102 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
namespace ClassicAssist.UO.Network.PacketFilter
{
public class PacketFilterInfo
{
private PacketFilterCondition[] _conditions;
public PacketFilterInfo( int packetId, Action<byte[], PacketFilterInfo> onMatch = null )
{
PacketID = packetId;
_conditions = null;
Action = onMatch;
}
public PacketFilterInfo( int packetid, PacketFilterCondition[] conditions,
Action<byte[], PacketFilterInfo> onMatch = null )
{
PacketID = packetid;
_conditions = conditions;
Action = onMatch;
}
public Action<byte[], PacketFilterInfo> Action { get; set; }
public int PacketID { get; set; }
public override bool Equals( object obj )
{
if ( !( obj is PacketFilterInfo pfi ) )
{
return false;
}
// Disabled because won't remove filter as Action == null in Remove methods.
//if ( Action != pfi.Action )
// return false;
if ( GetConditions() == null && pfi.GetConditions() != null )
{
return false;
}
if ( pfi.GetConditions() == null && GetConditions() != null )
{
return false;
}
if ( GetConditions() == null && pfi.GetConditions() == null )
{
return PacketID == pfi.PacketID;
}
return PacketID == pfi.PacketID && GetConditions().SequenceEqual( pfi.GetConditions() );
}
public PacketFilterCondition[] GetConditions()
{
return _conditions;
}
public override int GetHashCode()
{
int hashCode = -1985156662;
hashCode = hashCode * -1521134295 +
EqualityComparer<PacketFilterCondition[]>.Default.GetHashCode( _conditions );
hashCode = hashCode * -1521134295 + PacketID.GetHashCode();
hashCode = hashCode * -1521134295 +
EqualityComparer<Action<byte[], PacketFilterInfo>>.Default.GetHashCode( Action );
return hashCode;
}
public void SetConditions( PacketFilterCondition[] value )
{
_conditions = value;
}
public override string ToString()
{
StringBuilder sb = new StringBuilder();
sb.AppendFormat( "PacketID: 0x{0:X}\r\n", PacketID );
if ( _conditions == null )
{
sb.AppendLine( "No conditions." );
}
else
{
foreach ( PacketFilterCondition pfc in _conditions )
{
sb.Append( pfc );
}
}
return sb.ToString();
}
}
}
@@ -0,0 +1,163 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading;
namespace ClassicAssist.UO.Network.PacketFilter
{
public class PacketWaitEntries
{
public delegate void dWaitEntryAddedRemoved( PacketWaitEntry entry );
private readonly List<PacketWaitEntry> _waitEntries = new List<PacketWaitEntry>();
private readonly object _waitEntryLock = new object();
public event dWaitEntryAddedRemoved WaitEntryAddedEvent;
public event dWaitEntryAddedRemoved WaitEntryRemovedEvent;
public PacketWaitEntry Add( PacketFilterInfo pfi, PacketDirection direction, bool autoRemove = false,
bool filteredOnly = false )
{
PacketWaitEntry we = new PacketWaitEntry
{
PFI = pfi,
Lock = new AutoResetEvent( false ),
PacketDirection = direction,
AutoRemove = autoRemove,
MatchFilteredOnly = filteredOnly
};
lock ( _waitEntryLock )
{
_waitEntries.Add( we );
WaitEntryAddedEvent?.Invoke( we );
}
return we;
}
public bool CheckWait( byte[] packet, PacketDirection direction, bool filteredPacket = false )
{
lock ( _waitEntryLock )
{
if ( _waitEntries.Count == 0 )
{
return false;
}
}
List<PacketWaitEntry> matchedEntries = new List<PacketWaitEntry>();
lock ( _waitEntryLock )
{
foreach ( PacketWaitEntry t in _waitEntries.Where( t => packet[0] == t.PFI.PacketID ).Where( t =>
direction == t.PacketDirection &&
( filteredPacket ? t.MatchFilteredOnly : !t.MatchFilteredOnly ) ) )
{
if ( t.PFI.GetConditions() == null )
{
// No condition so just match packetid
matchedEntries.Add( t );
}
else
{
bool result = false;
foreach ( PacketFilterCondition fc in t.PFI.GetConditions() )
{
if ( fc.Position + fc.Length > packet.Length )
{
result = false;
break;
}
byte[] tmp = new byte[fc.Length];
Buffer.BlockCopy( packet, fc.Position, tmp, 0, fc.Length );
if ( !tmp.SequenceEqual( fc.GetBytes() ) )
{
result = false;
break;
}
result = true;
}
if ( result )
{
matchedEntries.Add( t );
}
}
}
}
if ( matchedEntries.Count == 0 )
{
return false;
}
List<PacketWaitEntry> removeList = new List<PacketWaitEntry>();
foreach ( PacketWaitEntry entry in matchedEntries )
{
entry.Packet = new byte[packet.Length];
Buffer.BlockCopy( packet, 0, entry.Packet, 0, packet.Length );
entry.Lock.Set();
entry.PFI.Action?.Invoke( packet, entry.PFI );
if ( entry.AutoRemove )
{
removeList.Add( entry );
}
}
removeList.ForEach( Remove );
return true;
}
public void Clear()
{
lock ( _waitEntryLock )
{
foreach ( PacketWaitEntry we in _waitEntries )
{
we.Lock.Set();
}
_waitEntries?.Clear();
}
}
public PacketWaitEntry[] GetEntries()
{
lock ( _waitEntryLock )
{
return _waitEntries?.ToArray();
}
}
public void Remove( PacketWaitEntry we )
{
lock ( _waitEntryLock )
{
_waitEntries.Remove( we );
WaitEntryRemovedEvent?.Invoke( we );
}
}
public void RemoveRange( IEnumerable<PacketWaitEntry> wes )
{
lock ( _waitEntryLock )
{
foreach ( PacketWaitEntry we in wes )
{
_waitEntries.Remove( we );
WaitEntryRemovedEvent?.Invoke( we );
}
}
}
}
}
@@ -0,0 +1,14 @@
using System.Threading;
namespace ClassicAssist.UO.Network.PacketFilter
{
public class PacketWaitEntry
{
public bool AutoRemove { get; set; }
public AutoResetEvent Lock { get; set; }
public bool MatchFilteredOnly { get; set; } = false;
public byte[] Packet { get; set; }
public PacketDirection PacketDirection { get; set; }
public PacketFilterInfo PFI { get; set; }
}
}
@@ -0,0 +1,20 @@
using ClassicAssist.UO.Data;
namespace ClassicAssist.UO.Network
{
internal delegate void OnPacketReceive( PacketReader reader );
internal class PacketHandler
{
public PacketHandler( int packetId, int length, OnPacketReceive onReceive )
{
PacketID = packetId;
Length = length;
OnReceive = onReceive;
}
public int Length { get; set; }
public int PacketID { get; set; }
internal OnPacketReceive OnReceive { get; set; }
}
}
@@ -0,0 +1,17 @@
using ClassicAssist.UO.Data;
namespace ClassicAssist.UO.Network.Packets
{
public class AcceptPartyInvitation : BasePacket
{
public AcceptPartyInvitation( int partyLeaderSerial )
{
_writer = new PacketWriter( 10 );
_writer.Write( (byte) 0xBF );
_writer.Write( (short) 10 );
_writer.Write( (short) 0x06 );
_writer.Write( (byte) 0x08 );
_writer.Write( partyLeaderSerial );
}
}
}
@@ -0,0 +1,14 @@
using ClassicAssist.UO.Data;
namespace ClassicAssist.UO.Network.Packets
{
public class AttackRequest : BasePacket
{
public AttackRequest( int serial )
{
_writer = new PacketWriter( 5 );
_writer.Write( (byte) 0x05 );
_writer.Write( serial );
}
}
}
@@ -0,0 +1,31 @@
using ClassicAssist.UO.Data;
using ClassicAssist.UO.Network.PacketFilter;
namespace ClassicAssist.UO.Network.Packets
{
public abstract class BasePacket
{
protected PacketWriter _writer;
protected BasePacket()
{
}
protected BasePacket( int length )
{
_writer = new PacketWriter( length );
}
protected BasePacket( PacketDirection direction )
{
Direction = direction;
}
public PacketDirection Direction { get; set; } = PacketDirection.Any;
public virtual byte[] ToArray()
{
return _writer?.ToArray();
}
}
}
@@ -0,0 +1,28 @@
using System.Collections.Generic;
using ClassicAssist.UO.Data;
namespace ClassicAssist.UO.Network.Packets
{
public class BatchQueryProperties : BasePacket
{
public BatchQueryProperties( int serial )
{
_writer = new PacketWriter();
_writer.Write( (byte) 0xD6 );
_writer.Write( (short) 7 );
_writer.Write( serial );
}
public BatchQueryProperties( IReadOnlyCollection<int> serials )
{
_writer = new PacketWriter();
_writer.Write( (byte) 0xD6 );
_writer.Write( (short) ( 3 + serials.Count * 4 ) );
foreach ( int serial in serials )
{
_writer.Write( serial );
}
}
}
}
@@ -0,0 +1,27 @@
using ClassicAssist.UO.Data;
namespace ClassicAssist.UO.Network.Packets
{
public class CancelTargetCursor : BasePacket
{
public CancelTargetCursor( uint senderSerial )
{
_writer = new PacketWriter( 19 );
_writer.Write( (byte) 0x6C );
_writer.Write( (byte) 0 );
_writer.Write( senderSerial );
_writer.Write( (byte) 3 );
_writer.Fill();
}
public CancelTargetCursor( int senderSerial )
{
_writer = new PacketWriter( 19 );
_writer.Write( (byte) 0x6C );
_writer.Write( (byte) 0 );
_writer.Write( senderSerial );
_writer.Write( (byte) 3 );
_writer.Fill();
}
}
}
@@ -0,0 +1,39 @@
using ClassicAssist.Data.Spells;
using ClassicAssist.UO.Data;
using ClassicAssist.UO.Network.PacketFilter;
namespace ClassicAssist.UO.Network.Packets
{
public class CastSpell : BasePacket, IMacroCommandParser
{
public CastSpell()
{
}
public CastSpell( int id )
{
_writer = new PacketWriter( 9 );
_writer.Write( (byte) 0xBF );
_writer.Write( (short) 9 );
_writer.Write( (short) 0x1C );
_writer.Write( (short) 0x02 );
_writer.Write( (short) id );
}
public string Parse( byte[] packet, int length, PacketDirection direction )
{
if ( packet[0] != 0xBF || packet[4] != 0x1C || direction != PacketDirection.Outgoing )
{
return null;
}
int spellId = ( packet[7] << 8 ) | packet[8];
SpellManager manager = SpellManager.GetInstance();
SpellData sd = manager.GetSpellData( spellId ) ?? manager.GetMasteryData( spellId );
return sd != null ? $"Cast(\"{sd.Name}\")\r\n" : null;
}
}
}
@@ -0,0 +1,15 @@
using ClassicAssist.UO.Data;
using ClassicAssist.UO.Network.PacketFilter;
namespace ClassicAssist.UO.Network.Packets
{
public class ChangeCombatant : BasePacket
{
public ChangeCombatant( int serial ) : base( PacketDirection.Incoming )
{
_writer = new PacketWriter( 5 );
_writer.Write( (byte) 0xAA );
_writer.Write( serial );
}
}
}
@@ -0,0 +1,18 @@
using ClassicAssist.Data.Skills;
using ClassicAssist.UO.Data;
namespace ClassicAssist.UO.Network.Packets
{
public class ChangeSkillLock : BasePacket
{
public ChangeSkillLock( SkillEntry skill, LockStatus lockStatus )
{
_writer = new PacketWriter( 6 );
_writer.Write( (byte) 0x3A );
_writer.Write( (short) 6 );
_writer.Write( (byte) 0 );
_writer.Write( (byte) skill.Skill.ID );
_writer.Write( (byte) lockStatus );
}
}
}
@@ -0,0 +1,54 @@
#region License
// Copyright (C) 2020 Reetus
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program. If not, see <https://www.gnu.org/licenses/>.
#endregion
using ClassicAssist.UO.Data;
using ClassicAssist.UO.Network.PacketFilter;
namespace ClassicAssist.UO.Network.Packets
{
public class ChangeStatLock : BasePacket, IMacroCommandParser
{
public ChangeStatLock()
{
}
public ChangeStatLock( StatType stat, LockStatus lockStatus )
{
_writer = new PacketWriter( 7 );
_writer.Write( (byte) 0xBF );
_writer.Write( (short) 7 );
_writer.Write( (short) 0x1A );
_writer.Write( (byte) stat );
_writer.Write( (byte) lockStatus );
}
public string Parse( byte[] packet, int length, PacketDirection direction )
{
if ( direction != PacketDirection.Outgoing || packet[0] != 0xBF || packet[4] != 0x1A )
{
return null;
}
StatType stat = (StatType) packet[5];
LockStatus lockStatus = (LockStatus) packet[6];
return $"SetStatus(\"{stat.ToString().ToLower()}\", \"{lockStatus.ToString().ToLower()}\")\r\n";
}
}
}
@@ -0,0 +1,16 @@
using ClassicAssist.UO.Data;
namespace ClassicAssist.UO.Network.Packets
{
public class ClearWeaponAbility : BasePacket
{
public ClearWeaponAbility()
{
_writer = new PacketWriter( 5 );
_writer.Write( (byte) 0xBF );
_writer.Write( (short) 5 );
_writer.Write( (short) 0x21 );
}
}
}
@@ -0,0 +1,17 @@
using ClassicAssist.UO.Data;
namespace ClassicAssist.UO.Network.Packets
{
public class CloseClientGump : BasePacket
{
public CloseClientGump( uint gumpID )
{
_writer = new PacketWriter( 13 );
_writer.Write( (byte) 0xBF );
_writer.Write( (short) 13 );
_writer.Write( (short) 0x04 );
_writer.Write( gumpID );
_writer.Fill();
}
}
}
@@ -0,0 +1,19 @@
using ClassicAssist.UO.Network.PacketFilter;
namespace ClassicAssist.UO.Network.Packets
{
public class CompressedGump : IMacroCommandParser
{
public string Parse( byte[] packet, int length, PacketDirection direction )
{
if ( packet[0] != 0xDD || direction != PacketDirection.Incoming )
{
return null;
}
int serial = ( packet[7] << 24 ) | ( packet[8] << 16 ) | ( packet[9] << 8 ) | packet[10];
return $"WaitForGump(0x{serial:x}, 5000)\r\n";
}
}
}
@@ -0,0 +1,33 @@
using System;
using ClassicAssist.Shared;
using ClassicAssist.UO.Data;
using ClassicAssist.UO.Network.PacketFilter;
namespace ClassicAssist.UO.Network.Packets
{
public class ContainerContentUpdate : BasePacket
{
public ContainerContentUpdate( int serial, int id, Direction direction, int amount, int x, int y, int grid,
int parentSerial, int hue ) : base( PacketDirection.Incoming )
{
bool isNew = Engine.ClientVersion == null || Engine.ClientVersion >= new Version( 6, 0, 1, 7 );
_writer = new PacketWriter( isNew ? 21 : 20 );
_writer.Write( (byte) 0x25 );
_writer.Write( serial );
_writer.Write( (short) id );
_writer.Write( (byte) direction );
_writer.Write( (short) amount );
_writer.Write( (short) x );
_writer.Write( (short) y );
if ( isNew )
{
_writer.Write( (byte) grid );
}
_writer.Write( parentSerial );
_writer.Write( (short) hue );
}
}
}
@@ -0,0 +1,30 @@
using ClassicAssist.UO.Data;
using ClassicAssist.UO.Objects;
namespace ClassicAssist.UO.Network.Packets
{
public class ContainerContentsPacket : BasePacket
{
public ContainerContentsPacket( ItemCollection collection )
{
_writer = new PacketWriter( 5 + collection.GetTotalItemCount() * 20 );
_writer.Write( (byte) 0x3C );
_writer.Write( (short) ( collection.GetTotalItemCount() * 20 ) );
_writer.Write( (short) collection.GetTotalItemCount() );
foreach ( Item item in collection.GetItems() )
{
_writer.Write( item.Serial );
_writer.Write( (short) item.ID );
_writer.Write( (byte) 0 ); // itemid offset
_writer.Write( (short) item.Count );
_writer.Write( (short) item.X );
_writer.Write( (short) item.Y );
_writer.Write( (byte) item.Grid );
_writer.Write( collection.Serial );
_writer.Write( (short) item.Hue );
}
}
}
}
@@ -0,0 +1,39 @@
using ClassicAssist.UO.Data;
using ClassicAssist.UO.Network.PacketFilter;
namespace ClassicAssist.UO.Network.Packets
{
public class ContextMenuClick : BasePacket, IMacroCommandParser
{
public ContextMenuClick()
{
}
public ContextMenuClick( int serial, int index )
{
_writer = new PacketWriter( 11 );
_writer.Write( (byte) 0xBF );
_writer.Write( (short) 11 );
_writer.Write( (short) 0x15 );
_writer.Write( serial );
_writer.Write( (short) index );
}
public string Parse( byte[] packet, int length, PacketDirection direction )
{
if ( packet[0] != 0xBF || packet[4] != 0x15 || direction != PacketDirection.Outgoing )
{
return null;
}
PacketReader reader = new PacketReader( packet, length, false );
reader.ReadInt16();
int serial = reader.ReadInt32();
int index = reader.ReadInt16();
return $"WaitForContext(0x{serial:x}, {index}, 5000)\r\n";
}
}
}
@@ -0,0 +1,16 @@
using ClassicAssist.UO.Data;
namespace ClassicAssist.UO.Network.Packets
{
public class ContextMenuRequest : BasePacket
{
public ContextMenuRequest( int serial )
{
_writer = new PacketWriter( 9 );
_writer.Write( (byte) 0xBF );
_writer.Write( (short) 9 );
_writer.Write( (short) 0x13 );
_writer.Write( serial );
}
}
}
@@ -0,0 +1,45 @@
#region License
// Copyright (C) 2020 Reetus
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program. If not, see <https://www.gnu.org/licenses/>.
#endregion
using ClassicAssist.UO.Data;
using ClassicAssist.UO.Network.PacketFilter;
namespace ClassicAssist.UO.Network.Packets
{
public class DisarmRequest : BasePacket, IMacroCommandParser
{
public DisarmRequest()
{
_writer = new PacketWriter( 5 );
_writer.Write( (byte) 0xBF );
_writer.Write( (short) 5 );
_writer.Write( (short) 0x09 );
}
public string Parse( byte[] packet, int length, PacketDirection direction )
{
if ( direction == PacketDirection.Outgoing && packet[0] == 0xBF && packet[4] == 0x09 )
{
return "SetAbility(\"Disarm\")\r\n";
}
return null;
}
}
}
@@ -0,0 +1,42 @@
#region License
// Copyright (C) 2020 Reetus
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program. If not, see <https://www.gnu.org/licenses/>.
#endregion
using System;
using ClassicAssist.Shared;
using ClassicAssist.UO.Data;
namespace ClassicAssist.UO.Network.Packets
{
public class DisplayQuestPointer : BasePacket
{
public DisplayQuestPointer( bool active, int x, int y, int serial = 0 )
{
_writer = new PacketWriter( 10 );
_writer.Write( (byte) 0xBA );
_writer.Write( (byte) ( active ? 1 : 0 ) );
_writer.Write( (short) x );
_writer.Write( (short) y );
if ( Engine.ClientVersion >= new Version( 7, 0, 9, 0 ) )
{
_writer.Write( serial );
}
}
}
}
@@ -0,0 +1,27 @@
using ClassicAssist.Shared;
using ClassicAssist.UO.Data;
using ClassicAssist.UO.Objects;
namespace ClassicAssist.UO.Network.Packets
{
public class DragItem : BasePacket
{
public DragItem( int serial, int amount, bool checkAmount = false )
{
if ( checkAmount && amount == -1 )
{
Item item = Engine.Items.GetItem( serial );
if ( item != null )
{
amount = item.Count;
}
}
_writer = new PacketWriter( 7 );
_writer.Write( (byte) 0x07 );
_writer.Write( serial );
_writer.Write( (short) amount );
}
}
}
@@ -0,0 +1,49 @@
using System;
using ClassicAssist.Shared;
using ClassicAssist.UO.Data;
using ClassicAssist.UO.Network.PacketFilter;
namespace ClassicAssist.UO.Network.Packets
{
public class DropItem : BasePacket, IMacroCommandParser
{
public DropItem()
{
}
public DropItem( int serial, int containerSerial, int x, int y, int z )
{
_writer = new PacketWriter( 15 );
_writer.Write( (byte) 0x08 );
_writer.Write( serial );
_writer.Write( (short) x );
_writer.Write( (short) y );
_writer.Write( (sbyte) z );
if ( Engine.ClientVersion == null || Engine.ClientVersion >= new Version( 6, 0, 1, 7 ) )
{
_writer.Write( (byte) 0 );
}
_writer.Write( containerSerial );
}
public string Parse( byte[] packet, int length, PacketDirection direction )
{
if ( packet[0] != 0x08 || direction != PacketDirection.Outgoing )
{
return null;
}
int serial = ( packet[1] << 24 ) | ( packet[2] << 16 ) | ( packet[3] << 8 ) | packet[4];
int containerSerial = ( packet[11] << 24 ) | ( packet[12] << 16 ) | ( packet[13] << 8 ) | packet[14];
if ( serial == 0 || containerSerial == 0 )
{
return null;
}
return $"MoveItem(0x{serial:x}, 0x{containerSerial:x})\r\n";
}
}
}
@@ -0,0 +1,34 @@
using ClassicAssist.Shared;
using ClassicAssist.UO.Data;
using ClassicAssist.UO.Network.PacketFilter;
namespace ClassicAssist.UO.Network.Packets
{
public class EquipLastWeapon : BasePacket, IMacroCommandParser
{
public EquipLastWeapon()
{
if ( Engine.Player == null )
{
return;
}
_writer = new PacketWriter( 10 );
_writer.Write( (byte) 0xD7 );
_writer.Write( (short) 10 ); // size
_writer.Write( Engine.Player.Serial );
_writer.Write( (short) 0x1E );
_writer.Write( (byte) 0x0A );
}
public string Parse( byte[] packet, int length, PacketDirection direction )
{
if ( packet[0] != 0xD7 || packet[8] != 0x1E || packet[9] != 0x0A || direction != PacketDirection.Outgoing )
{
return null;
}
return "EquipLastWeapon()\r\n";
}
}
}
@@ -0,0 +1,35 @@
using ClassicAssist.UO.Data;
using ClassicAssist.UO.Network.PacketFilter;
namespace ClassicAssist.UO.Network.Packets
{
public class EquipRequest : BasePacket, IMacroCommandParser
{
public EquipRequest()
{
}
public EquipRequest( int serial, Layer layer, int containerSerial )
{
_writer = new PacketWriter( 10 );
_writer.Write( (byte) 0x13 );
_writer.Write( serial );
_writer.Write( (byte) layer );
_writer.Write( containerSerial );
}
public string Parse( byte[] packet, int length, PacketDirection direction )
{
if ( packet[0] != 0x13 || direction != PacketDirection.Outgoing )
{
return null;
}
int serial = ( packet[1] << 24 ) | ( packet[2] << 16 ) | ( packet[3] << 8 ) | packet[4];
Layer layer = (Layer) packet[5];
return $"EquipItem(0x{serial:x}, \"{layer.ToString()}\")\r\n";
}
}
}
@@ -0,0 +1,53 @@
#region License
// Copyright (C) 2020 Reetus
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program. If not, see <https://www.gnu.org/licenses/>.
#endregion
using ClassicAssist.Shared;
using ClassicAssist.UO.Data;
using ClassicAssist.UO.Network.PacketFilter;
namespace ClassicAssist.UO.Network.Packets
{
public class GuildButtonRequest : BasePacket, IMacroCommandParser
{
public GuildButtonRequest()
{
if ( Engine.Player == null )
{
return;
}
_writer = new PacketWriter( 10 );
_writer.Write( (byte) 0xD7 );
_writer.Write( (short) 10 ); // size
_writer.Write( Engine.Player.Serial );
_writer.Write( (short) 0x28 );
_writer.Write( (byte) 0x0A );
}
public string Parse( byte[] packet, int length, PacketDirection direction )
{
if ( packet[0] != 0xD7 || packet[8] != 0x28 || packet[9] != 0x0A || direction != PacketDirection.Outgoing )
{
return null;
}
return "OpenGuildGump()\r\n";
}
}
}
@@ -0,0 +1,49 @@
using System.Collections.Generic;
using ClassicAssist.UO.Data;
using ClassicAssist.UO.Network.PacketFilter;
namespace ClassicAssist.UO.Network.Packets
{
public class GumpButtonClick : BasePacket, IMacroCommandParser
{
public GumpButtonClick()
{
}
public GumpButtonClick( int gumpID, int serial, int buttonID, IReadOnlyCollection<int> switches = null )
{
//TODO switches etc
_writer = new PacketWriter( 23 + ( switches?.Count * 4 ?? 0 ) );
_writer.Write( (byte) 0xB1 );
_writer.Write( (short) ( 23 + ( switches?.Count * 4 ?? 0 ) ) );
_writer.Write( serial );
_writer.Write( gumpID );
_writer.Write( buttonID );
if ( switches != null )
{
_writer.Write( switches.Count );
foreach ( int @switch in switches )
{
_writer.Write( @switch );
}
}
_writer.Fill();
}
public string Parse( byte[] packet, int length, PacketDirection direction )
{
if ( packet[0] != 0xB1 || direction != PacketDirection.Outgoing )
{
return null;
}
uint gumpId = (uint) ( ( packet[7] << 24 ) | ( packet[8] << 16 ) | ( packet[9] << 8 ) | packet[10] );
int buttonId = ( packet[11] << 24 ) | ( packet[12] << 16 ) | ( packet[13] << 8 ) | packet[14];
return $"ReplyGump(0x{gumpId:x}, {buttonId})\r\n";
}
}
}
@@ -0,0 +1,9 @@
using ClassicAssist.UO.Network.PacketFilter;
namespace ClassicAssist.UO.Network.Packets
{
public interface IMacroCommandParser
{
string Parse( byte[] packet, int length, PacketDirection direction );
}
}
@@ -0,0 +1,32 @@
using ClassicAssist.UO.Data;
using ClassicAssist.UO.Network.PacketFilter;
namespace ClassicAssist.UO.Network.Packets
{
public class InvokeVirtue : BasePacket, IMacroCommandParser
{
public InvokeVirtue()
{
}
public InvokeVirtue( Virtues virtue )
{
_writer = new PacketWriter( 6 );
_writer.Write( (byte) 0x12 );
_writer.Write( (short) 6 );
_writer.Write( (byte) 0xF4 );
_writer.Write( (byte) virtue );
_writer.Write( (byte) 0 );
}
public string Parse( byte[] packet, int length, PacketDirection direction )
{
if ( packet[0] != 0x12 || packet[3] != 0xF4 || direction != PacketDirection.Outgoing )
{
return null;
}
return $"InvokeVirtue(\"{(Virtues) packet[4]}\")\r\n";
}
}
}
@@ -0,0 +1,18 @@
using ClassicAssist.UO.Network.PacketFilter;
namespace ClassicAssist.UO.Network.Packets
{
public class ItemListMenu : BasePacket, IMacroCommandParser
{
public string Parse( byte[] packet, int length, PacketDirection direction )
{
if ( packet[0] != 0x7C || direction != PacketDirection.Incoming )
{
return null;
}
int gumpId = ( packet[7] << 8 ) | packet[8];
return $"WaitForMenu(0x{gumpId:x}, 5000)\r\n";
}
}
}
@@ -0,0 +1,32 @@
using ClassicAssist.UO.Data;
using ClassicAssist.UO.Network.PacketFilter;
namespace ClassicAssist.UO.Network.Packets
{
public class LookRequest : BasePacket, IMacroCommandParser
{
public LookRequest()
{
}
public LookRequest( int serial )
{
_writer = new PacketWriter( 5 );
_writer.Write( (byte) 0x09 );
_writer.Write( serial );
}
public string Parse( byte[] packet, int length, PacketDirection direction )
{
//if ( basePacket[0] != 0x09 || direction != PacketDirection.Outgoing )
//{
// return null;
//}
//int serial = ( basePacket[1] << 24 ) | ( basePacket[2] << 16 ) | ( basePacket[3] << 8 ) | basePacket[4];
//return $"ClickObject(0x{serial:x})\r\nPause({Options.CurrentOptions.ActionDelayMS})\r\n";
return null;
}
}
}
@@ -0,0 +1,71 @@
#region License
// Copyright (C) 2020 Reetus
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program. If not, see <https://www.gnu.org/licenses/>.
#endregion
using ClassicAssist.Shared;
using ClassicAssist.UO.Data;
using ClassicAssist.UO.Network.PacketFilter;
using ClassicAssist.UO.Objects;
namespace ClassicAssist.UO.Network.Packets
{
public class MenuButtonClick : BasePacket, IMacroCommandParser
{
public MenuButtonClick()
{
}
public MenuButtonClick( int gumpId, int serial, int index, int id = 0, int hue = 0 )
{
if ( serial == -1 )
{
if ( !Engine.Menus.GetMenu( gumpId, out Menu menu ) )
{
return;
}
serial = menu.Serial;
}
_writer = new PacketWriter( 13 );
_writer.Write( (byte) 0x7D );
_writer.Write( serial );
_writer.Write( (short) gumpId );
_writer.Write( (short) index );
_writer.Write( (short) id );
_writer.Write( (short) hue );
Engine.Menus?.Remove( gumpId );
}
public string Parse( byte[] packet, int length, PacketDirection direction )
{
if ( packet[0] != 0x7D || direction != PacketDirection.Outgoing )
{
return null;
}
int gumpId = ( packet[5] << 8 ) | packet[6];
int index = ( packet[7] << 8 ) | packet[8];
int id = ( packet[9] << 8 ) | packet[10];
int hue = ( packet[11] << 8 ) | packet[12];
return $"ReplyMenu(0x{gumpId:x}, {index}, 0x{id:x}, {hue})\r\n";
}
}
}
@@ -0,0 +1,60 @@
#region License
// Copyright (C) 2020 Reetus
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program. If not, see <https://www.gnu.org/licenses/>.
#endregion
using System;
using System.Linq;
using ClassicAssist.Shared;
using ClassicAssist.UO.Data;
using ClassicAssist.UO.Objects;
namespace ClassicAssist.UO.Network.Packets
{
public class MobileIncoming : BasePacket
{
public MobileIncoming( Mobile mobile, ItemCollection equipment, int overrideHue = 0 )
{
bool useNewIncoming = Engine.ClientVersion == null || Engine.ClientVersion >= new Version( 7, 0, 33, 1 );
int len = 23 + equipment.Count() * 9;
_writer = new PacketWriter( len );
_writer.Write( (byte) 0x78 );
_writer.Write( (short) len );
_writer.Write( mobile.Serial );
_writer.Write( (short) mobile.ID );
_writer.Write( (short) mobile.X );
_writer.Write( (short) mobile.Y );
_writer.Write( (sbyte) mobile.Z );
_writer.Write( (byte) mobile.Direction );
_writer.Write( (short) ( overrideHue > 0 ? overrideHue : mobile.Hue ) );
_writer.Write( (byte) mobile.Status );
_writer.Write( (byte) mobile.Notoriety );
foreach ( Item item in equipment.GetItems() )
{
_writer.Write( item.Serial );
_writer.Write( (short) ( useNewIncoming ? item.ID : item.ID | 0x8000 ) );
_writer.Write( (byte) item.Layer );
_writer.Write( (short) ( overrideHue > 0 ? overrideHue : item.Hue ) );
}
_writer.Write( 0 );
}
}
}
@@ -0,0 +1,42 @@
#region License
// Copyright (C) 2020 Reetus
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program. If not, see <https://www.gnu.org/licenses/>.
#endregion
using ClassicAssist.UO.Data;
using ClassicAssist.UO.Objects;
namespace ClassicAssist.UO.Network.Packets
{
public class MobileMoving : BasePacket
{
public MobileMoving( Mobile mobile, int hueOverride = 0 )
{
_writer = new PacketWriter( 17 );
_writer.Write( (byte) 0x77 );
_writer.Write( mobile.Serial );
_writer.Write( (short) mobile.ID );
_writer.Write( (short) mobile.X );
_writer.Write( (short) mobile.Y );
_writer.Write( (sbyte) mobile.Z );
_writer.Write( (byte) mobile.Direction );
_writer.Write( (short) ( hueOverride > 0 ? hueOverride : mobile.Hue ) );
_writer.Write( (byte) mobile.Status );
_writer.Write( (byte) mobile.Notoriety );
}
}
}
@@ -0,0 +1,16 @@
using ClassicAssist.UO.Data;
namespace ClassicAssist.UO.Network.Packets
{
public class MobileQuery : BasePacket
{
public MobileQuery( int serial, MobileQueryType queryType = MobileQueryType.StatsRequest )
{
_writer = new PacketWriter( 10 );
_writer.Write( (byte) 0x34 );
_writer.Write( 0xEDEDEDED );
_writer.Write( (byte) queryType );
_writer.Write( serial );
}
}
}
@@ -0,0 +1,23 @@
using ClassicAssist.UO.Data;
namespace ClassicAssist.UO.Network.Packets
{
public class MobileUpdate : BasePacket
{
public MobileUpdate( int serial, int id, int hue, MobileStatus flags, int x, int y, int z, Direction direction )
{
_writer = new PacketWriter( 19 );
_writer.Write( (byte) 0x20 );
_writer.Write( serial );
_writer.Write( (short) id );
_writer.Write( (byte) 0 );
_writer.Write( (short) hue );
_writer.Write( (byte) flags );
_writer.Write( (short) x );
_writer.Write( (short) y );
_writer.Write( (short) 0 );
_writer.Write( (byte) direction );
_writer.Write( (sbyte) z );
}
}
}
@@ -0,0 +1,20 @@
using ClassicAssist.UO.Data;
using ClassicAssist.UO.Network.PacketFilter;
namespace ClassicAssist.UO.Network.Packets
{
public class MovementRequest : BasePacket, IMacroCommandParser
{
public string Parse( byte[] packet, int length, PacketDirection direction )
{
if ( packet[0] != 0x02 || direction != PacketDirection.Outgoing )
{
return null;
}
Direction d = (Direction) packet[1];
return $"Walk(\"{d.ToString()}\")\r\n";
}
}
}
@@ -0,0 +1,36 @@
#region License
// Copyright (C) 2020 Reetus
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program. If not, see <https://www.gnu.org/licenses/>.
#endregion
using ClassicAssist.UO.Network.PacketFilter;
namespace ClassicAssist.UO.Network.Packets
{
public class MultiTarget : BasePacket, IMacroCommandParser
{
public string Parse( byte[] packet, int length, PacketDirection direction )
{
if ( packet[0] != 0x99 )
{
return null;
}
return direction == PacketDirection.Incoming ? "WaitForTarget(5000)\r\n" : null;
}
}
}
@@ -0,0 +1,11 @@
namespace ClassicAssist.UO.Network.Packets
{
public class Ping : BasePacket
{
public Ping( byte value ) : base( 2 )
{
_writer.Write( (byte) 0x73 );
_writer.Write( value );
}
}
}
@@ -0,0 +1,20 @@
using ClassicAssist.Shared;
using ClassicAssist.UO.Data;
namespace ClassicAssist.UO.Network.Packets
{
public class PlaySound : BasePacket
{
public PlaySound( int id )
{
_writer = new PacketWriter( 12 );
_writer.Write( (byte) 0x54 );
_writer.Write( (byte) 0x01 );
_writer.Write( (short) id );
_writer.Write( (short) 0 );
_writer.Write( (short) ( Engine.Player?.X ?? 0 ) );
_writer.Write( (short) ( Engine.Player?.Y ?? 0 ) );
_writer.Write( (short) ( Engine.Player?.Z ?? 0 ) );
}
}
}
@@ -0,0 +1,35 @@
#region License
// Copyright (C) 2020 Reetus
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program. If not, see <https://www.gnu.org/licenses/>.
#endregion
using ClassicAssist.UO.Data;
namespace ClassicAssist.UO.Network.Packets
{
public class QueryProperties : BasePacket
{
public QueryProperties( int serial )
{
_writer = new PacketWriter( 9 );
_writer.Write( (byte) 0xBF );
_writer.Write( (short) 9 );
_writer.Write( (short) 0x10 );
_writer.Write( serial );
}
}
}
@@ -0,0 +1,53 @@
#region License
// Copyright (C) 2020 Reetus
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program. If not, see <https://www.gnu.org/licenses/>.
#endregion
using ClassicAssist.Shared;
using ClassicAssist.UO.Data;
using ClassicAssist.UO.Network.PacketFilter;
namespace ClassicAssist.UO.Network.Packets
{
public class QuestsButtonRequest : BasePacket, IMacroCommandParser
{
public QuestsButtonRequest()
{
if ( Engine.Player == null )
{
return;
}
_writer = new PacketWriter( 10 );
_writer.Write( (byte) 0xD7 );
_writer.Write( (short) 10 ); // size
_writer.Write( Engine.Player.Serial );
_writer.Write( (short) 0x32 );
_writer.Write( (byte) 0x0A );
}
public string Parse( byte[] packet, int length, PacketDirection direction )
{
if ( packet[0] != 0xD7 || packet[8] != 0x32 || packet[9] != 0x0A || direction != PacketDirection.Outgoing )
{
return null;
}
return "OpenQuestsGump()\r\n";
}
}
}
@@ -0,0 +1,17 @@
using ClassicAssist.UO.Data;
namespace ClassicAssist.UO.Network.Packets
{
public class RejectPartyInvitation : BasePacket
{
public RejectPartyInvitation( int partyLeaderSerial )
{
_writer = new PacketWriter( 10 );
_writer.Write( (byte) 0xBF );
_writer.Write( (short) 10 );
_writer.Write( (short) 0x06 );
_writer.Write( (byte) 0x09 );
_writer.Write( partyLeaderSerial );
}
}
}
@@ -0,0 +1,33 @@
#region License
// Copyright (C) 2020 Reetus
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program. If not, see <https://www.gnu.org/licenses/>.
#endregion
using ClassicAssist.UO.Data;
namespace ClassicAssist.UO.Network.Packets
{
public class RemoveObject : BasePacket
{
public RemoveObject( int serial )
{
_writer = new PacketWriter( 5 );
_writer.Write( (byte) 0x1D );
_writer.Write( serial );
}
}
}
@@ -0,0 +1,35 @@
using ClassicAssist.UO.Data;
using ClassicAssist.UO.Network.PacketFilter;
namespace ClassicAssist.UO.Network.Packets
{
public class RenameRequest : BasePacket, IMacroCommandParser
{
public RenameRequest()
{
}
public RenameRequest( int serial, string name )
{
_writer = new PacketWriter( 35 );
_writer.Write( (byte) 0x75 );
_writer.Write( serial );
_writer.WriteAsciiFixed( name, 30 );
}
public string Parse( byte[] packet, int length, PacketDirection direction )
{
if ( packet[0] != 0x75 || direction != PacketDirection.Outgoing )
{
return null;
}
PacketReader reader = new PacketReader( packet, length, true );
int serial = reader.ReadInt32();
string name = reader.ReadString( 30 );
return $"Rename(0x{serial:x}, \"{name}\")\r\n";
}
}
}
@@ -0,0 +1,14 @@
using ClassicAssist.UO.Data;
namespace ClassicAssist.UO.Network.Packets
{
public class Resync : BasePacket
{
public Resync()
{
_writer = new PacketWriter( 3 );
_writer.Write( (byte) 0x22 );
_writer.Write( (short) 0 );
}
}
}
@@ -0,0 +1,42 @@
#region License
// Copyright (C) 2020 Reetus
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program. If not, see <https://www.gnu.org/licenses/>.
#endregion
using System.IO;
using ClassicAssist.UO.Data;
namespace ClassicAssist.UO.Network.Packets
{
public class SAWorldItem : BasePacket
{
public SAWorldItem( byte[] packet, int length, int hueOverride )
{
_writer = new PacketWriter( length );
_writer.Write( packet, 0, length );
if ( hueOverride <= 0 )
{
return;
}
_writer.Seek( 21, SeekOrigin.Begin );
_writer.Write( (short) hueOverride );
_writer.Seek( 0, SeekOrigin.End );
}
}
}
@@ -0,0 +1,21 @@
using ClassicAssist.Shared;
using ClassicAssist.UO.Data;
namespace ClassicAssist.UO.Network.Packets
{
public class SetWeaponAbility : BasePacket
{
public SetWeaponAbility( int abilityIndex )
{
_writer = new PacketWriter( 15 );
_writer.Write( (byte) 0xD7 );
_writer.Write( (short) 15 );
_writer.Write( Engine.Player?.Serial ?? 0 );
_writer.Write( (short) 0x19 );
_writer.Write( (byte) 0 );
_writer.Write( abilityIndex );
_writer.Write( (byte) 0x0A );
}
}
}
@@ -0,0 +1,45 @@
#region License
// Copyright (C) 2020 Reetus
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program. If not, see <https://www.gnu.org/licenses/>.
#endregion
using ClassicAssist.UO.Data;
using ClassicAssist.UO.Network.PacketFilter;
namespace ClassicAssist.UO.Network.Packets
{
public class StunRequest : BasePacket, IMacroCommandParser
{
public StunRequest()
{
_writer = new PacketWriter( 5 );
_writer.Write( (byte) 0xBF );
_writer.Write( (short) 5 );
_writer.Write( (short) 0x0A );
}
public string Parse( byte[] packet, int length, PacketDirection direction )
{
if ( direction == PacketDirection.Outgoing && packet[0] == 0xBF && packet[4] == 0x0A )
{
return "SetAbility(\"Stun\")\r\n";
}
return null;
}
}
}
@@ -0,0 +1,97 @@
using ClassicAssist.Shared;
using ClassicAssist.Data;
using ClassicAssist.Data.Macros.Commands;
using ClassicAssist.Shared.Resources;
using ClassicAssist.Shared.UO;
using ClassicAssist.UO.Data;
using ClassicAssist.UO.Network.PacketFilter;
using ClassicAssist.UO.Objects;
namespace ClassicAssist.UO.Network.Packets
{
public class Target : BasePacket, IMacroCommandParser
{
public Target()
{
}
public Target( int senderSerial, Entity entity, bool cancelClientCursor ) : this( TargetType.Object,
senderSerial, TargetFlags.None, entity.Serial, -1, -1, -1, entity.ID, cancelClientCursor )
{
}
public Target( TargetType targetType, int senderSerial, TargetFlags flags, int targetSerial, int x, int y,
int z, int id, bool cancelClientCursor )
{
if ( senderSerial == -1 )
{
senderSerial = Engine.TargetSerial;
}
if ( targetSerial > 0 )
{
Engine.Player.LastTargetSerial = targetSerial;
Engine.Player.LastTargetType = targetType;
}
if ( Engine.TargetFlags == TargetFlags.Harmful &&
Options.CurrentOptions.PreventTargetingFriendsWithHarmful &&
MobileCommands.InFriendList( targetSerial ) )
{
Commands.SystemMessage( Strings.Target_blocked____try_again___ );
Commands.ResendTargetToClient();
return;
}
_writer = new PacketWriter( 19 );
_writer.Write( (byte) 0x6C );
_writer.Write( (byte) targetType );
_writer.Write( senderSerial );
_writer.Write( (byte) flags );
_writer.Write( targetSerial );
_writer.Write( (short) x );
_writer.Write( (short) y );
_writer.Write( (short) z );
_writer.Write( (short) id );
Engine.TargetExists = false;
if ( cancelClientCursor )
{
Engine.SendPacketToClient( new Target( targetType, senderSerial, TargetFlags.Cancel, targetSerial, x, y,
z, id, false ) );
Engine.AddSendPreFilter( new PacketFilterInfo( 0x6C,
new[] { PacketFilterConditions.IntAtPositionCondition( senderSerial, 2 ) },
( p, pfi ) => { Engine.RemoveSendPreFilter( pfi ); } ) );
}
}
public string Parse( byte[] packet, int length, PacketDirection direction )
{
if ( packet[0] != 0x6C )
{
return null;
}
if ( direction == PacketDirection.Incoming )
{
return "WaitForTarget(5000)\r\n";
}
if ( packet[1] == 0 )
{
uint serial = (uint) ( ( packet[7] << 24 ) | ( packet[8] << 16 ) | ( packet[9] << 8 ) | packet[10] );
return $"Target(0x{serial:x})\r\n";
}
int x = ( packet[11] << 8 ) | packet[12];
int y = ( packet[13] << 8 ) | packet[14];
int z = (sbyte) ( packet[15] << 8 ) | (sbyte) packet[16];
int id = ( packet[17] << 8 ) | packet[18];
return id == 0 ? $"TargetXYZ({x}, {y}, {z})\r\n" : $"TargetXYZ({x}, {y}, {z}, {id})\r\n";
}
}
}
@@ -0,0 +1,36 @@
#region License
// Copyright (C) 2020 Reetus
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program. If not, see <https://www.gnu.org/licenses/>.
#endregion
using ClassicAssist.UO.Data;
namespace ClassicAssist.UO.Network.Packets
{
public class TargetByResource : BasePacket
{
public TargetByResource( int serial, int resourceType )
{
_writer = new PacketWriter( 11 );
_writer.Write( (byte) 0xBF );
_writer.Write( (short) 11 );
_writer.Write( (short) 0x30 );
_writer.Write( serial );
_writer.Write( (short) resourceType );
}
}
}
@@ -0,0 +1,18 @@
using ClassicAssist.UO.Data;
namespace ClassicAssist.UO.Network.Packets
{
public class ToggleGargoyleFlying : BasePacket
{
public ToggleGargoyleFlying()
{
_writer = new PacketWriter( 11 );
_writer.Write( (byte) 0xBF );
_writer.Write( (short) 11 );
_writer.Write( (short) 0x32 );
_writer.Write( (short) 1 );
_writer.Write( 0 );
}
}
}
@@ -0,0 +1,54 @@
#region License
// Copyright (C) 2020 Reetus
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program. If not, see <https://www.gnu.org/licenses/>.
#endregion
using ClassicAssist.Shared.Resources;
using ClassicAssist.UO.Data;
using ClassicAssist.UO.Network.PacketFilter;
namespace ClassicAssist.UO.Network.Packets
{
public class UnicodePromptCancel : BasePacket, IMacroCommandParser
{
public UnicodePromptCancel()
{
}
public UnicodePromptCancel( int senderSerial, int promptId )
{
_writer = new PacketWriter( 20 );
_writer.Write( (byte) 0xC2 );
_writer.Write( (short) 20 );
_writer.Write( senderSerial );
_writer.Write( promptId );
_writer.Write( 0 );
_writer.WriteAsciiFixed( Strings.UO_LOCALE, 4 );
_writer.Fill();
}
public string Parse( byte[] packet, int length, PacketDirection direction )
{
if ( packet[0] != 0xC2 || direction != PacketDirection.Outgoing )
{
return null;
}
return packet[14] != 0x00 ? null : "CancelPrompt()\r\n";
}
}
}
@@ -0,0 +1,24 @@
using ClassicAssist.UO.Network.PacketFilter;
namespace ClassicAssist.UO.Network.Packets
{
public class UnicodePromptRequest : BasePacket, IMacroCommandParser
{
// ReSharper disable once EmptyConstructor
public UnicodePromptRequest()
{
// TODO
// byte[] packet = new byte[] { 0xC2, 0x00, 0x15, 0x00, 0x00, 0x04, 0x82, 0x00, 0x00, 0x04, 0x82, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 };
}
public string Parse( byte[] packet, int length, PacketDirection direction )
{
if ( packet[0] == 0xC2 && direction == PacketDirection.Incoming )
{
return "WaitForPrompt(5000)\r\n";
}
return null;
}
}
}
@@ -0,0 +1,47 @@
using System.Text;
using ClassicAssist.Shared.Resources;
using ClassicAssist.UO.Data;
using ClassicAssist.UO.Network.PacketFilter;
namespace ClassicAssist.UO.Network.Packets
{
public class UnicodePromptResponse : BasePacket, IMacroCommandParser
{
public UnicodePromptResponse()
{
}
public UnicodePromptResponse( int senderSerial, int promptId, string text )
{
byte[] textBytes = Encoding.Unicode.GetBytes( text );
int len = 19 + textBytes.Length;
_writer = new PacketWriter( len );
_writer.Write( (byte) 0xC2 );
_writer.Write( (short) len );
_writer.Write( senderSerial );
_writer.Write( promptId );
_writer.Write( 1 );
_writer.WriteAsciiFixed( Strings.UO_LOCALE, 4 );
_writer.Write( textBytes, 0, textBytes.Length );
}
public string Parse( byte[] packet, int length, PacketDirection direction )
{
if ( packet[0] != 0xC2 || direction != PacketDirection.Outgoing )
{
return null;
}
if ( packet[14] == 0x00 )
{
return null;
}
string text = Encoding.Unicode.GetString( packet, 19, packet.Length - 20 ).Trim( '\0' );
return $"PromptMsg(\"{text}\")\r\n";
}
}
}
@@ -0,0 +1,27 @@
using System.Text;
using ClassicAssist.UO.Data;
namespace ClassicAssist.UO.Network.Packets
{
public class UnicodeText : BasePacket
{
public UnicodeText( int serial, int graphic, JournalSpeech speechType, int hue, int font, string lang,
string name, string text )
{
byte[] textBytes = Encoding.BigEndianUnicode.GetBytes( text );
int len = 48 + textBytes.Length;
_writer = new PacketWriter( len );
_writer.Write( (byte) 0xAE );
_writer.Write( (short) len );
_writer.Write( serial );
_writer.Write( (ushort) graphic );
_writer.Write( (byte) speechType );
_writer.Write( (short) hue );
_writer.Write( (short) font );
_writer.WriteAsciiFixed( lang, 4 );
_writer.WriteAsciiFixed( name, 30 );
_writer.Write( textBytes, 0, textBytes.Length );
}
}
}
@@ -0,0 +1,31 @@
using ClassicAssist.UO.Data;
using ClassicAssist.UO.Network.PacketFilter;
namespace ClassicAssist.UO.Network.Packets
{
public class UseObject : BasePacket, IMacroCommandParser
{
public UseObject()
{
}
public UseObject( int serial )
{
_writer = new PacketWriter( 5 );
_writer.Write( (byte) 0x06 );
_writer.Write( serial );
}
public string Parse( byte[] packet, int length, PacketDirection direction )
{
if ( packet[0] != 0x06 || direction != PacketDirection.Outgoing )
{
return null;
}
int serial = ( packet[1] << 24 ) | ( packet[2] << 16 ) | ( packet[3] << 8 ) | packet[4];
return $"UseObject(0x{serial:x})\r\n";
}
}
}
@@ -0,0 +1,67 @@
#region License
// Copyright (C) 2020 Reetus
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program. If not, see <https://www.gnu.org/licenses/>.
#endregion
using System;
using ClassicAssist.UO.Data;
using ClassicAssist.UO.Network.PacketFilter;
using static System.Text.Encoding;
namespace ClassicAssist.UO.Network.Packets
{
public class UseSkill : BasePacket, IMacroCommandParser
{
public UseSkill()
{
}
public UseSkill( int index )
{
string args = $"{index} 0";
_writer = new PacketWriter( 5 + args.Length );
_writer.Write( (byte) 0x12 );
_writer.Write( (short) ( 5 + args.Length ) );
_writer.Write( (byte) 0x24 );
_writer.WriteAsciiFixed( args, args.Length );
_writer.Write( (byte) 0 );
}
public string Parse( byte[] packet, int length, PacketDirection direction )
{
if ( packet[0] != 0x12 || packet[3] != 0x24 || direction != PacketDirection.Outgoing )
{
return null;
}
int len = packet.Length - 4;
byte[] skillPart = new byte[len];
Buffer.BlockCopy( packet, 4, skillPart, 0, len );
string skill = ASCII.GetString( skillPart );
if ( !int.TryParse( skill.Substring( 0, skill.IndexOf( ' ' ) ), out int id ) )
{
return null;
}
string skillName = Skills.GetSkillName( id );
return string.IsNullOrEmpty( skillName ) ? null : $"UseSkill(\"{skillName}\")\r\n";
}
}
}
@@ -0,0 +1,34 @@
#region License
// Copyright (C) 2020 Reetus
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program. If not, see <https://www.gnu.org/licenses/>.
#endregion
using ClassicAssist.UO.Data;
namespace ClassicAssist.UO.Network.Packets
{
public class UseTargetedItem : BasePacket
{
public UseTargetedItem( int serial, int targetSerial )
{
_writer = new PacketWriter( 13 );
_writer.Write( (byte) 0xBF );
_writer.Write( (short) 13 );
_writer.Write( (short) 0x2C );
_writer.Write( serial );
_writer.Write( targetSerial );
}
}
}

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