I would think an MMOOutboundS2SPeerBase would be a great idea as it creates the basis for multiple server types that are communicating to each other. This just pulls what appears to be common code out of the MMO.Server.Region.MasterServerPeer class and moves it one level down to where it can be re-used with multiple server types.
I haven't compiled or tested this code, just a quick copy paste refactor. But this is the direction I would start heading in.
Code:
using MMO.Base;
using MMO.Client.Infrastructure;
using MMO.Client.Systems;
using Photon.SocketServer;
using Photon.SocketServer.ServerToServer;
using PhotonHostRuntimeInterfaces;
using System;
using System.Collections.Generic;
using MMO.Base.Async;
using Microsoft.Extensions.DependencyInjection;
namespace MMO.Server
{
public abstract class MMOOutboundS2SPeerBase<TContext> : OutboundS2SPeer, ISystemFactory, IClientTransport
where TContext : ServerContext
{
public event Action OnDisconnected;
protected TContext _serverContext { get; }
protected Client.Systems.ClientSystems _clientSystems { get; }
protected ComponentMap _componentMap { get; }
protected IServiceScope _scope { get; }
protected CallbackByteMap<Action<OperationCode, Dictionary<byte, object>>> _callbacks { get; }
protected Action<EventCode, Dictionary<byte, object>>[] _eventHandlers { get; }
protected HashSet<IClientTransportListener> Listeners { get; }
protected MMOOutboundS2SPeerBase(ApplicationBase app, TContext application) : base(app)
{
_callbacks = new CallbackByteMap<Action<OperationCode, Dictionary<byte, object>>>();
_eventHandlers = new Action<EventCode, Dictionary<byte, object>>[byte.MaxValue + 1];
Listeners = new HashSet<IClientTransportListener>();
_serverContext = application;
_scope = application.Container.CreateScope();
_componentMap = new ComponentMap();
var operationWriter = new SystemsOperationWriter(_serverContext.Serializer, this);
_clientSystems = new Client.Systems.ClientSystems(_componentMap, this, operationWriter);
AddEventReader(new SystemsEventReader(_serverContext.Serializer, _clientSystems, this));
application.OnDiposed += OnApplicationDisposed;
}
#region Methods
protected void HandleSystemCallbak(OperationCode code, Dictionary<byte, object> parameters)
{
if (code != OperationCode.SendSystemResponse)
throw new ArgumentException($"Code {code} is not valid for handling responses", nameof(code));
var methodInvokeId = (byte)parameters[(byte)OperationParameter.SystemInvokeId];
var callback = _callbacks.GetCallback(methodInvokeId);
callback(code, parameters);
}
protected void HandleEvent(EventCode code, Dictionary<byte, object> parameters)
{
var handler = _eventHandlers[(byte)code];
if (handler == null)
throw new InvalidOperationException($"Handler for event code {code} was not registered");
handler(code, parameters);
}
protected void SendOperationInternal(OperationCode code, Dictionary<byte, object> parameters)
{
SendOperationRequest(new OperationRequest((byte)code, parameters), new SendParameters { Unreliable = false });
}
private void OnApplicationDisposed()
{
Disconnect();
}
#endregion
#region Overrides
protected override void OnConnectionEstablished(object responseObject)
{
SendOperation(OperationCode.InitContext, new Dictionary<byte, object> { [(byte)OperationParameter.ContextType] = ContextType.Region });
}
protected override void OnConnectionFailed(int errorCode, string errorMessage)
{
}
protected override void OnDisconnect(DisconnectReason reasonCode, string reasonDetail)
{
OnDisconnected?.Invoke();
_scope.Dispose();
_serverContext.OnDiposed -= OnApplicationDisposed;
}
protected override void OnOperationRequest(OperationRequest operationRequest, SendParameters sendParameters)
{
}
protected override void OnEvent(IEventData eventData, SendParameters sendParameters)
{
HandleEvent((EventCode)eventData.Code, eventData.Parameters);
}
protected override void OnOperationResponse(OperationResponse operationResponse, SendParameters sendParameters)
{
HandleSystemCallbak((OperationCode)operationResponse.OperationCode, operationResponse.Parameters);
}
#endregion
#region ISystemFactory Members
public abstract ISystemBase CreateSystem(Type interfaceType, Func<Type, object> proxyFactory, out Type concreteType);
#endregion
#region IClientTransport Members
public void SendOperation(OperationCode code, Dictionary<byte, object> parameters)
{
SendOperationInternal(code, parameters);
}
public void SendOperation(OperationCode code, Dictionary<byte, object> parameters, Action<OperationCode, Dictionary<byte, object>> onResponse)
{
parameters[(byte)OperationParameter.SystemInvokeId] = _callbacks.RegisterCallback(onResponse);
SendOperationInternal(code, parameters);
}
public void AddEventReader(IEventReaderModule eventReader)
{
foreach (var registration in eventReader.GetRegistration())
{
if (_eventHandlers[(int)registration.Code] != null)
{
var oldHandler = _eventHandlers[(int)registration.Code];
var action = registration.Action;
_eventHandlers[(int)registration.Code] = (code, parameter) =>
{
oldHandler(code, parameter);
action(code, parameter);
};
}
else
{
_eventHandlers[(int)registration.Code] = registration.Action;
}
}
}
public void AddListener(IClientTransportListener listener)
{
Listeners.Add(listener);
}
public Deferred Connect()
{
return Deferred.Success();
}
public void Service()
{
}
Deferred IClientTransport.Disconnect()
{
return Deferred.Success();
}
#endregion
}
}
Code:
using log4net;
using MMO.Base;
using PhotonHostRuntimeInterfaces;
using System;
namespace MMO.Server.Region
{
public class MasterServerPeer : MMOOutboundS2SPeerBase<GameServerContext>
{
private static readonly ILog Log = LogManager.GetLogger(typeof(MasterServerPeer));
public MasterServerPeer(Application app, GameServerContext serverContext) : base(app, serverContext)
{
}
public override ISystemBase CreateSystem(Type interfaceType, Func<Type, object> proxyFactory, out Type concreteType)
{
var registeredSystem = _serverContext.SystemTypeRegistry.GetSystemFromClientInterfaceType(interfaceType);
var instance = _scope.ServiceProvider.GetService(registeredSystem.ConcreteType);
var proxy = proxyFactory(registeredSystem.ServerInterfaceType);
((IMasterSystemBase)instance).SetContext(_serverContext, proxy);
concreteType = registeredSystem.ConcreteType;
return (ISystemBase)instance;
}
protected override void OnDisconnect(DisconnectReason reasonCode, string reasonDetail)
{
Log.Info($"MasterServerPeer Disconnect - {reasonCode}: {reasonDetail}");
base.OnDisconnect(reasonCode, reasonDetail);
}
}
}