Convert Regions/Spawns to JSON (#138)

This commit is contained in:
Kamron Batman 2020-05-26 00:34:29 -07:00 • committed by GitHub
parent b8316e9202
commit 390f30e706
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
117 changed files with 97306 additions and 4848 deletions

View file

@ -58,7 +58,8 @@ namespace Server
public static TypeCache GetTypeCache(Assembly asm)
{
if (asm == null) return m_NullCache ??= new TypeCache(null);
if (asm == null)
return m_NullCache ??= new TypeCache(null);
if (m_TypeCaches.TryGetValue(asm, out var c))
return c;
@ -68,6 +69,8 @@ namespace Server
public static Type FindFirstTypeForName(string name, bool ignoreCase = false, Func<Type, bool> predicate = null)
{
if (string.IsNullOrWhiteSpace(name)) return null;
var types = FindTypesByName(name, ignoreCase).ToList();
if (types.Count == 0)
return null;
@ -87,14 +90,19 @@ namespace Server
types[0];
}
public static IEnumerable<Type> FindTypesByName(string name, bool ignoreCase = false)
public static List<Type> FindTypesByName(string name, bool ignoreCase = false)
{
var types = new List<Type>();
if (ignoreCase)
name = name.ToLower();
for (var i = 0; i < Assemblies.Length; i++) types.AddRange(GetTypeCache(Assemblies[i])[name]);
for (var i = 0; i < Assemblies.Length; i++)
types.AddRange(GetTypeCache(Assemblies[i])[name]);
if (types.Count == 0)
types.AddRange(GetTypeCache(Core.Assembly)[name]);
return types;
}
@ -122,6 +130,7 @@ namespace Server
public TypeCache(Assembly asm)
{
m_Types = asm?.GetTypes() ?? Type.EmptyTypes;
var nameMap = new Dictionary<string, HashSet<int>>();
HashSet<int> refs;
Action<int, string> addToRefs = (index, key) =>
@ -136,6 +145,7 @@ namespace Server
nameMap.Add(key, refs);
}
};
var aliasType = typeof(TypeAliasAttribute);
for (var i = 0; i < m_Types.Length; i++)
{

View file

@ -3,7 +3,6 @@
// See LICENSE file in the project root for full license information.
using System.Buffers.Binary;
using System.Diagnostics;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;

View file

@ -1,7 +1,6 @@
// Copyright (c) Microsoft. All rights reserved.
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
using System.Diagnostics;
using System.Runtime.CompilerServices;
namespace System.Buffers

View file

@ -2,7 +2,6 @@
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
using System.Collections.Concurrent;
using System.Diagnostics;
using System.Threading;
namespace System.Buffers

View file

@ -71,14 +71,18 @@ namespace Server
void OnStatsQuery(Mobile beholder, Mobile beheld);
}
public interface ISpawner
// TODO: Add SpawnMap and change Spawner.Map to use it
public interface ISpawner : IEntity
{
bool UnlinkOnTaming { get; }
Point3D HomeLocation { get; }
int HomeRange { get; }
Region Region { get; }
bool ReturnOnDeactivate { get; }
void Remove(ISpawnable spawn);
Point3D GetSpawnPosition(ISpawnable spawned, Map map);
void Respawn();
}
public interface ISpawnable : IEntity

View file

@ -0,0 +1,34 @@
/*************************************************************************
* ModernUO *
* Copyright (C) 2019-2020 - ModernUO Development Team *
* Email: hi@modernuo.com *
* File: MapConverterFactory.cs *
* Created: 2020/05/23 - Updated: 2020/05/23 *
* *
* This program is free software: you can redistribute it and/or modify *
* it under the terms of the GNU General Public License as published by *
* the Free Software Foundation, either version 3 of the License, or *
* (at your option) any later version. *
* *
* This program is distributed in the hope that it will be useful, *
* but WITHOUT ANY WARRANTY; without even the implied warranty of *
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
* GNU General Public License for more details. *
* *
* You should have received a copy of the GNU General Public License *
* along with this program. If not, see <http://www.gnu.org/licenses/>. *
*************************************************************************/
using System;
using System.Text.Json;
using System.Text.Json.Serialization;
namespace Server.Json
{
public class MapConverterFactory : JsonConverterFactory
{
public override bool CanConvert(Type typeToConvert) => typeToConvert == typeof(Map);
public override JsonConverter CreateConverter(Type typeToConvert, JsonSerializerOptions options) => new MapConverter();
}
}

View file

@ -0,0 +1,105 @@
/*************************************************************************
* ModernUO *
* Copyright (C) 2019-2020 - ModernUO Development Team *
* Email: hi@modernuo.com *
* File: Point2DConverter.cs *
* Created: 2020/04/12 - Updated: 2020/05/23 *
* *
* This program is free software: you can redistribute it and/or modify *
* it under the terms of the GNU General Public License as published by *
* the Free Software Foundation, either version 3 of the License, or *
* (at your option) any later version. *
* *
* This program is distributed in the hope that it will be useful, *
* but WITHOUT ANY WARRANTY; without even the implied warranty of *
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
* GNU General Public License for more details. *
* *
* You should have received a copy of the GNU General Public License *
* along with this program. If not, see <http://www.gnu.org/licenses/>. *
*************************************************************************/
using System;
using System.Text.Json;
using System.Text.Json.Serialization;
namespace Server.Json
{
public class Point2DConverter : JsonConverter<Point2D>
{
private Point2D DeserializeArray(ref Utf8JsonReader reader)
{
Span<int> data = stackalloc int[2];
var count = 0;
while (true)
{
reader.Read();
if (reader.TokenType == JsonTokenType.EndArray)
break;
if (reader.TokenType == JsonTokenType.Number)
{
if (count < 2)
data[count] = reader.GetInt32();
count++;
}
}
if (count > 2)
throw new JsonException("Point2D must be an array of x, y");
return new Point2D(data[0], data[1]);
}
private Point2D DeserializeObj(ref Utf8JsonReader reader)
{
Span<int> data = stackalloc int[2];
while (true)
{
reader.Read();
if (reader.TokenType == JsonTokenType.EndObject)
break;
if (reader.TokenType != JsonTokenType.PropertyName)
throw new JsonException("Invalid Json structure for Point2D object");
var key = reader.GetString();
var i = key switch
{
"x" => 0,
"y" => 1,
_ => throw new JsonException($"Invalid property {key} for Point2D")
};
reader.Read();
if (reader.TokenType != JsonTokenType.Number)
throw new JsonException($"Value for {key} must be a number");
data[i] = reader.GetInt32();
}
return new Point2D(data[0], data[1]);
}
public override Point2D Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options) =>
reader.TokenType switch
{
JsonTokenType.StartArray => DeserializeArray(ref reader),
JsonTokenType.StartObject => DeserializeObj(ref reader),
_ => throw new JsonException("Invalid Json for Point3D")
};
public override void Write(Utf8JsonWriter writer, Point2D value, JsonSerializerOptions options)
{
writer.WriteStartArray();
writer.WriteNumberValue(value.X);
writer.WriteNumberValue(value.Y);
writer.WriteEndArray();
}
}
}

