Jump to content

Recommended Posts

Posted
16 minutes ago, user854221 said:

I built my own decompiler, turn lua into python. Their Lua have "List", "Dict", "Class", "Inheritance" feature

I have a list of locale id and text in lua, but i don't know how to repack into game

  • Members
Posted
18 minutes ago, unforced said:

I have a list of locale id and text in lua, but i don't know how to repack into game

There are uncompiled lua scripts stored in LocalDB/db_mp sqlite db file, you can try inject lua script which have your text to the game.

Posted
2 hours ago, user854221 said:

Có các đoạn mã Lua chưa được biên dịch được lưu trữ trong tệp cơ sở dữ liệu SQLite LocalDB/db_mp, bạn có thể thử chèn đoạn mã Lua chứa văn bản của mình vào trò chơi.

I tried, but db_mp keep refreshing from the server

Posted
6 hours ago, user854221 said:

我编写了自己的反编译器,可以将 Lua 代码转换为 Python 代码。他们的 Lua 具有“列表”、“字典”、“类”和“继承”特性。

My goal is to decompile all the Lua scripts of the game and fix its custom opcodes, so that I can use standard Lua decompilers to obtain the full Lua source code. Is this task difficult? I'd love to discuss it with you. I have decompilation experience with another NetEase game that uses a custom Python engine (Neox) and I’ve already managed to restore the Python code. However, I have no knowledge of this Lua engine.

Posted
On 2025/12/2 at PM2点49分, wq223 said:

他们使用了修改过的操作码,因此无法进行常规的反编译,但可以进行反汇编,我目前正在对 luac 反编译进行逆向工程。

好的,我可以反编译你提供的两个文件,但是应该会有错误,因为我还没有完成操作码修改工作。

一起交流一下?我也在尝试反编译自定义的字节码。对lua不熟。

Posted
15 hours ago, user854221 said:

I built my own decompiler, turn lua into python. Their Lua have "List", "Dict", "Class", "Inheritance" feature
我自己做了反编译器,把 Lua 变成了 Python。他们的 Lua 有“列表”、“指令”、“类别”、“继承”功能

Can you share it? thks

Posted (edited)

Does anyone know why some of the text that has been translated in the translated_map still displays in Chinese in the game?

Edited by unokiller
  • Members
Posted
4 hours ago, unokiller said:

Does anyone know why some of the text that has been translated in the translated_map still displays in Chinese in the game?

Text id not exist in the map, you can try remove the words_map_zh_cn and _diff file to let the game reveal the id. After got the id, add the translation in hotfix file

  • 3 weeks later...
Posted (edited)
On 12/2/2025 at 9:08 PM, und3fy said:

I got lua file but now need decompile byte code of hotfix_manager: `G.hotfix_manager:update_table(table_data)`

this is content of `hotfix_manager.lua` (AI decompiler version), i attach bycode version, can you decompile it?
 

local require = require
local hex_object = require("hexm.client.h")
local HexObject = require("hexm.common.hex_object")
local cjson = require("cjson")
local util = require("hexm.common.util.util")
local TimerManager = require("hexm.client.manager.timer_manager").TimerManager
local DateTimeManager = require("hexm.common.datetime_manager").DateTimeManager
local patch_utils = require("patch.patch_utils")
local cmsgpack = require("cmsgpack")
local patch_config = require("patch.patch_config")

local class = class

local HotfixDataProxy = class("HotfixDataProxy", HexObject)

function HotfixDataProxy:ctor(data_object)
    self.data_object = data_object
    self.n_rows = data_object.n_rows
    self.r_rows = data_object.r_rows
    self.n_values = data_object.n_values
    self.r_values = data_object.r_values
    self.table_data = {}
end

function HotfixDataProxy:remove_row(row_id)
    self.r_rows:append(row_id)
end

function HotfixDataProxy:add_row(row_id, row_data)
    self.n_rows[row_id] = row_data
end

function HotfixDataProxy:remove_value(row_id, col_id, row_data)
    if row_data.get then
        local type_val = row_data:get("type")
        if type_val ~= "dict" then
            G.__G__TRACKBACK__("remove value is error, because the row_data is not dict!!")
        end
    end
    self.r_values:append({row_id, col_id})
end

function HotfixDataProxy:add_value(row_id, col_id, value, row_data)
    if row_data.get then
        local type_val = row_data:get("type")
        if type_val ~= "dict" then
            G.__G__TRACKBACK__("remove value is error, because the row_data is not dict!!")
        end
    end
    self.n_values[{row_id, col_id}] = value
