* Re-wrote the Server code again. Now much more reliable and passes all connections to the ConnectionManager properly.

* StandardCharacter re-wrote to support the new Server code.  Also added event method support for various states.
* ConnectionManager re-wrote to support the new server.
* Work on Log message importance started
* INetworked.Connect now requires a Socket as its parameter.
* StandardGame no longer has Properties for MaxConnections and MaxQueuedConnections.  This is handle via StandardGame.Start() parameters.
* CommandLogin command added.  Initial check-in and not fully implemented.
This commit is contained in:
Scionwest_cp 2012-03-03 11:17:01 -08:00
parent 5b141c62ed
commit 27f7e31772
12 changed files with 314 additions and 163 deletions

View file

@ -2,6 +2,7 @@
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Net.Sockets;
namespace MudEngine.Core.Interfaces
{
@ -24,6 +25,6 @@ namespace MudEngine.Core.Interfaces
/// <summary>
/// Method for connecting a object to the server.
/// </summary>
void Connect();
void Connect(Socket connection);
}
}

View file

@ -21,7 +21,7 @@ namespace MudEngine.Core.Interfaces
/// <param name="path"></param>
Boolean Save(String filename);
Boolean Save(String filename, Boolean ignoreignoreFileWrite);
Boolean Save(String filename, Boolean ignoreFileWrite);
/// <summary>
/// Load method for retrieving saved data from file.

View file

@ -11,6 +11,16 @@ namespace MudEngine.Core
/// </summary>
public static class Logger
{
public enum Importance
{
Critical = 0,
Error = 1,
Warning = 2,
Information = 3,
Chat = 4,
All = 5
}
/// <summary>
/// The Log Filename for the engine log.
/// </summary>
@ -42,6 +52,10 @@ namespace MudEngine.Core
_Messages.Clear();
}
public static void WriteLine(String message, Importance importance)
{
}
/// <summary>
/// Writes a single line to the engine log file.
/// </summary>

View file