View file

@ -0,0 +1,34 @@
/*************************************************************************
* ModernUO *
* Copyright (C) 2019-2020 - ModernUO Development Team *
* Email: hi@modernuo.com *
* File: Point2DConverterFactory.cs *
* Created: 2020/05/23 - Updated: 2020/05/23 *
* *
* This program is free software: you can redistribute it and/or modify *
* it under the terms of the GNU General Public License as published by *
* the Free Software Foundation, either version 3 of the License, or *
* (at your option) any later version. *
* *
* This program is distributed in the hope that it will be useful, *
* but WITHOUT ANY WARRANTY; without even the implied warranty of *
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
* GNU General Public License for more details. *
* *
* You should have received a copy of the GNU General Public License *
* along with this program. If not, see <http://www.gnu.org/licenses/>. *
*************************************************************************/
using System;
using System.Text.Json;
using System.Text.Json.Serialization;
namespace Server.Json
{
public class Point2DConverterFactory : JsonConverterFactory
{
public override bool CanConvert(Type typeToConvert) => typeToConvert == typeof(Point2D) || typeToConvert == typeof(IPoint2D);
public override JsonConverter CreateConverter(Type typeToConvert, JsonSerializerOptions options) => new Point2DConverter();
}
}

View file

@ -2,8 +2,8 @@
* ModernUO *
* Copyright (C) 2019-2020 - ModernUO Development Team *
* Email: hi@modernuo.com *
* File: Point3dConverter.cs *
* Created: 2020/04/12 - Updated: 2020/05/02 *
* File: Point3DConverter.cs *
* Created: 2020/04/12 - Updated: 2020/05/23 *
* *
* This program is free software: you can redistribute it and/or modify *
* it under the terms of the GNU General Public License as published by *
@ -25,14 +25,11 @@ using System.Text.Json.Serialization;
namespace Server.Json
{
public class Point3dConverter : JsonConverter<Point3D>
public class Point3DConverter : JsonConverter<Point3D>
{
public override Point3D Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options)
private Point3D DeserializeArray(ref Utf8JsonReader reader)
{
if (reader.TokenType != JsonTokenType.StartArray)
throw new JsonException("Point3d must be an array of x, y, z");
var data = new int[3];
Span<int> data = stackalloc int[3];
var count = 0;
while (true)
@ -50,12 +47,54 @@ namespace Server.Json
}
}
if (count < 2 || count > 3)
throw new JsonException("Point3d must be an array of x, y, z");
if (count > 3)
throw new JsonException("Point3D must be an array of x, y, z");
return new Point3D(data[0], data[1], data[2]);
}
private Point3D DeserializeObj(ref Utf8JsonReader reader)
{
Span<int> data = stackalloc int[3];
while (true)
{
reader.Read();
if (reader.TokenType == JsonTokenType.EndObject)
break;
if (reader.TokenType != JsonTokenType.PropertyName)
throw new JsonException("Invalid Json structure for Point3D object");
var key = reader.GetString();
var i = key switch
{
"x" => 0,
"y" => 1,
"z" => 2,
_ => throw new JsonException($"Invalid property {key} for Point3D")
};
reader.Read();
if (reader.TokenType != JsonTokenType.Number)
throw new JsonException($"Value for {key} must be a number");
data[i] = reader.GetInt32();
}
return new Point3D(data[0], data[1], data[2]);
}
public override Point3D Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options) =>
reader.TokenType switch
{
JsonTokenType.StartArray => DeserializeArray(ref reader),
JsonTokenType.StartObject => DeserializeObj(ref reader),
_ => throw new JsonException("Invalid Json for Point3D")
};
public override void Write(Utf8JsonWriter writer, Point3D value, JsonSerializerOptions options)
{
writer.WriteStartArray();

View file

@ -0,0 +1,34 @@
/*************************************************************************
* ModernUO *
* Copyright (C) 2019-2020 - ModernUO Development Team *
* Email: hi@modernuo.com *
* File: Point3DConverterFactory.cs *
* Created: 2020/05/23 - Updated: 2020/05/23 *
* *
* This program is free software: you can redistribute it and/or modify *
* it under the terms of the GNU General Public License as published by *
* the Free Software Foundation, either version 3 of the License, or *
* (at your option) any later version. *
* *
* This program is distributed in the hope that it will be useful, *
* but WITHOUT ANY WARRANTY; without even the implied warranty of *
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
* GNU General Public License for more details. *
* *
* You should have received a copy of the GNU General Public License *
* along with this program. If not, see <http://www.gnu.org/licenses/>. *
*************************************************************************/
using System;
using System.Text.Json;
using System.Text.Json.Serialization;
namespace Server.Json
{
public class Point3DConverterFactory : JsonConverterFactory
{
public override bool CanConvert(Type typeToConvert) => typeToConvert == typeof(Point3D) || typeToConvert == typeof(IPoint3D);
public override JsonConverter CreateConverter(Type typeToConvert, JsonSerializerOptions options) => new Point3DConverter();
}
}

View file

@ -0,0 +1,156 @@
/*************************************************************************
* ModernUO *
* Copyright (C) 2019-2020 - ModernUO Development Team *
* Email: hi@modernuo.com *
* File: Rectangle3DConverter.cs *
* Created: 2020/05/23 - Updated: 2020/05/23 *
* *
* This program is free software: you can redistribute it and/or modify *
* it under the terms of the GNU General Public License as published by *
* the Free Software Foundation, either version 3 of the License, or *
* (at your option) any later version. *
* *
* This program is distributed in the hope that it will be useful, *
* but WITHOUT ANY WARRANTY; without even the implied warranty of *
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
* GNU General Public License for more details. *
* *
* You should have received a copy of the GNU General Public License *
* along with this program. If not, see <http://www.gnu.org/licenses/>. *
*************************************************************************/
using System;
using System.Text.Json;
using System.Text.Json.Serialization;
namespace Server.Json
{
public class Rectangle3DConverter : JsonConverter<Rectangle3D>
{
private Rectangle3D DeserializeArray(ref Utf8JsonReader reader)
{
Span<int> data = stackalloc int[6];
var count = 0;
while (true)
{
reader.Read();
if (reader.TokenType == JsonTokenType.EndArray)
break;
if (reader.TokenType == JsonTokenType.Number)
{
if (count < 6)
data[count] = reader.GetInt32();
count++;
}
}
if (count > 6)
throw new JsonException("Rectangle3D must be an array of x, y, z, h, w, d");
return new Rectangle3D(data[0], data[1], data[2], data[3], data[4], data[5]);
}
private Rectangle3D DeserializeObj(ref Utf8JsonReader reader, JsonSerializerOptions options)
{
Span<int> data = stackalloc int[6];
// 0 - xyzwhd, 1 - x1y1z1x2y2z2, 2 - start/end
int objType = -1;
while (true)
{
reader.Read();
if (reader.TokenType == JsonTokenType.EndObject)
break;
if (reader.TokenType != JsonTokenType.PropertyName)
throw new JsonException("Invalid Json structure for Rectangle3D object");
var key = reader.GetString();
reader.Read();
if (key == "start" || key == "end")
{
if (objType > -1 && objType != 2)
throw new JsonException("Rectangle3D must have a start/end, or x/y/z/w/h/d, but not both.");
objType = 2;
var point3D = reader.ToObject<Point3D>(options);
var offset = key == "end" ? 3 : 0;
data[0 + offset] = point3D.X;
data[1 + offset] = point3D.Y;
data[1 + offset] = point3D.Z;
continue;
}
var i = key switch
{
"x" => 0,
"y" => 1,
"z" => 2,
"w" => 3,
"width" => 3,
"h" => 4,
"height" => 4,
"d" => 5,
"depth" => 5,
"x1" => 10,
"y1" => 11,
"z1" => 12,
"x2" => 13,
"y2" => 14,
"z2" => 15,
_ => throw new JsonException($"Invalid property {key} for Rectangle3D")
};
if (i < 10)
{
if (objType > -1 && objType != 0)
throw new JsonException("Rectangle3D must have a start/end, or x/y/z/w/h/d, but not both.");
objType = 0;
data[i] = reader.GetInt32();
continue;
}
if (objType > -1 && objType != 1)
throw new JsonException("Rectangle3D must have a start/end, or x/y/z/w/h/d, but not both.");
objType = 1;
data[i - 10] = reader.GetInt32();
}
return objType == 0
? new Rectangle3D(data[0], data[1], data[2], data[3], data[4], data[5])
: new Rectangle3D(
new Point3D(data[0], data[1], data[2]),
new Point3D(data[3], data[4], data[5])
);
}
public override Rectangle3D Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options) =>
reader.TokenType switch
{
JsonTokenType.StartArray => DeserializeArray(ref reader),
JsonTokenType.StartObject => DeserializeObj(ref reader, options),
_ => throw new JsonException("Invalid Json for Point3D")
};
public override void Write(Utf8JsonWriter writer, Rectangle3D value, JsonSerializerOptions options)
{
writer.WriteStartArray();
writer.WriteNumberValue(value.Start.X);
writer.WriteNumberValue(value.Start.Y);
writer.WriteNumberValue(value.Start.Z);
writer.WriteNumberValue(value.Width);
writer.WriteNumberValue(value.Height);
writer.WriteNumberValue(value.Depth);
writer.WriteEndArray();
}
}
}

