Skip to content

Commit

Permalink
Track players in game context
Browse files Browse the repository at this point in the history
  • Loading branch information
DogLooksGood committed Mar 4, 2024
1 parent 0d98ab9 commit e36580f
Show file tree
Hide file tree
Showing 21 changed files with 234 additions and 110 deletions.
59 changes: 47 additions & 12 deletions api/src/effect.rs
Original file line number Diff line number Diff line change
Expand Up @@ -88,13 +88,19 @@ impl SubGame {
pub struct EmitBridgeEvent {
pub dest: usize,
pub raw: Vec<u8>,
pub join_players: Vec<GamePlayer>,
}

impl EmitBridgeEvent {
pub fn try_new<E: BridgeEvent>(dest: usize, bridge_event: E) -> Result<Self> {
pub fn try_new<E: BridgeEvent>(
dest: usize,
bridge_event: E,
join_players: Vec<GamePlayer>,
) -> Result<Self> {
Ok(Self {
dest,
raw: bridge_event.try_to_vec()?,
join_players,
})
}
}
Expand Down Expand Up @@ -216,9 +222,19 @@ pub struct Effect {
pub transfers: Vec<Transfer>,
pub launch_sub_games: Vec<SubGame>,
pub bridge_events: Vec<EmitBridgeEvent>,
pub valid_players: Vec<GamePlayer>,
}

impl Effect {

fn assert_player_id(&self, id: u64) -> Result<()> {
if self.valid_players.iter().find(|p| p.id == id).is_some() {
Ok(())
} else {
Err(Error::InvalidPlayerId(id, self.valid_players.iter().map(|p| p.id).collect()))
}
}

/// Return the number of nodes, including both the pending and joined.
pub fn count_nodes(&self) -> usize {
self.nodes_count as usize
Expand All @@ -233,12 +249,14 @@ impl Effect {
}

/// Assign some random items to a specific player.
pub fn assign(&mut self, random_id: RandomId, player_id: u64, indexes: Vec<usize>) {
pub fn assign(&mut self, random_id: RandomId, player_id: u64, indexes: Vec<usize>) -> Result<()> {
self.assert_player_id(player_id)?;
self.assigns.push(Assign {
random_id,
player_id,
indexes,
})
});
Ok(())
}

/// Reveal some random items to the public.
Expand Down Expand Up @@ -268,20 +286,23 @@ impl Effect {
}

/// Ask a player for a decision, return the new decision id.
pub fn ask(&mut self, player_id: u64) -> DecisionId {
pub fn ask(&mut self, player_id: u64) -> Result<DecisionId> {
self.assert_player_id(player_id)?;
self.asks.push(Ask { player_id });
let decision_id = self.curr_decision_id;
self.curr_decision_id += 1;
decision_id
Ok(decision_id)
}

pub fn release(&mut self, decision_id: DecisionId) {
self.releases.push(Release { decision_id })
}

/// Dispatch action timeout event for a player after certain milliseconds.
pub fn action_timeout(&mut self, player_id: u64, timeout: u64) {
pub fn action_timeout(&mut self, player_id: u64, timeout: u64) -> Result<()> {
self.assert_player_id(player_id)?;
self.action_timeout = Some(ActionTimeout { player_id, timeout });
Ok(())
}

/// Return current timestamp.
Expand Down Expand Up @@ -331,8 +352,10 @@ impl Effect {
}

/// Submit settlements.
pub fn settle(&mut self, settle: Settle) {
pub fn settle(&mut self, settle: Settle) -> Result<()> {
self.assert_player_id(settle.id)?;
self.settles.push(settle);
Ok(())
}

/// Transfer the assets to a recipient slot
Expand All @@ -350,6 +373,10 @@ impl Effect {
init_data: D,
checkpoint: C,
) -> Result<()> {
for p in players.iter() {
self.assert_player_id(p.id)?;
}

self.launch_sub_games.push(SubGame {
id,
bundle_addr,
Expand Down Expand Up @@ -408,11 +435,18 @@ impl Effect {
}

/// Emit a bridge event.
pub fn bridge_event<E: BridgeEvent>(&mut self, dest: usize, evt: E) -> Result<()> {
self.bridge_events.push(EmitBridgeEvent {
dest,
raw: evt.try_to_vec()?,
});
pub fn bridge_event<E: BridgeEvent>(
&mut self,
dest: usize,
evt: E,
join_players: Vec<GamePlayer>,
) -> Result<()> {
for p in join_players.iter() {
self.assert_player_id(p.id)?;
}

self.bridge_events
.push(EmitBridgeEvent::try_new(dest, evt, join_players)?);
Ok(())
}
}
Expand Down Expand Up @@ -469,6 +503,7 @@ mod tests {
checkpoint: None,
launch_sub_games: vec![],
bridge_events: vec![],
valid_players: vec![],
};
let bs = effect.try_to_vec()?;

Expand Down
6 changes: 6 additions & 0 deletions api/src/error.rs
Original file line number Diff line number Diff line change
Expand Up @@ -278,6 +278,12 @@ pub enum Error {

#[error("Game uninitialized")]
GameUninitialized,

#[error("Invalid bridge event")]
InvalidBridgeEvent,

#[error("Invalid player id: {0}, availables: {1:?}")]
InvalidPlayerId(u64, Vec<u64>),
}

#[cfg(feature = "serde")]
Expand Down
32 changes: 22 additions & 10 deletions api/src/event.rs
Original file line number Diff line number Diff line change
Expand Up @@ -129,14 +129,31 @@ pub enum Event {
Bridge {
dest: usize,
raw: Vec<u8>,
join_players: Vec<GamePlayer>,
},
}

impl std::fmt::Display for Event {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match self {
Event::Custom { sender, raw } => write!(f, "Custom from {}, inner: {:?}", sender, raw),
Event::Bridge { dest, raw } => write!(f, "Bridge to {}, inner: [{}...]", dest, raw[0]),
Event::Bridge {
dest,
raw,
join_players,
} => {
let players = join_players
.iter()
.map(|p| p.id.to_string())
.collect::<Vec<String>>()
.join(",");

write!(
f,
"Bridge to {}, inner: [{}...], join_players: [{}]",
dest, raw[0], players
)
}
Event::Ready => write!(f, "Ready"),
Event::ShareSecrets { sender, shares } => {
let repr = shares
Expand Down Expand Up @@ -165,7 +182,7 @@ impl std::fmt::Display for Event {
write!(f, "Join, players: [{}]", players)
}
Event::Leave { player_id } => write!(f, "Leave from {}", player_id),
Event::GameStart { } => {
Event::GameStart {} => {
write!(f, "GameStart")
}
Event::WaitingTimeout => write!(f, "WaitTimeout"),
Expand All @@ -183,13 +200,7 @@ impl std::fmt::Display for Event {
Event::SecretsReady { random_ids } => {
write!(f, "SecretsReady for {:?}", random_ids)
}
Event::ServerLeave {
server_id,
} => write!(
f,
"ServerLeave {}",
server_id
),
Event::ServerLeave { server_id } => write!(f, "ServerLeave {}", server_id),
Event::AnswerDecision { decision_id, .. } => {
write!(f, "AnswerDecision for {}", decision_id)
}
Expand All @@ -211,10 +222,11 @@ impl Event {
}
}

pub fn bridge<E: BridgeEvent>(dest: usize, e: &E) -> Self {
pub fn bridge<E: BridgeEvent>(dest: usize, e: &E, join_players: Vec<GamePlayer>) -> Self {
Self::Bridge {
dest,
raw: e.try_to_vec().unwrap(),
join_players,
}
}
}
Expand Down
2 changes: 1 addition & 1 deletion api/src/prelude.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
pub use crate::effect::Effect;
pub use crate::effect::{Effect, SubGame};
pub use crate::engine::{GameHandler, InitAccount};
pub use crate::error::{HandleError, HandleResult};
pub use crate::event::{CustomEvent, Event, BridgeEvent};
Expand Down
1 change: 1 addition & 0 deletions core/src/context.rs
Original file line number Diff line number Diff line change
Expand Up @@ -724,6 +724,7 @@ impl GameContext {
transfers: Vec::new(),
launch_sub_games: Vec::new(),
bridge_events: Vec::new(),
valid_players: self.players.clone(),
}
}

Expand Down
14 changes: 11 additions & 3 deletions core/src/engine.rs
Original file line number Diff line number Diff line change
Expand Up @@ -19,9 +19,7 @@ pub fn general_handle_event(
) -> Result<(), Error> {
// General event handling
match event {
Event::Ready => {
Ok(())
}
Event::Ready => Ok(()),

Event::ShareSecrets { sender, shares } => {
let addr = context.id_to_addr(*sender)?;
Expand Down Expand Up @@ -144,6 +142,16 @@ pub fn general_handle_event(
Ok(())
}

Event::Bridge {
join_players,
..
} => {
for p in join_players {
context.add_player(p.to_owned());
}
Ok(())
}

_ => Ok(()),
}
}
Expand Down
8 changes: 8 additions & 0 deletions core/src/types/common.rs
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,14 @@ pub enum ClientMode {
Validator,
}

#[derive(Debug, PartialEq, Eq, Clone, Copy, BorshSerialize, BorshDeserialize)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
#[cfg_attr(feature = "serde", serde(rename_all = "camelCase"))]
pub enum GameMode {
Main,
Sub,
}

#[derive(Debug, Clone, PartialEq, Eq, BorshSerialize, BorshDeserialize)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
pub struct Signature {
Expand Down
4 changes: 4 additions & 0 deletions js/borsh/src/errors.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,7 @@
export function invalidCtor(path: string[]) {
throw new Error(`Borsh: Cannot deserialize, missing type annotation at: ${path.join(',')}`);
}

export function invalidByteArrayLength(path: string[], expected: number, actual: number) {
throw new Error(`Borsh: Invalid byte array length at: ${path.join(',')}, expected: ${expected}, actual: ${actual}`);
}
Expand Down
14 changes: 9 additions & 5 deletions js/borsh/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ import {
} from './types';
import { BinaryWriter } from './writer';
import { BinaryReader } from './reader';
import { invalidByteArrayLength, extendedWriterNotFound, extendedReaderNotFound, invalidEnumField } from './errors';
import { invalidByteArrayLength, extendedWriterNotFound, extendedReaderNotFound, invalidEnumField, invalidCtor } from './errors';

class DeserializeError extends Error {
cause: Error;
Expand Down Expand Up @@ -123,7 +123,7 @@ function serializeValue(path: string[], value: any, fieldType: FieldType, writer
} else if (kind === 'array') {
writer.writeU32(value.length);
for (let i = 0; i < value.length; i++) {
serializeValue([...path, `<Array[${i}]>`], value[i], v, writer);
serializeValue([...path, `<Array[${i}/${value.length}]>`], value[i], v, writer);
}
} else if (kind === 'struct') {
serializeStruct(path, value, writer);
Expand Down Expand Up @@ -187,7 +187,7 @@ function deserializeValue(path: string[], fieldType: FieldType, reader: BinaryRe
let arr = [];
const length = reader.readU32();
for (let i = 0; i < length; i++) {
let v = deserializeValue([...path, `<Array[${i}]>`], value, reader);
let v = deserializeValue([...path, `<Array[${i}/${length}]>`], value, reader);
arr.push(v);
}
return arr;
Expand Down Expand Up @@ -279,6 +279,7 @@ function deserializeEnum(path: string[], enumClass: Function, reader: BinaryRead
}

function deserializeStruct<T>(path: string[], ctor: Ctor<T>, reader: BinaryReader): T {
if (ctor === undefined) invalidCtor(path);
const prototype = ctor.prototype;
const fields = getSchemaFields(prototype);
let obj = {};
Expand All @@ -288,7 +289,9 @@ function deserializeStruct<T>(path: string[], ctor: Ctor<T>, reader: BinaryReade
}
} catch (e) {
if (e instanceof DeserializeError) {
e.obj = obj;
if (e.obj === undefined) {
e.obj = obj;
}
}
throw e;
}
Expand All @@ -297,6 +300,7 @@ function deserializeStruct<T>(path: string[], ctor: Ctor<T>, reader: BinaryReade

export function field(fieldType: FieldType) {
return function (target: any, key: PropertyKey) {
if (target?.constructor?.prototype === undefined) throw new Error(`Invalid field argument for key: ${key.toString()}`)
addSchemaField(target.constructor.prototype, key, fieldType);
};
}
Expand Down Expand Up @@ -360,7 +364,7 @@ export function deserialize<T>(classType: Ctor<T> | EnumClass<T>, data: Uint8Arr
}
} catch (e) {
if (e instanceof DeserializeError) {
console.error('Deserialize failed, path:', e.path, ', current object:', e.obj, ', cause:', e.cause);
console.error('Deserialize failed, path:', e.path, ', current object:', e.obj, ', cause:', e.cause, ', data:', data);
}
throw e;
}
Expand Down
6 changes: 5 additions & 1 deletion js/sdk-core/src/accounts.ts
Original file line number Diff line number Diff line change
Expand Up @@ -205,6 +205,7 @@ export class EntryTypeCash extends EntryType implements IEntryTypeKind {
constructor(fields: any) {
super();
Object.assign(this, fields);
Object.setPrototypeOf(this, EntryTypeCash.prototype);
}
kind(): EntryTypeKind {
return 'Cash';
Expand All @@ -220,6 +221,7 @@ export class EntryTypeTicket extends EntryType implements IEntryTypeKind {
constructor(fields: any) {
super();
Object.assign(this, fields);
Object.setPrototypeOf(this, EntryTypeTicket.prototype);
}
kind(): EntryTypeKind {
return 'Ticket';
Expand All @@ -233,6 +235,7 @@ export class EntryTypeGating extends EntryType implements IEntryTypeKind {
constructor(fields: any) {
super();
Object.assign(this, fields);
Object.setPrototypeOf(this, EntryTypeGating.prototype);
}
kind(): EntryTypeKind {
return 'Gating';
Expand All @@ -243,9 +246,10 @@ export class EntryTypeGating extends EntryType implements IEntryTypeKind {
export class EntryTypeDisabled extends EntryType implements IEntryTypeKind {
constructor(_: any) {
super();
Object.setPrototypeOf(this, EntryTypeDisabled.prototype);
}
kind(): EntryTypeKind {
return 'Disabled'
return 'Disabled';
}
}

Expand Down
Loading

0 comments on commit e36580f

Please sign in to comment.