Welcome to mirror list, hosted at ThFree Co, Russian Federation.

git.blender.org/blender-addons.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
Diffstat (limited to 'game_engine_publishing.py')
-rw-r--r--game_engine_publishing.py576
1 files changed, 0 insertions, 576 deletions
diff --git a/game_engine_publishing.py b/game_engine_publishing.py
deleted file mode 100644
index 495b0123..00000000
--- a/game_engine_publishing.py
+++ /dev/null
@@ -1,576 +0,0 @@
-# ##### BEGIN GPL LICENSE BLOCK #####
-#
-# This program is free software; you can redistribute it and/or
-# modify it under the terms of the GNU General Public License
-# as published by the Free Software Foundation; either version 2
-# of the License, or (at your option) any later version.
-#
-# This program is distributed in the hope that it will be useful,
-# but WITHOUT ANY WARRANTY; without even the implied warranty of
-# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
-# GNU General Public License for more details.
-#
-# You should have received a copy of the GNU General Public License
-# along with this program; if not, write to the Free Software Foundation,
-# Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
-#
-# ##### END GPL LICENSE BLOCK #####
-
-import bpy
-import os
-import tempfile
-import shutil
-import tarfile
-import time
-import stat
-
-
-bl_info = {
- "name": "Game Engine Publishing",
- "author": "Mitchell Stokes (Moguri), Oren Titane (Genome36)",
- "version": (0, 1, 0),
- "blender": (2, 75, 0),
- "location": "Render Properties > Publishing Info",
- "description": "Publish .blend file as game engine runtime, manage versions and platforms",
- "warning": "",
- "wiki_url": "http://wiki.blender.org/index.php/Extensions:2.6/Py/Scripts/Game_Engine/Publishing",
- "category": "Game Engine",
-}
-
-
-def WriteRuntime(player_path, output_path, asset_paths, copy_python, overwrite_lib, copy_dlls, make_archive, report=print):
- import struct
-
- player_path = bpy.path.abspath(player_path)
- ext = os.path.splitext(player_path)[-1].lower()
- output_path = bpy.path.abspath(output_path)
- output_dir = os.path.dirname(output_path)
- if not os.path.exists(output_dir):
- os.makedirs(output_dir)
-
- python_dir = os.path.join(os.path.dirname(player_path),
- bpy.app.version_string.split()[0],
- "python",
- "lib")
-
- # Check the paths
- if not os.path.isfile(player_path) and not(os.path.exists(player_path) and player_path.endswith('.app')):
- report({'ERROR'}, "The player could not be found! Runtime not saved")
- return
-
- # Check if we're bundling a .app
- if player_path.lower().endswith('.app'):
- # Python doesn't need to be copied for OS X since it's already inside blenderplayer.app
- copy_python = False
-
- output_path = bpy.path.ensure_ext(output_path, '.app')
-
- if os.path.exists(output_path):
- shutil.rmtree(output_path)
-
- shutil.copytree(player_path, output_path)
- bpy.ops.wm.save_as_mainfile(filepath=os.path.join(output_path, 'Contents', 'Resources', 'game.blend'),
- relative_remap=False,
- compress=False,
- copy=True,
- )
- else:
- # Enforce "exe" extension on Windows
- if player_path.lower().endswith('.exe'):
- output_path = bpy.path.ensure_ext(output_path, '.exe')
-
- # Get the player's binary and the offset for the blend
- with open(player_path, "rb") as file:
- player_d = file.read()
- offset = file.tell()
-
- # Create a tmp blend file (Blenderplayer doesn't like compressed blends)
- tempdir = tempfile.mkdtemp()
- blend_path = os.path.join(tempdir, bpy.path.clean_name(output_path))
- bpy.ops.wm.save_as_mainfile(filepath=blend_path,
- relative_remap=False,
- compress=False,
- copy=True,
- )
-
- # Get the blend data
- with open(blend_path, "rb") as blend_file:
- blend_d = blend_file.read()
-
- # Get rid of the tmp blend, we're done with it
- os.remove(blend_path)
- os.rmdir(tempdir)
-
- # Create a new file for the bundled runtime
- with open(output_path, "wb") as output:
- # Write the player and blend data to the new runtime
- print("Writing runtime...", end=" ", flush=True)
- output.write(player_d)
- output.write(blend_d)
-
- # Store the offset (an int is 4 bytes, so we split it up into 4 bytes and save it)
- output.write(struct.pack('BBBB', (offset >> 24) & 0xFF,
- (offset >> 16) & 0xFF,
- (offset >> 8) & 0xFF,
- (offset >> 0) & 0xFF))
-
- # Stuff for the runtime
- output.write(b'BRUNTIME')
-
- print("done", flush=True)
-
- # Make sure the runtime is executable
- os.chmod(output_path, 0o755)
-
- # Copy bundled Python
- blender_dir = os.path.dirname(player_path)
-
- if copy_python:
- print("Copying Python files...", end=" ", flush=True)
- py_folder = os.path.join(bpy.app.version_string.split()[0], "python", "lib")
- dst = os.path.join(output_dir, py_folder)
- src = python_dir
-
- if os.path.exists(dst) and overwrite_lib:
- shutil.rmtree(dst)
-
- if not os.path.exists(dst):
- shutil.copytree(src, dst, ignore=lambda dir, contents: [i for i in contents if i == '__pycache__'])
- print("done", flush=True)
- else:
- print("used existing Python folder", flush=True)
-
- # And DLLs if we're doing a Windows runtime)
- if copy_dlls and ext == ".exe":
- print("Copying DLLs...", end=" ", flush=True)
- for file in [i for i in os.listdir(blender_dir) if i.lower().endswith('.dll')]:
- src = os.path.join(blender_dir, file)
- dst = os.path.join(output_dir, file)
- shutil.copy2(src, dst)
-
- print("done", flush=True)
-
- # Copy assets
- for ap in asset_paths:
- src = bpy.path.abspath(ap.name)
- dst = os.path.join(output_dir, ap.name[2:] if ap.name.startswith('//') else ap.name)
-
- if os.path.exists(src):
- if os.path.isdir(src):
- if ap.overwrite and os.path.exists(dst):
- shutil.rmtree(dst)
- elif not os.path.exists(dst):
- shutil.copytree(src, dst)
- else:
- if ap.overwrite or not os.path.exists(dst):
- shutil.copy2(src, dst)
- else:
- report({'ERROR'}, "Could not find asset path: '%s'" % src)
-
- # Make archive
- if make_archive:
- print("Making archive...", end=" ", flush=True)
-
- arctype = ''
- if player_path.lower().endswith('.exe'):
- arctype = 'zip'
- elif player_path.lower().endswith('.app'):
- arctype = 'zip'
- else: # Linux
- arctype = 'gztar'
-
- basedir = os.path.normpath(os.path.join(os.path.dirname(output_path), '..'))
- afilename = os.path.join(basedir, os.path.basename(output_dir))
-
- if arctype == 'gztar':
- # Create the tarball ourselves instead of using shutil.make_archive
- # so we can handle permission bits.
-
- # The runtimename needs to use forward slashes as a path separator
- # since this is what tarinfo.name is using.
- runtimename = os.path.relpath(output_path, basedir).replace('\\', '/')
-
- def _set_ex_perm(tarinfo):
- if tarinfo.name == runtimename:
- tarinfo.mode = 0o755
- return tarinfo
-
- with tarfile.open(afilename + '.tar.gz', 'w:gz') as tf:
- tf.add(output_dir, os.path.relpath(output_dir, basedir), filter=_set_ex_perm)
- elif arctype == 'zip':
- shutil.make_archive(afilename, 'zip', output_dir)
- else:
- report({'ERROR'}, "Unknown archive type %s for runtime %s\n" % (arctype, player_path))
-
- print("done", flush=True)
-
-
-class PublishAllPlatforms(bpy.types.Operator):
- bl_idname = "wm.publish_platforms"
- bl_label = "Exports a runtime for each listed platform"
-
- def execute(self, context):
- ps = context.scene.ge_publish_settings
-
- if ps.publish_default_platform:
- print("Publishing default platform")
- blender_bin_path = bpy.app.binary_path
- blender_bin_dir = os.path.dirname(blender_bin_path)
- ext = os.path.splitext(blender_bin_path)[-1].lower()
- WriteRuntime(os.path.join(blender_bin_dir, 'blenderplayer' + ext),
- os.path.join(ps.output_path, 'default', ps.runtime_name),
- ps.asset_paths,
- True,
- True,
- True,
- ps.make_archive,
- self.report
- )
- else:
- print("Skipping default platform")
-
- for platform in ps.platforms:
- if platform.publish:
- print("Publishing", platform.name)
- WriteRuntime(platform.player_path,
- os.path.join(ps.output_path, platform.name, ps.runtime_name),
- ps.asset_paths,
- True,
- True,
- True,
- ps.make_archive,
- self.report
- )
- else:
- print("Skipping", platform.name)
-
- return {'FINISHED'}
-
-
-class RENDER_UL_assets(bpy.types.UIList):
- bl_label = "Asset Paths Listing"
-
- def draw_item(self, context, layout, data, item, icon, active_data, active_propname):
- layout.prop(item, "name", text="", emboss=False)
-
-
-class RENDER_UL_platforms(bpy.types.UIList):
- bl_label = "Platforms Listing"
-
- def draw_item(self, context, layout, data, item, icon, active_data, active_propname):
- row = layout.row()
- row.label(item.name)
- row.prop(item, "publish", text="")
-
-
-class RENDER_PT_publish(bpy.types.Panel):
- bl_label = "Publishing Info"
- bl_space_type = "PROPERTIES"
- bl_region_type = "WINDOW"
- bl_context = "render"
-
- @classmethod
- def poll(cls, context):
- scene = context.scene
- return scene and (scene.render.engine == "BLENDER_GAME")
-
- def draw(self, context):
- ps = context.scene.ge_publish_settings
- layout = self.layout
-
- # config
- layout.prop(ps, 'output_path')
- layout.prop(ps, 'runtime_name')
- layout.prop(ps, 'lib_path')
- layout.prop(ps, 'make_archive')
-
- layout.separator()
-
- # assets list
- layout.label("Asset Paths")
-
- # UI_UL_list
- row = layout.row()
- row.template_list("RENDER_UL_assets", "assets_list", ps, 'asset_paths', ps, 'asset_paths_active')
-
- # operators
- col = row.column(align=True)
- col.operator(PublishAddAssetPath.bl_idname, icon='ZOOMIN', text="")
- col.operator(PublishRemoveAssetPath.bl_idname, icon='ZOOMOUT', text="")
-
- # indexing
- if len(ps.asset_paths) > ps.asset_paths_active >= 0:
- ap = ps.asset_paths[ps.asset_paths_active]
- row = layout.row()
- row.prop(ap, 'overwrite')
-
- layout.separator()
-
- # publishing list
- row = layout.row(align=True)
- row.label("Platforms")
- row.prop(ps, 'publish_default_platform')
-
- # UI_UL_list
- row = layout.row()
- row.template_list("RENDER_UL_platforms", "platforms_list", ps, 'platforms', ps, 'platforms_active')
-
- # operators
- col = row.column(align=True)
- col.operator(PublishAddPlatform.bl_idname, icon='ZOOMIN', text="")
- col.operator(PublishRemovePlatform.bl_idname, icon='ZOOMOUT', text="")
- col.menu("PUBLISH_MT_platform_specials", icon='DOWNARROW_HLT', text="")
-
- # indexing
- if len(ps.platforms) > ps.platforms_active >= 0:
- platform = ps.platforms[ps.platforms_active]
- layout.prop(platform, 'name')
- layout.prop(platform, 'player_path')
-
- layout.operator(PublishAllPlatforms.bl_idname, 'Publish Platforms')
-
-
-class PublishAutoPlatforms(bpy.types.Operator):
- bl_idname = "scene.publish_auto_platforms"
- bl_label = "Auto Add Platforms"
-
- def execute(self, context):
- ps = context.scene.ge_publish_settings
-
- # verify lib folder
- lib_path = bpy.path.abspath(ps.lib_path)
- if not os.path.exists(lib_path):
- self.report({'ERROR'}, "Could not add platforms, lib folder (%s) does not exist" % lib_path)
- return {'CANCELLED'}
-
- for lib in [i for i in os.listdir(lib_path) if os.path.isdir(os.path.join(lib_path, i))]:
- print("Found folder:", lib)
- player_found = False
- for root, dirs, files in os.walk(os.path.join(lib_path, lib)):
- if "__MACOSX" in root:
- continue
-
- for f in dirs + files:
- if f.startswith("blenderplayer.app") or f.startswith("blenderplayer"):
- a = ps.platforms.add()
- if lib.startswith('blender-'):
- # Clean up names for packages from blender.org
- # example: blender-2.71-RC2-OSX_10.6-x86_64.zip => OSX_10.6-x86_64.zip
- # We're pretty consistent on naming, so this should hold up.
- a.name = '-'.join(lib.split('-')[3 if 'rc' in lib.lower() else 2:])
- else:
- a.name = lib
- a.player_path = bpy.path.relpath(os.path.join(root, f))
- player_found = True
- break
-
- if player_found:
- break
-
- return {'FINISHED'}
-
-# TODO This operator takes a long time to run, which is bad for UX. Could this instead be done as some sort of
-# modal dialog? This could also allow users to select which platforms to download and give a better progress
-# indicator.
-class PublishDownloadPlatforms(bpy.types.Operator):
- bl_idname = "scene.publish_download_platforms"
- bl_label = "Download Platforms"
-
- def execute(self, context):
- import html.parser
- import urllib.request
-
- remote_platforms = []
-
- ps = context.scene.ge_publish_settings
-
- # create lib folder if not already available
- lib_path = bpy.path.abspath(ps.lib_path)
- if not os.path.exists(lib_path):
- os.makedirs(lib_path)
-
- print("Retrieving list of platforms from blender.org...", end=" ", flush=True)
-
- class AnchorParser(html.parser.HTMLParser):
- def handle_starttag(self, tag, attrs):
- if tag == 'a':
- for key, value in attrs:
- if key == 'href' and value.startswith('blender'):
- remote_platforms.append(value)
-
- url = 'http://download.blender.org/release/Blender' + bpy.app.version_string.split()[0]
- parser = AnchorParser()
- data = urllib.request.urlopen(url).read()
- parser.feed(str(data))
-
- print("done", flush=True)
-
- print("Downloading files (this will take a while depending on your internet connection speed).", flush=True)
- for i in remote_platforms:
- src = '/'.join((url, i))
- dst = os.path.join(lib_path, i)
-
- dst_dir = '.'.join([i for i in dst.split('.') if i not in {'zip', 'tar', 'bz2'}])
- if not os.path.exists(dst) and not os.path.exists(dst.split('.')[0]):
- print("Downloading " + src + "...", end=" ", flush=True)
- urllib.request.urlretrieve(src, dst)
- print("done", flush=True)
- else:
- print("Reusing existing file: " + dst, flush=True)
-
- print("Unpacking " + dst + "...", end=" ", flush=True)
- if os.path.exists(dst_dir):
- shutil.rmtree(dst_dir)
- shutil.unpack_archive(dst, dst_dir)
- print("done", flush=True)
-
- print("Creating platform from libs...", flush=True)
- bpy.ops.scene.publish_auto_platforms()
- return {'FINISHED'}
-
-
-class PublishAddPlatform(bpy.types.Operator):
- bl_idname = "scene.publish_add_platform"
- bl_label = "Add Publish Platform"
-
- def execute(self, context):
- a = context.scene.ge_publish_settings.platforms.add()
- a.name = a.name
- return {'FINISHED'}
-
-
-class PublishRemovePlatform(bpy.types.Operator):
- bl_idname = "scene.publish_remove_platform"
- bl_label = "Remove Publish Platform"
-
- def execute(self, context):
- ps = context.scene.ge_publish_settings
- if ps.platforms_active < len(ps.platforms):
- ps.platforms.remove(ps.platforms_active)
- return {'FINISHED'}
- return {'CANCELLED'}
-
-
-# TODO maybe this should display a file browser?
-class PublishAddAssetPath(bpy.types.Operator):
- bl_idname = "scene.publish_add_assetpath"
- bl_label = "Add Asset Path"
-
- def execute(self, context):
- a = context.scene.ge_publish_settings.asset_paths.add()
- a.name = a.name
- return {'FINISHED'}
-
-
-class PublishRemoveAssetPath(bpy.types.Operator):
- bl_idname = "scene.publish_remove_assetpath"
- bl_label = "Remove Asset Path"
-
- def execute(self, context):
- ps = context.scene.ge_publish_settings
- if ps.asset_paths_active < len(ps.asset_paths):
- ps.asset_paths.remove(ps.asset_paths_active)
- return {'FINISHED'}
- return {'CANCELLED'}
-
-
-class PUBLISH_MT_platform_specials(bpy.types.Menu):
- bl_label = "Platform Specials"
-
- def draw(self, context):
- layout = self.layout
- layout.operator(PublishAutoPlatforms.bl_idname)
- layout.operator(PublishDownloadPlatforms.bl_idname)
-
-
-class PlatformSettings(bpy.types.PropertyGroup):
- name = bpy.props.StringProperty(
- name = "Platform Name",
- description = "The name of the platform",
- default = "Platform",
- )
-
- player_path = bpy.props.StringProperty(
- name = "Player Path",
- description = "The path to the Blenderplayer to use for this platform",
- default = "//lib/platform/blenderplayer",
- subtype = 'FILE_PATH',
- )
-
- publish = bpy.props.BoolProperty(
- name = "Publish",
- description = "Whether or not to publish to this platform",
- default = True,
- )
-
-
-class AssetPath(bpy.types.PropertyGroup):
- # TODO This needs a way to be a FILE_PATH or a DIR_PATH
- name = bpy.props.StringProperty(
- name = "Asset Path",
- description = "Path to the asset to be copied",
- default = "//src",
- subtype = 'FILE_PATH',
- )
-
- overwrite = bpy.props.BoolProperty(
- name = "Overwrite Asset",
- description = "Overwrite the asset if it already exists in the destination folder",
- default = True,
- )
-
-
-class PublishSettings(bpy.types.PropertyGroup):
- output_path = bpy.props.StringProperty(
- name = "Publish Output",
- description = "Where to publish the game",
- default = "//bin/",
- subtype = 'DIR_PATH',
- )
-
- runtime_name = bpy.props.StringProperty(
- name = "Runtime name",
- description = "The filename for the created runtime",
- default = "game",
- )
-
- lib_path = bpy.props.StringProperty(
- name = "Library Path",
- description = "Directory to search for platforms",
- default = "//lib/",
- subtype = 'DIR_PATH',
- )
-
- publish_default_platform = bpy.props.BoolProperty(
- name = "Publish Default Platform",
- description = "Whether or not to publish the default platform (the Blender install running this addon) when publishing platforms",
- default = True,
- )
-
-
- platforms = bpy.props.CollectionProperty(type=PlatformSettings, name="Platforms")
- platforms_active = bpy.props.IntProperty()
-
- asset_paths = bpy.props.CollectionProperty(type=AssetPath, name="Asset Paths")
- asset_paths_active = bpy.props.IntProperty()
-
- make_archive = bpy.props.BoolProperty(
- name = "Make Archive",
- description = "Create a zip archive of the published game",
- default = True,
- )
-
-
-def register():
- bpy.utils.register_module(__name__)
-
- bpy.types.Scene.ge_publish_settings = bpy.props.PointerProperty(type=PublishSettings)
-
-
-def unregister():
- bpy.utils.unregister_module(__name__)
- del bpy.types.Scene.ge_publish_settings
-
-
-if __name__ == "__main__":
- register()