@ -7,6 +7,7 @@ using System.Net;
using System.Net.Sockets;
using System.Threading;
using System.Xml.Linq;
using System.Text.RegularExpressions;
using MudEngine.GameScripts;
using MudEngine.Core.Interfaces;
@ -25,6 +26,8 @@ namespace MudEngine.Game.Characters
/// </summary>
public StandardGame Game { get; private set; }
public string Filename { get; set; }
/// <summary>
/// Gets what this Characters role on the server is.
/// </summary>
@ -43,12 +46,15 @@ namespace MudEngine.Game.Characters
/// </summary>
public Boolean Immovable { get; set; }
public Boolean Enabled { get; set; }
//TODO: Add current location to characters
//public IEnvironment CurrentLocation
protected CommandSystem Commands { get; private set; }
public StandardCharacter(StandardGame game, String name, String description) : base(game, name, description)
public StandardCharacter(StandardGame game, String name, String description)
: base(game, name, description)
{
this.Game = game;
@ -59,7 +65,8 @@ namespace MudEngine.Game.Characters
this.OnConnectEvent += new OnConnectHandler(OnConnect);
}
public StandardCharacter(StandardGame game, String name, String description, Socket connection) : this(game, name, description)
public StandardCharacter(StandardGame game, String name, String description, Socket connection)
: this(game, name, description)
{
this._Connection = connection;
@ -82,23 +89,93 @@ namespace MudEngine.Game.Characters
return true;
}
public override void Load(string filename)
{
base.Load(filename);
}
public void Initialize()
{
//throw new NotImplementedException();
this.Enabled = true;
}
public void Destroy()
{
throw new NotImplementedException();
}
internal void ExecuteCommand(string command)
{
//Process commands here.
if (this._InitialMessage)
if (this.Enabled)
{
command = this.CleanString(command);
this._InitialMessage = false;
Commands.Execute(command, this);
SendMessage("");
SendMessage("Command:", false);
}
}
this.Commands.Execute(command, this);
public void SendMessage(String data)
{
this.SendMessage(data, true);
}
public void SendMessage(string message)
public void SendMessage(String data, Boolean newLine)
{
lock (this)
try
{
_Writer.WriteLine(message);
System.Text.ASCIIEncoding encoding = new System.Text.ASCIIEncoding();
if (newLine)
data += "\n\r";
this._Connection.Send(encoding.GetBytes(data));
}
catch (Exception)
{
Disconnect();
}
}
public String GetInput()
{
string input = String.Empty;
while (true)
{
try
{
byte[] buf = new byte[1];
Int32 recved = this._Connection.Receive(buf);
if (recved > 0)
{
if (buf[0] == '\n' && buffer.Count > 0)
{
if (buffer[buffer.Count - 1] == '\r')
buffer.RemoveAt(buffer.Count - 1);
System.Text.UTF8Encoding enc = new System.Text.UTF8Encoding();
input = enc.GetString(buffer.ToArray());
buffer.Clear();
return input;
}
else
buffer.Add(buf[0]);
}
else if (recved == 0) //Disconnected
{
this.Enabled = false;
return "Disconnected.";
}
}
catch (Exception e)
{
//Flag as disabled
this.Enabled = false;
return e.Message;
}
}
}
@ -108,21 +185,16 @@ namespace MudEngine.Game.Characters
//Close our currently open socket.
this._Connection.Close();
//this._LoopThread.Abort();
//Remove this character from the Connection Manager
ConnectionManager.RemoveConnection(this);
//ConnectionManager.RemoveConnection(this, );
Console.WriteLine("Disconnect Complete.");
//Stop the Update() thread.
this._LoopThread.Abort();
}
public void Connect()
public void Connect(Socket connection)
{
_LoopThread = new Thread(Update);
_LoopThread.Start();
this._Connection = connection;
this.SendMessage("");
OnConnectEvent();
}
@ -133,8 +205,9 @@ namespace MudEngine.Game.Characters
while (this.Game.Enabled)
{
_Writer.Flush();
String line = _Reader.ReadLine();
ExecuteCommand(line);
//String line = CleanString(GetInput());
//Console.WriteLine(line);
//ExecuteCommand(line);
}
}
catch
@ -146,8 +219,9 @@ namespace MudEngine.Game.Characters
}
}
String CleanString(string line)
String CleanString(String line)
{
/*
if ((!String.IsNullOrEmpty(line)) && (line.Length > 0))
{
System.Text.StringBuilder sb = new System.Text.StringBuilder(line.Length);
@ -162,46 +236,43 @@ namespace MudEngine.Game.Characters
}
else
return String.Empty;
* */
Regex invalidChars = new Regex(
@"(?<![\uD800-\uDBFF])[\uDC00-\uDFFF]|[\uD800-\uDBFF](?![\uDC00-\uDFFF])|[\x00-\x08\x0B\x0C\x0E-\x1F\x7F-\x9F\uFEFF\uFFFE\uFFFF]",
RegexOptions.Compiled);
if (String.IsNullOrEmpty(line))
return "";
else
return invalidChars.Replace(line, "");
}
public delegate void OnConnectHandler();
public event OnConnectHandler OnConnectEvent;
public void OnConnect()
{
_Writer.WriteLine(this.Game.Server.MOTD);
this.SendMessage(this.Game.Server.MOTD);
}
public delegate void OnDisconnectHandler();
public event OnDisconnectHandler OnDisconnectEvent;
public void OnDisconnect()
{
}
public delegate void OnLoginHandler();
public event OnLoginHandler OnLoginEvent;
public void OnLogin()
{
}
private Socket _Connection;
private Thread _LoopThread;
private StreamReader _Reader;
private StreamWriter _Writer;
private Boolean _InitialMessage;
public string Filename
{
get
{
throw new NotImplementedException();
}
set
{
throw new NotImplementedException();
}
}
public void Load(string filename)
{
throw new NotImplementedException();
}
public void Initialize()
{
throw new NotImplementedException();
}
public void Destroy()
{
throw new NotImplementedException();
}
private List<byte> buffer = new List<byte>();
}
}

