Sign in to follow this  

Entity Component system review

This topic is 2550 days old which is more than the 365 day threshold we allow for new replies. Please post a new topic.

If you intended to correct an error in the post then please contact us.

Recommended Posts

Hi. I have read a lot about Entity Component (System-SubSytem) architecture and everywhere is somewhat similiar story but not quite the same on how should you manage Entites, Components, Systems, system dependencies, etc...

Here is my take on that (link), so if anyone is interested to take a look and leave a comment. I am planing to develop a small 2D and 3D game (in one) in C# using XNA 4.0 framework.

Thanks!

[Edited by - zveljkovic on December 24, 2010 10:12:22 PM]

Share this post


Link to post
Share on other sites
Ok :) It's pretty long but here it go :D

ZveenGame.cs - This is main class in library that inherits from Xna's Game class. It's used in executable to run the game (instance.Run()). I haven't got yet states and state management but in main executable I would like to inherit from gamestate, create entites and add them to gamestate and register state to this class so library could run the defined state.
This currently contains World instance (next code) and ComponentSystemCollection where are stored available ComponentSystems which will operate on Components.


using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using ZveenGameLibrary.System;
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Graphics;
using Microsoft.Xna.Framework.Input;

namespace ZveenGameLibrary
{
public class ZveenGame: Game
{
public ZveenGame()
{
Systems = new ComponentSystemCollection();
World = new World(this);

Graphics = new GraphicsDeviceManager(this);
Content.RootDirectory = "Content";
}

public GraphicsDeviceManager Graphics { get; private set; }
public SpriteBatch SpriteBatch { get; private set; }
public World World { get; private set; }
public ComponentSystemCollection Systems { get; private set; }

protected override void Initialize()
{
Systems.AddSystem(new GraphicsSystem(this));

base.Initialize();
}

protected override void LoadContent()
{
// Create a new SpriteBatch, which can be used to draw textures.
SpriteBatch = new SpriteBatch(GraphicsDevice);

// TODO: use this.Content to load your game content here
}

protected override void UnloadContent()
{
// TODO: Unload any non ContentManager content here
}

protected override void Update(GameTime gameTime)
{
// Allows the game to exit
if (GamePad.GetState(PlayerIndex.One).Buttons.Back == ButtonState.Pressed)
this.Exit();
// TODO: Add your update logic here
base.Update(gameTime);
}

protected override void Draw(GameTime gameTime)
{
GraphicsDevice.Clear(Color.CornflowerBlue);
// TODO: Add your drawing code here
base.Draw(gameTime);
}
}
}



World.cs - World is container for entites and his function is to properly register entites, resolve component dependancies in entity, register component to system...

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Collections;

namespace ZveenGameLibrary
{
public class World
{
public World(ZveenGame game)
{
Game = game;
}

public ZveenGame Game { get; private set; }
private Dictionary<String, IEntity> _entities = new Dictionary<String, IEntity>();

public void AddEntity(IEntity entity)
{
if (entity == null)
throw new ArgumentNullException("entity");

if (String.IsNullOrWhiteSpace(entity.Id))
throw new ArgumentException("Adding component without id", "entity");

if (_entities.ContainsKey(entity.Id))
throw new ArgumentException("Entity allready added", "entity");

_entities.Add(entity.Id, entity);
foreach (IComponent component in entity.Components)
component.ResolveDependancies(entity.Components);
foreach (IComponent component in entity.Components)
component.RegisterSystems(Game.Systems);
foreach (IComponent component in entity.Components)
component.Start();
}
}
}



IEntity.cs - base class for entities. I choose to have Component collection there to sneak in a little OOP :)

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

namespace ZveenGameLibrary
{
public interface IEntity
{
String Id { get; }
ComponentCollection Components { get; }
}
}



Entity.cs - base class

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

namespace ZveenGameLibrary
{
public class Entity: IEntity
{
public Entity()
{
Components = new ComponentCollection();
}

public String Id { get; set; }
public ComponentCollection Components { get; private set; }
}
}



