Jump to content
XCOMUFO & Xenocide

Xna Gamecomponent Branch


dteviot

Recommended Posts

Observations.

  • The factoring out of the EmbeddedScene class was a good idea.
  • Making the screen manager and the screens a Game Component looks like a good idea as well. It looks cleaner than the screen manager being a static member of the root Xenocide object.
  • However, I fail to see why the Game object is passed into the constructor for each screen/dialog. E.g.
    protected Frame(Game game, string ceguiId, System.Drawing.SizeF size)
      : base(game)
    {
      this.ceguiId = ceguiId;			
      this.size = size;
    ?
    }


    As we only have the one instance of the Xenocide object, we could add putting a reference to it in the frame base class and be done with it. E.g.

    protected Frame(string ceguiId, System.Drawing.SizeF size)
    : base(Xenocide.Instance)
    {
    this.ceguiId = ceguiId;			
    this.size = size;
     ?
    }


    Of course, the Xenocide class isn't a static or singleton class, but there is only one instance of it, created in the Program class. So you could put a static accessor method into the Program class.
     

  • Consider changing code like this
    public IList<ResearchTopic> ResearchableTopics
    {
    get
    {
    	List<ResearchTopic> result = new List<ResearchTopic>();
    	foreach (ResearchTopic topic in researchTopics.Values)
    	{
    		if (topic.IsResearchable)
    		  result.Add(topic);
    	}
    	return result;
    }
    }


    To this:

    public IEnumerator<ResearchTopic> ResearchableTopics
    {
    get
    {
    	foreach (ResearchTopic topic in researchTopics.Values)
    	{
    	   if (topic.IsResearchable)
    		  yield return topic;
    	}
    }
    }


    In fact, you could even abstract further and have a FilteredResearchTopics() function that takes a predicate to specify the filter critera, then use an anonymous method to specify the predicate. E.g. something like this:
     

    private delegate bool ResearchTopicFilterPredicate(ResearchTopic topic);
    private IEnumerable<ResearchTopic> FilteredTopics(ResearchTopicFilterPredicate filter)
    {
    foreach (ResearchTopic topic in researchTopics.Values)
    {
    	if (filter(topic))
    	{
    		yield return topic;
    	}
    }
    }
    
    public IEnumerable<ResearchTopic> ResearchableTopics
    {
    get
    {
    	return FilteredTopics(delegate(ResearchTopic topic){ return topic.IsResearchable; });
    }
    }


  • As regards updating the game state via delegates such as GameTime.DayPassedHandler(), as opposed to the existing Update() hierarchy, I think it's something of a matter of personal taste.
    The Handler method has the advantages:
    • It is more flexible. (In that any class just registers an event with the handler to get plugged into the update stream.)
    • It's slightly less work to add something to the update ''pump''.

    However it does have the following disadvantages:

    • It's a lot harder to guarantee the order the items will be updated in. This may give problems when order of events is important. For example, if during an update cycle a UFO ended its mission and an aircraft reached attack range. Under existing model, the UFO ending is evaluated before aircraft attacks, so aircraft won't reach the UFO. However, under the handler model, the aircraft could be evaluated before the UFO ending. This would result in both ''Craft reached UFO'' and ''UFO escaped'' events being posted into the queue for processing. (Because the UFO won't be destroyed by the Craft reached UFO event.) If the user then selects ''Attack UFO'' in the dialog that pops up, bad things start to happen, as the UFO is gone.
      (This is, in my opinion, the main problem with the Handler approach.)
    • The logic of what is called is called during an update is not quite as obvious. (With the hierarchy, you just follow the update() calls from the root.)

Link to comment
Share on other sites

Of course, the Xenocide class isn't a static or singleton class, but there is only one instance of it, created in the Program class. So you could put a static accessor method into the Program class.

Following the Game on construction pattern is something that XNA people advocate pretty fiercely, I still cannot grasp why do it that way (Away of avoiding static references that in my opinion is a very wise decition), but there must be a pretty important reason behind, we should follow that pattern for GameComponents.

 

Greetings

Red Knight

Link to comment
Share on other sites

Additional problems with GameComponent branch.

1. When a dialog is up on the Geoscape, (e.g. Options) geoscape time needs to stop. It doesn't. I suspect this is because with GameComponent model, as long as the GeoscapeScreen is visible, its Update() method is being called. And this pumps Xenocide.GameState.GeoData.Update()

2. Scene in Bases Screen is distorted. (I suspect viewport isn't being set correctly.)

Link to comment
Share on other sites

Posible solutions to that involve the decoupling of the Update in the time service from the time event. We talked about it with Rincewind the other day.

 

Basicly it involves creating a service that publish the following items (push format):

- 1 Hour event

- 1 Day event

- 1 Month event

- 1 Year event

 

And handle the higher resolution events in a pull format from the service, so that the service can be signaled to Stop counting on a condition (condition may be it is in battlescape, dialog appeared, etc). Any idea about it?

 

Greetings

Red Knight

Link to comment
Share on other sites

Posible solutions to that involve the decoupling of the Update in the time service from the time event. We talked about it with Rincewind the other day.

 

Basicly it involves creating a service that publish the following items (push format):

- 1 Hour event

- 1 Day event

- 1 Month event

- 1 Year event

 

And handle the higher resolution events in a pull format from the service, so that the service can be signaled to Stop counting on a condition (condition may be it is in battlescape, dialog appeared, etc). Any idea about it?

 

Greetings

Red Knight

 

Isn't there an "Enabled" property in GameComponent that stops the calls to Update? I guess ScreenManager could then just "disable" GameTime once a dialog is up.

 

About push vs. pull and the game ticks. I initially updated the ResearchTopics from the update method, but ran into the following problem: due to the limited accuracy of even "double", the research time was significantly off when using higher simulation speeds. On the 60 seconds speed, it was only a quarter of an hour per day, on the fastest, it just dropped several days for each gameday. Thats how the push concept with larger time steps came up. I still think that the AI needs to be run by Update (which possible also involves investigating if it holds up for higher game speeds), but for research, purchasing, etc. the pushmodel is more intuitive to code and we don't have to duplicate the code necessary to deal with the rounding errors. We could also introduce a timer-concept, so a component could register with GameTime to receive an event once a particular date is reached.

 

Passing in the Game-object into each Component allows the Game-Property in the base class and access to the Game-Object from all components, after this initial refactoring is done, I don't think it's that much of a hassle and I really, really dislike having global variables (and static-members are just a nicer way of declaring them).

 

I really like the way, you can "yield return", didn't know this existed and will change the code accordingly. I'll also look into the filtered topics method.

 

I will take a look at the BasesScreen, I guess I missed something there as getting the Scene-based screens working was the last thing I did and I just wanted to finish it to a demoable state :-)

 

Anyway, thanks for all the feedback, I might be able to come on irc tonight (tonight being in GMT -2), so if there's someone there, we could discuss this further.

 

Cheers,

 

Rincewind

Link to comment
Share on other sites

We could also introduce a timer-concept, so a component could register with GameTime to receive an event once a particular date is reached.

The name for that could Scheduler ;) ... You can even go further with it, I implemented something like that before to defer the execution of commands until the Update Render Loop of Xna starts (was mixing Xna with Winforms :)).

 

Bear in mind this is not extrictly XNA, but Hydra a framework that I did to mix XNA style programming and Winforms.

 

The idea is the following:

 

public interface ICommand
{
 void Execute ();
}

 

Now, you can create functional style functors :)

	public class FunctionCommand<Param1, Param2, Param3> : ICommand where Param2 : EventArgs
{
	public delegate void FunctionCommandHandler(Param1 param1, Param2 param2, Param3 param3);

	private FunctionCommandHandler handler;
	private Param1 param1;
	private Param2 param2;
	private Param3 param3;

