技术解析 | 适用于TeamCity的Unreal Engine支持插件,提升游戏构建效率
龙智是JetBrains授权合作伙伴、Perforce授权合作伙伴,为您提供TeamCity、Perforce Helix Core等热门的游戏开发工具及一站式服务
TeamCity 是游戏开发的热门选择,大家选择它的原因包括支持 Perforce、可以进行本地安装,并提供了多种配置选项。除了将它主要用作通用 CI/CD 解决方案之外,JetBrains 还努力为各种构建工具提供专门支持。JetBrains 已提供 Unity 插件数年,去年也正式推出了 Unreal Engine 支持插件!
在这款新插件的开发过程中,JetBrains 一直与多位游戏开发客户保持密切联系,以确保它符合从事实际 Unreal Engine 项目工作的 DevOps 团队的需求。
为 Unreal Engine 游戏设置合适的构建管道可能是一项艰巨的任务,特别是对于那些在平台独特性方面经验有限的人来说。本文,我们将带您了解基本设置,同时会展示该插件的功能,并演示分布式 BuildGraph 支持等高级功能。
新 Unreal Engine 插件生成的示例构建管道
主要功能
以下是此版插件所提供功能的快速概览:
-
专为最常见用例(BuildCookRun、BuildGraph 和自动化测试)定制的专用运行程序。
-
基于 BuildGraph 描述构建发行版。
-
即时自动化测试报告。
-
自动发现构建计算机上的 Unreal Engine 安装。
-
兼容最新的 5.x 版 Unreal Engine,还支持 4.x 版本。
演示
在本演示中,我们将为随 Unreal 提供的两款入门级游戏 Cropout 和 Lyra 设置管道:我们将为构建代理和安装了 Unreal Engine 插件的 TeamCity Cloud 使用 AWS 基础架构(EC2 和用于 FSx for OpenZFS)。
我们将介绍两个场景:引擎已安装在代理上,以及引擎与游戏一起通过源代码构建。目前,TeamCity Cloud 不为代理提供预装的 Unreal Engine,但您可以随时添加您自己的包含所有必需 SDK 的自托管代理。我们将在这篇博文中采用这种方式。
使用 BuildCookRun 构建 Cropout
构建 Unreal 项目通常涉及多个步骤,例如:
-
采用指定配置针对目标平台进行编译。
-
资源烘焙(将所有资源转换为可在目标平台上读取的资源)。
-
将项目封装为合适的分发格式。
-
等等。
当然,之后还要进行测试!
首先,我们来看一个简单的场景,并使用标准 BuildCookRun 命令在一台计算机上按顺序运行所有阶段。
对于此场景,我们将使用撰写这篇博文时可以获取的最新普通版 Unreal Engine,并通过 EGL(Epic Games 启动器)进行安装。
将代理成功连接到 TeamCity Cloud 服务器后,我们可以在 Agents(代理)标签页中看到它。
现在,我想简单探讨一下代理的一些属性。
查看上图,我们可以看到代理发现了引擎及其版本(如您所见,我们的计算机上安装了多个引擎)。请记下此信息,因为稍后我们会将此信息用于构建步骤配置。
现在,常用的方式是将您所有的配置以代码形式存储在源代码控制系统下(也就是“配置即代码”)。在 TeamCity 中,您可以使用 Kotlin DSL 执行此操作。当然,您还可以使用传统 UI 配置,但今天我们将使用第一种方式(由于 YAML 已得到广泛使用,并且已成为事实上的标准,我们已将它添加到最近发布的 TeamCity Pipelines – 如果您还没有查看,请查看)。
用于在 TeamCity 中构建 Cropout 的 Kotlin DSL 配置如下所示:
unrealEngine {engineDetectionMode = automatic {identifier = "5.4"}command = buildCookRun {project = "cropout/CropoutSampleProject.uproject"buildConfiguration = standaloneGame {configurations = "Development+Shipping"platforms = "Mac"}cook = cookConfiguration {maps = "Village+MainMenu"cultures = "en"unversionedContent = true}stage = stageConfiguration {directory = "./staged"}archive = archiveConfiguration {directory = "./archived"}pak = truecompressed = trueprerequisites = true}additionalArguments = "-utf8output -buildmachine -unattended -noP4 -nosplash -stdout -NoCodeSign"
}
所有设置的作用都一目了然,但有几项设置需要进一步说明:
-
engine detection mode
我们可以在此处指定“automatic”或“manual”。前者假定您已经在代理上安装引擎并在系统中注册。
我借此机会澄清一下“注册 ”的含义:当您通过 EGL 安装 Unreal Engine 或通过源代码构建 Unreal Engine 时,此操作会写入目标计算机上的某些文件(如果使用的是 Windows,则会写入注册表)。
这基本上就是自动检测模式的用途。我们读取这些文件并发布标识符作为代理属性,这样一来,您稍后便可使用这些属性为构建选择合适的代理。
“手动”模式允许您设置 Unreal Engine 根文件夹的准确路径,当您通过源代码构建时可能会用到该路径。稍后我们将介绍这部分内容。
-
build configuration
通过调整此参数,我们可以指定所创建构建的类型,它是单机游戏、客户端、服务器,还是既是客户端又是服务器组件。
根据所选的值,插件将应用
-client
、-server
、-noserver
标志,并相应地管理-clientconfig
、-serverconfig
、-config
、-targetplatform
和-servertargetplatform
参数。
到现在为止都没问题。现在,该添加一些自动化测试并运行最终管道了。
unrealEngine {engineDetectionMode = automatic {identifier = "5.4"}command = runAutomation {project = "cropout/CropoutSampleProject.uproject"execCommand = runTests {tests = """StartsWith:JsonConfigInput.Triggers.Released""".trimIndent()}nullRHI = true}additionalArguments = "-utf8output -buildmachine -unattended -noP4 -nosplash -stdout -NoCodeSign"
}
如您所知,使用 Unreal Engine 自动化框架时,通常使用以下自动化“子命令”之一执行测试:
-
RunAll
– 这是一个非常简单的命令,能够运行所有必需的测试。 -
RunFilter
– 借助此命令,可以执行使用一个指定筛选器(包括Engine
、Stress
、Smoke
等值)标记的测试。 -
RunTests
– 这是最通用的命令,因为您可以使用该命令指定要运行的测试列表(包括使用StartsWith
的前缀筛选器、运行组筛选器和简单的子字符串匹配)。
我们尝试在 Kotlin DSL 中保留相同的用词,以便 Unreal 用户对此感到熟悉。
还要指出的是,插件会即时解析测试结果,并以 TeamCity 自带的正确显示的格式呈现测试结果。
以下是构建结果示例:
在下方,我们可以看到发布到工件存储的游戏二进制文件。默认情况下,工件会发布到内置存储;但 TeamCity 还支持多种外部存储选项。在云环境中,可以配置发布到 S3。
UI 中的最终配置将如下所示:
下面是测试运行的步骤:
由于我们已启用 Kotlin DSL 配置并禁止在 UI 中对其进行编辑,所有设置均被禁用。
使用 BuildGraph 构建 Lyra
我们将逐步过渡到使用 Lyra 的更复杂示例。由于它是一款多人游戏,我们来构建 Linux 服务器(据我们所知,一切高性能游戏都应在 Linux 上运行!)以及 Windows 和 Linux 游戏客户端。
在前面的示例中,我们在一台计算机上按顺序运行了所有步骤。这一过程通常需要大量时间。但还有一种更好的方式,即 BuildGraph。这是一种 Unreal 方法,以声明方式(或几乎以声明方式)将构建描述为一组具有依赖关系的任务。随后,构件图的不同部分可以共同执行,或拆分到不同的计算机上。后一种方式可以显著加快整个构建过程的速度,特别适合大型复杂项目。
BuildGraph 的优点是它会自动管理作业之间的所有中间工件。您只需要共享存储。构建 Lyra 时,我们会将此共享存储用于两个目的:在单个构建中的节点之间共享数据,以及维护共享派生数据缓存 (DDC)。
我们之前提到过,我们还将通过源代码构建引擎。您通过源代码构建引擎可能是出于多种原因,但就我们的特定情况而言,我们这样做是因为无法使用任何方式通过引擎(从 EGL 安装)构建 Linux 服务器(至少在撰写这篇博文时无法实现)。这是因为此特定引擎版本未提供相应文件和 SDK。
好的,序言介绍完毕 — 我们来看看代码。
项目结构和 Perforce 中的相应流如下所示(假设您已从 Epic 的 Perforce 获取 Unreal Engine 源代码):
下面是我们将用于构建的 BuildGraph XML 脚本的节选。脚本的这一特定部分给出了编辑器的构建方式以及编译所需的工具, 其中还包含几项测试。在我们的简单示例中,这些测试采用硬编码。在真实场景中,您可能会将测试列表传递给脚本并采用更复杂的逻辑。
...
<!-- Editors --><ForEach Name="Platform" Values="$(EditorPlatforms)" Separator="+"><Property Name="Compiler" Value="$(AgentPrefixCompile)$(Platform)" /><Agent Name="Build Editor and tools $(Platform)" Type="$(Compiler)">...<Property Name="ExtraToolCompileArguments" Value="$(ExtraToolCompileArguments) -architecture=arm64" If="'$(Platform)' == 'Mac'" /><Node Name="$(ToolsNodeName) $(Platform)" Requires="Setup Toolchain $(Platform)" Produces="#$(Platform)ToolBinaries"><Compile Target="CrashReportClient" Platform="$(Platform)" Configuration="Shipping" Arguments="$(ExtraToolCompileArguments)" Tag="#$(Platform)ToolBinaries"/><Compile Target="CrashReportClientEditor" Platform="$(Platform)" Configuration="Shipping" Arguments="$(ExtraToolCompileArguments)" Tag="#$(Platform)ToolBinaries"/><Compile Target="ShaderCompileWorker" Platform="$(Platform)" Configuration="Development" Arguments="$(ExtraToolCompileArguments)" Tag="#$(Platform)ToolBinaries"/><Compile Target="UnrealLightmass" Platform="$(Platform)" Configuration="Development" Tag="#$(Platform)ToolBinaries"/><Compile Target="InterchangeWorker" Platform="$(Platform)" Configuration="Development" Arguments="$(ExtraToolCompileArguments)" Tag="#$(Platform)ToolBinaries"/><Compile Target="UnrealPak" Platform="$(Platform)" Configuration="Development" Arguments="$(ExtraToolCompileArguments)" Tag="#$(Platform)ToolBinaries"/><Compile Target="BootstrapPackagedGame" Platform="$(Platform)" Configuration="Shipping" Arguments="$(ExtraToolCompileArguments)" Tag="#$(Platform)ToolBinaries" If="'$(Platform)' == 'Win64'"/></Node>...<Node Name="$(EditorNodeName) $(Platform)" Requires="$(ToolsNodeName) $(Platform)" Produces="#$(Platform)EditorBinaries"><Compile Target="$(ProjectName)Editor" Project="$(UProject)" Platform="$(Platform)" Configuration="Development" Arguments="$(ExtraEditorCompileArguments)" Tag="#$(Platform)EditorBinaries"/></Node><Property Name="AutomationTestsNodeName" Value="Run Tests $(Platform)" /><Node Name="$(AutomationTestsNodeName)" Requires="$(EditorNodeName) $(Platform)"><Property Name="TestArgs" Value="-Project=$(UProject) -NullRHI -Deterministic" /><Property Name="TestArgs" Value="$(TestArgs) -Test=UE.EditorAutomation -RunTest="StartsWith:Input"" /><Property Name="TestArgs" Value="$(TestArgs) -Build=Editor -UseEditor" /><Command Name="RunUnreal" Arguments="$(TestArgs)" /></Node></Agent><Property Name="BuildNodes" Value="$(BuildNodes);$(EditorNodeName) $(Platform);$(AutomationTestsNodeName);" /></ForEach>...
有关完整语法的描述,请参阅 Epic 网站。这里,我们本质上要做的是通过选项 EditorPlatforms
迭代传递给脚本的平台列表,并为每个平台构建编辑器。这部分代码的一个有趣的功能是代理节点的 Type
属性。
<Agent ... Type="">
在文档中查看此表:
因此,为了在特定代理上运行一组任务,您可以修改此字段。撰写这篇博文时,为了确保一切正常运行,本部分需要对构建代理进行一些配置。也就是说,您应当在代理配置文件中定义两个属性:
-
unreal-engine.build-graph.agent.type
:此属性可以是任意值(或由 ; 分隔的值列表)。相应的任务集随后将仅在至少有一个匹配项的代理上运行。 -
unreal-engine.build-graph.agent.shared-dir
:前文中讨论过,为了运行分布式 BuildGraph 构建,我们需要一个共享存储位置。通过此属性,我们可以在特定代理上设置该存储位置的路径。
以下是我们的 EC2 构建代理设置:
-
对于 Linux:
unreal-engine.build-graph.agent.type = CompileLinux;CookLinux;Linux
unreal-engine.build-graph.agent.shared-dir = /mnt/agent-shared-dir/intermediate
- 对于 Windows(由于某些原因,将文件夹作为单独的驱动器进行装载导致一次 Windows 系统调用失败,因此我们在配置中选择了网络共享):
unreal-engine.build-graph.agent.type = CompileWin64;CookWin64;Win64
unreal-engine.build-graph.agent.shared-dir = \\\\fs-040b8d6dab476baf1.fsx.eu-west-1.amazonaws.com\\fsx\\
在真实场景中,可能需要区分执行烘焙的代理和执行编译的代理,并确保它们采用合适的规范。
我们来快速浏览一下游戏的编译、烘焙和封装过程。在我们的脚本中,我们已将构建客户端的逻辑和构建服务器的逻辑分开(因为它们传递的标志不同):
<ForEach Name="Platform" Values="$(ClientPlatforms)" Separator="+"><!-- COMPILATION --><Property Name="Compiler" Value="$(AgentPrefixCompile)$(Platform)" /><Agent Name="Compile $(Platform) Client" Type="$(Compiler)"><Property Name="CompileNodeName" Value="Compile $(Platform) Client" /><Node Name="$(CompileNodeName)" Requires="$(ToolsNodeName) $(Platform)" Produces="#$(Platform) Client Binaries"><ForEach Name="TargetConfiguration" Values="$(TargetConfigurations)" Separator="+"><Compile Target="$(ProjectName)Client" Project="$(UProject)" Platform="$(Platform)" Configuration="$(TargetConfiguration)" Arguments="$(ExtraProjectCompileArguments)" /></ForEach></Node><Property Name="BuildNodes" Value="$(BuildNodes);$(CompileNodeName);" /></Agent><!-- COOKING --><Property Name="Cooker" Value="$(AgentPrefixCook)$(Platform)" /><Property Name="CookPlatformNodeName" Value="Cook $(Platform) Client" /><Agent Name="Cook $(Platform) Client" Type="$(Cooker)"><Property Name="CookPlatform" Value="$(Platform)" /><Property Name="CookPlatform" Value="Windows" If="'$(Platform)' == 'Win64'" /><Node Name="$(CookPlatformNodeName)" Requires="$(EditorNodeName) $(Platform)" Produces="#Cook $(Platform) Client Complete"><Cook Project="$(UProject)" Platform="$(CookPlatform)Client"/></Node></Agent><Property Name="BuildNodes" Value="$(BuildNodes);$(CookPlatformNodeName);" /><!-- PACKAGING --><Agent Name="Package $(Platform) Client" Type="$(Platform)"><Property Name="BCRArgs" Value="-Project='$(UProject)' -Platform=$(Platform) -NoCodeSign -Client" /><!-- Stage --><Node Name="Stage $(Platform) Client" Requires="Compile $(Platform) Client;Cook $(Platform) Client"><ForEach Name="TargetConfiguration" Values="$(TargetConfigurations)" Separator="+"><Command Name="BuildCookRun" Arguments="$(BCRArgs) -Configuration=$(TargetConfiguration) -SkipBuild -SkipCook -Stage -Pak" /></ForEach></Node><!-- Package --><Node Name="Package $(Platform) Client" Requires="Stage $(Platform) Client"><ForEach Name="TargetConfiguration" Values="$(TargetConfigurations)" Separator="+"><Command Name="BuildCookRun" Arguments="$(BCRArgs) -Configuration=$(TargetConfiguration) -SkipBuild -SkipCook -SkipStage -Package" /></ForEach></Node><!-- Publish (Packages) --><Node Name="Archive $(Platform) Client" Requires="Package $(Platform) Client"><ForEach Name="TargetConfiguration" Values="$(TargetConfigurations)" Separator="+"><Command Name="BuildCookRun" Arguments="$(BCRArgs) -Configuration=$(TargetConfiguration) -SkipBuild -SkipCook -SkipStage -SkipPak -SkipPackage -Archive" /></ForEach></Node><Node Name="Publish $(Platform) Client" Requires="Archive $(Platform) Client"><Property Name="PublishPlatform" Value="$(Platform)" /><Property Name="PublishPlatform" Value="Windows" If="'$(Platform)' == 'Win64'" /><Log Message="##teamcity[publishArtifacts 'game/ArchivedBuilds/$(PublishPlatform)Client=>$(PublishPlatform)Client.zip']" /></Node></Agent><Property Name="BuildNodes" Value="$(BuildNodes);Archive $(Platform) Client;Publish $(Platform) Client" />
</ForEach>
脚本本身不需要过多解释。我们会迭代希望客户端在其上运行的平台列表,该列表会再次作为选项传递。我们针对每个平台进行编译、烘焙资源,最后将一切封装起来。作为封装过程的最后一步,我们使用服务消息将构建的客户端作为 TeamCity 构建工件发布。
您可能已经注意到,在这部分脚本中,我们有三个代理代表构建过程的三个阶段(编译、烘焙和封装)。编译需要使用属于编辑器的工具,但不需要编辑器本身:
<Node Name="$(CompileNodeName)" Requires="$(ToolsNodeName) $(Platform)" Produces="#$(Platform) Client Binaries">
同时,烘焙过程需要实际的编辑器:
<Node Name="$(CookPlatformNodeName)" Requires="$(EditorNodeName) $(Platform)" Produces="#Cook $(Platform) Client Complete">
由于这两个过程之间没有依赖关系,它们可以在不同的计算机上并行运行(在有足够多的计算机可用的情况下)。这一简单示例演示了使用 BuildGraph 的强大功能:您只需描述共享依赖项的各项工作,然后它们就会同时运行。
为了简洁起见,我们不会显示包含服务器部分的脚本的其余部分,因为这部分内容与我们刚才描述的内容非常相似,只有标志集合不同。
最后,我们要介绍 TeamCity 中的插件配置。由于大部分工作已在 BuildGraph XML 脚本中描述,DSL 配置相当简单:
params {param("env.UE_SharedDataCachePath", "/mnt/agent-shared-dir/ddc")param("env.UE-SharedDataCachePath", "\\\\fs-040b8d6dab476baf1.fsx.eu-west-1.amazonaws.com\\fsx\\ddc")
}
steps {unrealEngine {id = "Unreal_Engine"name = "Build"engineDetectionMode = manual {rootDir = "engine"}command = buildGraph {script = "game/BuildProject.xml"targetNode = "BuildProject"options = """ProjectPath=%teamcity.build.checkoutDir%/gameProjectName=LyraClientPlatforms=Linux+Win64ServerPlatforms=LinuxEditorPlatforms=Linux+Win64TargetConfigurations=Shipping""".trimIndent()mode = UnrealEngine.BuildGraphMode.Distributed}additionalArguments = "-utf8output -buildmachine -unattended -noP4 -nosplash -stdout -NoCodeSign"}
}
在这里,指定我们希望在分布式模式下运行提供的 BuildGraph 脚本。但我们可以根据自己的意愿随时选择在一台计算机上按顺序运行所有节点。我们还指定了一些环境变量,如果引擎要启用共享 DDC,则需要使用这些变量。这些文件夹应当已装载到连接至 TeamCity 的代理。
现在,您可以为我们的游戏传递以下包含三个平台选项的列表:ClientPlatforms、ServerPlatforms 和 EditorPlatforms。
UI 中的显示如下:
启动构建时,我们会获得此构建链:
因此,如我们所见,插件已将构建图转换为适当的 TeamCity 构建链。
我们可以在指定构建的相应标签页上查看已发布的工件。Linux 服务器的显示如下:
在结束之前,我们想强调一些要点,请您务必牢记:
-
为了确保 BuildGraph 分发过程正常运行,您的构建配置应仅包含一个有效构建步骤。
-
目前,该插件不会以任何方式管理共享存储中生成的工件的保留期。您负责正确设置保留期。
-
这篇博文中的示例绝不可用于生产。您的真实场景可能会更加复杂。但这些示例可作为一个良好的起点。
您可以从 GitHub 仓库中获取这篇博文中展示的演示代码。如果您想尝试用于 TeamCity 的 Unreal Engine 插件,可从 JetBrains Marketplace 下载,并在 TeamCity On-Premises 服务器上安装。
对于 TeamCity Cloud 用户,我们已预装了 Unreal Engine 插件,您只需添加 Unreal Engine 构建步骤便可使用此插件。需要提醒您的是,TeamCity Cloud 代理没有预装 Unreal Engine,因此您需要使用自托管代理运行 Unreal Engine 构建。
针对一直使用预览版插件(从 0.x.x 开始的任何版本)的用户的重要说明:请注意,由于我们在 1.0.0 版中进行了更改并重新设计了多个功能,您的现有配置将被破坏。您可能需要使用新版插件重新创建这些配置。
后续计划
首先,我们期待收到您的反馈。您可以随时联系我们:通过我们的问题跟踪器提交工单或对这篇博文发表评论。
我们对今后的工作还有一些想法,包括:
-
提供更加深入的构建日志分析。
-
包含与 UnrealGameSync (UGS) 的集成,添加构建状态发布等功能,以及提供开箱即用的元数据服务器。
-
添加对构建计算机上可用 SDK 的检测,可能会使用整体解决方案。
-
了解 Gauntlet 自动化框架以及我们可以在其中执行的操作。
-
了解我们如何利用 Epic Games 推出的构建过程中的最新进展,即查看 Unreal Build Accelerator (UBA) 并通过 TeamCity 在代理上进行协调。
-
将插件开源。我们相信这将提高透明度,并从整体上创建更好的插件。此外,开源会带来很多乐趣。
本博文英文原作者:Vladislav Grinin
关于 TeamCity
TeamCity 是一款强大的持续集成和部署服务器,面向以 DevOps 为中心的团队提供开箱即用的测试智能、构建问题的实时报告以及无与伦比的可扩展性。安装和部署 TeamCity,几分钟之内即可开始构建您的 DevOps 管道。TeamCity 提供本地部署和基于云的版本。
进一步了解 TeamCity:
JetBrains中国授权合作伙伴-龙智
官网:www.shdsd.com
电话:400-666-7732
邮箱:marketing@shdsd.com
相关文章:
技术解析 | 适用于TeamCity的Unreal Engine支持插件,提升游戏构建效率
龙智是JetBrains授权合作伙伴、Perforce授权合作伙伴,为您提供TeamCity、Perforce Helix Core等热门的游戏开发工具及一站式服务 TeamCity 是游戏开发的热门选择,大家选择它的原因包括支持 Perforce、可以进行本地安装,并提供了多种配置选项。…...
uniapp h5端和app端 使用 turn.js
前提:添加页后,添加页与当前页会重叠在一起,不知道为什么,没有找到解决办法 1.h5端 <template><view class"container"><view id"flipbook"><view class"page page1">Page 1</view><view class"page pag…...
智慧校园系统在学生学习与生活中的应用
随着科技的快速发展,智慧校园系统逐渐成为现代教育不可或缺的一部分。它整合了先进的信息技术、物联网技术以及人工智能等,旨在构建一个全面、智能、个性化的学习与生活环境。对于学生而言,这一系统不仅能够极大地提高学习效率,还…...
umi: valtio的使用
一、基本用法 import { proxy, useSnapshot } from umijs/max;// 1、定义数据 const state proxy({ count: 33 });export default () > {// 2、使用数据const snap useSnapshot(state);function increaseCount() {state.count 1;}return (<><h1>{snap.count}…...
什么是矩阵账号?如何高效运营tiktok矩阵账号
…...
C语言.h头文件的写法
头文件的内容 #ifndef __SEQUENCE_LIST_H // 定义以防止递归包含 #define __SEQUENCE_LIST_H // (1)、其它头文件 #include <stdio.h> #include <stdlib.h> #include <strings.h> #include <stdbool.h> // (2)、宏定义(函数、变量、常量) // (3)、…...
【Day44 LeetCode】图论问题 Ⅱ
一、图论问题 Ⅱ 1、岛屿的最大面积 这题和上一篇博客求岛屿数量如出一辙,都是要找出所有岛屿,深度优先搜索代码如下: # include<iostream> # include<vector>using namespace std;int dfs(vector<vector<int>> …...
设计模式教程:责任链模式(Chain of Responsibility Pattern)
责任链模式(Chain of Responsibility Pattern)是一种常用的设计模式,它属于行为型模式,主要解决的是多个对象处理一个请求时,如何解耦请求的发送者和接收者,以及如何将请求的处理职责分配给不同的对象。 1…...
java网络编程
计算机网络基础 网络编程的目的就是直接或间接地通过网络协议与其他计算机进行通信。 在 Java 语言中包含网络编程所需要的各种类,编程人员只需要创建这些类的对象,调用相应的方法,就可以进行网络应用程序的编写。 要进行网络程序的编写&am…...
计算机网络面试知识点总结
目录 1. 计算机网络的基本知识点2. OSI 七层模型3. TCP/IP 四层模型4. TCP 和 UDP4.1 TCP 协议4.2 TCP 流量控制4.3 TCP 拥塞控制4.4 TCP 三次握手4.5 TCP 四次挥手4.6 TCP 粘包问题4.7 TCP Socket交互流程4.8 UDP 协议以及和 TCP 协议的不同 5. HTTP协议5.1 HTTP 请求方法以及…...
ubuntu22.4搭建单节点es8.1
下载对应的包 elasticsearch-8.1.1-linux-x86_64.tar.gz 创建es租户 groupadd elasticsearc useradd elasticsearch -g elasticsearch -p elasticsearch chmod uw /etc/sudoers chmod -R elasticsearch:elasticsearch elasticsearch 修改配置文件 vim /etc/sysctl.conf vm…...
卷积与动态特征选择:重塑YOLOv8的多尺度目标检测能力
文章目录 1. YOLOv8的网络结构概述2. 添加注意力机制2.1 为什么添加注意力机制?2.2 如何将注意力机制集成到YOLOv8中?2.3 效果分析 3. C2f模块的集成3.1 C2f模块简介3.2 如何在YOLOv8中集成C2f模块?3.3 效果分析 4. 卷积操作的优化4.1 卷积操…...
【Altium Designer】差分对等长设置以及绕线
在Altium Designer 17中设置差分对的等差规则及绕等长操作,需结合规则配置与交互式布线工具完成。以下是详细操作步骤: 目录 一、差分对等差规则设置 1. 原理图端差分对定义 2. PCB端差分规则配置 二、差分对等长绕线操作 1. 差分对布线 2. 交互式…...
BFS 和 DFS(深度优先搜索、广度优先搜索)
深度优先搜索(DFS)和广度优先搜索(BFS)是两种常用的图遍历算法,用于解决图相关的问题。它们在搜索问题中具有广泛的应用,如路径搜索、连通性检测等。 以下是具体区别: (图片引自&am…...
汽车免拆诊断案例 | 2013 款奔驰 S300L 车起步时车身明显抖动
故障现象 一辆2013款奔驰S300L车,搭载272 946发动机,累计行驶里程约为15万km。车主反映,将挡位置于D挡,稍微释放一点制动踏板,车辆蠕动时车身明显抖动,类似气缸失火时的抖动,又类似手动变速器…...
基于UnrealEngine(UE5)的太空探索
视频部分可参见:https://www.bilibili.com/video/BV1JWA8eSEVg/ 中国 天宫号 空间站 人造卫星可视化 星链卫星可视化 小行星分布及运动轨迹可视化 月球基地 可视化 八大行星轨道 太阳系宜居带可视化 阿波罗8号拍摄的地球升起 谷神星模型及轨迹可视化 星座可视化 十…...
HTML Application(hta)入门教程
简介 HTA是HTML Application的缩写,又称为HTML应用程序。 hta是一个可执行文件,双击可以直接运行 hta与html非常相似,可直接将文件后缀改为.hta来获得HTA格式的文件。 支持VBS和JavaScript html的权限被限制在网页浏览器内,只有操…...
IOS UITextField 无法隐藏键盘问题
设置UITextField 键盘按钮返回键为“完成”,即return key 设置done .m代码设置代理 //设置代理协议 UITextFieldDelegate, self.mobileTextField.delegate self; ///点击完成键隐藏键盘 - (BOOL)textFieldShouldReturn:(UITextField *)textField{//取…...
ES6箭头函数:基础与进阶指南
目录 引言 一、基础篇:核心语法与特性 1.1 语法革新 1.2 this绑定机制 二、进阶篇:深度特性解析 2.1 闭包中的this继承 2.2 限制与注意事项 三、实践指南:应用场景与陷阱规避 3.1 推荐使用场景 3.2 应避免的场景 四、性能考量 结语…...
AI赋能编程:PyCharm与DeepSeek的智能开发革命
在这个智能化的时代,人工智能技术正在深刻地改变着我们的工作方式,尤其是在编程领域。无论是初学者还是资深开发者,都希望借助更高效的工具和智能助手来提升生产力、优化代码质量。今天,我们将聚焦于两个强大的工具:Py…...
在Spring Boot中如何使用Freemaker模板引擎
在 Spring Boot 中使用 FreeMarker 模板引擎可以帮助你创建动态的 Web 页面。以下是详细的步骤和示例代码,介绍如何在 Spring Boot 项目里集成和使用 FreeMarker。 1. 添加依赖 如果你使用的是 Maven 项目,需要在 pom.xml 文件中添加 FreeMarker 相关依赖。Spring Boot 提供…...
【论文精读】VLM-AD:通过视觉-语言模型监督实现端到端自动驾驶
论文地址: VLM-AD: End-to-End Autonomous Driving through Vision-Language Model Supervision 摘要 人类驾驶员依赖常识推理来应对复杂多变的真实世界驾驶场景。现有的端到端(E2E)自动驾驶(AD)模型通常被优化以模仿…...
【HarmonyOS Next】鸿蒙应用进程和线程详解
【HarmonyOS Next】鸿蒙应用进程和线程详解 一、前言 进程的定义: 进程是系统进行资源分配的基本单位,是操作系统结构的基础。 在鸿蒙系统中,一个应用下会有三类进程: (1) 主进程, (2) ExtensionAbility进程ÿ…...
基于深度学习的信号滤波:创新技术与应用挑战
一、引言 1.1 研究背景 随着科技的不断发展,信号处理领域面临着越来越复杂的挑战。在众多信号处理技术中,基于深度学习的信号滤波技术逐渐崭露头角,成为研究的热点。 基于深度学习的信号滤波在信号处理领域具有至关重要的地位。如今&#…...
从【人工智能】到【计算机视觉】,【深度学习】引领的未来科技创新与变革
前几天偶然发现了一个超棒的人工智能学习网站,内容通俗易懂,讲解风趣幽默,简直让人欲罢不能。忍不住分享给大家,点击这里立刻跳转,开启你的AI学习之旅吧! 前言 – 人工智能教程https://www.captainbed.cn/l…...
什么是方法
System.out.println(),那么它是什么呢? Java方法是语句的集合,它们在一起执行一个功能。 方法是解决一类问题的步骤的有序组合 方法包含于类或对象中 方法在程序中被创建,在其他地方被使用 这段Java代码出现错误的原因在于,在…...
Python strip() 方法详解:用途、应用场景及示例解析(中英双语)
Python strip() 方法详解:用途、应用场景及示例解析 在 Python 处理字符串时,经常会遇到字符串前后存在多余的空格或特殊字符的问题。strip() 方法就是 Python 提供的一个强大工具,专门用于去除字符串两端的指定字符。本文将详细介绍 strip(…...
合并区间(56)
56. 合并区间 - 力扣(LeetCode) 解法: class Solution { public:vector<vector<int>> merge(vector<vector<int>>& intervals) {if (intervals.size() 1) {return intervals;}//现根据每一项的第一个值&#…...
如何保存爬虫获取商品评论的数据?
保存爬取的评论数据是爬虫项目中的一个重要环节。根据需求,你可以选择将数据保存为本地文件(如CSV、JSON、TXT),或者存储到数据库(如MySQL、MongoDB等)。以下是几种常见的数据保存方式及其示例代码。 1. 保…...
使用docker配置PostgreSQL
配置docker阿里云镜像仓库 国内使用docker hub拉取镜像比较慢,所以首先配置个人的镜像仓库。 阿里云的个人镜像仓库是免费的,对个人来说足够用。 具体操作参考阿里云官方链接 。 关于个人镜像仓库的使用参考链接。 配置完个人镜像仓库后将公网配置到doc…...
阿里云虚机的远程桌面登录提示帐户被锁定了
提示由于安全原因,帐户被锁定。 阿里云虚机ECS的远程桌面登录提示帐户被锁定了,只能登录阿里云处理 阿里云-计算,为了无法计算的价值 需选择通过VNC连接 然后计算机管理,解除帐户锁定即可。...
SAP on Microsoft Azure Architecture and Administration (Ravi Kashyap)
SAP on Microsoft Azure Architecture and Administration (Ravi Kashyap)...
抽象类、接口、枚举
一、抽象类 1.1 简介 作为父类,里面的方法逻辑不能满足任何一个子类的需求,提供的逻辑根本就用不上,那么就不添加方法体了,此时这个方法需要 使用关键字abstract来修饰,表示为抽象方法,而抽象方法所在的类…...
GO系列-IO 文件操作
os io 判断文件是否存在 func fileExist(filePath string) (bool, error) {_, err : os.Stat(filePath)if err nil {return true, nil}if os.IsNotExist(err) {return false, nil}return false, &CheckFileExistError{filePath} } 读取文件内容 func readFileContext(…...
【前端】使用WebStorm创建第一个项目
文章目录 前言一、步骤1、启动2、创建项目3、配置Node.js4、运行项目 二、Node.js介绍 前言 根据前面文章中记录的步骤,已经安装好了WebStorm开发软件,接下来我们就用这个IDE开发软件创建第一个项目。 一、步骤 1、启动 启动软件。 2、创建项目 新建…...
自定义类型:结构体,枚举,联合
结构体 结构体的基本声明 基础知识:结构是一些值的集合,这些值称为成员变量,结构的每个成员可以是不同类型的变量。 //学生 struct Stu {//学生的相关信息char name[20];int age; }s1,s2; //s1,s2是直接创建的结构体变量(全局变…...
柠檬水找零(力扣860)
这道题的贪心很简单,就是体现在对于20元的找零上。根据题意,20元有两种找零方式:1.找一张5元和一张10元;2.找3张5元。但是5元比较万能,因为无论是10还是20都需要用5元来找零,所以我们优先考虑第一种找零方式…...
sprintf和sscanf的用法和区别
sprintf和sscanf是C语言中常用的字符串处理函数,它们各自有不同的用途和用法。 1、sprintf的用法 sprintf函数主要用于将数据格式化为字符串并存储在指定的缓冲区中。其函数原型为: int sprintf(char *str, const char *format, ...); str:…...
deepseek自动化代码生成
使用流程 效果第一步:注册生成各种大模型的API第二步:注册成功后生成API第三步:下载vscode在vscode中下载agent,这里推荐使用cline 第四步:安装完成后,设置模型信息第一步选择API provider: Ope…...
静态时序分析:时钟组间的逻辑独立、物理独立和异步的区别
相关阅读 静态时序分析https://blog.csdn.net/weixin_45791458/category_12567571.html 当设计中存在多个时钟(同步或异步)时,该如何使用SDC命令约束设计呢?本文就将对此进行讨论。 逻辑独立 例1 多个时钟完全逻辑独立 图1 逻辑…...
深入HBase——数据结构与算法
引入 通过前面的文章,我们对HBase已经有了基本认识,下面我们从HBase最核心的算法和数据结构进一步深入HBase。 HBase的一个列簇(Column Family)本质上就是一棵LSM树(Log-Structured Merge-Tree)。LSM树…...
学习Linux准备2
使用win10系统带的wsl配置ubuntu系统,通过wsl功能我们可以更简单更轻松的获得Linux系统环境。 首先开启Windows自带的wsl功能 打开控制面板,选中启用或关闭Windows功能 这里我们点击进入 将上图红√点击上,点击确定,然后重新启动…...
MATLAB图像处理:图像分割方法
图像分割将图像划分为具有特定意义的子区域,是目标检测、医学影像分析、自动驾驶等领域的核心预处理步骤。本文讲解阈值分割、边缘检测、区域生长、聚类分割、基于图的方法等经典与前沿技术,提供MATLAB代码实现。 目录 1. 图像分割基础 2. 经典分割方…...
什么是“可迭代”
在 Python 中,“可迭代”(Iterable)是一个非常重要的概念,它指的是任何可以被逐个访问其元素的对象。换句话说,如果一个对象支持迭代操作(比如可以通过 for 循环逐个访问其元素),那么…...
使用Ubuntu搭建Java部署环境
White graces:个人主页 🙉专栏推荐:Java入门知识🙉 🐹今日诗词:小舟从此逝,江海寄余生🐹 ⛳️点赞 ☀️收藏⭐️关注💬卑微小博主🙏 ⛳️点赞 ☀️收藏⭐️关注💬卑微小…...
Linux 文件的三个时间:Access、Modify 和 Change
目录 一、文件的三个时间戳 1. 访问时间(Access Time) 2. 修改时间(Modify Time) 3. 更改时间(Change Time) 二、如何查看文件的三个时间戳 三、文件时间戳的变化规则 1. 修改文件内容 2. 修改文件…...
移植live555 上的 rtsp
一、V4L2视频采集模块(完整示例) #include <linux/videodev2.h> #include <sys/ioctl.h> #include <fcntl.h>// 初始化V4L2摄像头 int init_v4l2_camera(const char* dev_path, int width, int height) {int fd open(dev_path, O_RD…...
基于SpringBoot的“宠物救助及领养平台”的设计与实现(源码+数据库+文档+PPT)
基于SpringBoot的“宠物救助及领养平台”的设计与实现(源码数据库文档PPT) 开发语言:Java 数据库:MySQL 技术:SpringBoot 工具:IDEA/Ecilpse、Navicat、Maven 系统展示 系统整体功能图 系统首页界面 系统注册页面…...
Gemini 2.0助力科学突破,AI联合科学家系统登场
每周跟踪AI热点新闻动向和震撼发展 想要探索生成式人工智能的前沿进展吗?订阅我们的简报,深入解析最新的技术突破、实际应用案例和未来的趋势。与全球数同行一同,从行业内部的深度分析和实用指南中受益。不要错过这个机会,成为AI领…...
electron提升软件运行权限,以管理员权限运行
大家有任何想法,都可以联系博主沟通。 本系列为实战文章,最终实现的桌面工具软件,获取方式:百度网盘地址:https://pan.baidu.com/s/1yrl0jYpti7QCn8CHBRT2lw?pwd1234 正文开始 前言一、提升electron运行权限的三种方…...