Adds official .NET 5 support (#300)

This commit is contained in:
Kamron Batman 2020-11-07 13:26:27 -08:00 committed by GitHub
parent c3289a20ab
commit 013f333898
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
39 changed files with 496 additions and 668 deletions

View file

@ -19,15 +19,15 @@ jobs:
- uses: actions/checkout@v2
with:
fetch-depth: 0 # avoid shallow clone so nbgv can do its work.
- name: Setup .NET Core
- name: Setup .NET 5
uses: actions/setup-dotnet@v1
with:
dotnet-version: 3.1.401
dotnet-version: 5.0.100-rc.2.20479.15
- name: Restore NuGet Packages
run: dotnet restore --force-evaluate
- name: Build Server
run: dotnet build -c Release -f netcoreapp3.1 --no-restore Projects/Server/Server.csproj
run: dotnet build -c Release -f net5.0 --no-restore Projects/Server/Server.csproj
- name: Build UO Content
run: dotnet build -c Release -f netcoreapp3.1 --no-restore Projects/UOContent/UOContent.csproj
run: dotnet build -c Release -f net5.0 --no-restore Projects/UOContent/UOContent.csproj
- name: Test
run: dotnet test --no-restore -f netcoreapp3.1
run: dotnet test --no-restore -f net5.0

View file

@ -14,10 +14,10 @@ jobs:
- uses: actions/checkout@v2
with:
fetch-depth: 0 # avoid shallow clone so nbgv can do its work.
- name: Setup .NET Core
- name: Setup .NET 5
uses: actions/setup-dotnet@v1
with:
dotnet-version: 3.1.401
dotnet-version: 5.0.100-rc.2.20479.15
- uses: dotnet/nbgv@master
id: nbgv
- name: Create Release
@ -50,10 +50,10 @@ jobs:
- uses: actions/checkout@v2
with:
fetch-depth: 0 # avoid shallow clone so nbgv can do its work.
- name: Setup .NET Core
- name: Setup .NET 5
uses: actions/setup-dotnet@v1
with:
dotnet-version: 3.1.401
dotnet-version: 5.0.100-rc.2.20479.15
- uses: dotnet/nbgv@master
id: nbgv
- name: Load Release URL File from release job
@ -67,7 +67,7 @@ jobs:
echo ::set-output name=upload_url::$value
rm -rf ./release_url
- name: Publish Distribution
run: ./publish.cmd ${{ matrix.os }} core Release
run: ./publish.cmd release ${{ matrix.os }}
- name: Create Artifact
run: rm -rf ./Projects/*/bin && rm -rf ./Projects/*/obj && zip -9 -r modernuo-asset.zip ./*
- name: Upload Release

View file

@ -41,5 +41,5 @@ jobs:
shell: powershell
run: |
.\.sonar\scanner\dotnet-sonarscanner begin /k:"modernuo_ModernUO" /o:"modernuo" /d:sonar.login="${{ secrets.SONAR_TOKEN }}" /d:sonar.host.url="https://sonarcloud.io"
.\publish.cmd win core Release
.\publish.cmd release win
.\.sonar\scanner\dotnet-sonarscanner end /d:sonar.login="${{ secrets.SONAR_TOKEN }}"

22
.gitignore vendored
View file

@ -8,33 +8,13 @@
/Distribution/Backups
/Distribution/Saves
/Distribution/docs
/Distribution/System.IO.Pipelines.dll
/Distribution/libdrng.dll
/Distribution/zlib.dll
/Distribution/libz.dylib
/Distribution/libz.so
/Distribution/Microsoft.Bcl.AsyncInterfaces.dll
/Distribution/System.ComponentModel.Annotations.dll
/Distribution/System.Runtime.CompilerServices.Unsafe.dll
/Distribution/ZLib.Bindings.dll
/Distribution/Data/modernuo.json
/Distribution/runtimes
/Distribution/nohup.out
# LibUv Dependencies
/Distribution/libuv.dylib
/Distribution/libuv.dll
/Distribution/libuv.so
/Distribution/Microsoft.AspNetCore.Connections.Abstractions.dll
/Distribution/Microsoft.AspNetCore.Http.Features.dll
/Distribution/Microsoft.AspNetCore.Server.Kestrel.Transport.Libuv.dll
/Distribution/Microsoft.Extensions.Configuration.Abstractions.dll
/Distribution/Microsoft.Extensions.DependencyInjection.Abstractions.dll
/Distribution/Microsoft.Extensions.FileProviders.Abstractions.dll
/Distribution/Microsoft.Extensions.Hosting.Abstractions.dll
/Distribution/Microsoft.Extensions.Logging.Abstractions.dll
/Distribution/Microsoft.Extensions.Options.dll
/Distribution/Microsoft.Extensions.Primitives.dll
/Distribution/ref
/Projects/*/obj
/Projects/*/bin

View file

@ -4,10 +4,10 @@
<Authors>Kamron Batman</Authors>
<Company>ModernUO</Company>
<Copyright>2019-2020</Copyright>
<TargetFramework>netcoreapp3.1</TargetFramework>
<TargetFrameworks>netcoreapp3.1;net5.0</TargetFrameworks>
<Platforms>x64</Platforms>
<PlatformTarget>x64</PlatformTarget>
<LangVersion>8.0</LangVersion>
<LangVersion>9</LangVersion>
<PublicRelease>true</PublicRelease>
<AutoGenerateBindingRedirects>true</AutoGenerateBindingRedirects>
<NoWarn>NU1603</NoWarn>
@ -30,6 +30,7 @@
<DisableFastUpToDateCheck>true</DisableFastUpToDateCheck>
<SelfContained>false</SelfContained>
<CopyLocalLockFileAssemblies>true</CopyLocalLockFileAssemblies>
<InvariantGlobalization>true</InvariantGlobalization>
</PropertyGroup>
<PropertyGroup Condition="'$(IsWindows)'=='true' AND '$(RuntimeIdentifier)'==''">
<RuntimeIdentifier>win-x64</RuntimeIdentifier>

View file

@ -64,7 +64,7 @@ namespace Server.Tests.Network
public long GetTotalGold() => TotalGold + TotalPlat * 100;
public int CompareTo(IAccount other) => other == null ? 1 : Username.CompareTo(other.Username);
public int CompareTo(IAccount other) => string.CompareOrdinal(Username, other?.Username);
public string Username { get; set; }
public string Email { get; set; }
@ -90,6 +90,6 @@ namespace Server.Tests.Network
public bool CheckPassword(string password) => m_Password == password;
public int CompareTo(MockAccount other) => other == null ? 1 : Username.CompareTo(other.Username);
public int CompareTo(MockAccount other) => string.CompareOrdinal(Username, other?.Username);
}
}

View file

@ -3,7 +3,7 @@
<IsPackable>false</IsPackable>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="16.7.0" />
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="16.7.1" />
<PackageReference Include="xunit" Version="2.4.1" />
<PackageReference Include="xunit.runner.visualstudio" Version="2.4.3" />
<PackageReference Include="coverlet.collector" Version="1.3.0" />

View file

@ -60,12 +60,12 @@ namespace Server
}
}
if (fmt.IndexOf("god") >= 0 || fmt.IndexOf("gq") >= 0)
if (fmt.IndexOf("god", StringComparison.Ordinal) >= 0 || fmt.IndexOf("gq", StringComparison.Ordinal) >= 0)
{
Type = ClientType.God;
}
else if (fmt.IndexOf("third dawn") >= 0 || fmt.IndexOf("uo:td") >= 0 || fmt.IndexOf("uotd") >= 0 ||
fmt.IndexOf("uo3d") >= 0 || fmt.IndexOf("uo:3d") >= 0)
else if (fmt.IndexOf("third dawn", StringComparison.Ordinal) >= 0 || fmt.IndexOf("uo:td", StringComparison.Ordinal) >= 0 || fmt.IndexOf("uotd", StringComparison.Ordinal) >= 0 ||
fmt.IndexOf("uo3d", StringComparison.Ordinal) >= 0 || fmt.IndexOf("uo:3d", StringComparison.Ordinal) >= 0)
{
Type = ClientType.UOTD;
}

View file

@ -108,7 +108,7 @@ namespace Server
public AccessLevel AccessLevel { get; }
public int CompareTo(CommandEntry e) => e == null ? 1 : Command.CompareTo(e.Command);
public int CompareTo(CommandEntry e) => string.CompareOrdinal(Command, e?.Command);
}
public static class CommandSystem

View file

@ -244,7 +244,7 @@ namespace Server
break;
}
if (ipStr.IndexOf(":", StringComparison.Ordinal) == -1)
if (ipStr.IndexOf(':') == -1)
{
ipStr += ":2593";
}

View file

@ -104,23 +104,13 @@ namespace Server
public int CompareTo(Point2D other)
{
var xComparison = m_X.CompareTo(other.m_X);
if (xComparison != 0)
{
return xComparison;
}
return m_Y.CompareTo(other.m_Y);
return xComparison != 0 ? xComparison : m_Y.CompareTo(other.m_Y);
}
public int CompareTo(IPoint2D other)
{
var xComparison = m_X.CompareTo(other.X);
if (xComparison != 0)
{
return xComparison;
}
return m_Y.CompareTo(other.Y);
return xComparison != 0 ? xComparison : m_Y.CompareTo(other.Y);
}
}
}

View file

@ -1,3 +1,4 @@
using System;
using System.Collections.Generic;
using System.Linq;
@ -63,7 +64,7 @@ namespace Server.Guilds
{
var name = g.Name.ToLower();
if (words.All(t => name.IndexOf(t) != -1))
if (words.All(t => name.IndexOf(t, StringComparison.Ordinal) != -1))
{
results.Add(g);
}

View file

@ -163,7 +163,7 @@ namespace Server.Network
public bool IsDisposing => m_Disposing != 0;
public int CompareTo(NetState other) => other == null ? 1 : m_ToString.CompareTo(other.m_ToString);
public int CompareTo(NetState other) => string.CompareOrdinal(m_ToString, other?.m_ToString);
public void ValidateAllTrades()
{

View file

@ -26,7 +26,7 @@
<Delete Files="..\..\Distribution\$(AssemblyName).runtimeconfig.json" ContinueOnError="true" />
</Target>
<ItemGroup>
<PackageReference Include="Nerdbank.GitVersioning" Version="3.2.31">
<PackageReference Include="Nerdbank.GitVersioning" Version="3.3.37">
<PrivateAssets>all</PrivateAssets>
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
</PackageReference>

View file

@ -3,7 +3,7 @@
<IsPackable>false</IsPackable>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="16.7.0" />
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="16.7.1" />
<PackageReference Include="xunit" Version="2.4.1" />
<PackageReference Include="xunit.runner.visualstudio" Version="2.4.3" />
<PackageReference Include="coverlet.collector" Version="1.3.0" />

View file

@ -403,7 +403,7 @@ namespace Server.Accounting
}
}
public int CompareTo(IAccount other) => other == null ? 1 : Username.CompareTo(other.Username);
public int CompareTo(IAccount other) => string.CompareOrdinal(Username, other?.Username);
/// <summary>
/// This amount represents the current amount of Gold owned by the player.
@ -512,7 +512,7 @@ namespace Server.Accounting
/// <returns>Total gold, capped at Int32.MaxValue</returns>
public long GetTotalGold() => TotalGold + TotalPlat * AccountGold.CurrencyThreshold;
public int CompareTo(Account other) => other == null ? 1 : Username.CompareTo(other.Username);
public int CompareTo(Account other) => string.CompareOrdinal(Username, other?.Username);
/// <summary>
/// Gets the value of a specific flag in the Flags bitfield.

View file

@ -2835,7 +2835,10 @@ namespace Server.Commands
if (v == 0)
{
v = GetNameFrom(aCtor, aProp, aMethod).CompareTo(GetNameFrom(bCtor, bProp, bMethod));
v = string.CompareOrdinal(
GetNameFrom(aCtor, aProp, aMethod),
GetNameFrom(bCtor, bProp, bMethod)
);
}
if (v == 0 && aCtor != null && bCtor != null)
@ -2879,11 +2882,7 @@ namespace Server.Commands
private class TypeComparer : IComparer<TypeInfo>
{
public int Compare(TypeInfo x, TypeInfo y) =>
x == null && y == null ? 0 :
x == null ? -1 :
y == null ? 1 :
x.TypeName.CompareTo(y.TypeName);
public int Compare(TypeInfo x, TypeInfo y) => string.CompareOrdinal(x?.TypeName, y?.TypeName);
}
private class TypeInfo
@ -2973,12 +2972,7 @@ namespace Server.Commands
var v = b?.AccessLevel.CompareTo(a?.AccessLevel) ?? 1;
if (v != 0)
{
return v;
}
return a?.Name.CompareTo(b?.Name) ?? 1;
return v != 0 ? v : string.CompareOrdinal(a?.Name, b?.Name);
}
}
}
@ -3034,12 +3028,7 @@ namespace Server.Commands
v = a?.Body.BodyID.CompareTo(b?.Body.BodyID) ?? 1;
}
if (v != 0)
{
return v;
}
return a?.Name.CompareTo(b?.Name) ?? 1;
return v != 0 ? v : string.CompareOrdinal(a?.Name, b?.Name);
}
}
}

View file

@ -135,7 +135,7 @@ namespace Server.Gumps
var t = types[i];
if ((typeofMobile.IsAssignableFrom(t) || typeofItem.IsAssignableFrom(t)) &&
t.Name.ToLower().IndexOf(match) >= 0 && !results.Contains(t))
t.Name.ToLower().IndexOf(match, StringComparison.Ordinal) >= 0 && !results.Contains(t))
{
var ctors = t.GetConstructors();
@ -236,7 +236,7 @@ namespace Server.Gumps
private class TypeNameComparer : IComparer<Type>
{
public int Compare(Type x, Type y) => x?.Name.CompareTo(y?.Name) ?? 1;
public int Compare(Type x, Type y) => string.CompareOrdinal(x?.Name, y?.Name);
}
public class InternalTarget : Target

View file

@ -265,34 +265,14 @@ namespace Server.Commands
public class CategorySorter : IComparer<CategoryEntry>
{
public int Compare(CategoryEntry x, CategoryEntry y)
{
var a = x?.Title;
var b = y?.Title;
return a switch
{
null when b == null => 0,
null => 1,
_ => a.CompareTo(b)
};
}
public int Compare(CategoryEntry x, CategoryEntry y) =>
string.CompareOrdinal(x?.Title, y?.Title);
}
public class CategoryTypeSorter : IComparer<CategoryTypeEntry>
{
public int Compare(CategoryTypeEntry x, CategoryTypeEntry y)
{
var a = x?.Type.Name;
var b = y?.Type.Name;
return a switch
{
null when b == null => 0,
null => 1,
_ => a.CompareTo(b)
};
}
public int Compare(CategoryTypeEntry x, CategoryTypeEntry y) =>
string.CompareOrdinal(x?.Type.Name, y?.Type.Name);
}
public class CategoryTypeEntry

View file

@ -390,7 +390,7 @@ namespace Server.Commands
var v = -aCount.CompareTo(bCount);
return v != 0 ? v : x.Key.FullName?.CompareTo(y.Key.FullName) ?? -1;
return v != 0 ? v : string.CompareOrdinal(x.Key.FullName, y.Key.FullName);
}
}
@ -403,7 +403,7 @@ namespace Server.Commands
var v = -aCount.CompareTo(bCount);
return v != 0 ? v : x.Key.FullName?.CompareTo(y.Key.FullName) ?? 1;
return v != 0 ? v : string.CompareOrdinal(x.Key.FullName, y.Key.FullName);
}
}
}

View file

@ -522,28 +522,7 @@ namespace Server.Engines.ConPVP
public static List<Arena> Arenas { get; } = new List<Arena>();
public int CompareTo(Arena c)
{
var a = m_Name;
var b = c.m_Name;
if (a == null && b == null)
{
return 0;
}
if (a == null)
{
return -1;
}
if (b == null)
{
return +1;
}
return a.CompareTo(b);
}
public int CompareTo(Arena c) => string.CompareOrdinal(m_Name, c?.m_Name);
public Ladder AcquireLadder() => Ladder?.Ladder ?? ConPVP.Ladder.Instance;

View file

@ -2463,12 +2463,12 @@ namespace Server.Gumps
var m = ns.Mobile;
var a = ns.Account;
isMatch = m?.Name.ToLower().IndexOf(match) >= 0
|| a?.Username.ToLower().IndexOf(match) >= 0;
isMatch = m?.Name.ToLower().IndexOf(match, StringComparison.Ordinal) >= 0
|| a?.Username.ToLower().IndexOf(match, StringComparison.Ordinal) >= 0;
}
else
{
isMatch = ns.ToString().IndexOf(match) >= 0;
isMatch = ns.ToString().IndexOf(match, StringComparison.Ordinal) >= 0;
}
if (isMatch)
@ -2725,7 +2725,8 @@ namespace Server.Gumps
else
{
results = Accounts.GetAccounts()
.Where(acct => acct.Username.ToLower().IndexOf(match) >= 0)
.Where(acct =>
acct.Username.ToLower().IndexOf(match, StringComparison.Ordinal) >= 0)
.ToList();
results.Sort(AccountComparer.Instance);
}

View file

@ -1,3 +1,4 @@
using System;
using Server.Gumps;
using Server.Misc;
using Server.Mobiles;
@ -119,7 +120,7 @@ namespace Server.Guilds
for (var i = 0; i < disallowed.Length; i++)
{
if (s.IndexOf(disallowed[i]) != -1)
if (s.IndexOf(disallowed[i], StringComparison.Ordinal) != -1)
{
return false;
}

View file

@ -868,7 +868,7 @@ namespace Server.Gumps
return -1;
}
return y == null ? 1 : x.Name.CompareTo(x.Name);
return y == null ? 1 : string.CompareOrdinal(x.Name, x.Name);
}
}

View file

@ -330,12 +330,7 @@ namespace Server.Gumps
public int CompareTo(InternalEntry comp)
{
if (Name == null && comp.Name == null)
{
return 0;
}
var v = Name?.CompareTo(comp.Name) ?? 1;
var v = string.CompareOrdinal(Name, comp.Name);
return v == 0 ? Body.CompareTo(comp.Body) : v;
}

View file

@ -634,7 +634,7 @@ namespace Server.Gumps
var aName = SkillInfo.Table[(int)a].Name;
var bName = SkillInfo.Table[(int)b].Name;
return aName.CompareTo(bName);
return string.CompareOrdinal(aName, bName);
}
}
}

View file

@ -105,7 +105,7 @@ namespace Server.Gumps
if (m != null && (m == owner || !m.Hidden || owner.AccessLevel >= m.AccessLevel ||
m is PlayerMobile mobile && mobile.VisibilityList.Contains(owner)))
{
if (filter != null && !(m.Name?.ToLower().IndexOf(filter) >= 0))
if (filter != null && !(m.Name?.ToLower().IndexOf(filter, StringComparison.Ordinal) >= 0))
{
continue;
}

View file

@ -598,7 +598,7 @@ namespace Server.Items
{
isMatch = true;
}
else if (m_Substring != null && e.Speech.ToLower().IndexOf(m_Substring.ToLower()) >= 0)
else if (m_Substring != null && e.Speech.ToLower().IndexOf(m_Substring.ToLower(), StringComparison.Ordinal) >= 0)
{
isMatch = true;
}

View file

@ -1,10 +1,11 @@
using System;
using System.Collections.Generic;
using System.IO;
using Server.Accounting;
namespace Server.Misc
{
/**
/**
* This file requires to be saved in a Unicode
* compatible format.
*
@ -19,368 +20,348 @@ namespace Server.Misc
* If you do not see the above chars, please
* enable showing of unicode control chars
**/
public class LanguageStatistics
{
private static readonly InternationalCode[] InternationalCodes =
public static class LanguageStatistics
{
new InternationalCode("ARA", "Arabic", "Saudi Arabia", "العربية", "السعودية"),
new InternationalCode("ARI", "Arabic", "Iraq", "العربية", "العراق"),
new InternationalCode("ARE", "Arabic", "Egypt", "العربية", "مصر"),
new InternationalCode("ARL", "Arabic", "Libya", "العربية", "ليبيا"),
new InternationalCode("ARG", "Arabic", "Algeria", "العربية", "الجزائر"),
new InternationalCode("ARM", "Arabic", "Morocco", "العربية", "المغرب"),
new InternationalCode("ART", "Arabic", "Tunisia", "العربية", "تونس"),
new InternationalCode("ARO", "Arabic", "Oman", "العربية", "عمان"),
new InternationalCode("ARY", "Arabic", "Yemen", "العربية", "اليمن"),
new InternationalCode("ARS", "Arabic", "Syria", "العربية", "سورية"),
new InternationalCode("ARJ", "Arabic", "Jordan", "العربية", "الأردن"),
new InternationalCode("ARB", "Arabic", "Lebanon", "العربية", "لبنان"),
new InternationalCode("ARK", "Arabic", "Kuwait", "العربية", "الكويت"),
new InternationalCode("ARU", "Arabic", "U.A.E.", "العربية", "الامارات"),
new InternationalCode("ARH", "Arabic", "Bahrain", "العربية", "البحرين"),
new InternationalCode("ARQ", "Arabic", "Qatar", "العربية", "قطر"),
new InternationalCode("BGR", "Bulgarian", "Bulgaria", "Български", "България"),
new InternationalCode("CAT", "Catalan", "Spain", "Català", "Espanya"),
new InternationalCode("CHT", "Chinese", "Taiwan", "台語", "臺灣"),
new InternationalCode("CHS", "Chinese", "PRC", "中文", "中国"),
new InternationalCode("ZHH", "Chinese", "Hong Kong", "中文", "香港"),
new InternationalCode("ZHI", "Chinese", "Singapore", "中文", "新加坡"),
new InternationalCode("ZHM", "Chinese", "Macau", "中文", "澳門"),
new InternationalCode("CSY", "Czech", "Czech Republic", "Čeština", "Česká republika"),
new InternationalCode("DAN", "Danish", "Denmark", "Dansk", "Danmark"),
new InternationalCode("DEU", "German", "Germany", "Deutsch", "Deutschland"),
new InternationalCode("DES", "German", "Switzerland", "Deutsch", "der Schweiz"),
new InternationalCode("DEA", "German", "Austria", "Deutsch", "Österreich"),
new InternationalCode("DEL", "German", "Luxembourg", "Deutsch", "Luxembourg"),
new InternationalCode("DEC", "German", "Liechtenstein", "Deutsch", "Liechtenstein"),
new InternationalCode("ELL", "Greek", "Greece", "Ελληνικά", "Ελλάδα"),
new InternationalCode("ENU", "English", "United States"),
new InternationalCode("ENG", "English", "United Kingdom"),
new InternationalCode("ENA", "English", "Australia"),
new InternationalCode("ENC", "English", "Canada"),
new InternationalCode("ENZ", "English", "New Zealand"),
new InternationalCode("ENI", "English", "Ireland"),
new InternationalCode("ENS", "English", "South Africa"),
new InternationalCode("ENJ", "English", "Jamaica"),
new InternationalCode("ENB", "English", "Caribbean"),
new InternationalCode("ENL", "English", "Belize"),
new InternationalCode("ENT", "English", "Trinidad"),
new InternationalCode("ENW", "English", "Zimbabwe"),
new InternationalCode("ENP", "English", "Philippines"),
new InternationalCode("ESP", "Spanish", "Spain (Traditional Sort)", "Español", "España (tipo tradicional)"),
new InternationalCode("ESM", "Spanish", "Mexico", "Español", "México"),
new InternationalCode("ESN", "Spanish", "Spain (International Sort)", "Español", "España (tipo internacional)"),
new InternationalCode("ESG", "Spanish", "Guatemala", "Español", "Guatemala"),
new InternationalCode("ESC", "Spanish", "Costa Rica", "Español", "Costa Rica"),
new InternationalCode("ESA", "Spanish", "Panama", "Español", "Panama"),
new InternationalCode("ESD", "Spanish", "Dominican Republic", "Español", "Republica Dominicana"),
new InternationalCode("ESV", "Spanish", "Venezuela", "Español", "Venezuela"),
new InternationalCode("ESO", "Spanish", "Colombia", "Español", "Colombia"),
new InternationalCode("ESR", "Spanish", "Peru", "Español", "Peru"),
new InternationalCode("ESS", "Spanish", "Argentina", "Español", "Argentina"),
new InternationalCode("ESF", "Spanish", "Ecuador", "Español", "Ecuador"),
new InternationalCode("ESL", "Spanish", "Chile", "Español", "Chile"),
new InternationalCode("ESY", "Spanish", "Uruguay", "Español", "Uruguay"),
new InternationalCode("ESZ", "Spanish", "Paraguay", "Español", "Paraguay"),
new InternationalCode("ESB", "Spanish", "Bolivia", "Español", "Bolivia"),
new InternationalCode("ESE", "Spanish", "El Salvador", "Español", "El Salvador"),
new InternationalCode("ESH", "Spanish", "Honduras", "Español", "Honduras"),
new InternationalCode("ESI", "Spanish", "Nicaragua", "Español", "Nicaragua"),
new InternationalCode("ESU", "Spanish", "Puerto Rico", "Español", "Puerto Rico"),
new InternationalCode("FIN", "Finnish", "Finland", "Suomi", "Suomi"),
new InternationalCode("FRA", "French", "France", "Français", "France"),
new InternationalCode("FRB", "French", "Belgium", "Français", "Belgique"),
new InternationalCode("FRC", "French", "Canada", "Français", "Canada"),
new InternationalCode("FRS", "French", "Switzerland", "Français", "Suisse"),
new InternationalCode("FRL", "French", "Luxembourg", "Français", "Luxembourg"),
new InternationalCode("FRM", "French", "Monaco", "Français", "Monaco"),
new InternationalCode("HEB", "Hebrew", "Israel", "עִבְרִית", "ישׂראל"),
new InternationalCode("HUN", "Hungarian", "Hungary", "Magyar", "Magyarország"),
new InternationalCode("ISL", "Icelandic", "Iceland", "Íslenska", "Ísland"),
new InternationalCode("ITA", "Italian", "Italy", "Italiano", "Italia"),
new InternationalCode("ITS", "Italian", "Switzerland", "Italiano", "Svizzera"),
new InternationalCode("JPN", "Japanese", "Japan", "日本語", "日本"),
new InternationalCode("KOR", "Korean (Extended Wansung)", "Korea", "한국어", "한국"),
new InternationalCode("NLD", "Dutch", "Netherlands", "Nederlands", "Nederland"),
new InternationalCode("NLB", "Dutch", "Belgium", "Nederlands", "België"),
new InternationalCode("NOR", "Norwegian", "Norway (Bokmål)", "Norsk", "Norge (Bokmål)"),
new InternationalCode("NON", "Norwegian", "Norway (Nynorsk)", "Norsk", "Norge (Nynorsk)"),
new InternationalCode("PLK", "Polish", "Poland", "Polski", "Polska"),
new InternationalCode("PTB", "Portuguese", "Brazil", "Português", "Brasil"),
new InternationalCode("PTG", "Portuguese", "Portugal", "Português", "Brasil"),
new InternationalCode("ROM", "Romanian", "Romania", "Limba Română", "România"),
new InternationalCode("RUS", "Russian", "Russia", "Русский", "Россия"),
new InternationalCode("HRV", "Croatian", "Croatia", "Hrvatski", "Hrvatska"),
new InternationalCode("SRL", "Serbian", "Serbia (Latin)", "Srpski", "Srbija i Crna Gora"),
new InternationalCode("SRB", "Serbian", "Serbia (Cyrillic)", "Српски", "Србија и Црна Гора"),
new InternationalCode("SKY", "Slovak", "Slovakia", "Slovenčina", "Slovensko"),
new InternationalCode("SQI", "Albanian", "Albania", "Shqip", "Shqipëria"),
new InternationalCode("SVE", "Swedish", "Sweden", "Svenska", "Sverige"),
new InternationalCode("SVF", "Swedish", "Finland", "Svenska", "Finland"),
new InternationalCode("THA", "Thai", "Thailand", "ภาษาไทย", "ประเทศไทย"),
new InternationalCode("TRK", "Turkish", "Turkey", "Türkçe", "Türkiye"),
new InternationalCode("URP", "Urdu", "Pakistan", "اردو", "پاکستان"),
new InternationalCode("IND", "Indonesian", "Indonesia", "Bahasa Indonesia", "Indonesia"),
new InternationalCode("UKR", "Ukrainian", "Ukraine", "Українська", "Украина"),
new InternationalCode("BEL", "Belarusian", "Belarus", "Беларускі", "Беларусь"),
new InternationalCode("SLV", "Slovene", "Slovenia", "Slovenščina", "Slovenija"),
new InternationalCode("ETI", "Estonian", "Estonia", "Eesti", "Eesti"),
new InternationalCode("LVI", "Latvian", "Latvia", "Latviešu", "Latvija"),
new InternationalCode("LTH", "Lithuanian", "Lithuania", "Lietuvių", "Lietuva"),
new InternationalCode("LTC", "Classic Lithuanian", "Lithuania", "Lietuviškai", "Lietuva"),
new InternationalCode("FAR", "Farsi", "Iran", "فارسى", "ايران"),
new InternationalCode("VIT", "Vietnamese", "Viet Nam", "tiếng Việt", "Việt Nam"),
new InternationalCode("HYE", "Armenian", "Armenia", "Հայերէն", "Հայաստան"),
new InternationalCode("AZE", "Azeri", "Azerbaijan (Latin)", "Azərbaycanca", "Azərbaycan"),
new InternationalCode("AZE", "Azeri", "Azerbaijan (Cyrillic)", "Азәрбајҹанҹа", "Азәрбајҹан"),
new InternationalCode("EUQ", "Basque", "Spain", "Euskera", "Espainia"),
new InternationalCode("MKI", "Macedonian", "Macedonia", "Македонски", "Македонија"),
new InternationalCode("AFK", "Afrikaans", "South Africa", "Afrikaans", "Republiek van Suid-Afrika"),
new InternationalCode("KAT", "Georgian", "Georgia", "ქართული", "საკარტველო"),
new InternationalCode("FOS", "Faeroese", "Faeroe Islands", "Føroyska", "Føroya"),
new InternationalCode("HIN", "Hindi", "India", "हिन्दी", "भारत"),
new InternationalCode("MSL", "Malay", "Malaysia", "Bahasa melayu", "Malaysia"),
new InternationalCode("MSB", "Malay", "Brunei Darussalam", "Bahasa melayu", "Negara Brunei Darussalam"),
new InternationalCode("KAZ", "Kazak", "Kazakstan", "Қазақ", "Қазақстан"),
new InternationalCode("SWK", "Swahili", "Kenya", "Kiswahili", "Kenya"),
new InternationalCode("UZB", "Uzbek", "Uzbekistan (Latin)", "O'zbek", "O'zbekiston"),
new InternationalCode("UZB", "Uzbek", "Uzbekistan (Cyrillic)", "Ўзбек", "Ўзбекистон"),
new InternationalCode("TAT", "Tatar", "Tatarstan", "Татарча", "Татарстан"),
new InternationalCode("BEN", "Bengali", "India", "বাংলা", "ভারত"),
new InternationalCode("PAN", "Punjabi", "India", "ਪੰਜਾਬੀ", "ਭਾਰਤ"),
new InternationalCode("GUJ", "Gujarati", "India", "ગુજરાતી", "ભારત"),
new InternationalCode("ORI", "Oriya", "India", "ଓଡ଼ିଆ", "ଭାରତ"),
new InternationalCode("TAM", "Tamil", "India", "தமிழ்", "இந்தியா"),
new InternationalCode("TEL", "Telugu", "India", "తెలుగు", "భారత"),
new InternationalCode("KAN", "Kannada", "India", "ಕನ್ನಡ", "ಭಾರತ"),
new InternationalCode("MAL", "Malayalam", "India", "മലയാളം", "ഭാരത"),
new InternationalCode("ASM", "Assamese", "India", "অসমিয়া", "Bhārat"), // missing correct country name
new InternationalCode("MAR", "Marathi", "India", "मराठी", "भारत"),
new InternationalCode("SAN", "Sanskrit", "India", "संस्कृत", "भारतम्"),
new InternationalCode("KOK", "Konkani", "India", "कोंकणी", "भारत")
};
private static readonly InternationalCode[] InternationalCodes =
{
new InternationalCode("ARA", "Arabic", "Saudi Arabia", "العربية", "السعودية"),
new InternationalCode("ARI", "Arabic", "Iraq", "العربية", "العراق"),
new InternationalCode("ARE", "Arabic", "Egypt", "العربية", "مصر"),
new InternationalCode("ARL", "Arabic", "Libya", "العربية", "ليبيا"),
new InternationalCode("ARG", "Arabic", "Algeria", "العربية", "الجزائر"),
new InternationalCode("ARM", "Arabic", "Morocco", "العربية", "المغرب"),
new InternationalCode("ART", "Arabic", "Tunisia", "العربية", "تونس"),
new InternationalCode("ARO", "Arabic", "Oman", "العربية", "عمان"),
new InternationalCode("ARY", "Arabic", "Yemen", "العربية", "اليمن"),
new InternationalCode("ARS", "Arabic", "Syria", "العربية", "سورية"),
new InternationalCode("ARJ", "Arabic", "Jordan", "العربية", "الأردن"),
new InternationalCode("ARB", "Arabic", "Lebanon", "العربية", "لبنان"),
new InternationalCode("ARK", "Arabic", "Kuwait", "العربية", "الكويت"),
new InternationalCode("ARU", "Arabic", "U.A.E.", "العربية", "الامارات"),
new InternationalCode("ARH", "Arabic", "Bahrain", "العربية", "البحرين"),
new InternationalCode("ARQ", "Arabic", "Qatar", "العربية", "قطر"),
new InternationalCode("BGR", "Bulgarian", "Bulgaria", "Български", "България"),
new InternationalCode("CAT", "Catalan", "Spain", "Català", "Espanya"),
new InternationalCode("CHT", "Chinese", "Taiwan", "台語", "臺灣"),
new InternationalCode("CHS", "Chinese", "PRC", "中文", "中国"),
new InternationalCode("ZHH", "Chinese", "Hong Kong", "中文", "香港"),
new InternationalCode("ZHI", "Chinese", "Singapore", "中文", "新加坡"),
new InternationalCode("ZHM", "Chinese", "Macau", "中文", "澳門"),
new InternationalCode("CSY", "Czech", "Czech Republic", "Čeština", "Česká republika"),
new InternationalCode("DAN", "Danish", "Denmark", "Dansk", "Danmark"),
new InternationalCode("DEU", "German", "Germany", "Deutsch", "Deutschland"),
new InternationalCode("DES", "German", "Switzerland", "Deutsch", "der Schweiz"),
new InternationalCode("DEA", "German", "Austria", "Deutsch", "Österreich"),
new InternationalCode("DEL", "German", "Luxembourg", "Deutsch", "Luxembourg"),
new InternationalCode("DEC", "German", "Liechtenstein", "Deutsch", "Liechtenstein"),
new InternationalCode("ELL", "Greek", "Greece", "Ελληνικά", "Ελλάδα"),
new InternationalCode("ENU", "English", "United States"),
new InternationalCode("ENG", "English", "United Kingdom"),
new InternationalCode("ENA", "English", "Australia"),
new InternationalCode("ENC", "English", "Canada"),
new InternationalCode("ENZ", "English", "New Zealand"),
new InternationalCode("ENI", "English", "Ireland"),
new InternationalCode("ENS", "English", "South Africa"),
new InternationalCode("ENJ", "English", "Jamaica"),
new InternationalCode("ENB", "English", "Caribbean"),
new InternationalCode("ENL", "English", "Belize"),
new InternationalCode("ENT", "English", "Trinidad"),
new InternationalCode("ENW", "English", "Zimbabwe"),
new InternationalCode("ENP", "English", "Philippines"),
new InternationalCode("ESP", "Spanish", "Spain (Traditional Sort)", "Español", "España (tipo tradicional)"),
new InternationalCode("ESM", "Spanish", "Mexico", "Español", "México"),
new InternationalCode("ESN", "Spanish", "Spain (International Sort)", "Español", "España (tipo internacional)"),
new InternationalCode("ESG", "Spanish", "Guatemala", "Español", "Guatemala"),
new InternationalCode("ESC", "Spanish", "Costa Rica", "Español", "Costa Rica"),
new InternationalCode("ESA", "Spanish", "Panama", "Español", "Panama"),
new InternationalCode("ESD", "Spanish", "Dominican Republic", "Español", "Republica Dominicana"),
new InternationalCode("ESV", "Spanish", "Venezuela", "Español", "Venezuela"),
new InternationalCode("ESO", "Spanish", "Colombia", "Español", "Colombia"),
new InternationalCode("ESR", "Spanish", "Peru", "Español", "Peru"),
new InternationalCode("ESS", "Spanish", "Argentina", "Español", "Argentina"),
new InternationalCode("ESF", "Spanish", "Ecuador", "Español", "Ecuador"),
new InternationalCode("ESL", "Spanish", "Chile", "Español", "Chile"),
new InternationalCode("ESY", "Spanish", "Uruguay", "Español", "Uruguay"),
new InternationalCode("ESZ", "Spanish", "Paraguay", "Español", "Paraguay"),
new InternationalCode("ESB", "Spanish", "Bolivia", "Español", "Bolivia"),
new InternationalCode("ESE", "Spanish", "El Salvador", "Español", "El Salvador"),
new InternationalCode("ESH", "Spanish", "Honduras", "Español", "Honduras"),
new InternationalCode("ESI", "Spanish", "Nicaragua", "Español", "Nicaragua"),
new InternationalCode("ESU", "Spanish", "Puerto Rico", "Español", "Puerto Rico"),
new InternationalCode("FIN", "Finnish", "Finland", "Suomi", "Suomi"),
new InternationalCode("FRA", "French", "France", "Français", "France"),
new InternationalCode("FRB", "French", "Belgium", "Français", "Belgique"),
new InternationalCode("FRC", "French", "Canada", "Français", "Canada"),
new InternationalCode("FRS", "French", "Switzerland", "Français", "Suisse"),
new InternationalCode("FRL", "French", "Luxembourg", "Français", "Luxembourg"),
new InternationalCode("FRM", "French", "Monaco", "Français", "Monaco"),
new InternationalCode("HEB", "Hebrew", "Israel", "עִבְרִית", "ישׂראל"),
new InternationalCode("HUN", "Hungarian", "Hungary", "Magyar", "Magyarország"),
new InternationalCode("ISL", "Icelandic", "Iceland", "Íslenska", "Ísland"),
new InternationalCode("ITA", "Italian", "Italy", "Italiano", "Italia"),
new InternationalCode("ITS", "Italian", "Switzerland", "Italiano", "Svizzera"),
new InternationalCode("JPN", "Japanese", "Japan", "日本語", "日本"),
new InternationalCode("KOR", "Korean (Extended Wansung)", "Korea", "한국어", "한국"),
new InternationalCode("NLD", "Dutch", "Netherlands", "Nederlands", "Nederland"),
new InternationalCode("NLB", "Dutch", "Belgium", "Nederlands", "België"),
new InternationalCode("NOR", "Norwegian", "Norway (Bokmål)", "Norsk", "Norge (Bokmål)"),
new InternationalCode("NON", "Norwegian", "Norway (Nynorsk)", "Norsk", "Norge (Nynorsk)"),
new InternationalCode("PLK", "Polish", "Poland", "Polski", "Polska"),
new InternationalCode("PTB", "Portuguese", "Brazil", "Português", "Brasil"),
new InternationalCode("PTG", "Portuguese", "Portugal", "Português", "Brasil"),
new InternationalCode("ROM", "Romanian", "Romania", "Limba Română", "România"),
new InternationalCode("RUS", "Russian", "Russia", "Русский", "Россия"),
new InternationalCode("HRV", "Croatian", "Croatia", "Hrvatski", "Hrvatska"),
new InternationalCode("SRL", "Serbian", "Serbia (Latin)", "Srpski", "Srbija i Crna Gora"),
new InternationalCode("SRB", "Serbian", "Serbia (Cyrillic)", "Српски", "Србија и Црна Гора"),
new InternationalCode("SKY", "Slovak", "Slovakia", "Slovenčina", "Slovensko"),
new InternationalCode("SQI", "Albanian", "Albania", "Shqip", "Shqipëria"),
new InternationalCode("SVE", "Swedish", "Sweden", "Svenska", "Sverige"),
new InternationalCode("SVF", "Swedish", "Finland", "Svenska", "Finland"),
new InternationalCode("THA", "Thai", "Thailand", "ภาษาไทย", "ประเทศไทย"),
new InternationalCode("TRK", "Turkish", "Turkey", "Türkçe", "Türkiye"),
new InternationalCode("URP", "Urdu", "Pakistan", "اردو", "پاکستان"),
new InternationalCode("IND", "Indonesian", "Indonesia", "Bahasa Indonesia", "Indonesia"),
new InternationalCode("UKR", "Ukrainian", "Ukraine", "Українська", "Украина"),
new InternationalCode("BEL", "Belarusian", "Belarus", "Беларускі", "Беларусь"),
new InternationalCode("SLV", "Slovene", "Slovenia", "Slovenščina", "Slovenija"),
new InternationalCode("ETI", "Estonian", "Estonia", "Eesti", "Eesti"),
new InternationalCode("LVI", "Latvian", "Latvia", "Latviešu", "Latvija"),
new InternationalCode("LTH", "Lithuanian", "Lithuania", "Lietuvių", "Lietuva"),
new InternationalCode("LTC", "Classic Lithuanian", "Lithuania", "Lietuviškai", "Lietuva"),
new InternationalCode("FAR", "Farsi", "Iran", "فارسى", "ايران"),
new InternationalCode("VIT", "Vietnamese", "Viet Nam", "tiếng Việt", "Việt Nam"),
new InternationalCode("HYE", "Armenian", "Armenia", "Հայերէն", "Հայաստան"),
new InternationalCode("AZE", "Azeri", "Azerbaijan (Latin)", "Azərbaycanca", "Azərbaycan"),
new InternationalCode("AZE", "Azeri", "Azerbaijan (Cyrillic)", "Азәрбајҹанҹа", "Азәрбајҹан"),
new InternationalCode("EUQ", "Basque", "Spain", "Euskera", "Espainia"),
new InternationalCode("MKI", "Macedonian", "Macedonia", "Македонски", "Македонија"),
new InternationalCode("AFK", "Afrikaans", "South Africa", "Afrikaans", "Republiek van Suid-Afrika"),
new InternationalCode("KAT", "Georgian", "Georgia", "ქართული", "საკარტველო"),
new InternationalCode("FOS", "Faeroese", "Faeroe Islands", "Føroyska", "Føroya"),
new InternationalCode("HIN", "Hindi", "India", "हिन्दी", "भारत"),
new InternationalCode("MSL", "Malay", "Malaysia", "Bahasa melayu", "Malaysia"),
new InternationalCode("MSB", "Malay", "Brunei Darussalam", "Bahasa melayu", "Negara Brunei Darussalam"),
new InternationalCode("KAZ", "Kazak", "Kazakstan", "Қазақ", "Қазақстан"),
new InternationalCode("SWK", "Swahili", "Kenya", "Kiswahili", "Kenya"),
new InternationalCode("UZB", "Uzbek", "Uzbekistan (Latin)", "O'zbek", "O'zbekiston"),
new InternationalCode("UZB", "Uzbek", "Uzbekistan (Cyrillic)", "Ўзбек", "Ўзбекистон"),
new InternationalCode("TAT", "Tatar", "Tatarstan", "Татарча", "Татарстан"),
new InternationalCode("BEN", "Bengali", "India", "বাংলা", "ভারত"),
new InternationalCode("PAN", "Punjabi", "India", "ਪੰਜਾਬੀ", "ਭਾਰਤ"),
new InternationalCode("GUJ", "Gujarati", "India", "ગુજરાતી", "ભારત"),
new InternationalCode("ORI", "Oriya", "India", "ଓଡ଼ିଆ", "ଭାରତ"),
new InternationalCode("TAM", "Tamil", "India", "தமிழ்", "இந்தியா"),
new InternationalCode("TEL", "Telugu", "India", "తెలుగు", "భారత"),
new InternationalCode("KAN", "Kannada", "India", "ಕನ್ನಡ", "ಭಾರತ"),
new InternationalCode("MAL", "Malayalam", "India", "മലയാളം", "ഭാരത"),
new InternationalCode("ASM", "Assamese", "India", "অসমিয়া", "Bhārat"), // missing correct country name
new InternationalCode("MAR", "Marathi", "India", "मराठी", "भारत"),
new InternationalCode("SAN", "Sanskrit", "India", "संस्कृत", "भारतम्"),
new InternationalCode("KOK", "Konkani", "India", "कोंकणी", "भारत")
};
private static readonly bool DefaultLocalNames = false;
private static readonly bool ShowAlternatives = true;
private static readonly bool CountAccounts = true; // will consider only first character's valid language
private static readonly bool DefaultLocalNames = false;
private static readonly bool ShowAlternatives = true;
private static readonly bool CountAccounts = true; // will consider only first character's valid language
private static string GetFormattedInfo(string code)
{
if (code == null || code.Length != 3)
{
return $"Unknown code {code}";
}
private static string GetFormattedInfo(string code)
{
if (code == null || code.Length != 3)
{
return $"Unknown code {code}";
}
for (var i = 0; i < InternationalCodes.Length; i++)
{
if (code == InternationalCodes[i].Code)
{
return $"{InternationalCodes[i].GetName()}";
}
}
for (var i = 0; i < InternationalCodes.Length; i++)
{
if (code == InternationalCodes[i].Code)
{
return $"{InternationalCodes[i].GetName()}";
}
}
return $"Unknown code {code}";
return $"Unknown code {code}";
}
public static void Initialize()
{
CommandSystem.Register("LanguageStatistics", AccessLevel.Administrator, LanguageStatistics_OnCommand);
}
[Usage("LanguageStatistics")]
[Description("Generate a file containing the list of languages for each PlayerMobile.")]
public static void LanguageStatistics_OnCommand(CommandEventArgs e)
{
var ht = new Dictionary<string, InternationalCodeCounter>();
using var writer = new StreamWriter("languages.txt");
if (CountAccounts)
{
foreach (Account acc in Accounts.GetAccounts())
{
for (var i = 0; i < acc.Length; i++)
{
var mob = acc[i];
var lang = mob?.Language;
if (lang == null)
{
continue;
}
lang = lang.ToUpper();
if (ht.TryGetValue(lang, out var codes))
{
codes.Increase();
}
else
{
ht[lang] = new InternationalCodeCounter(lang);
}
break;
}
}
}
else
{
foreach (var mob in World.Mobiles.Values)
{
if (mob.Player)
{
var lang = mob.Language;
if (lang == null)
{
continue;
}
lang = lang.ToUpper();
if (ht.TryGetValue(lang, out var codes))
{
codes.Increase();
}
else
{
ht[lang] = new InternationalCodeCounter(lang);
}
}
}
}
writer.WriteLine(
$"Language statistics. Numbers show how many {(CountAccounts ? "accounts" : "playermobile")} use the specified language.");
writer.WriteLine(
"====================================================================================================");
writer.WriteLine();
// sort the list
var list = new List<InternationalCodeCounter>(ht.Values);
list.Sort(InternationalCodeComparer.Instance);
foreach (var c in list)
{
writer.WriteLine($"{GetFormattedInfo(c.Code)} : {c.Count}");
}
e.Mobile.SendMessage("Languages list generated.");
}
private struct InternationalCode
{
private readonly bool m_HasLocalInfo;
public string Code { get; }
public string Language { get; }
public string Country { get; }
public string Language_LocalName { get; }
public string Country_LocalName { get; }
public InternationalCode(string code, string language, string country) : this(code, language, country, null,
null) =>
m_HasLocalInfo = false;
public InternationalCode(string code, string language, string country, string language_localname,
string country_localname)
{
Code = code;
Language = language;
Country = country;
Language_LocalName = language_localname;
Country_LocalName = country_localname;
m_HasLocalInfo = true;
}
public string GetName()
{
string s;
if (m_HasLocalInfo)
{
s =
$"{(DefaultLocalNames ? Language_LocalName : Language)} - {(DefaultLocalNames ? Country_LocalName : Country)}";
if (ShowAlternatives)
{
s +=
$" 【{(DefaultLocalNames ? Language : Language_LocalName)} - {(DefaultLocalNames ? Country : Country_LocalName)}‎】";
}
}
else
{
s = $"{Language} - {Country}";
}
return s;
}
}
private class InternationalCodeCounter
{
public InternationalCodeCounter(string code)
{
Code = code;
Count = 1;
}
public string Code { get; }
public int Count { get; private set; }
public void Increase()
{
Count++;
}
}
private class InternationalCodeComparer : IComparer<InternationalCodeCounter>
{
public static readonly InternationalCodeComparer Instance = new InternationalCodeComparer();
public int Compare(InternationalCodeCounter x, InternationalCodeCounter y)
{
var ca = x?.Count ?? 0;
var cb = y?.Count ?? 0;
if (ca > cb)
{
return -1;
}
if (ca < cb)
{
return 1;
}
return string.CompareOrdinal(x?.Code, y?.Code);
}
}
}
public static void Initialize()
{
CommandSystem.Register("LanguageStatistics", AccessLevel.Administrator, LanguageStatistics_OnCommand);
}
[Usage("LanguageStatistics")]
[Description("Generate a file containing the list of languages for each PlayerMobile.")]
public static void LanguageStatistics_OnCommand(CommandEventArgs e)
{
var ht = new Dictionary<string, InternationalCodeCounter>();
using var writer = new StreamWriter("languages.txt");
if (CountAccounts)
{
foreach (Account acc in Accounts.GetAccounts())
{
for (var i = 0; i < acc.Length; i++)
{
var mob = acc[i];
var lang = mob?.Language;
if (lang == null)
{
continue;
}
lang = lang.ToUpper();
if (ht.TryGetValue(lang, out var codes))
{
codes.Increase();
}
else
{
ht[lang] = new InternationalCodeCounter(lang);
}
break;
}
}
}
else
{
foreach (var mob in World.Mobiles.Values)
{
if (mob.Player)
{
var lang = mob.Language;
if (lang == null)
{
continue;
}
lang = lang.ToUpper();
if (ht.TryGetValue(lang, out var codes))
{
codes.Increase();
}
else
{
ht[lang] = new InternationalCodeCounter(lang);
}
}
}
}
writer.WriteLine(
$"Language statistics. Numbers show how many {(CountAccounts ? "accounts" : "playermobile")} use the specified language.");
writer.WriteLine(
"====================================================================================================");
writer.WriteLine();
// sort the list
var list = new List<InternationalCodeCounter>(ht.Values);
list.Sort(InternationalCodeComparer.Instance);
foreach (var c in list)
{
writer.WriteLine($"{GetFormattedInfo(c.Code)} : {c.Count}");
}
e.Mobile.SendMessage("Languages list generated.");
}
private struct InternationalCode
{
private readonly bool m_HasLocalInfo;
public string Code { get; }
public string Language { get; }
public string Country { get; }
public string Language_LocalName { get; }
public string Country_LocalName { get; }
public InternationalCode(string code, string language, string country) : this(code, language, country, null,
null) =>
m_HasLocalInfo = false;
public InternationalCode(string code, string language, string country, string language_localname,
string country_localname)
{
Code = code;
Language = language;
Country = country;
Language_LocalName = language_localname;
Country_LocalName = country_localname;
m_HasLocalInfo = true;
}
public string GetName()
{
string s;
if (m_HasLocalInfo)
{
s =
$"{(DefaultLocalNames ? Language_LocalName : Language)} - {(DefaultLocalNames ? Country_LocalName : Country)}";
if (ShowAlternatives)
{
s +=
$" 【{(DefaultLocalNames ? Language : Language_LocalName)} - {(DefaultLocalNames ? Country : Country_LocalName)}‎】";
}
}
else
{
s = $"{Language} - {Country}";
}
return s;
}
}
private class InternationalCodeCounter
{
public InternationalCodeCounter(string code)
{
Code = code;
Count = 1;
}
public string Code { get; }
public int Count { get; private set; }
public void Increase()
{
Count++;
}
}
private class InternationalCodeComparer : IComparer<InternationalCodeCounter>
{
public static readonly InternationalCodeComparer Instance = new InternationalCodeComparer();
public int Compare(InternationalCodeCounter x, InternationalCodeCounter y)
{
string a = null, b = null;
int ca = 0, cb = 0;
a = x.Code;
ca = x.Count;
b = y.Code;
cb = y.Count;
if (ca > cb)
{
return -1;
}
if (ca < cb)
{
return 1;
}
if (a == null && b == null)
{
return 0;
}
if (a == null)
{
return 1;
}
if (b == null)
{
return -1;
}
return a.CompareTo(b);
}
}
}
}