	public FunctionCommand(FunctionCommandHandler handler, Param1 param1, Param2 param2, Param3 param3)
	{
		this.handler = handler;
		this.param1 = param1;
		this.param2 = param2;
		this.param3 = param3;
	}

	public void Execute()
	{
		handler(this.param1, this.param2, this.param3);
	}
}


public class FunctionCommand<Param1, Param2> : ICommand where Param2 : EventArgs
{
	public delegate void FunctionCommandHandler(Param1 param1, Param2 param2);

	private FunctionCommandHandler handler;
	private Param1 param1;
	private Param2 param2;

	public FunctionCommand(FunctionCommandHandler handler, Param1 param1, Param2 param2)
	{
		this.handler = handler;
		this.param1 = param1;
		this.param2 = param2;
	}

	public void Execute()
	{
		handler(this.param1, this.param2);
	}
}

public class FunctionCommand<Param1> : ICommand
{
	public delegate void FunctionCommandHandler(Param1 param1);

	private FunctionCommandHandler handler;
	private Param1 param1;

	public FunctionCommand(FunctionCommandHandler handler, Param1 param1)
	{
		this.handler = handler;
		this.param1 = param1;
	}

	public void Execute()
	{
		handler(this.param1);
	}
}

public class FunctionCommand : ICommand 
{
	public delegate void FunctionCommandHandler();

	private FunctionCommandHandler handler;

	public FunctionCommand(FunctionCommandHandler handler)
	{
		this.handler = handler;
	}

	public void Execute()
	{
		handler();
	}
}

In the end the code looks like this, and gets executed in when it is needed.

 

		public void ChangeScene(object sender, EventArgs e)
	{
		SceneRenderService renderService = this.WorkItem.Services.Get<SceneRenderService>(true);
		renderService.ExecuteOnUpdate(new FunctionCommand(onchangeScene));
	}
	
	private void onchangeScene ()
	{
		SceneRenderService renderService = this.WorkItem.Services.Get<SceneRenderService>(true);
		Scene currentScene = renderService.SceneGraph.CurrentScene;
		SceneNode parallaxNode = new ParallaxSceneNode(currentScene.RootNode);
	}

 

Greetings

Red Knight

Link to comment
Share on other sites

That command stuff is kinda like the GeoEvent-System dteviot has introduced. It's currently not working as I need to refactor it, so the events keep a game-reference or get it passed into the Execute-Method (or Process, thats how it's called with GeoEvents), as they open up dialogs, etc where we need the Game reference.

 

Will try to get some of that implemented this week.

 

Rincewind

Link to comment
Share on other sites

Posible solutions to that involve the decoupling of the Update in the time service from the time event. We talked about it with Rincewind the other day.

 

Basicly it involves creating a service that publish the following items (push format):

- 1 Hour event

- 1 Day event

- 1 Month event

- 1 Year event

 

And handle the higher resolution events in a pull format from the service, so that the service can be signaled to Stop counting on a condition (condition may be it is in battlescape, dialog appeared, etc). Any idea about it?

 

Greetings

Red Knight

 

Isn't there an "Enabled" property in GameComponent that stops the calls to Update? I guess ScreenManager could then just "disable" GameTime once a dialog is up.

 

About push vs. pull and the game ticks. I initially updated the ResearchTopics from the update method, but ran into the following problem: due to the limited accuracy of even "double", the research time was significantly off when using higher simulation speeds. On the 60 seconds speed, it was only a quarter of an hour per day, on the fastest, it just dropped several days for each gameday.

That's odd. Facility construction times use the update() hierachy. And when I last checked, the facility was built to within the accuracy of the update quantum. (That is, it was within 1 minute, over a period of 10 game days.)

 

Thats how the push concept with larger time steps came up. I still think that the AI needs to be run by Update (which possible also involves investigating if it holds up for higher game speeds), but for research, purchasing, etc. the pushmodel is more intuitive to code and we don't have to duplicate the code necessary to deal with the rounding errors. We could also introduce a timer-concept, so a component could register with GameTime to receive an event once a particular date is reached.

 

Passing in the Game-object into each Component allows the Game-Property in the base class and access to the Game-Object from all components, after this initial refactoring is done, I don't think it's that much of a hassle and I really, really dislike having global variables (and static-members are just a nicer way of declaring them).

 

I really like the way, you can "yield return", didn't know this existed and will change the code accordingly. I'll also look into the filtered topics method.

I've already updated it in the main line, so you can just copy the code.

I've put a generic algorythm FilterCollection that should work on any type of collection into the Util class.

I think I'm re-inventing STL.

 

 

I will take a look at the BasesScreen, I guess I missed something there as getting the Scene-based screens working was the last thing I did and I just wanted to finish it to a demoable state :-)

 

Anyway, thanks for all the feedback, I might be able to come on irc tonight (tonight being in GMT -2), so if there's someone there, we could discuss this further.

 

Cheers,

 

Rincewind

Link to comment
Share on other sites

Update:

 

- I fixed the BaseScreen to correctly show the floorplan. Some leftover fields from EmbeddedScene-Refactoring messed it up

- Showing a dialog now stops the Frame's that are below it. By the way, this allowed to make Frame's Enable() method private and just hook it up to the GameComponents EnabledChanged-event. I guess we should really try to look towards XNA for solutions when trying to tackle a problem as most of it is already in there.

 

About the push vs. pull, I think I have something even better, that combines the two approaches. We enlarge on the Event-Scheduling idea. What this means is moving the logic about counting down a particular time into the Event/GameTime-Parts of the game.

 

This poses that problem that we have to tell the user how long a particular project is going to take, so we could just keep a reference to the scheduled event and a UI-Component could then ask it for the amount of time left. GameTime automatically counts it down. We could even have GameTime keep these events in a sortedqueue (kinda like the facility-building code currently does) and have it only count down the first one on a regular basis and the rest only if it's asked for its time left.

 

What do you guys think? This would at least remove that millis-to-second conversion and custom handling of "things" to update inside each part of the model that needs it.

 

Edit: this makes something else possible: we could then stop gametime to have the clock stop and still keep the "screen" running (e.g. to show a rotating model in xnet, would be nice to have that running even while we are in a dialog).

Edited by rincewind
Link to comment
Share on other sites

(08:48:34) dteviot: rincewind1010: OK, I've had a quick look at your post.

(08:48:46) dteviot: I don't have time to give a full reply now.

(08:48:59) rincewind1010: nevermind, I won't work on it tomorrow anyway

(08:49:02) dteviot: Will try and post feedback tonight.

(08:49:19) rincewind1010: great

(08:49:28) dteviot: one question, what exactly is distinction between pull and push?

(08:49:37) dteviot: as I see it there isn't much difference

(08:49:56) dteviot: between having the hieracy in the main loop

(08:50:03) dteviot: call into the sub components

(08:50:16) dteviot: as opposed to subcomponents register a delegate.

(08:50:22) dteviot: Either way, the main loop runs,

(08:50:31) dteviot: then pushes the update down into the sub compoents.

(08:51:30) rincewind1010: I want the following

(08:51:41) rincewind1010: you don't register a delegate but a fullblown object

(08:51:51) rincewind1010: Kind of like you GEoEvent

(08:52:05) rincewind1010: except the subcomponent gives that object a time-to-run

(08:52:22) rincewind1010: this time is automatically counted down by the GeoTime-Objekt

(08:53:01) rincewind1010: anytime, you can poll the GeoEvent-object what time is left (so you can show days-left for research and the like or use it for AI decisions)

(08:53:34) rincewind1010: once the geoevent's time-left has reached zero, it's process-method is called

(08:54:16) rincewind1010: that way, GeoTime can only update GeoEvents when it's needed (e.g. every time for the Event that has the lowest time left, and only when timeleft is requested for all the rest)