IComponent.cs - Interface for components. I added additional functions so component writer could resolve needed dependencies when adding entity to World and later will i add code to resolve dependencies when adding to Entity (for dynamic component add/remove)


using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

namespace ZveenGameLibrary
{
public interface IComponent
{
IEntity Entity { get; }
String Id { get; }
void ResolveDependancies(ComponentCollection components);
void RegisterSystems(ComponentSystemCollection systems);
void Start();
}
}



Component.cs - concrete base class

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

namespace ZveenGameLibrary
{
public class Component: IComponent
{
public Component(String name, IEntity entity)
{
Id = name;
Entity = entity;
}

public String Id { get; private set; }
public IEntity Entity { get ; private set; }

public virtual void ResolveDependancies(ComponentCollection components) { }
public virtual void RegisterSystems(ComponentSystemCollection systems) { }
public virtual void Start() { }
}
}



ComponentColection.cs - collection of components that add components to two collections one for every component and other for components based on type so I can quickly retrive component by type which dont have a name.
(Maybe I will call here for ResolveDependancies on adding or removing)


using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Collections;

namespace ZveenGameLibrary
{
public class ComponentCollection: IEnumerable<IComponent>
{
private List<IComponent> _componentList = new List<IComponent>();
private Dictionary<Type, Dictionary<String, IComponent>> _componentsPerType = new Dictionary<Type, Dictionary<String, IComponent>>();
public Boolean Contains<T>()
{
return _componentsPerType.ContainsKey(typeof(T));
}
public Boolean Contains<T>(String id)
{
if (_componentsPerType.ContainsKey(typeof(T)))
return _componentsPerType[typeof(T)].ContainsKey(id);
return false;
}
public void Add(IComponent component)
{
if (component == null)
throw new ArgumentNullException("component");
if (String.IsNullOrWhiteSpace(component.Id))
throw new ArgumentException("Adding component without id","component");
if (_componentList.Contains(component))
throw new ArgumentException("Component allready added","component");
if (_componentsPerType.ContainsKey(component.GetType()))
{
if (_componentsPerType[component.GetType()].ContainsKey(component.Id))
throw new ArgumentException("Component with same type and id allready exists","component");
_componentsPerType[component.GetType()][component.Id] = component;
_componentList.Add(component);
}
else
{
_componentsPerType[component.GetType()] = new Dictionary<String, IComponent>();
_componentsPerType[component.GetType()][component.Id] = component;
_componentList.Add(component);
}
}
public IEnumerator<IComponent> GetEnumerator()
{
return _componentList.GetEnumerator();
}
IEnumerator IEnumerable.GetEnumerator()
{
return _componentList.GetEnumerator();
}
}
}



IComponentSystem.cs - is base for ComponentSystem classes that will handle the Components in Entites.

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Microsoft.Xna.Framework;

namespace ZveenGameLibrary
{
public interface IComponentSystem
{
ZveenGame Game { get; }
void Update(GameTime gameTime);
void Draw(GameTime gameTime);
}
}



ComponentSystemCollection.cs - collection of ComponentSystems. It's used in ZveenGame and passed down to components so they can register to just needed ones.

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Collections;

namespace ZveenGameLibrary
{
public class ComponentSystemCollection: IEnumerable<IComponentSystem>
{
private List<IComponentSystem> _systemList = new List<IComponentSystem>();
private Dictionary<Type, IComponentSystem> _systems = new Dictionary<Type, IComponentSystem>();
public void AddSystem(IComponentSystem system)
{
if (system == null)
throw new ArgumentNullException("system");
if (_systems.ContainsKey(system.GetType()))
throw new ArgumentException("System allready added", "system");

_systems.Add(system.GetType(), system);
_systemList.Add(system);
}

public T GetSystem<T>() where T : IComponentSystem
{
if (_systems.ContainsKey(typeof(T)))
return (T)_systems[typeof(T)];
return default(T);
}

public IEnumerator<IComponentSystem> GetEnumerator()
{
return _systemList.GetEnumerator();
}

IEnumerator IEnumerable.GetEnumerator()
{
return _systemList.GetEnumerator(); ;
}
}
}



Now comes the usage:

