merge: validate invader resume watchdog fix

This commit is contained in:
2026-08-12 15:35:57 +00:00
37 changed files with 1778 additions and 131 deletions
+27 -15
View File
@@ -8,6 +8,7 @@ use sammo\AppConf;
use sammo\Enums\APIRecoveryType;
use sammo\KVStorage;
use sammo\RootDB;
use sammo\RemoteUserIconUploadClient;
use sammo\TimeUtil;
use sammo\UniqueConst;
use sammo\Validator;
@@ -60,22 +61,33 @@ class UploadImage extends \sammo\BaseAPI
$imgName = hash_final($oMD);
$imgFullName = "{$imgName}.{$extension}";
$destDir = AppConf::getUserIconPathFS() . '/uploaded_image';
$destPath = "{$destDir}/{$imgFullName}";
$remotePath = null;
if (RemoteUserIconUploadClient::isConfiguredEnabled()) {
try {
RemoteUserIconUploadClient::uploadContentConfigured($imgFullName, $contentType, $imageData);
$remotePath = RemoteUserIconUploadClient::getConfiguredContentPublicUrl($imgFullName);
} catch (\Throwable $error) {
RemoteUserIconUploadClient::logFailure('content-image', $error);
return '원격 이미지 저장소 업로드에 실패했습니다!';
}
} else {
$destDir = AppConf::getUserIconPathFS() . '/uploaded_image';
$destPath = "{$destDir}/{$imgFullName}";
if (!file_exists($destPath)) {
if (!file_exists($destDir)) {
mkdir($destDir);
}
if (!is_dir($destDir)) {
return '버그! 업로드 경로 확인!';
}
if (!is_writable($destDir)) {
return '버그! 업로드 권한 확인!';
}
if (!file_exists($destPath)) {
if (!file_exists($destDir)) {
mkdir($destDir);
}
if (!is_dir($destDir)) {
return '버그! 업로드 경로 확인!';
}
if (!is_writable($destDir)) {
return '버그! 업로드 권한 확인!';
}
if (!file_put_contents($destPath, $imageData)) {
return '업로드에 실패했습니다!';
if (!file_put_contents($destPath, $imageData)) {
return '업로드에 실패했습니다!';
}
}
}
@@ -96,7 +108,7 @@ class UploadImage extends \sammo\BaseAPI
return [
'result' => true,
'path'=>AppConf::getUserIconPathWeb().'/uploaded_image/'.$imgFullName,
'path'=>$remotePath ?? AppConf::getUserIconPathWeb().'/uploaded_image/'.$imgFullName,
];
}
}
+8 -4
View File
@@ -151,6 +151,11 @@ class GeneralList extends \sammo\BaseAPI
$gameStor = \sammo\KVStorage::getStorage($db, 'game_env');
$env = $gameStor->getValues(['year', 'month', 'turntime', 'turnterm', 'autorun_user', 'killturn']);
$clock = GameClock::isInitialized($gameStor) ? GameClock::fromStorage($gameStor) : null;
$formatStoredTime = static fn (mixed $value): string => $clock === null
? (string)$value
: $clock->formatTick(Util::toInt($value));
$env['turntime'] = $formatStoredTime($env['turntime']);
$me = $db->queryFirstRow(
'SELECT refresh_score, turntime, belong, nation, officer_level, permission, penalty FROM `general`
@@ -187,7 +192,7 @@ class GeneralList extends \sammo\BaseAPI
if (!key_exists($troopLeaderID, $rawGeneralList)) {
continue;
}
$troopTurnTime = $rawGeneralList[$troopLeaderID]['turntime'];
$troopTurnTime = $formatStoredTime($rawGeneralList[$troopLeaderID]['turntime']);
$troops[$troopLeaderID] = new ArrayObject([
'id' => $troopLeaderID,
'name' => $troopName,
@@ -265,11 +270,10 @@ class GeneralList extends \sammo\BaseAPI
'honorText' => fn ($rawGeneral) => getHonor($rawGeneral['experience']),
'dedLevelText' => fn ($rawGeneral) => getDedLevelText($rawGeneral['dedlevel']),
//'0000-00-00 11:23';
'turntime' => fn ($rawGeneral) => GameClock::fromStorage($gameStor)
->formatTick(Util::toInt($rawGeneral['turntime'])),
'turntime' => fn ($rawGeneral) => substr($formatStoredTime($rawGeneral['turntime']), 0, 19),
'recent_war' => fn ($rawGeneral) => $rawGeneral['recent_war'] === null
? null
: GameClock::fromStorage($gameStor)->formatTick(Util::toInt($rawGeneral['recent_war'])),
: substr($formatStoredTime($rawGeneral['recent_war']), 0, 19),
'bill' => fn ($rawGeneral) => getBillByLevel($rawGeneral['dedlevel']),
'reservedCommand' => fn ($rawGeneral) => $reservedCommand[$rawGeneral['no']] ?? null,
'autorun_limit' => fn ($rawGeneral) => ($rawGeneral['aux'] ?? [])['autorun_limit'] ?? 0,
+3 -1
View File
@@ -2,6 +2,7 @@
namespace sammo\Event\Action;
use sammo\ActionLogger;
use sammo\AutoResetProgress;
use sammo\CityConst;
use sammo\DB;
use sammo\KVStorage;
@@ -61,6 +62,7 @@ class InvaderEnding extends \sammo\Event\Action{
$logger->pushGlobalHistoryLog("<L><b>【이벤트】</b></>백성은 언젠가 영웅이 나타나길 기다립니다.");
}
$gameStor->setValue('isunited', 3);
AutoResetProgress::record($gameStor);
$logger->flush();
$gameStor->refreshLimit = $gameStor->refreshLimit * 100;
@@ -71,4 +73,4 @@ class InvaderEnding extends \sammo\Event\Action{
return [__CLASS__, 'Deleted'];
}
}
}
+2
View File
@@ -4,6 +4,7 @@ namespace sammo\Event\Action;
use Ds\Set;
use sammo\ActionLogger;
use sammo\AutoResetProgress;
use sammo\CityConst;
use sammo\DB;
use sammo\Json;
@@ -119,6 +120,7 @@ class RaiseInvader extends \sammo\Event\Action
$gameStor = KVStorage::getStorage($db, 'game_env');
$gameStor->setValue('isunited', 1);
AutoResetProgress::record($gameStor);
$turnterm = $gameStor->turnterm;
$generalCnt = $db->queryFirstField('SELECT count(*) FROM general');
+23 -13
View File
@@ -152,19 +152,7 @@ class InheritancePointManager
};
switch ($key) {
case InheritanceKey::dex:
$extractFn = function () use ($general, $multiplier) {
$dexLimit = Util::array_last(getDexLevelList())[0];
$totalDex = 0;
foreach (array_keys(GameUnitConst::allType()) as $armType) {
$subDex = $general->getVar("dex{$armType}");
if ($subDex > $dexLimit) {
$totalDex += ($subDex - $dexLimit) / 3;
$subDex = $dexLimit;
}
$totalDex += $subDex;
}
return [$totalDex * $multiplier, null];
};
$extractFn = fn () => [$this->getDexInheritancePoint($general), null];
break;
case InheritanceKey::betting:
$extractFn = function () use ($general, $multiplier) {
@@ -189,6 +177,28 @@ class InheritancePointManager
return $value;
}
/** @param null|int[] $armTypes */
public function getDexInheritancePoint(
General $general,
?int $dexLimit = null,
?array $armTypes = null
): float
{
$dexLimit ??= Util::array_last(getDexLevelList())[0];
$armTypes ??= array_keys(GameUnitConst::allType());
$multiplier = $this->getInheritancePointType(InheritanceKey::dex)->pointCoeff;
$totalDex = 0;
foreach ($armTypes as $armType) {
$subDex = CentennialAllStarGrowthService::recordableValue($general, "dex{$armType}");
if ($subDex > $dexLimit) {
$totalDex += ($subDex - $dexLimit) / 3;
$subDex = $dexLimit;
}
$totalDex += $subDex;
}
return $totalDex * $multiplier;
}
public function setInheritancePoint(General $general, InheritanceKey $key, $value, $aux = null)
{
+16
View File
@@ -39,6 +39,18 @@ class TurnExecutionHelper
{
return max($completedTick, $candidateTick);
}
private static function recordInvaderProgress(KVStorage $gameStorage, int $beforeTick): void
{
if (Util::toInt($gameStorage->isunited) !== 1) {
return;
}
AutoResetProgress::recordIfAdvanced(
$gameStorage,
$beforeTick,
Util::toInt($gameStorage->turntime),
);
}
public function __destruct()
{
@@ -637,6 +649,8 @@ class TurnExecutionHelper
$locked = true;
return $gameStor->turntime;
}
$completionTickBeforeRun = Util::toInt($gameStor->turntime);
$gameStor->cacheAll();
// 1턴이상 갱신 없었으면 서버 지연
@@ -683,6 +697,7 @@ class TurnExecutionHelper
$currentTurn,
);
}
self::recordInvaderProgress($gameStor, $completionTickBeforeRun);
unlock();
return $gameStor->turntime;
}
@@ -768,6 +783,7 @@ class TurnExecutionHelper
processTournament();
//거래 처리
processAuction();
self::recordInvaderProgress($gameStor, $completionTickBeforeRun);
// 잠금 해제
$turntime = $gameStor->turntime;