当前位置: 首页 > article >正文

Unity NTPComponent应用, 实现一个无后端高效获取网络时间的组件

无后端高效获取网络时间的组件

  • 废话不多说,直接上源码
  • m_NowSerivceTime 一个基于你发行游戏地区的时间偏移, 比如北京时区就是 8, 巴西就是-3,美国就是-5
  • using Newtonsoft.Json; 如果这里报错, 就说明项目没有 NewtonsoftJson插件, 没关系,这里你改成Unity内置的就行
  • 总之就一句, 没有必要就直接使用NTPComponent.m_NowUtc, 有时区要求就用 NTPComponent.m_NowSerivceTime, 总之,优势在我!
  • 原理, 这块没必要看,如果有同学有兴趣,可以继续看看

废话不多说,直接上源码

直接新建一个脚本 NTPComponent.cs
将脚本Copy到你的项目,拖入场景节点上
获取UTC时间 NTPComponent.m_NowUtc


using UnityEngine;
using System;
using System.Collections;
using UnityEngine.Networking;
using Newtonsoft.Json;
using System.Globalization;

namespace GameContent
{
    /// <summary>
    /// 启动游戏后,将所有地址列表遍历
    /// </summary>
    [DisallowMultipleComponent]
    public class NTPComponent : MonoBehaviour
    {
        /// <summary>
        /// 网络时间是否生效中
        /// </summary>
        public static bool m_IsValid { get; private set; } = true;

        /// <summary>
        /// 当前Utc时间
        /// </summary>
        public static DateTime m_NowUtc
        {
            get
            {
                return m_NowUtcServerDate.AddSeconds( ( int ) ( Time.unscaledTime - m_ServerTimePoint ) );
            }
            private set
            {
                m_NowUtcServerDate = value;
            }
        }


        /// <summary>
        /// 当前服务器时间
        /// </summary>
        public static DateTime m_NowSerivceTime
        {
            get
            {
                return m_NowUtc.AddHours( GlobalConfig.TIME_ZONE_OFFSET );
            }
        }



        /// <summary>
        /// 服务器标记时间对象
        /// </summary>
        private static DateTime m_NowUtcServerDate;
        /// <summary>
        /// 拉取服务器的标记时间尺
        /// </summary>
        private static float m_ServerTimePoint = 0f;

        /// <summary>
        /// 是否已经成功拉取到服务器时间
        /// </summary>
        private bool m_Inited = false;

        private void Awake( )
        {
            m_NowUtc = DateTime.UtcNow;
            m_ServerTimePoint = Time.unscaledTime;
            DontDestroyOnLoad( gameObject );
        }

        private void Start( )
        {
#if !SANDBOX_MODE
            StartCoroutine( GetNetTimeFromWorldTimeApi() );
            StartCoroutine( GetNetTimeFromTimeIOApi() );
            StartCoroutine( GetNetTimeFromGoogleApi() );

#else
            Log.Green( $"当前是沙盒环境,你可以更改时间 {DateTime.Now}" );
#endif
        }


        #region WebApi


        IEnumerator GetApi( string api, Action<string> callback )
        {
            using ( var request = UnityWebRequest.Get( "https://worldtimeapi.org/api/timezone/Etc/UTC" ) )
            {
                yield return request.SendWebRequest();
                try
                {
                    if ( request.result == UnityWebRequest.Result.Success )
                    {
                        callback( request.downloadHandler.text );
                    }
                    //else
                    //{
                    //    Debug.LogError( $"Failed to fetch server time: {request.error}" );
                    //}
                }
                catch ( Exception e )
                {
                    //不处理
                }
            }
        }


        struct WorldTimeData
        {
            public string datetime;
            public string timezone;
            public string utc_offset;
        }

        struct TimeIOData
        {
            public int year;
            public int month;
            public int day;
            public int hour;
            public int minute;
            public int seconds;
            public string dateTime;
            public string timeZone;
            public string dayOfWeek;
            public bool dstActive;
        }