Components/Spatial/Spatial2D.cs - Class for 2D position

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Microsoft.Xna.Framework;

namespace ZveenGameLibrary.Spatial.Components
{
public class Spatial2D: Component
{
public Spatial2D(String id, IEntity entity)
: base(id, entity) { }
public Rectangle Rectangle { get; set; }
public Single Rotation { get; set; }
public Vector2 Origin { get; set; }
public Single Scale { get; set; }
}
}



Components/Graphics/Sprite.cs - this is class that represents single static image. It's used to show how dependacies work and how to register to needed system.

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Microsoft.Xna.Framework;
using ZveenGameLibrary.Spatial.Components;
using ZveenGameLibrary.System;

namespace ZveenGameLibrary.Components.Graphics
{
public class Sprite : Component
{
public Sprite(String name, IEntity entity)
: base(name, entity) { }
public String TextureId { get; set; }
public Rectangle Source { get; set; }
public override void ResolveDependancies(ComponentCollection components)
{
if (!components.Contains<Spatial2D>())
{
components.Add(new Spatial2D("Spatial2D",this.Entity));
}
}
public override void RegisterSystems(ComponentSystemCollection systems)
{
GraphicsSystem gfx = systems.GetSystem<GraphicsSystem>();
gfx.RegisterSprite(this);
base.RegisterSystems(systems);
}
}
}



Systems/GraphicsSystem.cs - and lastly minimal GraphicsSystem that does nothing. In Game.cs I need to create a Updateable and Drawing lists and let Systems add to those to be called in Update or Draw functions.

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using ZveenGameLibrary.Components.Graphics;
using Microsoft.Xna.Framework;

namespace ZveenGameLibrary.System
{
public class GraphicsSystem: IComponentSystem
{
public GraphicsSystem(ZveenGame game)
{
Game = game;
}
public ZveenGame Game { get; private set; }

public void RegisterSprite(Sprite sprite)
{
}

public void Update(GameTime gameTime)
{
}

public void Draw(GameTime gameTime)
{
}
}
}



With this system, I intend to add components to specific System/s, and to call specific Systems when needed. What i still dont get is broader picture how this all will work but I think it will go better than my previous half inheritance half something like this approach...

Share this post


Link to post
Share on other sites
I got some things I would like clarified. How I think its made: A Entity is a collection of components and this is a single game object and each component has a single responsibility like updating physics, spawning a projectile or rending the object.
Thats all good, how a component system should work.
But some things I can't figure out. Do you plan to separate the update and the drawing of the object? It could give some performance boost later if you cache the rendering of similar types in a single call (or at least fewer).
How are two entities communicating? I can't find any kind of messaging or similar.
How does a object get drawn? Its a terrible idea creating a new spritebatch or similar for every object.

Other small stuff, its slow to compare IDs of entities by string if you do it many times during an update. Use an int or cache the hashCode of the string in a small wrapper.

Overall though, it seems like you have a good start. Just a few important things left.

Share this post


Link to post
Share on other sites
As i got it, i would argue with you that each component has a responsibility to update physics, etc... I read that component should be only data and no functions but i sneaked in my
        void ResolveDependancies(ComponentCollection components);
void RegisterSystems(ComponentSystemCollection systems);
void Start();


functions to be able to selectively register my components to certain systems. For me those methods (for changing entity states) are in ComponentSystem classes, but certainly few more opinions like yours will change my mind to redesign that part.

For your questions, yeah i did separate update and drawing of object, even so that only components that need to be updated got that call (albeit at cost of memory, which i think is not a problem for today, because i have another separate list of IUpdatable which is just list of pointers ~ 10K max). I will show code later that i wrote after initial posting.

How are two entities communicating? - Well this is my unknown now, because i still haven't got there. My idea is that in some ComponentSystem when updating object i will access world instance and from there get an entity by id and change state of some of his components.
Thanks for tip to store hashCode of string ill try to implement it.

Ok. Here are changes and I will not update my first post but just a download link and thanks for taking your time to review my code.

