From 98c1213b103193b58455d89529f8fc728d0a2080 Mon Sep 17 00:00:00 2001 From: Hide_D Date: Sat, 21 May 2022 15:44:05 +0900 Subject: [PATCH 01/23] =?UTF-8?q?feat:=20=EA=B2=BD=EB=A7=A4=EC=9E=A5=20?= =?UTF-8?q?=ED=85=8C=EC=9D=B4=EB=B8=94=20=EC=83=88=EB=A1=9C=20=EC=9E=91?= =?UTF-8?q?=EC=84=B1?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- hwe/sql/reset.sql | 4 +++- hwe/sql/schema.sql | 24 +++++++++++++++++++++++- 2 files changed, 26 insertions(+), 2 deletions(-) diff --git a/hwe/sql/reset.sql b/hwe/sql/reset.sql index 7cc725cc..4fdbf35b 100644 --- a/hwe/sql/reset.sql +++ b/hwe/sql/reset.sql @@ -66,4 +66,6 @@ ENGINE=Aria; DROP TABLE IF EXISTS ng_betting; DROP TABLE IF EXISTS vote; -DROP TABLE IF EXISTS vote_comment; \ No newline at end of file +DROP TABLE IF EXISTS vote_comment; + +DROP TABLE IF EXISTS `ng_auction`; \ No newline at end of file diff --git a/hwe/sql/schema.sql b/hwe/sql/schema.sql index 5a459bf6..1bba1110 100644 --- a/hwe/sql/schema.sql +++ b/hwe/sql/schema.sql @@ -684,4 +684,26 @@ CREATE TABLE `vote_comment` ( `date` DATETIME NULL DEFAULT NULL, PRIMARY KEY (`id`), INDEX `by_vote` (`vote_id`) -) COLLATE = 'utf8mb4_general_ci' ENGINE = Aria; \ No newline at end of file +) COLLATE = 'utf8mb4_general_ci' ENGINE = Aria; + + +############################## +## 거래장 / 경매장 +############################## +# 경매 객체는 KVStorage에 저장 +CREATE TABLE `ng_auction` ( + `no` INT(11) NOT NULL AUTO_INCREMENT, + `auction_id` INT(11) NOT NULL, + `owner` INT(11) NULL DEFAULT NULL, + `general_id` INT(11) NOT NULL, + `amount` INT(11) NOT NULL, + `date` DATETIME NOT NULL, + `aux` LONGTEXT NOT NULL COLLATE 'utf8mb4_bin', + PRIMARY KEY (`no`), + UNIQUE INDEX `by_general` (`general_id`, `auction_id`, `amount`), + UNIQUE INDEX `by_owner` (`owner`, `auction_id`, `amount`), + INDEX `by_amount` (`auction_id`, `amount`, `date`), + CONSTRAINT `aux` CHECK (json_valid(`aux`)) +) +COLLATE='utf8mb4_general_ci' +ENGINE = Aria; \ No newline at end of file -- 2.54.0 From 837b936f0d88515dc459cfc0610b28d606a9349f Mon Sep 17 00:00:00 2001 From: Hide_D Date: Sat, 21 May 2022 22:16:25 +0900 Subject: [PATCH 02/23] feat: DTO\Caster\BackedEnumCaster --- hwe/sammo/DTO/Caster/BackedEnumCaster.php | 27 +++++++++++++++++++++++ 1 file changed, 27 insertions(+) create mode 100644 hwe/sammo/DTO/Caster/BackedEnumCaster.php diff --git a/hwe/sammo/DTO/Caster/BackedEnumCaster.php b/hwe/sammo/DTO/Caster/BackedEnumCaster.php new file mode 100644 index 00000000..d8c9b04e --- /dev/null +++ b/hwe/sammo/DTO/Caster/BackedEnumCaster.php @@ -0,0 +1,27 @@ +backedEnumFrom = $reflector->getMethod('from'); + } + + public function cast(mixed $value): \BackedEnum + { + if(!is_string($value) && !is_int($value)){ + throw new \Exception('BackedEnumCaster only accepts string|int'); + } + + return $this->backedEnumFrom->invoke(null, $value); + } +} \ No newline at end of file -- 2.54.0 From b7f86551099eb5d90196e3361e5d202a595eb88d Mon Sep 17 00:00:00 2001 From: Hide_D Date: Sun, 22 May 2022 01:01:26 +0900 Subject: [PATCH 03/23] WIP --- hwe/sammo/Auction.php | 46 ++++++++++++++++++++++++++++ hwe/sammo/DTO/AuctionBidItem.php | 38 +++++++++++++++++++++++ hwe/sammo/DTO/AuctionBidItemData.php | 10 ++++++ hwe/sammo/DTO/AuctionInfo.php | 28 +++++++++++++++++ hwe/sammo/Enums/ResourceType.php | 9 ++++++ 5 files changed, 131 insertions(+) create mode 100644 hwe/sammo/Auction.php create mode 100644 hwe/sammo/DTO/AuctionBidItem.php create mode 100644 hwe/sammo/DTO/AuctionBidItemData.php create mode 100644 hwe/sammo/DTO/AuctionInfo.php create mode 100644 hwe/sammo/Enums/ResourceType.php diff --git a/hwe/sammo/Auction.php b/hwe/sammo/Auction.php new file mode 100644 index 00000000..78169f1e --- /dev/null +++ b/hwe/sammo/Auction.php @@ -0,0 +1,46 @@ +invalidateCacheValue(self::LAST_AUCTION_ID_KEY); + $auctionID = ($gameStor->getValue(self::LAST_AUCTION_ID_KEY) ?? 0) + 1; + + $gameStor->setValue(self::LAST_AUCTION_ID_KEY, $auctionID); + + return $auctionID; + } + + + static public function openAuction(AuctionInfo $info) + { + $db = DB::db(); + $auctionID = $info->id; + $auctionStor = KVStorage::getStorage($db, 'auction'); + $auctionStor->setValue("id_{$auctionID}", $info->toArray()); + } + + public function __construct(private readonly int $auctionID) + { + $db = DB::db(); + $auctionStor = KVStorage::getStorage($db, 'auction'); + $rawBettingInfo = $auctionStor->getValue("id_{$auctionID}"); + if ($rawBettingInfo === null) { + throw new \RuntimeException("해당 경매가 없습니다: {$auctionID}"); + } + $this->info = new AuctionInfo($rawBettingInfo); + } +} diff --git a/hwe/sammo/DTO/AuctionBidItem.php b/hwe/sammo/DTO/AuctionBidItem.php new file mode 100644 index 00000000..185cc37a --- /dev/null +++ b/hwe/sammo/DTO/AuctionBidItem.php @@ -0,0 +1,38 @@ +data); + return $result; + } +} \ No newline at end of file diff --git a/hwe/sammo/DTO/AuctionBidItemData.php b/hwe/sammo/DTO/AuctionBidItemData.php new file mode 100644 index 00000000..5d065ddb --- /dev/null +++ b/hwe/sammo/DTO/AuctionBidItemData.php @@ -0,0 +1,10 @@ + Date: Sun, 22 May 2022 22:50:08 +0900 Subject: [PATCH 04/23] =?UTF-8?q?refac:=20Auction=20DTO=EB=A5=98=EB=A5=BC?= =?UTF-8?q?=20=EC=83=88=20DTO=EC=97=90=20=EB=A7=9E=EA=B2=8C=20=EB=B3=80?= =?UTF-8?q?=EA=B2=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- hwe/sammo/Auction.php | 2 +- hwe/sammo/DTO/AuctionBidItem.php | 46 +++++++++-------------- hwe/sammo/DTO/AuctionBidItemData.php | 14 ++++--- hwe/sammo/DTO/AuctionInfo.php | 32 ++++++---------- hwe/sammo/DTO/Caster/BackedEnumCaster.php | 27 ------------- 5 files changed, 38 insertions(+), 83 deletions(-) delete mode 100644 hwe/sammo/DTO/Caster/BackedEnumCaster.php diff --git a/hwe/sammo/Auction.php b/hwe/sammo/Auction.php index 78169f1e..a5282512 100644 --- a/hwe/sammo/Auction.php +++ b/hwe/sammo/Auction.php @@ -41,6 +41,6 @@ class Auction if ($rawBettingInfo === null) { throw new \RuntimeException("해당 경매가 없습니다: {$auctionID}"); } - $this->info = new AuctionInfo($rawBettingInfo); + $this->info = AuctionInfo::fromArray($rawBettingInfo); } } diff --git a/hwe/sammo/DTO/AuctionBidItem.php b/hwe/sammo/DTO/AuctionBidItem.php index 185cc37a..296a2df7 100644 --- a/hwe/sammo/DTO/AuctionBidItem.php +++ b/hwe/sammo/DTO/AuctionBidItem.php @@ -2,37 +2,25 @@ namespace sammo\DTO; -use sammo\Json; -use Spatie\DataTransferObject\DataTransferObject; -use Spatie\DataTransferObject\Attributes\MapFrom; -use Spatie\DataTransferObject\Attributes\MapTo; +use sammo\DTO\Attr\JsonString; +use sammo\DTO\Attr\RawName; -class AuctionBidItem extends DataTransferObject{ - public int $id; - #[MapFrom('auction_id')] - #[MapTo('auction_id')] - public int $auctionID; - public ?int $owner; +class AuctionBidItem extends DTO +{ + public function __construct( + public int $id, + #[RawName('auction_id')] + public int $auctionID, + public ?int $owner, - #[MapFrom('general_id')] - #[MapTo('general_id')] - public int $generalID; + #[RawName('general_id')] + public int $generalID, - public int $amount; + public int $amount, - public string $date; - public AuctionBidItemData $data; - - public static function parse(array $data): self - { - $data['data'] = Json::decode($data['data']); - return new self($data); + public string $date, + #[JsonString] + public AuctionBidItemData $data, + ) { } - - public function toArray(): array - { - $result = parent::toArray(); - $result['data'] = Json::encode($this->data); - return $result; - } -} \ No newline at end of file +} diff --git a/hwe/sammo/DTO/AuctionBidItemData.php b/hwe/sammo/DTO/AuctionBidItemData.php index 5d065ddb..86f7e9d3 100644 --- a/hwe/sammo/DTO/AuctionBidItemData.php +++ b/hwe/sammo/DTO/AuctionBidItemData.php @@ -2,9 +2,11 @@ namespace sammo\DTO; -use Spatie\DataTransferObject\DataTransferObject; - -class AuctionBidItemData extends DataTransferObject{ - public string $ownerName; - public string $generalName; -} \ No newline at end of file +class AuctionBidItemData extends DTO +{ + public function __construct( + public string $ownerName, + public string $generalName, + ) { + } +} diff --git a/hwe/sammo/DTO/AuctionInfo.php b/hwe/sammo/DTO/AuctionInfo.php index 7f2b5ff5..d1b86113 100644 --- a/hwe/sammo/DTO/AuctionInfo.php +++ b/hwe/sammo/DTO/AuctionInfo.php @@ -2,27 +2,19 @@ namespace sammo\DTO; -use Spatie\DataTransferObject\Attributes\CastWith; -use Spatie\DataTransferObject\Attributes\Strict; -use Spatie\DataTransferObject\DataTransferObject; - use sammo\Enums\ResourceType; -use sammo\DTO\Caster\BackedEnumCaster; -//https://json2dto.atymic.dev/ - - - -#[Strict] -class AuctionInfo extends DataTransferObject +class AuctionInfo extends DTO { - public int $id; - public int $openerName; - public int $openerGeneralID; - public string $type; - public bool $finished; - #[CastWith(BackedEnumCaster::class)] - public ResourceType $reqResource; - public string $openDate; - public string $closeDate; + public function __construct( + public int $id, + public int $openerName, + public int $openerGeneralID, + public string $type, + public bool $finished, + public ResourceType $reqResource, + public string $openDate, + public string $closeDate, + ) { + } } diff --git a/hwe/sammo/DTO/Caster/BackedEnumCaster.php b/hwe/sammo/DTO/Caster/BackedEnumCaster.php deleted file mode 100644 index d8c9b04e..00000000 --- a/hwe/sammo/DTO/Caster/BackedEnumCaster.php +++ /dev/null @@ -1,27 +0,0 @@ -backedEnumFrom = $reflector->getMethod('from'); - } - - public function cast(mixed $value): \BackedEnum - { - if(!is_string($value) && !is_int($value)){ - throw new \Exception('BackedEnumCaster only accepts string|int'); - } - - return $this->backedEnumFrom->invoke(null, $value); - } -} \ No newline at end of file -- 2.54.0 From 9f76bc786f651c45c14d76d673cffceda8bf7015 Mon Sep 17 00:00:00 2001 From: Hide_D Date: Wed, 25 May 2022 02:50:01 +0900 Subject: [PATCH 05/23] =?UTF-8?q?feat(wip):=20=EA=B2=BD=EB=A7=A4=20?= =?UTF-8?q?=EC=9E=85=EC=B0=B0=20=EA=B8=B0=EB=8A=A5?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- hwe/sammo/API/Auction/OpenUniqueAuction.php | 71 +++++++ hwe/sammo/Auction.php | 207 +++++++++++++++++++- hwe/sammo/DTO/AuctionBidItem.php | 4 +- hwe/sammo/DTO/AuctionBidItemData.php | 7 +- hwe/sammo/DTO/AuctionInfo.php | 2 + hwe/sammo/Enums/ResourceType.php | 9 + 6 files changed, 293 insertions(+), 7 deletions(-) create mode 100644 hwe/sammo/API/Auction/OpenUniqueAuction.php diff --git a/hwe/sammo/API/Auction/OpenUniqueAuction.php b/hwe/sammo/API/Auction/OpenUniqueAuction.php new file mode 100644 index 00000000..21c6e2ee --- /dev/null +++ b/hwe/sammo/API/Auction/OpenUniqueAuction.php @@ -0,0 +1,71 @@ + $amount) { + if ($amount == 0) { + continue; + } + $availableItems[$itemKey] = $amount; + } + } + + $v = new Validator($this->args); + $v->rule('required', [ + 'itemID', + 'amount' + ]) + ->rule('int', 'amount') + ->rule('min', 'amount', GameConst::$inheritItemUniqueMinPoint) + ->rule('keyExists', 'item', $availableItems); + + + if (!$v->validate()) { + return $v->errorStr(); + } + return null; + } + + public function getRequiredSessionMode(): int + { + return static::REQ_GAME_LOGIN; + } + + public function launch(Session $session, ?DateTimeInterface $modifiedSince, ?string $reqEtag) + { + /** @var int */ + $bettingID = $this->args['bettingID']; + /** @var int[] */ + $bettingType = $this->args['bettingType']; + /** @var int */ + $amount = $this->args['amount']; + + $bettingHelper = new Betting($bettingID); + try { + $bettingHelper->bet($session->generalID, $session->userID, $bettingType, $amount); + } catch (\Throwable $e) { + return $e->getMessage(); + } + + return [ + 'result' => true + ]; + } +} diff --git a/hwe/sammo/Auction.php b/hwe/sammo/Auction.php index a5282512..e4c127c9 100644 --- a/hwe/sammo/Auction.php +++ b/hwe/sammo/Auction.php @@ -3,7 +3,13 @@ namespace sammo; use Ds\Map; +use sammo\DTO\AuctionBidItem; +use sammo\DTO\AuctionBidItemData; use sammo\DTO\AuctionInfo; +use sammo\Enums\InheritanceKey; +use sammo\Enums\RankColumn; +use sammo\Enums\ResourceType; +use sammo\VO\InheritancePointType; class Auction { @@ -25,22 +31,213 @@ class Auction } - static public function openAuction(AuctionInfo $info) + static public function openAuction(AuctionInfo $info, General $general): ?string { $db = DB::db(); $auctionID = $info->id; $auctionStor = KVStorage::getStorage($db, 'auction'); + + $openedAuction = $general->getAuxVar('openedAuction') ?? []; + $prevAuctionID = $openedAuction[$info->reqResource->value] ?? null; + if ($prevAuctionID !== null) { + //XXX: 이 구조보다는 차라리 DB에 insert로 넣는 게 나을 수도. + $prevAuction = new Auction($prevAuctionID, $general); + if (!$prevAuction->info->finished) { + return '아직 경매가 끝나지 않았습니다.'; + } + } + $auctionStor->setValue("id_{$auctionID}", $info->toArray()); } - public function __construct(private readonly int $auctionID) + public function __construct(private readonly int $auctionID, private General $general) { $db = DB::db(); $auctionStor = KVStorage::getStorage($db, 'auction'); - $rawBettingInfo = $auctionStor->getValue("id_{$auctionID}"); - if ($rawBettingInfo === null) { + $rawAuctionInfo = $auctionStor->getValue("id_{$auctionID}"); + if ($rawAuctionInfo === null) { throw new \RuntimeException("해당 경매가 없습니다: {$auctionID}"); } - $this->info = AuctionInfo::fromArray($rawBettingInfo); + $this->info = AuctionInfo::fromArray($rawAuctionInfo); + } + + public function closeAuction(): void + { + $db = DB::db(); + $auctionStor = KVStorage::getStorage($db, 'auction'); + + $this->info->finished = true; + + $openedAuction = $this->general->getAuxVar('openedAuction') ?? []; + if (key_exists($this->info->reqResource->value, $openedAuction)) { + unset($openedAuction[$this->info->reqResource->value]); + $this->general->setAuxVar('openedAuction', $openedAuction); + } + $auctionID = $this->info->id; + $auctionStor->setValue("id_{$auctionID}", $this->info->toArray()); + } + + private function bidInheritPoint(int $amount, ?int $priority, string $now): void + { + $db = DB::db(); + + $auctionInfo = $this->info; + $general = $this->general; + + $rawHighestBid = $db->queryFirstRow( + 'SELECT * FROM ng_auction WHERE auction_id = %i ORDER BY `amount` DESC LIMIT 1', + $auctionInfo->id + ); + if (!$rawHighestBid) { + throw new \RuntimeException('최고 입찰가가 없습니다.'); + } + $highestBid = AuctionBidItem::fromArray($rawHighestBid); + if ($amount <= $highestBid->amount) { + throw new \RuntimeException('현재입찰가보다 높게 입찰해야 합니다.'); + } + + $rawMyPrevBid = $db->queryFirstRow( + 'SELECT * FROM ng_auction WHERE general_id = %i AND auction_id = %i ORDER BY `amount` DESC LIMIT 1', + $general->getID(), + $auctionInfo->id + ); + if ($rawMyPrevBid === null) { + $myPrevBid = null; + } else { + $myPrevBid = AuctionBidItem::fromArray($rawMyPrevBid); + } + + $morePoint = $amount - ($myPrevBid ? $myPrevBid->amount : 0); + $currPoint = $general->getInheritancePoint(InheritanceKey::previous); + if($currPoint === null || $currPoint < $morePoint){ + throw new \RuntimeException('유산포인트가 부족합니다.'); + } + + //여기서부터 입찰 성공 + + $newBid = new AuctionBidItem( + null, + $auctionInfo->id, + $general->getVar('owner'), + $general->getID(), + $amount, + $now, + new AuctionBidItemData( + $general->getVar('owner_name'), + $general->getName(), + $priority + ) + ); + $db->insert('ng_auction', $newBid->toArray()); + + $general->increaseInheritancePoint(InheritanceKey::previous, -$morePoint); + $general->increaseRankVar(RankColumn::inherit_point_spent_dynamic, $morePoint); + + if ($myPrevBid === null || $myPrevBid->id !== $highestBid->id) { + $oldBidder = General::createGeneralObjFromDB($highestBid->general_id); + $oldBidder->increaseInheritancePoint(InheritanceKey::previous, $highestBid->amount); + //TODO: 전역 알림이 나타나야한다. + $oldBidder->getLogger()->pushGeneralActionLog( + "유산포인트 {$auctionInfo->id}번 경매에 상회입찰이 되었습니다.", + ); + $oldBidder->applyDB($db); + } + $general->applyDB($db); + } + + public function bid(int $amount, ?int $priority): void + { + $auctionInfo = $this->info; + $general = $this->general; + + if ($auctionInfo->finished) { + throw new \RuntimeException('경매가 이미 끝났습니다.'); + } + + $now = TimeUtil::now(); + + if ($auctionInfo->closeDate < $now) { + throw new \RuntimeException('경매가 이미 끝났습니다.'); + } + if ($auctionInfo->closeDate > $now) { + throw new \RuntimeException('경매가 아직 시작되지 않았습니다.'); + } + + if ($auctionInfo->buyImmediatelyAmount !== null && $auctionInfo->buyImmediatelyAmount < $amount) { + throw new \RuntimeException('즉시판매가보다 높을 수 없습니다.'); + } + + if ($auctionInfo->reqResource === ResourceType::inheritancePoint) { + $this->bidInheritPoint($amount, $priority, $now); + return; + } + + //reqResource는 말 그대로 '구매자가 내야하는 자원'이다. + + $db = DB::db(); + + $rawHighestBid = $db->queryFirstRow( + 'SELECT * FROM ng_auction WHERE auction_id = %i ORDER BY `amount` DESC LIMIT 1', + $auctionInfo->id + ); + if (!$rawHighestBid) { + throw new \RuntimeException('최고 입찰가가 없습니다.'); + } + $highestBid = AuctionBidItem::fromArray($rawHighestBid); + if ($amount <= $highestBid->amount) { + throw new \RuntimeException('현재입찰가보다 높게 입찰해야 합니다.'); + } + + $rawMyPrevBid = $db->queryFirstRow( + 'SELECT * FROM ng_auction WHERE general_id = %i AND auction_id = %i ORDER BY `amount` DESC LIMIT 1', + $general->getID(), + $auctionInfo->id + ); + if ($rawMyPrevBid === null) { + $myPrevBid = null; + } else { + $myPrevBid = AuctionBidItem::fromArray($rawMyPrevBid); + } + + $morePoint = $amount - ($myPrevBid ? $myPrevBid->amount : 0); + $resType = $auctionInfo->reqResource; + $minReqRes = match ($resType) { + ResourceType::gold => GameConst::$defaultGold, + ResourceType::rice => GameConst::$defaultRice, + }; + + if ($general->getVar($resType->value) < $morePoint + $minReqRes) { + throw new \RuntimeException($resType->getName() . '이 부족합니다.'); + } + + //여기서부터 입찰 성공 + + $newBid = new AuctionBidItem( + null, + $auctionInfo->id, + $general->getVar('owner'), + $general->getID(), + $amount, + $now, + new AuctionBidItemData( + $general->getVar('owner_name'), + $general->getName(), + $priority + ) + ); + $db->insert('ng_auction', $newBid->toArray()); + + $general->setVar($resType->value, $general->getVar($resType->value) - $morePoint); + + if ($myPrevBid === null || $myPrevBid->id !== $highestBid->id) { + $oldBidder = General::createGeneralObjFromDB($highestBid->general_id); + $oldBidder->increaseVar($resType->value, $highestBid->amount); + //TODO: 전역 알림이 나타나야한다. + $oldBidder->getLogger()->pushGeneralActionLog( + "{$resType->getName()} {$auctionInfo->id}번 경매에 상회입찰이 되었습니다.", + ); + $oldBidder->applyDB($db); + } + $general->applyDB($db); } } diff --git a/hwe/sammo/DTO/AuctionBidItem.php b/hwe/sammo/DTO/AuctionBidItem.php index 296a2df7..d16beffc 100644 --- a/hwe/sammo/DTO/AuctionBidItem.php +++ b/hwe/sammo/DTO/AuctionBidItem.php @@ -3,12 +3,14 @@ namespace sammo\DTO; use sammo\DTO\Attr\JsonString; +use sammo\DTO\Attr\NullIsUndefined; use sammo\DTO\Attr\RawName; class AuctionBidItem extends DTO { public function __construct( - public int $id, + #[NullIsUndefined] + public ?int $id, #[RawName('auction_id')] public int $auctionID, public ?int $owner, diff --git a/hwe/sammo/DTO/AuctionBidItemData.php b/hwe/sammo/DTO/AuctionBidItemData.php index 86f7e9d3..76d51e9d 100644 --- a/hwe/sammo/DTO/AuctionBidItemData.php +++ b/hwe/sammo/DTO/AuctionBidItemData.php @@ -2,11 +2,16 @@ namespace sammo\DTO; +use sammo\DTO\Attr\NullIsUndefined; + class AuctionBidItemData extends DTO { public function __construct( - public string $ownerName, + #[NullIsUndefined] + public ?string $ownerName, public string $generalName, + #[NullIsUndefined] + public ?int $priority, ) { } } diff --git a/hwe/sammo/DTO/AuctionInfo.php b/hwe/sammo/DTO/AuctionInfo.php index d1b86113..7534081c 100644 --- a/hwe/sammo/DTO/AuctionInfo.php +++ b/hwe/sammo/DTO/AuctionInfo.php @@ -13,6 +13,8 @@ class AuctionInfo extends DTO public string $type, public bool $finished, public ResourceType $reqResource, + public int $minAmount, + public ?int $buyImmediatelyAmount, public string $openDate, public string $closeDate, ) { diff --git a/hwe/sammo/Enums/ResourceType.php b/hwe/sammo/Enums/ResourceType.php index d24481d0..ca0f0348 100644 --- a/hwe/sammo/Enums/ResourceType.php +++ b/hwe/sammo/Enums/ResourceType.php @@ -6,4 +6,13 @@ enum ResourceType: string case gold = 'gold'; case rice = 'rice'; case inheritancePoint = 'inheritancePoint'; + + public function getName(): string + { + return match($this){ + ResourceType::gold => '금', + ResourceType::rice => '쌀', + ResourceType::inheritancePoint => '유산 포인트', + }; + } } -- 2.54.0 From a61a1814b1ad2481eed220725097f0b99c7b1542 Mon Sep 17 00:00:00 2001 From: Hide_D Date: Thu, 26 May 2022 03:23:58 +0900 Subject: [PATCH 06/23] =?UTF-8?q?feat(wip):=20=EA=B2=BD=EB=A7=A4=20?= =?UTF-8?q?=EC=84=B8=EB=B6=80=20=EA=B8=B0=EB=8A=A5=20=EA=B5=AC=ED=98=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- hwe/sammo/Auction.php | 145 +++++++++++++++++++-------- hwe/sammo/DTO/AuctionBidItem.php | 5 +- hwe/sammo/DTO/AuctionBidItemData.php | 2 - hwe/sammo/DTO/AuctionInfo.php | 10 +- 4 files changed, 117 insertions(+), 45 deletions(-) diff --git a/hwe/sammo/Auction.php b/hwe/sammo/Auction.php index e4c127c9..e390cee5 100644 --- a/hwe/sammo/Auction.php +++ b/hwe/sammo/Auction.php @@ -2,6 +2,9 @@ namespace sammo; +use DateTime; +use DateTimeImmutable; +use DateTimeInterface; use Ds\Map; use sammo\DTO\AuctionBidItem; use sammo\DTO\AuctionBidItemData; @@ -45,11 +48,41 @@ class Auction if (!$prevAuction->info->finished) { return '아직 경매가 끝나지 않았습니다.'; } + unset($openedAuction[$info->reqResource->value]); + $general->setAuxVar('openedAuction', $openedAuction); } $auctionStor->setValue("id_{$auctionID}", $info->toArray()); } + public function getHighestBid(): AuctionBidItem + { + $db = DB::db(); + $rawHighestBid = $db->queryFirstRow( + 'SELECT * FROM ng_auction WHERE auction_id = %i ORDER BY `amount` DESC LIMIT 1', + $this->info->id + ); + if(!$rawHighestBid){ + throw new \Exception('입찰이 없습니다.'); + } + + return AuctionBidItem::fromArray($rawHighestBid); + } + + public function getMyPrevBid(): ?AuctionBidItem + { + $db = DB::db(); + $rawMyPrevBid = $db->queryFirstRow( + 'SELECT * FROM ng_auction WHERE general_id = %i AND auction_id = %i ORDER BY `amount` DESC LIMIT 1', + $this->general->getID(), + $this->info->id + ); + if (!$rawMyPrevBid) { + return null; + } + return AuctionBidItem::fromArray($rawMyPrevBid); + } + public function __construct(private readonly int $auctionID, private General $general) { $db = DB::db(); @@ -61,7 +94,66 @@ class Auction $this->info = AuctionInfo::fromArray($rawAuctionInfo); } - public function closeAuction(): void + public function getInfo(): AuctionInfo + { + return $this->info; + } + + public function extendCloseDate(DateTimeInterface $date): void + { + $db = DB::db(); + $auctionStor = KVStorage::getStorage($db, 'auction'); + $this->info->closeDate = DateTimeImmutable::createFromInterface($date); + $auctionStor->setValue("id_{$this->info->id}", $this->info->toArray()); + } + + public function refundBid(AuctionBidItem $bidItem, string $reason): void + { + if($bidItem->auctionID !== $this->info->id){ + throw new \RuntimeException('잘못된 경매입니다.'); + } + + $db = DB::db(); + if($bidItem->generalID === $this->general->generalID) { + $oldBidder = $this->general; + } else { + $oldBidder = General::createGeneralObjFromDB($bidItem->general_id); + } + + if($this->info->reqResource === ResourceType::inheritancePoint){ + $oldBidder->increaseInheritancePoint(InheritanceKey::previous, $bidItem->amount); + } + else{ + $oldBidder->increaseVar($this->info->reqResource->value, $bidItem->amount); + } + + $staticNation = $oldBidder->getStaticNation(); + $src = new MessageTarget(0, '', 0, 'System', '#000000'); + $dest = new MessageTarget( + $oldBidder->getID(), + $oldBidder->getName(), + $oldBidder->getNationID(), + $staticNation['name'], + $staticNation['color'], + GetImageURL($oldBidder->getVar('imgsvr'), $oldBidder->getVar('picture')) + ); + + //TODO: 전역 알림이 나타나야한다. 일반 메시지보다는 중요하고, 메시지보단 약하게.. + //TODO: 바로가기를 제공하는 편이 좋을 것 같다. + $msg = new Message( + Message::MSGTYPE_PRIVATE, + $src, + $dest, + $reason, + new DateTime(), + new DateTime('9999-12-31'), + [] + ); + $oldBidder->applyDB($db); + $msg->send(true); + } + + public function closeAuction(bool $isRollback = false): void { $db = DB::db(); $auctionStor = KVStorage::getStorage($db, 'auction'); @@ -73,25 +165,21 @@ class Auction unset($openedAuction[$this->info->reqResource->value]); $this->general->setAuxVar('openedAuction', $openedAuction); } + if ($isRollback) { + $this->refundBid($this->getHighestBid(), "{$this->info->title} 경매가 취소되었습니다."); + } $auctionID = $this->info->id; $auctionStor->setValue("id_{$auctionID}", $this->info->toArray()); } - private function bidInheritPoint(int $amount, ?int $priority, string $now): void + private function bidInheritPoint(int $amount, \DateTimeImmutable $now): void { $db = DB::db(); $auctionInfo = $this->info; $general = $this->general; - $rawHighestBid = $db->queryFirstRow( - 'SELECT * FROM ng_auction WHERE auction_id = %i ORDER BY `amount` DESC LIMIT 1', - $auctionInfo->id - ); - if (!$rawHighestBid) { - throw new \RuntimeException('최고 입찰가가 없습니다.'); - } - $highestBid = AuctionBidItem::fromArray($rawHighestBid); + $highestBid = $this->getHighestBid(); if ($amount <= $highestBid->amount) { throw new \RuntimeException('현재입찰가보다 높게 입찰해야 합니다.'); } @@ -109,7 +197,7 @@ class Auction $morePoint = $amount - ($myPrevBid ? $myPrevBid->amount : 0); $currPoint = $general->getInheritancePoint(InheritanceKey::previous); - if($currPoint === null || $currPoint < $morePoint){ + if ($currPoint === null || $currPoint < $morePoint) { throw new \RuntimeException('유산포인트가 부족합니다.'); } @@ -125,7 +213,6 @@ class Auction new AuctionBidItemData( $general->getVar('owner_name'), $general->getName(), - $priority ) ); $db->insert('ng_auction', $newBid->toArray()); @@ -134,18 +221,12 @@ class Auction $general->increaseRankVar(RankColumn::inherit_point_spent_dynamic, $morePoint); if ($myPrevBid === null || $myPrevBid->id !== $highestBid->id) { - $oldBidder = General::createGeneralObjFromDB($highestBid->general_id); - $oldBidder->increaseInheritancePoint(InheritanceKey::previous, $highestBid->amount); - //TODO: 전역 알림이 나타나야한다. - $oldBidder->getLogger()->pushGeneralActionLog( - "유산포인트 {$auctionInfo->id}번 경매에 상회입찰이 되었습니다.", - ); - $oldBidder->applyDB($db); + $this->refundBid($highestBid, "{$auctionInfo->title} 경매에 상회입찰자가 나타났습니다."); } $general->applyDB($db); } - public function bid(int $amount, ?int $priority): void + public function bid(int $amount): void { $auctionInfo = $this->info; $general = $this->general; @@ -154,7 +235,7 @@ class Auction throw new \RuntimeException('경매가 이미 끝났습니다.'); } - $now = TimeUtil::now(); + $now = new \DateTimeImmutable(); if ($auctionInfo->closeDate < $now) { throw new \RuntimeException('경매가 이미 끝났습니다.'); @@ -168,7 +249,7 @@ class Auction } if ($auctionInfo->reqResource === ResourceType::inheritancePoint) { - $this->bidInheritPoint($amount, $priority, $now); + $this->bidInheritPoint($amount, $now); return; } @@ -188,16 +269,7 @@ class Auction throw new \RuntimeException('현재입찰가보다 높게 입찰해야 합니다.'); } - $rawMyPrevBid = $db->queryFirstRow( - 'SELECT * FROM ng_auction WHERE general_id = %i AND auction_id = %i ORDER BY `amount` DESC LIMIT 1', - $general->getID(), - $auctionInfo->id - ); - if ($rawMyPrevBid === null) { - $myPrevBid = null; - } else { - $myPrevBid = AuctionBidItem::fromArray($rawMyPrevBid); - } + $myPrevBid = $this->getMyPrevBid(); $morePoint = $amount - ($myPrevBid ? $myPrevBid->amount : 0); $resType = $auctionInfo->reqResource; @@ -222,7 +294,6 @@ class Auction new AuctionBidItemData( $general->getVar('owner_name'), $general->getName(), - $priority ) ); $db->insert('ng_auction', $newBid->toArray()); @@ -230,13 +301,7 @@ class Auction $general->setVar($resType->value, $general->getVar($resType->value) - $morePoint); if ($myPrevBid === null || $myPrevBid->id !== $highestBid->id) { - $oldBidder = General::createGeneralObjFromDB($highestBid->general_id); - $oldBidder->increaseVar($resType->value, $highestBid->amount); - //TODO: 전역 알림이 나타나야한다. - $oldBidder->getLogger()->pushGeneralActionLog( - "{$resType->getName()} {$auctionInfo->id}번 경매에 상회입찰이 되었습니다.", - ); - $oldBidder->applyDB($db); + $this->refundBid($highestBid, "{$auctionInfo->title} 경매에 상회입찰자가 나타났습니다."); } $general->applyDB($db); } diff --git a/hwe/sammo/DTO/AuctionBidItem.php b/hwe/sammo/DTO/AuctionBidItem.php index d16beffc..d4eecc84 100644 --- a/hwe/sammo/DTO/AuctionBidItem.php +++ b/hwe/sammo/DTO/AuctionBidItem.php @@ -2,9 +2,11 @@ namespace sammo\DTO; +use sammo\DTO\Attr\Convert; use sammo\DTO\Attr\JsonString; use sammo\DTO\Attr\NullIsUndefined; use sammo\DTO\Attr\RawName; +use sammo\DTO\Converter\DateTimeConverter; class AuctionBidItem extends DTO { @@ -20,7 +22,8 @@ class AuctionBidItem extends DTO public int $amount, - public string $date, + #[Convert(DateTimeConverter::class)] + public \DateTimeImmutable $date, #[JsonString] public AuctionBidItemData $data, ) { diff --git a/hwe/sammo/DTO/AuctionBidItemData.php b/hwe/sammo/DTO/AuctionBidItemData.php index 76d51e9d..41478689 100644 --- a/hwe/sammo/DTO/AuctionBidItemData.php +++ b/hwe/sammo/DTO/AuctionBidItemData.php @@ -10,8 +10,6 @@ class AuctionBidItemData extends DTO #[NullIsUndefined] public ?string $ownerName, public string $generalName, - #[NullIsUndefined] - public ?int $priority, ) { } } diff --git a/hwe/sammo/DTO/AuctionInfo.php b/hwe/sammo/DTO/AuctionInfo.php index 7534081c..6854ed37 100644 --- a/hwe/sammo/DTO/AuctionInfo.php +++ b/hwe/sammo/DTO/AuctionInfo.php @@ -2,6 +2,8 @@ namespace sammo\DTO; +use sammo\DTO\Attr\Convert; +use sammo\DTO\Converter\DateTimeConverter; use sammo\Enums\ResourceType; class AuctionInfo extends DTO @@ -11,12 +13,16 @@ class AuctionInfo extends DTO public int $openerName, public int $openerGeneralID, public string $type, + public string $title, public bool $finished, public ResourceType $reqResource, public int $minAmount, public ?int $buyImmediatelyAmount, - public string $openDate, - public string $closeDate, + + #[Convert(DateTimeConverter::class)] + public \DateTimeImmutable $openDate, + #[Convert(DateTimeConverter::class)] + public \DateTimeImmutable $closeDate, ) { } } -- 2.54.0 From 48dbd449bd8f5748a4c545f3776f2329453636e4 Mon Sep 17 00:00:00 2001 From: Hide_D Date: Fri, 27 May 2022 03:14:22 +0900 Subject: [PATCH 07/23] =?UTF-8?q?feat(wip):=20=EC=9C=A0=EB=8B=88=ED=81=AC?= =?UTF-8?q?=20=EC=98=A5=EC=85=98=20=EC=8B=9C=EC=9E=91?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- hwe/sammo/Auction.php | 31 +++++------ hwe/sammo/AuctionUniqueItem.php | 95 +++++++++++++++++++++++++++++++++ hwe/sammo/DTO/AuctionInfo.php | 2 +- 3 files changed, 110 insertions(+), 18 deletions(-) create mode 100644 hwe/sammo/AuctionUniqueItem.php diff --git a/hwe/sammo/Auction.php b/hwe/sammo/Auction.php index e390cee5..c4fc69d6 100644 --- a/hwe/sammo/Auction.php +++ b/hwe/sammo/Auction.php @@ -17,7 +17,7 @@ use sammo\VO\InheritancePointType; class Auction { - private AuctionInfo $info; + protected AuctionInfo $info; public const LAST_AUCTION_ID_KEY = 'last_auction_id'; static public function genNextAuctionID(): int @@ -55,7 +55,7 @@ class Auction $auctionStor->setValue("id_{$auctionID}", $info->toArray()); } - public function getHighestBid(): AuctionBidItem + public function getHighestBid(): ?AuctionBidItem { $db = DB::db(); $rawHighestBid = $db->queryFirstRow( @@ -63,7 +63,7 @@ class Auction $this->info->id ); if(!$rawHighestBid){ - throw new \Exception('입찰이 없습니다.'); + return null; } return AuctionBidItem::fromArray($rawHighestBid); @@ -83,7 +83,7 @@ class Auction return AuctionBidItem::fromArray($rawMyPrevBid); } - public function __construct(private readonly int $auctionID, private General $general) + public function __construct(protected readonly int $auctionID, protected General $general) { $db = DB::db(); $auctionStor = KVStorage::getStorage($db, 'auction'); @@ -166,7 +166,11 @@ class Auction $this->general->setAuxVar('openedAuction', $openedAuction); } if ($isRollback) { - $this->refundBid($this->getHighestBid(), "{$this->info->title} 경매가 취소되었습니다."); + $highestBid = $this->getHighestBid(); + if($highestBid !== null){ + $this->refundBid($highestBid, "{$this->info->title} 경매가 취소되었습니다."); + } + } $auctionID = $this->info->id; $auctionStor->setValue("id_{$auctionID}", $this->info->toArray()); @@ -180,7 +184,7 @@ class Auction $general = $this->general; $highestBid = $this->getHighestBid(); - if ($amount <= $highestBid->amount) { + if ($highestBid !== null && $amount <= $highestBid->amount) { throw new \RuntimeException('현재입찰가보다 높게 입찰해야 합니다.'); } @@ -220,7 +224,7 @@ class Auction $general->increaseInheritancePoint(InheritanceKey::previous, -$morePoint); $general->increaseRankVar(RankColumn::inherit_point_spent_dynamic, $morePoint); - if ($myPrevBid === null || $myPrevBid->id !== $highestBid->id) { + if ($myPrevBid === null || ($highestBid !== null && $myPrevBid->id !== $highestBid->id)) { $this->refundBid($highestBid, "{$auctionInfo->title} 경매에 상회입찰자가 나타났습니다."); } $general->applyDB($db); @@ -257,15 +261,8 @@ class Auction $db = DB::db(); - $rawHighestBid = $db->queryFirstRow( - 'SELECT * FROM ng_auction WHERE auction_id = %i ORDER BY `amount` DESC LIMIT 1', - $auctionInfo->id - ); - if (!$rawHighestBid) { - throw new \RuntimeException('최고 입찰가가 없습니다.'); - } - $highestBid = AuctionBidItem::fromArray($rawHighestBid); - if ($amount <= $highestBid->amount) { + $highestBid = $this->getHighestBid(); + if ($highestBid !== null && $amount <= $highestBid->amount) { throw new \RuntimeException('현재입찰가보다 높게 입찰해야 합니다.'); } @@ -300,7 +297,7 @@ class Auction $general->setVar($resType->value, $general->getVar($resType->value) - $morePoint); - if ($myPrevBid === null || $myPrevBid->id !== $highestBid->id) { + if ($myPrevBid === null || ($highestBid !== null && $myPrevBid->id !== $highestBid->id)) { $this->refundBid($highestBid, "{$auctionInfo->title} 경매에 상회입찰자가 나타났습니다."); } $general->applyDB($db); diff --git a/hwe/sammo/AuctionUniqueItem.php b/hwe/sammo/AuctionUniqueItem.php new file mode 100644 index 00000000..e52969d2 --- /dev/null +++ b/hwe/sammo/AuctionUniqueItem.php @@ -0,0 +1,95 @@ +getValue('obfuscatedNamePool'); + if($namePool === null){ + $rng = new RandUtil(new LiteHashDRBG(Util::simpleSerialize( + UniqueConst::$hiddenSeed, + ))); + $namePool = []; + foreach(GameConst::$randGenFirstName as $ch0){ + foreach(GameConst::$randGenMiddleName as $ch1){ + foreach(GameConst::$randGenLastName as $ch2){ + $namePool[] = "{$ch0}{$ch1}{$ch2}"; + } + } + } + $namePool = $rng->shuffle($namePool); + $gameStor->setValue('obfuscatedNamePool', $namePool); + } + + + $dupIdx = intdiv($id, count($namePool)); + $subIdx = $id % count($namePool); + if ($dupIdx == 0) { + return $namePool[$subIdx]; + } + return "{$namePool[$subIdx]}{$dupIdx}"; + } + + static public function openItemAuction(BaseItem $item, General $general, int $startAmount): ?string + { + if ($startAmount < GameConst::$inheritItemUniqueMinPoint) { + return '최소 경매 금액은 ' . GameConst::$inheritItemUniqueMinPoint . '입니다.'; + } + + if($general->getInheritancePoint(InheritanceKey::previous) < $startAmount){ + return '경매를 시작할 포인트가 부족합니다.'; + } + + $auctionID = static::genNextAuctionID(); + $now = new DateTimeImmutable(); + + $db = DB::db(); + $gameStor = KVStorage::getStorage($db, 'game_env'); + $turnTerm = $gameStor->getValue('turnterm'); + + $closeDate = $now->add(TimeUtil::secondsToDateInterval(24 * 60 * $turnTerm * 60)); + + $info = new AuctionInfo( + $auctionID, + static::genObfuscatedName($general->getID()), + $general->getID(), + $item->getRawClassName(), + "{$item->getName()} 경매", + false, + ResourceType::inheritancePoint, + $startAmount, + null, + $now, + $closeDate + ); + + $result = static::openAuction($info, $general); + if($result !== null){ + return $result; + } + $auction = new static($auctionID, $general); + try{ + $auction->bid($startAmount); + } + catch(\Exception $e){ + //실패해선 안된다. + $msg = $e->getMessage(); + $auction->closeAuction(); + return "경매를 시작했지만, 첫 입찰에 실패했습니다: {$msg}"; + } + + return null; + } +} diff --git a/hwe/sammo/DTO/AuctionInfo.php b/hwe/sammo/DTO/AuctionInfo.php index 6854ed37..c34dd50f 100644 --- a/hwe/sammo/DTO/AuctionInfo.php +++ b/hwe/sammo/DTO/AuctionInfo.php @@ -10,7 +10,7 @@ class AuctionInfo extends DTO { public function __construct( public int $id, - public int $openerName, + public string $openerName, public int $openerGeneralID, public string $type, public string $title, -- 2.54.0 From 6e87aef09fb92904243ebb4749a8ad3eb8757fad Mon Sep 17 00:00:00 2001 From: Hide_D Date: Mon, 30 May 2022 01:10:23 +0900 Subject: [PATCH 08/23] =?UTF-8?q?feat(wip):=20=EC=9C=A0=EB=8B=88=ED=81=AC?= =?UTF-8?q?=20=EA=B2=BD=EB=A7=A4=20=EC=84=B8=EB=B6=80=20=EC=A7=84=ED=96=89?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- hwe/sammo/Auction.php | 108 +++++++++++---- hwe/sammo/AuctionUniqueItem.php | 197 +++++++++++++++++++++++++-- hwe/sammo/DTO/AuctionBidItemData.php | 2 + hwe/sammo/DTO/AuctionInfo.php | 7 +- hwe/sammo/Enums/AuctionType.php | 8 ++ 5 files changed, 281 insertions(+), 41 deletions(-) create mode 100644 hwe/sammo/Enums/AuctionType.php diff --git a/hwe/sammo/Auction.php b/hwe/sammo/Auction.php index c4fc69d6..62ce874e 100644 --- a/hwe/sammo/Auction.php +++ b/hwe/sammo/Auction.php @@ -20,6 +20,16 @@ class Auction protected AuctionInfo $info; public const LAST_AUCTION_ID_KEY = 'last_auction_id'; + + public const COEFF_AUCTION_CLOSE_MINUTES = 24; + public const COEFF_EXTENSION_MINUTES_PER_BID = 1 / 6; + public const COEFF_EXTENSION_MINUTES_LIMIT_BY_BID = 1; + public const COEFF_EXTENSION_MINUTES_BY_EXTENSION_QUERY = 1; + public const MIN_AUCTION_CLOSE_MINUTES = 30; + public const MIN_EXTENSION_MINUTES_PER_BID = 1; + public const MIN_EXTENSION_MINUTES_LIMIT_BY_BID = 5; + public const MIN_EXTENSION_MINUTES_BY_EXTENSION_QUERY = 5; + static public function genNextAuctionID(): int { $db = DB::db(); @@ -41,14 +51,14 @@ class Auction $auctionStor = KVStorage::getStorage($db, 'auction'); $openedAuction = $general->getAuxVar('openedAuction') ?? []; - $prevAuctionID = $openedAuction[$info->reqResource->value] ?? null; + $prevAuctionID = $openedAuction[$info->type->value] ?? null; if ($prevAuctionID !== null) { //XXX: 이 구조보다는 차라리 DB에 insert로 넣는 게 나을 수도. $prevAuction = new Auction($prevAuctionID, $general); if (!$prevAuction->info->finished) { return '아직 경매가 끝나지 않았습니다.'; } - unset($openedAuction[$info->reqResource->value]); + unset($openedAuction[$info->type->value]); $general->setAuxVar('openedAuction', $openedAuction); } @@ -62,7 +72,7 @@ class Auction 'SELECT * FROM ng_auction WHERE auction_id = %i ORDER BY `amount` DESC LIMIT 1', $this->info->id ); - if(!$rawHighestBid){ + if (!$rawHighestBid) { return null; } @@ -99,31 +109,49 @@ class Auction return $this->info; } - public function extendCloseDate(DateTimeInterface $date): void + public function extendCloseDate(DateTimeInterface $date, bool $force = false): ?string { + if(!$force){ + if ($this->info->remainCloseExtensionCnt === null) { + return '연장할 수 없는 경매입니다.'; + } + if ($this->info->remainCloseExtensionCnt === 0) { + return '더 이상 연장할 수 없습니다'; + } + if ($this->info->remainCloseExtensionCnt > 0) { + $this->info->remainCloseExtensionCnt--; + } + } + $db = DB::db(); $auctionStor = KVStorage::getStorage($db, 'auction'); - $this->info->closeDate = DateTimeImmutable::createFromInterface($date); + $gameStor = KVStorage::getStorage($db, 'game_env'); + $turnTerm = $gameStor->getValue('turnterm'); + $closeDate = DateTimeImmutable::createFromInterface($date); + $this->info->closeDate = $closeDate; + $this->info->availableLatestBidCloseDate = $closeDate->add(TimeUtil::secondsToDateInterval( + max(static::MIN_EXTENSION_MINUTES_PER_BID, $turnTerm * static::COEFF_EXTENSION_MINUTES_PER_BID) * 60 + )); $auctionStor->setValue("id_{$this->info->id}", $this->info->toArray()); + return null; } public function refundBid(AuctionBidItem $bidItem, string $reason): void { - if($bidItem->auctionID !== $this->info->id){ + if ($bidItem->auctionID !== $this->info->id) { throw new \RuntimeException('잘못된 경매입니다.'); } $db = DB::db(); - if($bidItem->generalID === $this->general->generalID) { + if ($bidItem->generalID === $this->general->generalID) { $oldBidder = $this->general; } else { - $oldBidder = General::createGeneralObjFromDB($bidItem->general_id); + $oldBidder = General::createGeneralObjFromDB($bidItem->generalID); } - if($this->info->reqResource === ResourceType::inheritancePoint){ + if ($this->info->reqResource === ResourceType::inheritancePoint) { $oldBidder->increaseInheritancePoint(InheritanceKey::previous, $bidItem->amount); - } - else{ + } else { $oldBidder->increaseVar($this->info->reqResource->value, $bidItem->amount); } @@ -161,22 +189,21 @@ class Auction $this->info->finished = true; $openedAuction = $this->general->getAuxVar('openedAuction') ?? []; - if (key_exists($this->info->reqResource->value, $openedAuction)) { - unset($openedAuction[$this->info->reqResource->value]); + if (key_exists($this->info->type->value, $openedAuction)) { + unset($openedAuction[$this->info->type->value]); $this->general->setAuxVar('openedAuction', $openedAuction); } if ($isRollback) { $highestBid = $this->getHighestBid(); - if($highestBid !== null){ + if ($highestBid !== null) { $this->refundBid($highestBid, "{$this->info->title} 경매가 취소되었습니다."); } - } $auctionID = $this->info->id; $auctionStor->setValue("id_{$auctionID}", $this->info->toArray()); } - private function bidInheritPoint(int $amount, \DateTimeImmutable $now): void + private function bidInheritPoint(int $amount, \DateTimeImmutable $now, bool $tryExtendCloseDate): ?string { $db = DB::db(); @@ -185,7 +212,7 @@ class Auction $highestBid = $this->getHighestBid(); if ($highestBid !== null && $amount <= $highestBid->amount) { - throw new \RuntimeException('현재입찰가보다 높게 입찰해야 합니다.'); + return '현재입찰가보다 높게 입찰해야 합니다.'; } $rawMyPrevBid = $db->queryFirstRow( @@ -202,7 +229,7 @@ class Auction $morePoint = $amount - ($myPrevBid ? $myPrevBid->amount : 0); $currPoint = $general->getInheritancePoint(InheritanceKey::previous); if ($currPoint === null || $currPoint < $morePoint) { - throw new \RuntimeException('유산포인트가 부족합니다.'); + return '유산포인트가 부족합니다.'; } //여기서부터 입찰 성공 @@ -217,9 +244,26 @@ class Auction new AuctionBidItemData( $general->getVar('owner_name'), $general->getName(), + $tryExtendCloseDate, ) ); $db->insert('ng_auction', $newBid->toArray()); + if ($db->affectedRows() == 0) { + return '입찰에 실패했습니다: DB 오류'; + } + + $gameStor = KVStorage::getStorage($db, 'game_env'); + $turnTerm = $gameStor->getValue('turnterm'); + + if ($this->info->availableLatestBidCloseDate !== null) { + $extendedCloseDate = $this->info->closeDate->add(TimeUtil::secondsToDateInterval( + max(static::MIN_EXTENSION_MINUTES_PER_BID, $turnTerm * static::COEFF_EXTENSION_MINUTES_PER_BID) * 60 + )); + + if ($extendedCloseDate > $this->info->closeDate && $this->info->closeDate < $this->info->availableLatestBidCloseDate) { + $this->extendCloseDate(min($extendedCloseDate, $this->info->availableLatestBidCloseDate)); + } + } $general->increaseInheritancePoint(InheritanceKey::previous, -$morePoint); $general->increaseRankVar(RankColumn::inherit_point_spent_dynamic, $morePoint); @@ -228,33 +272,33 @@ class Auction $this->refundBid($highestBid, "{$auctionInfo->title} 경매에 상회입찰자가 나타났습니다."); } $general->applyDB($db); + return null; } - public function bid(int $amount): void + public function bid(int $amount, bool $tryExtendCloseDate = false): ?string { $auctionInfo = $this->info; $general = $this->general; if ($auctionInfo->finished) { - throw new \RuntimeException('경매가 이미 끝났습니다.'); + return '경매가 이미 끝났습니다.'; } $now = new \DateTimeImmutable(); if ($auctionInfo->closeDate < $now) { - throw new \RuntimeException('경매가 이미 끝났습니다.'); + return '경매가 이미 끝났습니다.'; } - if ($auctionInfo->closeDate > $now) { - throw new \RuntimeException('경매가 아직 시작되지 않았습니다.'); + if ($auctionInfo->openDate > $now) { + return '경매가 아직 시작되지 않았습니다.'; } if ($auctionInfo->buyImmediatelyAmount !== null && $auctionInfo->buyImmediatelyAmount < $amount) { - throw new \RuntimeException('즉시판매가보다 높을 수 없습니다.'); + return '즉시판매가보다 높을 수 없습니다.'; } if ($auctionInfo->reqResource === ResourceType::inheritancePoint) { - $this->bidInheritPoint($amount, $now); - return; + return $this->bidInheritPoint($amount, $now, $tryExtendCloseDate); } //reqResource는 말 그대로 '구매자가 내야하는 자원'이다. @@ -263,7 +307,7 @@ class Auction $highestBid = $this->getHighestBid(); if ($highestBid !== null && $amount <= $highestBid->amount) { - throw new \RuntimeException('현재입찰가보다 높게 입찰해야 합니다.'); + return '현재입찰가보다 높게 입찰해야 합니다.'; } $myPrevBid = $this->getMyPrevBid(); @@ -276,7 +320,7 @@ class Auction }; if ($general->getVar($resType->value) < $morePoint + $minReqRes) { - throw new \RuntimeException($resType->getName() . '이 부족합니다.'); + return $resType->getName() . '이 부족합니다.'; } //여기서부터 입찰 성공 @@ -291,9 +335,16 @@ class Auction new AuctionBidItemData( $general->getVar('owner_name'), $general->getName(), + $tryExtendCloseDate, ) ); + $db->insert('ng_auction', $newBid->toArray()); + if ($db->affectedRows() == 0) { + return '입찰에 실패했습니다: DB 오류'; + } + + $general->setVar($resType->value, $general->getVar($resType->value) - $morePoint); @@ -301,5 +352,6 @@ class Auction $this->refundBid($highestBid, "{$auctionInfo->title} 경매에 상회입찰자가 나타났습니다."); } $general->applyDB($db); + return null; } } diff --git a/hwe/sammo/AuctionUniqueItem.php b/hwe/sammo/AuctionUniqueItem.php index e52969d2..6a6e87df 100644 --- a/hwe/sammo/AuctionUniqueItem.php +++ b/hwe/sammo/AuctionUniqueItem.php @@ -3,7 +3,9 @@ namespace sammo; use DateTimeImmutable; +use sammo\DTO\AuctionBidItem; use sammo\DTO\AuctionInfo; +use sammo\Enums\AuctionType; use sammo\Enums\InheritanceKey; use sammo\Enums\ResourceType; use sammo\RandUtil; @@ -17,14 +19,14 @@ class AuctionUniqueItem extends Auction $gameStor = KVStorage::getStorage($db, 'game_env'); $namePool = $gameStor->getValue('obfuscatedNamePool'); - if($namePool === null){ + if ($namePool === null) { $rng = new RandUtil(new LiteHashDRBG(Util::simpleSerialize( UniqueConst::$hiddenSeed, ))); $namePool = []; - foreach(GameConst::$randGenFirstName as $ch0){ - foreach(GameConst::$randGenMiddleName as $ch1){ - foreach(GameConst::$randGenLastName as $ch2){ + foreach (GameConst::$randGenFirstName as $ch0) { + foreach (GameConst::$randGenMiddleName as $ch1) { + foreach (GameConst::$randGenLastName as $ch2) { $namePool[] = "{$ch0}{$ch1}{$ch2}"; } } @@ -48,42 +50,55 @@ class AuctionUniqueItem extends Auction return '최소 경매 금액은 ' . GameConst::$inheritItemUniqueMinPoint . '입니다.'; } - if($general->getInheritancePoint(InheritanceKey::previous) < $startAmount){ + if ($general->getInheritancePoint(InheritanceKey::previous) < $startAmount) { return '경매를 시작할 포인트가 부족합니다.'; } - $auctionID = static::genNextAuctionID(); + if ($item->isBuyable()) { + return '구매할 수 있는 아이템입니다.'; + } + + if (GameConst::$allItems) + + $auctionID = static::genNextAuctionID(); $now = new DateTimeImmutable(); $db = DB::db(); $gameStor = KVStorage::getStorage($db, 'game_env'); $turnTerm = $gameStor->getValue('turnterm'); - $closeDate = $now->add(TimeUtil::secondsToDateInterval(24 * 60 * $turnTerm * 60)); + $closeDate = $now->add(TimeUtil::secondsToDateInterval( + max(static::MIN_AUCTION_CLOSE_MINUTES, $turnTerm * static::COEFF_AUCTION_CLOSE_MINUTES) * 60 + )); + $availableLatestBidCloseDate = $closeDate->add(TimeUtil::secondsToDateInterval( + max(static::MIN_EXTENSION_MINUTES_LIMIT_BY_BID, $turnTerm * static::COEFF_EXTENSION_MINUTES_LIMIT_BY_BID) * 60 + )); $info = new AuctionInfo( $auctionID, static::genObfuscatedName($general->getID()), $general->getID(), + AuctionType::UniqueItem, $item->getRawClassName(), "{$item->getName()} 경매", false, ResourceType::inheritancePoint, $startAmount, null, + 1, $now, - $closeDate + $closeDate, + $availableLatestBidCloseDate, ); $result = static::openAuction($info, $general); - if($result !== null){ + if ($result !== null) { return $result; } $auction = new static($auctionID, $general); - try{ + try { $auction->bid($startAmount); - } - catch(\Exception $e){ + } catch (\Exception $e) { //실패해선 안된다. $msg = $e->getMessage(); $auction->closeAuction(); @@ -92,4 +107,162 @@ class AuctionUniqueItem extends Auction return null; } + + private function giveUniqueItem(AuctionBidItem $highestBid, General $bidder): ?string + { + + $itemKey = $this->info->target; + $itemObj = buildItemClass($itemKey); + $general = $bidder; + $availableItemTypes = []; + $reasons = []; + $db = DB::db(); + $gameStor = KVStorage::getStorage($db, 'game_env'); + [$startYear, $year] = $gameStor->getValuesAsArray(['startyear', 'year']); + $relYear = $year - $startYear; + $availableEquipUniqueCnt = 1; + foreach (GameConst::$maxUniqueItemLimit as $tmpVals) { + [$targetYear, $targetTrialCnt] = $tmpVals; + if ($relYear < $targetYear) { + break; + } + $availableEquipUniqueCnt = $targetTrialCnt; + } + + $availableEquipUniqueCnt = Util::valueFit($availableEquipUniqueCnt, null, count(GameConst::$allItems)); + + foreach ($general->getItems() as $item) { + if (!$item->isBuyable()) { + $availableEquipUniqueCnt -= 1; + } + } + + if($availableEquipUniqueCnt <= 0){ + return '유니크 아이템 소유 제한 상태입니다. 종료 시간이 연장됩니다.'; + } + + foreach (GameConst::$allItems as $itemType => $itemList) { + //아직은 그런 경우는 없지만 동일 유니크를 여러 부위에 장착할 수 있을지도 모름 + if (!key_exists($itemKey, $itemList)) { + continue; + } + + $ownItem = $general->getItem($itemType); + if ($ownItem->getRawClassName() == $itemKey) { + //FIXME: 이 경우에는 환불이 되던가 해야함. + $reasons[] = '이미 그 유니크를 가지고 있습니다.'; + continue; + } + + if (!$ownItem->isBuyable()) { + $reasons[] = '이미 다른 유니크를 가지고 있습니다.'; + continue; + } + + $availableCnt = $itemList[$itemKey]; + $occupiedCnt = $db->queryFirstField('SELECT count(*) FROM general WHERE %b = %s', $itemType, $itemKey); + if ($occupiedCnt >= $availableCnt) { + //FIXME: 이 경우에는 환불이 되던가 해야함. + $reasons[] = '그 유니크는 모두 점유되었습니다.'; + continue; + } + $availableItemTypes[] = $itemType; + } + + if (!$availableItemTypes) { + return join(' ', $reasons); + } + + $itemType = $availableItemTypes[0]; + + $general->setVar($itemType, $itemKey); + + $logger = $general->getLogger(); + $nationName = $general->getStaticNation()['name']; + $generalName = $general->getName(); + $josaYi = JosaUtil::pick($generalName, '이'); + $itemName = $itemObj->getName(); + $itemRawName = $itemObj->getRawName(); + $josaUl = JosaUtil::pick($itemRawName, '을'); + + $logger->pushGeneralActionLog("{$itemName}{$josaUl} 습득했습니다!"); + $logger->pushGeneralHistoryLog("{$itemName}{$josaUl} 습득"); + $logger->pushGlobalActionLog("{$generalName}{$josaYi} {$itemName}{$josaUl} 습득했습니다!"); + $logger->pushGlobalHistoryLog("【보물발견】{$nationName}{$generalName}{$josaYi} {$itemName}{$josaUl} 습득했습니다!"); + + $general->applyDB($db); + return null; + } + + public function checkCloseDate(): ?bool + { + $now = new DateTimeImmutable(); + if ($now < $this->info->closeDate) { + return null; + } + + //경매를 닫아야한다. + $highestBid = $this->getHighestBid(); + if ($highestBid === null) { + $this->closeAuction(); + return true; + } + + if ($highestBid->data->tryExtendCloseDate) { + //연장 요청이 있었다. + $extendedCloseDate = $this->info->closeDate->add(TimeUtil::secondsToDateInterval( + max(static::MIN_EXTENSION_MINUTES_BY_EXTENSION_QUERY, $this->info->turnTerm * static::COEFF_EXTENSION_MINUTES_BY_EXTENSION_QUERY) * 60 + )); + + if ($this->extendCloseDate($extendedCloseDate) === null) { + return false; + } + } + + $bidder = General::createGeneralObjFromDB($highestBid->generalID); + + $failReason = $this->giveUniqueItem($highestBid, $bidder); + if ($failReason === null) { + $this->closeAuction(); + return true; + } + + $staticNation = $bidder->getStaticNation(); + $src = new MessageTarget(0, '', 0, 'System', '#000000'); + $dest = new MessageTarget( + $bidder->getID(), + $bidder->getName(), + $bidder->getNationID(), + $staticNation['name'], + $staticNation['color'], + GetImageURL($bidder->getVar('imgsvr'), $bidder->getVar('picture')) + ); + + //TODO: 전역 알림이 나타나야한다. 일반 메시지보다는 중요하고, 메시지보단 약하게.. + //TODO: 바로가기를 제공하는 편이 좋을 것 같다. + $msg = new Message( + Message::MSGTYPE_PRIVATE, + $src, + $dest, + $failReason, + new \DateTime(), + new \DateTime('9999-12-31'), + [] + ); + $msg->send(true); + + //아이템 제한에 걸렸다면 자동 연장 + $extendedCloseDate = $this->info->closeDate->add(TimeUtil::secondsToDateInterval( + max(static::MIN_EXTENSION_MINUTES_BY_EXTENSION_QUERY, $this->info->turnTerm * static::COEFF_EXTENSION_MINUTES_BY_EXTENSION_QUERY) * 60 + )); + + $this->extendCloseDate($extendedCloseDate, true); + return false; + } + + public function bid(int $amount, bool $tryExtendCloseDate = false): ?string + { + //TODO: 입찰 가능 제한확인(다른 경매 진행 등) + return parent::bid($amount, $tryExtendCloseDate); + } } diff --git a/hwe/sammo/DTO/AuctionBidItemData.php b/hwe/sammo/DTO/AuctionBidItemData.php index 41478689..ddb23d4d 100644 --- a/hwe/sammo/DTO/AuctionBidItemData.php +++ b/hwe/sammo/DTO/AuctionBidItemData.php @@ -10,6 +10,8 @@ class AuctionBidItemData extends DTO #[NullIsUndefined] public ?string $ownerName, public string $generalName, + #[NullIsUndefined] + public ?bool $tryExtendCloseDate, ) { } } diff --git a/hwe/sammo/DTO/AuctionInfo.php b/hwe/sammo/DTO/AuctionInfo.php index c34dd50f..10087714 100644 --- a/hwe/sammo/DTO/AuctionInfo.php +++ b/hwe/sammo/DTO/AuctionInfo.php @@ -4,6 +4,7 @@ namespace sammo\DTO; use sammo\DTO\Attr\Convert; use sammo\DTO\Converter\DateTimeConverter; +use sammo\Enums\AuctionType; use sammo\Enums\ResourceType; class AuctionInfo extends DTO @@ -12,17 +13,21 @@ class AuctionInfo extends DTO public int $id, public string $openerName, public int $openerGeneralID, - public string $type, + public AuctionType $type, + public ?string $target, public string $title, public bool $finished, public ResourceType $reqResource, public int $minAmount, public ?int $buyImmediatelyAmount, + public ?int $remainCloseDateExtensionCnt, #[Convert(DateTimeConverter::class)] public \DateTimeImmutable $openDate, #[Convert(DateTimeConverter::class)] public \DateTimeImmutable $closeDate, + #[Convert(DateTimeConverter::class)] + public ?\DateTimeImmutable $availableLatestBidCloseDate, ) { } } diff --git a/hwe/sammo/Enums/AuctionType.php b/hwe/sammo/Enums/AuctionType.php new file mode 100644 index 00000000..ee40883e --- /dev/null +++ b/hwe/sammo/Enums/AuctionType.php @@ -0,0 +1,8 @@ + Date: Sat, 4 Jun 2022 13:22:19 +0900 Subject: [PATCH 09/23] =?UTF-8?q?feat,wip=20:=20=EA=B1=B0=EB=9E=98?= =?UTF-8?q?=EC=9E=A5=20=ED=8E=98=EC=9D=B4=EC=A7=80=20=EC=9E=91=EC=97=85=20?= =?UTF-8?q?=EC=A4=80=EB=B9=84=20-=20=ED=95=84=EC=9A=94=EC=97=90=20?= =?UTF-8?q?=EB=94=B0=EB=9D=BC=20=ED=85=8C=EC=9D=B4=EB=B8=94=20=EC=A4=80?= =?UTF-8?q?=EB=B9=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- hwe/sammo/Auction.php | 10 +++++----- hwe/scss/auction.scss | 0 hwe/sql/reset.sql | 2 +- hwe/sql/schema.sql | 17 +++++++++++++++++ hwe/ts/PageAuction.vue | 11 +++++++++++ hwe/ts/v_auction.ts | 14 ++++++++++++++ hwe/v_auction.php | 39 +++++++++++++++++++++++++++++++++++++++ 7 files changed, 87 insertions(+), 6 deletions(-) create mode 100644 hwe/scss/auction.scss create mode 100644 hwe/ts/PageAuction.vue create mode 100644 hwe/ts/v_auction.ts create mode 100644 hwe/v_auction.php diff --git a/hwe/sammo/Auction.php b/hwe/sammo/Auction.php index 62ce874e..c3ca625e 100644 --- a/hwe/sammo/Auction.php +++ b/hwe/sammo/Auction.php @@ -69,7 +69,7 @@ class Auction { $db = DB::db(); $rawHighestBid = $db->queryFirstRow( - 'SELECT * FROM ng_auction WHERE auction_id = %i ORDER BY `amount` DESC LIMIT 1', + 'SELECT * FROM ng_auction_bid WHERE auction_id = %i ORDER BY `amount` DESC LIMIT 1', $this->info->id ); if (!$rawHighestBid) { @@ -83,7 +83,7 @@ class Auction { $db = DB::db(); $rawMyPrevBid = $db->queryFirstRow( - 'SELECT * FROM ng_auction WHERE general_id = %i AND auction_id = %i ORDER BY `amount` DESC LIMIT 1', + 'SELECT * FROM ng_auction_bid WHERE general_id = %i AND auction_id = %i ORDER BY `amount` DESC LIMIT 1', $this->general->getID(), $this->info->id ); @@ -216,7 +216,7 @@ class Auction } $rawMyPrevBid = $db->queryFirstRow( - 'SELECT * FROM ng_auction WHERE general_id = %i AND auction_id = %i ORDER BY `amount` DESC LIMIT 1', + 'SELECT * FROM ng_auction_bid WHERE general_id = %i AND auction_id = %i ORDER BY `amount` DESC LIMIT 1', $general->getID(), $auctionInfo->id ); @@ -247,7 +247,7 @@ class Auction $tryExtendCloseDate, ) ); - $db->insert('ng_auction', $newBid->toArray()); + $db->insert('ng_auction_bid', $newBid->toArray()); if ($db->affectedRows() == 0) { return '입찰에 실패했습니다: DB 오류'; } @@ -339,7 +339,7 @@ class Auction ) ); - $db->insert('ng_auction', $newBid->toArray()); + $db->insert('ng_auction_bid', $newBid->toArray()); if ($db->affectedRows() == 0) { return '입찰에 실패했습니다: DB 오류'; } diff --git a/hwe/scss/auction.scss b/hwe/scss/auction.scss new file mode 100644 index 00000000..e69de29b diff --git a/hwe/sql/reset.sql b/hwe/sql/reset.sql index 4fdbf35b..c6464e56 100644 --- a/hwe/sql/reset.sql +++ b/hwe/sql/reset.sql @@ -68,4 +68,4 @@ DROP TABLE IF EXISTS ng_betting; DROP TABLE IF EXISTS vote; DROP TABLE IF EXISTS vote_comment; -DROP TABLE IF EXISTS `ng_auction`; \ No newline at end of file +DROP TABLE IF EXISTS `ng_auction_bid`; \ No newline at end of file diff --git a/hwe/sql/schema.sql b/hwe/sql/schema.sql index 1bba1110..00a1897c 100644 --- a/hwe/sql/schema.sql +++ b/hwe/sql/schema.sql @@ -706,4 +706,21 @@ CREATE TABLE `ng_auction` ( CONSTRAINT `aux` CHECK (json_valid(`aux`)) ) COLLATE='utf8mb4_general_ci' +ENGINE = Aria; + +CREATE TABLE `ng_auction_bid` ( + `no` INT(11) NOT NULL AUTO_INCREMENT, + `auction_id` INT(11) NOT NULL, + `owner` INT(11) NULL DEFAULT NULL, + `general_id` INT(11) NOT NULL, + `amount` INT(11) NOT NULL, + `date` DATETIME NOT NULL, + `aux` LONGTEXT NOT NULL COLLATE 'utf8mb4_bin', + PRIMARY KEY (`no`), + UNIQUE INDEX `by_general` (`general_id`, `auction_id`, `amount`), + UNIQUE INDEX `by_owner` (`owner`, `auction_id`, `amount`), + INDEX `by_amount` (`auction_id`, `amount`, `date`), + CONSTRAINT `aux` CHECK (json_valid(`aux`)) +) +COLLATE='utf8mb4_general_ci' ENGINE = Aria; \ No newline at end of file diff --git a/hwe/ts/PageAuction.vue b/hwe/ts/PageAuction.vue new file mode 100644 index 00000000..1f53efe3 --- /dev/null +++ b/hwe/ts/PageAuction.vue @@ -0,0 +1,11 @@ + + + diff --git a/hwe/ts/v_auction.ts b/hwe/ts/v_auction.ts new file mode 100644 index 00000000..4bef455a --- /dev/null +++ b/hwe/ts/v_auction.ts @@ -0,0 +1,14 @@ +import "@scss/auction.scss"; +import { createApp } from 'vue' +import PageAuction from '@/PageAuction.vue'; +import BootstrapVue3 from 'bootstrap-vue-3' +import { auto500px } from "./util/auto500px"; +import { insertCustomCSS } from "./util/customCSS"; +import { htmlReady } from "./util/htmlReady"; + +auto500px(); + +htmlReady(() => { + insertCustomCSS(); + }); +createApp(PageAuction).use(BootstrapVue3).mount('#app') \ No newline at end of file diff --git a/hwe/v_auction.php b/hwe/v_auction.php new file mode 100644 index 00000000..f9c26bae --- /dev/null +++ b/hwe/v_auction.php @@ -0,0 +1,39 @@ +setReadOnly(); +$userID = Session::getUserID(); + +$db = DB::db(); +$gameStor = KVStorage::getStorage($db, 'game_env'); + +$me = $db->queryFirstRow('SELECT no, nation, officer_level, permission, con, turntime, belong, penalty FROM general WHERE owner=%i', $userID); + +?> + + + + + <?= UniqueConst::$serverName ?>: <?= $boardName ?> + + + + + [ + 'serverID' => UniqueConst::$serverID, + 'serverNick' => DB::prefix(), + 'turnterm' => $gameStor->turnterm, + ] + ]) ?> + + + +
+ + + \ No newline at end of file -- 2.54.0 From 9b1a57bbd23dae2ab8a48ef19d3dac175ff81469 Mon Sep 17 00:00:00 2001 From: Hide_D Date: Sat, 4 Jun 2022 21:27:09 +0900 Subject: [PATCH 10/23] =?UTF-8?q?feat,wip:=20=EA=B2=BD=EB=A7=A4=EC=9E=A5?= =?UTF-8?q?=EC=9D=84=20db=20table=EB=A1=9C=20=EB=B6=84=EB=A6=AC?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- hwe/sammo/Auction.php | 65 +++++++++-------------------- hwe/sammo/AuctionUniqueItem.php | 41 +++++++++--------- hwe/sammo/DTO/AuctionInfo.php | 19 ++++----- hwe/sammo/DTO/AuctionInfoDetail.php | 26 ++++++++++++ hwe/sql/schema.sql | 28 +++++++------ 5 files changed, 90 insertions(+), 89 deletions(-) create mode 100644 hwe/sammo/DTO/AuctionInfoDetail.php diff --git a/hwe/sammo/Auction.php b/hwe/sammo/Auction.php index c3ca625e..7284b779 100644 --- a/hwe/sammo/Auction.php +++ b/hwe/sammo/Auction.php @@ -5,21 +5,17 @@ namespace sammo; use DateTime; use DateTimeImmutable; use DateTimeInterface; -use Ds\Map; use sammo\DTO\AuctionBidItem; use sammo\DTO\AuctionBidItemData; use sammo\DTO\AuctionInfo; use sammo\Enums\InheritanceKey; use sammo\Enums\RankColumn; use sammo\Enums\ResourceType; -use sammo\VO\InheritancePointType; class Auction { protected AuctionInfo $info; - public const LAST_AUCTION_ID_KEY = 'last_auction_id'; - public const COEFF_AUCTION_CLOSE_MINUTES = 24; public const COEFF_EXTENSION_MINUTES_PER_BID = 1 / 6; @@ -30,39 +26,24 @@ class Auction public const MIN_EXTENSION_MINUTES_LIMIT_BY_BID = 5; public const MIN_EXTENSION_MINUTES_BY_EXTENSION_QUERY = 5; - static public function genNextAuctionID(): int + static public function openAuction(AuctionInfo $info, General $general): int|string { $db = DB::db(); - - $gameStor = KVStorage::getStorage($db, 'game_env'); - $gameStor->invalidateCacheValue(self::LAST_AUCTION_ID_KEY); - $auctionID = ($gameStor->getValue(self::LAST_AUCTION_ID_KEY) ?? 0) + 1; - - $gameStor->setValue(self::LAST_AUCTION_ID_KEY, $auctionID); - - return $auctionID; - } - - - static public function openAuction(AuctionInfo $info, General $general): ?string - { - $db = DB::db(); - $auctionID = $info->id; - $auctionStor = KVStorage::getStorage($db, 'auction'); - - $openedAuction = $general->getAuxVar('openedAuction') ?? []; - $prevAuctionID = $openedAuction[$info->type->value] ?? null; - if ($prevAuctionID !== null) { - //XXX: 이 구조보다는 차라리 DB에 insert로 넣는 게 나을 수도. - $prevAuction = new Auction($prevAuctionID, $general); - if (!$prevAuction->info->finished) { - return '아직 경매가 끝나지 않았습니다.'; - } - unset($openedAuction[$info->type->value]); - $general->setAuxVar('openedAuction', $openedAuction); + if ($info->id !== null) { + return 'id가 지정되어 있습니다.'; } - $auctionStor->setValue("id_{$auctionID}", $info->toArray()); + $prevAuctionID = $db->queryFirstField( + 'SELECT id FROM ng_auction WHERE opener_general_id = %i AND finished = 0 AND `type` = %s', + $general->getID(), + $info->type + ); + if ($prevAuctionID !== null) { + return '아직 경매가 끝나지 않았습니다.'; + } + + $db->insert('ng_auction', $info->toArray()); + return $db->insertId(); } public function getHighestBid(): ?AuctionBidItem @@ -96,8 +77,7 @@ class Auction public function __construct(protected readonly int $auctionID, protected General $general) { $db = DB::db(); - $auctionStor = KVStorage::getStorage($db, 'auction'); - $rawAuctionInfo = $auctionStor->getValue("id_{$auctionID}"); + $rawAuctionInfo = $db->query('SELECT * FROM ng_auction WHERE id = %i', $auctionID); if ($rawAuctionInfo === null) { throw new \RuntimeException("해당 경매가 없습니다: {$auctionID}"); } @@ -111,7 +91,7 @@ class Auction public function extendCloseDate(DateTimeInterface $date, bool $force = false): ?string { - if(!$force){ + if (!$force) { if ($this->info->remainCloseExtensionCnt === null) { return '연장할 수 없는 경매입니다.'; } @@ -124,7 +104,6 @@ class Auction } $db = DB::db(); - $auctionStor = KVStorage::getStorage($db, 'auction'); $gameStor = KVStorage::getStorage($db, 'game_env'); $turnTerm = $gameStor->getValue('turnterm'); $closeDate = DateTimeImmutable::createFromInterface($date); @@ -132,7 +111,7 @@ class Auction $this->info->availableLatestBidCloseDate = $closeDate->add(TimeUtil::secondsToDateInterval( max(static::MIN_EXTENSION_MINUTES_PER_BID, $turnTerm * static::COEFF_EXTENSION_MINUTES_PER_BID) * 60 )); - $auctionStor->setValue("id_{$this->info->id}", $this->info->toArray()); + $db->update('ng_auction', $this->info->toArrayExcept('id'), 'id = %i', $this->info->id); return null; } @@ -184,23 +163,17 @@ class Auction public function closeAuction(bool $isRollback = false): void { $db = DB::db(); - $auctionStor = KVStorage::getStorage($db, 'auction'); $this->info->finished = true; - $openedAuction = $this->general->getAuxVar('openedAuction') ?? []; - if (key_exists($this->info->type->value, $openedAuction)) { - unset($openedAuction[$this->info->type->value]); - $this->general->setAuxVar('openedAuction', $openedAuction); - } if ($isRollback) { $highestBid = $this->getHighestBid(); if ($highestBid !== null) { $this->refundBid($highestBid, "{$this->info->title} 경매가 취소되었습니다."); } } - $auctionID = $this->info->id; - $auctionStor->setValue("id_{$auctionID}", $this->info->toArray()); + + $db->update('ng_auction', $this->info->toArrayExcept('id'), 'id = %i', $this->info->id); } private function bidInheritPoint(int $amount, \DateTimeImmutable $now, bool $tryExtendCloseDate): ?string diff --git a/hwe/sammo/AuctionUniqueItem.php b/hwe/sammo/AuctionUniqueItem.php index 6a6e87df..21af7aae 100644 --- a/hwe/sammo/AuctionUniqueItem.php +++ b/hwe/sammo/AuctionUniqueItem.php @@ -5,6 +5,7 @@ namespace sammo; use DateTimeImmutable; use sammo\DTO\AuctionBidItem; use sammo\DTO\AuctionInfo; +use sammo\DTO\AuctionInfoDetail; use sammo\Enums\AuctionType; use sammo\Enums\InheritanceKey; use sammo\Enums\ResourceType; @@ -44,7 +45,7 @@ class AuctionUniqueItem extends Auction return "{$namePool[$subIdx]}{$dupIdx}"; } - static public function openItemAuction(BaseItem $item, General $general, int $startAmount): ?string + static public function openItemAuction(BaseItem $item, General $general, int $startAmount): self|string { if ($startAmount < GameConst::$inheritItemUniqueMinPoint) { return '최소 경매 금액은 ' . GameConst::$inheritItemUniqueMinPoint . '입니다.'; @@ -58,9 +59,6 @@ class AuctionUniqueItem extends Auction return '구매할 수 있는 아이템입니다.'; } - if (GameConst::$allItems) - - $auctionID = static::genNextAuctionID(); $now = new DateTimeImmutable(); $db = DB::db(); @@ -75,25 +73,28 @@ class AuctionUniqueItem extends Auction )); $info = new AuctionInfo( - $auctionID, - static::genObfuscatedName($general->getID()), - $general->getID(), - AuctionType::UniqueItem, - $item->getRawClassName(), - "{$item->getName()} 경매", - false, - ResourceType::inheritancePoint, - $startAmount, null, - 1, + AuctionType::UniqueItem, + false, + $item->getRawClassName(), + $general->getID(), + ResourceType::inheritancePoint, $now, $closeDate, - $availableLatestBidCloseDate, + new AuctionInfoDetail( + "{$item->getName()} 경매", + static::genObfuscatedName($general->getID()), + + $startAmount, + null, + 1, + $availableLatestBidCloseDate, + ) ); - $result = static::openAuction($info, $general); - if ($result !== null) { - return $result; + $auctionID = static::openAuction($info, $general); + if (!is_int($auctionID)) { + return $auctionID; } $auction = new static($auctionID, $general); try { @@ -105,7 +106,7 @@ class AuctionUniqueItem extends Auction return "경매를 시작했지만, 첫 입찰에 실패했습니다: {$msg}"; } - return null; + return $auction; } private function giveUniqueItem(AuctionBidItem $highestBid, General $bidder): ?string @@ -137,7 +138,7 @@ class AuctionUniqueItem extends Auction } } - if($availableEquipUniqueCnt <= 0){ + if ($availableEquipUniqueCnt <= 0) { return '유니크 아이템 소유 제한 상태입니다. 종료 시간이 연장됩니다.'; } diff --git a/hwe/sammo/DTO/AuctionInfo.php b/hwe/sammo/DTO/AuctionInfo.php index 10087714..3116680b 100644 --- a/hwe/sammo/DTO/AuctionInfo.php +++ b/hwe/sammo/DTO/AuctionInfo.php @@ -3,6 +3,8 @@ namespace sammo\DTO; use sammo\DTO\Attr\Convert; +use sammo\DTO\Attr\JsonString; +use sammo\DTO\Attr\NullIsUndefined; use sammo\DTO\Converter\DateTimeConverter; use sammo\Enums\AuctionType; use sammo\Enums\ResourceType; @@ -10,24 +12,21 @@ use sammo\Enums\ResourceType; class AuctionInfo extends DTO { public function __construct( - public int $id, - public string $openerName, - public int $openerGeneralID, + #[NullIsUndefined] + public ?int $id, public AuctionType $type, - public ?string $target, - public string $title, public bool $finished, + public ?string $target, + public int $openerGeneralID, public ResourceType $reqResource, - public int $minAmount, - public ?int $buyImmediatelyAmount, - public ?int $remainCloseDateExtensionCnt, #[Convert(DateTimeConverter::class)] public \DateTimeImmutable $openDate, #[Convert(DateTimeConverter::class)] public \DateTimeImmutable $closeDate, - #[Convert(DateTimeConverter::class)] - public ?\DateTimeImmutable $availableLatestBidCloseDate, + + #[JsonString] + public AuctionInfoDetail $detail, ) { } } diff --git a/hwe/sammo/DTO/AuctionInfoDetail.php b/hwe/sammo/DTO/AuctionInfoDetail.php new file mode 100644 index 00000000..b73dfc11 --- /dev/null +++ b/hwe/sammo/DTO/AuctionInfoDetail.php @@ -0,0 +1,26 @@ + Date: Sun, 5 Jun 2022 16:06:00 +0900 Subject: [PATCH 11/23] =?UTF-8?q?feat,wip:=20=EC=9C=A0=EB=8B=88=ED=81=AC?= =?UTF-8?q?=20=EA=B2=BD=EB=A7=A4=20=EC=8B=9C=EC=9E=91,=20=EC=9E=85?= =?UTF-8?q?=EC=B0=B0=20=EA=B5=AC=ED=98=84=20-=20=EB=88=84=EA=B5=B0?= =?UTF-8?q?=EA=B0=80=EB=8A=94=20=EB=AC=B4=EC=9E=91=EC=9C=84=EB=A1=9C=20?= =?UTF-8?q?=EC=9C=A0=EB=8B=88=ED=81=AC=EB=A5=BC=20=ED=95=98=EB=82=98?= =?UTF-8?q?=EB=A5=BC=20=EB=A8=B9=EC=9D=84=20=EC=88=98=20=EC=9E=88=EB=8F=84?= =?UTF-8?q?=EB=A1=9D=20=EC=84=A4=EA=B3=84=3F?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- hwe/_admin2.php | 57 ++++--------- hwe/_admin2_submit.php | 89 -------------------- hwe/func.php | 10 +++ hwe/sammo/API/Auction/BidUniqueAuction.php | 65 ++++++++++++++ hwe/sammo/API/Auction/OpenUniqueAuction.php | 34 ++++---- hwe/sammo/API/General/DropItem.php | 22 ++++- hwe/sammo/Auction.php | 2 - hwe/sammo/AuctionUniqueItem.php | 93 ++++++++++++++++++++- hwe/sammo/Command/General/che_장비매매.php | 21 ++++- 9 files changed, 234 insertions(+), 159 deletions(-) create mode 100644 hwe/sammo/API/Auction/BidUniqueAuction.php diff --git a/hwe/_admin2.php b/hwe/_admin2.php index 4281e2eb..913f7fac 100644 --- a/hwe/_admin2.php +++ b/hwe/_admin2.php @@ -48,54 +48,25 @@ $db = DB::db(); 접속제한
블럭회원 - + query('SELECT `no`, `name`, npc, `block` FROM general ORDER BY npc, binary(`name`)'); - echo " - - - 아이템 지급 - - - - - - diff --git a/hwe/_admin2_submit.php b/hwe/_admin2_submit.php index fb0b717f..660c204b 100644 --- a/hwe/_admin2_submit.php +++ b/hwe/_admin2_submit.php @@ -200,95 +200,6 @@ switch ($btn) { $msg->send(true); } break; - case "무기지급": - - if ($item == 'None') { - $text = "무기 회수!"; - } else { - $text = getItemName($item) . " 지급!"; - } - - foreach ($genlist as $generalID) { - $msg = new Message(Message::MSGTYPE_PRIVATE, $src, MessageTarget::buildQuick($generalID), $text, new \DateTime(), new \DateTime('9999-12-31'), []); - $msg->send(true); - } - - if ($item === 'None') { - $db->update('general', [ - 'weapon' => 'None' - ], '`no` IN %li', $genlist); - } else { - $db->update('general', [ - 'weapon' => $item - ], '`no` IN %li', $genlist, $item); - } - break; - case "책지급": - if ($item == 'None') { - $text = "책 회수!"; - } else { - $text = getItemName($item) . " 지급!"; - } - - foreach ($genlist as $generalID) { - $msg = new Message(Message::MSGTYPE_PRIVATE, $src, MessageTarget::buildQuick($generalID), $text, new \DateTime(), new \DateTime('9999-12-31'), []); - $msg->send(true); - } - - if ($item == 'None') { - $db->update('general', [ - 'book' => 'None' - ], '`no` IN %li', $genlist); - } else { - $db->update('general', [ - 'book' => $item - ], '`no` IN %li', $genlist); - } - break; - case "말지급": - if ($item == 'None') { - $text = "말 회수!"; - } else { - $text = getItemName($item) . " 지급!"; - } - - foreach ($genlist as $generalID) { - $msg = new Message(Message::MSGTYPE_PRIVATE, $src, MessageTarget::buildQuick($generalID), $text, new \DateTime(), new \DateTime('9999-12-31'), []); - $msg->send(true); - } - - if ($item == 'None') { - $db->update('general', [ - 'horse' => 'None' - ], '`no` IN %li', $genlist); - } else { - $db->update('general', [ - 'horse' => $item - ], '`no` IN %li', $genlist); - } - break; - case "도구지급": - if ($item == 'None') { - $text = "특수도구 회수!"; - } else { - $text = getItemName($item) . " 지급!"; - } - - foreach ($genlist as $generalID) { - $msg = new Message(Message::MSGTYPE_PRIVATE, $src, MessageTarget::buildQuick($generalID), $text, new \DateTime(), new \DateTime('9999-12-31'), []); - $msg->send(true); - } - - if ($item == 'None') { - $db->update('general', [ - 'item' => 'None' - ], '`no` IN %li', $genlist); - } else { - $db->update('general', [ - 'item' => $item - ], '`no` IN %li AND item < %i', $genlist, $item); - } - break; case "하야입력": $db->update('general_turn', [ 'action' => 'che_하야', diff --git a/hwe/func.php b/hwe/func.php index 66774df1..0e7b550b 100644 --- a/hwe/func.php +++ b/hwe/func.php @@ -3,6 +3,7 @@ namespace sammo; use DateTime; +use Ds\Set; use sammo\Enums\InheritanceKey; use sammo\Enums\RankColumn; use sammo\Event\Action; @@ -1665,6 +1666,10 @@ function giveRandomUniqueItem(RandUtil $rng, General $general, string $acquireTy $logger->pushGlobalActionLog("{$generalName}{$josaYi} {$itemName}{$josaUl} 습득했습니다!"); $logger->pushGlobalHistoryLog("【{$acquireType}】{$nationName}{$generalName}{$josaYi} {$itemName}{$josaUl} 습득했습니다!"); + $givenUnique =$gameStor->getValue('givenUnique') ?? []; + $givenUnique[$itemCode] = ($givenUnique[$itemCode] ?? 0) + 1; + $gameStor->setValue('givenUnique', $givenUnique); + return true; } @@ -1866,6 +1871,11 @@ function tryInheritUniqueItem(RandUtil $rng, General $general, string $acquireTy $general->applyDB($db); + $gameStor = KVStorage::getStorage($db, 'game_env'); + $givenUnique =$gameStor->getValue('givenUnique') ?? []; + $givenUnique[$ownTarget] = ($givenUnique[$ownTarget] ?? 0) + 1; + $gameStor->setValue('givenUnique', $givenUnique); + //같은 종류의 유니크를 입찰했을 수 있으니 한번 더 검사한다. tryRollbackInheritUniqueItem($rng, $general); diff --git a/hwe/sammo/API/Auction/BidUniqueAuction.php b/hwe/sammo/API/Auction/BidUniqueAuction.php new file mode 100644 index 00000000..9c5c7948 --- /dev/null +++ b/hwe/sammo/API/Auction/BidUniqueAuction.php @@ -0,0 +1,65 @@ + $amount) { + if ($amount == 0) { + continue; + } + $availableItems[$itemKey] = $amount; + } + } + + $v = new Validator($this->args); + $v->rule('required', [ + 'auctionID', + 'amount', + ]) + ->rule('int', 'amount') + ->rule('int', 'auctionID') + ->rule('boolean', 'extendCloseDate'); + + if (!$v->validate()) { + return $v->errorStr(); + } + return null; + } + + public function getRequiredSessionMode(): int + { + return static::REQ_GAME_LOGIN; + } + + public function launch(Session $session, ?DateTimeInterface $modifiedSince, ?string $reqEtag) + { + $auctionID = $this->args['auctionID']; + $amount = $this->args['amount']; + $tryExtendCloseDate = $this->arg['extendCloseDate'] ?? false; + + $generalID = $session->generalID; + $general = General::createGeneralObjFromDB($generalID); + $auction = new AuctionUniqueItem($auctionID, $general); + $result = $auction->bid($amount, $tryExtendCloseDate); + + if (is_string($result)) { + return $result; + } + + return null; + } +} diff --git a/hwe/sammo/API/Auction/OpenUniqueAuction.php b/hwe/sammo/API/Auction/OpenUniqueAuction.php index 21c6e2ee..ef3928ec 100644 --- a/hwe/sammo/API/Auction/OpenUniqueAuction.php +++ b/hwe/sammo/API/Auction/OpenUniqueAuction.php @@ -4,14 +4,12 @@ namespace sammo\API\Auction; use sammo\Session; use DateTimeInterface; -use sammo\Betting; -use sammo\DB; -use sammo\DTO\BettingItem; +use sammo\AuctionUniqueItem; use sammo\Validator; use sammo\GameConst; -use sammo\KVStorage; -use sammo\UserLogger; -use sammo\Util; +use sammo\General; + +use function sammo\buildItemClass; class OpenUniqueAuction extends \sammo\BaseAPI { @@ -34,7 +32,7 @@ class OpenUniqueAuction extends \sammo\BaseAPI ]) ->rule('int', 'amount') ->rule('min', 'amount', GameConst::$inheritItemUniqueMinPoint) - ->rule('keyExists', 'item', $availableItems); + ->rule('keyExists', 'itemID', $availableItems); if (!$v->validate()) { @@ -50,22 +48,22 @@ class OpenUniqueAuction extends \sammo\BaseAPI public function launch(Session $session, ?DateTimeInterface $modifiedSince, ?string $reqEtag) { - /** @var int */ - $bettingID = $this->args['bettingID']; - /** @var int[] */ - $bettingType = $this->args['bettingType']; - /** @var int */ + $itemID = $this->args['itemID']; $amount = $this->args['amount']; + $generalID = $session->generalID; - $bettingHelper = new Betting($bettingID); - try { - $bettingHelper->bet($session->generalID, $session->userID, $bettingType, $amount); - } catch (\Throwable $e) { - return $e->getMessage(); + $itemObj = buildItemClass($itemID); + $general = General::createGeneralObjFromDB($generalID); + + $auctionResult = AuctionUniqueItem::openItemAuction($itemObj, $general, $amount); + + if(is_string($auctionResult)) { + return $auctionResult; } return [ - 'result' => true + 'result' => true, + 'auctionID' => $auctionResult->id, ]; } } diff --git a/hwe/sammo/API/General/DropItem.php b/hwe/sammo/API/General/DropItem.php index 61c82db9..317aea01 100644 --- a/hwe/sammo/API/General/DropItem.php +++ b/hwe/sammo/API/General/DropItem.php @@ -2,6 +2,7 @@ namespace sammo\API\General; +use Ds\Set; use sammo\DB; use sammo\Validator; @@ -9,6 +10,7 @@ use sammo\Session; use sammo\GameConst; use sammo\General; use sammo\JosaUtil; +use sammo\KVStorage; class DropItem extends \sammo\BaseAPI { @@ -56,12 +58,30 @@ class DropItem extends \sammo\BaseAPI $logger->pushGeneralActionLog("{$itemName}{$josaUl} 버렸습니다."); $nationName = $me->getStaticNation()['name']; + $db = DB::db(); if (!$item->isBuyable()) { $logger->pushGlobalActionLog("{$generalName}{$josaYi} {$itemName}{$josaUl} 잃었습니다!"); $logger->pushGlobalHistoryLog("【망실】{$nationName}{$generalName}{$josaYi} {$itemName}{$josaUl} 잃었습니다!"); + + $gameStor = KVStorage::getStorage($db, 'game_env'); + $givenUnique = $gameStor->getValue('givenUnique') ?? []; + $itemCode = $item->getRawClassName(); + if (key_exists($itemCode, $givenUnique)) { + $givenUniqueCnt = $givenUnique[$itemCode]; + $givenUniqueCnt -= 1; + if ($givenUniqueCnt <= 0) { + unset($givenUnique[$itemCode]); + } else { + $givenUnique[$itemCode] = $givenUniqueCnt; + } + } else { + //XXX: 처리하지 못한 코드가 있는가? + //FIXME: 여기에서 무언가 경고를 내야함 + } + $gameStor->setValue('givenUnique', $givenUnique); } - $me->applyDB(DB::db()); + $me->applyDB($db); return null; } diff --git a/hwe/sammo/Auction.php b/hwe/sammo/Auction.php index 7284b779..1d609c80 100644 --- a/hwe/sammo/Auction.php +++ b/hwe/sammo/Auction.php @@ -317,8 +317,6 @@ class Auction return '입찰에 실패했습니다: DB 오류'; } - - $general->setVar($resType->value, $general->getVar($resType->value) - $morePoint); if ($myPrevBid === null || ($highestBid !== null && $myPrevBid->id !== $highestBid->id)) { diff --git a/hwe/sammo/AuctionUniqueItem.php b/hwe/sammo/AuctionUniqueItem.php index 21af7aae..c717d08b 100644 --- a/hwe/sammo/AuctionUniqueItem.php +++ b/hwe/sammo/AuctionUniqueItem.php @@ -3,6 +3,7 @@ namespace sammo; use DateTimeImmutable; +use Ds\Set; use sammo\DTO\AuctionBidItem; use sammo\DTO\AuctionInfo; use sammo\DTO\AuctionInfoDetail; @@ -59,10 +60,33 @@ class AuctionUniqueItem extends Auction return '구매할 수 있는 아이템입니다.'; } + $itemKey = $item->getRawClassName(); + $db = DB::db(); + $auctionIDonProgress = $db->queryFirstField( + 'SELECT `id` FROM ng_auction WHERE `finished` = 0 AND `target` = %s AND `target` = %s', + AuctionType::UniqueItem->value, + $itemKey + ); + if ($auctionIDonProgress !== null) { + return '이미 경매가 진행중입니다.'; + } + + $gameStor = KVStorage::getStorage($db, 'game_env'); + $givenUnique = $gameStor->getValue('givenUnique') ?? []; + $givenUniqueCnt = $givenUnique[$itemKey] ?? 0; + $remainUniqueCnt = 0; + foreach (GameConst::$allItems as $itemList) { + if (!key_exists($itemKey, $itemList)) { + continue; + } + $remainUniqueCnt += $itemList[$itemKey]; + } + if ($remainUniqueCnt > 1 && $givenUniqueCnt >= $remainUniqueCnt - 1) { + return '더이상 이 아이템을 경매로 가져갈 수 없습니다.'; + } + $now = new DateTimeImmutable(); - $db = DB::db(); - $gameStor = KVStorage::getStorage($db, 'game_env'); $turnTerm = $gameStor->getValue('turnterm'); $closeDate = $now->add(TimeUtil::secondsToDateInterval( @@ -76,7 +100,7 @@ class AuctionUniqueItem extends Auction null, AuctionType::UniqueItem, false, - $item->getRawClassName(), + $itemKey, $general->getID(), ResourceType::inheritancePoint, $now, @@ -113,6 +137,9 @@ class AuctionUniqueItem extends Auction { $itemKey = $this->info->target; + if ($itemKey === null) { + throw new \Exception('아이템 키가 없습니다.'); + } $itemObj = buildItemClass($itemKey); $general = $bidder; $availableItemTypes = []; @@ -192,6 +219,11 @@ class AuctionUniqueItem extends Auction $logger->pushGlobalHistoryLog("【보물발견】{$nationName}{$generalName}{$josaYi} {$itemName}{$josaUl} 습득했습니다!"); $general->applyDB($db); + + $givenUnique = $gameStor->getValue('givenUnique') ?? []; + $givenUnique[$itemKey] = ($givenUnique[$itemKey] ?? 0) + 1; + $gameStor->setValue('givenUnique', $givenUnique); + return null; } @@ -263,7 +295,60 @@ class AuctionUniqueItem extends Auction public function bid(int $amount, bool $tryExtendCloseDate = false): ?string { - //TODO: 입찰 가능 제한확인(다른 경매 진행 등) + + $db = DB::db(); + /** @var AuctionInfo[] */ + $openUniqueAuctions = array_map(fn ($raw) => AuctionInfo::fromArray($raw), $db->query( + 'SELECT * FROM `ng_auction` WHERE `finished` = 0 AND `type`= %s', + AuctionType::UniqueItem->value + ) ?? []); + + $db = DB::db(); + $rawHighestBids = Util::convertArrayToDict($db->queryFirstRow( + 'SELECT * FROM ng_auction_bid WHERE auction_id IN %i ORDER BY `amount` DESC LIMIT 1', + $this->info->id + ) ?? [], 'auction_id'); + /** @var array */ + $highestBids = Util::mapWithKey( + fn ($auctionID, $bid) => AuctionBidItem::fromArray($bid), + $rawHighestBids + ); + + $itemCode = $this->info->target; + $bidItemTypes = new Set(); + foreach (GameConst::$allItems as $itemType => $itemList) { + if (($itemList[$itemCode] ?? 0) <= 0) { + continue; + } + $bidItemTypes->add($itemType); + } + + foreach ($openUniqueAuctions as $auction) { + $auctionID = $auction->id; + if (!isset($highestBids[$auctionID])) { + continue; + } + if ($auctionID === $this->auctionID) { + continue; + } + + $bid = $highestBids[$auctionID]; + if ($bid->generalID !== $this->general->getID()) { + continue; + } + + $itemCodeComp = $auction->target; + + foreach (GameConst::$allItems as $itemType => $itemList) { + if (($itemList[$itemCodeComp] ?? 0) <= 0) { + continue; + } + if ($bidItemTypes->contains($itemType)) { + return '1순위 입찰자인 경매중에 같은 부위가 있습니다.'; + } + } + } + return parent::bid($amount, $tryExtendCloseDate); } } diff --git a/hwe/sammo/Command/General/che_장비매매.php b/hwe/sammo/Command/General/che_장비매매.php index 2204dae9..a7faaf89 100644 --- a/hwe/sammo/Command/General/che_장비매매.php +++ b/hwe/sammo/Command/General/che_장비매매.php @@ -13,7 +13,8 @@ use \sammo\{ GameConst, GameUnitConst, LastTurn, - Command + Command, + KVStorage }; use function \sammo\buildItemClass; @@ -181,12 +182,28 @@ class che_장비매매 extends Command\GeneralCommand $general->onArbitraryAction($general, $rng, '장비매매', '판매', ['itemCode' => $itemCode]); $general->setItem($itemType, null); - if(!$itemObj->isBuyable()){ + if (!$itemObj->isBuyable()) { $generalName = $general->getName(); $josaYi = JosaUtil::pick($generalName, '이'); $nationName = $general->getStaticNation()['name']; $logger->pushGlobalActionLog("{$generalName}{$josaYi} {$itemName}{$josaUl} 판매했습니다!"); $logger->pushGlobalHistoryLog("【판매】{$nationName}{$generalName}{$josaYi} {$itemName}{$josaUl} 판매했습니다!"); + + $gameStor = KVStorage::getStorage($db, 'game_env'); + $givenUnique = $gameStor->getValue('givenUnique') ?? []; + if (key_exists($itemCode, $givenUnique)) { + $givenUniqueCnt = $givenUnique[$itemCode]; + $givenUniqueCnt -= 1; + if ($givenUniqueCnt <= 0) { + unset($givenUnique[$itemCode]); + } else { + $givenUnique[$itemCode] = $givenUniqueCnt; + } + } else { + //XXX: 처리하지 못한 코드가 있는가? + //FIXME: 여기에서 무언가 경고를 내야함 + } + $gameStor->setValue('givenUnique', $givenUnique); } } -- 2.54.0 From f607350442ceb9bc59f665974b95c70b1ca410a0 Mon Sep 17 00:00:00 2001 From: Hide_D Date: Mon, 6 Jun 2022 22:33:31 +0900 Subject: [PATCH 12/23] =?UTF-8?q?feat,wip:=20=EA=B8=88,=EC=8C=80=20?= =?UTF-8?q?=EA=B1=B0=EB=9E=98=EC=9E=A5=20=EA=B4=80=EB=A0=A8=20=EB=A1=9C?= =?UTF-8?q?=EC=A7=81=20=EC=A1=B0=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- hwe/sammo/API/Auction/OpenBuyRiceAuction.php | 69 +++++ hwe/sammo/Auction.php | 143 ++++++++-- hwe/sammo/AuctionBasicResource.php | 223 +++++++++++++++ hwe/sammo/AuctionBuyRice.php | 14 + hwe/sammo/AuctionSellRice.php | 17 ++ hwe/sammo/AuctionUniqueItem.php | 274 ++++++++----------- hwe/sammo/DTO/AuctionInfoDetail.php | 10 +- hwe/sammo/Enums/AuctionType.php | 10 +- hwe/sql/schema.sql | 2 +- 9 files changed, 572 insertions(+), 190 deletions(-) create mode 100644 hwe/sammo/API/Auction/OpenBuyRiceAuction.php create mode 100644 hwe/sammo/AuctionBasicResource.php create mode 100644 hwe/sammo/AuctionBuyRice.php create mode 100644 hwe/sammo/AuctionSellRice.php diff --git a/hwe/sammo/API/Auction/OpenBuyRiceAuction.php b/hwe/sammo/API/Auction/OpenBuyRiceAuction.php new file mode 100644 index 00000000..ef3928ec --- /dev/null +++ b/hwe/sammo/API/Auction/OpenBuyRiceAuction.php @@ -0,0 +1,69 @@ + $amount) { + if ($amount == 0) { + continue; + } + $availableItems[$itemKey] = $amount; + } + } + + $v = new Validator($this->args); + $v->rule('required', [ + 'itemID', + 'amount' + ]) + ->rule('int', 'amount') + ->rule('min', 'amount', GameConst::$inheritItemUniqueMinPoint) + ->rule('keyExists', 'itemID', $availableItems); + + + if (!$v->validate()) { + return $v->errorStr(); + } + return null; + } + + public function getRequiredSessionMode(): int + { + return static::REQ_GAME_LOGIN; + } + + public function launch(Session $session, ?DateTimeInterface $modifiedSince, ?string $reqEtag) + { + $itemID = $this->args['itemID']; + $amount = $this->args['amount']; + $generalID = $session->generalID; + + $itemObj = buildItemClass($itemID); + $general = General::createGeneralObjFromDB($generalID); + + $auctionResult = AuctionUniqueItem::openItemAuction($itemObj, $general, $amount); + + if(is_string($auctionResult)) { + return $auctionResult; + } + + return [ + 'result' => true, + 'auctionID' => $auctionResult->id, + ]; + } +} diff --git a/hwe/sammo/Auction.php b/hwe/sammo/Auction.php index 1d609c80..e4b923f3 100644 --- a/hwe/sammo/Auction.php +++ b/hwe/sammo/Auction.php @@ -12,7 +12,7 @@ use sammo\Enums\InheritanceKey; use sammo\Enums\RankColumn; use sammo\Enums\ResourceType; -class Auction +abstract class Auction { protected AuctionInfo $info; @@ -26,22 +26,15 @@ class Auction public const MIN_EXTENSION_MINUTES_LIMIT_BY_BID = 5; public const MIN_EXTENSION_MINUTES_BY_EXTENSION_QUERY = 5; - static public function openAuction(AuctionInfo $info, General $general): int|string + protected AuctionBidItem|null|false $_highestBid = false; + + static protected function openAuction(AuctionInfo $info, General $general): int|string { $db = DB::db(); if ($info->id !== null) { return 'id가 지정되어 있습니다.'; } - $prevAuctionID = $db->queryFirstField( - 'SELECT id FROM ng_auction WHERE opener_general_id = %i AND finished = 0 AND `type` = %s', - $general->getID(), - $info->type - ); - if ($prevAuctionID !== null) { - return '아직 경매가 끝나지 않았습니다.'; - } - $db->insert('ng_auction', $info->toArray()); return $db->insertId(); } @@ -49,15 +42,31 @@ class Auction public function getHighestBid(): ?AuctionBidItem { $db = DB::db(); - $rawHighestBid = $db->queryFirstRow( - 'SELECT * FROM ng_auction_bid WHERE auction_id = %i ORDER BY `amount` DESC LIMIT 1', - $this->info->id - ); + + if($this->_highestBid !== false){ + return $this->_highestBid; + } + + if (!$this->info->detail->isReverse) { + $rawHighestBid = $db->queryFirstRow( + 'SELECT * FROM ng_auction_bid WHERE auction_id = %i ORDER BY `amount` DESC LIMIT 1', + $this->info->id + ); + } else { + $rawHighestBid = $db->queryFirstRow( + 'SELECT * FROM ng_auction_bid WHERE auction_id = %i ORDER BY `amount` ASC LIMIT 1', + $this->info->id + ); + } + if (!$rawHighestBid) { + $this->_highestBid = null; return null; } - return AuctionBidItem::fromArray($rawHighestBid); + $highestBid = AuctionBidItem::fromArray($rawHighestBid); + $this->_highestBid = $highestBid; + return $highestBid; } public function getMyPrevBid(): ?AuctionBidItem @@ -89,6 +98,18 @@ class Auction return $this->info; } + public function shrinkCloseDate(?DateTimeInterface $date): ?string{ + if($date === null){ + $date = new DateTimeImmutable(); + } + + $this->info->closeDate = $date; + $db = DB::db(); + $db->update('ng_auction', $this->info->toArrayExcept('id'), 'id = %i', $this->info->id); + + return null; + } + public function extendCloseDate(DateTimeInterface $date, bool $force = false): ?string { if (!$force) { @@ -171,6 +192,7 @@ class Auction if ($highestBid !== null) { $this->refundBid($highestBid, "{$this->info->title} 경매가 취소되었습니다."); } + $this->rollbackAuction(); } $db->update('ng_auction', $this->info->toArrayExcept('id'), 'id = %i', $this->info->id); @@ -248,7 +270,7 @@ class Auction return null; } - public function bid(int $amount, bool $tryExtendCloseDate = false): ?string + protected function _bid(int $amount, bool $tryExtendCloseDate = false): ?string { $auctionInfo = $this->info; $general = $this->general; @@ -266,10 +288,17 @@ class Auction return '경매가 아직 시작되지 않았습니다.'; } - if ($auctionInfo->buyImmediatelyAmount !== null && $auctionInfo->buyImmediatelyAmount < $amount) { - return '즉시판매가보다 높을 수 없습니다.'; + if (!$auctionInfo->detail->isReverse) { + if ($auctionInfo->buyImmediatelyAmount !== null && $auctionInfo->buyImmediatelyAmount < $amount) { + return '즉시판매가보다 높을 수 없습니다.'; + } + } else { + if ($auctionInfo->buyImmediatelyAmount !== null && $auctionInfo->buyImmediatelyAmount > $amount) { + return '즉시판매가보다 낮을 수 없습니다.'; + } } + if ($auctionInfo->reqResource === ResourceType::inheritancePoint) { return $this->bidInheritPoint($amount, $now, $tryExtendCloseDate); } @@ -279,10 +308,17 @@ class Auction $db = DB::db(); $highestBid = $this->getHighestBid(); - if ($highestBid !== null && $amount <= $highestBid->amount) { - return '현재입찰가보다 높게 입찰해야 합니다.'; + if (!$auctionInfo->detail->isReverse) { + if ($highestBid !== null && $amount <= $highestBid->amount) { + return '현재입찰가보다 높게 입찰해야 합니다.'; + } + } else { + if ($highestBid !== null && $amount >= $highestBid->amount) { + return '현재입찰가보다 낮게 입찰해야 합니다.'; + } } + $myPrevBid = $this->getMyPrevBid(); $morePoint = $amount - ($myPrevBid ? $myPrevBid->amount : 0); @@ -317,7 +353,7 @@ class Auction return '입찰에 실패했습니다: DB 오류'; } - $general->setVar($resType->value, $general->getVar($resType->value) - $morePoint); + $general->increaseVar($resType->value, -$morePoint); if ($myPrevBid === null || ($highestBid !== null && $myPrevBid->id !== $highestBid->id)) { $this->refundBid($highestBid, "{$auctionInfo->title} 경매에 상회입찰자가 나타났습니다."); @@ -325,4 +361,67 @@ class Auction $general->applyDB($db); return null; } + + public function tryFinish(): ?bool + { + $now = new DateTimeImmutable(); + if ($now < $this->info->closeDate) { + return null; + } + + //경매를 닫아야한다. + $highestBid = $this->getHighestBid(); + if ($highestBid === null) { + $this->closeAuction(true); + return true; + } + + if ($highestBid->data->tryExtendCloseDate) { + //연장 요청이 있었다. + $extendedCloseDate = $this->info->closeDate->add(TimeUtil::secondsToDateInterval( + max(static::MIN_EXTENSION_MINUTES_BY_EXTENSION_QUERY, $this->info->turnTerm * static::COEFF_EXTENSION_MINUTES_BY_EXTENSION_QUERY) * 60 + )); + + if ($this->extendCloseDate($extendedCloseDate) === null) { + return false; + } + } + + $bidder = General::createGeneralObjFromDB($highestBid->generalID); + $failReason = $this->finishAuction($highestBid, $bidder); + if ($failReason === null) { + $this->closeAuction(); + return true; + } + + $staticNation = $bidder->getStaticNation(); + $src = new MessageTarget(0, '', 0, 'System', '#000000'); + $dest = new MessageTarget( + $bidder->getID(), + $bidder->getName(), + $bidder->getNationID(), + $staticNation['name'], + $staticNation['color'], + GetImageURL($bidder->getVar('imgsvr'), $bidder->getVar('picture')) + ); + + //TODO: 전역 알림이 나타나야한다. 일반 메시지보다는 중요하고, 메시지보단 약하게.. + //TODO: 바로가기를 제공하는 편이 좋을 것 같다. + $msg = new Message( + Message::MSGTYPE_PRIVATE, + $src, + $dest, + $failReason, + new \DateTime(), + new \DateTime('9999-12-31'), + [] + ); + $msg->send(true); + return false; + } + + abstract public function bid(int $amount, bool $tryExtendCloseDate = false): ?string; + + abstract protected function rollbackAuction(): void; + abstract protected function finishAuction(AuctionBidItem $highestBid, General $bidder): ?string; } diff --git a/hwe/sammo/AuctionBasicResource.php b/hwe/sammo/AuctionBasicResource.php new file mode 100644 index 00000000..9dd1d7c9 --- /dev/null +++ b/hwe/sammo/AuctionBasicResource.php @@ -0,0 +1,223 @@ + 24) { + return '종료기한은 1 ~ 24 턴 이어야 합니다.'; + } + if ($amount < self::MIN_AUCTION_AMOUNT || $amount > self::MAX_AUCTION_AMOUNT) { + return '거래량은 ' . self::MIN_AUCTION_AMOUNT . ' ~ ' . self::MAX_AUCTION_AMOUNT . ' 이어야 합니다.'; + } + if ($startBidAmount < $amount * 0.5 || $amount * 2 < $startBidAmount) { + return '시작거래가는 50% ~ 200% 이어야 합니다.'; + } + if ($finishBidAmount < $amount * 1.1 || $amount * 2 < $finishBidAmount) { + return '즉시거래가는 110% ~ 200% 이어야 합니다.'; + } + if ($finishBidAmount < $startBidAmount * 1.1) { + return '즉시거래가는 시작판매가의 110% 이상이어야 합니다.'; + } + + $hostRes = static::$hostRes; + $hostResName = $hostRes->getName(); + $bidderRes = static::$bidderRes; + $minimumRes = static::$hostRes === ResourceType::rice ? GameConst::$generalMinimumRice : GameConst::$generalMinimumGold; + if ($general->getVar($hostRes->value) < $amount + $minimumRes) { + return "기본 {$hostRes->getName()} {$minimumRes}은 거래할 수 없습니다."; + } + + $db = DB::db(); + $prevAuctionID = $db->queryFirstField( + 'SELECT id FROM ng_auction WHERE opener_general_id = %i AND finished = 0 AND `type` IN %ls', + $general->getID(), + [AuctionType::BuyRice->value, AuctionType::SellRice->value], + ); + if ($prevAuctionID !== null) { + return '아직 경매가 끝나지 않았습니다.'; + } + + $now = new \DateTimeImmutable(); + $gameStor = KVStorage::getStorage($db, 'game_env'); + $turnTerm = $gameStor->getValue('turnterm'); + $closeDate = $now->add(TimeUtil::secondsToDateInterval($closeTurnCnt * $turnTerm * 60)); + + $openResult = static::openAuction(new AuctionInfo( + null, + static::$auctionType, + false, + "$amount", + $general->getId(), + $bidderRes, + $now, + $closeDate, + new AuctionInfoDetail( + "{$hostResName} {$amount} 경매", + $general->getName(), + $amount, + false, + $startBidAmount, + $finishBidAmount, + null, + null + ) + ), $general); + + if (is_string($openResult)) { + return $openResult; + } + + $general->increaseVarWithLimit($hostRes->value, -$amount, 0); + $general->applyDB($db); + + return new self($openResult, $general); + } + + protected function rollbackAuction(): void + { + if ($this->general->getID() === $this->info->openerGeneralID) { + $auctionHost = $this->general; + } else { + $auctionHost = General::createGeneralObjFromDB($this->info->openerGeneralID); + } + + $hostRes = static::$hostRes; + $hostResName = $hostRes->getName(); + + $auctionHost->increaseVar($hostRes->value, $this->info->detail->amount); + $auctionHost->applyDB(DB::db()); + + $staticNation = $auctionHost->getStaticNation(); + $src = new MessageTarget(0, '', 0, 'System', '#000000'); + $dest = new MessageTarget( + $auctionHost->getID(), + $auctionHost->getName(), + $auctionHost->getNationID(), + $staticNation['name'], + $staticNation['color'], + GetImageURL($auctionHost->getVar('imgsvr'), $auctionHost->getVar('picture')) + ); + + //TODO: 전역 알림이 나타나야한다. 일반 메시지보다는 중요하고, 메시지보단 약하게.. + //TODO: 바로가기를 제공하는 편이 좋을 것 같다. + $msg = new Message( + Message::MSGTYPE_PRIVATE, + $src, + $dest, + "{$this->auctionID}번 {$hostResName} 경매에 입찰이 없어 취소되었습니다.", + new \DateTime(), + new \DateTime('9999-12-31'), + [] + ); + $msg->send(true); + } + + protected function finishAuction(AuctionBidItem $highestBid, General $bidder): ?string + { + if ($this->general->getID() === $this->info->openerGeneralID) { + $auctionHost = $this->general; + } else { + $auctionHost = General::createGeneralObjFromDB($this->info->openerGeneralID); + } + + $highestBid = $this->getHighestBid(); + if ($highestBid === null) { + throw new \Exception('입찰자가 없습니다.'); + } + + if ($this->general->getID() === $highestBid->generalID) { + $bidder = $this->general; + } else { + $bidder = General::createGeneralObjFromDB($highestBid->generalID); + } + + $hostRes = static::$hostRes; + $hostResName = $hostRes->getName(); + $bidderRes = static::$bidderRes; + $bidderResName = $bidderRes->getName(); + + $bidAmount = $highestBid->amount; + $auctionAmount = $this->info->detail->amount; + + //거래 종료이므로 서로 반대 + $josaUlBidder = JosaUtil::pick($bidAmount, '을'); + $josaUlHost = JosaUtil::pick($auctionAmount, '을'); + $auctionHost->increaseVar($bidderRes->value, $bidAmount); + $bidder->increaseVar($hostRes->value, $auctionAmount); + + $auctionID = $this->info->id; + + $auctionHost->getLogger()->pushGeneralActionLog( + "{$auctionID}번 거래 성사로 {$bidderResName} {$bidAmount}{$josaUlBidder} 지불, {$hostResName} {$auctionAmount}{$josaUlHost} 획득!", + ActionLogger::EVENT_PLAIN + ); + $bidder->getLogger()->pushGeneralActionLog( + "{$auctionID}번 거래 성사로 {$hostResName} {$auctionAmount}{$josaUlHost} 판매, {$bidderResName} {$bidAmount}{$josaUlBidder} 획득!", + ActionLogger::EVENT_PLAIN + ); + + $josaYiHost = JosaUtil::pick($auctionHost->getName(), '이'); + $josaYiBidder = JosaUtil::pick($bidder->getName(), '이'); + $josaRo = JosaUtil::pick($bidAmount, '로'); + + $auctionLog = []; + $auctionLog[] = "{$auctionID}번 {$hostResName} 경매 성사 : {$auctionHost->getName()}{$josaYiHost} {$hostResName} {$auctionAmount} 판매, {$bidder->getName()}{$josaYiBidder} {$bidAmount} 구매"; + + + if ($highestBid->amount === $this->info->detail->finishBidAmount) { + $auctionLog[0] .= ' ★ 즉시구매가 거래 ★'; + } else if ($highestBid->amount === $this->info->detail->startBidAmount) { + $auctionLog[0] .= " ★ 최고가 거래 ★"; + } + + pushAuctionLog(array_map( + fn ($log) => + $auctionHost->getLogger()->formatText($log, ActionLogger::EVENT_PLAIN), + $auctionLog + )); + + $db = DB::db(); + $bidder->applyDB($db); + $auctionHost->applyDB($db); + + return null; + } + + public function bid(int $amount, bool $_tryExtendCloseDate = true): ?string + { + if ($this->info->openerGeneralID === $this->general->getID()) { + return '자신이 연 경매에 입찰할 수 없습니다.'; + } + $result = $this->_bid($amount, true); + + if(is_string($result)){ + return $result; + } + + if($amount === $this->info->detail->finishBidAmount){ + //즉구, 1턴 후 지급 + $db = DB::db(); + $gameStor = KVStorage::getStorage($db, 'game_env'); + $turnTerm = $gameStor->getValue('turnterm'); + $date = (new DateTimeImmutable())->add(TimeUtil::secondsToDateInterval($turnTerm * 60)); + $this->shrinkCloseDate($date); + } + + } +} diff --git a/hwe/sammo/AuctionBuyRice.php b/hwe/sammo/AuctionBuyRice.php new file mode 100644 index 00000000..71bf451f --- /dev/null +++ b/hwe/sammo/AuctionBuyRice.php @@ -0,0 +1,14 @@ +getRawClassName(); $db = DB::db(); $auctionIDonProgress = $db->queryFirstField( - 'SELECT `id` FROM ng_auction WHERE `finished` = 0 AND `target` = %s AND `target` = %s', + 'SELECT `id` FROM ng_auction WHERE `finished` = 0 AND `type` = %s AND `target` = %s', AuctionType::UniqueItem->value, $itemKey ); @@ -71,6 +70,15 @@ class AuctionUniqueItem extends Auction return '이미 경매가 진행중입니다.'; } + $prevAuctionID = $db->queryFirstField( + 'SELECT id FROM ng_auction WHERE opener_general_id = %i AND finished = 0 AND `type` = %s', + $general->getID(), + AuctionType::UniqueItem->value, + ); + if ($prevAuctionID !== null) { + return '아직 경매가 끝나지 않았습니다.'; + } + $gameStor = KVStorage::getStorage($db, 'game_env'); $givenUnique = $gameStor->getValue('givenUnique') ?? []; $givenUniqueCnt = $givenUnique[$itemKey] ?? 0; @@ -108,7 +116,8 @@ class AuctionUniqueItem extends Auction new AuctionInfoDetail( "{$item->getName()} 경매", static::genObfuscatedName($general->getID()), - + 1, + false, $startAmount, null, 1, @@ -133,164 +142,9 @@ class AuctionUniqueItem extends Auction return $auction; } - private function giveUniqueItem(AuctionBidItem $highestBid, General $bidder): ?string + protected function rollbackAuction(): void { - - $itemKey = $this->info->target; - if ($itemKey === null) { - throw new \Exception('아이템 키가 없습니다.'); - } - $itemObj = buildItemClass($itemKey); - $general = $bidder; - $availableItemTypes = []; - $reasons = []; - $db = DB::db(); - $gameStor = KVStorage::getStorage($db, 'game_env'); - [$startYear, $year] = $gameStor->getValuesAsArray(['startyear', 'year']); - $relYear = $year - $startYear; - $availableEquipUniqueCnt = 1; - foreach (GameConst::$maxUniqueItemLimit as $tmpVals) { - [$targetYear, $targetTrialCnt] = $tmpVals; - if ($relYear < $targetYear) { - break; - } - $availableEquipUniqueCnt = $targetTrialCnt; - } - - $availableEquipUniqueCnt = Util::valueFit($availableEquipUniqueCnt, null, count(GameConst::$allItems)); - - foreach ($general->getItems() as $item) { - if (!$item->isBuyable()) { - $availableEquipUniqueCnt -= 1; - } - } - - if ($availableEquipUniqueCnt <= 0) { - return '유니크 아이템 소유 제한 상태입니다. 종료 시간이 연장됩니다.'; - } - - foreach (GameConst::$allItems as $itemType => $itemList) { - //아직은 그런 경우는 없지만 동일 유니크를 여러 부위에 장착할 수 있을지도 모름 - if (!key_exists($itemKey, $itemList)) { - continue; - } - - $ownItem = $general->getItem($itemType); - if ($ownItem->getRawClassName() == $itemKey) { - //FIXME: 이 경우에는 환불이 되던가 해야함. - $reasons[] = '이미 그 유니크를 가지고 있습니다.'; - continue; - } - - if (!$ownItem->isBuyable()) { - $reasons[] = '이미 다른 유니크를 가지고 있습니다.'; - continue; - } - - $availableCnt = $itemList[$itemKey]; - $occupiedCnt = $db->queryFirstField('SELECT count(*) FROM general WHERE %b = %s', $itemType, $itemKey); - if ($occupiedCnt >= $availableCnt) { - //FIXME: 이 경우에는 환불이 되던가 해야함. - $reasons[] = '그 유니크는 모두 점유되었습니다.'; - continue; - } - $availableItemTypes[] = $itemType; - } - - if (!$availableItemTypes) { - return join(' ', $reasons); - } - - $itemType = $availableItemTypes[0]; - - $general->setVar($itemType, $itemKey); - - $logger = $general->getLogger(); - $nationName = $general->getStaticNation()['name']; - $generalName = $general->getName(); - $josaYi = JosaUtil::pick($generalName, '이'); - $itemName = $itemObj->getName(); - $itemRawName = $itemObj->getRawName(); - $josaUl = JosaUtil::pick($itemRawName, '을'); - - $logger->pushGeneralActionLog("{$itemName}{$josaUl} 습득했습니다!"); - $logger->pushGeneralHistoryLog("{$itemName}{$josaUl} 습득"); - $logger->pushGlobalActionLog("{$generalName}{$josaYi} {$itemName}{$josaUl} 습득했습니다!"); - $logger->pushGlobalHistoryLog("【보물발견】{$nationName}{$generalName}{$josaYi} {$itemName}{$josaUl} 습득했습니다!"); - - $general->applyDB($db); - - $givenUnique = $gameStor->getValue('givenUnique') ?? []; - $givenUnique[$itemKey] = ($givenUnique[$itemKey] ?? 0) + 1; - $gameStor->setValue('givenUnique', $givenUnique); - - return null; - } - - public function checkCloseDate(): ?bool - { - $now = new DateTimeImmutable(); - if ($now < $this->info->closeDate) { - return null; - } - - //경매를 닫아야한다. - $highestBid = $this->getHighestBid(); - if ($highestBid === null) { - $this->closeAuction(); - return true; - } - - if ($highestBid->data->tryExtendCloseDate) { - //연장 요청이 있었다. - $extendedCloseDate = $this->info->closeDate->add(TimeUtil::secondsToDateInterval( - max(static::MIN_EXTENSION_MINUTES_BY_EXTENSION_QUERY, $this->info->turnTerm * static::COEFF_EXTENSION_MINUTES_BY_EXTENSION_QUERY) * 60 - )); - - if ($this->extendCloseDate($extendedCloseDate) === null) { - return false; - } - } - - $bidder = General::createGeneralObjFromDB($highestBid->generalID); - - $failReason = $this->giveUniqueItem($highestBid, $bidder); - if ($failReason === null) { - $this->closeAuction(); - return true; - } - - $staticNation = $bidder->getStaticNation(); - $src = new MessageTarget(0, '', 0, 'System', '#000000'); - $dest = new MessageTarget( - $bidder->getID(), - $bidder->getName(), - $bidder->getNationID(), - $staticNation['name'], - $staticNation['color'], - GetImageURL($bidder->getVar('imgsvr'), $bidder->getVar('picture')) - ); - - //TODO: 전역 알림이 나타나야한다. 일반 메시지보다는 중요하고, 메시지보단 약하게.. - //TODO: 바로가기를 제공하는 편이 좋을 것 같다. - $msg = new Message( - Message::MSGTYPE_PRIVATE, - $src, - $dest, - $failReason, - new \DateTime(), - new \DateTime('9999-12-31'), - [] - ); - $msg->send(true); - - //아이템 제한에 걸렸다면 자동 연장 - $extendedCloseDate = $this->info->closeDate->add(TimeUtil::secondsToDateInterval( - max(static::MIN_EXTENSION_MINUTES_BY_EXTENSION_QUERY, $this->info->turnTerm * static::COEFF_EXTENSION_MINUTES_BY_EXTENSION_QUERY) * 60 - )); - - $this->extendCloseDate($extendedCloseDate, true); - return false; + // 유니크 옥션의 개최자는 운영자이므로 할 일이 없다. } public function bid(int $amount, bool $tryExtendCloseDate = false): ?string @@ -349,6 +203,104 @@ class AuctionUniqueItem extends Auction } } - return parent::bid($amount, $tryExtendCloseDate); + return $this->_bid($amount, $tryExtendCloseDate); + } + + protected function finishAuction(AuctionBidItem $highestBid, General $bidder): ?string + { + $itemKey = $this->info->target; + if ($itemKey === null) { + throw new \Exception('아이템 키가 없습니다.'); + } + $itemObj = buildItemClass($itemKey); + $general = $bidder; + $availableItemTypes = []; + $reasons = []; + $db = DB::db(); + $gameStor = KVStorage::getStorage($db, 'game_env'); + [$startYear, $year] = $gameStor->getValuesAsArray(['startyear', 'year']); + $relYear = $year - $startYear; + $availableEquipUniqueCnt = 1; + foreach (GameConst::$maxUniqueItemLimit as $tmpVals) { + [$targetYear, $targetTrialCnt] = $tmpVals; + if ($relYear < $targetYear) { + break; + } + $availableEquipUniqueCnt = $targetTrialCnt; + } + + $availableEquipUniqueCnt = Util::valueFit($availableEquipUniqueCnt, null, count(GameConst::$allItems)); + + foreach ($general->getItems() as $item) { + if (!$item->isBuyable()) { + $availableEquipUniqueCnt -= 1; + } + } + + if ($availableEquipUniqueCnt <= 0) { + //제한에 걸렸다면 자동 연장 + $extendedCloseDate = $this->info->closeDate->add(TimeUtil::secondsToDateInterval( + max(static::MIN_EXTENSION_MINUTES_BY_EXTENSION_QUERY, $this->info->turnTerm * static::COEFF_EXTENSION_MINUTES_BY_EXTENSION_QUERY) * 60 + )); + + $this->extendCloseDate($extendedCloseDate, true); + return '유니크 아이템 소유 제한 상태입니다. 종료 시간이 연장됩니다.'; + } + + foreach (GameConst::$allItems as $itemType => $itemList) { + //아직은 그런 경우는 없지만 동일 유니크를 여러 부위에 장착할 수 있을지도 모름 + if (!key_exists($itemKey, $itemList)) { + continue; + } + + $ownItem = $general->getItem($itemType); + if ($ownItem->getRawClassName() == $itemKey) { + //FIXME: 이 경우에는 환불이 되던가 해야함. + $reasons[] = '이미 그 유니크를 가지고 있습니다.'; + continue; + } + + if (!$ownItem->isBuyable()) { + $reasons[] = '이미 다른 유니크를 가지고 있습니다.'; + continue; + } + + $availableCnt = $itemList[$itemKey]; + $occupiedCnt = $db->queryFirstField('SELECT count(*) FROM general WHERE %b = %s', $itemType, $itemKey); + if ($occupiedCnt >= $availableCnt) { + //FIXME: 이 경우에는 환불이 되던가 해야함. + $reasons[] = '그 유니크는 모두 점유되었습니다.'; + continue; + } + $availableItemTypes[] = $itemType; + } + + if (!$availableItemTypes) { + return join(' ', $reasons); + } + + $itemType = $availableItemTypes[0]; + + $general->setVar($itemType, $itemKey); + + $logger = $general->getLogger(); + $nationName = $general->getStaticNation()['name']; + $generalName = $general->getName(); + $josaYi = JosaUtil::pick($generalName, '이'); + $itemName = $itemObj->getName(); + $itemRawName = $itemObj->getRawName(); + $josaUl = JosaUtil::pick($itemRawName, '을'); + + $logger->pushGeneralActionLog("{$itemName}{$josaUl} 습득했습니다!"); + $logger->pushGeneralHistoryLog("{$itemName}{$josaUl} 습득"); + $logger->pushGlobalActionLog("{$generalName}{$josaYi} {$itemName}{$josaUl} 습득했습니다!"); + $logger->pushGlobalHistoryLog("【보물발견】{$nationName}{$generalName}{$josaYi} {$itemName}{$josaUl} 습득했습니다!"); + + $general->applyDB($db); + + $givenUnique = $gameStor->getValue('givenUnique') ?? []; + $givenUnique[$itemKey] = ($givenUnique[$itemKey] ?? 0) + 1; + $gameStor->setValue('givenUnique', $givenUnique); + return null; } } diff --git a/hwe/sammo/DTO/AuctionInfoDetail.php b/hwe/sammo/DTO/AuctionInfoDetail.php index b73dfc11..d394fd76 100644 --- a/hwe/sammo/DTO/AuctionInfoDetail.php +++ b/hwe/sammo/DTO/AuctionInfoDetail.php @@ -11,11 +11,13 @@ class AuctionInfoDetail extends DTO public function __construct( public string $title, public string $openerName, - - public int $minAmount, - + public int $amount, #[NullIsUndefined] - public ?int $buyImmediatelyAmount, + public ?bool $isReverse, + + public int $startBidAmount, + #[NullIsUndefined] + public ?int $finishBidAmount, #[NullIsUndefined] public ?int $remainCloseDateExtensionCnt, #[NullIsUndefined] diff --git a/hwe/sammo/Enums/AuctionType.php b/hwe/sammo/Enums/AuctionType.php index ee40883e..e0a7e39d 100644 --- a/hwe/sammo/Enums/AuctionType.php +++ b/hwe/sammo/Enums/AuctionType.php @@ -1,8 +1,14 @@ Date: Mon, 6 Jun 2022 23:52:52 +0900 Subject: [PATCH 13/23] =?UTF-8?q?feat,wip:=20=EA=B2=BD=EB=A7=A4=EC=9E=A5?= =?UTF-8?q?=20API=20=EC=9E=91=EC=84=B1?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- hwe/func_history.php | 4 +- hwe/sammo/API/Auction/BidBuyRiceAuction.php | 51 +++++++++ hwe/sammo/API/Auction/BidSellRiceAuction.php | 50 +++++++++ hwe/sammo/API/Auction/BidUniqueAuction.php | 12 --- .../Auction/GetActiveResourceAuctionList.php | 101 ++++++++++++++++++ hwe/sammo/API/Auction/OpenBuyRiceAuction.php | 39 ++++--- hwe/sammo/API/Auction/OpenSellRiceAuction.php | 84 +++++++++++++++ hwe/sammo/API/Auction/OpenUniqueAuction.php | 2 +- hwe/sammo/Auction.php | 8 +- hwe/sammo/AuctionBasicResource.php | 45 +++++--- hwe/sammo/AuctionSellRice.php | 3 - hwe/sammo/AuctionUniqueItem.php | 24 ++++- hwe/sql/schema.sql | 2 +- 13 files changed, 372 insertions(+), 53 deletions(-) create mode 100644 hwe/sammo/API/Auction/BidBuyRiceAuction.php create mode 100644 hwe/sammo/API/Auction/BidSellRiceAuction.php create mode 100644 hwe/sammo/API/Auction/GetActiveResourceAuctionList.php create mode 100644 hwe/sammo/API/Auction/OpenSellRiceAuction.php diff --git a/hwe/func_history.php b/hwe/func_history.php index fd5241e4..687e9e52 100644 --- a/hwe/func_history.php +++ b/hwe/func_history.php @@ -90,8 +90,8 @@ function pushAuctionLog($log) { pushRawFileLog(__DIR__."/logs/".UniqueConst::$serverID."/_auctionlog.txt", $log); } -function getAuctionLogRecent(int $count) { - return join('
', array_reverse(getFormattedFileLogRecent(__DIR__."/logs/".UniqueConst::$serverID."/_auctionlog.txt", $count, 300))); +function getAuctionLogRecent(int $count): array { + return array_reverse(getRawFileLogRecent(__DIR__."/logs/".UniqueConst::$serverID."/_auctionlog.txt", $count, 300)); } //DB-based diff --git a/hwe/sammo/API/Auction/BidBuyRiceAuction.php b/hwe/sammo/API/Auction/BidBuyRiceAuction.php new file mode 100644 index 00000000..8c8b1043 --- /dev/null +++ b/hwe/sammo/API/Auction/BidBuyRiceAuction.php @@ -0,0 +1,51 @@ +args); + $v->rule('required', [ + 'auctionID', + 'amount', + ]) + ->rule('int', 'amount') + ->rule('int', 'auctionID'); + + if (!$v->validate()) { + return $v->errorStr(); + } + return null; + } + + public function getRequiredSessionMode(): int + { + return static::REQ_GAME_LOGIN; + } + + public function launch(Session $session, ?DateTimeInterface $modifiedSince, ?string $reqEtag) + { + $auctionID = $this->args['auctionID']; + $amount = $this->args['amount']; + + $generalID = $session->generalID; + $general = General::createGeneralObjFromDB($generalID); + $auction = new AuctionBuyRice($auctionID, $general); + $result = $auction->bid($amount, true); + + if (is_string($result)) { + return $result; + } + + return null; + } +} diff --git a/hwe/sammo/API/Auction/BidSellRiceAuction.php b/hwe/sammo/API/Auction/BidSellRiceAuction.php new file mode 100644 index 00000000..3acb49fa --- /dev/null +++ b/hwe/sammo/API/Auction/BidSellRiceAuction.php @@ -0,0 +1,50 @@ +args); + $v->rule('required', [ + 'auctionID', + 'amount', + ]) + ->rule('int', 'amount') + ->rule('int', 'auctionID'); + + if (!$v->validate()) { + return $v->errorStr(); + } + return null; + } + + public function getRequiredSessionMode(): int + { + return static::REQ_GAME_LOGIN; + } + + public function launch(Session $session, ?DateTimeInterface $modifiedSince, ?string $reqEtag) + { + $auctionID = $this->args['auctionID']; + $amount = $this->args['amount']; + + $generalID = $session->generalID; + $general = General::createGeneralObjFromDB($generalID); + $auction = new AuctionSellRice($auctionID, $general); + $result = $auction->bid($amount, true); + + if (is_string($result)) { + return $result; + } + + return null; + } +} diff --git a/hwe/sammo/API/Auction/BidUniqueAuction.php b/hwe/sammo/API/Auction/BidUniqueAuction.php index 9c5c7948..725bae84 100644 --- a/hwe/sammo/API/Auction/BidUniqueAuction.php +++ b/hwe/sammo/API/Auction/BidUniqueAuction.php @@ -9,22 +9,10 @@ use sammo\Validator; use sammo\GameConst; use sammo\General; -use function sammo\buildItemClass; - class BidUniqueAuction extends \sammo\BaseAPI { public function validateArgs(): ?string { - $availableItems = []; - foreach (GameConst::$allItems as $items) { - foreach ($items as $itemKey => $amount) { - if ($amount == 0) { - continue; - } - $availableItems[$itemKey] = $amount; - } - } - $v = new Validator($this->args); $v->rule('required', [ 'auctionID', diff --git a/hwe/sammo/API/Auction/GetActiveResourceAuctionList.php b/hwe/sammo/API/Auction/GetActiveResourceAuctionList.php new file mode 100644 index 00000000..c7057341 --- /dev/null +++ b/hwe/sammo/API/Auction/GetActiveResourceAuctionList.php @@ -0,0 +1,101 @@ +query( + 'SELECT * FROM `ng_auction` WHERE `type` IN %ls AND `finished` != 0 ORDER BY `close_date` DESC', + [ + AuctionType::BuyRice->value, + AuctionType::SellRice->value, + ] + ); + + $auctionIDList = []; + foreach ($rawAuctions as $rawAuction) { + $auctionIDList[] = $rawAuction['id']; + } + + $rawHighestBids = Util::convertArrayToDict($db->query( + 'SELECT bid.* FROM `ng_auction_bid` bid INNER JOIN ( + SELECT `auction_id`, MAX(`amount`) as `max_amount` + FROM `ng_auction_bid` + WHERE `auction_id` IN %li + GROUP BY `auction_id` + ORDER BY `amount` + ) AS max_bid + ON bid.`auction_id` = max_bid.`auction_id` AND bid.`amount` = max_bid.`max_amount`', + $auctionIDList, + ) ?? [], 'auction_id'); + /** @var array */ + $highestBids = Util::mapWithKey( + fn ($auctionID, $bid) => AuctionBidItem::fromArray($bid), + $rawHighestBids + ); + + foreach ($rawAuctions as $rawAuction) { + $detail = Json::decode($rawAuction['detail']); + unset($rawAuction['detail']); + $rawAuction = array_merge($rawAuction, $detail); + + $highestBid = $highestBids[$rawAuction['id']] ?? null; + if($highestBid === null){ + $rawAuction['highestBid'] = null; + } + else { + $rawAuction['hightestBid'] = [ + 'amount' => $highestBid->amount, + 'date' => TimeUtil::format($highestBid->date, false), + 'generalID' => $highestBid->generalID, + 'generalName' => $highestBid->data->generalName, + ]; + } + + if ($rawAuction['type'] == AuctionType::BuyRice->value) { + $buyRiceList[] = $rawAuction; + } else { + $sellRiceList[] = $rawAuction; + } + } + + $recentLogs = getAuctionLogRecent(20); + + return [ + 'result' => true, + 'buyRice' => $buyRiceList, + 'sellRice' => $sellRiceList, + 'recentLogs' => $recentLogs + ]; + } +} diff --git a/hwe/sammo/API/Auction/OpenBuyRiceAuction.php b/hwe/sammo/API/Auction/OpenBuyRiceAuction.php index ef3928ec..c51d128c 100644 --- a/hwe/sammo/API/Auction/OpenBuyRiceAuction.php +++ b/hwe/sammo/API/Auction/OpenBuyRiceAuction.php @@ -4,13 +4,11 @@ namespace sammo\API\Auction; use sammo\Session; use DateTimeInterface; -use sammo\AuctionUniqueItem; +use sammo\AuctionBuyRice; use sammo\Validator; use sammo\GameConst; use sammo\General; -use function sammo\buildItemClass; - class OpenUniqueAuction extends \sammo\BaseAPI { public function validateArgs(): ?string @@ -27,12 +25,17 @@ class OpenUniqueAuction extends \sammo\BaseAPI $v = new Validator($this->args); $v->rule('required', [ - 'itemID', - 'amount' + 'amount', + 'closeTurnCnt', + 'startBidAmount', + 'finishBidAmount', ]) ->rule('int', 'amount') - ->rule('min', 'amount', GameConst::$inheritItemUniqueMinPoint) - ->rule('keyExists', 'itemID', $availableItems); + ->rule('int', 'closeTurnCnt') + ->rule('min', 'amount', 100) + ->rule('max', 'amount', 10000) + ->rule('int', 'startBidAmount') + ->rule('int', 'finishBidAmount'); if (!$v->validate()) { @@ -48,22 +51,34 @@ class OpenUniqueAuction extends \sammo\BaseAPI public function launch(Session $session, ?DateTimeInterface $modifiedSince, ?string $reqEtag) { - $itemID = $this->args['itemID']; + /** @var int */ $amount = $this->args['amount']; + /** @var int */ + $closeTurnCnt = $this->args['closeTurnCnt']; + + /** @var int */ + $startBidAmount = $this->args['startBidAmount']; + /** @var int */ + $finishBidAmount = $this->args['finishBidAmount']; $generalID = $session->generalID; - $itemObj = buildItemClass($itemID); $general = General::createGeneralObjFromDB($generalID); - $auctionResult = AuctionUniqueItem::openItemAuction($itemObj, $general, $amount); + $auctionResult = AuctionBuyRice::openResourceAuction( + $general, + $amount, + $closeTurnCnt, + $startBidAmount, + $finishBidAmount + ); - if(is_string($auctionResult)) { + if (is_string($auctionResult)) { return $auctionResult; } return [ 'result' => true, - 'auctionID' => $auctionResult->id, + 'auctionID' => $auctionResult->getInfo()->id, ]; } } diff --git a/hwe/sammo/API/Auction/OpenSellRiceAuction.php b/hwe/sammo/API/Auction/OpenSellRiceAuction.php new file mode 100644 index 00000000..deeabb44 --- /dev/null +++ b/hwe/sammo/API/Auction/OpenSellRiceAuction.php @@ -0,0 +1,84 @@ + $amount) { + if ($amount == 0) { + continue; + } + $availableItems[$itemKey] = $amount; + } + } + + $v = new Validator($this->args); + $v->rule('required', [ + 'amount', + 'closeTurnCnt', + 'startBidAmount', + 'finishBidAmount', + ]) + ->rule('int', 'amount') + ->rule('int', 'closeTurnCnt') + ->rule('min', 'amount', 100) + ->rule('max', 'amount', 10000) + ->rule('int', 'startBidAmount') + ->rule('int', 'finishBidAmount'); + + + if (!$v->validate()) { + return $v->errorStr(); + } + return null; + } + + public function getRequiredSessionMode(): int + { + return static::REQ_GAME_LOGIN; + } + + public function launch(Session $session, ?DateTimeInterface $modifiedSince, ?string $reqEtag) + { + /** @var int */ + $amount = $this->args['amount']; + /** @var int */ + $closeTurnCnt = $this->args['closeTurnCnt']; + + /** @var int */ + $startBidAmount = $this->args['startBidAmount']; + /** @var int */ + $finishBidAmount = $this->args['finishBidAmount']; + $generalID = $session->generalID; + + $general = General::createGeneralObjFromDB($generalID); + + $auctionResult = AuctionSellRice::openResourceAuction( + $general, + $amount, + $closeTurnCnt, + $startBidAmount, + $finishBidAmount + ); + + if (is_string($auctionResult)) { + return $auctionResult; + } + + return [ + 'result' => true, + 'auctionID' => $auctionResult->getInfo()->id, + ]; + } +} diff --git a/hwe/sammo/API/Auction/OpenUniqueAuction.php b/hwe/sammo/API/Auction/OpenUniqueAuction.php index ef3928ec..1a70ec1e 100644 --- a/hwe/sammo/API/Auction/OpenUniqueAuction.php +++ b/hwe/sammo/API/Auction/OpenUniqueAuction.php @@ -63,7 +63,7 @@ class OpenUniqueAuction extends \sammo\BaseAPI return [ 'result' => true, - 'auctionID' => $auctionResult->id, + 'auctionID' => $auctionResult->getInfo()->id, ]; } } diff --git a/hwe/sammo/Auction.php b/hwe/sammo/Auction.php index e4b923f3..94fdb3e7 100644 --- a/hwe/sammo/Auction.php +++ b/hwe/sammo/Auction.php @@ -8,6 +8,7 @@ use DateTimeInterface; use sammo\DTO\AuctionBidItem; use sammo\DTO\AuctionBidItemData; use sammo\DTO\AuctionInfo; +use sammo\Enums\AuctionType; use sammo\Enums\InheritanceKey; use sammo\Enums\RankColumn; use sammo\Enums\ResourceType; @@ -17,6 +18,8 @@ abstract class Auction protected AuctionInfo $info; + static AuctionType $auctionType; + public const COEFF_AUCTION_CLOSE_MINUTES = 24; public const COEFF_EXTENSION_MINUTES_PER_BID = 1 / 6; public const COEFF_EXTENSION_MINUTES_LIMIT_BY_BID = 1; @@ -91,6 +94,9 @@ abstract class Auction throw new \RuntimeException("해당 경매가 없습니다: {$auctionID}"); } $this->info = AuctionInfo::fromArray($rawAuctionInfo); + if($this->info->type !== $this->auctionType){ + throw new \RuntimeException("잘못된 경매 타입입니다: {$this->info->type} != {$this->auctionType}"); + } } public function getInfo(): AuctionInfo @@ -420,7 +426,7 @@ abstract class Auction return false; } - abstract public function bid(int $amount, bool $tryExtendCloseDate = false): ?string; + abstract public function bid(int $amount, bool $tryExtendCloseDate): ?string; abstract protected function rollbackAuction(): void; abstract protected function finishAuction(AuctionBidItem $highestBid, General $bidder): ?string; diff --git a/hwe/sammo/AuctionBasicResource.php b/hwe/sammo/AuctionBasicResource.php index 9dd1d7c9..6fbf7420 100644 --- a/hwe/sammo/AuctionBasicResource.php +++ b/hwe/sammo/AuctionBasicResource.php @@ -13,11 +13,10 @@ abstract class AuctionBasicResource extends Auction { const MIN_AUCTION_AMOUNT = 100; const MAX_AUCTION_AMOUNT = 10000; - static AuctionType $auctionType; static ResourceType $hostRes; static ResourceType $bidderRes; - static public function openBuyRiceAuction(General $general, int $amount, int $closeTurnCnt, int $startBidAmount, int $finishBidAmount): self|string + static public function openResourceAuction(General $general, int $amount, int $closeTurnCnt, int $startBidAmount, int $finishBidAmount): self|string { if ($closeTurnCnt < 1 || $closeTurnCnt > 24) { return '종료기한은 1 ~ 24 턴 이어야 합니다.'; @@ -44,15 +43,18 @@ abstract class AuctionBasicResource extends Auction } $db = DB::db(); - $prevAuctionID = $db->queryFirstField( - 'SELECT id FROM ng_auction WHERE opener_general_id = %i AND finished = 0 AND `type` IN %ls', - $general->getID(), - [AuctionType::BuyRice->value, AuctionType::SellRice->value], - ); - if ($prevAuctionID !== null) { - return '아직 경매가 끝나지 않았습니다.'; + if (!($general instanceof DummyGeneral)) { + $prevAuctionID = $db->queryFirstField( + 'SELECT id FROM ng_auction WHERE opener_general_id = %i AND finished = 0 AND `type` IN %ls', + $general->getID(), + [AuctionType::BuyRice->value, AuctionType::SellRice->value], + ); + if ($prevAuctionID !== null) { + return '아직 경매가 끝나지 않았습니다.'; + } } + $now = new \DateTimeImmutable(); $gameStor = KVStorage::getStorage($db, 'game_env'); $turnTerm = $gameStor->getValue('turnterm'); @@ -89,10 +91,21 @@ abstract class AuctionBasicResource extends Auction return new self($openResult, $general); } + static public function genDummy(): DummyGeneral + { + $dummyGeneral = new DummyGeneral(true); + $dummyGeneral->setVar('name', '상인'); + $dummyGeneral->setVar('gold', static::MAX_AUCTION_AMOUNT * 10); + $dummyGeneral->setVar('rice', static::MAX_AUCTION_AMOUNT * 10); + return $dummyGeneral; + } + protected function rollbackAuction(): void { if ($this->general->getID() === $this->info->openerGeneralID) { $auctionHost = $this->general; + } else if ($this->info->openerGeneralID == 0) { + $auctionHost = $this->genDummy(); } else { $auctionHost = General::createGeneralObjFromDB($this->info->openerGeneralID); } @@ -132,6 +145,8 @@ abstract class AuctionBasicResource extends Auction { if ($this->general->getID() === $this->info->openerGeneralID) { $auctionHost = $this->general; + } else if ($this->info->openerGeneralID == 0) { + $auctionHost = $this->genDummy(); } else { $auctionHost = General::createGeneralObjFromDB($this->info->openerGeneralID); } @@ -174,7 +189,6 @@ abstract class AuctionBasicResource extends Auction $josaYiHost = JosaUtil::pick($auctionHost->getName(), '이'); $josaYiBidder = JosaUtil::pick($bidder->getName(), '이'); - $josaRo = JosaUtil::pick($bidAmount, '로'); $auctionLog = []; $auctionLog[] = "{$auctionID}번 {$hostResName} 경매 성사 : {$auctionHost->getName()}{$josaYiHost} {$hostResName} {$auctionAmount} 판매, {$bidder->getName()}{$josaYiBidder} {$bidAmount} 구매"; @@ -188,7 +202,7 @@ abstract class AuctionBasicResource extends Auction pushAuctionLog(array_map( fn ($log) => - $auctionHost->getLogger()->formatText($log, ActionLogger::EVENT_PLAIN), + $bidder->getLogger()->formatText($log, ActionLogger::EVENT_PLAIN), $auctionLog )); @@ -199,18 +213,18 @@ abstract class AuctionBasicResource extends Auction return null; } - public function bid(int $amount, bool $_tryExtendCloseDate = true): ?string + public function bid(int $amount, bool $tryExtendCloseDate = false): ?string { if ($this->info->openerGeneralID === $this->general->getID()) { return '자신이 연 경매에 입찰할 수 없습니다.'; } - $result = $this->_bid($amount, true); + $result = $this->_bid($amount, $tryExtendCloseDate); - if(is_string($result)){ + if (is_string($result)) { return $result; } - if($amount === $this->info->detail->finishBidAmount){ + if ($amount === $this->info->detail->finishBidAmount) { //즉구, 1턴 후 지급 $db = DB::db(); $gameStor = KVStorage::getStorage($db, 'game_env'); @@ -218,6 +232,5 @@ abstract class AuctionBasicResource extends Auction $date = (new DateTimeImmutable())->add(TimeUtil::secondsToDateInterval($turnTerm * 60)); $this->shrinkCloseDate($date); } - } } diff --git a/hwe/sammo/AuctionSellRice.php b/hwe/sammo/AuctionSellRice.php index 98e9dd45..5f87ab5e 100644 --- a/hwe/sammo/AuctionSellRice.php +++ b/hwe/sammo/AuctionSellRice.php @@ -2,9 +2,6 @@ namespace sammo; -use sammo\DTO\AuctionBidItem; -use sammo\DTO\AuctionInfo; -use sammo\DTO\AuctionInfoDetail; use sammo\Enums\AuctionType; use sammo\Enums\ResourceType; diff --git a/hwe/sammo/AuctionUniqueItem.php b/hwe/sammo/AuctionUniqueItem.php index 2a64755e..516f211e 100644 --- a/hwe/sammo/AuctionUniqueItem.php +++ b/hwe/sammo/AuctionUniqueItem.php @@ -14,6 +14,9 @@ use sammo\RandUtil; class AuctionUniqueItem extends Auction { + + static AuctionType $auctionType = AuctionType::UniqueItem; + static public function genObfuscatedName(int $id): string { $db = DB::db(); @@ -131,7 +134,7 @@ class AuctionUniqueItem extends Auction } $auction = new static($auctionID, $general); try { - $auction->bid($startAmount); + $auction->bid($startAmount, false); } catch (\Exception $e) { //실패해선 안된다. $msg = $e->getMessage(); @@ -147,7 +150,7 @@ class AuctionUniqueItem extends Auction // 유니크 옥션의 개최자는 운영자이므로 할 일이 없다. } - public function bid(int $amount, bool $tryExtendCloseDate = false): ?string + public function bid(int $amount, bool $tryExtendCloseDate): ?string { $db = DB::db(); @@ -157,10 +160,21 @@ class AuctionUniqueItem extends Auction AuctionType::UniqueItem->value ) ?? []); + $auctionIDList = []; + foreach($openUniqueAuctions as $auction){ + $auctionIDList[] = $auction->id; + } $db = DB::db(); - $rawHighestBids = Util::convertArrayToDict($db->queryFirstRow( - 'SELECT * FROM ng_auction_bid WHERE auction_id IN %i ORDER BY `amount` DESC LIMIT 1', - $this->info->id + $rawHighestBids = Util::convertArrayToDict($db->query( + 'SELECT bid.* FROM `ng_auction_bid` bid INNER JOIN ( + SELECT `auction_id`, MAX(`amount`) as `max_amount` + FROM `ng_auction_bid` + WHERE `auction_id` IN %li + GROUP BY `auction_id` + ORDER BY `amount` + ) AS max_bid + ON bid.`auction_id` = max_bid.`auction_id` AND bid.`amount` = max_bid.`max_amount`', + $auctionIDList, ) ?? [], 'auction_id'); /** @var array */ $highestBids = Util::mapWithKey( diff --git a/hwe/sql/schema.sql b/hwe/sql/schema.sql index 9a153abf..ef37427b 100644 --- a/hwe/sql/schema.sql +++ b/hwe/sql/schema.sql @@ -721,7 +721,7 @@ CREATE TABLE `ng_auction_bid` ( PRIMARY KEY (`no`), UNIQUE INDEX `by_general` (`general_id`, `auction_id`, `amount`), UNIQUE INDEX `by_owner` (`owner`, `auction_id`, `amount`), - INDEX `by_amount` (`auction_id`, `amount`, `date`), + UNIQUE INDEX `by_amount` (`auction_id`, `amount`), CONSTRAINT `aux` CHECK (json_valid(`aux`)) ) COLLATE='utf8mb4_general_ci' -- 2.54.0 From 52554048ae95053a58998ad6568545b3ac78f641 Mon Sep 17 00:00:00 2001 From: Hide_D Date: Tue, 7 Jun 2022 00:28:37 +0900 Subject: [PATCH 14/23] =?UTF-8?q?refac:=20toArrayExcept=EB=A5=BC=20toArray?= =?UTF-8?q?=EB=A1=9C=20=ED=86=B5=ED=95=A9?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/sammo/DTO/DTO.php | 24 +++++++++--------------- 1 file changed, 9 insertions(+), 15 deletions(-) diff --git a/src/sammo/DTO/DTO.php b/src/sammo/DTO/DTO.php index 45cae1ca..3a9542f9 100644 --- a/src/sammo/DTO/DTO.php +++ b/src/sammo/DTO/DTO.php @@ -2,6 +2,7 @@ namespace sammo\DTO; +use Ds\Set; use sammo\DTO\Attr\Convert; use sammo\DTO\Converter\DefaultConverter; @@ -110,10 +111,13 @@ abstract class DTO return $object; } - public function toArray(): array + public function toArray(string ...$exceptKeys): array { $reflection = new \ReflectionClass($this::class); $result = []; + + $exceptKeySet = new Set($exceptKeys); + foreach ($reflection->getProperties(\ReflectionProperty::IS_PUBLIC) as $property) { $value = $property->getValue($this); $attrs = Util\DTOUtil::getAttrs($property); @@ -123,6 +127,10 @@ abstract class DTO continue; } + if($exceptKeySet->contains($name)){ + continue; + } + if (key_exists(Attr\RawName::class, $attrs)) { $rawAttr = $attrs[Attr\RawName::class]; $attr = new Attr\RawName(...$rawAttr->getArguments()); @@ -153,18 +161,4 @@ abstract class DTO } return $result; } - - public function toArrayExcept(string ...$keys): array{ - $reflection = new \ReflectionClass($this::class); - $values = $this->toArray(); - foreach($keys as $key){ - if(!$reflection->hasProperty($key)){ - throw new \Exception("Key {$key} does not exist"); - } - if(key_exists($key, $values)){ - unset($values[$key]); - } - } - return $values; - } } -- 2.54.0 From 40eda5477a7ac2ade3077f8905363b482c528917 Mon Sep 17 00:00:00 2001 From: Hide_D Date: Tue, 7 Jun 2022 01:18:02 +0900 Subject: [PATCH 15/23] =?UTF-8?q?feat,wip:=20=EA=B2=BD=EB=A7=A4=EC=9E=A5?= =?UTF-8?q?=20API=20export?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- hwe/sammo/API/Auction/BidSellRiceAuction.php | 2 +- .../Auction/GetActiveResourceAuctionList.php | 36 +- .../Auction/GetUniqueItemAuctionDetail.php | 85 ++++ .../API/Auction/GetUniqueItemAuctionList.php | 90 ++++ hwe/sammo/API/Auction/OpenBuyRiceAuction.php | 2 +- hwe/sammo/API/Auction/OpenSellRiceAuction.php | 2 +- hwe/sammo/Auction.php | 6 +- hwe/sammo/DTO/AuctionInfo.php | 7 +- hwe/sammo/DTO/AuctionInfoDetail.php | 2 +- hwe/sql/schema.sql | 4 +- hwe/ts/SammoAPI.ts | 408 +++++++++++------- hwe/ts/defs/API/Auction.ts | 64 +++ 12 files changed, 525 insertions(+), 183 deletions(-) create mode 100644 hwe/sammo/API/Auction/GetUniqueItemAuctionDetail.php create mode 100644 hwe/sammo/API/Auction/GetUniqueItemAuctionList.php create mode 100644 hwe/ts/defs/API/Auction.ts diff --git a/hwe/sammo/API/Auction/BidSellRiceAuction.php b/hwe/sammo/API/Auction/BidSellRiceAuction.php index 3acb49fa..2448a08f 100644 --- a/hwe/sammo/API/Auction/BidSellRiceAuction.php +++ b/hwe/sammo/API/Auction/BidSellRiceAuction.php @@ -8,7 +8,7 @@ use sammo\AuctionSellRice; use sammo\Validator; use sammo\General; -class BidBuyRiceAuction extends \sammo\BaseAPI +class BidSellRiceAuction extends \sammo\BaseAPI { public function validateArgs(): ?string { diff --git a/hwe/sammo/API/Auction/GetActiveResourceAuctionList.php b/hwe/sammo/API/Auction/GetActiveResourceAuctionList.php index c7057341..b3608e14 100644 --- a/hwe/sammo/API/Auction/GetActiveResourceAuctionList.php +++ b/hwe/sammo/API/Auction/GetActiveResourceAuctionList.php @@ -7,6 +7,7 @@ use DateTimeInterface; use sammo\AuctionSellRice; use sammo\DB; use sammo\DTO\AuctionBidItem; +use sammo\DTO\AuctionInfo; use sammo\Enums\AuctionType; use sammo\Validator; use sammo\General; @@ -34,17 +35,18 @@ class GetActiveResourceAuctionList extends \sammo\BaseAPI $buyRiceList = []; $sellRiceList = []; - $rawAuctions = $db->query( + /** @var AuctionInfo[] */ + $auctions = array_map(fn ($raw) => AuctionInfo::fromArray($raw), $db->query( 'SELECT * FROM `ng_auction` WHERE `type` IN %ls AND `finished` != 0 ORDER BY `close_date` DESC', [ AuctionType::BuyRice->value, AuctionType::SellRice->value, ] - ); + )); $auctionIDList = []; - foreach ($rawAuctions as $rawAuction) { - $auctionIDList[] = $rawAuction['id']; + foreach ($auctions as $auction) { + $auctionIDList[] = $auction->id; } $rawHighestBids = Util::convertArrayToDict($db->query( @@ -64,17 +66,24 @@ class GetActiveResourceAuctionList extends \sammo\BaseAPI $rawHighestBids ); - foreach ($rawAuctions as $rawAuction) { - $detail = Json::decode($rawAuction['detail']); - unset($rawAuction['detail']); - $rawAuction = array_merge($rawAuction, $detail); + foreach ($auctions as $auction) { + $rawAuction = [ + 'id' => $auction->id, + 'type' => $auction->type->value, + 'hostGeneralID' => $auction->hostGeneralID, + 'hostName' => $auction->detail->hostName, + 'openDate' => TimeUtil::format($auction->openDate, false), + 'closeDate' => TimeUtil::format($auction->closeDate, false), + 'amount' => $auction->detail->amount, + 'startBidAmount' => $auction->detail->startBidAmount, + 'finishBidAmount' => $auction->detail->finishBidAmount, + ]; $highestBid = $highestBids[$rawAuction['id']] ?? null; - if($highestBid === null){ + if ($highestBid === null) { $rawAuction['highestBid'] = null; - } - else { - $rawAuction['hightestBid'] = [ + } else { + $rawAuction['highestBid'] = [ 'amount' => $highestBid->amount, 'date' => TimeUtil::format($highestBid->date, false), 'generalID' => $highestBid->generalID, @@ -95,7 +104,8 @@ class GetActiveResourceAuctionList extends \sammo\BaseAPI 'result' => true, 'buyRice' => $buyRiceList, 'sellRice' => $sellRiceList, - 'recentLogs' => $recentLogs + 'recentLogs' => $recentLogs, + 'generalID' => $session->generalID, ]; } } diff --git a/hwe/sammo/API/Auction/GetUniqueItemAuctionDetail.php b/hwe/sammo/API/Auction/GetUniqueItemAuctionDetail.php new file mode 100644 index 00000000..5428e62d --- /dev/null +++ b/hwe/sammo/API/Auction/GetUniqueItemAuctionDetail.php @@ -0,0 +1,85 @@ +args); + $v->rule('required', [ + 'auction_id', + ]) + ->rule('integer', 'auction_id'); + + if (!$v->validate()) { + return $v->errorStr(); + } + $this->args['auction_id'] = (int)$this->args['auction_id']; + return null; + } + + public function getRequiredSessionMode(): int + { + return static::REQ_GAME_LOGIN; + } + + public function launch(Session $session, ?DateTimeInterface $modifiedSince, ?string $reqEtag) + { + $db = DB::db(); + + $generalID = $session->generalID; + $auctionID = $this->args['auction_id']; + + $rawAuction = $db->queryFirstRow( + 'SELECT * FROM `ng_auction` WHERE `type` = %s AND `auction_id` = %s', + AuctionType::UniqueItem, + $auctionID + ); + + if (!$rawAuction) { + return '선택한 경매가 없습니다.'; + } + + $auction = AuctionInfo::fromArray($rawAuction); + + /** @var AuctionBidItem[] */ + $bidList = array_map(fn ($raw) => AuctionBidItem::fromArray($raw), $db->query( + 'SELECT * FROM `ng_auction_bid` WHERE `auction_id` = %s ORDER BY `amount` DESC', + $auctionID + ) ?? []); + + $responseBid = []; + foreach ($bidList as $bid) { + $responseBid[] = [ + 'generalName' => $bid->data->generalName, + 'amount' => $bid->amount, + 'isCallerHighestBidder' => $bid->generalID === $generalID, + 'date' => $bid->date, + ]; + } + + return [ + 'result' => true, + 'auction' => [ + 'id' => $auction->id, + 'finished' => $auction->finished, + 'title' => $auction->detail->title, + 'target' => $auction->target, + 'isCallerHost' => $auction->openerGeneralID === $generalID, + 'hostName' => $auction->detail->hostName, + 'remainCloseDateExtensionCnt' => $auction->detail->remainCloseDateExtensionCnt, + 'availableLatestBidCloseDate' => $auction->detail->availableLatestBidCloseDate, + ], + 'bidList' => $responseBid, + ]; + } +} diff --git a/hwe/sammo/API/Auction/GetUniqueItemAuctionList.php b/hwe/sammo/API/Auction/GetUniqueItemAuctionList.php new file mode 100644 index 00000000..47a3027d --- /dev/null +++ b/hwe/sammo/API/Auction/GetUniqueItemAuctionList.php @@ -0,0 +1,90 @@ +generalID; + + /** @var AuctionInfo[] */ + $auctions = array_map(fn($raw)=>AuctionInfo::fromArray($raw), $db->query( + 'SELECT * FROM `ng_auction` WHERE `type` = %s ORDER BY `close_date` DESC', + AuctionType::UniqueItem->value + ) ?? []); + + $auctionIDList = []; + foreach ($auctions as $auction) { + $auctionIDList[] = $auction->id; + } + + $rawHighestBids = Util::convertArrayToDict($db->query( + 'SELECT bid.* FROM `ng_auction_bid` bid INNER JOIN ( + SELECT `auction_id`, MAX(`amount`) as `max_amount` + FROM `ng_auction_bid` + WHERE `auction_id` IN %li + GROUP BY `auction_id` + ORDER BY `amount` + ) AS max_bid + ON bid.`auction_id` = max_bid.`auction_id` AND bid.`amount` = max_bid.`max_amount`', + $auctionIDList, + ) ?? [], 'auction_id'); + /** @var array */ + $highestBids = Util::mapWithKey( + fn ($auctionID, $bid) => AuctionBidItem::fromArray($bid), + $rawHighestBids + ); + + $response = []; + foreach ($auctions as $auction) { + $auctionID = $auction->id; + $highestBid = $highestBids[$auctionID] ?? null; + if($highestBid === null){ + continue; + } + + $response[] = [ + 'id' => $auctionID, + 'finished' => $auction->finished, + 'title' => $auction->detail->title, + 'target' => $auction->target, + 'isCallerHost' => $auction->openerGeneralID === $generalID, + 'hostName' => $auction->detail->hostName, + 'remainCloseDateExtensionCnt' => $auction->detail->remainCloseDateExtensionCnt, + 'availableLatestBidCloseDate' => $auction->detail->availableLatestBidCloseDate, + 'highestBid' => [ + 'generalName' => $highestBid->data->generalName, + 'amount' => $highestBid->amount, + 'isCallerHighestBidder' => $highestBid->generalID === $generalID, + 'date' => $highestBid->date, + ], + ]; + } + + return [ + 'result' => true, + 'list' => $response, + ]; + } +} diff --git a/hwe/sammo/API/Auction/OpenBuyRiceAuction.php b/hwe/sammo/API/Auction/OpenBuyRiceAuction.php index c51d128c..dc07a667 100644 --- a/hwe/sammo/API/Auction/OpenBuyRiceAuction.php +++ b/hwe/sammo/API/Auction/OpenBuyRiceAuction.php @@ -9,7 +9,7 @@ use sammo\Validator; use sammo\GameConst; use sammo\General; -class OpenUniqueAuction extends \sammo\BaseAPI +class OpenBuyRiceAuction extends \sammo\BaseAPI { public function validateArgs(): ?string { diff --git a/hwe/sammo/API/Auction/OpenSellRiceAuction.php b/hwe/sammo/API/Auction/OpenSellRiceAuction.php index deeabb44..c1092494 100644 --- a/hwe/sammo/API/Auction/OpenSellRiceAuction.php +++ b/hwe/sammo/API/Auction/OpenSellRiceAuction.php @@ -9,7 +9,7 @@ use sammo\Validator; use sammo\GameConst; use sammo\General; -class OpenUniqueAuction extends \sammo\BaseAPI +class OpenSellRiceAuction extends \sammo\BaseAPI { public function validateArgs(): ?string { diff --git a/hwe/sammo/Auction.php b/hwe/sammo/Auction.php index 94fdb3e7..be771268 100644 --- a/hwe/sammo/Auction.php +++ b/hwe/sammo/Auction.php @@ -111,7 +111,7 @@ abstract class Auction $this->info->closeDate = $date; $db = DB::db(); - $db->update('ng_auction', $this->info->toArrayExcept('id'), 'id = %i', $this->info->id); + $db->update('ng_auction', $this->info->toArray('id'), 'id = %i', $this->info->id); return null; } @@ -138,7 +138,7 @@ abstract class Auction $this->info->availableLatestBidCloseDate = $closeDate->add(TimeUtil::secondsToDateInterval( max(static::MIN_EXTENSION_MINUTES_PER_BID, $turnTerm * static::COEFF_EXTENSION_MINUTES_PER_BID) * 60 )); - $db->update('ng_auction', $this->info->toArrayExcept('id'), 'id = %i', $this->info->id); + $db->update('ng_auction', $this->info->toArray('id'), 'id = %i', $this->info->id); return null; } @@ -201,7 +201,7 @@ abstract class Auction $this->rollbackAuction(); } - $db->update('ng_auction', $this->info->toArrayExcept('id'), 'id = %i', $this->info->id); + $db->update('ng_auction', $this->info->toArray('id'), 'id = %i', $this->info->id); } private function bidInheritPoint(int $amount, \DateTimeImmutable $now, bool $tryExtendCloseDate): ?string diff --git a/hwe/sammo/DTO/AuctionInfo.php b/hwe/sammo/DTO/AuctionInfo.php index 3116680b..dc8a63f7 100644 --- a/hwe/sammo/DTO/AuctionInfo.php +++ b/hwe/sammo/DTO/AuctionInfo.php @@ -5,6 +5,7 @@ namespace sammo\DTO; use sammo\DTO\Attr\Convert; use sammo\DTO\Attr\JsonString; use sammo\DTO\Attr\NullIsUndefined; +use sammo\DTO\Attr\RawName; use sammo\DTO\Converter\DateTimeConverter; use sammo\Enums\AuctionType; use sammo\Enums\ResourceType; @@ -17,11 +18,15 @@ class AuctionInfo extends DTO public AuctionType $type, public bool $finished, public ?string $target, - public int $openerGeneralID, + #[RawName('host_general_id')] + public int $hostGeneralID, + #[RawName('reqResource')] public ResourceType $reqResource, + #[RawName('open_date')] #[Convert(DateTimeConverter::class)] public \DateTimeImmutable $openDate, + #[RawName('close_date')] #[Convert(DateTimeConverter::class)] public \DateTimeImmutable $closeDate, diff --git a/hwe/sammo/DTO/AuctionInfoDetail.php b/hwe/sammo/DTO/AuctionInfoDetail.php index d394fd76..e30726c2 100644 --- a/hwe/sammo/DTO/AuctionInfoDetail.php +++ b/hwe/sammo/DTO/AuctionInfoDetail.php @@ -10,7 +10,7 @@ class AuctionInfoDetail extends DTO { public function __construct( public string $title, - public string $openerName, + public string $hostName, public int $amount, #[NullIsUndefined] public ?bool $isReverse, diff --git a/hwe/sql/schema.sql b/hwe/sql/schema.sql index ef37427b..5e7746b1 100644 --- a/hwe/sql/schema.sql +++ b/hwe/sql/schema.sql @@ -696,14 +696,14 @@ CREATE TABLE `ng_auction` ( `type` ENUM('buyRice','sellRice','uniqueItem') NOT NULL COLLATE 'utf8mb4_bin', `finished` BIT(1) NOT NULL, `target` VARCHAR(50) NULL DEFAULT NULL COLLATE 'utf8mb4_bin', - `opener_general_id` INT(11) NOT NULL, + `host_general_id` INT(11) NOT NULL, `req_resource` ENUM('gold','rice','inheritPoint') NOT NULL COLLATE 'utf8mb4_bin', `open_date` DATETIME NOT NULL, `close_date` DATETIME NOT NULL, `detail` LONGTEXT NOT NULL COLLATE 'utf8mb4_bin', PRIMARY KEY (`id`) USING BTREE, INDEX `by_close` (`finished`, `type`, `close_date`) USING BTREE, - INDEX `by_general_id` (`opener_general_id`, `type`, `finished`) USING BTREE, + INDEX `by_general_id` (`host_general_id`, `type`, `finished`) USING BTREE, CONSTRAINT `detail` CHECK (json_valid(`detail`)) ) COLLATE='utf8mb4_general_ci' diff --git a/hwe/ts/SammoAPI.ts b/hwe/ts/SammoAPI.ts index 041102cf..e60ddca0 100644 --- a/hwe/ts/SammoAPI.ts +++ b/hwe/ts/SammoAPI.ts @@ -1,7 +1,16 @@ import type { Args } from "./processing/args"; import { - callSammoAPI, extractHttpMethod, GET, PATCH, POST, PUT, - type APITail, type APICallT, type RawArgType, type ValidResponse, type InvalidResponse + callSammoAPI, + extractHttpMethod, + GET, + PATCH, + POST, + PUT, + type APITail, + type APICallT, + type RawArgType, + type ValidResponse, + type InvalidResponse, } from "./util/callSammoAPI"; export type { ValidResponse, InvalidResponse }; import { APIPathGen, NumVar, StrVar } from "./util/APIPathGen.js"; @@ -12,168 +21,247 @@ import type { inheritBuffType } from "./defs/API/InheritAction"; import type { SetBlockWarResponse, GeneralListResponse as NationGeneralListResponse } from "./defs/API/Nation"; import type { UploadImageResponse } from "./defs/API/Misc"; import type { GeneralLogType, GetGeneralLogResponse, JoinArgs } from "./defs/API/General"; -import type { GetConstResponse, GetCurrentHistoryResponse, GetDiplomacyResponse, GetHistoryResponse } from "./defs/API/Global"; +import type { + GetConstResponse, + GetCurrentHistoryResponse, + GetDiplomacyResponse, + GetHistoryResponse, +} from "./defs/API/Global"; import type { CachedMapResult, GeneralListResponse, ItemTypeKey, MapResult } from "./defs"; import type { VoteDetailResult, VoteListResult } from "./defs/API/Vote"; +import type { ActiveResourceAuctionList, OpenAuctionResponse, UniqueItemAuctionDetail, UniqueItemAuctionList } from "./defs/API/Auction"; const apiRealPath = { - Betting: { - Bet: PUT as APICallT<{ - bettingID: number, - bettingType: number[], - amount: number, - }>, - GetBettingDetail: NumVar('betting_id', - GET as APICallT - ), - GetBettingList: GET as APICallT<{ - req?: 'bettingNation' | 'tournament' - }, BettingListResponse>, - }, - Command: { - GetReservedCommand: GET as APICallT, - PushCommand: PUT as APICallT<{ - amount: number - }>, - RepeatCommand: PUT as APICallT<{ - amount: number - }>, - ReserveCommand: PUT as APICallT<{ - turnList: number[], - action: string, - arg?: Args - }, ReserveCommandResponse>, - ReserveBulkCommand: PUT as APICallT<{ - turnList: number[], - action: string, - arg?: Args - }[], ReserveBulkCommandResponse>, - }, - General: { - Join: POST as APICallT, - GetGeneralLog: GET as APICallT<{ - reqType: GeneralLogType, - reqTo?: number - }, GetGeneralLogResponse>, - DropItem: PUT as APICallT<{ - itemType: ItemTypeKey - }> - }, - Global: { - GeneralList: GET as APICallT, - GeneralListWithToken: GET as APICallT, - GetConst: GET as APICallT, - GetHistory: StrVar('serverID')( - NumVar('year', - NumVar('month', GET as APICallT - ))), - GetCurrentHistory: GET as APICallT, - GetMap: GET as APICallT<{ - neutralView?: 0 | 1, - showMe?: 0 | 1, - }, MapResult>, - GetCachedMap: GET as APICallT, - GetDiplomacy: GET as APICallT, - ExecuteEngine: POST as APICallT, - }, - InheritAction: { - BuyHiddenBuff: PUT as APICallT<{ - type: inheritBuffType, - level: number - }>, - BuyRandomUnique: PUT as APICallT, - BuySpecificUnique: PUT as APICallT<{ - item: string, - amount: number, - }>, - ResetSpecialWar: PUT as APICallT, - ResetTurnTime: PUT as APICallT, - SetNextSpecialWar: PUT as APICallT<{ - type: string, - }>, - }, - Misc: { - UploadImage: POST as APICallT<{ - imageData: string, - }, UploadImageResponse> - }, - NationCommand: { - GetReservedCommand: GET as APICallT, - PushCommand: PUT as APICallT<{ - amount: number - }>, - RepeatCommand: PUT as APICallT<{ - amount: number - }>, - ReserveCommand: PUT as APICallT<{ - turnList: number[], - action: string, - arg?: Args - }, ReserveCommandResponse>, - ReserveBulkCommand: PUT as APICallT<{ - turnList: number[], - action: string, - arg?: Args - }[], ReserveBulkCommandResponse>, - }, - Nation: { - GeneralList: GET as APICallT, - SetNotice: PUT as APICallT<{ - msg: string, - }>, - SetScoutMsg: PUT as APICallT<{ - msg: string, - }>, - SetBill: PATCH as APICallT<{ - amount: number, - }>, - SetRate: PATCH as APICallT<{ - amount: number, - }>, - SetSecretLimit: PATCH as APICallT<{ - amount: number, - }>, - SetBlockWar: PATCH as APICallT<{ - value: boolean, - }, SetBlockWarResponse>, - SetBlockScout: PATCH as APICallT<{ - value: boolean, - }>, - GetGeneralLog: GET as APICallT<{ - generalID: number, - reqType: GeneralLogType, - reqTo?: number - }, GetGeneralLogResponse> - }, - Vote: { - AddComment: POST as APICallT<{ - voteID: number, - text: string, - }>, - GetVoteList: GET as APICallT, - GetVoteDetail: GET as APICallT<{ - voteID: number - }, VoteDetailResult>, - NewVote: POST as APICallT<{ - title: string, - multipleOptions?: number, - endDate?: string, - options: string[], - keepOldVote?: boolean, - }>, - Vote: POST as APICallT<{ - voteID: number, - selection: number[], - }, ValidResponse & {wonLottery: boolean}>, - } + Auction: { + BidBuyRiceAuction: PUT as APICallT<{ + auctionID: number; + amount: number; + }>, + BidSellRiceAuction: PUT as APICallT<{ + auctionID: number; + amount: number; + }>, + GetActiveResourceAuctionList: GET as APICallT, + OpenBuyRiceAuction: POST as APICallT< + { + amount: number; + closeTurnCnt: number; + startBidAmount: number; + finishBidAmount: number; + }, + OpenAuctionResponse + >, + OpenSellRiceAuction: POST as APICallT< + { + amount: number; + closeTurnCnt: number; + startBidAmount: number; + finishBidAmount: number; + }, + OpenAuctionResponse + >, + + BidUniqueAuction: PUT as APICallT<{ + auctionID: number; + amount: number; + }>, + GetUniqueItemAuctionDetail: GET as APICallT<{ + auctionID: number; + }, UniqueItemAuctionDetail>, + GetUniqueItemAuctionList: GET as APICallT, + OpenUniqueAuction: POST as APICallT<{ + itemID: string, + amount: number, + }, OpenAuctionResponse>, + }, + Betting: { + Bet: PUT as APICallT<{ + bettingID: number; + bettingType: number[]; + amount: number; + }>, + GetBettingDetail: NumVar("betting_id", GET as APICallT), + GetBettingList: GET as APICallT< + { + req?: "bettingNation" | "tournament"; + }, + BettingListResponse + >, + }, + Command: { + GetReservedCommand: GET as APICallT, + PushCommand: PUT as APICallT<{ + amount: number; + }>, + RepeatCommand: PUT as APICallT<{ + amount: number; + }>, + ReserveCommand: PUT as APICallT< + { + turnList: number[]; + action: string; + arg?: Args; + }, + ReserveCommandResponse + >, + ReserveBulkCommand: PUT as APICallT< + { + turnList: number[]; + action: string; + arg?: Args; + }[], + ReserveBulkCommandResponse + >, + }, + General: { + Join: POST as APICallT, + GetGeneralLog: GET as APICallT< + { + reqType: GeneralLogType; + reqTo?: number; + }, + GetGeneralLogResponse + >, + DropItem: PUT as APICallT<{ + itemType: ItemTypeKey; + }>, + }, + Global: { + GeneralList: GET as APICallT, + GeneralListWithToken: GET as APICallT, + GetConst: GET as APICallT, + GetHistory: StrVar("serverID")(NumVar("year", NumVar("month", GET as APICallT))), + GetCurrentHistory: GET as APICallT, + GetMap: GET as APICallT< + { + neutralView?: 0 | 1; + showMe?: 0 | 1; + }, + MapResult + >, + GetCachedMap: GET as APICallT, + GetDiplomacy: GET as APICallT, + ExecuteEngine: POST as APICallT, + }, + InheritAction: { + BuyHiddenBuff: PUT as APICallT<{ + type: inheritBuffType; + level: number; + }>, + BuyRandomUnique: PUT as APICallT, + BuySpecificUnique: PUT as APICallT<{ + item: string; + amount: number; + }>, + ResetSpecialWar: PUT as APICallT, + ResetTurnTime: PUT as APICallT, + SetNextSpecialWar: PUT as APICallT<{ + type: string; + }>, + }, + Misc: { + UploadImage: POST as APICallT< + { + imageData: string; + }, + UploadImageResponse + >, + }, + NationCommand: { + GetReservedCommand: GET as APICallT, + PushCommand: PUT as APICallT<{ + amount: number; + }>, + RepeatCommand: PUT as APICallT<{ + amount: number; + }>, + ReserveCommand: PUT as APICallT< + { + turnList: number[]; + action: string; + arg?: Args; + }, + ReserveCommandResponse + >, + ReserveBulkCommand: PUT as APICallT< + { + turnList: number[]; + action: string; + arg?: Args; + }[], + ReserveBulkCommandResponse + >, + }, + Nation: { + GeneralList: GET as APICallT, + SetNotice: PUT as APICallT<{ + msg: string; + }>, + SetScoutMsg: PUT as APICallT<{ + msg: string; + }>, + SetBill: PATCH as APICallT<{ + amount: number; + }>, + SetRate: PATCH as APICallT<{ + amount: number; + }>, + SetSecretLimit: PATCH as APICallT<{ + amount: number; + }>, + SetBlockWar: PATCH as APICallT< + { + value: boolean; + }, + SetBlockWarResponse + >, + SetBlockScout: PATCH as APICallT<{ + value: boolean; + }>, + GetGeneralLog: GET as APICallT< + { + generalID: number; + reqType: GeneralLogType; + reqTo?: number; + }, + GetGeneralLogResponse + >, + }, + Vote: { + AddComment: POST as APICallT<{ + voteID: number; + text: string; + }>, + GetVoteList: GET as APICallT, + GetVoteDetail: GET as APICallT< + { + voteID: number; + }, + VoteDetailResult + >, + NewVote: POST as APICallT<{ + title: string; + multipleOptions?: number; + endDate?: string; + options: string[]; + keepOldVote?: boolean; + }>, + Vote: POST as APICallT< + { + voteID: number; + selection: number[]; + }, + ValidResponse & { wonLottery: boolean } + >, + }, } as const; export const SammoAPI = APIPathGen(apiRealPath, (path: string[], tail: APITail, pathParam) => { - const method = extractHttpMethod(tail); - return (args?: RawArgType, returnError?: boolean) => { - if (returnError) { - return callSammoAPI(method, path.join('/'), args, pathParam, true); - } - return callSammoAPI(method, path.join('/'), args, pathParam); - }; -}); \ No newline at end of file + const method = extractHttpMethod(tail); + return (args?: RawArgType, returnError?: boolean) => { + if (returnError) { + return callSammoAPI(method, path.join("/"), args, pathParam, true); + } + return callSammoAPI(method, path.join("/"), args, pathParam); + }; +}); diff --git a/hwe/ts/defs/API/Auction.ts b/hwe/ts/defs/API/Auction.ts new file mode 100644 index 00000000..5fcd792b --- /dev/null +++ b/hwe/ts/defs/API/Auction.ts @@ -0,0 +1,64 @@ +import type { ValidResponse } from "@/util/callSammoAPI"; + +export type BasicResourceAuctionBidder = { + amount: number; + date: string; + generalID: number; + generalName: string; +}; + +export type BasicResourceAuctionInfo = { + id: number; + type: "buyRice" | "sellRice"; + hostGeneralID: number; + hostName: string; + openDate: string; + closeDate: string; + amount: number; + startBidAmount: number; + finishBidAmount: number; + highestBid: BasicResourceAuctionBidder; +}; + +export type UniqueItemAuctionBidder = { + generalName: string; + amount: number; + isCallerHighestBidder: boolean; + date: string; +}; + +export type UniqueItemAuctionInfo = { + id: number; + finished: boolean; + title: string; + target: string; + isCallerHost: boolean; + hostName: string; + remainCloseDateExtensionCnt: number; + availableLatestBidCloseDate: string; +}; + +export type ActiveResourceAuctionList = ValidResponse & { + buyRice: BasicResourceAuctionInfo[]; + sellRice: BasicResourceAuctionInfo[]; + recentLogs: string[]; + generalID: number; +}; + +export type UniqueItemAuctionList = ValidResponse & { + list: ( + | UniqueItemAuctionInfo + | { + highestBid: UniqueItemAuctionBidder; + } + )[]; +}; + +export type UniqueItemAuctionDetail = ValidResponse & { + auction: UniqueItemAuctionInfo; + bidList: UniqueItemAuctionBidder[]; +} + +export type OpenAuctionResponse = ValidResponse & { + auctionID: number; +} \ No newline at end of file -- 2.54.0 From 30401382fdf45d24400dbe0cb70f44099dac8ed9 Mon Sep 17 00:00:00 2001 From: Hide_D Date: Tue, 7 Jun 2022 23:50:07 +0900 Subject: [PATCH 16/23] =?UTF-8?q?fix:=20DTO=EC=9D=98=20=EA=B8=B0=EB=B3=B8?= =?UTF-8?q?=20converter=EC=97=90=EC=84=9C=20bool=EC=9D=80=200,1=20?= =?UTF-8?q?=EA=B0=80=EB=8A=A5?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/sammo/DTO/Converter/DefaultConverter.php | 15 +++++++++++---- 1 file changed, 11 insertions(+), 4 deletions(-) diff --git a/src/sammo/DTO/Converter/DefaultConverter.php b/src/sammo/DTO/Converter/DefaultConverter.php index 49011ea5..27451cdf 100644 --- a/src/sammo/DTO/Converter/DefaultConverter.php +++ b/src/sammo/DTO/Converter/DefaultConverter.php @@ -78,11 +78,18 @@ class DefaultConverter implements Converter return $raw; } - if($type === 'bool' && is_bool($raw)){ - $success = true; - return $raw; + if($type === 'bool'){ + if(is_bool($raw)){ + $success = true; + return $raw; + } + if($raw === 0 || $raw === 1){ + $success = true; + return $raw !== 0; + } } + return null; } @@ -100,7 +107,7 @@ class DefaultConverter implements Converter } } - throw new \Exception('DefaultConverter can not convert'); + throw new \Exception('DefaultConverter can not convert '.gettype($raw)); } public function convertTo(mixed $data): string|array|int|float|bool|null -- 2.54.0 From e0c209bcbbd1691fe6028e222c363302da1417c8 Mon Sep 17 00:00:00 2001 From: Hide_D Date: Wed, 8 Jun 2022 01:09:48 +0900 Subject: [PATCH 17/23] =?UTF-8?q?feat,wip:=20=EC=9E=85=EC=B0=B0=20?= =?UTF-8?q?=EA=B8=B0=EB=8A=A5=20=EB=8F=99=EC=9E=91?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- hwe/_119_b.php | 8 +- hwe/j_install.php | 2 - .../Auction/GetActiveResourceAuctionList.php | 20 +- .../Auction/GetUniqueItemAuctionDetail.php | 4 +- .../API/Auction/GetUniqueItemAuctionList.php | 4 +- hwe/sammo/Auction.php | 61 ++-- hwe/sammo/AuctionBasicResource.php | 20 +- hwe/sammo/AuctionUniqueItem.php | 2 +- hwe/sammo/DTO/AuctionBidItem.php | 4 +- hwe/sammo/DTO/AuctionInfo.php | 2 +- hwe/sql/reset.sql | 1 + hwe/templates/commandButton.php | 2 +- hwe/ts/PageAuction.vue | 27 +- hwe/ts/build_exports.json | 1 + hwe/ts/components/AuctionResource.vue | 279 ++++++++++++++++++ hwe/ts/components/AuctionUniqueItem.vue | 7 + hwe/ts/v_auction.ts | 4 +- hwe/v_auction.php | 2 +- src/sammo/APIHelper.php | 4 +- 19 files changed, 390 insertions(+), 64 deletions(-) create mode 100644 hwe/ts/components/AuctionResource.vue create mode 100644 hwe/ts/components/AuctionUniqueItem.vue diff --git a/hwe/_119_b.php b/hwe/_119_b.php index 9e18f21f..364d36af 100644 --- a/hwe/_119_b.php +++ b/hwe/_119_b.php @@ -50,9 +50,7 @@ switch ($btn) { $db->update('general', [ 'turntime' => $db->sqleval('DATE_SUB(turntime, INTERVAL %i MINUTE)', $minute) ], true); - $db->update('auction', [ - 'expire' => $db->sqleval('DATE_SUB(expire, INTERVAL %i MINUTE)', $minute) - ], true); + //TODO: 경매 시간도 모두 당겨야함 if ($locked) { unlock(); } @@ -78,9 +76,7 @@ switch ($btn) { $db->update('general', [ 'turntime' => $db->sqleval('DATE_ADD(turntime, INTERVAL %i MINUTE)', $minute) ], true); - $db->update('auction', [ - 'expire' => $db->sqleval('DATE_ADD(expire, INTERVAL %i MINUTE)', $minute) - ], true); + //TODO: 경매시간도 모두 뒤로 미뤄야함 if ($locked) { unlock(); } diff --git a/hwe/j_install.php b/hwe/j_install.php index 43301a3d..9a3124ba 100644 --- a/hwe/j_install.php +++ b/hwe/j_install.php @@ -4,8 +4,6 @@ namespace sammo; include "lib.php"; include "func.php"; -WebUtil::requireAJAX(); - $session = Session::requireLogin([])->setReadOnly(); if(!class_exists('\\sammo\\DB')){ diff --git a/hwe/sammo/API/Auction/GetActiveResourceAuctionList.php b/hwe/sammo/API/Auction/GetActiveResourceAuctionList.php index b3608e14..099fec0d 100644 --- a/hwe/sammo/API/Auction/GetActiveResourceAuctionList.php +++ b/hwe/sammo/API/Auction/GetActiveResourceAuctionList.php @@ -37,18 +37,32 @@ class GetActiveResourceAuctionList extends \sammo\BaseAPI $sellRiceList = []; /** @var AuctionInfo[] */ $auctions = array_map(fn ($raw) => AuctionInfo::fromArray($raw), $db->query( - 'SELECT * FROM `ng_auction` WHERE `type` IN %ls AND `finished` != 0 ORDER BY `close_date` DESC', + 'SELECT * FROM `ng_auction` WHERE `type` IN %ls AND `finished` = 0 ORDER BY `close_date` DESC', [ AuctionType::BuyRice->value, AuctionType::SellRice->value, ] )); + $recentLogs = getAuctionLogRecent(20); + + + if (!$auctions) { + return [ + 'result' => true, + 'buyRice' => $buyRiceList, + 'sellRice' => $sellRiceList, + 'recentLogs' => $recentLogs, + 'generalID' => $session->generalID, + ]; + } + $auctionIDList = []; foreach ($auctions as $auction) { $auctionIDList[] = $auction->id; } + $rawHighestBids = Util::convertArrayToDict($db->query( 'SELECT bid.* FROM `ng_auction_bid` bid INNER JOIN ( SELECT `auction_id`, MAX(`amount`) as `max_amount` @@ -87,7 +101,7 @@ class GetActiveResourceAuctionList extends \sammo\BaseAPI 'amount' => $highestBid->amount, 'date' => TimeUtil::format($highestBid->date, false), 'generalID' => $highestBid->generalID, - 'generalName' => $highestBid->data->generalName, + 'generalName' => $highestBid->aux->generalName, ]; } @@ -98,8 +112,6 @@ class GetActiveResourceAuctionList extends \sammo\BaseAPI } } - $recentLogs = getAuctionLogRecent(20); - return [ 'result' => true, 'buyRice' => $buyRiceList, diff --git a/hwe/sammo/API/Auction/GetUniqueItemAuctionDetail.php b/hwe/sammo/API/Auction/GetUniqueItemAuctionDetail.php index 5428e62d..2564995a 100644 --- a/hwe/sammo/API/Auction/GetUniqueItemAuctionDetail.php +++ b/hwe/sammo/API/Auction/GetUniqueItemAuctionDetail.php @@ -60,7 +60,7 @@ class GetUniqueItemAuctionDetail extends \sammo\BaseAPI $responseBid = []; foreach ($bidList as $bid) { $responseBid[] = [ - 'generalName' => $bid->data->generalName, + 'generalName' => $bid->aux->generalName, 'amount' => $bid->amount, 'isCallerHighestBidder' => $bid->generalID === $generalID, 'date' => $bid->date, @@ -74,7 +74,7 @@ class GetUniqueItemAuctionDetail extends \sammo\BaseAPI 'finished' => $auction->finished, 'title' => $auction->detail->title, 'target' => $auction->target, - 'isCallerHost' => $auction->openerGeneralID === $generalID, + 'isCallerHost' => $auction->hostGeneralID === $generalID, 'hostName' => $auction->detail->hostName, 'remainCloseDateExtensionCnt' => $auction->detail->remainCloseDateExtensionCnt, 'availableLatestBidCloseDate' => $auction->detail->availableLatestBidCloseDate, diff --git a/hwe/sammo/API/Auction/GetUniqueItemAuctionList.php b/hwe/sammo/API/Auction/GetUniqueItemAuctionList.php index 47a3027d..0faaf0f8 100644 --- a/hwe/sammo/API/Auction/GetUniqueItemAuctionList.php +++ b/hwe/sammo/API/Auction/GetUniqueItemAuctionList.php @@ -69,12 +69,12 @@ class GetUniqueItemAuctionList extends \sammo\BaseAPI 'finished' => $auction->finished, 'title' => $auction->detail->title, 'target' => $auction->target, - 'isCallerHost' => $auction->openerGeneralID === $generalID, + 'isCallerHost' => $auction->hostGeneralID === $generalID, 'hostName' => $auction->detail->hostName, 'remainCloseDateExtensionCnt' => $auction->detail->remainCloseDateExtensionCnt, 'availableLatestBidCloseDate' => $auction->detail->availableLatestBidCloseDate, 'highestBid' => [ - 'generalName' => $highestBid->data->generalName, + 'generalName' => $highestBid->aux->generalName, 'amount' => $highestBid->amount, 'isCallerHighestBidder' => $highestBid->generalID === $generalID, 'date' => $highestBid->date, diff --git a/hwe/sammo/Auction.php b/hwe/sammo/Auction.php index be771268..cef03db5 100644 --- a/hwe/sammo/Auction.php +++ b/hwe/sammo/Auction.php @@ -46,7 +46,7 @@ abstract class Auction { $db = DB::db(); - if($this->_highestBid !== false){ + if ($this->_highestBid !== false) { return $this->_highestBid; } @@ -75,11 +75,19 @@ abstract class Auction public function getMyPrevBid(): ?AuctionBidItem { $db = DB::db(); - $rawMyPrevBid = $db->queryFirstRow( - 'SELECT * FROM ng_auction_bid WHERE general_id = %i AND auction_id = %i ORDER BY `amount` DESC LIMIT 1', - $this->general->getID(), - $this->info->id - ); + if (!$this->info->detail->isReverse) { + $rawMyPrevBid = $db->queryFirstRow( + 'SELECT * FROM ng_auction_bid WHERE general_id = %i AND auction_id = %i ORDER BY `amount` DESC LIMIT 1', + $this->general->getID(), + $this->info->id + ); + } else { + $rawMyPrevBid = $db->queryFirstRow( + 'SELECT * FROM ng_auction_bid WHERE general_id = %i AND auction_id = %i ORDER BY `amount` ASC LIMIT 1', + $this->general->getID(), + $this->info->id + ); + } if (!$rawMyPrevBid) { return null; } @@ -89,13 +97,14 @@ abstract class Auction public function __construct(protected readonly int $auctionID, protected General $general) { $db = DB::db(); - $rawAuctionInfo = $db->query('SELECT * FROM ng_auction WHERE id = %i', $auctionID); - if ($rawAuctionInfo === null) { + $rawAuctionInfo = $db->queryFirstRow('SELECT * FROM `ng_auction` WHERE id = %i', $auctionID); + if (!$rawAuctionInfo) { throw new \RuntimeException("해당 경매가 없습니다: {$auctionID}"); } $this->info = AuctionInfo::fromArray($rawAuctionInfo); - if($this->info->type !== $this->auctionType){ - throw new \RuntimeException("잘못된 경매 타입입니다: {$this->info->type} != {$this->auctionType}"); + $thisAuctionType = static::$auctionType; + if ($this->info->type !== $thisAuctionType) { + throw new \RuntimeException("잘못된 경매 타입입니다: {$this->info->type->value} != {$thisAuctionType->value}"); } } @@ -104,8 +113,9 @@ abstract class Auction return $this->info; } - public function shrinkCloseDate(?DateTimeInterface $date): ?string{ - if($date === null){ + public function shrinkCloseDate(?DateTimeInterface $date): ?string + { + if ($date === null) { $date = new DateTimeImmutable(); } @@ -196,7 +206,7 @@ abstract class Auction if ($isRollback) { $highestBid = $this->getHighestBid(); if ($highestBid !== null) { - $this->refundBid($highestBid, "{$this->info->title} 경매가 취소되었습니다."); + $this->refundBid($highestBid, "{$this->info->id}번 {$this->info->detail->title} 경매가 취소되었습니다."); } $this->rollbackAuction(); } @@ -216,15 +226,10 @@ abstract class Auction return '현재입찰가보다 높게 입찰해야 합니다.'; } - $rawMyPrevBid = $db->queryFirstRow( - 'SELECT * FROM ng_auction_bid WHERE general_id = %i AND auction_id = %i ORDER BY `amount` DESC LIMIT 1', - $general->getID(), - $auctionInfo->id - ); - if ($rawMyPrevBid === null) { + $myPrevBid = $this->getMyPrevBid(); + if($myPrevBid !== null && $highestBid->no !== $myPrevBid->no){ + //이미 환불 받았으니 무효. $myPrevBid = null; - } else { - $myPrevBid = AuctionBidItem::fromArray($rawMyPrevBid); } $morePoint = $amount - ($myPrevBid ? $myPrevBid->amount : 0); @@ -269,8 +274,8 @@ abstract class Auction $general->increaseInheritancePoint(InheritanceKey::previous, -$morePoint); $general->increaseRankVar(RankColumn::inherit_point_spent_dynamic, $morePoint); - if ($myPrevBid === null || ($highestBid !== null && $myPrevBid->id !== $highestBid->id)) { - $this->refundBid($highestBid, "{$auctionInfo->title} 경매에 상회입찰자가 나타났습니다."); + if ($highestBid !== null && $myPrevBid === null) { + $this->refundBid($highestBid, "{$auctionInfo->id}번 $auctionInfo->detail->title}에 상회입찰자가 나타났습니다."); } $general->applyDB($db); return null; @@ -326,6 +331,10 @@ abstract class Auction $myPrevBid = $this->getMyPrevBid(); + if($myPrevBid !== null && $highestBid->no !== $myPrevBid->no){ + //이미 환불 받았으니 무효. + $myPrevBid = null; + } $morePoint = $amount - ($myPrevBid ? $myPrevBid->amount : 0); $resType = $auctionInfo->reqResource; @@ -361,8 +370,8 @@ abstract class Auction $general->increaseVar($resType->value, -$morePoint); - if ($myPrevBid === null || ($highestBid !== null && $myPrevBid->id !== $highestBid->id)) { - $this->refundBid($highestBid, "{$auctionInfo->title} 경매에 상회입찰자가 나타났습니다."); + if ($highestBid !== null && $myPrevBid === null) { + $this->refundBid($highestBid, "{$auctionInfo->id}번 {$auctionInfo->detail->title}에 상회입찰자가 나타났습니다."); } $general->applyDB($db); return null; @@ -382,7 +391,7 @@ abstract class Auction return true; } - if ($highestBid->data->tryExtendCloseDate) { + if ($highestBid->aux->tryExtendCloseDate) { //연장 요청이 있었다. $extendedCloseDate = $this->info->closeDate->add(TimeUtil::secondsToDateInterval( max(static::MIN_EXTENSION_MINUTES_BY_EXTENSION_QUERY, $this->info->turnTerm * static::COEFF_EXTENSION_MINUTES_BY_EXTENSION_QUERY) * 60 diff --git a/hwe/sammo/AuctionBasicResource.php b/hwe/sammo/AuctionBasicResource.php index 6fbf7420..4d647bad 100644 --- a/hwe/sammo/AuctionBasicResource.php +++ b/hwe/sammo/AuctionBasicResource.php @@ -45,7 +45,7 @@ abstract class AuctionBasicResource extends Auction $db = DB::db(); if (!($general instanceof DummyGeneral)) { $prevAuctionID = $db->queryFirstField( - 'SELECT id FROM ng_auction WHERE opener_general_id = %i AND finished = 0 AND `type` IN %ls', + 'SELECT id FROM ng_auction WHERE host_general_id = %i AND finished = 0 AND `type` IN %ls', $general->getID(), [AuctionType::BuyRice->value, AuctionType::SellRice->value], ); @@ -88,7 +88,7 @@ abstract class AuctionBasicResource extends Auction $general->increaseVarWithLimit($hostRes->value, -$amount, 0); $general->applyDB($db); - return new self($openResult, $general); + return new static($openResult, $general); } static public function genDummy(): DummyGeneral @@ -102,12 +102,12 @@ abstract class AuctionBasicResource extends Auction protected function rollbackAuction(): void { - if ($this->general->getID() === $this->info->openerGeneralID) { + if ($this->general->getID() === $this->info->hostGeneralID) { $auctionHost = $this->general; - } else if ($this->info->openerGeneralID == 0) { + } else if ($this->info->hostGeneralID == 0) { $auctionHost = $this->genDummy(); } else { - $auctionHost = General::createGeneralObjFromDB($this->info->openerGeneralID); + $auctionHost = General::createGeneralObjFromDB($this->info->hostGeneralID); } $hostRes = static::$hostRes; @@ -143,12 +143,12 @@ abstract class AuctionBasicResource extends Auction protected function finishAuction(AuctionBidItem $highestBid, General $bidder): ?string { - if ($this->general->getID() === $this->info->openerGeneralID) { + if ($this->general->getID() === $this->info->hostGeneralID) { $auctionHost = $this->general; - } else if ($this->info->openerGeneralID == 0) { + } else if ($this->info->hostGeneralID == 0) { $auctionHost = $this->genDummy(); } else { - $auctionHost = General::createGeneralObjFromDB($this->info->openerGeneralID); + $auctionHost = General::createGeneralObjFromDB($this->info->hostGeneralID); } $highestBid = $this->getHighestBid(); @@ -215,7 +215,7 @@ abstract class AuctionBasicResource extends Auction public function bid(int $amount, bool $tryExtendCloseDate = false): ?string { - if ($this->info->openerGeneralID === $this->general->getID()) { + if ($this->info->hostGeneralID === $this->general->getID()) { return '자신이 연 경매에 입찰할 수 없습니다.'; } $result = $this->_bid($amount, $tryExtendCloseDate); @@ -232,5 +232,7 @@ abstract class AuctionBasicResource extends Auction $date = (new DateTimeImmutable())->add(TimeUtil::secondsToDateInterval($turnTerm * 60)); $this->shrinkCloseDate($date); } + + return null; } } diff --git a/hwe/sammo/AuctionUniqueItem.php b/hwe/sammo/AuctionUniqueItem.php index 516f211e..4c0b4381 100644 --- a/hwe/sammo/AuctionUniqueItem.php +++ b/hwe/sammo/AuctionUniqueItem.php @@ -74,7 +74,7 @@ class AuctionUniqueItem extends Auction } $prevAuctionID = $db->queryFirstField( - 'SELECT id FROM ng_auction WHERE opener_general_id = %i AND finished = 0 AND `type` = %s', + 'SELECT id FROM ng_auction WHERE host_general_id = %i AND finished = 0 AND `type` = %s', $general->getID(), AuctionType::UniqueItem->value, ); diff --git a/hwe/sammo/DTO/AuctionBidItem.php b/hwe/sammo/DTO/AuctionBidItem.php index d4eecc84..d91d77f4 100644 --- a/hwe/sammo/DTO/AuctionBidItem.php +++ b/hwe/sammo/DTO/AuctionBidItem.php @@ -12,7 +12,7 @@ class AuctionBidItem extends DTO { public function __construct( #[NullIsUndefined] - public ?int $id, + public ?int $no, #[RawName('auction_id')] public int $auctionID, public ?int $owner, @@ -25,7 +25,7 @@ class AuctionBidItem extends DTO #[Convert(DateTimeConverter::class)] public \DateTimeImmutable $date, #[JsonString] - public AuctionBidItemData $data, + public AuctionBidItemData $aux, ) { } } diff --git a/hwe/sammo/DTO/AuctionInfo.php b/hwe/sammo/DTO/AuctionInfo.php index dc8a63f7..f8e1a385 100644 --- a/hwe/sammo/DTO/AuctionInfo.php +++ b/hwe/sammo/DTO/AuctionInfo.php @@ -20,7 +20,7 @@ class AuctionInfo extends DTO public ?string $target, #[RawName('host_general_id')] public int $hostGeneralID, - #[RawName('reqResource')] + #[RawName('req_resource')] public ResourceType $reqResource, #[RawName('open_date')] diff --git a/hwe/sql/reset.sql b/hwe/sql/reset.sql index c6464e56..1bb8ec24 100644 --- a/hwe/sql/reset.sql +++ b/hwe/sql/reset.sql @@ -68,4 +68,5 @@ DROP TABLE IF EXISTS ng_betting; DROP TABLE IF EXISTS vote; DROP TABLE IF EXISTS vote_comment; +DROP TABLE IF EXISTS `ng_auction`; DROP TABLE IF EXISTS `ng_auction_bid`; \ No newline at end of file diff --git a/hwe/templates/commandButton.php b/hwe/templates/commandButton.php index 13341621..12f163e4 100644 --- a/hwe/templates/commandButton.php +++ b/hwe/templates/commandButton.php @@ -16,5 +16,5 @@ '>감 찰 부 유산 관리 내 정보&설정 -거 래 장 +거 래 장 베 팅 장 \ No newline at end of file diff --git a/hwe/ts/PageAuction.vue b/hwe/ts/PageAuction.vue index 1f53efe3..43c77a1f 100644 --- a/hwe/ts/PageAuction.vue +++ b/hwe/ts/PageAuction.vue @@ -1,11 +1,32 @@ - + diff --git a/hwe/ts/build_exports.json b/hwe/ts/build_exports.json index 7c47de8c..20065ecc 100644 --- a/hwe/ts/build_exports.json +++ b/hwe/ts/build_exports.json @@ -22,6 +22,7 @@ "common": "common_deprecated.ts" }, "ingame_vue": { + "v_auction": "v_auction.ts", "v_inheritPoint": "v_inheritPoint.ts", "v_board": "v_board.ts", "v_cachedMap": "v_cachedMap", diff --git a/hwe/ts/components/AuctionResource.vue b/hwe/ts/components/AuctionResource.vue new file mode 100644 index 00000000..3c8d2cb2 --- /dev/null +++ b/hwe/ts/components/AuctionResource.vue @@ -0,0 +1,279 @@ + + + diff --git a/hwe/ts/components/AuctionUniqueItem.vue b/hwe/ts/components/AuctionUniqueItem.vue new file mode 100644 index 00000000..528108c0 --- /dev/null +++ b/hwe/ts/components/AuctionUniqueItem.vue @@ -0,0 +1,7 @@ + + + \ No newline at end of file diff --git a/hwe/ts/v_auction.ts b/hwe/ts/v_auction.ts index 4bef455a..a17a0102 100644 --- a/hwe/ts/v_auction.ts +++ b/hwe/ts/v_auction.ts @@ -1,7 +1,7 @@ import "@scss/auction.scss"; import { createApp } from 'vue' import PageAuction from '@/PageAuction.vue'; -import BootstrapVue3 from 'bootstrap-vue-3' +import BootstrapVue3, { BToastPlugin } from 'bootstrap-vue-3' import { auto500px } from "./util/auto500px"; import { insertCustomCSS } from "./util/customCSS"; import { htmlReady } from "./util/htmlReady"; @@ -11,4 +11,4 @@ auto500px(); htmlReady(() => { insertCustomCSS(); }); -createApp(PageAuction).use(BootstrapVue3).mount('#app') \ No newline at end of file +createApp(PageAuction).use(BootstrapVue3).use(BToastPlugin).mount('#app') \ No newline at end of file diff --git a/hwe/v_auction.php b/hwe/v_auction.php index f9c26bae..2184eb43 100644 --- a/hwe/v_auction.php +++ b/hwe/v_auction.php @@ -18,7 +18,7 @@ $me = $db->queryFirstRow('SELECT no, nation, officer_level, permission, con, tur - <?= UniqueConst::$serverName ?>: <?= $boardName ?> + <?= UniqueConst::$serverName ?>: 경매장 diff --git a/src/sammo/APIHelper.php b/src/sammo/APIHelper.php index 2b74f7e4..8e1bca15 100644 --- a/src/sammo/APIHelper.php +++ b/src/sammo/APIHelper.php @@ -98,10 +98,10 @@ class APIHelper } Json::die($result, $setCache ? 0 : Json::NO_CACHE); } catch (\Exception $e) { - Json::dieWithReason($e->getMessage()); + Json::dieWithReason($e->getMessage()."\n".$e->getTraceAsString()); } catch (\Throwable $e) { logExceptionByCustomHandler($e, false); - Json::dieWithReason($e->getMessage()); + Json::dieWithReason($e->getMessage()."\n".$e->getTraceAsString()); } catch (mixed $e) { Json::dieWithReason(strval($e)); } -- 2.54.0 From 606fa4a922af54546be341eef44d6024589e63b4 Mon Sep 17 00:00:00 2001 From: Hide_D Date: Wed, 8 Jun 2022 01:22:31 +0900 Subject: [PATCH 18/23] =?UTF-8?q?feat,wip:=20=EC=9C=A0=EB=8B=88=ED=81=AC?= =?UTF-8?q?=20=EA=B2=BD=EB=A7=A4=20=EC=8B=9C=EC=9E=91?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- hwe/func.php | 218 ------------------ .../API/InheritAction/BuySpecificUnique.php | 104 --------- hwe/sammo/Enums/ResourceType.php | 2 +- hwe/sammo/General.php | 18 +- hwe/ts/PageInheritPoint.vue | 30 +-- hwe/ts/SammoAPI.ts | 4 - 6 files changed, 18 insertions(+), 358 deletions(-) delete mode 100644 hwe/sammo/API/InheritAction/BuySpecificUnique.php diff --git a/hwe/func.php b/hwe/func.php index 0e7b550b..1dbc29ab 100644 --- a/hwe/func.php +++ b/hwe/func.php @@ -1673,215 +1673,6 @@ function giveRandomUniqueItem(RandUtil $rng, General $general, string $acquireTy return true; } -function rollbackInheritUniqueTrial(General $general, string $itemKey, string $reason) -{ - - $ownerID = $general->getVar('owner'); - - $db = DB::db(); - - $itemTrials = $general->getAuxVar('inheritUniqueTrial'); - LogText("선택유니크 롤백:{$ownerID}", [$itemKey, $itemTrials]); - unset($itemTrials[$itemKey]); - if (count($itemTrials) == 0) { - $itemTrials = null; - } - $general->setAuxVar('inheritUniqueTrial', $itemTrials); - - - $trialStor = KVStorage::getStorage($db, "ut_{$itemKey}"); - $ownTrial = $trialStor->getValue("u{$ownerID}"); - - $itemObj = buildItemClass($itemKey); - $itemName = $itemObj->getName(); - - if ($ownTrial) { - //두 값이 general, KVStorage 둘다 있고, 이중에선 KVStorage 값을 기준으로 하자 따르자 - [,, $amount] = $ownTrial; - $trialStor->deleteValue("u{$ownerID}"); - $general->increaseInheritancePoint(InheritanceKey::previous, $amount); - $general->increaseRankVar(RankColumn::inherit_point_spent_dynamic, -$amount); - LogText("선택유니크 롤백포인트:{$ownerID}", $amount); - - $userLogger = new UserLogger($ownerID); - $userLogger->push("{$itemName} 입찰에 사용한 {$amount} 포인트 반환", "inheritPoint"); - } - - //메시지 - - $staticNation = $general->getStaticNation(); - - $unlimited = new \DateTime('9999-12-31'); - $src = new MessageTarget(0, '', 0, 'System', '#000000'); - $dest = new MessageTarget($general->getID(), $general->getName(), $general->getNationID(), $staticNation['name'], $staticNation['color'], GetImageURL($general->getVar('imgsvr'), $general->getVar('picture'))); - $josaUl = JosaUtil::pick($itemName, '을'); - $msg = new Message( - Message::MSGTYPE_PRIVATE, - $src, - $dest, - "{$itemName}{$josaUl} 얻지 못했습니다. {$reason}", - new DateTime(), - $unlimited, - [] - ); - - $general->applyDB($db); - $msg->send(true); -} - -function tryRollbackInheritUniqueItem(RandUtil $rng, General $general): void -{ - tryInheritUniqueItem($rng, $general, 'Rollback', true); -} - -function tryInheritUniqueItem(RandUtil $rng, General $general, string $acquireType = '아이템', bool $justRollback = false): bool -{ - $ownerID = $general->getVar('owner'); - if (!$ownerID) { - LogText("선택유니크 실패???: {$ownerID}", $general->getName()); - return false; - } - - $itemTrials = $general->getAuxVar('inheritUniqueTrial') ?? []; - arsort($itemTrials); - LogText("선택유니크항목: {$ownerID}", $itemTrials); - - $db = DB::db(); - - $ownTarget = null; - $ownType = null; - - foreach ($itemTrials as $itemKey => $amount) { - $availableItemTypes = []; - $reasons = []; - foreach (GameConst::$allItems as $itemType => $itemList) { - //아직은 그런 경우는 없지만 동일 유니크를 여러 부위에 장착할 수 있을지도 모름 - if (!key_exists($itemKey, $itemList)) { - continue; - } - - $ownItem = $general->getItem($itemType); - if ($ownItem->getRawClassName() == $itemKey) { - $reasons[] = '이미 그 유니크를 가지고 있습니다.'; - continue; - } - - if (!$ownItem->isBuyable()) { - $reasons[] = '이미 다른 유니크를 가지고 있습니다.'; - continue; - } - - $availableCnt = $itemList[$itemKey]; - $occupiedCnt = $db->queryFirstField('SELECT count(*) FROM general WHERE %b = %s', $itemType, $itemKey); - if ($occupiedCnt >= $availableCnt) { - $reasons[] = '그 유니크는 모두 점유되었습니다.'; - continue; - } - $availableItemTypes[] = $itemType; - } - - if (!$availableItemTypes) { - rollbackInheritUniqueTrial($general, $itemKey, join(' ', $reasons)); - continue; - } - $reasons = []; - - $itemType = $rng->choice($availableItemTypes); - - $trialStor = KVStorage::getStorage($db, "ut_{$itemKey}"); //혹시 itemKey의 크기가 37이 넘을 수 있을까? - $anyTrials = $trialStor->getAll(); - if (!$anyTrials) { - //순서가 꼬였던 모양, 실제 값은 storage를 우선시하자 - rollbackInheritUniqueTrial($general, $itemKey, '절차상의 오류입니다.'); - continue; - } - - //XXX: 정렬할 필요 없지 않나? - usort($anyTrials, function ($lhsTrial, $rhsTrial) { - [,, $lhsAmount] = $lhsTrial; - [,, $rhsAmount] = $rhsTrial; - return $rhsAmount <=> $lhsAmount; //큰 값이 앞에 오도록 - }); - - LogText("선택유니크상태 {$ownerID} {$itemKey}", $anyTrials); - - //공동 1등인데 본인이 있을 수도 있다. - [,, $topAmount] = $anyTrials[0]; - if ($amount < $topAmount) { - $compAmount = $topAmount / $amount; - if ($compAmount > 2.0) { - $compText = '엄청난 차이로 '; - } else if ($compAmount > 1.2) { - $compText = '큰 차이로 '; - } else if ($compAmount > 1.05) { - $compText = ''; - } else { - $compText = '아슬아슬한 차이로 '; - } - rollbackInheritUniqueTrial($general, $itemKey, "{$compText}상위 입찰한 장수가 있습니다."); - continue; - } - - //내가 1위다 - if ($ownTarget !== null) { - //이미 다른 아이템을 얻기로 되어있다. - continue; - } - $ownTarget = $itemKey; - $ownType = $itemType; - } - unset($itemKey); - unset($itemType); - - if ($ownTarget === null) { - return false; - } - if ($justRollback) { - return false; - } - - LogText("선택유니크획득{$ownerID}", $ownTarget); - - $trialStor = KVStorage::getStorage($db, "ut_{$ownTarget}"); - $trialStor->deleteValue("u{$ownerID}"); - - //rollbackInheritUniqueTrial 과정 때문에 새로 받아와야함 - $itemTrials = $general->getAuxVar('inheritUniqueTrial'); - unset($itemTrials[$ownTarget]); - $general->setAuxVar('inheritUniqueTrial', $itemTrials); - - $nationName = $general->getStaticNation()['name']; - $generalName = $general->getName(); - $josaYi = JosaUtil::pick($generalName, '이'); - $itemObj = buildItemClass($ownTarget); - $itemName = $itemObj->getName(); - $itemRawName = $itemObj->getRawName(); - $josaUl = JosaUtil::pick($itemRawName, '을'); - - - $general->setVar($ownType, $ownTarget); - - - $logger = $general->getLogger(); - - $logger->pushGeneralActionLog("{$itemName}{$josaUl} 습득했습니다!"); - $logger->pushGeneralHistoryLog("{$itemName}{$josaUl} 습득"); - $logger->pushGlobalActionLog("{$generalName}{$josaYi} {$itemName}{$josaUl} 습득했습니다!"); - $logger->pushGlobalHistoryLog("【{$acquireType}】{$nationName}{$generalName}{$josaYi} {$itemName}{$josaUl} 습득했습니다!"); - - $general->applyDB($db); - - $gameStor = KVStorage::getStorage($db, 'game_env'); - $givenUnique =$gameStor->getValue('givenUnique') ?? []; - $givenUnique[$ownTarget] = ($givenUnique[$ownTarget] ?? 0) + 1; - $gameStor->setValue('givenUnique', $givenUnique); - - //같은 종류의 유니크를 입찰했을 수 있으니 한번 더 검사한다. - tryRollbackInheritUniqueItem($rng, $general); - - return true; -} - function tryUniqueItemLottery(RandUtil $rng, General $general, string $acquireType = '아이템'): bool { $db = DB::db(); @@ -1927,18 +1718,9 @@ function tryUniqueItemLottery(RandUtil $rng, General $general, string $acquireTy $userLogger = new UserLogger($general->getVar('owner')); $userLogger->push(sprintf("유니크를 얻을 공간이 없어 %d 포인트 반환", GameConst::$inheritItemRandomPoint), "inheritPoint"); } - tryRollbackInheritUniqueItem($rng, $general); return false; } - $inheritUnique = $general->getAuxVar('inheritUniqueTrial'); - if ($acquireType != '설문조사' && $inheritUnique && count($inheritUnique) && $availableBuyUnique) { - $trialResult = tryInheritUniqueItem($rng, $general, $acquireType); - if ($trialResult) { - return true; - } - } - $scenario = $gameStor->scenario; $genCount = $db->queryFirstField('SELECT count(*) FROM general WHERE npc<2'); diff --git a/hwe/sammo/API/InheritAction/BuySpecificUnique.php b/hwe/sammo/API/InheritAction/BuySpecificUnique.php deleted file mode 100644 index 3b3f7071..00000000 --- a/hwe/sammo/API/InheritAction/BuySpecificUnique.php +++ /dev/null @@ -1,104 +0,0 @@ - $amount) { - if ($amount == 0) { - continue; - } - $availableItems[$itemKey] = $amount; - } - } - - $v = new Validator($this->args); - $v->rule('required', [ - 'item', - 'amount', - ]) - ->rule('int', 'amount') - ->rule('min', 'amount', GameConst::$inheritItemUniqueMinPoint) - ->rule('keyExists', 'item', $availableItems); - - if (!$v->validate()) { - return $v->errorStr(); - } - return null; - } - - public function getRequiredSessionMode(): int - { - //KVStrorage, General.aux 모두 쓰므로 lock; - return static::REQ_GAME_LOGIN; - } - - public function launch(Session $session, ?DateTimeInterface $modifiedSince, ?string $reqEtag) - { - $itemKey = $this->args['item']; - $amount = $this->args['amount']; - - $userID = $session->userID; - $generalID = $session->generalID; - - $general = General::createGeneralObjFromDB($generalID); - if ($userID != $general->getVar('owner')) { - return '로그인 상태가 이상합니다. 다시 로그인해 주세요.'; - } - - $itemTrials = $general->getAuxVar('inheritUniqueTrial') ?? []; - if (key_exists($itemKey, $itemTrials)) { - return '이미 입찰한 아이템입니다. 다음 턴에 시도해 주세요.'; - } - - foreach(GameConst::$allItems as $itemType => $items){ - if(!key_exists($itemKey, $items)){ - continue; - } - - $prevItem = $general->getItem($itemType); - if(!$prevItem->isBuyable()){ - return '이미 같은 자리에 유니크를 보유하고 있습니다.'; - } - - break; - } - - $db = DB::db(); - $inheritStor = KVStorage::getStorage($db, "inheritance_{$userID}"); - $trialStor = KVStorage::getStorage($db, "ut_{$itemKey}"); - $previousPoint = ($inheritStor->getValue('previous') ?? [0, 0])[0]; - if ($previousPoint < $amount) { - return '충분한 유산 포인트를 가지고 있지 않습니다.'; - } - - $itemObj = buildItemClass($itemKey); - $userLogger = new UserLogger($userID); - $userLogger->push("{$amount} 포인트로 유니크 {$itemObj->getName()} 구입 시도", "inheritPoint"); - $userLogger->flush(); - - $itemTrials[$itemKey] = $amount; - $general->setAuxVar('inheritUniqueTrial', $itemTrials); - $inheritStor->setValue('previous', [$previousPoint - $amount, null]); - $general->increaseRankVar(RankColumn::inherit_point_spent_dynamic, $amount); - $trialStor->setValue("u{$userID}", [$userID, $generalID, $amount]); - $general->applyDB($db); - return null; - } -} diff --git a/hwe/sammo/Enums/ResourceType.php b/hwe/sammo/Enums/ResourceType.php index ca0f0348..ad40456b 100644 --- a/hwe/sammo/Enums/ResourceType.php +++ b/hwe/sammo/Enums/ResourceType.php @@ -5,7 +5,7 @@ enum ResourceType: string { case gold = 'gold'; case rice = 'rice'; - case inheritancePoint = 'inheritancePoint'; + case inheritancePoint = 'inheritPoint'; public function getName(): string { diff --git a/hwe/sammo/General.php b/hwe/sammo/General.php index cb9bab77..c5a5257c 100644 --- a/hwe/sammo/General.php +++ b/hwe/sammo/General.php @@ -610,23 +610,7 @@ class General implements iAction $refundPoint += GameConst::$inheritItemRandomPoint; } - $itemTrials = $this->getAuxVar('inheritUniqueTrial') ?? []; - foreach (array_keys($itemTrials) as $itemKey) { - $trialStor = KVStorage::getStorage($db, "ut_{$itemKey}"); - $ownTrial = $trialStor->getValue("u{$userID}"); - - $itemObj = buildItemClass($itemKey); - $itemName = $itemObj->getName(); - - if (!$ownTrial) { - continue; - } - - [,, $amount] = $ownTrial; - $trialStor->deleteValue("u{$userID}"); - $userLogger->push("사망으로 {$itemName} 입찰에 사용한 {$amount} 포인트 반환", "inheritPoint"); - $refundPoint += $amount; - } + //TODO: 경매 최우선 입찰자인경우 반환 if ($this->getAuxVar('inheritSpecificSpecialWar')) { $this->setAuxVar('inheritSpecificSpecialWar', null); diff --git a/hwe/ts/PageInheritPoint.vue b/hwe/ts/PageInheritPoint.vue index 2bfef428..e97ef8f2 100644 --- a/hwe/ts/PageInheritPoint.vue +++ b/hwe/ts/PageInheritPoint.vue @@ -62,12 +62,12 @@ >
- 구입 + 구입
-
유니크 입찰
+
유니크 경매
- - - - - - - - - - - - - - - - - - - - - - query('SELECT * from auction where type=0 order by expire') as $auction) { - $radio = ""; - $alert = ""; - $alert2 = ""; - if ($auction['no1'] == $me['no']) { - $radio = " disabled"; - } elseif ($auction['no2'] > 0 && $auction['amount'] * 2 <= $auction['value']) { - $radio = " disabled"; - $alert = ""; - $alert2 = ""; - } elseif ($auction['no2'] > 0 && $auction['topv'] <= $auction['value']) { - $radio = " disabled"; - $alert = ""; - $alert2 = ""; - } elseif ($chk == 0) { - $radio = " checked"; - $chk = 1; - } - $pv = round($auction['value'] * 100 / $auction['amount']) / 100 + 0.001; - $pv = substr((string)$pv, 0, 4); - - echo " - - - - - - - - - - - - - - "; - } - ?> - - - - - - - - - - - - - - - - -
- 팝 니 다 -
거래번호선택판매자물품수량시작판매가현재판매가즉시판매가단가구매 예정자거래종료
{$auction['no']}{$auction['name1']}{$auction['amount']}금 {$auction['cost']}{$alert}금 {$auction['value']}{$alert2}{$alert}금 {$auction['topv']}{$alert2}{$alert}{$pv}{$alert2}{$alert}{$auction['name2']}{$alert2}{$auction['expire']}
등록결과
입찰등록 -  지불할 금액: - name=btn value='구매시도' onclick='return confirm("정말 입찰하시겠습니까?");'> -
거래등록 -  종료: 턴 후 -  물품: 쌀 -  판매량: -  시작가: -  즉구가: - name=btn value='판매' onclick='return confirm("정말 판매하시겠습니까?");'> -
- ㆍHint) 거래자가 판매(물품 판매, 금 수령), 입찰자가 구매(물품 구입, 금 지불).
- ㆍHint) 단가가 1.00보다 높을수록 판매자 유리.
- ㆍHint) 단가가 1.00보다 낮을수록 입찰자 유리.
-
-
- - - - - - - - - - - - - - - - - - - query('SELECT * from auction where type=1 order by expire') as $auction) { - $radio = ""; - $alert = ""; - $alert2 = ""; - if ($auction['no1'] == $me['no']) { - $radio = " disabled"; - } elseif ($auction['no2'] > 0 && $auction['amount'] >= $auction['value'] * 2) { - $radio = " disabled"; - $alert = ""; - $alert2 = ""; - } elseif ($auction['no2'] > 0 && $auction['topv'] >= $auction['value']) { - $radio = " disabled"; - $alert = ""; - $alert2 = ""; - } elseif ($chk == 0) { - $radio = " checked"; - $chk = 1; - } - $pv = round($auction['value'] * 100 / $auction['amount']) / 100 + 0.001; - $pv = substr((string)$pv, 0, 4); - echo " - - - - - - - - - - - - - - "; - } - ?> - - - - - - - - - - - - - - - - -
- 삽 니 다 -
거래번호선택구매자물품수량시작구매가현재구매가즉시구매가단가판매 예정자거래종료
{$auction['no']}{$auction['name1']}{$auction['amount']}금 {$auction['cost']}{$alert}금 {$auction['value']}{$alert2}{$alert}금 {$auction['topv']}{$alert2}{$alert}{$pv}{$alert2}{$alert}{$auction['name2']}{$alert2}{$auction['expire']}
등록결과
입찰등록 -  수령할 금액: - name=btn value='판매시도' onclick='return confirm("정말 입찰하시겠습니까?");'> -
거래등록 -  종료: 턴 후 -  물품: 쌀 -  구입량: -  시작가: -  즉구가: - name=btn value='구매' onclick='return confirm("정말 구매하시겠습니까?");'> -
- ㆍHint) 거래자가 구매(물품 구매, 금 지불), 입찰자가 판매(물품 판매, 금 수령).
- ㆍHint) 단가가 1.00보다 낮을수록 구매자 유리.
- ㆍHint) 단가가 1.00보다 높을수록 입찰자 유리.
-
-
- - - - - - - - - - - - - - - - - - - -
- 최 근 기 록 -
- -
- 도 움 말 -
- - ㆍ판매거래는 거래자가 판매할 물품을 거래하면, 구입을 희망하는 사람이 현재가보다 높게 입찰하여 구입하는 방식입니다.
- ㆍHint) 쌀이 귀한 경우는 입찰자가 많아서 자연스레 단가가 오르게 됩니다. (해당 물품을 사려는 가격이 오름)
- ㆍHint) 쌀이 흔한 경우는 초기 가격을 낮게 책정해야 판매가 가능할 겁니다.
- ㆍ구매거래는 거래자가 구입할 물품을 거래하면, 판매를 희망하는 사람이 현재가보다 낮게 입찰하여 판매하는 방식입니다.
- ㆍHint) 쌀이 흔한 경우는 입찰자가 많아서 자연스레 단가가 내리게 됩니다. (해당 물품을 팔려는 가격이 내림)
- ㆍHint) 쌀이 귀한 경우는 초기 가격을 높게 책정해야 구입이 가능할 겁니다.
- ㆍ마감임박때 입찰하는 경우 입찰후 1턴 후로 종료시간이 연장됩니다.
- ㆍ즉시구매가로 입찰하는 경우 입찰후 1턴 후로 종료시간이 결정됩니다.
- ㆍ악용 방지를 위해 50% ~ 200%의 가격에서 거래시작이 가능합니다.
- ㆍ악용 방지를 위해 즉시판매가는 110% 이상, 즉시구매가는 90% 이하의 시세로 가능합니다.
- ㆍ악용 방지를 위해 즉시판매가는 시작판매가의 110% 이상, 즉시구매가는 시작구매가의 90% 이하로 가능합니다.
- ㆍ1인당 도합 1건의 거래와 입찰이 가능합니다.
- ㆍ기본금쌀 1000은 거래에 사용되지 못합니다.
- ㆍ유찰될 때는 거래 과실자에게 거래금의 1%가 벌금으로 부과됩니다.
- ㆍ10단위로 거래가 가능합니다. 1자리는 반올림 처리 됩니다.
- ㆍ★ 최고가 거래 ★ 혹은 ★ 최저가 거래 ★ 는 암거래 및 악용의 가능성이니 감시 부탁드립니다.
- ㆍ거래와 입찰은 취소가 불가능하니 주의하세요!
- ㆍHint) 단가는 금/쌀로 쌀1을 거래하기 위한 금의 양입니다.
- ㆍHint) 단가가 높으면(>1.00) 쌀이 비싸므로 판매가 이득입니다.
- ㆍHint) 단가가 낮으면(<1.00) 금이 비싸므로 구매가 이득입니다.
- ㆍ즐거운 거래! -
-
- - - \ No newline at end of file diff --git a/hwe/c_auction.php b/hwe/c_auction.php deleted file mode 100644 index c4f6c517..00000000 --- a/hwe/c_auction.php +++ /dev/null @@ -1,247 +0,0 @@ -rule('integer', [ - 'amount', - 'cost', - 'topv', - 'value', - 'term', - 'sel' -]); - -$btn = Util::getPost('btn'); -$amount = Util::getPost('amount', 'int'); -$cost = Util::getPost('cost', 'int'); -$topv = Util::getPost('topv', 'int'); -$value = Util::getPost('value', 'int'); -$term = Util::getPost('term', 'int'); -$sel = Util::getPost('sel', 'int'); - -$msg = ''; -$msg2 = ''; - - -//로그인 검사 -$session = Session::requireGameLogin()->setReadOnly(); -$userID = Session::getUserID(); - -$db = DB::db(); -$gameStor = KVStorage::getStorage($db, 'game_env'); - -increaseRefresh("입찰", 1); - -$turnterm = $gameStor->turnterm; - -$me = $db->queryFirstRow('SELECT no,name,gold,rice,special from general where owner=%i', $userID); - -$tradeCount = $db->queryFirstField('SELECT count(no) FROM auction WHERE no1=%i', $me['no']); -$bidCount = $db->queryFirstField('SELECT count(no) FROM auction WHERE no2=%i', $me['no']); - -$btCount = $tradeCount + $bidCount; - -$unit = $turnterm * 60; - -$amount = Util::round($amount, -1); -$cost = Util::round($cost, -1); -$topv = Util::round($topv, -1); -$value = Util::round($value, -1); -if ($term > 24) { - $term = 24; -} - -$valid = 1; -if ($session->userGrade >= 5 || $btCount < 1) { -} else { - $msg = "ㆍ더이상 등록할 수 없습니다."; - $msg2 = "ㆍ더이상 등록할 수 없습니다."; - $valid = 0; - $btn = "hidden"; -} - -if ($btn == "판매") { - if ($term < 0 || $term > 24) { - $msg = "ㆍ종료기한은 1 ~ 24 턴 이어야 합니다."; - $valid = 0; - } - if ($amount < 100 || $amount > 10000) { - $msg = "ㆍ거래량은 100 ~ 10000 이어야 합니다."; - $valid = 0; - } - if ($cost > $amount * 2 || $cost * 2 < $amount) { - $msg = "ㆍ시작판매가는 50% ~ 200% 이어야 합니다."; - $valid = 0; - } - if ($topv * 10 < $amount * 11 || $topv > $amount * 2) { - $msg = "ㆍ즉시판매가는 110% ~ 200% 이어야 합니다."; - $valid = 0; - } - if ($topv * 10 < $cost * 11) { - $msg = "ㆍ즉시판매가는 시작판매가의 110% 이상이어야 합니다."; - $valid = 0; - } - if ($amount > $me['rice'] - GameConst::$defaultRice) { - $msg = "ㆍ기본 군량 ".GameConst::$defaultRice."은 거래할 수 없습니다."; - $valid = 0; - } - if ($valid == 1) { - $msg = "ㆍ등록 성공."; - $date = date("Y-m-d H:i:s", strtotime(date("Y-m-d H:i:s")) + $unit * $term); - $db->insert('auction', [ - 'type'=>0, - 'no1'=>$me['no'], - 'name1'=>$me['name'], - 'amount'=>$amount, - 'cost'=>$cost, - 'value'=>$cost, - 'topv'=>$topv, - 'expire'=>$date - ]); - } -} elseif ($btn == "구매시도") { - $auction = $db->queryFirstRow('SELECT no2,value,topv,expire,amount FROM auction WHERE no=%i LIMIT 1', $sel); - - if ($value == $auction['topv']) { - $valid = 2; - } - if (!$auction) { - $msg = "ㆍ종료된 거래입니다."; - $valid = 0; - } - if ($auction['no2'] > 0 && $value <= $auction['value']) { - $msg = "ㆍ현재판매가보다 높게 입찰해야 합니다."; - $valid = 0; - } - if ($value < $auction['value']) { - $msg = "ㆍ현재판매가보다 높게 입찰해야 합니다."; - $valid = 0; - } - if ($value > $auction['topv']) { - $msg = "ㆍ즉시판매가보다 높을 수 없습니다."; - $valid = 0; - } - if ($value > $me['gold'] - GameConst::$defaultGold) { - $msg = "ㆍ기본 자금 ".GameConst::$defaultGold."은 거래할 수 없습니다."; - $valid = 0; - } - if ($valid == 1) { - $msg = "ㆍ입찰 성공. 거래완료는 빨라도 현재로부터 1턴 뒤입니다."; - $date = date("Y-m-d H:i:s", strtotime(date("Y-m-d H:i:s")) + $unit); - if ($auction['expire'] > $date) { - $date = $auction['expire']; - } - - $db->update('auction', [ - 'value'=>$value, - 'no2'=>$me['no'], - 'name2'=>$me['name'], - 'expire'=>$date, - ], 'no=%i', $sel); - } elseif ($valid == 2) { - $msg = "ㆍ즉시판매 성공. 거래완료는 빨라도 현재로부터 1턴 뒤입니다."; - $date = date("Y-m-d H:i:s", strtotime(date("Y-m-d H:i:s")) + $unit); - - $db->update('auction', [ - 'value'=>$value, - 'no2'=>$me['no'], - 'name2'=>$me['name'], - 'expire'=>$date, - ], 'no=%i', $sel); - } -} elseif ($btn == "구매") { - if ($term < 0 || $term > 24) { - $msg2 = "ㆍ종료기한은 1 ~ 24 턴 이어야 합니다."; - $valid = 0; - } - if ($amount < 100 || $amount > 10000) { - $msg2 = "ㆍ거래량은 100 ~ 10000 이어야 합니다."; - $valid = 0; - } - if ($cost > $amount * 2 || $cost * 2 < $amount) { - $msg2 = "ㆍ시작구매가는 50% ~ 200% 이어야 합니다."; - $valid = 0; - } - if ($topv < $amount * 0.5 || $topv > $amount * 0.9) { - $msg2 = "ㆍ즉시구매가는 50% ~ 90% 이어야 합니다."; - $valid = 0; - } - if ($topv > $cost * 0.9) { - $msg2 = "ㆍ즉시구매가는 시작구매가의 90% 이하이어야 합니다."; - $valid = 0; - } - if ($cost > $me['gold'] - GameConst::$defaultGold) { - $msg2 = "ㆍ기본 자금 ".GameConst::$defaultGold."은 거래할 수 없습니다."; - $valid = 0; - } - if ($valid == 1) { - $msg2 = "ㆍ등록 성공."; - $date = date("Y-m-d H:i:s", strtotime(date("Y-m-d H:i:s")) + $unit * $term); - - $db->insert('auction', [ - 'type'=>1, - 'no1'=>$me['no'], - 'name1'=>$me['name'], - 'amount'=>$amount, - 'cost'=>$cost, - 'value'=>$cost, - 'topv'=>$topv, - 'expire'=>$date - ]); - } -} elseif ($btn == "판매시도") { - $auction = $db->queryFirstRow('SELECT no2,value,topv,expire,amount FROM auction WHERE no=%i LIMIT 1', $sel); - - if ($value == $auction['topv']) { - $valid = 2; - } - if (!$auction) { - $msg2 = "ㆍ종료된 거래입니다."; - $valid = 0; - } - if ($auction['no2'] > 0 && $value >= $auction['value']) { - $msg2 = "ㆍ현재구매가보다 낮게 입찰해야 합니다."; - $valid = 0; - } - if ($value > $auction['value']) { - $msg2 = "ㆍ현재구매가보다 낮게 입찰해야 합니다."; - $valid = 0; - } - if ($value < $auction['topv']) { - $msg2 = "ㆍ즉시구매가보다 낮을 수 없습니다."; - $valid = 0; - } - if ($value > $me['rice'] - GameConst::$defaultRice) { - $msg2 = "ㆍ기본 군량 ".GameConst::$defaultRice."은 거래할 수 없습니다."; - $valid = 0; - } - if ($valid == 1) { - $msg2 = "ㆍ입찰 성공. 거래완료는 빨라도 현재로부터 1턴 뒤입니다."; - $date = date("Y-m-d H:i:s", strtotime(date("Y-m-d H:i:s")) + $unit); - if ($auction['expire'] > $date) { - $date = $auction['expire']; - } - - $db->update('auction', [ - 'value'=>$value, - 'no2'=>$me['no'], - 'name2'=>$me['name'], - 'expire'=>$date, - ], 'no=%i', $sel); - } elseif ($valid == 2) { - $msg2 = "ㆍ즉시구매 성공. 거래완료는 빨라도 현재로부터 1턴 뒤입니다."; - $date = date("Y-m-d H:i:s", strtotime(date("Y-m-d H:i:s")) + $unit); - - $db->update('auction', [ - 'value'=>$value, - 'no2'=>$me['no'], - 'name2'=>$me['name'], - 'expire'=>$date, - ], 'no=%i', $sel); - } -} - -Submit("b_auction.php", $msg, $msg2); diff --git a/hwe/func.php b/hwe/func.php index a90a618c..c3a1bf60 100644 --- a/hwe/func.php +++ b/hwe/func.php @@ -1207,7 +1207,7 @@ function checkDelay() ], 'turntime<=DATE_ADD(turntime, INTERVAL %i MINUTE)', $term); $db->update('ng_auction', [ 'close_date' => $db->sqleval('DATE_ADD(close_date, INTERVAL %i MINUTE)', $minute) - ], true); + ], 'finished = 0'); } } diff --git a/hwe/func_auction.php b/hwe/func_auction.php index bc5f07ae..e9f38529 100644 --- a/hwe/func_auction.php +++ b/hwe/func_auction.php @@ -7,39 +7,25 @@ use sammo\Enums\AuctionType; function registerAuction(RandUtil $rng) { $db = DB::db(); - $gameStor = KVStorage::getStorage($db, 'game_env'); - - $admin = $gameStor->getValues(['startyear', 'year', 'month', 'turnterm']); - - $unit = 60 * $admin['turnterm']; // 장수들 평금,평쌀 - $general = $db->queryFirstRow('SELECT avg(gold) as gold, avg(rice) as rice,max(gold) as maxgold from general where npc<2'); + [$avgGold, $avgRice] = $db->queryFirstList('SELECT avg(gold), avg(rice) from general where npc<2'); + $avgGold = Util::valueFit($avgGold, 1000, 20000); + $avgRice = Util::valueFit($avgRice, 1000, 20000); - if ($general['gold'] < 1000) { - $general['gold'] = 1000; - } - if ($general['gold'] > 20000) { - $general['gold'] = 20000; - } - if ($general['rice'] < 1000) { - $general['rice'] = 1000; - } - if ($general['rice'] > 20000) { - $general['rice'] = 20000; - } + $neutralAuctionCnt = Util::convertPairArrayToDict($db->queryAllLists( + 'SELECT `type`, count(*) FROM ng_auction WHERE `type` IN %s AND `host_general_id`=0 GROUP BY `type`', + [AuctionType::BuyRice->value, AuctionType::SellRice->value], + )); - $count = $db->queryFirstField('SELECT count(*) FROM auction WHERE type=0 AND no1=0'); - $count += 5; - - $dummyGeneral = AuctionBasicResource::genDummy(); + $neutralbuyRiceCnt = $neutralAuctionCnt[AuctionType::BuyRice->value]; // 판매건 등록 - if ($rng->nextBool(1 / $count)) { + if ($rng->nextBool(1 / ($neutralbuyRiceCnt + 5))) { //평균 쌀의 5% ~ 25% $mul = $rng->nextRangeInt(1, 5); - $amount = $general['rice'] / 20 * $mul; - $cost = $general['gold'] / 20 * 0.9 * $mul; + $amount = $avgRice / 20 * $mul; + $cost = $avgGold / 20 * 0.9 * $mul; $topv = $amount * 2; $cost = Util::valueFit($cost, $amount * 0.8, $amount * 1.2); @@ -48,18 +34,18 @@ function registerAuction(RandUtil $rng) $topv = Util::round($topv, -1); $term = $rng->nextRangeInt(3, 12); + $dummyGeneral = AuctionBasicResource::genDummy(); AuctionBuyRice::openResourceAuction($dummyGeneral, $amount, $term, $cost, $topv); } - $count = $db->queryFirstField('SELECT count(*) FROM auction WHERE type=1 AND no1=0'); - $count += 5; + $neutralSellRiceCnt = $neutralAuctionCnt[AuctionType::SellRice->value]; // 구매건 등록 - if ($rng->nextBool(1 / $count)) { + if ($rng->nextBool(1 / ($neutralSellRiceCnt + 5))) { //평균 쌀의 5% ~ 25% $mul = $rng->nextRangeInt(1, 5); - $amount = $general['rice'] / 20 * $mul; - $cost = $general['gold'] / 20 * 1.1 * $mul; - $topv = $amount * 0.5; + $amount = $avgGold / 20 * $mul; + $cost = $avgRice / 20 * 1.1 * $mul; + $topv = $amount * 2; $cost = Util::valueFit($cost, $amount * 0.8, $amount * 1.2); $amount = Util::round($amount, -1); @@ -67,6 +53,7 @@ function registerAuction(RandUtil $rng) $topv = Util::round($topv, -1); $term = $rng->nextRangeInt(3, 12); + $dummyGeneral = AuctionBasicResource::genDummy(); AuctionSellRice::openResourceAuction($dummyGeneral, $amount, $term, $cost, $topv); } } @@ -74,32 +61,29 @@ function registerAuction(RandUtil $rng) function processAuction() { $db = DB::db(); - $gameStor = KVStorage::getStorage($db, 'game_env'); $now = TimeUtil::now(); - [$year, $month] = $gameStor->getValuesAsArray(['year', 'month']); - - $admin = $gameStor->getValues(['year', 'month']); - $auctionList = $db->queryFirstColumn( - 'SELECT id, `type` FROM ng_auction WHERE `type` IN %ls AND `close_date` <= %s', - [AuctionType::BuyRice->value, AuctionType::SellRice->value], + 'SELECT id, `type` FROM ng_auction WHERE `close_date` <= %s AND finished = 0', $now ); - if(!$auctionList){ + if (!$auctionList) { return; } $dummyGeneral = AuctionBasicResource::genDummy(); - foreach($auctionList as [$auctionID, $rawAuctionType]) { + foreach ($auctionList as [$auctionID, $rawAuctionType]) { $auctionType = AuctionType::from($rawAuctionType); - if($auctionType === AuctionType::BuyRice){ + if ($auctionType === AuctionType::BuyRice) { $auction = new AuctionBuyRice($auctionID, $dummyGeneral); - } - else{ + } else if ($auctionType === AuctionType::SellRice) { $auction = new AuctionSellRice($auctionID, $dummyGeneral); + } else if ($auctionType === AuctionType::UniqueItem) { + $auction = new AuctionUniqueItem($auctionID, $dummyGeneral); + } else { + throw new \Exception('Unknown auction type'); } $auction->tryFinish(); } diff --git a/hwe/index.php b/hwe/index.php index a8fc6416..be5f5c2e 100644 --- a/hwe/index.php +++ b/hwe/index.php @@ -88,7 +88,7 @@ $color = "cyan"; $serverName = UniqueConst::$serverName; $serverCnt = $gameStor->server_cnt; -$auctionCount = $db->queryFirstField('SELECT count(`no`) FROM auction'); +$auctionCount = $db->queryFirstField('SELECT count(*) FROM ng_auction WHERE finished = 0'); $isTournamentActive = $gameStor->tournament > 0; $isTournamentApplicationOpen = $gameStor->tournament == 1; $isBettingActive = $gameStor->tournament == 6; diff --git a/hwe/sammo/Command/General/che_NPC능동.php b/hwe/sammo/Command/General/che_NPC능동.php index 70815485..7ee07fc3 100644 --- a/hwe/sammo/Command/General/che_NPC능동.php +++ b/hwe/sammo/Command/General/che_NPC능동.php @@ -13,8 +13,6 @@ use \sammo\{ use \sammo\Constraint\Constraint; use \sammo\Constraint\ConstraintHelper; -use function sammo\tryRollbackInheritUniqueItem; - class che_NPC능동 extends Command\GeneralCommand{ static protected $actionName = 'NPC능동'; @@ -98,7 +96,6 @@ class che_NPC능동 extends Command\GeneralCommand{ $this->setResultTurn(new LastTurn(static::getName(), $this->arg)); } - tryRollbackInheritUniqueItem($rng, $general); $general->applyDB($db); return true; diff --git a/hwe/sammo/Command/General/che_모반시도.php b/hwe/sammo/Command/General/che_모반시도.php index 0be42d50..c5e0d005 100644 --- a/hwe/sammo/Command/General/che_모반시도.php +++ b/hwe/sammo/Command/General/che_모반시도.php @@ -15,8 +15,6 @@ use \sammo\Constraint\Constraint; use \sammo\Constraint\ConstraintHelper; use sammo\Enums\InheritanceKey; -use function sammo\tryRollbackInheritUniqueItem; - class che_모반시도 extends Command\GeneralCommand{ static protected $actionName = '모반시도'; @@ -99,7 +97,6 @@ class che_모반시도 extends Command\GeneralCommand{ $this->setResultTurn(new LastTurn(static::getName(), $this->arg)); $general->increaseInheritancePoint(InheritanceKey::active_action, 1); $general->checkStatChange(); - tryRollbackInheritUniqueItem($rng, $general); $general->applyDB($db); $lordGeneral->applyDB($db); diff --git a/hwe/sammo/Command/General/che_방랑.php b/hwe/sammo/Command/General/che_방랑.php index 74a559be..786f7464 100644 --- a/hwe/sammo/Command/General/che_방랑.php +++ b/hwe/sammo/Command/General/che_방랑.php @@ -17,7 +17,6 @@ use sammo\Enums\InheritanceKey; use function sammo\DeleteConflict; use function sammo\refreshNationStaticInfo; -use function sammo\tryRollbackInheritUniqueItem; class che_방랑 extends Command\GeneralCommand{ static protected $actionName = '방랑'; @@ -125,7 +124,6 @@ class che_방랑 extends Command\GeneralCommand{ refreshNationStaticInfo(); $general->increaseInheritancePoint(InheritanceKey::active_action, 1); $this->setResultTurn(new LastTurn(static::getName(), $this->arg)); - tryRollbackInheritUniqueItem($rng, $general); $general->applyDB($db); return true; diff --git a/hwe/sammo/Command/General/che_선양.php b/hwe/sammo/Command/General/che_선양.php index 922dcc48..6fc4f3f3 100644 --- a/hwe/sammo/Command/General/che_선양.php +++ b/hwe/sammo/Command/General/che_선양.php @@ -21,8 +21,6 @@ use \sammo\Constraint\Constraint; use \sammo\Constraint\ConstraintHelper; use sammo\Enums\InheritanceKey; -use function sammo\tryRollbackInheritUniqueItem; - class che_선양 extends Command\GeneralCommand { static protected $actionName = '선양'; @@ -139,7 +137,6 @@ class che_선양 extends Command\GeneralCommand $general->increaseInheritancePoint(InheritanceKey::active_action, 1); $this->setResultTurn(new LastTurn(static::getName(), $this->arg)); $general->checkStatChange(); - tryRollbackInheritUniqueItem($rng, $general); $general->applyDB($db); $destGeneral->applyDB($db); diff --git a/hwe/sammo/Command/General/che_소집해제.php b/hwe/sammo/Command/General/che_소집해제.php index 6d474a4b..f4a9b84f 100644 --- a/hwe/sammo/Command/General/che_소집해제.php +++ b/hwe/sammo/Command/General/che_소집해제.php @@ -13,8 +13,6 @@ use \sammo\{ use \sammo\Constraint\Constraint; use \sammo\Constraint\ConstraintHelper; -use function sammo\tryRollbackInheritUniqueItem; - class che_소집해제 extends Command\GeneralCommand{ static protected $actionName = '소집해제'; @@ -82,7 +80,6 @@ class che_소집해제 extends Command\GeneralCommand{ $general->addDedication($ded); $this->setResultTurn(new LastTurn(static::getName(), $this->arg)); $general->checkStatChange(); - tryRollbackInheritUniqueItem($rng, $general); $general->applyDB($db); return true; diff --git a/hwe/sammo/Command/General/che_요양.php b/hwe/sammo/Command/General/che_요양.php index b316240e..8b06217f 100644 --- a/hwe/sammo/Command/General/che_요양.php +++ b/hwe/sammo/Command/General/che_요양.php @@ -13,8 +13,6 @@ use \sammo\{ use \sammo\Constraint\Constraint; use \sammo\Constraint\ConstraintHelper; -use function sammo\tryRollbackInheritUniqueItem; - class che_요양 extends Command\GeneralCommand{ static protected $actionName = '요양'; @@ -71,7 +69,6 @@ class che_요양 extends Command\GeneralCommand{ $general->addDedication($ded); $this->setResultTurn(new LastTurn(static::getName(), $this->arg)); $general->checkStatChange(); - tryRollbackInheritUniqueItem($rng, $general); $general->applyDB($db); return true; diff --git a/hwe/sammo/Command/General/che_첩보.php b/hwe/sammo/Command/General/che_첩보.php index 5102d1aa..1f3c72c2 100644 --- a/hwe/sammo/Command/General/che_첩보.php +++ b/hwe/sammo/Command/General/che_첩보.php @@ -12,7 +12,6 @@ use \sammo\Command; use \sammo\Json; use function \sammo\searchDistance; -use function sammo\tryRollbackInheritUniqueItem; use \sammo\Constraint\ConstraintHelper; use sammo\CityConst; @@ -214,7 +213,6 @@ class che_첩보 extends Command\GeneralCommand $general->increaseVar('leadership_exp', 1); $this->setResultTurn(new LastTurn(static::getName(), $this->arg)); $general->checkStatChange(); - tryRollbackInheritUniqueItem($rng, $general); $general->applyDB($db); return true; diff --git a/hwe/sammo/Command/General/che_하야.php b/hwe/sammo/Command/General/che_하야.php index 5cbcbd69..b00aea20 100644 --- a/hwe/sammo/Command/General/che_하야.php +++ b/hwe/sammo/Command/General/che_하야.php @@ -15,8 +15,6 @@ use \sammo\Constraint\ConstraintHelper; use sammo\CityConst; use sammo\Enums\InheritanceKey; -use function sammo\tryRollbackInheritUniqueItem; - class che_하야 extends Command\GeneralCommand{ static protected $actionName = '하야'; @@ -118,7 +116,6 @@ class che_하야 extends Command\GeneralCommand{ $this->setResultTurn(new LastTurn(static::getName(), $this->arg)); $general->checkStatChange(); - tryRollbackInheritUniqueItem($rng, $general); $general->applyDB($db); return true; diff --git a/hwe/sammo/Command/General/che_해산.php b/hwe/sammo/Command/General/che_해산.php index 48389b07..c3dd00f2 100644 --- a/hwe/sammo/Command/General/che_해산.php +++ b/hwe/sammo/Command/General/che_해산.php @@ -20,7 +20,6 @@ use sammo\Event\EventHandler; use function sammo\refreshNationStaticInfo; use function sammo\deleteNation; -use function sammo\tryRollbackInheritUniqueItem; class che_해산 extends Command\GeneralCommand{ static protected $actionName = '해산'; @@ -99,7 +98,6 @@ class che_해산 extends Command\GeneralCommand{ $oldGeneral->setVar('makelimit', 12); $oldGeneral->applyDB($db); } - tryRollbackInheritUniqueItem($rng, $general); $general->applyDB($db); // 이벤트 핸들러 동작 diff --git a/hwe/sammo/Command/General/che_화계.php b/hwe/sammo/Command/General/che_화계.php index 0b2cd114..ce06a2b2 100644 --- a/hwe/sammo/Command/General/che_화계.php +++ b/hwe/sammo/Command/General/che_화계.php @@ -12,7 +12,6 @@ use \sammo\LastTurn; use \sammo\Command; use function \sammo\searchDistance; -use function sammo\tryRollbackInheritUniqueItem; use \sammo\Constraint\ConstraintHelper; use sammo\CityConst; @@ -296,7 +295,6 @@ class che_화계 extends Command\GeneralCommand $this->setResultTurn(new LastTurn(static::getName(), $this->arg)); $general->checkStatChange(); - tryRollbackInheritUniqueItem($rng, $general); $general->applyDB($db); return false; } @@ -330,7 +328,6 @@ class che_화계 extends Command\GeneralCommand $general->increaseRankVar(RankColumn::firenum, 1); $this->setResultTurn(new LastTurn(static::getName(), $this->arg)); $general->checkStatChange(); - tryRollbackInheritUniqueItem($rng, $general); $general->applyDB($db); return true; diff --git a/hwe/sammo/Command/General/휴식.php b/hwe/sammo/Command/General/휴식.php index ecdb0b83..8dcba62c 100644 --- a/hwe/sammo/Command/General/휴식.php +++ b/hwe/sammo/Command/General/휴식.php @@ -7,8 +7,6 @@ use \sammo\JosaUtil; use \sammo\LastTurn; use \sammo\DB; -use function sammo\tryRollbackInheritUniqueItem; - class 휴식 extends Command\GeneralCommand{ static protected $actionName = '휴식'; @@ -41,7 +39,6 @@ class 휴식 extends Command\GeneralCommand{ $logger->pushGeneralActionLog("아무것도 실행하지 않았습니다. <1>$date"); $this->setResultTurn(new LastTurn()); - tryRollbackInheritUniqueItem($rng, $general); $general->applyDB(DB::db()); return true; } diff --git a/hwe/sql/reset.sql b/hwe/sql/reset.sql index 1bb8ec24..6bc7fb73 100644 --- a/hwe/sql/reset.sql +++ b/hwe/sql/reset.sql @@ -32,9 +32,6 @@ DROP TABLE IF EXISTS ng_diplomacy; # 토너먼트 테이블 삭제 DROP TABLE IF EXISTS tournament; -# 거래 테이블 삭제 -DROP TABLE IF EXISTS auction; - # 통계 테이블 삭제 DROP TABLE IF EXISTS statistic; diff --git a/hwe/sql/schema.sql b/hwe/sql/schema.sql index 5e7746b1..5d300bbb 100644 --- a/hwe/sql/schema.sql +++ b/hwe/sql/schema.sql @@ -441,23 +441,6 @@ CREATE TABLE `tournament` ( INDEX `grp` (`grp`, `grp_no`) ) COLLATE = 'utf8mb4_general_ci' ENGINE = Aria; ############################## -## 거래 테이블 -############################## -create table `auction` ( - `no` int(6) not null auto_increment, - `type` int(6) default 0, - `no1` int(6) default 0, - `name1` varchar(64) default '-', - `amount` int(6) default 0, - `cost` int(6) default 0, - `value` int(6) default 0, - `topv` int(6) default 0, - `no2` int(6) default 0, - `name2` varchar(64) default '-', - `expire` datetime, - PRIMARY KEY (`no`) -) COLLATE = 'utf8mb4_general_ci' ENGINE = Aria; -############################## ## 통계 테이블 ############################## CREATE TABLE `statistic` ( -- 2.54.0 From 717ec012bacd88f7cbb35e3daa4f108cd7857b8e Mon Sep 17 00:00:00 2001 From: Hide_D Date: Thu, 9 Jun 2022 01:17:18 +0900 Subject: [PATCH 23/23] =?UTF-8?q?feat,fix,wip:=20=EB=B2=84=EA=B7=B8=20?= =?UTF-8?q?=EC=88=98=EC=A0=95,=20=EC=97=B0=EC=9E=A5=20=EC=8B=9C=EA=B0=84?= =?UTF-8?q?=20=EB=A1=9C=EC=A7=81=20=EB=B3=80=EA=B2=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .phan/config.php | 2 - hwe/func.php | 21 +++-- hwe/func_auction.php | 4 +- .../Auction/GetActiveResourceAuctionList.php | 2 +- .../Auction/GetUniqueItemAuctionDetail.php | 3 +- .../API/Auction/GetUniqueItemAuctionList.php | 15 ++- hwe/sammo/API/Auction/OpenBuyRiceAuction.php | 13 +++ hwe/sammo/API/Auction/OpenSellRiceAuction.php | 13 +++ hwe/sammo/API/Auction/OpenUniqueAuction.php | 13 +++ hwe/sammo/API/General/DropItem.php | 17 ---- hwe/sammo/Auction.php | 94 ++++++++++++++----- hwe/sammo/AuctionBuyRice.php | 2 +- hwe/sammo/AuctionUniqueItem.php | 35 ++++--- hwe/sammo/Command/General/che_장비매매.php | 16 ---- hwe/sammo/DummyGeneral.php | 68 ++++++++------ hwe/ts/PageAuction.vue | 4 +- hwe/ts/components/AuctionResource.vue | 16 +++- hwe/ts/components/AuctionUniqueItem.vue | 31 +++--- 18 files changed, 232 insertions(+), 137 deletions(-) diff --git a/.phan/config.php b/.phan/config.php index d7f23571..b808b9ed 100644 --- a/.phan/config.php +++ b/.phan/config.php @@ -45,7 +45,6 @@ return [ 'hwe/api.php', 'hwe/a_traffic.php', 'hwe/battle_simulator.php', - 'hwe/b_auction.php', 'hwe/b_battleCenter.php', 'hwe/b_betting.php', 'hwe/v_chiefCenter.php', @@ -60,7 +59,6 @@ return [ 'hwe/v_processing.php', 'hwe/b_tournament.php', 'hwe/b_troop.php', - 'hwe/c_auction.php', 'hwe/c_tournament.php', 'hwe/func_auction.php', 'hwe/func_command.php', diff --git a/hwe/func.php b/hwe/func.php index c3a1bf60..e464a532 100644 --- a/hwe/func.php +++ b/hwe/func.php @@ -4,6 +4,7 @@ namespace sammo; use DateTime; use Ds\Set; +use sammo\Enums\AuctionType; use sammo\Enums\InheritanceKey; use sammo\Enums\RankColumn; use sammo\Event\Action; @@ -1558,6 +1559,7 @@ function CheckHall($no) function giveRandomUniqueItem(RandUtil $rng, General $general, string $acquireType): bool { $db = DB::db(); + $gameStor = KVStorage::getStorage($db, 'game_env'); //아이템 습득 상황 $availableUnique = []; @@ -1589,6 +1591,18 @@ function giveRandomUniqueItem(RandUtil $rng, General $general, string $acquireTy } } + $auctionItems = $db->queryFirstColumn( + 'SELECT `target` FROM `ng_auction` WHERE `type` = %s AND `finished` = 0', + AuctionType::UniqueItem->value + ); + foreach ($auctionItems as $itemCode) { + if (key_exists($itemCode, $occupiedUnique)) { + $occupiedUnique[$itemCode]++; + } else { + $occupiedUnique[$itemCode] = 1; + } + } + foreach ($db->queryAllLists('SELECT namespace, count(*) as cnt FROM `storage` WHERE namespace LIKE "ut_%" GROUP BY namespace') as [$uniqueNS, $cnt]) { $itemCode = substr($uniqueNS, 3); $itemClass = buildItemClass($itemCode); @@ -1632,10 +1646,7 @@ function giveRandomUniqueItem(RandUtil $rng, General $general, string $acquireTy return false; } - - if ($general->getAuxVar('inheritRandomUnique')) { - $gameStor = KVStorage::getStorage($db, 'game_env'); [$year, $month, $initYear, $initMonth] = $gameStor->getValuesAsArray(['year', 'month', 'init_year', 'init_month']); $relMonthByInit = Util::joinYearMonth($year, $month) - Util::joinYearMonth($initYear, $initMonth); @@ -1666,10 +1677,6 @@ function giveRandomUniqueItem(RandUtil $rng, General $general, string $acquireTy $logger->pushGlobalActionLog("{$generalName}{$josaYi} {$itemName}{$josaUl} 습득했습니다!"); $logger->pushGlobalHistoryLog("【{$acquireType}】{$nationName}{$generalName}{$josaYi} {$itemName}{$josaUl} 습득했습니다!"); - $givenUnique =$gameStor->getValue('givenUnique') ?? []; - $givenUnique[$itemCode] = ($givenUnique[$itemCode] ?? 0) + 1; - $gameStor->setValue('givenUnique', $givenUnique); - return true; } diff --git a/hwe/func_auction.php b/hwe/func_auction.php index e9f38529..c4fc2d6d 100644 --- a/hwe/func_auction.php +++ b/hwe/func_auction.php @@ -14,7 +14,7 @@ function registerAuction(RandUtil $rng) $avgRice = Util::valueFit($avgRice, 1000, 20000); $neutralAuctionCnt = Util::convertPairArrayToDict($db->queryAllLists( - 'SELECT `type`, count(*) FROM ng_auction WHERE `type` IN %s AND `host_general_id`=0 GROUP BY `type`', + 'SELECT `type`, count(*) FROM ng_auction WHERE `type` IN %ls AND `host_general_id`=0 GROUP BY `type`', [AuctionType::BuyRice->value, AuctionType::SellRice->value], )); @@ -64,7 +64,7 @@ function processAuction() $now = TimeUtil::now(); - $auctionList = $db->queryFirstColumn( + $auctionList = $db->queryAllLists( 'SELECT id, `type` FROM ng_auction WHERE `close_date` <= %s AND finished = 0', $now ); diff --git a/hwe/sammo/API/Auction/GetActiveResourceAuctionList.php b/hwe/sammo/API/Auction/GetActiveResourceAuctionList.php index 099fec0d..d0d42dc2 100644 --- a/hwe/sammo/API/Auction/GetActiveResourceAuctionList.php +++ b/hwe/sammo/API/Auction/GetActiveResourceAuctionList.php @@ -37,7 +37,7 @@ class GetActiveResourceAuctionList extends \sammo\BaseAPI $sellRiceList = []; /** @var AuctionInfo[] */ $auctions = array_map(fn ($raw) => AuctionInfo::fromArray($raw), $db->query( - 'SELECT * FROM `ng_auction` WHERE `type` IN %ls AND `finished` = 0 ORDER BY `close_date` DESC', + 'SELECT * FROM `ng_auction` WHERE `type` IN %ls AND `finished` = 0 ORDER BY `close_date` ASC', [ AuctionType::BuyRice->value, AuctionType::SellRice->value, diff --git a/hwe/sammo/API/Auction/GetUniqueItemAuctionDetail.php b/hwe/sammo/API/Auction/GetUniqueItemAuctionDetail.php index 9c13b103..2051ec30 100644 --- a/hwe/sammo/API/Auction/GetUniqueItemAuctionDetail.php +++ b/hwe/sammo/API/Auction/GetUniqueItemAuctionDetail.php @@ -80,8 +80,9 @@ class GetUniqueItemAuctionDetail extends \sammo\BaseAPI 'target' => $auction->target, 'isCallerHost' => $auction->hostGeneralID === $generalID, 'hostName' => $auction->detail->hostName, + 'closeDate' => TimeUtil::format($auction->closeDate, false), 'remainCloseDateExtensionCnt' => $auction->detail->remainCloseDateExtensionCnt, - 'availableLatestBidCloseDate' => $auction->detail->availableLatestBidCloseDate, + 'availableLatestBidCloseDate' => TimeUtil::format($auction->detail->availableLatestBidCloseDate, false), ], 'bidList' => $responseBid, 'obfuscatedName' => $obfuscatedName, diff --git a/hwe/sammo/API/Auction/GetUniqueItemAuctionList.php b/hwe/sammo/API/Auction/GetUniqueItemAuctionList.php index 765833a3..5937581e 100644 --- a/hwe/sammo/API/Auction/GetUniqueItemAuctionList.php +++ b/hwe/sammo/API/Auction/GetUniqueItemAuctionList.php @@ -32,10 +32,21 @@ class GetUniqueItemAuctionList extends \sammo\BaseAPI /** @var AuctionInfo[] */ $auctions = array_map(fn($raw)=>AuctionInfo::fromArray($raw), $db->query( - 'SELECT * FROM `ng_auction` WHERE `type` = %s ORDER BY `close_date` DESC', + 'SELECT * FROM `ng_auction` WHERE `type` = %s ORDER BY `close_date` ASC', AuctionType::UniqueItem->value ) ?? []); + $obfuscatedName = AuctionUniqueItem::genObfuscatedName($generalID); + + if(!$auctions){ + return [ + 'result' => true, + 'list' => [], + 'obfuscatedName' => $obfuscatedName, + ]; + } + + $auctionIDList = []; foreach ($auctions as $auction) { $auctionIDList[] = $auction->id; @@ -58,8 +69,6 @@ class GetUniqueItemAuctionList extends \sammo\BaseAPI $rawHighestBids ); - $obfuscatedName = AuctionUniqueItem::genObfuscatedName($generalID); - $response = []; foreach ($auctions as $auction) { $auctionID = $auction->id; diff --git a/hwe/sammo/API/Auction/OpenBuyRiceAuction.php b/hwe/sammo/API/Auction/OpenBuyRiceAuction.php index dc07a667..ae2b74c8 100644 --- a/hwe/sammo/API/Auction/OpenBuyRiceAuction.php +++ b/hwe/sammo/API/Auction/OpenBuyRiceAuction.php @@ -5,9 +5,12 @@ namespace sammo\API\Auction; use sammo\Session; use DateTimeInterface; use sammo\AuctionBuyRice; +use sammo\DB; use sammo\Validator; use sammo\GameConst; use sammo\General; +use sammo\KVStorage; +use sammo\Util; class OpenBuyRiceAuction extends \sammo\BaseAPI { @@ -64,6 +67,16 @@ class OpenBuyRiceAuction extends \sammo\BaseAPI $general = General::createGeneralObjFromDB($generalID); + $db = DB::db(); + $gameStor = KVStorage::getStorage($db, 'game_env'); + [$initYear, $initMonth, $year, $month] = $gameStor->getValuesAsArray(['init_year', 'init_month', 'year', 'month']); + $initYearMonth = Util::joinYearMonth($initYear, $initMonth); + $yearMonth = Util::joinYearMonth($year, $month); + + if($yearMonth <= $initYearMonth + 3){ + return '시작 후 3개월이 지나야 경매를 열 수 있습니다.'; + } + $auctionResult = AuctionBuyRice::openResourceAuction( $general, $amount, diff --git a/hwe/sammo/API/Auction/OpenSellRiceAuction.php b/hwe/sammo/API/Auction/OpenSellRiceAuction.php index c1092494..efb38972 100644 --- a/hwe/sammo/API/Auction/OpenSellRiceAuction.php +++ b/hwe/sammo/API/Auction/OpenSellRiceAuction.php @@ -5,9 +5,12 @@ namespace sammo\API\Auction; use sammo\Session; use DateTimeInterface; use sammo\AuctionSellRice; +use sammo\DB; use sammo\Validator; use sammo\GameConst; use sammo\General; +use sammo\KVStorage; +use sammo\Util; class OpenSellRiceAuction extends \sammo\BaseAPI { @@ -64,6 +67,16 @@ class OpenSellRiceAuction extends \sammo\BaseAPI $general = General::createGeneralObjFromDB($generalID); + $db = DB::db(); + $gameStor = KVStorage::getStorage($db, 'game_env'); + [$initYear, $initMonth, $year, $month] = $gameStor->getValuesAsArray(['init_year', 'init_month', 'year', 'month']); + $initYearMonth = Util::joinYearMonth($initYear, $initMonth); + $yearMonth = Util::joinYearMonth($year, $month); + + if($yearMonth <= $initYearMonth + 3){ + return '시작 후 3개월이 지나야 경매를 열 수 있습니다.'; + } + $auctionResult = AuctionSellRice::openResourceAuction( $general, $amount, diff --git a/hwe/sammo/API/Auction/OpenUniqueAuction.php b/hwe/sammo/API/Auction/OpenUniqueAuction.php index 1a70ec1e..40def74d 100644 --- a/hwe/sammo/API/Auction/OpenUniqueAuction.php +++ b/hwe/sammo/API/Auction/OpenUniqueAuction.php @@ -5,9 +5,12 @@ namespace sammo\API\Auction; use sammo\Session; use DateTimeInterface; use sammo\AuctionUniqueItem; +use sammo\DB; use sammo\Validator; use sammo\GameConst; use sammo\General; +use sammo\KVStorage; +use sammo\Util; use function sammo\buildItemClass; @@ -55,6 +58,16 @@ class OpenUniqueAuction extends \sammo\BaseAPI $itemObj = buildItemClass($itemID); $general = General::createGeneralObjFromDB($generalID); + $db = DB::db(); + $gameStor = KVStorage::getStorage($db, 'game_env'); + [$initYear, $initMonth, $year, $month] = $gameStor->getValuesAsArray(['init_year', 'init_month', 'year', 'month']); + $initYearMonth = Util::joinYearMonth($initYear, $initMonth); + $yearMonth = Util::joinYearMonth($year, $month); + + if($yearMonth <= $initYearMonth + 3){ + return '시작 후 3개월이 지나야 경매를 열 수 있습니다.'; + } + $auctionResult = AuctionUniqueItem::openItemAuction($itemObj, $general, $amount); if(is_string($auctionResult)) { diff --git a/hwe/sammo/API/General/DropItem.php b/hwe/sammo/API/General/DropItem.php index 317aea01..21c2728c 100644 --- a/hwe/sammo/API/General/DropItem.php +++ b/hwe/sammo/API/General/DropItem.php @@ -62,23 +62,6 @@ class DropItem extends \sammo\BaseAPI if (!$item->isBuyable()) { $logger->pushGlobalActionLog("{$generalName}{$josaYi} {$itemName}{$josaUl} 잃었습니다!"); $logger->pushGlobalHistoryLog("【망실】{$nationName}{$generalName}{$josaYi} {$itemName}{$josaUl} 잃었습니다!"); - - $gameStor = KVStorage::getStorage($db, 'game_env'); - $givenUnique = $gameStor->getValue('givenUnique') ?? []; - $itemCode = $item->getRawClassName(); - if (key_exists($itemCode, $givenUnique)) { - $givenUniqueCnt = $givenUnique[$itemCode]; - $givenUniqueCnt -= 1; - if ($givenUniqueCnt <= 0) { - unset($givenUnique[$itemCode]); - } else { - $givenUnique[$itemCode] = $givenUniqueCnt; - } - } else { - //XXX: 처리하지 못한 코드가 있는가? - //FIXME: 여기에서 무언가 경고를 내야함 - } - $gameStor->setValue('givenUnique', $givenUnique); } $me->applyDB($db); diff --git a/hwe/sammo/Auction.php b/hwe/sammo/Auction.php index 878ceefd..349855f8 100644 --- a/hwe/sammo/Auction.php +++ b/hwe/sammo/Auction.php @@ -21,7 +21,7 @@ abstract class Auction static AuctionType $auctionType; public const COEFF_AUCTION_CLOSE_MINUTES = 24; - public const COEFF_EXTENSION_MINUTES_PER_BID = 1 / 6; + public const COEFF_EXTENSION_MINUTES_PER_BID = (1 / 6); public const COEFF_EXTENSION_MINUTES_LIMIT_BY_BID = 1; public const COEFF_EXTENSION_MINUTES_BY_EXTENSION_QUERY = 1; public const MIN_AUCTION_CLOSE_MINUTES = 30; @@ -157,32 +157,55 @@ abstract class Auction return null; } + public function extendLatestBidCloseDate(?DateTimeInterface $date): ?string + { + if ($date === null) { + $db = DB::db(); + $gameStor = KVStorage::getStorage($db, 'game_env'); + $turnTerm = $gameStor->getValue('turnterm'); + $date = $this->info->closeDate->add(TimeUtil::secondsToDateInterval( + max(static::MIN_EXTENSION_MINUTES_PER_BID, $turnTerm * static::COEFF_EXTENSION_MINUTES_PER_BID) * 60 + )); + } + else{ + $date = DateTimeImmutable::createFromInterface($date); + } + if ($this->info->detail->availableLatestBidCloseDate !== null && $date < $this->info->detail->availableLatestBidCloseDate) { + return '기간보다 짧습니다.'; + } + $this->info->detail->availableLatestBidCloseDate = $date; + return null; + } + public function extendCloseDate(DateTimeInterface $date, bool $force = false): ?string { if (!$force) { - if ($this->info->remainCloseExtensionCnt === null) { + if ($this->info->detail->remainCloseDateExtensionCnt === null) { return '연장할 수 없는 경매입니다.'; } - if ($this->info->remainCloseExtensionCnt === 0) { + if ($this->info->detail->remainCloseDateExtensionCnt === 0) { return '더 이상 연장할 수 없습니다'; } - if ($this->info->remainCloseExtensionCnt > 0) { - $this->info->remainCloseExtensionCnt--; + if ($this->info->detail->remainCloseDateExtensionCnt > 0) { + $this->info->detail->remainCloseDateExtensionCnt--; } } - $db = DB::db(); - $gameStor = KVStorage::getStorage($db, 'game_env'); - $turnTerm = $gameStor->getValue('turnterm'); + if ($date < $this->info->closeDate) { + return '종료 기간보다 짧습니다.'; + } + $closeDate = DateTimeImmutable::createFromInterface($date); $this->info->closeDate = $closeDate; - $this->info->availableLatestBidCloseDate = $closeDate->add(TimeUtil::secondsToDateInterval( - max(static::MIN_EXTENSION_MINUTES_PER_BID, $turnTerm * static::COEFF_EXTENSION_MINUTES_PER_BID) * 60 - )); - $db->update('ng_auction', $this->info->toArray('id'), 'id = %i', $this->info->id); return null; } + public function applyDB(): void + { + $db = DB::db(); + $db->update('ng_auction', $this->info->toArray('id'), 'id = %i', $this->info->id); + } + public function refundBid(AuctionBidItem $bidItem, string $reason): void { if ($bidItem->auctionID !== $this->info->id) { @@ -190,7 +213,7 @@ abstract class Auction } $db = DB::db(); - if ($bidItem->generalID === $this->general->generalID) { + if ($bidItem->generalID === $this->general->getID()) { $oldBidder = $this->general; } else { $oldBidder = General::createGeneralObjFromDB($bidItem->generalID); @@ -198,10 +221,15 @@ abstract class Auction if ($this->info->reqResource === ResourceType::inheritancePoint) { $oldBidder->increaseInheritancePoint(InheritanceKey::previous, $bidItem->amount); + $oldBidder->increaseRankVar(RankColumn::inherit_point_spent_dynamic, -$bidItem->amount); } else { $oldBidder->increaseVar($this->info->reqResource->value, $bidItem->amount); } + if ($oldBidder instanceof DummyGeneral) { + return; + } + $staticNation = $oldBidder->getStaticNation(); $src = new MessageTarget(0, '', 0, 'System', '#000000'); $dest = new MessageTarget( @@ -258,7 +286,7 @@ abstract class Auction } $myPrevBid = $this->getMyPrevBid(); - if($myPrevBid !== null && $highestBid->no !== $myPrevBid->no){ + if ($myPrevBid !== null && $highestBid->no !== $myPrevBid->no) { //이미 환불 받았으니 무효. $myPrevBid = null; } @@ -293,13 +321,14 @@ abstract class Auction $gameStor = KVStorage::getStorage($db, 'game_env'); $turnTerm = $gameStor->getValue('turnterm'); - if ($this->info->availableLatestBidCloseDate !== null) { - $extendedCloseDate = $this->info->closeDate->add(TimeUtil::secondsToDateInterval( + if ($this->info->detail->availableLatestBidCloseDate !== null) { + $extendedCloseDate = $now->add(TimeUtil::secondsToDateInterval( max(static::MIN_EXTENSION_MINUTES_PER_BID, $turnTerm * static::COEFF_EXTENSION_MINUTES_PER_BID) * 60 )); - if ($extendedCloseDate > $this->info->closeDate && $this->info->closeDate < $this->info->availableLatestBidCloseDate) { - $this->extendCloseDate(min($extendedCloseDate, $this->info->availableLatestBidCloseDate)); + if ($extendedCloseDate > $this->info->closeDate && $this->info->closeDate < $this->info->detail->availableLatestBidCloseDate) { + $this->extendCloseDate(min($extendedCloseDate, $this->info->detail->availableLatestBidCloseDate), true); + $this->applyDB(); } } @@ -332,11 +361,11 @@ abstract class Auction } if (!$auctionInfo->detail->isReverse) { - if ($auctionInfo->buyImmediatelyAmount !== null && $auctionInfo->buyImmediatelyAmount < $amount) { + if ($auctionInfo->detail->finishBidAmount !== null && $auctionInfo->detail->finishBidAmount < $amount) { return '즉시판매가보다 높을 수 없습니다.'; } } else { - if ($auctionInfo->buyImmediatelyAmount !== null && $auctionInfo->buyImmediatelyAmount > $amount) { + if ($auctionInfo->detail->finishBidAmount !== null && $auctionInfo->detail->finishBidAmount > $amount) { return '즉시판매가보다 낮을 수 없습니다.'; } } @@ -363,7 +392,7 @@ abstract class Auction $myPrevBid = $this->getMyPrevBid(); - if($myPrevBid !== null && $highestBid->no !== $myPrevBid->no){ + if ($myPrevBid !== null && $highestBid->no !== $myPrevBid->no) { //이미 환불 받았으니 무효. $myPrevBid = null; } @@ -402,6 +431,17 @@ abstract class Auction $general->increaseVar($resType->value, -$morePoint); + $gameStor = KVStorage::getStorage($db, 'game_env'); + $turnTerm = $gameStor->getValue('turnterm'); + $extendedCloseDate = $now->add(TimeUtil::secondsToDateInterval( + max(static::MIN_EXTENSION_MINUTES_PER_BID, $turnTerm * static::COEFF_EXTENSION_MINUTES_PER_BID) * 60 + )); + + if ($extendedCloseDate > $this->info->closeDate) { + $this->extendCloseDate($extendedCloseDate, true); + $this->applyDB(); + } + if ($highestBid !== null && $myPrevBid === null) { $this->refundBid($highestBid, "{$auctionInfo->id}번 {$auctionInfo->detail->title}에 상회입찰자가 나타났습니다."); } @@ -424,12 +464,18 @@ abstract class Auction } if ($highestBid->aux->tryExtendCloseDate) { + $db = DB::db(); + $gameStor = KVStorage::getStorage($db, 'game_env'); + $turnTerm = $gameStor->getValue('turnterm'); + //연장 요청이 있었다. $extendedCloseDate = $this->info->closeDate->add(TimeUtil::secondsToDateInterval( - max(static::MIN_EXTENSION_MINUTES_BY_EXTENSION_QUERY, $this->info->turnTerm * static::COEFF_EXTENSION_MINUTES_BY_EXTENSION_QUERY) * 60 + max(static::MIN_EXTENSION_MINUTES_BY_EXTENSION_QUERY, $turnTerm * static::COEFF_EXTENSION_MINUTES_BY_EXTENSION_QUERY) * 60 )); if ($this->extendCloseDate($extendedCloseDate) === null) { + $this->extendLatestBidCloseDate(null); + $this->applyDB(); return false; } } @@ -441,6 +487,10 @@ abstract class Auction return true; } + if ($bidder instanceof DummyGeneral) { + return false; + } + $staticNation = $bidder->getStaticNation(); $src = new MessageTarget(0, '', 0, 'System', '#000000'); $dest = new MessageTarget( diff --git a/hwe/sammo/AuctionBuyRice.php b/hwe/sammo/AuctionBuyRice.php index 71bf451f..6b864265 100644 --- a/hwe/sammo/AuctionBuyRice.php +++ b/hwe/sammo/AuctionBuyRice.php @@ -8,7 +8,7 @@ use sammo\Enums\ResourceType; /** 경매에 쌀을 매물로 등록, 입찰자가 금으로 구매 */ class AuctionBuyRice extends AuctionBasicResource { - static AuctionType $auctionType = AuctionType::SellRice; + static AuctionType $auctionType = AuctionType::BuyRice; static ResourceType $hostRes = ResourceType::rice; static ResourceType $bidderRes = ResourceType::gold; } diff --git a/hwe/sammo/AuctionUniqueItem.php b/hwe/sammo/AuctionUniqueItem.php index 65a6c618..3d6b0216 100644 --- a/hwe/sammo/AuctionUniqueItem.php +++ b/hwe/sammo/AuctionUniqueItem.php @@ -14,6 +14,7 @@ use sammo\RandUtil; class AuctionUniqueItem extends Auction { + const COEFF_EXTENSION_MINUTES_LIMIT_UNIQUE_CNT = 24; static AuctionType $auctionType = AuctionType::UniqueItem; @@ -52,18 +53,6 @@ class AuctionUniqueItem extends Auction } $gameStor = KVStorage::getStorage($db, 'game_env'); - $givenUnique = $gameStor->getValue('givenUnique') ?? []; - $givenUniqueCnt = $givenUnique[$itemKey] ?? 0; - $remainUniqueCnt = 0; - foreach (GameConst::$allItems as $itemList) { - if (!key_exists($itemKey, $itemList)) { - continue; - } - $remainUniqueCnt += $itemList[$itemKey]; - } - if ($remainUniqueCnt > 1 && $givenUniqueCnt >= $remainUniqueCnt - 1) { - return '더이상 이 아이템을 경매로 가져갈 수 없습니다.'; - } $now = new DateTimeImmutable(); @@ -115,7 +104,7 @@ class AuctionUniqueItem extends Auction $josaRa = JosaUtil::pick($item->getRawName(), '라'); $logger = new ActionLogger(0, 0, $year, $month); - $logger->pushGlobalHistoryLog("누군가가 {$itemName}{$josaRa}는 보물을 구한다는 소문이 들려옵니다."); + $logger->pushGlobalHistoryLog("【보물수배】누군가가 {$itemName}{$josaRa}는 보물을 구한다는 소문이 들려옵니다."); $logger->flush(); return $auction; @@ -159,9 +148,14 @@ class AuctionUniqueItem extends Auction ); $itemCode = $this->info->target; + + if($itemCode === null){ + throw new \Exception('아이템 코드가 없습니다.'); + } + $bidItemTypes = new Set(); foreach (GameConst::$allItems as $itemType => $itemList) { - if (($itemList[$itemCode] ?? 0) <= 0) { + if (key_exists($itemCode, $itemList) && $itemList[$itemCode] <= 0) { continue; } $bidItemTypes->add($itemType); @@ -228,12 +222,15 @@ class AuctionUniqueItem extends Auction } if ($availableEquipUniqueCnt <= 0) { + $turnTerm = $gameStor->getValue('turnterm'); //제한에 걸렸다면 자동 연장 $extendedCloseDate = $this->info->closeDate->add(TimeUtil::secondsToDateInterval( - max(static::MIN_EXTENSION_MINUTES_BY_EXTENSION_QUERY, $this->info->turnTerm * static::COEFF_EXTENSION_MINUTES_BY_EXTENSION_QUERY) * 60 + max(static::MIN_EXTENSION_MINUTES_BY_EXTENSION_QUERY, $turnTerm * static::COEFF_EXTENSION_MINUTES_LIMIT_UNIQUE_CNT) * 60 )); $this->extendCloseDate($extendedCloseDate, true); + $this->extendLatestBidCloseDate(null); + $this->applyDB(); return '유니크 아이템 소유 제한 상태입니다. 종료 시간이 연장됩니다.'; } @@ -284,13 +281,13 @@ class AuctionUniqueItem extends Auction $logger->pushGeneralActionLog("{$itemName}{$josaUl} 습득했습니다!"); $logger->pushGeneralHistoryLog("{$itemName}{$josaUl} 습득"); $logger->pushGlobalActionLog("{$generalName}{$josaYi} {$itemName}{$josaUl} 습득했습니다!"); - $logger->pushGlobalHistoryLog("【보물발견】{$nationName}{$generalName}{$josaYi} {$itemName}{$josaUl} 습득했습니다!"); + $logger->pushGlobalHistoryLog("【보물수배】{$nationName}{$generalName}{$josaYi} {$itemName}{$josaUl} 습득했습니다!"); + + $userLogger = new UserLogger($general->getVar('owner')); + $userLogger->push(sprintf("유니크 %s 경매로 %d 포인트 사용", $itemName, $highestBid->amount), "inheritPoint"); $general->applyDB($db); - $givenUnique = $gameStor->getValue('givenUnique') ?? []; - $givenUnique[$itemKey] = ($givenUnique[$itemKey] ?? 0) + 1; - $gameStor->setValue('givenUnique', $givenUnique); return null; } } diff --git a/hwe/sammo/Command/General/che_장비매매.php b/hwe/sammo/Command/General/che_장비매매.php index a7faaf89..a786c8d2 100644 --- a/hwe/sammo/Command/General/che_장비매매.php +++ b/hwe/sammo/Command/General/che_장비매매.php @@ -188,22 +188,6 @@ class che_장비매매 extends Command\GeneralCommand $nationName = $general->getStaticNation()['name']; $logger->pushGlobalActionLog("{$generalName}{$josaYi} {$itemName}{$josaUl} 판매했습니다!"); $logger->pushGlobalHistoryLog("【판매】{$nationName}{$generalName}{$josaYi} {$itemName}{$josaUl} 판매했습니다!"); - - $gameStor = KVStorage::getStorage($db, 'game_env'); - $givenUnique = $gameStor->getValue('givenUnique') ?? []; - if (key_exists($itemCode, $givenUnique)) { - $givenUniqueCnt = $givenUnique[$itemCode]; - $givenUniqueCnt -= 1; - if ($givenUniqueCnt <= 0) { - unset($givenUnique[$itemCode]); - } else { - $givenUnique[$itemCode] = $givenUniqueCnt; - } - } else { - //XXX: 처리하지 못한 코드가 있는가? - //FIXME: 여기에서 무언가 경고를 내야함 - } - $gameStor->setValue('givenUnique', $givenUnique); } } diff --git a/hwe/sammo/DummyGeneral.php b/hwe/sammo/DummyGeneral.php index 88312519..6d01d40f 100644 --- a/hwe/sammo/DummyGeneral.php +++ b/hwe/sammo/DummyGeneral.php @@ -4,67 +4,79 @@ namespace sammo; use sammo\Enums\InheritanceKey; -class DummyGeneral extends General{ - public function __construct(bool $initLogger=true){ +class DummyGeneral extends General +{ + public function __construct(bool $initLogger = true) + { $raw = [ - 'no'=>0, - 'name'=>'Dummy', - 'npc'=>3, - 'city'=>0, - 'nation'=>0, - 'officer_level'=>0, - 'crewtype'=>-1, - 'turntime'=>'2012-03-04 05:06:07.000000', - 'experience'=>0, - 'dedication'=>0, - 'gold'=>0, - 'rice'=>0, - 'leadership'=>10, - 'strength'=>10, - 'intel'=>10, + 'no' => 0, + 'name' => 'Dummy', + 'npc' => 3, + 'city' => 0, + 'nation' => 0, + 'officer_level' => 0, + 'crewtype' => -1, + 'turntime' => '2012-03-04 05:06:07.000000', + 'experience' => 0, + 'dedication' => 0, + 'gold' => 0, + 'rice' => 0, + 'leadership' => 10, + 'strength' => 10, + 'intel' => 10, + 'imgsvr' => 0, + 'picture' => 'default.jpg', ]; $this->raw = $raw; $this->resultTurn = new LastTurn(); - if($initLogger){ + if ($initLogger) { $this->initLogger(1, 1); } } - public function getBattleInitSkillTriggerList(WarUnit $unit):?WarUnitTriggerCaller{ + public function getBattleInitSkillTriggerList(WarUnit $unit): ?WarUnitTriggerCaller + { return new WarUnitTriggerCaller(); } - public function getBattlePhaseSkillTriggerList(WarUnit $unit):?WarUnitTriggerCaller{ + public function getBattlePhaseSkillTriggerList(WarUnit $unit): ?WarUnitTriggerCaller + { return new WarUnitTriggerCaller(); } - public function onCalcStat(General $general, string $statName, $value, $aux=null){ + public function onCalcStat(General $general, string $statName, $value, $aux = null) + { return $value; } - public function onCalcOpposeStat(General $general, string $statName, $value, $aux=null){ + public function onCalcOpposeStat(General $general, string $statName, $value, $aux = null) + { return $value; } - public function getInheritancePoint(InheritanceKey $key, &$aux = null, bool $forceCalc = false): int|float{ + public function getInheritancePoint(InheritanceKey $key, &$aux = null, bool $forceCalc = false): int|float + { return 0; } - public function setInheritancePoint(InheritanceKey $key, $value, $aux = null){ + public function setInheritancePoint(InheritanceKey $key, $value, $aux = null) + { return; } - public function increaseInheritancePoint(InheritanceKey $key, $value, $aux = null){ + public function increaseInheritancePoint(InheritanceKey $key, $value, $aux = null) + { return; } - function applyDB($db):bool{ - if($this->logger){ + function applyDB($db): bool + { + if ($this->logger) { $this->initLogger($this->logger->getYear(), $this->logger->getMonth()); } return true; } -} \ No newline at end of file +} diff --git a/hwe/ts/PageAuction.vue b/hwe/ts/PageAuction.vue index 43c77a1f..b0dc4b07 100644 --- a/hwe/ts/PageAuction.vue +++ b/hwe/ts/PageAuction.vue @@ -1,12 +1,12 @@