        IEnumerator GetNetTimeFromWorldTimeApi( )
        {
            string result = string.Empty;
            yield return GetApi( "https://worldtimeapi.org/api/timezone/Etc/UTC", _ => result = _ );
            if ( !string.IsNullOrEmpty( result ) )
            {
                var data = JsonConvert.DeserializeObject<WorldTimeData>( result );
                Debug.Log( $"World Time: {data.datetime}" );
                if ( TryParseUTCString( data.datetime, out var utcNow ) && utcNow != DateTime.MinValue )
                {
                    OnPullServerTimeOK( utcNow );
                }
            }
        }


        IEnumerator GetNetTimeFromTimeIOApi( )
        {
            string result = string.Empty;
            yield return GetApi( "https://timeapi.io/api/Time/current/zone?timeZone=UTC", _ => result = _ );
            if ( !string.IsNullOrEmpty( result ) )
            {
                var data = JsonConvert.DeserializeObject<TimeIOData>( result );
                Debug.Log( $"Time IO API: {data.dateTime}" );
                if ( TryParseUTCString( data.dateTime, out var utcNow ) && utcNow != DateTime.MinValue )
                {
                    OnPullServerTimeOK( utcNow );
                }
            }
        }


        IEnumerator GetNetTimeFromGoogleApi( )
        {

            using ( var request = UnityWebRequest.Get( "https://www.google.com" ) )
            {
                yield return request.SendWebRequest();
                try
                {
                    if ( request.result == UnityWebRequest.Result.Success )
                    {
                        if ( request.GetResponseHeader( "Date" ) != null )
                        {
                            string serverDate = request.GetResponseHeader( "Date" );
                            Debug.Log( $"Google Server Time: {serverDate}" );
                            var utcDate = ParseServerDateToUTC( serverDate );
                            if ( utcDate != DateTime.MinValue )
                            {
                                OnPullServerTimeOK( utcDate );
                            }
                        }
                    }
                }
                catch ( Exception e )
                {
                    //不处理
                }
            }
        }



        #endregion



        #region Common


        /// <summary>
        /// Parses a UTC time string into a DateTime object.
        /// </summary>
        /// <param name="utcString">The UTC time string.</param>
        /// <returns>A DateTime object in UTC, or DateTime.MinValue if parsing fails.</returns>
        public static bool TryParseUTCString( string utcString, out DateTime utcNow )
        {
            try
            {
                // Attempt to parse the string with DateTime.Parse
                DateTime parsedDate = DateTime.Parse( utcString, null, DateTimeStyles.AdjustToUniversal | DateTimeStyles.AssumeUniversal );
                utcNow = parsedDate;
                return true;
            }
            catch ( FormatException )
            {
                Debug.Log( "Failed to parse UTC time string." );
                utcNow = DateTime.MinValue;
                return false;
            }
        }


        /// <summary>
        /// Parses a server date string (from HTTP header) into a UTC DateTime object.
        /// </summary>
        /// <param name="serverDate">The server date string in RFC1123 format.</param>
        /// <returns>A DateTime object in UTC.</returns>
        public static DateTime ParseServerDateToUTC( string serverDate )
        {
            try
            {
                // Use DateTime.ParseExact to parse RFC1123 format
                DateTime parsedDate = DateTime.ParseExact(
                    serverDate,
                    "r", // "r" or "R" stands for RFC1123 pattern
                    CultureInfo.InvariantCulture,
                    DateTimeStyles.AssumeUniversal | DateTimeStyles.AdjustToUniversal
                );

                return parsedDate;
            }
            catch ( FormatException ex )
            {
                Debug.Log( $"Error parsing server date: {ex.Message}" );
                return DateTime.MinValue; // Return MinValue to indicate failure
            }
        }


