Where can I find the current QOTM? - Charriu

Create an account  

 
Developer thread

It binds UIRuleSetAccessor in constructor parameters to a singleton UIRuleSetAccessor instance, meaning, there will be only created one.


Ill try to explain on an example.
Your system looks like this:

You have soem classes A B C D, that are part of the system "frame", you will only ever need one of each. The following constuctors apply:
A()
B(A a)
C(A a)
D(A a,B b)
Then you have some dynamic parts of the system E F G, that are needed for each Foobar the system handles (how many of which tehre are, you can only tell in runtime)
Following constructors apply:
E(A a,F f, G g)
F()
G(F f)
Where the two f-s shuold be the same instance
HOw would yuo design the build of such a system? Clearly, A B C and D should be singletons, while E F and G are not. One of the cleverer apporach is to create a "System builder" class, that creates all instances as necessary, so in the class itself, you dont have to worry about system building. However, if you make a lot of projects, you will eventually distill a reusable system builder class, that you can parametrize for the given project. Then, if that is done, you will want to pack it into a neat package and sell/give it to others. This is basically how most DI frameworks happen, including Ninject.

There is a plethora of ways how parametrization can happen (XML, annotations or constructor params are a few possibilities) We use constructor parameters, which has a few drawbacks ( that we can avoid by not using static instances of basically anything that needs injection) The Binding rules give the needed context for Ninject to properly build things. (Ideally, all things would be created by this, but I found it easier to include a factory class or two as well)
We mostly use Bind<X>.ToSelf(). This tells Ninject to just make a new instance of X whenever he wants to create something with a constuctor that needs X.
Bind<Y>.ToSelf().InSingletonScope() makes it so Y is a singleton, an that singleton is used whenever Y is a ctor param.
Screens are special: we bind multiple implementors to the Screen interface (or is it a base class? whatever) in effect creating a list of screens.
NEQueue is also special: we have 2 named instances, the "in" queue and the "out" queue, whenever we need a NEQueue in a ctor, we have to specify which one we want. (with the help of an attribute on the parameter, you can see how this works in the MainLogic class)
There are some other possibilities, so if these dont cover your usecase, you need to read the docs.

To finish our example:
Bind<A>().ToSelf().InSingletonScope();
Bind<B>().ToSelf().InSingletonScope();
Bind<C>().ToSelf().InSingletonScope();
Bind<D>().ToSelf().InSingletonScope();

Bind<E>().ToSelf();
Bind<F>().ToSelf().InCallScope();
Bind<G>().ToSelf();

Then you do
...
var kernel=new StandardKernel(context);
A main=kernel.Get<A>();
...
and whenewer a FooBar is oincoming

...
void handleFoobarIncoming()
{
Elist.Add(kernel.Get<E>())
}

This is a very basic rundown, but they have a very nice wiki, you can easily read up on everything there.
Reply

And to answer the question: you add it in the ctor params, then make a field to hold the rules within. IN the ctor you do:

class A
{
RuleSet rules;
public A(RuleSetAccessor ac)
{
rules=ac.Rules; //I do this a lot, so feel free to look it up
}

public void FuntionToUseRules()
{
...rules...
}
}
Reply

Much obliged :D

Now I can't actually wait to get back home from work :D

BAH another 8 hours to go frown
Reply

VM et al.,
Please spend time on code documentation while you are coding and not later once you forgot what you did. My very successful colleagues tell me from hard earned experience with long-term development projects that even a clean code should be documented by spending EQUAL amount of time on documentation and coding. Otherwise, once the original developer stops working on the code, new developers will be unable and unwilling to continue to work on that code. They will rather rewrite the code than learn it. I hope you stay on this project for a very long time, but others must be able to understand the logic, the goals, and the code with the help of in-code documentation.
This forum is only the last resort to speak technical details of code. Years or decades later it will be hard to find random topics by a programmer on forums, even if it is organized. The code (and not forums) should include the documentation.