(08:54:45) rincewind1010: in addition, we don't have to have to low-level timeadvancing and checking code in all components that need to count down to a particular event

(08:57:16) dteviot: On thing, GeoEvents are not time related.

(08:57:34) dteviot: They're the channel for the model to signal that a state transition has occured

(08:57:47) dteviot: That the GUI side probably needs to do something about.

(08:58:10) dteviot: That is, I've tried to make the code follow a View-Model pattern.

(08:58:29) dteviot: The View (GUI) is allowed to manipulate the model directly.

(08:58:39) dteviot: The model is NOT allowed to manipulate the View.

(08:59:26) dteviot: But yes, I've been contemplating a global "timer queue". Probably should be implemented as a priority queue.

(09:00:42) rincewind1010: I guess you are right, that GeoEvents are a different thing

(09:00:53) rincewind1010: but I guess you got the concept I was trying to bring across, right?

(09:02:26) dteviot: I think so. Have an "alarm clock" in the main update pump. And allow components to put "times" in the queue when they are to be notified.

(09:03:07) dteviot: er that is, as well as (or instead of) getting a pump every hour, it gets a pump when a given time occurs.

(09:03:35) rincewind1010: yep

(09:04:02) rincewind1010: it's basicly just refactoring the current manual code into another class

(09:04:10) rincewind1010: to make it reusable

(09:04:47) rincewind1010: as currently, those Update-methods look like this: take step, subtract it from some time left and if timeleft has reached zero, execute some method

(09:05:02) rincewind1010: addionally, the value of timeleft is sometimes used to show some status in a dialog or screen

(09:05:45) rincewind1010: the behaviour that results (and that we need to preserve) is: advance timeleft, execute when reached, make timeleft readable

(09:05:54) dteviot: Yes, that's done in the base layout screen.

(09:06:15) rincewind1010: yep, and research "should" do the same, if we don't use that daily notification thing

(09:06:22) dteviot: Essentially, when the screen comes up, I go through the queue, updating all the time left.

(09:06:48) dteviot: And I think you'll find, in all cases where we show time left in queue, game time stops.

(09:07:05) rincewind1010: And we'll find plenty of other places where we need to do the same (purchase orders, item transfers, rearming of crafts...)

(09:07:07) dteviot: So can be implemented in timer with an "update all times remaining" call

(09:07:34) rincewind1010: basicly, yes

(09:07:38) dteviot: So each screen makes the call just before it populates itself.

