update: converted Japanese comments to English

This commit is contained in:
kuwacom 2026-03-07 20:12:17 +09:00
parent 83063f4b73
commit 35f81c6e4f
5 changed files with 169 additions and 121 deletions

View file

@ -13,11 +13,14 @@ namespace ServerRuntime
}; };
/** /**
* `debug`/`info`/`warn`/`error` * **Parse Log Level String**
* *
* @param value * Converts a string value into log level (`debug`/`info`/`warn`/`error`)
* @param outLevel *
* @return `true` *
* @param value Source string
* @param outLevel Output location for parsed level
* @return `true` when conversion succeeds
*/ */
bool TryParseServerLogLevel(const char *value, EServerLogLevel *outLevel); bool TryParseServerLogLevel(const char *value, EServerLogLevel *outLevel);
@ -29,7 +32,7 @@ namespace ServerRuntime
void LogWarn(const char *category, const char *message); void LogWarn(const char *category, const char *message);
void LogError(const char *category, const char *message); void LogError(const char *category, const char *message);
/** 指定レベル・カテゴリでフォーマットログを出力する */ /** Emit formatted log output with the specified level and category */
void LogDebugf(const char *category, const char *format, ...); void LogDebugf(const char *category, const char *format, ...);
void LogInfof(const char *category, const char *format, ...); void LogInfof(const char *category, const char *format, ...);
void LogWarnf(const char *category, const char *format, ...); void LogWarnf(const char *category, const char *format, ...);

View file

