-
Notifications
You must be signed in to change notification settings - Fork 10
backwardCallbackResult and Undoing a Game State Step
backwardCallbackResult
rolls back the game state by 1 block with the undo data from the previous block. It is similar to forwardCallbackResult
, but we don't create any undo data because we're consuming some undo data.
In a production game, you will likely want to store more undo data than just for 1 block. This allows you to have a greater buffer in the unlikely event that you discover that you've been on a fork for more than 1 block. Remember, you can always post questions in the XAYA Development forums at https://forum.xaya.io/forum/6-development/.
Here's the method signature:
public static string backwardCallbackResult(string newState,
string blockData,
string undoData)
-
newState
: OurGameState
data -
blockData
: This is unused in this example -
undoData
: This is the data we use to roll back the game state by 1 block
To start, we initialise GameState
and UndoData
objects with deserialised data from our parameters.
GameState state = JsonConvert.DeserializeObject<GameState>(newState);
UndoData undo = JsonConvert.DeserializeObject<UndoData>(undoData);
Any given block can have new players join, so we need to keep track of those independently. We'll do that in a string list.
List<string> playersToRemove = new List<string>();
We need to check each player to see if they need to be rolled back. We do this by iterating through all players in the game state.
foreach (var mi in state.players)
To start our loop, we initialise some variables. We need to know the player's name and PlayerState
. We get this from mi
. A PlayerUndo
variable is also created as null.
string name = mi.Key;
PlayerState p = mi.Value;
PlayerUndo u;
We only need to undo a player if they exist in our undo data, so we create a boolean flag for us to use and set its value.
undoIt = undo.players.ContainsKey(name);
The first thing to do if a player needs to be rewound, is to check if they are new players and add them to our playersToRemove
list. We get the specific player through undo.players[name]
and then we check the is_new
property. Also, if the player is a new player, then we skip to the top of the loop. We'll remove the new players all at once later with our playersToRemove
list.
if (undoIt)
{
u = undo.players[name];
if (u.is_new)
{
playersToRemove.Add(name);
continue;
}
}
Next, if the player has not finished moving according to the undo data, i.e. their Direction
is not Direction.NONE
, then we must check whether or not their current direction is NONE
and they have no steps left. If so, we set their current direction to their undo data direction.
if (undoIt)
{
u = undo.players[name];
if (u.finished_dir != Direction.NONE)
{
if (p.dir == Direction.NONE && p.steps_left == 0)
{
p.dir = u.finished_dir;
}
}
}
Now, for all players we check if their current direction is not NONE
. If so, we add a step and subtract the direction offset from their current position.
if (p.dir != Direction.NONE)
{
p.steps_left += 1;
Int32 dx = 0, dy = 0;
HelperFunctions.GetDirectionOffset(p.dir, ref dx, ref dy);
p.x -= dx;
p.y -= dy;
}
To undo a player move we must set their current player state to their undo player state if our undoIt
boolean flag is set for this player (this was set above in bool undoIt = undo.players.ContainsKey(name);
).
So, for all players in the undo data, if their direction is not NONE
, we set their current player state direction to the direction in the undo data. This effectively undoes their direction.
We also set their current player state steps to the number of steps in their undo data if it's not our default value of 99999999.
if (undoIt)
{
u = undo.players[name];
if (u.previous_dir != Direction.NONE)
{
p.dir = u.previous_dir;
}
if (u.previous_steps_left != 99999999)
{
p.steps_left = u.previous_steps_left;
}
}
This effectively completes undoing the player's last move so we return back to the start of the loop, i.e.:
foreach (var mi in state.players)
That completes our loop over the players. The only remaining step to rewind 1 block is to remove all the new players that we stored in playersToRemove
.
foreach (string nm in playersToRemove)
{
state.players.Remove(nm);
}
Finally, we return the serialised GameState
object so we can update the game state.
return JsonConvert.SerializeObject(state);
Your game will require more complex logic to undo a block, but the above should suffice to illustrate the general technique.
- Step 0: Blockchain Basics
- Step 1: xayad <⸺ start here
- Step 2: The Game State Processor
- Step 3a: libxayagame Component Relationships
- Step 3b: Compile libxayagame in Windows
- Step 3b: Compile libxayagame in Ubuntu
- Step 4: Run xayad for Games
- Step 5: Hello World! in C++
- Step 5: Hello World! in C#
- Step 5: Hello World! with SQLite
- Step 6a: Mover Overview
- Step 6b: Mover Console
- Step 6c: Mover Unity
- libxayagame Component Relationships
- How to Compile libxayagame in Ubuntu 20.04.03
- How to Compile libxayagame in Ubuntu 22.04
- How to Compile libxayagame in Windows
- Xayaships (How to get started playing)