View file

@ -0,0 +1,35 @@
/*************************************************************************
* ModernUO *
* Copyright (C) 2019-2020 - ModernUO Development Team *
* Email: hi@modernuo.com *
* File: Rectangle3DConverterFactory.cs *
* Created: 2020/05/23 - Updated: 2020/05/23 *
* *
* This program is free software: you can redistribute it and/or modify *
* it under the terms of the GNU General Public License as published by *
* the Free Software Foundation, either version 3 of the License, or *
* (at your option) any later version. *
* *
* This program is distributed in the hope that it will be useful, *
* but WITHOUT ANY WARRANTY; without even the implied warranty of *
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
* GNU General Public License for more details. *
* *
* You should have received a copy of the GNU General Public License *
* along with this program. If not, see <http://www.gnu.org/licenses/>. *
*************************************************************************/
using System;
using System.Text.Json;
using System.Text.Json.Serialization;
namespace Server.Json
{
public class Rectangle3DConverterFactory : JsonConverterFactory
{
public override bool CanConvert(Type typeToConvert) => typeToConvert == typeof(Rectangle3D);
public override JsonConverter CreateConverter(Type typeToConvert, JsonSerializerOptions options) =>
new Rectangle3DConverter();
}
}

View file

@ -0,0 +1,36 @@
/*************************************************************************
* ModernUO *
* Copyright (C) 2019-2020 - ModernUO Development Team *
* Email: hi@modernuo.com *
* File: TimeSpanConverter.cs *
* Created: 2020/04/12 - Updated: 2020/05/02 *
* *
* This program is free software: you can redistribute it and/or modify *
* it under the terms of the GNU General Public License as published by *
* the Free Software Foundation, either version 3 of the License, or *
* (at your option) any later version. *
* *
* This program is distributed in the hope that it will be useful, *
* but WITHOUT ANY WARRANTY; without even the implied warranty of *
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
* GNU General Public License for more details. *
* *
* You should have received a copy of the GNU General Public License *
* along with this program. If not, see <http://www.gnu.org/licenses/>. *
*************************************************************************/
using System;
using System.Text.Json;
using System.Text.Json.Serialization;
namespace Server.Json
{
public class TimeSpanConverter : JsonConverter<TimeSpan>
{
public override TimeSpan Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options)
=> TimeSpan.Parse(reader.GetString());
public override void Write(Utf8JsonWriter writer, TimeSpan value, JsonSerializerOptions options)
=> writer.WriteStringValue(value.ToString());
}
}

