Skip to content

Commit

Permalink
Merge pull request #4 from lindlof/friend-game
Browse files Browse the repository at this point in the history
Allow private game
  • Loading branch information
lindlof authored Dec 5, 2020
2 parents e51c29f + 2703b9c commit 2abb9e7
Show file tree
Hide file tree
Showing 11 changed files with 335 additions and 72 deletions.
167 changes: 149 additions & 18 deletions contract/src/contract.rs
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ pub fn handle<S: Storage, A: Api, Q: Querier>(
) -> StdResult<HandleResponse> {
match msg {
HandleMsg::JoinGame { locator } => join_game(deps, env, locator),
HandleMsg::PrivateGame { locator } => private_game(deps, env, locator),
HandleMsg::PlayHand { locator, handsign } => play_hand(deps, env, locator, handsign),
HandleMsg::ClaimInactivity { locator } => claim_inactivity(deps, env, locator),
}
Expand Down Expand Up @@ -134,6 +135,9 @@ pub fn join_game<S: Storage, A: Api, Q: Querier>(
Some(s) => {
// player2 joins player1 and lobby becomes empty
let p1_locator = Locator::load(&mut deps.storage, s)?;
if p1_locator.canceled {
return Err(StdError::generic_err("forbidden game canceled"));
}
let game_id = p1_locator.game;
let p2_locator = Locator::new(loc_b, game_id, env.message.sender);
p2_locator.save(&mut deps.storage);
Expand All @@ -146,6 +150,39 @@ pub fn join_game<S: Storage, A: Api, Q: Querier>(
Ok(HandleResponse::default())
}

pub fn private_game<S: Storage, A: Api, Q: Querier>(
deps: &mut Extern<S, A, Q>,
env: Env,
locator: String,
) -> StdResult<HandleResponse> {
let funds = &env.message.sent_funds[0];
if funds.denom != FUNDING_DENOM || funds.amount < Uint128(FUNDING_AMOUNT) {
return Err(StdError::generic_err(
"insufficient_funds 100000 uscrt required",
));
}
let mut loc_b = [0u8; 32];
match hex::decode_to_slice(locator, &mut loc_b as &mut [u8]) {
Err(_) => return Err(StdError::generic_err("bad_request invalid_locator")),
Ok(_) => (),
}
match Locator::may_load(&deps.storage, loc_b)? {
None => {
// player1 waits for player2
Locator::new(loc_b, loc_b, env.message.sender).save(&mut deps.storage);
}
Some(l) => {
// player2 joins player1
if l.canceled {
return Err(StdError::generic_err("forbidden game canceled"));
}
let game = Game::new(l.game, l.player, env.message.sender);
game.save(&mut deps.storage);
}
}
Ok(HandleResponse::default())
}

pub fn claim_inactivity<S: Storage, A: Api, Q: Querier>(
deps: &mut Extern<S, A, Q>,
env: Env,
Expand All @@ -156,27 +193,28 @@ pub fn claim_inactivity<S: Storage, A: Api, Q: Querier>(
Err(_) => return Err(StdError::generic_err("bad_request invalid_locator")),
Ok(_) => (),
}
let locator = Locator::load(&deps.storage, bytes)?;
let mut locator = Locator::load(&deps.storage, bytes)?;
if locator.canceled {
return Err(StdError::generic_err("forbidden game canceled"));
}
let mut game;

match Game::load(&deps.storage, locator.game) {
Err(_) => {
match lobby_game(&mut deps.storage).load()? {
None => return Err(StdError::generic_err("not_found No game or lobby found")),
Some(s) => {
if s != bytes {
return Err(StdError::generic_err("not_found No game or lobby match"));
}
match Game::may_load(&deps.storage, locator.game)? {
None => {
if let Some(l) = lobby_game(&mut deps.storage).load()? {
if l == bytes {
lobby_game(&mut deps.storage).save(&None)?;
return Ok(payout(
env.contract.address,
env.message.sender,
Uint128(FUNDING_AMOUNT),
));
}
};
}
locator.canceled = true;
locator.save(&mut deps.storage);
return Ok(payout(
env.contract.address,
env.message.sender,
Uint128(FUNDING_AMOUNT),
));
}
Ok(g) => game = g,
Some(g) => game = g,
}

if game.game_over {
Expand Down Expand Up @@ -223,8 +261,7 @@ fn game_lobby<S: Storage, A: Api, Q: Querier>(
Ok(_) => (),
}
let locator = Locator::load(&deps.storage, bytes)?;
let game = Game::may_load(&deps.storage, locator.game)?;
match game {
match Game::may_load(&deps.storage, locator.game)? {
None => Ok(GameLobbyResponse {
game_started: false,
player1_locator: false,
Expand Down Expand Up @@ -421,6 +458,47 @@ mod tests {
assert_eq!(false, value.game_started);
}

#[test]
fn private_game_matching() {
let mut deps = mock_dependencies(20, &coins(0, "uscrt"));
let env = mock_env("creator", &[]);
let msg = InitMsg {};
let _res = init(&mut deps, env, msg).unwrap();

let env = mock_env("player1", &coins(FUNDING_AMOUNT, "uscrt"));
let msg = HandleMsg::PrivateGame { locator: loc(5) };
let _res = handle(&mut deps, env, msg).unwrap();

// JoinGame shouldn't interfere
let env = mock_env("player3", &coins(FUNDING_AMOUNT, "uscrt"));
let msg = HandleMsg::JoinGame { locator: loc(1) };
let _res = handle(&mut deps, env, msg).unwrap();

let env = mock_env("player2", &coins(FUNDING_AMOUNT, "uscrt"));
let msg = HandleMsg::PrivateGame { locator: loc(5) };
let _res = handle(&mut deps, env, msg).unwrap();

let env = mock_env("player1", &coins(1000, "token"));
let msg = HandleMsg::PlayHand {
locator: loc(5),
handsign: Handsign::ROCK,
};
let _res = handle(&mut deps, env, msg).unwrap();

let env = mock_env("player2", &coins(2, "token"));
let msg = HandleMsg::PlayHand {
locator: loc(5),
handsign: Handsign::PAPR,
};
let _res = handle(&mut deps, env, msg).unwrap();

let res = query(&deps, QueryMsg::GameStatus { locator: loc(5) }).unwrap();
let value: GameStatusResponse = from_binary(&res).unwrap();
assert_eq!(0, value.player1_wins);
assert_eq!(1, value.player2_wins);
assert_eq!(false, value.game_over);
}

#[test]
fn claim_opponent_inactivity() {
let mut deps = mock_dependencies(20, &coins(0, "uscrt"));
Expand Down Expand Up @@ -484,6 +562,7 @@ mod tests {
let msg = HandleMsg::ClaimInactivity { locator: loc(1) };
handle(&mut deps, env, msg).unwrap_err();
}

#[test]
fn claim_lobby_inactivity() {
let mut deps = mock_dependencies(20, &coins(0, "uscrt"));
Expand Down Expand Up @@ -518,9 +597,61 @@ mod tests {
}
}

// Can't double-claim
let env = mock_env("player1", &[]);
let msg = HandleMsg::ClaimInactivity { locator: loc(1) };
handle(&mut deps, env, msg).unwrap_err();

// Lobby becomes empty
let env = mock_env("player2", &coins(FUNDING_AMOUNT, "uscrt"));
let msg = HandleMsg::JoinGame { locator: loc(2) };
handle(&mut deps, env, msg).unwrap();

let msg = QueryMsg::GameLobby { locator: loc(2) };
let res = query(&deps, msg).unwrap();
let value: GameLobbyResponse = from_binary(&res).unwrap();
assert_eq!(false, value.game_started);
}

#[test]
fn claim_private_lobby_inactivity() {
let mut deps = mock_dependencies(20, &coins(0, "uscrt"));
let env = mock_env("creator", &[]);
let msg = InitMsg {};
init(&mut deps, env, msg).unwrap();

let env = mock_env("player1", &coins(FUNDING_AMOUNT, "uscrt"));
let msg = HandleMsg::PrivateGame { locator: loc(5) };
handle(&mut deps, env, msg).unwrap();

let env = mock_env("player1", &[]);
let msg = HandleMsg::ClaimInactivity { locator: loc(5) };
let res = handle(&mut deps, env, msg).unwrap();

assert_eq!(res.messages.len(), 1);
match &res.messages[0] {
CosmosMsg::Bank(BankMsg::Send {
to_address, amount, ..
}) => {
assert_eq!(to_address.as_str(), "player1");
assert_eq!(amount.len(), 1);
assert_eq!(amount[0].denom, "uscrt");
assert_eq!(amount[0].amount, Uint128(FUNDING_AMOUNT));
}
_ => {
panic!("Expected claim for inactivity");
}
}

// Can't double-claim
let env = mock_env("player1", &[]);
let msg = HandleMsg::ClaimInactivity { locator: loc(5) };
handle(&mut deps, env, msg).unwrap_err();

// Lobby becomes non-joinable
let env = mock_env("player2", &coins(FUNDING_AMOUNT, "uscrt"));
let msg = HandleMsg::PrivateGame { locator: loc(5) };
handle(&mut deps, env, msg).unwrap_err();
}

#[test]
Expand Down
1 change: 1 addition & 0 deletions contract/src/msg.rs
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ pub struct InitMsg {}
#[serde(rename_all = "snake_case")]
pub enum HandleMsg {
JoinGame { locator: String },
PrivateGame { locator: String },
PlayHand { locator: String, handsign: Handsign },
ClaimInactivity { locator: String },
}
Expand Down
4 changes: 3 additions & 1 deletion contract/src/state.rs
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ pub struct Locator {
id: [u8; 32],
pub game: [u8; 32],
pub player: HumanAddr,
pub canceled: bool,
}

impl Locator {
Expand All @@ -20,6 +21,7 @@ impl Locator {
id: id,
game: game,
player: player,
canceled: false,
}
}

Expand All @@ -36,7 +38,7 @@ impl Locator {
Ok(Self { id, ..data })
}

pub fn may_load<S: Storage>(&self, storage: &S, id: [u8; 32]) -> StdResult<Option<Self>> {
pub fn may_load<S: Storage>(storage: &S, id: [u8; 32]) -> StdResult<Option<Self>> {
let mut space = prefixed_read(b"lobby", storage);
let bucket = typed_read::<_, Locator>(&mut space);
bucket
Expand Down
4 changes: 2 additions & 2 deletions web/Dockerfile
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
FROM node:15.2.1
FROM node:15.3.0
WORKDIR /app
COPY package.json package-lock.json /app/
RUN npm install
COPY . /app
CMD ["npm", "run", "start"]
CMD ["npm", "run", "build"]
14 changes: 14 additions & 0 deletions web/docker-build.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
version: '3.8'
services:
web:
build:
context: .
command: ['npm', 'run', 'build']
environment:
- 'REACT_APP_CHAIN_ID=${REACT_APP_CHAIN_ID}'
- 'REACT_APP_CHAIN_NAME=${REACT_APP_CHAIN_NAME}'
- 'REACT_APP_LCD_URL=${REACT_APP_LCD_URL}'
- 'REACT_APP_RPC_URL=${REACT_APP_RPC_URL}'
- 'REACT_APP_CONTRACT=${REACT_APP_CONTRACT}'
volumes:
- './build/:/app/build'
1 change: 1 addition & 0 deletions web/docker-compose.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ services:
stdin_open: true
build:
context: .
command: ['npm', 'run', 'start']
environment:
- 'REACT_APP_CHAIN_ID=${REACT_APP_CHAIN_ID}'
- 'REACT_APP_CHAIN_NAME=${REACT_APP_CHAIN_NAME}'
Expand Down
Loading

0 comments on commit 2abb9e7

Please sign in to comment.