export_mps.py 18 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270
  1. #
  2. # Copyright (c) Contributors to the Open 3D Engine Project.
  3. # For complete copyright and license terms please see the LICENSE at the root of https://www.github.com/o3de/o3de.
  4. #
  5. # SPDX-License-Identifier: Apache-2.0 OR MIT
  6. #
  7. #
  8. import argparse
  9. import logging
  10. import sys
  11. import o3de.export_project as exp
  12. import o3de.enable_gem as enable_gem
  13. import o3de.disable_gem as disable_gem
  14. import o3de.manifest as manifest
  15. import pathlib
  16. def export_multiplayer_sample(ctx: exp.O3DEScriptExportContext,
  17. selected_platform: str,
  18. output_path: pathlib.Path,
  19. should_build_tools: bool,
  20. build_config: str,
  21. asset_bundling_path: pathlib.Path,
  22. max_bundle_size: int,
  23. enable_gamelift: bool,
  24. should_build_all_assets: bool,
  25. should_build_game_launcher: bool,
  26. should_build_server_launcher: bool,
  27. should_build_headless_server_launcher: bool,
  28. should_build_unified_launcher: bool,
  29. allow_registry_overrides: bool,
  30. tools_build_path: pathlib.Path,
  31. game_build_path: pathlib.Path,
  32. archive_output_format: str,
  33. fail_on_asset_errors: bool,
  34. engine_centric: bool,
  35. logger: logging.Logger):
  36. if not logger:
  37. logger = logging.getLogger()
  38. logger.setLevel(logging.ERROR)
  39. default_base_build_path = ctx.engine_path / 'build' if engine_centric else ctx.project_path / 'build'
  40. if not tools_build_path:
  41. tools_build_path = default_base_build_path / 'tools'
  42. if not game_build_path:
  43. game_build_path = default_base_build_path / 'game'
  44. if not asset_bundling_path:
  45. asset_bundling_path = default_base_build_path / 'asset_bundling'
  46. seed_folder_path = ctx.project_path / 'AssetBundling' / 'SeedLists'
  47. seedlist_paths = [
  48. seed_folder_path / 'BasePopcornFxSeedList.seed',
  49. seed_folder_path / 'GameSeedList.seed',
  50. seed_folder_path / 'VFXSeedList.seed'
  51. ]
  52. if build_config == 'profile':
  53. # Dev branch has removed the profile seed list, but it still remains in main for now.
  54. # This will be removed after the next release, when both branches are synchronized
  55. profile_seed_list_path = seed_folder_path / 'ProfileOnlySeedList.seed'
  56. if profile_seed_list_path.is_file():
  57. seedlist_paths.extend([profile_seed_list_path])
  58. validated_seedslist_paths = exp.validate_project_artifact_paths(project_path=ctx.project_path,
  59. artifact_paths=seedlist_paths)
  60. exp.kill_existing_processes(ctx.project_name)
  61. gamelift_gem_added = False
  62. gamelift_gem_removed = False
  63. try:
  64. # Check if we need to enable or disable the MPSGameLift gem for the requested packages
  65. project_json_data = manifest.get_project_json_data(project_path=ctx.project_path)
  66. assert project_json_data, f"Invalid project configuration file '{ctx.project_path}/project.json'. Invalid settings."
  67. assert project_json_data['gem_names'], f"Invalid project configuration file '{ctx.project_path}/project.json'. Invalid settings."
  68. has_mps_gem = 'MPSGameLift' in project_json_data['gem_names']
  69. if enable_gamelift and not has_mps_gem:
  70. if enable_gem.enable_gem_in_project(gem_name="MPSGameLift", project_path=ctx.project_path) != 0:
  71. raise exp.ExportProjectError("Unable to enable the MPSGameLift gem for the project")
  72. gamelift_gem_added = True
  73. elif not enable_gamelift and has_mps_gem:
  74. if disable_gem.disable_gem_in_project(gem_name="MPSGameLift", project_path=ctx.project_path) != 0:
  75. raise exp.ExportProjectError("Unable to disable the MPSGameLift gem for the project")
  76. gamelift_gem_removed = True
  77. # Optionally build the toolchain needed to bundle the assets
  78. if should_build_tools:
  79. exp.build_export_toolchain(ctx=ctx,
  80. tools_build_path=tools_build_path,
  81. engine_centric=engine_centric,
  82. logger=logger)
  83. launcher_type = 0
  84. if should_build_game_launcher:
  85. launcher_type |= exp.LauncherType.GAME
  86. if should_build_server_launcher:
  87. launcher_type |= exp.LauncherType.SERVER
  88. if should_build_headless_server_launcher:
  89. launcher_type |= exp.LauncherType.HEADLESS_SERVER
  90. if should_build_unified_launcher:
  91. launcher_type |= exp.LauncherType.UNIFIED
  92. if launcher_type != 0:
  93. exp.build_game_targets(ctx=ctx,
  94. build_config=build_config,
  95. game_build_path=game_build_path,
  96. engine_centric=engine_centric,
  97. launcher_types=launcher_type,
  98. allow_registry_overrides=allow_registry_overrides,
  99. logger=logger)
  100. if should_build_all_assets:
  101. asset_processor_path = exp.get_asset_processor_batch_path(tools_build_path=tools_build_path,
  102. required=True)
  103. logger.info(f"Using '{asset_processor_path}' to process the assets.")
  104. exp.build_assets(ctx=ctx,
  105. tools_build_path=tools_build_path,
  106. engine_centric=engine_centric,
  107. fail_on_ap_errors=fail_on_asset_errors,
  108. logger=logger)
  109. # Generate the bundle
  110. asset_bundler_path = exp.get_asset_bundler_batch_path(tools_build_path=tools_build_path,
  111. required=True)
  112. logger.info(f"Using '{asset_bundler_path}' to bundle the assets.")
  113. expected_bundles_path = exp.bundle_assets(ctx=ctx,
  114. selected_platforms=[selected_platform],
  115. seedlist_paths=validated_seedslist_paths,
  116. seedfile_paths=[],
  117. tools_build_path=tools_build_path,
  118. engine_centric=engine_centric,
  119. asset_bundling_path=asset_bundling_path,
  120. max_bundle_size=max_bundle_size)
  121. project_file_patterns_to_copy = []
  122. game_project_file_patterns_to_copy = ['launch_client.cfg']
  123. server_project_file_patterns_to_copy = ['launch_server.cfg']
  124. if enable_gamelift:
  125. server_project_file_patterns_to_copy.append('MPSGameLift/Scripts/install.sh')
  126. export_layouts = []
  127. if should_build_game_launcher:
  128. export_layouts.append(exp.ExportLayoutConfig(output_path=output_path / f'{ctx.project_name}GamePackage',
  129. project_file_patterns=project_file_patterns_to_copy + game_project_file_patterns_to_copy,
  130. ignore_file_patterns=[f'*.HeadlessServerLauncher{exp.EXECUTABLE_EXTENSION}', f'*.ServerLauncher{exp.EXECUTABLE_EXTENSION}', f'*.UnifiedLauncher{exp.EXECUTABLE_EXTENSION}']))
  131. if should_build_server_launcher:
  132. export_layouts.append(exp.ExportLayoutConfig(output_path=output_path / f'{ctx.project_name}ServerPackage',
  133. project_file_patterns=project_file_patterns_to_copy + server_project_file_patterns_to_copy,
  134. ignore_file_patterns=[f'*.HeadlessServerLauncher{exp.EXECUTABLE_EXTENSION}', f'*.GameLauncher{exp.EXECUTABLE_EXTENSION}', f'*.UnifiedLauncher{exp.EXECUTABLE_EXTENSION}']))
  135. if should_build_headless_server_launcher:
  136. export_layouts.append(exp.ExportLayoutConfig(output_path=output_path / f'{ctx.project_name}HeadlessServerPackage',
  137. project_file_patterns=project_file_patterns_to_copy + server_project_file_patterns_to_copy,
  138. ignore_file_patterns=[f'*.ServerLauncher{exp.EXECUTABLE_EXTENSION}', f'*.GameLauncher{exp.EXECUTABLE_EXTENSION}', f'*.UnifiedLauncher{exp.EXECUTABLE_EXTENSION}']))
  139. if should_build_unified_launcher:
  140. export_layouts.append(exp.ExportLayoutConfig(output_path=output_path / f'{ctx.project_name}UnifiedPackage',
  141. project_file_patterns=project_file_patterns_to_copy + game_project_file_patterns_to_copy + server_project_file_patterns_to_copy,
  142. ignore_file_patterns=[f'*.HeadlessServerLauncher{exp.EXECUTABLE_EXTENSION}', f'*.ServerLauncher{exp.EXECUTABLE_EXTENSION}', f'*.GameLauncher{exp.EXECUTABLE_EXTENSION}']))
  143. for export_layout in export_layouts:
  144. exp.setup_launcher_layout_directory(project_path=ctx.project_path,
  145. project_name='MultiplayerSample',
  146. asset_platform=selected_platform,
  147. launcher_build_path=game_build_path,
  148. build_config=build_config,
  149. bundles_to_copy=[expected_bundles_path / f'game_{selected_platform}.pak',
  150. expected_bundles_path / f'engine_{selected_platform}.pak'],
  151. export_layout=export_layout,
  152. archive_output_format=archive_output_format,
  153. logger=logger)
  154. finally:
  155. # Make sure to clean up and restore the state of the MPSGame
  156. if gamelift_gem_added:
  157. if disable_gem.disable_gem_in_project(gem_name="MPSGameLift", project_path=ctx.project_path) != 0:
  158. logger.warning("Unable to remove the project's 'MPSGameList' gem")
  159. elif gamelift_gem_removed:
  160. if enable_gem.enable_gem_in_project(gem_name="MPSGameLift", project_path=ctx.project_path) != 0:
  161. logger.warning("Unable to restore project's 'MPSGameList' gem")
  162. # This code is only run by the 'export-project' O3DE CLI command
  163. if "o3de_context" in globals():
  164. global o3de_context
  165. global o3de_logger
  166. def parse_args(o3de_context: exp.O3DEScriptExportContext):
  167. parser = argparse.ArgumentParser(
  168. prog=f'o3de.py export-project -es {__file__}',
  169. description="Exports the Multiplayer Samples project as standalone to the desired output directory with release layout. "
  170. "In order to use this script, the engine and project must be setup and registered beforehand. ",
  171. epilog=exp.CUSTOM_CMAKE_ARG_HELP_EPILOGUE,
  172. formatter_class=argparse.RawTextHelpFormatter,
  173. add_help=False
  174. )
  175. parser.add_argument(exp.CUSTOM_SCRIPT_HELP_ARGUMENT,default=False,action='store_true',help='Show this help message and exit.')
  176. parser.add_argument('-out', '--output-path', type=pathlib.Path, required=True, help='Path that describes the final resulting Release Directory path location.')
  177. parser.add_argument('-cfg', '--config', type=str, default='profile', choices=['release', 'profile'],
  178. help='The CMake build configuration to use when building project binaries. Tool binaries built with this script will always be built with the profile configuration.')
  179. parser.add_argument('-a', '--archive-output', type=str,
  180. help="Option to create a compressed archive the output. "
  181. "Specify the format of archive to create from the output directory. If 'none' specified, no archiving will occur.",
  182. choices=["none", "zip", "gzip", "bz2", "xz"], default="none")
  183. parser.add_argument('-gl', '--game-lift', default=False, action='store_true',
  184. help='Enable Gamelift for the Multiplayer Sample package')
  185. parser.add_argument('-assets', '--should-build-assets', default=False, action='store_true',
  186. help='Toggles building all assets for the project by launcher type (game, server, unified).')
  187. parser.add_argument('-foa', '--fail-on-asset-errors', default=False, action='store_true',
  188. help='Option to fail the project export process on any failed asset during asset building (applicable if --should-build-assets is true)')
  189. parser.add_argument('-bt', '--build-tools', default=True, type=bool,
  190. help="Specifies whether to build O3DE toolchain executables. This will build AssetBundlerBatch, AssetProcessorBatch.")
  191. parser.add_argument('-tbp', '--tools-build-path', type=pathlib.Path, default=None,
  192. help='Designates where O3DE toolchain executables go. If not specified, default is <o3de_project_path>/build/tools.')
  193. parser.add_argument('-gbp', '--game-build-path', type=pathlib.Path, default=None,
  194. help="Designates where project executables (like Game/Server Launcher) go."
  195. " If not specified, default is <o3de_project_path>/build/game.")
  196. parser.add_argument('-regovr', '--allow-registry-overrides', default=False, type = bool,
  197. help="When configuring cmake builds, this determines if the script allows for overriding registry settings from external sources.")
  198. parser.add_argument('-abp', '--asset-bundling-path', type=pathlib.Path, default=None,
  199. help="Designates where the artifacts from the asset bundling process will be written to before creation of the package. If not specified, default is <o3de_project_path>/build/asset_bundling.")
  200. parser.add_argument('-maxsize', '--max-bundle-size', type=int, default=2048, help='Specify the maximum size of a given asset bundle.')
  201. parser.add_argument('-nogame', '--no-game-launcher', action='store_true', help='This flag skips building the Game Launcher on a platform if not needed.')
  202. parser.add_argument('-noserver', '--no-server-launcher', action='store_true', help='This flag skips building the Server Launcher on a platform if not needed.')
  203. parser.add_argument('-noheadless', '--no-headless-server-launcher', action='store_true', help='This flag skips building the Headless Server Launcher on a platform if not needed.')
  204. parser.add_argument('-nounified', '--no-unified-launcher', action='store_true', help='This flag skips building the Unified Launcher on a platform if not needed.')
  205. parser.add_argument('-pl', '--platform', type=str, default=exp.get_default_asset_platform(), choices=['pc', 'linux', 'mac'])
  206. parser.add_argument('-ec', '--engine-centric', action='store_true', default=False, help='Option use the engine-centric work flow to export the project.')
  207. parser.add_argument('-q', '--quiet', action='store_true', help='Suppresses logging information unless an error occurs.')
  208. if o3de_context is None:
  209. parser.print_help()
  210. exit(0)
  211. parsed_args = parser.parse_args(o3de_context.args)
  212. if parsed_args.script_help:
  213. parser.print_help()
  214. exit(0)
  215. return parsed_args
  216. args = parse_args(o3de_context)
  217. if args.quiet:
  218. o3de_logger.setLevel(logging.ERROR)
  219. try:
  220. export_multiplayer_sample(ctx=o3de_context,
  221. selected_platform=args.platform,
  222. output_path=args.output_path,
  223. should_build_tools=args.build_tools,
  224. build_config=args.config,
  225. asset_bundling_path=args.asset_bundling_path,
  226. max_bundle_size=args.max_bundle_size,
  227. enable_gamelift=args.game_lift,
  228. should_build_all_assets=args.should_build_assets,
  229. fail_on_asset_errors=args.fail_on_asset_errors,
  230. should_build_game_launcher=not args.no_game_launcher,
  231. should_build_server_launcher=not args.no_server_launcher,
  232. should_build_headless_server_launcher=not args.no_headless_server_launcher,
  233. should_build_unified_launcher=not args.no_unified_launcher,
  234. allow_registry_overrides=args.allow_registry_overrides,
  235. tools_build_path=args.tools_build_path,
  236. game_build_path=args.game_build_path,
  237. archive_output_format=args.archive_output,
  238. engine_centric=args.engine_centric,
  239. logger=o3de_logger)
  240. except exp.ExportProjectError as err:
  241. print(err)
  242. sys.exit(1)