View file

@ -0,0 +1,34 @@
/*************************************************************************
* ModernUO *
* Copyright (C) 2019-2020 - ModernUO Development Team *
* Email: hi@modernuo.com *
* File: TimeSpanConverterFactory.cs *
* Created: 2020/05/23 - Updated: 2020/05/23 *
* *
* This program is free software: you can redistribute it and/or modify *
* it under the terms of the GNU General Public License as published by *
* the Free Software Foundation, either version 3 of the License, or *
* (at your option) any later version. *
* *
* This program is distributed in the hope that it will be useful, *
* but WITHOUT ANY WARRANTY; without even the implied warranty of *
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
* GNU General Public License for more details. *
* *
* You should have received a copy of the GNU General Public License *
* along with this program. If not, see <http://www.gnu.org/licenses/>. *
*************************************************************************/
using System;
using System.Text.Json;
using System.Text.Json.Serialization;
namespace Server.Json
{
public class TimeSpanConverterFactory : JsonConverterFactory
{
public override bool CanConvert(Type typeToConvert) => typeToConvert == typeof(TimeSpan);
public override JsonConverter CreateConverter(Type typeToConvert, JsonSerializerOptions options) => new TimeSpanConverter();
}
}

View file

@ -0,0 +1,57 @@
/*************************************************************************
* ModernUO *
* Copyright (C) 2019-2020 - ModernUO Development Team *
* Email: hi@modernuo.com *
* File: DynamicJson.cs - Created: 2020/05/23 - Updated: 2020/05/23 *
* *
* This program is free software: you can redistribute it and/or modify *
* it under the terms of the GNU General Public License as published by *
* the Free Software Foundation, either version 3 of the License, or *
* (at your option) any later version. *
* *
* This program is distributed in the hope that it will be useful, *
* but WITHOUT ANY WARRANTY; without even the implied warranty of *
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
* GNU General Public License for more details. *
* *
* You should have received a copy of the GNU General Public License *
* along with this program. If not, see <http://www.gnu.org/licenses/>. *
*************************************************************************/
using System;
using System.Collections.Generic;
using System.Text.Json;
using System.Text.Json.Serialization;
namespace Server.Json
{
public class DynamicJson
{
[JsonPropertyName("type")]
public string Type { get; set; }
[JsonExtensionData]
public Dictionary<string, JsonElement> data { get; set; }
public bool GetProperty<T>(string key, JsonSerializerOptions options, out T t)
{
if (data.TryGetValue(key, out var el))
{
t = el.ToObject<T>(options);
return true;
}
t = default;
return false;
}
public bool GetEnumProperty<T>(string key, JsonSerializerOptions options, out T t) where T : struct, Enum
{
if (data.TryGetValue(key, out var el))
return Enum.TryParse(el.ToObject<string>(options), out t);
t = default;
return false;
}
}
}

