diff --git a/Docs/English/Technical/ServicesReference.md b/Docs/English/Technical/ServicesReference.md index bd02b46e..bcec48a4 100644 --- a/Docs/English/Technical/ServicesReference.md +++ b/Docs/English/Technical/ServicesReference.md @@ -135,4 +135,5 @@ All services are registered as singletons in `Bootstrapper.cs` and injected via - **Mods storage policy:** profile switching does not redirect `UserData/Mods` to `Profiles/.../Mods`; mods remain instance-local. - **Profile folder format:** profile folders are stored under `Profiles/{profileId}` (GUID). - **Legacy migration:** launcher attempts to migrate legacy name-based profile folders in `Profiles/` to ID-based layout at startup (best-effort, non-destructive merge when both folders exist). -- **Official profile auth routing:** switching to an official profile automatically sets auth domain to `sessionserver.hytale.com`. +- **Official profile creation:** `hyprism:profile:create` passes the `isOfficial` flag into `ProfileManagementService.CreateProfile` before `Profiles/profiles.json` is written. +- **Official profile auth routing:** switching to an official profile automatically sets auth domain to `sessions.hytale.com`. diff --git a/Docs/English/User/Configuration.md b/Docs/English/User/Configuration.md index 1cbd0dad..ec0f8800 100644 --- a/Docs/English/User/Configuration.md +++ b/Docs/English/User/Configuration.md @@ -130,8 +130,10 @@ HyPrism supports multiple player profiles. Switch between profiles via the sideb Each profile stores: - **Nickname** — Display name in-game - **UUID** — Unique player identifier +- **Official account status** — Hytale OAuth profiles are saved with `IsOfficial: true` in `Profiles/profiles.json` - **Avatar** — Profile picture (optional) - **Skin backup** — Saved skin data +- **Hytale session** — Official account tokens are stored in the profile folder as `hytale_session.json` ### Skin Backup @@ -141,6 +143,7 @@ Profiles can back up your Hytale skin. Backups are stored in: Profiles/ ├── {ProfileUUID}/ │ ├── profile.json # Profile metadata +│ ├── hytale_session.json # Official account session, when present │ └── skin.png # Backed up skin └── ... ``` diff --git a/Docs/Russian/Technical/ServicesReference.md b/Docs/Russian/Technical/ServicesReference.md index 0176691a..747178fc 100644 --- a/Docs/Russian/Technical/ServicesReference.md +++ b/Docs/Russian/Technical/ServicesReference.md @@ -120,4 +120,5 @@ - **Политика хранения модов:** переключение профиля не перенаправляет `UserData/Mods` в `Profiles/.../Mods`; моды остаются в папке выбранного экземпляра. - **Формат папок профилей:** профили хранятся в `Profiles/{profileId}` (GUID). - **Миграция legacy-формата:** при старте лаунчер пытается мигрировать старые name-based папки в `Profiles/` в ID-based формат (best-effort, неразрушающее объединение при наличии обеих папок). +- **Создание official-профиля:** `hyprism:profile:create` передаёт флаг `isOfficial` в `ProfileManagementService.CreateProfile` до записи `Profiles/profiles.json`. - **Маршрутизация auth для official-профиля:** при переключении на официальный профиль домен аутентификации автоматически устанавливается в `sessions.hytale.com`. diff --git a/Docs/Russian/User/Configuration.md b/Docs/Russian/User/Configuration.md index e041e86e..ef153d00 100644 --- a/Docs/Russian/User/Configuration.md +++ b/Docs/Russian/User/Configuration.md @@ -116,8 +116,10 @@ HyPrism поддерживает несколько профилей игрок Каждый профиль хранит: - **Никнейм** — Отображаемое имя в игре - **UUID** — Уникальный идентификатор игрока +- **Статус официального аккаунта** — Профили, созданные через Hytale OAuth, сохраняются с `IsOfficial: true` в `Profiles/profiles.json` - **Аватар** — Изображение профиля (опционально) - **Резервная копия скина** — Сохранённые данные скина +- **Сессию Hytale** — Токены официального аккаунта хранятся в папке профиля как `hytale_session.json` ### Резервное копирование скина @@ -127,6 +129,7 @@ HyPrism поддерживает несколько профилей игрок Profiles/ ├── {ProfileUUID}/ │ ├── profile.json # Метаданные профиля +│ ├── hytale_session.json # Сессия официального аккаунта, если есть │ └── skin.png # Резервная копия скина └── ... ``` diff --git a/HyPrism.Tests/User/ProfileManagementServiceTests.cs b/HyPrism.Tests/User/ProfileManagementServiceTests.cs index 65102119..2ae5cb1e 100644 --- a/HyPrism.Tests/User/ProfileManagementServiceTests.cs +++ b/HyPrism.Tests/User/ProfileManagementServiceTests.cs @@ -2,6 +2,7 @@ using HyPrism.Services.Core.Infrastructure; using HyPrism.Services.Game.Instance; using HyPrism.Services.User; +using System.Text.Json; namespace HyPrism.Tests.User; @@ -69,6 +70,23 @@ public void CreateProfile_AfterCreate_AppearsInGetProfiles() Assert.Contains(profiles, p => p.Name == "Visible"); } + [Fact] + public void CreateProfile_OfficialProfile_PersistsIsOfficialInProfilesJson() + { + var uuid = Guid.NewGuid().ToString(); + var profile = _svc.CreateProfile("OfficialUser", uuid, isOfficial: true); + + Assert.NotNull(profile); + Assert.True(profile!.IsOfficial); + Assert.Contains(_svc.GetProfiles(), p => p.Id == profile.Id && p.IsOfficial); + + var profilesPath = Path.Combine(_svc.GetProfilesFolder(), "profiles.json"); + var savedProfiles = JsonSerializer.Deserialize>(File.ReadAllText(profilesPath)); + + Assert.NotNull(savedProfiles); + Assert.Contains(savedProfiles!, p => p.Id == profile.Id && p.IsOfficial); + } + [Fact] public void CreateProfile_DuplicateName_ReturnsProfile() { diff --git a/Services/Core/Ipc/IpcService.cs b/Services/Core/Ipc/IpcService.cs index a29d6fc7..28e0d9c5 100644 --- a/Services/Core/Ipc/IpcService.cs +++ b/Services/Core/Ipc/IpcService.cs @@ -408,10 +408,8 @@ public SuccessResult SetUuid(string uuid) public IpcProfile? CreateProfile(CreateProfileRequest req) { var mgmt = Services.GetRequiredService(); - var profile = mgmt.CreateProfile(req.Name, req.Uuid); + var profile = mgmt.CreateProfile(req.Name, req.Uuid, req.IsOfficial ?? false); if (profile == null) return null; - profile.IsOfficial = req.IsOfficial ?? false; - Services.GetRequiredService().SaveConfig(); if (profile.IsOfficial) Services.GetRequiredService().SaveSessionToProfile(profile); return new IpcProfile(profile.Id, profile.Name, profile.UUID, profile.IsOfficial); @@ -1493,4 +1491,4 @@ private static Dictionary ParseHeadersString(string headersStrin return headers; } #endregion -} \ No newline at end of file +} diff --git a/Services/User/IProfileManagementService.cs b/Services/User/IProfileManagementService.cs index a9e4f170..5315fd2f 100644 --- a/Services/User/IProfileManagementService.cs +++ b/Services/User/IProfileManagementService.cs @@ -32,12 +32,13 @@ public interface IProfileManagementService Profile? GetSelectedProfile(); /// - /// Creates a new profile with the specified name and UUID. + /// Creates a new profile with the specified name, UUID, and account type. /// /// The profile name (1-16 characters). /// The UUID for the profile. + /// Whether the profile is linked to an official Hytale account. /// The created profile, or null if creation failed. - Profile? CreateProfile(string name, string uuid); + Profile? CreateProfile(string name, string uuid, bool isOfficial = false); /// /// Deletes a profile by its unique identifier. diff --git a/Services/User/ProfileManagementService.cs b/Services/User/ProfileManagementService.cs index 14f23164..48b4646f 100644 --- a/Services/User/ProfileManagementService.cs +++ b/Services/User/ProfileManagementService.cs @@ -200,7 +200,7 @@ public int GetActiveProfileIndex() /// /// Validates name length (1-16 characters) and UUID format before creation. - public Profile? CreateProfile(string name, string uuid) + public Profile? CreateProfile(string name, string uuid, bool isOfficial = false) { try { @@ -230,6 +230,7 @@ public int GetActiveProfileIndex() Id = Guid.NewGuid().ToString(), UUID = parsedUuid.ToString(), Name = trimmedName, + IsOfficial = isOfficial, CreatedAt = DateTime.UtcNow };