Merge branch 'main' into webSupport

This commit is contained in:
Kamron Batman 2022-01-06 12:20:03 -08:00 committed by GitHub
commit f0471f35f8
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
61 changed files with 2614 additions and 1641 deletions

View file

@ -3,6 +3,7 @@ name: Updates Docs
on:
repository_dispatch:
types: [docs]
workflow_dispatch:
jobs:
update-docs:

View file

@ -11,7 +11,7 @@
<PublicRelease>true</PublicRelease>
<AutoGenerateBindingRedirects>true</AutoGenerateBindingRedirects>
<NoWarn>NU1603</NoWarn>
<RuntimeIdentifiers>win-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>
<RuntimeIdentifiers>win-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;linuxmint.17-x64;linuxmint.18-x64;linuxmint.19-x64;osx-x64</RuntimeIdentifiers>
<Configurations>Debug;Release;Analyze</Configurations>
<PackageRequireLicenseAcceptance>false</PackageRequireLicenseAcceptance>
<TreatWarningsAsErrors>true</TreatWarningsAsErrors>

View file

@ -0,0 +1,665 @@
using BenchmarkDotNet.Attributes;
using BenchmarkDotNet.Jobs;
using Server;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace Benchmarks
{
[SimpleJob(RuntimeMoniker.NetCoreApp50)]
public class MapSelectors
{
static readonly Sector sector = new Sector();
static Server.Rectangle2D bounds = new Server.Rectangle2D(0, 0, 100, 100);
public static void Init()
{
for (int i = 0; i < 50; ++i)
{
sector.Multis.Add(new BaseMulti());
}
for (int i = 0; i < 1000; ++i)
{
sector.BItems.Add(new BItem());
}
for (int i = 0; i < 1000; ++i)
{
sector.Mobiles.Add(new Mobile());
}
}
#region MultiTiles
[Benchmark]
public void SelectMultiTilesNew()
{
foreach(StaticTile[] tiles in SelectMultiTiles(sector, bounds))
{
for(int i = 0; i < tiles.Length; ++i)
{
int id = tiles[i].ID;
}
}
}
[Benchmark]
public void SelectMultiTilesLinq()
{
foreach(StaticTile[] tiles in SelectMultiTilesLinq(sector, bounds))
{
for(int i = 0; i < tiles.Length; ++i)
{
int id = tiles[i].ID;
}
}
}
public IEnumerable<StaticTile[]> SelectMultiTilesLinq(Sector s, Server.Rectangle2D bounds)
{
foreach (var o in s.Multis.Where(o => o != null && !o.Deleted))
{
var c = o.Components;
int x, y, xo, yo;
StaticTile[] t, r;
for (x = bounds.Start.X; x < bounds.End.X; x++)
{
xo = x - (o.X + c.Min.X);
if (xo < 0 || xo >= c.Width)
{
continue;
}
for (y = bounds.Start.Y; y < bounds.End.Y; y++)
{
yo = y - (o.Y + c.Min.Y);
if (yo < 0 || yo >= c.Height)
{
continue;
}
t = c.Tiles[xo][yo];
if (t.Length <= 0)
{
continue;
}
r = new StaticTile[t.Length];
for (var i = 0; i < t.Length; i++)
{
r[i] = t[i];
r[i].Z += o.Z;
}
yield return r;
}
}
}
}
public IEnumerable<StaticTile[]> SelectMultiTiles(Sector s, Server.Rectangle2D bounds)
{
for (int l = s.Multis.Count - 1; l >= 0; --l)
{
BaseMulti o = s.Multis[l];
if (o != null && !o.Deleted)
{
MultiComponentList c = o.Components;
int x, y, xo, yo;
StaticTile[] t, r;
for (x = bounds.Start.X; x < bounds.End.X; x++)
{
xo = x - (o.X + c.Min.X);
if (xo < 0 || xo >= c.Width)
{
continue;
}
for (y = bounds.Start.Y; y < bounds.End.Y; y++)
{
yo = y - (o.Y + c.Min.Y);
if (yo < 0 || yo >= c.Height)
{
continue;
}
t = c.Tiles[xo][yo];
if (t.Length <= 0)
{
continue;
}
r = new StaticTile[t.Length];
for (var i = 0; i < t.Length; i++)
{
r[i] = t[i];
r[i].Z += o.Z;
}
yield return r;
}
}
}
}
}
#endregion
#region Multis
[Benchmark]
public void SelectMultisNew()
{
SelectMultis(sector, bounds);
}
[Benchmark]
public void SelectMultisLinq()
{
SelectMultisLinq(sector, bounds);
}
public IEnumerable<BaseMulti> SelectMultisLinq(Sector s, Server.Rectangle2D bounds)
{
return s.Multis.Where(o => o != null && !o.Deleted && bounds.Contains(o.Location));
}
public IEnumerable<BaseMulti> SelectMultis(Sector s, Server.Rectangle2D bounds)
{
List<BaseMulti> entities = new List<BaseMulti>(s.Multis.Count);
for (int i = s.Multis.Count - 1; i >= 0; --i)
{
BaseMulti BItem = s.Multis[i];
if (BItem != null && !BItem.Deleted && bounds.Contains(BItem.Location))
entities.Add(BItem);
}
return entities;
}
#endregion
#region BItems
[Benchmark]
public void SelectBItemsNew()
{
SelectBItems<BItem>(sector, bounds);
}
[Benchmark]
public void SelectBItemsLinq()
{
SelectBItemsLinq<BItem>(sector, bounds);
}
public IEnumerable<T> SelectBItemsLinq<T>(Sector s, Server.Rectangle2D bounds) where T : BItem
{
return s.BItems.OfType<T>().Where(o => o != null && !o.Deleted && o.Parent == null && bounds.Contains(o.Location));
}
public IEnumerable<T> SelectBItems<T>(Sector s, Server.Rectangle2D bounds) where T : BItem
{
List<T> entities = new List<T>(s.BItems.Count);
Type type = typeof(T);
for (int i = s.BItems.Count - 1; i >= 0; --i)
{
BItem BItem = s.BItems[i];
if (BItem != null && !BItem.Deleted && BItem.Parent == null && bounds.Contains(BItem.Location) && type.IsAssignableFrom(BItem.GetType()))
entities.Add(BItem as T);
}
return entities;
}
#endregion
#region Mobiles
[Benchmark]
public void SelectMobilesNew()
{
SelectMobiles<Mobile>(sector, bounds);
}
[Benchmark]
public void SelectMobilesLinq()
{
SelectMobilesLinq<Mobile>(sector, bounds);
}
public IEnumerable<T> SelectMobilesLinq<T>(Sector s, Server.Rectangle2D bounds) where T : Mobile
{
return s.Mobiles.OfType<T>().Where(o => o != null && !o.Deleted && bounds.Contains(o.Location));
}
public IEnumerable<T> SelectMobiles<T>(Sector s, Server.Rectangle2D bounds) where T : Mobile
{
List<T> entities = new List<T>(s.Mobiles.Count);
Type type = typeof(T);
for (int i = s.Mobiles.Count - 1; i >= 0; --i)
{
Mobile mob = s.Mobiles[i];
if (mob != null && !mob.Deleted && bounds.Contains(mob.Location) && type.IsAssignableFrom(mob.GetType()))
entities.Add(mob as T);
}
return entities;
}
#endregion
#region Entities
[Benchmark]
public void SelectEntitiesNew()
{
SelectEntities(sector, bounds);
}
[Benchmark]
public void SelectEntitiesLinq()
{
SelectEntitiesLinq(sector, bounds);
}
public IEnumerable<Server.IEntity> SelectEntitiesLinq(Sector s, Server.Rectangle2D bounds)
{
return Enumerable.Empty<IEntity>()
.Union(s.Mobiles.Where(o => o != null && !o.Deleted))
.Union(s.BItems.Where(o => o != null && !o.Deleted && o.Parent == null))
.Where(o => bounds.Contains(o.Location));
}
private readonly List<Server.IEntity> entities = new (10);
public IEnumerable<Server.IEntity> SelectEntities(Sector s, Server.Rectangle2D bounds)
{
entities.Clear();
entities.Capacity = s.Mobiles.Count + s.BItems.Count;
for (int i = s.Mobiles.Count - 1, j = s.BItems.Count - 1; i >= 0 || j >= 0; --i, --j)
{
if (j >= 0)
{
BItem BItem = s.BItems[j];
if (BItem != null && !BItem.Deleted && BItem.Parent == null && bounds.Contains(BItem.Location))
entities.Add(BItem);
}
if (i >= 0)
{
Mobile mob = s.Mobiles[i];
if (mob != null && !mob.Deleted && bounds.Contains(mob.Location))
entities.Add(mob);
}
}
return entities;
}
#endregion
}
public class BItem : Server.IPoint3D, IEntity
{
public object Parent { get; set; } = null;
public bool Deleted { get; set; } = false;
public int Z { get; set; } = 1;
public int X { get; set; } = 1;
public int Y { get; set; } = 1;
public Serial Serial => throw new System.NotImplementedException();
public Point3D Location { get => throw new System.NotImplementedException(); set => throw new System.NotImplementedException(); }
public Map Map { get => throw new System.NotImplementedException(); set => throw new System.NotImplementedException(); }
public Region Region => throw new System.NotImplementedException();
public string Name { get => throw new System.NotImplementedException(); set => throw new System.NotImplementedException(); }
public int Hue { get => throw new System.NotImplementedException(); set => throw new System.NotImplementedException(); }
public Direction Direction { get => throw new System.NotImplementedException(); set => throw new System.NotImplementedException(); }
public DateTime Created { get => throw new NotImplementedException(); set => throw new NotImplementedException(); }
public int TypeRef => throw new NotImplementedException();
Point3D IEntity.Location => throw new NotImplementedException();
Map IEntity.Map => throw new NotImplementedException();
int IPoint3D.Z => throw new NotImplementedException();
int IPoint2D.X => throw new NotImplementedException();
int IPoint2D.Y => throw new NotImplementedException();
DateTime ISerializable.Created { get => throw new NotImplementedException(); set => throw new NotImplementedException(); }
DateTime ISerializable.LastSerialized { get => throw new NotImplementedException(); set => throw new NotImplementedException(); }
long ISerializable.SavePosition { get => throw new NotImplementedException(); set => throw new NotImplementedException(); }
BufferWriter ISerializable.SaveBuffer { get => throw new NotImplementedException(); set => throw new NotImplementedException(); }
int ISerializable.TypeRef => throw new NotImplementedException();
Serial ISerializable.Serial => throw new NotImplementedException();
bool ISerializable.Deleted => throw new NotImplementedException();
public BItem()
{
}
public void Delete()
{
throw new System.NotImplementedException();
}
public void ProcessDelta()
{
throw new System.NotImplementedException();
}
public void OnStatsQuery(Server.Mobile m)
{
throw new System.NotImplementedException();
}
public void InvalidateProperties()
{
throw new System.NotImplementedException();
}
public int CompareTo(object obj)
{
throw new System.NotImplementedException();
}
public int CompareTo(IEntity other)
{
throw new System.NotImplementedException();
}
public void MoveToWorld(Point3D location, Map map)
{
throw new NotImplementedException();
}
public bool InRange(Point2D p, int range)
{
throw new NotImplementedException();
}
public bool InRange(Point3D p, int range)
{
throw new NotImplementedException();
}
public void RemoveBItem(BItem BItem)
{
throw new NotImplementedException();
}
public void BeforeSerialize()
{
throw new NotImplementedException();
}
public void Deserialize(IGenericReader reader)
{
throw new NotImplementedException();
}
public void Serialize(IGenericWriter writer)
{
throw new NotImplementedException();
}
public void SetTypeRef(Type type)
{
throw new NotImplementedException();
}
void IEntity.MoveToWorld(Point3D location, Map map)
{
throw new NotImplementedException();
}
void IEntity.ProcessDelta()
{
throw new NotImplementedException();
}
bool IEntity.InRange(Point2D p, int range)
{
throw new NotImplementedException();
}
bool IEntity.InRange(Point3D p, int range)
{
throw new NotImplementedException();
}
void ISerializable.BeforeSerialize()
{
throw new NotImplementedException();
}
void ISerializable.Deserialize(IGenericReader reader)
{
throw new NotImplementedException();
}
void ISerializable.Serialize(IGenericWriter writer)
{
throw new NotImplementedException();
}
void ISerializable.Delete()
{
throw new NotImplementedException();
}
void ISerializable.SetTypeRef(Type type)
{
throw new NotImplementedException();
}
public void RemoveItem(Item item)
{
throw new NotImplementedException();
}
}
public class Mobile : Server.IPoint3D, IEntity
{
public bool Deleted { get; set; } = false;
public int Z { get; set; } = 1;
public int X { get; set; } = 1;
public int Y { get; set; } = 1;
public Serial Serial => throw new System.NotImplementedException();
public Point3D Location { get => throw new System.NotImplementedException(); set => throw new System.NotImplementedException(); }
public Map Map { get => throw new System.NotImplementedException(); set => throw new System.NotImplementedException(); }
public Region Region => throw new System.NotImplementedException();
public string Name { get => throw new System.NotImplementedException(); set => throw new System.NotImplementedException(); }
public int Hue { get => throw new System.NotImplementedException(); set => throw new System.NotImplementedException(); }
public Direction Direction { get => throw new System.NotImplementedException(); set => throw new System.NotImplementedException(); }
public DateTime Created { get => throw new NotImplementedException(); set => throw new NotImplementedException(); }
public int TypeRef => throw new NotImplementedException();
Point3D IEntity.Location => throw new NotImplementedException();
Map IEntity.Map => throw new NotImplementedException();
int IPoint3D.Z => throw new NotImplementedException();
int IPoint2D.X => throw new NotImplementedException();
int IPoint2D.Y => throw new NotImplementedException();
DateTime ISerializable.Created { get => throw new NotImplementedException(); set => throw new NotImplementedException(); }
DateTime ISerializable.LastSerialized { get => throw new NotImplementedException(); set => throw new NotImplementedException(); }
long ISerializable.SavePosition { get => throw new NotImplementedException(); set => throw new NotImplementedException(); }
BufferWriter ISerializable.SaveBuffer { get => throw new NotImplementedException(); set => throw new NotImplementedException(); }
int ISerializable.TypeRef => throw new NotImplementedException();
Serial ISerializable.Serial => throw new NotImplementedException();
bool ISerializable.Deleted => throw new NotImplementedException();
public Mobile()
{
}
public void Delete()
{
throw new System.NotImplementedException();
}
public void ProcessDelta()
{
throw new System.NotImplementedException();
}
public void OnStatsQuery(Server.Mobile m)
{
throw new System.NotImplementedException();
}
public void InvalidateProperties()
{
throw new System.NotImplementedException();
}
public int CompareTo(object obj)
{
throw new System.NotImplementedException();
}
public int CompareTo(IEntity other)
{
throw new System.NotImplementedException();
}
public void MoveToWorld(Point3D location, Map map)
{
throw new NotImplementedException();
}
public bool InRange(Point2D p, int range)
{
throw new NotImplementedException();
}
public bool InRange(Point3D p, int range)
{
throw new NotImplementedException();
}
public void RemoveBItem(BItem BItem)
{
throw new NotImplementedException();
}
public void BeforeSerialize()
{
throw new NotImplementedException();
}
public void Deserialize(IGenericReader reader)
{
throw new NotImplementedException();
}
public void Serialize(IGenericWriter writer)
{
throw new NotImplementedException();
}
public void SetTypeRef(Type type)
{
throw new NotImplementedException();
}
void IEntity.MoveToWorld(Point3D location, Map map)
{
throw new NotImplementedException();
}
void IEntity.ProcessDelta()
{
throw new NotImplementedException();
}
bool IEntity.InRange(Point2D p, int range)
{
throw new NotImplementedException();
}
bool IEntity.InRange(Point3D p, int range)
{
throw new NotImplementedException();
}
void ISerializable.BeforeSerialize()
{
throw new NotImplementedException();
}
void ISerializable.Deserialize(IGenericReader reader)
{
throw new NotImplementedException();
}
void ISerializable.Serialize(IGenericWriter writer)
{
throw new NotImplementedException();
}
void ISerializable.Delete()
{
throw new NotImplementedException();
}
void ISerializable.SetTypeRef(Type type)
{
throw new NotImplementedException();
}
public void RemoveItem(Item item)
{
throw new NotImplementedException();
}
}
public class BaseMulti : BItem
{
public MultiComponentList Components = MultiComponentList.Empty;
public BaseMulti()
{
for (int i = 0; i < 20; ++i)
for (int j = 0; j < 20; ++j)
for (int z = 0; z < 20; ++z)
Components.Add(123, i, j, z);
}
}
public class Sector
{
public List<BItem> BItems { get; set; } = new List<BItem>();
public List<Mobile> Mobiles { get; set; } = new List<Mobile>();
public List<BaseMulti> Multis { get; set; } = new List<BaseMulti>();
}
}

View file

@ -0,0 +1,28 @@
using BenchmarkDotNet.Attributes;
using BenchmarkDotNet.Jobs;
using Server.Random;
namespace Benchmarks.Benchmarks.Rng
{
[MemoryDiagnoser]
[SimpleJob(RuntimeMoniker.Net60)]
public class BenchmarkDoubleVsFixed
{
private Xoshiro256PlusPlus _xoshiro256PlusPlus;
[GlobalSetup]
public void Setup()
{
_xoshiro256PlusPlus = new Xoshiro256PlusPlus();
}
[Benchmark]
public bool NextDouble() => 50.1 < _xoshiro256PlusPlus.NextDouble() * 100;
[Benchmark]
public bool NextFixedInt() => 501 < _xoshiro256PlusPlus.Next(1000);
[Benchmark]
public bool NextHighResDouble() => 50.1 < _xoshiro256PlusPlus.NextDoubleHighRes() * 100;
}
}

View file

@ -15,7 +15,10 @@ namespace Benchmarks
// var textEncoding = BenchmarkRunner.Run<BenchmarkTextEncoding>();
// var logging = BenchmarkRunner.Run<BenchmarkConsoleLogging>();
// var gumpPacket = BenchmarkRunner.Run<OutgoingGumpPacketBenchmarks>();
var rngTest = BenchmarkRunner.Run<BenchmarkXoshiro>();
// MapSelectors.Init();
// var mapSelectors = BenchmarkRunner.Run<MapSelectors>();
// var rngTest = BenchmarkRunner.Run<BenchmarkXoshiro>();
var doubleRngText = BenchmarkRunner.Run<BenchmarkDoubleVsFixed>();
}
}
}

View file

@ -37,12 +37,9 @@ namespace SerializationGenerator
foreach (var serializableProperty in properties)
{
var propertyType = serializableProperty.Type;
var type = compilation.GetTypeByMetadataName(propertyType)?.IsValueType == true
|| SymbolMetadata.IsPrimitiveFromTypeDisplayString(propertyType) && propertyType != "bool"
? $"{propertyType}{(serializableProperty.UsesSaveFlag == true ? "?" : "")}" : propertyType;
source.AppendLine($"{indent} internal readonly {type} {serializableProperty.Name};");
SerializableMigrationRulesEngine.Rules[serializableProperty.Rule].GenerateMigrationProperty(
source, compilation, $"{indent} ", serializableProperty
);
}
var innerIndent = $"{indent} ";
@ -100,7 +97,8 @@ namespace SerializationGenerator
source,
$"{innerIndent} ",
property,
"entity"
"entity",
true
);
source.AppendLine($"{innerIndent}}}\n{innerIndent}else\n{innerIndent}{{");
@ -114,7 +112,8 @@ namespace SerializationGenerator
source,
innerIndent,
property,
"entity"
"entity",
true
);
}
}

View file

