z4-engine 0.2.0

a multiplayer real-time decentralized game framework
Documentation
use ethers::prelude::*;
use std::collections::HashMap;
use std::sync::Arc;
use tokio::sync::mpsc::{unbounded_channel, UnboundedReceiver, UnboundedSender};
use z4_types::{Result, RoomId};

use crate::contracts::RoomMarket;
use crate::{ChainMessage, PoolMessage};

const GAS_PRICE: u64 = 20_000_000_000; // 20 GWEI
const EXTRA_GAS: u64 = 10; // extra 10%

/// Create pool channel
pub fn pool_channel() -> (UnboundedSender<PoolMessage>, UnboundedReceiver<PoolMessage>) {
    unbounded_channel()
}

/// Listen pool task
pub async fn listen(
    client: Arc<SignerMiddleware<Arc<Provider<Http>>, LocalWallet>>,
    market_address: Address,
    sender: UnboundedSender<ChainMessage>,
    mut receiver: UnboundedReceiver<PoolMessage>,
) -> Result<()> {
    let market = RoomMarket::new(market_address, client.clone());
    let mut games: HashMap<RoomId, (Vec<u8>, Vec<u8>)> = HashMap::new();

    while let Some(msg) = receiver.recv().await {
        match msg {
            PoolMessage::AcceptRoom(id, params) => {
                let gas_price = market
                    .client_ref()
                    .get_gas_price()
                    .await
                    .unwrap_or(GAS_PRICE.into());
                let extra_gas = gas_price + gas_price / U256::from(EXTRA_GAS);

                match market
                    .accept_room(U256::from(id), params.into())
                    .gas_price(extra_gas)
                    .send()
                    .await
                {
                    Ok(pending) => {
                        if let Ok(receipt) = pending.await {
                            info!(
                                "Accepted, Gas used: {:?}",
                                receipt
                                    .expect("Failed to accept receipt")
                                    .cumulative_gas_used
                            );
                        } else {
                            error!("Failed to accept event");
                        }
                    }
                    Err(err) => {
                        if let Some(rcode) = err.decode_revert::<String>() {
                            error!("{}", rcode);
                        } else {
                            error!("{}", err);
                        }
                    }
                }
            }
            PoolMessage::OverRoom(id, result, proof) => {
                games.insert(id, (result.clone(), proof.clone()));

                let gas_price = market
                    .client_ref()
                    .get_gas_price()
                    .await
                    .unwrap_or(GAS_PRICE.into());
                let extra_gas = gas_price + gas_price / U256::from(EXTRA_GAS);

                match market
                    .over_room_with_zk(U256::from(id), result.into(), proof.into())
                    .gas_price(extra_gas)
                    .send()
                    .await
                {
                    Ok(pending) => {
                        if let Ok(receipt) = pending.await {
                            info!(
                                "Game over sent, Gas used: {:?}",
                                receipt
                                    .expect("Failed to claim receipt")
                                    .cumulative_gas_used
                            );
                        } else {
                            error!("Failed to sent event");
                        }
                    }
                    Err(err) => {
                        if let Some(rcode) = err.decode_revert::<String>() {
                            error!("{}", rcode);
                        } else {
                            error!("{}", err);
                        }
                        let _ = sender.send(ChainMessage::Reprove);
                    }
                }
            }
            PoolMessage::Submitted(id) => {
                games.remove(&id);
            }
        }
    }

    Ok(())
}