170 lines
6.1 KiB
C#

using System.Net.WebSockets;
using Microsoft.AspNetCore.Mvc;
using pacMan.DTOs;
using pacMan.Exceptions;
using pacMan.GameStuff;
using pacMan.GameStuff.Items;
using pacMan.Services;
using pacMan.Utils;
namespace pacMan.Controllers;
/// <summary>
/// Controls the game logic and handles requests related to games.
/// </summary>
[ApiController]
[Route("api/[controller]")]
public class GameController(ILogger<GameController> logger, IGameService webSocketService, IActionService actionService)
: GenericController(logger, webSocketService)
{
[HttpGet("[action]")]
public override Task Connect() => base.Connect();
/// <summary>
/// Retrieves all games from the WebSocketService.
/// </summary>
/// <returns>
/// An IEnumerable of Game objects representing all the games.
/// </returns>
[HttpGet("[action]")]
public IEnumerable<Game> All()
{
Logger.LogDebug("Returning all games");
return webSocketService.Games;
}
/// <summary>
/// Adds a player to a game.
/// </summary>
/// <param name="gameId">The unique identifier of the game.</param>
/// <param name="player">The player to be joined.</param>
/// <returns>An IActionResult representing the result of the operation.</returns>
[HttpPost("[action]/{gameId:guid}")]
public IActionResult Join(Guid gameId, [FromBody] Player player) // TODO what if player is in a game already?
{
Logger.LogDebug("Joining game {}", gameId);
try
{
webSocketService.JoinById(gameId, player);
return Ok("Game joined successfully");
}
catch (GameNotFoundException e)
{
return NotFound(e.Message);
}
catch (Exception e)
{
return BadRequest(e.Message);
}
}
/// <summary>
/// Checks if a game with the specified ID exists.
/// </summary>
/// <param name="gameId">The ID of the game to check.</param>
/// <returns>
/// Returns an <see cref="IActionResult" /> representing the result of the operation.
/// If a game with the specified ID exists, returns an <see cref="OkResult" />.
/// If a game with the specified ID doesn't exist, returns a <see cref="NotFoundResult" />.
/// </returns>
[HttpGet("[action]/{gameId:guid}")]
public IActionResult Exists(Guid gameId)
{
Logger.LogDebug("Checking if game {} exists", gameId);
return webSocketService.Games.Any(game => game.Id == gameId) ? Ok() : NotFound();
}
/// <summary>
/// Creates a new game and adds the specified player to it.
/// </summary>
/// <param name="data">The data required to create the game.</param>
/// <returns>
/// Returns an <see cref="IActionResult" /> representing the result of the operation.
/// If the game is successfully created, returns a <see cref="CreatedResult" /> with the game details and a location
/// URL.
/// If there is an error during creation, returns a <see cref="BadRequestObjectResult" /> with the error message.
/// </returns>
[HttpPost("[action]")]
public IActionResult Create([FromBody] CreateGameData data)
{
Logger.LogDebug("Creating game");
try
{
var game = webSocketService.CreateAndJoin(data.Player, data.Spawns);
return Created($"/{game.Id}", game);
}
catch (Exception e)
{
return BadRequest(e.Message);
}
}
protected override Task Echo()
{
actionService.WebSocket = WebSocket ?? throw new NullReferenceException("WebSocket is null");
return base.Echo();
}
/// <summary>
/// Runs the given WebSocketReceiveResult and byte array data to perform an action.
/// </summary>
/// <param name="result">The WebSocketReceiveResult object containing information about the received data.</param>
/// <param name="data">The byte array data received from the WebSocket.</param>
/// <returns>
/// Returns an ArraySegment object representing the action response in byte array format.
/// </returns>
protected override ArraySegment<byte> Run(WebSocketReceiveResult result, byte[] data)
{
var stringResult = data.GetString(result.Count);
Logger.LogInformation("Received: {}", stringResult);
var action = ActionMessage.FromJson(stringResult);
try
{
DoAction(action);
}
catch (Exception e)
{
Logger.LogError("{}", e.Message);
action = new ActionMessage { Action = GameAction.Error, Data = e.Message };
}
return action.ToArraySegment();
}
/// <summary>
/// Sends the specified data segment.
/// </summary>
/// <param name="segment">The data segment to send.</param>
protected override void Send(ArraySegment<byte> segment)
{
if (actionService.Game is not null)
actionService.SendToAll(segment);
else if (WebSocket is not null)
webSocketService.Send(WebSocket, segment);
}
protected override ArraySegment<byte>? Disconnect() =>
new ActionMessage { Action = GameAction.Disconnect, Data = actionService.Disconnect() }
.ToArraySegment();
protected override void SendDisconnectMessage(ArraySegment<byte> segment) => actionService.SendToAll(segment);
/// <summary>
/// Performs the specified action based on the given message.
/// </summary>
/// <param name="message">The action message containing the action to be performed.</param>
public void DoAction(ActionMessage message) =>
message.Data = message.Action switch
{
GameAction.RollDice => actionService.RollDice(),
GameAction.MoveCharacter => actionService.HandleMoveCharacter(message.Data),
GameAction.JoinGame => actionService.FindGame(message.Data),
GameAction.Ready => actionService.Ready(),
GameAction.NextPlayer => actionService.FindNextPlayer(),
GameAction.Disconnect => actionService.LeaveGame(),
_ => message.Data
};
}