(09:07:58) rincewind1010: you can even make it more efficient as you only need to update the timer with the lowest timeleft each frame (to check if we need to fire it's execute-method)

(09:08:28) rincewind1010: the rest only needs to be updated if someone wants to read the timeleft (which usually only happens when you show the dialog/screen that shows the status)

(09:08:38) dteviot: exactly.

(09:11:17) rincewind1010: so what do you think if I extend GeoTime to support this kind of system?

(09:11:25) rincewind1010: in the gamecomponent-branch

(09:12:23) dteviot: Go for it.

(09:12:37) rincewind1010: alright

Link to comment
Share on other sites

And some more progress:

 

I gave the GameState part of the game some refactoring. We needed to get a Game-reference into all things gamestate in order for them to use services. Since GameComponent is not serializable, we needed to introduce a way to inject the Game-object after a saved state has been loaded. This works now thanks to the GameStateComponent-class

 

Additionally, since we don't want to remove and add Services in between, GameState data is now accessed through Services. I implemented this already for HumanBases, Research and GeoTime. One could argue that we could just have a single GameState-Service that returns the entire current GameState, but I think it makes it clearer what UI part uses what part of GameState if we keep it this way.

 

Also, I refactored the Load/Save logic into a Service that is now being used by LoadSaveScreen.

 

As always, this is work in progress, so expect quite a lot of stuff to be broken :-) (But don't hesitate to report those broken things here).

 

Cheers,

 

Rincewind

 

P.S: This way a prerequisite to implementing the GeoTime Scheduler as I needed Research and Co. to have access to Services before they could actually schedule something. So this is next on my plate. (As well as GeoEvents as they got mostly disabled for now during this refactoring).

Link to comment
Share on other sites

Excelent, when do you estimate you will be able to merge the changes with Dteviot works on the main branch?

 

Greetings

Red Knight

Hold on. I have yet to be persuaded that converting everything in model to GameComponenets is something we should do.

In fact, the more I look at the UI stuff, the less convinced I am that what has been done is an improvement.

However, I'm willing to hold off making a decision until I see what Rincewind finishes up with.

 

Second Point: As regards the "Schedulded events" that we recently discussed.

  • Rather than store a "minutes/seconds remaining" for each event, it would probably be better to store the DateTime at which the event will occur. This would give two advantages. Firstly, we can eliminate the quantization rounding errors that are introduced by subtracting small time slices from the "time remaining". Instead we just check the DateTime against the GeoTime's now. Second, getting "time remaining" for display in dialogs is simple, we just get the "occurs" DateTime, and subtract the GeoTime's now from it.
  • The Scheduler is simple enough for events that proceed at a fixed rate: e.g. facility construction, delivery of supplies, transfers, healing of agents. However, Research and Item construction projects are more difficult, because the finish time can change as staff are added/removed from a project.

Link to comment
Share on other sites

In fact, the more I look at the UI stuff, the less convinced I am that what has been done is an improvement.

OK, I'll expand on this claim.

 

In the mainline, the constructor for all the dialogs and screens required a ScreenManager parameter.

This was a reference to the single, static ScreenManager.

In the GameComponent branch, this was removed, and replaced with this in the Frame class (the base class for all screens and dialogs)

 

		public override void Initialize()
	{
		this.screenManager = (IScreenManager)Game.Services.GetService(typeof(IScreenManager));
		base.Initialize();
		Show();
	}

 

However, we've now got a Game parameter that needs to be passed into the constructor for every dialog and screen.

And again, this is a reference to the single, static Game object. Which is only needed for passing into the DrawableGameComponent constructor.

So again, I argue that the Game parameter should be removed from every dialog and frame's constructor, and used directly in Frame's constructor.

 

 

Another bit I can't figure out,

Why does the Frame constructor have this?

			EnabledChanged += delegate(object sender, EventArgs args)
			{
				Enable(this.Enabled);
			};

 

and then this?

		private void Enable(bool enableFrame)
	{
		if (enableFrame)
		{
			frameWidget.Enable();
		}
		else
		{
			frameWidget.Disable();
		}
	}

 

as near as I can figure it, what we need to do is change Enable() to

		protected override void OnEnableChanged(Object sender, EventArgs args)
	{
		base.OnEnableChanged(sender, args);

		if (Enabled)
		{
			frameWidget.Enable();
		}
		else
		{
			frameWidget.Disable();
		}
	}

 

As regards making everything in the model section a game component (e.g. GeoTime, HumanBases, etc.) I believe this is totaly wrong.

According to article on Nuclex on GameComponents, the idea is to allow reuse of modules between games, by reducing coupling between modules.

But most of the components inside the Geodata, e.g. HumanBase are not intended for reuse. (And quite frankly, I don't see us wanting to reuse them.)

Well, OK the research tree could possibly be reused in other games, but it's very tightly coupled to the other geodata. Decoupling for reuse would be a significant piece of work, for no benefit to Xenocide.

 

The GeoData IS the module. The GameTime pump is possibly reusable between games, but I'm not sure even it makes a good game component.

Because we're going to need at least 2 of them, one for the Geoscape, and one for the battlescape, and I don't see how the GameComponent model supports having 2.

 

Final comment, I've now spent 6 hours trying to figure out how Rincewind's changes to the Model part of the code base even work now that GameComponent has been added, with little success.

In fact, so far as I can tell, it doesn't. The fact that it doesn't compile makes testing my theory difficult.

Maybe it's my arrogance, but if I can't figure it out in that much time, I suspect most other people are not going to do any better.

Which means we're not going to have many people working on the code.

Certainly, if I can't understand the code, I'm not going to work on it.

 

And I'm not going to spend any more of my time trying to figure it out.

When Rincewind is done, he can post up some notes explaining how it works, and then I'll look at it again.

Link to comment
Share on other sites

Alright, here we go:

 

first of all, the current commits are heavily work in progress. I committed for two reasons, one is that those interested can already see some progress, the other is that I can go back to defined points in my changes if I mess something up. Thats what branches are for.

 

I think the EnabledChanged issue is one of the points. What it allows us to do is use the facilities provided by GameComponents (e.g. have a unified concept of stopping the "Update-Pump" on an object and being able to react on it). Before, there was the Enable-Method that basicly did the same thing and replicated much of this logic. Since I'm in the process(!) of changing the code, I just kept the method and hooked up its logic to the EnabledChanged (if I remember correctly, I even made it private). As you correctly pointed out, the naming is no longer exaclty right, but again, we are work in progress here.

 

The comment about reusablity of GameComponents, reusability requires decoupling of components, which is exaclty what GameComponent/Services provide (and static "globals" do not). As you said, most of those GameComponents are not going to be reusable in other projects. Nevertheless, having less coupling also benefits the development process for a single project especially if you have several people working on it. So I'm advocating this Service based architecture not on the grounds of reusability but on the grounds of reduced coupling.

 

The question of whether the code structure is understandable, I again refer to the work in progress and experimental nature of the branch. The current state is not my proposal for a new "trunk", I'm still trying out stuff and have rewritten some parts of it already several times. So I really love to get comments about what it good, and especially what is not and questions about how stuff works, but please refrain from taking it as a final proposal of how the code is supposed to be structured. Rather take it as an opportunity to challenge ways of thinking and lets see what we can learn from it, maybe it will turn out just as a playground to try out ideas that could incrementally improve trunk.

 

The current code not compiling is something I'm really sorry about and I'm curious as to the why, as SVN shows no pending changes here, and it compiles fine for me. Could you post the compile errors?

 

Anyway, I would really like to get back to a productive discussion, as I think we all still have to learn a lot about the xna-way of doing things and I think we should try to get familiar with that in order to use the framework to its full potential. (Sorry if I sound a little pissed, but I think most of what is provided in terms of examples and documentation is more toy-examples and doesn't help with big projects, so we should try to figure it out together and not see it as a contest as to whoose way of thinking is the right one).

 

Cheers,

 

Rincewind

Link to comment
Share on other sites

A couple of comments regarding XNA that you both may have missed, I have been following Managed DirectX from Beta2 and XNA development since the early beta (started to use it for trying out stuff on the very first beta release day) because in the company we use XNA to do visualization stuff. Current project ended up being on Windows Presentation Foundation because of technology limitations on the clients, not because XNA wasnt up to par with what we needed.

 

I had to get very deep, up to the point of constructing a framework that simulated the Game Framework because it cannot be used in a Winforms (shared context) application. So basicly you can now code in Game Framework and with minimal changes it runs on Hydra a SmartClient enabled framework (for more information on SmartClient google it on the Microsoft Patterns and Practices site).

 

Having had to go that deep on the foundation (even using reflector), this are the most important things to get right in any application that pretends to use XNA to its full potential:

  • Update/Draw pump must not be taken lightly, even a small derouted message on the Update/Draw can make your application behave wrongly or create rippling effect on the whole application structure so you have to be very conservative on how you use it. What I am saying is that you shouldnt do more than what they are supposed to do. In draw, just push triangles to GPU, set rendering context, there you go; avoid any contact if possible with any one of the objects arround (only with childrens), remember in draw your updating power is NULL. Regarding that, always remember that the pump is hierarchical, you have a Enabled and Visible properties to control it; do not under any circunstance reinvent that cause you will end up in a long time mess.
     
  • Beware of the samples, sometimes not even XNA developers may fully realize what do they have created. A similar architecture has been arround for a long time in the .Net business industry named CAB and its newborn SmartClient (strange isnt it?? ;)) dont know if the developers are connected to those in the P&P team though. Furthermore, remember that those that do the examples most of the times are not the ones doing XNA development, but 3rd parties that most of the times work in C++ for a living; so they bring their C++ knowledge to .Net that sometimes is not the "right" way to do (I found that after several years of interaction with Microsoft's Consultants). Samples arent bad, but do not take them as stricly the way to do things. I can mention a couple of things like for instance Spacewars do not even use the concept of a GameComponent (again, strange isnt it? ;) ) as they are the ones that invented the concept :) the original developer just copied the standard C++/C way of handling the input in the main loop and work from there. Just as another example take a look at how this article finish: http://blogs.msdn.com/shawnhar/archive/200...subfolders.aspx
     
  • Everything has to be a GameComponent? Every GameComponent has to publish a GameService?? heck no, and everything has to be a GameService there again, NO!!! But then what it is what differenciate a GameComponent and a GameService? I tried to explain that to Rincewind on MSN (if you have the log publish it). To my knowledge (that it is pretty poor IMHO) there are 2 things that are very important. GameComponents are a mix of a WorkItem and Presenter in SmartClient's jargon (that is if anyone of you want to look it at?). As I explained to Rincewind, Services are like the "sensors" and "world actuators" you need to have them to interact with other "agents" (entities in the system). Do all GameComponents have to publish GameServices? No really. Do all services have to be published by Component? There again, no. Everything is about the worlds hierarchical nature, so lower level hierarchies should be able to reason about the sensors provided by higher level world entities, but not on cousins that are at the same level (this is counterintuitive at the start but when you stop a minute and realize that not everything it is what it seams, it starts to make sense). For instance: We have a "Main" world named "Xenocide" that is where we start our journey, we are at the UI and have several things to do there, but nothing happens until we move deeper to what we call a "Xenocide Game" (XGame for Short), "Xenocide" doesnt mind and doesnt care what XGame do with its time, but if Xenocide ensures that it would setup an Input Device and provides the keymapping (as an example) then Xenocide should be able to use that knowledge. Xenocide is a higher world entity, XGame is a second order one. Let think what does XGame knows about the world, it does know that he is provided the KeyBindings and probably is able to change it, who know, probably not. If he is not able, that is just a sensor it is posible, it becomes an actuator too. Then what would out XGame needs to know, he has to be able to track the game state, important things like winning conditions, important stage like how many bases we have, soldiers, which soldiers, whatever it makes sense for him to have. However, what about Basescape and Geoscape?? What about Battlescape? In the scheme of things Geoscape, Battlescape and unbeliabable Basescapes are just plain cousings. Noone of them have to know that the other one exists :). As a user we know they are related, but for them there is just no reference, you activate the Geoscape (then Geoscape is activated and the blood starts flowing again with Enabled = true and Visible = true) :), of course that means too that if you come from the Basescape you have to do the "deactivation" of Basescape to be consistent ;).

Hope this helps to realize what I mean about the difference between traditional development and what XNA devs have devised (that it is indeed pretty good). :D

 

I learned a couple of other things, but this sums up the needed for this discussion. If you have any specific or general question, just post it and I will try to respond it (if I can of course).

 

EDIT: This is what I mean about misunderstanding of the concepts coming from the C++ style developers: http://forums.microsoft.com/MSDN/ShowPost....24&SiteID=1

 

Greetings

Red Knight

Edited by red knight
Link to comment
Share on other sites

I think the EnabledChanged issue is one of the points. What it allows us to do is use the facilities provided by GameComponents (e.g. have a unified concept of stopping the "Update-Pump" on an object and being able to react on it). Before, there was the Enable-Method that basically did the same thing and replicated much of this logic. Since I'm in the process(!) of changing the code, I just kept the method and hooked up its logic to the EnabledChanged (if I remember correctly, I even made it private). As you correctly pointed out, the naming is no longer exactly right, but again, we are work in progress here.

I brought up the EnabledChanged points to show that I am trying to get my head around the GameComponents. (And that you can simplify and clarify the code in Frame.)

 

The comment about reusability of GameComponents, reusability requires decoupling of components, which is exactly what GameComponent/Services provide (and static "globals" do not). As you said, most of those GameComponents are not going to be reusable in other projects. Nevertheless, having less coupling also benefits the development process for a single project especially if you have several people working on it. So I'm advocating this Service based architecture not on the grounds of reusability but on the grounds of reduced coupling.

I agree that decoupling is usually a ?good thing?. But, decoupling has it?s costs. There?s extra effort required to do the decoupling, and more effort involved in understanding it. Like most things in software, it?s a trade-off, and you need to consider when it?s a net benefit, and when it?s a cost.

 

Making the UI a GameComponent looks like a good idea. I?m yet to be convinced that making everything in model a GameComonent is a good idea, mostly because of the tight coupling involved between the model classes. That said, I?m willing to listen to counterarguments, and if you can show me some solid examples that would be most helpful.

 

The question of whether the code structure is understandable, I again refer to the work in progress and experimental nature of the branch. The current state is not my proposal for a new "trunk", I'm still trying out stuff and have rewritten some parts of it already several times. So I really love to get comments about what it good, and especially what is not and questions about how stuff works, but please refrain from taking it as a final proposal of how the code is supposed to be structured. Rather take it as an opportunity to challenge ways of thinking and lets see what we can learn from it, maybe it will turn out just as a playground to try out ideas that could incrementally improve trunk.

Understood. The problem is that from the code, I can?t see where you?re going, or how it?s supposed to work. This makes it difficult to offer comment. And after studying for hours and seeing stuff which makes me think ?that can?t possibly be right?, I?m very frustrated.

 

As an example, I don?t see how we control the order of the Update pump to the different Model classes.

 

The current code not compiling is something I'm really sorry about and I'm curious as to the why, as SVN shows no pending changes here, and it compiles fine for me. Could you post the compile errors?

C:\Build\Xenocide\XNA\Xenocide.componentbranch.1568\componentbranch\Xenocide\Source\Model\Geoscape\HumanBases\Floorplan.cs(173,34): error CS0117: 'Xenocide.Xenocide' does not contain a definition for 'GameState'

C:\Build\Xenocide\XNA\Xenocide.componentbranch.1568\componentbranch\Xenocide\Source\Model\Geoscape\Research\ResearchPreRequisite.cs(40,30): error CS0117: 'Xenocide.Xenocide' does not contain a definition for 'GameState'

C:\Build\Xenocide\XNA\Xenocide.componentbranch.1568\componentbranch\Xenocide\Source\UI\Scenes\Geoscape\GeoscapeScene.cs(215,42): error CS0117: 'Xenocide.Xenocide' does not contain a definition for 'GameState'

C:\Build\Xenocide\XNA\Xenocide.componentbranch.1568\componentbranch\Xenocide\Source\Model\Geoscape\HumanBases\HumanBaseService.cs(14,18): warning CS1591: Missing XML comment for publicly visible type or member 'Xenocide.Model.Geoscape.HumanBases.HumanBaseService'

C:\Build\Xenocide\XNA\Xenocide.componentbranch.1568\componentbranch\Xenocide\Source\Model\Geoscape\HumanBases\HumanBaseService.cs(38,33): warning CS1591: Missing XML comment for publicly visible type or member 'Xenocide.Model.Geoscape.HumanBases.HumanBaseService.HumanBases'

C:\Build\Xenocide\XNA\Xenocide.componentbranch.1568\componentbranch\Xenocide\Source\UI\Screens\GeoscapeScreen.cs(280,34): error CS0117: 'Xenocide.Xenocide' does not contain a definition for 'GameState'

C:\Build\Xenocide\XNA\Xenocide.componentbranch.1568\componentbranch\Xenocide\Source\UI\Screens\GeoscapeScreen.cs(282,40): error CS0117: 'Xenocide.Xenocide' does not contain a definition for 'GameState'

C:\Build\Xenocide\XNA\Xenocide.componentbranch.1568\componentbranch\Xenocide\Source\Model\Geoscape\Craft\InterceptCraftState.cs(117,22): error CS0117: 'Xenocide.Xenocide' does not contain a definition for 'GameState'

C:\Build\Xenocide\XNA\Xenocide.componentbranch.1568\componentbranch\Xenocide\Source\Model\Geoscape\Craft\InterceptCraftState.cs(155,22): error CS0117: 'Xenocide.Xenocide' does not contain a definition for 'GameState'

Anyway, I would really like to get back to a productive discussion, as I think we all still have to learn a lot about the xna-way of doing things and I think we should try to get familiar with that in order to use the framework to its full potential. (Sorry if I sound a little pissed, but I think most of what is provided in terms of examples and documentation is more toy-examples and doesn't help with big projects, so we should try to figure it out together and not see it as a contest as to whose way of thinking is the right one).

Well, I hope that when the books on XNA I?ve ordered arrive they?re going to explain GameComponents better.

 

Update/Draw pump must not be taken lightly, even a small derouted message on the Update/Draw can make your application

Are you talking about when GameComponent is used, or when GameComponent is NOT used?

 

Everything has to be a GameComponent?...

Sorry, that paragraph isn?t very clear. Possibly if Rincewind posts the MSN discussion it will be clearer. (or when I get the XNA books.)

Link to comment
Share on other sites

I agree that decoupling is usually a ?good thing?. But, decoupling has it?s costs. There?s extra effort required to do the decoupling, and more effort involved in understanding it. Like most things in software, it?s a trade-off, and you need to consider when it?s a net benefit, and when it?s a cost.
Yes, but as you said, dont go overboard with it either. First measure, then act. By today if I measure I see 2 very opposite approaches.

 

Rincewind approach: He is exploring how to use GameComponents and GameServices abstractions, and even if it has a noble intention as an exploratory concept he needs to go overboard with it with very liberal mind (I forsee pretty interesting concepts to be born from this intend). But as everything experimental thing the approach tends to the overengineering until it settles down; as such is pretty difficult to see now exactly where he is going.

Dteviot approach: Heavily rooted on having software that works, with very well defined functionality; the code is a little bit oriented toward the more hardcore C++ way of thinking and because of that may lack some of the flexibility that a more complex environment as .net may bring, but it works. Just a word of caution be careful with statics, naming and global variables. As intros of EA games say: "Challenge everything" in this regard challenge your own development ideas.

 

The possible acts that I would like you both to discuss is the following: How do you foresee the win would be to try to shrink the "static" communication into a single Game entry point and use the update/draw pump from XNA? What would be the effort for such a thing?

Making the UI a GameComponent looks like a good idea.
You have a winning one there, putting the whole Cegui update/draw mechanism inside a game component with a published service if required would be a win.

 

Well, I hope that when the books on XNA I?ve ordered arrive they?re going to explain GameComponents better.
Hope so, but dont count on it. That sounds like it is still a dark concept regarding big games; with small things you wont see a difference.

 

Update/Draw pump must not be taken lightly, even a small derouted message on the Update/Draw can make your application

Are you talking about when GameComponent is used, or when GameComponent is NOT used?

XNA controls your Update/Draw pump and publish it only to you Game and you Components (no wonder that Services are not required to have an Update/Draw behavior ;) ) but you have to be very careful what you do in your timeslice. First you are under no circunstance assured the order (you can have some control over it, but it is pretty lazy in its approach to handle the Updating behavior). That gives you an extra thing to worry, too many Components in the Game level can be a logistic nightmare; what they missed is a hierarchical component structure (that is found in SmartClient and GUI interfaces) but there are several reasons to do that, as GameComponents are sort of Heavyweight components. Services on the other hand assume that their state is either unchanged (provide some sort of by call state change) or they are owned by a component that would feed the Update/Draw events that they require to update their status.

 

One example of the first is an inmediate mode InputService, it will return exactly what it reads from the Input Device States. Second example is an inteligent AdvancedInputService that will have to know update frequencies, have buffered inputs, keymapping bindings, etc that requires to give a unified response for the whole update frame. For a well designed (XNA Compliant) Service style only should exists if it is published by a Components that would handle the timing of the Update let him know; because of the need to know of the update sequence. Following that very same idea, the whole GameSimulation could be exposed by multiple difference services that are created by a GameComponent that handles how to perform the update procedure (Just an example). With that you avoid the coupling while you still know where to look for everything. I will try to draft in paper what I mean and scan it at the office.

 

Everything has to be a GameComponent?...

Sorry, that paragraph isn?t very clear. Possibly if Rincewind posts the MSN discussion it will be clearer. (or when I get the XNA books.)

Means that the line between what has to be a GameComponent, what has to be a GameService and what it isnt one or the other is pretty blur yet. I dont pretend to have a definite answer, as I doubt someone will have it in the short run If that would have happened chances are very high that I would have stumbled upon it in my research. The most interesting variant I found was a GameComponent/GameServices example by Nuclex, still too low level but it is promising: http://www.nuclex.org/articles/xna-gamecom...nd-gameservices

 

Greetings

Red Knight

Edited by red knight
Link to comment
Share on other sites

Guys, just let me drop one line here: If you really are taking the idea of XNA being only a prototyping "engine" and porting the whole thing back to C++ serious, I think it would be best to keep a very C++ oriented coding style, making the port more easy. But that are just my 2 ct
Link to comment
Share on other sites

Hi,

 

some reports from the playground: I committed the changes to make the branch compile again. Sorry, TortoiseSVN showed everything as being up to date even when it wasn't. Should be fixed now.

 

Anyway, here's the MSN-Discussion I had with red knight about services/components. It's a little long and we didn't reach a final conclusion, but it should provide some insights:

 

(22:52:30) red knight msn: GameStateComponent

(22:52:36) red knight msn: dont like how this looks like

(22:52:46) MSNJE: the naming or the structure?

(22:53:40) MSNJE: The basic idea was to somehow provide that Game-injection service

(22:53:56) red knight msn: how it works

(22:54:11) MSNJE: alright, what don't you like about it?

(22:55:03) red knight msn: I have a feeling

(22:55:07) red knight msn: dont know yet

(22:55:41) MSNJE: well, you need inheritance for it right now, thats true

(22:56:06) red knight msn: IGeoTimeService -> No need to have this Interface

(22:56:09) MSNJE: another idea is also to enlarge it and have GameStateComponent manage a collection of children

(22:56:21) MSNJE: why? (about IGeoTimeService)?

(22:56:41) MSNJE: Move it all in one big service?

(22:56:47) red knight msn: no need for an interface

(22:56:58) red knight msn: you are not going to provide multiple implementations of it

(22:57:04) red knight msn: IGameStateService -> Same applies

(22:57:27) MSNJE: ah, right, I just realized, AddService just needs a type

(22:57:35) MSNJE: no idea why I thought it would NEED an interface

(22:57:38) MSNJE: nevermind then

(22:57:39) MSNJE: :-)

(22:57:42) MSNJE: will change it

(22:57:57) red knight msn: SaveGameHeader -> no need for it

(22:58:11) MSNJE: that came from the old game-code

(22:58:18) MSNJE: errr, load/save code

(22:58:33) MSNJE: I didn't really touch any functionality for now, just refactored

(22:58:55) red knight msn: GameState public string RealTime public string GameTime rest of the state

(22:59:07) MSNJE: I think dteviot wanted to avoid having to read the entire savegame just for the load-game screen and info per entry

(22:59:25) MSNJE: so the header holds all the info you need to show the list of savegame

(22:59:25) red knight msn: then Serializer.Serialize ( gameState ); Serializer.Deserialize ( Stream );

(22:59:28) MSNJE: savegames

(22:59:40) red knight msn: no need for that...

(23:00:16) red knight msn: unless you want to pass arround files...

(23:00:21) red knight msn: in that case is useful

(23:00:43) red knight msn: nonetheless much code that is not required... implementation wise

(23:00:44) MSNJE: ? I'm confused

(23:00:54) MSNJE: I didn't even look at it

(23:01:00) MSNJE: just moved it from the screen to the service

(23:01:48) MSNJE: another thing. Can you compile it? As Mad seems to have problems with it

(23:01:53) red knight msn: with a [serializable] public class SaveGameHeader { public string Name; public DateTime Date; public long GameTime; }

(23:02:00) red knight msn: would have been enough... :)

(23:02:11) red knight msn: all the extra tweaking is unnecesary ;)

