def get_own_area(country_id, timestamp=None):
    """
    領地を取得
    :param country_id:
    :return: 成功 ? area : None
    """

    try:
        db = DBSingleton()

        where = "country_id = " + str(country_id)

        # タイムスタンプによる条件
        if timestamp is not None:
            datetime = BJTime.encode_to_sql(timestamp)
            where += " and timestamp >= \"" + datetime + "\""

        result = db.select("col",
                           "row",
                           "type",
                           "country_id",
                           "food",
                           "money",
                           table="hex_grid",
                           where=where)
        if len(result) == 0:
            return None
        else:
            return result

    except DBError as e:
        logging.error(e.message, detailed_error.get_error())
        return None
Ejemplo n.º 2
0
def create_event(datetime, object, subject, event_name, data, status="ready"):
    """
    イベントレコードを生成するヘルパー
    イベントレコードの形式を強要する
    :param datetime: 予定時間(必須)
    :param object: 主体
    :param subject: 対象
    :param event_name: イベントの種類名(必須)
    :param data: イベント用のデータ(str)
    :param status: レコードの状態
    :return: 成功 ? event_record{datetime, ...} : None
    """

    event_record = {
        "datetime": BJTime.encode_to_sql(datetime),
        "object": str(object),
        "subject": str(subject),
        "event_name": str(event_name),
        "data": str(data),
        "status": str(status)
    }

    if datetime is None or event_name is None:
        logging.error(event_record)
        return None

    return event_record
def get_unvisible_area(visibility, timestamp=None):
    """
    可視権から不可視範囲のグリッドを取得
    :param visibility:
    :param [timestamp]: オプション。この時刻オブジェクト以降に変更されたグリッドのみ取得する
    :return: list[dict[col, row, type]]
    """
    result = None
    try:
        # 可視権による条件
        where = str(visibility) + " = 0"

        # タイムスタンプによる条件
        if timestamp is not None:
            datetime = BJTime.encode_to_sql(timestamp)
            where += " and timestamp >= \"" + datetime + "\""

        db = DBSingleton()
        result = db.select("col", "row", "type", table="hex_grid", where=where)
    except DBError as e:
        logging.error(e.message)
        raise Exception("DBエラー : visibilityが" + str(visibility) +
                        u"の不可視範囲のグリッド取得失敗")
        return

    return result
Ejemplo n.º 4
0
    def __start_new_year(cls):
        u"""新年度をスタートさせる"""

        # 情勢読み込み
        cls._game_settings["affair"].init()

        # マップ作製
        cls._game_settings["map"].init()

        # TODO : この位置で開始させると他のイベントが入るまでcurrent_event_timeがNoneでループ回らない
        # 内政イベントチェーン開始
        from lib.event.Sched.SchedDomestic import SchedEventDomestic
        from lib.DB import event_controller
        next_time = BJTime.add_time(BJTime.get_time_now(), 1000 * 10)
        event = SchedEventDomestic.create_recode(next_time)  # 10秒後に開始
        event_controller.add_event(event)
        GameMain.set_event_timestamp(next_time)
Ejemplo n.º 5
0
    def loop(cls):
        u"""イベントループ
            スケジューラが空の時、スレッドはConditionでブロックされ、新しいスケジュールが追加されるまで待機する"""

        cls._logger.debug("loop() started")

        # イベントループ本体
        while True:

            time.sleep(0.1)

            # 待機中のイベントがなければスキップ
            if cls._current_event_timestamp is None:
                continue

            # 最速のイベントの実行時間が過ぎていれば実行
            if cls._current_event_timestamp <= BJTime.get_time_now():

                # 予定時間が一番早いイベントをDBから参照
                current_event_record = event_controller.peek_event()

                # 待機中のイベントがなければスキップ
                if current_event_record is None:
                    continue

                # イベントレコードを削除
                event_controller.remove_event(current_event_record["event_id"])

                # イベントディスパッチ処理
                event = None
                if current_event_record["event_name"] == "move":
                    event = EventMove(current_event_record)
                elif current_event_record["event_name"] == "domestic":
                    event = EventDomestic(current_event_record)
                elif current_event_record["event_name"] == "sched_domestic":
                    event = SchedDomestic.SchedEventDomestic(
                        current_event_record)

                try:
                    event.run()
                except NotImplementedError:
                    cls._logger.error("実装されていないイベントが呼び出された",
                                      detailed_error.get_error())
                except Exception as e:
                    cls._logger.error(e, detailed_error.get_error())

                # 次に待機しているイベントがあれば予定時間をセット
                next_event_record = event_controller.peek_event()
                print("next_event_record=", next_event_record)
                if next_event_record is not None:
                    cls._current_event_timestamp = next_event_record[
                        "datetime"]
                else:
                    cls.set_event_timestamp(None)

        cls._logger.info("loop() ended")