end

function HotfixDataProxy:commit()
    return {
        name = self.data_object:get("__table_name"),
        n_rows = self.n_rows,
        r_rows = self.r_rows,
        n_values = self.n_values,
        r_values = self.r_values,
        datam = "store_hotfix"
    }
end

local HotfixManager = class("HotfixManager", HexObject)

function HotfixManager:ctor()
    self.super.ctor(self)
    self._timer_mgr = TimerManager
    self._fetch_timerid = nil
    self._script_version = tostring(self:get_script_version())
    self._cur_hotfix_idx = 0
    self._hotfix_id_data_list_need_fetch = {}
    self._hotfix_id_data_list = {}
    self._fetch_hotfix_batch_size = 12
    self._exec_list = {}
    self._mark_exec_list = {}
    self._launch_hotfix_done = false
    self._fetch_cb = nil
    self._localdb_data = {}
    self._is_localdb_data_dirty = false
    self._max_delay = 15.0
    
    if self.is_publish and MPatch.FileExistInSystem(LocalData .. "uwsgi_img") then
        -- Logic for loading hotfix data
    end
end

function HotfixManager:get_script_version()
    local data_object = {}
    require("patch.patch_config")
    
    if self.is_publish then
        if MPatch.FileExistInSystem(LocalData .. "hotfix_version.json") then
            local raw_data = MPatch.GetRawDataInSystem(LocalData .. "hotfix_version.json")
            local json_data = cjson_decode(raw_data)
            if json_data.get then
                local script_version = json_data:get("script_version")
                return tostring(script_version)
            end
        end
    end
    
    local patch_md5_file = "patchmd5.txt"
    local data = MPatch.GetRawDataInPatch(patch_md5_file)
    if not data then
        data = MPatch.GetRawDataInPackage(patch_md5_file)
    end
    
    local patch_utils = require("patch.patch_utils")
    local read_decompress_data = patch_utils.read_decompress_data
    local config_dict = cjson
    
    local ret = bool(try_catch_call(function()
        data = read_decompress_data(data)
        config_dict = cjson_decode(data)
    end))
    
    if not ret then
        G.logger:error("hotfix", "get_script_version err")
        return ""
    end
    
    return ""
end

function HotfixManager:add_timer_start_fetch_hotfix()
    if self._fetch_timerid then
        self._fetch_timerid:cancel()
    end
    
    local delay = math.random() * self._max_delay
    G.logger:info("hotfix", "add_timer_start_fetch_hotfix delay %s", delay)
    self._fetch_timerid = self._timer_mgr:add_timer(delay, function()
        self:start_fetch_hotfix()
    end)
end

function HotfixManager:start_fetch_hotfix()
    if self._fetch_timerid then
        self._fetch_timerid:cancel()
        self._fetch_timerid = nil
    end
    
    self._cur_hotfix_idx = 0
    
    G.logger:info("hotfix", "start_fetch_hotfix _cur_hotfix_idx %s", self._cur_hotfix_idx)
    
    if not self:hotfix_enable() then
        self:start_main()
        return
    end
    
    G.logger:info("hotfix", "start_fetch_hotfix %s %s", self._script_version, 0)
    
    self._hotfix_id_data_list_need_fetch = {}
    self._hotfix_id_data_list = {}
    
    G.uwsgi_manager:fetch_hotfix_list(function(guid, result, pack)
        self:_fetch_hotfix_list_callback(guid, result, pack)
    end)
end

function HotfixManager:_fetch_hotfix_list_callback(fetch_cb)
    self._fetch_cb = fetch_cb
end

function HotfixManager:load_local_hotfix_data_and_return()
    local hotfix_data = self._localdb_data:get("version")
    
    if hotfix_data ~= self._script_version then
        local localdb = G.localdb
        local hotfix_meta = localdb:get_global("hotfix_meta")
        local hotfix_id_d = hotfix_meta["hotfix_id_d"]
        
        for hotfix_id, _ in pairs(hotfix_id_d) do
            localdb:remove_global(self:get_hotfix_db_key(hotfix_id))
        end
        
        localdb:set_global(self:get_hotfix_db_key("hotfix_meta"), {})
        localdb:get_global_db_recheck()
        
        self._localdb_data = {}
        self._is_localdb_data_dirty = false
    else
        G.logger:error("hotfix", "load_local_hotfix_data_and_return hotfix_data err %s %s", hotfix_data, self._script_version)
    end
    
    local hotfix_id_2_data = {}
    
    for hotfix_id, hotfix_data in pairs(hotfix_meta["hotfix_id_d"]) do
        hotfix_id_2_data[hotfix_id] = localdb:get_global(self:get_hotfix_db_key(hotfix_id))
    end
    
    return hotfix_id_2_data