IComponent.cs - I added IDrawable and IUpdateable to each component. The ones not needed will never be called. I will not show base class where i implemented the added interfaces.

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Microsoft.Xna.Framework;

namespace ZveenGameLibrary
{
public interface IComponent : IDrawable, IUpdateable
{
IEntity Entity { get; }
String Id { get; }
void ResolveDependancies(ComponentCollection components);
void RegisterSystems(ComponentSystemCollection systems);
void Start();
}
}





IComponentSystem.cs - The same as component. I added the IUpdateable and IDrawable interfaces and implemented them in base class.

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Microsoft.Xna.Framework;

namespace ZveenGameLibrary
{
public interface IComponentSystem: IDrawable, IUpdateable
{
ZveenGame Game { get; }
}
}




ZveenGame.cs - my master class. In there i instanced my component systems which now have UpdateOrder and DrawOrder and added them to collection of Systems (available to components on RegisterSytem func). I also added systems to IDrawable and IUpdateble SortedList. This is key factor to separate ComponentSystems that doesn't need some processing in either Update or Draw function. TextureSystem doesn't need update or draw called so he gets none. He is accessed by other systems which i show after this code.

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using ZveenGameLibrary.Systems;
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Graphics;
using Microsoft.Xna.Framework.Input;

namespace ZveenGameLibrary
{
public class ZveenGame: Game
{
public ZveenGame()
{
Systems = new ComponentSystemCollection();
World = new World(this);

Graphics = new GraphicsDeviceManager(this);
Content.RootDirectory = "Content";
}

public World World { get; private set; }
public ComponentSystemCollection Systems { get; private set; }
public GraphicsDeviceManager Graphics { get; private set; }
public SpriteBatch SpriteBatch { get; private set; }

private SortedList<Int32,IUpdateable> _updatebles = new SortedList<Int32,IUpdateable>();
private SortedList<Int32, IDrawable> _drawables = new SortedList<Int32, IDrawable>();

protected override void Initialize()
{
GraphicsSystem gfx = new GraphicsSystem(this);
gfx.DrawOrder = 0;
gfx.UpdateOrder = 100;
Systems.AddSystem(gfx);
_updatebles.Add(gfx.UpdateOrder,gfx);
_drawables.Add(gfx.DrawOrder,gfx);

TextureSystem txt = new TextureSystem(this);
Systems.AddSystem(txt);

base.Initialize();
}

protected override void LoadContent()
{
SpriteBatch = new SpriteBatch(GraphicsDevice);
}

protected override void UnloadContent()
{
}

protected override void Update(GameTime gameTime)
{
foreach (IUpdateable updateable in _updatebles.Values)
updateable.Update(gameTime);

base.Update(gameTime);
}

protected override void Draw(GameTime gameTime)
{
GraphicsDevice.Clear(Color.CornflowerBlue);

foreach (IDrawable drawable in _drawables.Values)
drawable.Draw(gameTime);

base.Draw(gameTime);
}
}
}




Systems/TextureSystem.cs - Here is texture storage class. Still simple with just 2 functions.


using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Microsoft.Xna.Framework.Graphics;
using Microsoft.Xna.Framework;

namespace ZveenGameLibrary.Systems
{
public class TextureSystem: ComponentSystem
{
public TextureSystem(ZveenGame game)
: base(game) { }

private Dictionary<String, Texture2D> _textures = new Dictionary<String, Texture2D>();

public void LoadTexture(String id)
{
if (String.IsNullOrWhiteSpace(id))
throw new ArgumentException("Texture id is null or whitespace","id");
if (_textures.ContainsKey(id))
throw new InvalidOperationException("Texture allready loaded");
Texture2D texture = Game.Content.Load<Texture2D>(id);
_textures[id] = texture;
}
public Texture2D GetTexture(String id)
{
return _textures[id];
}
}
}




Components/Graphics/Sprite.cs - Component that has sprite that and implements function for registering into GraphicsSystem and resolve Spatial2D dependency.

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Microsoft.Xna.Framework;
using ZveenGameLibrary.Spatial.Components;
using ZveenGameLibrary.Systems;