def update_user_before_move(user_id, wait_untill):
    """
    移動に伴うユーザー情報の更新
    :param user_id:
    :param wait_untill: 到着予定時刻を表す時刻オブジェクト
    :param status: 移動中の状態
    :return: 更新成功 ?  : Exception
    """

    try:
        db = DBSingleton()
        query = "update user set"  + \
                " wait_untill = \"" +  BJTime.encode_to_sql(wait_untill)+ "\"" \
                " where user_id = \"" + str(user_id) + "\""
        result = db.exec(query)

    except DBError as e:
        logging.error(e.message)
        raise Exception("DBエラー : 移動前のユーザー情報更新失敗")
Ejemplo n.º 7
0
    def run(self):
        """
        内政をする
        :return: 成功 ? True : False
        """

        self._logger.debug("enter EventDomestic run")
        self._logger.debug("event_record=", self._event_record)

        # 例外処理用
        user_id = None
        division_id = None
        try:
            # デコード
            if self._decode_recode() is False:
                raise Exception("デコード失敗")
                return False

            # プレイヤー、部隊情報
            player_info = player_controller.get_playerinfo_by_id(self._user_id)
            division_info = division_controller.get_division_info(
                self._division_id)

            # 内政中でないならキャンセル(本来はイベントレコード自体がキャンセルされるべき
            if player_info["status"] != "domestic":
                raise Exception("プレイヤーの状態がdomesticではない")
                return

            # 移動中でないならキャンセル(本来はイベントレコード自体がキャンセルされるべき)
            if division_info["status"] != "domestic":
                raise Exception("部隊の状態がdomesticではない")
                return

            # エラーに備えて本処理前にプレイヤーと部隊の状態を初期化する
            player_controller.update_user_status(self._user_id, "ready")
            division_controller.update_division_status(self._division_id,
                                                       "ready")

            # 現在時刻
            now = BJTime.get_time_now()

            # ヘックス情報
            hex = hexgrid_controller.get_hexinfo(self._domestic_col,
                                                 self._domestic_row)

            # 対象ヘックスの国籍が変わっていれば失敗
            if hex["country_id"] != player_info["country_id"]:
                BJSocketHandler.BJSocketHandler.send_member_by_id(
                    player_info["user_id"], {
                        "event": "cancel",
                        "data": {
                            "title": "内政キャンセル",
                            "reason": "対象ヘックスの国籍が違う"
                        }
                    })
                return

            # 内政実行
            # TODO : ステータスなどに合わせた変化

            gm = GameMain.GameMain()
            message = "(" + str(self._domestic_col) + "," + str(
                self._domestic_row) + ")\n "  # 結果メッセージ

            isSuccessed = False

            # 最大値増量
            if self._type == "foster_food":
                new_food = gm.get_affair().get_op_food_lv() + hex["food"]
                if not hexgrid_controller.update_hex(
                        hex["col"], hex["row"], food=new_food):
                    message = message + "エラー。農業生産失敗"
                else:
                    message = message + "農業生産 " + str(
                        hex["food"]) + " → " + str(new_food)
                    isSuccessed = True
            elif self._type == "foster_money":
                new_money = gm.get_affair().get_op_food_lv() + hex["money"]
                if not hexgrid_controller.update_hex(
                        hex["col"], hex["row"], money=new_money):
                    message = message + "エラー。商業生産失敗"
                else:
                    message = message + "商業生産 " + str(
                        hex["money"]) + " → " + str(new_money)
                    isSuccessed = True

            # 徴税
            elif self._type == "get_food":
                new_food = hex["food"] + division_info["food"]
                if not division_controller.update_division(
                        division_info["division_id"], food=new_food):
                    message = message + "エラー。農業徴税失敗"
                else:
                    message = message + "領地から" + str(
                        hex["food"]) + "の食糧を部隊に徴税した"
                    isSuccessed = True
            elif self._type == "get_money":
                new_money = hex["money"] + division_info["money"]
                if not division_controller.update_division(
                        division_info["division_id"], money=new_money):
                    message = message + "エラー。商業徴税失敗"
                else:
                    message = message + "領地から" + str(
                        hex["money"]) + "の資金を部隊に徴税した"
                    isSuccessed = True

            # 内政に失敗していればメッセージ送信
            if not isSuccessed:
                BJSocketHandler.BJSocketHandler.send_member_by_id(
                    player_info["user_id"], {
                        "event": "cancel",
                        "data": {
                            "title": "内政失敗",
                            "reason": "不明な内政種類"
                        }
                    })
                return

            # 内政に成功。結果をプレイヤーに通知
            BJSocketHandler.BJSocketHandler.send_member_by_id(
                player_info["user_id"], {
                    "event": "notify",
                    "data": {
                        "title": "内政成功",
                        "message": message
                    }
                })
        except DBError as e:
            logging.error("EventMove::run: caught DBError: " + e.message)

        except Exception as e:

            # 状態を初期化しておく
            player_controller.update_user_status(user_id, "ready")
            division_controller.update_division_status(division_id, "ready")

            logging.error(e)

            # プレイヤーにエラーを通知
            payload = {
                "event": "cancel",
                "data": {
                    "title": "内政キャンセル",
                    "reason": "内部エラー"
                }
            }
            BJSocketHandler.BJSocketHandler.send_member_by_id(
                self._user_id, payload)