end

function HotfixManager:update_local_hotfix_data(hotfix_id, hotfix_data)
    if not self._is_localdb_data_dirty then
        G.logger:info("hotfix", "update_local_hotfix_data %s", hotfix_id)
        
        self:load_local_hotfix_data_and_return()
        self._is_localdb_data_dirty = true
    end
end

function HotfixManager:try_save_local_hotfix_data()
    if not self._is_localdb_data_dirty then
        return
    end
    
    G.logger:info("hotfix", "try_save_local_hotfix_data saved", true)
    
    local version = self._script_version
    local hotfix_meta = {"version": version, "hotfix_id_d": {}}
    
    local hotfix_id_2_data = self:load_local_hotfix_data_and_return()
    
    for hotfix_id, keys in pairs(hotfix_id_2_data) do
        if keys == "HOTFIX_DATA_ALREADY_SAVED" then
            hotfix_meta["hotfix_id_d"][hotfix_id] = self:get_hotfix_db_key(hotfix_id)
        end
    end
    
    local localdb = G.localdb
    for hotfix_id, _ in pairs(hotfix_meta["hotfix_id_d"]) do
        localdb:set_global(self:get_hotfix_db_key(hotfix_id), hotfix_id_2_data[hotfix_id])
    end
    
    localdb:clear_global_cache(1)
    localdb:set_global("hotfix_meta", hotfix_meta)
end

function HotfixManager:ensure_hotfix_data(hotfix_id)
    local hotfix_id_2_data = self:load_local_hotfix_data_and_return()
    local hotfix_data = hotfix_id_2_data:get("HOTFIX_DATA_ALREADY_SAVED")
    
    if not hotfix_data then
        return nil
    end
    
    return self:get_hotfix_db_key(hotfix_id)
end

function HotfixManager:exec_hotfix_id_list(hotfix_id_data_list)
    local now_int = DateTimeManager:now_int()
    
    for _, hotfix_id_data in pairs(hotfix_id_data_list) do
        local hotfix_id = hotfix_id_data:get("hotfix_id")
        
        if not hotfix_id then
            G.logger:error("hotfix", "exec_hotfix_id_list hotfix_id nil %s", hotfix_id_data)
            goto continue
        end
        
        if self._exec_list:contains(hotfix_id) then
            G.logger:info("hotfix", "exec_hotfix_id_list hotfix already exec %s", hotfix_id)
            goto continue
        end
        
        local pc_patch_version = hotfix_id_data["pc_patch_version"]
        local android_patch_version = hotfix_id_data["android_patch_version"]
        local ios_patch_version = hotfix_id_data["ios_patch_version"]
        local pc_low2_patch_version = hotfix_id_data["pc_low2_patch_version"]
        
        local patch_version = self:choose_patch_version(pc_patch_version, android_patch_version, ios_patch_version, pc_low2_patch_version)
        
        if not bool(patch_utils.check_patch_version(patch_version, MPlatform.GetOsName())) then
            G.logger:error("hotfix", "patch version check failed %s %s %s", hotfix_id, patch_version, MPlatform.GetOsName())
            goto continue
        end
        
        self:ensure_hotfix_data(hotfix_id)
        
        local hotfix_data = self:ensure_hotfix_data(hotfix_id)
        
        if not hotfix_data then
            G.logger:error("hotfix", "exec_hotfix_id_list hotfix_data not exist %s", hotfix_id)
            goto continue
        end
        
        G.logger:info("hotfix", "exec_hotfix_id_list exec %s", hotfix_id)
        
        local result = try_catch_call(function()
            util.eval_hotfix(hotfix_data, hotfix_id)
        end)
        
        if not result then
            G.logger:error("hotfix", "exec_hotfix_id_list exec err %s", hotfix_id)
            G.sdk_manager:report_sa_log("Hotfix", {
                state_name = "exec_hotfix_err",
                hotfix_id_data = hotfix_id_data,
                hotfix_data = hotfix_data
            })
        end
        
        self._exec_list:append(hotfix_id)
        
        ::continue::
    end
end

