本期作者

游戏全球发行提效-- 发行 iOS SDK的主动化接入探究

1、前言

游戏SDK作为游戏技术中台赋能游戏研制的中心产品之一,其中心才能包括账号、交易、合规(实名、防沉迷),以及交际、营销等才能。为游戏开发者供给了快捷的方法快速完结底层事务才能的建立,降低开发和保护的难度。协助游戏研制更专注于游戏内容的创造,缩短游戏研制周期的一起保证游戏的安全性合规性。

2、背景

为了更有用的满意不同区域商场的战略和法规要求,为不同区域供给符合当地玩家喜爱的游戏体会,哔哩哔哩游戏在发行上采取差异化的运营和推广战略。

游戏在发行中会按区域和主体分包发行,咱们将游戏的主体和区域发行方案称为发行方案。

游戏SDK作为游戏的底层事务支撑,为了适配游戏的发行方案,也供给了与之对应的游戏SDK。

游戏全球发行提效-- 发行 iOS SDK的主动化接入探究

跟着游戏事务的开展,游戏SDK的数量也不断增加。在保证游戏事务开展的一起,咱们整合了一切海外发行的SDK,游戏在海外发行中不需求依据发行区域接入对应的区域包,只需求接入全球版游戏SDK即可。

这在很大程度上提高了游戏海外发行的功率,但并非最优解,在高效支持游戏差异化发行的一起,咱们一直在考虑如何更好的提升全球化发行功率。

3、定个小方针

基于当时的游戏事务状况,咱们设想了一个比较理想的SDK接入情景:

  1. 一次接入,全球适配:咱们期望游戏只需接入一次SDK API,就能够轻松完结全球范围的适配作业。
  2. 即拿即用,无需装备:研制无需装备游戏SDK所需求的参数,由东西主动化完结SDK的装备作业。

在当时游戏分主体区域运营战略下,如果能整合一切SDK的接入并且简化接入过程的话,这对游戏的接入提效无疑是巨大的。

4、 方案挑选

1、重构现有游戏SDK,兼并一切SDK

在过往的游戏事务中,咱们一度保护超越20款SDK。经过两次事务重构后,SDK已减缩为6个。

由于国服和海外服的事务差异和合规要求,以及发行主体间的合规要求,SDK已无法再减缩。

在当时游戏的全球化发行的旅程中,最好状况只需求接入两个SDK,但不能扫除接多个SDK的状况。

在当时政策和前史技术的要求下,兼并一切SDK已然不是一条能走通的路。

2、在游戏SDK的上层规划聚合SDK,经过出包东西主动化接入

聚合SDK在游戏行业是比较常见的事务形式,在安卓发行中较为常见,它能够协助开发者削减对接华为、小米、运用宝等途径SDK的研制本钱。

在咱们的事务场景中,聚合SDK的使命是一致bilibili游戏事务中的各个游戏SDK,在产品上输出聚合SDK供游戏研制接入。

聚合SDK会依据游戏的发行方案在内部完结相应游戏SDK的接入和调度,原有的游戏SDK则退居二线,研制无需感知详细接入的游戏SDK。

聚合SDK完结一切游戏SDK的功用兼并,对外供给一致的API。在游戏完结API接入后,由打包东西完结底层游戏SDK的接入和装备。

5、如何做?

5.1、OneSDK一致API

尽管各游戏SDK在事务逻辑上处理方案不一致,但对外供给的事务才能大致类似,比如账号体系、支付才能、客服、广告归因等才能都是通用的。这给顶层的聚合SDK——OneSDK的开发供给了先决条件。

在顶层规划上,OneSDK最大可能的兼并和简化各个游戏SDK的参数差异,一致各个SDK的API定义,给游戏供给一致全面的API接口。

在事务分工中,OneSDK不直接参与游戏事务,在顶层API和底层事务SDK中充当桥接效果,当调用OneSDK API时,经过AdapterProtocol寻找能够呼应API的游戏事务Adapter。

实践游戏SDK事务调用的作业在Adapter层面完结,每个游戏SDK有自己的Adapter,Adapter依据游戏SDK的特性完结参数的转化和适配,终究调用到实践的游戏事务中。

游戏全球发行提效-- 发行 iOS SDK的主动化接入探究

后续游戏在SDK的接入过程中,只需求接入OneSDK即可完结全球范围的发行适配作业,无需感知底层游戏SDK。

咱们经过OneSDK完结了的各游戏SDK的聚合,在API层面上完结了一致。但Xcode工程的接入装备中,仍是需求实践接入对应的主体游戏SDK,SDK所需求装备仍然是需求的。

如何协助游戏快速完结底层游戏SDK的接入装备,是咱们下一步需求考虑的事情。

5.2、打包东西完结主动化接入

在接入OneSDK后,在API层面上已经无需感知底层SDK,在装备上咱们也需求有相应方案协助游戏防止繁复的SDK装备作业。

已然SDK接入装备无法防止,咱们决定规划一款打包东西,经过脚本替代人工完结一切SDK的装备和接入作业。

在安卓中有着和iOS平台上类似的场景,在安卓中有多途径发行的场景,一套代码需求发行谷歌、华为、小米、运用宝等不同的途径。

对于这种状况安卓游戏SDK能够运用官方供给的APKTool东西,反编译替换SDK文件后,再回编译生成各个途径包。

游戏全球发行提效-- 发行 iOS SDK的主动化接入探究

那儿iOS可否像安卓相同修正母包IPA,替换相关代码和资源文件终究生成新的IPA包呢?

很遗憾,这种办法是不可靠的。iOS没有像谷歌相同官方供给的APKTool东西,来履行反编译和回编译的才能。

要想在iOS完结一套代码主动完结多途径发行的打包,咱们能够比安卓更早一步介入打包的流程中。
安卓是在源码编译成APK包体后介入打包的,咱们能够在源码阶段介入,经过修正Xcode工程的方法,完结多途径包的主动化接入。

5.2.1、打包东西规划

考虑到游戏的Xcode工程动辄几十G的巨细,上传下载非常耗时,以及游戏研制对源码工程泄露的忧虑。打包东西在产品形式上以MacOS端的运用交付给游戏研制运用,在本地完结游戏的打包作业。

简而言之,打包东西的主要功用是在完结鉴权后,获取与发行方案对应的打包方案,然后依据该方案完结OneSDK基层的游戏SDK接入和装备。

在运用层面,(被苹果原生MacOS开发结构短暂折磨过之后)咱们挑选对客户端开发更为亲近的Flutter作为结构,对于轻量级的运用,Flutter一致的API体会、热重载等特性能够协助咱们快速完结运用的建立。

在偏底层作业的Xcode工程修正上,Flutter没有非常安稳的类库能够完结Xcode工程的修正作业,但能够调用脚本完结前述作业。

脚本所要做的作业,能够简单概括成以下过程:

  1. 删去游戏SDK文件夹以及里面的内容

  2. 导入新的游戏SDK文件

  3. 修正Info.plist文件,增加事务参数

  4. 修正build-settings,增加指定编译指令

  5. 导入游戏SDK所依靠的体系库

游戏全球发行提效-- 发行 iOS SDK的主动化接入探究

在脚本结构上,咱们运用Xcodeproj模拟在Xcode界面完结的代码、资源的删去&替换操作

Xcodeproj是一个常用的修正Xcode工程的Ruby结构,由咱们熟知的Cocoapods团队开发。其内部完好映射了project.pbxproj的Object类型及其对应的特点。将Xcode项目文件解析成一个抽象语法树(AST),经过遍历语法树来获取项目装备信息,并供给相应的API来修正和办理这些信息。经过代码来增加、删去、修正项目中的文件、依靠、编译选项等,以达到主动化接入的意图。

5.2.2 project.pbxproj 介绍

要想经过脚本修正Xcode工程,咱们先要了解project.pbxproj文件。

project.pbxproj被包装在xcodeproj文件中,存储着Xcode中一切能看见的公共装备信息。它本质上是一种旧风格的 Property List 文件,前史可追溯到 NeXT 的 OpenStep。

换句话说,咱们能够经过修正project.pbxproj来完结咱们在Xcode中能够修正的装备。

信任很多人都有由于代码冲突而手动修正过project.pbxproj,也为project.pbxproj冗杂的内容感到眼花缭乱。这是由于project.pbxproj文件的可读性较差且内容较多。实践上project.pbxproj有固定的结构和格局,了解大致结构和分类用处就会对其认知更明晰,project.pbxproj的总体结构如下所示:

project.pbxproj结构

// !$*UTF8*$!
{
    archiveVersion = 1;
    classes = {
    };
    objectVersion = 46;  
    objects = { 
    ...  
    }  
    rootObject = E6A557831C22979700A81AD5 /* Project object */;
}

在project.pbxproj咱们需求重视的是objects的内容,一般来说,工程越大越杂乱,objects的内容就会越多。objects里面实践上是一个元素的Map, 每个Key用一个全文档唯一的uuid来表示。

objects中包括了多个Section, 也便是多种类型的信息。

// !$*UTF8*$!
{ 
    archiveVersion = 1;  
    classes = { 
    };  
    objectVersion = 56; 
    objects = { 
/* Begin PBXBuildFile section */   
        XXXXXXXXXXXXXXXXXXXXXXXXXXX /* AppDelegate.m in Sources */ = {isa = PBXBuildFile; fileRef = 880B73262AE53EFB00027A9F /* AppDelegate.m */; };
/* End PBXBuildFile section */ 
/* Begin PBXFileReference section */
/* End PBXFileReference section */ 
/* Begin PBXFrameworksBuildPhase section */
/* End PBXFrameworksBuildPhase section */ 
/* Begin PBXGroup section */    
        880B73192AE53EFB00027A9F = {   
            isa = PBXGroup;  
            children = (   
                XXXXXXXXXXXXXXXXXXXXXXXX* DEMO */,    
                880B73232AE53EFB00027A9F /* Products */,     
            );  
            sourceTree = "<group>"; 
        };
/* End PBXGroup section */ 
/* Begin PBXNativeTarget section */ 
        880B73212AE53EFB00027A9F /* DEMO */ = {  
            isa = PBXNativeTarget;    
                  buildConfigurationList = 880B733B2AE53EFC00027A9F /* Build configuration list for PBXNativeTarget "DEMO" */;  
                  buildPhases = (  
                      880B731E2AE53EFB00027A9F /* Sources */, 
                      880B731F2AE53EFB00027A9F /* Frameworks */,  
                      XXXXXXXXXXXXXXXXXXXXXXXX /* Resources */,   
                  );  
                  buildRules = (    
                  );   
                  dependencies = (     
                  );     
                  name = DEMO;    
                  productName = DEMO;  
                  productReference = 880B73222AE53EFB00027A9F /* DEMO.app */;   
                  productType = "com.apple.product-type.application";  
          };
/* End PBXNativeTarget section */  
    };  
    rootObject = 880B731A2AE53EFB00027A9F /* Project object */;
}

以上是obejcts的简化数据内容,它包括了许多咱们在Xcode中经常会用到的装备内容

  1. PBXProject:
    – 代表整个 Xcode 项目。
    – 包括项意图全局装备和设置,如项目名称、项目根目录、构建装备等。
    – 包括了项目中的一切 PBXNativeTarget 的列表。

  2. PBXNativeTarget:
    – 代表一个构建方针,能够是运用程序、库或其他可履行文件。
    – 包括了该方针的详细装备,如编译器选项、输出途径、依靠关系等。

  3. PBXFileReference:
    – 代表项目中的文件或文件夹。
    – 包括文件的途径、文件类型、引证等信息。

  4. PBXGroup:
    – 用于安排项目中的文件和文件夹,以创立项意图文件结构。
    – 能够包括 PBXFileReference 和其他 PBXGroup

  5. PBXBuildFile:
    – 描绘编译阶段的文件引证,包括源文件(.m)和资源文件(xib或许storyboard).

6.PBXBuildPhase:
– 描绘编译阶段. 是一个抽象概念,实践出现的是下面详细的项目;BuildPhase对应的是Xcode图形化界面上的Build Phase装备.
– PBXCopyFilesBuildPhase: 拷贝文件阶段.PBXFrameworksBuildPhase: Framework编译阶段
– PBXHeadersBuildPhase: 头文件编译阶段.
– PBXResourcesBuildPhase: 资源文件拷贝编译阶段.
– PBXShellScriptBuildPhase: 脚本履行阶段.
– PBXSourcesBuildPhase: 源文件编译阶段.

  1. PBXContainerItemProxy:
    – 用于办理依靠关系和跨项目引证,通常在多项目工程中运用。

  2. XCBuildConfiguration:
    – 包括特定构建装备的设置,如编译器选项、链接选项等。
    XCBuildConfiguration 目标通常归于 PBXNativeTarget

  3. XCConfigurationList:
    – 用于办理构建装备列表,每个 PBXNativeTarget 都有一个 `XCConfigurationList

这些内容在Xcode界面中有与之类似的装备项,命名也大多近似。针对这些Section修正能够完结Xcode装备项的修正,但需求留意将一切影响项一同修正完毕,不然很简单就会导致工程文件错误。

5.2.3、脚本操作之增删改查

在咱们脚本操作的第一步是旧SDK包体的删去和新的SDK包体的导入。这些文件的增删、装备的修正都是基于Target修正的,在project.pbxproj中咱们修正的是PBXNativeTarget Section。

以下是PBXNativeTarget文件结构

PBXNativeTarget结构

880288D929013C8100802937 /* OneSDKDemo */ = { 
    isa = PBXNativeTarget; 
    buildConfigurationList = 880288F329013C8300802937 /* Build configuration list for PBXNativeTarget "OneSDKDemo" */;  
    buildPhases = (  
        880288D629013C8100802937 /* Sources */,   
        880288D729013C8100802937 /* Frameworks */,  
        880288D829013C8100802937 /* Resources */, 
    );  
    buildRules = ( 
    );  
    dependencies = (  
    );  
    name = OneSDKDemo;
    productName = OneSDKDemo_GSO;  
    productReference = 880288DA29013C8100802937 /* OneSDKDemo.app */; 
    productType = "com.apple.product-type.application";
};

以上面文件为例,要增删Framework,咱们就要去修正UUID为880288D729013C8100802937的PBXFrameworksBuildPhase Section,而要增删bundle、图片等资源文件,咱们就要修正UUID为880288D829013C8100802937的PBXResourcesBuildPhase。

以删去SDK为例,脚本会去遍历SDK文件夹内的一切内容,然后将其依据文件类型分别在不同的PBXBuildPhase中移除引证,随后删去文件。

需求留意的是:Unity游戏导出的工程有两个Target,代码挂载在UnityFramework Target上,图片等资源文件则是挂载在主Target上。咱们在处理Unity工程的时候要区别Target操作。

删去SDK

def delete_group(group)
    group.files.each do |file|  
      if file.real_path.to_s.end_with?('.m', '.mm', '.cpp', '.h', '.swift')           @code_target.source_build_phase.remove_file_reference(file)           
        delete_path(file.real_path)  
        file.remove_from_project  
      elsif file.real_path.to_s.end_with?('.a') || file.real_path.to_s.end_with?('.framework')    
        @code_target.frameworks_build_phases.remove_file_reference(file)      
        delete_path(file.real_path)   
        file.remove_from_project  
      elsif file.real_path.to_s.end_with?('.plist', '.bundle') 
        @main_target.resources_build_phase.remove_file_reference(file)       
        delete_path(file.real_path) 
        file.remove_from_project  
      end  
    end 
    group.version_groups.each do |child| 
      if child.real_path.to_s.end_with?('.xcdatamodeld')        
        @main_target.resources_build_phase.remove_file_reference(child)      
        delete_path(child.real_path)  
        child.remove_from_project  
      else   
        delete_group(child, delete_files) 
      end 
    end  
    group.groups.each do |child|  
      delete_group(child) 
    end 
  end

5.2.4、脚本操作之参数装备

游戏SDK所需求修正的参数会集在Info.plist中,这儿咱们运用Ruby的Plist库来履行对plist文件的修正

Plist文件选用XML格局存储数据,这种格局可读性比project.pbxproj好多了,咱们能够很明晰的知道需求修正文件层级和位置。

下面的代码咱们演示了如何在Xcode工程中增加权限描绘案牍。这些权限和描绘案牍来自游戏运营在发行平台上的装备,打包东西在打包时会获取最新的装备。

增加权限案牍

# 增加权限描绘字符串
def add_app_permissions(target_name, permissions) 
    return if permissions.nil?  
    plist_full_path = info_plist_path(target_name)
    info = Plist.parse_xml(plist_full_path) 
    permissions.each do |key, value|   
        puts 'key : ', key, 'value:', value  
        info[key] = value  
    end  
    Plist::Emit.save_plist(info, plist_full_path)
end

在游戏的主动化出包流程中,打包脚本还有更多的作业,但大多都是运用Xcodeproj和Plist两个东西完结的作业。了解并掌握project.pbxproj的内容和意义,并运用Xcodeproj和Plist对其修正修正,就能够完结绝大多数Xcode工程修正的作业。

5.2.5、打包东西概览

游戏全球发行提效-- 发行 iOS SDK的主动化接入探究

在全体规划上,打包东西结合游戏发行体系,获取游戏维度的发行方案,研制无需感知游戏SDK所需求装备的参数。

一起打包东西将SDK版别的接入办理归拢至打包东西中,能够依据游戏的运营状态而差异化下发接入SDK的版别。

游戏全球发行提效-- 发行 iOS SDK的主动化接入探究
游戏全球发行提效-- 发行 iOS SDK的主动化接入探究

打包东西在运用上也很简单,游戏研制在登录游戏发行账号后,只需求完结三步装备即可快速完结发行包的接入。

未来后续打包东西会纳入更多符合事务形式的功用,例如接入合规检测、SDK功用自检等功用,继续为游戏的开发提效。

6、未来规划

当时,OneSDK已交付给游戏运用,研制需求接入的SDK从最少2个削减至最多1个。打包东西将在年内上线供研制运用,在结合运用OneSDK和打包东西的状况下,预计能够协助游戏削减60%的接入耗时。

提高功率是一个不断迭代的过程,未来,咱们会站在全球发行的视点,不断完善和丰厚OneSDK和打包东西,简化游戏的接入过程,一起供给更多有用的东西协助研制提升功率。

参考文献:

www.rubydoc.info/gems/xcodep…

github.com/patsplat/pl…

muhuashanjin.github.io/2017/10/09/…