I know that multiple inheritance in C# is only allowed by using Interfaces and that there are very valid reasons why multiple inheritance can quickly become a real headache. (Working in .NET Framework if that makes any difference to the answers)
However.
In working on various projects accross many classes I find myself returning to the same patterns to handle behaviour.
For example I have an Interface IXMLSavable which requires the functions GetXML() and SetFromXML(XElement e) to be implemented. The way I implement this in every class is, that I write different functions for different versions of the XML (If I changed something in the GetXML() I want to maintain backwards compatibility...). And according to a version-attribute on the root Element I switch case to the right ReadVersionX(XElement e) function so all my data stays consitent.
Another example would be centered around eventing. If for example I want to implement a "stop firing events for the time being"-Lock I would go about thusly:
private bool suppressEvents;
public bool SuppressEvents
{
get { return suppressEvents; }
set
{
bool prevValue=SuppressEvents;
suppressEvents=value;
if(prevValue!=SuppressEvents && !SuppressEvents) TheChangeEvent?.Invoke();
}
}
So I can run multiple operations on the object in question without it giving of a right old firework display of events. Again: This code will be almost unchanged for a lot of classes.
For the XML one I could refactor this to a class that has a Dictionary<int,delegate> ReadFunctions which I could then set in every implementation (I concede that there needs to be a bit of customisation in the "implementing" class) and reduce the amount of bolierplate for every class (the explicit switching on the version attribute) to just filling this dictionary.
The eventing one could go into a class on its own quite readily, I would probably only need to hook up the right event to the invokation, but that could easily be remedied by an abstract function I will have to implement (again: customisation still necessary but much less boilerplate).
Each "now-class-was-interface" on its own would make a splendid base class for any object. I could use functionality down an inheritance tree and customise it by overwriting functionality with new if I would need it.
The problem starts when I want to combine the two now-classes together. Due to the limitation in C# (which, again, is there for a reason) I cannot inherit from both above described classes at the same time. This would only be possible if I have one of these classes inherit from the other. Which would be possible, but would lead to a whole lot of a different headache when I want one functionality but not the other. Or the other functionality and not the one. The way around that would be to create a plethora of permutation classes (so one class for each combination of the functionaities). And while that would solve the problem it would probably be a nightmare to maintain.
So the real question is: Is there a way to correctly plug in different already implemented functionality into a class in an inheritance like manner that allows the addition of multiple distinct functionality packages as opposed to interfaces that cannot by the very nature of themselves provide any concrete implementation.
In many cases you can avoid inheritance with the use of interfaces/default interface methods/extension methods, decorators, or some other pattern.
In your case with xml you could simply change your interface to have one ReadMethod per version, and use a extension method to select the correct one
public interface IXMLReadable{
void ReadVersion1(XElement e);
void ReadVersion2(XElement e);
}
public static class IXMLReadableExtensions {
public static void Read(this IXMLReadable self, XElement e){
// Read version from xml, call ReadVersion1 or ReadVersion2
}
}
default interface methods would do more or less the same thing, with the added advantage of allowing the class to override the Read-method if it wants some other behavior.
However, my preferred solution would be to instead convert your object to a Data Transfer Object (DTO), add any required serialization attributes to this object, and use a library to serialize this. Added fields etc can usually be accommodated by just marking it as optional. Larger changes can usually be done by creating a new DTO class.
One way to solve your event problem could be to move this logic to a separate class
public class SuppressibleEvent
{
private bool suppressEvents;
private bool pendingEvent;
public void Raise()
{
if (!suppressEvents)
{
TheChangeEvent?.Invoke(this, EventArgs.Empty);
}
else
{
pendingEvent = true;
}
}
public event EventHandler TheChangeEvent;
public bool SuppressEvents
{
get => suppressEvents;
set
{
suppressEvents = value;
if (!suppressEvents && pendingEvent)
{
TheChangeEvent?.Invoke(this, EventArgs.Empty);
pendingEvent = false;
}
}
}
}
Optionally you may add a interface, so that only the owner can raise the event, but others can listen and register. You could also add methods/events to your class that just forwards to the actual implementation.
The overall point is that there is usually a better pattern to use than implementation inheritance. Some might require a bit more code, but usually gain a bit of flexibility as a result.
Using the Entity-Component-System pattern I want to connect some systems with events. So some systems shouldn't run in a loop, they should just run on demand.
Given the example of a Health system a Death system should only run when a component gets below 1 health.
I thought about having two types of systems. The first type is a periodic system. This runs once per frame, for example a Render or Movement System. The other type is an event based system. As mentioned before a connection between Health and Death.
First I created a basic interface used by both system types.
internal interface ISystem
{
List<Guid> EntityCache { get; } // Only relevant entities get stored in there
ComponentRequirements ComponentRequirements { get; } // the required components for this system
void InitComponentRequirements();
void InitComponentPools(EntityManager entityManager);
void UpdateCacheEntities(); // update all entities from the cache
void UpdateCacheEntity(Guid cacheEntityId); // update a single entity from the cache
}
Further I created the interfaces
internal interface IReactiveSystem : ISystem
{
// event based
}
and
internal interface IPeriodicSystem : ISystem
{
// runs in a loop
}
but I'm not sure if they will be necessary. There is no problem using
foreach (ISystem system in entityManager.Systems)
{
system.UpdateCacheEntities();
}
but I don't want to run a system if not needed.
There are two types of Events, a ChangeEvent and a ExecuteEvent. The first gets triggered when a value from a component has changed. The second one gets triggered when something should be done with a specific entity.
If you Need or want to you can have a look at the EntityManager
https://pastebin.com/NnfBc0N9
the ComponentRequirements
https://pastebin.com/xt3YGVSv
and the usage of the ECS
https://pastebin.com/Yuze72xf
An example System would be something like this
internal class HealthSystem : IReactiveSystem
{
public HealthSystem(EntityManager entityManager)
{
InitComponentRequirements();
InitComponentPools(entityManager);
}
private Dictionary<Guid, HealthComponent> healthComponentPool;
public List<Guid> EntityCache { get; } = new List<Guid>();
public ComponentRequirements ComponentRequirements { get; } = new ComponentRequirements();
public void InitComponentRequirements()
{
ComponentRequirements.AddRequiredType<HealthComponent>();
}
public void InitComponentPools(EntityManager entityManager)
{
healthComponentPool = entityManager.GetComponentPoolByType<HealthComponent>();
}
public void UpdateCacheEntities()
{
for (int i = 0; i < EntityCache.Count; i++)
{
UpdateCacheEntity(EntityCache[i]);
}
}
public void UpdateCacheEntity(Guid cacheEntityId)
{
Health healthComponent = healthComponentPool[cacheEntityId];
healthComponent.Value += 10; // just some tests
// update UI
}
}
How can I create ChangeEvents and ExecuteEvents for the different systems?
EDIT
Is there a way to add event delegates to the components to run a specific system for this entity on change if a change event is listening or on demand if an execute event is listening?
By mentioning ChangeEvent and ExecuteEvent I just mean event delegates.
Currently I could do something like this
internal class HealthSystem : IReactiveSystem
{
//… other stuff
IReactiveSystem deathSystem = entityManager.GetSystem<Death>(); // Get a system by its type
public void UpdateCacheEntity(Guid cacheEntityId)
{
// Change Health component
// Update UI
if(currentHealth < 1) // call the death system if the entity will be dead
{
deathSystem.UpdateCacheEntity(cacheEntityId);
}
}
}
But I was hoping to achieve a better architecture by using event delegates to make systems communicate and share data between each other.
I am not an expert on this design pattern but I read something on it and my advice is: try not to forget the real purpose of this pattern. This time I found the article on Wikipedia really interesting.
It is basically saying (at least it is what I understood) that this pattern has been "designed" to avoid creating too many dependencies, losing the decoupling. Here an example I took from the article:
Suppose there is a drawing function. This would be a "System" that
iterates through all entities that have both a physical and a visible
component, and draws them. The visible component could typically have
some information about how an entity should look (e.g. human, monster,
sparks flying around, flying arrow), and use the physical component to
know where to draw it. Another system could be collision detection. It
would iterate through all entities that have a physical component, as
it would not care how the entity is drawn. This system would then, for
instance, detect arrows that collide with monsters, and generate an
event when that happens. It should not need to understand what an
arrow is, and what it means when another object is hit by an arrow.
Yet another component could be health data, and a system that manages
health. Health components would be attached to the human and monster
entities, but not to arrow entities. The health management system
would subscribe to the event generated from collisions and update
health accordingly. This system could also now and then iterate
through all entities with the health component, and regenerate health.
I think that you overcomplicated your architecture, losing the advantages that this pattern can give you.
First of all: why do you need the EntityManager? I quote again:
The ECS architecture handles dependencies in a very safe and simple
way. Since components are simple data buckets, they have no
dependencies.
Instead your components are constructed with the EntityManager dependency injected:
entityManager.AddSystem(new Movement(entityManager));
The outcome is a relatively complex internal structure to store entities and the associated components.
After fixing this, the question is: how can you "communicate" with the ISystems?
Again, answer is in the article: Observer Pattern. Essentially each component has a set of attached systems, which are notified every time a certain action occurs.
by what im getting at this, you want to have a repetitive, once every tick type event alongside a once in a year type event (exaggerated but clear), you can do this with a delegate call back function IE:
public delegate void Event(object Sender, EventType Type, object EventData);
public event Event OnDeath;
public event Event OnMove;
public void TakeDamage(int a)
{
Health-=a;
if(Health<1)
OnDeath?.Invoke(this,EventType.PlayerDeath,null);
}
public void ThreadedMovementFunction()
{
while(true)
{
int x,y;
(x,y) = GetMovementDirection();
if(x!=0||y!=0)
OnMove?.Invoke(this,EventType.PlayerMove,(x,y));
}
}
you can implement this into an interface, and then store the object class and only access the needed stuff like events and so on. but tbh i don't quite understand what you're looking for, so if you could elaborate on the exact issue or thing you need to solve, that would be greatly appreciated!
I am trying to write a text-based RPG in C#. I am having issues understanding how I could make the character data accessible to many other objects in an effective manner.
I am fairly new to programming, and I certainly lack information. I've been reading guides and questions online and it got me so far, but I feel like I am either thinking about this the wrong way or missing something completely.
I am trying to write everything as flexible as possible since I am planning on adding a lot to this project if I can get past this stage. But it seems difficult for me to allow all of these objects to actively communicate with each other. Would using Unity help with this? I am mostly doing this to learn the language so I can later use it w/ Unity, but I wanted to learn the language directly so I opted out of starting with Unity. If there are any suggested resources to learn about the language I could really use recommendations.
I am sorry if the question is too open ended, but I can't really find anything regarding the mindset behind how I should be building a system like this. The last thing I've learned is ref but I feel like that isn't the best answer.
namespace GameMain
{
public class Game
{
static void Main()
{
MainMenu Game = new MainMenu();
Game.Menu();
return;
}
}
public class MainMenu
{
Character CurrentPlayer = new Character();
public void Menu()
{
Music MusicPlayer = new Music();
LocationEngine Location = new LocationEngine();
Tester Testman = new Tester();
MusicPlayer.Track("0");
while (true)
{
Menu:
Console.WriteLine("Welcome to the main menu. Enter 'create' to create a character and begin the game. Enter 'play' to log in.\r\nSaving features are to be implemented.\r\n");
while (true)
{
string MenuSelection = Console.ReadLine();
if (MenuSelection == "create")
{
CurrentPlayer.Creation();
goto Menu;
}
else if (MenuSelection == "play")
{
if (CurrentPlayer.CharacterPass != "")
{
Console.Write("\r\nEnter your password: ");
string Password = Console.ReadLine();
bool Check = CurrentPlayer.Login(Password);
if (Check == true)
{
Console.WriteLine("\r\nAdd transition to location here.");
break;
}
else
{
break;
}
}
else
{
Console.WriteLine("Please create a character first.\r\n");
break;
}
}
}
while (true)
{
Console.WriteLine("\r\nPress any key to continue.\r\n");
Console.ReadKey();
string TownSelection = "";
Console.WriteLine("\r\nYou are in Town.\r\n\r\nYou can 'explore' for encounters\r\nYou can use 'stats' to check your character\r\nOr you can 'sleep' to return to the main menu\r\n");
TownSelection = Console.ReadLine();
if (TownSelection == "sleep")
{
Console.WriteLine("");
break;
}
else if (TownSelection == "stats")
{
CurrentPlayer.CharacterStats();
}
}
}
}
}
}
I don't know how I can make the CurrentPlayer object accessible to the other objects such as Location. Is it better to pass every relevant bit as reference all the way down the rabbit hole?
I have posted the entire thing on github here if you are feeling extra patient: https://github.com/Slocknog/rpgproject
...and thank you for the help. Please do comment on anything else that you think I should be doing differently or reading up on.
I would take a step back and make sure you understand the purpose of each class you write. (This may sound patronizing, but it's really not trying to be. This is a difficult part of design.)
The purpose of each class should be clear and constrained: it should be reasonably obvious whether any piece of functionality belongs in a particular class or not, and no class should take on too much responsibility. I'd encourage you to write documentation comments on each class to explain its purpose - this will make it easier for you to come back and ask yourself whether some aspect of state and functionality really makes sense for that class.
Next, think about three ways data can be available to the class:
Through static variables. This is effectively global state, and is best restricted to natural constants. Global state should usually be immutable (unless it's something like a cache) as otherwise it can make testing and reasoning about your code fairly difficult.
Through instance variables. This is the state for an instance of the class (an object). It should be state that feels like it naturally belongs to an instance for its whole lifetime.
Through method parameters. This is information that is useful just for the duration of a single method call. For example, I don't think a LocationEngine should really know about the Character as part of global or instance state, but it might make sense to pass a Character reference into a method. (It might make more sense to pass a Location reference into the method, which might be obtained from a Character - it's hard to say without effectively trying to do a large portion of design for you.)
It's definitely worth putting significant thought into these decisions early on - although you should still expect to make mistakes. If you find yourself having to write code that feels ugly quite a lot of the time, in terms of how it accesses information, think about whether that information currently "lives" in the right place.
(Oh, and you're wise to separate "learning C#" from "learning Unity" in my view. Quite a lot of Unity uses idioms/techniques/conventions that would raise eyebrows in other C# codebases. Learning about them only when you move into Unity means you're less likely to carry them over to writing C# code elsewhere. Additionally, debugging "regular" C# code, particularly console applications, is somewhat simpler than having to worry about the Unity editor etc.)
you could have all those "other objects" Constructors accept a Character object to which you'll provide CurrentPlayer
supposing you have the following Character class
class Character
{
public string Name { get; set; }
}
then your LocationEngine class would be:
class LocationEngine
{
Character _player;
public LocationEngine(Character player)
{
_player = player;
}
public void SomeMethod()
{
string playerName = _player.Name;
}
}
and in your MainMenu class you'd code:
public class MainMenu
{
Character CurrentPlayer = new Character();
LocationEngine location = new LocationEngine(CurrentPlayer);
...
}
In the perspective of callbacks, I am facing a strange situation when I knew that myDelegate.Target contains the reference to the class whose method it contains. (I searched it on SO, however I excuse if I missed some thread already answering this)
For example
public delegate void TravePlanDelegate();
public class Traveller
{
//Papa is planing a tour, along with Mama
public void Planner()
{
//Asking me (delegate) to hold a letter about PlanA's detail
TravelPlanDelegate myPlan = PlanA;
//Sending me to TravelAgency office with letter
new TravelAgency().ExecuteTravelPlan(myPlan);
}
public void PlanA()
{
//Papa's open plan with Mama
Console.WriteLine("First Berline, then New Yark and finally Lahore");
}
public void PlanB()
{
//Papa's secret plan
Console.WriteLine("First Dubai, and then Lahore");
}
}
public class TravelAgency
{
public void ExecuteTravelPlan(TravePlanDelegate tp)
{
Traveller traveller = (Traveller)tp.Target;
//Here it should execute plan
//tp.Target - A reference to Traveler class, which can lead travel
//agency to Papa's secret plan (And exposes it to Mama)
}
}
In this example, TravelAgency can get information from delegate about papa's secret plan too. Did I get delegate concept properly or missing something?
Your assumption is correct. Unfortunately, however you try to "encapsulate" your object- there must always be a reference to it somewhere, otherwise it would be impossible to invoke it's instance method.
As some kind of counter measure, you can proxy the method invocation to a lambda expression:
TravelPlanDelegate myPlan = (args) =>PlanA(args);
This makes it less likely that any rogue code will attempt to carry out some ill intended operations on your code, since knowing how your code looks like in advance will not help it accomplish a thing.
Note that this does not ensure a thing, since the produced delegate still has a Target property to an object which holds a reference to yours.
Crackers which are smart enough can still apply reflection to the generated class and obtain a reference to your object.
Conclusion:
Only consume code you trust - it is not much of a problem in today's Open Source driven world.
Right now I have six classes:
Listener - manages socket connections
World - a collection of entities and tasks
Ticker - coordinates updating the world
MessageProcessor - receives commands from players
Intelligence - governs the behavior of non-player characters
Tasks - tracking and execution of tasks
But they are like spaghetti with reference to each other all over the place... The World is a data model which the MessageProcessor, Intelligence, and Tasks classes modify. The Ticker coordiates those three classes updating the World. The Listener is used by the MessageProcessor for incomming messages, and by the other classes to push updates.
How can I improve this situation?
I gave a related answer not long ago. The subject was on improving the testability of code, for which the general solution is to loosen coupling. The main focus on that previous answer was on decoupling networking related code from the world and it's logic, because networking code is not unit testable and is a pain to mock too.
The solution given there was to use an interface for incoming messages, such that you decouple the MessageProcessor (named Handler in other post) from the network code, and similarly, decouple the UpdateNotifier from the World.
The dashed line is just an indirect reference handled either by an interface or delegate. There exists no direct relation between the World and networking component now, making it testable. This is really just an application of the Model View Adapter pattern.
This doesn't seem dissimilar to the design you've described having, except perhaps you are missing a few interfaces. With this pattern of interface based UpdateNotifiers used to push updates, I essentially reuse the same architecture for handling NPCs, tasks, or anything else which is processed elsewhere. You cherry pick the event notifiers you need for a particular area, and implement a concrete Notifier class for them, such that you have multiple adapters on the same model.
And that really only looks more complicated than it is. The World object has no direct dependencies on anything else, and each other class has at most one direct dependency. You can also isolate the timer from the World, as it probably isn't needed there - but perhaps one of the biggest hurdles is handling synchronization between the different adapters.
Well, I'm not sure I have a full picture of what the issues you are having are, but I have a few possibilities from what you have laid out so far. (I may be actually suggesting some things that are already done since I'm not sure I have enough from the one-liner descriptions to understand fully.
The Model
I would say from what you've described, the main thing that strikes me is that you'll want to start implementing common functionality in a class model; you will want either interfaces or base classes that you can use to derive your high-level objects from.
This way you can handle things consistently with little extra effort. I think the idea of "architectural layers" can be useful as a first cut of how to think about it, (e.g. low-level hardware stuff, socket handling etc., then middle-layers stuff like what kind of things happen in your game, and the details behind how game mechanics work, etc., and high-level stuff like what can the PC or NPCs do, what's the environment doing, etc.. and also the idea that you never want to "jump" layers). However, when it comes down to it the important thing is to just find the right abstractions for your game, and keep everything organized in such a way as you never feel like the bit of code you're working on is doing two completely different kinds of things.
So, first, let's take the fact that it sounds like (naturally) there are a lot of things interacting with world state. For something like this, it's probably advantageous to factor a lot of the 'stuff' out into a single class, and then mostly only have the one class doing that job. Ideally you implement, say, event communication/message passing, in it's own little group, so that there's no need to pollute your higher-level objects with the nitty-gritty of handling stuff.
e.g., you want to focus on what things are doing at a high level in the higher-level objects: in an AI perhaps "begin movement toward a location", "set my haste", "stop movement"; and in an environment subsystem do "start raining", "increase windspeed", "dim lights"; in a user class "fire weapon", "sleep", "cast spell". But I wouldn't want any of my high-level classes to even know about things like "send message to world", or "reset thirst timer", or "receive socket data", or "health cycle tick". (These are all just elucidations, not suggestions. ;D)
Events
For instance, I think it may be valuable to keep one object in charge of dispatching events to the World, that way you no longer have everyone talking to everyone. I would likely just create a set of stuff to handle events in general. So, maybe EventMain and an enumEvents that you use so that each type of event has a special ID. And then use Event as the base class for particular events that need extra functionality. (I have both the ID as well as a derivation model in mind, so that things thing like the Dispatcher which likely only need to know very basic things about the event don't have to also know about the derived classes. For instance, the dispatcher could take an event in and send it out without ever having to know the internals of a derived event. This may or may not turn out to be useful, but it's good to have the options.) You could also have an EventDispatcher that has a queue of events to be send to other subsystems.
You will want something common for recieving and sending events. You could do EventSourcer and EventSinker standalone classes that can be set up in any class that is generating or receiving events. Or, you could instead do IEventSource and IEventSink so that you could implement a common interface on multiple classes, or perhaps a common class EventSourceAndSink that implements both, and which is part of your base class model, so that anything that might need to handle events can just derive from it.
I would probably make ProtocolEncoder and ProtocolDecoder classes. You can always combine them into a single object, but it may be valuable, and shouldn't cause any issues if done adequately, to have them be two separate pieces of code. You could also have a ProtocolHelper class that factors out anything in common between the two. The encoders sole job is to receive messages from the network and turn them into events for your game, which it will then pass on to the EventDispatcher. The decoder class will take events from the dispatcher that need to go out to the network, and it will take the data from them and send it out.
How to Get Where You're Going
Since you do have working code, I would recommend that you just start doing it wherever seems natural. That could be things that are bogging you down, or things you've noticed to be very similar in different places that you could make regular with a class or some other type of abstraction, then pull out the old and put in the new. Once you have figured out a workable first cut of a class model, that should give you ideas based on what you already have and as you go be constantly reconsidering your model, fixing the things that are a problem, lather, rinse, repeat.
It doesn't have to be a lot of work, in fact, some of the most gratifying moments I've had working on code was when I was able to do a neat refactor that left a formerly hideous mess in much better shape, removing a lot of hard-to-understand code, and replacing it with something that's easier to understand, in fewer lines of code, and that opened up a path toward my next addition being a pleasure instead of another "zomg I don't have to touch that code again do I?" moment.
Best of luck, follows is a nominal guide to the things I was talking about; the first bit is more detailed because the main event class is one of the more important concepts, and then I try to just give a nominal overview of the classes and how they interact. I'm sure I could spend even more hours on this, but at this point I'll just say: ask me if you have questions and I'll do what I can to give you a good answer :)
Ideas in Code
Oh, one more thing of note is I didn't deal at all with the complexities added if you have multiple threads; there are things ranging from simple to intricate to manage it all if you do, but that's another exercise. :)
using System;
using System.Collections;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Windows.Forms;
// this is internal to the project namespace, say, TimsWorld_o_Hurt
// I'm now resisting calling everything Xxxx_o_Hurt :)
// examples o' hurt
using EventHandlingLibrary;
namespace EventHandlingLibrary
{
// this will provide the base class for all the events, and can
// also have static methods like factory methods, destination
// lookups etc.
// I have the enums set to protected with the intent being that
// specific factory functions should be called by other classes.
// You should change this if it turns out to be too cumbersome.
public class EventOfHurt
{
#region Event Definitions
protected enum EEventType
{
// System Events
SystemInitializing,
SubsystemInitComplete,
FatalErrorNotification,
SubsystemPingReponse,
SubsystemPingRequest,
// Network Events
FrameRateError,
ThroughputData,
ServerTimeout,
ServerPingRequest,
ServerPingResponse,
// User Events
WeaponsFire,
MovementNotification,
FatigueUpdate
// and so forth
}
protected enum ESubsystem
{
System,
Dispatcher,
TickerTimer,
WorldEntity,
WorldTaskManager,
UserMessageProcessor,
NetworkListener,
NetworkTransmitter,
ProtocolEncoder,
ProtocolDecoder,
PlayerCharacter,
NonPlayerCharacter,
EventSink,
EventSource
// and such
}
#endregion
#region Event Information
public Guid EventId { get; protected set; }
public EEventType EventType { get; protected set; }
public ESubsystem SourceSubsystem { get; protected set; }
public ESubsystem DestSubsystem { get; protected set; }
private List<Tuple<EventOfHurt, DateTime>>
myEventReferences;
// the event(s) that triggered it, if any, and when rec'd
public Tuple<EventOfHurt, DateTime>[]
EventReferences
{
get { return myEventReferences.ToArray(); }
}
public DateTime Timestamp { get; private set; }
#endregion
// we'll be using factor methods to create events
// so keep constructors private; no default constructor
private EventOfHurt(
EEventType evt,
ESubsystem src,
ESubsystem dest = ESubsystem.Dispatcher
)
{
EventType = evt;
SourceSubsystem = src;
DestSubsystem = dest;
EventId = Guid.NewGuid();
Timestamp = DateTime.UtcNow;
}
// called to create a non-derived event for simple things;
// but keep other classes limited to calling specific factory
// methods
protected static EventOfHurt CreateGeneric(
EEventType evt, ESubsystem src,
ESubsystem dest = ESubsystem.Dispatcher,
Tuple<EventOfHurt, DateTime>[] reasons = null
)
{
EventOfHurt RetVal;
if (dest == null)
dest = ESubsystem.Dispatcher;
List<Tuple<EventOfHurt, DateTime>> ReasonList =
new List<Tuple<EventOfHurt,DateTime>>();
if (reasons != null)
ReasonList.AddRange(reasons);
// the initializer after the constructor allows for a
// lot more flexibility than e.g., optional params
RetVal = new EventOfHurt(evt, src) {
myEventReferences = ReasonList
};
return RetVal;
}
// some of the specific methods can just return a generic
// non-derived event
public static EventOfHurt CreateTickerTimerEvent(
EEventType evt, ESubsystem dest
)
{
ESubsystem src = ESubsystem.TickerTimer;
return CreateGeneric(evt, src, dest, null);
}
// some may return actual derived classes
public static EventOfHurt CreatePlayerActionEvent(
EEventType evt, ESubsystem dest,
Tuple<EventOfHurt, DateTime>[] reasons
)
{
PlayerEvent PE = new PlayerActionEvent(42);
return PE;
}
}
// could have some specific info relevant to player
// events in this class, world location, etc.
public class PlayerEvent :
EventOfHurt
{
};
// and even further speciailzation here, weapon used
// speed, etc.
public class PlayerActionEvent :
PlayerEvent
{
public PlayerActionEvent(int ExtraInfo)
{
}
};
}
namespace EntitiesOfHurt
{
public class LatchedBool
{
private bool myValue = false;
public bool Value
{
get { return myValue; }
set {
if (!myValue)
myValue = value;
}
}
}
public class EventOfHurtArgs :
EventArgs
{
public EventOfHurtArgs(EventOfHurt evt)
{
myDispatchedEvent = evt;
}
private EventOfHurt myDispatchedEvent;
public EventOfHurt DispatchedEvent
{
get { return myDispatchedEvent; }
}
}
public class MultiDispatchEventArgs :
EventOfHurtArgs
{
public MultiDispatchEventArgs(EventOfHurt evt) :
base(evt)
{
}
public LatchedBool isHandled;
}
public interface IEventSink
{
// could do this via methods like this, or by attching to the
// events in a source
void MultiDispatchRecieve(object sender, MultiDispatchEventArgs e);
void EventOfHurt(object sender, EventOfHurtArgs e);
// to allow attaching an event source and notifying that
// the events need to be hooked
void AttachEventSource(IEventSource evtSource);
void DetachEventSource(IEventSource evtSource);
}
// you could hook things up in your app so that most requests
// go through the Dispatcher
public interface IEventSource
{
// for IEventSinks to map
event EventHandler<MultiDispatchEventArgs> onMultiDispatchEvent;
event EventHandler<EventOfHurtArgs> onEventOfHurt;
void FireEventOfHurt(EventOfHurt newEvent);
void FireMultiDispatchEvent(EventOfHurt newEvent);
// to allow attaching an event source and notifying that
// the events need to be hooked
void AttachEventSink(IEventSink evtSink);
void DetachEventSink(IEventSink evtSink);
}
// to the extent that it works with your model, I think it likely
// that you'll want to keep the event flow being mainly just
// Dispatcher <---> Others and to minimize except where absolutely
// necessary (e.g., performance) Others <---> Others.
// DON'T FORGET THREAD SAFETY! :)
public class Dispatcher :
IEventSource, IEventSink
{
}
public class ProtocolDecoder :
IEventSource
{
}
public class ProtocolEncoder :
IEventSink
{
}
public class NetworkListener
{
// just have these as members, then you can have the
// functionality of both on the listener, but the
// listener will not send or receive events, it will
// focus on the sockets.
private ProtocolEncoder myEncoder;
private ProtocolDecoder myDecoder;
}
public class TheWorld :
IEventSink, IEventSource
{
}
public class Character
{
}
public class NonPlayerCharacter :
Character,
IEventSource, IEventSink
{
}
public class PlayerCharacter :
Character,
IEventSource, IEventSink
{
}
}