Ejemplo n.º 8
0
    def run(self):
        """
        プレイヤーを移動させる
        終了後はmove_playerイベントを送信する
        :return: 成功 ? True : False
        """

        self._logger.debug("enter run")
        self._logger.debug("event_record=", self._event_record)

        # 例外処理用
        user_id = None
        division_id = None
        try:
            # デコード
            if self._decode_recode() is False:
                raise Exception("デコード失敗")
                return False

            # プレイヤー情報
            player = player_controller.get_playerinfo_by_id(self._user_id)
            user_id = player["user_id"]
            moving_player_info = {
                "user_id": player["user_id"],
                "country_id": player["country_id"],
                "ex_col": player["col"],
                "ex_row": player["row"],
                "new_col": self._dest_col,
                "new_row": self._dest_row,
                "icon": player["icon_id"]
            }

            # 移動中でないならキャンセル(本来はイベントレコード自体がキャンセルされるべき
            if player["status"] != "moving":
                raise Exception("プレイヤーの状態がmovingではない")
                return

            # 部隊情報
            division = division_controller.get_division_info(
                player["division_id"])

            # 移動中でないならキャンセル(本来はイベントレコード自体がキャンセルされるべき)
            if division["status"] != "moving":
                raise Exception("部隊の状態がmovingではない")
                return

            # エラーに備えて本処理前にプレイヤーと部隊の状態を初期化する
            player_controller.update_user_status(self._user_id, "ready")
            division_controller.update_division_status(self._division_id,
                                                       "ready")

            # 現在時刻
            now = BJTime.get_time_now()
            """
            移動先に既に他の部隊がいる場合
            """
            division_dest = division_controller.get_division_info_by_colrow(
                self._dest_col, self._dest_row)
            if division_dest:

                # 味方なら移動キャンセルイベント
                if division_dest["country_id"] == player["country_id"]:
                    data = {
                        "event": "cancel",
                        "data": {
                            "reason": "移動先に味方の部隊がいるため、移動はキャンセルされた"
                        }
                    }
                    BJSocketHandler.BJSocketHandler.send_member_by_id(
                        player["user_id"], data)
                    return

                # 敵なら戦闘開始
                if division_dest["country_id"] != player["country_id"]:
                    return
            """
            移動
            """
            # 移動前の可視領域減算
            if not hexgrid_controller.update_visible_area(
                    visibility=player["visibility"],
                    division_id=division["division_id"],
                    switch=False):
                raise Exception("可視領域減算に失敗")

            # 移動(部隊)
            if not division_controller.move_division(
                    self._division_id, self._dest_col, self._dest_row):
                self._logger.error("部隊の移動に失敗")
                raise Exception("部隊の移動に失敗")

            # 移動(プレイヤー)
            if not player_controller.move_user(self._user_id, self._dest_col,
                                               self._dest_row):
                self._logger.error("プレイヤーの移動に失敗")
                raise Exception("プレイヤーの移動に失敗")

            # 移動後のヘックスを国の支配下に
            if not hexgrid_controller.update_hex(
                    self._dest_col,
                    self._dest_row,
                    country_id=player["country_id"]):
                self._logging.error("移動後の支配に失敗")
                raise Exception("移動後の支配に失敗")

            # 移動後の可視領域可算
            if not hexgrid_controller.update_visible_area(
                    visibility=player["visibility"],
                    division_id=division["division_id"],
                    switch=True):
                self._logging.error("移動後の可視領域可算に失敗")
                raise Exception("移動後の可視領域可算に失敗")

            # 可視範囲を共有する通信中のプレイヤー(移動するプレイヤーも含む)にプレイヤーの移動を通知
            notify_move_player(moving_player_info, player["visibility"], now)

        except DBError as e:
            logging.error("EventMove::run: caught DBError: " + e.message)

        except Exception as e:

            # 状態を初期化しておく
            player_controller.update_user_status(user_id, "ready")
            division_controller.update_division_status(division_id, "ready")

            logging.error(e)

            # プレイヤーにエラーを通知
            payload = {"event": "error", "data": {"message": "進軍はキャンセルされた"}}
            BJSocketHandler.send_member_by_id(self._user_id, payload)
