dama 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (107) hide show
  1. checksums.yaml +7 -0
  2. data/LICENSE +21 -0
  3. data/README.md +227 -0
  4. data/dama-logo.svg +91 -0
  5. data/exe/dama +4 -0
  6. data/ext/dama_native/.cargo/config.toml +3 -0
  7. data/ext/dama_native/Cargo.lock +3575 -0
  8. data/ext/dama_native/Cargo.toml +39 -0
  9. data/ext/dama_native/extconf.rb +72 -0
  10. data/ext/dama_native/src/audio.rs +134 -0
  11. data/ext/dama_native/src/engine.rs +339 -0
  12. data/ext/dama_native/src/lib.rs +396 -0
  13. data/ext/dama_native/src/renderer/screenshot.rs +84 -0
  14. data/ext/dama_native/src/renderer/shape_renderer.rs +507 -0
  15. data/ext/dama_native/src/renderer/text_renderer.rs +192 -0
  16. data/ext/dama_native/src/renderer.rs +563 -0
  17. data/ext/dama_native/src/window.rs +255 -0
  18. data/lib/dama/animation.rb +66 -0
  19. data/lib/dama/asset_cache.rb +56 -0
  20. data/lib/dama/audio.rb +47 -0
  21. data/lib/dama/auto_loader.rb +54 -0
  22. data/lib/dama/backend/base.rb +137 -0
  23. data/lib/dama/backend/native/ffi_bindings.rb +122 -0
  24. data/lib/dama/backend/native.rb +191 -0
  25. data/lib/dama/backend/web.rb +199 -0
  26. data/lib/dama/backend.rb +13 -0
  27. data/lib/dama/camera.rb +68 -0
  28. data/lib/dama/cli/new_project.rb +112 -0
  29. data/lib/dama/cli/release.rb +45 -0
  30. data/lib/dama/cli.rb +22 -0
  31. data/lib/dama/colors.rb +30 -0
  32. data/lib/dama/command_buffer.rb +83 -0
  33. data/lib/dama/component/attribute_definition.rb +13 -0
  34. data/lib/dama/component/attribute_set.rb +32 -0
  35. data/lib/dama/component.rb +28 -0
  36. data/lib/dama/configuration.rb +18 -0
  37. data/lib/dama/debug/frame_controller.rb +35 -0
  38. data/lib/dama/debug/screenshot_tool.rb +19 -0
  39. data/lib/dama/debug.rb +4 -0
  40. data/lib/dama/event_bus.rb +47 -0
  41. data/lib/dama/game/builder.rb +31 -0
  42. data/lib/dama/game/loop.rb +44 -0
  43. data/lib/dama/game.rb +88 -0
  44. data/lib/dama/geometry/circle.rb +28 -0
  45. data/lib/dama/geometry/rect.rb +16 -0
  46. data/lib/dama/geometry/sprite.rb +18 -0
  47. data/lib/dama/geometry/triangle.rb +13 -0
  48. data/lib/dama/geometry.rb +4 -0
  49. data/lib/dama/input/keyboard_state.rb +44 -0
  50. data/lib/dama/input/mouse_state.rb +45 -0
  51. data/lib/dama/input.rb +38 -0
  52. data/lib/dama/keys.rb +67 -0
  53. data/lib/dama/node/component_slot.rb +18 -0
  54. data/lib/dama/node/draw_context.rb +96 -0
  55. data/lib/dama/node.rb +139 -0
  56. data/lib/dama/physics/body.rb +57 -0
  57. data/lib/dama/physics/collider.rb +152 -0
  58. data/lib/dama/physics/collision.rb +15 -0
  59. data/lib/dama/physics/world.rb +125 -0
  60. data/lib/dama/physics.rb +4 -0
  61. data/lib/dama/registry/class_resolver.rb +48 -0
  62. data/lib/dama/registry.rb +21 -0
  63. data/lib/dama/release/archiver.rb +100 -0
  64. data/lib/dama/release/defaults/icon.icns +0 -0
  65. data/lib/dama/release/defaults/icon.ico +0 -0
  66. data/lib/dama/release/defaults/icon.png +0 -0
  67. data/lib/dama/release/dylib_relinker.rb +95 -0
  68. data/lib/dama/release/game_file_copier.rb +35 -0
  69. data/lib/dama/release/game_metadata.rb +61 -0
  70. data/lib/dama/release/icon_provider.rb +36 -0
  71. data/lib/dama/release/native_builder.rb +44 -0
  72. data/lib/dama/release/packager/linux.rb +62 -0
  73. data/lib/dama/release/packager/macos.rb +99 -0
  74. data/lib/dama/release/packager/web.rb +32 -0
  75. data/lib/dama/release/packager/windows.rb +61 -0
  76. data/lib/dama/release/packager.rb +9 -0
  77. data/lib/dama/release/platform_detector.rb +23 -0
  78. data/lib/dama/release/ruby_bundler.rb +163 -0
  79. data/lib/dama/release/stdlib_trimmer.rb +133 -0
  80. data/lib/dama/release/template_renderer.rb +40 -0
  81. data/lib/dama/release/templates/info_plist.xml.erb +19 -0
  82. data/lib/dama/release/templates/launcher_linux.sh.erb +10 -0
  83. data/lib/dama/release/templates/launcher_macos.sh.erb +10 -0
  84. data/lib/dama/release/templates/launcher_windows.bat.erb +11 -0
  85. data/lib/dama/release.rb +7 -0
  86. data/lib/dama/scene/composer.rb +65 -0
  87. data/lib/dama/scene.rb +233 -0
  88. data/lib/dama/scene_graph/class_index.rb +26 -0
  89. data/lib/dama/scene_graph/group_node.rb +27 -0
  90. data/lib/dama/scene_graph/instance_node.rb +30 -0
  91. data/lib/dama/scene_graph/path_selector.rb +25 -0
  92. data/lib/dama/scene_graph/query.rb +34 -0
  93. data/lib/dama/scene_graph/tag_index.rb +26 -0
  94. data/lib/dama/scene_graph/tree.rb +65 -0
  95. data/lib/dama/scene_graph.rb +4 -0
  96. data/lib/dama/sprite_sheet.rb +36 -0
  97. data/lib/dama/tween/easing.rb +31 -0
  98. data/lib/dama/tween/lerp.rb +35 -0
  99. data/lib/dama/tween/manager.rb +28 -0
  100. data/lib/dama/tween.rb +4 -0
  101. data/lib/dama/version.rb +3 -0
  102. data/lib/dama/vertex_batch.rb +35 -0
  103. data/lib/dama/web/entry.rb +79 -0
  104. data/lib/dama/web/static/index.html +142 -0
  105. data/lib/dama/web_builder.rb +232 -0
  106. data/lib/dama.rb +42 -0
  107. metadata +198 -0
