Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Out of the box in game chat support #460

Closed
haoyangnz opened this issue Sep 16, 2019 · 20 comments
Closed

Out of the box in game chat support #460

haoyangnz opened this issue Sep 16, 2019 · 20 comments

Comments

@haoyangnz
Copy link
Contributor

haoyangnz commented Sep 16, 2019

Any plan for out of the box in game chat support?
This would be very helpful.

@haoyangnz haoyangnz changed the title Any plan for out of the box in game chat support? Out of the box in game chat support Sep 16, 2019
@nicolodavis
Copy link
Member

I'm not opposed to this idea, especially if it can be configured nicely so that users that don't need it don't incur any cost.

We'd just need someone to sign up for this as I have my hands full with other areas of the framework.

@haoyangnz
Copy link
Contributor Author

@nicolodavis If I were to do this, do you have a recommended approach in mind?

@nicolodavis
Copy link
Member

We currently store game data in two areas:

  1. Game State - this is where G and ctx are stored.
  2. Game Metadata - this is where lobby info and other player related metadata are stored.

The areas above are two key spaces in the same table (although in theory it is possible to change the storage adapters to use two entirely different tables for this).

For chat, I'd propose using a separate table that is able to store messages between players per game in a nice normalized flat structure.

key: <gameID>:<messageID>
value: { from: <playerID>, to: <playerID>, message: ' ... ' }

Links:

  1. Storage Adapters - link
  2. Lobby API - link

@blunket
Copy link
Contributor

blunket commented Oct 2, 2019

Yesss, I want this. Can this be opt-in, though? Preferably on a per-game basis?

I assume we'd have a pre-built chat component for the front-end while offering options to implement your own front-end (similar to the current lobby discussion).

@nicolodavis
Copy link
Member

nicolodavis commented Oct 5, 2019

Yes I'd hope that we make this opt in. We can also implement (as a first step) a purely ephemeral chat that uses the existing socket layer. That way you don't have to worry about storage, which might be exactly what some people want.

@vdfdev
Copy link
Contributor

vdfdev commented Apr 19, 2020

I want to implement this, it is one of the most important features missing on FBG right now.

Cant we really re-use the current store ? I was thinking something on the sorts of:

For sending messages:

this.props.chat.send('User message'); 

For listing all messages for all players:

this.props.chat.get();

Each message could be a specific action type on the store/reducer, and change the state on a separate part of the state object. This will mean that if a user reloads the page, the messages are still going to be there (retrieved from the store).

In fact, this is so close to what Plugins offer that I thought I could do it in a plugin, but I had trouble with the Plugins API not being exposed to the Board

@delucis
Copy link
Member

delucis commented Apr 19, 2020

@flamecoals Would be great if you could do this!

I’m not sure about storing chat messages in the main game state. Given that chat could be fairly busy involving all players simultaneously, it would potentially increase the risk for race conditions once chat messages are trying to write to the same blob as the main game engine. We should probably add database methods for reading or adding to a game’s chat messages, which could be implemented in a transactionally safe way similar to how the deltalog is currently appended in setState.

How do you think we can manage enabling/disabling chat?

💭 One nice to have feature would be the ability for game logic to post a chat messages, enabling game achievements or events in the chat log.

One further alternative to consider would be not to store chat messages at all, just providing an ephemeral chat room for players connected at any one moment. (Potentially a good idea from a privacy perspective, because users are more likely to reveal personal information in a chat, and storing that data could introduce oversight headaches.)

@vdfdev
Copy link
Contributor

vdfdev commented Apr 19, 2020

I didn't know that the main game state could run into a race condition, because the store is always changed using a message and reducers for each state transactions... Can you detail more about how this can run into a race condition? Some of the moves I have on my games are "clicks" which are very frequent and I've never noticed race conditions.

Enabling/disabling chat could probably be done on the game or client configuration with a flag?

If we use a plugin under the hood, the game logic could easily post chat messages.

This even makes me think that we should maybe just invest in the plugin API and maybe make a plugin available for users that want, but maybe not a built-in in BGIO

@delucis
Copy link
Member

delucis commented Apr 19, 2020

Currently the reducer guards against race conditions using an incrementing state ID. Every move, the state ID increments and the reducer checks to make sure actions are sequential. If it receives a bad state ID, the action is rejected. This approach isn’t so helpful in an asynchronous, multi-client context because two users could send messages more or less simultaneously (each with the same state ID) and one of the two would be rejected as its state ID would be “stale”.

