From: Stefan F. <ste...@we...> - 2012-07-06 03:54:04
|
Below I discuss the per-requisites of the State mechanism. The latter will be covered in a soon-to-follow email. So most likely the only one interested in the bits below will be Erik. Stefan Remark: I changed the term "Move" in Rails1.x to the term "Change" in Rails2.0 to avoid the confusion with an in-game tile move or token move. ChangeStack and ChangeSet are MoveStack and MoveSet in Rails1.x. *** So how do State objects work in Rails internally? Assume a BooleanState variable was created: If the boolean value is changed by e.g. stateVariable.set(true), an object of a Change subclass is created (here a BooleanChange). The change object contains both links to the state and the old and the new variable. The change object then is added to the ChangeStack, which is located inside the StateManager. *** The redesigned ChangeStack: * Changes get collected in ChangeSets, where are two types of ChangeSets: - ActionChangeSet (all Changes associated with an action) - AutoChangeSet (all Changes during automated processing) * ChangeSets can be open=accepting new Changes or closed=raise errors if one tries to add Changes. * The current ChangeSet of the ChangeStack is always open. If the current ChangeSet is closed, a new (Auto)ChangeSet is automatically opened and is the new current ChangeSet of the ChangeStack. Remark: This is different to Rails1.x, for which some State changes could get lost, as there was no open ChangeSet available at the time of processing. * At the start of the game an AutoChangeSet created which is defined the terminal, thus it is not undoable. This collects all changes that precede any player action. * The typical sequence is the following: Player action is processed: ChangeStack.startActionChangeSet(player, action) opens a new ActionChangeSet (and closes the current AutoChangeSet automatically) After processing the action: ChangeStack.closeCurrentChangeSet() closes the current ActionChangeSet (and opens a new AutoChangeSet) Then the games process non-action related changes (e.g. ending the SR, starting a new OR, paying revenue to privates, etc.) all of this gets collected into AutoChangeSet(s). * The major advantage of the separation of changes is that it will allow to link output in the report window and player comments to either a player action or the automated game processing. Currently all output of the game engine is linked to the previous player action, however this is only based on game sequence and not on game logic. * Still undo/redo will always un-execute/re-execute the next player action (including all automated changes). * An internal change is that there are are now two stacks (one for undo, one for redo), this avoids the usage of an index that separates the undo-part from the redo-part if using one stack only. Thus an undo command the top ChangeSet of the undo-stack onto the redo stack and the reverse for redos. The current (open) ChangeSet is stored in a separately as long as it is open, it only gets moved onto the undo stack after its close. Thus all ChangeSets on the undo (and redo) stack are always closed. * Setting up of the ChangeStack: The ChangeStack is automatically setup to full functionality after creating the Root: So Root root = Root.create() will have working ChangeStack inside. If one adds a state now, e.g. BooleanState state = BooleanState.create(root, "example") every change of that state is put on the according ChangeStack. The ChangeStack is unique to the Root item hierarchy, however it is possible to create several roots with each having its own independent ChangeStack. Access to the ChangeStack: From root: root.getStateManager().getChangeStack() From any item: item.getRoot().getStateManager().getChangeStack() * Current state of implementation: The core functionality is working and covered by tests (see ChangeStackTest and ChangeSetTest). |
From: Erik V. <eri...@xs...> - 2012-07-06 09:01:24
|
Hi Stefan, Just a few comments. > Remark: I changed the term "Move" in Rails1.x to the term "Change" in > Rails2.0 to avoid the confusion with an in-game tile move or token move. > ChangeStack and ChangeSet are MoveStack and MoveSet in Rails1.x. I agree that Change is better than Move. > Remark: This is different to Rails1.x, for which some State changes could get > lost, as there was no open ChangeSet available at the time of processing. The explicit opening and closing of MoveSets was intentional, to make sure that no changes would exist outside of the "change window" after receiving and validating a player action. Just a matter of trying to ensure some programming discipline: any change outside that window was considered illegal. Unfortunately, I never got to catch such illegal changes, so the only way to find these errors was inspecting the logs. (This omission has been the cause of several undo bugs, so this incomplete cure may have been worse than the disease.) In fact you are dropping this change window concept, allowing changes at any time. Two further comments on that: 1. You no longer can enforce that no changes are done before validation is complete. Any such premature changes will now end up in an open AutoChangeSet. I would prefer an approach where whatever changeset is open would be closed before the engine passes control back to the UI, and any premature changes when no changeset is open should be catched somehow, perhaps by throwing an exception. I'm not sure if that is practical, however; if not, I may have to settle with your approach, although somewhat reluctantly. 2. I'm not sure if 'action' and 'auto' changes can be separated so easily, in the sense that the latter always follow the former. Did you check that? I'm particularly thinking of special cases like the CGR formation, where player and auto actions seem rather intertwined to me. 3. Do you still link change sets so that these can only be undone as a whole? Apart from these comments, I generally like your changes. Erik. |
From: Stefan F. <ste...@we...> - 2012-07-06 09:40:17
|
Erik: thanks for your comments: Some quick replies, see below. Stefan > >> Remark: This is different to Rails1.x, for which some State changes could > get >> lost, as there was no open ChangeSet available at the time of processing. > > The explicit opening and closing of MoveSets was intentional, to make sure > that no changes would exist outside of the "change window" after receiving > and validating a player action. > Just a matter of trying to ensure some programming discipline: any change > outside that window was considered illegal. > Unfortunately, I never got to catch such illegal changes, so the only way to > find these errors was inspecting the logs. > (This omission has been the cause of several undo bugs, so this incomplete > cure may have been worse than the disease.) > > In fact you are dropping this change window concept, allowing changes at any > time. Two further comments on that: > > 1. You no longer can enforce that no changes are done before validation is > complete. Any such premature changes will now end up in an open > AutoChangeSet. > I would prefer an approach where whatever changeset is open would be closed > before the engine passes control back to the UI, and any premature changes > when no changeset is open should be catched somehow, perhaps by throwing an > exception. I'm not sure if that is practical, however; if not, I may have to > settle with your approach, although somewhat reluctantly. > It is possible to add that behavior, by adding an explicit lock to the ChangeStack. Maybe we could even make the behavior then different in development (fail) and production (only error in log). For me that is not a property of the ChangeSet, but more of the ChangeStack itself. Then you could lock e.g. during validations. I did not spec it until now, as it was not correctly implemented in Rails1.x and was more a source of errors than a feature. For me this is different to open and close of ChangeSets (open means that ChangeSet accepts additional changes, closed means that it will NEVER accept any additional changes). > 2. I'm not sure if 'action' and 'auto' changes can be separated so easily, > in the sense that the latter always follow the former. Did you check that? > I'm particularly thinking of special cases like the CGR formation, where > player and auto actions seem rather intertwined to me. It is not easy in ALL cases, but it is easy in MOST cases. And as it only informational value, it is not an issue if it is done wrong. It will be * Start ActionChangeSet (closes AutoChangeSet) * Process Action * Close ActionChangeSet (opens AutoChangeSet) * Game Processing * Start ActionChangeSet (closes AutoChangeSet) * Process Action and so on... Game Processing could be split in separate parts * Last Action in SR processed * AutoChangeSet open * End of StockRound in-game processing (e.g. share price changes due to shares sold out) * Open next AutoChangeSet * In-Game OR processing (e.g. income of privates) * Start ActionChangeSet * Process Tile Lay Action and so on... The "base" functionality of ActionChangeSet and AutoChangeSet is identical, it merely defines the "owner" of changes and this could be used in displaying user comments and the game reports. What I have not mentioned so far is I intend to make user Comments and Report Text (sub-)components of the ChangeSets in future Rails2.0 development. > > 3. Do you still link change sets so that these can only be undone as a > whole? > There is an implicit link of ActionChangeSets to AutoChangeSets as undo/redo will always jump to the next action. So all AutoChangeSets will be undone automatically. I consider linking ActionChangeSets that belong to separate actions as harmful, as in my opinion it always indicate an inherent undo problem, which should be fixed instead of artificially linking the actions. |