@@ -0,0 +1,99 @@
1
+ require "cgi"
2
+ require "fileutils"
3
+
4
+ module Dama
5
+ module Release
6
+ module Packager
7
+ # Creates a self-contained macOS .app bundle containing
8
+ # the Ruby runtime, gems, native library, game code, and assets.
9
+ class Macos
10
+ def initialize(project_root:)
11
+ @project_root = project_root
12
+ @metadata = GameMetadata.new(project_root:)
13
+ @icon_provider = IconProvider.new(project_root:, platform: :macos)
14
+ end
15
+
16
+ def package
17
+ native_library_path = NativeBuilder.new.build
18
+ prepare_app_structure
19
+ ruby_path = RubyBundler.new(destination: resources_path, project_root:).bundle
20
+ relink_dylibs(ruby_path:)
21
+ FileUtils.cp(native_library_path, resources_path)
22
+ GameFileCopier.new(project_root:, destination: resources_path).copy
23
+ copy_icon
24
+ write_info_plist
25
+ write_launcher_script(native_library_path:)
26
+
27
+ archive_path = Archiver.new(source_path: app_path).create_macos_zip
28
+ puts "macOS release created: #{archive_path}"
29
+ end
30
+
31
+ private
32
+
33
+ attr_reader :project_root, :metadata, :icon_provider
34
+
35
+ def app_name
36
+ "#{metadata.release_name}.app"
37
+ end
38
+
39
+ def app_path
40
+ File.join(project_root, "release", app_name)
41
+ end
42
+
43
+ def contents_path
44
+ File.join(app_path, "Contents")
45
+ end
46
+
47
+ def resources_path
48
+ File.join(contents_path, "Resources")
49
+ end
50
+
51
+ def macos_path
52
+ File.join(contents_path, "MacOS")
53
+ end
54
+
55
+ def prepare_app_structure
56
+ FileUtils.rm_rf(app_path)
57
+ FileUtils.mkdir_p(macos_path)
58
+ FileUtils.mkdir_p(resources_path)
59
+ end
60
+
61
+ # Rewrites hardcoded absolute dylib paths in the bundled Ruby binary
62
+ # to use @loader_path-relative references, so the .app works on any Mac.
63
+ def relink_dylibs(ruby_path:)
64
+ ruby_binary_name = RbConfig::CONFIG.fetch("ruby_install_name")
65
+ ruby_binary_path = File.join(ruby_path, "bin", ruby_binary_name)
66
+ lib_destination = File.join(ruby_path, "lib")
67
+ DylibRelinker.new(binary_path: ruby_binary_path, lib_destination:).relink
68
+ end
69
+
70
+ def copy_icon
71
+ icon_source = icon_provider.icon_path
72
+ FileUtils.cp(icon_source, File.join(resources_path, "icon.icns")) if File.exist?(icon_source)
73
+ end
74
+
75
+ def write_info_plist
76
+ content = TemplateRenderer.new(
77
+ template_name: "info_plist.xml.erb",
78
+ variables: { escaped_title: CGI.escapeHTML(metadata.title) },
79
+ ).render
80
+ File.write(File.join(contents_path, "Info.plist"), content)
81
+ end
82
+
83
+ def write_launcher_script(native_library_path:)
84
+ launcher_path = File.join(macos_path, "launch")
85
+ content = TemplateRenderer.new(
86
+ template_name: "launcher_macos.sh.erb",
87
+ variables: {
88
+ native_lib_name: File.basename(native_library_path),
89
+ ruby_version: RbConfig::CONFIG.fetch("ruby_version"),
90
+ ruby_arch: RbConfig::CONFIG.fetch("arch"),
91
+ },
92
+ ).render
93
+ File.write(launcher_path, content)
94
+ FileUtils.chmod(0o755, launcher_path)
95
+ end
96
+ end
97
+ end
98
+ end
99
+ end
@@ -0,0 +1,32 @@
1
+ require "fileutils"
2
+
3
+ module Dama
4
+ module Release
5
+ module Packager
6
+ # Produces a web-deployable build by delegating to
7
+ # WebBuilder, then copying the output to release/web/.
8
+ class Web
9
+ def initialize(project_root:)
10
+ @project_root = project_root
11
+ end
12
+
13
+ def package
14
+ builder = Dama::WebBuilder.new(project_root:)
15
+ builder.build
16
+
17
+ release_dir = File.join(project_root, "release", "web")
18
+ FileUtils.rm_rf(release_dir)
19
+ FileUtils.mkdir_p(File.dirname(release_dir))
20
+ FileUtils.cp_r(File.join(project_root, "dist"), release_dir)
21
+
22
+ archive_path = Archiver.new(source_path: release_dir).create_zip
23
+ puts "Web release created: #{archive_path}"
24
+ end
25
+
26
+ private
27
+
28
+ attr_reader :project_root
29
+ end
30
+ end
31
+ end
32
+ end
@@ -0,0 +1,61 @@
1
+ require "fileutils"
2
+
3
+ module Dama
4
+ module Release
5
+ module Packager
6
+ # Creates a self-contained Windows directory with a .bat launcher,
7
+ # bundled Ruby, native .dll, game files, and assets.
8
+ class Windows
9
+ def initialize(project_root:)
10
+ @project_root = project_root
11
+ @metadata = GameMetadata.new(project_root:)
12
+ @icon_provider = IconProvider.new(project_root:, platform: :windows)
13
+ end
14
+
15
+ def package
16
+ native_library_path = NativeBuilder.new.build
17
+ prepare_structure
18
+ RubyBundler.new(destination: release_path, project_root:).bundle
19
+ FileUtils.cp(native_library_path, release_path)
20
+ GameFileCopier.new(project_root:, destination: release_path).copy
21
+ copy_icon
22
+ write_launcher_script(native_library_path:)
23
+
24
+ archive_path = Archiver.new(source_path: release_path).create_zip
25
+ puts "Windows release created: #{archive_path}"
26
+ end
27
+
28
+ private
29
+
30
+ attr_reader :project_root, :metadata, :icon_provider
31
+
32
+ def release_path
33
+ File.join(project_root, "release", metadata.release_name)
34
+ end
35
+
36
+ def prepare_structure
37
+ FileUtils.rm_rf(release_path)
38
+ FileUtils.mkdir_p(release_path)
39
+ end
40
+
41
+ def copy_icon
42
+ icon_source = icon_provider.icon_path
43
+ FileUtils.cp(icon_source, File.join(release_path, "icon.ico")) if File.exist?(icon_source)
44
+ end
45
+
46
+ def write_launcher_script(native_library_path:)
47
+ launcher_path = File.join(release_path, "#{metadata.release_name}.bat")
48
+ content = TemplateRenderer.new(
49
+ template_name: "launcher_windows.bat.erb",
50
+ variables: {
51
+ native_lib_name: File.basename(native_library_path),
52
+ ruby_version: RbConfig::CONFIG.fetch("ruby_version"),
53
+ ruby_arch: RbConfig::CONFIG.fetch("arch"),
54
+ },
55
+ ).render
56
+ File.write(launcher_path, content)
57
+ end
58
+ end
59
+ end
60
+ end
61
+ end
@@ -0,0 +1,9 @@
1
+ module Dama
2
+ module Release
3
+ # Namespace for platform-specific packagers.
4
+ # Each packager creates a self-contained distributable
5
+ # for its target platform.
6
+ module Packager
7
+ end
8
+ end
9
+ end
@@ -0,0 +1,23 @@
1
+ module Dama
2
+ module Release
3
+ # Resolves the current OS into a platform symbol
4
+ # used to select the correct packager.
5
+ class PlatformDetector
6
+ PLATFORMS = {
7
+ "darwin" => :macos,
8
+ "linux" => :linux,
9
+ "mingw" => :windows,
10
+ "mswin" => :windows,
11
+ }.freeze
12
+
13
+ class UnsupportedPlatformError < StandardError; end
14
+
15
+ def self.detect
16
+ platform_key = PLATFORMS.keys.detect { |k| RUBY_PLATFORM.include?(k) }
17
+ raise UnsupportedPlatformError, "Unsupported platform: #{RUBY_PLATFORM}" unless platform_key
18
+
19
+ PLATFORMS.fetch(platform_key)
20
+ end
21
+ end
22
+ end
23
+ end
@@ -0,0 +1,163 @@
1
+ require "bundler"
2
+ require "fileutils"
3
+ require "rbconfig"
4
+
5
+ module Dama
6
+ module Release
7
+ # Copies the Ruby runtime and gem dependencies into a release directory.
8
+ # Creates a self-contained Ruby environment so the packaged game
9
+ # can run without a system Ruby installation.
10
+ class RubyBundler
11
+ def initialize(destination:, project_root:)
12
+ @destination = destination
13
+ @project_root = project_root
14
+ end
15
+
16
+ def bundle
17
+ copy_ruby_runtime
18
+ install_gems
19
+ ruby_destination
20
+ end
21
+
22
+ private
23
+
24
+ attr_reader :destination, :project_root
25
+
26
+ def ruby_destination
27
+ File.join(destination, "ruby")
28
+ end
29
+
30
+ def copy_ruby_runtime
31
+ ruby_binary = RbConfig.ruby
32
+ dest_bindir = File.join(ruby_destination, "bin")
33
+
34
+ FileUtils.mkdir_p(dest_bindir)
35
+ FileUtils.cp(ruby_binary, File.join(dest_bindir, File.basename(ruby_binary)))
36
+
37
+ copy_shared_library
38
+ copy_ruby_stdlib
39
+ end
40
+
41
+ # Copies the Ruby shared library (e.g. libruby.3.4.dylib) into
42
+ # the bundle so the binary doesn't depend on the build machine's paths.
43
+ def copy_shared_library
44
+ shared_lib = RbConfig::CONFIG.fetch("LIBRUBY_SO")
45
+ return if shared_lib.empty?
46
+
47
+ source = File.join(RbConfig::CONFIG.fetch("libdir"), shared_lib)
48
+ return unless File.exist?(source)
49
+
50
+ dest_libdir = File.join(ruby_destination, "lib")
51
+ FileUtils.mkdir_p(dest_libdir)
52
+ FileUtils.cp(source, dest_libdir)
53
+ end
54
+
55
+ def copy_ruby_stdlib
56
+ rubylibdir = RbConfig::CONFIG.fetch("rubylibdir")
57
+ dest_libdir = File.join(ruby_destination, "lib", "ruby", RbConfig::CONFIG.fetch("ruby_version"))
58
+ FileUtils.mkdir_p(dest_libdir)
59
+ FileUtils.cp_r("#{rubylibdir}/.", dest_libdir)
60
+
61
+ archdir = RbConfig::CONFIG.fetch("archdir")
62
+ dest_archdir = File.join(dest_libdir, RbConfig::CONFIG.fetch("arch"))
63
+ FileUtils.mkdir_p(dest_archdir)
64
+ FileUtils.cp_r("#{archdir}/.", dest_archdir)
65
+
66
+ StdlibTrimmer.new(stdlib_dir: dest_libdir, arch_dir: dest_archdir).trim
67
+ end
68
+
69
+ # Runs bundle install using the project's original Gemfile so that
70
+ # relative path: references (e.g. path: "../..") resolve correctly
71
+ # against the project root, not the release directory.
72
+ # Uses env vars instead of CLI flags to avoid writing .bundle/config
73
+ # into the project directory. The --standalone flag generates a
74
+ # bundler/setup.rb that sets up load paths without Bundler at runtime.
75
+ def install_gems
76
+ vendor_dir = File.join(destination, "vendor", "bundle")
77
+ FileUtils.mkdir_p(vendor_dir)
78
+
79
+ gemfile = File.join(project_root, "Gemfile")
80
+ return unless File.exist?(gemfile)
81
+
82
+ Bundler.with_unbundled_env do
83
+ env = {
84
+ "BUNDLE_PATH" => vendor_dir,
85
+ "BUNDLE_GEMFILE" => gemfile,
86
+ }
87
+ success = system(env, "bundle", "install", "--standalone")
88
+ raise "Gem bundling failed" unless success
89
+ end
90
+
91
+ embed_path_gems(vendor_dir:, gemfile:)
92
+ end
93
+
94
+ # Bundler's --standalone mode generates relative paths for path: gems
95
+ # (e.g. path: "../..") that point back to the build machine's source tree.
96
+ # For a portable app bundle, we copy each path gem's lib/ into the vendor
97
+ # directory and rewrite setup.rb so it references the local copy.
98
+ def embed_path_gems(vendor_dir:, gemfile:)
99
+ setup_rb = File.join(vendor_dir, "bundler", "setup.rb")
100
+ return unless File.exist?(setup_rb)
101
+
102
+ gems = path_gem_lines(gemfile:)
103
+ return if gems.empty?
104
+
105
+ source_to_name = copy_path_gems(gems:, vendor_dir:)
106
+ rewrite_setup_rb(
107
+ setup_rb:,
108
+ bundler_dir: File.join(vendor_dir, "bundler"),
109
+ source_to_name:,
110
+ )
111
+ end
112
+
113
+ def copy_path_gems(gems:, vendor_dir:)
114
+ gems.to_h do |gem_name, source_lib|
115
+ embedded_lib = File.join(vendor_dir, "path_gems", gem_name, "lib")
116
+ FileUtils.mkdir_p(embedded_lib)
117
+ FileUtils.cp_r("#{source_lib}/.", embedded_lib)
118
+ [source_lib, gem_name]
119
+ end
120
+ end
121
+
122
+ # Rewrites path-gem entries in setup.rb to reference the embedded copies.
123
+ # Identifies each line's target gem by resolving its relative path back to
124
+ # an absolute path and matching against known source lib directories.
125
+ # This handles multiple path gems correctly, unlike a generic regex gsub.
126
+ def rewrite_setup_rb(setup_rb:, bundler_dir:, source_to_name:)
127
+ content = File.readlines(setup_rb).map do |line|
128
+ gem_name = match_path_gem(line:, bundler_dir:, source_to_name:)
129
+ gem_name ? path_gem_load_line(gem_name:) : line
130
+ end.join
131
+
132
+ File.write(setup_rb, content)
133
+ end
134
+
135
+ # Matches any $:.unshift line that loads from a #{__dir__}-relative path.
136
+ # The captured rel_path is resolved against the bundler directory to
137
+ # determine which path gem (if any) this line references.
138
+ SETUP_LOAD_PATTERN = %r{\A\$:\.\s*unshift\s+File\.expand_path\("\#\{__dir__\}/(?<rel_path>[^"]+)"\)\n?\z}
139
+
140
+ def match_path_gem(line:, bundler_dir:, source_to_name:)
141
+ match = line.match(SETUP_LOAD_PATTERN)
142
+ return unless match
143
+
144
+ resolved = File.expand_path(match[:rel_path], bundler_dir)
145
+ source_to_name[resolved]
146
+ end
147
+
148
+ def path_gem_load_line(gem_name:)
149
+ "$:.unshift File.expand_path(\"\#{__dir__}/../path_gems/#{gem_name}/lib\")\n"
150
+ end
151
+
152
+ def path_gem_lines(gemfile:)
153
+ File.readlines(gemfile).filter_map do |line|
154
+ match = line.match(/gem\s+"(?<name>[^"]+)".*path:\s*"(?<path>[^"]+)"/)
155
+ next unless match
156
+
157
+ gem_path = File.expand_path(match[:path], File.dirname(gemfile))
158
+ [match[:name], File.join(gem_path, "lib")]
159
+ end
160
+ end
161
+ end
162
+ end
163
+ end
@@ -0,0 +1,133 @@
1
+ require "fileutils"
2
+
3
+ module Dama
4
+ module Release
5
+ # Removes stdlib modules and native extensions that games don't
6
+ # need at runtime, reducing the bundled Ruby size by ~60%.
7
+ # Uses an exclusion-based approach (safer than a whitelist) so
8
+ # new stdlib additions aren't accidentally removed.
9
+ class StdlibTrimmer
10
+ # Pure-Ruby stdlib directories that are build tools, documentation
11
+ # generators, or parser infrastructure -- never needed at game runtime.
12
+ # Networking (net/, openssl/) and other libraries are intentionally
13
+ # kept since games may use them for multiplayer, asset loading, etc.
14
+ EXCLUDED_DIRS = %w[
15
+ bundler
16
+ did_you_mean
17
+ error_highlight
18
+ irb
19
+ prism
20
+ racc
21
+ rbs
22
+ rdoc
23
+ reline
24
+ ripper
25
+ ruby_vm
26
+ rubygems
27
+ syntax_suggest
28
+ ].freeze
29
+
30
+ # Top-level .rb files that correspond to excluded directories
31
+ # or standalone modules not needed at runtime.
32
+ EXCLUDED_FILES = %w[
33
+ bundler.rb
34
+ bundled_gems.rb
35
+ debug.rb
36
+ did_you_mean.rb
37
+ error_highlight.rb
38
+ irb.rb
39
+ mkmf.rb
40
+ prism.rb
41
+ rdoc.rb
42
+ reline.rb
43
+ ripper.rb
44
+ rubygems.rb
45
+ syntax_suggest.rb
46
+ ].freeze
47
+
48
+ # Native extensions for build/dev-only tools.
49
+ EXCLUDED_NATIVE_EXTENSIONS = %w[
50
+ ripper
51
+ ].freeze
52
+
53
+ def initialize(stdlib_dir:, arch_dir:)
54
+ @stdlib_dir = stdlib_dir
55
+ @arch_dir = arch_dir
56
+ end
57
+
58
+ def trim
59
+ remove_excluded_dirs
60
+ remove_excluded_files
61
+ remove_excluded_native_extensions
62
+ remove_debug_symbols
63
+ trim_encodings
64
+ end
65
+
66
+ private
67
+
68
+ attr_reader :stdlib_dir, :arch_dir
69
+
70
+ def remove_excluded_dirs
71
+ EXCLUDED_DIRS.each do |dir|
72
+ FileUtils.rm_rf(File.join(stdlib_dir, dir))
73
+ end
74
+ end
75
+
76
+ def remove_excluded_files
77
+ EXCLUDED_FILES.each do |file|
78
+ FileUtils.rm_f(File.join(stdlib_dir, file))
79
+ end
80
+ end
81
+
82
+ def remove_excluded_native_extensions
83
+ EXCLUDED_NATIVE_EXTENSIONS.each do |name|
84
+ Dir.glob(File.join(arch_dir, "#{name}.*")).each { |f| FileUtils.rm_rf(f) }
85
+ FileUtils.rm_rf(File.join(arch_dir, name))
86
+ end
87
+ end
88
+
89
+ # Debug symbol directories (.dSYM) are macOS build artifacts
90
+ # that add megabytes without any runtime benefit.
91
+ def remove_debug_symbols
92
+ Dir.glob(File.join(arch_dir, "**", "*.dSYM")).each do |dsym|
93
+ FileUtils.rm_rf(dsym)
94
+ end
95
+ end
96
+
97
+ # Ruby's encoding directory contains bundles for dozens of legacy
98
+ # character encodings. Games only need the encoding database
99
+ # (encdb) and transcoding database (transdb) that Ruby loads at boot,
100
+ # plus UTF-8 which is already compiled into the Ruby binary.
101
+ def trim_encodings
102
+ enc_dir = File.join(arch_dir, "enc")
103
+ return unless File.directory?(enc_dir)
104
+
105
+ keep_encoding_essentials(enc_dir:)
106
+ end
107
+
108
+ def keep_encoding_essentials(enc_dir:)
109
+ trans_dir = File.join(enc_dir, "trans")
110
+
111
+ essential_files = [
112
+ File.join(enc_dir, "encdb.bundle"),
113
+ File.join(enc_dir, "encdb.so"),
114
+ ].select { |f| File.exist?(f) }
115
+
116
+ essential_trans = [
117
+ File.join(trans_dir, "transdb.bundle"),
118
+ File.join(trans_dir, "transdb.so"),
119
+ ].select { |f| File.exist?(f) }
120
+
121
+ # Remove everything except the essential encoding files
122
+ non_essential_entries(enc_dir:, essential_files:).each { |e| FileUtils.rm_rf(e) }
123
+ non_essential_entries(enc_dir: trans_dir, essential_files: essential_trans).each { |e| FileUtils.rm_rf(e) }
124
+ end
125
+
126
+ def non_essential_entries(enc_dir:, essential_files:)
127
+ Dir.children(enc_dir)
128
+ .map { |child| File.join(enc_dir, child) }
129
+ .reject { |path| essential_files.include?(path) || File.directory?(path) }
130
+ end
131
+ end
132
+ end
133
+ end
@@ -0,0 +1,40 @@
1
+ require "erb"
2
+
3
+ module Dama
4
+ module Release
5
+ # Renders ERB templates from the templates/ directory.
6
+ # Separates template content (shell scripts, XML plists) from
7
+ # Ruby packaging logic so each can be edited independently.
8
+ class TemplateRenderer
9
+ TEMPLATES_DIR = File.expand_path("templates", __dir__)
10
+
11
+ def initialize(template_name:, variables:)
12
+ @template_name = template_name
13
+ @variables = variables
14
+ end
15
+
16
+ def render
17
+ template = ERB.new(template_content, trim_mode: "-")
18
+ template.result(template_binding)
19
+ end
20
+
21
+ private
22
+
23
+ attr_reader :template_name, :variables
24
+
25
+ def template_content
26
+ File.read(File.join(TEMPLATES_DIR, template_name))
27
+ end
28
+
29
+ # Builds a clean binding where each variable key becomes
30
+ # a local method, keeping templates simple and explicit.
31
+ def template_binding
32
+ namespace = Object.new
33
+ variables.each do |key, value|
34
+ namespace.define_singleton_method(key) { value }
35
+ end
36
+ namespace.instance_eval { binding }
37
+ end
38
+ end
39
+ end
40
+ end
@@ -0,0 +1,19 @@
1
+ <?xml version="1.0" encoding="UTF-8"?>
2
+ <!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN"
3
+ "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
4
+ <plist version="1.0">
5
+ <dict>
6
+ <key>CFBundleName</key>
7
+ <string><%= escaped_title %></string>
8
+ <key>CFBundleDisplayName</key>
9
+ <string><%= escaped_title %></string>
10
+ <key>CFBundleExecutable</key>
11
+ <string>launch</string>
12
+ <key>CFBundleIconFile</key>
13
+ <string>icon</string>
14
+ <key>CFBundlePackageType</key>
15
+ <string>APPL</string>
16
+ <key>NSHighResolutionCapable</key>
17
+ <true/>
18
+ </dict>
19
+ </plist>
@@ -0,0 +1,10 @@
1
+ #!/usr/bin/env bash
2
+ DIR="$(cd "$(dirname "$0")" && pwd)"
3
+
4
+ export DAMA_NATIVE_LIB="$DIR/<%= native_lib_name %>"
5
+ export GEM_PATH="$DIR/vendor/bundle"
6
+ export BUNDLE_GEMFILE="$DIR/Gemfile"
7
+ export RUBYLIB="$DIR/ruby/lib/ruby/<%= ruby_version %>:$DIR/ruby/lib/ruby/<%= ruby_version %>/<%= ruby_arch %>"
8
+
9
+ exec "$DIR/ruby/bin/ruby" -r "$DIR/vendor/bundle/bundler/setup.rb" \
10
+ -e "require 'dama'; Dama.boot(root: \"$DIR\")"
@@ -0,0 +1,10 @@
1
+ #!/usr/bin/env bash
2
+ DIR="$(cd "$(dirname "$0")/../Resources" && pwd)"
3
+
4
+ export DAMA_NATIVE_LIB="$DIR/<%= native_lib_name %>"
5
+ export GEM_PATH="$DIR/vendor/bundle"
6
+ export BUNDLE_GEMFILE="$DIR/Gemfile"
7
+ export RUBYLIB="$DIR/ruby/lib/ruby/<%= ruby_version %>:$DIR/ruby/lib/ruby/<%= ruby_version %>/<%= ruby_arch %>"
8
+
9
+ exec "$DIR/ruby/bin/ruby" -r "$DIR/vendor/bundle/bundler/setup.rb" \
10
+ -e "require 'dama'; Dama.boot(root: \"$DIR\")"
@@ -0,0 +1,11 @@
1
+ @echo off
2
+ set DIR=%~dp0
3
+ if "%DIR:~-1%"=="\" set DIR=%DIR:~0,-1%
4
+
5
+ set DAMA_NATIVE_LIB=%DIR%\<%= native_lib_name %>
6
+ set GEM_PATH=%DIR%\vendor\bundle
7
+ set BUNDLE_GEMFILE=%DIR%\Gemfile
8
+ set RUBYLIB=%DIR%\ruby\lib\ruby\<%= ruby_version %>;%DIR%\ruby\lib\ruby\<%= ruby_version %>\<%= ruby_arch %>
9
+
10
+ "%DIR%\ruby\bin\ruby.exe" -r "%DIR%\vendor\bundle\bundler\setup.rb" ^
11
+ -e "require 'dama'; Dama.boot(root: '%DIR%')"
@@ -0,0 +1,7 @@
1
+ module Dama
2
+ # Packaging and distribution module for dama games.
3
+ # Creates self-contained, distributable game packages
4
+ # for the current OS/architecture or web deployment.
5
+ module Release
6
+ end
7
+ end