From a924cd9c019852106c6299be14c1b896e51e8b3b Mon Sep 17 00:00:00 2001 From: "Bob.Song" Date: Thu, 26 Mar 2026 18:01:04 +0800 Subject: [PATCH] fkkkkk --- Entity/Entity.csproj | 1 + Entity/Fantasy.config | 12 ++-- Hotfix/Api/GameHttpApplicationHandler.cs | 66 ++++++++++++++++++ Hotfix/Api/GameHttpServicesHandler.cs | 89 ++++++++++++++++++++++++ Hotfix/Api/PlayerController.cs | 89 ++++++++++++++++++++++++ Hotfix/Api/PlayerService.cs | 6 ++ 6 files changed, 256 insertions(+), 7 deletions(-) create mode 100644 Hotfix/Api/GameHttpApplicationHandler.cs create mode 100644 Hotfix/Api/GameHttpServicesHandler.cs create mode 100644 Hotfix/Api/PlayerController.cs create mode 100644 Hotfix/Api/PlayerService.cs diff --git a/Entity/Entity.csproj b/Entity/Entity.csproj index c9ca112..398f5e7 100644 --- a/Entity/Entity.csproj +++ b/Entity/Entity.csproj @@ -10,6 +10,7 @@ + diff --git a/Entity/Fantasy.config b/Entity/Fantasy.config index eb56289..65ed1a7 100644 --- a/Entity/Fantasy.config +++ b/Entity/Fantasy.config @@ -57,13 +57,11 @@ networkProtocol="" outerPort="0" innerPort="11031" /> - - - - - - - + + + \ No newline at end of file diff --git a/Hotfix/Api/GameHttpApplicationHandler.cs b/Hotfix/Api/GameHttpApplicationHandler.cs new file mode 100644 index 0000000..478d8e9 --- /dev/null +++ b/Hotfix/Api/GameHttpApplicationHandler.cs @@ -0,0 +1,66 @@ +using Fantasy.Async; +using Fantasy.Event; +using Fantasy.Network.HTTP; +using Microsoft.AspNetCore.Builder; +using System; +using Fantasy; + +namespace NBF +{ + public class GameHttpApplicationHandler : AsyncEventSystem + { + protected override async FTask Handler(OnConfigureHttpApplication self) + { + var app = self.Application; + + // 1. CORS(必须在认证之前) + app.UseCors("GameClient"); + + // 2. 请求日志中间件 + app.Use(async (context, next) => + { + var requestId = Guid.NewGuid().ToString("N"); + context.Items["RequestId"] = requestId; + + var start = DateTime.UtcNow; + var method = context.Request.Method; + var path = context.Request.Path; + var ip = context.Connection.RemoteIpAddress?.ToString(); + + Log.Info($"[HTTP-{requestId}] {method} {path} - IP: {ip}"); + + try + { + await next.Invoke(); + + var duration = (DateTime.UtcNow - start).TotalMilliseconds; + var status = context.Response.StatusCode; + Log.Info($"[HTTP-{requestId}] {status} - {duration}ms"); + } + catch (Exception e) + { + Log.Error($"[HTTP-{requestId}] 异常: {e.Message}"); + throw; + } + }); + + // 3. 认证 + app.UseAuthentication(); + + // 4. 授权 + app.UseAuthorization(); + + // 5. 自定义响应头 + app.Use(async (context, next) => + { + context.Response.Headers.Add("X-Game-Server", "Fantasy"); + context.Response.Headers.Add("X-Server-Version", "1.0.0"); + await next.Invoke(); + }); + + Log.Info($"[HTTP] 游戏应用配置完成: Scene {self.Scene.SceneConfigId}"); + + await FTask.CompletedTask; + } + } +} \ No newline at end of file diff --git a/Hotfix/Api/GameHttpServicesHandler.cs b/Hotfix/Api/GameHttpServicesHandler.cs new file mode 100644 index 0000000..2603b45 --- /dev/null +++ b/Hotfix/Api/GameHttpServicesHandler.cs @@ -0,0 +1,89 @@ +using Fantasy.Async; +using Fantasy.Event; +using Fantasy.Network.HTTP; +using Microsoft.AspNetCore.Authentication.JwtBearer; +using Microsoft.Extensions.DependencyInjection; +using Microsoft.IdentityModel.Tokens; +using System.Text; +using System.Text.Json; +using System.Text.Json.Serialization; + +namespace NBF; + +public class GameHttpServicesHandler : AsyncEventSystem +{ + protected override async FTask Handler(OnConfigureHttpServices self) + { + // 1. 配置 JSON 序列化 + self.MvcBuilder.AddJsonOptions(options => + { + options.JsonSerializerOptions.PropertyNamingPolicy = JsonNamingPolicy.CamelCase; + options.JsonSerializerOptions.WriteIndented = true; + options.JsonSerializerOptions.DefaultIgnoreCondition = + JsonIgnoreCondition.WhenWritingNull; + options.JsonSerializerOptions.Converters.Add(new JsonStringEnumConverter()); + }); + + // 2. 添加全局过滤器 + self.MvcBuilder.AddMvcOptions(options => + { + options.Filters.Add(); + options.Filters.Add(); + }); + + // 3. 配置 JWT 认证 + var jwtSecret = "YourSuperSecretKeyForJwtTokenGeneration123!"; + self.Builder.Services.AddAuthentication(JwtBearerDefaults.AuthenticationScheme) + .AddJwtBearer(options => + { + options.TokenValidationParameters = new TokenValidationParameters + { + ValidateIssuer = true, + ValidateAudience = true, + ValidateLifetime = true, + ValidateIssuerSigningKey = true, + ValidIssuer = "GameServer", + ValidAudience = "GameClient", + IssuerSigningKey = new SymmetricSecurityKey( + Encoding.UTF8.GetBytes(jwtSecret)), + ClockSkew = TimeSpan.Zero + }; + }); + + // 4. 配置授权策略 + self.Builder.Services.AddAuthorization(options => + { + options.AddPolicy("Player", policy => + policy.RequireRole("Player", "Admin")); + options.AddPolicy("Admin", policy => + policy.RequireRole("Admin")); + options.AddPolicy("VIP", policy => + policy.RequireClaim("VIPLevel")); + }); + + // 5. 配置 CORS + self.Builder.Services.AddCors(options => + { + options.AddPolicy("GameClient", builder => + { + builder.WithOrigins( + "https://game.example.com", + "https://cdn.game.example.com" + ) + .AllowAnyMethod() + .AllowAnyHeader() + .AllowCredentials(); + }); + }); + + // 6. 注册游戏服务 + self.Builder.Services.AddSingleton(); + self.Builder.Services.AddSingleton(); + self.Builder.Services.AddScoped(); + self.Builder.Services.AddScoped(); + + Log.Info($"[HTTP] 游戏服务配置完成: Scene {self.Scene.SceneConfigId}"); + + await FTask.CompletedTask; + } +} \ No newline at end of file diff --git a/Hotfix/Api/PlayerController.cs b/Hotfix/Api/PlayerController.cs new file mode 100644 index 0000000..d61f69b --- /dev/null +++ b/Hotfix/Api/PlayerController.cs @@ -0,0 +1,89 @@ +using Fantasy; +using Microsoft.AspNetCore.Authorization; +using Microsoft.AspNetCore.Mvc; + +namespace NBF.Controllers +{ + [ApiController] + [Route("api/[controller]")] + public class PlayerController : ControllerBase + { + private readonly Scene _scene; + private readonly IPlayerService _playerService; + + public PlayerController(Scene scene, IPlayerService playerService) + { + _scene = scene; + _playerService = playerService; + } + + /// + /// 获取玩家信息(需要认证) + /// + [HttpGet("{playerId}")] + [Authorize(Policy = "Player")] + public async Task GetPlayer(long playerId) + { + var player = await _playerService.GetPlayerAsync(playerId); + if (player == null) + { + return NotFound(new { error = "Player not found" }); + } + + return Ok(new + { + playerId = player.Id, + name = player.Name, + level = player.Level, + exp = player.Exp + }); + } + + /// + /// 登录接口(无需认证) + /// + [HttpPost("login")] + [AllowAnonymous] + public async Task Login([FromBody] LoginRequest request) + { + var (success, token, playerId) = await _playerService.LoginAsync( + request.Username, request.Password); + + if (!success) + { + return Unauthorized(new { error = "Invalid credentials" }); + } + + return Ok(new + { + token = token, + playerId = playerId, + expiresIn = 3600 + }); + } + + /// + /// 管理员接口(需要 Admin 角色) + /// + [HttpPost("ban/{playerId}")] + [Authorize(Policy = "Admin")] + public async Task BanPlayer(long playerId, [FromBody] BanRequest request) + { + await _playerService.BanPlayerAsync(playerId, request.Reason, request.Duration); + + return Ok(new { message = "Player banned successfully" }); + } + } + + public class LoginRequest + { + public string Username { get; set; } + public string Password { get; set; } + } + + public class BanRequest + { + public string Reason { get; set; } + public int Duration { get; set; } // 分钟 + } +} \ No newline at end of file diff --git a/Hotfix/Api/PlayerService.cs b/Hotfix/Api/PlayerService.cs new file mode 100644 index 0000000..5bfa052 --- /dev/null +++ b/Hotfix/Api/PlayerService.cs @@ -0,0 +1,6 @@ +namespace NBF; + +public class PlayerService : IPlayerService +{ + +} \ No newline at end of file