namespace ZveenGameLibrary.Components.Graphics
{
public class Sprite : Component
{
public Sprite(String name, IEntity entity)
: base(name, entity) { }

private String _textureId;
public String TextureId
{
get { return _textureId; }
set
{
_textureId = value;
if (TextureIdChanged != null)
TextureIdChanged.Invoke();
}
}
public event Action TextureIdChanged;
private Rectangle _source;
public Rectangle Source
{
get { return _source; }
set
{
_source = value;
if (SourceChanged != null)
SourceChanged.Invoke();
}
}
public event Action SourceChanged;

public override void ResolveDependancies(ComponentCollection components)
{
if (!components.Contains<Spatial2D>())
{
components.Add(new Spatial2D("Spatial2D",this.Entity));
}
}
public override void RegisterSystems(ComponentSystemCollection systems)
{
GraphicsSystem gfx = systems.GetSystem<GraphicsSystem>();
gfx.RegisterSprite(this);
base.RegisterSystems(systems);
}
}
}




Systems/GraphicsSystem.cs - Here I have function to add sprite to drawing list and in draw function to draw registered sprites. In draw function is how i access other components and there i could access via Game object World instance and from there other entities.

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using ZveenGameLibrary.Components.Graphics;
using Microsoft.Xna.Framework;
using ZveenGameLibrary.Spatial.Components;
using Microsoft.Xna.Framework.Graphics;

namespace ZveenGameLibrary.Systems
{
public class GraphicsSystem: ComponentSystem
{
public GraphicsSystem(ZveenGame game)
: base(game) { }

private SortedList<Int32, IDrawable> _sprites = new SortedList<Int32, IDrawable>();
public void RegisterSprite(Sprite sprite)
{
_sprites.Add(sprite.DrawOrder, sprite);
}

public override void Update(GameTime gameTime)
{
//working on this
}

public override void Draw(GameTime gameTime)
{
TextureSystem textures = Game.Systems.GetSystem<TextureSystem>();
Game.SpriteBatch.Begin();
foreach( Sprite sprite in _sprites.Values.OfType<Sprite>() )
{
Spatial2D sp = sprite.Entity.Components.GetFirst<Spatial2D>();
Game.SpriteBatch.Draw(
textures.GetTexture(sprite.TextureId),
sp.Destination,
sprite.Source,
Color.White,
sp.Rotation,
sp.Origin,
SpriteEffects.None,
0
);
}
Game.SpriteBatch.End();
}
}
}



[Edited by - zveljkovic on December 24, 2010 10:25:18 PM]

Share this post


Link to post
Share on other sites
I've got a question:

You said you seperate components (data) with functions. What will your functions be? Objects? How general will you make them? Lets say you have a function to remove health from another entity, could you give me an example of how you would solve it.

I'm also working on a component based engine, where I have "states" which are components containing data and "actions" component containing functionallity. Now my actions are class objects, but I have thoughts of making them to functionpointers that points to static functions.

[Edited by - Doggolainen on December 25, 2010 6:03:14 AM]

Share this post


Link to post
Share on other sites
Today I tested inter ComponentSystem interactions and this is what i got.


using System.Text;
using ZveenGameLibrary;
using ZveenGameLibrary.Components.Graphics;
using Microsoft.Xna.Framework.Content;
using ZveenGameLibrary.Systems;
using ZveenGameLibrary.Spatial.Components;
using Microsoft.Xna.Framework;

namespace ICGame.Backgrounds
{
public class lala: Entity
{
public lala()
{
Id = "llala";
}

public void LoadContent(ZveenGame game)
{
AnimatedSprite sp = game.Content.Load<AnimatedSprite>("zbutton");
sp.Id = "asdf";
sp.Entity = this;
TextureSystem txt = game.Systems.GetSystem<TextureSystem>();
txt.LoadTexture("button");
Components.Add(sp);
Spatial2D spatial = new Spatial2D("Spatial2D", this);
spatial.Destination = new Rectangle(0, 0, 300, 300);
this.Components.Add(spatial);
sp.AddAnimationToQueue("show");
sp.AddAnimationToQueue("normal");

}
}
}



