Esempio n. 1
0
    def Join(self, player):
        if self.IsFull:
            # can't really be hit by normal users, this can only get hit by trying to add agents
            LogUtility.Warning(
                f"'{player.Name}' cannot join the game, the game is full!",
                self)
            return

        # TODO: This is kind of nasty and we should probably have a server player
        # and a game instance player entity, but this will do as a first pass. This
        # implies that the server is a "common world" instead of having a meta-player identity
        player._Player__isReady = False
        self.__players.add(player)
        LogUtility.CreateGameMessage(f"'{player.Name}' has joined.", self)
        return
Esempio n. 2
0
    def Vote(self, vote):
        if not vote:
            LogUtility.Error("Vote cannot be casted, it is null.", self)
            return

        if self.TimeOfDay == TimeOfDayEnum.Day:
            self.VoteDay(vote)
            return

        if self.TimeOfDay == TimeOfDayEnum.Night:
            self.VoteNight(vote)
            return

        LogUtility.Error("Time of day is not Day/Night", self)
        return
Esempio n. 3
0
    def Run(self):
        try:
            self.__connection.bind(NetConstants.ADDRESS);
            self.__connection.listen();

            LogUtility.Information(f"Server successfully started at {NetConstants.IP}:{NetConstants.PORT}");

            self.ShowActiveConnections();
        except socket.error as error:
            trace = traceback.format_exc();
            LogUtility.Error(str(error) + "\n\n" + trace);

        while True:
            connection, address = self.__connection.accept();
            threading.Thread(target = self.ClientHandle, args = (connection, address)).start();
        return;
Esempio n. 4
0
    def VoteNight(self, vote):
        alivePlayers = [ap for ap in self.Players if ap.IsAlive]
        playerIdentifiers = [p.Identifier for p in alivePlayers]

        if not vote.Player.Identifier in self.__playerIdentifiersThatCanVote:
            playerDetails = "'" + vote.Player.Name + "' - " + vote.Player.Identifier
            LogUtility.Error(f"{playerDetails} cannot act in the night.", self)
            return VoteResultTypeEnum.InvalidAction

        if vote.VotedPlayer\
            and not vote.VotedPlayer.Identifier in playerIdentifiers\
            and not vote.Player.Role.CanTargetDeadPlayers:

            # the seer can check dead players
            playerDetails = vote.VotedPlayer.Name + " - " + vote.VotedPlayer.Identifier
            LogUtility.Error(
                f"Invalid vote, target player {playerDetails} is not in the game",
                self)
            return VoteResultTypeEnum.DeadPlayerTargeted

        player = self.GetPlayerByIdentifier(vote.Player.Identifier)
        targetPlayer = self.GetPlayerByIdentifier(
            vote.VotedPlayer.Identifier) if vote.VotedPlayer else None

        voteResult = VoteResultTypeEnum.WaitAction

        if vote.PlayerType == PlayerTypeEnum.Werewolf:
            voteResult = self.Attack(player, targetPlayer)
        elif vote.PlayerType == PlayerTypeEnum.Seer:
            voteResult = self.Divine(player, targetPlayer)
        elif vote.PlayerType == PlayerTypeEnum.Guard:
            voteResult = self.Guard(player, targetPlayer)
        else:
            # I know this should semantically be before the actual addition of
            # the vote. However, we rely on the previous security checks
            LogUtility.Error(
                f"'{player.Name}' does not have a valid night role - {player.Role.Type}",
                self)
            return VoteResultTypeEnum.CannotActDuringTimeOfDay

        self.Votes.add(vote)
        self.__playerIdentifiersThatCanVote.remove(player.Identifier)

        if not self.__playerIdentifiersThatCanVote:
            self.CountNightVotesAndEvents()

        return voteResult
Esempio n. 5
0
def DoVillagersWin(game):
    werewolves = [w for w in game.Players if w.IsAlive\
        and w.Role.Type == PlayerTypeEnum.Werewolf]

    if werewolves:
        return False;

    # if all werewolves are dead, the villagers win
    LogUtility.CreateGameMessage("\n\n\n\t\t\tVillagers win!\n\n", game);
    return True;
Esempio n. 6
0
    def Connect(self, connection, packet):
        dto = packet.Data;

        player = Player(dto.ClientName, dto.ClientIdentifier);

        connectionKey = connection.getpeername();
        self.Connections[connectionKey] = player;

        LogUtility.Information(f"Connected to server (player {player}) - {connectionKey}");
        self.ShowActiveConnections();

        connection.sendall(pickle.dumps(player));

        return;