(23:03:15) MSNJE: yeah, just had a look at it. you are right

(23:03:23) MSNJE: didn't even see that he didn'T just serialize it

(23:03:46) MSNJE: anyway, currently I'm going for the overall structure. Implementation is last

(23:03:59) MSNJE: (and as long as it fullfills the interface, I don't care for now :-) )

(23:06:01) red knight msn: those small things make the difference between simple and readable code and an uncontrolable mess ;)

(23:06:16) MSNJE: I know, but I'm still cleaning up the big stuff

(23:06:32) MSNJE: also, I don't want to mess too much with dteviots code for now

(23:06:50) MSNJE: I think he doesn't feel to good about all those changes anyway

(23:07:10) red knight msn: public GameStateService(Game game, Stream loadStream) { }

(23:07:25) red knight msn: convince him with simplicity ;)

(23:07:31) red knight msn: make sure you do it that way

(23:07:40) red knight msn: rip it appart and simplify everywhere you can

(23:07:49) MSNJE: wait, why pass it a stream in the constructor? That service lives forever

(23:08:29) red knight msn: if you provide a Load method it may... but what if you create one of those every time you load / create a game ;)

(23:08:36) red knight msn: understand?

(23:08:54) MSNJE: cause then I have the same problem, I just created it to avoid having to reconnect to services