View file

@ -202,7 +202,7 @@ namespace Server.Misc
for (var i = 0; i < disallowed.Length; ++i)
{
var indexOf = name.IndexOf(disallowed[i]);
var indexOf = name.IndexOf(disallowed[i], StringComparison.Ordinal);
if (indexOf == -1)
{

View file

@ -1,3 +1,5 @@
using System;
namespace Server.Misc
{
public static class RenameRequests
@ -32,7 +34,7 @@ namespace Server.Misc
for (var i = 0; i < disallowed.Length; i++)
{
if (name.IndexOf(disallowed[i]) != -1)
if (name.IndexOf(disallowed[i], StringComparison.Ordinal) != -1)
{
from.SendLocalizedMessage(1072622); // That name isn't very polite.
return;

View file

@ -806,9 +806,8 @@ namespace Server.Items
/// Returns the first <see cref="CraftResource" /> in the series of resources for which '<paramref name="resource" />'
/// belongs.
/// </summary>
public static CraftResource GetStart(CraftResource resource)
{
return GetType(resource) switch
public static CraftResource GetStart(CraftResource resource) =>
GetType(resource) switch
{
CraftResourceType.Metal => CraftResource.Iron,
CraftResourceType.Leather => CraftResource.RegularLeather,
@ -816,7 +815,6 @@ namespace Server.Items
CraftResourceType.Wood => CraftResource.RegularWood,
_ => CraftResource.None
};
}
/// <summary>
/// Returns the index of '<paramref name="resource" />' in the seriest of resources for which it belongs.
@ -872,72 +870,39 @@ namespace Server.Items
/// </summary>
public static CraftResource GetFromOreInfo(OreInfo info)
{
if (info.Name.IndexOf("Spined") >= 0)
if (info.Name.IndexOf("Spined", StringComparison.Ordinal) >= 0)
{
return CraftResource.SpinedLeather;
}
if (info.Name.IndexOf("Horned") >= 0)
if (info.Name.IndexOf("Horned", StringComparison.Ordinal) >= 0)
{
return CraftResource.HornedLeather;
}
if (info.Name.IndexOf("Barbed") >= 0)
if (info.Name.IndexOf("Barbed", StringComparison.Ordinal) >= 0)
{
return CraftResource.BarbedLeather;
}
if (info.Name.IndexOf("Leather") >= 0)
if (info.Name.IndexOf("Leather", StringComparison.Ordinal) >= 0)
{
return CraftResource.RegularLeather;
}
if (info.Level == 0)
return info.Level switch
{
return CraftResource.Iron;
}
if (info.Level == 1)
{
return CraftResource.DullCopper;
}
if (info.Level == 2)
{
return CraftResource.ShadowIron;
}
if (info.Level == 3)
{
return CraftResource.Copper;
}
if (info.Level == 4)
{
return CraftResource.Bronze;
}
if (info.Level == 5)
{
return CraftResource.Gold;
}
if (info.Level == 6)
{
return CraftResource.Agapite;
}
if (info.Level == 7)
{
return CraftResource.Verite;
}
if (info.Level == 8)
{
return CraftResource.Valorite;
}
return CraftResource.None;
0 => CraftResource.Iron,
1 => CraftResource.DullCopper,
2 => CraftResource.ShadowIron,
3 => CraftResource.Copper,
4 => CraftResource.Bronze,
5 => CraftResource.Gold,
6 => CraftResource.Agapite,
7 => CraftResource.Verite,
8 => CraftResource.Valorite,
_ => CraftResource.None
};
}
/// <summary>
@ -946,34 +911,22 @@ namespace Server.Items
/// </summary>
public static CraftResource GetFromOreInfo(OreInfo info, ArmorMaterialType material)
{
if (material == ArmorMaterialType.Studded || material == ArmorMaterialType.Leather ||
material == ArmorMaterialType.Spined ||
material == ArmorMaterialType.Horned || material == ArmorMaterialType.Barbed)
if (material != ArmorMaterialType.Studded && material != ArmorMaterialType.Leather &&
material != ArmorMaterialType.Spined && material != ArmorMaterialType.Horned &&
material != ArmorMaterialType.Barbed)
{
if (info.Level == 0)
{
return CraftResource.RegularLeather;
}
if (info.Level == 1)
{
return CraftResource.SpinedLeather;
}
if (info.Level == 2)
{
return CraftResource.HornedLeather;
}
if (info.Level == 3)
{
return CraftResource.BarbedLeather;
}
return CraftResource.None;
return GetFromOreInfo(info);
}
return GetFromOreInfo(info);
return info.Level switch
{
0 => CraftResource.RegularLeather,
1 => CraftResource.SpinedLeather,
2 => CraftResource.HornedLeather,
3 => CraftResource.BarbedLeather,
_ => CraftResource.None
};
}
}

View file

@ -393,7 +393,7 @@ namespace Server.Misc
do
{
++count;
index = title.IndexOf("<br>", index + 1);
index = title.IndexOf("<br>", index + 1, StringComparison.Ordinal);
} while (index >= 0);
return count;

View file

@ -7,7 +7,6 @@ ModernUO [![Discord](https://img.shields.io/discord/751317910504603701?logo=disc
##### Ultima Online Server Emulator for the modern era!
[![.NET](https://img.shields.io/badge/.NET-%205.0-5C2D91)](https://dotnet.microsoft.com/download/dotnet/5.0)
[![.NET Core](https://img.shields.io/badge/.NET-Core%203.1.9-5C2D91)](https://dotnet.microsoft.com/download/dotnet-core/3.1)
<br />
![Windows](https://img.shields.io/badge/-server%202019-0078D6?logo=windows)
![OSX](https://img.shields.io/badge/-catalina-222222?logo=apple&logoColor=white)
@ -27,31 +26,24 @@ ModernUO [![Discord](https://img.shields.io/discord/751317910504603701?logo=disc
## Publishing a build
#### Requirements
- [.NET Core 3.1.9 SDK](https://dotnet.microsoft.com/download/dotnet-core/3.1)
<br />or
- [.NET 5.0 (Preview) SDK](https://dotnet.microsoft.com/download/dotnet/5.0)
- [.NET 5 SDK](https://dotnet.microsoft.com/download/dotnet/5.0)
#### Publishing Builds
- Using terminal or powershell: `./publish.cmd [os] [framework] [release|debug (default: release)]`
- Using terminal or powershell: `./publish.cmd [release|debug (default: release)] [os]`
- Supported `os`:
- `win` for Windows 8/10/2019
- `osx` for MacOS
- `ubuntu.16.04`, `ubuntu.18.04` `ubuntu.20.04` for Ubuntu LTS
- `debian.9`, `debian.10` for Debian
- `centos.7`, `centos.8` for CentOS
- If blank, will use host operating system
- Supported `framework`:
- `core` for .NET Core 3.1.9
- `net` for .NET 5.0
- If blank, the host operating system is used
## Deploying / Running Server
- Follow the [publish](https://github.com/modernuo/ModernUO#publishing-a-build) instructions
- Copy `Distribution` directory to the production server
#### Requirements
- [.NET Core 3.1.9 Runtime](https://dotnet.microsoft.com/download/dotnet-core/3.1)
<br />or
- [.NET 5.0 (Preview) Runtime](https://dotnet.microsoft.com/download/dotnet/5.0)
- [.NET 5 Runtime](https://dotnet.microsoft.com/download/dotnet/5.0)
#### Running
- `dotnet ModernUO.dll`

View file

@ -15,29 +15,35 @@ jobs:
steps:
- task: UseDotNet@2
displayName: 'Install .NET Core'
displayName: 'Install .NET 5'
inputs:
packageType: sdk
version: '3.1.401'
version: '5.0.100-rc.2.20479.15'
- task: NuGetAuthenticate@0
- script: dotnet restore --force-evaluate --source https://api.nuget.org/v3/index.json
displayName: 'Restore NuGet Packages'
- script: dotnet build -c $(buildConfiguration) -f netcoreapp3.1 --no-restore Projects/Server/Server.csproj
- script: dotnet build -c $(buildConfiguration) -f net5.0 --no-restore Projects/Server/Server.csproj
displayName: 'Build Server'
- script: dotnet build -c $(buildConfiguration) -f netcoreapp3.1 --no-restore Projects/UOContent/UOContent.csproj
- script: dotnet build -c $(buildConfiguration) -f net5.0 --no-restore Projects/UOContent/UOContent.csproj
displayName: 'Build UO Content'
- script: dotnet test --no-restore -f netcoreapp3.1
- script: dotnet test --no-restore -f net5.0
displayName: 'Test'
- job: BuildLinux
strategy:
matrix:
'Debian 9':
containerImage: amd64/buildpack-deps:stretch
'Debian 10':
containerImage: mcr.microsoft.com/dotnet/core/sdk:3.1-buster
containerImage: amd64/buildpack-deps:buster
# 'Debian 11':
# containerImage: amd64/buildpack-deps:bullseye
'Ubuntu 16':
containerImage: amd64/buildpack-deps:xenial
'Ubuntu 18':
containerImage: mcr.microsoft.com/dotnet/core/sdk:3.1-bionic
containerImage: amd64/buildpack-deps:bionic
'Ubuntu 20':
containerImage: mcr.microsoft.com/dotnet/core/sdk:3.1-focal
containerImage: amd64/buildpack-deps:focal
displayName: Linux
@ -48,16 +54,16 @@ jobs:
steps:
- task: UseDotNet@2
displayName: 'Install .NET Core'
displayName: 'Install .NET 5'
inputs:
packageType: sdk
version: '3.1.401'
version: '5.0.100-rc.2.20479.15'
- task: NuGetAuthenticate@0
- script: dotnet restore --force-evaluate --source https://api.nuget.org/v3/index.json
displayName: 'Restore NuGet Packages'
- script: dotnet build -c $(buildConfiguration) -f netcoreapp3.1 --no-restore Projects/Server/Server.csproj
- script: dotnet build -c $(buildConfiguration) -f net5.0 --no-restore Projects/Server/Server.csproj
displayName: 'Build Server'
- script: dotnet build -c $(buildConfiguration) -f netcoreapp3.1 --no-restore Projects/UOContent/UOContent.csproj
- script: dotnet build -c $(buildConfiguration) -f net5.0 --no-restore Projects/UOContent/UOContent.csproj
displayName: 'Build UO Content'
- script: dotnet test --no-restore -f netcoreapp3.1
- script: dotnet test --no-restore -f net5.0
displayName: 'Test'

View file

@ -9,13 +9,13 @@ The server software must be built before it can be run.
=== "Windows"
Using _command prompt_, _git bash_, or _powershell_, run:
```bash
./publish.cmd <os> <framework> <release|debug>
./publish.cmd <release|debug> <os>
```
=== "OSX/Linux"
Using _terminal_, run:
```bash
./publish.sh <os> <framework> <release|debug>
./publish.sh <release|debug> <os>
```
<br><br>
`os` (_Optional_)<br>
@ -25,11 +25,4 @@ The operating system to build the server against. If not specified then the serv
:fontawesome-brands-apple:{: .apple } `osx`<br>
:fontawesome-brands-ubuntu:{: .ubuntu } `ubuntu.16.04`, `ubuntu.18.04` `ubuntu.20.04`<br>
:brands-debian:{: .debian } `debian.9`, `debian.10`<br>
:fontawesome-brands-centos:{: .centos } `centos.7`, `centos.8`<br>
<br>
`framework` (_Optional_)<br>
The framework used to build the server. That framework must be installed.
.NET Core 3.1 `core`<br>
.NET 5 Preview `net`
:fontawesome-brands-centos:{: .centos } `centos.7`, `centos.8`

View file

@ -6,7 +6,7 @@ title: Installation
=== "Windows"
### Prerequisites
1. Download and install the latest [.NET Core SDK](https://dotnet.microsoft.com/download/dotnet-core/3.1)
1. Download and install the latest [.NET 5 SDK](https://dotnet.microsoft.com/download/dotnet/5.0)
1. Download and install from [here](https://git-scm.com/download/win)
!!! Tip
@ -22,7 +22,7 @@ title: Installation
=== "OSX"
<h3>Prerequisites</h3>
1. Download and install the latest [.NET Core SDK](https://dotnet.microsoft.com/download/dotnet-core/3.1)
1. Download and install the latest [.NET 5 SDK](https://dotnet.microsoft.com/download/dotnet/5.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)"

View file

@ -7,19 +7,19 @@ title: 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 Core Runtime](https://dotnet.microsoft.com/download/dotnet-core/thank-you/runtime-3.1.8-windows-x64-installer)
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 Core Runtime](https://dotnet.microsoft.com/download/dotnet-core/thank-you/runtime-aspnetcore-3.1.8-macos-x64-binaries)
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 Core Runtime](instructions [here](https://docs.microsoft.com/en-us/dotnet/core/install/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`

View file

@ -5,63 +5,47 @@ dotnet restore --force-evaluate
GOTO :CMDSCRIPT
::SHELLSCRIPT
os=$1
config=$1
os=$2
if [[ $os ]]; then
r="-r $os-x64"
os="-r $os-x64"
elif [[ $(uname) = "Darwin" ]]; then
r="-r osx-x64"
os="-r osx-x64"
elif [[ -f /etc/os-release ]]; then
. /etc/os-release
NAME="$(tr '[:upper:]' '[:lower:]' <<< $NAME)"
r="-r $NAME.$VERSION_ID-x64"
os="-r $NAME.$VERSION_ID-x64"
fi
framework="$(tr '[:upper:]' '[:lower:]' <<< $2)"
if [[ $framework = "net" ]]; then
f="-f net5.0"
else
f="-f netcoreapp3.1"
fi
if [[ -z $3 ]]; then
c="-c Release"
else
if [[ $config ]]; then
config="$(tr '[:lower:]' '[:upper:]' <<< ${3:0:1})${3:1}"
c="-c $config"
config="-c $config"
else
config="-c Release"
fi
echo dotnet publish ${c} ${r} ${f} --no-restore --self-contained=false -o Distribution Projects/Server/Server.csproj
dotnet publish ${c} ${r} ${f} --no-restore --self-contained=false -o Distribution Projects/Server/Server.csproj
echo dotnet publish ${c} ${r} ${f} --no-restore --self-contained=false -o Distribution/Assemblies Projects/UOContent/UOContent.csproj
dotnet publish ${c} ${r} ${f} --no-restore --self-contained=false -o Distribution/Assemblies Projects/UOContent/UOContent.csproj
echo dotnet publish ${config} ${os} -f net5.0 --no-restore --self-contained=false -o Distribution Projects/Server/Server.csproj
dotnet publish ${config} ${os} -f net5.0 --no-restore --self-contained=false -o Distribution Projects/Server/Server.csproj
echo dotnet publish ${config} ${os} -f net5.0 --no-restore --self-contained=false -o Distribution/Assemblies Projects/UOContent/UOContent.csproj
dotnet publish ${config} ${os} -f net5.0 --no-restore --self-contained=false -o Distribution/Assemblies Projects/UOContent/UOContent.csproj
exit $?
:CMDSCRIPT
IF "%~1" == "" or "%~1" == "release" (
SET config=-c Release
) ELSE (
SET config=-c Debug
)
IF "%~2" == "" (
SET f=-f netcoreapp3.1
SET os=-r win-x64
) ELSE (
IF "%~2" == "core" (
SET f=-f netcoreapp3.1
) ELSE (
SET f=-f net5.0
)
SET os=-r %~1-x64
)
IF "%~3" == "" or "%~3" == "release" (
SET c=-c Release
) ELSE (
SET c=-c Debug
)
IF "%~1" == "" (
SET r=-r win-x64
) ELSE (
SET r=-r %~1-x64
)
echo dotnet publish %c% %r% %f% --no-restore --self-contained=false -o Distribution Projects\Server\Server.csproj
dotnet publish %c% %r% %f% --no-restore --self-contained=false -o Distribution Projects\Server\Server.csproj
echo dotnet publish %c% %r% %f% --no-restore --self-contained=false -o Distribution\Assemblies Projects\UOContent\UOContent.csproj
dotnet publish %c% %r% %f% --no-restore --self-contained=false -o Distribution\Assemblies Projects\UOContent\UOContent.csproj
echo dotnet publish %config% %os% -f net5.0 --no-restore --self-contained=false -o Distribution Projects\Server\Server.csproj
dotnet publish %config% %os% -f net5.0 --no-restore --self-contained=false -o Distribution Projects\Server\Server.csproj
echo dotnet publish %config% %os% -f net5.0 --no-restore --self-contained=false -o Distribution\Assemblies Projects\UOContent\UOContent.csproj
dotnet publish %config% %os% -f net5.0 --no-restore --self-contained=false -o Distribution\Assemblies Projects\UOContent\UOContent.csproj