Esempio n. 7
0
    def ClientHandle(self, connection, address):
        while True:
            try:
                packetStream = connection.recv(4 * NetConstants.KILOBYTE);

                if not packetStream:
                    # connection is interrupted/closed by client because we get null back
                    break;

                packet = pickle.loads(packetStream);

                if not packet or not packet.PacketType:
                    # This is really just a sanity check and making sure nothing
                    # unknown is coming that could potentially break the server
                    break;

                LogUtility.Request(f"Packet type - {packet.PacketType}");

                if packet.PacketType in self.ValidPacketTypes:
                    self.HandlerContext.RedirectPacket(connection, packet);
                else:
                    clientKey = connection.getpeername();
                    client = self.Connections[clientKey];
                    # this has caught me off a few times
                    LogUtility.Error(f"Packet type is not supported - {packet.PacketType}, " +\
                        f"sent from {client.Name} - {clientKey}. Add to valid packet types!");
                    connection.sendall(pickle.dumps(False));

            except Exception as error:
                trace = traceback.format_exc();
                LogUtility.Error(str(error) + "\n\n" + trace);
                break;

        self.Disconnect(connection);

        return;
Esempio n. 8
0
    def __init__(self):
        self.__connection = socket.socket(socket.AF_INET, socket.SOCK_STREAM);
        self.__handlerContext = HandlerContext(self);
        self.__validPacketTypes = PacketTypeEnum.Values();
        self.__connections = dict();
        self.__games = dict();

        self.__handlerContext.CreateGame("Game Alpha");
        self.__handlerContext.CreateGame("Game Beta");
        self.__handlerContext.CreateGame("Game Gamma");
        self.__handlerContext.CreateGame("Game Delta");

        LogUtility.Information("Server start up");

        return;
Esempio n. 9
0
def DoWerewolvesWin(game):
    werewolves = [w for w in game.Players if w.IsAlive\
        and w.Role.Type == PlayerTypeEnum.Werewolf];

    villagers = [w for w in game.Players if w.IsAlive\
        and w.Role.Type != PlayerTypeEnum.Werewolf];

    # this just makes debugging easier since this gets called many times
    if len(werewolves) < len(villagers):
        return False;

    werewolfNames = "\n\t\t\t  "+ "\n\t\t\t  "\
        .join([w.Name for w in game.Players if w.Role.Type == PlayerTypeEnum.Werewolf]);

    LogUtility.CreateGameMessage(f"\n\n\n\t\t\tWerewolves win!{werewolfNames}\n\n", game);
    return True;
Esempio n. 10
0
    def CheckWinCondition(self):
        if not self.HasStarted:
            return (False, FactionTypeEnum._None)

        if len(self.Players) < GameConstants.MINIMAL_PLAYER_COUNT:
            LogUtility.CreateGameMessage(
                f"Minimum of {GameConstants.MINIMAL_PLAYER_COUNT} players required.",
                self)
            return (True, FactionTypeEnum._None)

        # this will be defined in GameRules.py
        if GameRules.DoVillagersWin(self):
            return (True, FactionTypeEnum.Villagers)

        if GameRules.DoWerewolvesWin(self):
            return (True, FactionTypeEnum.Werewolves)

        return (False, FactionTypeEnum._None)
Esempio n. 11
0
    def VotePlayer(self, connection, packet):
        dto = packet.Data
        game = self.HandlerContext.GetGameWithIdentifier(dto.GameIdentifier)

        if not self.HandlerContext.IsGameActionValid(game, dto):
            connection.sendall(pickle.dumps(False))

        player = game.GetPlayerByIdentifier(dto.Player.Identifier)
        targetPlayer = game.GetPlayerByIdentifier(dto.TargetPlayerIdentifier)

        if game.HasPlayerVotedAlready(player.Identifier):
            LogUtility.Error(
                f"'{player.Name}' - {player.Identifier} has voted already",
                game)
            connection.sendall(pickle.dumps(False))
            return

        vote = Vote(player, targetPlayer)
        game.Vote(vote)
        connection.sendall(pickle.dumps(True))
        return
Esempio n. 12
0
    def Disconnect(self, connection):
        connectionKey = connection.getpeername();
        player = None;

        if connectionKey in self.Connections.keys():
            player = self.Connections[connectionKey];
            gameIdentifier = self.HandlerContext.IsPlayerAlreadyInAGame(player);

            if gameIdentifier:
                game = self.HandlerContext.GetGameWithIdentifier(gameIdentifier);
                game.Leave(player);

            self.Connections.pop(connectionKey);

        LogUtility.Information(f"Lost connection (player {player}) to server - {connectionKey}");
        connection.shutdown(socket.SHUT_RDWR);
        connection.close();

        self.ShowActiveConnections();

        return;