View file

@ -18,6 +18,8 @@
* along with this program. If not, see <http://www.gnu.org/licenses/>. *
*************************************************************************/
using System;
using System.Buffers;
using System.IO;
using System.Text.Json;
@ -46,5 +48,23 @@ namespace Server.Json
File.WriteAllText(filePath, JsonSerializer.Serialize(value, options ?? Options));
}
public static T ToObject<T>(this ref Utf8JsonReader reader, JsonSerializerOptions options = null) =>
JsonSerializer.Deserialize<T>(ref reader, options);
public static T ToObject<T>(this JsonElement element, JsonSerializerOptions options = null)
{
var bufferWriter = new ArrayBufferWriter<byte>();
using (var writer = new Utf8JsonWriter(bufferWriter))
element.WriteTo(writer);
return JsonSerializer.Deserialize<T>(bufferWriter.WrittenSpan, options);
}
public static T ToObject<T>(this JsonDocument document, JsonSerializerOptions options = null)
{
if (document == null)
throw new ArgumentNullException(nameof(document));
return document.RootElement.ToObject<T>(options);
}
}
}

View file

@ -376,7 +376,7 @@ namespace Server
AssemblyHandler.Invoke("Configure");
Region.Load();
RegionLoader.LoadRegions();
World.Load();
AssemblyHandler.Invoke("Initialize");

View file

