orders revisited

Programmers discuss here anything related to FreeOrion programming. Primarily for the developers to discuss.

Moderator: Committer

Post Reply
Message
Author
jbarcz1
Creative Contributor
Posts: 226
Joined: Thu Jun 26, 2003 4:33 pm
Location: Baltimore, MD

orders revisited

#1 Post by jbarcz1 »

There's been some discussion about this before, but I think it's worth revisiting before I start writing code that will lock us into it.
Our plan so far regarding the handling of orders is that we're going to store all of the orders that the user issues on the client, but not modify anything until we get the turn update from the server (as it processes the orders).
Since it's undesirable for the user to have to wait a whole turn just to split a fleet or change move orders, we want the UI to display updates to things like fleet composition and move orders as soon as the user changes them. Since we aren't actually going to change the state on the client side, the plan was to have the UI walk the orderset (or some subset thereof) and display what the state WOULD be after the orders are executed.

This is doable, and I advocated it myself, but I've been thinking it might be the wrong approach. I'm about to start coding the guts of the fleet window, and that code would be a lot simpler if I could execute merge and split orders on the client side, change the local copy of the state, and display the universe as it is, then insert the order I just executed into the orderset to ship to the server. Doing otherwise would involve executing all of the pertinent orders every time the user opens the fleetwindow, which will not only slow the code down, but also make it more complex.

At first I thought that executing orders client-side would involve a large slowdown in processing the turn updates, but now that I think of it, it shouldn't. Each time we receive an update, we have to XMLEncode the universe state, then xpatch it, then reconstruct it based on the resulting XMLElement. If we simply store the XMLElement we used to construct the universe after the turn update, we could throw away the changes we made, and just patch that element to produce the new state. Since the server will still execute all the orders we do (and in the same sequence) there's no reason the states wouldn't match.

I could be wrong, but I have a feeling the UI code would be a lot easier to handle if we allowed the universe state to change on the client side in response to the user's orders. Any comments? If everyone else disagrees I'll go ahead and try to follow the original plan.

JB
Empire Team Lead

tzlaine
Programming Lead Emeritus
Posts: 1092
Joined: Thu Jun 26, 2003 1:33 pm

#2 Post by tzlaine »

I agree that the current plan for orders is probably overcomplicated, and is certainly clunky. It would be great if we had a more elegant way of skinning the same cat. I'd like to consider the option you outlined here, but frankly I don't get what you want to do from what you wrote; it's a little confusing. Could you rewrite this paragraph another way?
At first I thought that executing orders client-side would involve a large slowdown in processing the turn updates, but now that I think of it, it shouldn't. Each time we receive an update, we have to XMLEncode the universe state, then xpatch it, then reconstruct it based on the resulting XMLElement. If we simply store the XMLElement we used to construct the universe after the turn update, we could throw away the changes we made, and just patch that element to produce the new state. Since the server will still execute all the orders we do (and in the same sequence) there's no reason the states wouldn't match.
And specifically, which XMLElement do you mean when you say "If we simply store the XMLElement we used to construct the universe after the turn update..."?

jbarcz1
Creative Contributor
Posts: 226
Joined: Thu Jun 26, 2003 4:33 pm
Location: Baltimore, MD

#3 Post by jbarcz1 »

On the client end, handling a turn update could look something like this:

// initialized when universe is received from server
static XMLElement old_state;

void HandleUniverseUpdate(XMLElement update)
{
XMLElement new_state = xpatch( old_state, update)

client_universe.ReConstructFromXML(new_state);
old_state = new_state;
}

So we must, at a minimum, perform one XPatch and one universe construction to update the universe between turns.

If we're doing this, then it doesn't matter if the state of client_universe has changed locally between the turn updates, because the state we're patching with the update will be the state from the beginning of this turn (before any of the user's changes took place).

What this means is that we can make any and all universe changes we want in between the turn updates, because when we process the next update, we'll be using a state which doesn't have them. We'd basically be discarding all the user's local changes and replacing them with the server's set of changes, which is a superset of the user's changes.

So, for example, my job, in the fleet window, is made much easier, because all I need to do is something like this:

void HandleMerge(int fleet1, int fleet2, vector<int> ships)
{
FleetMergeOrder order(fleet1, fleet2, ships)
OrderSet.Add(order) // will be sent to server

order.Execute(); // change local universe state
UpdateDisplay() // display the updated state, showing merge result
}

