Python在游戲中的熱更新實(shí)現(xiàn)
介紹:
熱更新,就是在服務(wù)器不重啟的的情況下,對(duì)游戲增加新的功能或者修復(fù)出現(xiàn)bug 的代碼。游戲更新迭代速度快,催生了熱更技術(shù)的需求,在我經(jīng)歷過的游戲項(xiàng)目中,無論是服務(wù)端還是客戶端,版本的更新都是圍繞著熱更新,特別是現(xiàn)在游戲動(dòng)輒幾個(gè)G,每次讓玩家下載完整的包不現(xiàn)實(shí),隨意游戲必須要支持熱更。下面來談一下客戶端Python熱更新的處理。
原理:
1.標(biāo)準(zhǔn)import
都知道Python提供了import可以導(dǎo)入一個(gè)標(biāo)準(zhǔn)的python模塊,將模塊載入內(nèi)存,并加到sys.modules中。但是多次import同一模塊只是將名稱導(dǎo)入到當(dāng)前的Local名字空間,也就是一個(gè)模塊不會(huì)重復(fù)載入,所以想要熱更靠這個(gè)特性是不行的。此路不通,請(qǐng)換個(gè)思路。
2.reload函數(shù)
reload()函數(shù)可以重新載入已經(jīng)導(dǎo)入的模塊,這樣似乎就可以熱更新Python的代碼了。但是python原生的reload函數(shù)太過簡單,不足以支撐游戲的熱更新需求,主要原因有幾個(gè): reload重新加載的模塊不會(huì)替換舊版本的模塊,也就是已經(jīng)引用的舊模塊無法更新 同樣因?yàn)椴荒芘f對(duì)象的引用,使用from ... import ... 方式引用的模塊同樣不能更新 reloas(m)后,class及其派生class的實(shí)例對(duì)象,仍然使用舊的class定義。 同時(shí)加載模塊失敗時(shí)候,沒有回滾機(jī)制,導(dǎo)致需要重新import該模塊 因此,結(jié)合游戲的熱更新需求,自定義合適的reload。新的自定義reload目的是為了達(dá)到在原程序不結(jié)束的情況下,讓程序能動(dòng)態(tài)加載改動(dòng)后的代碼。主要想達(dá)到下面兩點(diǎn): 提升開發(fā)效率 在游戲不重啟的情況下修復(fù)緊急BUG
實(shí)現(xiàn):
熱更新最核心的需求就是讓python解釋器執(zhí)行最新的代碼,同時(shí)保證其他關(guān)聯(lián)模塊不會(huì)出現(xiàn)問題。對(duì)于刷新function,class內(nèi)定義的method比較容易實(shí)現(xiàn),但對(duì)于刷新module內(nèi)定義的變量,class內(nèi)定義的變量,還有新增加的成員變量,則需要有統(tǒng)一的約定。所以在實(shí)現(xiàn)熱更新過程中,我們需要考慮好代碼更新和數(shù)據(jù)更新這兩點(diǎn),下面羅列一下新的reload具備哪些特性:
1.更新代碼定義(function/method/static_method/class_method) 不更新數(shù)據(jù)(除了代碼定義外的類型都當(dāng)作是數(shù)據(jù)) 在module中約定reload_module接口,class中約定reload_class接口,在這兩個(gè)接口中手動(dòng)處理數(shù)據(jù)的更新,還有更多的約定和接口待完成 替換函數(shù)對(duì)象的內(nèi)容
# 用新的函數(shù)對(duì)象內(nèi)容更新舊的函數(shù)對(duì)象中的內(nèi)容,保持函數(shù)對(duì)象本身地址不變
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.替換類的內(nèi)容
# 用新類內(nèi)容更新舊類內(nèi)容,保持舊類本身地址不變
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對(duì)象類型在新舊class間不同,那留用舊class的對(duì)象
if type(oldv) != type(v):
_log("[RD] %s : %s"%(k, _S(oldv)), depth)
continue
# 更新當(dāng)前支持更新的對(duì)象
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的內(nèi)容,所以不用再setattr了。
else:
_log("[RC] %s : %s : %s"%(k, type(oldv), _S(oldv)), depth)
# 調(diào)用約定的reload_class接口,處理類變量的替換邏輯
object_list = gc.get_referrers(oldobj)
for obj in object_list:
# 只有類型相同的才是類的實(shí)例對(duì)象
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):
# 一個(gè)staticmethod對(duì)象,它的 sm.__get__(object)便是那個(gè)function對(duì)象
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基礎(chǔ)上進(jìn)行改良,對(duì)于模塊熱更新,還約定了一個(gè)reload_module接口,可以自定義數(shù)據(jù)的更新。 下面添加一些用例:
def x_reload_class(self):
""" 熱更新后,每個(gè)重新對(duì)象的實(shí)例都會(huì)執(zhí)行這個(gè)函數(shù)
由于新老對(duì)象的替換不會(huì)重新調(diào)用構(gòu)造函數(shù),因此有必要對(duì)熱更新的類對(duì)象執(zhí)行初始化邏輯
處理新老變量的修復(fù),函數(shù)執(zhí)行環(huán)境的修復(fù)
"""
self._new_var = 5000 # 新變量的初始化
self.runLogic() # 新修復(fù)的邏輯
總結(jié):
只是在基礎(chǔ)的reload模塊上做了一些定制,讓熱更新更適合游戲的開發(fā)節(jié)奏,而不是簡單暴力的reload模塊
到此這篇關(guān)于Python在游戲中的熱更新實(shí)現(xiàn)的文章就介紹到這了,更多相關(guān)Python 熱更新內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
Python中關(guān)于面向?qū)ο蟾拍畹脑敿?xì)講解
要了解面向?qū)ο笪覀兛隙ㄐ枰戎缹?duì)象到底是什么玩意兒。關(guān)于對(duì)象的理解很簡單,在我們的身邊,每一種事物的存在都是一種對(duì)象??偨Y(jié)為一句話也就是:對(duì)象就是事物存在的實(shí)體2021-10-10
python3.x 將byte轉(zhuǎn)成字符串的方法
今天小編就為大家分享一篇python3.x 將byte轉(zhuǎn)成字符串的方法,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。一起跟隨小編過來看看吧2018-07-07
圖解python全局變量與局部變量相關(guān)知識(shí)
這篇文章主要介紹了圖解python全局變量與局部變量相關(guān)知識(shí),文中通過示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友可以參考下2019-11-11
Python實(shí)現(xiàn)向列表或數(shù)組添加元素
Python中的列表是一種動(dòng)態(tài)數(shù)組,可以存儲(chǔ)不同數(shù)據(jù)類型的元素,并提供多種方法進(jìn)行元素的添加和刪除,列表是Python中非常靈活和強(qiáng)大的數(shù)據(jù)結(jié)構(gòu),可以通過索引訪問、修改和操作列表中的元素,列表的創(chuàng)建十分簡單,只需使用方括號(hào)括起元素,并用逗號(hào)分隔2024-09-09
Python轉(zhuǎn)換itertools.chain對(duì)象為數(shù)組的方法
這篇文章主要介紹了Python轉(zhuǎn)換itertools.chain對(duì)象為數(shù)組的方法,通過代碼給大家介紹了itertools 的 chain() 方法,需要的朋友可以參考下2020-02-02
VS Code配置Anaconda Python環(huán)境的詳細(xì)教程
在 Visual Studio Code (VS Code) 中可以使用 Anaconda 環(huán)境進(jìn)行 Python 開發(fā),可以充分利用 Anaconda 提供的包管理和虛擬環(huán)境功能,同時(shí)享受 VS Code 提供的強(qiáng)大開發(fā)工具和調(diào)試功能,本文主要介紹了VS Code配置Anaconda Python環(huán)境的詳細(xì)教程,需要的朋友可以參考下2024-09-09
PyTorch一小時(shí)掌握之a(chǎn)utograd機(jī)制篇
這篇文章主要介紹了PyTorch一小時(shí)掌握之a(chǎn)utograd機(jī)制篇,本文給大家介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友可以參考下2021-09-09
如何在keras中添加自己的優(yōu)化器(如adam等)
這篇文章主要介紹了在keras中實(shí)現(xiàn)添加自己的優(yōu)化器(如adam等)方式,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。一起跟隨小編過來看看吧2020-06-06

