【UE】pak的mount(带源码解析)

news2024/12/26 22:08:09

本文使用的引擎版本为UE4.27
为了方便理解,文中选取的代码均为部分截取,只截取与小节相关的部分

文章目录

  • 概述
  • 几个涉及到的结构
  • Mount时机
  • pak读取优先级
    • 目录优先级
    • 根据文件名定优先级
    • 综上所述

概述

正常的散文件加载是使用FFileHelper::LoadFileToArray等接口来读取文件内容。但pak作为一个类似于压缩包的格式,其中的文件无法直接使用这种方式读取。故需要使用mount来挂载。

mount操作告诉系统有哪些文件可以从pak中读到,并提供虚拟路径使系统可以通过FPakPlatformFile::CopyFileFFileHelper::LoadFileToString等操作普通文件的方法操作pak中的文件

几个涉及到的结构

IPlatformFile:文件IO的接口,是整个文件系统的基类。该类及其子类以链式组织,每个实例存有其下层的引用,每层访问的时候都先查找自身,找不到才向下层查找。

FPakPlatformFile:继承于IPlatformFile,负责pak的mount。它的私有成员ExcludedNonPakExtensions记录了几个只应存在pak里的文件拓展:uasset、umap、ubulk、uexp、uptnl和ushaderbytecode。这些文件如果在pak里没有找到,就不再查找它的下层了。

FPakListEntry:定义在FPakPlatformFile内的结构体,有ReadOrder和PakFile两个属性。FPakPlatformFile中用几个TArray来记录mount到的pak

FPakFile:Pak在C++中存储的形式。有PakFilename、LastUseTime、Info等成员
FPakInfo:Pak的提纲信息,存有版本号、哈希值、魔数、是否加密等
FPakEntry:Pak中单个文件的信息,存储文件大小、在pak中的偏移量、是否加密等

Mount时机

很多地方都会调到FPakPlatformFile::Mount进行pak的Mount,这里只分析引擎启动时进行的mount

引擎启动时进行的初始化mount位于 FPakPlatformFile::Initialize。如果使用命令行指定了PakFile单例,会在引擎PreInit的阶段通过ConditionallyCreateFileWrapper函数创建单例并调用 FPakPlatformFile::Initialize。如果没有在命令行中指定,则该初始化函数在EditorInit阶段才进行。后者的调用堆栈如下:
在这里插入图片描述

pak读取优先级

概述中提到,mount的作用是为了提供一种读取pak文件的方法,使pak中的文件也能使用操作散文件的操作方法。即:mount并不是读取pak中的文件。故mount顺序其实对pak的读取优先级没有影响

那么pak读取的优先级到底取决于什么呢? 做热更需求的时候如何保证自己新添加的pak可以覆盖掉原来pak中的资源?答案是取决于FPakListEntry的ReadOrder:ReadOrder大的pak会覆盖小的pak的资源内容

// IPlatformFilePak.h
struct FPakListEntry
{
	FPakListEntry(): ReadOrder(0), PakFile(nullptr){}

	uint32		ReadOrder;
	TRefCountPtr<FPakFile>	PakFile;
	
	// ReadOrder越大,优先级越高。故热更的时候只需要保证新pak的ReadOrder大于原来的pak即可
	FORCEINLINE bool operator < (const FPakListEntry& RHS) const
	{
		return ReadOrder > RHS.ReadOrder;
	}
};	

这个ReadOrder是在FPakPlatformFile::Mount(const TCHAR* InPakFilename, uint32 PakOrder, ...)指定的,未指定的话使用最低的优先级0

在进入mount之前,Initialize函数中会按照pak的路径和名字,给这个pak赋一个ReadOrder(即下文中的PakOrder)。

目录优先级

// IPlatformFilePak.cpp
bool FPakPlatformFile::Initialize(IPlatformFile* Inner, const TCHAR* CmdLine)
{
	……
#if EXCLUDE_NONPAK_UE_EXTENSIONS && !WITH_EDITOR
	// Extensions for file types that should only ever be in a pak file. Used to stop unnecessary access to the lower level platform file
	ExcludedNonPakExtensions.Add(TEXT("uasset"));
	ExcludedNonPakExtensions.Add(TEXT("umap"));
	ExcludedNonPakExtensions.Add(TEXT("ubulk"));
	ExcludedNonPakExtensions.Add(TEXT("uexp"));
	ExcludedNonPakExtensions.Add(TEXT("uptnl"));
	ExcludedNonPakExtensions.Add(TEXT("ushaderbytecode"));
#endif
	……
	// Find and mount pak files from the specified directories.
	TArray<FString> PakFolders;
	GetPakFolders(FCommandLine::Get(), PakFolders);
	MountAllPakFiles(PakFolders, *StartupPaksWildcard);
	……
}

FPakPlatformFile初始化的时候除了对一些成员变量进行初始化以外,还会调用MountAllPakFiles进行pak的mount。

int32 FPakPlatformFile::MountAllPakFiles(const TArray<FString>& PakFolders, const FString& WildCard)
{
	……
	if (bMountPaks)
	{
		TArray<FString> FoundPakFiles;
		FindAllPakFiles(LowerLevel, PakFolders, WildCard, FoundPakFiles);
		……
		for (int32 PakFileIndex = 0; PakFileIndex < FoundPakFiles.Num(); PakFileIndex++)
		{
			const FString& PakFilename = FoundPakFiles[PakFileIndex];
			……
			uint32 PakOrder = GetPakOrderFromPakFilePath(PakFilename);

			UE_LOG(LogPakFile, Display, TEXT("Mounting pak file %s."), *PakFilename);

			if (Mount(*PakFilename, PakOrder))
			{
				++NumPakFilesMounted;
			}
		}
	}
	return NumPakFilesMounted;
}

MountAllPakFiles其实就是读取需要mount的pak,遍历进行挂载。
其中GetPakOrderFromPakFilePath依据pak所属目录获取其优先级:

int32 FPakPlatformFile::GetPakOrderFromPakFilePath(const FString& PakFilePath)
{
	if (PakFilePath.StartsWith(FString::Printf(TEXT("%sPaks/%s-"), *FPaths::ProjectContentDir(), FApp::GetProjectName())))
	{
		return 4;
	}
	else if (PakFilePath.StartsWith(FPaths::ProjectContentDir()))
	{
		return 3;
	}
	else if (PakFilePath.StartsWith(FPaths::EngineContentDir()))
	{
		return 2;
	}
	else if (PakFilePath.StartsWith(FPaths::ProjectSavedDir()))
	{
		return 1;
	}

	return 0;
}

可以看到,确立pak读取优先级的基本策略是:Project/Content/Paks/ProjectName-*.pak > Project/Content/Paks/*.pak > Engine/Content/Paks/*.pak > Project/Saved/Paks/*.pak

根据文件名定优先级

在经过上述目录优先级的处理以后,如果文件名以_n_P结尾,则将其优先级提升到 P a k O r d e r + ( n + 1 ) × 100     ( n ≥ 1 ) PakOrder + (n + 1)×100 \space\space\space(n \geq 1) PakOrder+(n+1)×100   (n1)

// IPlatformFilePak.cpp
bool FPakPlatformFile::Mount(const TCHAR* InPakFilename, uint32 PakOrder, const TCHAR* InPath /*= NULL*/, bool bLoadIndex /*= true*/)
{
	……
	if (PakFilename.EndsWith(TEXT("_P.pak")))
	{
		// Prioritize based on the chunk version number
		// Default to version 1 for single patch system
		uint32 ChunkVersionNumber = 1;
		FString StrippedPakFilename = PakFilename.LeftChop(6);
		int32 VersionEndIndex = PakFilename.Find("_", ESearchCase::CaseSensitive, ESearchDir::FromEnd);
		if (VersionEndIndex != INDEX_NONE && VersionEndIndex > 0)
		{
			int32 VersionStartIndex = PakFilename.Find("_", ESearchCase::CaseSensitive, ESearchDir::FromEnd, VersionEndIndex - 1);
			if (VersionStartIndex != INDEX_NONE)
			{
				VersionStartIndex++;
				FString VersionString = PakFilename.Mid(VersionStartIndex, VersionEndIndex - VersionStartIndex);
				if (VersionString.IsNumeric())
				{
					int32 ChunkVersionSigned = FCString::Atoi(*VersionString);
					if (ChunkVersionSigned >= 1)
					{
						// Increment by one so that the first patch file still gets more priority than the base pak file
						ChunkVersionNumber = (uint32)ChunkVersionSigned + 1;
					}
				}
			}
		}
		PakOrder += 100 * ChunkVersionNumber;
	}
	……
}

综上所述