This is class for animated button. And this is how i create it in code

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using ZveenGameLibrary;
using ICGame.Backgrounds;
using ZveenGameLibrary.Systems;

namespace ICGame
{
public class KamaHama: ZveenGame
{
MainMenuBackground bg = new MainMenuBackground();
lala la = new lala();
protected override void LoadContent()
{
//TextureSystem ts = this.Systems.GetSystem<TextureSystem>();
//ts.LoadTexture("sprite");
//World.AddEntity(bg);
la.LoadContent(this);
World.AddEntity(la);
base.LoadContent();
}
}
}



and code for rendering

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using ZveenGameLibrary.Components.Graphics;
using Microsoft.Xna.Framework;
using ZveenGameLibrary.Spatial.Components;
using Microsoft.Xna.Framework.Graphics;

namespace ZveenGameLibrary.Systems
{
public class GraphicsSystem: ComponentSystem
{
public GraphicsSystem(ZveenGame game)
: base(game) { }

private SortedList<Int32, IDrawable> _drawableSprites = new SortedList<Int32, IDrawable>();
private SortedList<Int32, IUpdateable> _updateableSprites = new SortedList<Int32, IUpdateable>();

public void RegisterSprite(Sprite sprite)
{
_drawableSprites.Add(sprite.DrawOrder, sprite);
}

public void RegisterAnimatedSprite(AnimatedSprite sprite)
{
_drawableSprites.Add(sprite.DrawOrder, sprite);
_updateableSprites.Add(sprite.UpdateOrder, sprite);
}

public override void Update(GameTime gameTime)
{
foreach (IUpdateable sprite in _updateableSprites.Values)
{
sprite.Update(gameTime);
}
}

public override void Draw(GameTime gameTime)
{
TextureSystem textures = Game.Systems.GetSystem<TextureSystem>();
Game.SpriteBatch.Begin();
foreach( IDrawable drawable in _drawableSprites.Values )
{
if (drawable is Sprite)
{
Sprite sprite = (Sprite)drawable;
Spatial2D sp = sprite.Entity.Components.GetFirst<Spatial2D>();
Game.SpriteBatch.Draw(
textures.GetTexture(sprite.TextureId),
sp.Destination,
sprite.Source,
Color.White,
sp.Rotation,
sp.Origin,
SpriteEffects.None,
0
);
}
else if (drawable is AnimatedSprite)
{
AnimatedSprite sprite = (AnimatedSprite)drawable;
if (sprite.IsDrawable)
{
Spatial2D sp = sprite.Entity.Components.GetFirst<Spatial2D>();
Game.SpriteBatch.Draw(
textures.GetTexture(sprite.TextureId),
sp.Destination,
sprite.Source,
Color.White,
sp.Rotation,
sp.Origin,
SpriteEffects.None,
0
);
}
}

}
Game.SpriteBatch.End();
}


}
}



As I can see, your "actions" are same as mine ComponentSystem but my ComponentSystems seems to be little higher than yours. In my case they are public inside game class and the way to entites access other entities is to one have a component that will have a state for which the CS will access other entity.

Ie. i want to my mesh of actor entity always point at head mesh of enemy. I will on keypress 't' (target nearest enemy) create MeshToMeshTracker. It will have properties:

ModelComponent Source; //or could be string to get component by name
String SourceMeshName;
... same for target



when adding this to entity it will call my RegisterToSystem func in this component and it will register to coresponding system. When that system updates it will pickup this component, see if there is Source ModelComponent and modify it to point of direction where is Target Mesh... CompponentSystem has access to target entity via this relation (the one thing i said about higher in hierarchy) this->Game->World->GetEntity(currComp->TargetId)

Share this post


Link to post
Share on other sites

This topic is 2550 days old which is more than the 365 day threshold we allow for new replies. Please post a new topic.

If you intended to correct an error in the post then please contact us.

Create an account or sign in to comment

You need to be a member in order to leave a comment

Create an account

Sign up for a new account in our community. It's easy!

Register a new account

Sign in

Already have an account? Sign in here.

Sign In Now

Sign in to follow this