1
1
mirror of https://github.com/OpenDiablo2/OpenDiablo2 synced 2024-06-20 14:15:23 +00:00

Fleshed out mob management on server. Added player location message frame.

This commit is contained in:
Tim Sarbin 2018-12-01 02:44:40 -05:00
parent 1fc0eec1be
commit 1c5a0d3361
35 changed files with 461 additions and 349 deletions

View File

@ -27,7 +27,7 @@ namespace OpenDiablo2.Common.UT
2250,
4125,
});
PlayerState ps = new PlayerState("player1", id: 1, level: 1, x: 0, y: 0,
PlayerState ps = new PlayerState(0, "player1", id: 1, level: 1, x: 0, y: 0,
vitality: herotypeconfig.StartingVitality,
strength: herotypeconfig.StartingStrength,
energy: herotypeconfig.StartingEnergy,

View File

@ -6,11 +6,13 @@ using System.Threading.Tasks;
namespace OpenDiablo2.Common.Enums
{
// TODO: I don't think this needs to live in core...
public enum eMessageFrameType
{
None = 0x00,
SetSeed = 0x01,
JoinGame = 0x02,
LocatePlayers = 0x03,
MAX = 0xFF, // NOTE:
// You absolutely cannot have a higher ID than this without

View File

@ -0,0 +1,12 @@
using System.Collections.Generic;
using System.Linq;
using OpenDiablo2.Common.Models.Mobs;
namespace OpenDiablo2.Common.Extensions
{
public static class MobManagerExtensions
{
public static IEnumerable<MobState> FindInRadius(this IEnumerable<MobState> mobs, float centerx, float centery, float radius)
=> mobs.Where(x => x.GetDistance(centerx, centery) <= radius);
}
}

View File

@ -0,0 +1,16 @@
using System;
using System.Collections.Generic;
using OpenDiablo2.Common.Enums;
using OpenDiablo2.Common.Models.Mobs;
namespace OpenDiablo2.Common.Interfaces
{
public interface IGameServer : IDisposable
{
IEnumerable<PlayerState> Players { get; }
int Seed { get; }
void InitializeNewGame();
int SpawnNewPlayer(int clientHash, string playerName, eHero heroType);
}
}

View File

@ -1,11 +1,10 @@
using System;
using System.Drawing;
using OpenDiablo2.Common.Models;
using System.Drawing;
namespace OpenDiablo2.Common.Interfaces
{
public interface IMapEngine
{
int FocusedMobId { get; set; }
PointF CameraLocation { get; set; }
void Update(long ms);
void Render();

View File

@ -9,6 +9,6 @@ namespace OpenDiablo2.Common.Interfaces
public interface IMessageFrame
{
byte[] Data { get; set; }
void Process(object sender, ISessionEventProvider sessionEventProvider);
void Process(int clientHash, ISessionEventProvider sessionEventProvider);
}
}

View File

@ -1,18 +1,18 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using OpenDiablo2.Common.Enums;
using OpenDiablo2.Common.Interfaces.MessageBus;
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 delegate void OnSetSeedEvent(int clientHash, int seed);
public delegate void OnJoinGameEvent(int clientHash, eHero heroType, string playerName);
public delegate void OnLocatePlayersEvent(int clientHash, IEnumerable<PlayerLocationDetails> playerLocationDetails);
public interface ISessionEventProvider
{
OnSetSeedEvent OnSetSeed { get; set; }
OnJoinGameEvent OnJoinGame { get; set; }
OnLocatePlayersEvent OnLocatePlayers { get; set; }
}
}

View File

@ -3,15 +3,18 @@ using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using OpenDiablo2.Common.Enums;
namespace OpenDiablo2.Common.Interfaces
{
public interface ISessionManager : ISessionEventProvider, IDisposable
{
Guid PlayerId { get; }
void Initialize();
void Stop();
void JoinGame(string playerName, Action<Guid> callback);
void JoinGame(string playerName, eHero heroType);
}
}

View File

@ -0,0 +1,44 @@
using System;
using System.Collections.Generic;
using OpenDiablo2.Common.Models.Mobs;
namespace OpenDiablo2.Common.Interfaces.MessageBus
{
public sealed class PlayerLocationDetails
{
public int PlayerId { get; set; }
public float PlayerX { get; set; }
public float PlayerY { get; set; }
// TODO: They may not be on the same 'anchor map'...
public byte[] GetBytes()
{
var result = new List<byte>();
result.AddRange(BitConverter.GetBytes(PlayerId));
result.AddRange(BitConverter.GetBytes(PlayerX));
result.AddRange(BitConverter.GetBytes(PlayerY));
return result.ToArray();
}
public static PlayerLocationDetails FromBytes(byte[] data, int offset = 0)
=> new PlayerLocationDetails
{
PlayerId = BitConverter.ToInt32(data, offset + 0),
PlayerX = BitConverter.ToSingle(data, offset + 4),
PlayerY = BitConverter.ToSingle(data, offset + 8)
};
public static int SizeInBytes => 12;
}
public static class PlayerLocationDetailsExtensions
{
public static PlayerLocationDetails ToPlayerLocationDetails(this PlayerState source)
=> new PlayerLocationDetails
{
PlayerId = source.Id,
PlayerX = source.GetPosition().X,
PlayerY = source.GetPosition().Y
};
}
}

View File

@ -1,14 +0,0 @@
using OpenDiablo2.Common.Models.Mobs;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace OpenDiablo2.Common.Interfaces.Mobs
{
public interface IMobCondition
{
bool Evaluate(MobState mob);
}
}

View File

@ -1,13 +1,22 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Collections.Generic;
using OpenDiablo2.Common.Models.Mobs;
namespace OpenDiablo2.Common.Interfaces.Mobs
{
public interface IMobManager
{
// TODO:
IEnumerable<MobState> Mobs { get; }
IEnumerable<PlayerState> Players { get; }
IEnumerable<EnemyState> Enemies { get; }
void AddPlayer(PlayerState player);
void RemovePlayer(PlayerState player);
void AddMob(MobState mob);
void RemoveMob(MobState mob);
int GetNextAvailableMobId();
void AddEnemy(EnemyState enemy);
void RemoveEnemy(EnemyState enemy);
}
}

View File

@ -1,9 +1,4 @@
using OpenDiablo2.Common.Enums.Mobs;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace OpenDiablo2.Common.Models.Mobs
{
@ -11,6 +6,8 @@ namespace OpenDiablo2.Common.Models.Mobs
{
public int ExperienceGiven { get; protected set; }
public EnemyState() : base() { }
public EnemyState(string name, int id, int level, int maxhealth, float x, float y, int experiencegiven)
: base(name, id, level, maxhealth, x, y)
{

View File

@ -1,97 +0,0 @@
using OpenDiablo2.Common.Enums.Mobs;
using OpenDiablo2.Common.Interfaces.Mobs;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace OpenDiablo2.Common.Models.Mobs
{
public class MobConditionAnd : IMobCondition
{
public List<IMobCondition> Conditions = new List<IMobCondition>();
public MobConditionAnd(List<IMobCondition> conditions)
{
Conditions = conditions;
}
public bool Evaluate(MobState mob)
{
foreach(IMobCondition condition in Conditions)
{
if (!condition.Evaluate(mob))
{
return false;
}
}
return true;
}
}
public class MobConditionOr : IMobCondition
{
public List<IMobCondition> Conditions = new List<IMobCondition>();
public MobConditionOr(List<IMobCondition> conditions)
{
Conditions = conditions;
}
public bool Evaluate(MobState mob)
{
foreach (IMobCondition condition in Conditions)
{
if (condition.Evaluate(mob))
{
return true;
}
}
return false;
}
}
public class MobConditionNot : IMobCondition
{
public IMobCondition Condition = null;
public MobConditionNot(IMobCondition condition)
{
Condition = condition;
}
public bool Evaluate(MobState mob)
{
if(Condition == null)
{
return false;
}
return !Condition.Evaluate(mob);
}
}
public class MobConditionFlags : IMobCondition
{
public Dictionary<eMobFlags, bool> Flags = new Dictionary<eMobFlags, bool>();
public MobConditionFlags(Dictionary<eMobFlags, bool> flags)
{
Flags = flags;
}
public bool Evaluate(MobState mob)
{
foreach(eMobFlags flag in Flags.Keys)
{
if(Flags[flag] != mob.HasFlag(flag))
{
return false;
}
}
return true;
}
}
// TODO: implement more of these
}

View File

@ -1,14 +1,11 @@
using OpenDiablo2.Common.Enums.Mobs;
using System;
using System;
using System.Collections.Generic;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using OpenDiablo2.Common.Enums.Mobs;
namespace OpenDiablo2.Common.Models.Mobs
{
public class MobState
public class MobState : IComparable
{
public readonly string Name;
public readonly int Id;
@ -26,6 +23,8 @@ namespace OpenDiablo2.Common.Models.Mobs
protected Dictionary<eMobFlags, bool> Flags = new Dictionary<eMobFlags, bool>();
public MobState() { }
public MobState(string name, int id, int level, int maxhealth, float x, float y)
{
Name = name;
@ -163,6 +162,10 @@ namespace OpenDiablo2.Common.Models.Mobs
}
return false;
}
#endregion Flags
public int CompareTo(object obj) => Id - ((MobState)obj).Id;
}
}

View File

@ -1,27 +1,23 @@
using OpenDiablo2.Common.Enums;
using System;
using OpenDiablo2.Common.Enums;
using OpenDiablo2.Common.Enums.Mobs;
using OpenDiablo2.Common.Interfaces.Mobs;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
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;
public int ClientHash { get; protected set; }
// Player character stats
protected Stat Vitality;
protected Stat Strength;
protected Stat Energy;
protected Stat Dexterity;
protected Stat DefenseRating;
protected Stat AttackRating;
@ -30,11 +26,14 @@ namespace OpenDiablo2.Common.Models.Mobs
public int Experience { get; protected set; }
public PlayerState(string name, int id, int level, float x, float y,
int vitality, int strength, int energy, int dexterity, int experience, eHero herotype,
public PlayerState() : base() { }
public PlayerState(int clientHash, string name, int id, int level, float x, float y,
int vitality, int strength, int energy, int dexterity, int experience, eHero herotype,
IHeroTypeConfig heroconfig, ILevelExperienceConfig expconfig)
: base(name, id, level, 0, x, y)
{
this.ClientHash = clientHash;
Stamina = new Stat(0, 0, 0, true);
Mana = new Stat(0, 0, 0, true);
@ -43,7 +42,7 @@ namespace OpenDiablo2.Common.Models.Mobs
Energy = new Stat(0, energy, energy, true);
Dexterity = new Stat(0, dexterity, dexterity, true);
AttackRating = new Stat(0, 0, 0, false);
AttackRating = new Stat(0, 0, 0, false);
DefenseRating = new Stat(0, 0, 0, false);
Experience = experience; // how much total exp do they have
@ -82,11 +81,11 @@ namespace OpenDiablo2.Common.Models.Mobs
}
public bool TryLevelUp()
{
if(Level >= GetMaxLevel())
if (Level >= GetMaxLevel())
{
return false; // can't level up anymore
}
if(GetExperienceToLevel() > 0)
if (GetExperienceToLevel() > 0)
{
return false; // not enough exp
}

View File

@ -69,10 +69,13 @@
<Compile Include="Enums\Mobs\eDamageTypes.cs" />
<Compile Include="Enums\Mobs\eMobFlags.cs" />
<Compile Include="Enums\Mobs\eStatModifierType.cs" />
<Compile Include="Extensions\MobManagerExtensions.cs" />
<Compile Include="Interfaces\IGameServer.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\MessageBus\PlayerLocationDetails.cs" />
<Compile Include="Interfaces\UI\IButton.cs" />
<Compile Include="Interfaces\UI\IPanelFrame.cs" />
<Compile Include="Interfaces\UI\IInventoryPanel.cs" />
@ -101,7 +104,6 @@
<Compile Include="Interfaces\Drawing\ITexture.cs" />
<Compile Include="Interfaces\Mobs\IHeroTypeConfig.cs" />
<Compile Include="Interfaces\Mobs\ILevelExperienceConfig.cs" />
<Compile Include="Interfaces\Mobs\IMobCondition.cs" />
<Compile Include="Interfaces\Mobs\IMobManager.cs" />
<Compile Include="Interfaces\Mobs\IStatModifier.cs" />
<Compile Include="Models\BitStream.cs" />
@ -114,7 +116,6 @@
<Compile Include="Models\Mobs\EnemyState.cs" />
<Compile Include="Models\Mobs\HeroTypeConfig.cs" />
<Compile Include="Models\Mobs\LevelExperienceConfig.cs" />
<Compile Include="Models\Mobs\MobCondition.cs" />
<Compile Include="Models\Mobs\MobState.cs" />
<Compile Include="Models\Mobs\PlayerState.cs" />
<Compile Include="Models\Mobs\Stat.cs" />

View File

@ -1,13 +1,9 @@
using Microsoft.VisualStudio.TestTools.UnitTesting;
using System.Linq;
using Microsoft.VisualStudio.TestTools.UnitTesting;
using OpenDiablo2.Common.Enums.Mobs;
using OpenDiablo2.Common.Extensions;
using OpenDiablo2.Common.Models.Mobs;
using OpenDiablo2.Core.GameState_;
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace OpenDiablo2.Core.UT
{
@ -19,34 +15,21 @@ namespace OpenDiablo2.Core.UT
{
MobManager mobman = new MobManager();
MobState mob1 = new MobState("test1", mobman.GetNextAvailableMobId(), 1, 100, 0, 0);
MobState mob2 = new MobState("test2", mobman.GetNextAvailableMobId(), 1, 100, 0, 0);
MobState mob3 = new MobState("test3", mobman.GetNextAvailableMobId(), 1, 100, 0, 0);
mob1.AddFlag(eMobFlags.ENEMY);
mobman.AddMob(mob1);
MobState mob2 = new MobState("test2", mobman.GetNextAvailableMobId(), 1, 100, 0, 0);
mob2.AddFlag(eMobFlags.ENEMY);
mob2.AddFlag(eMobFlags.INVULNERABLE);
mobman.AddMob(mob1);
mobman.AddMob(mob2);
MobState mob3 = new MobState("test3", mobman.GetNextAvailableMobId(), 1, 100, 0, 0);
mobman.AddMob(mob3);
Assert.IsTrue(mobman.FindMobs(new MobConditionFlags(
new Dictionary<eMobFlags, bool> {
{ eMobFlags.ENEMY, true }
})).Count == 2);
Assert.IsTrue(mobman.FindMobs(new MobConditionFlags(
new Dictionary<eMobFlags, bool> {
{ eMobFlags.INVULNERABLE, true }
})).Count == 1);
Assert.IsTrue(mobman.FindMobs(new MobConditionFlags(
new Dictionary<eMobFlags, bool> {
{ eMobFlags.PLAYER, true }
})).Count == 0);
Assert.IsTrue(mobman.FindMobs(new MobConditionFlags(
new Dictionary<eMobFlags, bool> {
{ eMobFlags.PLAYER, false }
})).Count == 3);
Assert.IsTrue(mobman.Mobs.Count(x => x.HasFlag(eMobFlags.ENEMY)) == 2);
Assert.IsTrue(mobman.Mobs.Count(x => x.HasFlag(eMobFlags.INVULNERABLE)) == 1);
Assert.IsTrue(mobman.Mobs.Count(x => x.HasFlag(eMobFlags.PLAYER)) == 0);
Assert.IsTrue(mobman.Mobs.Count(x => !x.HasFlag(eMobFlags.PLAYER)) == 3);
}
[TestMethod]
@ -54,19 +37,21 @@ namespace OpenDiablo2.Core.UT
{
MobManager mobman = new MobManager();
MobState mob1 = new MobState("test1", mobman.GetNextAvailableMobId(), 1, 100, 0, 0);
MobState mob2 = new MobState("test2", mobman.GetNextAvailableMobId(), 1, 100, 10, 10);
MobState mob3 = new MobState("test3", mobman.GetNextAvailableMobId(), 1, 100, 3, 1);
mob1.AddFlag(eMobFlags.ENEMY);
mobman.AddMob(mob1);
MobState mob2 = new MobState("test2", mobman.GetNextAvailableMobId(), 1, 100, 10, 10);
mob2.AddFlag(eMobFlags.ENEMY);
mob2.AddFlag(eMobFlags.INVULNERABLE);
mobman.AddMob(mob1);
mobman.AddMob(mob2);
MobState mob3 = new MobState("test3", mobman.GetNextAvailableMobId(), 1, 100, 3, 1);
mobman.AddMob(mob3);
Assert.IsTrue(mobman.FindMobsInRadius(0, 0, 1, null).Count == 1);
Assert.IsTrue(mobman.FindMobsInRadius(0, 0, 7, null).Count == 2);
Assert.IsTrue(mobman.FindMobsInRadius(0, 0, 20, null).Count == 3);
Assert.IsTrue(mobman.FindMobsInRadius(10, 10, 1, null).Count == 1);
Assert.IsTrue(mobman.Mobs.FindInRadius(0, 0, 1).Count() == 1);
Assert.IsTrue(mobman.Mobs.FindInRadius(0, 0, 7).Count() == 2);
Assert.IsTrue(mobman.Mobs.FindInRadius(0, 0, 20).Count() == 3);
Assert.IsTrue(mobman.Mobs.FindInRadius(10, 10, 1).Count() == 1);
}
}
}

View File

@ -1,5 +1,6 @@
using Autofac;
using OpenDiablo2.Common.Interfaces;
using OpenDiablo2.Common.Interfaces.Mobs;
using OpenDiablo2.Core.GameState_;
using OpenDiablo2.Core.Map_Engine;
using OpenDiablo2.Core.UI;
@ -27,6 +28,8 @@ namespace OpenDiablo2.Core
builder.RegisterType<ResourceManager>().As<IResourceManager>().SingleInstance();
builder.RegisterType<TextDictionary>().As<ITextDictionary>().SingleInstance();
builder.RegisterType<TextBox>().As<ITextBox>().InstancePerDependency();
builder.RegisterType<MobManager>().As<IMobManager>().SingleInstance(); // TODO: This needs to have client and server versions...
}
}
}

View File

@ -1,6 +1,5 @@
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Drawing;
using System.Linq;
using System.Threading;

View File

@ -4,6 +4,7 @@ using System.Drawing;
using System.Linq;
using OpenDiablo2.Common.Enums;
using OpenDiablo2.Common.Interfaces;
using OpenDiablo2.Common.Interfaces.MessageBus;
using OpenDiablo2.Common.Models;
using OpenDiablo2.Core.Map_Engine;
@ -21,7 +22,6 @@ namespace OpenDiablo2.Core.GameState_
private readonly Func<IMapEngine> getMapEngine;
private readonly Func<eSessionType, ISessionManager> getSessionManager;
private Guid playerId;
private float animationTime = 0f;
private List<MapInfo> mapInfo;
private List<MapCellInfo> mapDataLookup = new List<MapCellInfo>();
@ -30,6 +30,7 @@ namespace OpenDiablo2.Core.GameState_
public int Act { get; private set; }
public string MapName { get; private set; }
public Palette CurrentPalette => paletteProvider.PaletteTable[$"ACT{Act}"];
public IEnumerable<PlayerLocationDetails> PlayerLocationDetails { get; private set; } = new List<PlayerLocationDetails>();
public bool ShowInventoryPanel { get; set; } = false;
public bool ShowCharacterPanel { get; set; } = false;
@ -64,19 +65,20 @@ namespace OpenDiablo2.Core.GameState_
sessionManager.Initialize();
sessionManager.OnSetSeed += OnSetSeedEvent;
sessionManager.OnLocatePlayers += OnLocatePlayers;
mapInfo = new List<MapInfo>();
sceneManager.ChangeScene("Game");
sessionManager.JoinGame(characterName, (id) =>
{
log.Info("hoo");
playerId = id;
}); // TODO: we need more attributes...
log.Info("woo");
sessionManager.JoinGame(characterName, hero);
}
private void OnSetSeedEvent(object sender, int seed)
private void OnLocatePlayers(int clientHash, IEnumerable<PlayerLocationDetails> playerLocationDetails)
{
PlayerLocationDetails = playerLocationDetails;
}
private void OnSetSeedEvent(int clientHash, int seed)
{
log.Info($"Setting seed to {seed}");
this.Seed = seed;
@ -224,15 +226,7 @@ namespace OpenDiablo2.Core.GameState_
return ShowCharacterPanel;
}
private MapCellInfo GetMapCellInfo(
MapInfo map,
int cellX,
int cellY,
MPQDS1TileProps props,
eRenderCellType cellType,
MPQDS1WallOrientationTileProps wallOrientations = null
)
private MapCellInfo GetMapCellInfo(MapInfo map, int cellX, int cellY, MPQDS1TileProps props, eRenderCellType cellType, MPQDS1WallOrientationTileProps wallOrientations = null)
{
if (!map.CellInfo.ContainsKey(cellType))
{

View File

@ -1,20 +1,21 @@
using OpenDiablo2.Common.Enums;
using System;
using System.Collections.Generic;
using OpenDiablo2.Common.Interfaces.Mobs;
using OpenDiablo2.Common.Models.Mobs;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace OpenDiablo2.Core.GameState_
{
public class MobManager : IMobManager
{
private List<MobState> Mobs = new List<MobState>(); // all mobs (including players!)
private List<PlayerState> Players = new List<PlayerState>();
private List<EnemyState> Enemies = new List<EnemyState>();
private List<int> IdsUsed = new List<int>();
public HashSet<MobState> Mobs { get; private set; } = new HashSet<MobState>(); // all mobs (including players!)
public HashSet<PlayerState> Players { get; private set; } = new HashSet<PlayerState>();
public HashSet<EnemyState> Enemies { get; private set;} = new HashSet<EnemyState>();
IEnumerable<MobState> IMobManager.Mobs => Mobs;
IEnumerable<PlayerState> IMobManager.Players => Players;
IEnumerable<EnemyState> IMobManager.Enemies => Enemies;
private HashSet<int> IdsUsed = new HashSet<int>();
#region Player Controls
public void AddPlayer(PlayerState player)
@ -33,22 +34,9 @@ namespace OpenDiablo2.Core.GameState_
public void AddMob(MobState mob)
{
Mobs.Add(mob);
// add id to idsused in order
int i = 0;
while(i < IdsUsed.Count)
{
if(IdsUsed[i] > mob.Id)
{
IdsUsed.Insert(i, mob.Id);
break;
}
i++;
}
if(i == IdsUsed.Count)
{
// didn't get added
IdsUsed.Add(mob.Id);
}
if (IdsUsed.Contains(mob.Id))
throw new ApplicationException("Tried to insert an existing mob id!");
IdsUsed.Add(mob.Id);
}
public void RemoveMob(MobState mob)
{
@ -57,16 +45,11 @@ namespace OpenDiablo2.Core.GameState_
}
public int GetNextAvailableMobId()
{
int i = 0;
while(i < IdsUsed.Count)
{
if(IdsUsed[i] != i)
{
for (var i = 1; i < int.MaxValue; i++)
if (!IdsUsed.Contains(i))
return i;
}
i++;
}
return IdsUsed.Count;
throw new ApplicationException("Ran out of IDs. How did this even happen?!");
}
#endregion Mob Controls
@ -83,59 +66,5 @@ namespace OpenDiablo2.Core.GameState_
}
#endregion Enemy Controls
#region Searching and Filtering
public List<MobState> FilterMobs(IEnumerable<MobState> mobs, IMobCondition condition)
{
// note: if condition is null, returns full list
List<MobState> filtered = new List<MobState>();
foreach(MobState mob in mobs)
{
if (condition == null || condition.Evaluate(mob))
{
filtered.Add(mob);
}
}
return filtered;
}
public List<MobState> FindMobs(IMobCondition condition)
{
return FilterMobs(Mobs, condition);
}
public List<MobState> FindEnemies(IMobCondition condition)
{
return FilterMobs(Enemies, condition);
}
public List<MobState> FindPlayers(IMobCondition condition)
{
return FilterMobs(Players, condition);
}
public List<MobState> FindInRadius(IEnumerable<MobState> mobs, float centerx, float centery, float radius)
{
List<MobState> filtered = new List<MobState>();
foreach(MobState mob in mobs)
{
if(mob.GetDistance(centerx, centery) <= radius)
{
filtered.Add(mob);
}
}
return filtered;
}
public List<MobState> FindMobsInRadius(float centerx, float centery, float radius, IMobCondition condition)
{
return FilterMobs(FindInRadius(Mobs, centerx, centery, radius), condition);
}
public List<MobState> FindEnemiesInRadius(float centerx, float centery, float radius, IMobCondition condition)
{
return FilterMobs(FindInRadius(Enemies, centerx, centery, radius), condition);
}
public List<MobState> FindPlayersInRadius(float centerx, float centery, float radius, IMobCondition condition)
{
return FilterMobs(FindInRadius(Players, centerx, centery, radius), condition);
}
#endregion Searching and Filtering
}
}

View File

@ -12,6 +12,8 @@ namespace OpenDiablo2.Core.Map_Engine
private readonly IRenderWindow renderWindow;
private readonly IResourceManager resourceManager;
public int FocusedMobId { get; set; } = -1;
private PointF cameraLocation = new PointF();
public PointF CameraLocation
{

View File

@ -0,0 +1,18 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using Autofac;
using OpenDiablo2.Common.Interfaces;
namespace OpenDiablo2.GameServer_
{
public sealed class AutofacModule : Module
{
protected override void Load(ContainerBuilder builder)
{
builder.RegisterType<GameServer>().As<IGameServer>().SingleInstance();
}
}
}

View File

@ -0,0 +1,43 @@
using System;
using System.Collections.Generic;
using OpenDiablo2.Common.Enums;
using OpenDiablo2.Common.Interfaces;
using OpenDiablo2.Common.Interfaces.Mobs;
using OpenDiablo2.Common.Models.Mobs;
namespace OpenDiablo2.GameServer_
{
public sealed class GameServer : IGameServer
{
private static readonly log4net.ILog log = log4net.LogManager.GetLogger(System.Reflection.MethodBase.GetCurrentMethod().DeclaringType);
private readonly IMobManager mobManager;
public int Seed { get; private set; }
public IEnumerable<PlayerState> Players => mobManager.Players;
public GameServer(IMobManager mobManager)
{
this.mobManager = mobManager;
}
public void InitializeNewGame()
{
log.Info("Initializing a new game");
Seed = (new Random()).Next();
}
public int SpawnNewPlayer(int clientHash, string playerName, eHero heroType)
{
var newPlayer = new PlayerState(clientHash, playerName, mobManager.GetNextAvailableMobId(), 1, 20.0f, 20.0f, 10, 10, 10, 10, 0, heroType,
new HeroTypeConfig(10, 10, 10, 50, 50, 50, 10, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1), new LevelExperienceConfig(new List<int>() { 100 }));
mobManager.AddPlayer(newPlayer);
return newPlayer.Id;
}
public void Dispose()
{
}
}
}

View File

@ -0,0 +1,65 @@
<?xml version="1.0" encoding="utf-8"?>
<Project ToolsVersion="15.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<Import Project="$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props" Condition="Exists('$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props')" />
<PropertyGroup>
<Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration>
<Platform Condition=" '$(Platform)' == '' ">AnyCPU</Platform>
<ProjectGuid>{C29A84E8-E708-4BE2-9946-202899B68E19}</ProjectGuid>
<OutputType>Library</OutputType>
<AppDesignerFolder>Properties</AppDesignerFolder>
<RootNamespace>OpenDiablo2.GameServer</RootNamespace>
<AssemblyName>OpenDiablo2.GameServer</AssemblyName>
<TargetFrameworkVersion>v4.6.1</TargetFrameworkVersion>
<FileAlignment>512</FileAlignment>
<Deterministic>true</Deterministic>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)' == 'Debug|x64'">
<DebugSymbols>true</DebugSymbols>
<OutputPath>bin\x64\Debug\</OutputPath>
<DefineConstants>DEBUG;TRACE</DefineConstants>
<DebugType>full</DebugType>
<PlatformTarget>x64</PlatformTarget>
<ErrorReport>prompt</ErrorReport>
<CodeAnalysisRuleSet>MinimumRecommendedRules.ruleset</CodeAnalysisRuleSet>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)' == 'Release|x64'">
<OutputPath>bin\x64\Release\</OutputPath>
<DefineConstants>TRACE</DefineConstants>
<Optimize>true</Optimize>
<DebugType>pdbonly</DebugType>
<PlatformTarget>x64</PlatformTarget>
<ErrorReport>prompt</ErrorReport>
<CodeAnalysisRuleSet>MinimumRecommendedRules.ruleset</CodeAnalysisRuleSet>
</PropertyGroup>
<ItemGroup>
<Reference Include="Autofac, Version=4.8.1.0, Culture=neutral, PublicKeyToken=17863af14b0044da, processorArchitecture=MSIL">
<HintPath>..\packages\Autofac.4.8.1\lib\net45\Autofac.dll</HintPath>
</Reference>
<Reference Include="log4net, Version=2.0.8.0, Culture=neutral, PublicKeyToken=669e0ddf0bb1aa2a, processorArchitecture=MSIL">
<HintPath>..\packages\log4net.2.0.8\lib\net45-full\log4net.dll</HintPath>
</Reference>
<Reference Include="System" />
<Reference Include="System.Core" />
<Reference Include="System.Xml.Linq" />
<Reference Include="System.Data.DataSetExtensions" />
<Reference Include="Microsoft.CSharp" />
<Reference Include="System.Data" />
<Reference Include="System.Net.Http" />
<Reference Include="System.Xml" />
</ItemGroup>
<ItemGroup>
<Compile Include="AutofacModule.cs" />
<Compile Include="GameServer.cs" />
<Compile Include="Properties\AssemblyInfo.cs" />
</ItemGroup>
<ItemGroup>
<None Include="packages.config" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\OpenDiablo2.Common\OpenDiablo2.Common.csproj">
<Project>{b743160e-a0bb-45dc-9998-967a85e50562}</Project>
<Name>OpenDiablo2.Common</Name>
</ProjectReference>
</ItemGroup>
<Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
</Project>

View File

@ -0,0 +1,36 @@
using System.Reflection;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
// General Information about an assembly is controlled through the following
// set of attributes. Change these attribute values to modify the information
// associated with an assembly.
[assembly: AssemblyTitle("OpenDiablo2.GameServer")]
[assembly: AssemblyDescription("")]
[assembly: AssemblyConfiguration("")]
[assembly: AssemblyCompany("")]
[assembly: AssemblyProduct("OpenDiablo2.GameServer")]
[assembly: AssemblyCopyright("Copyright © 2018")]
[assembly: AssemblyTrademark("")]
[assembly: AssemblyCulture("")]
// Setting ComVisible to false makes the types in this assembly not visible
// to COM components. If you need to access a type in this assembly from
// COM, set the ComVisible attribute to true on that type.
[assembly: ComVisible(false)]
// The following GUID is for the ID of the typelib if this project is exposed to COM
[assembly: Guid("c29a84e8-e708-4be2-9946-202899b68e19")]
// Version information for an assembly consists of the following four values:
//
// Major Version
// Minor Version
// Build Number
// Revision
//
// You can specify all the values or you can default the Build and Revision Numbers
// by using the '*' as shown below:
// [assembly: AssemblyVersion("1.0.*")]
[assembly: AssemblyVersion("1.0.0.0")]
[assembly: AssemblyFileVersion("1.0.0.0")]

View File

@ -0,0 +1,5 @@
<?xml version="1.0" encoding="utf-8"?>
<packages>
<package id="Autofac" version="4.8.1" targetFramework="net461" />
<package id="log4net" version="2.0.8" targetFramework="net461" />
</packages>

View File

@ -5,18 +5,19 @@ using OpenDiablo2.Common.Attributes;
using OpenDiablo2.Common.Enums;
using OpenDiablo2.Common.Interfaces;
namespace OpenDiablo2.ServiceBus.Message_Frames
namespace OpenDiablo2.ServiceBus.Message_Frames.Client
{
[MessageFrame(eMessageFrameType.JoinGame)]
public sealed class MFJoinGame : IMessageFrame
{
public Guid PlayerId { get; set; }
public string PlayerName { get; set; }
public eHero HeroType { get; set; }
public byte[] Data
{
get
{
return PlayerId.ToByteArray()
return new byte[] { (byte)HeroType }
.Concat(BitConverter.GetBytes((UInt16)PlayerName.Length))
.Concat(Encoding.UTF8.GetBytes(PlayerName))
.ToArray();
@ -24,10 +25,9 @@ namespace OpenDiablo2.ServiceBus.Message_Frames
set
{
PlayerId = new Guid(value.Take(16).ToArray());
var playerNameLen = BitConverter.ToUInt16(value, 16);
PlayerName = Encoding.UTF8.GetString(value, 18, value.Length - 18);
HeroType = (eHero)value[0];
var playerNameLen = BitConverter.ToUInt16(value, 1);
PlayerName = Encoding.UTF8.GetString(value, 3, value.Length - 3);
if (PlayerName.Length != playerNameLen)
throw new ApplicationException("Invalid player length!");
@ -35,15 +35,15 @@ namespace OpenDiablo2.ServiceBus.Message_Frames
}
public MFJoinGame() { }
public MFJoinGame(string playerName)
public MFJoinGame(string playerName, eHero heroType)
{
PlayerId = Guid.NewGuid();
PlayerName = playerName;
HeroType = heroType;
}
public void Process(object sender, ISessionEventProvider sessionEventProvider)
public void Process(int clientHash, ISessionEventProvider sessionEventProvider)
{
sessionEventProvider.OnJoinGame(sender, PlayerId, PlayerName);
sessionEventProvider.OnJoinGame(clientHash, HeroType, PlayerName);
}
}
}

View File

@ -0,0 +1,49 @@
using System;
using System.Collections.Generic;
using System.Linq;
using OpenDiablo2.Common.Attributes;
using OpenDiablo2.Common.Enums;
using OpenDiablo2.Common.Interfaces;
using OpenDiablo2.Common.Interfaces.MessageBus;
namespace OpenDiablo2.ServiceBus.Message_Frames.Server
{
[MessageFrame(eMessageFrameType.LocatePlayers)]
public sealed class MFLocatePlayers : IMessageFrame
{
public IEnumerable<PlayerLocationDetails> LocationDetails { get; set; }
public byte[] Data
{
get
{
var result = new List<byte>();
result.AddRange(BitConverter.GetBytes((UInt16)LocationDetails.Count()));
result.AddRange(LocationDetails.SelectMany(x => x.GetBytes()));
return result.ToArray();
}
set
{
var count = BitConverter.ToUInt16(value, 0);
var result = new List<PlayerLocationDetails>();
for(var i = 0; i < count; i++)
{
result.Add(PlayerLocationDetails.FromBytes(value, 2 + (i * PlayerLocationDetails.SizeInBytes)));
}
LocationDetails = result;
}
}
public MFLocatePlayers() { }
public MFLocatePlayers(IEnumerable<PlayerLocationDetails> locationDetails)
{
this.LocationDetails = locationDetails;
}
public void Process(int clientHash, ISessionEventProvider sessionEventProvider)
=> sessionEventProvider.OnLocatePlayers(clientHash, LocationDetails);
}
}

View File

@ -3,7 +3,7 @@ using OpenDiablo2.Common.Attributes;
using OpenDiablo2.Common.Enums;
using OpenDiablo2.Common.Interfaces;
namespace OpenDiablo2.ServiceBus.Message_Frames
namespace OpenDiablo2.ServiceBus.Message_Frames.Server
{
[MessageFrame(eMessageFrameType.SetSeed)]
public sealed class MFSetSeed : IMessageFrame
@ -19,7 +19,6 @@ namespace OpenDiablo2.ServiceBus.Message_Frames
public MFSetSeed()
{
Seed = (new Random()).Next();
}
public MFSetSeed(int seed)
@ -27,9 +26,8 @@ namespace OpenDiablo2.ServiceBus.Message_Frames
Seed = seed;
}
public void Process(object sender, ISessionEventProvider sessionEventProvider)
{
sessionEventProvider.OnSetSeed?.Invoke(sender, Seed);
}
public void Process(int clientHash, ISessionEventProvider sessionEventProvider)
=> sessionEventProvider.OnSetSeed?.Invoke(clientHash, Seed);
}
}

View File

@ -21,6 +21,7 @@
<PlatformTarget>x64</PlatformTarget>
<ErrorReport>prompt</ErrorReport>
<CodeAnalysisRuleSet>MinimumRecommendedRules.ruleset</CodeAnalysisRuleSet>
<AllowUnsafeBlocks>false</AllowUnsafeBlocks>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)' == 'Release|x64'">
<OutputPath>bin\x64\Release\</OutputPath>
@ -30,6 +31,7 @@
<PlatformTarget>x64</PlatformTarget>
<ErrorReport>prompt</ErrorReport>
<CodeAnalysisRuleSet>MinimumRecommendedRules.ruleset</CodeAnalysisRuleSet>
<AllowUnsafeBlocks>false</AllowUnsafeBlocks>
</PropertyGroup>
<ItemGroup>
<Reference Include="AsyncIO, Version=0.1.61.0, Culture=neutral, PublicKeyToken=44a94435bd6f33f8, processorArchitecture=MSIL">
@ -56,8 +58,9 @@
</ItemGroup>
<ItemGroup>
<Compile Include="AutofacModule.cs" />
<Compile Include="Message Frames\MFJoinGame.cs" />
<Compile Include="Message Frames\MFSetSeed.cs" />
<Compile Include="Message Frames\Client\MFJoinGame.cs" />
<Compile Include="Message Frames\Server\MFLocatePlayers.cs" />
<Compile Include="Message Frames\Server\MFSetSeed.cs" />
<Compile Include="SessionManager.cs" />
<Compile Include="Properties\AssemblyInfo.cs" />
<Compile Include="SessionServer.cs" />

View File

@ -7,7 +7,8 @@ using NetMQ.Sockets;
using OpenDiablo2.Common.Attributes;
using OpenDiablo2.Common.Enums;
using OpenDiablo2.Common.Interfaces;
using OpenDiablo2.ServiceBus.Message_Frames;
using OpenDiablo2.ServiceBus.Message_Frames.Client;
using OpenDiablo2.ServiceBus.Message_Frames.Server;
namespace OpenDiablo2.ServiceBus
{
@ -23,11 +24,12 @@ namespace OpenDiablo2.ServiceBus
private RequestSocket requestSocket;
private AutoResetEvent resetEvent = new AutoResetEvent(false);
private ISessionServer sessionServer;
private Guid playerId;
public Guid PlayerId { get; private set; }
private bool running = false;
public OnSetSeedEvent OnSetSeed { get; set; }
public OnJoinGameEvent OnJoinGame { get; set; }
public OnLocatePlayersEvent OnLocatePlayers { get; set; }
public SessionManager(
eSessionType sessionType,
@ -71,15 +73,7 @@ namespace OpenDiablo2.ServiceBus
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;
@ -124,22 +118,17 @@ namespace OpenDiablo2.ServiceBus
messageFrame.Data = frameData;
lock (getGameState().ThreadLocker)
{
messageFrame.Process(requestSocket, this);
messageFrame.Process(requestSocket.GetHashCode(), this);
}
}
public void JoinGame(string playerName, Action<Guid> callback)
public void JoinGame(string playerName, eHero heroType)
{
Task.Run(() =>
{
var mf = new MFJoinGame(playerName);
playerId = mf.PlayerId;
Send(mf);
Send(new MFJoinGame(playerName, heroType));
ProcessMessageFrame<MFSetSeed>();
lock (getGameState().ThreadLocker)
{
callback(playerId);
}
ProcessMessageFrame<MFLocatePlayers>();
});
}
}

View File

@ -7,7 +7,8 @@ using NetMQ.Sockets;
using OpenDiablo2.Common.Attributes;
using OpenDiablo2.Common.Enums;
using OpenDiablo2.Common.Interfaces;
using OpenDiablo2.ServiceBus.Message_Frames;
using OpenDiablo2.Common.Interfaces.MessageBus;
using OpenDiablo2.ServiceBus.Message_Frames.Server;
namespace OpenDiablo2.ServiceBus
{
@ -16,26 +17,34 @@ namespace OpenDiablo2.ServiceBus
private static readonly log4net.ILog log = log4net.LogManager.GetLogger(System.Reflection.MethodBase.GetCurrentMethod().DeclaringType);
private readonly eSessionType sessionType;
private readonly IGameServer gameServer;
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 OnLocatePlayersEvent OnLocatePlayers { get; set; }
public SessionServer(eSessionType sessionType, Func<eMessageFrameType, IMessageFrame> getMessageFrame)
public SessionServer(
eSessionType sessionType,
IGameServer gameServer,
Func<eMessageFrameType, IMessageFrame> getMessageFrame
)
{
this.sessionType = sessionType;
this.getMessageFrame = getMessageFrame;
this.gameServer = gameServer;
}
public void Start()
{
gameSeed = (new Random()).Next();
// TODO: Loading existing games...
gameServer.InitializeNewGame();
Task.Run(() => Serve());
}
@ -64,7 +73,7 @@ namespace OpenDiablo2.ServiceBus
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);
messageFrame.Process(socket.GetHashCode(), this);
});
running = true;
WaitServerStartEvent.Set();
@ -89,16 +98,17 @@ namespace OpenDiablo2.ServiceBus
Stop();
}
private void Send(NetMQSocket target, IMessageFrame messageFrame)
private void Send(IMessageFrame messageFrame, bool more = false)
{
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());
responseSocket.SendFrame(new byte[] { (byte)attr.FrameType }.Concat(messageFrame.Data).ToArray(), more);
}
private void OnJoinGameHandler(object sender, Guid playerId, string playerName)
private void OnJoinGameHandler(int clientHash, eHero heroType, string playerName)
{
// TODO: Try to make this less stupid
Send(sender as NetMQSocket, new MFSetSeed(gameSeed));
gameServer.SpawnNewPlayer(clientHash, playerName, heroType);
Send(new MFSetSeed(gameServer.Seed), true);
Send(new MFLocatePlayers(gameServer.Players.Select(x => x.ToPlayerLocationDetails())));
}
}
}

