Merge branch 'main' into webSupport

This commit is contained in:
Kamron Batman 2021-12-04 23:43:12 -08:00 committed by GitHub
commit 3650878399
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
50 changed files with 365 additions and 319 deletions

View file

@ -30,4 +30,4 @@ jobs:
- name: Build
run: ./publish.cmd
- name: Test
run: dotnet test --no-restore --framework net6.0
run: dotnet test --no-restore

View file

@ -3,6 +3,7 @@ name: Create Release
on:
repository_dispatch:
types: [release]
workflow_dispatch:
jobs:
release:

View file

@ -11,7 +11,7 @@
<PublicRelease>true</PublicRelease>
<AutoGenerateBindingRedirects>true</AutoGenerateBindingRedirects>
<NoWarn>NU1603</NoWarn>
<RuntimeIdentifiers>win-x64;debian.10-x64;debian.9-x64;ubuntu.16.04-x64;ubuntu.18.04-x64;ubuntu.20.04-x64;centos.7-x64;centos.8-x64;fedora.32-x64;fedora.33-x64;rhel.7-x64;rhel.8-x64;osx-x64</RuntimeIdentifiers>
<RuntimeIdentifiers>win-x64;debian.9-x64;debian.10-x64;debian.11-x64;ubuntu.16.04-x64;ubuntu.18.04-x64;ubuntu.20.04-x64;centos.7-x64;centos.8-x64;fedora.32-x64;fedora.33-x64;fedora.34-x64;rhel.7-x64;rhel.8-x64;osx-x64</RuntimeIdentifiers>
<Configurations>Debug;Release;Analyze</Configurations>
<PackageRequireLicenseAcceptance>false</PackageRequireLicenseAcceptance>
<TreatWarningsAsErrors>true</TreatWarningsAsErrors>

View file

@ -1,6 +1,6 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>netstandard2.0</TargetFramework>
<TargetFramework>netstandard2.1</TargetFramework>
<LangVersion>preview</LangVersion>
<BuildOutputTargetFolder>analyzers</BuildOutputTargetFolder>
</PropertyGroup>
@ -21,9 +21,9 @@
<Target Name="GetDependencyTargetPaths">
<ItemGroup>
<TargetPathWithTargetPlatformMoniker Include="$(PKGHumanizer_Core)\lib\netstandard2.0\*.dll" IncludeRuntimeDependency="false" />
<TargetPathWithTargetPlatformMoniker Include="$(PKGSystem_Text_Json)\lib\netstandard2.0\*.dll" IncludeRuntimeDependency="false" />
<TargetPathWithTargetPlatformMoniker Include="$(PKGSystem_Text_Encodings_Web)\lib\netstandard2.0\*.dll" IncludeRuntimeDependency="false" />
<TargetPathWithTargetPlatformMoniker Include="$(PKGMicrosoft_Bcl_AsyncInterfaces)\lib\netstandard2.0\*.dll" IncludeRuntimeDependency="false" />
<TargetPathWithTargetPlatformMoniker Include="$(PKGSystem_Text_Json)\lib\netstandard2.1\*.dll" IncludeRuntimeDependency="false" />
<TargetPathWithTargetPlatformMoniker Include="$(PKGSystem_Text_Encodings_Web)\lib\netstandard2.1\*.dll" IncludeRuntimeDependency="false" />
<TargetPathWithTargetPlatformMoniker Include="$(PKGMicrosoft_Bcl_AsyncInterfaces)\lib\netstandard2.1\*.dll" IncludeRuntimeDependency="false" />
</ItemGroup>
</Target>
</Project>

View file

@ -12,6 +12,7 @@
</ItemGroup>
<ItemGroup>
<PackageReference Include="Humanizer.Core" Version="2.13.14" />
<PackageReference Include="Microsoft.Build.Locator" Version="1.4.1" />
<PackageReference Include="Microsoft.CodeAnalysis.CSharp.Workspaces" Version="4.0.1" />
<PackageReference Include="Microsoft.CodeAnalysis.Workspaces.MSBuild" Version="4.0.1" />

View file

@ -7,7 +7,7 @@
<PackageReference Include="Moq" Version="4.16.1" />
<PackageReference Include="xunit" Version="2.4.1" />
<PackageReference Include="xunit.runner.visualstudio" Version="2.4.3" />
<PackageReference Include="Zlib.Bindings" Version="1.5.0" />
<PackageReference Include="Zlib.Bindings" Version="1.7.2" />
<ProjectReference Include="..\Server\Server.csproj" />
<ProjectReference Include="..\UOContent\UOContent.csproj" />
<DataFiles Include="$(SolutionDir)\Distribution\Data\**" />

View file