        /// <summary>
        /// 当服务器拉取到时间后调用, 锁定一次
        /// </summary>
        /// <param name="utcTime"></param>
        private void OnPullServerTimeOK( DateTime utcTime )
        {
            if ( m_Inited ) return;
            m_Inited = true;

            StopAllCoroutines();
            m_ServerTimePoint = Time.unscaledTime;
            m_NowUtc = utcTime;
            Log.Green( $"获取服务器时间成功: {utcTime}" );
        }

        #endregion

    }
}



m_NowSerivceTime 一个基于你发行游戏地区的时间偏移, 比如北京时区就是 8, 巴西就是-3,美国就是-5

具体根项目运营确认

//这个就是一个全局的定义,自己写一个类或者 写死一个也行
GlobalConfig.TIME_ZONE_OFFSET = 8;

using Newtonsoft.Json; 如果这里报错, 就说明项目没有 NewtonsoftJson插件, 没关系,这里你改成Unity内置的就行

改成 JsonUtility.FromJson( result ); //Unity 内置的Json库
在这里插入图片描述

总之就一句, 没有必要就直接使用NTPComponent.m_NowUtc, 有时区要求就用 NTPComponent.m_NowSerivceTime, 总之,优势在我!

在这里插入图片描述

原理, 这块没必要看,如果有同学有兴趣,可以继续看看

真实时间由两个部分组成, 一个是请求一次得到的 真实云UTC时间, 另外一个是当前游戏的秒数TimePoint
通过 基数 + 秒数偏移。 能在游戏内断网的时候有效获取到真实的云时间
需要注意的是,在游戏启动的时候,你得确保用户是联网的
当然,如果你的游戏是纯单机的,也不会报错,因为在Awake的时候默认用的是本地的TimePoint,如果你用纯单机也就不需要考虑真实的时间, 这里是能保证你的项目能在任何条件下安全的跑起来

在游戏启动的时候获取一个 UTC时间 基数
然后记录当前的游戏运行时间 Time.unscaledTime
获取当前真实的UTC时间时 => UTC时间 基数 + ( 当前游戏运行时间 - 记录时间 ) 秒数偏移

在这里插入图片描述


http://www.kler.cn/a/442626.html

相关文章:

  • VS Code 的扩展下载安装的最新方式
  • C#,图片分层(Layer Bitmap)绘制,反色、高斯模糊及凹凸贴图等处理的高速算法与源程序
  • AR 在高校实验室安全教育中的应用
  • dockerfile2.0
  • Python在Excel工作表中创建数据透视表
  • 如何将原来使用cmakelist编译的qt工程转换为可使用Visual Studio编译的项目
  • 云计算笔记
  • 基于AI对话生成剧情AVG游戏
  • 数据结构之顺序存储二叉树
  • kubernetes学习-应用程序的生命周期管理
  • 【从零开始入门unity游戏开发之——C#篇11】一个标准 C# 程序介绍、新的值类型——枚举
  • SEO初学者-SEO基础
  • 《云原生安全攻防》-- K8s安全框架:认证、鉴权与准入控制
  • 在JVM(Java虚拟机)中,PC寄存器(Program Counter Register)扮演着至关重要的角色。
  • STM32 IIC协议实现
  • 银行金融项目测试+常问面试题(附答案)
  • XXE-Lab for PHP
  • uniapp scroll-view 不生效排查
  • RT-Thread 的时钟管理
  • 3_使用 HTML5 Canvas API (2) --[HTML5 API 学习之旅]
  • Qt之自定义标题栏拓展(十)
  • Tree-of-Counterfactual Prompting for Zero-Shot Stance Detection
  • spring使用rabbitmq当rabbitmq集群节点挂掉 spring rabbitmq怎么保证高可用,rabbitmq网络怎么重新连接
  • 使用Python打造高效的PDF文件管理应用(合并以及分割)
  • Spring Boot 集成 Elasticsearch怎样在不启动es的情况下正常启动服务
  • 【21天学习AI底层概念】day5 机器学习的三大类型不能解决哪些问题?