@ -374,15 +374,9 @@ namespace Server
public int CompareTo(Map other) => other == null ? -1 : MapID.CompareTo(other.MapID);
public static string[] GetMapNames()
{
return Maps.Where(m => m != null).Select(m => m.Name).ToArray();
}
public static string[] GetMapNames() => Maps.Where(m => m != null).Select(m => m.Name).ToArray();
public static Map[] GetMapValues()
{
return Maps.Where(m => m != null).ToArray();
}
public static Map[] GetMapValues() => Maps.Where(m => m != null).ToArray();
public static Map Parse(string value)
{

View file

@ -20,7 +20,6 @@
*************************************************************************/
using System;
using System.Threading;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Connections;
using Microsoft.Extensions.Logging;

View file

@ -20,11 +20,10 @@
using System;
using System.Collections.Generic;
using System.IO;
using System.Xml;
using System.Text.Json;
using Server.Json;
using Server.Network;
using Server.Targeting;
using Server.Utilities;
namespace Server
{
@ -146,10 +145,10 @@ namespace Server
}
}
public Region(XmlElement xml, Map map, Region parent)
public Region(DynamicJson json, JsonSerializerOptions options)
{
Map = map;
Parent = parent;
Map = json.GetProperty("map", options, out Map map) ? map : null;
Parent = json.GetProperty("parent", options, out string parent) ? Find(parent, Map) : null;
Dynamic = false;
if (Parent == null)
@ -163,31 +162,21 @@ namespace Server
m_Priority = Parent.Priority;
}
ReadString(xml, "name", ref m_Name, false);
m_Name = json.GetProperty("name", options, out string name) ? name : null;
if (parent == null)
ReadInt32(xml, "priority", ref m_Priority, false);
m_Priority = json.GetProperty("priority", options, out int priority) ? priority : 0;
var minZ = MinZ;
var maxZ = MaxZ;
var zrange = xml["zrange"];
ReadInt32(zrange, "min", ref minZ, false);
ReadInt32(zrange, "max", ref maxZ, false);
var area = new List<Rectangle3D>();
foreach (XmlElement xmlRect in xml.SelectNodes("rect"))
{
if (ReadRectangle3D(xmlRect, minZ, maxZ, out var rect))
area.Add(rect);
}
Area = area.ToArray();
Area = json.GetProperty("rects", options, out List<Rectangle3D> rects) ?
rects.ToArray() : Array.Empty<Rectangle3D>();
if (Area.Length == 0)
Console.WriteLine("Empty area for region '{0}'", this);
if (!ReadPoint3D(xml["go"], map, ref m_GoLocation, false) && Area.Length > 0)
if (json.GetProperty("go", options, out Point3D go))
{
m_GoLocation = go;
}
else if (Area.Length > 0)
{
var start = Area[0].Start;
var end = Area[0].End;
@ -195,14 +184,10 @@ namespace Server
var x = start.X + (end.X - start.X) / 2;
var y = start.Y + (end.Y - start.Y) / 2;
m_GoLocation = new Point3D(x, y, Map.GetAverageZ(x, y));
m_GoLocation = new Point3D(x, y, Map?.GetAverageZ(x, y) ?? start.Z + (end.Z - start.Z) / 2);
}
var music = DefaultMusic;
ReadEnum(xml["music"], "name", ref music, false);
Music = music;
Music = json.GetEnumProperty("music", options, out MusicName music) ? music : DefaultMusic;
}
public static List<Region> Regions { get; } = new List<Region>();
@ -267,6 +252,29 @@ namespace Server
return reg.ChildLevel - ChildLevel;
}
// This is not optimized. Use sparingly
public static Region Find(string name, Map map, bool insensitive = false)
{
if (insensitive)
name = name.ToLower();
for (int i = 0; i < Regions.Count; i++)
{
var region = Regions[i];
if (region.Map != map)
continue;
string rName = region.Name;
if (insensitive)
rName = rName.ToLower();
if (rName == name)
return region;
}
return null;
}
public static Region Find(Point3D p, Map map)
{
if (map == null)
@ -727,338 +735,5 @@ namespace Server
}
}
}
internal static void Load()
{
if (!File.Exists("Data/Regions.xml"))
{
Console.WriteLine("Error: Data/Regions.xml does not exist");
return;
}
Console.Write("Regions: Loading...");
var doc = new XmlDocument();
doc.Load(Path.Combine(Core.BaseDirectory, "Data/Regions.xml"));
var root = doc["ServerRegions"];
if (root == null)
{
Console.WriteLine("Could not find root element 'ServerRegions' in Regions.xml");
return;
}
foreach (XmlElement facet in root.SelectNodes("Facet"))
{
Map map = null;
if (ReadMap(facet, "name", ref map))
{
if (map == Map.Internal)
Console.WriteLine("Invalid internal map in a facet element");
else
LoadRegions(facet, map, null);
}
}
Console.WriteLine("done");
}
private static void LoadRegions(XmlElement xml, Map map, Region parent)
{
foreach (XmlElement xmlReg in xml.SelectNodes("region"))
{
var type = DefaultRegionType;
ReadType(xmlReg, "type", ref type, false);
if (!typeof(Region).IsAssignableFrom(type))
{
Console.WriteLine("Invalid region type '{0}' in regions.xml", type.FullName);
continue;
}
Region region;
try
{
region = (Region)ActivatorUtil.CreateInstance(type, xmlReg, map, parent);
}
catch (Exception ex)
{
Console.WriteLine("Error during the creation of region type '{0}': {1}", type.FullName, ex);
continue;
}
region.Register();
LoadRegions(xmlReg, map, region);
}
}
protected static string GetAttribute(XmlElement xml, string attribute, bool mandatory)
{
if (xml == null)
{
if (mandatory)
Console.WriteLine("Missing element for attribute '{0}'", attribute);
return null;
}
if (xml.HasAttribute(attribute)) return xml.GetAttribute(attribute);
if (mandatory)
Console.WriteLine("Missing attribute '{0}' in element '{1}'", attribute, xml.Name);
return null;
}
public static bool ReadString(XmlElement xml, string attribute, ref string value) =>
ReadString(xml, attribute, ref value, true);
public static bool ReadString(XmlElement xml, string attribute, ref string value, bool mandatory)
{
var s = GetAttribute(xml, attribute, mandatory);
if (s == null)
return false;
value = s;
return true;
}
public static bool ReadInt32(XmlElement xml, string attribute, ref int value) =>
ReadInt32(xml, attribute, ref value, true);
public static bool ReadInt32(XmlElement xml, string attribute, ref int value, bool mandatory)
{
var s = GetAttribute(xml, attribute, mandatory);
if (s == null)
return false;
try
{
value = XmlConvert.ToInt32(s);
}
catch
{
Console.WriteLine("Could not parse integer attribute '{0}' in element '{1}'", attribute, xml.Name);
return false;
}
return true;
}
public static bool ReadBoolean(XmlElement xml, string attribute, ref bool value) =>
ReadBoolean(xml, attribute, ref value, true);
public static bool ReadBoolean(XmlElement xml, string attribute, ref bool value, bool mandatory)
{
var s = GetAttribute(xml, attribute, mandatory);
if (s == null)
return false;
try
{
value = XmlConvert.ToBoolean(s);
}
catch
{
Console.WriteLine("Could not parse boolean attribute '{0}' in element '{1}'", attribute, xml.Name);
return false;
}
return true;
}
public static bool ReadDateTime(XmlElement xml, string attribute, ref DateTime value) =>
ReadDateTime(xml, attribute, ref value, true);
public static bool ReadDateTime(XmlElement xml, string attribute, ref DateTime value, bool mandatory)
{
var s = GetAttribute(xml, attribute, mandatory);
if (s == null)
return false;
try
{
value = XmlConvert.ToDateTime(s, XmlDateTimeSerializationMode.Utc);
}
catch
{
Console.WriteLine("Could not parse DateTime attribute '{0}' in element '{1}'", attribute, xml.Name);
return false;
}
return true;
}
public static bool ReadTimeSpan(XmlElement xml, string attribute, ref TimeSpan value) =>
ReadTimeSpan(xml, attribute, ref value, true);
public static bool ReadTimeSpan(XmlElement xml, string attribute, ref TimeSpan value, bool mandatory)
{
var s = GetAttribute(xml, attribute, mandatory);
if (s == null)
return false;
try
{
value = XmlConvert.ToTimeSpan(s);
}
catch
{
Console.WriteLine("Could not parse TimeSpan attribute '{0}' in element '{1}'", attribute, xml.Name);
return false;
}
return true;
}
public static bool ReadEnum<T>(XmlElement xml, string attribute, ref T value) where T : struct =>
ReadEnum(xml, attribute, ref value, true);
public static bool ReadEnum<T>(XmlElement xml, string attribute, ref T value, bool mandatory)
where T : struct // We can't limit the where clause to Enums only
{
var s = GetAttribute(xml, attribute, mandatory);
if (s == null)
return false;
var type = typeof(T);
if (type.IsEnum && Enum.TryParse(s, true, out T tempVal))
{
value = tempVal;
return true;
}
Console.WriteLine("Could not parse {0} enum attribute '{1}' in element '{2}'", type, attribute, xml.Name);
return false;
}
public static bool ReadMap(XmlElement xml, string attribute, ref Map value) => ReadMap(xml, attribute, ref value, true);
public static bool ReadMap(XmlElement xml, string attribute, ref Map value, bool mandatory)
{
var s = GetAttribute(xml, attribute, mandatory);
if (s == null)
return false;
try
{
value = Map.Parse(s);
}
catch
{
Console.WriteLine("Could not parse Map attribute '{0}' in element '{1}'", attribute, xml.Name);
return false;
}
return true;
}
public static bool ReadType(XmlElement xml, string attribute, ref Type value) =>
ReadType(xml, attribute, ref value, true);
public static bool ReadType(XmlElement xml, string attribute, ref Type value, bool mandatory)
{
var s = GetAttribute(xml, attribute, mandatory);
if (s == null)
return false;
Type type;
try
{
type = AssemblyHandler.FindFirstTypeForName(s);
}
catch
{
Console.WriteLine("Could not parse Type attribute '{0}' in element '{1}'", attribute, xml.Name);
return false;
}
if (type == null)
{
Console.WriteLine("Could not find Type '{0}'", s);
return false;
}
value = type;
return true;
}
public static bool ReadPoint3D(XmlElement xml, Map map, ref Point3D value) => ReadPoint3D(xml, map, ref value, true);
public static bool ReadPoint3D(XmlElement xml, Map map, ref Point3D value, bool mandatory)
{
int x = 0, y = 0, z = 0;
var xyOk = ReadInt32(xml, "x", ref x, mandatory) & ReadInt32(xml, "y", ref y, mandatory);
var zOk = ReadInt32(xml, "z", ref z, mandatory && map == null);
if (xyOk && (zOk || map != null))
{
if (!zOk)
z = map.GetAverageZ(x, y);
value = new Point3D(x, y, z);
return true;
}
return false;
}
public static bool ReadRectangle3D(XmlElement xml, int defaultMinZ, int defaultMaxZ, out Rectangle3D value) =>
ReadRectangle3D(xml, defaultMinZ, defaultMaxZ, out value, true);
public static bool ReadRectangle3D(XmlElement xml, int defaultMinZ, int defaultMaxZ, out Rectangle3D value,
bool mandatory)
{
int x1 = 0, y1 = 0, x2 = 0, y2 = 0;
var z1 = defaultMinZ;
var z2 = defaultMaxZ;
if (xml.HasAttribute("x"))
{
if (ReadInt32(xml, "x", ref x1, mandatory)
& ReadInt32(xml, "y", ref y1, mandatory)
& ReadInt32(xml, "width", ref x2, mandatory)
& ReadInt32(xml, "height", ref y2, mandatory))
{
x2 += x1;
y2 += y1;
}
else
{
value = new Rectangle3D(new Point3D(x1, y1, z1), new Point3D(x2, y2, z2));
return false;
}
}
else
{
if (!ReadInt32(xml, "x1", ref x1, mandatory)
| !ReadInt32(xml, "y1", ref y1, mandatory)
| !ReadInt32(xml, "x2", ref x2, mandatory)
| !ReadInt32(xml, "y2", ref y2, mandatory))
{
value = new Rectangle3D(new Point3D(x1, y1, z1), new Point3D(x2, y2, z2));
return false;
}
}
ReadInt32(xml, "zmin", ref z1, false);
ReadInt32(xml, "zmax", ref z2, false);
value = new Rectangle3D(new Point3D(x1, y1, z1), new Point3D(x2, y2, z2));
return true;
}
}
}

