From 8b78055fcfbde4b41547388818c67c6225a79c0f Mon Sep 17 00:00:00 2001 From: Hide_D Date: Thu, 18 Aug 2022 01:07:23 +0900 Subject: [PATCH] wip: GetFrontInfo --- hwe/sammo/API/General/GetFrontInfo.php | 467 +++++++++++++++++++++++++ hwe/ts/PageFront.vue | 85 ++++- hwe/ts/SammoAPI.ts | 2 + hwe/ts/defs/API/Global.ts | 281 ++++++++++----- 4 files changed, 739 insertions(+), 96 deletions(-) create mode 100644 hwe/sammo/API/General/GetFrontInfo.php diff --git a/hwe/sammo/API/General/GetFrontInfo.php b/hwe/sammo/API/General/GetFrontInfo.php new file mode 100644 index 00000000..b7e414a8 --- /dev/null +++ b/hwe/sammo/API/General/GetFrontInfo.php @@ -0,0 +1,467 @@ +args); + $v->rule('lengthMin', 'lastNationNoticeDate', 19); + if (!$v->validate()) { + return $v->errorStr(); + } + return null; + } + + private function generateGlobalInfo(MeekroDB $db): array + { + $gameStor = KVStorage::getStorage($db, 'game_env'); + + [ + $scenarioText, $extendedGeneral, $isFiction, $npcMode, + $joinMode, $autorunUser, $turnterm, $lastExecuted, + $lastVoteID, $develCost, $noticeMsg, + $onlineNations, $onlineUserCnt, + $year, $month, $startYear, + $generalCntLimit, + $apiLimit, + ] = $gameStor->getValuesAsArray([ + 'scenario_text', 'extended_general', 'fiction', 'npcmode', + 'join_mode', 'autorun_user', 'turnterm', 'turntime', + 'lastVote', 'develcost', 'msg', + 'online_nation', 'online_user_cnt', + 'year', 'month', 'startyear', + 'maxgeneral', + 'conlimit', + ]); + + if ($lastVoteID) { + $voteStor = KVStorage::getStorage($db, 'vote'); + $lastVote = VoteInfo::fromArray($voteStor->getValue("vote_{$lastVoteID}")); + if ($lastVote->endDate && $lastVote->endDate < TimeUtil::now()) { + $lastVote = null; + } + } + + $auctionCount = $db->queryFirstField('SELECT count(*) FROM ng_auction WHERE finished = 0'); + $isTournamentActive = $gameStor->tournament > 0; + $isTournamentApplicationOpen = $gameStor->tournament == 1; + $isBettingActive = $gameStor->tournament == 6; + $tournamentType = $gameStor->tnmt_type; + $tournamentState = getTournamentTermText($gameStor->turnterm); + $isLocked = boolval($db->queryFirstField('SELECT plock FROM plock WHERE `type`="GAME" LIMIT 1')); + + $globalGenCount = $db->queryAllLists('SELECT npc, count(no) FROM general GROUP BY npc'); + + return [ + 'scenarioText' => $scenarioText, + 'extendedGeneral' => $extendedGeneral, + 'isFiction' => $isFiction, + 'npcMode' => $npcMode, + 'joinMode' => $joinMode, + 'startyear' => $startYear, + 'year' => $year, + 'month' => $month, + 'autorunUser' => $autorunUser, + 'turnterm' => $turnterm, + 'lastExecuted' => $lastExecuted, + 'lastVoteID' => $lastVoteID, + 'develCost' => $develCost, + 'noticeMsg' => $noticeMsg, + 'onlineNations' => $onlineNations, + 'onlineUserCnt' => $onlineUserCnt, + 'apiLimit' => $apiLimit, + 'auctionCount' => $auctionCount, + 'isTournamentActive' => $isTournamentActive, + 'isTournamentApplicationOpen' => $isTournamentApplicationOpen, + 'isBettingActive' => $isBettingActive, + 'isLocked' => $isLocked, + 'tournamentType' => $tournamentType, + 'tournamentState' => $tournamentState, + 'genCount' => $globalGenCount, + 'generalCntLimit' => $generalCntLimit, + 'lastVote' => $lastVote === null ? null : $lastVote->toArray(), + ]; + } + + public function generateNationInfo(MeekroDB $db, General $general, array $rawNation): array + { + $gameStor = KVStorage::getStorage($db, 'game_env'); + $admin = $gameStor->getAll(true); + + $lastNationNoticeDate = $this->args['lastNationNoticeDate'] ?? '2022-08-19 00:00:00'; + + $nationID = $general->getNationID(); + + //XXX: 매번 더하는가? + $nationPopulation = $db->queryFirstRow( + 'SELECT COUNT(*) as cityCnt, SUM(pop) as `now`, SUM(pop_max) as `max` from city where nation=%i', + $nationID + ); + + //XXX: 매번 더하는가? + $nationCrew = $db->queryFirstRow( + 'SELECT COUNT(*) as generalCnt, SUM(crew) as `now`,SUM(leadership)*100 as `max` from general where nation=%i AND npc != 5', + $nationID + ); + + $nationStor = KVStorage::getStorage($db, $nationID, 'nation_env'); + [$nationNotice, $onlineGen] = $nationStor->getValuesAsArray(['nationNotice', 'online_genenerals']); + if (!$nationNotice || ($nationNotice['date'] ?? '') <= $lastNationNoticeDate) { + $nationNotice = null; + } + + $topChiefs = Util::convertArrayToDict($db->query( + 'SELECT officer_level, no, name, npc FROM general WHERE nation = %i AND officer_level >= 11', + $nationID + ), 'officer_level'); + + $impossibleStrategicCommandLists = []; + $strategicCommandLists = GameConst::$availableChiefCommand['전략']; + $yearMonth = Util::joinYearMonth($admin['year'], $admin['month']); + foreach ($strategicCommandLists as $command) { + $cmd = buildNationCommandClass($command, $general, $admin, new LastTurn()); + $nextAvailableTurn = $cmd->getNextAvailableTurn(); + if ($nextAvailableTurn > $yearMonth) { + $impossibleStrategicCommandLists[] = [$cmd->getName(), $nextAvailableTurn - $yearMonth]; + } + } + + $nationClass = buildNationTypeClass($rawNation['type']); + + //TODO: 이부분은 const로 빼야하지 않는가? + $officerLevelTexts = []; + foreach (range(0, 4) as $officerLevel) { + $officerLevelTexts[$officerLevel] = getOfficerLevelText($officerLevel); + } + foreach (range(5, 12) as $officerLevel) { + $officerLevelTexts[$officerLevel] = getOfficerLevelText($rawNation['level'] * 100 + $officerLevel); + } + + + return [ + 'id' => $nationID, + 'name' => $rawNation['name'], + 'population' => $nationPopulation, + 'crew' => $nationCrew, + 'type' => [ + 'raw' => $rawNation['type'], + 'name' => $nationClass->getName(), + 'pros' => $nationClass::$pros, + 'cons' => $nationClass::$cons, + ], + 'color' => $rawNation['color'], + 'level' => $rawNation['level'], + 'capital' => $rawNation['capital'], + 'gold' => $rawNation['gold'], + 'rice' => $rawNation['rice'], + 'tech' => $rawNation['tech'], + 'gennum' => $rawNation['gennum'], + 'power' => $rawNation['power'], + + 'bill' => $rawNation['bill'], + 'taxRate' => $rawNation['rate'], + 'onlineGen' => $onlineGen, + 'notice' => $nationNotice, + 'topChiefs' => $topChiefs, + 'officerLevelTexts' => $officerLevelTexts, + + 'diplomaticLimit' => $rawNation['surlimit'], + 'strategicCmdLimit' => $rawNation['strategic_cmd_limit'], + 'impossibleStrategicCommand' => $impossibleStrategicCommandLists, + + + 'prohibitScout' => $rawNation['scout'], + 'prohibitWar' => $rawNation['war'], + ]; + } + + public function generateDummyNationInfo(MeekroDB $db, General $general, array $rawNation): array + { + $staticInfo = getNationStaticInfo(0); + return [ + 'id' => 0, + 'name' => $staticInfo['name'], + 'population' => [ + 'cityCnt' => 0, + 'now' => 0, + 'max' => 0, + ], + 'crew' => [ + 'generalCnt' => 0, + 'now' => 0, + 'max' => 0, + ], + 'type' => [ + 'raw' => 'None', + 'name' => '-', + 'pros' => '', + 'cons' => '', + ], + 'color' => $staticInfo['color'], + 'level' => $staticInfo['level'], + 'capital' => $staticInfo['capital'], + 'gold' => $staticInfo['gold'], + 'rice' => $staticInfo['rice'], + 'tech' => $staticInfo['tech'], + 'gennum' => $staticInfo['gennum'], + 'power' => $staticInfo['power'], + 'onlineGen' => '', //TODO 접속자 + 'notice' => '', + 'topChiefs' => [], + 'impossibleStrategicCommand' => [], + 'officerLevelTexts' => [], + ]; + } + + public function generateGeneralInfo(MeekroDB $db, General $general, array $rawNation): array + { + $result = [ + 'no' => $general->getID(), // number; + 'name' => $general->getName(), // string; + 'nation' => $general->getNationID(), // number; + 'npc' => $general->getNPCType(), // number; + 'injury' => $general->getVar(GeneralColumn::injury), // number; + 'leadership' => $general->getVar(GeneralColumn::leadership), // number; + 'strength' => $general->getVar('strength'), // number; + 'intel' => $general->getVar('intel'), // number; + 'explevel' => $general->getVar('explevel'), // number; + 'dedlevel' => $general->getVar('dedlevel'), // number; + 'gold' => $general->getVar('gold'), // number; + 'rice' => $general->getVar('rice'), // number; + 'killturn' => $general->getVar(GeneralColumn::injury), // number; + 'picture' => $general->getVar(GeneralColumn::injury), // string; + 'imgsvr' => $general->getVar(GeneralColumn::injury), // 0 | 1; + 'age' => $general->getVar(GeneralColumn::injury), // number; + 'specialDomestic' => $general->getVar(GeneralColumn::injury), // GameObjClassKey; + 'specialWar' => $general->getVar(GeneralColumn::injury), // GameObjClassKey; + 'personal' => $general->getVar(GeneralColumn::injury), // GameObjClassKey; + 'belong' => $general->getVar(GeneralColumn::injury), // number; + 'connect' => $general->getVar(GeneralColumn::injury), // number; + + 'officerLevel' => $general->getVar(GeneralColumn::injury), // number; + 'officerLevelText' => $general->getVar(GeneralColumn::injury), // string; + 'lbonus' => $general->getVar(GeneralColumn::injury), // number; + 'ownerName' => $general->getVar(GeneralColumn::injury), // string | null; + 'honorText' => $general->getVar(GeneralColumn::injury), // string; + 'dedLevelText' => $general->getVar(GeneralColumn::injury), // string; + 'bill' => $general->getVar(GeneralColumn::injury), // number; + 'reservedCommand' => $general->getVar(GeneralColumn::injury), // TurnObj[] | null; + + 'autorun_limit' => $general->getVar(GeneralColumn::injury), // number; + + 'city' => $general->getVar(GeneralColumn::injury), // number; + 'troop' => $general->getVar(GeneralColumn::injury), // number; + //P0 End + + 'con' => $general->getVar(GeneralColumn::injury), // number; + 'specage' => $general->getVar(GeneralColumn::injury), // number; + 'specage2' => $general->getVar(GeneralColumn::injury), // number; + 'leadership_exp' => $general->getVar(GeneralColumn::injury), // number; + 'strength_exp' => $general->getVar(GeneralColumn::injury), // number; + 'intel_exp' => $general->getVar(GeneralColumn::injury), // number; + + 'dex1' => $general->getVar(GeneralColumn::dex1), // number; + 'dex2' => $general->getVar(GeneralColumn::dex2), // number; + 'dex3' => $general->getVar(GeneralColumn::dex3), // number; + 'dex4' => $general->getVar(GeneralColumn::dex4), // number; + 'dex5' => $general->getVar(GeneralColumn::dex5), // number; + + 'experience' => $general->getVar(GeneralColumn::experience), // number; + 'dedication' => $general->getVar(GeneralColumn::dedication), // number; + 'officer_level' => $general->getVar(GeneralColumn::officer_level), // number; + 'officer_city' => $general->getVar(GeneralColumn::officer_city), // number; + 'defence_train' => $general->getVar(GeneralColumn::defence_train), // number; + 'crewtype' => $general->getVar(GeneralColumn::crewtype), // GameObjClassKey; + 'crew' => $general->getVar(GeneralColumn::crew), // number; + 'train' => $general->getVar(GeneralColumn::train), // number; + 'atmos' => $general->getVar(GeneralColumn::atmos), // number; + 'turntime' => $general->getVar(GeneralColumn::turntime), // string; + 'recent_war' => $general->getVar(GeneralColumn::recent_war), // string; + 'horse' => $general->getVar(GeneralColumn::horse), // GameObjClassKey; + 'weapon' => $general->getVar(GeneralColumn::weapon), // GameObjClassKey; + 'book' => $general->getVar(GeneralColumn::book), // GameObjClassKey; + 'item' => $general->getVar(GeneralColumn::item), // GameObjClassKey; + + 'warnum' => $general->getRankVar(RankColumn::warnum), // number; + 'killnum' => $general->getRankVar(RankColumn::killnum), // number; + 'deathnum' => $general->getRankVar(RankColumn::deathnum), // number; + 'killcrew' => $general->getRankVar(RankColumn::killcrew), // number; + 'deathcrew' => $general->getRankVar(RankColumn::deathcrew), // number; + 'firenum' => $general->getRankVar(RankColumn::firenum), // number; + //P1 End + ]; + + $nationID = $general->getNationID(); + $troopID = $general->getVar(GeneralColumn::troop); + if (!$troopID) { + return $result; + } + + $troopName = $db->queryFirstField( + 'SELECT `name` FROM troop WHERE nation = %i AND troop_leader = %i', + $nationID, + $troopID + ); + if (!$troopName) { + return $result; + } + + $troopCityID = $db->queryFirstField( + 'SELECT city FROM general WHERE nation = %i AND `no` = %i', + $nationID, + $troopID + ); + if (!$troopCityID) { + return $result; + } + + $troopReservedCommand = $db->query( + 'SELECT action, arg, brief FROM general_turn WHERE general_id = %i AND turn_idx < 5 ORDER BY turn_idx asc', + $troopID + ); + if ($troopReservedCommand) { + return $result; + } + + $result['troopInfo'] = [ + 'leader' => [ + 'city' => $troopCityID, + 'reservedCommand' => $troopReservedCommand, + ], + 'name' => $troopName, + ]; + + return $result; + } + + public function generateCityInfo(MeekroDB $db, General $general, array $rawNation): array + { + $rawCity = $general->getRawCity() ?? []; + $cityID = $rawCity[CityColumn::city->value]; + + $nationID = $rawCity[CityColumn::nation->value]; + if ($nationID == $general->getNationID()) { + $nationName = $rawNation['name']; + $nationColor = $rawNation['color']; + } else { + $staticNation = getNationStaticInfo($nationID); + $nationName = $staticNation['name']; + $nationColor = $staticNation['color']; + } + + $rawOfficerList = $db->query('SELECT officer_level, name, npc FROM general WHERE city = %i AND officer_level IN (4,3,2)', $cityID); + $officerList = [4=>null, 3=>null, 2=>null]; + foreach($rawOfficerList as $officer){ + $officerLevel = $officer['officer_level']; + $officerList[$officerLevel] = $officer; + } + + $result = [ + 'id' => $rawCity[CityColumn::city->value], + 'name' => $rawCity[CityColumn::name->value], + 'nationInfo' => [ + 'id' => $nationID, + 'name' => $nationName, + 'color' => $nationColor, + ], + 'level' => $rawCity[CityColumn::level->value], + 'trust' => $rawCity[CityColumn::trust->value], + 'pop' => [$rawCity[CityColumn::pop->value], $rawCity[CityColumn::pop_max->value]], + 'agri' => [$rawCity[CityColumn::agri->value], $rawCity[CityColumn::agri_max->value]], + 'comm' => [$rawCity[CityColumn::comm->value], $rawCity[CityColumn::comm_max->value]], + 'secu' => [$rawCity[CityColumn::secu->value], $rawCity[CityColumn::secu_max->value]], + 'def' => [$rawCity[CityColumn::def->value], $rawCity[CityColumn::def_max->value]], + 'wall' => [$rawCity[CityColumn::wall->value], $rawCity[CityColumn::wall_max->value]], + 'trade' => $rawCity[CityColumn::trade->value], + 'officerList' => $officerList, + ]; + return $result; + } + + public function launch(Session $session, ?DateTimeInterface $modifiedSince, ?string $reqEtag) + { + $generalID = $session->generalID; + //NOTE: 이 경우 staticNation 정보를 조회한다. + $general = General::createGeneralObjFromDB($generalID); + $nationID = $general->getNationID(); + $cityID = $general->getCityID(); + + $con = checkLimit($general->getVar('con')); + if ($con >= 2) { + return [ + 'result' => false, + 'reason' => '접속 제한중입니다.', + 'recovery' => APIRecoveryType::GameQuota, + 'recovery_arg' => $general->getVar('turntime'), + ]; + } + + $db = DB::db(); + $gameStor = KVStorage::getStorage($db, 'game_env'); + $gameStor->cacheAll(true); + + $rawCity = $db->queryFirstRow('SELECT * FROM city WHERE city = %i', $cityID); + $general->setRawCity($rawCity); + + if ($nationID == 0) { + $rawNation = getNationStaticInfo(0); + } else { + $rawNation = $db->queryFirstRow('SELECT * FROM nation WHERE nation = %i', $nationID); + } + + $globalInfo = $this->generateGlobalInfo($db); + $nationInfo = $nationID != 0 ? + $this->generateNationInfo($db, $general, $rawNation) : + $this->generateDummyNationInfo($db, $general, $rawNation); + $generalInfo = $this->generateGeneralInfo($db, $general, $rawNation); + $cityInfo = $this->generateCityInfo($db, $general, $rawNation); + + //TODO: 마지막 투표, 토너먼트, 베팅을 했는지 정보를 별도로 가져와야 함. aux? + + + return [ + 'result' => true, + 'global' => $globalInfo, + 'nation' => $nationInfo, + 'general' => $generalInfo, + 'city' => $cityInfo, + ]; + } +} diff --git a/hwe/ts/PageFront.vue b/hwe/ts/PageFront.vue index e98eb89e..aec94611 100644 --- a/hwe/ts/PageFront.vue +++ b/hwe/ts/PageFront.vue @@ -27,7 +27,15 @@
예턴
도시 정보
국가 정보
-
장수 정보
+
+ +
국가 툴바
갱신/로비 버튼
@@ -87,8 +95,11 @@ import { parseTime } from "./util/parseTime"; import { unwrap } from "./util/unwrap"; import Denque from "denque"; import { formatLog } from "@/utilGame/formatLog"; -import type { ExecuteResponse } from "./defs/API/Global"; +import type { ExecuteResponse, GetFrontInfoResponse } from "./defs/API/Global"; import { delay } from "./util/delay"; +import GeneralBasicCard from "./components/GeneralBasicCard.vue"; +import type { GeneralListItemP1 } from "./defs/API/Nation"; +import type { NationStaticItem } from "./defs"; const { serverName, serverNick, serverID } = staticValues; @@ -115,21 +126,19 @@ async function tryRefresh() { } try { responseLock = true; - const responseP = SammoAPI.Global.ExecuteEngine({ serverID }, true).then( - (response) => { - if (response.result) { - lastExecuted.value = parseTime(response.lastExecuted); - serverLocked.value = response.locked; - } - return response; + const responseP = SammoAPI.Global.ExecuteEngine({ serverID }, true).then((response) => { + if (response.result) { + lastExecuted.value = parseTime(response.lastExecuted); + serverLocked.value = response.locked; } - ); + return response; + }); //TODO: 갱신 알림 띄우기 const response = await Promise.race([delay(3000), responseP]); responseLock = false; - if(response === undefined){ + if (response === undefined) { //timeout이지만 일단 갱신한다. refreshCounter.value += 1; return; @@ -179,7 +188,7 @@ const worldHistory = ref(new Denque<[number, string]>()); let recordLock = false; watch(refreshCounter, async () => { - if(recordLock){ + if (recordLock) { return; } try { @@ -215,11 +224,10 @@ watch(refreshCounter, async () => { if (!generalRecords.value.isEmpty() && id <= unwrap(generalRecords.value.get(0))[0]) { continue; } - if(generalRecords.value.length >= 15) { + if (generalRecords.value.length >= 15) { generalRecords.value.pop(); } generalRecords.value.unshift([id, record]); - } while (response.global.length) { @@ -227,7 +235,7 @@ watch(refreshCounter, async () => { if (!globalRecords.value.isEmpty() && id <= unwrap(globalRecords.value.get(0))[0]) { continue; } - if(globalRecords.value.length >= 15) { + if (globalRecords.value.length >= 15) { globalRecords.value.pop(); } globalRecords.value.unshift([id, record]); @@ -238,7 +246,7 @@ watch(refreshCounter, async () => { if (!worldHistory.value.isEmpty() && id <= unwrap(worldHistory.value.get(0))[0]) { continue; } - if(worldHistory.value.length >= 15) { + if (worldHistory.value.length >= 15) { worldHistory.value.pop(); } worldHistory.value.unshift([id, record]); @@ -252,6 +260,51 @@ watch(refreshCounter, async () => { }); } }); + +const frontInfo = ref(); + +const generalInfo = ref(); +const nationStaticInfo = ref(); + +let generalInfoLock = false; + +watch(refreshCounter, async () => { + if (generalInfoLock) { + return; + } + try { + generalInfoLock = true; + const response = await SammoAPI.General.GetFrontInfo(); + generalInfoLock = false; + + frontInfo.value = response; + generalInfo.value = response.general; + + const newLastExecuted = parseTime(response.global.lastExecuted); + if(newLastExecuted.getTime() > lastExecuted.value.getTime()){ + lastExecuted.value = newLastExecuted; + } + + const rawNation = response.nation; + nationStaticInfo.value = { + nation: rawNation.id, + name: rawNation.name, + color: rawNation.color, + type: rawNation.type.raw, + level: rawNation.level, + capital: rawNation.capital, + gennum: rawNation.gennum, + power: rawNation.power, + }; + } catch (e) { + recordLock = false; + console.error(e); + toasts.danger({ + title: "최근 정보 갱신 실패", + body: `${e}`, + }); + } +});