feat: add logical game clock

This commit is contained in:
2026-08-04 02:51:27 +00:00
parent 87965a39d6
commit a26031dc3f
51 changed files with 1605 additions and 398 deletions
+2 -1
View File
@@ -192,7 +192,7 @@ export const createAuctionBidder = async (options: {
reason: '경매가 종료되었습니다.',
};
}
const now = new Date();
const now = world.getGameNow(new Date());
if (auction.closeAt.getTime() <= now.getTime()) {
return {
type: 'auctionBid',
@@ -379,6 +379,7 @@ export const createAuctionBidder = async (options: {
GamePrisma.sql`
UPDATE auction
SET close_at = ${nextCloseAt},
close_tick = ${BigInt(world.dateToGameTick(nextCloseAt))},
latest_event_id = ${eventId},
latest_event_at = ${eventAt},
updated_at = ${eventAt}
+7 -16
View File
@@ -1,12 +1,5 @@
import { createGamePostgresConnector, GamePrisma } from '@sammo-ts/infra';
import {
ActionLogger,
ItemLoader,
LogFormat,
UserLogger,
isItemKey,
resolveUniqueConfig,
} from '@sammo-ts/logic';
import { ActionLogger, ItemLoader, LogFormat, UserLogger, isItemKey, resolveUniqueConfig } from '@sammo-ts/logic';
import { cloneItemInventory, ensureItemInventory, equipNewItem } from '@sammo-ts/logic/items/index.js';
import { asRecord, JosaUtil } from '@sammo-ts/common';
@@ -180,7 +173,7 @@ export const createAuctionFinalizer = async (options: {
);
const highestBid = bidRows[0] ?? null;
const now = new Date();
const now = world.getGameNow(new Date());
const logs: LogEntryDraft[] = [];
const globalLogger = new ActionLogger();
@@ -244,9 +237,7 @@ export const createAuctionFinalizer = async (options: {
const remainExtension = detail.remainCloseDateExtensionCnt ?? 0;
if (bidMeta.tryExtendCloseDate === true && remainExtension > 0) {
const turnMinutes = await resolveTurnMinutes(db);
const nextCloseAt = new Date(
auction.closeAt.getTime() + Math.max(5, turnMinutes) * 60_000
);
const nextCloseAt = new Date(auction.closeAt.getTime() + Math.max(5, turnMinutes) * 60_000);
const nextLatestBidCloseAt = new Date(
nextCloseAt.getTime() +
Math.max(MIN_EXTENSION_MINUTES_PER_BID, turnMinutes * COEFF_EXTENSION_MINUTES_PER_BID) *
@@ -263,6 +254,7 @@ export const createAuctionFinalizer = async (options: {
SET status = 'OPEN',
detail = ${JSON.stringify(nextDetail)}::jsonb,
close_at = ${nextCloseAt},
close_tick = ${BigInt(world.dateToGameTick(nextCloseAt))},
updated_at = ${now}
WHERE id = ${auctionId}
`
@@ -419,6 +411,7 @@ export const createAuctionFinalizer = async (options: {
host_name = ${bidder.id === auction.hostGeneralId ? auction.hostName : '(상인)'},
detail = ${JSON.stringify(nextDetail)}::jsonb,
close_at = ${nextCloseAt},
close_tick = ${BigInt(world.dateToGameTick(nextCloseAt))},
updated_at = ${now}
WHERE id = ${auctionId}
`
@@ -449,10 +442,7 @@ export const createAuctionFinalizer = async (options: {
);
const nextLatestBidCloseAt = new Date(
nextCloseAt.getTime() +
Math.max(
MIN_EXTENSION_MINUTES_PER_BID,
turnMinutes * COEFF_EXTENSION_MINUTES_PER_BID
) *
Math.max(MIN_EXTENSION_MINUTES_PER_BID, turnMinutes * COEFF_EXTENSION_MINUTES_PER_BID) *
60_000
);
const nextDetail = {
@@ -467,6 +457,7 @@ export const createAuctionFinalizer = async (options: {
host_name = ${bidder.id === auction.hostGeneralId ? auction.hostName : '(상인)'},
detail = ${JSON.stringify(nextDetail)}::jsonb,
close_at = ${nextCloseAt},
close_tick = ${BigInt(world.dateToGameTick(nextCloseAt))},
updated_at = ${now}
WHERE id = ${auctionId}
`
+6 -2
View File
@@ -94,7 +94,7 @@ const openResourceAuction = async (
return fail(`기본 ${hostResource === 'rice' ? '쌀' : '금'} ${minimumResource}은 거래할 수 없습니다.`);
}
const now = new Date();
const now = world.getGameNow(new Date());
const turnMinutes = Math.max(1, Math.round(world.getState().tickSeconds / 60));
const closeAt = new Date(now.getTime() + closeTurnCnt * turnMinutes * 60_000);
const auction = await db.auction.create({
@@ -113,6 +113,8 @@ const openResourceAuction = async (
},
status: 'OPEN',
closeAt,
openTick: BigInt(world.dateToGameTick(now)),
closeTick: BigInt(world.dateToGameTick(closeAt)),
},
});
world.updateGeneral(general.id, {
@@ -218,7 +220,7 @@ const openUniqueAuction = async (
const state = world.getState();
const turnMinutes = Math.max(1, Math.round(state.tickSeconds / 60));
const now = new Date();
const now = world.getGameNow(new Date());
const closeMinutes = Math.max(MIN_AUCTION_CLOSE_MINUTES, turnMinutes * COEFF_AUCTION_CLOSE_MINUTES);
const closeAt = new Date(now.getTime() + closeMinutes * 60_000);
const extensionLimitMinutes = Math.max(
@@ -250,6 +252,8 @@ const openUniqueAuction = async (
},
status: 'OPEN',
closeAt,
openTick: BigInt(world.dateToGameTick(now)),
closeTick: BigInt(world.dateToGameTick(closeAt)),
latestEventId: eventId,
latestEventAt: now,
bids: {