@ -23,6 +23,13 @@ namespace SerializableMigration
{
string RuleName { get; }
void GenerateMigrationProperty(
StringBuilder source,
Compilation compilation,
string indent,
SerializableProperty serializableProperty
);
bool GenerateRuleState(
Compilation compilation,
ISymbol symbol,
@ -37,7 +44,8 @@ namespace SerializableMigration
StringBuilder source,
string indent,
SerializableProperty property,
string? parentReference
string? parentReference,
bool isMigration = false
);
void GenerateSerializationMethod(

View file

@ -18,117 +18,118 @@ using System.Collections.Immutable;
using System.Text;
using Microsoft.CodeAnalysis;
namespace SerializableMigration
namespace SerializableMigration;
public class ArrayMigrationRule : MigrationRule
{
public class ArrayMigrationRule : ISerializableMigrationRule
public override string RuleName => nameof(ArrayMigrationRule);
public override bool GenerateRuleState(
Compilation compilation,
ISymbol symbol,
ImmutableArray<AttributeData> attributes,
ImmutableArray<INamedTypeSymbol> serializableTypes,
ImmutableArray<INamedTypeSymbol> embeddedSerializableTypes,
ISymbol? parentSymbol,
out string[] ruleArguments
)
{
public string RuleName => nameof(ArrayMigrationRule);
public bool GenerateRuleState(
Compilation compilation,
ISymbol symbol,
ImmutableArray<AttributeData> attributes,
ImmutableArray<INamedTypeSymbol> serializableTypes,
ImmutableArray<INamedTypeSymbol> embeddedSerializableTypes,
ISymbol? parentSymbol,
out string[] ruleArguments
)
if (symbol is not IArrayTypeSymbol arrayTypeSymbol)
{
if (symbol is not IArrayTypeSymbol arrayTypeSymbol)
{
ruleArguments = null;
return false;
}
var serializableArrayType = SerializableMigrationRulesEngine.GenerateSerializableProperty(
compilation,
"ArrayEntry",
arrayTypeSymbol.ElementType,
0,
attributes,
serializableTypes,
embeddedSerializableTypes,
parentSymbol,
null
);
var length = serializableArrayType.RuleArguments?.Length?? 0;
ruleArguments = new string[length + 2];
ruleArguments[0] = arrayTypeSymbol.ElementType.ToDisplayString();
ruleArguments[1] = serializableArrayType.Rule;
if (length > 0)
{
Array.Copy(serializableArrayType.RuleArguments!, 0, ruleArguments, 2, length);
}
return true;
ruleArguments = null;
return false;
}
public void GenerateDeserializationMethod(StringBuilder source, string indent, SerializableProperty property, string? parentReference)
var serializableArrayType = SerializableMigrationRulesEngine.GenerateSerializableProperty(
compilation,
"ArrayEntry",
arrayTypeSymbol.ElementType,
0,
attributes,
serializableTypes,
embeddedSerializableTypes,
parentSymbol,
null
);
var length = serializableArrayType.RuleArguments?.Length?? 0;
ruleArguments = new string[length + 2];
ruleArguments[0] = arrayTypeSymbol.ElementType.ToDisplayString();
ruleArguments[1] = serializableArrayType.Rule;
if (length > 0)
{
var expectedRule = RuleName;
var ruleName = property.Rule;
if (expectedRule != ruleName)
{
throw new ArgumentException($"Invalid rule applied to property {ruleName}. Expecting {expectedRule}, but received {ruleName}.");
}
var ruleArguments = property.RuleArguments;
var arrayElementRule = SerializableMigrationRulesEngine.Rules[ruleArguments![1]];
var arrayElementRuleArguments = new string[ruleArguments.Length - 2];
Array.Copy(ruleArguments, 2, arrayElementRuleArguments, 0, ruleArguments.Length - 2);
var propertyIndex = $"{property.Name}Index";
source.AppendLine($"{indent}{property.Name} = new {ruleArguments[0]}[reader.ReadEncodedInt()];");
source.AppendLine($"{indent}for (var {propertyIndex} = 0; {propertyIndex} < {property.Name}.Length; {propertyIndex}++)");
source.AppendLine($"{indent}{{");
var serializableArrayElement = new SerializableProperty
{
Name = $"{property.Name}[{propertyIndex}]",
Type = ruleArguments[0],
Rule = arrayElementRule.RuleName,
RuleArguments = arrayElementRuleArguments
};
arrayElementRule.GenerateDeserializationMethod(source, $"{indent} ", serializableArrayElement, parentReference);
source.AppendLine($"{indent}}}");
Array.Copy(serializableArrayType.RuleArguments!, 0, ruleArguments, 2, length);
}
public void GenerateSerializationMethod(StringBuilder source, string indent, SerializableProperty property)
return true;
}
public override void GenerateDeserializationMethod(
StringBuilder source, string indent, SerializableProperty property, string? parentReference, bool isMigration = false
)
{
var expectedRule = RuleName;
var ruleName = property.Rule;
if (expectedRule != ruleName)
{
var expectedRule = RuleName;
var ruleName = property.Rule;
if (expectedRule != ruleName)
{
throw new ArgumentException($"Invalid rule applied to property {ruleName}. Expecting {expectedRule}, but received {ruleName}.");
}
var ruleArguments = property.RuleArguments;
var arrayElementRule = SerializableMigrationRulesEngine.Rules[ruleArguments![1]];
var arrayElementRuleArguments = new string[ruleArguments.Length - 2];
Array.Copy(ruleArguments, 2, arrayElementRuleArguments, 0, ruleArguments.Length - 2);
var propertyName = property.Name;
var propertyVarPrefix = $"{char.ToLower(propertyName[0])}{propertyName.Substring(1, propertyName.Length - 1)}";
var propertyIndex = $"{propertyVarPrefix}Index";
var propertyLength = $"{propertyVarPrefix}Length";
source.AppendLine($"{indent}var {propertyLength} = {property.Name}?.Length ?? 0;");
source.AppendLine($"{indent}writer.WriteEncodedInt({propertyLength});");
source.AppendLine($"{indent}for (var {propertyIndex} = 0; {propertyIndex} < {propertyLength}; {propertyIndex}++)");
source.AppendLine($"{indent}{{");
var serializableArrayElement = new SerializableProperty
{
Name = $"{property.Name}![{propertyIndex}]",
Type = ruleArguments[0],
Rule = arrayElementRule.RuleName,
RuleArguments = arrayElementRuleArguments
};
arrayElementRule.GenerateSerializationMethod(source, $"{indent} ", serializableArrayElement);
source.AppendLine($"{indent}}}");
throw new ArgumentException($"Invalid rule applied to property {ruleName}. Expecting {expectedRule}, but received {ruleName}.");
}
var ruleArguments = property.RuleArguments;
var arrayElementRule = SerializableMigrationRulesEngine.Rules[ruleArguments![1]];
var arrayElementRuleArguments = new string[ruleArguments.Length - 2];
Array.Copy(ruleArguments, 2, arrayElementRuleArguments, 0, ruleArguments.Length - 2);
var propertyIndex = $"{property.Name}Index";
source.AppendLine($"{indent}{property.Name} = new {ruleArguments[0]}[reader.ReadEncodedInt()];");
source.AppendLine($"{indent}for (var {propertyIndex} = 0; {propertyIndex} < {property.Name}.Length; {propertyIndex}++)");
source.AppendLine($"{indent}{{");
var serializableArrayElement = new SerializableProperty
{
Name = $"{property.Name}[{propertyIndex}]",
Type = ruleArguments[0],
Rule = arrayElementRule.RuleName,
RuleArguments = arrayElementRuleArguments
};
arrayElementRule.GenerateDeserializationMethod(source, $"{indent} ", serializableArrayElement, parentReference);
source.AppendLine($"{indent}}}");
}
public override void GenerateSerializationMethod(StringBuilder source, string indent, SerializableProperty property)
{
var expectedRule = RuleName;
var ruleName = property.Rule;
if (expectedRule != ruleName)
{
throw new ArgumentException($"Invalid rule applied to property {ruleName}. Expecting {expectedRule}, but received {ruleName}.");
}
var ruleArguments = property.RuleArguments;
var arrayElementRule = SerializableMigrationRulesEngine.Rules[ruleArguments![1]];
var arrayElementRuleArguments = new string[ruleArguments.Length - 2];
Array.Copy(ruleArguments, 2, arrayElementRuleArguments, 0, ruleArguments.Length - 2);
var propertyName = property.Name;
var propertyVarPrefix = $"{char.ToLower(propertyName[0])}{propertyName.Substring(1, propertyName.Length - 1)}";
var propertyIndex = $"{propertyVarPrefix}Index";
var propertyLength = $"{propertyVarPrefix}Length";
source.AppendLine($"{indent}var {propertyLength} = {property.Name}?.Length ?? 0;");
source.AppendLine($"{indent}writer.WriteEncodedInt({propertyLength});");
source.AppendLine($"{indent}for (var {propertyIndex} = 0; {propertyIndex} < {propertyLength}; {propertyIndex}++)");
source.AppendLine($"{indent}{{");
var serializableArrayElement = new SerializableProperty
{
Name = $"{property.Name}![{propertyIndex}]",
Type = ruleArguments[0],
Rule = arrayElementRule.RuleName,
RuleArguments = arrayElementRuleArguments
};
arrayElementRule.GenerateSerializationMethod(source, $"{indent} ", serializableArrayElement);
source.AppendLine($"{indent}}}");
}
}

View file

@ -20,230 +20,231 @@ using System.Text;
using Microsoft.CodeAnalysis;
using SerializationGenerator;
namespace SerializableMigration
namespace SerializableMigration;
public class DictionaryMigrationRule : MigrationRule
{
public class DictionaryMigrationRule : ISerializableMigrationRule
public override string RuleName => nameof(DictionaryMigrationRule);
public override bool GenerateRuleState(
Compilation compilation,
ISymbol symbol,
ImmutableArray<AttributeData> attributes,
ImmutableArray<INamedTypeSymbol> serializableTypes,
ImmutableArray<INamedTypeSymbol> embeddedSerializableTypes,
ISymbol? parentSymbol,
out string[] ruleArguments
)
{
public string RuleName => nameof(DictionaryMigrationRule);
public bool GenerateRuleState(
Compilation compilation,
ISymbol symbol,
ImmutableArray<AttributeData> attributes,
ImmutableArray<INamedTypeSymbol> serializableTypes,
ImmutableArray<INamedTypeSymbol> embeddedSerializableTypes,
ISymbol? parentSymbol,
out string[] ruleArguments
)
if (symbol is not INamedTypeSymbol namedTypeSymbol || !symbol.IsDictionary(compilation))
{
if (symbol is not INamedTypeSymbol namedTypeSymbol || !symbol.IsDictionary(compilation))
{
ruleArguments = null;
return false;
}
var keySymbolType = namedTypeSymbol.TypeArguments[0];
var serializableKeyProperty = SerializableMigrationRulesEngine.GenerateSerializableProperty(
compilation,
"KeyEntry",
keySymbolType,
0,
attributes,
serializableTypes,
embeddedSerializableTypes,
parentSymbol,
null
);
var valueSymbolType = namedTypeSymbol.TypeArguments[1];
var serializableValueProperty = SerializableMigrationRulesEngine.GenerateSerializableProperty(
compilation,
"ValueEntry",
valueSymbolType,
0,
attributes,
serializableTypes,
embeddedSerializableTypes,
parentSymbol,
null
);
var extraOptions = "";
if (attributes.Any(a => a.IsTidy(compilation)))
{
extraOptions += "@Tidy";
}
var keyArgumentsLength = serializableKeyProperty.RuleArguments?.Length ?? 0;
var valueArgumentsLength = serializableValueProperty.RuleArguments?.Length ?? 0;
var index = 0;
ruleArguments = new string[7 + keyArgumentsLength + valueArgumentsLength];
ruleArguments[index++] = extraOptions;
ruleArguments[index++] = keySymbolType.ToDisplayString();
ruleArguments[index++] = serializableKeyProperty.Rule;
ruleArguments[index++] = keyArgumentsLength.ToString();
if (keyArgumentsLength > 0)
{
Array.Copy(serializableKeyProperty.RuleArguments!, 0, ruleArguments, index, keyArgumentsLength);
index += keyArgumentsLength;
}
ruleArguments[index++] = valueSymbolType.ToDisplayString();
ruleArguments[index++] = serializableValueProperty.Rule;
ruleArguments[index++] = valueArgumentsLength.ToString();
if (valueArgumentsLength > 0)
{
Array.Copy(serializableValueProperty.RuleArguments!, 0, ruleArguments, index, valueArgumentsLength);
}
return true;
ruleArguments = null;
return false;
}
public void GenerateDeserializationMethod(StringBuilder source, string indent, SerializableProperty property, string? parentReference)
var keySymbolType = namedTypeSymbol.TypeArguments[0];
var serializableKeyProperty = SerializableMigrationRulesEngine.GenerateSerializableProperty(
compilation,
"KeyEntry",
keySymbolType,
0,
attributes,
serializableTypes,
embeddedSerializableTypes,
parentSymbol,
null
);
var valueSymbolType = namedTypeSymbol.TypeArguments[1];
var serializableValueProperty = SerializableMigrationRulesEngine.GenerateSerializableProperty(
compilation,
"ValueEntry",
valueSymbolType,
0,
attributes,
serializableTypes,
embeddedSerializableTypes,
parentSymbol,
null
);
var extraOptions = "";
if (attributes.Any(a => a.IsTidy(compilation)))
{
var expectedRule = RuleName;
var ruleName = property.Rule;
if (expectedRule != ruleName)
{
throw new ArgumentException($"Invalid rule applied to property {ruleName}. Expecting {expectedRule}, but received {ruleName}.");
}
var ruleArguments = property.RuleArguments;
var index = 1;
var keyType = ruleArguments![index++];
var keyElementRule = SerializableMigrationRulesEngine.Rules[ruleArguments[index++]];
var keyRuleArguments = new string[int.Parse(ruleArguments[index++])];
if (keyRuleArguments.Length > 0)
{
Array.Copy(ruleArguments, index, keyRuleArguments, 0, keyRuleArguments.Length);
index += keyRuleArguments.Length;
}
var valueType = ruleArguments[index++];
var valueElementRule = SerializableMigrationRulesEngine.Rules[ruleArguments[index++]];
var valueRuleArguments = new string[int.Parse(ruleArguments[index++])];
if (valueRuleArguments.Length > 0)
{
Array.Copy(ruleArguments, index, valueRuleArguments, 0, valueRuleArguments.Length);
}
var propertyName = property.Name;
var propertyVarPrefix = $"{char.ToLower(propertyName[0])}{propertyName.Substring(1, propertyName.Length - 1)}";
var propertyIndex = $"{propertyVarPrefix}Index";
var propertyKeyEntry = $"{propertyVarPrefix}Key";
var propertyValueEntry = $"{propertyVarPrefix}Value";
var propertyCount = $"{propertyVarPrefix}Count";
source.AppendLine($"{indent}{ruleArguments[1]} {propertyKeyEntry};");
source.AppendLine($"{indent}{valueType} {propertyValueEntry};");
source.AppendLine($"{indent}var {propertyCount} = reader.ReadEncodedInt();");
source.AppendLine($"{indent}{propertyName} = new System.Collections.Generic.Dictionary<{keyType}, {valueType}>({propertyCount});");
source.AppendLine($"{indent}for (var {propertyIndex} = 0; {propertyIndex} < {propertyCount}; {propertyIndex}++)");
source.AppendLine($"{indent}{{");
var serializableKeyElement = new SerializableProperty
{
Name = propertyKeyEntry,
Type = keyType,
Rule = keyElementRule.RuleName,
RuleArguments = keyRuleArguments
};
keyElementRule.GenerateDeserializationMethod(source, $"{indent} ", serializableKeyElement, parentReference);
var serializableValueElement = new SerializableProperty
{
Name = propertyValueEntry,
Type = valueType,
Rule = valueElementRule.RuleName,
RuleArguments = valueRuleArguments
};
valueElementRule.GenerateDeserializationMethod(source, $"{indent} ", serializableValueElement, parentReference);
source.AppendLine($"{indent} {propertyName}.Add({propertyKeyEntry}, {propertyValueEntry});");
source.AppendLine($"{indent}}}");
extraOptions += "@Tidy";
}
public void GenerateSerializationMethod(StringBuilder source, string indent, SerializableProperty property)
var keyArgumentsLength = serializableKeyProperty.RuleArguments?.Length ?? 0;
var valueArgumentsLength = serializableValueProperty.RuleArguments?.Length ?? 0;
var index = 0;
ruleArguments = new string[7 + keyArgumentsLength + valueArgumentsLength];
ruleArguments[index++] = extraOptions;
ruleArguments[index++] = keySymbolType.ToDisplayString();
ruleArguments[index++] = serializableKeyProperty.Rule;
ruleArguments[index++] = keyArgumentsLength.ToString();
if (keyArgumentsLength > 0)
{
var expectedRule = RuleName;
var ruleName = property.Rule;
if (expectedRule != ruleName)
{
throw new ArgumentException($"Invalid rule applied to property {ruleName}. Expecting {expectedRule}, but received {ruleName}.");
}
var ruleArguments = property.RuleArguments;
var index = 0;
var shouldTidy = ruleArguments![index++].Contains("@Tidy");
var keyType = ruleArguments![index++];
var keyElementRule = SerializableMigrationRulesEngine.Rules[ruleArguments![index++]];
var keyRuleArguments = new string[int.Parse(ruleArguments[index++])];
if (keyRuleArguments.Length > 0)
{
Array.Copy(ruleArguments, index, keyRuleArguments, 0, keyRuleArguments.Length);
index += keyRuleArguments.Length;
}
var valueType = ruleArguments[index++];
var valueElementRule = SerializableMigrationRulesEngine.Rules[ruleArguments[index++]];
var valueRuleArguments = new string[int.Parse(ruleArguments[index++])];
if (valueRuleArguments.Length > 0)
{
Array.Copy(ruleArguments, index, valueRuleArguments, 0, valueRuleArguments.Length);
}
var propertyName = property.Name;
var propertyVarPrefix = $"{char.ToLower(propertyName[0])}{propertyName.Substring(1, propertyName.Length - 1)}";
var propertyKeyEntry = $"{propertyVarPrefix}Key";
var propertyValueEntry = $"{propertyVarPrefix}Value";
var propertyCount = $"{propertyVarPrefix}Count";
if (shouldTidy)
{
source.AppendLine($"{indent}{property.Name}?.Tidy();");
}
source.AppendLine($"{indent}var {propertyCount} = {property.Name}?.Count ?? 0;");
source.AppendLine($"{indent}writer.WriteEncodedInt({propertyCount});");
source.AppendLine($"{indent}if ({propertyCount} > 0)");
source.AppendLine($"{indent}{{");
source.AppendLine($"{indent} foreach (var ({propertyKeyEntry}, {propertyValueEntry}) in {property.Name}!)");
source.AppendLine($"{indent} {{");
var serializableKeyElement = new SerializableProperty
{
Name = propertyKeyEntry,
Type = keyType,
Rule = keyElementRule.RuleName,
RuleArguments = keyRuleArguments
};
keyElementRule.GenerateSerializationMethod(source, $"{indent} ", serializableKeyElement);
var serializableValueElement = new SerializableProperty
{
Name = propertyValueEntry,
Type = valueType,
Rule = valueElementRule.RuleName,
RuleArguments = valueRuleArguments
};
valueElementRule.GenerateSerializationMethod(source, $"{indent} ", serializableValueElement);
source.AppendLine($"{indent} }}");
source.AppendLine($"{indent}}}");
Array.Copy(serializableKeyProperty.RuleArguments!, 0, ruleArguments, index, keyArgumentsLength);
index += keyArgumentsLength;
}
ruleArguments[index++] = valueSymbolType.ToDisplayString();
ruleArguments[index++] = serializableValueProperty.Rule;
ruleArguments[index++] = valueArgumentsLength.ToString();
if (valueArgumentsLength > 0)
{
Array.Copy(serializableValueProperty.RuleArguments!, 0, ruleArguments, index, valueArgumentsLength);
}
return true;
}
public override void GenerateDeserializationMethod(
StringBuilder source, string indent, SerializableProperty property, string? parentReference, bool isMigration = false
)
{
var expectedRule = RuleName;
var ruleName = property.Rule;
if (expectedRule != ruleName)
{
throw new ArgumentException($"Invalid rule applied to property {ruleName}. Expecting {expectedRule}, but received {ruleName}.");
}
var ruleArguments = property.RuleArguments;
var index = 1;
var keyType = ruleArguments![index++];
var keyElementRule = SerializableMigrationRulesEngine.Rules[ruleArguments[index++]];
var keyRuleArguments = new string[int.Parse(ruleArguments[index++])];
if (keyRuleArguments.Length > 0)
{
Array.Copy(ruleArguments, index, keyRuleArguments, 0, keyRuleArguments.Length);
index += keyRuleArguments.Length;
}
var valueType = ruleArguments[index++];
var valueElementRule = SerializableMigrationRulesEngine.Rules[ruleArguments[index++]];
var valueRuleArguments = new string[int.Parse(ruleArguments[index++])];
if (valueRuleArguments.Length > 0)
{
Array.Copy(ruleArguments, index, valueRuleArguments, 0, valueRuleArguments.Length);
}
var propertyName = property.Name;
var propertyVarPrefix = $"{char.ToLower(propertyName[0])}{propertyName.Substring(1, propertyName.Length - 1)}";
var propertyIndex = $"{propertyVarPrefix}Index";
var propertyKeyEntry = $"{propertyVarPrefix}Key";
var propertyValueEntry = $"{propertyVarPrefix}Value";
var propertyCount = $"{propertyVarPrefix}Count";
source.AppendLine($"{indent}{ruleArguments[1]} {propertyKeyEntry};");
source.AppendLine($"{indent}{valueType} {propertyValueEntry};");
source.AppendLine($"{indent}var {propertyCount} = reader.ReadEncodedInt();");
source.AppendLine($"{indent}{propertyName} = new System.Collections.Generic.Dictionary<{keyType}, {valueType}>({propertyCount});");
source.AppendLine($"{indent}for (var {propertyIndex} = 0; {propertyIndex} < {propertyCount}; {propertyIndex}++)");
source.AppendLine($"{indent}{{");
var serializableKeyElement = new SerializableProperty
{
Name = propertyKeyEntry,
Type = keyType,
Rule = keyElementRule.RuleName,
RuleArguments = keyRuleArguments
};
keyElementRule.GenerateDeserializationMethod(source, $"{indent} ", serializableKeyElement, parentReference);
var serializableValueElement = new SerializableProperty
{
Name = propertyValueEntry,
Type = valueType,
Rule = valueElementRule.RuleName,
RuleArguments = valueRuleArguments
};
valueElementRule.GenerateDeserializationMethod(source, $"{indent} ", serializableValueElement, parentReference);
source.AppendLine($"{indent} {propertyName}.Add({propertyKeyEntry}, {propertyValueEntry});");
source.AppendLine($"{indent}}}");
}
public override void GenerateSerializationMethod(StringBuilder source, string indent, SerializableProperty property)
{
var expectedRule = RuleName;
var ruleName = property.Rule;
if (expectedRule != ruleName)
{
throw new ArgumentException($"Invalid rule applied to property {ruleName}. Expecting {expectedRule}, but received {ruleName}.");
}
var ruleArguments = property.RuleArguments;
var index = 0;
var shouldTidy = ruleArguments![index++].Contains("@Tidy");
var keyType = ruleArguments![index++];
var keyElementRule = SerializableMigrationRulesEngine.Rules[ruleArguments![index++]];
var keyRuleArguments = new string[int.Parse(ruleArguments[index++])];
if (keyRuleArguments.Length > 0)
{
Array.Copy(ruleArguments, index, keyRuleArguments, 0, keyRuleArguments.Length);
index += keyRuleArguments.Length;
}
var valueType = ruleArguments[index++];
var valueElementRule = SerializableMigrationRulesEngine.Rules[ruleArguments[index++]];
var valueRuleArguments = new string[int.Parse(ruleArguments[index++])];
if (valueRuleArguments.Length > 0)
{
Array.Copy(ruleArguments, index, valueRuleArguments, 0, valueRuleArguments.Length);
}
var propertyName = property.Name;
var propertyVarPrefix = $"{char.ToLower(propertyName[0])}{propertyName.Substring(1, propertyName.Length - 1)}";
var propertyKeyEntry = $"{propertyVarPrefix}Key";
var propertyValueEntry = $"{propertyVarPrefix}Value";
var propertyCount = $"{propertyVarPrefix}Count";
if (shouldTidy)
{
source.AppendLine($"{indent}{property.Name}?.Tidy();");
}
source.AppendLine($"{indent}var {propertyCount} = {property.Name}?.Count ?? 0;");
source.AppendLine($"{indent}writer.WriteEncodedInt({propertyCount});");
source.AppendLine($"{indent}if ({propertyCount} > 0)");
source.AppendLine($"{indent}{{");
source.AppendLine($"{indent} foreach (var ({propertyKeyEntry}, {propertyValueEntry}) in {property.Name}!)");
source.AppendLine($"{indent} {{");
var serializableKeyElement = new SerializableProperty
{
Name = propertyKeyEntry,
Type = keyType,
Rule = keyElementRule.RuleName,
RuleArguments = keyRuleArguments
};
keyElementRule.GenerateSerializationMethod(source, $"{indent} ", serializableKeyElement);
var serializableValueElement = new SerializableProperty
{
Name = propertyValueEntry,
Type = valueType,
Rule = valueElementRule.RuleName,
RuleArguments = valueRuleArguments
};
valueElementRule.GenerateSerializationMethod(source, $"{indent} ", serializableValueElement);
source.AppendLine($"{indent} }}");
source.AppendLine($"{indent}}}");
}
}

View file

@ -19,54 +19,55 @@ using System.Text;
using Microsoft.CodeAnalysis;
using SerializationGenerator;
namespace SerializableMigration
namespace SerializableMigration;
public class EnumMigrationRule : MigrationRule
{
public class EnumMigrationRule : ISerializableMigrationRule
public override string RuleName => nameof(EnumMigrationRule);
public override bool GenerateRuleState(
Compilation compilation,
ISymbol symbol,
ImmutableArray<AttributeData> attributes,
ImmutableArray<INamedTypeSymbol> serializableTypes,
ImmutableArray<INamedTypeSymbol> embeddedSerializableTypes,
ISymbol? parentSymbol,
out string[] ruleArguments
)
{
public string RuleName => nameof(EnumMigrationRule);
public bool GenerateRuleState(
Compilation compilation,
ISymbol symbol,
ImmutableArray<AttributeData> attributes,
ImmutableArray<INamedTypeSymbol> serializableTypes,
ImmutableArray<INamedTypeSymbol> embeddedSerializableTypes,
ISymbol? parentSymbol,
out string[] ruleArguments
)
if (symbol is not ITypeSymbol typeSymbol || !typeSymbol.IsEnum())
{
if (symbol is not ITypeSymbol typeSymbol || !typeSymbol.IsEnum())
{
ruleArguments = null;
return false;
}
ruleArguments = Array.Empty<string>();
return true;
ruleArguments = null;
return false;
}
public void GenerateDeserializationMethod(StringBuilder source, string indent, SerializableProperty property, string? parentReference)
{
var expectedRule = RuleName;
var ruleName = property.Rule;
if (expectedRule != ruleName)
{
throw new ArgumentException($"Invalid rule applied to property {ruleName}. Expecting {expectedRule}, but received {ruleName}.");
}
ruleArguments = Array.Empty<string>();
return true;
}
source.AppendLine($"{indent}{property.Name} = reader.ReadEnum<{property.Type}>();");
public override void GenerateDeserializationMethod(
StringBuilder source, string indent, SerializableProperty property, string? parentReference, bool isMigration = false
)
{
var expectedRule = RuleName;
var ruleName = property.Rule;
if (expectedRule != ruleName)
{
throw new ArgumentException($"Invalid rule applied to property {ruleName}. Expecting {expectedRule}, but received {ruleName}.");
}
public void GenerateSerializationMethod(StringBuilder source, string indent, SerializableProperty property)
{
var expectedRule = RuleName;
var ruleName = property.Rule;
if (expectedRule != ruleName)
{
throw new ArgumentException($"Invalid rule applied to property {ruleName}. Expecting {expectedRule}, but received {ruleName}.");
}
source.AppendLine($"{indent}{property.Name} = reader.ReadEnum<{property.Type}>();");
}
source.AppendLine($"{indent}writer.WriteEnum<{property.Type}>({property.Name});");
public override void GenerateSerializationMethod(StringBuilder source, string indent, SerializableProperty property)
{
var expectedRule = RuleName;
var ruleName = property.Rule;
if (expectedRule != ruleName)
{
throw new ArgumentException($"Invalid rule applied to property {ruleName}. Expecting {expectedRule}, but received {ruleName}.");
}
source.AppendLine($"{indent}writer.WriteEnum<{property.Type}>({property.Name});");
}
}

View file

@ -20,151 +20,152 @@ using System.Text;
using Microsoft.CodeAnalysis;
using SerializationGenerator;
namespace SerializableMigration
namespace SerializableMigration;
public class HashSetMigrationRule : MigrationRule
{
public class HashSetMigrationRule : ISerializableMigrationRule
public override string RuleName => nameof(HashSetMigrationRule);
public override bool GenerateRuleState(
Compilation compilation,
ISymbol symbol,
ImmutableArray<AttributeData> attributes,
ImmutableArray<INamedTypeSymbol> serializableTypes,
ImmutableArray<INamedTypeSymbol> embeddedSerializableTypes,
ISymbol? parentSymbol,
out string[] ruleArguments
)
{
public string RuleName => nameof(HashSetMigrationRule);
public bool GenerateRuleState(
Compilation compilation,
ISymbol symbol,
ImmutableArray<AttributeData> attributes,
ImmutableArray<INamedTypeSymbol> serializableTypes,
ImmutableArray<INamedTypeSymbol> embeddedSerializableTypes,
ISymbol? parentSymbol,
out string[] ruleArguments
)
if (symbol is not INamedTypeSymbol namedTypeSymbol || !symbol.IsHashSet(compilation))
{
if (symbol is not INamedTypeSymbol namedTypeSymbol || !symbol.IsHashSet(compilation))
{
ruleArguments = null;
return false;
}
var setTypeSymbol = namedTypeSymbol.TypeArguments[0];
var serializableSetType = SerializableMigrationRulesEngine.GenerateSerializableProperty(
compilation,
"SetEntry",
setTypeSymbol,
0,
attributes,
serializableTypes,
embeddedSerializableTypes,
parentSymbol,
null
);
var extraOptions = "";
if (attributes.Any(a => a.IsTidy(compilation)))
{
extraOptions += "@Tidy";
}
var length = serializableSetType.RuleArguments?.Length ?? 0;
ruleArguments = new string[length + 3];
ruleArguments[0] = extraOptions;
ruleArguments[1] = setTypeSymbol.ToDisplayString();
ruleArguments[2] = serializableSetType.Rule;
if (length > 0)
{
Array.Copy(serializableSetType.RuleArguments, 0, ruleArguments, 3, length);
}
return true;
ruleArguments = null;
return false;
}
public void GenerateDeserializationMethod(StringBuilder source, string indent, SerializableProperty property, string? parentReference)
var setTypeSymbol = namedTypeSymbol.TypeArguments[0];
var serializableSetType = SerializableMigrationRulesEngine.GenerateSerializableProperty(
compilation,
"SetEntry",
setTypeSymbol,
0,
attributes,
serializableTypes,
embeddedSerializableTypes,
parentSymbol,
null
);
var extraOptions = "";
if (attributes.Any(a => a.IsTidy(compilation)))
{
var expectedRule = RuleName;
var ruleName = property.Rule;
if (expectedRule != ruleName)
{
throw new ArgumentException($"Invalid rule applied to property {ruleName}. Expecting {expectedRule}, but received {ruleName}.");
}
var ruleArguments = property.RuleArguments;
var hasExtraOptions = ruleArguments![0] == "" || ruleArguments[0].StartsWith("@", StringComparison.Ordinal);
var argumentsOffset = hasExtraOptions ? 1 : 0;
var setElementRule = SerializableMigrationRulesEngine.Rules[ruleArguments[1 + argumentsOffset]];
var setElementRuleArguments = new string[ruleArguments.Length - 2 - argumentsOffset];
Array.Copy(ruleArguments, 2 + argumentsOffset, setElementRuleArguments, 0, ruleArguments.Length - 2 - argumentsOffset);
var propertyName = property.Name;
var propertyVarPrefix = $"{char.ToLower(propertyName[0])}{propertyName.Substring(1, propertyName.Length - 1)}";
var propertyIndex = $"{propertyVarPrefix}Index";
var propertyEntry = $"{propertyVarPrefix}Entry";
var propertyCount = $"{propertyVarPrefix}Count";
source.AppendLine($"{indent}{ruleArguments[argumentsOffset]} {propertyEntry};");
source.AppendLine($"{indent}var {propertyCount} = reader.ReadEncodedInt();");
source.AppendLine($"{indent}{property.Name} = new System.Collections.Generic.HashSet<{ruleArguments[argumentsOffset]}>({propertyCount});");
source.AppendLine($"{indent}for (var {propertyIndex} = 0; {propertyIndex} < {propertyCount}; {propertyIndex}++)");
source.AppendLine($"{indent}{{");
var serializableSetElement = new SerializableProperty
{
Name = propertyEntry,
Type = ruleArguments[argumentsOffset],
Rule = setElementRule.RuleName,
RuleArguments = setElementRuleArguments
};
setElementRule.GenerateDeserializationMethod(source, $"{indent} ", serializableSetElement, parentReference);
source.AppendLine($"{indent} {property.Name}.Add({propertyEntry});");
source.AppendLine($"{indent}}}");
extraOptions += "@Tidy";
}
public void GenerateSerializationMethod(StringBuilder source, string indent, SerializableProperty property)
var length = serializableSetType.RuleArguments?.Length ?? 0;
ruleArguments = new string[length + 3];
ruleArguments[0] = extraOptions;
ruleArguments[1] = setTypeSymbol.ToDisplayString();
ruleArguments[2] = serializableSetType.Rule;
if (length > 0)
{
var expectedRule = RuleName;
var ruleName = property.Rule;
if (expectedRule != ruleName)
{
throw new ArgumentException($"Invalid rule applied to property {ruleName}. Expecting {expectedRule}, but received {ruleName}.");
}
var ruleArguments = property.RuleArguments;
var hasExtraOptions = ruleArguments![0] == "" || ruleArguments[0].StartsWith("@", StringComparison.Ordinal);
var shouldTidy = hasExtraOptions && ruleArguments[0].Contains("@Tidy");
var argumentsOffset = hasExtraOptions ? 1 : 0;
var setElementRule = SerializableMigrationRulesEngine.Rules[ruleArguments[1 + argumentsOffset]];
var setElementRuleArguments = new string[ruleArguments.Length - 2 - argumentsOffset];
Array.Copy(ruleArguments, 2 + argumentsOffset, setElementRuleArguments, 0, ruleArguments.Length - 2 - argumentsOffset);
var propertyName = property.Name;
var propertyVarPrefix = $"{char.ToLower(propertyName[0])}{propertyName.Substring(1, propertyName.Length - 1)}";
var propertyEntry = $"{propertyVarPrefix}Entry";
var propertyCount = $"{propertyVarPrefix}Count";
if (shouldTidy)
{
source.AppendLine($"{indent}{property.Name}?.Tidy();");
}
source.AppendLine($"{indent}var {propertyCount} = {property.Name}?.Count ?? 0;");
source.AppendLine($"{indent}writer.WriteEncodedInt({propertyCount});");
source.AppendLine($"{indent}if ({propertyCount} > 0)");
source.AppendLine($"{indent}{{");
source.AppendLine($"{indent} foreach (var {propertyEntry} in {property.Name}!)");
source.AppendLine($"{indent} {{");
var serializableSetElement = new SerializableProperty
{
Name = propertyEntry,
Type = ruleArguments[argumentsOffset],
Rule = setElementRule.RuleName,
RuleArguments = setElementRuleArguments
};
setElementRule.GenerateSerializationMethod(source, $"{indent} ", serializableSetElement);
source.AppendLine($"{indent} }}");
source.AppendLine($"{indent}}}");
Array.Copy(serializableSetType.RuleArguments, 0, ruleArguments, 3, length);
}
return true;
}
public override void GenerateDeserializationMethod(
StringBuilder source, string indent, SerializableProperty property, string? parentReference, bool isMigration = false
)
{
var expectedRule = RuleName;
var ruleName = property.Rule;
if (expectedRule != ruleName)
{
throw new ArgumentException($"Invalid rule applied to property {ruleName}. Expecting {expectedRule}, but received {ruleName}.");
}
var ruleArguments = property.RuleArguments;
var hasExtraOptions = ruleArguments![0] == "" || ruleArguments[0].StartsWith("@", StringComparison.Ordinal);
var argumentsOffset = hasExtraOptions ? 1 : 0;
var setElementRule = SerializableMigrationRulesEngine.Rules[ruleArguments[1 + argumentsOffset]];
var setElementRuleArguments = new string[ruleArguments.Length - 2 - argumentsOffset];
Array.Copy(ruleArguments, 2 + argumentsOffset, setElementRuleArguments, 0, ruleArguments.Length - 2 - argumentsOffset);
var propertyName = property.Name;
var propertyVarPrefix = $"{char.ToLower(propertyName[0])}{propertyName.Substring(1, propertyName.Length - 1)}";
var propertyIndex = $"{propertyVarPrefix}Index";
var propertyEntry = $"{propertyVarPrefix}Entry";
var propertyCount = $"{propertyVarPrefix}Count";
source.AppendLine($"{indent}{ruleArguments[argumentsOffset]} {propertyEntry};");
source.AppendLine($"{indent}var {propertyCount} = reader.ReadEncodedInt();");
source.AppendLine($"{indent}{property.Name} = new System.Collections.Generic.HashSet<{ruleArguments[argumentsOffset]}>({propertyCount});");
source.AppendLine($"{indent}for (var {propertyIndex} = 0; {propertyIndex} < {propertyCount}; {propertyIndex}++)");
source.AppendLine($"{indent}{{");
var serializableSetElement = new SerializableProperty
{
Name = propertyEntry,
Type = ruleArguments[argumentsOffset],
Rule = setElementRule.RuleName,
RuleArguments = setElementRuleArguments
};
setElementRule.GenerateDeserializationMethod(source, $"{indent} ", serializableSetElement, parentReference);
source.AppendLine($"{indent} {property.Name}.Add({propertyEntry});");
source.AppendLine($"{indent}}}");
}
public override void GenerateSerializationMethod(StringBuilder source, string indent, SerializableProperty property)
{
var expectedRule = RuleName;
var ruleName = property.Rule;
if (expectedRule != ruleName)
{
throw new ArgumentException($"Invalid rule applied to property {ruleName}. Expecting {expectedRule}, but received {ruleName}.");
}
var ruleArguments = property.RuleArguments;
var hasExtraOptions = ruleArguments![0] == "" || ruleArguments[0].StartsWith("@", StringComparison.Ordinal);
var shouldTidy = hasExtraOptions && ruleArguments[0].Contains("@Tidy");
var argumentsOffset = hasExtraOptions ? 1 : 0;
var setElementRule = SerializableMigrationRulesEngine.Rules[ruleArguments[1 + argumentsOffset]];
var setElementRuleArguments = new string[ruleArguments.Length - 2 - argumentsOffset];
Array.Copy(ruleArguments, 2 + argumentsOffset, setElementRuleArguments, 0, ruleArguments.Length - 2 - argumentsOffset);
var propertyName = property.Name;
var propertyVarPrefix = $"{char.ToLower(propertyName[0])}{propertyName.Substring(1, propertyName.Length - 1)}";
var propertyEntry = $"{propertyVarPrefix}Entry";
var propertyCount = $"{propertyVarPrefix}Count";
if (shouldTidy)
{
source.AppendLine($"{indent}{property.Name}?.Tidy();");
}
source.AppendLine($"{indent}var {propertyCount} = {property.Name}?.Count ?? 0;");
source.AppendLine($"{indent}writer.WriteEncodedInt({propertyCount});");
source.AppendLine($"{indent}if ({propertyCount} > 0)");
source.AppendLine($"{indent}{{");
source.AppendLine($"{indent} foreach (var {propertyEntry} in {property.Name}!)");
source.AppendLine($"{indent} {{");
var serializableSetElement = new SerializableProperty
{
Name = propertyEntry,
Type = ruleArguments[argumentsOffset],
Rule = setElementRule.RuleName,
RuleArguments = setElementRuleArguments
};
setElementRule.GenerateSerializationMethod(source, $"{indent} ", serializableSetElement);
source.AppendLine($"{indent} }}");
source.AppendLine($"{indent}}}");
}
}

View file

@ -19,206 +19,207 @@ using System.Text;
using Microsoft.CodeAnalysis;
using SerializationGenerator;
namespace SerializableMigration
namespace SerializableMigration;
public class KeyValuePairMigrationRule : MigrationRule
{
public class KeyValuePairMigrationRule : ISerializableMigrationRule
public override string RuleName => nameof(KeyValuePairMigrationRule);
public override bool GenerateRuleState(
Compilation compilation,
ISymbol symbol,
ImmutableArray<AttributeData> attributes,
ImmutableArray<INamedTypeSymbol> serializableTypes,
ImmutableArray<INamedTypeSymbol> embeddedSerializableTypes,
ISymbol? parentSymbol,
out string[] ruleArguments
)
{
public string RuleName => nameof(KeyValuePairMigrationRule);
public bool GenerateRuleState(
Compilation compilation,
ISymbol symbol,
ImmutableArray<AttributeData> attributes,
ImmutableArray<INamedTypeSymbol> serializableTypes,
ImmutableArray<INamedTypeSymbol> embeddedSerializableTypes,
ISymbol? parentSymbol,
out string[] ruleArguments
)
if (symbol is not INamedTypeSymbol namedTypeSymbol || !symbol.IsKeyValuePair(compilation))
{
if (symbol is not INamedTypeSymbol namedTypeSymbol || !symbol.IsKeyValuePair(compilation))
{
ruleArguments = null;
return false;
}
var keySymbolType = namedTypeSymbol.TypeArguments[0];
var keySerializedProperty = SerializableMigrationRulesEngine.GenerateSerializableProperty(
compilation,
"key",
keySymbolType,
0,
attributes,
serializableTypes,
embeddedSerializableTypes,
parentSymbol,
null
);
var valueSymbolType = namedTypeSymbol.TypeArguments[1];
var valueSerializedProperty = SerializableMigrationRulesEngine.GenerateSerializableProperty(
compilation,
"value",
valueSymbolType,
1,
attributes,
serializableTypes,
embeddedSerializableTypes,
parentSymbol,
null
);
var keyArgumentsLength = keySerializedProperty.RuleArguments?.Length ?? 0;
var valueArgumentsLength = valueSerializedProperty.RuleArguments?.Length ?? 0;
var index = 0;
// Key
ruleArguments = new string[6 + keyArgumentsLength + valueArgumentsLength];
ruleArguments[index++] = ""; // Extra options
ruleArguments[index++] = keySymbolType.ToDisplayString();
ruleArguments[index++] = keySerializedProperty.Rule;
ruleArguments[index++] = keyArgumentsLength.ToString();
if (keyArgumentsLength > 0)
{
Array.Copy(keySerializedProperty.RuleArguments!, 0, ruleArguments, index, keyArgumentsLength);
index += keyArgumentsLength;
}
// Value
ruleArguments[index++] = valueSymbolType.ToDisplayString();
ruleArguments[index++] = valueSerializedProperty.Rule;
if (valueArgumentsLength > 0)
{
Array.Copy(valueSerializedProperty.RuleArguments!, 0, ruleArguments, index, valueArgumentsLength);
}
return true;
ruleArguments = null;
return false;
}
public void GenerateDeserializationMethod(StringBuilder source, string indent, SerializableProperty property, string? parentReference)
var keySymbolType = namedTypeSymbol.TypeArguments[0];
var keySerializedProperty = SerializableMigrationRulesEngine.GenerateSerializableProperty(
compilation,
"key",
keySymbolType,
0,
attributes,
serializableTypes,
embeddedSerializableTypes,
parentSymbol,
null
);
var valueSymbolType = namedTypeSymbol.TypeArguments[1];
var valueSerializedProperty = SerializableMigrationRulesEngine.GenerateSerializableProperty(
compilation,
"value",
valueSymbolType,
1,
attributes,
serializableTypes,
embeddedSerializableTypes,
parentSymbol,
null
);
var keyArgumentsLength = keySerializedProperty.RuleArguments?.Length ?? 0;
var valueArgumentsLength = valueSerializedProperty.RuleArguments?.Length ?? 0;
var index = 0;
// Key
ruleArguments = new string[6 + keyArgumentsLength + valueArgumentsLength];
ruleArguments[index++] = ""; // Extra options
ruleArguments[index++] = keySymbolType.ToDisplayString();
ruleArguments[index++] = keySerializedProperty.Rule;
ruleArguments[index++] = keyArgumentsLength.ToString();
if (keyArgumentsLength > 0)
{
var expectedRule = RuleName;
var ruleName = property.Rule;
if (expectedRule != ruleName)
{
throw new ArgumentException($"Invalid rule applied to property {ruleName}. Expecting {expectedRule}, but received {ruleName}.");
}
var ruleArguments = property.RuleArguments;
var index = 1; // skip extra options
var keyType = ruleArguments![index++];
var keyRule = SerializableMigrationRulesEngine.Rules[ruleArguments[index++]];
var keyRuleArguments = new string[int.Parse(ruleArguments[index++])];
if (keyRuleArguments.Length > 0)
{
Array.Copy(ruleArguments, index, keyRuleArguments, 0, keyRuleArguments.Length);
index += keyRuleArguments.Length;
}
var serializableKeyProperty = new SerializableProperty
{
Name = "key",
Type = keyType,
Rule = keyRule.RuleName,
RuleArguments = keyRuleArguments
};
keyRule.GenerateDeserializationMethod(
source,
indent,
serializableKeyProperty,
parentReference
);
var valueType = ruleArguments[index++];
var valueRule = SerializableMigrationRulesEngine.Rules[ruleArguments[index++]];
var valueRuleArguments = new string[int.Parse(ruleArguments[index++])];
if (valueRuleArguments.Length > 0)
{
Array.Copy(ruleArguments, index, valueRuleArguments, 0, valueRuleArguments.Length);
}
var serializableValueProperty = new SerializableProperty
{
Name = "value",
Type = valueType,
Rule = valueRule.RuleName,
RuleArguments = valueRuleArguments
};
valueRule.GenerateDeserializationMethod(
source,
indent,
serializableValueProperty,
parentReference
);
source.AppendLine(
$"{indent}{property.Name} = new {SymbolMetadata.KEYVALUEPAIR_STRUCT}<{keyType}, {valueType}>(key, value);"
);
Array.Copy(keySerializedProperty.RuleArguments!, 0, ruleArguments, index, keyArgumentsLength);
index += keyArgumentsLength;
}
public void GenerateSerializationMethod(StringBuilder source, string indent, SerializableProperty property)
// Value
ruleArguments[index++] = valueSymbolType.ToDisplayString();
ruleArguments[index++] = valueSerializedProperty.Rule;
if (valueArgumentsLength > 0)
{
var expectedRule = RuleName;
var ruleName = property.Rule;
if (expectedRule != ruleName)
{
throw new ArgumentException($"Invalid rule applied to property {ruleName}. Expecting {expectedRule}, but received {ruleName}.");
}
var ruleArguments = property.RuleArguments;
var index = 1; // skip extra options
var keyType = ruleArguments![index++];
var keyRule = SerializableMigrationRulesEngine.Rules[ruleArguments[index++]];
var keyRuleArguments = new string[int.Parse(ruleArguments[index++])];
if (keyRuleArguments.Length > 0)
{
Array.Copy(ruleArguments, index, keyRuleArguments, 0, keyRuleArguments.Length);
index += keyRuleArguments.Length;
}
var serializableKeyProperty = new SerializableProperty
{
Name = $"{property.Name}.Key",
Type = keyType,
Rule = keyRule.RuleName,
RuleArguments = keyRuleArguments
};
keyRule.GenerateSerializationMethod(
source,
indent,
serializableKeyProperty
);
var valueType = ruleArguments[index++];
var valueRule = SerializableMigrationRulesEngine.Rules[ruleArguments[index++]];
var valueRuleArguments = new string[int.Parse(ruleArguments[index++])];
if (valueRuleArguments.Length > 0)
{
Array.Copy(ruleArguments, index, valueRuleArguments, 0, valueRuleArguments.Length);
}
var serializableValueProperty = new SerializableProperty
{
Name = $"{property.Name}.Value",
Type = valueType,
Rule = valueRule.RuleName,
RuleArguments = valueRuleArguments
};
valueRule.GenerateSerializationMethod(
source,
indent,
serializableValueProperty
);
Array.Copy(valueSerializedProperty.RuleArguments!, 0, ruleArguments, index, valueArgumentsLength);
}
return true;
}
public override void GenerateDeserializationMethod(
StringBuilder source, string indent, SerializableProperty property, string? parentReference, bool isMigration = false
)
{
var expectedRule = RuleName;
var ruleName = property.Rule;
if (expectedRule != ruleName)
{
throw new ArgumentException($"Invalid rule applied to property {ruleName}. Expecting {expectedRule}, but received {ruleName}.");
}
var ruleArguments = property.RuleArguments;
var index = 1; // skip extra options
var keyType = ruleArguments![index++];
var keyRule = SerializableMigrationRulesEngine.Rules[ruleArguments[index++]];
var keyRuleArguments = new string[int.Parse(ruleArguments[index++])];
if (keyRuleArguments.Length > 0)
{
Array.Copy(ruleArguments, index, keyRuleArguments, 0, keyRuleArguments.Length);
index += keyRuleArguments.Length;
}
var serializableKeyProperty = new SerializableProperty
{
Name = "key",
Type = keyType,
Rule = keyRule.RuleName,
RuleArguments = keyRuleArguments
};
keyRule.GenerateDeserializationMethod(
source,
indent,
serializableKeyProperty,
parentReference
);
var valueType = ruleArguments[index++];
var valueRule = SerializableMigrationRulesEngine.Rules[ruleArguments[index++]];
var valueRuleArguments = new string[int.Parse(ruleArguments[index++])];
if (valueRuleArguments.Length > 0)
{
Array.Copy(ruleArguments, index, valueRuleArguments, 0, valueRuleArguments.Length);
}
var serializableValueProperty = new SerializableProperty
{
Name = "value",
Type = valueType,
Rule = valueRule.RuleName,
RuleArguments = valueRuleArguments
};
valueRule.GenerateDeserializationMethod(
source,
indent,
serializableValueProperty,
parentReference
);
source.AppendLine(
$"{indent}{property.Name} = new {SymbolMetadata.KEYVALUEPAIR_STRUCT}<{keyType}, {valueType}>(key, value);"
);
}
public override void GenerateSerializationMethod(StringBuilder source, string indent, SerializableProperty property)
{
var expectedRule = RuleName;
var ruleName = property.Rule;
if (expectedRule != ruleName)
{
throw new ArgumentException($"Invalid rule applied to property {ruleName}. Expecting {expectedRule}, but received {ruleName}.");
}
var ruleArguments = property.RuleArguments;
var index = 1; // skip extra options
var keyType = ruleArguments![index++];
var keyRule = SerializableMigrationRulesEngine.Rules[ruleArguments[index++]];
var keyRuleArguments = new string[int.Parse(ruleArguments[index++])];
if (keyRuleArguments.Length > 0)
{
Array.Copy(ruleArguments, index, keyRuleArguments, 0, keyRuleArguments.Length);
index += keyRuleArguments.Length;
}
var serializableKeyProperty = new SerializableProperty
{
Name = $"{property.Name}.Key",
Type = keyType,
Rule = keyRule.RuleName,
RuleArguments = keyRuleArguments
};
keyRule.GenerateSerializationMethod(
source,
indent,
serializableKeyProperty
);
var valueType = ruleArguments[index++];
var valueRule = SerializableMigrationRulesEngine.Rules[ruleArguments[index++]];
var valueRuleArguments = new string[int.Parse(ruleArguments[index++])];
if (valueRuleArguments.Length > 0)
{
Array.Copy(ruleArguments, index, valueRuleArguments, 0, valueRuleArguments.Length);
}
var serializableValueProperty = new SerializableProperty
{
Name = $"{property.Name}.Value",
Type = valueType,
Rule = valueRule.RuleName,
RuleArguments = valueRuleArguments
};
valueRule.GenerateSerializationMethod(
source,
indent,
serializableValueProperty
);
}
}

View file

@ -20,152 +20,153 @@ using System.Text;
using Microsoft.CodeAnalysis;
using SerializationGenerator;
namespace SerializableMigration
namespace SerializableMigration;
public class ListMigrationRule : MigrationRule
{
public class ListMigrationRule : ISerializableMigrationRule
public override string RuleName => nameof(ListMigrationRule);
public override bool GenerateRuleState(
Compilation compilation,
ISymbol symbol,
ImmutableArray<AttributeData> attributes,
ImmutableArray<INamedTypeSymbol> serializableTypes,
ImmutableArray<INamedTypeSymbol> embeddedSerializableTypes,
ISymbol? parentSymbol,
out string[] ruleArguments
)
{
public string RuleName => nameof(ListMigrationRule);
public bool GenerateRuleState(
Compilation compilation,
ISymbol symbol,
ImmutableArray<AttributeData> attributes,
ImmutableArray<INamedTypeSymbol> serializableTypes,
ImmutableArray<INamedTypeSymbol> embeddedSerializableTypes,
ISymbol? parentSymbol,
out string[] ruleArguments
)
if (symbol is not INamedTypeSymbol namedTypeSymbol || !symbol.IsList(compilation))
{
if (symbol is not INamedTypeSymbol namedTypeSymbol || !symbol.IsList(compilation))
{
ruleArguments = null;
return false;
}
var listTypeSymbol = namedTypeSymbol.TypeArguments[0];
var serializableListType = SerializableMigrationRulesEngine.GenerateSerializableProperty(
compilation,
"ListEntry",
listTypeSymbol,
0,
attributes,
serializableTypes,
embeddedSerializableTypes,
parentSymbol,
null
);
var extraOptions = "";
if (attributes.Any(a => a.IsTidy(compilation)))
{
extraOptions += "@Tidy";
}
var length = serializableListType.RuleArguments?.Length ?? 0;
ruleArguments = new string[length + 3];
ruleArguments[0] = extraOptions;
ruleArguments[1] = listTypeSymbol.ToDisplayString();
ruleArguments[2] = serializableListType.Rule;
if (length > 0)
{
Array.Copy(serializableListType.RuleArguments!, 0, ruleArguments, 3, length);
}
return true;
ruleArguments = null;
return false;
}
public void GenerateDeserializationMethod(StringBuilder source, string indent, SerializableProperty property, string? parentReference)
var listTypeSymbol = namedTypeSymbol.TypeArguments[0];
var serializableListType = SerializableMigrationRulesEngine.GenerateSerializableProperty(
compilation,
"ListEntry",
listTypeSymbol,
0,
attributes,
serializableTypes,
embeddedSerializableTypes,
parentSymbol,
null
);
var extraOptions = "";
if (attributes.Any(a => a.IsTidy(compilation)))
{
var expectedRule = RuleName;
var ruleName = property.Rule;
if (expectedRule != ruleName)
{
throw new ArgumentException($"Invalid rule applied to property {ruleName}. Expecting {expectedRule}, but received {ruleName}.");
}
var ruleArguments = property.RuleArguments;
var hasExtraOptions = ruleArguments![0] == "" || ruleArguments[0].StartsWith("@", StringComparison.Ordinal);
var argumentsOffset = hasExtraOptions ? 1 : 0;
var listElementRule = SerializableMigrationRulesEngine.Rules[ruleArguments[argumentsOffset + 1]];
var listElementRuleArguments = new string[ruleArguments.Length - 2 - argumentsOffset];
Array.Copy(ruleArguments, 2 + argumentsOffset, listElementRuleArguments, 0, ruleArguments.Length - 2 - argumentsOffset);
var propertyName = property.Name;
var propertyVarPrefix = $"{char.ToLower(propertyName[0])}{propertyName.Substring(1, propertyName.Length - 1)}";
var propertyIndex = $"{propertyVarPrefix}Index";
var propertyEntry = $"{propertyVarPrefix}Entry";
var propertyCount = $"{propertyVarPrefix}Count";
source.AppendLine($"{indent}{ruleArguments[argumentsOffset]} {propertyEntry};");
source.AppendLine($"{indent}var {propertyCount} = reader.ReadEncodedInt();");
source.AppendLine($"{indent}{propertyName} = new System.Collections.Generic.List<{ruleArguments[argumentsOffset]}>({propertyCount});");
source.AppendLine($"{indent}for (var {propertyIndex} = 0; {propertyIndex} < {propertyCount}; {propertyIndex}++)");
source.AppendLine($"{indent}{{");
var serializableListElement = new SerializableProperty
{
Name = propertyEntry,
Type = ruleArguments[argumentsOffset],
Rule = listElementRule.RuleName,
RuleArguments = listElementRuleArguments
};
listElementRule.GenerateDeserializationMethod(source, $"{indent} ", serializableListElement, parentReference);
source.AppendLine($"{indent} {propertyName}.Add({propertyEntry});");
source.AppendLine($"{indent}}}");
extraOptions += "@Tidy";
}
public void GenerateSerializationMethod(StringBuilder source, string indent, SerializableProperty property)
var length = serializableListType.RuleArguments?.Length ?? 0;
ruleArguments = new string[length + 3];
ruleArguments[0] = extraOptions;
ruleArguments[1] = listTypeSymbol.ToDisplayString();
ruleArguments[2] = serializableListType.Rule;
if (length > 0)
{
var expectedRule = RuleName;
var ruleName = property.Rule;
if (expectedRule != ruleName)
{
throw new ArgumentException($"Invalid rule applied to property {ruleName}. Expecting {expectedRule}, but received {ruleName}.");
}
var ruleArguments = property.RuleArguments;
var hasExtraOptions = ruleArguments![0] == "" || ruleArguments[0].StartsWith("@", StringComparison.Ordinal);
var shouldTidy = hasExtraOptions && ruleArguments[0].Contains("@Tidy");
var argumentsOffset = hasExtraOptions ? 1 : 0;
var listElementRule = SerializableMigrationRulesEngine.Rules[ruleArguments[1 + argumentsOffset]];
var listElementRuleArguments = new string[ruleArguments.Length - 2 - argumentsOffset];
Array.Copy(ruleArguments, 2 + argumentsOffset, listElementRuleArguments, 0, ruleArguments.Length - 2 - argumentsOffset);
var propertyName = property.Name;
var propertyVarPrefix = $"{char.ToLower(propertyName[0])}{propertyName.Substring(1, propertyName.Length - 1)}";
var propertyEntry = $"{propertyVarPrefix}Entry";
var propertyCount = $"{propertyVarPrefix}Count";
if (shouldTidy)
{
source.AppendLine($"{indent}{property.Name}?.Tidy();");
}
source.AppendLine($"{indent}var {propertyCount} = {property.Name}?.Count ?? 0;");
source.AppendLine($"{indent}writer.WriteEncodedInt({propertyCount});");
source.AppendLine($"{indent}if ({propertyCount} > 0)");
source.AppendLine($"{indent}{{");
source.AppendLine($"{indent} foreach (var {propertyEntry} in {property.Name}!)");
source.AppendLine($"{indent} {{");
var serializableListElement = new SerializableProperty
{
Name = propertyEntry,
Type = ruleArguments[argumentsOffset],
Rule = listElementRule.RuleName,
RuleArguments = listElementRuleArguments
};
listElementRule.GenerateSerializationMethod(source, $"{indent} ", serializableListElement);
source.AppendLine($"{indent} }}");
source.AppendLine($"{indent}}}");
Array.Copy(serializableListType.RuleArguments!, 0, ruleArguments, 3, length);
}
return true;
}
public override void GenerateDeserializationMethod(
StringBuilder source, string indent, SerializableProperty property, string? parentReference, bool isMigration = false
)
{
var expectedRule = RuleName;
var ruleName = property.Rule;
if (expectedRule != ruleName)
{
throw new ArgumentException($"Invalid rule applied to property {ruleName}. Expecting {expectedRule}, but received {ruleName}.");
}
var ruleArguments = property.RuleArguments;
var hasExtraOptions = ruleArguments![0] == "" || ruleArguments[0].StartsWith("@", StringComparison.Ordinal);
var argumentsOffset = hasExtraOptions ? 1 : 0;
var listElementRule = SerializableMigrationRulesEngine.Rules[ruleArguments[argumentsOffset + 1]];
var listElementRuleArguments = new string[ruleArguments.Length - 2 - argumentsOffset];
Array.Copy(ruleArguments, 2 + argumentsOffset, listElementRuleArguments, 0, ruleArguments.Length - 2 - argumentsOffset);
var propertyName = property.Name;
var propertyVarPrefix = $"{char.ToLower(propertyName[0])}{propertyName.Substring(1, propertyName.Length - 1)}";
var propertyIndex = $"{propertyVarPrefix}Index";
var propertyEntry = $"{propertyVarPrefix}Entry";
var propertyCount = $"{propertyVarPrefix}Count";
source.AppendLine($"{indent}{ruleArguments[argumentsOffset]} {propertyEntry};");
source.AppendLine($"{indent}var {propertyCount} = reader.ReadEncodedInt();");
source.AppendLine($"{indent}{propertyName} = new System.Collections.Generic.List<{ruleArguments[argumentsOffset]}>({propertyCount});");
source.AppendLine($"{indent}for (var {propertyIndex} = 0; {propertyIndex} < {propertyCount}; {propertyIndex}++)");
source.AppendLine($"{indent}{{");
var serializableListElement = new SerializableProperty
{
Name = propertyEntry,
Type = ruleArguments[argumentsOffset],
Rule = listElementRule.RuleName,
RuleArguments = listElementRuleArguments
};
listElementRule.GenerateDeserializationMethod(source, $"{indent} ", serializableListElement, parentReference);
source.AppendLine($"{indent} {propertyName}.Add({propertyEntry});");
source.AppendLine($"{indent}}}");
}
public override void GenerateSerializationMethod(StringBuilder source, string indent, SerializableProperty property)
{
var expectedRule = RuleName;
var ruleName = property.Rule;
if (expectedRule != ruleName)
{
throw new ArgumentException($"Invalid rule applied to property {ruleName}. Expecting {expectedRule}, but received {ruleName}.");
}
var ruleArguments = property.RuleArguments;
var hasExtraOptions = ruleArguments![0] == "" || ruleArguments[0].StartsWith("@", StringComparison.Ordinal);
var shouldTidy = hasExtraOptions && ruleArguments[0].Contains("@Tidy");
var argumentsOffset = hasExtraOptions ? 1 : 0;
var listElementRule = SerializableMigrationRulesEngine.Rules[ruleArguments[1 + argumentsOffset]];
var listElementRuleArguments = new string[ruleArguments.Length - 2 - argumentsOffset];
Array.Copy(ruleArguments, 2 + argumentsOffset, listElementRuleArguments, 0, ruleArguments.Length - 2 - argumentsOffset);
var propertyName = property.Name;
var propertyVarPrefix = $"{char.ToLower(propertyName[0])}{propertyName.Substring(1, propertyName.Length - 1)}";
var propertyEntry = $"{propertyVarPrefix}Entry";
var propertyCount = $"{propertyVarPrefix}Count";
if (shouldTidy)
{
source.AppendLine($"{indent}{property.Name}?.Tidy();");
}
source.AppendLine($"{indent}var {propertyCount} = {property.Name}?.Count ?? 0;");
source.AppendLine($"{indent}writer.WriteEncodedInt({propertyCount});");
source.AppendLine($"{indent}if ({propertyCount} > 0)");
source.AppendLine($"{indent}{{");
source.AppendLine($"{indent} foreach (var {propertyEntry} in {property.Name}!)");
source.AppendLine($"{indent} {{");
var serializableListElement = new SerializableProperty
{
Name = propertyEntry,
Type = ruleArguments[argumentsOffset],
Rule = listElementRule.RuleName,
RuleArguments = listElementRuleArguments
};
listElementRule.GenerateSerializationMethod(source, $"{indent} ", serializableListElement);
source.AppendLine($"{indent} }}");
source.AppendLine($"{indent}}}");
}
}

View file

@ -0,0 +1,36 @@
using System;
using System.Collections.Immutable;
using System.Text;
using Microsoft.CodeAnalysis;
using SerializationGenerator;
namespace SerializableMigration;
public abstract class MigrationRule : ISerializableMigrationRule
{
public abstract string RuleName { get; }
public virtual void GenerateMigrationProperty(
StringBuilder source, Compilation compilation, string indent, SerializableProperty serializableProperty
)
{
var propertyType = serializableProperty.Type;
var type = compilation.GetTypeByMetadataName(propertyType)?.IsValueType == true
|| SymbolMetadata.IsPrimitiveFromTypeDisplayString(propertyType) && propertyType != "bool"
? $"{propertyType}{(serializableProperty.UsesSaveFlag == true ? "?" : "")}" : propertyType;
source.AppendLine($"{indent}internal readonly {type} {serializableProperty.Name};");
}
public abstract bool GenerateRuleState(
Compilation compilation, ISymbol symbol, ImmutableArray<AttributeData> attributes,
ImmutableArray<INamedTypeSymbol> serializableTypes,
ImmutableArray<INamedTypeSymbol> embeddedSerializableTypes, ISymbol? parentSymbol, out string[] ruleArguments
);
public abstract void GenerateDeserializationMethod(
StringBuilder source, string indent, SerializableProperty property, string? parentReference, bool isMigration = false
);
public abstract void GenerateSerializationMethod(StringBuilder source, string indent, SerializableProperty property);
}

View file

@ -20,129 +20,130 @@ using System.Text;
using Microsoft.CodeAnalysis;
using SerializationGenerator;
namespace SerializableMigration
namespace SerializableMigration;
public class PrimitiveTypeMigrationRule : MigrationRule
{
public class PrimitiveTypeMigrationRule : ISerializableMigrationRule
public override string RuleName => nameof(PrimitiveTypeMigrationRule);
public override bool GenerateRuleState(
Compilation compilation,
ISymbol symbol,
ImmutableArray<AttributeData> attributes,
ImmutableArray<INamedTypeSymbol> serializableTypes,
ImmutableArray<INamedTypeSymbol> embeddedSerializableTypes,
ISymbol? parentSymbol,
out string[] ruleArguments
)
{
public string RuleName => nameof(PrimitiveTypeMigrationRule);
public bool GenerateRuleState(
Compilation compilation,
ISymbol symbol,
ImmutableArray<AttributeData> attributes,
ImmutableArray<INamedTypeSymbol> serializableTypes,
ImmutableArray<INamedTypeSymbol> embeddedSerializableTypes,
ISymbol? parentSymbol,
out string[] ruleArguments
)
if (symbol.IsIpAddress(compilation) || symbol.IsTimeSpan(compilation))
{
if (symbol.IsIpAddress(compilation) || symbol.IsTimeSpan(compilation))
{
ruleArguments = Array.Empty<string>();
return true;
}
if (
symbol is not ITypeSymbol {
SpecialType: not (not
SpecialType.System_Boolean and not
SpecialType.System_SByte and not
SpecialType.System_Int16 and not
SpecialType.System_Int32 and not
SpecialType.System_Int64 and not
SpecialType.System_Byte and not
SpecialType.System_UInt16 and not
SpecialType.System_UInt32 and not
SpecialType.System_UInt64 and not
SpecialType.System_Single and not
SpecialType.System_Double and not
SpecialType.System_String and not
SpecialType.System_Decimal and not
SpecialType.System_DateTime)
} typeSymbol
)
{
ruleArguments = null;
return false;
}
ruleArguments = typeSymbol.SpecialType switch
{
SpecialType.System_Int32 when attributes.Any(a => a.IsEncodedInt(compilation)) =>
new[] { "EncodedInt" },
SpecialType.System_DateTime when attributes.Any(a => a.IsDeltaDateTime(compilation)) =>
new[] { "DeltaTime" },
SpecialType.System_String when attributes.Any(a => a.IsInternString(compilation)) =>
new[] { "InternString" },
_ => new[] { "" }
};
ruleArguments = Array.Empty<string>();
return true;
}
public void GenerateDeserializationMethod(StringBuilder source, string indent, SerializableProperty property, string? parentReference)
if (
symbol is not ITypeSymbol {
SpecialType: not (not
SpecialType.System_Boolean and not
SpecialType.System_SByte and not
SpecialType.System_Int16 and not
SpecialType.System_Int32 and not
SpecialType.System_Int64 and not
SpecialType.System_Byte and not
SpecialType.System_UInt16 and not
SpecialType.System_UInt32 and not
SpecialType.System_UInt64 and not
SpecialType.System_Single and not
SpecialType.System_Double and not
SpecialType.System_String and not
SpecialType.System_Decimal and not
SpecialType.System_DateTime)
} typeSymbol
)
{
var expectedRule = RuleName;
var ruleName = property.Rule;
if (expectedRule != ruleName)
{
throw new ArgumentException($"Invalid rule applied to property {ruleName}. Expecting {expectedRule}, but received {ruleName}.");
}
var propertyName = property.Name;
var argument = property.RuleArguments?.Length >= 1 ? property.RuleArguments[0] : null;
const string ipAddress = SymbolMetadata.IPADDRESS_CLASS;
const string timeSpan = SymbolMetadata.TIMESPAN_STRUCT;
const string date = "System.DateTime";
var readMethod = property.Type switch
{
"bool" => "ReadBool",
"sbyte" => "ReadSByte",
"short" => "ReadShort",
"int" when argument == "EncodedInt" => "ReadEncodedInt",
"int" => "ReadInt",
"long" => "ReadLong",
"byte" => "ReadByte",
"ushort" => "ReadUShort",
"uint" => "ReadUInt",
"ulong" => "ReadULong",
"float" => "ReadFloat",
"double" => "ReadDouble",
"string" => "ReadString",
"decimal" => "ReadDecimal",
date when argument == "DeltaTime" => "ReadDeltaTime",
date => "ReadDateTime",
ipAddress => "ReadIPAddress",
timeSpan => "ReadTimeSpan"
};
var readArgument = readMethod == "ReadString" && argument == "InternString" ? "true" : "";
source.AppendLine($"{indent}{propertyName} = reader.{readMethod}({readArgument});");
ruleArguments = null;
return false;
}
public void GenerateSerializationMethod(StringBuilder source, string indent, SerializableProperty property)
ruleArguments = typeSymbol.SpecialType switch
{
var expectedRule = RuleName;
var ruleName = property.Rule;
if (expectedRule != ruleName)
{
throw new ArgumentException($"Invalid rule applied to property {ruleName}. Expecting {expectedRule}, but received {ruleName}.");
}
SpecialType.System_Int32 when attributes.Any(a => a.IsEncodedInt(compilation)) =>
new[] { "EncodedInt" },
SpecialType.System_DateTime when attributes.Any(a => a.IsDeltaDateTime(compilation)) =>
new[] { "DeltaTime" },
SpecialType.System_String when attributes.Any(a => a.IsInternString(compilation)) =>
new[] { "InternString" },
_ => new[] { "" }
};
var propertyName = property.Name;
var argument = property.RuleArguments?.Length >= 1 ? property.RuleArguments[0] : null;
return true;
}
var writeMethod = property.Type switch
{
"System.DateTime" when argument == "DeltaTime" => "WriteDeltaTime",
"int" when argument == "EncodedInt" => "WriteEncodedInt",
_ => "Write"
};
source.AppendLine($"{indent}writer.{writeMethod}({propertyName});");
public override void GenerateDeserializationMethod(
StringBuilder source, string indent, SerializableProperty property, string? parentReference, bool isMigration = false
)
{
var expectedRule = RuleName;
var ruleName = property.Rule;
if (expectedRule != ruleName)
{
throw new ArgumentException($"Invalid rule applied to property {ruleName}. Expecting {expectedRule}, but received {ruleName}.");
}
var propertyName = property.Name;
var argument = property.RuleArguments?.Length >= 1 ? property.RuleArguments[0] : null;
const string ipAddress = SymbolMetadata.IPADDRESS_CLASS;
const string timeSpan = SymbolMetadata.TIMESPAN_STRUCT;
const string date = "System.DateTime";
var readMethod = property.Type switch
{
"bool" => "ReadBool",
"sbyte" => "ReadSByte",
"short" => "ReadShort",
"int" when argument == "EncodedInt" => "ReadEncodedInt",
"int" => "ReadInt",
"long" => "ReadLong",
"byte" => "ReadByte",
"ushort" => "ReadUShort",
"uint" => "ReadUInt",
"ulong" => "ReadULong",
"float" => "ReadFloat",
"double" => "ReadDouble",
"string" => "ReadString",
"decimal" => "ReadDecimal",
date when argument == "DeltaTime" => "ReadDeltaTime",
date => "ReadDateTime",
ipAddress => "ReadIPAddress",
timeSpan => "ReadTimeSpan"
};
var readArgument = readMethod == "ReadString" && argument == "InternString" ? "true" : "";
source.AppendLine($"{indent}{propertyName} = reader.{readMethod}({readArgument});");
}
public override void GenerateSerializationMethod(StringBuilder source, string indent, SerializableProperty property)
{
var expectedRule = RuleName;
var ruleName = property.Rule;
if (expectedRule != ruleName)
{
throw new ArgumentException($"Invalid rule applied to property {ruleName}. Expecting {expectedRule}, but received {ruleName}.");
}
var propertyName = property.Name;
var argument = property.RuleArguments?.Length >= 1 ? property.RuleArguments[0] : null;
var writeMethod = property.Type switch
{
"System.DateTime" when argument == "DeltaTime" => "WriteDeltaTime",
"int" when argument == "EncodedInt" => "WriteEncodedInt",
_ => "Write"
};
source.AppendLine($"{indent}writer.{writeMethod}({propertyName});");
}
}

View file

@ -19,61 +19,62 @@ using System.Text;
using Microsoft.CodeAnalysis;
using SerializationGenerator;
namespace SerializableMigration
namespace SerializableMigration;
public class PrimitiveUOTypeMigrationRule : MigrationRule
{
public class PrimitiveUOTypeMigrationRule : ISerializableMigrationRule
public override string RuleName => nameof(PrimitiveUOTypeMigrationRule);
public override bool GenerateRuleState(
Compilation compilation,
ISymbol symbol,
ImmutableArray<AttributeData> attributes,
ImmutableArray<INamedTypeSymbol> serializableTypes,
ImmutableArray<INamedTypeSymbol> embeddedSerializableTypes,
ISymbol? parentSymbol,
out string[] ruleArguments
)
{
public string RuleName => nameof(PrimitiveUOTypeMigrationRule);
public bool GenerateRuleState(
Compilation compilation,
ISymbol symbol,
ImmutableArray<AttributeData> attributes,
ImmutableArray<INamedTypeSymbol> serializableTypes,
ImmutableArray<INamedTypeSymbol> embeddedSerializableTypes,
ISymbol? parentSymbol,
out string[] ruleArguments
)
ruleArguments = symbol switch
{
ruleArguments = symbol switch
{
_ when symbol.IsPoint2D(compilation) => new[] { "Point2D" },
_ when symbol.IsPoint3D(compilation) => new[] { "Point3D" },
_ when symbol.IsRectangle2D(compilation) => new[] { "Rect2D" },
_ when symbol.IsRectangle3D(compilation) => new[] { "Rect3D" },
_ when symbol.IsRace(compilation) => new[] { "Race" },
_ when symbol.IsMap(compilation) => new[] { "Map" },
_ when symbol.IsBitArray(compilation) => new[] { "BitArray" },
_ => null
};
_ when symbol.IsPoint2D(compilation) => new[] { "Point2D" },
_ when symbol.IsPoint3D(compilation) => new[] { "Point3D" },
_ when symbol.IsRectangle2D(compilation) => new[] { "Rect2D" },
_ when symbol.IsRectangle3D(compilation) => new[] { "Rect3D" },
_ when symbol.IsRace(compilation) => new[] { "Race" },
_ when symbol.IsMap(compilation) => new[] { "Map" },
_ when symbol.IsBitArray(compilation) => new[] { "BitArray" },
_ => null
};
return ruleArguments != null;
return ruleArguments != null;
}
public override void GenerateDeserializationMethod(
StringBuilder source, string indent, SerializableProperty property, string? parentReference, bool isMigration = false
)
{
var expectedRule = RuleName;
var ruleName = property.Rule;
if (expectedRule != ruleName)
{
throw new ArgumentException($"Invalid rule applied to property {ruleName}. Expecting {expectedRule}, but received {ruleName}.");
}
public void GenerateDeserializationMethod(StringBuilder source, string indent, SerializableProperty property, string? parentReference)
{
var expectedRule = RuleName;
var ruleName = property.Rule;
if (expectedRule != ruleName)
{
throw new ArgumentException($"Invalid rule applied to property {ruleName}. Expecting {expectedRule}, but received {ruleName}.");
}
var propertyName = property.Name;
source.AppendLine($"{indent}{propertyName} = reader.Read{property.RuleArguments?[0] ?? ""}();");
}
var propertyName = property.Name;
source.AppendLine($"{indent}{propertyName} = reader.Read{property.RuleArguments?[0] ?? ""}();");
public override void GenerateSerializationMethod(StringBuilder source, string indent, SerializableProperty property)
{
var expectedRule = RuleName;
var ruleName = property.Rule;
if (expectedRule != ruleName)
{
throw new ArgumentException($"Invalid rule applied to property {ruleName}. Expecting {expectedRule}, but received {ruleName}.");
}
public void GenerateSerializationMethod(StringBuilder source, string indent, SerializableProperty property)
{
var expectedRule = RuleName;
var ruleName = property.Rule;
if (expectedRule != ruleName)
{
throw new ArgumentException($"Invalid rule applied to property {ruleName}. Expecting {expectedRule}, but received {ruleName}.");
}
var propertyName = property.Name;
source.AppendLine($"{indent}writer.Write({propertyName});");
}
var propertyName = property.Name;
source.AppendLine($"{indent}writer.Write({propertyName});");
}
}

View file

@ -19,63 +19,64 @@ using System.Text;
using Microsoft.CodeAnalysis;
using SerializationGenerator;
namespace SerializableMigration
namespace SerializableMigration;
public class RawSerializableMigrationRule : MigrationRule
{
public class RawSerializableMigrationRule : ISerializableMigrationRule
public override string RuleName => nameof(RawSerializableMigrationRule);
public override bool GenerateRuleState(
Compilation compilation,
ISymbol symbol,
ImmutableArray<AttributeData> attributes,
ImmutableArray<INamedTypeSymbol> serializableTypes,
ImmutableArray<INamedTypeSymbol> embeddedSerializableTypes,
ISymbol? parentSymbol,
out string[] ruleArguments
)
{
public string RuleName => nameof(RawSerializableMigrationRule);
public bool GenerateRuleState(
Compilation compilation,
ISymbol symbol,
ImmutableArray<AttributeData> attributes,
ImmutableArray<INamedTypeSymbol> serializableTypes,
ImmutableArray<INamedTypeSymbol> embeddedSerializableTypes,
ISymbol? parentSymbol,
out string[] ruleArguments
)
if (symbol is not ITypeSymbol typeSymbol)
{
if (symbol is not ITypeSymbol typeSymbol)
{
ruleArguments = null;
return false;
}
if (!typeSymbol.HasRawSerializableInterface(compilation, embeddedSerializableTypes))
{
ruleArguments = null;
return false;
}
ruleArguments = new[] { "" };
return true;
ruleArguments = null;
return false;
}
public void GenerateDeserializationMethod(StringBuilder source, string indent, SerializableProperty property, string? parentReference)
if (!typeSymbol.HasRawSerializableInterface(compilation, embeddedSerializableTypes))
{
var expectedRule = RuleName;
var ruleName = property.Rule;
if (expectedRule != ruleName)
{
throw new ArgumentException($"Invalid rule applied to property {ruleName}. Expecting {expectedRule}, but received {ruleName}.");
}
var propertyName = property.Name;
source.AppendLine($"{indent}{propertyName} = new {property.Type}({parentReference ?? "this"});");
source.AppendLine($"{indent}{propertyName}.Deserialize(reader);");
ruleArguments = null;
return false;
}
public void GenerateSerializationMethod(StringBuilder source, string indent, SerializableProperty property)
{
var expectedRule = RuleName;
var ruleName = property.Rule;
if (expectedRule != ruleName)
{
throw new ArgumentException($"Invalid rule applied to property {ruleName}. Expecting {expectedRule}, but received {ruleName}.");
}
ruleArguments = new[] { "" };
return true;
}
var propertyName = property.Name;
source.AppendLine($"{indent}{propertyName}.Serialize(writer);");
public override void GenerateDeserializationMethod(
StringBuilder source, string indent, SerializableProperty property, string? parentReference, bool isMigration = false
)
{
var expectedRule = RuleName;
var ruleName = property.Rule;
if (expectedRule != ruleName)
{
throw new ArgumentException($"Invalid rule applied to property {ruleName}. Expecting {expectedRule}, but received {ruleName}.");
}
var propertyName = property.Name;
source.AppendLine($"{indent}{propertyName} = new {property.Type}({parentReference ?? "this"});");
source.AppendLine($"{indent}{propertyName}.Deserialize(reader);");
}
public override void GenerateSerializationMethod(StringBuilder source, string indent, SerializableProperty property)
{
var expectedRule = RuleName;
var ruleName = property.Rule;
if (expectedRule != ruleName)
{
throw new ArgumentException($"Invalid rule applied to property {ruleName}. Expecting {expectedRule}, but received {ruleName}.");
}
var propertyName = property.Name;
source.AppendLine($"{indent}{propertyName}.Serialize(writer);");
}
}

View file

@ -19,56 +19,57 @@ using System.Text;
using Microsoft.CodeAnalysis;
using SerializationGenerator;
namespace SerializableMigration
namespace SerializableMigration;
public class SerializableInterfaceMigrationRule : MigrationRule
{
public class SerializableInterfaceMigrationRule : ISerializableMigrationRule
public override string RuleName => nameof(SerializableInterfaceMigrationRule);
public override bool GenerateRuleState(
Compilation compilation,
ISymbol symbol,
ImmutableArray<AttributeData> attributes,
ImmutableArray<INamedTypeSymbol> serializableTypes,
ImmutableArray<INamedTypeSymbol> embeddedSerializableTypes,
ISymbol? parentSymbol,
out string[] ruleArguments
)
{
public string RuleName => nameof(SerializableInterfaceMigrationRule);
public bool GenerateRuleState(
Compilation compilation,
ISymbol symbol,
ImmutableArray<AttributeData> attributes,
ImmutableArray<INamedTypeSymbol> serializableTypes,
ImmutableArray<INamedTypeSymbol> embeddedSerializableTypes,
ISymbol? parentSymbol,
out string[] ruleArguments
)
if (symbol is ITypeSymbol typeSymbol && typeSymbol.HasSerializableInterface(compilation, serializableTypes))
{
if (symbol is ITypeSymbol typeSymbol && typeSymbol.HasSerializableInterface(compilation, serializableTypes))
{
ruleArguments = Array.Empty<string>();
return true;
}
ruleArguments = null;
return false;
ruleArguments = Array.Empty<string>();
return true;
}
public void GenerateDeserializationMethod(StringBuilder source, string indent, SerializableProperty property, string? parentReference)
{
var expectedRule = RuleName;
var ruleName = property.Rule;
if (expectedRule != ruleName)
{
throw new ArgumentException($"Invalid rule applied to property {ruleName}. Expecting {expectedRule}, but received {ruleName}.");
}
ruleArguments = null;
return false;
}
var propertyName = property.Name;
source.AppendLine($"{indent}{propertyName} = reader.ReadEntity<{property.Type}>();");
public override void GenerateDeserializationMethod(
StringBuilder source, string indent, SerializableProperty property, string? parentReference, bool isMigration = false
)
{
var expectedRule = RuleName;
var ruleName = property.Rule;
if (expectedRule != ruleName)
{
throw new ArgumentException($"Invalid rule applied to property {ruleName}. Expecting {expectedRule}, but received {ruleName}.");
}
public void GenerateSerializationMethod(StringBuilder source, string indent, SerializableProperty property)
{
var expectedRule = RuleName;
var ruleName = property.Rule;
if (expectedRule != ruleName)
{
throw new ArgumentException($"Invalid rule applied to property {ruleName}. Expecting {expectedRule}, but received {ruleName}.");
}
var propertyName = property.Name;
source.AppendLine($"{indent}{propertyName} = reader.ReadEntity<{property.Type}>();");
}
var propertyName = property.Name;
source.AppendLine($"{indent}writer.Write({propertyName});");
public override void GenerateSerializationMethod(StringBuilder source, string indent, SerializableProperty property)
{
var expectedRule = RuleName;
var ruleName = property.Rule;
if (expectedRule != ruleName)
{
throw new ArgumentException($"Invalid rule applied to property {ruleName}. Expecting {expectedRule}, but received {ruleName}.");
}
var propertyName = property.Name;
source.AppendLine($"{indent}writer.Write({propertyName});");
}
}

View file

@ -19,66 +19,67 @@ using System.Text;
using Microsoft.CodeAnalysis;
using SerializationGenerator;
namespace SerializableMigration
namespace SerializableMigration;
public class SerializationMethodSignatureMigrationRule : MigrationRule
{
public class SerializationMethodSignatureMigrationRule : ISerializableMigrationRule
public override string RuleName => nameof(SerializationMethodSignatureMigrationRule);
public override bool GenerateRuleState(
Compilation compilation,
ISymbol symbol,
ImmutableArray<AttributeData> attributes,
ImmutableArray<INamedTypeSymbol> serializableTypes,
ImmutableArray<INamedTypeSymbol> embeddedSerializableTypes,
ISymbol? parentSymbol,
out string[] ruleArguments
)
{
public string RuleName => nameof(SerializationMethodSignatureMigrationRule);
public bool GenerateRuleState(
Compilation compilation,
ISymbol symbol,
ImmutableArray<AttributeData> attributes,
ImmutableArray<INamedTypeSymbol> serializableTypes,
ImmutableArray<INamedTypeSymbol> embeddedSerializableTypes,
ISymbol? parentSymbol,
out string[] ruleArguments
)
if ((symbol as ITypeSymbol)?.HasPublicSerializeMethod(compilation, serializableTypes) != true)
{
if ((symbol as ITypeSymbol)?.HasPublicSerializeMethod(compilation, serializableTypes) != true)
{
ruleArguments = null;
return false;
}
if (symbol is not INamedTypeSymbol namedTypeSymbol ||
!namedTypeSymbol.HasGenericReaderCtor(compilation, parentSymbol, out var requiresParent))
{
ruleArguments = null;
return false;
}
ruleArguments = new[] { requiresParent ? "DeserializationRequiresParent" : "" };
return true;
ruleArguments = null;
return false;
}
public void GenerateDeserializationMethod(StringBuilder source, string indent, SerializableProperty property, string? parentReference)
if (symbol is not INamedTypeSymbol namedTypeSymbol ||
!namedTypeSymbol.HasGenericReaderCtor(compilation, parentSymbol, out var requiresParent))
{
var expectedRule = RuleName;
var ruleName = property.Rule;
if (expectedRule != ruleName)
{
throw new ArgumentException($"Invalid rule applied to property {ruleName}. Expecting {expectedRule}, but received {ruleName}.");
}
var propertyName = property.Name;
var argument = property.RuleArguments?.Length >= 1 &&
property.RuleArguments[0] == "DeserializationRequiresParent" ? ", this" : "";
source.AppendLine($"{indent}{propertyName} = new {property.Type}(reader{argument});");
ruleArguments = null;
return false;
}
public void GenerateSerializationMethod(StringBuilder source, string indent, SerializableProperty property)
{
var expectedRule = RuleName;
var ruleName = property.Rule;
if (expectedRule != ruleName)
{
throw new ArgumentException($"Invalid rule applied to property {ruleName}. Expecting {expectedRule}, but received {ruleName}.");
}
ruleArguments = new[] { requiresParent ? "DeserializationRequiresParent" : "" };
return true;
}
var propertyName = property.Name;
source.AppendLine($"{indent}{propertyName}.Serialize(writer);");
public override void GenerateDeserializationMethod(
StringBuilder source, string indent, SerializableProperty property, string? parentReference, bool isMigration = false
)
{
var expectedRule = RuleName;
var ruleName = property.Rule;
if (expectedRule != ruleName)
{
throw new ArgumentException($"Invalid rule applied to property {ruleName}. Expecting {expectedRule}, but received {ruleName}.");
}
var propertyName = property.Name;
var argument = property.RuleArguments?.Length >= 1 &&
property.RuleArguments[0] == "DeserializationRequiresParent" ? ", this" : "";
source.AppendLine($"{indent}{propertyName} = new {property.Type}(reader{argument});");
}
public override void GenerateSerializationMethod(StringBuilder source, string indent, SerializableProperty property)
{
var expectedRule = RuleName;
var ruleName = property.Rule;
if (expectedRule != ruleName)
{
throw new ArgumentException($"Invalid rule applied to property {ruleName}. Expecting {expectedRule}, but received {ruleName}.");
}
var propertyName = property.Name;
source.AppendLine($"{indent}{propertyName}.Serialize(writer);");
}
}

View file

@ -20,107 +20,117 @@ using System.Text;
using Microsoft.CodeAnalysis;
using SerializationGenerator;
namespace SerializableMigration
namespace SerializableMigration;
public class TimerMigrationRule : MigrationRule, IPostDeserializeMethod
{
public class TimerMigrationRule : ISerializableMigrationRule, IPostDeserializeMethod
public override string RuleName => nameof(TimerMigrationRule);
public override bool GenerateRuleState(
Compilation compilation,
ISymbol symbol,
ImmutableArray<AttributeData> attributes,
ImmutableArray<INamedTypeSymbol> serializableTypes,
ImmutableArray<INamedTypeSymbol> embeddedSerializableTypes,
ISymbol? parentSymbol,
out string[] ruleArguments
)
{
public string RuleName => nameof(TimerMigrationRule);
public bool GenerateRuleState(
Compilation compilation,
ISymbol symbol,
ImmutableArray<AttributeData> attributes,
ImmutableArray<INamedTypeSymbol> serializableTypes,
ImmutableArray<INamedTypeSymbol> embeddedSerializableTypes,
ISymbol? parentSymbol,
out string[] ruleArguments
)
if (!(symbol is ITypeSymbol typeSymbol && typeSymbol.IsTimer(compilation)))
{
if (!(symbol is ITypeSymbol typeSymbol && typeSymbol.IsTimer(compilation)))
{
ruleArguments = null;
return false;
}
ruleArguments = attributes.Any(a => a.IsTimerDrift(compilation))
? new[] { "@TimerDrift" }
: new[] { "" };
return true;
ruleArguments = null;
return false;
}
public void GenerateDeserializationMethod(StringBuilder source, string indent, SerializableProperty property, string? parentReference)
ruleArguments = attributes.Any(a => a.IsTimerDrift(compilation))
? new[] { "@TimerDrift" }
: new[] { "" };
return true;
}
public override void GenerateMigrationProperty(
StringBuilder source, Compilation compilation, string indent, SerializableProperty serializableProperty
)
{
source.AppendLine($"{indent}internal readonly System.DateTime {serializableProperty.Name}Next;");
source.AppendLine($"{indent}internal readonly System.TimeSpan {serializableProperty.Name}Delay;");
}
public override void GenerateDeserializationMethod(
StringBuilder source, string indent, SerializableProperty property, string? parentReference, bool isMigration = false
)
{
var expectedRule = RuleName;
var ruleName = property.Rule;
if (expectedRule != ruleName)
{
var expectedRule = RuleName;
var ruleName = property.Rule;
if (expectedRule != ruleName)
{
throw new ArgumentException($"Invalid rule applied to property {ruleName}. Expecting {expectedRule}, but received {ruleName}.");
}
var propertyName = property.Name;
var ruleArguments = property.RuleArguments;
var driftTimer = ruleArguments![0].Contains("@TimerDrift");
var readTimer = driftTimer ? "reader.ReadDeltaTime()" : "reader.ReadDateTime()";
source.AppendLine($"{indent}var {propertyName}Next = {readTimer};");
source.AppendLine($"{indent}var {propertyName}Delay = {propertyName}Next == System.DateTime.MinValue ? System.TimeSpan.MinValue : {propertyName}Next - Core.Now;");
throw new ArgumentException($"Invalid rule applied to property {ruleName}. Expecting {expectedRule}, but received {ruleName}.");
}
public void GenerateSerializationMethod(StringBuilder source, string indent, SerializableProperty property)
var propertyName = property.Name;
var ruleArguments = property.RuleArguments;
var driftTimer = ruleArguments![0].Contains("@TimerDrift");
var readTimer = driftTimer ? "reader.ReadDeltaTime()" : "reader.ReadDateTime()";
var useVar = isMigration ? "" : "var ";
source.AppendLine($"{indent}{useVar}{propertyName}Next = {readTimer};");
source.AppendLine($"{indent}{useVar}{propertyName}Delay = {propertyName}Next == System.DateTime.MinValue ? System.TimeSpan.MinValue : {propertyName}Next - Core.Now;");
}
public override void GenerateSerializationMethod(StringBuilder source, string indent, SerializableProperty property)
{
var expectedRule = RuleName;
var ruleName = property.Rule;
if (expectedRule != ruleName)
{
var expectedRule = RuleName;
var ruleName = property.Rule;
if (expectedRule != ruleName)
{
throw new ArgumentException($"Invalid rule applied to property {ruleName}. Expecting {expectedRule}, but received {ruleName}.");
}
var propertyName = property.Name;
var ruleArguments = property.RuleArguments;
var driftTimer = ruleArguments![0].Contains("@TimerDrift");
var writerMethod = driftTimer ? "WriteDeltaTime" : "Write";
source.AppendLine($"{indent}writer.{writerMethod}({propertyName}?.Next ?? System.DateTime.MinValue);");
throw new ArgumentException($"Invalid rule applied to property {ruleName}. Expecting {expectedRule}, but received {ruleName}.");
}
public void PostDeserializeMethod(
StringBuilder source, string indent, SerializableProperty property, Compilation compilation, INamedTypeSymbol classSymbol
)
{
var deserializeTimerMethod = classSymbol
.GetMembers()
.OfType<IMethodSymbol>()
.FirstOrDefault(
m =>
var propertyName = property.Name;
var ruleArguments = property.RuleArguments;
var driftTimer = ruleArguments![0].Contains("@TimerDrift");
var writerMethod = driftTimer ? "WriteDeltaTime" : "Write";
source.AppendLine($"{indent}writer.{writerMethod}({propertyName}?.Next ?? System.DateTime.MinValue);");
}
public void PostDeserializeMethod(
StringBuilder source, string indent, SerializableProperty property, Compilation compilation, INamedTypeSymbol classSymbol
)
{
var deserializeTimerMethod = classSymbol
.GetMembers()
.OfType<IMethodSymbol>()
.FirstOrDefault(
m =>
{
if (!m.ReturnsVoid || m.Parameters.Length != 1 || !m.Parameters[0].Type.IsTimeSpan(compilation))
{
if (!m.ReturnsVoid || m.Parameters.Length != 1 || !m.Parameters[0].Type.IsTimeSpan(compilation))
{
return false;
}
return false;
}
return m.GetAttributes()
.FirstOrDefault(
attr =>
{
if (!SymbolEqualityComparer.Default.Equals(
return m.GetAttributes()
.FirstOrDefault(
attr =>
{
if (!SymbolEqualityComparer.Default.Equals(
attr.AttributeClass,
compilation.GetTypeByMetadataName(
SymbolMetadata.DESERIALIZE_TIMER_FIELD_ATTRIBUTE
)
))
{
return false;
}
var order = (int)attr.ConstructorArguments[0].Value!;
return order == property.Order;
{
return false;
}
) != null;
}
) ?? throw new Exception("Serializing a timer requires a method with the DeserializeTimerField attribute to handle creating the timer itself.");
source.AppendLine($"{indent}{deserializeTimerMethod.Name}({property.Name}Delay);");
}
var order = (int)attr.ConstructorArguments[0].Value!;
return order == property.Order;
}
) != null;
}
) ?? throw new Exception("Serializing a timer requires a method with the DeserializeTimerField attribute to handle creating the timer itself.");
source.AppendLine($"{indent}{deserializeTimerMethod.Name}({property.Name}Delay);");
}
}

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.7.2" />
<PackageReference Include="Zlib.Bindings" Version="1.8.0" />
<ProjectReference Include="..\Server\Server.csproj" />
<ProjectReference Include="..\UOContent\UOContent.csproj" />
<DataFiles Include="$(SolutionDir)\Distribution\Data\**" />

View file

@ -26,65 +26,20 @@ namespace Server
public int Length => Arguments.Length;
public string GetString(int index)
{
if (index < 0 || index >= Arguments.Length)
{
return "";
}
public string GetString(int index) => index < 0 || index >= Arguments.Length ? "" : Arguments[index];
return Arguments[index];
}
public int GetInt32(int index) => index < 0 || index >= Arguments.Length ? 0 : Utility.ToInt32(Arguments[index]);
public int GetInt32(int index)
{
if (index < 0 || index >= Arguments.Length)
{
return 0;
}
public uint GetUInt32(int index) =>
index < 0 || index >= Arguments.Length ? 0 : Utility.ToUInt32(Arguments[index]);
return Utility.ToInt32(Arguments[index]);
}
public bool GetBoolean(int index) => index >= 0 && index < Arguments.Length && Utility.ToBoolean(Arguments[index]);
public uint GetUInt32(int index)
{
if (index < 0 || index >= Arguments.Length)
{
return 0;
}
public double GetDouble(int index) =>
index < 0 || index >= Arguments.Length ? 0.0 : Utility.ToDouble(Arguments[index]);
return Utility.ToUInt32(Arguments[index]);
}
public bool GetBoolean(int index)
{
if (index < 0 || index >= Arguments.Length)
{
return false;
}
return Utility.ToBoolean(Arguments[index]);
}
public double GetDouble(int index)
{
if (index < 0 || index >= Arguments.Length)
{
return 0.0;
}
return Utility.ToDouble(Arguments[index]);
}
public TimeSpan GetTimeSpan(int index)
{
if (index < 0 || index >= Arguments.Length)
{
return TimeSpan.Zero;
}
return Utility.ToTimeSpan(Arguments[index]);
}
public TimeSpan GetTimeSpan(int index) =>
index < 0 || index >= Arguments.Length ? TimeSpan.Zero : Utility.ToTimeSpan(Arguments[index]);
}
public static partial class EventSink
@ -137,27 +92,7 @@ namespace Server
}
}
public record CommandInfo
{
public CommandInfo(AccessLevel accessLevel, string name, string[] aliases, string usage, string description)
{
AccessLevel = accessLevel;
Name = name;
Aliases = aliases;
Usage = usage;
Description = description;
}
public AccessLevel AccessLevel { get; }
public string Name { get; }
public string[] Aliases { get; }
public string Usage { get; }
public string Description { get; }
}
public record CommandInfo(AccessLevel AccessLevel, string Name, string[] Aliases, string Usage, string Description);
public class CommandInfoSorter : IComparer<CommandInfo>
{

View file

@ -93,7 +93,7 @@ namespace Server.Items
public override bool IsChildVisibleTo(Mobile m, Item child) =>
child is VirtualCheck
? !AccountGold.Enabled || m.NetState?.NewSecureTrading != true
? AccountGold.Enabled && m.NetState is not { NewSecureTrading: true }
: base.IsChildVisibleTo(m, child);
public override void Serialize(IGenericWriter writer)

View file

@ -1,4 +1,5 @@
using System;
using System.Buffers;
using System.Collections;
using System.Collections.Generic;
using System.Diagnostics;
@ -54,49 +55,97 @@ namespace Server
public static IEnumerable<NetState> SelectClients(Sector s, Rectangle2D bounds)
{
return s.Clients.Where(o => o?.Mobile?.Deleted == false && bounds.Contains(o.Mobile.Location));
var clients = new List<NetState>(s.Clients.Count);
foreach (var client in s.Clients)
{
var m = client.Mobile;
if (m?.Deleted == false && bounds.Contains(m.Location))
{
clients.Add(client);
}
}
return clients;
}
public static IEnumerable<IEntity> SelectEntities(Sector s, Rectangle2D bounds) =>
SelectEntities(s, true, true, bounds);
public static IEnumerable<IEntity> SelectEntities(Sector s, bool items, bool mobiles, Rectangle2D bounds)
public static IEnumerable<IEntity> SelectEntities(Sector s, Rectangle2D bounds)
{
var eable = Enumerable.Empty<IEntity>();
if (mobiles)
var entities = new List<IEntity>(s.Mobiles.Count + s.Items.Count);
for (int i = s.Mobiles.Count - 1, j = s.Items.Count - 1; i >= 0 || j >= 0; --i, --j)
{
eable = eable.Union(s.Mobiles.Where(o => o?.Deleted == false));
}
if (j >= 0)
{
Item item = s.Items[j];
if (item is { Deleted: false, Parent: null } && bounds.Contains(item.Location))
{
entities.Add(item);
}
}
if (items)
{
eable = eable.Union(s.Items.Where(o => o?.Deleted == false && o.Parent == null));
if (i >= 0)
{
Mobile mob = s.Mobiles[i];
if (mob is { Deleted: false } && bounds.Contains(mob.Location))
{
entities.Add(mob);
}
}
}
return eable.Where(o => bounds.Contains(o.Location));
return entities;
}
public static IEnumerable<T> SelectMobiles<T>(Sector s, Rectangle2D bounds) where T : Mobile
{
return s.Mobiles.OfType<T>().Where(o => !o.Deleted && bounds.Contains(o.Location));
var entities = new List<T>(s.Mobiles.Count);
for (int i = s.Mobiles.Count - 1; i >= 0; --i)
{
if (s.Mobiles[i] is T { Deleted: false } mob && bounds.Contains(mob.Location))
{
entities.Add(mob);
}
}
return entities;
}
public static IEnumerable<T> SelectItems<T>(Sector s, Rectangle2D bounds) where T : Item
{
return s.Items.OfType<T>()
.Where(o => o.Deleted == false && o.Parent == null && bounds.Contains(o.Location));
var entities = new List<T>(s.Items.Count);
for (int i = s.Items.Count - 1; i >= 0; --i)
{
if (s.Items[i] is T { Deleted: false, Parent: null } item && bounds.Contains(item.Location))
{
entities.Add(item);
}
}
return entities;
}
public static IEnumerable<BaseMulti> SelectMultis(Sector s, Rectangle2D bounds)
{
return s.Multis.Where(o => o?.Deleted == false && bounds.Contains(o.Location));
var entities = new List<BaseMulti>(s.Multis.Count);
for (int i = s.Multis.Count - 1; i >= 0; --i)
{
BaseMulti multi = s.Multis[i];
if (multi is { Deleted: false } && bounds.Contains(multi.Location))
{
entities.Add(multi);
}
}
return entities;
}
public static IEnumerable<StaticTile[]> SelectMultiTiles(Sector s, Rectangle2D bounds)
{
foreach (var o in s.Multis.Where(o => o?.Deleted == false))
for (int l = s.Multis.Count - 1; l >= 0; --l)
{
var c = o.Components;
BaseMulti o = s.Multis[l];
if (o?.Deleted != false)
{
continue;
}
MultiComponentList c = o.Components;
int x, y, xo, yo;
StaticTile[] t, r;
@ -143,10 +192,8 @@ namespace Server
public static Map.PooledEnumerable<NetState> GetClients(Map map, Rectangle2D bounds) =>
Map.PooledEnumerable<NetState>.Instantiate(map, bounds, ClientSelector ?? SelectClients);
public static Map.PooledEnumerable<IEntity> GetEntities(
Map map, Rectangle2D bounds, bool items = true,
bool mobiles = true
) => Map.PooledEnumerable<IEntity>.Instantiate(map, bounds, EntitySelector ?? SelectEntities);
public static Map.PooledEnumerable<IEntity> GetEntities(Map map, Rectangle2D bounds) =>
Map.PooledEnumerable<IEntity>.Instantiate(map, bounds, EntitySelector ?? SelectEntities);
public static Map.PooledEnumerable<Mobile> GetMobiles(Map map, Rectangle2D bounds) =>
GetMobiles<Mobile>(map, bounds);
@ -272,9 +319,6 @@ namespace Server
public const int SectorShift = 4;
public const int SectorActiveRange = 2;
private static readonly Queue<List<Item>> m_FixPool = new(128);
private static readonly List<Item> m_EmptyFixItems = new();
private static ILogger _logger;
private static ILogger Logger => _logger ??= LogFactory.GetLogger(typeof(Map));
@ -389,9 +433,55 @@ namespace Server
public int CompareTo(Map other) => other == null ? -1 : MapID.CompareTo(other.MapID);
public static string[] GetMapNames() => Maps.Where(m => m != null).Select(m => m.Name).ToArray();
public static string[] GetMapNames()
{
var mapCount = 0;
for (var i = 0; i < Maps.Length; i++)
{
var map = Maps[i];
if (map != null)
{
mapCount++;
}
}
public static Map[] GetMapValues() => Maps.Where(m => m != null).ToArray();
var mapNames = new string[mapCount];
for (int i = 0, mIndex = 0; i < Maps.Length; i++)
{
var map = Maps[i];
if (map != null)
{
mapNames[mIndex++] = map.Name;
}
}
return mapNames;
}
public static Map[] GetMapValues()
{
var mapCount = 0;
for (var i = 0; i < Maps.Length; i++)
{
var map = Maps[i];
if (map != null)
{
mapCount++;
}
}
var mapValues = new Map[mapCount];
for (int i = 0, mIndex = 0; i < Maps.Length; i++)
{
var map = Maps[i];
if (map != null)
{
mapValues[mIndex++] = map;
}
}
return mapValues;
}
public static Map Parse(string value)
{
@ -498,54 +588,31 @@ namespace Server
public IPooledEnumerable<StaticTile[]> GetMultiTilesAt(int x, int y) =>
PooledEnumeration.GetMultiTiles(this, new Rectangle2D(x, y, 1, 1));
private static List<Item> AcquireFixItems(Map map, int x, int y)
private static void AcquireFixItems(Map map, int x, int y, Item[] pool, out int length)
{
length = 0;
if (map == null || map == Internal || x < 0 || x > map.Width || y < 0 || y > map.Height)
{
return m_EmptyFixItems;
}
List<Item> pool = null;
lock (m_FixPool)
{
if (m_FixPool.Count > 0)
{
pool = m_FixPool.Dequeue();
}
}
pool ??= new List<Item>(128); // Arbitrary limit
var eable = map.GetItemsInRange(new Point3D(x, y, 0), 0);
pool.AddRange(
eable.Where(item => item.ItemID <= TileData.MaxItemValue && item is not BaseMulti)
.OrderBy(item => item.Z)
.Take(pool.Capacity)
);
eable.Free();
return pool;
}
private static void FreeFixItems(List<Item> pool)
{
if (pool == m_EmptyFixItems)
{
return;
}
pool.Clear();
lock (m_FixPool)
var eable = map.GetItemsInRange(new Point3D(x, y, 0), 0);
foreach (var item in eable)
{
if (m_FixPool.Count < 128)
if (item is not BaseMulti && item.ItemID <= TileData.MaxItemValue)
{
m_FixPool.Enqueue(pool);
if (length == 128)
{
break;
}
pool[length++] = item;
}
}
eable.Free();
Array.Sort(pool, 0, length, ZComparer.Default);
}
public void FixColumn(int x, int y)
@ -555,9 +622,10 @@ namespace Server
GetAverageZ(x, y, out _, out var landAvg, out _);
var items = AcquireFixItems(this, x, y);
var items = ArrayPool<Item>.Shared.Rent(128);
AcquireFixItems(this, x, y, items, out var length);
for (var i = 0; i < items.Count; i++)
for (var i = 0; i < length; i++)
{
var toFix = items[i];
@ -592,7 +660,7 @@ namespace Server
}
}
for (var j = 0; j < items.Count; ++j)
for (var j = 0; j < length; ++j)
{
if (j == i)
{
@ -622,7 +690,7 @@ namespace Server
}
}
FreeFixItems(items);
ArrayPool<Item>.Shared.Return(items);
}
/* This could probably be re-implemented if necessary (perhaps via an ITile interface?).
@ -1031,15 +1099,11 @@ namespace Server
public IPooledEnumerable<IEntity> GetObjectsInRange(Point3D p) => GetObjectsInRange(p, Core.GlobalMaxUpdateRange);
public IPooledEnumerable<IEntity> GetObjectsInRange(Point3D p, int range, bool items = true, bool mobiles = true) =>
GetObjectsInBounds(
new Rectangle2D(p.m_X - range, p.m_Y - range, range * 2 + 1, range * 2 + 1),
items,
mobiles
);
public IPooledEnumerable<IEntity> GetObjectsInRange(Point3D p, int range) =>
GetObjectsInBounds(new Rectangle2D(p.m_X - range, p.m_Y - range, range * 2 + 1, range * 2 + 1));
public IPooledEnumerable<IEntity> GetObjectsInBounds(Rectangle2D bounds, bool items = true, bool mobiles = true) =>
PooledEnumeration.GetEntities(this, bounds, items, mobiles);
public IPooledEnumerable<IEntity> GetObjectsInBounds(Rectangle2D bounds) =>
PooledEnumeration.GetEntities(this, bounds);
public IPooledEnumerable<NetState> GetClientsInRange(Point3D p) => GetClientsInRange(p, Core.GlobalMaxUpdateRange);
@ -1188,6 +1252,13 @@ namespace Server
public bool CanSpawnMobile(int x, int y, int z) =>
Region.Find(new Point3D(x, y, z), this).AllowSpawn() && CanFit(x, y, z, 16);
private class ZComparer : IComparer<Item>
{
public static readonly ZComparer Default = new();
public int Compare(Item x, Item y) => x!.Z.CompareTo(y!.Z);
}
public Sector GetSector(Point3D p) => InternalGetSector(p.m_X >> SectorShift, p.m_Y >> SectorShift);
public Sector GetSector(Point2D p) => InternalGetSector(p.m_X >> SectorShift, p.m_Y >> SectorShift);
@ -1529,9 +1600,7 @@ namespace Server
{
public static readonly NullEnumerable<T> Instance = new();
private readonly IEnumerable<T> m_Empty;
private NullEnumerable() => m_Empty = Enumerable.Empty<T>();
private readonly IEnumerable<T> m_Empty = Enumerable.Empty<T>();
IEnumerator IEnumerable.GetEnumerator() => m_Empty.GetEnumerator();

View file

@ -36,7 +36,7 @@
<ItemGroup>
<PackageReference Include="Microsoft.Toolkit.HighPerformance" Version="7.1.2" />
<PackageReference Include="PollGroup" Version="1.1.0" />
<PackageReference Include="Zlib.Bindings" Version="1.7.2" />
<PackageReference Include="Zlib.Bindings" Version="1.8.0" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\SerializationGenerator\SerializationGenerator.csproj">

View file

@ -177,7 +177,8 @@ namespace Server.Commands
CommandSystem.Register("Batch", AccessLevel.Counselor, Batch_OnCommand);
}
[Usage("Batch"), Description("Allows multiple commands to be run at the same time.")]
[Usage("Batch")]
[Description("Allows multiple commands to be run at the same time.")]
public static void Batch_OnCommand(CommandEventArgs e)
{
e.Mobile.SendGump(new BatchGump(e.Mobile, new Batch()));

View file

@ -7,7 +7,8 @@ namespace Server.Commands
CommandSystem.Register("DragEffects", AccessLevel.Developer, DragEffects_OnCommand);
}
[Usage("DragEffects [enable=false]"), Description("Enables or disables the item drag and drop effects.")]
[Usage("DragEffects [enable=false]")]
[Description("Enables or disables the item drag and drop effects.")]
public static void DragEffects_OnCommand(CommandEventArgs e)
{
if (e.Length == 0)

View file

@ -13,7 +13,8 @@ namespace Server.Commands
CommandSystem.Register("DupeInBag", AccessLevel.GameMaster, DupeInBag_OnCommand);
}
[Usage("Dupe [amount]"), Description("Dupes a targeted item.")]
[Usage("Dupe [amount]")]
[Description("Dupes a targeted item.")]
private static void Dupe_OnCommand(CommandEventArgs e)
{
var amount = 1;
@ -26,7 +27,8 @@ namespace Server.Commands
e.Mobile.SendMessage("What do you wish to dupe?");
}
[Usage("DupeInBag <count>"), Description("Dupes an item at it's current location (count) number of times.")]
[Usage("DupeInBag <count>")]
[Description("Dupes an item at it's current location (count) number of times.")]
private static void DupeInBag_OnCommand(CommandEventArgs e)
{
var amount = 1;

View file

@ -43,13 +43,18 @@ namespace Server.Commands.Generic
return;
}
var eable = map.GetObjectsInBounds(rect, items, mobiles);
var eable = map.GetObjectsInBounds(rect);
var objs = new List<object>();
foreach (var obj in eable)
{
if ((!mobiles || obj is not Mobile || BaseCommand.IsAccessible(from, obj)) && ext.IsValid(obj))
if (!mobiles && obj is Mobile || !items && obj is Item)
{
continue;
}
if (BaseCommand.IsAccessible(from, obj) && ext.IsValid(obj))
{
objs.Add(obj);
}

View file

@ -73,7 +73,8 @@ namespace Server.Commands
CommandSystem.Register(command, access, handler);
}
[Usage("SpeedBoost [true|false]"), Description("Enables a speed boost for the invoker. Disable with parameters.")]
[Usage("SpeedBoost [true|false]")]
[Description("Enables a speed boost for the invoker. Disable with parameters.")]
private static void SpeedBoost_OnCommand(CommandEventArgs e)
{
var from = e.Mobile;
@ -97,7 +98,8 @@ namespace Server.Commands
}
}
[Usage("Where"), Description("Tells the commanding player his coordinates, region, and facet.")]
[Usage("Where")]
[Description("Tells the commanding player his coordinates, region, and facet.")]
public static void Where_OnCommand(CommandEventArgs e)
{
var from = e.Mobile;
@ -127,9 +129,8 @@ namespace Server.Commands
}
}
[Usage("DropHolding"), Description(
"Drops the item, if any, that a targeted player is holding. The item is placed into their backpack, or if that's full, at their feet."
)]
[Usage("DropHolding")]
[Description("Drops the item, if any, that a targeted player is holding. The item is placed into their backpack, or if that's full, at their feet.")]
public static void DropHolding_OnCommand(CommandEventArgs e)
{
e.Mobile.BeginTarget(-1, false, TargetFlags.None, DropHolding_OnTarget);
@ -274,7 +275,8 @@ namespace Server.Commands
}
}
[Usage("GetFollowers"), Description("Teleports all pets of a targeted player to your location.")]
[Usage("GetFollowers")]
[Description("Teleports all pets of a targeted player to your location.")]
public static void GetFollowers_OnCommand(CommandEventArgs e)
{
e.Mobile.BeginTarget(-1, false, TargetFlags.None, GetFollowers_OnTarget);
@ -374,9 +376,8 @@ namespace Server.Commands
e.Mobile.Target = new ViewEqTarget();
}
[Usage("Sound <index> [toAll=true]"), Description(
"Plays a sound to players within 12 tiles of you. The (toAll) argument specifies to everyone, or just those who can see you."
)]
[Usage("Sound <index> [toAll=true]")]
[Description("Plays a sound to players within 12 tiles of you. The (toAll) argument specifies to everyone, or just those who can see you.")]
public static void Sound_OnCommand(CommandEventArgs e)
{
if (e.Length == 1)
@ -423,7 +424,8 @@ namespace Server.Commands
}
}
[Usage("Echo <text>"), Description("Relays (text) as a system message.")]
[Usage("Echo <text>")]
[Description("Relays (text) as a system message.")]
public static void Echo_OnCommand(CommandEventArgs e)
{
var toEcho = e.ArgString.Trim();
@ -438,19 +440,22 @@ namespace Server.Commands
}
}
[Usage("Bank"), Description("Opens the bank box of a given target.")]
[Usage("Bank")]
[Description("Opens the bank box of a given target.")]
public static void Bank_OnCommand(CommandEventArgs e)
{
e.Mobile.Target = new BankTarget();
}
[Usage("Client"), Description("Opens the client gump menu for a given player.")]
[Usage("Client")]
[Description("Opens the client gump menu for a given player.")]
private static void Client_OnCommand(CommandEventArgs e)
{
e.Mobile.Target = new ClientTarget();
}
[Usage("Move"), Description("Repositions a targeted item or mobile.")]
[Usage("Move")]
[Description("Repositions a targeted item or mobile.")]
private static void Move_OnCommand(CommandEventArgs e)
{
e.Mobile.Target = new PickMoveTarget();
@ -473,9 +478,8 @@ namespace Server.Commands
return validMap;
}
[Usage("Go [name | serial | (x y [z]) | (deg min (N | S) deg min (E | W))]"), Description(
"With no arguments, this command brings up the go menu. With one argument, (name), you are moved to that regions \"go location.\" Or, if a numerical value is specified for one argument, (serial), you are moved to that object. Two or three arguments, (x y [z]), will move your character to that location. When six arguments are specified, (deg min (N | S) deg min (E | W)), your character will go to an approximate of those sextant coordinates."
)]
[Usage("Go [name | serial | (x y [z]) | (deg min (N | S) deg min (E | W))]")]
[Description("With no arguments, this command brings up the go menu. With one argument, (name), you are moved to that regions \"go location.\" Or, if a numerical value is specified for one argument, (serial), you are moved to that object. Two or three arguments, (x y [z]), will move your character to that location. When six arguments are specified, (deg min (N | S) deg min (E | W)), your character will go to an approximate of those sextant coordinates.")]
private static void Go_OnCommand(CommandEventArgs e)
{
var from = e.Mobile;
@ -685,7 +689,8 @@ namespace Server.Commands
}
}
[Usage("Help"), Description("Lists all available commands.")]
[Usage("Help")]
[Description("Lists all available commands.")]
public static void Help_OnCommand(CommandEventArgs e)
{
var m = e.Mobile;
@ -732,13 +737,15 @@ namespace Server.Commands
}
}
[Usage("SMsg <text>"), Aliases("S", "SM"), Description("Broadcasts a message to all online staff.")]
[Usage("SMsg <text>"), Aliases("S", "SM")]
[Description("Broadcasts a message to all online staff.")]
public static void StaffMessage_OnCommand(CommandEventArgs e)
{
BroadcastMessage(AccessLevel.Counselor, e.Mobile.SpeechHue, $"[{e.Mobile.Name}] {e.ArgString}");
}
[Usage("BCast <text>"), Aliases("B", "BC"), Description("Broadcasts a message to everyone online.")]
[Usage("BCast <text>"), Aliases("B", "BC")]
[Description("Broadcasts a message to everyone online.")]
public static void BroadcastMessage_OnCommand(CommandEventArgs e)
{
BroadcastMessage(AccessLevel.Player, 0x482, $"Staff message from {e.Mobile.Name}:");
@ -758,7 +765,8 @@ namespace Server.Commands
}
}
[Usage("AutoPageNotify"), Aliases("APN"), Description("Toggles your auto-page-notify status.")]
[Usage("AutoPageNotify"), Aliases("APN")]
[Description("Toggles your auto-page-notify status.")]
public static void APN_OnCommand(CommandEventArgs e)
{
var m = e.Mobile;
@ -789,7 +797,8 @@ namespace Server.Commands
}
}
[Usage("Cast <name>"), Description("Casts a spell by name.")]
[Usage("Cast <name>")]
[Description("Casts a spell by name.")]
public static void Cast_OnCommand(CommandEventArgs e)
{
if (e.Length == 1)
@ -816,19 +825,22 @@ namespace Server.Commands
}
}
[Usage("Stuck"), Description("Opens a menu of towns, used for teleporting stuck mobiles.")]
[Usage("Stuck")]
[Description("Opens a menu of towns, used for teleporting stuck mobiles.")]
public static void Stuck_OnCommand(CommandEventArgs e)
{
e.Mobile.Target = new StuckMenuTarget();
}
[Usage("Light <level>"), Description("Set your local lightlevel.")]
[Usage("Light <level>")]
[Description("Set your local lightlevel.")]
public static void Light_OnCommand(CommandEventArgs e)
{
e.Mobile.LightLevel = e.GetInt32(0);
}
[Usage("Stats"), Description("View some stats about the server.")]
[Usage("Stats")]
[Description("View some stats about the server.")]
public static void Stats_OnCommand(CommandEventArgs e)
{
e.Mobile.SendMessage("Open Connections: {0}", TcpServer.Instances.Count);

View file

@ -19,9 +19,8 @@ namespace Server.Commands
FillTable();
}
[Usage("HelpInfo [<command>]"), Description(
"Gives information on a specified command, or when no argument specified, displays a gump containing all commands"
)]
[Usage("HelpInfo [<command>]")]
[Description("Gives information on a specified command, or when no argument specified, displays a gump containing all commands")]
private static void HelpInfo_OnCommand(CommandEventArgs e)
{
if (e.Length > 0)

View file

@ -96,9 +96,8 @@ namespace Server.Gumps
CommandSystem.Register("t", AccessLevel.GameMaster, Sciter_OnCommand);
}
[Usage("AddMenu [searchString]"), Description(
"Opens an add menu, with an optional initial search string. This menu allows you to search for Items or Mobiles and add them interactively."
)]
[Usage("AddMenu [searchString]")]
[Description("Opens an add menu, with an optional initial search string. This menu allows you to search for Items or Mobiles and add them interactively.")]
private static void AddMenu_OnCommand(CommandEventArgs e)
{
var val = e.ArgString.Trim();

View file

@ -20,7 +20,8 @@ namespace Server.Commands
CommandSystem.Register("Decorate", AccessLevel.Administrator, Decorate_OnCommand);
}
[Usage("Decorate"), Description("Generates world decoration.")]
[Usage("Decorate")]
[Description("Generates world decoration.")]
private static void Decorate_OnCommand(CommandEventArgs e)
{
m_Mobile = e.Mobile;

View file

@ -20,7 +20,8 @@ namespace Server.Commands
CommandSystem.Register("DecorateMag", AccessLevel.Administrator, DecorateMag_OnCommand);
}
[Usage("DecorateMag"), Description("Generates world decoration.")]
[Usage("DecorateMag")]
[Description("Generates world decoration.")]
private static void DecorateMag_OnCommand(CommandEventArgs e)
{
m_Mobile = e.Mobile;

View file

@ -33,7 +33,8 @@ namespace Server.Commands
CommandSystem.Register("TelGenDelete", AccessLevel.Administrator, TelGenDelete_OnCommand);
}
[Usage("TelGenDelete"), Description("Destroys world/dungeon teleporters for all facets.")]
[Usage("TelGenDelete")]
[Description("Destroys world/dungeon teleporters for all facets.")]
public static void TelGenDelete_OnCommand(CommandEventArgs e)
{
var from = e.Mobile;
@ -63,7 +64,8 @@ namespace Server.Commands
from.SendMessage(WarningHue, $"{count} Teleporters Removed.");
}
[Usage("TelGen"), Description("Generates world/dungeon teleporters for all facets.")]
[Usage("TelGen")]
[Description("Generates world/dungeon teleporters for all facets.")]
public static void GenTeleporter_OnCommand(CommandEventArgs e)
{
var from = e.Mobile;

View file

@ -19,7 +19,8 @@ namespace Server.Commands
CommandSystem.Register("SetProfiles", AccessLevel.Administrator, SetProfiles_OnCommand);
}
[Usage("WriteProfiles"), Description("Generates a log files containing performance diagnostic information.")]
[Usage("WriteProfiles")]
[Description("Generates a log files containing performance diagnostic information.")]
public static void WriteProfiles_OnCommand(CommandEventArgs e)
{
try
@ -86,7 +87,8 @@ namespace Server.Commands
}
}
[Usage("CountObjects"), Description("Generates a log file detailing all item and mobile types in the world.")]
[Usage("CountObjects")]
[Description("Generates a log file detailing all item and mobile types in the world.")]
public static void CountObjects_OnCommand(CommandEventArgs e)
{
using (var op = new StreamWriter("objects.log"))
@ -140,7 +142,8 @@ namespace Server.Commands
e.Mobile.SendMessage("Object table has been generated. See the file : objects.log");
}
[Usage("TraceExpanded"), Description("Generates a log file describing all items using expanded memory.")]
[Usage("TraceExpanded")]
[Description("Generates a log file describing all items using expanded memory.")]
public static void TraceExpanded_OnCommand(CommandEventArgs e)
{
var typeTable = new Dictionary<Type, int[]>();
@ -251,7 +254,8 @@ namespace Server.Commands
}
}
[Usage("TraceInternal"), Description("Generates a log file describing all items in the 'internal' map.")]
[Usage("TraceInternal")]
[Description("Generates a log file describing all items in the 'internal' map.")]
public static void TraceInternal_OnCommand(CommandEventArgs e)
{
var totalCount = 0;

View file

@ -0,0 +1,80 @@
using System;
using Server.Items;
using Server.Mobiles;
using Server.Network;
using Server.Utilities;
namespace Server.Commands;
public static class StaffDress
{
private static readonly Type[] _staffRobeTypes =
{
null,
typeof(CounselorRobe),
typeof(GMRobe),
typeof(SeerRobe),
typeof(AdminRobe),
typeof(AdminRobe),
typeof(AdminRobe)
};
public static void Initialize()
{
CommandSystem.Register("ResetStaffDress", AccessLevel.Counselor, StaffDress_OnCommand);
}
[Usage("ResetStaffDress")]
[Description("Resets staff to proper GM")]
public static void StaffDress_OnCommand(CommandEventArgs e)
{
if (e.Mobile is not PlayerMobile pm)
{
return;
}
pm.Race = Race.Human;
pm.Karma = pm.Fame = pm.Kills = pm.ShortTermMurders = pm.BodyMod = 0;
pm.Body = 987;
pm.SolidHueOverride = pm.HueMod = -1;
pm.FacialHairItemID = 0;
pm.Blessed = true;
pm.DisplayGuildTitle = false;
pm.DisplayChampionTitle = false;
if (pm.Mount != null)
{
pm.Mount.Rider = null;
}
pm.NetState.SendSpeedControl(SpeedControlSetting.Mount);
pm.ResetStaffAccess();
if (pm.AccessLevel < AccessLevel.Administrator)
{
pm.Hue = Race.Human.ClipSkinHue((pm.Hue + 1) & 0x3FFF);
}
for (var i = pm.Items.Count - 1; i >= 0; i--)
{
var item = pm.Items[i];
if (item.Layer is Layer.FacialHair)
{
item.Delete();
}
if (item.Layer is not Layer.Backpack
and not Layer.Bank
and not Layer.Hair
and not Layer.Mount
and not Layer.ShopBuy
and not Layer.ShopResale
and not Layer.ShopSell)
{
pm.AddToBackpack(item);
}
}
pm.AddItem(_staffRobeTypes[(int)pm.AccessLevel].CreateInstance<BaseSuit>());
}
}

View file

@ -9,7 +9,8 @@ namespace Server.Commands
CommandSystem.Register("Time", AccessLevel.Player, Time_OnCommand);
}
[Usage("Time"), Description("Returns the server's local time.")]
[Usage("Time")]
[Description("Returns the server's local time.")]
private static void Time_OnCommand(CommandEventArgs e)
{
e.Mobile.SendMessage(Core.Now.ToString(CultureInfo.InvariantCulture));

View file

@ -15,7 +15,8 @@ namespace Server.Commands
CommandSystem.Register("SignGen", AccessLevel.Administrator, SignGen_OnCommand);
}
[Usage("SignGen"), Description("Generates world/shop signs on all facets.")]
[Usage("SignGen")]
[Description("Generates world/shop signs on all facets.")]
public static void SignGen_OnCommand(CommandEventArgs c)
{
Parse(c.Mobile);

View file

@ -12,7 +12,8 @@ namespace Server.Commands
CommandSystem.Register("SetAllSkills", AccessLevel.GameMaster, SetAllSkills_OnCommand);
}
[Usage("SetSkill <name> <value>"), Description("Sets a skill value by name of a targeted mobile.")]
[Usage("SetSkill <name> <value>")]
[Description("Sets a skill value by name of a targeted mobile.")]
public static void SetSkill_OnCommand(CommandEventArgs arg)
{
if (arg.Length != 2)
@ -32,7 +33,8 @@ namespace Server.Commands
}
}
[Usage("SetAllSkills <name> <value>"), Description("Sets all skill values of a targeted mobile.")]
[Usage("SetAllSkills <name> <value>")]
[Description("Sets all skill values of a targeted mobile.")]
public static void SetAllSkills_OnCommand(CommandEventArgs arg)
{
if (arg.Length != 1)
@ -45,7 +47,8 @@ namespace Server.Commands
}
}
[Usage("GetSkill <name>"), Description("Gets a skill value by name of a targeted mobile.")]
[Usage("GetSkill <name>")]
[Description("Gets a skill value by name of a targeted mobile.")]
public static void GetSkill_OnCommand(CommandEventArgs arg)
{
if (arg.Length != 1)

View file

@ -15,7 +15,8 @@ namespace Server.Commands
CommandSystem.Register("Skills", AccessLevel.Counselor, Skills_OnCommand);
}
[Usage("Skills"), Description("Opens a menu where you can view or edit skills of a targeted mobile.")]
[Usage("Skills")]
[Description("Opens a menu where you can view or edit skills of a targeted mobile.")]
private static void Skills_OnCommand(CommandEventArgs e)
{
e.Mobile.Target = new SkillsTarget();

View file

@ -0,0 +1,101 @@
using System;
using System.Collections.Generic;
using System.Runtime.CompilerServices;
using Server.Accounting;
using Server.Mobiles;
namespace Server.Commands;
public static class StaffAccess
{
private static readonly Dictionary<string, AccessLevel> _accessLevelByString = new();
[MethodImpl(MethodImplOptions.AggressiveInlining)]
private static string AccountTag(Serial serial) => $"OriginalStaffAccess:{serial}";
public static void Initialize()
{
CommandSystem.Register("StaffAccess", AccessLevel.Player, StaffAccess_OnCommand);
foreach (var accessLevel in Enum.GetValues<AccessLevel>())
{
_accessLevelByString[accessLevel.ToString().ToLower()] = accessLevel;
}
_accessLevelByString["gm"] = AccessLevel.GameMaster;
_accessLevelByString["dev"] = AccessLevel.Developer;
_accessLevelByString["admin"] = AccessLevel.Administrator;
}
public static void ResetStaffAccess(this PlayerMobile m)
{
if (m.Account is not Account account)
{
return;
}
var accountTag = AccountTag(m.Serial);
var originalAccessLevelString = account.GetTag(accountTag);
if (originalAccessLevelString == null)
{
return;
}
var accessLevel = _accessLevelByString[originalAccessLevelString];
account.RemoveTag(accountTag);
m.AccessLevel = accessLevel;
}
[Usage("StaffAccess <access level>")]
[Description("Overrides your access level.")]
public static void StaffAccess_OnCommand(CommandEventArgs e)
{
var m = e.Mobile;
if (m.Account is not Account account)
{
return;
}
var accountTag = AccountTag(m.Serial);
var originalAccessLevelString = account.GetTag(accountTag);
AccessLevel? originalAccessLevel = originalAccessLevelString != null ? _accessLevelByString[originalAccessLevelString] : null;
if (originalAccessLevel == null && m.AccessLevel == AccessLevel.Player)
{
return;
}
var accessLevelArgument = e.GetString(0)?.Trim().ToLower();
AccessLevel newAccessLevel = AccessLevel.Player;
var validAccessLevel = !string.IsNullOrEmpty(accessLevelArgument) &&
_accessLevelByString.TryGetValue(accessLevelArgument, out newAccessLevel);
if (!validAccessLevel && originalAccessLevel == null)
{
m.SendMessage("Invalid access level specified.");
m.SendMessage("Usage: [staffaccess <access level>.");
return;
}
if (originalAccessLevel != null && (!validAccessLevel || newAccessLevel == originalAccessLevel))
{
account.RemoveTag(accountTag);
newAccessLevel = originalAccessLevel.Value;
m.SendMessage("Restoring original staff access...");
}
if ((originalAccessLevel ?? m.AccessLevel) < newAccessLevel)
{
m.SendMessage($"You cannot set your staff access to {newAccessLevel.ToString()}.");
return;
}
if (originalAccessLevel == null)
{
// Save the original access level
account.AddTag(accountTag, m.AccessLevel.ToString().ToLower());
}
m.AccessLevel = newAccessLevel;
m.SendMessage($"Staff access set to {newAccessLevel.ToString()}.");
}
}

View file

@ -48,14 +48,16 @@ namespace Server
CommandSystem.Register("UnfreezeWorld", AccessLevel.Administrator, UnfreezeWorld_OnCommand);
}
[Usage("Freeze"), Description("Makes a targeted area of dynamic items static.")]
[Usage("Freeze")]
[Description("Makes a targeted area of dynamic items static.")]
public static void Freeze_OnCommand(CommandEventArgs e)
{
var from = e.Mobile;
BoundingBoxPicker.Begin(from, (map, start, end) => FreezeBox_Callback(from, map, start, end));
}
[Usage("FreezeMap"), Description("Makes every dynamic item in your map static.")]
[Usage("FreezeMap")]
[Description("Makes every dynamic item in your map static.")]
public static void FreezeMap_OnCommand(CommandEventArgs e)
{
var from = e.Mobile;
@ -75,7 +77,8 @@ namespace Server
}
}
[Usage("FreezeWorld"), Description("Makes every dynamic item on all maps static.")]
[Usage("FreezeWorld")]
[Description("Makes every dynamic item on all maps static.")]
public static void FreezeWorld_OnCommand(CommandEventArgs e)
{
SendWarning(
@ -404,14 +407,16 @@ namespace Server
}
}
[Usage("Unfreeze"), Description("Makes a targeted area of static items dynamic.")]
[Usage("Unfreeze")]
[Description("Makes a targeted area of static items dynamic.")]
public static void Unfreeze_OnCommand(CommandEventArgs e)
{
var from = e.Mobile;
BoundingBoxPicker.Begin(from, (map, start, end) => UnfreezeBox_Callback(from, map, start, end));
}
[Usage("UnfreezeMap"), Description("Makes every static item in your map dynamic.")]
[Usage("UnfreezeMap")]
[Description("Makes every static item in your map dynamic.")]
public static void UnfreezeMap_OnCommand(CommandEventArgs e)
{
var map = e.Mobile.Map;
@ -430,7 +435,8 @@ namespace Server
}
}
[Usage("UnfreezeWorld"), Description("Makes every static item on all maps dynamic.")]
[Usage("UnfreezeWorld")]
[Description("Makes every static item on all maps dynamic.")]
public static void UnfreezeWorld_OnCommand(CommandEventArgs e)
{
SendWarning(

View file

@ -22,9 +22,8 @@ namespace Server.Commands
(m as PlayerMobile)?.VisibilityList.Clear();
}
[Usage("Vis"), Description(
"Adds or removes a targeted player from your visibility list. Anyone on your visibility list will be able to see you at all times, even when you're hidden."
)]
[Usage("Vis")]
[Description("Adds or removes a targeted player from your visibility list. Anyone on your visibility list will be able to see you at all times, even when you're hidden.")]
public static void Vis_OnCommand(CommandEventArgs e)
{
if (e.Mobile is PlayerMobile)
@ -34,7 +33,8 @@ namespace Server.Commands
}
}
[Usage("VisList"), Description("Shows the names of everyone in your visibility list.")]
[Usage("VisList")]
[Description("Shows the names of everyone in your visibility list.")]
public static void VisList_OnCommand(CommandEventArgs e)
{
if (e.Mobile is PlayerMobile pm)
@ -57,7 +57,8 @@ namespace Server.Commands
}
}
[Usage("VisClear"), Description("Removes everyone from your visibility list.")]
[Usage("VisClear")]
[Description("Removes everyone from your visibility list.")]
public static void VisClear_OnCommand(CommandEventArgs e)
{
if (e.Mobile is PlayerMobile pm)

View file

@ -24,25 +24,29 @@ namespace Server.Commands
CommandSystem.Register("WipeMultis", AccessLevel.GameMaster, WipeMultis_OnCommand);
}
[Usage("Wipe"), Description("Wipes all items and npcs in a targeted bounding box.")]
[Usage("Wipe")]
[Description("Wipes all items and npcs in a targeted bounding box.")]
private static void WipeAll_OnCommand(CommandEventArgs e)
{
BeginWipe(e.Mobile, WipeType.Items | WipeType.Mobiles);
}
[Usage("WipeItems"), Description("Wipes all items in a targeted bounding box.")]
[Usage("WipeItems")]
[Description("Wipes all items in a targeted bounding box.")]
private static void WipeItems_OnCommand(CommandEventArgs e)
{
BeginWipe(e.Mobile, WipeType.Items);
}
[Usage("WipeNPCs"), Description("Wipes all npcs in a targeted bounding box.")]
[Usage("WipeNPCs")]
[Description("Wipes all npcs in a targeted bounding box.")]
private static void WipeNPCs_OnCommand(CommandEventArgs e)
{
BeginWipe(e.Mobile, WipeType.Mobiles);
}
[Usage("WipeMultis"), Description("Wipes all multis in a targeted bounding box.")]
[Usage("WipeMultis")]
[Description("Wipes all multis in a targeted bounding box.")]
private static void WipeMultis_OnCommand(CommandEventArgs e)
{
BeginWipe(e.Mobile, WipeType.Multis);

View file

@ -273,9 +273,14 @@ namespace Server.Factions
return false;
}
var eable = mob.Map.GetObjectsInRange(mob.Location, range, items, mobs);
var eable = mob.Map.GetObjectsInRange(mob.Location, range);
foreach (var obj in eable)
{
if (!mobs && obj is Mobile || !items && obj is Item)
{
continue;
}
if (type.IsInstanceOfType(obj))
{
eable.Free();

View file

@ -102,14 +102,8 @@ namespace Server.Items
banks = m_IlshenarBanks;
moongates = PMList.Ilshenar;
#else
from.NetState.SendMessageLocalized(
Serial,
ItemID,
MessageType.Label,
0x482,
3,
1061684
); // The magic of the sextant fails...
// The magic of the sextant fails...
from.NetState.SendMessageLocalized(Serial, ItemID, MessageType.Label, 0x482, 3, 1061684);
return;
#endif
@ -155,43 +149,23 @@ namespace Server.Items
}
}
int moonMsg;
if (moongateDistance == double.MaxValue)
int moonMsg = moongateDistance switch
{
moonMsg = 1048021; // The sextant fails to find a Moongate nearby.
}
else if (moongateDistance > m_LongDistance)
{
moonMsg = 1046449 + (int)from.GetDirectionTo(closestMoongate); // A moongate is * from here
}
else if (moongateDistance > m_ShortDistance)
{
moonMsg = 1048010 + (int)from.GetDirectionTo(closestMoongate); // There is a Moongate * of here.
}
else
{
moonMsg = 1048018; // You are next to a Moongate at the moment.
}
double.MaxValue => 1048021, // The sextant fails to find a Moongate nearby.
> m_LongDistance => 1046449 + (int)from.GetDirectionTo(closestMoongate), // A moongate is * from here
> m_ShortDistance => 1048010 + (int)from.GetDirectionTo(closestMoongate), // There is a Moongate * of here.
_ => 1048018 // You are next to a Moongate at the moment.
};
from.NetState.SendMessageLocalized(Serial, ItemID, MessageType.Label, 0x482, 3, moonMsg);
int bankMsg;
if (bankDistance == double.MaxValue)
int bankMsg = bankDistance switch
{
bankMsg = 1048020; // The sextant fails to find a Bank nearby.
}
else if (bankDistance > m_LongDistance)
{
bankMsg = 1046462 + (int)from.GetDirectionTo(closestBank); // A town is * from here
}
else if (bankDistance > m_ShortDistance)
{
bankMsg = 1048002 + (int)from.GetDirectionTo(closestBank); // There is a city Bank * of here.
}
else
{
bankMsg = 1048019; // You are next to a Bank at the moment.
}
double.MaxValue => 1048020, // The sextant fails to find a Bank nearby.
> m_LongDistance => 1046462 + (int)from.GetDirectionTo(closestBank), // A town is * from here
> m_ShortDistance => 1048002 + (int)from.GetDirectionTo(closestBank), // There is a city Bank * of here.
_ => 1048019 // You are next to a Bank at the moment.
};
from.NetState.SendMessageLocalized(Serial, ItemID, MessageType.Label, 0x5AA, 3, bankMsg);
}

View file

@ -31,7 +31,7 @@ namespace Server.Guilds
m_List = list;
}
public virtual bool WillFilter => m_Filter.Length >= 0;
public virtual bool WillFilter => m_Filter.Length > 0;
public override void PopulateGump()
{
@ -55,7 +55,7 @@ namespace Server.Guilds
}
m_List.Sort(m_Comparer);
m_StartNumber = Math.Clamp(m_StartNumber, 0, m_List.Count - 1);
m_StartNumber = Math.Max(Math.Min(m_StartNumber, m_List.Count - 1), 0);
AddBackground(130, 75, 385, 30, 0xBB8);
AddTextEntry(135, 80, 375, 30, 0x481, 1, m_Filter);
@ -109,8 +109,8 @@ namespace Server.Guilds
else // descending, go from bottom of list to the top
{
for (var i = m_List.Count - 1 - m_StartNumber;
i >= 0 && i >= m_List.Count - itemsPerPage - m_StartNumber;
i--)
i >= 0 && i >= m_List.Count - itemsPerPage - m_StartNumber;
i--)
{
DrawEntry(m_List[i], i, itemNumber++);
}

View file

@ -215,7 +215,7 @@ namespace Server.Items
alchemyBonus = (int)(from.Skills.Alchemy.Value / (Core.AOS ? 5 : 10));
}
var eable = map.GetObjectsInRange(loc, ExplosionRange, LeveledExplosion);
var eable = map.GetObjectsInRange(loc, ExplosionRange);
using var queue = PooledRefQueue<IEntity>.Create();
var toDamage = 0;
@ -234,7 +234,7 @@ namespace Server.Items
queue.Enqueue(entity);
}
}
else if (entity is BaseExplosionPotion)
else if (LeveledExplosion && entity is BaseExplosionPotion)
{
queue.Enqueue(entity);
}

View file

@ -25,6 +25,13 @@ namespace Server.Items
public abstract class BaseWeapon : Item, IWeapon, IFactionItem, ICraftable, ISlayer, IDurability
{
private static bool _enableInstaHit;
public static void Configure()
{
_enableInstaHit = ServerConfiguration.GetSetting("melee.enableInstaHit", !Core.UOR);
}
private WeaponAccuracyLevel m_AccuracyLevel;
private WeaponAnimation m_Animation;
private Mobile m_Crafter;
@ -950,7 +957,10 @@ namespace Server.Items
}
}
from.NextCombatTime = Core.TickCount + (int)GetDelay(from).TotalMilliseconds;
if (!_enableInstaHit)
{
from.NextCombatTime = Core.TickCount + (int)GetDelay(from).TotalMilliseconds;
}
if (UseSkillMod && m_AccuracyLevel != WeaponAccuracyLevel.Regular)
{
@ -989,45 +999,45 @@ namespace Server.Items
public override void OnRemoved(IEntity parent)
{
if (parent is Mobile m)
if (parent is not Mobile m)
{
var weapon = m.Weapon as BaseWeapon;
var modName = Serial.ToString();
m.RemoveStatMod($"{modName}Str");
m.RemoveStatMod($"{modName}Dex");
m.RemoveStatMod($"{modName}Int");
if (weapon != null)
{
m.NextCombatTime = Core.TickCount + (int)weapon.GetDelay(m).TotalMilliseconds;
}
if (UseSkillMod && m_SkillMod != null)
{
m_SkillMod.Remove();
m_SkillMod = null;
}
if (m_MageMod != null)
{
m_MageMod.Remove();
m_MageMod = null;
}
if (Core.AOS)
{
SkillBonuses.Remove();
}
ImmolatingWeaponSpell.StopImmolating(this);
ForceOfNature.Remove(m);
m.CheckStatTimers();
m.Delta(MobileDelta.WeaponDamage);
return;
}
var modName = Serial.ToString();
m.RemoveStatMod($"{modName}Str");
m.RemoveStatMod($"{modName}Dex");
m.RemoveStatMod($"{modName}Int");
if (!_enableInstaHit && m.Weapon is BaseWeapon weapon)
{
m.NextCombatTime = Core.TickCount + (long)weapon.GetDelay(m).TotalMilliseconds;
}
if (UseSkillMod && m_SkillMod != null)
{
m_SkillMod.Remove();
m_SkillMod = null;
}
if (m_MageMod != null)
{
m_MageMod.Remove();
m_MageMod = null;
}
if (Core.AOS)
{
SkillBonuses.Remove();
}
ImmolatingWeaponSpell.StopImmolating(this);
ForceOfNature.Remove(m);
m.CheckStatTimers();
m.Delta(MobileDelta.WeaponDamage);
}
public virtual SkillName GetUsedSkill(Mobile m, bool checkSkillAttrs)

View file

@ -3,68 +3,64 @@ using Server.Misc;
using Server.Mobiles;
using Server.Regions;
namespace Server.SkillHandlers
namespace Server.SkillHandlers;
public static class Snooping
{
public static class Snooping
public static void Configure()
{
public static void Configure()
Container.SnoopHandler = Container_Snoop;
}
public static bool CheckSnoopAllowed(Mobile from, Mobile to)
{
var map = from.Map;
if (to.Player)
{
Container.SnoopHandler = Container_Snoop;
return from.CanBeHarmful(to, false, true); // normal restrictions
}
public static bool CheckSnoopAllowed(Mobile from, Mobile to)
if ((map?.Rules & MapRules.HarmfulRestrictions) == 0)
{
var map = from.Map;
if (to.Player)
{
return from.CanBeHarmful(to, false, true); // normal restrictions
}
if ((map?.Rules & MapRules.HarmfulRestrictions) == 0)
{
return true; // felucca you can snoop anybody
}
var reg = to.Region.GetRegion<GuardedRegion>();
if (reg?.IsDisabled() != true)
{
return true; // not in town? we can snoop any npc
}
return !to.Body.IsHuman || to is BaseCreature cret && (cret.AlwaysAttackable || cret.AlwaysMurderer);
return true; // felucca you can snoop anybody
}
public static void Container_Snoop(Container cont, Mobile from)
var reg = to.Region.GetRegion<GuardedRegion>();
if (reg?.IsDisabled() != true)
{
if (from.AccessLevel <= AccessLevel.Player && !from.InRange(cont.GetWorldLocation(), 1))
{
from.SendLocalizedMessage(500446); // That is too far away.
return;
}
return true; // not in town? we can snoop any npc
}
var root = cont.RootParent as Mobile;
return !to.Body.IsHuman || to is BaseCreature cret && (cret.AlwaysAttackable || cret.AlwaysMurderer);
}
if (root?.Alive == false)
{
return;
}
public static void Container_Snoop(Container cont, Mobile from)
{
if (from.AccessLevel <= AccessLevel.Player && !from.InRange(cont.GetWorldLocation(), 1))
{
from.SendLocalizedMessage(500446); // That is too far away.
return;
}
if (root?.AccessLevel > AccessLevel.Player && from.AccessLevel == AccessLevel.Player)
{
from.SendLocalizedMessage(500209); // You can not peek into the container.
return;
}
var root = cont.RootParent as Mobile;
if (root?.AccessLevel == AccessLevel.Player && !CheckSnoopAllowed(from, root))
{
from.SendLocalizedMessage(1001018); // You cannot perform negative acts on your target.
return;
}
if (root?.Alive == false)
{
return;
}
if (root?.AccessLevel == AccessLevel.Player &&
from.Skills.Snooping.Value < Utility.Random(100))
if (root?.AccessLevel > AccessLevel.Player || !CheckSnoopAllowed(from, root))
{
from.SendLocalizedMessage(1001018); // You cannot perform negative acts on your target.
return;
}
if (from.AccessLevel == AccessLevel.Player)
{
var snooping = from.Skills.Snooping.Value;
if (root != null && snooping < 100.0 && snooping < Utility.RandomDouble() * 100)
{
var map = from.Map;
@ -86,28 +82,25 @@ namespace Server.SkillHandlers
}
}
if (from.AccessLevel == AccessLevel.Player)
Titles.AwardKarma(from, -4, true);
}
if (from.AccessLevel > AccessLevel.Player || from.CheckTargetSkill(SkillName.Snooping, cont, 0.0, 100.0))
{
if ((cont as TrappableContainer)?.ExecuteTrap(from) == true)
{
Titles.AwardKarma(from, -4, true);
return;
}
if (from.AccessLevel > AccessLevel.Player || from.CheckTargetSkill(SkillName.Snooping, cont, 0.0, 100.0))
{
if (cont is TrappableContainer container && container.ExecuteTrap(from))
{
return;
}
cont.DisplayTo(from);
}
else
{
from.SendLocalizedMessage(500210); // You failed to peek into the container.
cont.DisplayTo(from);
}
else
if (from.Skills.Hiding.Value / 2 < Utility.RandomDouble() * 100)
{
from.SendLocalizedMessage(500210); // You failed to peek into the container.
if (from.Skills.Hiding.Value / 2 < Utility.Random(100))
{
from.RevealingAction();
}
from.RevealingAction();
}
}
}

View file

@ -52,10 +52,10 @@ namespace Server.Misc
return;
}
var value = double.Parse(valueStr);
try
{
var value = double.Parse(valueStr);
if (name.InsensitiveEquals("str"))
{
ChangeStrength(from, (int)value);

View file

@ -38,12 +38,12 @@
<ProjectReference Include="..\Server\Server.csproj" Private="false" PrivateAssets="All" IncludeAssets="None">
<IncludeInPackage>false</IncludeInPackage>
</ProjectReference>
<PackageReference Include="MailKit" Version="2.15.0" />
<PackageReference Include="MailKit" Version="3.0.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.7.2" />
<PackageReference Include="Argon2.Bindings" Version="1.11.0" />
<PackageReference Include="Zstd.Binaries" Version="1.2.0" />
<PackageReference Include="Zlib.Bindings" Version="1.8.0" />
<PackageReference Include="Argon2.Bindings" Version="1.12.0" />
<PackageReference Include="Zstd.Binaries" Version="1.3.0" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\SerializationGenerator\SerializationGenerator.csproj">

View file

@ -19,16 +19,17 @@ ModernUO [![Discord](https://img.shields.io/discord/751317910504603701?logo=disc
![MacOS 10.15/11/12](https://img.shields.io/badge/-monterey-222222?logo=apple&logoColor=white)
[![Debian 10/11](https://img.shields.io/badge/-bullseye-A81D33?logo=debian)](https://www.debian.org/distrib/)
[![Ubuntu 16/18/20 LTS](https://img.shields.io/badge/-20LTS-E95420?logo=ubuntu&logoColor=white)](https://ubuntu.com/download/server)
[![Linux Mint 17/18/19/20](https://img.shields.io/badge/-20-87CF3E?logo=linux%20mint&logoColor=white)](https://linuxmint.com/download.php)
[![CentOS 7/8](https://img.shields.io/badge/-8.5-262577?logo=centos&logoColor=white)](https://www.centos.org/download/)
[![Fedora 32/33/34](https://img.shields.io/badge/-34-0B57A4?logo=fedora&logoColor=white)](https://getfedora.org/en/server/download/)
[![Fedora 32/33/34](https://img.shields.io/badge/-fedora%2034-0B57A4)](https://getfedora.org/en/server/download/)
[![RedHat 7/8](https://img.shields.io/badge/-8-BE0000?logo=red%20hat&logoColor=white)](https://access.redhat.com/downloads)
#### Running the server
[![.NET](https://img.shields.io/badge/.NET-%206.0-5C2D91)](https://dotnet.microsoft.com/download/dotnet/6.0)
[![.NET](https://img.shields.io/badge/-6.0-5C2D91?logo=.NET)](https://dotnet.microsoft.com/download/dotnet/6.0)
#### Development
[![git](https://img.shields.io/badge/-git-F05032?logo=git&logoColor=white)](https://git-scm.com/downloads)
[![.NET](https://img.shields.io/badge/.NET-%206.0%20SDK-5C2D91)](https://dotnet.microsoft.com/download/dotnet/6.0)
[![.NET](https://img.shields.io/badge/-%206.0%20SDK-5C2D91?logo=.NET)](https://dotnet.microsoft.com/download/dotnet/6.0)
#### Supported IDEs
&nbsp;&nbsp;&nbsp;
@ -51,11 +52,12 @@ Rider 2021.3+&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&
- `win` - Windows 10/11/2016/2019/2022
- `osx` - MacOS 10.15/11.0+/12.0+ (Catalina, Big Sur, Monterey)
- `ubuntu.16.04`, `ubuntu.18.04` `ubuntu.20.04` - Ubuntu LTS
- `linuxmint.17`, `linuxmint.18`, `linuxmint.19` - Linux Mint
- `debian.10`, `debian.11` - Debian
- `centos.7`, `centos.8` - CentOS
- `fedora.32`, `fedora.33`, `fedora.34` - Fedora
- `rhel.7`, `rhel.8` - Redhat
- If blank, the operating system running the build is used
- If blank, the operating system running the build is used. Linux Mint 20 is not supported directly yet, so build explicitly against `ubuntu.20.04` instead.
**Note:** Building in Visual Studio (or Rider) will not run the schema migration. The schema migration ensures future changes
to the code will be backward compatible.
@ -64,6 +66,9 @@ to the code will be backward compatible.
- Follow the [publish](https://github.com/modernuo/ModernUO#publishing-builds) instructions
- Run `ModernUO.exe` or `dotnet ModernUO.dll` from the `Distribution` directory on the server
**Note:** If you are running a version of linux that isn't listed above, then you may have to install the following using a package manager:
* `libargon2-dev`, `libz-dev`, and `zstd`
## Thanks
- RunUO Team & Community
- ServUO Team & Community

View file

@ -82,14 +82,26 @@ html .md-footer-meta.md-typeset a:hover {
color: #e95420;
}
.linuxmint {
color: #87cF3e;
}
.debian {
color: #d70a53;
}
.fedora {
color: #3c6eb4;
}
.centos {
color: #212078;
}
.redhat {
color: #be0000;
}
.codehilitetable .linenodiv pre, .highlighttable .linenodiv pre {
color: var(--md-default-bg-color);
}

View file

@ -23,6 +23,9 @@ The operating system to build the server against. If not specified then the serv
:fontawesome-brands-windows:{: .windows } `win`<br>
:fontawesome-brands-apple:{: .apple } `osx`<br>
:fontawesome-brands-ubuntu:{: .ubuntu } `ubuntu.16.04`, `ubuntu.18.04` `ubuntu.20.04`<br>
:fontawesome-brands-ubuntu:{: .ubuntu } `ubuntu.14.04` `ubuntu.16.04`, `ubuntu.18.04` `ubuntu.20.04`<br>
:brands-linuxmint:{: .linuxmint } `linuxmint.17` `linuxmint.18`, `linuxmint.19`, `ubuntu.20.04` for v20<br>
:brands-debian:{: .debian } `debian.9`, `debian.10`<br>
:fontawesome-brands-centos:{: .centos } `centos.7`, `centos.8`
:fontawesome-brands-fedora:{: .fedora } `fedora.32`, `fedora.33`, `fedora.34`<br>
:fontawesome-brands-centos:{: .centos } `centos.7`, `centos.8`<br>
:fontawesome-brands-redhat:{: .redhat } `redhat.7`, `redhat.8`

View file

@ -6,7 +6,7 @@ title: Installation
=== "Windows"
### Prerequisites
1. Download and install the latest [.NET 5 SDK](https://dotnet.microsoft.com/download/dotnet/5.0)
1. Download and install the latest [.NET 6 SDK](https://dotnet.microsoft.com/download/dotnet/6.0)
1. Download and install from [here](https://git-scm.com/download/win)
!!! Tip
@ -21,14 +21,14 @@ title: Installation
```
=== "OSX"
<h3>Prerequisites</h3>
1. Download and install the latest [.NET 5 SDK](https://dotnet.microsoft.com/download/dotnet/5.0)
### Prerequisites
1. Download and install the latest [.NET 6 SDK](https://dotnet.microsoft.com/download/dotnet/6.0).
1. Using _terminal_, install [homebrew](https://brew.sh) and git:
```bash
/bin/bash -c "$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/master/install.sh)"
brew install git
```
<h3>Install ModernUO</h3>
### Install ModernUO
1. Using _terminal_, navigate to the folder where you want to install ModernUO and run:
```bash
git clone https://github.com/modernuo/modernuo
@ -36,8 +36,8 @@ title: Installation
```
=== "Linux"
<h3>Prerequisites</h3>
1. Download and install the latest [.NET Core SDK](instructions [here](https://docs.microsoft.com/en-us/dotnet/core/install/linux))
### Prerequisites
1. Download and install the latest [.NET 6 SDK](https://docs.microsoft.com/en-us/dotnet/core/install/linux).
1. Using _bash_, install git:
```bash
sudo apt update && sudo apt install git
@ -46,7 +46,7 @@ title: Installation
!!! Note
The command to install git might be different for your flavor of linux. Consult your local Google search for answers.
<h3>Install ModernUO</h3>
### Install ModernUO
1. Using _bash_, navigate to the folder where you want to install ModernUO and run:
```bash
git clone https://github.com/modernuo/modernuo

View file

@ -0,0 +1 @@
<svg role="img" viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg"><title>Linux Mint</title><path d="M0 1.693v4.193h1.828c1.276 0 1.502.865 1.502 2.058l.01 7.412c0 3.84 3.44 6.951 7.68 6.951h10.464c1.342 0 2.516-.83 2.516-2.108V8.706c0-3.84-3.44-6.95-7.683-6.95h-4.405v-.013L0 1.693zm5.723 2.566h2.102V14.82c0 1.413.984 2.51 2.139 2.51l7.17.03c1.496 0 2.661-1.01 2.661-2.206l-.012-5.607a1.2 1.2 0 0 0-.386-.91 1.224 1.224 0 0 0-.917-.384c-.374 0-.65.12-.918.384a1.2 1.2 0 0 0-.386.91v4.798h-2.223V9.548c0-.364-.124-.648-.389-.91a1.208 1.208 0 0 0-.917-.384c-.366 0-.647.12-.914.384-.265.262-.39.546-.39.91v4.798H10.12V9.548c0-.95.36-1.792 1.042-2.466a3.445 3.445 0 0 1 2.485-1.022c.937 0 1.752.345 2.413.97a3.448 3.448 0 0 1 2.42-.97c.954 0 1.803.348 2.485 1.022a3.385 3.385 0 0 1 1.041 2.466l.009 5.991c-.105 1.004-.539 1.894-1.28 2.637h-.002a4.367 4.367 0 0 1-3.174 1.314H9.574v-.038c-.976-.103-1.846-.519-2.57-1.217-.845-.825-1.281-1.846-1.281-3.01V4.26z"/></svg>

After

Width:  |  Height:  |  Size: 970 B

View file

@ -1,28 +0,0 @@
---
title: Quick Start
---
# Quick Start
If you are familiar with RunUO, then follow these steps to get your ModernUO server running in less than 10 minutes:
=== "Windows"
1. Download and install the [.NET 5 Runtime](https://dotnet.microsoft.com/download/dotnet/5.0)
1. Download and extract [ModernUO](https://github.com/modernuo/ModernUO/releases/latest) to a folder.
1. Navigate to the _Distribution_ folder.
1. Run `ModernUO.exe`
=== "OSX"
1. Download and install the [.NET 5 Runtime](https://dotnet.microsoft.com/download/dotnet/5.0)
1. Download and extract [ModernUO](https://github.com/modernuo/ModernUO/releases/latest) to a folder.
1. Using _terminal_, navigate to the _Distribution_ folder.
1. Run `dotnet ModernUO.dll`
=== "Linux"
1. Download and install the [.NET 5 Runtime](instructions [here](https://docs.microsoft.com/en-us/dotnet/core/install/linux))
1. Download and extract [ModernUO](https://github.com/modernuo/ModernUO/releases/latest) to a folder.
1. Using _bash_, navigate to the _Distribution_ folder.
1. Run `dotnet ModernUO.dll`
!!! Note
Follow the rest of the [Get Started](../installation) guide to learn how to connect, configure, and customize, your server.

View file

@ -3,6 +3,7 @@
site_name: ModernUO
site_author: Kamron Batman
site_url: https://modernuo.com
edit_uri: edit/main/docs/
# Repository
repo_name: modernuo/modernuo
@ -41,14 +42,16 @@ markdown_extensions:
- admonition
- footnotes
- meta
- markdown.extensions.toc:
- toc:
toc_depth: "1-1"
permalink: true
- attr_list
- pymdownx.highlight
- pymdownx.inlinehilite
- pymdownx.superfences
- pymdownx.snippets
- pymdownx.tabbed
- pymdownx.tabbed:
alternate_style: true
- pymdownx.caret
- pymdownx.mark
- pymdownx.tilde
@ -67,6 +70,5 @@ markdown_extensions:
nav:
- Home: 'index.md'
- 'Get Started':
- 'quick-start.md'
- 'installation.md'
- 'building-server.md'

View file

@ -12,7 +12,7 @@ elif [[ $(uname) = "Darwin" ]]; then
os="-r osx-x64"
elif [[ -f /etc/os-release ]]; then
. /etc/os-release
NAME="$(tr '[:upper:]' '[:lower:]' <<< $NAME)"
NAME="$(tr '[:upper:]' '[:lower:]' <<< $NAME | tr -d [:blank:])"
os="-r $NAME.$VERSION_ID-x64"
fi
@ -32,8 +32,11 @@ dotnet clean --verbosity quiet
echo dotnet restore --force-evaluate --source https://api.nuget.org/v3/index.json
dotnet restore --force-evaluate --source https://api.nuget.org/v3/index.json
echo dotnet publish ${config} ${os} --framework net6.0 --no-restore --self-contained=false -o Distribution/Assemblies Projects/UOContent/UOContent.csproj
dotnet publish ${config} ${os} --framework net6.0 --no-restore --self-contained=false -o Distribution/Assemblies Projects/UOContent/UOContent.csproj
echo dotnet build -c Release Projects/SerializationGenerator/SerializationGenerator.csproj
dotnet build -c Release Projects/SerializationGenerator/SerializationGenerator.csproj
echo dotnet publish ${config} ${os} --no-restore --self-contained=false -o Distribution/Assemblies Projects/UOContent/UOContent.csproj
dotnet publish ${config} ${os} --no-restore --self-contained=false -o Distribution/Assemblies Projects/UOContent/UOContent.csproj
echo dotnet build -c Release Projects/SerializationSchemaGenerator/SerializationSchemaGenerator.csproj
dotnet build -c Release Projects/SerializationSchemaGenerator/SerializationSchemaGenerator.csproj
@ -65,8 +68,11 @@ dotnet clean --verbosity quiet
echo dotnet restore --force-evaluate --source https://api.nuget.org/v3/index.json
dotnet restore --force-evaluate --source https://api.nuget.org/v3/index.json
echo dotnet publish %config% %os% --framework net6.0 --no-restore --self-contained=false -o Distribution\Assemblies Projects\UOContent\UOContent.csproj
dotnet publish %config% %os% --framework net6.0 --no-restore --self-contained=false -o Distribution\Assemblies Projects\UOContent\UOContent.csproj
echo dotnet build -c Release Projects/SerializationGenerator/SerializationGenerator.csproj
dotnet build -c Release Projects/SerializationGenerator/SerializationGenerator.csproj
echo dotnet publish %config% %os% --no-restore --self-contained=false -o Distribution\Assemblies Projects\UOContent\UOContent.csproj
dotnet publish %config% %os% --no-restore --self-contained=false -o Distribution\Assemblies Projects\UOContent\UOContent.csproj
echo dotnet build -c Release Projects/SerializationSchemaGenerator/SerializationSchemaGenerator.csproj
dotnet build -c Release Projects/SerializationSchemaGenerator/SerializationSchemaGenerator.csproj