mirror of
https://github.com/OpenDiablo2/OpenDiablo2
synced 2024-10-01 15:46:17 -04: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 System.Drawing;
|
||||||
using OpenDiablo2.Common.Enums;
|
using OpenDiablo2.Common.Enums;
|
||||||
using OpenDiablo2.Common.Models;
|
using OpenDiablo2.Common.Models;
|
||||||
|
|
||||||
namespace OpenDiablo2.Common.Interfaces
|
namespace OpenDiablo2.Common.Interfaces
|
||||||
{
|
{
|
||||||
public interface IGameState
|
public interface IGameState : IDisposable
|
||||||
{
|
{
|
||||||
int Act { get; }
|
int Act { get; }
|
||||||
int Seed { get; }
|
int Seed { get; }
|
||||||
|
@ -6,7 +6,9 @@ using System.Threading.Tasks;
|
|||||||
|
|
||||||
namespace OpenDiablo2.Common.Interfaces
|
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
|
namespace OpenDiablo2.Common.Interfaces
|
||||||
{
|
{
|
||||||
public interface ISessionManager : IDisposable
|
|
||||||
|
public interface ISessionManager : ISessionEventProvider, IDisposable
|
||||||
{
|
{
|
||||||
void Initialize();
|
void Initialize();
|
||||||
void Stop();
|
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
|
// TODO: DI magic please
|
||||||
public MPQDS1(Stream stream, LevelPreset level, LevelDetail levelDetail, LevelType levelType, IEngineDataManager engineDataManager, IResourceManager resourceManager)
|
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);
|
var br = new BinaryReader(stream);
|
||||||
Version = br.ReadInt32();
|
Version = br.ReadInt32();
|
||||||
Width = br.ReadInt32() + 1;
|
Width = br.ReadInt32() + 1;
|
||||||
@ -296,8 +295,6 @@ namespace OpenDiablo2.Common.Models
|
|||||||
if (!isMasked || levelType.File[i] == "0")
|
if (!isMasked || levelType.File[i] == "0")
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
log.Debug($"Loading DT resource {levelType.File[i]}");
|
|
||||||
|
|
||||||
DT1s[i] = resourceManager.GetMPQDT1("data\\global\\tiles\\" + levelType.File[i].Replace("/", "\\"));
|
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 class PlayerState : MobState
|
||||||
{
|
{
|
||||||
|
public Guid Id { get; protected set; }
|
||||||
public eHero HeroType { get; protected set; }
|
public eHero HeroType { get; protected set; }
|
||||||
private IHeroTypeConfig HeroTypeConfig;
|
private IHeroTypeConfig HeroTypeConfig;
|
||||||
private ILevelExperienceConfig ExperienceConfig;
|
private ILevelExperienceConfig ExperienceConfig;
|
||||||
|
@ -52,8 +52,10 @@
|
|||||||
<Reference Include="System.Xml" />
|
<Reference Include="System.Xml" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
|
<Compile Include="Attributes\MessageFrameAttribute.cs" />
|
||||||
<Compile Include="Attributes\SceneAttribute.cs" />
|
<Compile Include="Attributes\SceneAttribute.cs" />
|
||||||
<Compile Include="AutofacModule.cs" />
|
<Compile Include="AutofacModule.cs" />
|
||||||
|
<Compile Include="Enums\eMessageFrameType.cs" />
|
||||||
<Compile Include="Enums\ePanelFrameType.cs" />
|
<Compile Include="Enums\ePanelFrameType.cs" />
|
||||||
<Compile Include="Enums\eButtonType.cs" />
|
<Compile Include="Enums\eButtonType.cs" />
|
||||||
<Compile Include="Enums\eHero.cs" />
|
<Compile Include="Enums\eHero.cs" />
|
||||||
@ -67,8 +69,10 @@
|
|||||||
<Compile Include="Enums\Mobs\eDamageTypes.cs" />
|
<Compile Include="Enums\Mobs\eDamageTypes.cs" />
|
||||||
<Compile Include="Enums\Mobs\eMobFlags.cs" />
|
<Compile Include="Enums\Mobs\eMobFlags.cs" />
|
||||||
<Compile Include="Enums\Mobs\eStatModifierType.cs" />
|
<Compile Include="Enums\Mobs\eStatModifierType.cs" />
|
||||||
<Compile Include="Interfaces\ISessionManager.cs" />
|
<Compile Include="Interfaces\MessageBus\ISessionEventProvider.cs" />
|
||||||
<Compile Include="Interfaces\ISessionServer.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\IButton.cs" />
|
||||||
<Compile Include="Interfaces\UI\IPanelFrame.cs" />
|
<Compile Include="Interfaces\UI\IPanelFrame.cs" />
|
||||||
<Compile Include="Interfaces\UI\IInventoryPanel.cs" />
|
<Compile Include="Interfaces\UI\IInventoryPanel.cs" />
|
||||||
@ -137,9 +141,6 @@
|
|||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<None Include="packages.config" />
|
<None Include="packages.config" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
<ItemGroup>
|
<ItemGroup />
|
||||||
<Folder Include="Message Frames\Requests\" />
|
|
||||||
<Folder Include="Message Frames\Responses\" />
|
|
||||||
</ItemGroup>
|
|
||||||
<Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
|
<Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
|
||||||
</Project>
|
</Project>
|
@ -27,7 +27,6 @@ namespace OpenDiablo2.Core
|
|||||||
builder.RegisterType<ResourceManager>().As<IResourceManager>().SingleInstance();
|
builder.RegisterType<ResourceManager>().As<IResourceManager>().SingleInstance();
|
||||||
builder.RegisterType<TextDictionary>().As<ITextDictionary>().SingleInstance();
|
builder.RegisterType<TextDictionary>().As<ITextDictionary>().SingleInstance();
|
||||||
builder.RegisterType<TextBox>().As<ITextBox>().InstancePerDependency();
|
builder.RegisterType<TextBox>().As<ITextBox>().InstancePerDependency();
|
||||||
builder.RegisterType<SessionServer>().As<ISessionServer>().InstancePerLifetimeScope();
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -60,14 +60,20 @@ namespace OpenDiablo2.Core.GameState_
|
|||||||
sessionManager = getSessionManager(sessionType);
|
sessionManager = getSessionManager(sessionType);
|
||||||
sessionManager.Initialize();
|
sessionManager.Initialize();
|
||||||
|
|
||||||
var random = new Random();
|
sessionManager.OnSetSeed += OnSetSeedEvent;
|
||||||
Seed = random.Next(); // TODO: Seed does not go here ;-(
|
|
||||||
|
|
||||||
sceneManager.ChangeScene("Game");
|
|
||||||
mapInfo = new List<MapInfo>();
|
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)
|
public MapInfo LoadSubMap(int levelDefId, Point origin)
|
||||||
{
|
{
|
||||||
@ -391,5 +397,10 @@ namespace OpenDiablo2.Core.GameState_
|
|||||||
animationTime += ((float)ms / 1000f);
|
animationTime += ((float)ms / 1000f);
|
||||||
animationTime -= (float)Math.Truncate(animationTime);
|
animationTime -= (float)Math.Truncate(animationTime);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void Dispose()
|
||||||
|
{
|
||||||
|
sessionManager?.Dispose();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -65,7 +65,6 @@
|
|||||||
<Compile Include="MPQProvider.cs" />
|
<Compile Include="MPQProvider.cs" />
|
||||||
<Compile Include="Properties\AssemblyInfo.cs" />
|
<Compile Include="Properties\AssemblyInfo.cs" />
|
||||||
<Compile Include="ResourceManager.cs" />
|
<Compile Include="ResourceManager.cs" />
|
||||||
<Compile Include="SessionServer.cs" />
|
|
||||||
<Compile Include="TextDictionary.cs" />
|
<Compile Include="TextDictionary.cs" />
|
||||||
<Compile Include="UI\Button.cs" />
|
<Compile Include="UI\Button.cs" />
|
||||||
<Compile Include="UI\PanelFrame.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;
|
||||||
|
using System.Linq;
|
||||||
using Autofac;
|
using Autofac;
|
||||||
|
using OpenDiablo2.Common.Attributes;
|
||||||
using OpenDiablo2.Common.Enums;
|
using OpenDiablo2.Common.Enums;
|
||||||
using OpenDiablo2.Common.Interfaces;
|
using OpenDiablo2.Common.Interfaces;
|
||||||
|
|
||||||
@ -9,24 +11,38 @@ namespace OpenDiablo2.ServiceBus
|
|||||||
{
|
{
|
||||||
protected override void Load(ContainerBuilder builder)
|
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 =>
|
builder.Register<Func<eSessionType, ISessionManager>>(c =>
|
||||||
{
|
{
|
||||||
var componentContext = c.Resolve<IComponentContext>();
|
var componentContext = c.Resolve<IComponentContext>();
|
||||||
return (sessionType) =>
|
return (sessionType) => componentContext.Resolve<ISessionManager>(new NamedParameter("sessionType", sessionType));
|
||||||
{
|
|
||||||
switch (sessionType)
|
|
||||||
{
|
|
||||||
case eSessionType.Local:
|
|
||||||
return componentContext.Resolve<LocalSessionManager>();
|
|
||||||
case eSessionType.Server:
|
|
||||||
case eSessionType.Remote:
|
|
||||||
default:
|
|
||||||
throw new ApplicationException("Unsupported session type.");
|
|
||||||
}
|
|
||||||
};
|
|
||||||
});
|
});
|
||||||
|
|
||||||
|
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>
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<Compile Include="AutofacModule.cs" />
|
<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="Properties\AssemblyInfo.cs" />
|
||||||
|
<Compile Include="SessionServer.cs" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<None Include="app.config" />
|
<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>
|
<Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration>
|
||||||
<Platform Condition=" '$(Platform)' == '' ">AnyCPU</Platform>
|
<Platform Condition=" '$(Platform)' == '' ">AnyCPU</Platform>
|
||||||
<ProjectGuid>{2B0CF1AC-06DD-4322-AE8B-FF8A8C70A3CD}</ProjectGuid>
|
<ProjectGuid>{2B0CF1AC-06DD-4322-AE8B-FF8A8C70A3CD}</ProjectGuid>
|
||||||
<OutputType>WinExe</OutputType>
|
<OutputType>Exe</OutputType>
|
||||||
<RootNamespace>OpenDiablo2</RootNamespace>
|
<RootNamespace>OpenDiablo2</RootNamespace>
|
||||||
<AssemblyName>OpenDiablo2</AssemblyName>
|
<AssemblyName>OpenDiablo2</AssemblyName>
|
||||||
<TargetFrameworkVersion>v4.6.1</TargetFrameworkVersion>
|
<TargetFrameworkVersion>v4.6.1</TargetFrameworkVersion>
|
||||||
|
@ -34,9 +34,20 @@ namespace OpenDiablo2
|
|||||||
try
|
try
|
||||||
{
|
{
|
||||||
#endif
|
#endif
|
||||||
BuildContainer()
|
var container = BuildContainer();
|
||||||
.Resolve<IGameEngine>()
|
try
|
||||||
.Run();
|
{
|
||||||
|
using (var gameEngine = container.Resolve<IGameEngine>())
|
||||||
|
{
|
||||||
|
gameEngine.Run();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
finally
|
||||||
|
{
|
||||||
|
container.Dispose();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
#if !DEBUG
|
#if !DEBUG
|
||||||
}
|
}
|
||||||
catch (Exception ex)
|
catch (Exception ex)
|
||||||
|
@ -29,7 +29,7 @@ You need to have MonoDevelop installed, as well as any depenencies for that. You
|
|||||||
## Command Line Parameters
|
## Command Line Parameters
|
||||||
| Long Name | Description |
|
| 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 |
|
| --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 |
|
| --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. |
|
| --fullscreen | (-f) When set, the game launches in full screen mode at 800x600. |
|
||||||
|
Loading…
Reference in New Issue
Block a user