@ -38,11 +38,11 @@ namespace Server
public static List<IPEndPoint> Listeners => m_Settings.Listeners;
public static string GetSetting(string key, string defaultValue)
{
m_Settings.Settings.TryGetValue(key, out var value);
return value ?? defaultValue;
}
public static ClientVersion GetSetting(string key, ClientVersion defaultValue) =>
m_Settings.Settings.TryGetValue(key, out var value) ? new ClientVersion(value) : defaultValue;
public static string GetSetting(string key, string defaultValue) =>
m_Settings.Settings.TryGetValue(key, out var value) ? value : defaultValue;
public static int GetSetting(string key, int defaultValue)
{

View file

@ -197,7 +197,9 @@ namespace Server.Network
public Pipe<byte> SendPipe { get; }
public Socket Connection { get; }
public bool Running => _running;
public Socket Connection { get; private set; }
public bool CompressionEnabled { get; set; }

View file

@ -39,7 +39,7 @@ namespace Server.Network
private static readonly byte[] _socketRejected = { 0x82, 0xFF };
public static IPEndPoint[] ListeningAddresses { get; private set; }
public static TcpListener[] Listeners { get; private set; }
public static Socket[] Listeners { get; private set; }
public static HashSet<NetState> Instances { get; } = new(2048);
private static readonly ConcurrentQueue<NetState> _connectedQueue = new();
@ -52,7 +52,7 @@ namespace Server.Network
public static void Start()
{
HashSet<IPEndPoint> listeningAddresses = new HashSet<IPEndPoint>();
List<TcpListener> listeners = new List<TcpListener>();
List<Socket> listeners = new List<Socket>();
foreach (var ipep in ServerConfiguration.Listeners)
{
@ -88,7 +88,7 @@ namespace Server.Network
{
foreach (var listener in Listeners)
{
listener.Server.Close();
listener.Close();
}
}
@ -99,21 +99,19 @@ namespace Server.Network
.Select(uip => new IPEndPoint(uip.Address, ipep.Port))
);
public static TcpListener CreateListener(IPEndPoint ipep)
public static Socket CreateListener(IPEndPoint ipep)
{
var listener = new TcpListener(ipep)
var listener = new Socket(ipep.AddressFamily, SocketType.Stream, ProtocolType.Tcp)
{
Server =
{
LingerState = new LingerOption(false, 0),
ExclusiveAddressUse = true,
NoDelay = true
}
LingerState = new LingerOption(false, 0),
ExclusiveAddressUse = true,
NoDelay = true
};
try
{
listener.Start(32);
listener.Bind(ipep);
listener.Listen(32);
return listener;
}
catch (SocketException se)
@ -148,13 +146,14 @@ namespace Server.Network
}
}
private static async void BeginAcceptingSockets(this TcpListener listener)
private static async void BeginAcceptingSockets(this Socket listener)
{
while (true)
{
try
{
var socket = await listener.AcceptSocketAsync();
var socket = await listener.AcceptAsync();
var rejected = false;
if (Instances.Count >= MaxConnections)
{

View file

@ -37,7 +37,7 @@ namespace Server
_encoding = encoding ?? TextEncoding.UTF8;
}
public BufferReader(byte[] buffer, DateTime LastSerialized) : this(buffer) => LastSerialized = LastSerialized;
public BufferReader(byte[] buffer, DateTime lastSerialized) : this(buffer) => LastSerialized = lastSerialized;
public void Reset(byte[] newBuffer, out byte[] oldBuffer)
{
@ -47,7 +47,7 @@ namespace Server
}
// Compatible with BinaryReader.ReadString()
public DateTime LastSerialized { get; init; } = DateTime.MinValue;
public DateTime LastSerialized { get; init; }
public string ReadString(bool intern = false)
{

View file

@ -36,11 +36,11 @@
<ItemGroup>
<PackageReference Include="Microsoft.Toolkit.HighPerformance" Version="7.1.2" />
<PackageReference Include="PollGroup" Version="1.1.0" />
<PackageReference Include="Zlib.Bindings" Version="1.5.0" />
<PackageReference Include="Zlib.Bindings" Version="1.7.2" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\SerializationGenerator\SerializationGenerator.csproj">
<SetTargetFramework>TargetFramework=netstandard2.0</SetTargetFramework>
<SetTargetFramework>TargetFramework=netstandard2.1</SetTargetFramework>
<OutputItemType>Analyzer</OutputItemType>
<ReferenceOutputAssembly>false</ReferenceOutputAssembly>
<PrivateAssets>all</PrivateAssets>

View file

@ -79,6 +79,72 @@ namespace Server.Targeting
OnTargetFinish(from);
}
protected virtual bool CanTarget(Mobile from, LandTarget landTarget, ref Point3D loc, ref Map map)
{
if (!AllowGround)
{
// We should actually never get here. If we do, it's probably a misbehaving client/macro.
OnTargetCancel(from, TargetCancelType.Canceled);
return false;
}
loc = landTarget.Location;
map = from.Map;
return true;
}
protected virtual bool CanTarget(Mobile from, StaticTarget staticTarget, ref Point3D loc, ref Map map)
{
loc = staticTarget.Location;
map = from.Map;
return true;
}
protected virtual bool CanTarget(Mobile from, Item item, ref Point3D loc, ref Map map)
{
if (item.Deleted)
{
OnTargetDeleted(from, item);
return false;
}
if (!item.CanTarget)
{
OnTargetUntargetable(from, item);
return false;
}
if (!AllowNonlocal && item.RootParent is Mobile && item.RootParent != from &&
from.AccessLevel == AccessLevel.Player)
{
OnNonlocalTarget(from, item);
return false;
}
loc = item.GetWorldLocation();
map = item.Map;
return true;
}
protected virtual bool CanTarget(Mobile from, Mobile mobile, ref Point3D loc, ref Map map)
{
if (mobile.Deleted)
{
OnTargetDeleted(from, mobile);
return false;
}
if (!mobile.CanTarget)
{
OnTargetUntargetable(from, mobile);
return false;
}
loc = mobile.Location;
map = mobile.Map;
return true;
}
public void Invoke(Mobile from, object targeted)
{
CancelTimeout();
@ -91,76 +157,32 @@ namespace Server.Targeting
return;
}
Point3D loc;
Map map;
Point3D loc = default;
Map map = null;
Item item = null;
Mobile mobile = null;
bool isValidTargetType = true;
var item = targeted as Item;
var mobile = targeted as Mobile;
bool valid = targeted switch
{
LandTarget landTarget => CanTarget(from, landTarget, ref loc, ref map),
StaticTarget staticTarget => CanTarget(from, staticTarget, ref loc, ref map),
Item i => CanTarget(from, item = i, ref loc, ref map),
Mobile m => CanTarget(from, mobile = m, ref loc, ref map),
_ => isValidTargetType = false
};
if (targeted is LandTarget target)
if (!valid)
{
loc = target.Location;
map = from.Map;
}
else if (targeted is StaticTarget staticTarget)
{
loc = staticTarget.Location;
map = from.Map;
}
else if (mobile != null)
{
if (mobile.Deleted)
if (!isValidTargetType)
{
OnTargetDeleted(from, mobile);
OnTargetFinish(from);
return;
OnTargetCancel(from, TargetCancelType.Canceled);
}
if (!mobile.CanTarget)
{
OnTargetUntargetable(from, mobile);
OnTargetFinish(from);
return;
}
loc = mobile.Location;
map = mobile.Map;
}
else if (item != null)
{
if (item.Deleted)
{
OnTargetDeleted(from, item);
OnTargetFinish(from);
return;
}
if (!item.CanTarget)
{
OnTargetUntargetable(from, item);
OnTargetFinish(from);
return;
}
if (!AllowNonlocal && item.RootParent is Mobile && item.RootParent != from &&
from.AccessLevel == AccessLevel.Player)
{
OnNonlocalTarget(from, item);
OnTargetFinish(from);
return;
}
loc = item.GetWorldLocation();
map = item.Map;
}
else
{
OnTargetCancel(from, TargetCancelType.Canceled);
OnTargetFinish(from);
return;
}
if (map == null || map != from.Map || Range != -1 && !from.InRange(loc, Range))
if (map == null || map != from.Map || Range >= 0 && !from.InRange(loc, Range))
{
OnTargetOutOfRange(from, targeted);
}

View file

@ -525,7 +525,7 @@ namespace Server
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static IEntity FindEntity(Serial serial, bool returnDeleted = false) => FindEntity<IEntity>(serial);
public static IEntity FindEntity(Serial serial, bool returnDeleted = false) => FindEntity<IEntity>(serial, returnDeleted);
public static T FindEntity<T>(Serial serial, bool returnDeleted = false) where T : class, IEntity
{
@ -669,15 +669,17 @@ namespace Server
if (typeof(BaseGuild).IsAssignableFrom(typeT))
{
entity = FindGuild(serial) as T;
// If we check for `entity.Deleted` here during deserialization then all guilds are deleted because
// Deleted -> Disbanded -> No leader, which is the case before deserialization.
// TODO: Use a deleted flag instead, and actively check for dibanded guilds properly.
}
else
{
entity = FindEntity<IEntity>(serial) as T;
}
if (entity?.Deleted == false)
{
return entity;
if (entity?.Deleted == false)
{
return entity;
}
}
return entity?.Created <= reader.LastSerialized ? entity : null;

View file

@ -1,5 +1,3 @@
using System.Collections.Generic;
using Server.ContextMenus;
using Server.Engines.Craft;
namespace Server.Items

View file

@ -290,9 +290,8 @@ namespace Server.Items
}
else if (BaseHouse.FindHouseAt(from) != house)
{
from.SendLocalizedMessage(
1062339
); // You must be located inside of the house in which you are trying to place the contract.
// You must be located inside of the house in which you are trying to place the contract.
from.SendLocalizedMessage(1062339);
}
else if (!house.IsAosRules)
{
@ -320,15 +319,13 @@ namespace Server.Items
if (vendor)
{
from.SendLocalizedMessage(
1062342
); // You may not place a rental contract at this location while other beings occupy it.
// You may not place a rental contract at this location while other beings occupy it.
from.SendLocalizedMessage(1062342);
}
else if (contract)
{
from.SendLocalizedMessage(
1062341
); // That location is cluttered. Please clear out any objects there and try again.
// That location is cluttered. Please clear out any objects there and try again.
from.SendLocalizedMessage(1062341);
}
else
{

View file

@ -1,6 +1,6 @@
using System;
using System.Collections.Generic;
using System.Linq;
using Server.Collections;
using Server.Network;
using Server.Spells;
using Server.Targeting;
@ -30,7 +30,7 @@ namespace Server.Items
public override bool RequireFreeHand => false;
public List<Mobile> Users { get; private set; }
private HashSet<Mobile> _users;
public override void Serialize(IGenericWriter writer)
{
@ -84,12 +84,8 @@ namespace Server.Items
from.RevealingAction();
Users ??= new List<Mobile>();
if (!Users.Contains(from))
{
Users.Add(from);
}
_users ??= new HashSet<Mobile>();
_users.Add(from);
from.Target = new ThrowTarget(this);
@ -195,65 +191,69 @@ namespace Server.Items
Consume();
for (var i = 0; i < Users?.Count; ++i)
foreach (var user in _users)
{
var m = Users[i];
if (m.Target is ThrowTarget targ && targ.Potion == this)
if (user.Target is ThrowTarget targ && targ.Potion == this)
{
Target.Cancel(m);
Target.Cancel(user);
}
}
_users.Clear();
if (map == null)
{
return;
}
Effects.PlaySound(loc, map, 0x307);
Effects.SendLocationEffect(loc, map, 0x36B0, 9);
var alchemyBonus = 0;
var alchemyBonus = 0;
if (direct)
{
alchemyBonus = (int)(from.Skills.Alchemy.Value / (Core.AOS ? 5 : 10));
}
var eable = map.GetObjectsInRange(loc, ExplosionRange, LeveledExplosion);
using var queue = PooledRefQueue<IEntity>.Create();
var toDamage = 0;
foreach (var entity in eable)
{
if (entity == this)
{
continue;
}
var toExplode = eable.Where(
o =>
if (entity is Mobile mobile)
{
if (from == null || SpellHelper.ValidIndirectTarget(from, mobile) && from.CanBeHarmful(mobile, false))
{
if (!(o is Mobile mobile) || from != null &&
(!SpellHelper.ValidIndirectTarget(from, mobile) || !from.CanBeHarmful(mobile, false)))
{
return o is BaseExplosionPotion && o != this;
}
++toDamage;
return true;
queue.Enqueue(entity);
}
)
.ToList();
}
else if (entity is BaseExplosionPotion)
{
queue.Enqueue(entity);
}
}
eable.Free();
var min = Scale(from, MinDamage);
var max = Scale(from, MaxDamage);
for (var i = 0; i < toExplode.Count; ++i)
while (queue.Count > 0)
{
var o = toExplode[i];
var entity = queue.Dequeue();
if (o is Mobile m)
if (entity is Mobile m)
{
from?.DoHarmful(m);
var damage = Utility.RandomMinMax(min, max);
damage += alchemyBonus;
var damage = Utility.RandomMinMax(min, max) + alchemyBonus;
if (!Core.AOS && damage > 40)
{
@ -266,7 +266,7 @@ namespace Server.Items
AOS.Damage(m, from, damage, 0, 100, 0, 0, 0);
}
else if (o is BaseExplosionPotion pot)
else if (entity is BaseExplosionPotion pot)
{
pot.Explode(from, false, pot.GetWorldLocation(), pot.Map);
}

View file

@ -1,4 +1,3 @@
using Server.Mobiles;
using System;
using System.Collections.Generic;

View file

@ -1,5 +1,3 @@
using System;
namespace Server.Items
{
/// <summary>

View file

@ -17,9 +17,31 @@ namespace Server.Items
public override int BaseMana => 30;
// When using Wrestling, tactics isnt needed.
// When using Wrestling, tactics isn't needed.
public override bool RequiresTactics(Mobile from) =>
!(from.Weapon is BaseWeapon weapon && weapon.Skill == SkillName.Wrestling);
Core.AOS && from.Weapon is not BaseWeapon { Skill: SkillName.Wrestling };
public override bool CheckSkills(Mobile from)
{
if (!base.CheckSkills(from))
{
return false;
}
if (Core.AOS || from.Weapon is not Fists)
{
return true;
}
if (from.Skills[SkillName.Anatomy] is { Value: >= 80.0 })
{
return true;
}
from.SendLocalizedMessage(1061811); // You lack the required anatomy skill to perform that attack!
return false;
}
public override bool OnBeforeSwing(Mobile attacker, Mobile defender)
{
@ -56,8 +78,15 @@ namespace Server.Items
var duration = defender.Player ? PlayerFreezeDuration : NPCFreezeDuration;
// Treat it as paralyze not as freeze, effect must be removed when damaged.
defender.Paralyze(duration);
// Pub 21: Treat it as paralyze, not as freeze, effect must be removed when damaged.
if (Core.AOS)
{
defender.Paralyze(duration);
}
else
{
defender.Freeze(duration);
}
BeginImmunity(defender, duration + FreezeDelayDuration);
}

View file

@ -1,6 +1,7 @@
using System;
using System.Diagnostics;
using System.Buffers.Binary;
using System.IO;
using Server.Buffers;
using Server.Gumps;
using Server.Logging;
using Server.Mobiles;
@ -12,29 +13,36 @@ namespace Server.Misc
{
private static readonly ILogger logger = LogFactory.GetLogger(typeof(ClientVerification));
private static bool m_DetectClientRequirement;
private static OldClientResponse m_OldClientResponse;
private static bool _enable;
private static bool _detectClientRequirement;
private static InvalidClientResponse _invalidClientResponse;
private static string _versionExpression;
private static TimeSpan m_AgeLeniency;
private static TimeSpan m_GameTimeLeniency;
private static TimeSpan _ageLeniency;
private static TimeSpan _gameTimeLeniency;
public static ClientVersion Required { get; set; }
public static ClientVersion MinRequired { get; private set; }
public static ClientVersion MaxRequired { get; private set; }
public static bool AllowRegular { get; set; } = true;
public static bool AllowUOTD { get; set; } = true;
public static bool AllowGod { get; set; } = true;
public static TimeSpan KickDelay { get; set; }
public static bool AllowRegular => true;
public static bool AllowUOTD => false;
public static TimeSpan KickDelay { get; private set; }
public static void Configure()
{
m_DetectClientRequirement = ServerConfiguration.GetOrUpdateSetting("clientVerification.enable", true);
m_OldClientResponse =
ServerConfiguration.GetOrUpdateSetting("clientVerification.oldClientResponse", OldClientResponse.Kick);
m_AgeLeniency = ServerConfiguration.GetOrUpdateSetting("clientVerification.ageLeniency", TimeSpan.FromDays(10));
m_GameTimeLeniency = ServerConfiguration.GetOrUpdateSetting(
MinRequired = ServerConfiguration.GetSetting("clientVerification.minRequired", (ClientVersion)null);
MaxRequired = ServerConfiguration.GetSetting("clientVerification.maxRequired", (ClientVersion)null);
if (MinRequired == null && MaxRequired == null)
{
_detectClientRequirement = ServerConfiguration.GetOrUpdateSetting("clientVerification.detectFromClientExe", true);
}
_enable = ServerConfiguration.GetOrUpdateSetting("clientVerification.enable", true);
_invalidClientResponse =
ServerConfiguration.GetOrUpdateSetting("clientVerification.invalidClientResponse", InvalidClientResponse.Kick);
_ageLeniency = ServerConfiguration.GetOrUpdateSetting("clientVerification.ageLeniency", TimeSpan.FromDays(10));
_gameTimeLeniency = ServerConfiguration.GetOrUpdateSetting(
"clientVerification.gameTimeLeniency",
TimeSpan.FromHours(25)
);
@ -45,121 +53,154 @@ namespace Server.Misc
{
EventSink.ClientVersionReceived += EventSink_ClientVersionReceived;
if (m_DetectClientRequirement)
if (_detectClientRequirement)
{
var path = Core.FindDataFile("client.exe", false);
if (File.Exists(path))
{
var info = FileVersionInfo.GetVersionInfo(path);
if (info.FileMajorPart != 0 || info.FileMinorPart != 0 || info.FileBuildPart != 0 ||
info.FilePrivatePart != 0)
using FileStream fs = new FileStream(path, FileMode.Open, FileAccess.Read, FileShare.Read);
var buffer = GC.AllocateUninitializedArray<byte>((int)fs.Length, true);
fs.Read(buffer);
// VS_VERSION_INFO (unicode)
Span<byte> vsVersionInfo = stackalloc byte[]
{
Required = new ClientVersion(
info.FileMajorPart,
info.FileMinorPart,
info.FileBuildPart,
info.FilePrivatePart
);
0x56, 0x00, 0x53, 0x00, 0x5F, 0x00, 0x56, 0x00,
0x45, 0x00, 0x52, 0x00, 0x53, 0x00, 0x49, 0x00,
0x4F, 0x00, 0x4E, 0x00, 0x5F, 0x00, 0x49, 0x00,
0x4E, 0x00, 0x46, 0x00, 0x4F, 0x00
};
for (var i = 0; i < buffer.Length; i++)
{
if (vsVersionInfo.SequenceEqual(buffer.AsSpan(i, 30)))
{
var offset = i + 42; // 30 + 12
var minorPart = BinaryPrimitives.ReadUInt16LittleEndian(buffer.AsSpan(offset));
var majorPart = BinaryPrimitives.ReadUInt16LittleEndian(buffer.AsSpan(offset + 2));
var privatePart = BinaryPrimitives.ReadUInt16LittleEndian(buffer.AsSpan(offset + 4));
var buildPart = BinaryPrimitives.ReadUInt16LittleEndian(buffer.AsSpan(offset + 6));
MinRequired = new ClientVersion(majorPart, minorPart, buildPart, privatePart);
break;
}
}
}
}
if (Required != null)
if (MinRequired != null || MaxRequired != null)
{
logger.Information(
"Restricting client version to {0}. Action to be taken: {1}",
Required,
m_OldClientResponse
$"Restricting client version to {GetVersionExpression()}. Action to be taken: {_invalidClientResponse}"
);
}
}
private static string GetVersionExpression()
{
if (_versionExpression == null)
{
if (MinRequired != null && MaxRequired != null)
{
_versionExpression = $"{MinRequired}-{MaxRequired}";
}
else if (MinRequired != null)
{
_versionExpression = $"{MinRequired} or newer";
}
else
{
_versionExpression = $"{MaxRequired} or older";
}
}
return _versionExpression;
}
private static void EventSink_ClientVersionReceived(NetState state, ClientVersion version)
{
string kickMessage = null;
using var message = new ValueStringBuilder();
if (state.Mobile?.AccessLevel != AccessLevel.Player)
if (!_enable || state.Mobile?.AccessLevel != AccessLevel.Player)
{
return;
}
if (Required != null && version < Required && (m_OldClientResponse == OldClientResponse.Kick ||
m_OldClientResponse == OldClientResponse.LenientKick &&
Core.Now - state.Mobile.Created > m_AgeLeniency &&
state.Mobile is PlayerMobile mobile &&
mobile.GameTime > m_GameTimeLeniency))
var strictRequirement = _invalidClientResponse == InvalidClientResponse.Kick ||
_invalidClientResponse == InvalidClientResponse.LenientKick &&
Core.Now - state.Mobile.Created > _ageLeniency &&
state.Mobile is PlayerMobile mobile &&
mobile.GameTime > _gameTimeLeniency;
bool shouldKick = false;
if (MinRequired != null && version < MinRequired)
{
kickMessage = $"This server requires your client version be at least {Required}.";
message.Append($"This server doesn't support clients older than {MinRequired}.");
shouldKick = strictRequirement;
}
else if (!AllowGod || !AllowRegular || !AllowUOTD)
else if (MaxRequired != null && version > MaxRequired)
{
if (!AllowGod && version.Type == ClientType.God)
message.Append($"This server doesn't support clients newer than {MaxRequired}.");
shouldKick = strictRequirement;
}
else if (!AllowRegular || !AllowUOTD)
{
if (!AllowRegular && version.Type == ClientType.Regular)
{
kickMessage = "This server does not allow god clients to connect.";
}
else if (!AllowRegular && version.Type == ClientType.Regular)
{
kickMessage = "This server does not allow regular clients to connect.";
message.Append("This server does not allow regular clients to connect.");
shouldKick = true;
}
else if (!AllowUOTD && state.IsUOTDClient)
{
kickMessage = "This server does not allow UO:TD clients to connect.";
message.Append("This server does not allow UO:TD clients to connect.");
shouldKick = true;
}
if (!AllowGod && !AllowRegular && !AllowUOTD)
{
kickMessage = "This server does not allow any clients to connect.";
}
else if (AllowGod && !AllowRegular && !AllowUOTD && version.Type != ClientType.God)
{
kickMessage = "This server requires you to use the god client.";
}
else if (kickMessage != null)
if (message.Length > 0)
{
if (AllowRegular && AllowUOTD)
{
kickMessage += " You can use regular or UO:TD clients.";
message.Append(" You can use regular or UO:TD clients.");
}
else if (AllowRegular)
{
kickMessage += " You can use regular clients.";
message.Append(" You can use regular clients.");
}
else if (AllowUOTD)
{
kickMessage += " You can use UO:TD clients.";
message.Append(" You can use UO:TD clients.");
}
}
}
if (kickMessage != null)
if (message.Length > 0)
{
state.Mobile.SendMessage(0x22, kickMessage);
state.Mobile.SendMessage(0x22, "You will be disconnected in {0} seconds.", KickDelay.TotalSeconds);
Timer.StartTimer(KickDelay, () => OnKick(state));
state.Mobile.SendMessage(0x22, message.ToString());
}
else if (Required != null && version < Required)
if (shouldKick)
{
switch (m_OldClientResponse)
state.Mobile.SendMessage(0x22, "You will be disconnected in {0} seconds.", KickDelay.TotalSeconds);
Timer.StartTimer(KickDelay, () => OnKick(state));
return;
}
if (message.Length > 0)
{
switch (_invalidClientResponse)
{
case OldClientResponse.Warn:
case InvalidClientResponse.Warn:
{
state.Mobile.SendMessage(
0x22,
"Your client is out of date. Please update your client.",
Required
);
state.Mobile.SendMessage(
0x22,
"This server recommends that your client version be at least {0}.",
Required
$"This server recommends that your client version is {GetVersionExpression()}."
);
break;
}
case OldClientResponse.LenientKick:
case OldClientResponse.Annoy:
case InvalidClientResponse.LenientKick:
case InvalidClientResponse.Annoy:
{
SendAnnoyGump(state.Mobile);
break;
@ -170,10 +211,11 @@ namespace Server.Misc
private static void OnKick(NetState ns)
{
if (ns.Connection != null)
if (ns.Running)
{
ns.LogInfo("Disconnecting, bad version");
ns.Disconnect($"Invalid client version {ns.Version}.");
var version = ns.Version;
ns.LogInfo($"Disconnecting, bad version ({version})");
ns.Disconnect($"Invalid client version {version}.");
}
}
@ -181,12 +223,12 @@ namespace Server.Misc
{
from.SendMessage("You will be reminded of this again.");
if (m_OldClientResponse == OldClientResponse.LenientKick)
if (_invalidClientResponse == InvalidClientResponse.LenientKick)
{
from.SendMessage(
"Old clients will be kicked after {0} days of character age and {1} hours of play time",
m_AgeLeniency,
m_GameTimeLeniency
"Invalid clients will be kicked after {0} days of character age and {1} hours of play time",
_ageLeniency,
_gameTimeLeniency
);
}
@ -195,28 +237,29 @@ namespace Server.Misc
private static void SendAnnoyGump(Mobile m)
{
if (m.NetState != null && m.NetState.Version < Required)
if (m.NetState != null)
{
Gump g = new WarningGump(
1060637,
30720,
$"Your client is out of date. Please update your client.<br>This server recommends that your client version be at least {Required}.<br> <br>You are currently using version {m.NetState.Version}.<br> <br>To patch, run UOPatch.exe inside your Ultima Online folder.",
$"Your client is invalid.<br>This server recommends that your client version is {GetVersionExpression()}.<br> <br>You are currently using version {m.NetState.Version}.",
0xFFC000,
480,
360,
okay => KickMessage(m, okay),
false
);
g.Draggable = false;
g.Closable = false;
g.Resizable = false;
)
{
Draggable = false,
Closable = false,
Resizable = false,
};
m.SendGump(g);
}
}
private enum OldClientResponse
private enum InvalidClientResponse
{
Ignore,
Warn,

View file

@ -661,15 +661,7 @@ namespace Server.Guilds
public AllianceInfo Alliance
{
get
{
if (m_AllianceInfo != null)
{
return m_AllianceInfo;
}
return m_AllianceLeader?.m_AllianceInfo;
}
get => m_AllianceInfo ?? m_AllianceLeader?.m_AllianceInfo;
set
{
var current = Alliance;
@ -1359,10 +1351,10 @@ namespace Server.Guilds
alliance = Alliance; // CheckLeader could possibly change the value of this.Alliance
if (alliance?.IsMember(this) == false && !alliance.IsPendingMember(this)
) // This block is there to fix a bug in the code in an older version.
// This block is there to fix a bug in the code in an older version.
if (alliance?.IsMember(this) == false && !alliance.IsPendingMember(this))
{
Alliance = null; // Will call Alliance.RemoveGuild which will set it null & perform all the pertient checks as far as alliacne disbanding
Alliance = null; // Will call Alliance.RemoveGuild which will set it null & perform all the pertinent checks as far as alliance disbanding
}
}
@ -1616,10 +1608,10 @@ namespace Server.Guilds
if (m_Leader != winner && winner != null)
{
GuildMessage(1018015, true, winner.Name); // Guild Message: Guildmaster changed to:
Leader = winner;
GuildMessage(1018015, true, winner.RawName); // Guild Message: Guildmaster changed to:
}
Leader = winner;
LastFealty = Core.Now;
}

View file

@ -1,5 +1,4 @@
using System;
using System.IO;
using System.Net;
using System.Net.Http;
using System.Net.NetworkInformation;

View file

@ -112,9 +112,8 @@ namespace Server.SkillHandlers
{
if (cancelType == TargetCancelType.Timeout)
{
from.SendLocalizedMessage(
501619
); // You have waited too long to make your inscribe selection, your inscription attempt has timed out.
// You have waited too long to make your inscribe selection, your inscription attempt has timed out.
from.SendLocalizedMessage(501619);
}
}
}
@ -172,9 +171,8 @@ namespace Server.SkillHandlers
{
if (cancelType == TargetCancelType.Timeout)
{
from.SendLocalizedMessage(
501619
); // You have waited too long to make your inscribe selection, your inscription attempt has timed out.
// You have waited too long to make your inscribe selection, your inscription attempt has timed out.
from.SendLocalizedMessage(501619);
}
}

View file

@ -790,9 +790,7 @@ namespace Server.Spells
return false;
}
public bool CheckBSequence(Mobile target) => CheckBSequence(target, false);
public bool CheckBSequence(Mobile target, bool allowDead)
public bool CheckBSequence(Mobile target, bool allowDead = false)
{
if (!target.Alive && !allowDead)
{

View file

@ -1,6 +1,5 @@
using Server.Items;
using Server.Misc;
using Server.Targeting;
namespace Server.Spells.Fifth
{

View file

@ -3,7 +3,6 @@ using Server.Collections;
using Server.Items;
using Server.Misc;
using Server.Mobiles;
using Server.Targeting;
namespace Server.Spells.Fifth
{

View file

@ -2,7 +2,6 @@ using System;
using System.Collections.Generic;
using System.Linq;
using Server.Mobiles;
using Server.Targeting;
namespace Server.Spells.Fourth
{

View file

@ -3,7 +3,6 @@ using System.Collections.Generic;
using Server.Collections;
using Server.Engines.PartySystem;
using Server.Spells.Second;
using Server.Targeting;
namespace Server.Spells.Fourth
{

View file

@ -3,7 +3,6 @@ using Server.Collections;
using Server.Items;
using Server.Misc;
using Server.Mobiles;
using Server.Targeting;
namespace Server.Spells.Fourth
{

View file

@ -4,7 +4,6 @@ using Server.Engines.Quests;
using Server.Engines.Quests.Necro;
using Server.Items;
using Server.Mobiles;
using Server.Targeting;
using Server.Utilities;
namespace Server.Spells.Necromancy

View file

@ -1,5 +1,4 @@
using Server.Items;
using Server.Targeting;
namespace Server.Spells.Second
{

View file

@ -1,5 +1,4 @@
using Server.Items;
using Server.Targeting;
namespace Server.Spells.Second
{

View file

@ -1,6 +1,5 @@
using System.Collections.Generic;
using System.Linq;
using Server.Targeting;
namespace Server.Spells.Seventh
{

View file

@ -2,7 +2,6 @@ using System;
using Server.Items;
using Server.Misc;
using Server.Mobiles;
using Server.Targeting;
namespace Server.Spells.Seventh
{

View file

@ -1,7 +1,6 @@
using Server.Collections;
using Server.Items;
using Server.Mobiles;
using Server.Targeting;
namespace Server.Spells.Seventh
{

View file

@ -1,6 +1,5 @@
using System.Collections.Generic;
using System.Linq;
using Server.Targeting;
namespace Server.Spells.Seventh
{

View file

@ -1,6 +1,5 @@
using Server.Items;
using Server.Network;
using Server.Targeting;
namespace Server.Spells.Sixth
{

View file

@ -1,5 +1,3 @@
using Server.Targeting;
namespace Server.Spells.Sixth
{
public class MassCurseSpell : MagerySpell, ISpellTargetingPoint3D

View file

@ -2,7 +2,6 @@ using System;
using Server.Items;
using Server.Misc;
using Server.Mobiles;
using Server.Targeting;
namespace Server.Spells.Sixth
{

View file

@ -1,5 +1,4 @@
using Server.Mobiles;
using Server.Targeting;
namespace Server.Spells.Sixth
{

View file

@ -16,12 +16,12 @@ namespace Server.Spells
public ISpell Spell => _spell;
protected override bool CanTarget(Mobile from, StaticTarget staticTarget, ref Point3D loc, ref Map map) => false;
protected override bool CanTarget(Mobile from, Mobile mobile, ref Point3D loc, ref Map map) => false;
protected override void OnTarget(Mobile from, object o)
{
if (o is Item item)
{
_spell.Target(item);
}
_spell.Target(o as Item);
}
protected override void OnTargetFinish(Mobile from)

View file

@ -16,6 +16,9 @@ namespace Server.Spells
public ISpell Spell => _spell;
protected override bool CanTarget(Mobile from, StaticTarget staticTarget, ref Point3D loc, ref Map map) => false;
protected override bool CanTarget(Mobile from, Item item, ref Point3D loc, ref Map map) => false;
protected override void OnTarget(Mobile from, object o)
{
_spell.Target(o as Mobile);

View file

@ -24,10 +24,7 @@ namespace Server.Spells
protected override void OnTarget(Mobile from, object o)
{
if (o is IPoint3D p)
{
_spell.Target(p);
}
_spell.Target(o as IPoint3D);
}
protected override void OnTargetOutOfLOS(Mobile from, object o)

View file

@ -1,7 +1,6 @@
using Server.Items;
using Server.Multis;
using Server.Network;
using Server.Targeting;
namespace Server.Spells.Third
{

View file

@ -1,5 +1,4 @@
using Server.Items;
using Server.Targeting;
namespace Server.Spells.Third
{

View file

@ -5,7 +5,6 @@ using Server.Regions;
using Server.Spells.Fifth;
using Server.Spells.Fourth;
using Server.Spells.Sixth;
using Server.Targeting;
namespace Server.Spells.Third
{

View file

@ -1,7 +1,6 @@
using Server.Items;
using Server.Multis;
using Server.Network;
using Server.Targeting;
namespace Server.Spells.Third
{

View file

@ -1,7 +1,6 @@
using System;
using Server.Misc;
using Server.Mobiles;
using Server.Targeting;
namespace Server.Spells.Third
{

View file

@ -41,13 +41,13 @@
<PackageReference Include="MailKit" Version="2.15.0" />
<PackageReference Include="Microsoft.Extensions.FileSystemGlobbing" Version="6.0.0" />
<PackageReference Include="Microsoft.Toolkit.HighPerformance" Version="7.1.2" />
<PackageReference Include="Zlib.Bindings" Version="1.5.0" />
<PackageReference Include="Argon2.Bindings" Version="1.9.1" />
<PackageReference Include="Zstd.Binaries" Version="1.0.0" />
<PackageReference Include="Zlib.Bindings" Version="1.7.2" />
<PackageReference Include="Argon2.Bindings" Version="1.11.0" />
<PackageReference Include="Zstd.Binaries" Version="1.2.0" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\SerializationGenerator\SerializationGenerator.csproj">
<SetTargetFramework>TargetFramework=netstandard2.0</SetTargetFramework>
<SetTargetFramework>TargetFramework=netstandard2.1</SetTargetFramework>
<OutputItemType>Analyzer</OutputItemType>
<ReferenceOutputAssembly>false</ReferenceOutputAssembly>
<PrivateAssets>all</PrivateAssets>

View file

@ -14,11 +14,6 @@ jobs:
vmImage: 'windows-latest'
steps:
- task: UseDotNet@2
displayName: 'Install .NET 5'
inputs:
packageType: sdk
version: 5.0.403
- task: UseDotNet@2
displayName: 'Install .NET 6'
inputs:
@ -27,7 +22,7 @@ jobs:
- task: NuGetAuthenticate@0
- script: ./publish.cmd Release win
displayName: 'Build'
- script: dotnet test --no-restore --framework net6.0
- script: dotnet test --no-restore
displayName: 'Test'
- job: BuildLinux
@ -39,14 +34,11 @@ jobs:
'CentOS 7':
containerImage: centos:7
os: centos.7
'Debian 10':
containerImage: mcr.microsoft.com/dotnet/sdk:5.0-buster-slim
os: debian.10
# 'Debian 11':
# containerImage: amd64/buildpack-deps:bullseye
# os: debian.11-x64
'Debian 11':
containerImage: mcr.microsoft.com/dotnet/sdk:6.0-bullseye-slim
os: debian.11
'Ubuntu 20':
containerImage: mcr.microsoft.com/dotnet/sdk:5.0-focal
containerImage: mcr.microsoft.com/dotnet/sdk:6.0-focal
os: ubuntu.20.04
'Fedora 32':
containerImage: fedora:32
@ -54,6 +46,9 @@ jobs:
'Fedora 33':
containerImage: fedora:33
os: fedora.33
'Fedora 34':
containerImage: fedora:34
os: fedora.34
displayName: Linux
@ -63,11 +58,6 @@ jobs:
container: $[ variables['containerImage'] ]
steps:
- task: UseDotNet@2
displayName: 'Install .NET 5'
inputs:
packageType: sdk
version: 5.0.403
- task: UseDotNet@2
displayName: 'Install .NET 6'
inputs:
@ -76,5 +66,5 @@ jobs:
- task: NuGetAuthenticate@0
- script: ./publish.cmd Release $(os)
displayName: 'Build'
- script: dotnet test --no-restore --framework net6.0
- script: dotnet test --no-restore
displayName: 'Test'