ReactVR and Social Apps: A Perfect Pair
Oculus Developer Blog
|
Posted by Jim Purbrick
|
July 1, 2017
|
Share

This article originally appeared on Oculus Software Engineer Jim Purbrick’s blog.

Mike Armstrong and I had been talking about how to easily build networked social applications with ReactVR for a while, so I spent some time hacking over a holiday break to see if I could build a ReactVR version of the pairs game in Oculus Rooms. Pairs is simple and fun, but also interesting as it’s real-time and has the potential to generate conflicting updates that need to be resolved.

Redux seemed like a promising starting point, since it reifies events and allows flexible event processing in a similar way to MASSIVE-3. I used websockets as they’re already supported by ReactVR along with wsrelay to network the clients.

With those pieces in place, the simplest way to network the clients is to implement a middleware function to send every action generated in one client to all the others. In the case of actions that show a tile, this is sufficient as the action is idempotent. If two players click on a square at the same time, the order that the actions are reduced in doesn’t matter—in either case, the result is that the element is revealed. We can exploit the idempotency by optimistically processing the action locally before sending it to other clients to minimize network latency.

  const sendAction = store => next => action => {
    
    if (! ('sender' in action)) {
    	action.sender = client
    	ws.send(JSON.stringify(action)) 
     }
  
   return next(action)
  }

    

Scoring is trickier. While each client can tell when a pair has been revealed, only the first player to reveal the pair should score a point. As the actions to reveal tiles are potentially processed in different orders on each client, that could lead to inconsistent scores even if only the first is processed. A simple way to avoid this inconsistency is to nominate one client to be the master and only have that client generate score actions. This can be implemented as another middleware to avoid generating actions inside a reducer.

 
 const scoreMaster = store => next => action => {
 	let state = store.getState()  if (isMasterClient(state.scores) && (action.type === 'SHOW')) {
		 let value = state.board[action.square.row][action.square.column]
 		 if (countValues(state.board, Math.abs(value)) === 1) {

    			// if one half of pair is visible now, both will be after
 			// action is reduced, so generate score action.
 				store.dispatch(score(state.scores, action.client))
 		}
	 }
	 return next(action)
}

    

The master client can also be made responsible for sending the current state of the simulation to new clients to support late joining.

ws.onmessage = (evt) => {
	let action = JSON.parse(evt.data)
	console.log(action)

	// existing master client sends state to other clients after processing join
	let master = isMasterClient(store.getState().scores) 
    
	// dispatch action locally
	store.dispatch(action)

	// sync state if join
	if (master && action.type === 'JOIN') {
		ws.send(JSON.stringify(setState(store.getState())))
	}
}

With those parts done, the app is usable and makes an interesting example of one possible way to network ReactVR applications. This was the first time I’d used React, ReactVR, or Redux, and I was very impressed by how easy-to-use and flexible they are. With the addition of some small pieces of middleware, Redux can be used to implement a distributed simulation with flexible consistency mechanisms to trade off latency and consistency. The Pairs example shows that, even within a simple application, applying different consistency mechanisms to different actions and parts of the application state is useful.

The next things to experiment with are using WebRTC to allow peer-to-peer communication between clients to further reduce latency, add a server to allow trusted and hidden state, and letting clients subscribe to a subset of actions to allow heterogeneous clients and interest management.

If you’d like to play the ReactVR version of Pairs or see the rest of the code, it’s available on GitHub.

All code in this post is made available under the ReactVR examples license.