def add_player(self, sessionplayer: ba.SessionPlayer) -> None: """(internal)""" assert sessionplayer.sessionteam is not None sessionplayer.resetinput() sessionteam = sessionplayer.sessionteam assert sessionplayer in sessionteam.players team = sessionteam.activityteam assert team is not None sessionplayer.setactivity(self) with _ba.Context(self): sessionplayer.activityplayer = player = self.create_player( sessionplayer) player.postinit(sessionplayer) assert player not in team.players team.players.append(player) assert player in team.players assert player not in self.players self.players.append(player) assert player in self.players try: self.on_player_join(player) except Exception: print_exception(f'Error in on_player_join for {self}.')
def postinit(self, sessionplayer: ba.SessionPlayer) -> None: """Wire up a newly created player. (internal) """ from ba._nodeactor import NodeActor # Sanity check; if a dataclass is created that inherits from us, # it will define an equality operator by default which will break # internal game logic. So complain loudly if we find one. if type(self).__eq__ is not object.__eq__: raise RuntimeError( f'Player class {type(self)} defines an equality' f' operator (__eq__) which will break internal' f' logic. Please remove it.\n' f'For dataclasses you can do "dataclass(eq=False)"' f' in the class decorator.') self.actor = None self.character = '' self._nodeactor: Optional[ba.NodeActor] = None self._sessionplayer = sessionplayer self.character = sessionplayer.character self.color = sessionplayer.color self.highlight = sessionplayer.highlight self._team = cast(TeamType, sessionplayer.sessionteam.activityteam) assert self._team is not None self._customdata = {} self._expired = False self._postinited = True node = _ba.newnode('player', attrs={'playerID': sessionplayer.id}) self._nodeactor = NodeActor(node) sessionplayer.setnode(node)
def register_sessionplayer(self, player: ba.SessionPlayer) -> None: """Register a ba.SessionPlayer with this score-set.""" assert player.exists() # Invalid refs should never be passed to funcs. name = player.getname() if name in self._player_records: # If the player already exists, update his character and such as # it may have changed. self._player_records[name].associate_with_sessionplayer(player) else: name_full = player.getname(full=True) self._player_records[name] = PlayerRecord(name, name_full, player, self)
def register_player(self, player: ba.SessionPlayer) -> None: """Register a player with this score-set.""" name = player.getname() name_full = player.getname(full=True) try: # If the player already exists, update his character and such as # it may have changed. self._player_records[name].associate_with_player(player) except Exception: # FIXME: Shouldn't use top level Exception catch for logic. # Should only have this as a fallback and always log it. self._player_records[name] = PlayerRecord(name, name_full, player, self)
def get_player_icon(sessionplayer: ba.SessionPlayer) -> dict[str, Any]: info = sessionplayer.get_icon_info() return { 'texture': _ba.gettexture(info['texture']), 'tint_texture': _ba.gettexture(info['tint_texture']), 'tint_color': info['tint_color'], 'tint2_color': info['tint2_color'] }
def on_player_leave(self, sessionplayer: ba.SessionPlayer) -> None: """Called when a previously-accepted ba.SessionPlayer leaves.""" if sessionplayer not in self.sessionplayers: print('ERROR: Session.on_player_leave called' ' for player not in our list.') return _ba.playsound(_ba.getsound('playerLeft')) activity = self._activity_weak() if not sessionplayer.in_game: # Ok, the player is still in the lobby; simply remove them. with _ba.Context(self): try: self.lobby.remove_chooser(sessionplayer) except Exception: print_exception('Error in Lobby.remove_chooser().') else: # Ok, they've already entered the game. Remove them from # teams/activities/etc. sessionteam = sessionplayer.sessionteam assert sessionteam is not None _ba.screenmessage( Lstr(resource='playerLeftText', subs=[('${PLAYER}', sessionplayer.getname(full=True))])) # Remove them from their SessionTeam. if sessionplayer in sessionteam.players: sessionteam.players.remove(sessionplayer) else: print('SessionPlayer not found in SessionTeam' ' in on_player_leave.') # Grab their activity-specific player instance. player = sessionplayer.activityplayer assert isinstance(player, (Player, type(None))) # Remove them from any current Activity. if activity is not None: if player in activity.players: activity.remove_player(sessionplayer) else: print('Player not found in Activity in on_player_leave.') # If we're a non-team session, remove their team too. if not self.use_teams: self._remove_player_team(sessionteam, activity) # Now remove them from the session list. self.sessionplayers.remove(sessionplayer)
def remove_player(self, sessionplayer: ba.SessionPlayer) -> None: """(internal)""" # This should only be called on unexpired activities # the player has been added to. assert not self.expired player: Any = sessionplayer.gameplayer assert isinstance(player, self._playertype) team: Any = sessionplayer.team.gameteam assert isinstance(team, self._teamtype) assert player in team.players team.players.remove(player) assert player not in team.players assert player in self.players self.players.remove(player) assert player not in self.players with _ba.Context(self): # Make a decent attempt to persevere if user code breaks. try: self.on_player_leave(player) except Exception: print_exception(f'Error in on_player_leave for {self}') try: sessionplayer.reset() sessionplayer.set_node(None) sessionplayer.set_activity(None) except Exception: print_exception(f'Error resetting player for {self}')
def postinit(self, sessionplayer: ba.SessionPlayer) -> None: """Wire up a newly created player. (internal) """ from ba._nodeactor import NodeActor # Sanity check; if a dataclass is created that inherits from us, # it will define an equality operator by default which will break # internal game logic. So complain loudly if we find one. if type(self).__eq__ is not object.__eq__: raise RuntimeError( f'Player class {type(self)} defines an equality' f' operator (__eq__) which will break internal' f' logic. Please remove it.\n' f'For dataclasses you can do "dataclass(eq=False)"' f' in the class decorator.') self.actor = None self.character = '' self._nodeactor: Optional[ba.NodeActor] = None self._sessionplayer = sessionplayer self.character = sessionplayer.character self.color = sessionplayer.color self.highlight = sessionplayer.highlight self.team = sessionplayer.team.gameteam # type: ignore assert self.team is not None self.sessiondata = sessionplayer.sessiondata self.gamedata = sessionplayer.gamedata # Create our player node in the current activity. # Note: do we want to save a few cycles here by managing our player # node manually instead of wrapping it in a NodeActor? node = _ba.newnode('player', attrs={'playerID': sessionplayer.id}) self._nodeactor = NodeActor(node) sessionplayer.set_node(node)
def _reset_session_player_for_no_activity( self, sessionplayer: ba.SessionPlayer) -> None: # Let's be extra-defensive here: killing a node/input-call/etc # could trigger user-code resulting in errors, but we would still # like to complete the reset if possible. try: sessionplayer.setnode(None) except Exception: print_exception( f'Error resetting SessionPlayer node on {sessionplayer}' f' for {self}.') try: sessionplayer.resetinput() except Exception: print_exception( f'Error resetting SessionPlayer input on {sessionplayer}' f' for {self}.') # These should never fail I think... sessionplayer.setactivity(None) sessionplayer.activityplayer = None
def on_player_request(self, player: ba.SessionPlayer) -> bool: """Called when a new ba.Player wants to join the Session. This should return True or False to accept/reject. """ # Limit player counts *unless* we're in a stress test. if _ba.app.stress_test_reset_timer is None: if len(self.players) >= self.max_players: # Print a rejection message *only* to the client trying to # join (prevents spamming everyone else in the game). _ba.playsound(_ba.getsound('error')) _ba.screenmessage( Lstr(resource='playerLimitReachedText', subs=[('${COUNT}', str(self.max_players))]), color=(0.8, 0.0, 0.0), clients=[player.get_input_device().client_id], transient=True) return False _ba.playsound(_ba.getsound('dripity')) return True
def player_got_hit(self, player: ba.SessionPlayer) -> None: """Call this when a player got hit.""" s_player = self._player_records[player.getname()] s_player.streak = 0
def on_player_leave(self, sessionplayer: ba.SessionPlayer) -> None: """Called when a previously-accepted ba.SessionPlayer leaves.""" # pylint: disable=too-many-branches if sessionplayer not in self.players: print('ERROR: Session.on_player_leave called' ' for player not in our list.') return _ba.playsound(_ba.getsound('playerLeft')) activity = self._activity_weak() if not sessionplayer.in_game: # Ok, the player's still in the lobby. Simply remove them from it. with _ba.Context(self): try: self.lobby.remove_chooser(sessionplayer) except Exception: print_exception('Error in Lobby.remove_chooser().') else: # Ok, they've already entered the game. Remove them from # teams/activities/etc. sessionteam = sessionplayer.team assert sessionteam is not None assert sessionplayer in sessionteam.players _ba.screenmessage( Lstr(resource='playerLeftText', subs=[('${PLAYER}', sessionplayer.get_name(full=True))])) # Remove them from their SessionTeam. if sessionplayer in sessionteam.players: sessionteam.players.remove(sessionplayer) else: print('SessionPlayer not found in SessionTeam' ' in on_player_leave.') # Grab their activity-specific player instance. player = sessionplayer.gameplayer assert isinstance(player, (Player, type(None))) # Remove them from any current Activity. if activity is not None: if player in activity.players: activity.remove_player(sessionplayer) else: print('Player not found in Activity in on_player_leave.') # If we're a non-team session, remove their team too. if not self.use_teams: # They should have been the only one on their team. assert not sessionteam.players # Remove their Team from the Activity. if activity is not None: if sessionteam.gameteam in activity.teams: activity.remove_team(sessionteam) else: print('Team not found in Activity in on_player_leave.') # And then from the Session. with _ba.Context(self): if sessionteam in self.teams: try: self.teams.remove(sessionteam) self.on_team_leave(sessionteam) except Exception: print_exception( f'Error in on_team_leave for Session {self}.') else: print('Team no in Session teams in on_player_leave.') try: sessionteam.reset_sessiondata() except Exception: print_exception( f'Error clearing sessiondata' f' for team {sessionteam} in session {self}.') # Now remove them from the session list. self.players.remove(sessionplayer)