Change Log out pit style

This commit is contained in:
linmeideli committed 2026-07-04 12:41:08 +08:00
1 parent 6ee0a117ce
commit 7dfcec19cf
11 files changed
+212 -17

No files matched your search

@@ -150,6 +150,7 @@
"System.Runtime.CompilerServices.Unsafe": "4.5.3"
}
}
}
},
".NETStandard,Version=v2.0/win-x64": {}
}
}
+8 -1
View File
@@ -34,6 +34,12 @@
"Microsoft.Extensions.DependencyInjection.Abstractions": "8.0.0"
}
},
"Microsoft.NET.ILLink.Tasks": {
"type": "Direct",
"requested": "[8.0.28, )",
"resolved": "8.0.28",
"contentHash": "XMqgVjlLxLqWmEh3c49haXLQwsMNtvo6YscUaqfvEGfg1iA8hnYgkUVq3i9Zu9gKeNKMWiiZKVwZExc/qyEAsQ=="
},
"StyleCop.Analyzers": {
"type": "Direct",
"requested": "[1.2.0-beta.556, )",
@@ -139,6 +145,7 @@
"NextFast.Hazel.Abstractions": {
"type": "Project"
}
}
},
"net8.0/win-x64": {}
}
}
@@ -37,12 +37,12 @@ namespace Empostor.Server.Http
_http = http;
_config = config.Value;
_pluginLoaderService = pluginLoaderService;
_passwordHash = AdminAuthHelper.ComputeHash(_config.Password);
_passwordHash = AdminController.ComputeHash(_config.Password);
}
private bool IsAuthenticated()
=> Request.Cookies.TryGetValue("empostor_admin", out var v)
&& AdminAuthHelper.ConstantTimeEquals(v, _passwordHash);
&& AdminController.ConstantTimeEquals(v, _passwordHash);
[HttpGet("/api/admin/marketplace/plugins")]
public async Task<IActionResult> ListPlugins()
@@ -1146,6 +1146,10 @@ namespace Empostor.Server.Net.Inner.Objects
private async ValueTask<bool> HandleSendChat(ClientPlayer sender, string message)
{
var clientId = sender.Client.Id;
var playerName = sender.Client.Name;
var slot = sender.Character?.PlayerId ?? 0;
if (message.StartsWith("#"))
{
var ctx = new CommandContext
@@ -1162,7 +1166,10 @@ namespace Empostor.Server.Net.Inner.Objects
};
var handled = await _commandService.TryHandleAsync(ctx);
if (handled)
{
_logger.LogInformation("[{ClientId}][{Name}][{Slot}][{Name}] => [255]Command:\n [{Message}]", clientId, playerName, slot, message);
return false;
}
}
var @event = new PlayerChatEvent(Game, sender, this, message);
@@ -1174,10 +1181,12 @@ namespace Empostor.Server.Net.Inner.Objects
await _eventManager.CallAsync(@event);
if (@event.IsCancelled)
{
_logger.LogInformation("[{ClientId}][{Name}][{Slot}][{Name}] => [0]Canceled:\n [{Message}]", clientId, playerName, slot, message);
return false;
}
else if (@event.SendToAllPlayers == false)
{
_logger.LogInformation("[{ClientId}][{Name}][{Slot}][{Name}] => [255]Command:\n [{Message}]", clientId, playerName, slot, message);
if (Game.Host != null)
{
await SendChatToPlayerAsync(message, Game.Host.Character);
@@ -1187,6 +1196,7 @@ namespace Empostor.Server.Net.Inner.Objects
}
else
{
_logger.LogInformation("[{ClientId}][{Name}][{Slot}][{Name}] => [-1]Public:\n [{Message}]", clientId, playerName, slot, message);
return true;
}
}
@@ -14,6 +14,7 @@ using Empostor.Server.Events.Client;
using Empostor.Server.Net.Factories;
using Empostor.Api.Service;
using Empostor.Server.Service.Auth;
using Empostor.Server.Utils;
using Microsoft.Extensions.Logging;
using Microsoft.Extensions.Options;
using Next.Hazel;
@@ -33,6 +34,7 @@ namespace Empostor.Server.Net.Manager
private readonly IHttpClientFactory _httpClientFactory;
private readonly AuthApiConfig _authApiConfig;
private readonly PortPoolService _portPool;
private readonly IpGeolocationService _ipGeo;
private int _idLast;
public ClientManager(
@@ -45,7 +47,8 @@ namespace Empostor.Server.Net.Manager
PlayerConnectStore playerConnectStore,
IHttpClientFactory httpClientFactory,
IOptions<AuthApiConfig> authApiConfig,
PortPoolService portPool)
PortPoolService portPool,
IpGeolocationService ipGeo)
{
_logger = logger;
_eventManager = eventManager;
@@ -58,6 +61,7 @@ namespace Empostor.Server.Net.Manager
_httpClientFactory = httpClientFactory;
_authApiConfig = authApiConfig.Value;
_portPool = portPool;
_ipGeo = ipGeo;
if (_compatibilityConfig.AllowFutureGameVersions)
{
@@ -138,6 +142,7 @@ namespace Empostor.Server.Net.Manager
string? friendCode = null;
UserAuthInfo? authInfo = null;
var clientIp = connection.EndPoint?.Address;
var location = await _ipGeo.GetLocationAsync(clientIp);
// Primary: match by delta port (nonce)
if (deltaPort > 0)
@@ -172,8 +177,8 @@ namespace Empostor.Server.Net.Manager
}
_logger.LogInformation(
"Client [{Id}]{Name} from port {Port} is authorized as {FriendCode} {HashPuid}",
id, name, deltaPort, friendCode ?? "unknown", HashPuid(authInfo.ProductUserId));
"Client [{Id}]{Name} from port {Port} {Location}is authorized as {FriendCode} {HashPuid}",
id, name, deltaPort, location, friendCode ?? "unknown", HashPuid(authInfo.ProductUserId));
}
else
{
@@ -191,14 +196,14 @@ namespace Empostor.Server.Net.Manager
{
friendCode = authInfo.FriendCode;
_logger.LogInformation(
"Client [{Id}]{Name} from {Ip} is authorized as {FriendCode} {HashPuid}",
id, name, NormalizeIp(clientIp), friendCode ?? "unknown", HashPuid(authInfo.ProductUserId));
"Client [{Id}]{Name} from {Ip} {Location}is authorized as {FriendCode} {HashPuid}",
id, name, NormalizeIp(clientIp), location, friendCode ?? "unknown", HashPuid(authInfo.ProductUserId));
}
else
{
_logger.LogWarning(
"Client [{Id}]{Name}: no auth info found (port={Port}, ip={Ip})",
id, name, deltaPort, NormalizeIp(clientIp));
"Client [{Id}]{Name}: no auth info found (port={Port}, ip={Ip} {Location})",
id, name, deltaPort, NormalizeIp(clientIp), location);
}
}
@@ -126,9 +126,10 @@ namespace Empostor.Server.Net.State
var version = client.GameVersion.ToString();
var hashPuid = client.ProductUserId?.Length >= 9 ? client.ProductUserId[..9] : client.ProductUserId ?? "0";
var deltaPort = client.DeltaPort;
var location = await _ipGeo.GetLocationAsync(clientIp);
_logger.LogInformation("{Code} - Player {Name} ({HashPuid}) ({Id}) is joining from ({Ip}:{RemotePort}) with v{Version}, Authority:{Authority}, port: {DeltaPort}",
Code, sender.Client.Name, hashPuid, sender.Client.Id, ipStr, remotePort, version, authority, deltaPort);
_logger.LogInformation("{Code} - Player {Name} ({HashPuid}) ({Id}) is joining from ({Ip} {Location}:{RemotePort}) with v{Version}, Authority:{Authority}, port: {DeltaPort}",
Code, sender.Client.Name, hashPuid, sender.Client.Id, ipStr, location, remotePort, version, authority, deltaPort);
if (isNew)
{
+5 -1
View File
@@ -16,6 +16,7 @@ using Empostor.Api.Net.Manager;
using Empostor.Api.Net.Messages.S2C;
using Empostor.Server.Events;
using Empostor.Server.Net.Manager;
using Empostor.Server.Utils;
using Microsoft.Extensions.Logging;
using Microsoft.Extensions.Options;
@@ -33,6 +34,7 @@ namespace Empostor.Server.Net.State
private readonly ICompatibilityManager _compatibilityManager;
private readonly CompatibilityConfig _compatibilityConfig;
private readonly TimeoutConfig _timeoutConfig;
private readonly IpGeolocationService _ipGeo;
public Game(
ILogger<Game> logger,
@@ -46,7 +48,8 @@ namespace Empostor.Server.Net.State
IEventManager eventManager,
ICompatibilityManager compatibilityManager,
IOptions<CompatibilityConfig> compatibilityConfig,
IOptions<TimeoutConfig> timeoutConfig)
IOptions<TimeoutConfig> timeoutConfig,
IpGeolocationService ipGeo)
{
_logger = logger;
_serviceProvider = serviceProvider;
@@ -65,6 +68,7 @@ namespace Empostor.Server.Net.State
_compatibilityManager = compatibilityManager;
_compatibilityConfig = compatibilityConfig.Value;
_timeoutConfig = timeoutConfig.Value;
_ipGeo = ipGeo;
Items = new ConcurrentDictionary<object, object>();
}
+10 -3
View File
@@ -54,7 +54,8 @@ namespace Empostor.Server
private static int Main(string[] args)
{
Log.Logger = new LoggerConfiguration()
.WriteTo.Console(outputTemplate: "{Timestamp:yyyy-MM-dd HH:mm:ss.fff zzz} [{Level:u3}] {Message:lj}{NewLine}{Exception}")
.WriteTo.Console(outputTemplate: "{Timestamp:yyyy-MM-dd HH:mm:ss.fff zzz} [{Level:u3}] {Source} - {Message:lj}{NewLine}{Exception}")
.Enrich.With<ShortSourceEnricher>()
.CreateBootstrapLogger();
try
{
@@ -156,7 +157,12 @@ namespace Empostor.Server
{
client.Timeout = TimeSpan.FromSeconds(15);
});
services.AddHttpClient("ipgeo", client =>
{
client.Timeout = TimeSpan.FromSeconds(5);
});
services.AddSingleton<IpGeolocationService>();
services.AddSingleton<ICompatibilityManager, CompatibilityManager>();
services.AddSingleton<ClientManager>();
services.AddSingleton<IClientManager>(p => p.GetRequiredService<ClientManager>());
@@ -248,12 +254,13 @@ namespace Empostor.Server
#endif
.MinimumLevel.Override("Microsoft.AspNetCore", LogEventLevel.Warning)
.Enrich.FromLogContext()
.WriteTo.Console(outputTemplate: "{Timestamp:yyyy-MM-dd HH:mm:ss.fff zzz} [{Level:u3}] {Message:lj}{NewLine}{Exception}")
.Enrich.With<ShortSourceEnricher>()
.WriteTo.Console(outputTemplate: "{Timestamp:yyyy-MM-dd HH:mm:ss.fff zzz} [{Level:u3}] {Source} - {Message:lj}{NewLine}{Exception}")
.WriteTo.File(
"Log/empostor-.log",
rollingInterval: RollingInterval.Day,
retainedFileCountLimit: 31,
outputTemplate: "{Timestamp:yyyy-MM-dd HH:mm:ss.fff zzz} [{Level:u3}] {Message:lj}{NewLine}{Exception}")
outputTemplate: "{Timestamp:yyyy-MM-dd HH:mm:ss.fff zzz} [{Level:u3}] {Source} - {Message:lj}{NewLine}{Exception}")
.ReadFrom.Configuration(context.Configuration,
new ConfigurationReaderOptions(ConfigurationAssemblySource.AlwaysScanDllFiles));
AssemblyLoadContext.Default.Resolving -= TryLoad;
@@ -0,0 +1,131 @@
using System;
using System.Collections.Concurrent;
using System.Net;
using System.Net.Http;
using System.Net.Sockets;
using System.Text.Json;
using System.Threading.Tasks;
using Microsoft.Extensions.Logging;
namespace Empostor.Server.Utils
{
internal class IpGeolocationService
{
private const string ApiUrl = "http://ip-api.com/json/{0}?lang=zh-CN&fields=status,country,regionName,city";
private static readonly TimeSpan CacheTtl = TimeSpan.FromHours(24);
private readonly ConcurrentDictionary<IPAddress, (string Location, DateTime Expiry)> _cache = new();
private readonly IHttpClientFactory _httpClientFactory;
private readonly ILogger<IpGeolocationService> _logger;
public IpGeolocationService(IHttpClientFactory httpClientFactory, ILogger<IpGeolocationService> logger)
{
_httpClientFactory = httpClientFactory;
_logger = logger;
}
public async ValueTask<string> GetLocationAsync(IPAddress? ip)
{
if (ip == null || IsPrivateIp(ip))
{
return string.Empty;
}
// Check cache
if (_cache.TryGetValue(ip, out var cached) && cached.Expiry > DateTime.UtcNow)
{
return cached.Location;
}
var location = await QueryApiAsync(ip);
_cache[ip] = (location, DateTime.UtcNow.Add(CacheTtl));
return location;
}
private async Task<string> QueryApiAsync(IPAddress ip)
{
try
{
var normalizedIp = ip.IsIPv4MappedToIPv6 ? ip.MapToIPv4() : ip;
var url = string.Format(ApiUrl, normalizedIp);
using var client = _httpClientFactory.CreateClient("ipgeo");
using var cts = new System.Threading.CancellationTokenSource(TimeSpan.FromSeconds(3));
var json = await client.GetStringAsync(url, cts.Token);
using var doc = JsonDocument.Parse(json);
var root = doc.RootElement;
if (root.TryGetProperty("status", out var status) && status.GetString() == "success")
{
var country = root.TryGetProperty("country", out var c) ? c.GetString() ?? string.Empty : string.Empty;
var region = root.TryGetProperty("regionName", out var r) ? r.GetString() ?? string.Empty : string.Empty;
var city = root.TryGetProperty("city", out var ct) ? ct.GetString() ?? string.Empty : string.Empty;
if (string.IsNullOrEmpty(country) && string.IsNullOrEmpty(city))
{
return string.Empty;
}
var parts = country != city
? (region != city ? $"{country} {region} {city}" : $"{country} {city}")
: country;
return parts.Trim();
}
return string.Empty;
}
catch (Exception ex)
{
_logger.LogDebug(ex, "IP geolocation lookup failed for {Ip}", ip);
return string.Empty;
}
}
private static bool IsPrivateIp(IPAddress ip)
{
if (ip.IsIPv4MappedToIPv6)
{
ip = ip.MapToIPv4();
}
if (ip.AddressFamily != AddressFamily.InterNetwork)
{
return true; // Don't query non-IPv4 for now
}
var bytes = ip.GetAddressBytes();
if (bytes.Length != 4)
{
return true;
}
// 127.0.0.0/8
if (bytes[0] == 127)
{
return true;
}
// 10.0.0.0/8
if (bytes[0] == 10)
{
return true;
}
// 172.16.0.0/12
if (bytes[0] == 172 && bytes[1] >= 16 && bytes[1] <= 31)
{
return true;
}
// 192.168.0.0/16
if (bytes[0] == 192 && bytes[1] == 168)
{
return true;
}
return false;
}
}
}
@@ -0,0 +1,23 @@
using Serilog.Core;
using Serilog.Events;
namespace Empostor.Server.Utils
{
internal class ShortSourceEnricher : ILogEventEnricher
{
public void Enrich(LogEvent logEvent, ILogEventPropertyFactory propertyFactory)
{
if (logEvent.Properties.TryGetValue("SourceContext", out var sourceContextValue) &&
sourceContextValue is ScalarValue scalar &&
scalar.Value is string fullName)
{
var shortName = fullName[(fullName.LastIndexOf('.') + 1)..];
logEvent.AddPropertyIfAbsent(propertyFactory.CreateProperty("Source", shortName));
}
else
{
logEvent.AddPropertyIfAbsent(propertyFactory.CreateProperty("Source", "Empostor"));
}
}
}
}
+6
View File
@@ -58,6 +58,12 @@
"resolved": "8.0.1",
"contentHash": "X21b8D6z58EG34OMLH955750mFaEaGs5xLGe9Vyi3059Cgb3sk+p2k/JlQXaieEBRrk+SVlFzDei3Q6PdVzJOA=="
},
"Microsoft.NET.ILLink.Tasks": {
"type": "Direct",
"requested": "[8.0.28, )",
"resolved": "8.0.28",
"contentHash": "XMqgVjlLxLqWmEh3c49haXLQwsMNtvo6YscUaqfvEGfg1iA8hnYgkUVq3i9Zu9gKeNKMWiiZKVwZExc/qyEAsQ=="
},
"Serilog.Extensions.Hosting": {
"type": "Direct",
"requested": "[8.0.0, )",