@ -196,22 +196,24 @@ static std::string LogLevelToPropertyValue(EServerLogLevel level)
} }
/** /**
* IDとして安全な形式に正規化する * **Normalize Save ID**
* *
* : * Normalizes an arbitrary string into a safe save destination ID
* - * Conversion rules:
* - `[a-z0-9_.-]` * - Lowercase alphabetic characters
* - / `_` * - Keep only `[a-z0-9_.-]`
* - `world` * - Replace spaces and unsupported characters with `_`
* - * - Fallback to `world` when empty
* - Enforce max length to match storage constraints
* IDの正規化処理
*/ */
static std::string NormalizeSaveId(const std::string &source) static std::string NormalizeSaveId(const std::string &source)
{ {
std::string out; std::string out;
out.reserve(source.length()); out.reserve(source.length());
// Storage 側の保存先IDとして安全に扱える文字セットへ正規化する // Normalize into a character set that is safe for storage save IDs
// 不正文字は '_' に落とし、大小は吸収して衝突を減らす // Replace invalid characters with '_' and fold letter case to reduce collisions
for (size_t i = 0; i < source.length(); ++i) for (size_t i = 0; i < source.length(); ++i)
{ {
unsigned char ch = (unsigned char)source[i]; unsigned char ch = (unsigned char)source[i];
@ -241,13 +243,13 @@ static std::string NormalizeSaveId(const std::string &source)
out = "world"; out = "world";
} }
// 先頭文字が扱いづらいケースを避けるため、必要に応じて接頭辞を付与する // Add a prefix when needed to avoid awkward leading characters
if (!((out[0] >= 'a' && out[0] <= 'z') || (out[0] >= '0' && out[0] <= '9'))) if (!((out[0] >= 'a' && out[0] <= 'z') || (out[0] >= '0' && out[0] <= '9')))
{ {
out = std::string("w_") + out; out = std::string("w_") + out;
} }
// 4J 側の filename バッファ制約に合わせて長さを制限する // Clamp length to the 4J-side filename buffer constraint
if (out.length() > kMaxSaveIdLength) if (out.length() > kMaxSaveIdLength)
{ {
out.resize(kMaxSaveIdLength); out.resize(kMaxSaveIdLength);
@ -271,11 +273,13 @@ static void ApplyDefaultServerProperties(std::unordered_map<std::string, std::st
} }
/** /**
* `server.properties` key/value * **Parse server.properties Text**
* *
* - `#` / `!` * Extracts key/value pairs from `server.properties` format text
* - `=` `:` * - Ignores lines starting with `#` or `!` as comments
* - * - Accepts `=` or `:` as separators
* - Skips invalid lines and continues
* server.propertiesのパース処理
*/ */
static bool ReadServerPropertiesFile(const char *filePath, std::unordered_map<std::string, std::string> *properties, int *outParsedCount) static bool ReadServerPropertiesFile(const char *filePath, std::unordered_map<std::string, std::string> *properties, int *outParsedCount)
{ {
@ -351,9 +355,11 @@ static bool ReadServerPropertiesFile(const char *filePath, std::unordered_map<st
} }
/** /**
* key/value `server.properties` * **Write server.properties Text**
* *
* * Writes key/value data back as `server.properties`
* Sorts keys before writing to keep output order stable
* server.propertiesの書き戻し処理
*/ */
static bool WriteServerPropertiesFile(const char *filePath, const std::unordered_map<std::string, std::string> &properties) static bool WriteServerPropertiesFile(const char *filePath, const std::unordered_map<std::string, std::string> &properties)
{ {
@ -574,12 +580,14 @@ static std::string ReadNormalizedLevelTypeProperty(
} }
/** /**
* / * **Load Effective Server Properties Config**
* *
* - * Loads effective world settings, repairs missing or invalid values, and returns normalized config
* - * - Creates defaults when file is missing
* - `level-id` * - Fills required keys when absent
* - * - Normalizes `level-id` to a safe format
* - Auto-saves when any fix is applied
*
*/ */
ServerPropertiesConfig LoadServerPropertiesConfig() ServerPropertiesConfig LoadServerPropertiesConfig()
{ {
@ -620,7 +628,7 @@ ServerPropertiesConfig LoadServerPropertiesConfig()
for (std::unordered_map<std::string, std::string>::const_iterator it = loaded.begin(); it != loaded.end(); ++it) for (std::unordered_map<std::string, std::string>::const_iterator it = loaded.begin(); it != loaded.end(); ++it)
{ {
// 既存値をデフォルトへ上書きマージして、未知キーも可能な限り維持する // Merge loaded values over defaults and keep unknown keys whenever possible
merged[it->first] = it->second; merged[it->first] = it->second;
} }
@ -634,13 +642,13 @@ ServerPropertiesConfig LoadServerPropertiesConfig()
std::string worldSaveId = TrimAscii(merged["level-id"]); std::string worldSaveId = TrimAscii(merged["level-id"]);
if (worldSaveId.empty()) if (worldSaveId.empty())
{ {
// level-id が未設定なら level-name から自動生成して保存先を固定する // If level-id is missing, derive it from level-name to lock save destination
worldSaveId = NormalizeSaveId(worldName); worldSaveId = NormalizeSaveId(worldName);
shouldWrite = true; shouldWrite = true;
} }
else else
{ {
// 既存の level-id も正規化して、将来の不整合を防ぐ // Normalize existing level-id as well to avoid future inconsistencies
std::string normalized = NormalizeSaveId(worldSaveId); std::string normalized = NormalizeSaveId(worldSaveId);
if (normalized != worldSaveId) if (normalized != worldSaveId)
{ {
@ -713,10 +721,12 @@ ServerPropertiesConfig LoadServerPropertiesConfig()
} }
/** /**
* * **Save World Identity While Preserving Other Keys**
* *
* - * Saves world identity fields while preserving as many other settings as possible
* - `level-name` / `level-id` * - Reads existing file and merges including unknown keys
* - Updates only `level-name` and `level-id` before writing back
*
*/ */
bool SaveServerPropertiesConfig(const ServerPropertiesConfig &config) bool SaveServerPropertiesConfig(const ServerPropertiesConfig &config)
{ {
@ -729,7 +739,7 @@ bool SaveServerPropertiesConfig(const ServerPropertiesConfig &config)
{ {
for (std::unordered_map<std::string, std::string>::const_iterator it = loaded.begin(); it != loaded.end(); ++it) for (std::unordered_map<std::string, std::string>::const_iterator it = loaded.begin(); it != loaded.end(); ++it)
{ {
// 呼び出し側が触っていないキーを落とさないように、既存内容を保持する // Keep existing content so keys untouched by caller are not dropped
merged[it->first] = it->second; merged[it->first] = it->second;
} }
} }
@ -737,7 +747,7 @@ bool SaveServerPropertiesConfig(const ServerPropertiesConfig &config)
std::string worldName = TrimAscii(WideToUtf8(config.worldName)); std::string worldName = TrimAscii(WideToUtf8(config.worldName));
if (worldName.empty()) if (worldName.empty())
{ {
worldName = "world"; // デフォルト名 worldName = "world"; // Default world name
} }
std::string worldSaveId = TrimAscii(config.worldSaveId); std::string worldSaveId = TrimAscii(config.worldSaveId);

View file

@ -83,9 +83,10 @@ static BOOL WINAPI ConsoleCtrlHandlerProc(DWORD ctrlType)
} }
/** /**
* * **Wait For Server Stopped Signal**
* *
* 使 * Thread entry used during shutdown to wait until the network layer reports server stop completion
*
*/ */
static int WaitForServerStoppedThreadProc(void *) static int WaitForServerStoppedThreadProc(void *)
{ {
@ -258,10 +259,11 @@ static void ApplyServerPropertiesToDedicatedConfig(const ServerPropertiesConfig
} }
/** /**
* 1 * **Tick Core Async Subsystems**
* *
* // * Advances core subsystems for one frame to keep async processing alive
* * Call continuously even inside wait loops to avoid stalling storage/profile/network work
*
*/ */
static void TickCoreSystems() static void TickCoreSystems()
{ {
@ -271,7 +273,10 @@ static void TickCoreSystems()
} }
/** /**
* XUI / 1 * **Handle Queued XUI Server Action Once**
*
* Processes queued XUI/server action once
* XUIアクションの単発処理
*/ */
static void HandleXuiActions() static void HandleXuiActions()
{ {
@ -302,7 +307,7 @@ int main(int argc, char **argv)
SetConsoleCtrlHandler(ConsoleCtrlHandlerProc, TRUE); SetConsoleCtrlHandler(ConsoleCtrlHandlerProc, TRUE);
SetExeWorkingDirectory(); SetExeWorkingDirectory();
// server.properties の値をベース設定として読み込み、CLI で必要に応じて上書きする // Load base settings from server.properties, then override with CLI values when provided
ServerPropertiesConfig serverProperties = LoadServerPropertiesConfig(); ServerPropertiesConfig serverProperties = LoadServerPropertiesConfig();
ApplyServerPropertiesToDedicatedConfig(serverProperties, &config); ApplyServerPropertiesToDedicatedConfig(serverProperties, &config);
@ -445,12 +450,12 @@ int main(int argc, char **argv)
app.SetGameHostOption(eGameHostOption_DoDaylightCycle, serverProperties.doDaylightCycle ? 1 : 0); app.SetGameHostOption(eGameHostOption_DoDaylightCycle, serverProperties.doDaylightCycle ? 1 : 0);
StorageManager.SetSaveDisabled(serverProperties.disableSaving); StorageManager.SetSaveDisabled(serverProperties.disableSaving);
// server.properties から world 名と固定 save-id を取得し、 // Read world name and fixed save-id from server.properties
// WorldManager にロード/新規作成判定を委譲する // Delegate load-vs-create decision to WorldManager
std::wstring targetWorldName = serverProperties.worldName; std::wstring targetWorldName = serverProperties.worldName;
if (targetWorldName.empty()) if (targetWorldName.empty())
{ {
targetWorldName = L"world"; // デフォ名 targetWorldName = L"world"; // Default world name
} }
WorldBootstrapResult worldBootstrap = BootstrapWorldForServer(serverProperties, kServerActionPad, &TickCoreSystems); WorldBootstrapResult worldBootstrap = BootstrapWorldForServer(serverProperties, kServerActionPad, &TickCoreSystems);
if (worldBootstrap.status == eWorldBootstrap_Loaded) if (worldBootstrap.status == eWorldBootstrap_Loaded)
@ -458,8 +463,8 @@ int main(int argc, char **argv)
const std::string &loadedSaveFilename = worldBootstrap.resolvedSaveId; const std::string &loadedSaveFilename = worldBootstrap.resolvedSaveId;
if (!loadedSaveFilename.empty() && _stricmp(loadedSaveFilename.c_str(), serverProperties.worldSaveId.c_str()) != 0) if (!loadedSaveFilename.empty() && _stricmp(loadedSaveFilename.c_str(), serverProperties.worldSaveId.c_str()) != 0)
{ {
// 実際に読み込まれた save-id を設定ファイルへ戻して、 // Persist the actually loaded save-id back to config
// 次回起動時の探索キーを揃える // Keep lookup keys aligned for next startup
LogWorldIO("updating level-id to loaded save filename"); LogWorldIO("updating level-id to loaded save filename");
serverProperties.worldSaveId = loadedSaveFilename; serverProperties.worldSaveId = loadedSaveFilename;
if (!SaveServerPropertiesConfig(serverProperties)) if (!SaveServerPropertiesConfig(serverProperties))
@ -516,8 +521,8 @@ int main(int argc, char **argv)
LogInfof("startup", "Dedicated server listening on %s:%d", g_Win64MultiplayerIP, g_Win64MultiplayerPort); LogInfof("startup", "Dedicated server listening on %s:%d", g_Win64MultiplayerIP, g_Win64MultiplayerPort);
if (worldBootstrap.status == eWorldBootstrap_CreatedNew && !g_shutdownRequested && !app.m_bShutdown) if (worldBootstrap.status == eWorldBootstrap_CreatedNew && !g_shutdownRequested && !app.m_bShutdown)
{ {
// Windows64 では新規ワールド直後の saveToDisc を抑止しているため // Windows64 suppresses saveToDisc right after new world creation
// Dedicated Server はここで初回保存を明示的に実行する // Dedicated Server explicitly runs the initial save here
LogWorldIO("requesting initial save for newly created world"); LogWorldIO("requesting initial save for newly created world");
WaitForWorldActionIdle(kServerActionPad, 5000, &TickCoreSystems, &HandleXuiActions); WaitForWorldActionIdle(kServerActionPad, 5000, &TickCoreSystems, &HandleXuiActions);
app.SetXuiServerAction(kServerActionPad, eXuiServerAction_AutoSaveGame); app.SetXuiServerAction(kServerActionPad, eXuiServerAction_AutoSaveGame);
@ -582,7 +587,7 @@ int main(int argc, char **argv)
} }
LogWorldIO("requesting save before shutdown"); LogWorldIO("requesting save before shutdown");
// 終了時保存の前に Idle へ戻して、既存の action と競合しないようにする // Return to Idle before shutdown save to avoid action conflicts
WaitForWorldActionIdle(kServerActionPad, 5000, &TickCoreSystems, &HandleXuiActions); WaitForWorldActionIdle(kServerActionPad, 5000, &TickCoreSystems, &HandleXuiActions);
app.SetXuiServerAction(kServerActionPad, eXuiServerAction_SaveGame); app.SetXuiServerAction(kServerActionPad, eXuiServerAction_SaveGame);
if (!WaitForWorldActionIdle(kServerActionPad, 15000, &TickCoreSystems, &HandleXuiActions)) if (!WaitForWorldActionIdle(kServerActionPad, 15000, &TickCoreSystems, &HandleXuiActions))

View file

@ -51,12 +51,14 @@ struct SaveDataLoadContext
}; };
/** /**
* `StorageManager` ID`level-id` * **Apply Save ID To StorageManager**
* *
* - IDを設定し * Applies the configured save destination ID (`level-id`) to `StorageManager`
* - * - Re-applies the same ID at startup and before save to avoid destination drift
* - Ignores empty values as invalid
* IDの適用処理
* *
* @param saveFilename ID * @param saveFilename Normalized save destination ID
*/ */
static void SetStorageSaveUniqueFilename(const std::string &saveFilename) static void SetStorageSaveUniqueFilename(const std::string &saveFilename)
{ {
@ -94,9 +96,10 @@ static void LogEnumeratedSaveInfo(int index, const SAVE_INFO &saveInfo)
} }
/** /**
* (callback) * **Save List Callback**
* *
* `SaveInfoQueryContext` * Captures async save-list results into `SaveInfoQueryContext` and marks completion for the waiter
*
*/ */
static int GetSavesInfoCallbackProc(LPVOID lpParam, SAVE_DETAILS *pSaveDetails, const bool bRes) static int GetSavesInfoCallbackProc(LPVOID lpParam, SAVE_DETAILS *pSaveDetails, const bool bRes)
{ {
@ -111,9 +114,10 @@ static int GetSavesInfoCallbackProc(LPVOID lpParam, SAVE_DETAILS *pSaveDetails,
} }
/** /**
* (callback) * **Save Data Load Callback**
* *
* `SaveDataLoadContext` * Writes load results such as corruption status into `SaveDataLoadContext`
*
*/ */
static int LoadSaveDataCallbackProc(LPVOID lpParam, const bool bIsCorrupt, const bool bIsOwner) static int LoadSaveDataCallbackProc(LPVOID lpParam, const bool bIsCorrupt, const bool bIsOwner)
{ {
@ -128,13 +132,14 @@ static int LoadSaveDataCallbackProc(LPVOID lpParam, const bool bIsCorrupt, const
} }
/** /**
* * **Wait For Save List Completion**
* *
* - callback * Waits until save-list retrieval completes
* - callback `ReturnSavesInfo()` * - Prefers callback completion as the primary signal
* * - Also falls back to polling because some environments populate `ReturnSavesInfo()` before callback
*
* *
* @return `true` * @return `true` when completion is detected
*/ */
static bool WaitForSaveInfoResult(SaveInfoQueryContext *context, DWORD timeoutMs, WorldManagerTickProc tickProc) static bool WaitForSaveInfoResult(SaveInfoQueryContext *context, DWORD timeoutMs, WorldManagerTickProc tickProc)
{ {
@ -148,8 +153,8 @@ static bool WaitForSaveInfoResult(SaveInfoQueryContext *context, DWORD timeoutMs
if (context->details == NULL) if (context->details == NULL)
{ {
// 実装/環境によっては callback より先に ReturnSavesInfo が埋まるため、 // Some implementations fill ReturnSavesInfo before the callback
// callback 完了待ちだけに依存せずポーリングでも救済する // Keep polling as a fallback instead of relying only on callback completion
SAVE_DETAILS *details = StorageManager.ReturnSavesInfo(); SAVE_DETAILS *details = StorageManager.ReturnSavesInfo();
if (details != NULL) if (details != NULL)
{ {
@ -171,9 +176,12 @@ static bool WaitForSaveInfoResult(SaveInfoQueryContext *context, DWORD timeoutMs
} }
/** /**
* callback * **Wait For Save Data Load Completion**
* *
* @return callback `true` `false` * Waits for the save-data load callback to complete
*
*
* @return `true` when callback is reached, `false` on timeout
*/ */
static bool WaitForSaveLoadResult(SaveDataLoadContext *context, DWORD timeoutMs, WorldManagerTickProc tickProc) static bool WaitForSaveLoadResult(SaveDataLoadContext *context, DWORD timeoutMs, WorldManagerTickProc tickProc)
{ {
@ -196,9 +204,10 @@ static bool WaitForSaveLoadResult(SaveDataLoadContext *context, DWORD timeoutMs,
} }
/** /**
* `SAVE_INFO` * **Match SAVE_INFO By World Name**
* *
* * Compares both save title and save filename against the target world name
*
*/ */
static bool SaveInfoMatchesWorldName(const SAVE_INFO &saveInfo, const std::wstring &targetWorldName) static bool SaveInfoMatchesWorldName(const SAVE_INFO &saveInfo, const std::wstring &targetWorldName)
{ {
@ -223,7 +232,10 @@ static bool SaveInfoMatchesWorldName(const SAVE_INFO &saveInfo, const std::wstri
} }
/** /**
* ID`UTF8SaveFilename` `SAVE_INFO` * **Match SAVE_INFO By Save Filename**
*
* Checks whether `SAVE_INFO` matches by save destination ID (`UTF8SaveFilename`)
* ID一致判定
*/ */
static bool SaveInfoMatchesSaveFilename(const SAVE_INFO &saveInfo, const std::string &targetSaveFilename) static bool SaveInfoMatchesSaveFilename(const SAVE_INFO &saveInfo, const std::string &targetSaveFilename)
{ {
@ -236,30 +248,34 @@ static bool SaveInfoMatchesSaveFilename(const SAVE_INFO &saveInfo, const std::st
} }
/** /**
* `level-name` + `level-id` * **Apply World Identity To Storage**
* *
* - /IDだけの片設定を避け * Applies world identity (`level-name` + `level-id`) to storage
* - * - Always sets both display name and ID to avoid partial configuration
* - Helps prevent unintended new save destinations across environment differences
*
*/ */
static void ApplyWorldStorageTarget(const std::wstring &worldName, const std::string &saveId) static void ApplyWorldStorageTarget(const std::wstring &worldName, const std::string &saveId)
{ {
// タイトル(表示名)と保存先ID(実体フォルダ名)を明示的に両方設定する // Set both title (display name) and save ID (actual folder name) explicitly
// どちらか片方だけだと環境によって新規保存先が生成されることがある // Setting only one side can create unexpected new save targets in some environments
StorageManager.SetSaveTitle(worldName.c_str()); StorageManager.SetSaveTitle(worldName.c_str());
SetStorageSaveUniqueFilename(saveId); SetStorageSaveUniqueFilename(saveId);
} }
/** /**
* * **Prepare World Save Data For Startup**
* *
* : * Searches for a save matching the target world and extracts startup payload when found
* 1. `level-id``UTF8SaveFilename` * Match priority:
* 2. `level-name` / * 1. Exact match by `level-id` (`UTF8SaveFilename`)
* 2. Fallback match by `level-name` against title or filename
*
* *
* @return * @return
* - `eWorldSaveLoad_Loaded`: * - `eWorldSaveLoad_Loaded`: Existing save loaded successfully
* - `eWorldSaveLoad_NotFound`: * - `eWorldSaveLoad_NotFound`: No matching save found
* - `eWorldSaveLoad_Failed`: API失敗// * - `eWorldSaveLoad_Failed`: API failure, corruption, or invalid data
*/ */
static EWorldSaveLoadResult PrepareWorldSaveData( static EWorldSaveLoadResult PrepareWorldSaveData(
const std::wstring &targetWorldName, const std::wstring &targetWorldName,
@ -315,8 +331,8 @@ static EWorldSaveLoadResult PrepareWorldSaveData(
int matchedIndex = -1; int matchedIndex = -1;
if (!targetSaveFilename.empty()) if (!targetSaveFilename.empty())
{ {
// 1) 保存先IDが指定されている場合は最優先で一致検索 // 1) If save ID is provided, search by it first
// これが最も安定して「同じワールド」を再利用できる(勝手に上書きで新規作成されることがある) // This is the most stable way to reuse the same world target
for (int i = 0; i < infoContext.details->iSaveC; ++i) for (int i = 0; i < infoContext.details->iSaveC; ++i)
{ {
LogEnumeratedSaveInfo(i, infoContext.details->SaveInfoA[i]); LogEnumeratedSaveInfo(i, infoContext.details->SaveInfoA[i]);
@ -338,8 +354,8 @@ static EWorldSaveLoadResult PrepareWorldSaveData(
for (int i = 0; i < infoContext.details->iSaveC; ++i) for (int i = 0; i < infoContext.details->iSaveC; ++i)
{ {
// 2) 保存先IDで見つからない場合は互換フォールバックとして // 2) If no save matched by ID, try compatibility fallback
// タイトル/ファイル名と worldName の一致を試す // Match worldName against save title or save filename
if (matchedIndex >= 0) if (matchedIndex >= 0)
{ {
break; break;
@ -375,7 +391,7 @@ static EWorldSaveLoadResult PrepareWorldSaveData(
std::string resolvedSaveFilename; std::string resolvedSaveFilename;
if (matchedSaveInfo->UTF8SaveFilename[0] != 0) if (matchedSaveInfo->UTF8SaveFilename[0] != 0)
{ {
// 実際に見つかった保存先IDを優先採用し、今後の保存も同じ先に固定する // Prefer the save ID that was actually matched, then keep using it for future saves
resolvedSaveFilename = matchedSaveInfo->UTF8SaveFilename; resolvedSaveFilename = matchedSaveInfo->UTF8SaveFilename;
SetStorageSaveUniqueFilename(resolvedSaveFilename); SetStorageSaveUniqueFilename(resolvedSaveFilename);
} }
@ -414,7 +430,7 @@ static EWorldSaveLoadResult PrepareWorldSaveData(
unsigned int saveSize = StorageManager.GetSaveSize(); unsigned int saveSize = StorageManager.GetSaveSize();
if (saveSize == 0) if (saveSize == 0)
{ {
// 読み込み成功扱いでも実データが0byteなら安全側で失敗扱いにする // Treat zero-byte payload as failure even when load API reports success
LogWorldIO("loaded save has zero size"); LogWorldIO("loaded save has zero size");
return eWorldSaveLoad_Failed; return eWorldSaveLoad_Failed;
} }
@ -434,11 +450,13 @@ static EWorldSaveLoadResult PrepareWorldSaveData(
} }
/** /**
* * **Bootstrap World State For Server Startup**
* *
* - * Determines final world startup state
* - * - Returns loaded save data when an existing save is found
* - `Failed` * - Prepares a new world context when not found
* - Returns `Failed` when startup should be aborted
*
*/ */
WorldBootstrapResult BootstrapWorldForServer( WorldBootstrapResult BootstrapWorldForServer(
const ServerPropertiesConfig &config, const ServerPropertiesConfig &config,
@ -478,8 +496,8 @@ WorldBootstrapResult BootstrapWorldForServer(
} }
else if (worldLoadResult == eWorldSaveLoad_NotFound) else if (worldLoadResult == eWorldSaveLoad_NotFound)
{ {
// 一致セーブがない場合のみ新規コンテキストを作る // Create a new context only when no matching save exists
// この時点で saveId を固定しておくことで、次回起動時に同じ場所へ保存される // Fix saveId here so the next startup writes to the same location
result.status = eWorldBootstrap_CreatedNew; result.status = eWorldBootstrap_CreatedNew;
result.resolvedSaveId = targetSaveFilename; result.resolvedSaveId = targetSaveFilename;
LogStartupStep("configured world not found; creating new world"); LogStartupStep("configured world not found; creating new world");
@ -496,9 +514,10 @@ WorldBootstrapResult BootstrapWorldForServer(
} }
/** /**
* XUI `Idle` * **Wait Until Server XUI Action Is Idle**
* *
* tick/handle * Keeps tick/handle running during save action so async processing does not stall
* XUIアクション待機中の進行維持処理
*/ */
bool WaitForWorldActionIdle( bool WaitForWorldActionIdle(
int actionPad, int actionPad,
@ -509,8 +528,8 @@ bool WaitForWorldActionIdle(
DWORD start = GetTickCount(); DWORD start = GetTickCount();
while (app.GetXuiServerAction(actionPad) != eXuiServerAction_Idle && !MinecraftServer::serverHalted()) while (app.GetXuiServerAction(actionPad) != eXuiServerAction_Idle && !MinecraftServer::serverHalted())
{ {
// 待機中もネットワーク/ストレージ進行を止めない // Keep network and storage progressing while waiting
// ここを止めると save action 自体が進まずタイムアウトしやすい // If this stops, save action itself may stall and time out
if (tickProc != NULL) if (tickProc != NULL)
{ {
tickProc(); tickProc();

View file

@ -10,34 +10,40 @@ typedef struct _LoadSaveDataThreadParam LoadSaveDataThreadParam;
namespace ServerRuntime namespace ServerRuntime
{ {
/** 非同期ストレージ/ネットワーク待機中に回すティック関数 */ /** Tick callback used while waiting on async storage/network work */
typedef void (*WorldManagerTickProc)(); typedef void (*WorldManagerTickProc)();
/** サーバーアクション待機中に任意で回すアクション処理関数 */ /** Optional action handler used while waiting for server actions */
typedef void (*WorldManagerHandleActionsProc)(); typedef void (*WorldManagerHandleActionsProc)();
/** /**
* / * **World Bootstrap Status**
*
* Result type for world startup preparation, either loading an existing world or creating a new one
*
*/ */
enum EWorldBootstrapStatus enum EWorldBootstrapStatus
{ {
/** 既存ワールドを発見し、ロードできた */ /** Found and loaded an existing world */
eWorldBootstrap_Loaded, eWorldBootstrap_Loaded,
/** 一致するセーブが無く、新規ワールド文脈を作成した */ /** No matching save was found, created a new world context */
eWorldBootstrap_CreatedNew, eWorldBootstrap_CreatedNew,
/** 起動準備に失敗し、サーバー起動を中断すべき状態 */ /** Bootstrap failed and server startup should be aborted */
eWorldBootstrap_Failed eWorldBootstrap_Failed
}; };
/** /**
* **World Bootstrap Result**
*
* Output payload returned by world startup preparation
* *
*/ */
struct WorldBootstrapResult struct WorldBootstrapResult
{ {
/** 起動準備ステータス */ /** Bootstrap status */
EWorldBootstrapStatus status; EWorldBootstrapStatus status;
/** サーバー初期化用のセーブデータ(新規時は `NULL` */ /** Save data used for server initialization, `NULL` when creating a new world */
LoadSaveDataThreadParam *saveData; LoadSaveDataThreadParam *saveData;
/** 実際に採用された保存先ID */ /** Save ID that was actually selected */
std::string resolvedSaveId; std::string resolvedSaveId;
WorldBootstrapResult() WorldBootstrapResult()
@ -48,16 +54,18 @@ namespace ServerRuntime
}; };
/** /**
* / * **Bootstrap Target World For Server Startup**
* *
* - `server.properties` `level-name` / `level-id` * Resolves whether the target world should be loaded from an existing save or created as new
* - * - Applies `level-name` and `level-id` from `server.properties`
* - * - Loads when a matching save exists
* - Creates a new world context only when no save matches
*
* *
* @param config `server.properties` * @param config Normalized `server.properties` values
* @param actionPad APIで使うpadId * @param actionPad padId used by async storage APIs
* @param tickProc * @param tickProc Tick callback run while waiting for async completion
* @return * @return Bootstrap result including whether save data was loaded
*/ */
WorldBootstrapResult BootstrapWorldForServer( WorldBootstrapResult BootstrapWorldForServer(
const ServerPropertiesConfig &config, const ServerPropertiesConfig &config,
@ -65,13 +73,16 @@ namespace ServerRuntime
WorldManagerTickProc tickProc); WorldManagerTickProc tickProc);
/** /**
* `Idle` * **Wait Until Server Action Returns To Idle**
* *
* @param actionPad padId * Waits until server action state reaches `Idle`
* @param timeoutMs *
* @param tickProc *
* @param handleActionsProc * @param actionPad padId to monitor
* @return `Idle` `true` * @param timeoutMs Timeout in milliseconds
* @param tickProc Tick callback run inside the wait loop
* @param handleActionsProc Optional action handler callback
* @return `true` when `Idle` is reached before timeout
*/ */
bool WaitForWorldActionIdle( bool WaitForWorldActionIdle(
int actionPad, int actionPad,