def start_game(self): log_type = "INFO" log_code = "Game" log_message = "Game beginning" log_detail = 1 context_id = self.g_id log_handler.log(log_type=log_type, log_code=log_code, log_message=log_message, log_detail=log_detail, context_id=context_id) self.assign_roles() self.change_state("waiting") log_type = "INFO" log_code = "Game" log_message = "First round: night dawns" log_detail = 3 context_id = self.g_id log_handler.log(log_type=log_type, log_code=log_code, log_message=log_message, log_detail=log_detail, context_id=context_id) self.g_round += 1 new_event = event.EventFactory.create_event("night", self.g_id, self) self.add_event(new_event.e_id) self.check_event_queue()
def filter_JSON(self, game_json, filters): players_json = {} get_individuals_filters = [] for filter_p_ids, filters_to_apply in filters.items(): get_individuals_filters.append([filter_p_ids]) # convert to a list of individual filters # Obtain all players that are in this game_json knows_about = self.get_groups(get_individuals_filters, expected_count=len(get_individuals_filters)) for player in knows_about: if player.p_id in filters: players_json[player.p_id] = player.as_JSON(player_json={}, attribute_filter=filters[player.p_id]) else: log_type = "ERROR" log_code = "Game" log_message = "You were probably expecting a different p_id. Check the User() init function!" log_detail = 4 context_id = self.g_id log_handler.log(log_type=log_type, log_code=log_code, log_message=log_message, log_detail=log_detail, context_id=context_id) game_obj = json.loads(game_json) game_obj['players'] = players_json game_json = json.dumps(game_obj, sort_keys=True, indent=4) return game_json
def publish_all_games(): '''Finds all the games currently in redis and returns a JSON of their basic information''' data_dict = {} games_dict = {} all_redis_games = ww_redis_db.keys("g_list:*") ############################################################ # Get all games from redis as a JSON ############################################################ if len(all_redis_games) > 0: for redis_game_key in all_redis_games: g_id = str(redis_game_key.decode("utf-8")).split(":")[1] python_game = Game(g_id) games_dict["game:"+g_id] = python_game.as_JSON() else: log_handler.log( log_type = "INFO", log_code = "CurrentDev", log_message = "No games were found in Redis", log_detail = 5 ) ############################################################ # Publish information ############################################################ data_dict["games"] = games_dict data_dict["channel"] = "games_list" publish_data("lobbyinfo", data_dict) return data_dict
def __init__(self, p_id=None, session_key=None): self.name = "" self.g_history = [ ] # list of games just been in to assist matchmaking algorithm self.location = "" self.session_key = "" self.broadcastable = [] self.broadcastable.extend(["name", "location", "p_id"]) # look for p_id in session if session_key: self.session = SessionStore(session_key=session_key) self.session_key = session_key if 'p_id' in self.session: p_id = self.session['p_id'] try: # try loading from redis self.p_id = p_id self.load(p_id) except Exception as error: log_type = "ERROR" log_code = "Player" log_message = "Couldn't load user's p_id from redis, putting in default values: " + str( error) log_detail = 3 context_id = self.p_id log_handler.log(log_type=log_type, log_code=log_code, log_message=log_message, log_detail=log_detail, context_id=context_id) self.name = "Richard" self.p_id = str(uuid.uuid4()) self.location = "outgame" self.session = SessionStore(self.session_key) self.session_key = self.session.session_key self.session['p_id'] = self.p_id self.session['location'] = self.location self.session.save() self.save() log_type = "INFO" log_code = "Memory" log_message = "User initialised and saved" log_detail = 7 context_id = self.p_id log_handler.log(log_type=log_type, log_code=log_code, log_message=log_message, log_detail=log_detail, context_id=context_id)
def publish_detailed_game(g_id, p_id=None): '''Finds the specified game and creates a detailed JSON, optionally filtering for a specific player''' data_dict = {} # the entire dict to publish games_dict = {} # the dict containing a single record of the game as a JSON string. Kept as a dict to maintain compatibility with other games_dict parsers publish_channel = "game:" + g_id # default publishing to everyone subscribed to the game information_channel = publish_channel # information will always be a game ############################################################ # Get detailed game as a JSON ############################################################ try: redis_game = ww_redis_db.hgetall("g_list:"+g_id) if p_id: requesting_player = user.Player(p_id) publish_channel = "player:" + p_id # publish only to the requesting player if len(redis_game) > 0: #g_id is valid python_game = Game(g_id) full_json = python_game.as_JSON(meta=True, players=True, cur_events=True, hist_events=True) if p_id: filtered_json = python_game.filter_JSON(full_json, requesting_player.knows_about) games_dict["game:"+g_id] = filtered_json else: games_dict["game:"+g_id] = full_json else: log_handler.log( log_type = "ERROR", log_code = "CurrentDev", log_message = "No games were found!", log_detail = 3, context_id = g_id ) raise ValueError("There are no games in redis") ############################################################ # Publish information ############################################################ data_dict["games"] = games_dict data_dict["channel"] = information_channel publish_data(publish_channel, data_dict) return data_dict ############################################################ # Dismiss the error and log. ############################################################ except: logging.exception('') log_handler.log( log_type = "ERROR", log_code = "CurrentDev", log_message = "Could not publish this detailed game", log_detail = 3, context_id = g_id )
def get_player_ids(self): log_handler.log( log_type = "ERROR", log_code = "Game", log_message = "Redundant function called", log_detail = 1, context_id = self.g_id ) return self.players
def remove_callback(self, id, callback_reference): self.iol.remove_timeout(timeout=self.callbacks[id][int(callback_reference)]) self.callbacks[id].pop(int(callback_reference)) log_handler.log( log_type = "INFO", log_code = "Callbacks", log_message = "Removed callback reference from self.callbacks["+str(id)+"]["+str(callback_reference)+"]", log_detail = 7 )
def remove_callback(self, id, callback_reference): self.iol.remove_timeout( timeout=self.callbacks[id][int(callback_reference)]) self.callbacks[id].pop(int(callback_reference)) log_handler.log( log_type="INFO", log_code="Callbacks", log_message="Removed callback reference from self.callbacks[" + str(id) + "][" + str(callback_reference) + "]", log_detail=7)
def change_state(self, state, msg=""): self.state = state self.save() data_dict = {} if state == "lobby": msg = "waiting for more players. " + msg if state == "ready": msg = "publishing ready info. " + msg if state == "waiting": msg = "waiting for event. " + msg if state == "new_event": new_event = self.get_event_queue()[0] msg = "new event starting: " + new_event.e_type + ". " + msg data_dict["event"] = new_event.e_type if state == "voting": msg = "Waiting 10s to collect votes. " + msg cur_event = self.get_event_queue()[0] data_dict["subjects"] = [] for player in self.get_groups(cur_event.subjects): data_dict["subjects"].append(player.as_JSON()) data_dict["e_type"] = cur_event.e_type data_dict["channel"] = "event info" for p_id in cur_event.instigators: publish_data("player:"+p_id, data_dict) if state == "finished_voting": msg = "Votes collected, performing result" if state == "game_finished": msg = msg + "These guys won: " for group in self.get_winners(): msg = msg + group + ", " msg = "-------"+msg.upper()+"-------" log_type = "INFO" log_code = "Game" log_message = msg log_detail = 2 context_id = self.g_id log_handler.log(log_type=log_type, log_code=log_code, log_message=log_message, log_detail=log_detail, context_id=context_id)
def leave_game(self): self.session['location'] = "outgame" self.location = "outgame" self.g_history.append(self.g_id) # this should really have a timestamp self.save() log_handler.log( log_type="INFO", log_code="Player", log_message="Updating player location to reflect leaving game", log_detail=6, context_id=self.p_id)
def leave_game(self): self.session['location'] = "outgame" self.location = "outgame" self.g_history.append(self.g_id) # this should really have a timestamp self.save() log_handler.log( log_type = "INFO", log_code = "Player", log_message = "Updating player location to reflect leaving game", log_detail = 6, context_id = self.p_id )
def join_game(self, g_id): self.location = "ingame" self.session['location'] = "ingame" self.g_id = g_id self.session['g_id'] = g_id self.save() log_handler.log( log_type="INFO", log_code="Player", log_message="Updating player location to reflect joining game", log_detail=6, context_id=self.p_id)
def lose_info(self, attribute_filter, p_id=None, info_player=None, lose_all=False): # error checking if not p_id and not info_player: raise ValueError("Either p_id or player needs to be supplied") if not p_id and info_player and not isinstance(info_player, Player): raise ValueError("supplied player argument must be a Player") if not p_id and info_player: p_id = info_player.p_id if p_id not in self.knows_about: warnings.warn("No info held on this character anyway") log_type = "WARNING" log_code = "Player" log_message = "Player does not have information on: " + p_id + ". This could be because you're iterating through a loop which removes information from both source and target (see game.end_game())" log_detail = 4 context_id = self.p_id log_handler.log(log_type=log_type, log_code=log_code, log_message=log_message, log_detail=log_detail, context_id=context_id) return # populate list with all keys if knows about everything (filter on None) if self.knows_about[p_id] is None: self.knows_about[p_id] = Player(p_id).broadcastable log_type = "INFO" log_message = "Player is losing p_id " + p_id + " from knows_about dict" context_id = self.p_id log_code = "Player" log_handler.log(log_type, log_code, log_message, context_id=context_id) if lose_all: log_type = "INFO" log_code = "Player" log_message = "Losing all information on p_id " + p_id log_detail = 4 context_id = self.p_id log_handler.log(log_type=log_type, log_code=log_code, log_message=log_message, log_detail=log_detail, context_id=context_id) self.knows_about.pop(p_id) else: for attribute in attribute_filter: if attribute not in self.broadcastable: raise ValueError("Attribute '"+attribute+"' is not a broadcastable attribute.") elif attribute in self.knows_about[p_id]: self.knows_about[p_id].remove(attribute) else: log_type = "INFO" log_code = "Player" log_message = "attribute: "+attribute+" was not found in this players knows_about list" log_detail = 4 context_id = self.p_id log_handler.log(log_type=log_type, log_code=log_code, log_message=log_message, log_detail=log_detail, context_id=context_id) self.save()
def join_game(self, g_id): self.location = "ingame" self.session['location'] = "ingame" self.g_id = g_id self.session['g_id'] = g_id self.save() log_handler.log( log_type = "INFO", log_code = "Player", log_message = "Updating player location to reflect joining game", log_detail = 6, context_id = self.p_id )
def __init__(self, p_id=None, session_key=None): self.name = "" self.g_history = [] # list of games just been in to assist matchmaking algorithm self.location = "" self.session_key = "" self.broadcastable = [] self.broadcastable.extend(["name", "location", "p_id"]) # look for p_id in session if session_key: self.session = SessionStore(session_key=session_key) self.session_key = session_key if 'p_id' in self.session: p_id = self.session['p_id'] try: # try loading from redis self.p_id = p_id self.load(p_id) except Exception as error: log_type = "ERROR" log_code = "Player" log_message = "Couldn't load user's p_id from redis, putting in default values: "+str(error) log_detail = 3 context_id = self.p_id log_handler.log(log_type=log_type, log_code=log_code, log_message=log_message, log_detail=log_detail, context_id=context_id) self.name = "Richard" self.p_id = str(uuid.uuid4()) self.location = "outgame" self.session = SessionStore(self.session_key) self.session_key = self.session.session_key self.session['p_id'] = self.p_id self.session['location'] = self.location self.session.save() self.save() log_type = "INFO" log_code = "Memory" log_message = "User initialised and saved" log_detail = 7 context_id = self.p_id log_handler.log(log_type=log_type, log_code=log_code, log_message=log_message, log_detail=log_detail, context_id=context_id)
def vote_result(g_id, e_id): parent_game = wwss.game.Game(g_id) voting_event = Event.load(g_id, e_id) callback_handler.remove_callback(voting_event.e_id, voting_event.voting_callback_reference) if voting_event.votes: p_id_most_common = Counter(voting_event.votes).most_common(1) log_type = "INFO" log_code = "Event" log_message = "Most common vote was" + p_id_most_common log_detail = 5 context_id = self.e_id log_handler.log( log_type=log_type, log_code=log_code, log_message=log_message, log_detail=log_detail, context_id=context_id, ) voting_event.result_subjects = [p_id_most_common] else: shuffle(voting_event.subjects) log_type = "INFO" log_code = "Event" log_message = "No votes were given, a random choice has been selected: " + voting_event.subjects[0] log_detail = 5 context_id = e_id log_handler.log( log_type=log_type, log_code=log_code, log_message=log_message, log_detail=log_detail, context_id=context_id, ) voting_event.result_subjects = [voting_event.subjects[0]] parent_game.change_state("finished_voting") voting_event.save() voting_event.finish_event() return
def add_callback(self, id, callback_handler): if id in self.callbacks: callback_reference = len(self.callbacks[id]) else: self.callbacks[id] = {} callback_reference = 0 self.callbacks[id][callback_reference] = callback_handler log_handler.log( log_type = "INFO", log_code = "Callbacks", log_message = "Added callback reference to self.callbacks["+str(id)+"]["+str(callback_reference)+"]", log_detail = 7, context_id = None ) return callback_reference
def end_game(self): # log game into Relational DB ## remove players from game #leaving_players = list(self.players) #for p_id in leaving_players: # self.remove_player(leaving_p_id=p_id) # ## delete game from redis #self.redis_cleanup() log_handler.log( log_type = "WARNING", log_code = "CurrentDev", log_message = "Game has been left in a finished state to enable debugging after the game has ended. ", log_detail = 2, context_id = self.g_id )
def add_callback(self, id, callback_handler): if id in self.callbacks: callback_reference = len(self.callbacks[id]) else: self.callbacks[id] = {} callback_reference = 0 self.callbacks[id][callback_reference] = callback_handler log_handler.log( log_type="INFO", log_code="Callbacks", log_message="Added callback reference to self.callbacks[" + str(id) + "][" + str(callback_reference) + "]", log_detail=7, context_id=None) return callback_reference
def vote_result(g_id, e_id): parent_game = wwss.game.Game(g_id) voting_event = Event.load(g_id, e_id) callback_handler.remove_callback( voting_event.e_id, voting_event.voting_callback_reference) if voting_event.votes: p_id_most_common = Counter(voting_event.votes).most_common(1) log_type = "INFO" log_code = "Event" log_message = "Most common vote was" + p_id_most_common log_detail = 5 context_id = self.e_id log_handler.log(log_type=log_type, log_code=log_code, log_message=log_message, log_detail=log_detail, context_id=context_id) voting_event.result_subjects = [p_id_most_common] else: shuffle(voting_event.subjects) log_type = "INFO" log_code = "Event" log_message = "No votes were given, a random choice has been selected: " + voting_event.subjects[ 0] log_detail = 5 context_id = e_id log_handler.log(log_type=log_type, log_code=log_code, log_message=log_message, log_detail=log_detail, context_id=context_id) voting_event.result_subjects = [voting_event.subjects[0]] parent_game.change_state("finished_voting") voting_event.save() voting_event.finish_event() return
def save(self): ww_redis_db.hset("g_list:"+self.g_id, "name", self.name) ww_redis_db.hset("g_list:"+self.g_id, "g_round", self.g_round) ww_redis_db.hset("g_list:"+self.g_id, "state", self.state) ww_redis_db.hset("g_list:"+self.g_id, "max_players", str(self.max_players)) ww_redis_db.hset("g_list:"+self.g_id, "witch_enabled", str(self.witch_enabled)) ww_redis_db.hset("g_list:"+self.g_id, "mystic_enabled", str(self.mystic_enabled)) players_string = cur_events_string = old_events_string = "" if self.players: players_string = "|".join(self.players) ww_redis_db.hset("g_list:"+self.g_id, "players", players_string) if self.event_queue: cur_events_string = "|".join(self.event_queue) ww_redis_db.hset("g_list:"+self.g_id, "event_queue", cur_events_string) if self.event_history: old_events_string = "|".join(self.event_history) ww_redis_db.hset("g_list:"+self.g_id, "event_history", old_events_string) if hasattr(self, 'redis_cleanup_callback_reference'): if self.redis_cleanup_callback_reference is not None: ww_redis_db.hset("g_list:"+self.g_id, "redis_cleanup_callback_reference", self.redis_cleanup_callback_reference) games_dict = {} data_dict = {} games_dict["json"] = self.as_JSON() data_dict["game"] = games_dict data_dict["channel"] = "game:"+self.g_id publish_data("game:"+self.g_id, data_dict) self.saved = True log_type = "INFO" log_code = "Memory" log_message = "Game has been saved." log_detail = 2 context_id = self.g_id log_handler.log(log_type=log_type, log_code=log_code, log_message=log_message, log_detail=log_detail, context_id=context_id)
def add_vote(g_id, e_id, p_id_vote, voting_by_p_id=None): voting_event = Event.load(g_id, e_id) parent_game = wwss.game.Game(g_id) log_type = "INFO" log_code = "Event" log_message = "A player just voted for " + p_id_vote log_detail = 7 context_id = self.e_id log_handler.log( log_type=log_type, log_code=log_code, log_message=log_message, log_detail=log_detail, context_id=context_id ) voting_event.votes.append(p_id_vote) if len(voting_event.votes) == len(voting_event.voters): parent_game.change_state("finished_voting") voting_event.vote_result()
def get_individuals(self, selection_pool, filters, expected_count=None): """ Removes players from the individuals list that doesn't match the filters. Returns a list of individuals that are filtered out of the selection pool """ # loop through Player objects and remove those that don't fit the group as an array if not selection_pool: selection_pool = self.get_players() individuals = selection_pool # start with all individuals in selection pool for filter in filters: if not individuals: return individuals # selecting based on player.state if filter in ("alive", "dead", "dying"): individuals = [player for player in individuals if player.state == filter] # selecting based on last event elif filter == "last_event": last_event = event.Event.load(self.g_id, self.event_history[0]) individuals = [player for player in individuals if player.p_id in last_event.result_subjects] # selecting based on Class type elif inspect.isclass(filter) and issubclass(filter, Character): individuals = [player for player in individuals if isinstance(player, filter)] # selecting based on uuid string elif isinstance(filter, str): if uuid.UUID(filter, version=4): individuals = [player for player in individuals if player.p_id == filter] log_type = "INFO" log_code = "Game" log_message = "found p_id in game, returning player object" log_detail = 5 context_id = self.g_id log_handler.log(log_type=log_type, log_code=log_code, log_message=log_message, log_detail=log_detail, context_id=context_id) return individuals
def get_groups(self, selectors, expected_count=None): """ Selectors is a list of lists. Each list should contribute to the overall returned group, and each item within the list is used to filter the selection pool. Filters can be strings, class objects, or uuids. Selectors is a list. Each list is used to retrieve individuals according to the filter list supplied within. This allows you to build up a group with different filters. The following selectors retrieve all the alive werewolves and all the dead witches. [["alive", Werewolf], ["dead", Witch]] """ selection_pool = self.get_players() # get all players in the game group_list = [] ############################################################ # Validate parameters ############################################################ if len(selectors) == 0: raise ValueError ("No selectors provided for this group call!") if (not isinstance(selectors[0], list)): selectors = [selectors] log_handler.log( log_type = "INFO", log_code = "Game", log_message = "Single selector given, converting to make compatible", log_detail = 6, context_id = self.g_id ) ############################################################ # Build up group using the filters ############################################################ for filters in selectors: selected_list = self.get_individuals(selection_pool=selection_pool, filters=filters) for player in selected_list: if player not in group_list: group_list.append(player) if expected_count and expected_count != len(group_list): raise AssertionError if expected_count == 1: return group_list[0] return group_list
def add_vote(g_id, e_id, p_id_vote, voting_by_p_id=None): voting_event = Event.load(g_id, e_id) parent_game = wwss.game.Game(g_id) log_type = "INFO" log_code = "Event" log_message = "A player just voted for " + p_id_vote log_detail = 7 context_id = self.e_id log_handler.log(log_type=log_type, log_code=log_code, log_message=log_message, log_detail=log_detail, context_id=context_id) voting_event.votes.append(p_id_vote) if len(voting_event.votes) == len(voting_event.voters): parent_game.change_state("finished_voting") voting_event.vote_result()
def redis_cleanup(self): # players might need updating just to be doubly sure!! if hasattr(self, 'redis_cleanup_callback_reference') and self.redis_cleanup_callback_reference: callback_handler.remove_callback(self.g_id, self.redis_cleanup_callback_reference) self.redis_cleanup_callback_reference = None ww_redis_db.delete("g_list:"+self.g_id) for e_id in self.event_history: ww_redis_db.delete("event:"+e_id) for e_id in self.event_queue: ww_redis_db.delete("event:"+e_id) log_handler.log( log_type = "INFO", log_code = "Redis", log_message = "Events and game has been removed from redis", log_detail = 3, context_id = self.g_id )
def archive_event(self, old_event_id): if old_event_id in self.event_queue: self.event_queue.remove(old_event_id) else: log_handler.log( log_type = "ERROR", log_code = "Game", log_message = "Tried to archive an event that wasn't found in the queue: "+old_event_id, log_detail = 3, context_id = self.g_id ) warnings.warn("Tried to archive an event that wasn't found in the queue: " + old_event_id) if old_event_id not in self.event_history: self.event_history.append(old_event_id) log_handler.log( log_type = "INFO", log_code = "Game", log_message = "Archived an event successful - " + old_event_id, log_detail = 5, context_id = self.g_id ) else: log_handler.log( log_type = "ERROR", log_code = "Game", log_message = "That event "+old_event_id+" was already in the event_history", log_detail = 3, context_id = self.g_id ) self.save()
def assign_roles(self): shuffle(self.players) temp_characters = [] werewolves_count = ceil(0.3*self.config['max_players']) if self.config['witch_enabled']: witch_count = ceil(0.1*self.config['max_players']) else: witch_count = 0 for x in range(werewolves_count): temp_characters.append(CharacterFactory.create_character("werewolf", p_id=self.players[x])) for x in range(werewolves_count, werewolves_count+witch_count): temp_characters.append(CharacterFactory.create_character("witch", p_id=self.players[x])) for x in range(werewolves_count+witch_count, len(self.players)): temp_characters.append(CharacterFactory.create_character("human", p_id=self.players[x])) for player in temp_characters: player.save() log_type = "INFO" log_code = "Player" log_message = "After assigning the roles, this player is " + player.character log_detail = 3 context_id = player.p_id log_handler.log(log_type=log_type, log_code=log_code, log_message=log_message, log_detail=log_detail, context_id=context_id) log_type = "INFO" log_message = "Character roles assigned" context_id = self.g_id log_code = "Game" log_handler.log(log_type, log_code, log_message, context_id=context_id)
def start(self): log_type = "INFO" log_code = "Event" log_message = "Subjects of the event: " + str( self.subjects) + ". Instigators are: " + str(self.instigators) log_detail = 3 context_id = self.e_id log_handler.log(log_type=log_type, log_code=log_code, log_message=log_message, log_detail=log_detail, context_id=context_id) if not self.subjects or not self.instigators: self.finish_event( ) # has a 3 second pause to allow async to catch up and prevent needless call stack preservation return parent_game = wwss.game.Game(self.g_id) parent_game.change_state("new_event") if len(self.subjects) > 1 or len(self.instigators) > 1: if len(self.subjects) != 1 and len(self.instigators) != 1: log_type = "INFO" log_code = "Event" log_message = "Multiple options are available and a vote must be held." log_detail = 5 context_id = self.e_id log_handler.log(log_type=log_type, log_code=log_code, log_message=log_message, log_detail=log_detail, context_id=context_id) self.hold_vote() if len(self.subjects) == 1: self.result_subjects = self.subjects log_type = "INFO" log_code = "Event" log_message = "Only one option found, beginning immediately." log_detail = 5 context_id = self.e_id log_handler.log(log_type=log_type, log_code=log_code, log_message=log_message, log_detail=log_detail, context_id=context_id) self.finish_event()
def start(self): log_type = "INFO" log_code = "Event" log_message = "Subjects of the event: " + str(self.subjects) + ". Instigators are: " + str(self.instigators) log_detail = 3 context_id = self.e_id log_handler.log( log_type=log_type, log_code=log_code, log_message=log_message, log_detail=log_detail, context_id=context_id ) if not self.subjects or not self.instigators: self.finish_event() # has a 3 second pause to allow async to catch up and prevent needless call stack preservation return parent_game = wwss.game.Game(self.g_id) parent_game.change_state("new_event") if len(self.subjects) > 1 or len(self.instigators) > 1: if len(self.subjects) != 1 and len(self.instigators) != 1: log_type = "INFO" log_code = "Event" log_message = "Multiple options are available and a vote must be held." log_detail = 5 context_id = self.e_id log_handler.log( log_type=log_type, log_code=log_code, log_message=log_message, log_detail=log_detail, context_id=context_id, ) self.hold_vote() if len(self.subjects) == 1: self.result_subjects = self.subjects log_type = "INFO" log_code = "Event" log_message = "Only one option found, beginning immediately." log_detail = 5 context_id = self.e_id log_handler.log( log_type=log_type, log_code=log_code, log_message=log_message, log_detail=log_detail, context_id=context_id, ) self.finish_event()
def check_event_queue(self): self.load(self.g_id) if self.state == "game_finished": self.end_game() # tidy up return if not self.event_queue: # add new event based on round log_type = "INFO" log_code = "Game" log_message = "No event in queue, adding day/night" log_detail = 5 context_id = self.g_id log_handler.log(log_type=log_type, log_code=log_code, log_message=log_message, log_detail=log_detail, context_id=context_id) self.g_round += 1 if self.g_round % 2: e_type = "night" else: e_type = "day" new_event = event.EventFactory.create_event(e_type, self.g_id, parent_game=self) self.add_event(new_event.e_id) self.change_state("waiting") #if self.state == "new_event" or self.state == "waiting": # work through queue if self.state == "waiting": log_type = "INFO" log_code = "Game" log_message = "event in the queue, we've been waiting to start" log_detail = 5 context_id = self.g_id log_handler.log(log_type=log_type, log_code=log_code, log_message=log_message, log_detail=log_detail, context_id=context_id) next_event = event.Event.load(self.g_id, self.event_queue[0]) next_event.start() else: # if self.state = "new_event" log_type = "INFO" log_code = "Game" log_message = "Event in progress, resetting callback but nothing changed" log_detail = 5 context_id = self.g_id log_handler.log(log_type=log_type, log_code=log_code, log_message=log_message, log_detail=log_detail, context_id=context_id) IOLoop.current().call_later(10, self.check_event_queue) # permits constant callback. BUG: never cleaned up as I'm not saving the handler to remove from iol.
def finish_event(self): parent_game = wwss.game.Game(self.g_id) log_type = "INFO" log_code = "Event" log_message = "Result subjects of the event:" + str( self.result_subjects) log_detail = 5 context_id = self.e_id log_handler.log(log_type=log_type, log_code=log_code, log_message=log_message, log_detail=log_detail, context_id=context_id) if self.result_subjects and ( self.instigators or self.action_without_instigators ): # only add to history if there is an effect parent_game.archive_event(self.e_id) log_handler.log(log_type="INFO", log_code="Event", log_message="Event has been archived", log_detail=3, context_id=self.e_id) else: parent_game.delete_event("event_queue", self.e_id) log_type = "INFO" log_code = "Event" log_message = "This event has been deleted from its game's event_queue" log_detail = 5 context_id = self.e_id log_handler.log(log_type=log_type, log_code=log_code, log_message=log_message, log_detail=log_detail, context_id=context_id) ww_redis_db.delete("event:" + self.e_id) for p_id in self.result_subjects: # new events queued will be in reverse order to the order they were added to subjects player = wwss.characters.CharacterFactory.create_character( character=None, p_id=p_id) result = self.action(player) if result and isinstance(result, Event): result = [ result ] # forces into array to allow multiple events by default if result and any( isinstance(e, Event) for e in result): # if result contains any events for event in result: [ parent_game.add_event(event.e_id, at_front=True) for event in result if isinstance(event, Event) ] # adds events with the same order they were returned if parent_game.get_winners(): parent_game.change_state("game_finished") else: parent_game.change_state("waiting")
def finish_event(self): parent_game = wwss.game.Game(self.g_id) log_type = "INFO" log_code = "Event" log_message = "Result subjects of the event:" + str(self.result_subjects) log_detail = 5 context_id = self.e_id log_handler.log( log_type=log_type, log_code=log_code, log_message=log_message, log_detail=log_detail, context_id=context_id ) if self.result_subjects and ( self.instigators or self.action_without_instigators ): # only add to history if there is an effect parent_game.archive_event(self.e_id) log_handler.log( log_type="INFO", log_code="Event", log_message="Event has been archived", log_detail=3, context_id=self.e_id, ) else: parent_game.delete_event("event_queue", self.e_id) log_type = "INFO" log_code = "Event" log_message = "This event has been deleted from its game's event_queue" log_detail = 5 context_id = self.e_id log_handler.log( log_type=log_type, log_code=log_code, log_message=log_message, log_detail=log_detail, context_id=context_id, ) ww_redis_db.delete("event:" + self.e_id) for ( p_id ) in ( self.result_subjects ): # new events queued will be in reverse order to the order they were added to subjects player = wwss.characters.CharacterFactory.create_character(character=None, p_id=p_id) result = self.action(player) if result and isinstance(result, Event): result = [result] # forces into array to allow multiple events by default if result and any(isinstance(e, Event) for e in result): # if result contains any events for event in result: [ parent_game.add_event(event.e_id, at_front=True) for event in result if isinstance(event, Event) ] # adds events with the same order they were returned if parent_game.get_winners(): parent_game.change_state("game_finished") else: parent_game.change_state("waiting")
def lose_info(self, attribute_filter, p_id=None, info_player=None, lose_all=False): # error checking if not p_id and not info_player: raise ValueError("Either p_id or player needs to be supplied") if not p_id and info_player and not isinstance(info_player, Player): raise ValueError("supplied player argument must be a Player") if not p_id and info_player: p_id = info_player.p_id if p_id not in self.knows_about: warnings.warn("No info held on this character anyway") log_type = "WARNING" log_code = "Player" log_message = "Player does not have information on: " + p_id + ". This could be because you're iterating through a loop which removes information from both source and target (see game.end_game())" log_detail = 4 context_id = self.p_id log_handler.log(log_type=log_type, log_code=log_code, log_message=log_message, log_detail=log_detail, context_id=context_id) return # populate list with all keys if knows about everything (filter on None) if self.knows_about[p_id] is None: self.knows_about[p_id] = Player(p_id).broadcastable log_type = "INFO" log_message = "Player is losing p_id " + p_id + " from knows_about dict" context_id = self.p_id log_code = "Player" log_handler.log(log_type, log_code, log_message, context_id=context_id) if lose_all: log_type = "INFO" log_code = "Player" log_message = "Losing all information on p_id " + p_id log_detail = 4 context_id = self.p_id log_handler.log(log_type=log_type, log_code=log_code, log_message=log_message, log_detail=log_detail, context_id=context_id) self.knows_about.pop(p_id) else: for attribute in attribute_filter: if attribute not in self.broadcastable: raise ValueError("Attribute '" + attribute + "' is not a broadcastable attribute.") elif attribute in self.knows_about[p_id]: self.knows_about[p_id].remove(attribute) else: log_type = "INFO" log_code = "Player" log_message = "attribute: " + attribute + " was not found in this players knows_about list" log_detail = 4 context_id = self.p_id log_handler.log(log_type=log_type, log_code=log_code, log_message=log_message, log_detail=log_detail, context_id=context_id) self.save()
def __init__(self, g_id=None, session_key=None, config={}): self.state = "" # used to track the current operation or state of the game self.players = [] # list of Players self.event_queue = [] # events waiting to happen. Triggered based on Game.state self.event_history = [] # past events self.config = Game.default_config # default inputs potentitally contributed to by a player if g_id is None and session_key is not None: session_data = SessionStore(session_key=session_key) if 'g_id' in session_data: g_id = session_data['g_id'] try: self.load(g_id) except ValueError as error: log_handler.log( log_type = "ERROR", log_code = "Game", log_message = "Creating default game values because: " + str(error), log_detail = 4, context_id = g_id ) ############################################################################################################ # Default variables ############################################################################################################ self.g_id = str(uuid.uuid4()) self.g_round = 0 self.state = "matchmaking" ############################################################################################################ # Verify config ############################################################################################################ if config: try: ######################################################## # Convert from string data types ######################################################## if 'max_players' in config and config['max_players']: config['max_players'] = int(config['max_players']) if 'witch_enabled' in config and config['witch_enabled']: config['witch_enabled'] = {"true": True, "false": False}.get(config['witch_enabled'].lower()) if 'mystic_enabled' in config and config['mystic_enabled']: config['mystic_enabled'] = {"true": True, "false": False}.get(config['mystic_enabled'].lower()) ######################################################## # Validate/repair contents ######################################################## validated_config = dict(config) invalid_configs = {} for key, value in config.items(): if key not in self.config or value == None: invalid_configs[key] = value del validated_config[key] if invalid_configs: raise ContinueError("Some parameters were changed", error_dict=invalid_configs) ######################################################## # Error handling game config ######################################################## except ValueError as e: validated_config = {} log_handler.log( log_type = "ERROR", log_code = "Client", log_message = "Could not create game config dict, using default: " + str(e), log_detail = 3 ) except ContinueError as e: log_handler.log( log_type = "ERROR", log_code = "Client", log_message = "Repaired or removed errors: " + str(e) +" "+str(e.error_dict.items()), log_detail = 3 ) ######################################################## # Update config ######################################################## for key, value in validated_config.items(): if key in self.config: self.config[key] = value log_handler.log( log_type = "INFO", log_code = "Game", log_message = "Added (key)"+key+" to game as (value)"+str(value), log_detail = 8 ) else: log_handler.log( log_type = "ERROR", log_code = "Game", log_message = "Invalid configuration for the game: " + str(config.items()), log_detail = 3 ) raise ValueError("Invalid configuration for the game") # shouldn't ever error as it should've been cleared up earlier log_handler.log( log_type = "INFO", log_code = "Game", log_message = "Loaded game config: "+str(self.config.items()), log_detail = 5, ) ############################################################ # Apply config ############################################################ self.name = self.config['name'] self.max_players = self.config['max_players'] self.witch_enabled = self.config['witch_enabled'] self.mystic_enabled = self.config['mystic_enabled'] ################################################################################################################ # Variables independent of redis and defaults ################################################################################################################ self.saved = False # enables chain editing - redundant? self.iol = IOLoop.current() # main tornado loop uses for callbacks. SHOULD NOT BE USED! log_handler.log( log_type = "INFO", log_code = "Memory", log_message = "Game initialised", log_detail = 4, context_id = self.g_id )
def remove_player(self, leaving_p_id=None, leaving_player=None): try: if leaving_p_id: leaving_player = self.get_groups([leaving_p_id], expected_count=1) log_handler.log( log_type = "WARNING", log_code = "Game", log_message = "If iterating through and deleting players, you must shallow copy it first!", log_detail = 10, context_id = self.g_id ) else: leaving_p_id = leaving_player.p_id leaving_player = self.get_groups([leaving_p_id], expected_count=1) except AssertionError: log_handler.log( log_type = "ERROR", log_code = "Game", log_message = "Couldn't find exactly one player with p_id "+leaving_p_id+" in this game", log_detail = 3, context_id = self.g_id ) return for ingame_player in self.get_players(): if leaving_player != ingame_player: leaving_player.lose_info(None, info_player=ingame_player, lose_all=True) ingame_player.lose_info(None, info_player=leaving_player, lose_all=True) while leaving_p_id in self.players: self.players.remove(leaving_p_id) leaving_player.leave_game() log_handler.log( log_type = "INFO", log_code = "Game", log_message = "Player ("+leaving_p_id+") has beem removed from this game", log_detail = 5, context_id = self.g_id ) log_handler.log( log_type = "INFO", log_code = "Player", log_message = "This player has left the game ("+self.g_id+")", log_detail = 4, context_id = leaving_p_id ) # if there are no players left, initiate countdown for redis cleanup, unless there's already a callback for its deletion if len(self.players) == 0 and not hasattr(self, 'redis_cleanup_callback_reference') or (hasattr(self, 'redis_cleanup_callback_reference') and not self.redis_cleanup_callback_reference): callback_handle = IOLoop.current().call_later(10, self.redis_cleanup) # self works here because the data the cleanup needs should not change self.redis_cleanup_callback_reference = callback_handler.add_callback(self.g_id, callback_handle) log_handler.log( log_type = "INFO", log_code = "Redis", log_message = "Initiating game deletion in 10 seconds", log_detail = 5, context_id = self.g_id ) self.save()
def add_player(self, joining_p_id=None, joining_player=None): ############################################################ # Initialise player object ############################################################ if joining_p_id: joining_player = user.Player(p_id=joining_p_id) elif joining_player: joining_p_id = joining_player.p_id else: raise ValueError ############################################################ # Return immediately if the player is already in a game ############################################################ if joining_player.is_ingame(): log_handler.log( log_type = "ERROR.", log_code = "Player", log_message = "Player: " + joining_player.p_id + "is already in a game: "+ joining_player.g_id, log_detail = 4, context_id = self.g_id ) return ############################################################ # If game was empty and has been scheduled for cleanup, # remove the cleanup callback ############################################################ if hasattr(self, 'redis_cleanup_callback_reference') and self.redis_cleanup_callback_reference: callback_handler.remove_callback(self.g_id, self.redis_cleanup_callback_reference) self.redis_cleanup_callback_reference = None log_handler.log( log_type = "INFO", log_code = "Callbacks", log_message = "Preventing game deletion", log_detail = 5, context_id = self.g_id ) if joining_p_id not in self.players: self.players.append(joining_p_id) self.save() # all changes to the game must be immediately saved! otherwise due to call stacks, this would overwrite the new changes. You could pass by reference, but probably better to call load from redis again ############################################################ # Update known information for all players in the game ############################################################ for ingame_player in self.get_players(): if joining_player != ingame_player: # give ingame player information about joining player ingame_player.gain_info(['p_id', 'name'], info_player=joining_player) # give joining player information about ingame_players joining_player.gain_info(['p_id', 'name'], info_player=ingame_player) ############################################################ # Save changes for player ############################################################ joining_player.join_game(self.g_id) log_handler.log( log_type = "INFO", log_code = "Game", log_message = "Player ("+joining_p_id+") has been added to the game", log_detail = 5, context_id = self.g_id ) ############################################################ # Check game if game is full, if so begin ############################################################ if len(self.players) >= self.config['max_players']: log_handler.log( log_type = "INFO", log_code = "Game", log_message = "Game full, starting now", log_detail = 2, context_id = self.g_id ) self.change_state("ready", "starting game in 3 secs") IOLoop.current().call_later(3, self.start_game) # for production/give a delay self.save()