mirror of
https://github.com/OpenDiablo2/OpenDiablo2
synced 2025-02-03 15:17:04 -05:00
Added client/server architecture support.
This commit is contained in:
parent
0a3eb44248
commit
0cabaceb48
19
OpenDiablo2.Common/Attributes/MessageFrameAttribute.cs
Normal file
19
OpenDiablo2.Common/Attributes/MessageFrameAttribute.cs
Normal file
@ -0,0 +1,19 @@
|
||||
using System;
|
||||
using OpenDiablo2.Common.Enums;
|
||||
|
||||
namespace OpenDiablo2.Common.Attributes
|
||||
{
|
||||
[AttributeUsage(AttributeTargets.All, Inherited = false, AllowMultiple = true)]
|
||||
public sealed class MessageFrameAttribute : Attribute
|
||||
{
|
||||
public eMessageFrameType FrameType { get; private set; }
|
||||
|
||||
// This is a positional argument
|
||||
public MessageFrameAttribute(eMessageFrameType frameType)
|
||||
{
|
||||
this.FrameType = frameType;
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
}
|
19
OpenDiablo2.Common/Enums/eMessageFrameType.cs
Normal file
19
OpenDiablo2.Common/Enums/eMessageFrameType.cs
Normal file
@ -0,0 +1,19 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace OpenDiablo2.Common.Enums
|
||||
{
|
||||
public enum eMessageFrameType
|
||||
{
|
||||
None = 0x00,
|
||||
SetSeed = 0x01,
|
||||
JoinGame = 0x02,
|
||||
|
||||
MAX = 0xFF, // NOTE:
|
||||
// You absolutely cannot have a higher ID than this without
|
||||
// changing the message header to multi-byte for ALL frame types!!!
|
||||
}
|
||||
}
|
@ -1,11 +1,12 @@
|
||||
using System.Collections.Generic;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Drawing;
|
||||
using OpenDiablo2.Common.Enums;
|
||||
using OpenDiablo2.Common.Models;
|
||||
|
||||
namespace OpenDiablo2.Common.Interfaces
|
||||
{
|
||||
public interface IGameState
|
||||
public interface IGameState : IDisposable
|
||||
{
|
||||
int Act { get; }
|
||||
int Seed { get; }
|
||||
|
@ -6,7 +6,9 @@ using System.Threading.Tasks;
|
||||
|
||||
namespace OpenDiablo2.Common.Interfaces
|
||||
{
|
||||
public interface ISessionServer : IDisposable
|
||||
public interface IMessageFrame
|
||||
{
|
||||
byte[] Data { get; set; }
|
||||
void Process(object sender, ISessionEventProvider sessionEventProvider);
|
||||
}
|
||||
}
|
@ -0,0 +1,18 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace OpenDiablo2.Common.Interfaces
|
||||
{
|
||||
public delegate void OnSetSeedEvent(object sender, int seed);
|
||||
public delegate void OnJoinGameEvent(object sender, Guid playerId, string playerName); // TODO: Not the final version..
|
||||
|
||||
public interface ISessionEventProvider
|
||||
{
|
||||
|
||||
OnSetSeedEvent OnSetSeed { get; set; }
|
||||
OnJoinGameEvent OnJoinGame { get; set; }
|
||||
}
|
||||
}
|
@ -6,9 +6,11 @@ using System.Threading.Tasks;
|
||||
|
||||
namespace OpenDiablo2.Common.Interfaces
|
||||
{
|
||||
public interface ISessionManager : IDisposable
|
||||
|
||||
public interface ISessionManager : ISessionEventProvider, IDisposable
|
||||
{
|
||||
void Initialize();
|
||||
void Stop();
|
||||
void JoinGame(string playerName);
|
||||
}
|
||||
}
|
13
OpenDiablo2.Common/Interfaces/MessageBus/ISessionServer.cs
Normal file
13
OpenDiablo2.Common/Interfaces/MessageBus/ISessionServer.cs
Normal file
@ -0,0 +1,13 @@
|
||||
using System;
|
||||
using System.Threading;
|
||||
|
||||
namespace OpenDiablo2.Common.Interfaces
|
||||
{
|
||||
public interface ISessionServer : IDisposable
|
||||
{
|
||||
AutoResetEvent WaitServerStartEvent { get; set; }
|
||||
|
||||
void Start();
|
||||
void Stop();
|
||||
}
|
||||
}
|
@ -101,7 +101,6 @@ namespace OpenDiablo2.Common.Models
|
||||
// TODO: DI magic please
|
||||
public MPQDS1(Stream stream, LevelPreset level, LevelDetail levelDetail, LevelType levelType, IEngineDataManager engineDataManager, IResourceManager resourceManager)
|
||||
{
|
||||
log.Debug($"Loading {level.Name} (Act {levelDetail.Act})...");
|
||||
var br = new BinaryReader(stream);
|
||||
Version = br.ReadInt32();
|
||||
Width = br.ReadInt32() + 1;
|
||||
@ -296,8 +295,6 @@ namespace OpenDiablo2.Common.Models
|
||||
if (!isMasked || levelType.File[i] == "0")
|
||||
continue;
|
||||
|
||||
log.Debug($"Loading DT resource {levelType.File[i]}");
|
||||
|
||||
DT1s[i] = resourceManager.GetMPQDT1("data\\global\\tiles\\" + levelType.File[i].Replace("/", "\\"));
|
||||
|
||||
}
|
||||
|
@ -11,6 +11,7 @@ namespace OpenDiablo2.Common.Models.Mobs
|
||||
{
|
||||
public class PlayerState : MobState
|
||||
{
|
||||
public Guid Id { get; protected set; }
|
||||
public eHero HeroType { get; protected set; }
|
||||
private IHeroTypeConfig HeroTypeConfig;
|
||||
private ILevelExperienceConfig ExperienceConfig;
|
||||
|
@ -52,8 +52,10 @@
|
||||
<Reference Include="System.Xml" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<Compile Include="Attributes\MessageFrameAttribute.cs" />
|
||||
<Compile Include="Attributes\SceneAttribute.cs" />
|
||||
<Compile Include="AutofacModule.cs" />
|
||||
<Compile Include="Enums\eMessageFrameType.cs" />
|
||||
<Compile Include="Enums\ePanelFrameType.cs" />
|
||||
<Compile Include="Enums\eButtonType.cs" />
|
||||
<Compile Include="Enums\eHero.cs" />
|
||||
@ -67,8 +69,10 @@
|
||||
<Compile Include="Enums\Mobs\eDamageTypes.cs" />
|
||||
<Compile Include="Enums\Mobs\eMobFlags.cs" />
|
||||
<Compile Include="Enums\Mobs\eStatModifierType.cs" />
|
||||
<Compile Include="Interfaces\ISessionManager.cs" />
|
||||
<Compile Include="Interfaces\ISessionServer.cs" />
|
||||
<Compile Include="Interfaces\MessageBus\ISessionEventProvider.cs" />
|
||||
<Compile Include="Interfaces\MessageBus\IMessageFrame.cs" />
|
||||
<Compile Include="Interfaces\MessageBus\ISessionManager.cs" />
|
||||
<Compile Include="Interfaces\MessageBus\ISessionServer.cs" />
|
||||
<Compile Include="Interfaces\UI\IButton.cs" />
|
||||
<Compile Include="Interfaces\UI\IPanelFrame.cs" />
|
||||
<Compile Include="Interfaces\UI\IInventoryPanel.cs" />
|
||||
@ -137,9 +141,6 @@
|
||||
<ItemGroup>
|
||||
<None Include="packages.config" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<Folder Include="Message Frames\Requests\" />
|
||||
<Folder Include="Message Frames\Responses\" />
|
||||
</ItemGroup>
|
||||
<ItemGroup />
|
||||
<Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
|
||||
</Project>
|
@ -27,7 +27,6 @@ namespace OpenDiablo2.Core
|
||||
builder.RegisterType<ResourceManager>().As<IResourceManager>().SingleInstance();
|
||||
builder.RegisterType<TextDictionary>().As<ITextDictionary>().SingleInstance();
|
||||
builder.RegisterType<TextBox>().As<ITextBox>().InstancePerDependency();
|
||||
builder.RegisterType<SessionServer>().As<ISessionServer>().InstancePerLifetimeScope();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -60,14 +60,20 @@ namespace OpenDiablo2.Core.GameState_
|
||||
sessionManager = getSessionManager(sessionType);
|
||||
sessionManager.Initialize();
|
||||
|
||||
var random = new Random();
|
||||
Seed = random.Next(); // TODO: Seed does not go here ;-(
|
||||
sessionManager.OnSetSeed += OnSetSeedEvent;
|
||||
|
||||
sceneManager.ChangeScene("Game");
|
||||
mapInfo = new List<MapInfo>();
|
||||
(new MapGenerator(this)).Generate();
|
||||
sceneManager.ChangeScene("Game");
|
||||
|
||||
sessionManager.JoinGame(characterName); // TODO: we need more attributes...
|
||||
}
|
||||
|
||||
private void OnSetSeedEvent(object sender, int seed)
|
||||
{
|
||||
log.Info($"Setting seed to {seed}");
|
||||
this.Seed = seed;
|
||||
(new MapGenerator(this)).Generate();
|
||||
}
|
||||
|
||||
public MapInfo LoadSubMap(int levelDefId, Point origin)
|
||||
{
|
||||
@ -391,5 +397,10 @@ namespace OpenDiablo2.Core.GameState_
|
||||
animationTime += ((float)ms / 1000f);
|
||||
animationTime -= (float)Math.Truncate(animationTime);
|
||||
}
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
sessionManager?.Dispose();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -65,7 +65,6 @@
|
||||
<Compile Include="MPQProvider.cs" />
|
||||
<Compile Include="Properties\AssemblyInfo.cs" />
|
||||
<Compile Include="ResourceManager.cs" />
|
||||
<Compile Include="SessionServer.cs" />
|
||||
<Compile Include="TextDictionary.cs" />
|
||||
<Compile Include="UI\Button.cs" />
|
||||
<Compile Include="UI\PanelFrame.cs" />
|
||||
|
@ -1,18 +0,0 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
using OpenDiablo2.Common.Interfaces;
|
||||
|
||||
namespace OpenDiablo2.Core
|
||||
{
|
||||
public sealed class SessionServer : ISessionServer
|
||||
{
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
|
||||
}
|
||||
}
|
||||
}
|
@ -1,5 +1,7 @@
|
||||
using System;
|
||||
using System.Linq;
|
||||
using Autofac;
|
||||
using OpenDiablo2.Common.Attributes;
|
||||
using OpenDiablo2.Common.Enums;
|
||||
using OpenDiablo2.Common.Interfaces;
|
||||
|
||||
@ -9,24 +11,38 @@ namespace OpenDiablo2.ServiceBus
|
||||
{
|
||||
protected override void Load(ContainerBuilder builder)
|
||||
{
|
||||
builder.RegisterType<LocalSessionManager>().AsSelf().InstancePerLifetimeScope();
|
||||
builder.RegisterType<SessionManager>().As<ISessionManager>().InstancePerLifetimeScope();
|
||||
builder.RegisterType<SessionServer>().As<ISessionServer>().InstancePerLifetimeScope();
|
||||
|
||||
var types = ThisAssembly.GetTypes().Where(x => typeof(IMessageFrame).IsAssignableFrom(x) && x.IsClass);
|
||||
foreach (var type in types)
|
||||
{
|
||||
var att = type.GetCustomAttributes(true).First(x => typeof(MessageFrameAttribute).IsAssignableFrom(x.GetType())) as MessageFrameAttribute;
|
||||
builder
|
||||
.RegisterType(type)
|
||||
.Keyed<IMessageFrame>(att.FrameType)
|
||||
.InstancePerDependency();
|
||||
}
|
||||
|
||||
builder.Register<Func<eMessageFrameType, IMessageFrame>>(c =>
|
||||
{
|
||||
var componentContext = c.Resolve<IComponentContext>();
|
||||
return (frameType) => componentContext.ResolveKeyed<IMessageFrame>(frameType);
|
||||
});
|
||||
|
||||
builder.Register<Func<eSessionType, ISessionManager>>(c =>
|
||||
{
|
||||
var componentContext = c.Resolve<IComponentContext>();
|
||||
return (sessionType) =>
|
||||
{
|
||||
switch (sessionType)
|
||||
{
|
||||
case eSessionType.Local:
|
||||
return componentContext.Resolve<LocalSessionManager>();
|
||||
case eSessionType.Server:
|
||||
case eSessionType.Remote:
|
||||
default:
|
||||
throw new ApplicationException("Unsupported session type.");
|
||||
}
|
||||
};
|
||||
return (sessionType) => componentContext.Resolve<ISessionManager>(new NamedParameter("sessionType", sessionType));
|
||||
});
|
||||
|
||||
builder.Register<Func<eSessionType, ISessionServer>>(c =>
|
||||
{
|
||||
var componentContext = c.Resolve<IComponentContext>();
|
||||
return (sessionType) => componentContext.Resolve<ISessionServer>(new NamedParameter("sessionType", sessionType));
|
||||
});
|
||||
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -1,47 +0,0 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
using OpenDiablo2.Common.Interfaces;
|
||||
|
||||
namespace OpenDiablo2.ServiceBus
|
||||
{
|
||||
public sealed class LocalSessionManager : ISessionManager
|
||||
{
|
||||
private static readonly log4net.ILog log = log4net.LogManager.GetLogger(System.Reflection.MethodBase.GetCurrentMethod().DeclaringType);
|
||||
|
||||
private readonly ISessionServer sessionServer;
|
||||
volatile bool running = false;
|
||||
|
||||
public LocalSessionManager(ISessionServer sessionServer)
|
||||
{
|
||||
this.sessionServer = sessionServer;
|
||||
}
|
||||
|
||||
public void Initialize()
|
||||
{
|
||||
log.Info("Initializing a local multiplayer session.");
|
||||
running = true;
|
||||
Task.Run(() => Listen());
|
||||
}
|
||||
|
||||
private void Listen()
|
||||
{
|
||||
log.Info("Local session manager is starting.");
|
||||
while (running)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
log.Info("Local session manager has stopped.");
|
||||
}
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
public void Stop() => running = false;
|
||||
}
|
||||
}
|
49
OpenDiablo2.ServiceBus/Message Frames/MFJoinGame.cs
Normal file
49
OpenDiablo2.ServiceBus/Message Frames/MFJoinGame.cs
Normal file
@ -0,0 +1,49 @@
|
||||
using System;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using OpenDiablo2.Common.Attributes;
|
||||
using OpenDiablo2.Common.Enums;
|
||||
using OpenDiablo2.Common.Interfaces;
|
||||
|
||||
namespace OpenDiablo2.ServiceBus.Message_Frames
|
||||
{
|
||||
[MessageFrame(eMessageFrameType.JoinGame)]
|
||||
public sealed class MFJoinGame : IMessageFrame
|
||||
{
|
||||
public Guid PlayerId { get; set; }
|
||||
public string PlayerName { get; set; }
|
||||
public byte[] Data
|
||||
{
|
||||
get
|
||||
{
|
||||
return PlayerId.ToByteArray()
|
||||
.Concat(BitConverter.GetBytes((UInt16)PlayerName.Length))
|
||||
.Concat(Encoding.UTF8.GetBytes(PlayerName))
|
||||
.ToArray();
|
||||
}
|
||||
|
||||
set
|
||||
{
|
||||
|
||||
PlayerId = new Guid(value.Take(16).ToArray());
|
||||
var playerNameLen = BitConverter.ToUInt16(value, 16);
|
||||
PlayerName = Encoding.UTF8.GetString(value, 18, value.Length - 18);
|
||||
|
||||
if (PlayerName.Length != playerNameLen)
|
||||
throw new ApplicationException("Invalid player length!");
|
||||
}
|
||||
}
|
||||
|
||||
public MFJoinGame() { }
|
||||
public MFJoinGame(string playerName)
|
||||
{
|
||||
PlayerId = Guid.NewGuid();
|
||||
PlayerName = playerName;
|
||||
}
|
||||
|
||||
public void Process(object sender, ISessionEventProvider sessionEventProvider)
|
||||
{
|
||||
sessionEventProvider.OnJoinGame(sender, PlayerId, PlayerName);
|
||||
}
|
||||
}
|
||||
}
|
35
OpenDiablo2.ServiceBus/Message Frames/MFSetSeed.cs
Normal file
35
OpenDiablo2.ServiceBus/Message Frames/MFSetSeed.cs
Normal file
@ -0,0 +1,35 @@
|
||||
using System;
|
||||
using OpenDiablo2.Common.Attributes;
|
||||
using OpenDiablo2.Common.Enums;
|
||||
using OpenDiablo2.Common.Interfaces;
|
||||
|
||||
namespace OpenDiablo2.ServiceBus.Message_Frames
|
||||
{
|
||||
[MessageFrame(eMessageFrameType.SetSeed)]
|
||||
public sealed class MFSetSeed : IMessageFrame
|
||||
{
|
||||
|
||||
public byte[] Data
|
||||
{
|
||||
get => BitConverter.GetBytes(Seed);
|
||||
set => BitConverter.ToInt32(value, 0);
|
||||
}
|
||||
|
||||
public Int32 Seed { get; private set; }
|
||||
|
||||
public MFSetSeed()
|
||||
{
|
||||
Seed = (new Random()).Next();
|
||||
}
|
||||
|
||||
public MFSetSeed(int seed)
|
||||
{
|
||||
Seed = seed;
|
||||
}
|
||||
|
||||
public void Process(object sender, ISessionEventProvider sessionEventProvider)
|
||||
{
|
||||
sessionEventProvider.OnSetSeed?.Invoke(sender, Seed);
|
||||
}
|
||||
}
|
||||
}
|
@ -56,8 +56,11 @@
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<Compile Include="AutofacModule.cs" />
|
||||
<Compile Include="LocalSessionManager.cs" />
|
||||
<Compile Include="Message Frames\MFJoinGame.cs" />
|
||||
<Compile Include="Message Frames\MFSetSeed.cs" />
|
||||
<Compile Include="SessionManager.cs" />
|
||||
<Compile Include="Properties\AssemblyInfo.cs" />
|
||||
<Compile Include="SessionServer.cs" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<None Include="app.config" />
|
||||
|
129
OpenDiablo2.ServiceBus/SessionManager.cs
Normal file
129
OpenDiablo2.ServiceBus/SessionManager.cs
Normal file
@ -0,0 +1,129 @@
|
||||
using System;
|
||||
using System.Linq;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using NetMQ;
|
||||
using NetMQ.Sockets;
|
||||
using OpenDiablo2.Common.Attributes;
|
||||
using OpenDiablo2.Common.Enums;
|
||||
using OpenDiablo2.Common.Interfaces;
|
||||
using OpenDiablo2.ServiceBus.Message_Frames;
|
||||
|
||||
namespace OpenDiablo2.ServiceBus
|
||||
{
|
||||
public sealed class SessionManager : ISessionManager
|
||||
{
|
||||
private static readonly log4net.ILog log = log4net.LogManager.GetLogger(System.Reflection.MethodBase.GetCurrentMethod().DeclaringType);
|
||||
|
||||
private readonly Func<eSessionType, ISessionServer> getSessionServer;
|
||||
private readonly eSessionType sessionType;
|
||||
private readonly Func<eMessageFrameType, IMessageFrame> getMessageFrame;
|
||||
|
||||
private RequestSocket requestSocket;
|
||||
private AutoResetEvent resetEvent = new AutoResetEvent(false);
|
||||
private ISessionServer sessionServer;
|
||||
private Guid playerId;
|
||||
private bool running = false;
|
||||
|
||||
public OnSetSeedEvent OnSetSeed { get; set; }
|
||||
public OnJoinGameEvent OnJoinGame { get; set; }
|
||||
|
||||
public SessionManager(eSessionType sessionType, Func<eSessionType, ISessionServer> getSessionServer, Func<eMessageFrameType, IMessageFrame> getMessageFrame)
|
||||
{
|
||||
this.getSessionServer = getSessionServer;
|
||||
this.sessionType = sessionType;
|
||||
this.getMessageFrame = getMessageFrame;
|
||||
}
|
||||
|
||||
public void Initialize()
|
||||
{
|
||||
if (sessionType == eSessionType.Local || sessionType == eSessionType.Server)
|
||||
{
|
||||
sessionServer = getSessionServer(sessionType);
|
||||
sessionServer.Start();
|
||||
sessionServer.WaitServerStartEvent.WaitOne(); // Wait until the server starts...
|
||||
}
|
||||
else sessionServer = null;
|
||||
|
||||
log.Info("Initializing a local multiplayer session.");
|
||||
Task.Run(() => Listen());
|
||||
}
|
||||
|
||||
private void Listen()
|
||||
{
|
||||
log.Info("Session manager is starting.");
|
||||
requestSocket = new RequestSocket();
|
||||
|
||||
switch (sessionType)
|
||||
{
|
||||
case eSessionType.Local:
|
||||
requestSocket.Connect("inproc://opendiablo2-session");
|
||||
break;
|
||||
case eSessionType.Server:
|
||||
case eSessionType.Remote:
|
||||
default:
|
||||
throw new ApplicationException("This session type is currently unsupported.");
|
||||
}
|
||||
|
||||
|
||||
//var bytes = message.First().ToByteArray();
|
||||
//var frameType = (eMessageFrameType)bytes[0];
|
||||
//var frameData = bytes.Skip(1).ToArray(); // TODO: Can we maybe use pointers? This seems wasteful
|
||||
//var messageFrame = getMessageFrame(frameType);
|
||||
//messageFrame.Data = frameData;
|
||||
//messageFrame.Process(socket, this);
|
||||
|
||||
running = true;
|
||||
resetEvent.WaitOne();
|
||||
running = false;
|
||||
requestSocket.Dispose();
|
||||
log.Info("Session manager has stopped.");
|
||||
|
||||
}
|
||||
public void Stop()
|
||||
{
|
||||
if (!running)
|
||||
return;
|
||||
|
||||
resetEvent.Set();
|
||||
|
||||
if (sessionType == eSessionType.Local || sessionType == eSessionType.Server)
|
||||
sessionServer?.Stop();
|
||||
|
||||
}
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
Stop();
|
||||
}
|
||||
|
||||
public void Send(IMessageFrame messageFrame)
|
||||
{
|
||||
var attr = messageFrame.GetType().GetCustomAttributes(true).First(x => typeof(MessageFrameAttribute).IsAssignableFrom(x.GetType())) as MessageFrameAttribute;
|
||||
requestSocket.SendFrame(new byte[] { (byte)attr.FrameType }.Concat(messageFrame.Data).ToArray());
|
||||
}
|
||||
|
||||
private void ProcessMessageFrame<T>() where T : IMessageFrame, new()
|
||||
{
|
||||
if (!running)
|
||||
throw new ApplicationException("You have made a terrible mistake. Cannot get a message frame if you are not connected.");
|
||||
|
||||
var bytes = requestSocket.ReceiveFrameBytes();
|
||||
var frameType = (eMessageFrameType)bytes[0];
|
||||
var frameData = bytes.Skip(1).ToArray(); // TODO: Can we maybe use pointers? This seems wasteful
|
||||
var messageFrame = getMessageFrame(frameType);
|
||||
if (messageFrame.GetType() != typeof(T))
|
||||
throw new ApplicationException("Recieved unexpected message frame!");
|
||||
messageFrame.Data = frameData;
|
||||
messageFrame.Process(requestSocket, this);
|
||||
}
|
||||
|
||||
public void JoinGame(string playerName)
|
||||
{
|
||||
var mf = new MFJoinGame(playerName);
|
||||
playerId = mf.PlayerId;
|
||||
Send(mf);
|
||||
ProcessMessageFrame<MFSetSeed>();
|
||||
}
|
||||
}
|
||||
}
|
104
OpenDiablo2.ServiceBus/SessionServer.cs
Normal file
104
OpenDiablo2.ServiceBus/SessionServer.cs
Normal file
@ -0,0 +1,104 @@
|
||||
using System;
|
||||
using System.Linq;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using NetMQ;
|
||||
using NetMQ.Sockets;
|
||||
using OpenDiablo2.Common.Attributes;
|
||||
using OpenDiablo2.Common.Enums;
|
||||
using OpenDiablo2.Common.Interfaces;
|
||||
using OpenDiablo2.ServiceBus.Message_Frames;
|
||||
|
||||
namespace OpenDiablo2.ServiceBus
|
||||
{
|
||||
public sealed class SessionServer : ISessionServer, ISessionEventProvider
|
||||
{
|
||||
private static readonly log4net.ILog log = log4net.LogManager.GetLogger(System.Reflection.MethodBase.GetCurrentMethod().DeclaringType);
|
||||
|
||||
private readonly eSessionType sessionType;
|
||||
private readonly Func<eMessageFrameType, IMessageFrame> getMessageFrame;
|
||||
private AutoResetEvent resetEvent = new AutoResetEvent(false);
|
||||
public AutoResetEvent WaitServerStartEvent { get; set; } = new AutoResetEvent(false);
|
||||
|
||||
private int gameSeed;
|
||||
private bool running = false;
|
||||
private ResponseSocket responseSocket;
|
||||
|
||||
public OnSetSeedEvent OnSetSeed { get; set; }
|
||||
public OnJoinGameEvent OnJoinGame { get; set; }
|
||||
|
||||
public SessionServer(eSessionType sessionType, Func<eMessageFrameType, IMessageFrame> getMessageFrame)
|
||||
{
|
||||
this.sessionType = sessionType;
|
||||
this.getMessageFrame = getMessageFrame;
|
||||
}
|
||||
|
||||
public void Start()
|
||||
{
|
||||
gameSeed = (new Random()).Next();
|
||||
Task.Run(() => Serve());
|
||||
}
|
||||
|
||||
private void Serve()
|
||||
{
|
||||
log.Info("Session server is starting.");
|
||||
responseSocket = new ResponseSocket();
|
||||
|
||||
switch (sessionType)
|
||||
{
|
||||
case eSessionType.Local:
|
||||
responseSocket.Bind("inproc://opendiablo2-session");
|
||||
break;
|
||||
case eSessionType.Server:
|
||||
case eSessionType.Remote:
|
||||
default:
|
||||
throw new ApplicationException("This session type is currently unsupported.");
|
||||
}
|
||||
|
||||
OnJoinGame += OnJoinGameHandler;
|
||||
|
||||
var proactor = new NetMQProactor(responseSocket, (socket, message) =>
|
||||
{
|
||||
var bytes = message.First().ToByteArray();
|
||||
var frameType = (eMessageFrameType)bytes[0];
|
||||
var frameData = bytes.Skip(1).ToArray(); // TODO: Can we maybe use pointers? This seems wasteful
|
||||
var messageFrame = getMessageFrame(frameType);
|
||||
messageFrame.Data = frameData;
|
||||
messageFrame.Process(socket, this);
|
||||
});
|
||||
running = true;
|
||||
WaitServerStartEvent.Set();
|
||||
resetEvent.WaitOne();
|
||||
proactor.Dispose();
|
||||
running = false;
|
||||
responseSocket.Dispose();
|
||||
log.Info("Session server has stopped.");
|
||||
}
|
||||
|
||||
|
||||
public void Stop()
|
||||
{
|
||||
if (!running)
|
||||
return;
|
||||
|
||||
resetEvent.Set();
|
||||
}
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
Stop();
|
||||
}
|
||||
|
||||
private void Send(NetMQSocket target, IMessageFrame messageFrame)
|
||||
{
|
||||
var attr = messageFrame.GetType().GetCustomAttributes(true).First(x => typeof(MessageFrameAttribute).IsAssignableFrom(x.GetType())) as MessageFrameAttribute;
|
||||
responseSocket.SendFrame(new byte[] { (byte)attr.FrameType }.Concat(messageFrame.Data).ToArray());
|
||||
}
|
||||
|
||||
private void OnJoinGameHandler(object sender, Guid playerId, string playerName)
|
||||
{
|
||||
// TODO: Try to make this less stupid
|
||||
Send(sender as NetMQSocket, new MFSetSeed(gameSeed));
|
||||
}
|
||||
}
|
||||
}
|
@ -5,7 +5,7 @@
|
||||
<Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration>
|
||||
<Platform Condition=" '$(Platform)' == '' ">AnyCPU</Platform>
|
||||
<ProjectGuid>{2B0CF1AC-06DD-4322-AE8B-FF8A8C70A3CD}</ProjectGuid>
|
||||
<OutputType>WinExe</OutputType>
|
||||
<OutputType>Exe</OutputType>
|
||||
<RootNamespace>OpenDiablo2</RootNamespace>
|
||||
<AssemblyName>OpenDiablo2</AssemblyName>
|
||||
<TargetFrameworkVersion>v4.6.1</TargetFrameworkVersion>
|
||||
|
@ -34,9 +34,20 @@ namespace OpenDiablo2
|
||||
try
|
||||
{
|
||||
#endif
|
||||
BuildContainer()
|
||||
.Resolve<IGameEngine>()
|
||||
.Run();
|
||||
var container = BuildContainer();
|
||||
try
|
||||
{
|
||||
using (var gameEngine = container.Resolve<IGameEngine>())
|
||||
{
|
||||
gameEngine.Run();
|
||||
}
|
||||
}
|
||||
finally
|
||||
{
|
||||
container.Dispose();
|
||||
}
|
||||
|
||||
|
||||
#if !DEBUG
|
||||
}
|
||||
catch (Exception ex)
|
||||
|
@ -29,7 +29,7 @@ You need to have MonoDevelop installed, as well as any depenencies for that. You
|
||||
## Command Line Parameters
|
||||
| Long Name | Description |
|
||||
| ------------ | ------------------------------------------------------------ |
|
||||
| --datapath | (-d) Defines the path where the data files can be found |
|
||||
| --datapath | (-p) Defines the path where the data files can be found |
|
||||
| --hwmouse | Use the hardware mouse instead of software |
|
||||
| --mousescale | When hardware mouse is enabled, this defines the pixel scale of the mouse. No effect for software mode |
|
||||
| --fullscreen | (-f) When set, the game launches in full screen mode at 800x600. |
|
||||
|
Loading…
Reference in New Issue
Block a user