Chat would presumably be more permissive: message order is not crucial, so as long as the database’s message appending logic is atomic, parallel writes should be possible.

Improving the plugin API would be a great outcome too, to allow more custom behaviour without needing to fork the code.

@nicolodavis
Copy link
Member

nicolodavis commented Apr 19, 2020

I'm not sure if using the game state to store chat messages (even through a plugin) is a good idea. It makes application state too closely coupled with game state. Note that the Plugin API already supports intercepting custom actions (even bypassing the state ID check), so I don't think you need any changes to BGIO to implement this via a plugin.

I think a cleaner approach would be to allow associating custom user data to game instances via the API that the lobby uses. We already have this in the form of game metadata. We can explore expanding that to support a chat use-case.

@vdfdev
Copy link
Contributor

vdfdev commented Apr 19, 2020

Thanks for the response @nicolodavis, the issue with using lobby in my mind is that it is a REST API and we do pooling to check if the lobby status changes. I believe that would be a waste of resources to use the same technique for chat,and that it fits much more a websocket style communication.

Also, how would the board use the lobby API? Through the metadata object ? What about the users that do not implement lobby, would they not be able to implement text chat? For instance, in local or AI games we do not use the lobby API at all in FBG. Which would be useful if the game itself might send messages?

@nicolodavis
Copy link
Member

Note that you will be serializing / deserializing the entire game state for every chat message, which seems like a bigger waste of resources than polling.

Also, the game creation API is not limited to REST. We could use GraphQL or web sockets to implement it. I'm proposing that we look at how we can improve that to support chat rather than sticking it into the game state.

@vdfdev
Copy link
Contributor

vdfdev commented Apr 19, 2020

I don't agree that it would be a bigger waste of resource, serializing and desserializing happens only in memory and the only thing that goes through the network is the message. While polling might need to go through the network every X seconds, with or without new messages, which can drain the battery of mobile phones easily.

@nicolodavis
Copy link
Member

The chat API could very well use boardgame.io's websocket backend to do realtime updates. We just need to make sure that the data sits in a separate section of the storage (analogous to metadata, but could be something new) and is wired through the system so that it is available on the client.

That said, there is no harm in trying to write a plugin first. No changes are required in boardgame.io as far as I can tell.

@vdfdev
Copy link
Contributor

vdfdev commented Apr 19, 2020

I tried to write the plug-in yesterday, but the new plugin API wasn't available at the Board render() ctx, so I couldn't list the messages there and I couldn't call the API to add a new message. I even deleted the code, but I could try again ...

Are the plugins API supposed to be available in that ctx too? (In the render() on react components?) I might do a fiddle just to test the concept

@nicolodavis
Copy link
Member

You'll need to read the data from state.plugins.chat on the client.

Events can be dispatched to plugins using client.plugin.chat(). Need to verify if this is available in React, but it should be a one line change to add it there if it isn't already.

You would only need to interact with ctx if you're doing it inside a move.

@vdfdev
Copy link
Contributor

vdfdev commented Apr 19, 2020

Ok, thanks for the pointers ! I will try to add plug-in support to this use case then :)

@delucis
Copy link
Member

delucis commented Apr 19, 2020

the only thing that goes through the network is the message

It’s worth noting that this is only currently the case for sending the message, not receiving it. Once a plugin’s action is processed on the server, all clients would receive an update event containing the entire updated game state.

I wasn’t aware of the client.plugins[plugin]() API — sounds like chat might be possible using a plugin for now!

@nicolodavis
Copy link
Member

nicolodavis commented Apr 21, 2020

It’s worth noting that this is only currently the case for sending the message, not receiving it. Once a plugin’s action is processed on the server, all clients would receive an update event containing the entire updated game state.

Yes, the number of bytes sent on the network is going to be at least an order of magnitude larger than the chat message itself. Same with storage in the database. Each chat message is going to duplicate the entire game state and store it in a new record (unless you use a storage implementation that merely overwrites the game state in-place).

Perhaps an easy way to fix this is to introduce an option to the Plugin API to indicate that data for that particular plugin needs to sit in a new column in the database? This would effectively allow you to decouple plugin data from the game state and address most of the concerns in this thread.

@flamecoals @delucis What do you think?

@tomasvidhall tomasvidhall mentioned this issue Dec 15, 2020
2 tasks
@delucis
Copy link
Member

delucis commented Jan 15, 2021

Done in #871 / #879 and released in 0.43.0 thanks to @tomasvidhall 🎉

@delucis delucis closed this as completed Jan 15, 2021
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

5 participants