Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions .editorconfig
Original file line number Diff line number Diff line change
Expand Up @@ -136,9 +136,12 @@ csharp_preserve_single_line_blocks = true
csharp_style_namespace_declarations = file_scoped:silent

# ReSharper properties
resharper_csharp_wrap_after_declaration_lpar = true
resharper_csharp_wrap_after_invocation_lpar = true
resharper_csharp_wrap_arguments_style = chop_if_long
resharper_csharp_wrap_before_declaration_rpar = true
resharper_csharp_wrap_before_invocation_rpar = true
resharper_csharp_wrap_parameters_style = chop_if_long
resharper_keep_existing_invocation_parens_arrangement = false
resharper_keep_existing_property_patterns_arrangement = false
resharper_max_invocation_arguments_on_line = 3
Expand Down
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
11 changes: 11 additions & 0 deletions Documentation/AvatarController/AvatarController.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
# Intersect.Server /avatar/ API (AvatarController)

Avatars are loaded from the save `entities` and `faces` directories that the Client and Editor usually use. Since the server is the one sending them, in this rare case the server actually needs the resources.

For completeness and coverage of potential future features or enhancements, please copy the _entire_ **Editor** `resources` directory (don't copy resource packs meant for the client!) to the `assets/editor` directory (you may need to create this!) in the server's working directory.

At a minimum, the `entities` and `faces` directories are required. In the future, the `paperdolls` folder will also be required.

As shown in the below screenshot, `assets` should be a _sibling_ directory to the normal `logs` and `resources` directories of the server, and the editor resources are located in `assets/editor/resources`.

![File Structure Editor Resources](AvatarController.FileStructureEditorResources.png)
Binary file added Documentation/Features.HomepageLeaderboard.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
11 changes: 11 additions & 0 deletions Documentation/Features.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
# Features

## Server

### Homepage

This is a sample preview of the server built-in homepage, which includes a very basic leaderboard as well as user login/logout.

**By default the server does not have the assets to serve the in-game graphics as shown in the screenshot below, it requires a manual step. If you would like to see the in-game graphics please follow the instructions for the [AvatarController](./AvatarController/AvatarController.md).**

![Homepage Leaderboard](Features.HomepageLeaderboard.png)
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,11 @@ public static T Load<T>(T configuration, string filePath, bool failQuietly = fal
{
if (!File.Exists(filePath))
{
if (failQuietly)
{
return configuration;
}

throw new FileNotFoundException("Missing configuration file.", filePath);
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,6 @@
<PackageReference Include="System.Collections.Immutable" Version="9.0.0" />
<PackageReference Include="System.IO.Abstractions" Version="21.1.7" />
<PackageReference Include="System.IO.FileSystem" Version="4.3.0" />
<PackageReference Include="System.IO.Hashing" Version="9.0.0" />
</ItemGroup>

</Project>
7 changes: 7 additions & 0 deletions Framework/Intersect.Framework/EnumExtensions.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
namespace Intersect.Framework;

public static class EnumExtensions
{
public static TEnum[] GetFlags<TEnum>(this TEnum @enum) where TEnum : struct, Enum =>
Enum.GetValues<TEnum>().Where(flag => !flag.Equals(default(TEnum)) && @enum.HasFlag(flag)).ToArray();
}
38 changes: 38 additions & 0 deletions Framework/Intersect.Framework/IO/FileInfoExtensions.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
using System.Diagnostics.CodeAnalysis;
using System.IO.Hashing;

namespace Intersect.Framework.IO;

public static class FileInfoExtensions
{
public static bool TryComputeChecksum(this FileInfo fileInfo, [NotNullWhen(true)] out string? checksum)
{
if (!fileInfo.Exists)
{
checksum = null;
return false;
}

Crc64 algorithm = new();

using var fileStream = fileInfo.OpenRead();
algorithm.Append(fileStream);

var data = algorithm.GetHashAndReset();
if (data.Length < 1)
{
checksum = null;
return false;
}

checksum = Convert.ToBase64String(data);
if (!string.IsNullOrWhiteSpace(checksum))
{
return true;
}

checksum = null;
return false;

}
}
1 change: 1 addition & 0 deletions Framework/Intersect.Framework/Intersect.Framework.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@
<PackageReference Include="Microsoft.Extensions.Logging.Abstractions" Version="7.0.1" />
<PackageReference Include="Microsoft.Extensions.Options" Version="7.0.1" />
<PackageReference Include="Newtonsoft.Json" Version="13.0.3" />
<PackageReference Include="System.IO.Hashing" Version="9.0.1" />
<PackageReference Include="System.Text.Json" Version="8.0.5" />
</ItemGroup>

Expand Down
70 changes: 70 additions & 0 deletions Framework/Intersect.Framework/Net/IPAddressExtensions.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,70 @@

using System.Net;

namespace Intersect.Framework.Net;

public static class IPAddressExtensions
{
private static readonly Dictionary<NetworkTypes, IPRange[]> IPRangesForNetworkType = new()
{
{
NetworkTypes.Loopback, [
new IPRange(IPAddress.Parse("::1")),
new IPRange(IPAddress.Parse("0.0.0.0"), IPAddress.Parse("0.255.255.255")),
new IPRange(IPAddress.Parse("127.0.0.0"), IPAddress.Parse("127.255.255.255")),
]
},
{
NetworkTypes.Subnet, [
new IPRange(IPAddress.Parse("fe80::"), IPAddress.Parse("fe80::ffff:ffff:ffff:ffff")),
new IPRange(IPAddress.Parse("169.254.0.0"), IPAddress.Parse("169.254.255.255")),
new IPRange(IPAddress.Parse("255.255.255.255")),
]
},
{
NetworkTypes.PrivateNetwork, [
new IPRange(IPAddress.Parse("fc00::"), IPAddress.Parse("fdff:ffff:ffff:ffff:ffff:ffff:ffff:ffff")),
new IPRange(IPAddress.Parse("::ffff:10.0.0.0"), IPAddress.Parse("::ffff:10.255.255.255")),
// new IpRange(IPAddress.Parse("100.64.0.0"), IPAddress.Parse("100.127.255.255")),
// new IpRange(IPAddress.Parse("172.16.0.0"), IPAddress.Parse("172.31.255.255")),
// new IpRange(IPAddress.Parse("192.0.0.0"), IPAddress.Parse("192.0.0.255")),
// new IpRange(IPAddress.Parse("192.168.0.0"), IPAddress.Parse("192.168.255.255")),
// new IpRange(IPAddress.Parse("192.18.0.0"), IPAddress.Parse("192.19.255.255")),
new IPRange(IPAddress.Parse("10.0.0.0"), IPAddress.Parse("10.255.255.255")),
new IPRange(IPAddress.Parse("100.64.0.0"), IPAddress.Parse("100.127.255.255")),
new IPRange(IPAddress.Parse("172.16.0.0"), IPAddress.Parse("172.31.255.255")),
new IPRange(IPAddress.Parse("192.0.0.0"), IPAddress.Parse("192.0.0.255")),
new IPRange(IPAddress.Parse("192.168.0.0"), IPAddress.Parse("192.168.255.255")),
new IPRange(IPAddress.Parse("192.18.0.0"), IPAddress.Parse("192.19.255.255")),
]
},
};

public static bool IsLoopback(this IPAddress address) => MatchesNetworkTypes(address, NetworkTypes.Loopback);

public static bool IsPrivate(this IPAddress address) => MatchesNetworkTypes(address, NetworkTypes.PrivateNetwork);

public static bool IsPublic(this IPAddress address) =>
IPRangesForNetworkType.Values.All(ipRanges => !ipRanges.Any(range => range.Contains(address)));

public static bool IsSubnet(this IPAddress address) => MatchesNetworkTypes(address, NetworkTypes.Subnet);

public static bool MatchesNetworkTypes(this IPAddress address, NetworkTypes networkTypes)
{
if (address.IsIPv4MappedToIPv6)
{
return MatchesNetworkTypes(address.MapToIPv4(), networkTypes);
}

if (networkTypes == NetworkTypes.Public)
{
return IsPublic(address);
}

var individualFlags = networkTypes.GetFlags();
return individualFlags.Any(
flag => IPRangesForNetworkType.TryGetValue(flag, out var ranges) &&
ranges.Any(range => range.Contains(address))
);
}
}
65 changes: 65 additions & 0 deletions Framework/Intersect.Framework/Net/IPRange.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,65 @@
using System.Net;
using System.Net.Sockets;

namespace Intersect.Framework.Net;

public readonly record struct IPRange
{
public AddressFamily AddressFamily { get; }
public byte[] Start { get; }
public byte[] End { get; }

public IPRange(IPAddress address)
{
ArgumentNullException.ThrowIfNull(address, nameof(address));

AddressFamily = address.AddressFamily;
Start = address.GetAddressBytes();
End = address.GetAddressBytes();
}

public IPRange(IPAddress start, IPAddress end)
{
ArgumentNullException.ThrowIfNull(start, nameof(start));
ArgumentNullException.ThrowIfNull(end, nameof(end));

if (start.AddressFamily != end.AddressFamily)
{
throw new ArgumentException("AddressFamily mismatch");
}

AddressFamily = start.AddressFamily;
Start = start.GetAddressBytes();
End = end.GetAddressBytes();
}

public bool Contains(IPAddress address)
{
ArgumentNullException.ThrowIfNull(address, nameof(address));

if (address.AddressFamily != AddressFamily)
{
return false;
}

var octets = address.GetAddressBytes();
if (Start.Length != End.Length || End.Length != octets.Length)
{
return false;
}

for (var index = 0; index < octets.Length; index++)
{
var start = Start[index];
var end = End[index];
var octet = octets[index];

if (octet < start || end < octet)
{
return false;
}
}

return true;
}
}
10 changes: 10 additions & 0 deletions Framework/Intersect.Framework/Net/NetworkTypes.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
namespace Intersect.Framework.Net;

[Flags]
public enum NetworkTypes
{
Public = 0,
Loopback = 1,
Subnet = 2,
PrivateNetwork = 4,
}
6 changes: 6 additions & 0 deletions Framework/Intersect.Framework/Net/NetworkTypesExtensions.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
namespace Intersect.Framework.Net;

public static class NetworkTypesExtensions
{
public static readonly NetworkTypes[] Flags = Enum.GetValues<NetworkTypes>();
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
using System.Globalization;
using System.Resources;

namespace Intersect.Framework.Resources;

public static class ResourceManagerExtensions
{
public static string? GetStringWithFallback(
this ResourceManager resourceManager,
string name,
CultureInfo? cultureInfo
)
{
while (cultureInfo != null && cultureInfo.LCID != CultureInfo.InvariantCulture.LCID)
{
var value = resourceManager.GetString(name, cultureInfo);
if (!string.IsNullOrWhiteSpace(value))
{
return value;
}

cultureInfo = cultureInfo.Parent;
}

return null;
}
}
9 changes: 6 additions & 3 deletions Intersect (Core)/Logging/Output/FileOutput.cs
Original file line number Diff line number Diff line change
Expand Up @@ -126,9 +126,12 @@ params object[] args
args
);

Writer.Write(line);
Writer.Write(Spacer);
Writer.Flush();
lock (Writer)
{
Writer.Write(line);
Writer.Write(Spacer);
Writer.Flush();
}
}
catch (Exception exceptionWhileWriting)
{
Expand Down
Loading
Loading