Ejemplo n.º 9
0
        :param user_id: 移動するユーザー
        :param division_id: 移動する部隊
        :param dest_col: 目的地col
        :param dest_row: 目的地row
        :param datetime: 到着時刻
        :return: event_record[event_id, ...]
        """

        # データ部
        # "division_id,col,row"
        data = str(division_id) + "," + str(dest_col) + "," + str(dest_row)

        event = event_controller.create_event(datetime=datetime,
                                              object=user_id,
                                              subject="none",
                                              event_name="move",
                                              data=data)

        return event


if __name__ == "__main__":

    date = BJTime.get_time_now()
    print(date)
    e = EventMove.create_recode(user_id="u_id",
                                division_id="div_id",
                                dest_col="1",
                                dest_row="2",
                                datetime=date)
    print(e)
Ejemplo n.º 10
0
def request_domestic(_cls, _self, data):
    """
    内政要求
    """
    payload = {"event": "response_ask_domestic", "data": {}}

    player_info = player_controller.get_playerinfo_by_id(
        _self.get_secure_cookie("user_id").decode('utf-8'))
    col = data["col"]
    row = data["row"]

    # プレイヤーは行動可能か
    if not player_info["status"] == "ready":
        _cls.send_member_by_id(player_info["user_id"], {
            "event": "cancel",
            "data": {
                "title": "内政キャンセル",
                "reason": "行動中"
            }
        })
        return

    # 内政可能な半径か
    adjacent_area = hexgrid_controller.get_adjacent_area(
        player_info["col"], player_info["row"])
    if not (col == player_info["col"]
            and row == player_info["row"]) and not ({
                "col": col,
                "row": row
            } in adjacent_area):
        _cls.send_member_by_id(
            player_info["user_id"], {
                "event": "cancel",
                "data": {
                    "title": "内政キャンセル",
                    "reason": "内政可能な半径ではない"
                }
            })
        return

    # 現在のヘックスの状態
    target_hex = hexgrid_controller.get_hexinfo(col, row)
    if target_hex == None:
        _cls.send_member_by_id(
            player_info["user_id"], {
                "event": "cancel",
                "data": {
                    "title": "内政キャンセル",
                    "reason": "存在しないヘックス。"
                }
            })

    # イベント登録
    # TODO : プレイヤーのステータスなどによる処理
    gm = GameMain()
    finish_time = BJTime.add_time(BJTime.get_time_now(),
                                  gm.get_affair().get_op_domestic_speed())
    event = EventDomestic.create_recode(user_id=player_info["user_id"],
                                        division_id=player_info["division_id"],
                                        col=col,
                                        row=row,
                                        type=data["type"],
                                        datetime=finish_time)
    add_event(event)

    # プレイヤーと部隊の状態を変更
    division_controller.update_division_status(player_info["division_id"],
                                               "domestic")
    player_controller.update_user_status(player_info["user_id"], "domestic")

    # メインエンジンにイベントの実行時を通知
    gm = GameMain()
    gm.set_event_timestamp(finish_time)
Ejemplo n.º 11
0
    def run(self):
        """
        :return: 成功 ? True : False
        """

        self._logger.debug("enter EventSchedDomestic run")
        self._logger.debug("event_record=", self._event_record)

        try:

            db = DBSingleton()

            # 可算前のの国情報取得
            ex_countries = country_controller.get_all_countryinfo()

            # 全ての国で食糧可算
            query = "UPDATE country" \
                    "   SET food = ((SELECT" \
                    "       SUM(hex_grid.food) from hex_grid" \
                    "       WHERE hex_grid.country_id = country.country_id" \
                    "       GROUP BY hex_grid.country_id) + country.food)" \
                    "WHERE EXISTS(SELECT 1 FROM hex_grid" \
                    "          WHERE hex_grid.country_id = country.country_id);"

            db.exec(query)

            # 全ての国で商業可算
            query = "UPDATE country" \
                    "   SET money = ((SELECT" \
                    "       SUM(hex_grid.money) from hex_grid" \
                    "       WHERE hex_grid.country_id = country.country_id" \
                    "       GROUP BY hex_grid.country_id) + country.money)" \
                    "WHERE EXISTS(SELECT 1 FROM hex_grid" \
                    "          WHERE hex_grid.country_id = country.country_id);"
            db.exec(query)

            print("SchedDomestic run")

            # イベントをインターバルでチェーン
            gm = GameMain.GameMain()
            next_time = BJTime.add_time(
                BJTime.get_time_now(),
                gm.get_affair().get_domestic_interval())
            next_event = SchedEventDomestic.create_recode(next_time)
            event_controller.add_event(next_event)
            gm.set_event_timestamp(next_time)

            # それぞれの国へ通知
            new_countries = country_controller.get_all_countryinfo()
            for i in range(0, len(new_countries)):
                message = "食糧 " + str(new_countries[i]["food"]) + " → " + str(
                    ex_countries[i]["food"]) + "\n"
                message += "資金 " + str(
                    new_countries[i]["money"]) + " → " + str(
                        ex_countries[i]["money"])
                payload = {
                    "event": "notify",
                    "data": {
                        "title": "徴税結果",
                        "message": message
                    }
                }
                BJSocketHandler.BJSocketHandler.send_member_by_country(
                    new_countries["country_id"], payload)

        except DBError as e:
            logging.error("EventMove::run: caught DBError: " + e.message)

        except Exception as e:
            logging.error(e)
Ejemplo n.º 12
0
def request_move(_cls, _self, data):
    """
    行軍イベントをセットする
    :param _cls: BJSocketHandlerのクラス
    :param _self: BJSocketHandlerのインスタンス
    :param data: クライアントから受信したデータ
    :return:
    """

    user_id = _self.get_secure_cookie("user_id").decode('utf-8')
    payload = {"event" : "response_request_move" ,
               "data"  : {}}



    try:

        """
        move_queryの焼き増しな部分があるが、queryとrequestの時間差または不正防止のため
        再度移動可能かどうか調べる
        """

        # 目的地
        dest_col = data["col"]
        dest_row = data["row"]

        # プレイヤーの状態
        user_id = _self.get_secure_cookie("user_id").decode('utf-8')
        player = get_playerinfo_by_id(user_id)

        # プレイヤーが待機中でないと移動できない
        if player["status"] != "ready":

            payload["data"] = {"response" : "deny",
                                "reason"   : "行動中"}
            _self.send_you(payload)
            return

        # 配下の部隊の状態
        division = get_division_info(player["division_id"])

        # 部隊がセットされていないと移動できない
        if not division:
            payload["data"] = {"response" : "deny",
                                "reason"   : "配下の師団がセットされていない"}
            _self.send_you(payload)

        # 部隊の移動半径内でないと移動できない
        movable_area = get_movable_area_by_division_id(division["division_id"], player["col"], player["row"])

        required_time = False # 所要時間
        for hex in movable_area:
            if hex["col"] == dest_col and hex["row"] == dest_row:
                required_time = hex["time"]

        if not required_time:
            payload["data"] = {"response" : "deny",
                               "reason"   :  "[" + str(dest_col) + "," + str(dest_row) + "]は移動可能半径外"}
            _cls.send_player(user_id, payload)

        # 師団の兵科情報取得
        branch_info = get_branch_info(division["branch_id"])

        # ゲームレベルから設定値を取得
        gm = GameMain()
        op_food_lv = gm.get_affair().get_op_food_lv()
        op_money_lv = gm.get_affair().get_op_money_lv()
        op_speed_lv = gm.get_affair().get_op_speed_lv()

        # 運用に必要な食糧と金 : 師団規模 * 兵科固定値 * ゲームレベル
        food_needed = division["quantity"] * branch_info["op_food"] * op_food_lv
        money_needed = division["quantity"] * branch_info["op_money"] * op_money_lv

        # 資金と食糧、それぞれ足りなければ移動不可
        if division["food"] < food_needed:
            payload["data"] = {"response" : "deny",
                                "reason"   : "部隊の運用食糧が足りない。<br>" +
                                             "部隊の保持食糧 : " + str(division["food"]) + "<br>" +
                                             "必要な食糧 = 師団規模(" + division["quantity"] +
                                            ")×兵科補正(" + str(branch_info["op_food"]) +
                                             ")×情勢補正(" + str(op_food_lv) + ") = " + str(food_needed)}
            _self.send_you(payload)
            return False

        elif division["money"] < money_needed:
            payload["data"] = {"response" : "deny",
                                "reason"   : "部隊の運用資金が足りない。<br>" +
                                             "部隊の保持資金 : " + str(division["money"]) + "<br>" +
                                             "必要な資金 = 師団規模(" + division["quantity"] +
                                            ")×兵科補正(" + str(branch_info["op_money"]) +
                                             ")×情勢補正(" + str(op_money_lv) + ") = " + str(money_needed)}
            _self.send_you(payload)
            return False

        """
       チェックが終わったのでプレイヤー情報等を更新し、行軍イベントをセットする
        """

        # 到着予定時刻計算
        required_time = required_time * op_speed_lv # ゲームレベル適用
        current_time = BJTime.get_time_now()
        arrival_time = BJTime.add_time(current_time, required_time)

        # TODO: チェックと次の処理の間に状態が変更される可能性がある

        # 行軍のイベントレコードを作成
        event_record = EventMove.create_recode(user_id=player["user_id"],
                                               division_id=division["division_id"],
                                               dest_col=dest_col,
                                               dest_row=dest_row,
                                               datetime=arrival_time)
        # レコードの作成に失敗していれば失敗
        if event_record is None:
             payload["data"] = {"response" : "deny",
                                "reason"   : "イベントレコードの作成に失敗した。管理者に連絡して下さい。"}
             _self.send_you(payload)
             return

        # レコードをDBに登録
        add_event(event_record)

        # メインエンジンにイベントの実行時を通知
        gm = GameMain()
        gm.set_event_timestamp(arrival_time)

        # プレイヤーの状態を更新
        update_user_before_move(user_id=player["user_id"],
                                wait_untill=arrival_time)
        update_user_status(player["user_id"],"moving")

        # 部隊の状態を更新
        update_division_before_move(division_id=division["division_id"],
                                    food=food_needed,
                                    money=money_needed)
        update_division_status(division["division_id"], "moving")

        # クライアントに通知
        payload["data"] =  { "response" : "approval",
                              "arrival_time" : arrival_time.strftime("%Y-%m-%d %H:%M:%S")}

        _self.send_you(payload)
        return

    except Exception as e:
        logging.error(e)
        _self.send_error(e.message);
        return

    assert False