Esempio n. 13
0
    def Whisper(self, connection, packet):
        messageRequestDto = packet.Data

        if not messageRequestDto or not messageRequestDto.IsValid:
            connection.sendall(pickle.dumps(False))
            return

        game = self.HandlerContext.GetGameWithIdentifier(
            messageRequestDto.GameIdentifier)
        talkMessage = messageRequestDto.TalkMessage

        player = game.GetPlayerByIdentifier(messageRequestDto.PlayerIdentifier)
        targetPlayer = None

        if messageRequestDto.TargetPlayerIdentifier:
            targetPlayer = game.GetPlayerByIdentifier(
                messageRequestDto.TargetPlayerIdentifier)

        messageMetaDataDto = MessageMetaDataDto(\
            messageRequestDto.PlayerIdentifier,\
            messageRequestDto.TargetPlayerIdentifier,\
            messageRequestDto.TargetPlayerRole,\
            talkMessage.MessageType)

        messageText = self.ConstructMessageText(\
            talkMessage,\
            targetPlayer,\
            messageMetaDataDto.TargetRole)

        # this is automatically added to the game, no need to serialize
        # it and send it back the game loop will handle all of the magic
        messageDto = LogUtility.CreateWhisperGameMessage(\
            player,\
            messageText,\
            messageMetaDataDto,\
            game,\
            player.Role.Type)

        connection.sendall(pickle.dumps(True))
        return
Esempio n. 14
0
    def Start(self):
        if (len(self.__players) < GameConstants.MINIMAL_PLAYER_COUNT):
            LogUtility.Error(
                f"Cannot start game without having at least {GameConstants.MINIMAL_PLAYER_COUNT} players.",
                self)
            return

        for player in self.__players:
            player._Player__isAlive = True
            player._Player__isReady = False

        GameRules.DistributeRolesBaseGame(self)

        self.__hasStarted = True
        self.__turn = 0

        for agent in self.AgentPlayers:
            agent.PreGameSetup()

        self.StartDay()

        return
Esempio n. 15
0
    def Restart(self):
        LogUtility.CreateGameMessage("Restarting game lobby", self)

        for player in self.__players:
            player._Player__isAlive = True
            player._Player__isReady = False
            player._Player__role = None

        for agent in self.AgentPlayers:
            agent._Player__isReady = True

        self.__hasStarted = False
        self.__votes = set()

        messageCutOffPoint = datetime.datetime.utcnow() -\
           datetime.timedelta(minutes = 1)

        self.__messages = {
            m
            for m in self.__messages if m.TimeUtc > messageCutOffPoint
        }

        self.__turn = int()
        self.__timeOfDay = TimeOfDayEnum._None
Esempio n. 16
0
    def CountNightVotesAndEvents(self):
        # remove the "wait" calls
        actualVotes = [v for v in self.Votes if v.VotedPlayer]

        # actual votes
        votesToKill = [
            v for v in actualVotes if v.PlayerType == PlayerTypeEnum.Werewolf
        ]

        # independent actions
        votesToGuard = [
            v for v in actualVotes if v.PlayerType == PlayerTypeEnum.Guard
        ]
        votesToDivine = [
            v for v in actualVotes if v.PlayerType == PlayerTypeEnum.Seer
        ]

        (playerToKill,
         werewolfAttackTimes) = self.GetPlayerAndTimesVoted(votesToKill)

        if not playerToKill or not werewolfAttackTimes:
            LogUtility.CreateGameMessage(
                f"No one gets attacked during the night.", self)
        else:
            # we only want to make the fact known that someone was guarded if an attack on them
            # had occurred during the night. Otherwise it would advantage the werewolves.
            guardsForAttackedPlayer = [v.Player for v in votesToGuard \
                if playerToKill.Identifier == v.VotedPlayer.Identifier]

            if not guardsForAttackedPlayer:
                self.WerewolfKill(playerToKill)
            else:
                LogUtility.CreateGameMessage("'" + playerToKill.Name +\
                    "' was attacked by werewolves in the night but was guarded and lives to see another day.", self)

        for vote in votesToDivine:
            divinedPlayer = vote.VotedPlayer
            isDivinedPlayerWereolf = divinedPlayer.Role.Type == PlayerTypeEnum.Werewolf
            negator = str() if isDivinedPlayerWereolf else "NOT"

            message = f"'{divinedPlayer.Name}' was divined, they are {negator} a werewolf."
            LogUtility.CreatePrivateGameMessage(message, self,
                                                vote.Player.Identifier)

            pass

        # Get votes for seer (these are independent from everything else)

        gameIsOver, winningFaction = self.CheckWinCondition()

        if gameIsOver:
            for agent in self.AgentPlayers:
                agent.PreGameSetup()

            if self.__agentsAutomaticallyPlay:
                # don't go to other turn and don't start the day if the game is over
                self.Restart()
            return

        self.StartDay()

        return
Esempio n. 17
0
    def GetGameWithIdentifier(self, gameIdentifier):
        if not gameIdentifier in self.Server.Games.keys():
            LogUtility.Error(f"Game {gameIdentifier} does not exist.");
            return None;

        return self.Server.Games[gameIdentifier];
Esempio n. 18
0
 def ShowActiveConnections(self):
     LogUtility.Information(f"Active connections - {len(self.__connections)}");
Esempio n. 19
0
 def ShowTurnAndTime(self):
     capitalGameTime = str(self.TimeOfDay).capitalize()
     LogUtility.CreateGameMessage(
         f"\n\n\n\t\tTurn: {self.Turn}\t\tTime: {capitalGameTime}\n\n",
         self)
     return