|
using System;
using System.IO;
using System.Net;
using System.Reflection;
using System.Runtime.Serialization;
using Pfz.RemoteGaming.Internal;
using Pfz.Remoting;
using Pfz.Remoting.Udp;
using Pfz.Serialization;
using Pfz.Threading;
namespace Pfz.RemoteGaming
{
/// <summary>
/// Class that should be used by the game server.
/// It listens for client connections and then starts the game.
/// </summary>
public sealed class RemoteGameListener:
ThreadSafeExceptionAwareDisposable
{
#region Static Area
#region Constructor
static RemoteGameListener()
{
_AddCommonType(typeof(RemoteGameComponent));
}
#endregion
#region _AddCommonType
private static void _AddCommonType(Type type)
{
foreach (var property in type.GetProperties(BindingFlags.Public | BindingFlags.Instance | BindingFlags.DeclaredOnly))
{
if (!property.CanWrite)
continue;
if (!property.CanRead)
continue;
if (!property.GetGetMethod().IsAbstract)
continue;
if (!property.GetSetMethod().IsAbstract)
continue;
_AssemblyGenerator._GetOrCreateRemotePropertyInfo(property);
}
}
#endregion
#endregion
#region Fields
private IConnectionListener<IConnection> _listener;
private readonly Assembly[] _commonAssemblies;
#endregion
#region Constructors
#region By Address and Port
/// <summary>
/// Creates a new GameListener for the given address and port.
/// </summary>
public RemoteGameListener(IPAddress address, int port, params Assembly[] commonAssemblies):
this(new TcpWithUdpListener(address, port, false, 8*1024), commonAssemblies)
{
}
#endregion
#region By ConnectionListener
/// <summary>
/// Creates a new RemoteGameListener using the given Listener.
/// </summary>
public RemoteGameListener(IConnectionListener<IConnection> listener, params Assembly[] commonAssemblies)
{
if (listener == null)
throw new ArgumentNullException("listener");
if (commonAssemblies == null || commonAssemblies.Length == 0)
throw new ArgumentException("commonAssemblies must not be null or empty. If the server is directly acessible by the client, pass the server assembly as the common one.", "commonAssemblies");
_listener = listener;
_commonAssemblies = commonAssemblies;
foreach(var assembly in commonAssemblies)
{
if (assembly == null)
throw new ArgumentException("commonAssemblies can't contain null values.", "commonAssemblies");
foreach(var type in assembly.GetTypes())
{
if (!type.IsAbstract)
continue;
if (!type.IsSubclassOf(typeof(RemoteGameComponent)))
continue;
_AddCommonType(type);
}
}
}
#endregion
#endregion
#region Dispose
/// <summary>
/// Closes the internal listener.
/// </summary>
protected override void Dispose(bool disposing)
{
if (disposing)
{
Disposer.Dispose(ref _listener);
}
base.Dispose(disposing);
}
#endregion
#region Methods
#region _GetClientInitializationData
internal _InitializationData _GetClientInitializationData()
{
var initializationData = new _InitializationData();
initializationData._commonAssemblies = _commonAssemblies;
RemoteGameProperty._propertiesLock.EnterReadLock();
try
{
initializationData._propertyInfos = RemoteGameProperty._properties.ToArray();
}
finally
{
RemoteGameProperty._propertiesLock.ExitReadLock();
}
return initializationData;
}
#endregion
#region _RunClient
private void _RunClient(IConnection connection)
{
using(connection)
{
using(var participant = _OnCreateParticipant(connection))
{
if (participant == null)
return;
participant.Connection = connection;
participant._listener = this;
participant._Run();
}
}
}
#endregion
#region _OnCreateParticipant
private RemoteGameParticipant _OnCreateParticipant(IConnection connection)
{
RemoteGameParticipant result = null;
var handler = ClientConnected;
if (handler == null)
throw new RemoteGameException("RemoteGameListener is only useful if you set its ClientConnected event.");
var args = new RemoteGameConnectedEventArgs();
args.Connection = connection;
handler(this, args);
result = args.Participant;
if (result == null)
{
if (args.ShouldDisconnect)
return null;
throw new RemoteGameException("You must create and set a Participant to the event args or must set ShouldDisconnect to true.");
}
if (result.Room == null)
throw new RemoteGameException("The Participant must be added to a Room to be useful.");
result._commonAssemblies = _commonAssemblies;
return result;
}
#endregion
#region _OnException
internal bool _OnException(Exception exception)
{
var handler = ExceptionThrown;
if (handler == null)
return false;
var args = new RemoteGameExceptionEventArgs();
args.Exception = exception;
handler(this, args);
return args.WasHandled;
}
#endregion
#region Start
private bool _started;
/// <summary>
/// Starts listening.
/// </summary>
public void Start()
{
lock(DisposeLock)
{
CheckUndisposed();
var listener = _listener;
if (_started)
throw new RemoteGameException("Listener already started.");
UnlimitedThreadPool.Run
(
() =>
{
try
{
while(true)
{
var client = listener.TryAccept();
if (client == null)
{
Dispose();
return;
}
UnlimitedThreadPool.Run(_RunClient, client);
}
}
catch(Exception exception)
{
if (!WasDisposed)
Dispose(exception);
}
}
);
_started = true;
}
}
#endregion
#endregion
#region Events
#region ClientConnected
/// <summary>
/// Event invoked when a new connection is accepted. Use it to configure the Tcp/IP client.
/// </summary>
public event EventHandler<RemoteGameConnectedEventArgs> ClientConnected;
#endregion
#region ExceptionThrown
/// <summary>
/// Event invoked when an exception is thrown by the game. Such exception can be treated, but the connection
/// to that client will be closed in any case.
/// </summary>
public event EventHandler<RemoteGameExceptionEventArgs> ExceptionThrown;
#endregion
#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.
I started to program computers when I was 11 years old, as a hobbyist, programming in AMOS Basic and Blitz Basic for Amiga.
At 12 I had my first try with assembler, but it was too difficult at the time. Then, in the same year, I learned C and, after learning C, I was finally able to learn assembler (for Motorola 680x0).
Not sure, but probably between 12 and 13, I started to learn C++. I always programmed "in an object oriented way", but using function pointers instead of virtual methods.
At 15 I started to learn Pascal at school and to use Delphi. At 16 I started my first internship (using Delphi). At 18 I started to work professionally using C++ and since then I've developed my programming skills as a professional developer in C++ and C#, generally creating libraries that help other developers do their work easier, faster and with less errors.
Want more info or simply want to contact me?
Take a look at:
http://paulozemek.azurewebsites.net/
Or e-mail me at: paulozemek@outlook.com
Codeproject MVP 2012, 2015 & 2016
Microsoft MVP 2013-2014 (in October 2014 I started working at Microsoft, so I can't be a Microsoft MVP anymore).