Skip to content

Commit

Permalink
feat: basic homepage with leaderboard (#2475)
Browse files Browse the repository at this point in the history
* fix: make failQuietly do something

* IPAddress and NetworkTypes changes
- IPAddressExtensions
- EnumExtensions
- fixed IPv6 Remote addresses for NetworkFilterMiddleware

* feat: basic homepage with leaderboard

* fix: caching for avatars

* fix: make the fallback svg the same size as the image
- The border-color was being set to transparent for the svg (since we don't need it there), but the actual border itself wasn't being added so the sizes of the svg and img were different, leading to a re-positioning of elements when the images loaded in (not good)

* fix: localized summary/description for API spec

* fix: avatar controller summary/description, better route name for user avatars

* chore: removed unused usings

* fix: change schema filter to be more useful

* translations changes

* chore: document requirements for AvatarController

* chore: add reference to built-in homepage documentation to main README.md

* fix: update HTMX dependencies

* update .NET version in readme
  • Loading branch information
lodicolo authored Jan 16, 2025
1 parent 90b62d7 commit c639c8b
Show file tree
Hide file tree
Showing 150 changed files with 5,802 additions and 318 deletions.
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

0 comments on commit c639c8b

Please sign in to comment.