(23:08:59) red knight msn: sometimes simplify means challenge your our knowledge about things

(23:09:21) red knight msn: do not connect, create :)

(23:09:29) MSNJE: yeah, but why?

(23:09:30) red knight msn: Simplicity is the key to understanding....

(23:09:48) red knight msn: less code.. less worry, less state -> less bugs, more productivity

(23:09:50) MSNJE: I just don't see where it's simpler

(23:10:37) MSNJE: for me, there's currently a bunch of services that I (a Component) can use. These let me for example work with the current GameState or allow me to show stuff on the globe, etc

(23:11:08) red knight msn: you are giving the service concept a SAS entity

(23:11:17) red knight msn: in XNA that is not true... be careful about that

(23:11:25) red knight msn: services may have lifetime, and be transient

(23:11:43) MSNJE: true, in this case though, I don't see the advantage

(23:11:47) red knight msn: Game -> Input Service (lives forever), Streaming Service (lives forever), GameStateService (transient)

(23:11:54) MSNJE: or rather, why would i make it a service at all then?

(23:12:08) red knight msn: cause you need to shop for it...

(23:12:11) red knight msn: :)

(23:12:21) MSNJE: it's reduced to a function (in the form of a class-constructor) that gets passed in a Game-object and modifies it

(23:12:24) red knight msn: there is a paradigm shift in XNA...

(23:12:33) MSNJE: I don't need it to be even a service

(23:12:37) red knight msn: challenge everything you think

(23:12:40) MSNJE: it's back to functional programming

(23:12:44) red knight msn: it is hard, I know... if happened to me

