How do you keep players updated during a two player game?

With the suggestion(s) I received previously, I’ve been able to create a nice little game that two people can play–as long as they’re sitting side-by-side and sharing a screen. Now I’d like to create a version for players on separate computers.

It seems like state information is typically kept in a data table, and a lot of the code on the server. I can serialize the data shared between players:

  • the state of a playing board (dictionary),
  • deck of cards (string list),
  • two hands, and
  • whose turn it is (boolean).
    I can probably keep everything in a single row with five fields.

I’m thinking, though, that this is going to be slow–every time a player makes a move, data will have to be saved to the table. And the client code polls the table every couple seconds to keep both players updated.

Am I on the right track? Is this how the experienced amongst you would do this? And am I right in thinking game play will be slow, with so much stuff happening in the server?

I did something similar in one of my apps recently.

The accelerated tables has a row.refresh which is the fastest way to update a row in my opinion.

If you call row.refresh inside a timer, you can get updates as fast as within 0.1 - 0.2 seconds. Of course, that depends on your location and they amount of data you have.

Just remember to use q.fetch_only to remove any columns that are not used.

Edit: There is also a row.fetch method that keeps the rest of the columns intact.

1 Like

Yes, that’s the way I would do it.

A timer that calls a server function once per second is too slow for an action game, but for a card game is just fine. Don’t forget that the average reaction time is 0.5 seconds.

I have an app that polls every 5 seconds, and most of my users think it’s in real time.

1 Like

Thank you both. I appreciate you taking the time to respond to my question. Very helpful!

So far, I’ve set the app up to poll the database and refresh the display every 5 seconds. Everything’s working OK. The only thing I don’t love is that when the display refreshes there’s a moment when you see the “spinning wheel” as everything’s updated. The code only has one call to the server:

game_state = app_tables.board_state.get(id=1)

Followed by code assigning variables to the individual values from game_state. (There’s only one row, with id=1.) Is the little lag/“spinning wheel” inevitable?

Wrap your server call in this:

with anvil.server.no_loading_indicator:

Thanks for responding. I tried your code in my method:

  def update(self):
    with anvil.server.no_loading_indicator: 
      game_state = anvil.server.call('update')
    # game_state = anvil.server.call_s('update')
    self.deck = game_state['Deck']
    self.blue_hand = game_state['BlueHand']
    self.green_hand = game_state['GreenHand']
    player_color="green" if self.is_green_turn else "blue"
    self.update_hand_display(player_color)
    self.model = game_state['Board'] # doing this clears flags, too, even if it's mid-play
    self.draw_flags_for_hand(player_color) # add flags back to board
    self.canvas_1_reset()

as well as commenting out your code and uncommenting the third line, and still get the spinner. I don’t really understand why, since both your expertise and the docs (assuming I understand them correctly) say the spinner shouldn’t be visible. (I’m calling this method every 5 seconds.)

There are various hidden server interactions that can display the spinner. For example, if your game_state is a data table row that was fetched with q.fetch_only() so no columns were fetched, then accessing game_state['Deck'] would trigger a server interaction to get the value.

Not saying that’s it, but the point is there are more reasons than just a server call for the spinner to show. Maybe try putting all your update code in the with block?

2 Likes

Yeah, this is why I never send row objects to the client. I always fetch what I need server-side, turn it into a plain dict, and return that. This way, I know exactly when a server call happens—no surprises from lazy loads when you access a field.

It also helps keep the spinner under control, since all server interactions are where I expect them: inside anvil.server.call.

2 Likes

Your suggestion worked, Jay. Thank you. Forums are awesome :grinning:
Meanwhile, your point makes sense, too, Stefano. I will experiment to learn more about just what (data types) it is that I’m sending back and forth between client and server. Hopefully my code is uncoupled enough that the kind of change you’re discussing wouldn’t be too difficult to implement. (And, if it is, then I need to do some refactoring anyway, right? :grinning: )

Thanks again, everyone.

1 Like