function HotfixManager:start_main()
    G.sdk_manager:report_sa_log("Hotfix", {state_name = "start_main"})
    
    if type(self._hotfix_id_data_list) ~= "list" then
        self:exec_hotfix_id_list(self._hotfix_id_data_list)
    end
    
    self._cur_hotfix_idx = 120
    self._hotfix_id_data_list_need_fetch = false
    
    self:try_save_local_hotfix_data()
    
    self._launch_hotfix_done = true
    
    if self._fetch_cb then
        self._fetch_cb()
        self._fetch_cb = nil
    end
    
    G.logger:info("hotfix", "start_main end")
    G.net:get_avatar().dispatcher:dispatch("E_PHOTO_TAKE_PHOTO", {})
end

function HotfixManager:choose_patch_version(pc_patch_version, android_patch_version, ios_patch_version, pc_low2_patch_version)
    if patch_utils.is_android() then
        return android_patch_version
    elseif patch_utils.is_ios() then
        return ios_patch_version
    elseif patch_utils.is_low2() then
        return pc_low2_patch_version
    end
    
    return pc_patch_version
end

function HotfixManager:_fetch_hotfix_list_callback(e_c, data)
    G.sdk_manager:report_sa_log("Hotfix", {
        state_name = "fetch_hotfix_list_callback"
    })
    
    if type(data) ~= "dict" then
        G.logger:error("hotfix", "_fetch_hotfix_list_callback %s %s", e_c, data)
        self:add_timer_start_fetch_hotfix()
        self:start_main()
        return
    end
    
    local hotfix_list = data:get("hotfix_list")
    
    for _, item in pairs(hotfix_list) do
        local result = try_catch_call(function()
            return cmsgpack.unpack(item.raw_data)
        end)
        
        if not result then
            G.logger:error("hotfix", "raw_data unpack err %s", item)
        end
    end
    
    G.logger:info("hotfix", "_fetch_hotfix_list_callback len %s", #hotfix_list)
    
    local hotfix_id_2_data = self:load_local_hotfix_data_and_return()
    
    for hotfix_id_data in hotfix_list do
        local hotfix_id = hotfix_id_data.hotfix_id
        
        if not hotfix_id then
            G.logger:error("hotfix", "_fetch_hotfix_list_callback hotfix_id nil %s", hotfix_id_data)
            goto continue
        end
        
        if self._exec_list:contains(hotfix_id) then
            G.logger:info("hotfix", "_fetch_hotfix_list_callback already exec %s", hotfix_id)
            goto continue
        end
        
        if bool(hotfix_id_2_data[hotfix_id]) then
            G.logger:info("hotfix", "_fetch_hotfix_list_callback has local data %s", hotfix_id)
        else
            G.logger:info("hotfix", "_fetch_hotfix_list_callback no local data %s", hotfix_id)
            self._hotfix_id_data_list:append(hotfix_id_data)
        end
        
        ::continue::
    end
    
    self._cur_hotfix_idx = 0
    self._hotfix_id_data_list_need_fetch = true
    self._hotfix_id_data_list = need_fetch_hotfix_id_data_list
    
    self:fetch_next_hotfix_batch()
end

function HotfixManager:fetch_next_hotfix_batch()
    if not self._hotfix_id_data_list_need_fetch then
        self:start_main()
        return
    end
    
    if self._fetch_cb then
        return
    end
    
    self._fetch_cb = try_catch_call(function()
        return self:_fetch_hotfix_batch_callback()
    end)
    
    self._cur_hotfix_idx = self._cur_hotfix_idx + 1
    
    if not self._hotfix_id_data_list_need_fetch then
        return
    end
    
    local hotfix_id_data_list = {}
    
    for i = self._cur_hotfix_idx, math.min(self._cur_hotfix_idx + self._fetch_hotfix_batch_size - 1, #self._hotfix_id_data_list) do
        local hotfix_id_data = self._hotfix_id_data_list:get(i)
        
        if hotfix_id_data then
            local hotfix_id = hotfix_id_data.hotfix_id
            local hotfix_data_len = hotfix_id_data["hotfix_data_len"]
            local pc_patch_version = hotfix_id_data["pc_patch_version"]
            local android_patch_version = hotfix_id_data["android_patch_version"]
            local ios_patch_version = hotfix_id_data["ios_patch_version"]
            local pc_low2_patch_version = hotfix_id_data["pc_low2_patch_version"]
            
            hotfix_id_data_list:append({
                hotfix_id = hotfix_id,
                hotfix_data_len = hotfix_data_len,
                pc_patch_version = pc_patch_version,
                android_patch_version = android_patch_version,
                ios_patch_version = ios_patch_version,
                pc_low2_patch_version = pc_low2_patch_version
            })
        end
    end
    
    G.uwsgi_manager:fetch_hotfix_batch(hotfix_id_data_list, function(guid, result, pack)
        self:_fetch_hotfix_batch_callback(guid, result, pack)
    end)
end

function HotfixManager:_fetch_hotfix_batch_callback(e_c, data)
    G.sdk_manager:report_sa_log("Hotfix", {
        state_name = "fetch_hotfix_batch_callback"
    })
    
    if type(data) ~= "dict" then
        G.logger:error("hotfix", "_fetch_hotfix_batch_callback %s %s", e_c, data)
        self:add_timer_start_fetch_hotfix()
        self:start_main()
        return
    end
    
    for _, item in pairs(data:get("hotfix_data_list")) do
        local hotfix_id = item.hotfix_id
        
        if bool(self:update_local_hotfix_data(hotfix_id, item.hotfix_data)) then
            G.logger:info("hotfix", "_fetch_hotfix_batch_callback hotfix_id(%s) hotfix_data(%s)", hotfix_id, item.hotfix_data)
        end
        
        G.logger:info("hotfix", "_fetch_hotfix_batch_callback item %s", item)
    end
    
    self:fetch_next_hotfix_batch()
end

function HotfixManager:update_table(diff_dict)
    G.datam:store_hotfix(diff_dict)
end

function HotfixManager:hotfixServerEntityComponent(cls, component)
    assert(cls.__components__)
    
    if not cls.__components__:contains(component) then
        cls.__components__:append(component)
    end
    
    if not cls.__component_inits__:contains(component.init) then
        cls.__component_inits__:append(component.init)
        cls.__init_component__ = component.init
    end
    
    if not cls.__component_posts__:contains(component.post) then
        cls.__component_posts__:append(component.post)
        cls.__post_component__ = component.post
    end
    
    if not cls.__component_ticks__:contains(component.tick) then
        cls.__component_ticks__:append(component.tick)
        cls.__tick_component__ = component.tick
    end
    
    if not cls.__component_finis__:contains(component.fini) then
        cls.__component_finis__:append(component.fini)
        cls.__fini_component__ = component.fini
    end
end

function HotfixManager:hotfixClientEntityTwoStageComponent(cls, component, func_name)
    local old_func = cls[func_name]
    local __cls_two_stage_component_func_dict__ = cls.__cls_two_stage_component_func_dict__ or {}
    
    for component, func_map in ipairs(__cls_two_stage_component_func_dict__) do
        for funcname, func in pairs(func_map) do
            if funcname == func_name then
                old_func = func.component_old
            end
        end
    end
end

function HotfixManager:hotfixClientEntityComponent(cls, component, func_name)
    local old_func = cls[func_name]
    local __component_func_dict__ = cls.__component_func_dict__ or {}
    
    for component, func_map in ipairs(__component_func_dict__) do
        for funcname, func in pairs(func_map) do
            if funcname == func_name then
                old_func = func.component_old
            end
        end
    end
    
    if not found and component == "hotfixClientEntityTwoStageComponent" then
        table.insert(__component_func_dict__, {component = component, funcname = func_name, func = {component_old = old_func}})
    end
end

function HotfixManager:generate_data_table_proxy(data_object)
    return HotfixDataProxy(data_object)
end

function HotfixManager:commit_new_data_table(table_name, final_dict, hotfix_data)
    if final_dict.get then
        local type_val = final_dict:get("type")
        if type_val ~= "dict" then
            G.__G__TRACKBACK__("commit_new_data_table is error, because the final_dict is not dict!!")
        end
    end
    
    G.datam:store_hotfix({
        name = table_name,
        datam = "store_hotfix"
    })
end

function HotfixManager:test_run_hotfix_script()
    local script = [[
local PlayerAvatarMember = require("hexm.client.entities.local.player_avatar_members.photo.impl_photo_data").PlayerAvatarMember
local event_consts = require("hexm.client.consts.event_consts")
local photo_consts = require("hexm.client.consts.photo_consts")
local setting_config = require("hexm.client.manager.setting.setting_config")
local render_option_consts = require("hexm.client.consts.render_option_consts")

function PlayerAvatarMember:photo_take_photo()
	if self._photo_shot_waiting_id ~= nil then
		return
	end
	
	local hd_ratio, need_hd_ratio_delay = self:_push_pc_hd_screen_shot()

	local hd_screen_shot_ratio = G.setting_manager:get_setting(setting_config.SETTING_HD_SCREEN_SHOT_RATIO)
    if hd_screen_shot_ratio > 0 then
        G.platform_manager:set_screen_shot_jpg_enable(false)
    else
	    G.platform_manager:set_screen_shot_jpg_enable(true)
    end
    
	local grey_enable = self:_photo_get_photo_grey_info("enable_freeze_frame_after_screenshot")
	if grey_enable then
		if G.engine:IsWindows() and MRender.HasGlobalOption and MRender.HasGlobalOption("EnableFreezeFrameAfterScreenShot")then
			G.engine_layer:push_global_option("EnableFreezeFrameAfterScreenShot", true, "photo_shot", render_option_consts.RENDER_OPTION_PRIORITY_UI)
		end
	end
	
	G.sound_manager:play_sound(1500185, false)
	G.camera:push_lock_yaw_flag("photo_shot")
	G.camera:push_lock_pitch_flag("photo_shot")
	if G.ui_manager.watermark_window then
		G.ui_manager.watermark_window:set_visible(false, "photo")
	end
	G.ui_manager:set_all_windows_visible(false, "photo", list("PhotoPluginWindow"))
	G.input_manager:push_key_down_enable("photo", false)
	local win = G.ui_manager:get_window_by_name("PhotoPluginWindow")
	if win and not win:is_destroyed() then
		win.controller:leave_photo_logo_mode()
		win.view:get_node("btn_point"):set_visible(false)
	end
	
	if need_hd_ratio_delay then
		local hd_screen_shot_delay = bool(G.gm_hd_screen_shot_delay) and G.gm_hd_screen_shot_delay or G.datam.photo_default_config[1]:get("hd_screen_shot_delay", 5)
		self:add_named_none_block_timer(hd_screen_shot_delay, function()
			self:photo_take_photo_screen_shot(hd_ratio)
		end, "photo_screen_shot_delay_by_hd_shot")
	else
		self:photo_take_photo_screen_shot(hd_ratio)
	end
end

function PlayerAvatarMember:photo_take_photo_screen_shot(hd_ratio)
	local callback = function(guid, result, pack) self:_photo_shot_callback(guid, result, pack, nil, hd_ratio) end
	self._photo_shot_waiting_id = G.platform_manager:screen_shot2_as_scene_rt(true, true, callback)
	
	local ensure_water_func = function()
		if grey_enable then
			if G.engine:IsWindows() and MRender.HasGlobalOption and MRender.HasGlobalOption("EnableFreezeFrameAfterScreenShot")then
				G.engine_layer:pop_global_option("EnableFreezeFrameAfterScreenShot", "photo_shot")
			end
		end
		G.ui_manager:set_all_windows_visible(true, "photo")
		if G.ui_manager.watermark_window then
			G.ui_manager.watermark_window:set_visible(true, "photo")
		end

		self:_pop_pc_hd_screen_shot()
		G.camera:pop_lock_yaw_flag("photo_shot")
		G.camera:pop_lock_pitch_flag("photo_shot")
		G.input_manager:pop_key_down_enable("photo")
		G.platform_manager:set_screen_shot_jpg_enable(false)
	end
	local ensure_time = G.engine:IsAndroid() and 10 or 5
	self:add_named_none_block_timer(ensure_time, ensure_water_func, self._photo_shot_waiting_id)
    
	self:_photo_check_achievement()
    
	local photo_setting_config = require("hexm.client.consts.photo_setting_config")
	local template_ids = self:photo_get_setting_value(photo_setting_config.PHOTO_SETTING_SIMPLE_TEMPLATE)
	if template_ids and template_ids[1] and template_ids[1] > 1 then
		G.sdk_manager:report_sa_log("take_photo", dict(template_id = template_ids[1]))
	end

	G.net:get_avatar().dispatcher:dispatch(event_consts.E_PHOTO_TAKE_PHOTO, dict())
end

function PlayerAvatarMember:_push_pc_hd_screen_shot()
	G.space:group_photo_check_all_ghost_visible(false, "photo_shot")
	if not photo_consts:is_hd_screen_shot_valid() then
		return
	end
    
	local hd_screen_shot_ratio = G.setting_manager:get_setting(setting_config.SETTING_HD_SCREEN_SHOT_RATIO)
	if not (hd_screen_shot_ratio > 0) then
		return
	end
    
	if G.engine:IsMobilePlatform() and not G.main_player:mode_change_is_single_mode()  then
		return
	end
	
	local need_hd_ratio_delay = false

	if G.engine:IsMobilePlatform() then
		if G.engine:IsIOS() or G.engine_layer:get_is_incompatible_device_for_mobile_hd_screenshot() then
			G.engine_layer:push_render_option("Sharpening", 0.5, "camera", render_option_consts.RENDER_OPTION_PRIORITY_GAMEPLAY)
			local level = G.engine_layer:get_performance_info()
			local performance_config = require("patch.performance_config")
			if level >= performance_config.IOS_NAME_TO_LEVEL["high"] then
				G.engine_layer:push_render_option("ScreenSize", 2200, "camera", render_option_consts.RENDER_OPTION_PRIORITY_GAMEPLAY)
				need_hd_ratio_delay = true
			end
		elseif G.engine:IsAndroid() then
			G.main_player:push_game_speed_config(84, "camera")
			G.engine_layer:push_render_option("EnableTSAA", "false", "camera", render_option_consts.RENDER_OPTION_PRIORITY_GAMEPLAY)
		end
	elseif G.engine:IsWindows() then
		G.engine_layer:init_sr_options("camera",render_option_consts.RENDER_OPTION_PRIORITY_GAMEPLAY)  
		G.engine_layer:push_render_option("ScreenSize", 0, "camera", render_option_consts.RENDER_OPTION_PRIORITY_GAMEPLAY)
		need_hd_ratio_delay = true
		G.engine_layer:push_render_option("ScreenScale", hd_screen_shot_ratio, "camera", render_option_consts.RENDER_OPTION_PRIORITY_GAMEPLAY)
		G.engine_layer:push_render_option("SeparateMarchingShadowScale", 1 / hd_screen_shot_ratio, "camera", render_option_consts.RENDER_OPTION_PRIORITY_GAMEPLAY)
        
		local grey_enable = self:_photo_get_photo_grey_info("enable_ui_before_screen_shot")
		if grey_enable then
			if MRender.HasGlobalOption and MRender.HasGlobalOption("EnableUIBeforeScreenShot")then
				G.engine_layer:push_global_option("EnableUIBeforeScreenShot", true, "photo_shot", render_option_consts.RENDER_OPTION_PRIORITY_UI)
			end
		end
	end

	return hd_screen_shot_ratio, need_hd_ratio_delay
end

local PlayerAvatar = require("hexm.client.entities.local.player_avatar").PlayerAvatar
PlayerAvatar.photo_take_photo = PlayerAvatarMember.photo_take_photo
PlayerAvatar.photo_take_photo_screen_shot = PlayerAvatarMember.photo_take_photo_screen_shot
PlayerAvatar._push_pc_hd_screen_shot = PlayerAvatarMember._push_pc_hd_screen_shot
	]]
    util.eval_hotfix(script)
end

function HotfixManager:test_hotfix_data()
    local hotfix_data = dict (
		['name'] = "arrows",
		['n_rows'] = dict (
			[1] = dict (
				['arrow_id'] = 1,
				['arrow_model'] = 1,
			),
			[2] = dict (
				['arrow_id'] = 2,
				['arrow_model'] = 2,
			)
		),
		['r_rows'] = list (102025, 102026),
		['r_values'] = dict (
			[102027] = list ("line_png", "arrow_model"),
			[102028] = list ("line_png", "arrow_model", "arrow_name")
		),
		['n_values'] = dict (
			[102027] = dict (
				['test1'] = 1,
				['test2'] = 2,
			),
			[102028] = dict (
				['test3'] = 3,
				['test4'] = 4,
			)
		)
	)
	G.datam:store_hotfix(hotfix_data)
end

function HotfixManager:test_str_hotfix_data(diff_data)
    local ts = DateTimeManager.format_datetime(G.uwsgi_manager:test_add_hotfix_data(self._script_version .. "_fix_" .. tostring(ts)))
end

function HotfixManager:test_add_hotfix_data()
    local diff_factions = {['final_dict'] = {[1] = {[1] = 3, [2] = 1, [3] = 1, [4] = 3, [5] = 1}, [2] = {[1] = 1, [2] = 3, [3] = 1, [4] = 3, [5] = 1}, [3] = {[1] = 1, [2] = 1, [3] = 3, [4] = 2, [5] = 1}, [4] = {[1] = 3, [2] = 3, [3] = 2, [4] = 3, [5] = 1}, [5] = {[1] = 1, [2] = 1, [3] = 1, [4] = 1, [5] = 1}}, ['name'] = 'factions'}
    G.hotfix_manager.update_table(diff_factions)
    
    local ts = DateTimeManager.format_datetime(G.uwsgi_manager:test_add_hotfix_data(self._script_version .. "_fix_" .. tostring(ts)))
end

function HotfixManager:test_add_hotfix_table()
end

function HotfixManager:test_run_hotfix_lua_file(file_full_path)
    local util = require("hexm.common.util.util")
    local io = io
    local f1 = io.open(file_full_path, "r")
    util.eval_hotfix(f1:read("a"))
end

function HotfixManager:test_run_hotfix_data_proxy()
    local entity = G.datam.entity:generate_data_table_proxy()
    entity:add_row(102025, dict(
        ai_id = 1234567890,
        appear_show_type = 1,
        attr_id = 22356,
        base_tag = 21600,
        billboard_id = 2,
        combat_id = 2,
        destroy_show_type = 6,
        entity_faction = 0,
        entity_flag = 0,
        entity_sub_type = 0,
        entity_type = 0,
        id = 0,
        interact_config = 0,
        interact_id = 0,
        is_utility_ai = false,
        lod_max = 4,
        lod_min = 0,
        model_id = 12456,
        mould_id = 0,
        value_id = 0
    ))
    entity:remove_row(102026)
    entity:add_value(102025, "attr_id1", 1)
    entity:remove_value(102026, "attr_id1")
    
    local final_dict = entity:commit()
    
    assert(final_dict:contains("name"))
    
    G.hotfix_manager:commit_new_data_table(final_dict:get("name"), final_dict)
    
    print("test_wjc")
end

function HotfixManager:get_sorted_hotfix_exec_list()
    local hotfix_ids = self._exec_list:keys()
    table.sort(hotfix_ids)
    print(hotfix_ids)
end

function HotfixManager:check_hotfix_exec(hotfix_id)
    return self._exec_list[hotfix_id]
end

function HotfixManager:clear_local_cache()
    for _, hotfix_id in pairs(self._mark_exec_list) do
        local code = require("md5")
        if MPatch.FileExistInSystem(LocalData .. "uwsgi_img") then
            local format_dt = DateTimeManager.format_datetime(G.uwsgi_manager:test_add_hotfix_data(self._script_version .. "_fix_" .. tostring(format_dt)))
            
            local md5 = code:sumhex(string.sub(format_dt, 1, 8))
            
            if not self._exec_list:contains(hotfix_id) then
                G.logger:error("gm", "检查hotfix执行情况, 没有拉取到hotfix. hotfix_id=%s. ERROR!! ERROR!!", hotfix_id)
            else
                local has_exec = bool(self:check_hotfix_exec(hotfix_id))
                
                if not has_exec then
                    G.logger:error("gm", "检查hotfix执行情况, 没有成功执行hotfix. hotfix_id=%s. ERROR!! ERROR!!", hotfix_id)
                end
            end
        end
    end
end

return {
    HotfixManager = HotfixManager,
    HotfixDataProxy = HotfixDataProxy
}

 

hotfix_manager.luac.zip 11.21 kB · 33 downloads

@wq223 Hi! The format_hotfix_data.py attachment seems unavailable now. Could you kindly re-share it (or via Drive)? I need it to handle hotfix data decompression for text modding. Thanks a lot!

Edited by minhthanh
Posted

I joined a bit late. I’m currently struggling through the encoding chaos, trying to extract the .lua files. I can see that there are some scripts, but I can’t access them at the moment. Could someone provide a Python script to extract Lua files from an MKP archive? I’d really appreciate it.

  • 4 weeks later...
Posted

@wq223 Sorry for ping, but currently i has significant progress in restoring Lua code. How can connect to you?

the image from my decompiler

Screenshot2026-02-13at11_05_14.thumb.png.db0ccde08696ab654828564a3cb51eea.png

  • Like 1
  • Supporter
Posted
7 hours ago, und3fy said:

@wq223 Sorry for ping, but currently i has significant progress in restoring Lua code. How can connect to you?

the image from my decompiler

Screenshot2026-02-13at11_05_14.thumb.png.db0ccde08696ab654828564a3cb51eea.png

You have done a great job. I am not currently researching related content. You should also keep working hard. Come on my friend.

  • 3 months later...
Posted

Hey guys, not sure if this is related but I'm looking for some wwm API for pvp score. What method would you suggest to do that?

Create an account or sign in to comment

You need to be a member in order to leave a comment

Create an account

Sign up for a new account in our community. It's easy!

Register a new account

Sign in

Already have an account? Sign in here.

Sign In Now
×
×
  • Create New...