(23:12:47) MSNJE: just with a more elaborate syntax

(23:13:05) MSNJE: I'm fine with challenging, but I still need to see a benefit

(23:13:22) red knight msn: you are not far if you think it more througly, I can send you to a presentation from Tim Sweeny

(23:13:32) red knight msn: and Microsoft Research keynotes about it...

(23:13:36) red knight msn: if you want :)

(23:13:37) MSNJE: I could agree with the fact that it's just sitting around doing nothing, so I don't need it to be there as a service all the time

(23:14:07) MSNJE: ok, so then just add a static method somewhere "LoadGame(Game game, Stream loadStream)

(23:14:10) MSNJE: and thats it

(23:14:16) red knight msn: services exists because they serve a purpose

(23:14:33) MSNJE: no need to have a real object for it (with a lifetime of zero) and have that stuff executed in the constructor

(23:14:55) MSNJE: so we don't need a Service for GameLoad/-Save, right?

(23:15:00) red knight msn: but that doesnt means that the lifetime have to be the whole process execution time, not zero ;)

(23:15:19) red knight msn: it is the controlled lifetime what gives the Service the purpose and meaning

(23:15:27) MSNJE: it lives for as long as it's constructor takes time to run

(23:15:42) red knight msn: nope... that is essencially functional programming

(23:15:47) red knight msn: we are not speaking about it

(23:15:52) MSNJE: alright

(23:16:00) MSNJE: so how long should it live then?

(23:16:03) red knight msn: I mean that some principles apply

(23:16:20) MSNJE: we "can" change its purpose and have it give access to a GameState-Object

(23:16:30) red knight msn: nope...

(23:16:32) MSNJE: then it lives for as long as we have a valid GameState

(23:16:35) red knight msn: give me some characters to explain

(23:16:41) MSNJE: alright :-)

(23:16:42) red knight msn: I will ask you a couple of question

(23:16:43) red knight msn: s

(23:16:48) MSNJE: I'll be quiet for a while :-)

(23:16:58) red knight msn: If I tell you that some services has global scope, what would you tell me?

(23:17:10) MSNJE: ?

(23:17:17) MSNJE: not good, I guess ;-)

(23:17:29) MSNJE: and a service that lives forever has basicly global scope?

(23:17:32) red knight msn: Well... it is true that some services has global scope

(23:17:44) red knight msn: a process is a service that has global scope

(23:17:58) red knight msn: the OS is a service that has global scope

(23:18:20) MSNJE: so a service should live for as long as it is needed?

(23:18:36) red knight msn: as long as it starts at the creation and dies with their enclosing entity... they are global

(23:19:04) red knight msn: so going the other extreme... if you create a service, consume it, and after consuming it dies... is it a service at all?

(23:19:49) red knight msn: ????

(23:19:55) MSNJE: sorry, was on the phone

(23:19:59) MSNJE: back

(23:19:59) red knight msn: :)

(23:20:31) MSNJE: I don't see it as a service (unless you call a simple function call a service, then the livetime is just the time it takes that function to execute)

(23:21:49) red knight msn: conceptually even a function call can be though as a service... probably you wouldnt implement it that way for performance, readability, manteinability reasons... but conceptually there is no different (Dikstra put that in perpective when he argue that functional programming IS the paradigm... - I do not concurr though, but he has a point)

(23:22:21) red knight msn: State is your enemy... with multicore that is starting to take shape...

(23:22:38) MSNJE: alright, so now we have to see if the Service-Concept in xna (in term of objects being registered in the Services-Collection) and the concept we just discussed are the same thing

(23:23:20) red knight msn: ok... now we can say that Services are those that "provides" a service for the caller... no wonder the line gets very blur now

(23:23:27) red knight msn: :)

(23:23:42) red knight msn: now... lets try to understand what different kind of services exists

(23:23:50) MSNJE: so, GameStateService is not a Service in the xna-way

(23:23:57) MSNJE: or "shouldn't" be

(23:24:09) MSNJE: as those are only those that need some form of state and thus need to be saved centrally

(23:24:20) red knight msn: there are services that no matter what you do they always return the same, given the same input

(23:24:25) red knight msn: we will call them STATELESS

(23:24:41) red knight msn: then on the other side, you have STATEFUL services

(23:24:45) MSNJE: so those are basicly function calls!

(23:24:51) MSNJE: the stateless I mean

(23:25:19) red knight msn: so now we have some variants as you can have STATELESS-GLOBAL services

(23:25:30) red knight msn: or STATELESS-SCOPED services

(23:25:37) red knight msn: or STATEFUL-GLOBAL and so on

(23:25:48) red knight msn: nonetheless things get messier

(23:26:15) MSNJE: why stateless-scoped?

(23:26:29) MSNJE: or rather, whats the difference to stateless-global?

(23:30:19) red knight msn: when you start to say not all scoped services has the same lifetime :)

(23:30:46) MSNJE: I they are stateless, why do they have lifetime (or rather, what "lives"?)

(23:31:10) red knight msn: cause maybe they are not intended to be used :)

(23:31:16) red knight msn: at an specific time

(23:31:33) MSNJE: ok, then they still depend on state (even though they might not own it)

(23:31:51) red knight msn: suppose the following scenario... you have a very complex procedure that has to be performed that exausts system resources

(23:32:07) red knight msn: then the "owner" may decide not to allow its ose

(23:32:09) red knight msn: use

(23:32:21) red knight msn: exactly, they do not have state, they are STATELESS

(23:32:35) red knight msn: so now to XNA Services

(23:33:03) red knight msn: what is the difference between the services and components in XNA

(23:33:05) red knight msn: ?

(23:33:21) red knight msn: after all with this idea, all components are "services"

(23:33:41) red knight msn: yes, but the difference between a service and a component is that the component has an identity

(23:34:10) red knight msn: and with identity it means that it represents a "live" entity, where services do not

(23:34:24) MSNJE: jsut to make it clear:

(23:34:40) red knight msn: you shouldnt "interact" with services, but you may need to interact with components

(23:34:40) MSNJE: when we are talking services now, we talk about those things you get from the Services-Collection?

(23:34:46) red knight msn: exactly

(23:35:04) MSNJE: "interact" meaning?

(23:35:30) red knight msn: components follow the "agent" idea

(23:35:40) red knight msn: they sense the environment and act accordingly

(23:35:53) red knight msn: while services are more like the "sensors"

(23:36:11) red knight msn: services provide the context, components generate the emergent behaivior

(23:37:00) red knight msn: that points toward a very difficult task, identify when something is a component, when something is a service or when it is neither one nor the other

(23:37:10) red knight msn: :)

(23:37:20) red knight msn: blurred I said :P

(23:37:36) MSNJE: ok, lets get back to our GameStateService (or rather Load/Save-Service)

(23:37:39) MSNJE: is it a service?

(23:37:46) red knight msn: does it has an identity?

(23:37:53) red knight msn: is it an agent?

(23:37:54) MSNJE: nope, one is just like any other

(23:37:56) MSNJE: nope

(23:38:07) MSNJE: so it gets reduced to a simple function-call?

(23:38:10) red knight msn: now is it a service?

(23:38:14) MSNJE: nope

(23:38:18) MSNJE: not a component either

(23:38:27) red knight msn: that is where I wanted to go

(23:38:41) red knight msn: there is yet one thing that I didnt mention

(23:38:52) MSNJE: wait

(23:38:56) red knight msn: we have "agents", we have "sensor" what is missing?

(23:39:05) MSNJE: state

(23:39:08) MSNJE: ?

(23:39:18) red knight msn: not exactly... it is related though

(23:39:21) MSNJE: ?

(23:39:39) red knight msn: environment... a world

(23:39:50) MSNJE: which is what I call GameState

(23:40:04) red knight msn: exactly... that is neither an agent nor a sensor