View file

@ -0,0 +1,60 @@
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.IO;
using System.Text.Json;
using Server.Json;
using Server.Utilities;
namespace Server
{
public static class RegionLoader
{
public static void LoadRegions()
{
var path = Path.Join(Core.BaseDirectory, "Data/regions.json");
// Json Deserialization options for custom objects
JsonSerializerOptions options = new JsonSerializerOptions();
options.Converters.Add(new MapConverterFactory());
options.Converters.Add(new Point3DConverterFactory());
options.Converters.Add(new Rectangle3DConverterFactory());
List<string> failures = new List<string>();
int count = 0;
Console.Write("Regions: Loading...");
var stopwatch = Stopwatch.StartNew();
List<DynamicJson> regions = JsonConfig.Deserialize<List<DynamicJson>>(path);
foreach (var json in regions)
{
Type type = AssemblyHandler.FindFirstTypeForName(json.Type);
if (type == null || !typeof(Region).IsAssignableFrom(type))
{
failures.Add($"\tInvalid region type {json.Type}");
continue;
}
var region = ActivatorUtil.CreateInstance(type, json, options) as Region;
region?.Register();
count++;
}
stopwatch.Stop();
Console.ForegroundColor = failures.Count > 0 ? ConsoleColor.Yellow : ConsoleColor.Green;
Console.Write("done{0}. ", failures.Count > 0 ? " with failures" : "");
Console.ResetColor();
Console.WriteLine("({0} regions, {1} failures) ({2:F2} seconds)", count, failures.Count, stopwatch.Elapsed.TotalSeconds);
if (failures.Count > 0)
{
Console.ForegroundColor = ConsoleColor.Red;
Console.WriteLine(string.Join("\n", failures));
Console.ResetColor();
}
}
}
}

