- server.zip
- Local.testsettings
- packages
- Fleck.0.8.2.1
- Fleck.0.8.2.1.nupkg
- lib
- net40
- Fleck.dll
- HttpClient.0.6.0
- HttpClient.0.6.0.nupkg
- lib
- 40
- JsonValue.0.6.0
- JsonValue.0.6.0.nupkg
- lib
- 40
- Newtonsoft.Json.4.0.5
- repositories.config
- SpaceShooterServer.sln
- SpaceShooterServer.vsmdi
- SpaceShooterServer
- Test
- bin
- Debug
- Release
- Fleck.dll
- Microsoft.Json.dll
- Microsoft.Json.xml
- Microsoft.Server.Common.dll
- SpaceShooterServer.exe
- Test.dll
- Local.testsettings
- obj
- Debug
- TempPE
- Release
- TempPE
- packages.config
- Properties
- SpaceShooterServer.vsmdi
- Test.csproj
- Test.csproj.user
- TraceAndTestImpact.testsettings
- UnitTests.cs
- TraceAndTestImpact.testsettings
- client.zip
|
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Fleck;
using System.Json;
using SpaceShooterServer.Structures;
using System.Net;
using System.Threading;
namespace SpaceShooterServer.Game
{
/// <summary>
/// The basic class that contains all matches and players
/// </summary>
public class MatchCollection : MatchHelpers
{
#region ctor
public MatchCollection()
{
bannedConnections = new List<string>();
Lobby = new List<IWebSocketConnection>();
Matches = new List<Match>();
DebugLog = new Queue<string>(99);
}
#endregion
#region Members
List<string> bannedConnections;
#endregion
#region Properties
/// <summary>
/// Gets the array with all players that are connected to the server
/// </summary>
public List<IWebSocketConnection> Lobby { get; private set; }
/// <summary>
/// Gets a list of currently available matches
/// </summary>
public List<Match> Matches { get; private set; }
/// <summary>
/// Gets the list of debug log entries
/// </summary>
public Queue<string> DebugLog { get; private set; }
/// <summary>
/// Gets the number of all matches that have been created since the server is running
/// </summary>
public int MatchCount { get; private set; }
/// <summary>
/// Gets the currently banned IP addresses
/// </summary>
public string[] BannedConnections { get { return bannedConnections.ToArray(); } }
#endregion
#region Methods
private bool IsInList(IWebSocketConnection socket)
{
return Lobby.Contains(socket);
}
private bool IsInLobby(IWebSocketConnection socket)
{
return IsInList(socket) && !IsInMatch(socket);
}
private bool IsInMatch(IWebSocketConnection socket)
{
return Matches.Where(match => match.Ships.Any(ship => ship.Connection == socket)).Any();
}
private Match FindMatch(IWebSocketConnection socket)
{
return Matches.Where(match => match.Ships.Any(ship => ship.Connection == socket)).FirstOrDefault() ?? new Match();
}
/// <summary>
/// Bans a specific connection from the server
/// </summary>
/// <param name="ip">The IP address of the user to ban</param>
public void Ban(string ip)
{
if (!bannedConnections.Contains(ip))
{
bannedConnections.Add(ip);
for (int i = 0; i < Lobby.Count; i++)
{
var con = Lobby[i];
con.Close();
break;
}
}
}
/// <summary>
/// Removes a previously committed ban to a user
/// </summary>
/// <param name="ip">The IP address of the user to release</param>
public void Unban(string ip)
{
if(bannedConnections.Contains(ip))
bannedConnections.Remove(ip);
}
/// <summary>
/// Lists all existing (and running) matches
/// </summary>
/// <returns>A JSON Object that contains the list of matches</returns>
public JsonObject ListMatches()
{
var j = new JsonObject();
var ja = new JsonArray();
for (int i = Matches.Count - 1; i >= 0; i--)
{
if (Matches[i].Sockets.Count == 0)
Matches.RemoveAt(i);
else
ja.Add(Matches[i].ToJson());
}
j["list"] = ja;
j["cmd"] = "list";
return j;
}
/// <summary>
/// Appends a message to the debug log (max. 99 entries)
/// </summary>
/// <param name="msg">The message to append</param>
/// <param name="args">The arguments to display in the message using {0}, ...</param>
void AddToDebugLog(string msg, params object[] args)
{
if (DebugLog.Count == 99)
DebugLog.Dequeue();
DebugLog.Enqueue(string.Format(msg, args));
}
/// <summary>
/// Adds a player to the server
/// </summary>
/// <param name="socket">The player's connection</param>
public void AddPlayer(IWebSocketConnection socket)
{
if (bannedConnections.Contains(socket.ConnectionInfo.ClientIpAddress))
{
socket.Send(ReportError("You are banned from the server."));
new Timer(o => { socket.Close(); }, null, 100, Timeout.Infinite);
return;
}
if (IsInList(socket))
RemovePlayer(socket);
Lobby.Add(socket);
AddToDebugLog("Connection added [ Users : {0} ]", Lobby.Count);
}
/// <summary>
/// Removes a player from the server
/// </summary>
/// <param name="socket">The player's connection</param>
public void RemovePlayer(IWebSocketConnection socket)
{
if(IsInMatch(socket))
FindMatch(socket).RemovePlayer(socket);
Lobby.Remove(socket);
AddToDebugLog("Connection removed [ Users : {0} ]", Lobby.Count);
}
/// <summary>
/// Information update from a player
/// </summary>
/// <param name="socket">The player's connection</param>
/// <param name="message">The JSON string with the update information</param>
public void UpdatePlayer(IWebSocketConnection socket, string message)
{
var j = JsonObject.Parse(message);
if (j.ContainsKey("cmd"))
{
switch (ParseString(j["cmd"]))
{
//Sent update (new keyboard state) of a game
case "keys":
#if DEBUG
AddToDebugLog("Keyboard update received.");
#endif
FindMatch(socket).ReceiveKeyboard(socket, j);
break;
//Send a chat message
case "chat":
#if DEBUG
AddToDebugLog("Chat was sent.");
#endif
FindMatch(socket).Chat(socket, ParseString(j["msg"]));
break;
//Leave a game
case "leave":
#if DEBUG
AddToDebugLog("A player is leaving the match.");
#endif
FindMatch(socket).RemovePlayer(socket);
break;
//Sent update (new color / player name) of a game
case "update":
#if DEBUG
AddToDebugLog("Update received.");
#endif
FindMatch(socket).UpdatePlayer(socket, j);
break;
//Request the list of available games
case "list":
#if DEBUG
AddToDebugLog("The match list is requested.");
#endif
socket.Send(ListMatches());
break;
//Join an existing game
case "join":
#if DEBUG
AddToDebugLog("A player joins a match.");
#endif
socket.Send(JoinMatch(j, socket));
break;
//Host a game
case "host":
#if DEBUG
AddToDebugLog("A match is about to get hosted.");
#endif
socket.Send(AddMatch(j, socket));
break;
//Everything in order to control the console
case "console":
if (socket.ConnectionInfo.ClientIpAddress.Equals(IPAddress.Loopback.ToString()))
{
if (Commands.Instance.Invoke(ParseString(j["msg"])))
socket.Send(ReportConsole(Commands.Instance.Last.FlushOutput()));
else
socket.Send(ReportConsole("Command not found or wrong arguments."));
}
else
socket.Send(ReportConsole("Not enough privileges for sending commands."));
break;
}
}
}
private JsonObject AddPlayer(JsonValue j, Match match, IWebSocketConnection socket)
{
var name = "Player";
var color = new Colors();
if (j.ContainsKey("player"))
name = ParseString(j["player"]);
if (j.ContainsKey("primaryColor"))
color.Primary = ParseString(j["primaryColor"]);
if (j.ContainsKey("secondaryColor"))
color.Secondary = ParseString(j["secondaryColor"]);
return match.AddPlayer(socket, name, color);
}
/// <summary>
/// A player joins a certain game
/// </summary>
/// <param name="j">The JSON data of the game to join</param>
/// <param name="socket">The player's connection</param>
/// <returns>A JSON Object with more information about the joining process</returns>
public JsonObject JoinMatch(JsonValue j, IWebSocketConnection socket)
{
if (!j.ContainsKey("id") || !j.ContainsKey("password"))
return ReportError();
var id = ParseInt(j["id"], 0);
var matches = Matches.Where(m => m.Id == id);
if (!matches.Any())
return ReportError("The game you specified was not found.");
var match = matches.First();
if (!match.Password.Equals(ParseString(j["password"])))
return ReportError("Wrong password. The game is password protected.");
return AddPlayer(j, match, socket);
}
/// <summary>
/// Adds a match to the list of available matches
/// </summary>
/// <param name="j">The JSON data of the game to add</param>
/// <param name="socket">The creator's connection</param>
/// <returns>A JSON object with more information about the creation process</returns>
public JsonObject AddMatch(JsonValue j, IWebSocketConnection socket)
{
if (!j.ContainsKey("height") || !j.ContainsKey("width") || !j.ContainsKey("name") ||
!j.ContainsKey("password") || !j.ContainsKey("friendly") || !j.ContainsKey("maxplayers") ||
!j.ContainsKey("maxbots") || !j.ContainsKey("negative") || !j.ContainsKey("upgrades"))
return ReportError();
var match = new Match(Matches.Count + 1);
match.Height = ParseInt(j["height"], 768);
match.Width = ParseInt(j["width"], 1024);
match.Name = ParseString(j["name"]);
match.Password = ParseString(j["password"]);
match.FriendlyFire = ParseBool(j["friendly"]);
match.Maxplayers = ParseInt(j["maxplayers"], 64);
match.Maxbots = ParseInt(j["maxbots"], 0);
match.NegativePoints = ParseBool(j["negative"]);
match.AllowUpgrades = ParseBool(j["upgrades"]);
if (string.IsNullOrEmpty(match.Name))
return ReportError("You have to specify a proper name for the match.");
MatchCount++;
Matches.Add(match);
return AddPlayer(j, match, socket);
}
#endregion
}
}
|
By viewing downloads associated with this article you agree to the Terms of Service and the article's licence.
If a file you wish to view isn't highlighted, and is a text file (not binary), please
let us know and we'll add colourisation support for it.
Florian lives in Munich, Germany. He started his programming career with Perl. After programming C/C++ for some years he discovered his favorite programming language C#. He did work at Siemens as a programmer until he decided to study Physics.
During his studies he worked as an IT consultant for various companies. After graduating with a PhD in theoretical particle Physics he is working as a senior technical consultant in the field of home automation and IoT.
Florian has been giving lectures in C#, HTML5 with CSS3 and JavaScript, software design, and other topics. He is regularly giving talks at user groups, conferences, and companies. He is actively contributing to open-source projects. Florian is the maintainer of AngleSharp, a completely managed browser engine.