(23:40:12) MSNJE: alright, sooooooo:

(23:40:24) red knight msn: but must exists regardless of the existance of agents and sensors

(23:40:38) MSNJE: all the GameStateComponent-stuff is not added to Game-Components (since it doens't need Update-functionality anyways)

(23:40:57) red knight msn: the question for you is: "Is the world a unique entity?"

(23:40:58) MSNJE: rather just held in the gamestate-tree

(23:41:22) MSNJE: nope, there can be more than one, but only one is visible to the sensors

(23:41:34) red knight msn: that is not entirely true...

(23:41:37) red knight msn: but anyways...

(23:41:45) red knight msn: lets suppose that it is true

(23:42:05) red knight msn: how many worlds do we have in Xenocide?

(23:42:16) MSNJE: active or potential?

(23:42:22) MSNJE: active is only one

(23:42:28) red knight msn: I asked a straight question :)

(23:42:32) MSNJE: potential are all the savegames

(23:42:36) red knight msn: I want a straight answer

(23:42:40) MSNJE: one

(23:42:48) red knight msn: nope

(23:43:06) red knight msn: there are at least 3... where is the catch?

(23:43:37) MSNJE: I see different "views" on the world, overmind has a view, player has a view. game might have a view to save everything

(23:43:43) MSNJE: but it's still the same world

(23:44:10) red knight msn: those are enties that sense the world

(23:44:21) MSNJE: then what are the 3 worlds?

(23:44:54) red knight msn: I didnt asked what are those 3 worlds, I told you there are, what is the catch? ;)

(23:45:08) MSNJE: ?

(23:45:25) red knight msn: "The worlds are not disjoints"

(23:46:00) MSNJE: ?

(23:46:05) red knight msn: in fact they are hierarchical, that is why I dont want to call them views even if more approapriate... as sensors from one may not be the same as for the other...

(23:46:23) red knight msn: so in the end it is a question of scope

(23:46:50) red knight msn: in Xenocide we have a main world where agents like games exists... and nothing else

(23:47:38) red knight msn: then each game defines another worlds where the overmind, the humans, the input, etc exists

(23:48:32) red knight msn: then you have 2 disjoint worlds inside (Realtime maps, and battlescape ones )

(23:49:15) red knight msn: however... everything that applies for the Game, applies for all the games.

(23:49:39) red knight msn: the set of sensor that apply to the agents in the Realtime maps is disjoint to those of the battlescape ones

(23:50:12) red knight msn: even though sensors that are available to the Game apply for both realtime and battlescape agents :)

(23:50:20) red knight msn: do you understand what I meant?

(23:50:35) MSNJE: yes, I think so

(23:51:02) MSNJE: you got limited lifetime for some of the services as they only apply to certain game-modes

(23:51:02) red knight msn: If I ask you, of all the sensors that apply to the battlescape, what is their lifetime?

(23:51:11) red knight msn: exactly

(23:51:14) MSNJE: for as long as we are in battlescape mode

(23:51:20) red knight msn: (Y)

(23:51:23) MSNJE: se we remove those services afterwards

(23:51:28) red knight msn: a hit straight in the nail's head

(23:51:37) MSNJE: hehe, now I still want to go back

(23:51:43) MSNJE: down to nitty-gritty

(23:51:49) MSNJE: LoadGame, SaveGame

(23:51:50) red knight msn: I love that part

(23:52:07) red knight msn: is it a service?

(23:52:11) MSNJE: nope

(23:52:14) MSNJE: and not a component either

(23:52:18) red knight msn: are you sure?

(23:52:38) MSNJE: not in the xenocide-way

(23:52:42) MSNJE: errr xna-way

(23:53:04) MSNJE: e.g. it doesn't make sense to put it into services-collection

(23:53:10) MSNJE: no state to save

(23:53:15) red knight msn: however...

(23:53:23) MSNJE: so why would I need to keep an object around that has zero state?

(23:53:40) red knight msn: so why do I want a STATELESS-GLOBAL service?

(23:53:47) red knight msn: :)

(23:53:59) red knight msn: you have to ask... global for whom?

(23:54:20) MSNJE: for the xenocide-game

(23:54:34) MSNJE: ok, now you can argue that loading/saving should not be available in cut-scenes

(23:55:18) red knight msn: so you would have a "service" that only exists to provide a service at a certain point in existence

(23:55:28) MSNJE: stop

(23:55:40) red knight msn: the question I ask you is the following... are you sure that Save-Load is a service in itself?

(23:55:41) MSNJE: I need that service to exist, when I need to consume it

(23:56:04) MSNJE: I don't see where you want to go

(23:56:20) red knight msn: here is where things get blurry

(23:57:28) MSNJE: lets get back to the initial problem

(23:57:32) red knight msn: a PersistenceService may exist in a global scope... where every Service/Component that requires to enlist himself in the Persistence

(23:58:05) red knight msn: and when it removes itself got removed from the PersistenceService as he is not longer in the persist chain

(23:58:06) red knight msn: :)

(23:58:16) MSNJE: and I serialize the Persistance-chain?

(23:58:24) MSNJE: for loadgame, savegame?

(23:58:29) red knight msn: yeah... doesnt it has more sense?

(23:58:43) red knight msn: now you have a reason to live

(23:58:54) red knight msn: but defer the actual work to someone else to take care of it

(23:58:56) red knight msn: :)

(23:59:13) red knight msn: you can create persistence realms if you want :)

(23:59:35) red knight msn: but I dont see the reason for such a thing

(23:59:47) MSNJE: ok, lets see this from a different angle

(23:59:47) red knight msn: at least not for Xenocide

(00:00:08) MSNJE: I currently have a point where everything that needs to be persisted to load/save a game is collected: "GameState"

(00:00:45) MSNJE: now I need to add Methods for serialing/deserializing it

(00:01:22) red knight msn: and more important, track the changes that has been done, as services never pass out

(00:01:38) MSNJE: ????

(00:01:43) red knight msn: they never die

(00:01:46) red knight msn: :)

(00:01:50) MSNJE: what never dies?

(00:01:55) red knight msn: The service

(00:02:09) red knight msn: so you have to inject the Game reference

(00:02:10) MSNJE: but thats exactly what GameStateService is

(00:02:27) red knight msn: see the difference in angle...

(00:02:29) MSNJE: it is a service that never dies

(00:02:52) MSNJE: it collects what needs to be persisted (Just the current GameState-object-tree)

(00:02:57) MSNJE: and can serialize/deserialize it

(00:03:42) red knight msn: XenocideRealm (Services: Persistence, Input) - GameRealm (Services: Research, whatever) - StrategicRealm ( Services: Geoscape, whatever ) - BattlescapeRealm ( Services: whatever )

(00:06:11) red knight msn: Persistence.Load ( Stream ) { mainRealm = DeserializeRealms (); mainRealm.CreateService (); forall realms { foreach ( PersistentObject in mainRealm ) { DeserializeAndSet (PersistObject); } } }

(00:07:19) red knight msn: easy to do that with an attribute

(00:08:18) MSNJE: so you want to serialize all services available at a certain time?

(00:08:23) MSNJE: and preserve game state with this?

(00:08:41) MSNJE: since the services "may" contain state in the form of what currently is GameState?

(00:09:06) red knight msn: I have to go

(00:09:13) red knight msn: let me draft you something tomorrow

(00:09:25) MSNJE: ok

(00:09:29) MSNJE: remember one thing:

(00:09:32) red knight msn: I will be at home cause I will be studying we can speak on MSN for 30 minutes

(00:09:58) red knight msn: no more

(00:10:26) red knight msn: what?

(00:11:44) red knight msn: think about this: [Persist] private SomeType myStateObject

(00:11:45) MSNJE: we will have a lot of change in the services

Link to comment
Share on other sites

×
×
  • Create New...