As a programmer, I even found my own documentations helpful to understand my own code when picked up later. Not only a time saver, but a motivation booster too.
Reply

The code is pretty clear/nice even without the doc/comments, I just ran into concepts and techniques that I was not familiar with, so I had to ask. Now that I have the barebones explained I can google it myself further.

But sure, benefits of documentation are undeniable. I myself like to add lot of comments into the code (even thou I code/work alone) mainly because I do not remember things very well myself.
Reply

With XML:
Code:
...
<buttons>
<UIButton>
<ButtonID>farmerbutton</ButtonID>
<ButtonHelpText>Farmers work the earth</ButtonHelpText>
</UIButton>
</buttons>
</UIRuleSet>

With

Code:
var uib = uiRules.buttons.FirstOrDefault(p => p.ButtonID == "farmerbutton");

I get the following. I would figure it out but I got to go to bed now and I'm eager to make progress so I can get to do something useful tomorrow or weekend. Perhaps my questions are more about C# than the code itself but bear with me please. Thanks in any case.

Code:
A first chance exception of type 'System.ArgumentNullException' occurred in System.Core.dll
An exception of type 'System.ArgumentNullException' occurred in System.Core.dll but was not handled in user code
Additional information: Value cannot be null.


Other relevant code

Code:
public class UIButton
    {
        public string ButtonID { get; set; }
        public string ButtonHelpText { get; set; }
    }

Code:
//all others who need these will have a pointer to these objects
        public List<UIRace> races { get; set; }
        public List<UIUnit> units { get; set; }
        public List<UIButton> buttons { get; set; }
        //undecded how this should work...
        public List<UISpell> Spells { get; set; }
        //these need to be duplicated because different instances subscribe to events with different filters
        public List<UIBuilding> buildings { get; set; }
        public List<UISpecialAbility> SpecialAbilities { get; set; }
        public List<UIHero> heroes { get; set; }
Reply

Well, either uirules or uirules.buttons is null (Id wager its uirules.buttons). How did you modify the xml?
The recommended way is to read the rules, modify the resulting c# objects, then save the ruleset again, and not direclty interfere with the xml. This is because DataContractSerializer is used, and that has its quirks. Be mindful, that uibuttons.buttons need to be set to new List<UiButton>() before adding the uibuttons!
Reply

Yeah I hand edited the XML :P

Ok, I will look at these comments in detail during the weekend and modify accordingly. Thanks!
Reply

OK we have a problem with multithreading it seems.The problem is basically, that the locking is lacking.
I need to look into this a bit deeper, but if you guys have any comments on the issues, they are welcome.
Reply

OK this is tougher than I anticipated.
The problem is the following:

Drawing a frame queries the gamestate
Network events change the gamestate
UIActions do their work to make sure that the UI state is consistemt with the gamestate

HOWEVER: between processing a NE and the UIaction tick, there is a small time for the drawing to query into the gamestate.
This would not be a problem, would it not for the factm that the UI holds references to some transient GameState objects. (in this particular case, the Maphandler.SelectedStack. Stacks are transient for the sole reason, that they split/combine in an unpredictable manner.)
So. Solution?

1. make sure that queries cannot come between NE process and UIAction tick
problems with the solution
- Most UIActuions rely on the fact that they be drawn multiple times. We would need to introduce some second UIActin type, that needs this protection, but then we are doing something that a specialized system would do better, for example:
2. make an "after the fact" synced event, that can be responded to by the UI
problems: needs to be synced with everything else too, makes the threading model even more complex (it already too complex, IMHO)
3. change how selected stack works, instead of a reference, hold an ID, then do a query whenever the stack is requested
problems: we need to manage stacks in the queryer, need to have a very strict bookkeeping on them. This completely negates the advantages of their transience: if we do that anyway, we could simply define them better as part of the gamestate and not have this problem whatsoever
4. other idea I didnt think of yet.

I`d like your input please.
Reply



Forum Jump: