[Bf-extensions-cvs] [c5fddd6] master: BGE Publishing: Adding a new addon to help make publishing games easier.

Mitchell Stokes noreply at git.blender.org
Thu Jun 26 05:43:58 CEST 2014


Commit: c5fddd6f67ecde0aaca60337f0a3a5e635d3b671
Author: Mitchell Stokes
Date:   Wed Jun 25 16:49:50 2014 -0700
https://developer.blender.org/rBAc5fddd6f67ecde0aaca60337f0a3a5e635d3b671

BGE Publishing: Adding a new addon to help make publishing games easier.

Some highlights:
  * New panel in the render options to control publishing
  * Easier cross-platform publishing
  * Ability to archive published binaries

More information can be found on the addons wiki page:
http://wiki.blender.org/index.php/Extensions:2.6/Py/Scripts/Game_Engine/Publishing

Note: This addon is intended to replace the Save As Runtime addon.

===================================================================

A	game_engine_publishing.py

===================================================================

diff --git a/game_engine_publishing.py b/game_engine_publishing.py
new file mode 100644
index 0000000..991a9ac
--- /dev/null
+++ b/game_engine_publishing.py
@@ -0,0 +1,541 @@
+# ##### 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)",
+    "version": (0, 1, 0),
+    "blender": (2, 72, 0),
+    "location": "Render Properties > Publishing Info",
+    "description": "",
+    "warning": "beta",
+    "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
+        file = open(player_path, 'rb')
+        player_d = file.read()
+        offset = file.tell()
+        file.close()
+
+        # 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
+        blend_file = open(blend_path, 'rb')
+        blend_d = blend_file.read()
+        blend_file.close()
+
+        # 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
+        output = open(output_path, 'wb')
+
+        # 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')
+        output.close()
+
+        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_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
+
+        

@@ Diff output truncated at 10240 characters. @@



More information about the Bf-extensions-cvs mailing list