mirror of
https://github.com/Ikatono/BuckshotMultiServerMono.git
synced 2025-10-29 04:45:31 -05:00
Add project files.
This commit is contained in:
25
BuckshotMultiServerMono.sln
Normal file
25
BuckshotMultiServerMono.sln
Normal file
@@ -0,0 +1,25 @@
|
|||||||
|
|
||||||
|
Microsoft Visual Studio Solution File, Format Version 12.00
|
||||||
|
# Visual Studio Version 17
|
||||||
|
VisualStudioVersion = 17.9.34607.119
|
||||||
|
MinimumVisualStudioVersion = 10.0.40219.1
|
||||||
|
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "BuckshotMultiServerMono", "BuckshotMultiServerMono\BuckshotMultiServerMono.csproj", "{7C4C2010-3AE6-4BF6-985B-C78B69E8A93D}"
|
||||||
|
EndProject
|
||||||
|
Global
|
||||||
|
GlobalSection(SolutionConfigurationPlatforms) = preSolution
|
||||||
|
Debug|Any CPU = Debug|Any CPU
|
||||||
|
Release|Any CPU = Release|Any CPU
|
||||||
|
EndGlobalSection
|
||||||
|
GlobalSection(ProjectConfigurationPlatforms) = postSolution
|
||||||
|
{7C4C2010-3AE6-4BF6-985B-C78B69E8A93D}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||||
|
{7C4C2010-3AE6-4BF6-985B-C78B69E8A93D}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||||
|
{7C4C2010-3AE6-4BF6-985B-C78B69E8A93D}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||||
|
{7C4C2010-3AE6-4BF6-985B-C78B69E8A93D}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||||
|
EndGlobalSection
|
||||||
|
GlobalSection(SolutionProperties) = preSolution
|
||||||
|
HideSolutionNode = FALSE
|
||||||
|
EndGlobalSection
|
||||||
|
GlobalSection(ExtensibilityGlobals) = postSolution
|
||||||
|
SolutionGuid = {F4AAFB04-1F40-4DA4-980A-AF7AC4C5D54E}
|
||||||
|
EndGlobalSection
|
||||||
|
EndGlobal
|
||||||
21
BuckshotMultiServerMono/BuckshotMultiServerMono.csproj
Normal file
21
BuckshotMultiServerMono/BuckshotMultiServerMono.csproj
Normal file
@@ -0,0 +1,21 @@
|
|||||||
|
<Project Sdk="Microsoft.NET.Sdk">
|
||||||
|
|
||||||
|
<PropertyGroup>
|
||||||
|
<OutputType>Exe</OutputType>
|
||||||
|
<TargetFramework>net8.0</TargetFramework>
|
||||||
|
<ImplicitUsings>enable</ImplicitUsings>
|
||||||
|
<Nullable>enable</Nullable>
|
||||||
|
<PublishAot>False</PublishAot>
|
||||||
|
<InvariantGlobalization>true</InvariantGlobalization>
|
||||||
|
</PropertyGroup>
|
||||||
|
|
||||||
|
<ItemGroup>
|
||||||
|
<PackageReference Include="Microsoft.AspNetCore.Mvc.Core" Version="2.2.5" />
|
||||||
|
<PackageReference Include="Swashbuckle.AspNetCore" Version="6.5.0" />
|
||||||
|
</ItemGroup>
|
||||||
|
|
||||||
|
<ItemGroup>
|
||||||
|
<Folder Include="Models\" />
|
||||||
|
</ItemGroup>
|
||||||
|
|
||||||
|
</Project>
|
||||||
124
BuckshotMultiServerMono/Controllers/GameController.cs
Normal file
124
BuckshotMultiServerMono/Controllers/GameController.cs
Normal file
@@ -0,0 +1,124 @@
|
|||||||
|
using BuckshotMultiServerMono.Objects;
|
||||||
|
using BuckshotMultiServerMono.Serializable;
|
||||||
|
using Microsoft.AspNetCore.Http;
|
||||||
|
using Microsoft.AspNetCore.Http.HttpResults;
|
||||||
|
using Microsoft.AspNetCore.Mvc;
|
||||||
|
using Microsoft.Extensions.Primitives;
|
||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Linq;
|
||||||
|
using System.Text;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
|
||||||
|
namespace BuckshotMultiServerMono.Controllers
|
||||||
|
{
|
||||||
|
[ApiController]
|
||||||
|
[Route("api/v1")]
|
||||||
|
public class GameController : ControllerBase
|
||||||
|
{
|
||||||
|
//int counter = 0;
|
||||||
|
//[HttpGet]
|
||||||
|
//[ProducesResponseType(StatusCodes.Status200OK)]
|
||||||
|
//[Route("/count")]
|
||||||
|
//public ActionResult<int> Count()
|
||||||
|
//{
|
||||||
|
// return new ActionResult<int>(counter++);
|
||||||
|
//}
|
||||||
|
[HttpPost]
|
||||||
|
[Route("/create")]
|
||||||
|
public IActionResult CreateGame()
|
||||||
|
{
|
||||||
|
var game = GameContainer.Get().CreateGame();
|
||||||
|
var result = CreatedAtAction(nameof(CreateGame), null);
|
||||||
|
((IList<string?>)Response.Headers.Location).Add(game.Id);
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
//TODO move name to payload?
|
||||||
|
[HttpPut]
|
||||||
|
[ProducesResponseType(StatusCodes.Status201Created)]
|
||||||
|
[ProducesResponseType(StatusCodes.Status204NoContent)]
|
||||||
|
[ProducesResponseType<ErrorMessage>(StatusCodes.Status400BadRequest)]
|
||||||
|
[ProducesResponseType(StatusCodes.Status404NotFound)]
|
||||||
|
[ProducesResponseType(StatusCodes.Status409Conflict)]
|
||||||
|
[Route("/game/{id}/player/join/")]
|
||||||
|
public IActionResult JoinGame(string id, [FromBody] NewPlayer newPlayer)
|
||||||
|
{
|
||||||
|
var name = newPlayer.Name;
|
||||||
|
if (string.IsNullOrEmpty(name))
|
||||||
|
return new StatusCodeResult(StatusCodes.Status400BadRequest);
|
||||||
|
if (!GameContainer.ValidateName(name))
|
||||||
|
{
|
||||||
|
var error = new ErrorMessage();
|
||||||
|
this.Response.StatusCode = StatusCodes.Status400BadRequest;
|
||||||
|
return new JsonResult(error);
|
||||||
|
}
|
||||||
|
var game = GameContainer.Get().FindGame(id);
|
||||||
|
if (game is null)
|
||||||
|
return new StatusCodeResult(StatusCodes.Status404NotFound);
|
||||||
|
lock (game)
|
||||||
|
{
|
||||||
|
//Game.AddPlayer already avoids creating duplicate player,
|
||||||
|
//but won't tell you whether the player was a duplicate or
|
||||||
|
//the game was full
|
||||||
|
if (game.Player1 is Player p1)
|
||||||
|
{
|
||||||
|
if (p1.Name == name)
|
||||||
|
return new StatusCodeResult(StatusCodes.Status204NoContent);
|
||||||
|
}
|
||||||
|
if (game.Player2 is Player p2)
|
||||||
|
{
|
||||||
|
if (p2.Name == name)
|
||||||
|
return new StatusCodeResult(StatusCodes.Status204NoContent);
|
||||||
|
}
|
||||||
|
return game.AddPlayer(name) ?
|
||||||
|
new StatusCodeResult(StatusCodes.Status201Created) :
|
||||||
|
new StatusCodeResult(StatusCodes.Status409Conflict);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
|
[Route("/game/{id}/player/ws")]
|
||||||
|
public async Task Connect(string id, [FromQuery(Name = "PlayerName")] string? name)
|
||||||
|
{
|
||||||
|
if (HttpContext.WebSockets.IsWebSocketRequest)
|
||||||
|
{
|
||||||
|
if (name is null)
|
||||||
|
{
|
||||||
|
HttpContext.Response.StatusCode = StatusCodes.Status400BadRequest;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
var game = GameContainer.Get().FindGame(id);
|
||||||
|
if (game is null)
|
||||||
|
{
|
||||||
|
HttpContext.Response.StatusCode = StatusCodes.Status404NotFound;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
var player = game.FindPlayer(name);
|
||||||
|
if (player is null)
|
||||||
|
{
|
||||||
|
HttpContext.Response.StatusCode = StatusCodes.Status404NotFound;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
using var socket = await HttpContext.WebSockets.AcceptWebSocketAsync();
|
||||||
|
player.AddConnection(socket);
|
||||||
|
using ManualResetEventSlim mre = new();
|
||||||
|
player.ConnectionClosedEvent += (_, _) => mre.Set();
|
||||||
|
//prevent race condition where connection closes before event is registered
|
||||||
|
if (!player.Connected())
|
||||||
|
{
|
||||||
|
//make sure connection is cleaned up properly
|
||||||
|
player.Disconnect();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
//holds pipeline open for the duration of the connection
|
||||||
|
mre.Wait();
|
||||||
|
player.Disconnect();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
HttpContext.Response.StatusCode = StatusCodes.Status400BadRequest;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
55
BuckshotMultiServerMono/Objects/Game.cs
Normal file
55
BuckshotMultiServerMono/Objects/Game.cs
Normal file
@@ -0,0 +1,55 @@
|
|||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Linq;
|
||||||
|
using System.Text;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
|
||||||
|
namespace BuckshotMultiServerMono.Objects
|
||||||
|
{
|
||||||
|
public class Game
|
||||||
|
{
|
||||||
|
public string Id;
|
||||||
|
public Player? Player1;
|
||||||
|
public Player? Player2;
|
||||||
|
public GameState State;
|
||||||
|
|
||||||
|
public Game(string id)
|
||||||
|
{
|
||||||
|
Id = id;
|
||||||
|
State = new GameState();
|
||||||
|
}
|
||||||
|
//return true if player added or already exists, false if name doesn't
|
||||||
|
//exist and players are full
|
||||||
|
//not thread-safe, must be locked externally
|
||||||
|
public bool AddPlayer(string name)
|
||||||
|
{
|
||||||
|
if (Player1 is Player p1)
|
||||||
|
{
|
||||||
|
if (p1.Name == name)
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
if (Player2 is Player p2)
|
||||||
|
{
|
||||||
|
if (p2.Name == name)
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
if (Player1 is null)
|
||||||
|
{
|
||||||
|
Player1 = new Player(name);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
else if (Player2 is null)
|
||||||
|
{
|
||||||
|
Player2 = new Player(name);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
internal Player? FindPlayer(string name)
|
||||||
|
{
|
||||||
|
throw new NotImplementedException();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
63
BuckshotMultiServerMono/Objects/GameContainer.cs
Normal file
63
BuckshotMultiServerMono/Objects/GameContainer.cs
Normal file
@@ -0,0 +1,63 @@
|
|||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Linq;
|
||||||
|
using System.Text;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
|
||||||
|
namespace BuckshotMultiServerMono.Objects
|
||||||
|
{
|
||||||
|
public class GameContainer
|
||||||
|
{
|
||||||
|
private readonly List<Game> Games = [];
|
||||||
|
public Game CreateGame()
|
||||||
|
{
|
||||||
|
while (true)
|
||||||
|
{
|
||||||
|
var id = GenerateId();
|
||||||
|
lock(Games)
|
||||||
|
{
|
||||||
|
if (!Games.Any(g => g.Id == id))
|
||||||
|
{
|
||||||
|
Game game = new(id);
|
||||||
|
Games.Add(game);
|
||||||
|
return game;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
public bool EndGame(string id)
|
||||||
|
{
|
||||||
|
lock (Games)
|
||||||
|
{
|
||||||
|
Game? game = Games.FirstOrDefault(g => g.Id == id);
|
||||||
|
if (game is Game game_notnull)
|
||||||
|
{
|
||||||
|
Games.Remove(game_notnull);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
//generates an id which must be valid, but not necessarily unique
|
||||||
|
protected static string GenerateId()
|
||||||
|
{
|
||||||
|
throw new NotImplementedException();
|
||||||
|
}
|
||||||
|
public static bool ValidateName(string name)
|
||||||
|
{
|
||||||
|
throw new NotImplementedException();
|
||||||
|
}
|
||||||
|
public static GameContainer Get()
|
||||||
|
{
|
||||||
|
throw new NotImplementedException();
|
||||||
|
}
|
||||||
|
public Game? FindGame(string id)
|
||||||
|
{
|
||||||
|
lock (Games)
|
||||||
|
{
|
||||||
|
return Games.FirstOrDefault(g => g.Id == id);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
45
BuckshotMultiServerMono/Objects/GameState.cs
Normal file
45
BuckshotMultiServerMono/Objects/GameState.cs
Normal file
@@ -0,0 +1,45 @@
|
|||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Linq;
|
||||||
|
using System.Text;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
|
||||||
|
namespace BuckshotMultiServerMono.Objects
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Holds the state of the game, including player state
|
||||||
|
/// </summary>
|
||||||
|
public class GameState
|
||||||
|
{
|
||||||
|
public int Player1Score;
|
||||||
|
public int Player2Score;
|
||||||
|
public int RoundNumber;
|
||||||
|
public int Player1Health;
|
||||||
|
public int Player2Health;
|
||||||
|
public HandcuffState CurrentHandcuffState = HandcuffState.None;
|
||||||
|
public bool SawUsed = false;
|
||||||
|
public PlayerInventory Player1Inventory = new();
|
||||||
|
public PlayerInventory Player2Inventory = new();
|
||||||
|
public bool ActivePlayerIs1 = true;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// called after shooting (other than shooting self with blank)
|
||||||
|
/// automatically handles handcuffs
|
||||||
|
/// active player might not change, do not make any assumptions
|
||||||
|
/// about the state after callaing this
|
||||||
|
/// </summary>
|
||||||
|
public void NextTurn()
|
||||||
|
{
|
||||||
|
if (CurrentHandcuffState == HandcuffState.Active)
|
||||||
|
{
|
||||||
|
CurrentHandcuffState = HandcuffState.Inactive;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
if (CurrentHandcuffState == HandcuffState.Inactive)
|
||||||
|
CurrentHandcuffState = HandcuffState.None;
|
||||||
|
ActivePlayerIs1 = !ActivePlayerIs1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
20
BuckshotMultiServerMono/Objects/HandcuffState.cs
Normal file
20
BuckshotMultiServerMono/Objects/HandcuffState.cs
Normal file
@@ -0,0 +1,20 @@
|
|||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Linq;
|
||||||
|
using System.Text;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
|
||||||
|
namespace BuckshotMultiServerMono.Objects
|
||||||
|
{
|
||||||
|
public enum HandcuffState
|
||||||
|
{
|
||||||
|
//handcuffs have not been used this turn
|
||||||
|
None,
|
||||||
|
//handcuffs have been used, and the active player
|
||||||
|
//will go again
|
||||||
|
Active,
|
||||||
|
//handcuffs have been used and the active player went again,
|
||||||
|
//handcuffs cannot be used again this turn
|
||||||
|
Inactive,
|
||||||
|
}
|
||||||
|
}
|
||||||
34
BuckshotMultiServerMono/Objects/ItemType.cs
Normal file
34
BuckshotMultiServerMono/Objects/ItemType.cs
Normal file
@@ -0,0 +1,34 @@
|
|||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Linq;
|
||||||
|
using System.Security.Cryptography.X509Certificates;
|
||||||
|
using System.Text;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
|
||||||
|
namespace BuckshotMultiServerMono.Objects
|
||||||
|
{
|
||||||
|
public enum ItemType
|
||||||
|
{
|
||||||
|
None,
|
||||||
|
Handcuff,
|
||||||
|
Glass,
|
||||||
|
Beer,
|
||||||
|
Saw,
|
||||||
|
Cigarette,
|
||||||
|
}
|
||||||
|
public static class ItemTypeGetter
|
||||||
|
{
|
||||||
|
public static ItemType Random()
|
||||||
|
{
|
||||||
|
ItemType[] choices = {ItemType.Handcuff, ItemType.Glass, ItemType.Beer,
|
||||||
|
ItemType.Saw, ItemType.Cigarette};
|
||||||
|
return choices[System.Random.Shared.Next(choices.Length)];
|
||||||
|
}
|
||||||
|
public static ItemType NotCig()
|
||||||
|
{
|
||||||
|
ItemType[] choices = {ItemType.Handcuff, ItemType.Glass, ItemType.Beer,
|
||||||
|
ItemType.Saw};
|
||||||
|
return choices[System.Random.Shared.Next(choices.Length)];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
83
BuckshotMultiServerMono/Objects/Player.cs
Normal file
83
BuckshotMultiServerMono/Objects/Player.cs
Normal file
@@ -0,0 +1,83 @@
|
|||||||
|
using Microsoft.AspNetCore.Razor.TagHelpers;
|
||||||
|
using System;
|
||||||
|
using System.Collections.Concurrent;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Linq;
|
||||||
|
using System.Net.WebSockets;
|
||||||
|
using System.Text;
|
||||||
|
using System.Text.Json;
|
||||||
|
using System.Text.Json.Serialization;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
|
||||||
|
namespace BuckshotMultiServerMono.Objects
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Provides an interface to communicate with players that persists past
|
||||||
|
/// individual websocket connections. This should
|
||||||
|
/// </summary>
|
||||||
|
public partial class Player
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// A message to be sent to the client
|
||||||
|
/// </summary>
|
||||||
|
public class PlayerTransmitMessage
|
||||||
|
{
|
||||||
|
public ReadOnlyMemory<byte> GetMessageData()
|
||||||
|
{
|
||||||
|
throw new NotImplementedException();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
/// <summary>
|
||||||
|
/// A message received from the client
|
||||||
|
/// </summary>
|
||||||
|
public class PlayerReceiveMessage
|
||||||
|
{
|
||||||
|
public static PlayerReceiveMessage? Parse(ReadOnlySpan<byte> data)
|
||||||
|
{
|
||||||
|
return JsonSerializer.Deserialize<PlayerReceiveMessage>(data);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
/// <summary>
|
||||||
|
/// The player's name and identifier
|
||||||
|
/// </summary>
|
||||||
|
public readonly string Name;
|
||||||
|
/// <summary>
|
||||||
|
/// Wraps a websocket connection to the client
|
||||||
|
/// </summary>
|
||||||
|
private PlayerConnection? Connection;
|
||||||
|
public event EventHandler<PlayerReceiveMessage>? MessageReceivedEvent;
|
||||||
|
public event EventHandler<PlayerTransmitMessage>? MessageTransmittedEvent;
|
||||||
|
public event EventHandler? ConnectionOpenedEvent;
|
||||||
|
public event EventHandler? ConnectionClosedEvent;
|
||||||
|
public Player(string name)
|
||||||
|
{
|
||||||
|
Name = name;
|
||||||
|
}
|
||||||
|
/// <summary>
|
||||||
|
/// attach a new websocket
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="socket"></param>
|
||||||
|
public void AddConnection(WebSocket socket)
|
||||||
|
{
|
||||||
|
if (Connection is not null)
|
||||||
|
{
|
||||||
|
Connection.Close();
|
||||||
|
Connection = null;
|
||||||
|
}
|
||||||
|
Connection = new PlayerConnection(socket);
|
||||||
|
Connection.ConnectionCloseEvent += (_, args) =>
|
||||||
|
this.ConnectionClosedEvent?.Invoke(this, args);
|
||||||
|
ConnectionOpenedEvent?.Invoke(this, EventArgs.Empty);
|
||||||
|
}
|
||||||
|
public bool Connected()
|
||||||
|
{
|
||||||
|
var state = Connection?.Socket.State;
|
||||||
|
return (state == WebSocketState.Open || state == WebSocketState.Connecting);
|
||||||
|
}
|
||||||
|
|
||||||
|
internal void Disconnect()
|
||||||
|
{
|
||||||
|
throw new NotImplementedException();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
110
BuckshotMultiServerMono/Objects/PlayerConnection.cs
Normal file
110
BuckshotMultiServerMono/Objects/PlayerConnection.cs
Normal file
@@ -0,0 +1,110 @@
|
|||||||
|
using System;
|
||||||
|
using System.Collections.Concurrent;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Linq;
|
||||||
|
using System.Net.WebSockets;
|
||||||
|
using System.Text;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
|
||||||
|
namespace BuckshotMultiServerMono.Objects
|
||||||
|
{
|
||||||
|
public partial class Player
|
||||||
|
{
|
||||||
|
private class PlayerConnection : IDisposable
|
||||||
|
{
|
||||||
|
public static readonly TimeSpan EndThreadTimeout = TimeSpan.FromSeconds(5);
|
||||||
|
|
||||||
|
internal readonly WebSocket Socket;
|
||||||
|
internal readonly BlockingCollection<PlayerTransmitMessage> Queue = [];
|
||||||
|
internal readonly Thread TransmitThread;
|
||||||
|
internal readonly Thread ReceiveThread;
|
||||||
|
internal readonly CancellationTokenSource CancelTokenSource = new();
|
||||||
|
private bool disposedValue;
|
||||||
|
|
||||||
|
public event EventHandler<PlayerTransmitMessage>? TransmitEvent;
|
||||||
|
public event EventHandler<PlayerReceiveMessage>? ReceiveEvent;
|
||||||
|
public event EventHandler? ConnectionCloseEvent;
|
||||||
|
public PlayerConnection(WebSocket socket)
|
||||||
|
{
|
||||||
|
Socket = socket;
|
||||||
|
TransmitThread = new Thread(TransmitFunc);
|
||||||
|
TransmitThread.Start();
|
||||||
|
ReceiveThread = new Thread(ReceiveFunc);
|
||||||
|
ReceiveThread.Start();
|
||||||
|
}
|
||||||
|
private void TransmitFunc()
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
while (!CancelTokenSource.IsCancellationRequested)
|
||||||
|
{
|
||||||
|
var message = Queue.Take(CancelTokenSource.Token);
|
||||||
|
if (message is not null)
|
||||||
|
{
|
||||||
|
Socket.SendAsync(message.GetMessageData(), WebSocketMessageType.Text, true, CancelTokenSource.Token)
|
||||||
|
.AsTask().Wait();
|
||||||
|
TransmitEvent?.Invoke(this, message);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
catch (OperationCanceledException)
|
||||||
|
{
|
||||||
|
//this is expected, no need to act on it
|
||||||
|
}
|
||||||
|
}
|
||||||
|
private void ReceiveFunc()
|
||||||
|
{
|
||||||
|
var buffer = new Memory<byte>();
|
||||||
|
while (!CancelTokenSource.IsCancellationRequested)
|
||||||
|
{
|
||||||
|
List<byte> message = [];
|
||||||
|
ValueWebSocketReceiveResult res;
|
||||||
|
do
|
||||||
|
{
|
||||||
|
var task = Socket.ReceiveAsync(buffer, CancelTokenSource.Token).AsTask();
|
||||||
|
task.Wait();
|
||||||
|
if (task.Exception?.InnerException is OperationCanceledException)
|
||||||
|
return;
|
||||||
|
res = task.Result;
|
||||||
|
message.AddRange(buffer.Span);
|
||||||
|
if (res.MessageType == WebSocketMessageType.Close)
|
||||||
|
{
|
||||||
|
Close();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
} while (!res.EndOfMessage);
|
||||||
|
var sendMessage = PlayerReceiveMessage.Parse(message.ToArray());
|
||||||
|
if (sendMessage is not null)
|
||||||
|
ReceiveEvent?.Invoke(this, sendMessage);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
public void EnqueueMessage(PlayerTransmitMessage message)
|
||||||
|
{
|
||||||
|
Queue.Add(message);
|
||||||
|
}
|
||||||
|
public void Close()
|
||||||
|
{
|
||||||
|
CancelTokenSource.Cancel();
|
||||||
|
ConnectionCloseEvent?.Invoke(this, EventArgs.Empty);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected virtual void Dispose(bool disposing)
|
||||||
|
{
|
||||||
|
if (!disposedValue)
|
||||||
|
{
|
||||||
|
if (disposing)
|
||||||
|
{
|
||||||
|
Close();
|
||||||
|
}
|
||||||
|
disposedValue = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void Dispose()
|
||||||
|
{
|
||||||
|
Dispose(disposing: true);
|
||||||
|
GC.SuppressFinalize(this);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
13
BuckshotMultiServerMono/Objects/PlayerInventory.cs
Normal file
13
BuckshotMultiServerMono/Objects/PlayerInventory.cs
Normal file
@@ -0,0 +1,13 @@
|
|||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Linq;
|
||||||
|
using System.Text;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
|
||||||
|
namespace BuckshotMultiServerMono.Objects
|
||||||
|
{
|
||||||
|
public class PlayerInventory
|
||||||
|
{
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
34
BuckshotMultiServerMono/Program.cs
Normal file
34
BuckshotMultiServerMono/Program.cs
Normal file
@@ -0,0 +1,34 @@
|
|||||||
|
using Microsoft.AspNetCore.Builder;
|
||||||
|
using Microsoft.AspNetCore.Mvc;
|
||||||
|
using Microsoft.Extensions.DependencyInjection;
|
||||||
|
using Microsoft.OpenApi.Models;
|
||||||
|
|
||||||
|
// See https://aka.ms/new-console-template for more information
|
||||||
|
//Console.WriteLine("Hello, World!");
|
||||||
|
|
||||||
|
var builder = WebApplication.CreateBuilder(args);
|
||||||
|
|
||||||
|
builder.Services.AddEndpointsApiExplorer();
|
||||||
|
builder.Services.AddSwaggerGen(c =>
|
||||||
|
{
|
||||||
|
c.SwaggerDoc("v1", new OpenApiInfo { Title = "GameController API" });
|
||||||
|
});
|
||||||
|
|
||||||
|
builder.Services.AddControllers();
|
||||||
|
|
||||||
|
var app = builder.Build();
|
||||||
|
app.UseWebSockets();
|
||||||
|
app.UseSwagger();
|
||||||
|
app.UseSwaggerUI(c =>
|
||||||
|
{
|
||||||
|
c.SwaggerEndpoint("/swagger/v1/swagger.json", "GameController API V1");
|
||||||
|
});
|
||||||
|
|
||||||
|
//app.MapPost("/api/v1/newgame", () =>
|
||||||
|
//{
|
||||||
|
|
||||||
|
//});
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
app.Run();
|
||||||
13
BuckshotMultiServerMono/Serializable/ErrorMessage.cs
Normal file
13
BuckshotMultiServerMono/Serializable/ErrorMessage.cs
Normal file
@@ -0,0 +1,13 @@
|
|||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Linq;
|
||||||
|
using System.Text;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
|
||||||
|
namespace BuckshotMultiServerMono.Serializable
|
||||||
|
{
|
||||||
|
public class ErrorMessage
|
||||||
|
{
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
13
BuckshotMultiServerMono/Serializable/NewPlayer.cs
Normal file
13
BuckshotMultiServerMono/Serializable/NewPlayer.cs
Normal file
@@ -0,0 +1,13 @@
|
|||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Linq;
|
||||||
|
using System.Text;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
|
||||||
|
namespace BuckshotMultiServerMono.Serializable
|
||||||
|
{
|
||||||
|
public class NewPlayer
|
||||||
|
{
|
||||||
|
public string? Name { get; set; }
|
||||||
|
}
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user