保存独立KV用户服务、固定会话存档与原业务接线,以及C3d资料缓存和C3e本人统计写。 保留原PF/AGG路线、业务公式和时序;榜单读取、好友资料、公告、匹配及首场景闸仍待完成。 验证:ServiceHost两条件各33组646断言;Unity CLI Loop编译0错误269警告。 包含相关方案、评审裁决、测试及状态文档。未操作服务器、正式账号或打包。
259 lines
9.2 KiB
C#
259 lines
9.2 KiB
C#
using System.Globalization;
|
|
using System.Threading.Tasks;
|
|
using asap.core;
|
|
using Game;
|
|
using UnityEngine;
|
|
using System;
|
|
using game.PlayerDataStorage;
|
|
|
|
namespace game
|
|
{
|
|
public interface ISettingService
|
|
{
|
|
bool Music { get; set; }
|
|
bool Sound { get; set; }
|
|
bool Vibration { get; set; }
|
|
bool Notice { get; }
|
|
string Lang { get; set; }
|
|
int UnitOfWeight { get; set; }
|
|
bool ShowNotice { get; set; }
|
|
bool CheckRequestPermission();
|
|
}
|
|
|
|
public class DefaultSettingService : ISettingService
|
|
{
|
|
bool _music;
|
|
bool _sound;
|
|
bool _vibration;
|
|
string _lang;
|
|
int _unitOfWeight = 1;
|
|
bool? _showNotice;
|
|
|
|
readonly bool requiresFormalKV;
|
|
PlayerKVGameSessionHandle promptOwner;
|
|
internal PlayerKVEditResult LastPromptEdit { get; private set; }
|
|
internal bool HasKVPromptOwner => promptOwner != null;
|
|
|
|
// Preserve only our key/owner/error metadata across void UI boundaries, never platform payloads.
|
|
internal static InvalidOperationException PromptFailure(string message)
|
|
{
|
|
var error = new InvalidOperationException(message);
|
|
error.Data["B01PromptDiagnostic"] = message;
|
|
return error;
|
|
}
|
|
internal static void LogPromptFailure(Exception error) => Debug.LogError(
|
|
error.Data["B01PromptDiagnostic"] as string ?? "Notification prompt stopped: " + error.GetType().Name);
|
|
|
|
public DefaultSettingService() : this(false) { }
|
|
internal DefaultSettingService(bool formalKV) { requiresFormalKV = formalKV; }
|
|
internal DefaultSettingService(PlayerKVGameSessionHandle owner) : this(true)
|
|
{
|
|
promptOwner = owner ?? throw new ArgumentNullException(nameof(owner));
|
|
RequirePromptOwner();
|
|
}
|
|
|
|
// The frozen startup selection is supplied before login; this instance can never change owners.
|
|
internal void PreflightFormalPromptOwner(PlayerKVGameSessionHandle owner)
|
|
{
|
|
if (!requiresFormalKV || owner == null || (promptOwner != null && !ReferenceEquals(promptOwner, owner)))
|
|
throw PromptFailure("ShowNotice: formal owner mismatch.");
|
|
var read = KVMgr.ReadKV(owner, "ShowNotice");
|
|
if (!read.Success) throw PromptFailure($"ShowNotice owner={owner.OwnerInstanceId}: {read.Error}");
|
|
}
|
|
internal void BindFormalPromptOwner(PlayerKVGameSessionHandle owner)
|
|
{
|
|
PreflightFormalPromptOwner(owner);
|
|
if (promptOwner == null) promptOwner = owner;
|
|
RequirePromptOwner();
|
|
}
|
|
internal bool HasFormalPromptOwner(PlayerKVGameSessionHandle owner) =>
|
|
requiresFormalKV && ReferenceEquals(promptOwner, owner);
|
|
internal void RequirePromptOwner()
|
|
{
|
|
if (promptOwner != null) ReadPrompt();
|
|
else if (requiresFormalKV) throw PromptFailure("ShowNotice: formal owner not handed off.");
|
|
}
|
|
|
|
PlayerKVKeyReadResult ReadPrompt()
|
|
{
|
|
if (LastPromptEdit != null && !LastPromptEdit.Accepted)
|
|
throw PromptFailure($"ShowNotice owner={promptOwner.OwnerInstanceId}: {LastPromptEdit.StateResult.Error}; uncertain={LastPromptEdit.StateResult.LocalCommitMayHaveSucceeded}");
|
|
var read = KVMgr.ReadKV(promptOwner, "ShowNotice");
|
|
if (!read.Success)
|
|
throw PromptFailure($"ShowNotice owner={promptOwner.OwnerInstanceId}: {read.Error}");
|
|
return read;
|
|
}
|
|
|
|
public bool Music
|
|
{
|
|
set
|
|
{
|
|
if (value != _music)
|
|
{
|
|
PlayerPrefs.SetInt("Music", value ? 1 : 0);
|
|
_music = value;
|
|
GContext.Publish(new SoundMuteEvent(0));
|
|
}
|
|
}
|
|
get { return _music; }
|
|
}
|
|
|
|
public bool Sound
|
|
{
|
|
set
|
|
{
|
|
if (value != _sound)
|
|
{
|
|
PlayerPrefs.SetInt("Sound", value ? 1 : 0);
|
|
_sound = value;
|
|
GContext.Publish(new SoundMuteEvent(1));
|
|
}
|
|
}
|
|
get { return _sound; }
|
|
}
|
|
|
|
public bool Vibration
|
|
{
|
|
set
|
|
{
|
|
if (value != _vibration)
|
|
{
|
|
PlayerPrefs.SetInt("Vibration", value ? 1 : 0);
|
|
_vibration = value;
|
|
}
|
|
}
|
|
|
|
get { return _vibration; }
|
|
}
|
|
|
|
public bool Notice
|
|
{
|
|
get
|
|
{
|
|
return GContext.container.Resolve<ILocalNotificationService>().IsNoticeOpen;
|
|
}
|
|
}
|
|
|
|
public string Lang
|
|
{
|
|
set
|
|
{
|
|
if (value != _lang)
|
|
{
|
|
PlayerPrefs.SetString("Lang", value);
|
|
_lang = value;
|
|
}
|
|
}
|
|
get { return _lang; }
|
|
}
|
|
public int UnitOfWeight
|
|
{
|
|
set
|
|
{
|
|
if (value != _unitOfWeight)
|
|
{
|
|
PlayerPrefs.SetInt("UnitOfWeight", value);
|
|
_unitOfWeight = value;
|
|
}
|
|
}
|
|
get { return _unitOfWeight; }
|
|
}
|
|
public bool ShowNotice
|
|
{
|
|
get
|
|
{
|
|
if (requiresFormalKV && promptOwner == null) RequirePromptOwner();
|
|
if (promptOwner != null)
|
|
{
|
|
var read = ReadPrompt();
|
|
_showNotice = read.Found == true && read.Value == "1";
|
|
return _showNotice.Value;
|
|
}
|
|
_showNotice ??= PlayFabMgr.Instance.GetLocalData("ShowNotice") == "1";
|
|
return _showNotice.Value;
|
|
}
|
|
set
|
|
{
|
|
if (requiresFormalKV && promptOwner == null) RequirePromptOwner();
|
|
if (promptOwner != null)
|
|
{
|
|
var read = ReadPrompt();
|
|
var serialized = value ? "1" : "0";
|
|
if (read.Found != true || read.Value != serialized)
|
|
{
|
|
LastPromptEdit = KVMgr.WriteKV(promptOwner, "ShowNotice", serialized);
|
|
if (!LastPromptEdit.Accepted)
|
|
throw PromptFailure($"ShowNotice owner={promptOwner.OwnerInstanceId}: {LastPromptEdit.StateResult.Error}; uncertain={LastPromptEdit.StateResult.LocalCommitMayHaveSucceeded}");
|
|
}
|
|
RequirePromptOwner();
|
|
_showNotice = value;
|
|
return;
|
|
}
|
|
if (value != _showNotice)
|
|
{
|
|
_showNotice = value;
|
|
PlayFabMgr.Instance.UpdateUserDataValue("ShowNotice", value ? "1" : "0");
|
|
}
|
|
}
|
|
}
|
|
public void Init(cfg.Tables tables)
|
|
{
|
|
if (!PlayerPrefs.HasKey("Music"))
|
|
{
|
|
var globalConfig = tables.TbGlobalConfig;
|
|
Music = globalConfig.MusicSetting;
|
|
Sound = globalConfig.SoundSetting;
|
|
Vibration = true;
|
|
CultureInfo info = System.Globalization.CultureInfo.CurrentUICulture;
|
|
string Language = info.TwoLetterISOLanguageName;
|
|
if (Language == "zh")
|
|
{
|
|
if (info.DisplayName.Contains("Simplified"))
|
|
{
|
|
Language = "zh_Hans";
|
|
}
|
|
else
|
|
{
|
|
Language = "zh_Hant";
|
|
}
|
|
}
|
|
Lang = Language;
|
|
|
|
}
|
|
else
|
|
{
|
|
_music = PlayerPrefs.GetInt("Music") == 1;
|
|
_sound = PlayerPrefs.GetInt("Sound") == 1;
|
|
_vibration = PlayerPrefs.GetInt("Vibration") == 1;
|
|
_lang = PlayerPrefs.GetString("Lang");
|
|
_unitOfWeight = PlayerPrefs.GetInt("UnitOfWeight");
|
|
}
|
|
}
|
|
public bool CheckRequestPermission()
|
|
{
|
|
if (ShowNotice)
|
|
{
|
|
return false;
|
|
}
|
|
var notification = GContext.container.Resolve<ILocalNotificationService>();
|
|
if (notification is LocalNotificationService local) local.RequirePromptOwner(this);
|
|
if (notification.IsNoticeOpen)
|
|
{
|
|
ShowNotice = true;
|
|
return false;
|
|
}
|
|
return true;
|
|
}
|
|
private async Task<bool> AskPermission()
|
|
{
|
|
var result = new TaskCompletionSource<bool>();
|
|
var callbacks = new UnityEngine.Android.PermissionCallbacks();
|
|
callbacks.PermissionGranted += (x) => result.SetResult(true);
|
|
callbacks.PermissionDeniedAndDontAskAgain += (x) => result.SetResult(false);
|
|
callbacks.PermissionDenied += (x) => result.SetResult(false);
|
|
UnityEngine.Android.Permission.RequestUserPermission("android.permission.POST_NOTIFICATIONS", callbacks);
|
|
return await result.Task;
|
|
}
|
|
}
|
|
}
|