fix: reuse message ticks across mailboxes
This commit was merged in pull request #267.
This commit is contained in:
+30
-9
@@ -15,6 +15,9 @@ class Message
|
|||||||
|
|
||||||
protected $sendCnt = 0;
|
protected $sendCnt = 0;
|
||||||
|
|
||||||
|
private ?int $sendTimeTick = null;
|
||||||
|
private ?int $sendValidUntilTick = null;
|
||||||
|
|
||||||
public function __construct(
|
public function __construct(
|
||||||
public MessageType $msgType,
|
public MessageType $msgType,
|
||||||
public MessageTarget $src,
|
public MessageTarget $src,
|
||||||
@@ -324,15 +327,7 @@ class Message
|
|||||||
|
|
||||||
$db = DB::db();
|
$db = DB::db();
|
||||||
$clock = GameClock::fromStorage(KVStorage::getStorage($db, 'game_env'));
|
$clock = GameClock::fromStorage(KVStorage::getStorage($db, 'game_env'));
|
||||||
$timeTick = $clock->nowTick();
|
[$timeTick, $validUntilTick] = $this->resolveSendTicks($clock);
|
||||||
if (Util::toInt($this->validUntil->format('Y')) >= 9000) {
|
|
||||||
$validUntilTick = GameClock::MAX_SAFE_TICK;
|
|
||||||
} else {
|
|
||||||
$validitySeconds = $this->validUntil->getTimestamp() - $this->date->getTimestamp();
|
|
||||||
$validUntilTick = $timeTick + $clock->ticksFromSeconds($validitySeconds);
|
|
||||||
}
|
|
||||||
$this->date = \DateTime::createFromImmutable($clock->tickToDateTime($timeTick));
|
|
||||||
$this->validUntil = \DateTime::createFromImmutable($clock->tickToDateTime($validUntilTick));
|
|
||||||
$db->insert('message', [
|
$db->insert('message', [
|
||||||
'mailbox' => $mailbox,
|
'mailbox' => $mailbox,
|
||||||
'type' => $this->msgType->value,
|
'type' => $this->msgType->value,
|
||||||
@@ -350,6 +345,32 @@ class Message
|
|||||||
return [$mailbox, $db->insertId()];
|
return [$mailbox, $db->insertId()];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/** @return array{0:int, 1:int} */
|
||||||
|
protected function resolveSendTicks(GameClock $clock): array
|
||||||
|
{
|
||||||
|
if ($this->sendTimeTick !== null && $this->sendValidUntilTick !== null) {
|
||||||
|
return [$this->sendTimeTick, $this->sendValidUntilTick];
|
||||||
|
}
|
||||||
|
|
||||||
|
$timeTick = $clock->nowTick();
|
||||||
|
if (Util::toInt($this->validUntil->format('Y')) >= 9000) {
|
||||||
|
$validUntilTick = GameClock::MAX_SAFE_TICK;
|
||||||
|
} else {
|
||||||
|
$validitySeconds = $this->validUntil->getTimestamp() - $this->date->getTimestamp();
|
||||||
|
$validUntilTick = GameClock::addTicks(
|
||||||
|
$timeTick,
|
||||||
|
$clock->ticksFromSeconds($validitySeconds),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
$this->sendTimeTick = $timeTick;
|
||||||
|
$this->sendValidUntilTick = $validUntilTick;
|
||||||
|
$this->date = \DateTime::createFromImmutable($clock->tickToDateTime($timeTick));
|
||||||
|
$this->validUntil = \DateTime::createFromImmutable($clock->tickToDateTime($validUntilTick));
|
||||||
|
|
||||||
|
return [$timeTick, $validUntilTick];
|
||||||
|
}
|
||||||
|
|
||||||
private function sendToSender():array{
|
private function sendToSender():array{
|
||||||
if($this->sendCnt > 1){
|
if($this->sendCnt > 1){
|
||||||
throw new \RuntimeException('이미 전송한 메일입니다.');
|
throw new \RuntimeException('이미 전송한 메일입니다.');
|
||||||
|
|||||||
@@ -0,0 +1,95 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace sammo;
|
||||||
|
|
||||||
|
use PHPUnit\Framework\TestCase;
|
||||||
|
use sammo\Enums\MessageType;
|
||||||
|
|
||||||
|
require_once __DIR__ . '/../hwe/sammo/Target.php';
|
||||||
|
require_once __DIR__ . '/../hwe/sammo/MessageTarget.php';
|
||||||
|
require_once __DIR__ . '/../hwe/sammo/Enums/MessageType.php';
|
||||||
|
require_once __DIR__ . '/../hwe/sammo/Message.php';
|
||||||
|
|
||||||
|
final class MessageGameClockTest extends TestCase
|
||||||
|
{
|
||||||
|
public function testUnlimitedMessageReusesExactTicksAcrossReceiverAndSenderCopies(): void
|
||||||
|
{
|
||||||
|
$base = new \DateTimeImmutable('2026-08-04 00:00:00.000000');
|
||||||
|
$wallTimes = [
|
||||||
|
new \DateTimeImmutable('2026-08-04 00:00:00.000000'),
|
||||||
|
new \DateTimeImmutable('2026-08-04 00:00:00.938140'),
|
||||||
|
];
|
||||||
|
$wallReads = 0;
|
||||||
|
$clock = new GameClock(
|
||||||
|
$base,
|
||||||
|
1,
|
||||||
|
0,
|
||||||
|
GameClock::MODE_REALTIME,
|
||||||
|
$base,
|
||||||
|
static function () use (&$wallTimes, &$wallReads): \DateTimeImmutable {
|
||||||
|
return $wallTimes[$wallReads++];
|
||||||
|
},
|
||||||
|
);
|
||||||
|
$message = new TestableClockMessage(
|
||||||
|
MessageType::national,
|
||||||
|
$this->createMock(MessageTarget::class),
|
||||||
|
$this->createMock(MessageTarget::class),
|
||||||
|
'선전포고',
|
||||||
|
new \DateTime('2026-08-04 00:00:00.000000'),
|
||||||
|
new \DateTime('9999-12-31'),
|
||||||
|
[],
|
||||||
|
);
|
||||||
|
|
||||||
|
$receiverTicks = $message->resolveTicksForTest($clock);
|
||||||
|
self::assertLessThan(9000, Util::toInt($message->validUntil->format('Y')));
|
||||||
|
$legacySecondClock = new GameClock(
|
||||||
|
$base,
|
||||||
|
1,
|
||||||
|
0,
|
||||||
|
GameClock::MODE_REALTIME,
|
||||||
|
$base,
|
||||||
|
static fn (): \DateTimeImmutable => $wallTimes[1],
|
||||||
|
);
|
||||||
|
$legacySecondNowTick = $legacySecondClock->nowTick();
|
||||||
|
self::assertSame(562_884, $legacySecondNowTick);
|
||||||
|
$legacySecondExpiry = $legacySecondNowTick + $clock->ticksFromSeconds(
|
||||||
|
$message->validUntil->getTimestamp() - $message->date->getTimestamp(),
|
||||||
|
);
|
||||||
|
self::assertSame(9_007_199_254_762_884, $legacySecondExpiry);
|
||||||
|
$senderTicks = $message->resolveTicksForTest($clock);
|
||||||
|
|
||||||
|
self::assertSame([0, GameClock::MAX_SAFE_TICK], $receiverTicks);
|
||||||
|
self::assertSame($receiverTicks, $senderTicks);
|
||||||
|
self::assertSame(1, $wallReads);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function testFiniteMessageCachesOneValidatedExpiryForBothCopies(): void
|
||||||
|
{
|
||||||
|
$base = new \DateTimeImmutable('2026-08-04 00:00:00.000000');
|
||||||
|
$clock = new GameClock($base, 1, 120_000, GameClock::MODE_MANUAL, $base);
|
||||||
|
$message = new TestableClockMessage(
|
||||||
|
MessageType::national,
|
||||||
|
$this->createMock(MessageTarget::class),
|
||||||
|
$this->createMock(MessageTarget::class),
|
||||||
|
'유한 메시지',
|
||||||
|
new \DateTime('2026-08-04 00:00:02.000000'),
|
||||||
|
new \DateTime('2026-08-04 00:01:02.000000'),
|
||||||
|
[],
|
||||||
|
);
|
||||||
|
|
||||||
|
$receiverTicks = $message->resolveTicksForTest($clock);
|
||||||
|
$senderTicks = $message->resolveTicksForTest($clock);
|
||||||
|
|
||||||
|
self::assertSame([120_000, 36_120_000], $receiverTicks);
|
||||||
|
self::assertSame($receiverTicks, $senderTicks);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
final class TestableClockMessage extends Message
|
||||||
|
{
|
||||||
|
/** @return array{0:int, 1:int} */
|
||||||
|
public function resolveTicksForTest(GameClock $clock): array
|
||||||
|
{
|
||||||
|
return $this->resolveSendTicks($clock);
|
||||||
|
}
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user