实现一个高扩展、高性能、高可配置的C#版游戏战斗服务器是一个复杂的任务,涉及多个方面的设计和实现。以下是一个详细的实现示例,涵盖了玩家管理、战斗逻辑、网络通信、并发处理、日志记录等方面。
1. 项目结构
首先,我们定义项目的结构:
GameServer
├── Controllers
│ ├── BattleController.cs
│ └── PlayerController.cs
├── Models
│ ├── Player.cs
│ ├── Battle.cs
│ └── BattleResult.cs
├── Services
│ ├── PlayerService.cs
│ ├── BattleService.cs
│ └── AuthService.cs
├── Utils
│ ├── Logger.cs
│ └── Config.cs
├── Program.cs
└── Startup.cs
2. 依赖项
我们需要以下依赖项:
- ASP.NET Core
- Entity Framework Core
- Serilog
- JWT (JSON Web Token)
- AutoMapper
3. 配置文件
在appsettings.json
中配置数据库连接字符串和JWT密钥:
{
"ConnectionStrings": {
"DefaultConnection": "Server=localhost;Database=GameServer;User Id=sa;Password=your_password;"
},
"Jwt": {
"Key": "your_secret_key",
"Issuer": "your_issuer",
"Audience": "your_audience"
},
"Serilog": {
"MinimumLevel": {
"Default": "Information",
"Override": {
"Microsoft": "Warning",
"System": "Warning"
}
},
"WriteTo": [
{ "Name": "Console" },
{ "Name": "File", "Args": { "path": "Logs/log.txt", "rollingInterval": "Day" } }
]
}
}
4. 数据模型
4.1 Player.cs
namespace GameServer.Models
{
public class Player
{
public int Id { get; set; }
public string Username { get; set; }
public int Level { get; set; }
public int Health { get; set; }
public int Attack { get; set; }
public int Defense { get; set; }
}
}
4.2 Battle.cs
namespace GameServer.Models
{
public class Battle
{
public int Id { get; set; }
public int Player1Id { get; set; }
public int Player2Id { get; set; }
public DateTime StartTime { get; set; }
public DateTime EndTime { get; set; }
public BattleResult Result { get; set; }
}
}
4.3 BattleResult.cs
namespace GameServer.Models
{
public enum BattleResult
{
Player1Win,
Player2Win,
Draw
}
}
5. 服务层
5.1 PlayerService.cs
using GameServer.Models;
using Microsoft.EntityFrameworkCore;
using System.Collections.Generic;
using System.Threading.Tasks;
namespace GameServer.Services
{
public class PlayerService
{
private readonly GameDbContext _dbContext;
public PlayerService(GameDbContext dbContext)
{
_dbContext = dbContext;
}
public async Task<Player> GetPlayerAsync(int playerId)
{
return await _dbContext.Players.FindAsync(playerId);
}
public async Task<List<Player>> GetAllPlayersAsync()
{
return await _dbContext.Players.ToListAsync();
}
public async Task AddPlayerAsync(Player player)
{
_dbContext.Players.Add(player);
await _dbContext.SaveChangesAsync();
}
public async Task UpdatePlayerAsync(Player player)
{
_dbContext.Players.Update(player);
await _dbContext.SaveChangesAsync();
}
public async Task DeletePlayerAsync(int playerId)
{
var player = await _dbContext.Players.FindAsync(playerId);
if (player != null)
{
_dbContext.Players.Remove(player);
await _dbContext.SaveChangesAsync();
}
}
}
}
5.2 BattleService.cs
using GameServer.Models;
using Microsoft.EntityFrameworkCore;
using System;
using System.Threading.Tasks;
namespace GameServer.Services
{
public class BattleService
{
private readonly GameDbContext _dbContext;
private readonly PlayerService _playerService;
public BattleService(GameDbContext dbContext, PlayerService playerService)
{
_dbContext = dbContext;
_playerService = playerService;
}
public async Task<Battle> StartBattleAsync(int player1Id, int player2Id)
{
var player1 = await _playerService.GetPlayerAsync(player1Id);
var player2 = await _playerService.GetPlayerAsync(player2Id);
if (player1 == null || player2 == null)
{
throw new Exception("Player not found");
}
var battle = new Battle
{
Player1Id = player1Id,
Player2Id = player2Id,
StartTime = DateTime.UtcNow
};
_dbContext.Battles.Add(battle);
await _dbContext.SaveChangesAsync();
return battle;
}
public async Task<BattleResult> ExecuteBattleAsync(int battleId)
{
var battle = await _dbContext.Battles.FindAsync(battleId);
if (battle == null)
{
throw new Exception("Battle not found");
}
var player1 = await _playerService.GetPlayerAsync(battle.Player1Id);
var player2 = await _playerService.GetPlayerAsync(battle.Player2Id);
if (player1 == null || player2 == null)
{
throw new Exception("Player not found");
}
var result = SimulateBattle(player1, player2);
battle.EndTime = DateTime.UtcNow;
battle.Result = result;
_dbContext.Battles.Update(battle);
await _dbContext.SaveChangesAsync();
return result;
}
private BattleResult SimulateBattle(Player player1, Player player2)
{
// 简单的战斗模拟逻辑
var player1Power = player1.Attack - player2.Defense;
var player2Power = player2.Attack - player1.Defense;
if (player1Power > player2Power)
{
return BattleResult.Player1Win;
}
else if (player2Power > player1Power)
{
return BattleResult.Player2Win;
}
else
{
return BattleResult.Draw;
}
}
}
}
5.3 AuthService.cs
using Microsoft.Extensions.Configuration;
using Microsoft.IdentityModel.Tokens;
using System;
using System.IdentityModel.Tokens.Jwt;
using System.Security.Claims;
using System.Text;
namespace GameServer.Services
{
public class AuthService
{
private readonly string _secretKey;
private readonly string _issuer;
private readonly string _audience;
public AuthService(IConfiguration configuration)
{
_secretKey = configuration["Jwt:Key"];
_issuer = configuration["Jwt:Issuer"];
_audience = configuration["Jwt:Audience"];
}
public string GenerateToken(string username)
{
var tokenHandler = new JwtSecurityTokenHandler();
var key = Encoding.ASCII.GetBytes(_secretKey);
var tokenDescriptor = new SecurityTokenDescriptor
{
Subject = new ClaimsIdentity(new[] { new Claim(ClaimTypes.Name, username) }),
Expires = DateTime.UtcNow.AddHours(1),
Issuer = _issuer,
Audience = _audience,
SigningCredentials = new SigningCredentials(new SymmetricSecurityKey(key), SecurityAlgorithms.HmacSha256Signature)
};
var token = tokenHandler.CreateToken(tokenDescriptor);
return tokenHandler.WriteToken(token);
}
}
}
6. 控制器
6.1 PlayerController.cs
using GameServer.Models;
using GameServer.Services;
using Microsoft.AspNetCore.Mvc;
using System.Collections.Generic;
using System.Threading.Tasks;
namespace GameServer.Controllers
{
[ApiController]
[Route("api/[controller]")]
public class PlayerController : ControllerBase
{
private readonly PlayerService _playerService;
public PlayerController(PlayerService playerService)
{
_playerService = playerService;
}
[HttpGet("{id}")]
public async Task<ActionResult<Player>> GetPlayer(int id)
{
var player = await _playerService.GetPlayerAsync(id);
if (player == null)
{
return NotFound();
}
return player;
}
[HttpGet]
public async Task<ActionResult<List<Player>>> GetAllPlayers()
{
return await _playerService.GetAllPlayersAsync();
}
[HttpPost]
public async Task<ActionResult> AddPlayer(Player player)
{
await _playerService.AddPlayerAsync(player);
return CreatedAtAction(nameof(GetPlayer), new { id = player.Id }, player);
}
[HttpPut("{id}")]
public async Task<ActionResult> UpdatePlayer(int id, Player player)
{
if (id != player.Id)
{
return BadRequest();
}
await _playerService.UpdatePlayerAsync(player);
return NoContent();
}
[HttpDelete("{id}")]
public async Task<ActionResult> DeletePlayer(int id)
{
await _playerService.DeletePlayerAsync(id);
return NoContent();
}
}
}
6.2 BattleController.cs
using GameServer.Models;
using GameServer.Services;
using Microsoft.AspNetCore.Mvc;
using System.Threading.Tasks;
namespace GameServer.Controllers
{
[ApiController]
[Route("api/[controller]")]
public class BattleController : ControllerBase
{
private readonly BattleService _battleService;
public BattleController(BattleService battleService)
{
_battleService = battleService;
}
[HttpPost("start")]
public async Task<ActionResult<Battle>> StartBattle(int player1Id, int player2Id)
{
var battle = await _battleService.StartBattleAsync(player1Id, player2Id);
return CreatedAtAction(nameof(GetBattle), new { id = battle.Id }, battle);
}
[HttpGet("{id}")]
public async Task<ActionResult<Battle>> GetBattle(int id)
{
var battle = await _battleService.GetBattleAsync(id);
if (battle == null)
{
return NotFound();
}
return battle;
}
[HttpPost("execute/{id}")]
public async Task<ActionResult<BattleResult>> ExecuteBattle(int id)
{
var result = await _battleService.ExecuteBattleAsync(id);
return result;
}
}
}
7. 数据库上下文
7.1 GameDbContext.cs
using GameServer.Models;
using Microsoft.EntityFrameworkCore;
namespace GameServer
{
public class GameDbContext : DbContext
{
public GameDbContext(DbContextOptions<GameDbContext> options) : base(options) { }
public DbSet<Player> Players { get; set; }
public DbSet<Battle> Battles { get; set; }
}
}
8. 启动配置
8.1 Startup.cs
using GameServer.Services;
using Microsoft.AspNetCore.Authentication.JwtBearer;
using Microsoft.EntityFrameworkCore;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Hosting;
using Microsoft.IdentityModel.Tokens;
using Serilog;
using System.Text;
namespace GameServer
{
public class Startup
{
public Startup(IConfiguration configuration)
{
Configuration = configuration;
}
public IConfiguration Configuration { get; }
public void ConfigureServices(IServiceCollection services)
{
services.AddDbContext<GameDbContext>(options =>
options.UseSqlServer(Configuration.GetConnectionString("DefaultConnection")));
services.AddScoped<PlayerService>();
services.AddScoped<BattleService>();
services.AddScoped<AuthService>();
services.AddControllers();
var key = Encoding.ASCII.GetBytes(Configuration["Jwt:Key"]);
services.AddAuthentication(x =>
{
x.DefaultAuthenticateScheme = JwtBearerDefaults.AuthenticationScheme;
x.DefaultChallengeScheme = JwtBearerDefaults.AuthenticationScheme;
}).AddJwtBearer(x =>
{
x.RequireHttpsMetadata = false;
x.SaveToken = true;
x.TokenValidationParameters = new TokenValidationParameters
{
ValidateIssuerSigningKey = true,
IssuerSigningKey = new SymmetricSecurityKey(key),
ValidateIssuer = false,
ValidateAudience = false
};
});
services.AddSwaggerGen();
}
public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
{
if (env.IsDevelopment())
{
app.UseDeveloperExceptionPage();
}
app.UseSerilogRequestLogging();
app.UseRouting();
app.UseAuthentication();
app.UseAuthorization();
app.UseEndpoints(endpoints =>
{
endpoints.MapControllers();
});
app.UseSwagger();
app.UseSwaggerUI(c =>
{
c.SwaggerEndpoint("/swagger/v1/swagger.json", "GameServer API V1");
});
}
}
}
8.2 Program.cs
using Microsoft.AspNetCore.Hosting;
using Microsoft.Extensions.Hosting;
using Serilog;
namespace GameServer
{
public class Program
{
public static void Main(string[] args)
{
Log.Logger = new LoggerConfiguration()
.ReadFrom.Configuration(Configuration)
.CreateLogger();
try
{
Log.Information("Starting web host");
CreateHostBuilder(args).Build().Run();
}
catch (Exception ex)
{
Log.Fatal(ex, "Host terminated unexpectedly");
}
finally
{
Log.CloseAndFlush();
}
}
public static IHostBuilder CreateHostBuilder(string[] args) =>
Host.CreateDefaultBuilder(args)
.UseSerilog()
.ConfigureWebHostDefaults(webBuilder =>
{
webBuilder.UseStartup<Startup>();
});
}
}
9. 结论
以上是一个详细的高扩展、高性能、高可配置的C#版游戏战斗服务器的实现示例。这个示例涵盖了玩家管理、战斗逻辑、网络通信、并发处理、日志记录等方面。你可以根据具体需求进行进一步的扩展和优化,以满足不同类型游戏的需求。
希望这些内容能帮助你更好地理解和实现一个高扩展、高性能、高可配置的游戏战斗服务器。如果你有任何问题或需要进一步的帮助,请随时提问。
好的,接下来我们可以进一步扩展和优化这个游戏战斗服务器。我们将添加以下功能:
- 缓存机制:使用内存缓存来减少数据库查询次数,提高性能。
- 并发处理:使用并发队列来处理战斗请求,确保线程安全。
- 配置管理:使用配置文件来管理游戏参数。
- 日志记录:增强日志记录,便于调试和监控。
10. 缓存机制
我们可以使用MemoryCache
来缓存玩家数据,减少数据库查询次数。
10.1 PlayerService.cs
using GameServer.Models;
using Microsoft.EntityFrameworkCore;
using Microsoft.Extensions.Caching.Memory;
using System;
using System.Collections.Generic;
using System.Threading.Tasks;
namespace GameServer.Services
{
public class PlayerService
{
private readonly GameDbContext _dbContext;
private readonly IMemoryCache _cache;
public PlayerService(GameDbContext dbContext, IMemoryCache cache)
{
_dbContext = dbContext;
_cache = cache;
}
public async Task<Player> GetPlayerAsync(int playerId)
{
if (!_cache.TryGetValue(playerId, out Player player))
{
player = await _dbContext.Players.FindAsync(playerId);
if (player != null)
{
_cache.Set(playerId, player, TimeSpan.FromMinutes(5));
}
}
return player;
}
public async Task<List<Player>> GetAllPlayersAsync()
{
return await _dbContext.Players.ToListAsync();
}
public async Task AddPlayerAsync(Player player)
{
_dbContext.Players.Add(player);
await _dbContext.SaveChangesAsync();
_cache.Set(player.Id, player, TimeSpan.FromMinutes(5));
}
public async Task UpdatePlayerAsync(Player player)
{
_dbContext.Players.Update(player);
await _dbContext.SaveChangesAsync();
_cache.Set(player.Id, player, TimeSpan.FromMinutes(5));
}
public async Task DeletePlayerAsync(int playerId)
{
var player = await _dbContext.Players.FindAsync(playerId);
if (player != null)
{
_dbContext.Players.Remove(player);
await _dbContext.SaveChangesAsync();
_cache.Remove(playerId);
}
}
}
}
11. 并发处理
我们可以使用ConcurrentQueue
来处理战斗请求,确保线程安全。
11.1 BattleService.cs
using GameServer.Models;
using Microsoft.EntityFrameworkCore;
using System;
using System.Collections.Concurrent;
using System.Threading.Tasks;
namespace GameServer.Services
{
public class BattleService
{
private readonly GameDbContext _dbContext;
private readonly PlayerService _playerService;
private readonly ConcurrentQueue<Func<Task>> _battleQueue = new ConcurrentQueue<Func<Task>>();
public BattleService(GameDbContext dbContext, PlayerService playerService)
{
_dbContext = dbContext;
_playerService = playerService;
}
public async Task<Battle> StartBattleAsync(int player1Id, int player2Id)
{
var player1 = await _playerService.GetPlayerAsync(player1Id);
var player2 = await _playerService.GetPlayerAsync(player2Id);
if (player1 == null || player2 == null)
{
throw new Exception("Player not found");
}
var battle = new Battle
{
Player1Id = player1Id,
Player2Id = player2Id,
StartTime = DateTime.UtcNow
};
_dbContext.Battles.Add(battle);
await _dbContext.SaveChangesAsync();
return battle;
}
public async Task<BattleResult> ExecuteBattleAsync(int battleId)
{
var tcs = new TaskCompletionSource<BattleResult>();
_battleQueue.Enqueue(async () =>
{
var battle = await _dbContext.Battles.FindAsync(battleId);
if (battle == null)
{
tcs.SetException(new Exception("Battle not found"));
return;
}
var player1 = await _playerService.GetPlayerAsync(battle.Player1Id);
var player2 = await _playerService.GetPlayerAsync(battle.Player2Id);
if (player1 == null || player2 == null)
{
tcs.SetException(new Exception("Player not found"));
return;
}
var result = SimulateBattle(player1, player2);
battle.EndTime = DateTime.UtcNow;
battle.Result = result;
_dbContext.Battles.Update(battle);
await _dbContext.SaveChangesAsync();
tcs.SetResult(result);
});
await ProcessQueueAsync();
return await tcs.Task;
}
private async Task ProcessQueueAsync()
{
while (_battleQueue.TryDequeue(out var battleTask))
{
await battleTask();
}
}
private BattleResult SimulateBattle(Player player1, Player player2)
{
// 简单的战斗模拟逻辑
var player1Power = player1.Attack - player2.Defense;
var player2Power = player2.Attack - player1.Defense;
if (player1Power > player2Power)
{
return BattleResult.Player1Win;
}
else if (player2Power > player1Power)
{
return BattleResult.Player2Win;
}
else
{
return BattleResult.Draw;
}
}
}
}
12. 配置管理
我们可以使用配置文件来管理游戏参数,例如战斗参数、玩家属性等。
12.1 appsettings.json
{
"GameSettings": {
"DefaultPlayerHealth": 100,
"DefaultPlayerAttack": 10,
"DefaultPlayerDefense": 5
}
}
12.2 Config.cs
using Microsoft.Extensions.Configuration;
namespace GameServer.Utils
{
public class Config
{
private readonly IConfiguration _configuration;
public Config(IConfiguration configuration)
{
_configuration = configuration;
}
public int DefaultPlayerHealth => _configuration.GetValue<int>("GameSettings:DefaultPlayerHealth");
public int DefaultPlayerAttack => _configuration.GetValue<int>("GameSettings:DefaultPlayerAttack");
public int DefaultPlayerDefense => _configuration.GetValue<int>("GameSettings:DefaultPlayerDefense");
}
}
13. 日志记录
我们可以增强日志记录,便于调试和监控。
13.1 Logger.cs
using Serilog;
namespace GameServer.Utils
{
public static class Logger
{
public static void LogInformation(string message)
{
Log.Information(message);
}
public static void LogWarning(string message)
{
Log.Warning(message);
}
public static void LogError(string message)
{
Log.Error(message);
}
}
}
13.2 BattleService.cs
using GameServer.Models;
using GameServer.Utils;
using Microsoft.EntityFrameworkCore;
using System;
using System.Collections.Concurrent;
using System.Threading.Tasks;
namespace GameServer.Services
{
public class BattleService
{
private readonly GameDbContext _dbContext;
private readonly PlayerService _playerService;
private readonly ConcurrentQueue<Func<Task>> _battleQueue = new ConcurrentQueue<Func<Task>>();
public BattleService(GameDbContext dbContext, PlayerService playerService)
{
_dbContext = dbContext;
_playerService = playerService;
}
public async Task<Battle> StartBattleAsync(int player1Id, int player2Id)
{
var player1 = await _playerService.GetPlayerAsync(player1Id);
var player2 = await _playerService.GetPlayerAsync(player2Id);
if (player1 == null || player2 == null)
{
Logger.LogError("Player not found");
throw new Exception("Player not found");
}
var battle = new Battle
{
Player1Id = player1Id,
Player2Id = player2Id,
StartTime = DateTime.UtcNow
};
_dbContext.Battles.Add(battle);
await _dbContext.SaveChangesAsync();
Logger.LogInformation($"Battle started between Player {player1Id} and Player {player2Id}");
return battle;
}
public async Task<BattleResult> ExecuteBattleAsync(int battleId)
{
var tcs = new TaskCompletionSource<BattleResult>();
_battleQueue.Enqueue(async () =>
{
var battle = await _dbContext.Battles.FindAsync(battleId);
if (battle == null)
{
Logger.LogError("Battle not found");
tcs.SetException(new Exception("Battle not found"));
return;
}
var player1 = await _playerService.GetPlayerAsync(battle.Player1Id);
var player2 = await _playerService.GetPlayerAsync(battle.Player2Id);
if (player1 == null || player2 == null)
{
Logger.LogError("Player not found");
tcs.SetException(new Exception("Player not found"));
return;
}
var result = SimulateBattle(player1, player2);
battle.EndTime = DateTime.UtcNow;
battle.Result = result;
_dbContext.Battles.Update(battle);
await _dbContext.SaveChangesAsync();
Logger.LogInformation($"Battle {battleId} ended with result: {result}");
tcs.SetResult(result);
});
await ProcessQueueAsync();
return await tcs.Task;
}
private async Task ProcessQueueAsync()
{
while (_battleQueue.TryDequeue(out var battleTask))
{
await battleTask();
}
}
private BattleResult SimulateBattle(Player player1, Player player2)
{
// 简单的战斗模拟逻辑
var player1Power = player1.Attack - player2.Defense;
var player2Power = player2.Attack - player1.Defense;
if (player1Power > player2Power)
{
return BattleResult.Player1Win;
}
else if (player2Power > player1Power)
{
return BattleResult.Player2Win;
}
else
{
return BattleResult.Draw;
}
}
}
}
14. 结论
通过添加缓存机制、并发处理、配置管理和增强日志记录,我们进一步优化了游戏战斗服务器的性能和可扩展性。这些改进使得服务器能够更高效地处理玩家请求,并且更容易进行调试和监控。
希望这些内容能帮助你更好地理解和实现一个高扩展、高性能、高可配置的游戏战斗服务器。如果你有任何问题或需要进一步的帮助,请随时提问。
好的,我们可以继续扩展和优化游戏战斗服务器。接下来,我们将添加以下功能:
- 身份验证和授权:确保只有经过身份验证的用户才能访问服务器资源。
- 错误处理和异常管理:增强错误处理机制,确保系统的稳定性和可靠性。
- 单元测试和集成测试:确保代码的正确性和稳定性。
- 性能监控和分析:使用工具监控和分析服务器性能。
15. 身份验证和授权
我们可以使用JWT(JSON Web Token)来实现身份验证和授权。
15.1 安装必要的NuGet包
dotnet add package Microsoft.AspNetCore.Authentication.JwtBearer
dotnet add package Microsoft.IdentityModel.Tokens
15.2 配置JWT身份验证
在Startup.cs
中配置JWT身份验证。
using Microsoft.AspNetCore.Authentication.JwtBearer;
using Microsoft.IdentityModel.Tokens;
using System.Text;
public class Startup
{
public void ConfigureServices(IServiceCollection services)
{
services.AddControllers();
var key = Encoding.ASCII.GetBytes("YourSecretKeyHere");
services.AddAuthentication(x =>
{
x.DefaultAuthenticateScheme = JwtBearerDefaults.AuthenticationScheme;
x.DefaultChallengeScheme = JwtBearerDefaults.AuthenticationScheme;
})
.AddJwtBearer(x =>
{
x.RequireHttpsMetadata = false;
x.SaveToken = true;
x.TokenValidationParameters = new TokenValidationParameters
{
ValidateIssuerSigningKey = true,
IssuerSigningKey = new SymmetricSecurityKey(key),
ValidateIssuer = false,
ValidateAudience = false
};
});
services.AddAuthorization();
}
public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
{
if (env.IsDevelopment())
{
app.UseDeveloperExceptionPage();
}
app.UseRouting();
app.UseAuthentication();
app.UseAuthorization();
app.UseEndpoints(endpoints =>
{
endpoints.MapControllers();
});
}
}
15.3 创建身份验证控制器
创建一个控制器来处理用户登录和生成JWT。
using Microsoft.AspNetCore.Mvc;
using Microsoft.IdentityModel.Tokens;
using System;
using System.IdentityModel.Tokens.Jwt;
using System.Security.Claims;
using System.Text;
namespace GameServer.Controllers
{
[ApiController]
[Route("api/[controller]")]
public class AuthController : ControllerBase
{
[HttpPost("login")]
public IActionResult Login([FromBody] LoginModel model)
{
// 这里应该验证用户的用户名和密码
if (model.Username == "test" && model.Password == "password")
{
var tokenHandler = new JwtSecurityTokenHandler();
var key = Encoding.ASCII.GetBytes("YourSecretKeyHere");
var tokenDescriptor = new SecurityTokenDescriptor
{
Subject = new ClaimsIdentity(new Claim[]
{
new Claim(ClaimTypes.Name, model.Username)
}),
Expires = DateTime.UtcNow.AddHours(1),
SigningCredentials = new SigningCredentials(new SymmetricSecurityKey(key), SecurityAlgorithms.HmacSha256Signature)
};
var token = tokenHandler.CreateToken(tokenDescriptor);
var tokenString = tokenHandler.WriteToken(token);
return Ok(new { Token = tokenString });
}
return Unauthorized();
}
}
public class LoginModel
{
public string Username { get; set; }
public string Password { get; set; }
}
}
15.4 保护控制器
在需要保护的控制器上添加[Authorize]
属性。
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Mvc;
namespace GameServer.Controllers
{
[Authorize]
[ApiController]
[Route("api/[controller]")]
public class PlayerController : ControllerBase
{
private readonly PlayerService _playerService;
public PlayerController(PlayerService playerService)
{
_playerService = playerService;
}
[HttpGet("{id}")]
public async Task<IActionResult> GetPlayer(int id)
{
var player = await _playerService.GetPlayerAsync(id);
if (player == null)
{
return NotFound();
}
return Ok(player);
}
// 其他方法...
}
}
16. 错误处理和异常管理
我们可以使用全局异常处理来捕获和处理所有未处理的异常。
16.1 创建异常处理中间件
using Microsoft.AspNetCore.Http;
using Serilog;
using System;
using System.Net;
using System.Threading.Tasks;
namespace GameServer.Middleware
{
public class ExceptionMiddleware
{
private readonly RequestDelegate _next;
public ExceptionMiddleware(RequestDelegate next)
{
_next = next;
}
public async Task InvokeAsync(HttpContext httpContext)
{
try
{
await _next(httpContext);
}
catch (Exception ex)
{
Log.Error($"Something went wrong: {ex}");
await HandleExceptionAsync(httpContext, ex);
}
}
private Task HandleExceptionAsync(HttpContext context, Exception exception)
{
context.Response.ContentType = "application/json";
context.Response.StatusCode = (int)HttpStatusCode.InternalServerError;
return context.Response.WriteAsync(new ErrorDetails()
{
StatusCode = context.Response.StatusCode,
Message = "Internal Server Error from the custom middleware."
}.ToString());
}
}
public class ErrorDetails
{
public int StatusCode { get; set; }
public string Message { get; set; }
public override string ToString()
{
return Newtonsoft.Json.JsonConvert.SerializeObject(this);
}
}
}
16.2 注册中间件
在Startup.cs
中注册异常处理中间件。
public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
{
if (env.IsDevelopment())
{
app.UseDeveloperExceptionPage();
}
else
{
app.UseMiddleware<ExceptionMiddleware>();
}
app.UseRouting();
app.UseAuthentication();
app.UseAuthorization();
app.UseEndpoints(endpoints =>
{
endpoints.MapControllers();
});
}
17. 单元测试和集成测试
我们可以使用xUnit和Moq来编写单元测试和集成测试。
17.1 安装必要的NuGet包
dotnet add package xunit
dotnet add package Moq
dotnet add package Microsoft.AspNetCore.Mvc.Testing
17.2 编写单元测试
创建一个测试项目,并编写单元测试。
using GameServer.Controllers;
using GameServer.Models;
using GameServer.Services;
using Moq;
using System.Threading.Tasks;
using Xunit;
public class PlayerControllerTests
{
[Fact]
public async Task GetPlayer_ReturnsPlayer_WhenPlayerExists()
{
// Arrange
var mockService = new Mock<PlayerService>(null, null);
mockService.Setup(service => service.GetPlayerAsync(1)).ReturnsAsync(new Player { Id = 1, Name = "TestPlayer" });
var controller = new PlayerController(mockService.Object);
// Act
var result = await controller.GetPlayer(1);
// Assert
var okResult = Assert.IsType<OkObjectResult>(result);
var player = Assert.IsType<Player>(okResult.Value);
Assert.Equal(1, player.Id);
Assert.Equal("TestPlayer", player.Name);
}
// 其他测试...
}
18. 性能监控和分析
我们可以使用Application Insights或其他性能监控工具来监控和分析服务器性能。
18.1 安装Application Insights
dotnet add package Microsoft.ApplicationInsights.AspNetCore
18.2 配置Application Insights
在Startup.cs
中配置Application Insights。
public void ConfigureServices(IServiceCollection services)
{
services.AddControllers();
services.AddApplicationInsightsTelemetry(Configuration["ApplicationInsights:InstrumentationKey"]);
}
18.3 使用Application Insights
在代码中使用Application Insights记录自定义事件和指标。
using Microsoft.ApplicationInsights;
public class BattleService
{
private readonly TelemetryClient _telemetryClient;
public BattleService(TelemetryClient telemetryClient)
{
_telemetryClient = telemetryClient;
}
public async Task<BattleResult> ExecuteBattleAsync(int battleId)
{
_telemetryClient.TrackEvent("BattleStarted", new Dictionary<string, string> { { "BattleId", battleId.ToString() } });
// 其他代码...
_telemetryClient.TrackEvent("BattleEnded", new Dictionary<string, string> { { "BattleId", battleId.ToString() } });
return result;
}
}
19. 结论
通过添加身份验证和授权、错误处理和异常管理、单元测试和集成测试以及性能监控和分析,我们进一步增强了游戏战斗服务器的安全性、稳定性和可维护性。这些改进使得服务器能够更好地应对各种挑战,并且更容易进行调试和优化。
希望这些内容能帮助你更好地理解和实现一个全面的游戏战斗服务器。如果你有任何问题或需要进一步的帮助,请随时提问。