[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