Python在游戏中的热更新实现

作者:香菜聊游戏 时间:2022-04-05 14:10:15 

目录
  • 介绍:

  • 原理:

    • 1.标准import

    • 2.reload函数

  • 实现:

    • 总结:

      介绍:

      热更新,就是在服务器不重启的的情况下,对游戏增加新的功能或者修复出现bug 的代码。游戏更新迭代速度快,催生了热更技术的需求,在我经历过的游戏项目中,无论是服务端还是客户端,版本的更新都是围绕着热更新,特别是现在游戏动辄几个G,每次让玩家下载完整的包不现实,随意游戏必须要支持热更。下面来谈一下客户端Python热更新的处理。

      原理:

      1.标准import

      都知道Python提供了import可以导入一个标准的python模块,将模块载入内存,并加到sys.modules中。但是多次import同一模块只是将名称导入到当前的Local名字空间,也就是一个模块不会重复载入,所以想要热更靠这个特性是不行的。此路不通,请换个思路。

      2.reload函数

      reload()函数可以重新载入已经导入的模块,这样似乎就可以热更新Python的代码了。但是python原生的reload函数太过简单,不足以支撑游戏的热更新需求,主要原因有几个: reload重新加载的模块不会替换旧版本的模块,也就是已经引用的旧模块无法更新 同样因为不能旧对象的引用,使用from ... import ... 方式引用的模块同样不能更新 reloas(m)后,class及其派生class的实例对象,仍然使用旧的class定义。 同时加载模块失败时候,没有回滚机制,导致需要重新import该模块 因此,结合游戏的热更新需求,自定义合适的reload。新的自定义reload目的是为了达到在原程序不结束的情况下,让程序能动态加载改动后的代码。主要想达到下面两点: 提升开发效率 在游戏不重启的情况下修复紧急BUG

      实现:

      热更新最核心的需求就是让python解释器执行最新的代码,同时保证其他关联模块不会出现问题。对于刷新function,class内定义的method比较容易实现,但对于刷新module内定义的变量,class内定义的变量,还有新增加的成员变量,则需要有统一的约定。所以在实现热更新过程中,我们需要考虑好代码更新和数据更新这两点,下面罗列一下新的reload具备哪些特性:

      1.更新代码定义(function/method/static_method/class_method) 不更新数据(除了代码定义外的类型都当作是数据) 在module中约定reload_module接口,class中约定reload_class接口,在这两个接口中手动处理数据的更新,还有更多的约定和接口待完成 替换函数对象的内容


      # 用新的函数对象内容更新旧的函数对象中的内容,保持函数对象本身地址不变  
      def update_function(oldobj, newobj, depth=0):  
         setattr(oldobj, "func_code", newobj.func_code)  
         setattr(oldobj, "func_defaults", newobj.func_defaults)  
         setattr(oldobj, "func_doc", newobj.func_doc)

      2.替换类的内容


      # 用新类内容更新旧类内容,保持旧类本身地址不变  
      def _update_new_style_class(oldobj, newobj, depth):  
         handlers = get_valid_handlers()  
         for k, v in newobj.__dict__.iteritems():  
             # 如果新的key不在旧的class中,添加之  
             if k not in oldobj.__dict__:  
                 setattr(oldobj, k, v)  
                 _log("[A] %s : %s"%(k, _S(v)), depth)  
                 continue  
             oldv = oldobj.__dict__[k]  

      # 如果key对象类型在新旧class间不同,那留用旧class的对象  
             if type(oldv) != type(v):  
                 _log("[RD] %s : %s"%(k, _S(oldv)), depth)  
                 continue  

      # 更新当前支持更新的对象  
             v_type = type(v)  
             handler = handlers.get(v_type)  
             if handler:  
                 _log("[U] %s : %s"%(k, _S(v)), depth)  
                 handler(oldv, v, depth + 1)  
                 # 由于是直接改oldv的内容,所以不用再setattr了。  
             else:  
                 _log("[RC] %s : %s : %s"%(k, type(oldv), _S(oldv)), depth)  

      # 调用约定的reload_class接口,处理类变量的替换逻辑  
         object_list = gc.get_referrers(oldobj)  
         for obj in object_list:  
             # 只有类型相同的才是类的实例对象  
             if obj.__class__.__name__ != oldobj.__name__:  
                 continue  
             if hasattr(obj, "x_reload_class"):  
                 obj.x_reload_class()

      3.staticmethod


      def _update_staticmethod(oldobj, newobj, depth):  
         # 一个staticmethod对象,它的 sm.__get__(object)便是那个function对象  
         oldfunc = oldobj.__get__(object)  
         newfunc = newobj.__get__(object)  
         update_function(oldfunc, newfunc, depth)

      4.classmethod


      def _update_classmethod(oldobj, newobj, depth):  
         oldfunc = oldobj.__get__(object).im_func  
         newfunc = newobj.__get__(object).im_func  
         update_function(oldfunc, newfunc, depth)

      模块的更新也是相类似,就不一一粘贴了,只是在原来的reload基础上进行改良,对于模块热更新,还约定了一个reload_module接口,可以自定义数据的更新。 下面添加一些用例:


      def x_reload_class(self):  
         """ 热更新后,每个重新对象的实例都会执行这个函数
         由于新老对象的替换不会重新调用构造函数,因此有必要对热更新的类对象执行初始化逻辑
         处理新老变量的修复,函数执行环境的修复
         """  
         self._new_var = 5000    # 新变量的初始化  
         self.runLogic()      # 新修复的逻辑

      总结:

      只是在基础的reload模块上做了一些定制,让热更新更适合游戏的开发节奏,而不是简单暴力的reload模块

      来源:https://juejin.cn/post/7022904502197944328

      标签:Python,热更新
      0
      投稿

      猜你喜欢

    • asp无限级分类加js收缩伸展功能代码

      2009-12-08 12:25:00
    • 如何更优雅地写python代码

      2022-03-03 04:53:24
    • Python 使用Opencv实现目标检测与识别的示例代码

      2023-01-23 17:46:11
    • Javascript的匿名函数

      2008-05-07 13:33:00
    • SQL注入防御:用三种策略应对SQL注入攻击

      2008-12-15 16:31:00
    • Python列表(list)、字典(dict)、字符串(string)基本操作小结

      2023-03-02 01:33:01
    • 如何从Notes中读取数据?

      2009-11-15 19:57:00
    • Python-jenkins模块获取jobs的执行状态操作

      2022-07-29 14:15:09
    • 最简短的拖动对象js代码实例

      2007-10-09 13:33:00
    • 用python写一个定时提醒程序的实现代码

      2021-04-12 12:25:45
    • 如何批量消除网页超级链接上的“虚线框”

      2008-10-15 12:55:00
    • React开发进阶redux saga使用原理详解

      2023-08-06 09:08:33
    • 浅谈Python浅拷贝、深拷贝及引用机制

      2023-01-03 10:28:23
    • Python远程开发环境部署与调试过程图解

      2023-06-07 08:10:40
    • 如何防止Application对象在多线程访问中出现错误?

      2009-11-22 19:18:00
    • Python Pygame实战之实现经营类游戏梦想小镇代码版

      2021-08-06 19:13:23
    • Python Spyder 调出缩进对齐线的操作

      2023-07-17 00:56:04
    • sqlserver自动增长列引起的问题解决方法

      2011-10-24 19:37:28
    • 用python编写第一个IDA插件的实例

      2022-01-09 13:05:14
    • python配置虚拟环境步骤

      2023-10-22 09:34:51
    • asp之家 网络编程 m.aspxhome.com