View file

@ -75,10 +75,10 @@
<CodeAnalysisRuleSet>..\..\Rules.ruleset</CodeAnalysisRuleSet>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="Microsoft.AspNetCore.Connections.Abstractions" Version="3.1.3" />
<PackageReference Include="Microsoft.AspNetCore.Server.Kestrel.Transport.Libuv" Version="3.1.3" />
<PackageReference Include="Microsoft.Extensions.Hosting.Abstractions" Version="3.1.3" />
<PackageReference Include="System.IO.Pipelines" Version="4.7.1" />
<PackageReference Include="Microsoft.AspNetCore.Connections.Abstractions" Version="3.1.4" />
<PackageReference Include="Microsoft.AspNetCore.Server.Kestrel.Transport.Libuv" Version="3.1.4" />
<PackageReference Include="Microsoft.Extensions.Hosting.Abstractions" Version="3.1.4" />
<PackageReference Include="System.IO.Pipelines" Version="4.7.2" />
<PackageReference Include="ZLib.Bindings" Version="1.0.0" />
</ItemGroup>
<ItemGroup Condition="'$(Configuration)'=='Analyze'">
@ -87,7 +87,7 @@
<PrivateAssets>all</PrivateAssets>
</PackageReference>
<PackageReference Include="Microsoft.CodeAnalysis.FxCopAnalyzers">
<Version>2.9.8</Version>
<Version>3.0.0</Version>
<PrivateAssets>all</PrivateAssets>
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
</PackageReference>

View file

@ -19,7 +19,6 @@
***************************************************************************/
using System;
using System.Buffers.Binary;
using System.IO;
using System.Text;

View file

@ -47,7 +47,7 @@ namespace Server.Utilities
// check all given args map to params.
for (var i = 0; i < args.Length; i++)
// if a null reference is passed, but the type is not nullable
if ((args[i] == null && paramList[i].ParameterType.IsValueType)
if (args[i] == null && paramList[i].ParameterType.IsValueType
// or if an arg is not null and is not assignable to the parameter type, skip.
|| !(args[i] == null || paramList[i].ParameterType.IsAssignableFrom(args[i])))
return false;
@ -65,7 +65,8 @@ namespace Server.Utilities
}
catch (Exception e)
{
throw new TypeInitializationException(type.ToString(), e);
Console.WriteLine(e);
throw;
}
}

View file

@ -24,7 +24,8 @@ namespace Server
{
public interface IRandomProvider
{
public uint Next(uint c);
public ulong Next(ulong max);
public uint Next(uint max);
public bool NextBool();
public void GetBytes(Span<byte> b);
public double NextDouble();

View file

@ -31,6 +31,8 @@ namespace Server
public override void GetBytes(Span<byte> data) => m_Random.GetBytes(data);
public ulong Next(ulong c) => throw new NotImplementedException();
public uint Next(uint c) => throw new NotImplementedException();
public bool NextBool() => throw new NotImplementedException();