View File

@ -25,6 +25,8 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Tests", "Tests", "{1FAD6E2F
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "OpenDiablo2.ServiceBus", "OpenDiablo2.ServiceBus\OpenDiablo2.ServiceBus.csproj", "{D3F0F44F-2AB4-4342-A684-53193032A621}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "OpenDiablo2.GameServer", "OpenDiablo2.GameServer\OpenDiablo2.GameServer.csproj", "{C29A84E8-E708-4BE2-9946-202899B68E19}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|x64 = Debug|x64
@ -67,6 +69,10 @@ Global
{D3F0F44F-2AB4-4342-A684-53193032A621}.Debug|x64.Build.0 = Debug|x64
{D3F0F44F-2AB4-4342-A684-53193032A621}.Release|x64.ActiveCfg = Release|x64
{D3F0F44F-2AB4-4342-A684-53193032A621}.Release|x64.Build.0 = Release|x64
{C29A84E8-E708-4BE2-9946-202899B68E19}.Debug|x64.ActiveCfg = Debug|x64
{C29A84E8-E708-4BE2-9946-202899B68E19}.Debug|x64.Build.0 = Debug|x64
{C29A84E8-E708-4BE2-9946-202899B68E19}.Release|x64.ActiveCfg = Release|x64
{C29A84E8-E708-4BE2-9946-202899B68E19}.Release|x64.Build.0 = Release|x64
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE

View File

@ -5,7 +5,7 @@
<Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration>
<Platform Condition=" '$(Platform)' == '' ">AnyCPU</Platform>
<ProjectGuid>{2B0CF1AC-06DD-4322-AE8B-FF8A8C70A3CD}</ProjectGuid>
<OutputType>Exe</OutputType>
<OutputType>WinExe</OutputType>
<RootNamespace>OpenDiablo2</RootNamespace>
<AssemblyName>OpenDiablo2</AssemblyName>
<TargetFrameworkVersion>v4.6.1</TargetFrameworkVersion>
@ -99,6 +99,10 @@
<Project>{8fc6bf7d-835a-47c1-a6b2-125495fa0900}</Project>
<Name>OpenDiablo2.Core</Name>
</ProjectReference>
<ProjectReference Include="..\OpenDiablo2.GameServer\OpenDiablo2.GameServer.csproj">
<Project>{c29a84e8-e708-4be2-9946-202899b68e19}</Project>
<Name>OpenDiablo2.GameServer</Name>
</ProjectReference>
<ProjectReference Include="..\OpenDiablo2.Scenes\OpenDiablo2.Scenes.csproj">
<Project>{05224fe7-293f-4184-b1d6-89f5171b60e0}</Project>
<Name>OpenDiablo2.Scenes</Name>