pak优先级的确立方法:

  1. 确立pak读取优先级的基本策略是:Project/Content/Paks/ProjectName-*.pak > Project/Content/Paks/*.pak > Engine/Content/Paks/*.pak > Project/Saved/Paks/*.pak

  2. 在经过上述目录优先级的粗略处理以后,如果文件名以_n_P结尾,则将其优先级提升到 P a k O r d e r + ( n + 1 ) × 100     ( n ≥ 1 ) PakOrder + (n + 1)×100 \space\space\space(n \geq 1) PakOrder+(n+1)×100   (n1)

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/164706.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

【阶段四】Python深度学习06篇:深度学习项目实战:卷积神经网络进行狗狗图像分类项目

本篇的思维导图: 项目背景 应用Keras框架构建卷积神经网络进行狗狗图像分类的预测,以及模型的优化。主要用来熟悉Keras卷积层、池化层网络的使用以及模型的优化方法。 数据获取 本次建模数据来源于网络,数据项统计如下: 数据集为狗狗数据集,来自全国各地的狗狗图…

算法进阶指南:第一章练习题

1.The Pilots Brothers refrigerator 牛客竞赛-The Pilots Brothers refrigerator 116. 飞行员兄弟 - AcWing题库 开关问题的特点是每个开关只会作用某个特定范围&#xff0c;所以每个开关最多操作一次&#xff0c;且操作先后次序对最后结果无影响。用16位二进制存储状态&am…

Unity 过场工具(Cutscene)设计(一)

Unity 过场工具(Cutscene)设计&#xff08;一&#xff09; 游戏中通常会涉及到过场内容的制作&#xff0c;从而来进行一些强表现&#xff0c;从而来进行剧情相关的串联&#xff0c;使游戏表现类容更丰富。比较典型的游戏 像原神&#xff0c;天刀等等游戏。 过场工具制作选择 过…

Java程序设计实验2 | Java语言基础(1)

*本文是博主对Java各种实验的再整理与详解&#xff0c;除了代码部分和解析部分&#xff0c;一些题目还增加了拓展部分&#xff08;⭐&#xff09;。拓展部分不是实验报告中原有的内容&#xff0c;而是博主本人自己的补充&#xff0c;以方便大家额外学习、参考。 目录 一、实验…

写在壬寅年末,2023年春节

先回顾过去几年写过的年末总结写在戊戌年末&#xff0c;2019年春节写在己亥年末&#xff0c;2020年春节写在庚子年末&#xff0c;2021年春节写在辛丑年末&#xff0c;2022年春节又一个农历年即将过去&#xff0c;写下这样的年末总结&#xff0c;已经是第5年&#xff0c;于是便有…

Nginx面试题(史上最全 + 持续更新)

尼恩面试宝典专题39&#xff1a;Nginx面试题&#xff08;史上最全、持续更新&#xff09; 本文版本说明&#xff1a;V27 《尼恩面试宝典》升级规划为&#xff1a; 后续基本上&#xff0c;每一个月&#xff0c;都会发布一次&#xff0c;最新版本&#xff0c;可以联系构师尼恩…

本周大新闻|传苹果MR开发样机已送出,比尔盖茨:不太看好Web3和元宇宙

本周大新闻&#xff0c;AR方面&#xff0c;苹果软件曝光“Reality OS”和“xrOS”&#xff1b;IVAS项目仅获批4000万美元&#xff1b;比尔盖茨&#xff1a;不太看好Web3和元宇宙&#xff1b;DigiLens工业AR眼镜ARGO&#xff1b;Kopin拆分部分OLED部门&#xff1b;LetinAR展示塑…

【Linux】gcc/g++的使用

这里写目录标题&#x1f696;gcc如何使用&#x1f696;函数库&#x1f308;动态库和静态库&#x1f696;gcc如何使用 ✒️预处理 预处理功能主要包括宏定义、文件包含、条件编译、去掉注释 实例&#xff1a; gcc -E myfile.c -o myfile.i “-E&#xff08;大写&#xff09;”&…

JVM(二)——参数调优

JVM参数调优 前言 你说你做过JVM调优和参数配置&#xff0c;请问如何盘点查看JVM系统默认值 使用jps和jinfo进行查看 -Xms&#xff1a;初始堆空间 1/64 -Xmx&#xff1a;堆最大值 1/4 -Xss&#xff1a;栈空间-Xms 和 -Xmx最好调整一致&#xff0c;防止JVM频繁进行收集和…

微信小程序项目实例——摇色子

微信小程序项目实例——摇色子 文章目录微信小程序项目实例——摇色子一、项目展示二、核心代码三、效果图文末项目代码见文字底部&#xff0c;点赞关注有惊喜 一、项目展示 摇色子是一款简易的游戏类小程序 用户可以投出1-9个色子 二、核心代码 dice.wxml <!--pages/dic…

编解码标准-H.264

H.264是MPEG-4家族中的一员&#xff0c;即MPEG-4系列文档ISO-14496的第10部分&#xff0c;因此被称作MPEG-4 AVC&#xff0c;MPEG-4重点考虑灵活性和交互性&#xff0c;而H.264着重强调更高的编码压缩率和传输的可靠性。 1、H.264 编码流程 1.1、slice&block 第一步&…

字节面试官: 让你设计一个MQ每秒要抗几十万并发,怎么做?

目录 1、页缓存技术 磁盘顺序写2、零拷贝技术3、最后的总结 这篇文章来聊一下Kafka的一些架构设计原理&#xff0c;这也是互联网公司面试时非常高频的技术考点。 Kafka是高吞吐低延迟的高并发、高性能的消息中间件&#xff0c;在大数据领域有极为广泛的运用。配置良好的Kaf…

电商项目之如何迁移千万级别的数据表

1 背景 电商系统一般都会有一张表记录买家的浏览器信息&#xff0c;包含但不限于浏览器ip、浏览器cookie信息、浏览器user-agent、当前页面的url、当前页面的refer。买家在电商网站上每一次操作&#xff0c;都会记录到该表。该表的数量量至少达到千万级级别。该表有什么用处&a…

07.优雅地断开套接字连接

优雅地断开套接字连接 本章将讨论如何优雅地断开相互连接的套接字。之前用的方法不够优雅是因为&#xff0c;我们是调用close或closesocket函数单方面断开连接的。 基于TCP的半关闭 TCP中的断开连接过程比建立连接过程更重要&#xff0c;因为连接过程中一般不会出现大的变数…

八、MySQL 常用函数汇总(1)

文章目录一、函数1.1 函数简介1.2 不同DBMS函数的差异二、数学函数2.1 绝对值函数ABS(x)和返回圆周率的函数PI()2.2 平方根函数SQRT(x)和求余函数MOD(x,y)2.3 获取整数的函数CEIL(x)、CEILING(x)和FLOOR(x)2.4 获取随机数的函数RAND()和RAND(x)2.5 函数ROUND(x)、ROUND(x,y)和T…

keytool 工具介绍

使用JDK自带的 keytool 工具&#xff1a; 简介 keytool 命令是一个密钥和证书管理的工具。它允许用户使用数字签名管理自己的公钥/私钥对和相关证书&#xff0c;用于自我身份验证(向其他用户和服务验证自己)或数据完整性和身份验证服务。keytool 命令还允许用户缓存通信对等体…

【七牛云 后端】笔试面

一、选择、填空知识点整理 1. fork() 函数 fork() 函数通过系统调用创建一个与原来进程相同的进程&#xff08;如果初始参数或者传入的变量不同&#xff0c;两个进程也可以做不同的事&#xff09; 示例 —— #include <stdio.h> int main() {for(int i0; i<2; i){…

centos7 安装git

一、查看是否安装过git git --version若出现以上版本号&#xff0c;则代表已经安装了git&#xff0c;不需要再次安装了&#xff0c;git安装&#xff0c;分为用yum安装和下载git源码编译安装&#xff0c;以下两种方法&#xff1a; 二、使用yum安装git yum -y install git安装…

磨金石教育摄影技能干货分享|如何在纪实摄影中体现艺术内涵

纪实摄影往往是摄影师在生活中或者旅行中随手拍下的&#xff0c;记录人们平常的瞬间。这类摄影往往强调真实性&#xff0c;在技巧上不会过多的追求。但有时候摄影师为了体现照片中更多的内涵&#xff0c;或者个人的情感思想&#xff0c;会运用一些摄影技巧来表现。下面就让我们…

动态内存与动态内存函数详解

文章目录前言一、动态内存函数的介绍1.malloc函数2.calloc3.realloc4.free二、两种常见的内存申请方法1.返回指针2.传二级指针三.常见的动态内存错误1.对NULL指针的解引用操作2.对动态开辟空间的越界访问3.对非动态开辟内存使用free释放4.使用free释放一块动态开辟内存的一部分…