using System;
using System.Collections.Generic;
using System.Linq;
using System.IO;
using System.Text;
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;
using MudEngine.Networking;
using MudEngine.Core;
using MudEngine.Game.Environment;
namespace MudEngine.Game.Characters
{
///
/// Standard Character class used by all character based objects
///
public class StandardCharacter : BaseScript, INetworked, ISavable, IGameComponent
{
#region Properties
///
/// Gets a reference to the currently active game.
///
public string Filename
{
get
{
return this.Game.SavePaths.GetFilePath(DAL.DataTypes.Players, this.Name);
}
}
///
/// Gets what this Characters role on the server is.
///
public CharacterRoles Role { get; protected set; }
///
/// Gets what this characters stats are.
///
public CharacterStats Stats { get; protected set; }
///
/// User account password
///
//TODO: Character passwords should be a private set.
public String Password { get; set; }
///
/// Flags this object as non-movable in the world.
///
public Boolean Immovable { get; set; }
///
/// Gets or Sets if this character is enabled.
/// When disabled, they can not enter commands.
/// Characters can have LoggedIn and Connected TRUE with Enabled false resulting in a player connected to
/// the server but unable to interact.
///
public Boolean Enabled { get; set; }
///
/// Gets or Sets if this client is fully logged into the account and located in the World.
///
public Boolean LoggedIn
{
get
{
return this._LoggedIn;
}
private set
{
if (value)
this.OnLoginEvent();
this._LoggedIn = value;
}
}
///
/// Gets if the user is currently connected to a server or not.
///
public Boolean Connected { get; set; }
protected CommandSystem Commands { get; private set; }
public Room CurrentRoom { get; private set; }
#endregion
#region Constructors
public StandardCharacter(StandardGame game, String name, String description)
: base(game, name, description)
{
this.Role = CharacterRoles.Player;
//Instance this Characters personal Command System with a copy of the command
//collection already loaded and prepared by the Active Game.
this.Commands = new CommandSystem(CommandSystem.Commands);
this.OnConnectEvent += new OnConnectHandler(OnConnect);
this.OnDisconnectEvent += new OnDisconnectHandler(OnDisconnect);
this.OnLoginEvent += new OnLoginHandler(OnLogin);
this.OnWalkEvent += new OnWalkHandler(OnWalk);
this.OnEnterEvent += new OnEnterHandler(OnEnter);
this.OnLeaveEvent += new OnLeaveHandler(OnLeave);
}
public StandardCharacter(StandardGame game, String name, String description, Socket connection)
: this(game, name, description)
{
this._Connection = connection;
this._Reader = new StreamReader(new NetworkStream(this._Connection, false));
this._Writer = new StreamWriter(new NetworkStream(this._Connection, true));
this._Writer.AutoFlush = true; //Flushes the stream automatically.
}
#endregion
#region Inherited Methods
public virtual void Initialize()
{
//throw new NotImplementedException();
this.Enabled = true;
}
///
/// Destroys any resources used by this character.
/// Assumes that Save() has already been invoked.
///
public virtual void Destroy()
{
this.Commands = null;
}
public override bool Save(String filename)
{
return this.Save(filename, true);
}
public override bool Save(String filename, Boolean verbose)
{
base.Save(filename, true);
SaveData.AddSaveData("Immovable", Immovable.ToString());
SaveData.AddSaveData("Password", Password);
SaveData.AddSaveData("Role", this.Role.ToString());
if (this.SaveData.Save(filename))
{
if (!verbose)
this.SendMessage("Save completed.");
}
return true;
}
public override void Load(string filename)
{
base.Load(filename);
try { this.Immovable = Convert.ToBoolean(this.SaveData.GetData("Immovable")); }
catch { this.LoadFailedMessage("Immovable"); }
this.Password = this.SaveData.GetData("Password");
String role = this.SaveData.GetData("Role");
this.Role = GetRole(role);
}
#endregion
#region Networking Methods
///
/// Executes the specified command if it exists in the Command library.
///
///
///
public virtual Boolean ExecuteCommand(string command)
{
Boolean result = false;
if (this.Enabled && this.Connected)
{
result = Commands.Execute(command, this);
if (!result)
this.SendMessage("Invalid Command used.");
SendMessage("");
SendMessage("Command:", false);
return result;
}
else
{
return false;
}
}
///
/// Executes the spcified command if it exists in the Command library.
/// This method is verbose. Only messages sent from the Commands will be presented to the player.
/// The standard new line and "Command: " message is skipped with this method.
///
///
///
public virtual Boolean ExecuteSilentCommand(string command)
{
if (this.Enabled)
{
Boolean result = Commands.Execute(command, this);
return result;
}
else
return false;
}
public void Connect()
{
//this.Initialize();
this.Connected = true;
OnConnectEvent();
}
public void Disconnect()
{
this.Save(this.Filename);
//Purge all of this characters commands.
this.Destroy();
//Close our currently open socket.
this._Connection.Close();
this.LoggedIn = false;
this.Connected = false;
this.OnDisconnectEvent();
}
public virtual void SendMessage(String data)
{
this.SendMessage(data, true);
}
public virtual void SendMessage(String data, Boolean newLine)
{
try
{
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 a trimmed string.
return input;
}
else
buffer.Add(buf[0]);
}
else if (recved == 0) //Disconnected
{
this.Connected = false;
this.LoggedIn = false;
return "Disconnected.";
}
}
catch (Exception e)
{
//Flag as disabled
this.Connected = false;
this.LoggedIn = false;
return e.Message;
}
}
}
#endregion
#region Event Methods
public delegate void OnConnectHandler();
public event OnConnectHandler OnConnectEvent;
public void OnConnect()
{
//Greet the user with the game information
this.SendMessage(this.Game.Name);
this.SendMessage(this.Game.Description);
this.SendMessage(String.Empty);
//Log the user in.
Boolean result = this.ExecuteSilentCommand("Login");
this.SendMessage(String.Empty);
//Flags the character as fully logged in.
//This will automatically invoke the OnLoggedIn method
//which will present the user with the server MOTD
this.LoggedIn = result;
//Provide a blank line between the character loggin info
//and the characters actual first in-game Command prompt.
this.SendMessage(String.Empty);
//Make sure we are logged in.
if (!this.LoggedIn)
{
this.Disconnect();
}
this.ExecuteCommand("Look");
}
public delegate void OnDisconnectHandler();
public event OnDisconnectHandler OnDisconnectEvent;
public void OnDisconnect()
{
}
public delegate void OnLoginHandler();
public event OnLoginHandler OnLoginEvent;
public void OnLogin()
{
this.SendMessage(this.Game.Server.MOTD);
//Check to see if this user is the servers owner.
if (this.Name == this.Game.Server.ServerOwner)
{
//Set the role accordingly.
this.Role = CharacterRoles.Admin;
}
}
public delegate void OnWalkHandler(AvailableTravelDirections direction);
public event OnWalkHandler OnWalkEvent;
public void OnWalk(AvailableTravelDirections direction)
{
if (this.CurrentRoom.DoorwayExists(direction))
{
this.CurrentRoom = this.CurrentRoom.GetDoorway(direction).ArrivalRoom;
}
else
return;
}
public delegate void OnLeaveHandler(Room departingRoom, Room targetRoom, AvailableTravelDirections direction);
public event OnLeaveHandler OnLeaveEvent;
public void OnLeave(Room departingRoom, Room targetRoom, AvailableTravelDirections direction)
{
//This character was forced moved so we have no direction
if (direction == AvailableTravelDirections.None)
departingRoom.SendMessageToOccupants(this.Name + " has left the room.", this);
else
departingRoom.SendMessageToOccupants(this.Name + " has left the room heading " + direction.ToString(), this);
}
public delegate void OnEnterHandler(Room departingRoom, Room targetRoom, AvailableTravelDirections direction);
public event OnEnterHandler OnEnterEvent;
public void OnEnter(Room departingRoom, Room targetRoom, AvailableTravelDirections direction)
{
//This character was forced moved so we have no direction
if (direction == AvailableTravelDirections.None)
targetRoom.SendMessageToOccupants(this.Name + " has entered the room.");
else
targetRoom.SendMessageToOccupants(this.Name + " has entered the room from the " + direction.ToString(), this);
this.ExecuteCommand("Look");
}
#endregion
#region Character Methods
public void SetRole(StandardCharacter adminCharacter, StandardCharacter subordinateCharacter, CharacterRoles role)
{
//Check to make sure the admin character is truly a admin
//The Server Owner can change anyone characters Roles at any time regardless of the ServerOwners current Role.
if (adminCharacter.Role == CharacterRoles.Admin || adminCharacter.Name == this.Game.Server.ServerOwner)
{
//Do not allow any other Admins on the server to remove the ServerOwner from Admin status.
//Server owners can not have any other Role but Admin unless they change it themselves.
if (subordinateCharacter.Name == this.Game.Server.ServerOwner && adminCharacter.Name != this.Game.Server.ServerOwner)
{
//Tell the admin that is attempting to change the server owner that it's not allowed.
adminCharacter.SendMessage("You can not change the role of the Master admin.");
//Warn the server owner in the event this is a malicious attempt to take over the server.
subordinateCharacter.SendMessage(adminCharacter.Name + " attempted to change your server role!");
//Exit with no changes.
return;
}
//This was not the server owner, so change the role.
else
{
subordinateCharacter.Role = role;
subordinateCharacter.SendMessage("Your role was changed to " + role.ToString());
adminCharacter.SendMessage("You changed player '" + subordinateCharacter.Name + "' to the role of " + role.ToString());
}
}
else
{
adminCharacter.SendMessage("You do not have the rights to perform this command.");
}
}
private CharacterRoles GetRole(String role)
{
//Blow all of the available values up into an array.
Array values = Enum.GetValues(typeof(CharacterRoles));
//Loop through each available value, converting it into a string.
foreach (Int32 value in values)
{
//Get the string representation of the current value
String displayName = Enum.GetName(typeof(CharacterRoles), value);
//Check if this value matches that of the supplied one.
//If so, return it as a enum
if (displayName.ToLower() == role.ToLower())
return (CharacterRoles)Enum.Parse(typeof(CharacterRoles), displayName);
}
return CharacterRoles.Player;
}
#endregion
#region World Methods
public virtual void Walk(AvailableTravelDirections direction)
{
Room r = this.CurrentRoom;
this.OnWalkEvent(direction);
//If we didn't go anywhere, do nothing.
if (this.CurrentRoom == r)
return;
OnLeaveEvent(r, this.CurrentRoom, direction);
OnEnterEvent(r, this.CurrentRoom, direction);
}
public virtual void Move(Room room)
{
if (this.CurrentRoom != null)
this.CurrentRoom.SendMessageToOccupants(this.Name + " has left the room.", this);
this.CurrentRoom = room;
this.CurrentRoom.AddOccupant(this);
this.CurrentRoom.SendMessageToOccupants(this.Name + " has entered the room.", this);
}
#endregion
private Socket _Connection;
private StreamReader _Reader;
private StreamWriter _Writer;
private Boolean _LoggedIn;
private List buffer = new List();
}
}