View file

@ -23,10 +23,6 @@ namespace MudEngine.Game
public Boolean Multiplayer { get; set; }
public Int32 MaximumPlayers { get; set; }
public Int32 MaxQueueSize { get; set; }
public Int32 MinimumPasswordSize { get; set; }
public Boolean AutoSave { get; set; }
@ -49,18 +45,14 @@ namespace MudEngine.Game
this.Description = "A sample Mud game created using the Mud Designer kit.";
this.Version = "1.0";
this.Multiplayer = true;
this.MaximumPlayers = 50;
this.MinimumPasswordSize = 8;
this.MaxQueueSize = 20;
this.AutoSave = true;
//Setup our server.
this.Server = new Server(port);
this.Server.Port = port;
this.Server = new Server(this, port);
}
public Boolean Start()
public Boolean Start(Int32 maxPlayers, Int32 maxQueueSize)
{
Logger.WriteLine("Starting up Standard Game");
@ -74,7 +66,7 @@ namespace MudEngine.Game
//Load World
//Start our server.
this.Server.Start(this);
this.Server.Start(maxPlayers, maxQueueSize);
//If the server started without error, flag the Game as enabled.
if (this.Server.Enabled)

View file

@ -59,20 +59,20 @@ namespace MudEngine.GameScripts
return true;
}
public virtual Boolean Load(String filename)
public virtual void Load(String filename)
{
try
{
if (!File.Exists(filename))
return false;
return;
XElement data = XElement.Load(filename);
}
catch
{
return false;
return;
}
return true;
return;
}
public override string ToString()

View file

@ -0,0 +1,42 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using MudEngine.Core.Interface;
using MudEngine.Game;
using MudEngine.Game.Characters;
using MudEngine.Game.Environment;
using MudEngine.GameScripts;
namespace MudEngine.GameScripts.Commands
{
public class CommandLogin : ICommand
{
public string Name { get; set; }
public string Description { get; set; }
public List<string> Help { get; set; }
public CommandLogin()
{
Help = new List<string>();
Name = "Login";
Description = "Account login command.";
}
public void Execute(string command, Game.Characters.StandardCharacter character)
{
character.SendMessage("Please enter character name: ");
String name = String.Empty;
while (String.IsNullOrEmpty(name))
{
name = character.GetInput();
}
}
}
}

View file

@ -33,7 +33,7 @@ namespace MudEngine.GameScripts.Commands
//Loop through each character on the server and broadcast the message.
//TODO: This should only broadcast to characters that are in the same Environment.
foreach (StandardCharacter c in ConnectionManager.Connections)
foreach (StandardCharacter c in character.Game.Server.ConnectionManager.GetConnectedCharacters())
{
//Only broadcast this message to those that are not the broadcastor.
if (c != character)

View file

@ -12,54 +12,94 @@ using MudEngine.Game.Characters;
namespace MudEngine.Networking
{
public static class ConnectionManager
public class ConnectionManager
{
//Collection of currently connected players.
public static List<StandardCharacter> Connections { get; set; }
internal List<Thread> _ConnectedThreads;
internal List<StandardCharacter> _ConnectedCharacters;
public ConnectionManager(Int32 maxPlayers)
{
this._ConnectedCharacters = new List<StandardCharacter>(maxPlayers);
this._ConnectedThreads = new List<Thread>(maxPlayers);
}
/// <summary>
/// Creates a new character for the player and sets it up on the server.
/// </summary>
/// <param name="game"></param>
/// <param name="connection"></param>
public static void AddConnection(StandardGame game, Socket connection)
public void AddConnection(StandardGame game, Socket connection)
{
//Exception checking.
if (Connections == null)
Connections = new List<StandardCharacter>();
//Instance a new character and provide it with the Socket.
StandardCharacter character = new StandardCharacter(game, "New Player", "New networked client.", connection);
//Add it to the Connections collection
Connections.Add(character);
//Invoke the Characters Server connection method
character.Connect();
character.Connect(connection);
this._ConnectedCharacters.Add(character);
this._ConnectedThreads.Add(new Thread(ReceiveDataThread));
Int32 index = this._ConnectedThreads.Count - 1;
this._ConnectedThreads[index].Start(index);
}
public StandardCharacter[] GetConnectedCharacters()
{
return this._ConnectedCharacters.ToArray();
}
private void ReceiveDataThread(Object index)
{
StandardCharacter character = this._ConnectedCharacters[(Int32)index];
character.Initialize();
while (character.Game.Server.Status == ServerStatus.Running &&
character.Enabled)
{
try
{
character.ExecuteCommand(character.GetInput());
}
catch
{
RemoveConnection(character);
}
}
RemoveConnection(character);
}
/// <summary>
/// Removes the specified player character from the server.
/// </summary>
/// <param name="character"></param>
public static void RemoveConnection(StandardCharacter character)
public void RemoveConnection(StandardCharacter character)
{
Connections.Remove(character);
character.Disconnect();
foreach (StandardCharacter c in this._ConnectedCharacters)
{
if (c.ID == character.ID)
{
Int32 index = _ConnectedCharacters.IndexOf(c);
this._ConnectedCharacters.Remove(character);
this._ConnectedThreads[index].Abort();
}
}
}
/// <summary>
/// Disconnects all of the currently connected clients.
/// </summary>
public static void DisconnectAll()
public void DisconnectAll()
{
if (Connections == null)
return;
for (Int32 i = 0; i < this._ConnectedCharacters.Count; i++)
{
this._ConnectedCharacters[i].Disconnect();
this._ConnectedThreads[i].Abort();
}
foreach (StandardCharacter character in Connections)
{
character.Disconnect();
}
Connections.Clear();
this._ConnectedCharacters.Clear();
this._ConnectedThreads.Clear();
}
}
}

View file

@ -14,103 +14,83 @@ using MudEngine.Game.Characters;
namespace MudEngine.Networking
{
public enum ServerStatus
{
Stopped = 0,
Starting = 1,
Running = 2,
}
[Category("Networking")]
public class Server
{
[Category("Networking")]
[Description("The name of this server")]
public String Name { get; set; }
public ServerStatus Status { get; private set; }
[Category("Networking")]
public Int32 Port { get; set; }
public Int32 Port { get; private set; }
[Category("Networking")]
[Description("The Message Of The Day that is presented to users when they connect.")]
public String MOTD { get; set; }
public Int32 MaxConnections { get; private set; }
[Category("Networking")]
[Description("Maximum number of people that can connect and play on this server at any time.")]
public Int32 MaxConnections { get; set; }
public Int32 MaxQueuedConnections { get; private set; }
[Category("Networking")]
[Description("Maximum number of poeple that can be queued for connection.")]
public Int32 QueuedConnectionLimit { get; set; }
public ConnectionManager ConnectionManager { get; private set; }
[Browsable(false)]
public Boolean Enabled { get; private set; }
/// <summary>
/// Sets up a server on the specified port.
/// </summary>
/// <param name="port"></param>
/// <param name="maxConnections"></param>
public Server(Int32 port)
{
Logger.WriteLine("Initializing Mud Server Settings");
this.Port = port;
this.MaxConnections = 50;
this.QueuedConnectionLimit = 20;
this.Name = "New Server";
this.MOTD = "Welcome to a sample Mud Engine game server!";
public String MOTD { get; set; }
public Server(StandardGame game, Int32 port)
{
this.Port = port;
this.Status = ServerStatus.Stopped;
this.MaxConnections = 100;
this.MaxQueuedConnections = 10;
this._Game = game;
this._Server = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
this._Server.Bind(new IPEndPoint(IPAddress.Any, this.Port));
}
/// <summary>
/// Starts the game server. It will listen on a different thread of incoming connections
/// </summary>
public void Start(StandardGame game)
public void Start(Int32 maxConnections, Int32 maxQueueSize)
{
//Start the server.
Logger.WriteLine("Starting Mud Server.");
if (this.Status != ServerStatus.Stopped)
return;
//Start listening for connections
this._Server.Listen(this.QueuedConnectionLimit);
this.Status = ServerStatus.Starting;
//Launch the new connections listener on a new thread.
this._ConnectionPool = new Thread(AcceptConnection);
this._ConnectionPool.Start();
this.MaxConnections = maxConnections;
this.ConnectionManager = new ConnectionManager(this.MaxConnections);
//Flag the server as enabled.
try
{
IPEndPoint ip = new IPEndPoint(IPAddress.Any, this.Port);
this._Server.Bind(ip);
this._Server.Listen(this.MaxQueuedConnections);
this.Status = ServerStatus.Running;
this.Enabled = true;
//Save a reference to the currently active game
this._Game = game;
Logger.WriteLine("Server started.");
this._ServerThread = new Thread(ServerLoop);
this._ServerThread.Start();
}
catch
{
this.Status = ServerStatus.Stopped;
}
}
public void Stop()
{
//Flag the server as disabled.
this.Enabled = false;
//Stop the thread from listening for accepting new conections
this._ConnectionPool.Abort();
//Disconnect all of the currently connected clients.
ConnectionManager.DisconnectAll();
//Stop the server.
this._Server.Close();
}
private void AcceptConnection()
private void ServerLoop()
{
//While the server is enabled, constantly check for new connections.
while (this.Enabled)
while (this.Status == ServerStatus.Running)
{
//Grab the new connection.
Socket incomingConnection = this._Server.Accept();
//Send it to the Connection Manager so a Character can be instanced.
ConnectionManager.AddConnection(this._Game, incomingConnection);
this.ConnectionManager.AddConnection(this._Game, this._Server.Accept());
}
}
private Socket _Server;
private Thread _ConnectionPool;
private StandardGame _Game;
private Socket _Server;
private Thread _ServerThread;
}
}

View file

@ -54,6 +54,7 @@
<Compile Include="Core\ObjectCollection.cs" />
<Compile Include="DAL\DataPaths.cs" />
<Compile Include="DAL\XMLData.cs" />
<Compile Include="GameScripts\Commands\CommandLogin.cs" />
<Compile Include="GameScripts\Commands\CommandSay.cs" />
<Compile Include="Game\Characters\CharacterRoles.cs" />
<Compile Include="Game\Characters\CharacterStats.cs" />

View file

@ -23,9 +23,19 @@ namespace WinPC_Server
Logger.Enabled = true;
Logger.ConsoleOutPut = true;
//Instance and start the game. This will start the server.
//Instance and setup our game
StandardGame game = new StandardGame("Sample Game");
game.Start();
game.AutoSave = true;
game.Debugging = true;
game.Description = "This is a very simple game that was created to demonstrate MUD game creation with the Mud Designer Game Engine.";
game.HiddenRoomNames = false;
game.Multiplayer = true;
game.Server.MOTD = "Welcome to the Sample Game desmontratio server!";
game.Version = "1.0";
game.Website = "http://muddesigner.codeplex.com";
//Start the game and server.
game.Start(100, 20);
//Setup our Server console input class
ConsoleInput input = new ConsoleInput();
@ -38,7 +48,7 @@ namespace WinPC_Server
while (game.Enabled)
{
//Check the queued Console Input
if (input.Message.Equals("exit"))
if (input.Message.ToLower().Equals("exit"))
{
//If the server console has a exit command entered.
//stop the game. This will set game.Enabled to false.