If we write the orders such that they can change the state on both the server and client side, then not only is the UI code simpler, but it's also less compiling because we could remove all those #ifdefs which now exist in the order code.

Let me know if this is still unclear.
Empire Team Lead

jbarcz1
Creative Contributor
Posts: 226
Joined: Thu Jun 26, 2003 4:33 pm
Location: Baltimore, MD

#4 Post by jbarcz1 »

So what do we think then. THis thread has been quiet lately. Zach, do you think its a good idea to go with an implementation like this. Does anybody else have any thoughts on it?

If I dont get a response in a day or two I'll assume we're ok with it and I'll start rewriting orders so they can execute client-side.

JB
Empire Team Lead

tzlaine
Programming Lead Emeritus
Posts: 1092
Joined: Thu Jun 26, 2003 1:33 pm

#5 Post by tzlaine »

That is a truly awesome idea. Sounds good to me. A couple of caveats, though: 1) many Orders should not logically take effect immediately (like buying out the rest of production on a current build, a la Civ -- not that we'll necessarily have this feature, but you get the idea), so you should keep this in mind when designing the Orders and their interfaces. You should probably indicate which effects of an order are immedate and which are not in the documentation for each Order class. 2) Orders should be designed with an eye to making them easy to roll back. For instance, if an executing an order increases some value X by 10%, and another increases it by +3, rolling back the original order should decrease X to 0.9 * (X - 3) + 3, not 0.9 * X. Somehow, the relevant data must be preserved to allow arbitrary applications and rollbacks of orders.

EDIT: My formula was wrong.
Last edited by tzlaine on Fri Sep 05, 2003 5:57 am, edited 1 time in total.

jbarcz1
Creative Contributor
Posts: 226
Joined: Thu Jun 26, 2003 4:33 pm
Location: Baltimore, MD

#6 Post by jbarcz1 »

I'll keep that in mind. I think in virtually every case, an order will change something in the game state. Following your example, an order like "buy this thing" would not be completed immediately (it couldn't be, until production calculations are run), but it would probably at least set some sort of internal flag saying "we're going to buy this".

I'm not sure if we'd really need to roll back orders. If the user wants to cancel something, it may be easier just to issue a second order countermanding or adjusting the first one. For example, fleet movement:

Player says "Set fleet A's destination to planet X"
If they change their mind later, they can issue an order saying
"Set fleet A's destination to nowhere "
Rather than actually cancelling and undoing the first order.

In your production example, if the user wants to undo the production order, than rather rolling back the effects of the first, they can simply issue a new one to bring the level to where they want it.

It doesn't make as much intuitive sense but would probably be easier and cleaner to implement.

JB
Empire Team Lead

tzlaine
Programming Lead Emeritus
Posts: 1092
Joined: Thu Jun 26, 2003 1:33 pm

#7 Post by tzlaine »

Well, there's one problem I can see with that. Let's say we have a stat in the UI that is a projection of the future, for example next turn's projected income. This is a commonly desired piece of info to provide to the user in a 4X game. The same problem that I alluded to earlier may exist if you issue an order that will increase income by 10%, another that will increase it by 12, and then recind the first order, and finally recind the second order. But it may or may not be the case that X != (X * 1.1 + 12) / 1.1 - 12, depending on the value of X. This and related issues cry out for rolling back orders.

A possible implementation of rolling back an order is to rollback all relevant orders that were applied after the one you want to roll back in the reverse sequence that they were issued, then roll back the one you want, and finally reissue all the later orders in their original sequence. This of course requires keeping sorted lists of orders searchable by the items that they modify, which is certainly not trivial, especially considering that a single order may affect multiple objects, and that the objects may be of radically different types.

Feanor
Krill Swarm
Posts: 10
Joined: Thu Jul 31, 2003 8:56 pm
Location: Germany

#8 Post by Feanor »

This would only happen if an order affects X directly. But usually you wouldn't increase X by 10% or by 12 but set a variable a to 10 and b to 12.

If X=bla at the start of the round, then X would be recalculated after every order that affects it: X= bla * (1+a)/100 + b

I can't think of an order that can't be implemented in this way.
War is peace
Freedom is slavery
Ignorance is strength

Post Reply