factorix 0.5.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.
- checksums.yaml +7 -0
- data/CHANGELOG.md +20 -0
- data/LICENSE.txt +21 -0
- data/README.md +105 -0
- data/completion/_factorix.bash +202 -0
- data/completion/_factorix.fish +197 -0
- data/completion/_factorix.zsh +376 -0
- data/doc/factorix.1 +377 -0
- data/exe/factorix +20 -0
- data/lib/factorix/api/category.rb +69 -0
- data/lib/factorix/api/image.rb +35 -0
- data/lib/factorix/api/license.rb +71 -0
- data/lib/factorix/api/mod_download_api.rb +66 -0
- data/lib/factorix/api/mod_info.rb +166 -0
- data/lib/factorix/api/mod_management_api.rb +237 -0
- data/lib/factorix/api/mod_portal_api.rb +204 -0
- data/lib/factorix/api/release.rb +49 -0
- data/lib/factorix/api/tag.rb +95 -0
- data/lib/factorix/api.rb +7 -0
- data/lib/factorix/api_credential.rb +54 -0
- data/lib/factorix/application.rb +218 -0
- data/lib/factorix/cache/file_system.rb +307 -0
- data/lib/factorix/cli/commands/backup_support.rb +46 -0
- data/lib/factorix/cli/commands/base.rb +90 -0
- data/lib/factorix/cli/commands/cache/evict.rb +180 -0
- data/lib/factorix/cli/commands/cache/stat.rb +201 -0
- data/lib/factorix/cli/commands/command_wrapper.rb +71 -0
- data/lib/factorix/cli/commands/completion.rb +83 -0
- data/lib/factorix/cli/commands/confirmable.rb +53 -0
- data/lib/factorix/cli/commands/download_support.rb +123 -0
- data/lib/factorix/cli/commands/launch.rb +79 -0
- data/lib/factorix/cli/commands/man.rb +29 -0
- data/lib/factorix/cli/commands/mod/check.rb +99 -0
- data/lib/factorix/cli/commands/mod/disable.rb +188 -0
- data/lib/factorix/cli/commands/mod/download.rb +291 -0
- data/lib/factorix/cli/commands/mod/edit.rb +114 -0
- data/lib/factorix/cli/commands/mod/enable.rb +216 -0
- data/lib/factorix/cli/commands/mod/image/add.rb +47 -0
- data/lib/factorix/cli/commands/mod/image/edit.rb +41 -0
- data/lib/factorix/cli/commands/mod/image/list.rb +74 -0
- data/lib/factorix/cli/commands/mod/install.rb +443 -0
- data/lib/factorix/cli/commands/mod/list.rb +372 -0
- data/lib/factorix/cli/commands/mod/search.rb +134 -0
- data/lib/factorix/cli/commands/mod/settings/dump.rb +88 -0
- data/lib/factorix/cli/commands/mod/settings/restore.rb +101 -0
- data/lib/factorix/cli/commands/mod/show.rb +202 -0
- data/lib/factorix/cli/commands/mod/sync.rb +299 -0
- data/lib/factorix/cli/commands/mod/uninstall.rb +325 -0
- data/lib/factorix/cli/commands/mod/update.rb +222 -0
- data/lib/factorix/cli/commands/mod/upload.rb +90 -0
- data/lib/factorix/cli/commands/path.rb +79 -0
- data/lib/factorix/cli/commands/requires_game_stopped.rb +32 -0
- data/lib/factorix/cli/commands/version.rb +25 -0
- data/lib/factorix/cli.rb +42 -0
- data/lib/factorix/dependency/edge.rb +89 -0
- data/lib/factorix/dependency/entry.rb +124 -0
- data/lib/factorix/dependency/graph/builder.rb +108 -0
- data/lib/factorix/dependency/graph.rb +210 -0
- data/lib/factorix/dependency/list.rb +244 -0
- data/lib/factorix/dependency/mod_version_requirement.rb +73 -0
- data/lib/factorix/dependency/node.rb +60 -0
- data/lib/factorix/dependency/parser.rb +148 -0
- data/lib/factorix/dependency/validation_result.rb +138 -0
- data/lib/factorix/dependency/validator.rb +190 -0
- data/lib/factorix/errors.rb +112 -0
- data/lib/factorix/formatting.rb +56 -0
- data/lib/factorix/game_version.rb +98 -0
- data/lib/factorix/http/cache_decorator.rb +106 -0
- data/lib/factorix/http/cached_response.rb +37 -0
- data/lib/factorix/http/client.rb +187 -0
- data/lib/factorix/http/response.rb +31 -0
- data/lib/factorix/http/retry_decorator.rb +59 -0
- data/lib/factorix/http/retry_strategy.rb +80 -0
- data/lib/factorix/info_json.rb +90 -0
- data/lib/factorix/installed_mod.rb +239 -0
- data/lib/factorix/mod.rb +55 -0
- data/lib/factorix/mod_list.rb +174 -0
- data/lib/factorix/mod_settings.rb +278 -0
- data/lib/factorix/mod_state.rb +34 -0
- data/lib/factorix/mod_version.rb +99 -0
- data/lib/factorix/portal.rb +185 -0
- data/lib/factorix/progress/download_handler.rb +46 -0
- data/lib/factorix/progress/multi_presenter.rb +45 -0
- data/lib/factorix/progress/presenter.rb +67 -0
- data/lib/factorix/progress/presenter_adapter.rb +46 -0
- data/lib/factorix/progress/scan_handler.rb +33 -0
- data/lib/factorix/progress/upload_handler.rb +33 -0
- data/lib/factorix/runtime/base.rb +233 -0
- data/lib/factorix/runtime/linux.rb +32 -0
- data/lib/factorix/runtime/mac_os.rb +53 -0
- data/lib/factorix/runtime/user_configurable.rb +69 -0
- data/lib/factorix/runtime/windows.rb +85 -0
- data/lib/factorix/runtime/wsl.rb +118 -0
- data/lib/factorix/runtime.rb +32 -0
- data/lib/factorix/save_file.rb +178 -0
- data/lib/factorix/ser_des/deserializer.rb +198 -0
- data/lib/factorix/ser_des/serializer.rb +231 -0
- data/lib/factorix/ser_des/signed_integer.rb +63 -0
- data/lib/factorix/ser_des/unsigned_integer.rb +65 -0
- data/lib/factorix/service_credential.rb +127 -0
- data/lib/factorix/transfer/downloader.rb +162 -0
- data/lib/factorix/transfer/uploader.rb +232 -0
- data/lib/factorix/version.rb +6 -0
- data/lib/factorix.rb +38 -0
- data/sig/dry/auto_inject.rbs +15 -0
- data/sig/dry/cli.rbs +19 -0
- data/sig/dry/configurable.rbs +13 -0
- data/sig/dry/core/container.rbs +17 -0
- data/sig/dry/events/publisher.rbs +22 -0
- data/sig/dry/logger.rbs +16 -0
- data/sig/factorix/api/category.rbs +15 -0
- data/sig/factorix/api/image.rbs +15 -0
- data/sig/factorix/api/license.rbs +20 -0
- data/sig/factorix/api/mod_download_api.rbs +18 -0
- data/sig/factorix/api/mod_info.rbs +67 -0
- data/sig/factorix/api/mod_management_api.rbs +25 -0
- data/sig/factorix/api/mod_portal_api.rbs +31 -0
- data/sig/factorix/api/release.rbs +27 -0
- data/sig/factorix/api/tag.rbs +15 -0
- data/sig/factorix/api.rbs +8 -0
- data/sig/factorix/api_credential.rbs +17 -0
- data/sig/factorix/application.rbs +86 -0
- data/sig/factorix/cache/file_system.rbs +35 -0
- data/sig/factorix/cli/commands/base.rbs +13 -0
- data/sig/factorix/cli/commands/cache/evict.rbs +17 -0
- data/sig/factorix/cli/commands/cache/stat.rbs +17 -0
- data/sig/factorix/cli/commands/command_wrapper.rbs +13 -0
- data/sig/factorix/cli/commands/completion/zsh.rbs +15 -0
- data/sig/factorix/cli/commands/confirmable.rbs +12 -0
- data/sig/factorix/cli/commands/download_support.rbs +12 -0
- data/sig/factorix/cli/commands/launch.rbs +15 -0
- data/sig/factorix/cli/commands/mod/check.rbs +18 -0
- data/sig/factorix/cli/commands/mod/disable.rbs +20 -0
- data/sig/factorix/cli/commands/mod/download.rbs +18 -0
- data/sig/factorix/cli/commands/mod/edit.rbs +30 -0
- data/sig/factorix/cli/commands/mod/enable.rbs +20 -0
- data/sig/factorix/cli/commands/mod/image/add.rbs +19 -0
- data/sig/factorix/cli/commands/mod/image/edit.rbs +19 -0
- data/sig/factorix/cli/commands/mod/image/list.rbs +19 -0
- data/sig/factorix/cli/commands/mod/install.rbs +19 -0
- data/sig/factorix/cli/commands/mod/list.rbs +30 -0
- data/sig/factorix/cli/commands/mod/search.rbs +18 -0
- data/sig/factorix/cli/commands/mod/settings/dump.rbs +17 -0
- data/sig/factorix/cli/commands/mod/settings/restore.rbs +17 -0
- data/sig/factorix/cli/commands/mod/sync.rbs +19 -0
- data/sig/factorix/cli/commands/mod/uninstall.rbs +20 -0
- data/sig/factorix/cli/commands/mod/update.rbs +19 -0
- data/sig/factorix/cli/commands/mod/upload.rbs +24 -0
- data/sig/factorix/cli/commands/path.rbs +18 -0
- data/sig/factorix/cli/commands/requires_game_stopped.rbs +13 -0
- data/sig/factorix/cli/commands/version.rbs +13 -0
- data/sig/factorix/cli.rbs +11 -0
- data/sig/factorix/dependency/edge.rbs +32 -0
- data/sig/factorix/dependency/entry.rbs +30 -0
- data/sig/factorix/dependency/graph/builder.rbs +17 -0
- data/sig/factorix/dependency/graph.rbs +39 -0
- data/sig/factorix/dependency/list.rbs +69 -0
- data/sig/factorix/dependency/mod_version_requirement.rbs +18 -0
- data/sig/factorix/dependency/node.rbs +24 -0
- data/sig/factorix/dependency/parser.rbs +11 -0
- data/sig/factorix/dependency/validation_result.rbs +56 -0
- data/sig/factorix/dependency/validator.rbs +13 -0
- data/sig/factorix/errors.rbs +132 -0
- data/sig/factorix/formatting.rbs +8 -0
- data/sig/factorix/game_version.rbs +24 -0
- data/sig/factorix/http/cache_decorator.rbs +64 -0
- data/sig/factorix/http/client.rbs +55 -0
- data/sig/factorix/http/response.rbs +28 -0
- data/sig/factorix/http/retry_decorator.rbs +44 -0
- data/sig/factorix/http/retry_strategy.rbs +42 -0
- data/sig/factorix/info_json.rbs +19 -0
- data/sig/factorix/installed_mod.rbs +34 -0
- data/sig/factorix/mod.rbs +20 -0
- data/sig/factorix/mod_list.rbs +44 -0
- data/sig/factorix/mod_settings.rbs +47 -0
- data/sig/factorix/mod_state.rbs +18 -0
- data/sig/factorix/mod_version.rbs +23 -0
- data/sig/factorix/portal.rbs +37 -0
- data/sig/factorix/progress/download_handler.rbs +19 -0
- data/sig/factorix/progress/multi_presenter.rbs +15 -0
- data/sig/factorix/progress/presenter.rbs +17 -0
- data/sig/factorix/progress/presenter_adapter.rbs +17 -0
- data/sig/factorix/progress/scan_handler.rbs +16 -0
- data/sig/factorix/progress/upload_handler.rbs +17 -0
- data/sig/factorix/runtime/base.rbs +45 -0
- data/sig/factorix/runtime/linux.rbs +15 -0
- data/sig/factorix/runtime/mac_os.rbs +15 -0
- data/sig/factorix/runtime/user_configurable.rbs +13 -0
- data/sig/factorix/runtime/windows.rbs +23 -0
- data/sig/factorix/runtime/wsl.rbs +19 -0
- data/sig/factorix/runtime.rbs +9 -0
- data/sig/factorix/save_file.rbs +40 -0
- data/sig/factorix/ser_des/deserializer.rbs +49 -0
- data/sig/factorix/ser_des/serializer.rbs +45 -0
- data/sig/factorix/ser_des/signed_integer.rbs +37 -0
- data/sig/factorix/ser_des/unsigned_integer.rbs +37 -0
- data/sig/factorix/service_credential.rbs +19 -0
- data/sig/factorix/transfer/downloader.rbs +15 -0
- data/sig/factorix/transfer/uploader.rbs +21 -0
- data/sig/factorix.rbs +9 -0
- data/sig/tty/progressbar.rbs +18 -0
- metadata +431 -0
|
@@ -0,0 +1,67 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require "tty-progressbar"
|
|
4
|
+
|
|
5
|
+
module Factorix
|
|
6
|
+
module Progress
|
|
7
|
+
# Progress presenter implementation
|
|
8
|
+
#
|
|
9
|
+
# This class provides a simple progress presentation interface using tty-progressbar.
|
|
10
|
+
class Presenter
|
|
11
|
+
# Create a new progress presenter
|
|
12
|
+
#
|
|
13
|
+
# @param title [String] title of the progress presenter
|
|
14
|
+
# @param output [IO] output stream for the progress presenter
|
|
15
|
+
def initialize(title: "Progress", output: $stderr)
|
|
16
|
+
@title = title
|
|
17
|
+
@output = output
|
|
18
|
+
@tty_bar = nil
|
|
19
|
+
end
|
|
20
|
+
|
|
21
|
+
# Start the progress presentation with a specific total
|
|
22
|
+
#
|
|
23
|
+
# @param total [Integer] total size/count for progress tracking
|
|
24
|
+
# @param format [String] progress presenter format string (default: generic format)
|
|
25
|
+
# @return [void]
|
|
26
|
+
def start(total: nil, format: nil)
|
|
27
|
+
format ||= total.nil? ? "#{@title} [:bar] :current" : "#{@title} [:bar] :percent :current/:total"
|
|
28
|
+
@tty_bar = TTY::ProgressBar.new(
|
|
29
|
+
format,
|
|
30
|
+
total:,
|
|
31
|
+
output: @output,
|
|
32
|
+
frequency: 1, # Always update (important for testing with StringIO)
|
|
33
|
+
force: true # Force output even when not a TTY
|
|
34
|
+
)
|
|
35
|
+
end
|
|
36
|
+
|
|
37
|
+
# Update the progress presenter to a specific value
|
|
38
|
+
#
|
|
39
|
+
# @param current [Integer] current progress value
|
|
40
|
+
# @return [void]
|
|
41
|
+
def update(current=nil)
|
|
42
|
+
if current
|
|
43
|
+
@tty_bar&.current = current
|
|
44
|
+
else
|
|
45
|
+
# For indeterminate progress, just advance
|
|
46
|
+
@tty_bar&.advance
|
|
47
|
+
end
|
|
48
|
+
end
|
|
49
|
+
|
|
50
|
+
# Increase the total count dynamically
|
|
51
|
+
#
|
|
52
|
+
# @param increment [Integer] amount to add to current total
|
|
53
|
+
# @return [void]
|
|
54
|
+
def increase_total(increment)
|
|
55
|
+
return unless @tty_bar
|
|
56
|
+
|
|
57
|
+
current_total = @tty_bar.total || 0
|
|
58
|
+
@tty_bar.update(total: current_total + increment)
|
|
59
|
+
end
|
|
60
|
+
|
|
61
|
+
# Mark the progress presenter as finished
|
|
62
|
+
#
|
|
63
|
+
# @return [void]
|
|
64
|
+
def finish = @tty_bar&.finish
|
|
65
|
+
end
|
|
66
|
+
end
|
|
67
|
+
end
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Factorix
|
|
4
|
+
module Progress
|
|
5
|
+
# Adapter to make TTY::ProgressBar compatible with Presenter interface
|
|
6
|
+
#
|
|
7
|
+
# This adapter wraps a TTY::ProgressBar instance and provides the same
|
|
8
|
+
# interface as Progress::Presenter, allowing them to be used interchangeably.
|
|
9
|
+
class PresenterAdapter
|
|
10
|
+
# Create a new presenter adapter
|
|
11
|
+
#
|
|
12
|
+
# @param tty_bar [TTY::ProgressBar] the progress bar to adapt
|
|
13
|
+
# @param mutex [Mutex] mutex for thread-safe operations
|
|
14
|
+
def initialize(tty_bar, mutex)
|
|
15
|
+
@tty_bar = tty_bar
|
|
16
|
+
@mutex = mutex
|
|
17
|
+
@started = false
|
|
18
|
+
end
|
|
19
|
+
|
|
20
|
+
# Start the progress presentation
|
|
21
|
+
#
|
|
22
|
+
# @param total [Integer] total size/count for progress tracking
|
|
23
|
+
# @param format [String, nil] format string (ignored, already set in TTY::ProgressBar)
|
|
24
|
+
# @return [void]
|
|
25
|
+
def start(total:, format: nil)
|
|
26
|
+
_ = format # Acknowledge unused parameter
|
|
27
|
+
@mutex.synchronize do
|
|
28
|
+
@tty_bar.update(total:) if total
|
|
29
|
+
@tty_bar.start unless @started
|
|
30
|
+
@started = true
|
|
31
|
+
end
|
|
32
|
+
end
|
|
33
|
+
|
|
34
|
+
# Update the progress to a specific value
|
|
35
|
+
#
|
|
36
|
+
# @param current [Integer] current progress value
|
|
37
|
+
# @return [void]
|
|
38
|
+
def update(current) = @mutex.synchronize { @tty_bar.current = current }
|
|
39
|
+
|
|
40
|
+
# Mark the progress as finished
|
|
41
|
+
#
|
|
42
|
+
# @return [void]
|
|
43
|
+
def finish = @mutex.synchronize { @tty_bar.finish }
|
|
44
|
+
end
|
|
45
|
+
end
|
|
46
|
+
end
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Factorix
|
|
4
|
+
module Progress
|
|
5
|
+
# Scan event handler for progress presenters
|
|
6
|
+
#
|
|
7
|
+
# This class listens to scan events and updates a progress presenter accordingly.
|
|
8
|
+
class ScanHandler
|
|
9
|
+
# Create a new scan handler
|
|
10
|
+
#
|
|
11
|
+
# @param presenter [Presenter, PresenterAdapter] progress presenter to update
|
|
12
|
+
def initialize(presenter) = @presenter = presenter
|
|
13
|
+
|
|
14
|
+
# Handle scan started event
|
|
15
|
+
#
|
|
16
|
+
# @param event [Dry::Events::Event] event with total payload
|
|
17
|
+
# @return [void]
|
|
18
|
+
def on_scan_started(event) = @presenter.start(total: event[:total])
|
|
19
|
+
|
|
20
|
+
# Handle scan progress event
|
|
21
|
+
#
|
|
22
|
+
# @param event [Dry::Events::Event] event with current payload
|
|
23
|
+
# @return [void]
|
|
24
|
+
def on_scan_progress(event) = @presenter.update(event[:current])
|
|
25
|
+
|
|
26
|
+
# Handle scan completed event
|
|
27
|
+
#
|
|
28
|
+
# @param event [Dry::Events::Event] event with total payload
|
|
29
|
+
# @return [void]
|
|
30
|
+
def on_scan_completed(_event) = @presenter.finish
|
|
31
|
+
end
|
|
32
|
+
end
|
|
33
|
+
end
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Factorix
|
|
4
|
+
module Progress
|
|
5
|
+
# Upload event handler for progress presenters
|
|
6
|
+
#
|
|
7
|
+
# This class listens to upload events and updates a progress presenter accordingly.
|
|
8
|
+
class UploadHandler
|
|
9
|
+
# Create a new upload handler
|
|
10
|
+
#
|
|
11
|
+
# @param presenter [Presenter, PresenterAdapter] progress presenter to update
|
|
12
|
+
def initialize(presenter) = @presenter = presenter
|
|
13
|
+
|
|
14
|
+
# Handle upload started event
|
|
15
|
+
#
|
|
16
|
+
# @param event [Dry::Events::Event] event with total_size payload
|
|
17
|
+
# @return [void]
|
|
18
|
+
def on_upload_started(event) = @presenter.start(total: event[:total_size], format: "Uploading [:bar] :percent :byte/:total_byte")
|
|
19
|
+
|
|
20
|
+
# Handle upload progress event
|
|
21
|
+
#
|
|
22
|
+
# @param event [Dry::Events::Event] event with current_size payload
|
|
23
|
+
# @return [void]
|
|
24
|
+
def on_upload_progress(event) = @presenter.update(event[:current_size])
|
|
25
|
+
|
|
26
|
+
# Handle upload completed event
|
|
27
|
+
#
|
|
28
|
+
# @param event [Dry::Events::Event] event with total_size payload
|
|
29
|
+
# @return [void]
|
|
30
|
+
def on_upload_completed(_event) = @presenter.finish
|
|
31
|
+
end
|
|
32
|
+
end
|
|
33
|
+
end
|
|
@@ -0,0 +1,233 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require "pathname"
|
|
4
|
+
|
|
5
|
+
module Factorix
|
|
6
|
+
class Runtime
|
|
7
|
+
# Abstract base class for platform-specific runtime environments
|
|
8
|
+
#
|
|
9
|
+
# This class defines the interface that all platform-specific runtime
|
|
10
|
+
# implementations must provide. It provides common implementations for
|
|
11
|
+
# derived paths and XDG Base Directory specification support.
|
|
12
|
+
class Base
|
|
13
|
+
prepend UserConfigurable
|
|
14
|
+
|
|
15
|
+
# Hook called when Base is subclassed
|
|
16
|
+
#
|
|
17
|
+
# Automatically prepends UserConfigurable to all subclasses to ensure
|
|
18
|
+
# configuration support is available across all platform implementations.
|
|
19
|
+
#
|
|
20
|
+
# @param subclass [Class] the subclass being created
|
|
21
|
+
# @return [void]
|
|
22
|
+
def self.inherited(subclass)
|
|
23
|
+
super
|
|
24
|
+
subclass.prepend(UserConfigurable)
|
|
25
|
+
end
|
|
26
|
+
|
|
27
|
+
# Get the Factorio executable path
|
|
28
|
+
#
|
|
29
|
+
# Returns the path to the Factorio executable file.
|
|
30
|
+
# The exact location varies by platform and installation method (Steam, GOG, etc.).
|
|
31
|
+
#
|
|
32
|
+
# @return [Pathname] the Factorio executable path
|
|
33
|
+
# @raise [NotImplementedError] if not implemented by the platform
|
|
34
|
+
def executable_path = raise NotImplementedError, "#{self.class}#executable_path is not implemented"
|
|
35
|
+
|
|
36
|
+
# Get the Factorio user directory path
|
|
37
|
+
#
|
|
38
|
+
# This directory contains user-specific Factorio data including mods,
|
|
39
|
+
# saves, configuration files, and player data.
|
|
40
|
+
#
|
|
41
|
+
# @return [Pathname] the Factorio user directory
|
|
42
|
+
# @raise [NotImplementedError] if not implemented by the platform
|
|
43
|
+
def user_dir = raise NotImplementedError, "#{self.class}#user_dir is not implemented"
|
|
44
|
+
|
|
45
|
+
# Get the Factorio data directory path
|
|
46
|
+
#
|
|
47
|
+
# This directory contains the base game data and built-in expansion MODs.
|
|
48
|
+
# Each MOD (base, space-age, etc.) has its own subdirectory with info.json.
|
|
49
|
+
#
|
|
50
|
+
# @return [Pathname] the Factorio data directory
|
|
51
|
+
# @raise [NotImplementedError] if not implemented by the platform
|
|
52
|
+
def data_dir = raise NotImplementedError, "#{self.class}#data_dir is not implemented"
|
|
53
|
+
|
|
54
|
+
# Get the MOD directory path of Factorio
|
|
55
|
+
#
|
|
56
|
+
# This directory contains all installed MODs and MOD configuration files
|
|
57
|
+
# such as mod-list.json and mod-settings.dat.
|
|
58
|
+
#
|
|
59
|
+
# @return [Pathname] the MOD directory of Factorio
|
|
60
|
+
def mod_dir = user_dir + "mods"
|
|
61
|
+
|
|
62
|
+
# Get the save directory path of Factorio
|
|
63
|
+
#
|
|
64
|
+
# This directory contains all save game files.
|
|
65
|
+
#
|
|
66
|
+
# @return [Pathname] the save directory of Factorio
|
|
67
|
+
def save_dir = user_dir + "saves"
|
|
68
|
+
|
|
69
|
+
# Get the script-output directory path of Factorio
|
|
70
|
+
#
|
|
71
|
+
# This directory contains output files generated by Factorio Lua scripts.
|
|
72
|
+
#
|
|
73
|
+
# @return [Pathname] the script-output directory of Factorio
|
|
74
|
+
def script_output_dir = user_dir + "script-output"
|
|
75
|
+
|
|
76
|
+
# Get the path of the mod-list.json file
|
|
77
|
+
#
|
|
78
|
+
# This file contains the list of installed MODs and their enabled/disabled states.
|
|
79
|
+
#
|
|
80
|
+
# @return [Pathname] the path of the mod-list.json file
|
|
81
|
+
def mod_list_path = mod_dir + "mod-list.json"
|
|
82
|
+
|
|
83
|
+
# Get the path of the mod-settings.dat file
|
|
84
|
+
#
|
|
85
|
+
# This file contains the MOD settings for startup, runtime-global, and runtime-per-user.
|
|
86
|
+
#
|
|
87
|
+
# @return [Pathname] the path of the mod-settings.dat file
|
|
88
|
+
def mod_settings_path = mod_dir + "mod-settings.dat"
|
|
89
|
+
|
|
90
|
+
# Get the path of the player-data.json file
|
|
91
|
+
#
|
|
92
|
+
# This file contains player-specific data including authentication credentials,
|
|
93
|
+
# preferences, and game statistics.
|
|
94
|
+
#
|
|
95
|
+
# @return [Pathname] the path of the player-data.json file
|
|
96
|
+
def player_data_path = user_dir + "player-data.json"
|
|
97
|
+
|
|
98
|
+
# Get the path of the current Factorio log file
|
|
99
|
+
#
|
|
100
|
+
# This file contains the log output from the current Factorio session.
|
|
101
|
+
#
|
|
102
|
+
# @return [Pathname] the path of the current log file
|
|
103
|
+
def current_log_path = user_dir + "factorio-current.log"
|
|
104
|
+
|
|
105
|
+
# Get the path of the previous Factorio log file
|
|
106
|
+
#
|
|
107
|
+
# This file contains the log output from the previous Factorio session.
|
|
108
|
+
#
|
|
109
|
+
# @return [Pathname] the path of the previous log file
|
|
110
|
+
def previous_log_path = user_dir + "factorio-previous.log"
|
|
111
|
+
|
|
112
|
+
# Get the XDG cache home directory
|
|
113
|
+
#
|
|
114
|
+
# Returns the base directory for user-specific cache data according to
|
|
115
|
+
# the XDG Base Directory Specification. On platforms that don't follow
|
|
116
|
+
# XDG conventions, this returns the platform-appropriate equivalent.
|
|
117
|
+
#
|
|
118
|
+
# @return [Pathname] the XDG cache home directory
|
|
119
|
+
def xdg_cache_home_dir = Pathname(ENV.fetch("XDG_CACHE_HOME") { default_cache_home_dir })
|
|
120
|
+
|
|
121
|
+
# Get the XDG config home directory
|
|
122
|
+
#
|
|
123
|
+
# Returns the base directory for user-specific configuration files according to
|
|
124
|
+
# the XDG Base Directory Specification. On platforms that don't follow
|
|
125
|
+
# XDG conventions, this returns the platform-appropriate equivalent.
|
|
126
|
+
#
|
|
127
|
+
# @return [Pathname] the XDG config home directory
|
|
128
|
+
def xdg_config_home_dir = Pathname(ENV.fetch("XDG_CONFIG_HOME") { default_config_home_dir })
|
|
129
|
+
|
|
130
|
+
# Get the XDG data home directory
|
|
131
|
+
#
|
|
132
|
+
# Returns the base directory for user-specific data files according to
|
|
133
|
+
# the XDG Base Directory Specification. On platforms that don't follow
|
|
134
|
+
# XDG conventions, this returns the platform-appropriate equivalent.
|
|
135
|
+
#
|
|
136
|
+
# @return [Pathname] the XDG data home directory
|
|
137
|
+
def xdg_data_home_dir = Pathname(ENV.fetch("XDG_DATA_HOME") { default_data_home_dir })
|
|
138
|
+
|
|
139
|
+
# Get the XDG state home directory
|
|
140
|
+
#
|
|
141
|
+
# Returns the base directory for user-specific state files (logs, history, etc.)
|
|
142
|
+
# according to the XDG Base Directory Specification. On platforms that don't follow
|
|
143
|
+
# XDG conventions, this returns the platform-appropriate equivalent.
|
|
144
|
+
#
|
|
145
|
+
# @return [Pathname] the XDG state home directory
|
|
146
|
+
def xdg_state_home_dir = Pathname(ENV.fetch("XDG_STATE_HOME") { default_state_home_dir })
|
|
147
|
+
|
|
148
|
+
# Get the Factorix cache directory
|
|
149
|
+
#
|
|
150
|
+
# Returns the directory where Factorix stores its cache data.
|
|
151
|
+
#
|
|
152
|
+
# @return [Pathname] the Factorix cache directory
|
|
153
|
+
def factorix_cache_dir = xdg_cache_home_dir / "factorix"
|
|
154
|
+
|
|
155
|
+
# Get the Factorix configuration file path
|
|
156
|
+
#
|
|
157
|
+
# Returns the path to the Factorix configuration file.
|
|
158
|
+
#
|
|
159
|
+
# @return [Pathname] the Factorix configuration file path
|
|
160
|
+
def factorix_config_path = xdg_config_home_dir / "factorix" / "config.rb"
|
|
161
|
+
|
|
162
|
+
# Get the Factorix log file path
|
|
163
|
+
#
|
|
164
|
+
# Returns the path to the Factorix log file.
|
|
165
|
+
#
|
|
166
|
+
# @return [Pathname] the Factorix log file path
|
|
167
|
+
def factorix_log_path = xdg_state_home_dir / "factorix" / "factorix.log"
|
|
168
|
+
|
|
169
|
+
# Get the Factorio lock file path
|
|
170
|
+
#
|
|
171
|
+
# The lock file is created by Factorio when the game is running
|
|
172
|
+
# and is used to detect if the game is already running.
|
|
173
|
+
#
|
|
174
|
+
# @return [Pathname] the lock file path
|
|
175
|
+
def lock_path = user_dir / ".lock"
|
|
176
|
+
|
|
177
|
+
# Check if Factorio is currently running
|
|
178
|
+
#
|
|
179
|
+
# Because the game becomes daemonized on launch, we cannot use the process ID
|
|
180
|
+
# or process groups to check if the game is running. Instead, we check the
|
|
181
|
+
# existence of the lock file.
|
|
182
|
+
#
|
|
183
|
+
# @return [Boolean] true if the game is running, false otherwise
|
|
184
|
+
def running? = lock_path.exist?
|
|
185
|
+
|
|
186
|
+
# Launch Factorio with the specified options
|
|
187
|
+
#
|
|
188
|
+
# @param args [Array<String>] command-line arguments to pass to Factorio
|
|
189
|
+
# @param async [Boolean] whether to launch asynchronously (default: true)
|
|
190
|
+
# @return [void]
|
|
191
|
+
# @raise [RuntimeError] if the game is already running
|
|
192
|
+
def launch(*, async: true)
|
|
193
|
+
if async
|
|
194
|
+
spawn([executable_path.to_s, "factorio"], *, out: IO::NULL)
|
|
195
|
+
else
|
|
196
|
+
system([executable_path.to_s, "factorio"], *)
|
|
197
|
+
end
|
|
198
|
+
end
|
|
199
|
+
|
|
200
|
+
# Get the default cache home directory for this platform
|
|
201
|
+
#
|
|
202
|
+
# This method should be overridden by platform-specific subclasses
|
|
203
|
+
# to provide appropriate defaults.
|
|
204
|
+
#
|
|
205
|
+
# @return [Pathname] the default cache home directory
|
|
206
|
+
private def default_cache_home_dir = Pathname(Dir.home) + ".cache"
|
|
207
|
+
|
|
208
|
+
# Get the default config home directory for this platform
|
|
209
|
+
#
|
|
210
|
+
# This method should be overridden by platform-specific subclasses
|
|
211
|
+
# to provide appropriate defaults.
|
|
212
|
+
#
|
|
213
|
+
# @return [Pathname] the default config home directory
|
|
214
|
+
private def default_config_home_dir = Pathname(Dir.home) + ".config"
|
|
215
|
+
|
|
216
|
+
# Get the default data home directory for this platform
|
|
217
|
+
#
|
|
218
|
+
# This method should be overridden by platform-specific subclasses
|
|
219
|
+
# to provide appropriate defaults.
|
|
220
|
+
#
|
|
221
|
+
# @return [Pathname] the default data home directory
|
|
222
|
+
private def default_data_home_dir = Pathname(Dir.home) + ".local/share"
|
|
223
|
+
|
|
224
|
+
# Get the default state home directory for this platform
|
|
225
|
+
#
|
|
226
|
+
# This method should be overridden by platform-specific subclasses
|
|
227
|
+
# to provide appropriate defaults.
|
|
228
|
+
#
|
|
229
|
+
# @return [Pathname] the default state home directory
|
|
230
|
+
private def default_state_home_dir = Pathname(Dir.home) + ".local/state"
|
|
231
|
+
end
|
|
232
|
+
end
|
|
233
|
+
end
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Factorix
|
|
4
|
+
class Runtime
|
|
5
|
+
# Linux runtime environment
|
|
6
|
+
#
|
|
7
|
+
# Provides default paths for Steam installation on Linux.
|
|
8
|
+
# Users with non-Steam installations (standalone, Flatpak, Snap, etc.)
|
|
9
|
+
# should configure paths via the configuration file.
|
|
10
|
+
# See Runtime::UserConfigurable for configuration instructions.
|
|
11
|
+
class Linux < Base
|
|
12
|
+
# Get the Factorio executable path
|
|
13
|
+
#
|
|
14
|
+
# Returns the default Steam installation path on Linux.
|
|
15
|
+
#
|
|
16
|
+
# @return [Pathname] the Factorio executable path
|
|
17
|
+
def executable_path = Pathname(Dir.home) + ".steam/steam/steamapps/common/Factorio/bin/x64/factorio"
|
|
18
|
+
|
|
19
|
+
# Get the Factorio user directory path
|
|
20
|
+
#
|
|
21
|
+
# @return [Pathname] the Factorio user directory
|
|
22
|
+
def user_dir = Pathname(Dir.home) + ".factorio"
|
|
23
|
+
|
|
24
|
+
# Get the Factorio data directory path
|
|
25
|
+
#
|
|
26
|
+
# This directory contains the base game data and built-in expansion MODs.
|
|
27
|
+
#
|
|
28
|
+
# @return [Pathname] the Factorio data directory
|
|
29
|
+
def data_dir = Pathname(Dir.home) + ".steam/steam/steamapps/common/Factorio/data"
|
|
30
|
+
end
|
|
31
|
+
end
|
|
32
|
+
end
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Factorix
|
|
4
|
+
class Runtime
|
|
5
|
+
# macOS runtime environment
|
|
6
|
+
#
|
|
7
|
+
# This implementation assumes Factorio is installed via Steam.
|
|
8
|
+
# For other installation methods (GOG, itch.io, standalone), users should
|
|
9
|
+
# configure the installation path in the Factorix configuration file.
|
|
10
|
+
class MacOS < Base
|
|
11
|
+
# Get the Factorio executable path
|
|
12
|
+
#
|
|
13
|
+
# Returns the default Steam installation path on macOS.
|
|
14
|
+
#
|
|
15
|
+
# @return [Pathname] the Factorio executable path
|
|
16
|
+
def executable_path = Pathname(Dir.home) + "Library/Application Support/Steam/steamapps/common/Factorio/factorio.app/Contents/MacOS/factorio"
|
|
17
|
+
|
|
18
|
+
# Get the Factorio user directory path
|
|
19
|
+
#
|
|
20
|
+
# @return [Pathname] the Factorio user directory
|
|
21
|
+
def user_dir = Pathname(Dir.home) + "Library/Application Support/factorio"
|
|
22
|
+
|
|
23
|
+
# Get the Factorio data directory path
|
|
24
|
+
#
|
|
25
|
+
# This directory contains the base game data and built-in expansion MODs.
|
|
26
|
+
#
|
|
27
|
+
# @return [Pathname] the Factorio data directory
|
|
28
|
+
def data_dir = Pathname(Dir.home) + "Library/Application Support/Steam/steamapps/common/Factorio/factorio.app/Contents/data"
|
|
29
|
+
|
|
30
|
+
# Get the Factorix log file path
|
|
31
|
+
#
|
|
32
|
+
# Returns the path to the Factorix log file using macOS convention.
|
|
33
|
+
#
|
|
34
|
+
# @return [Pathname] the Factorix log file path
|
|
35
|
+
def factorix_log_path = Pathname(Dir.home) + "Library/Logs/factorix/factorix.log"
|
|
36
|
+
|
|
37
|
+
# Get the default cache home directory for macOS
|
|
38
|
+
#
|
|
39
|
+
# @return [Pathname] the default cache home directory
|
|
40
|
+
private def default_cache_home_dir = Pathname(Dir.home) + "Library/Caches"
|
|
41
|
+
|
|
42
|
+
# Get the default config home directory for macOS
|
|
43
|
+
#
|
|
44
|
+
# @return [Pathname] the default config home directory
|
|
45
|
+
private def default_config_home_dir = Pathname(Dir.home) + "Library/Application Support"
|
|
46
|
+
|
|
47
|
+
# Get the default data home directory for macOS
|
|
48
|
+
#
|
|
49
|
+
# @return [Pathname] the default data home directory
|
|
50
|
+
private def default_data_home_dir = Pathname(Dir.home) + "Library/Application Support"
|
|
51
|
+
end
|
|
52
|
+
end
|
|
53
|
+
end
|
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Factorix
|
|
4
|
+
class Runtime
|
|
5
|
+
# Provides user-configurable path overrides for Runtime
|
|
6
|
+
#
|
|
7
|
+
# This module is prepended to Runtime::Base to allow users to override
|
|
8
|
+
# auto-detected paths via configuration. When a configured path is available,
|
|
9
|
+
# it is used instead of platform-specific auto-detection.
|
|
10
|
+
#
|
|
11
|
+
# Configuration is done via Application.config:
|
|
12
|
+
#
|
|
13
|
+
# @example Configure paths in config file
|
|
14
|
+
# Factorix::Application.configure do |config|
|
|
15
|
+
# config.runtime.executable_path = "/opt/factorio/bin/x64/factorio"
|
|
16
|
+
# config.runtime.user_dir = "/home/user/.factorio"
|
|
17
|
+
# end
|
|
18
|
+
#
|
|
19
|
+
# All path resolution decisions are logged at DEBUG level.
|
|
20
|
+
module UserConfigurable
|
|
21
|
+
# Get the Factorio executable path
|
|
22
|
+
#
|
|
23
|
+
# Returns the configured executable path if available, otherwise falls back
|
|
24
|
+
# to platform-specific auto-detection.
|
|
25
|
+
#
|
|
26
|
+
# @return [Pathname] the Factorio executable path
|
|
27
|
+
# @raise [ConfigurationError] if auto-detection is not supported and no configuration is provided
|
|
28
|
+
def executable_path = configurable_path(:executable_path, example_path: "/path/to/factorio") { super }
|
|
29
|
+
|
|
30
|
+
# Get the Factorio user directory path
|
|
31
|
+
#
|
|
32
|
+
# Returns the configured user_dir if available, otherwise falls back
|
|
33
|
+
# to platform-specific auto-detection.
|
|
34
|
+
#
|
|
35
|
+
# @return [Pathname] the Factorio user directory
|
|
36
|
+
# @raise [ConfigurationError] if auto-detection is not supported and no configuration is provided
|
|
37
|
+
def user_dir = configurable_path(:user_dir, example_path: "/path/to/factorio/user/dir") { super }
|
|
38
|
+
|
|
39
|
+
# Get the Factorio data directory path
|
|
40
|
+
#
|
|
41
|
+
# Returns the configured data_dir if available, otherwise falls back
|
|
42
|
+
# to platform-specific auto-detection.
|
|
43
|
+
#
|
|
44
|
+
# @return [Pathname] the Factorio data directory
|
|
45
|
+
# @raise [ConfigurationError] if auto-detection is not supported and no configuration is provided
|
|
46
|
+
def data_dir = configurable_path(:data_dir, example_path: "/path/to/factorio/data") { super }
|
|
47
|
+
|
|
48
|
+
private def configurable_path(name, example_path:)
|
|
49
|
+
if (configured = Application.config.runtime.public_send(name))
|
|
50
|
+
Application[:logger].debug("Using configured #{name}", path: configured.to_s)
|
|
51
|
+
configured
|
|
52
|
+
else
|
|
53
|
+
Application[:logger].debug("No configuration for #{name}, using auto-detection")
|
|
54
|
+
yield.tap {|path| Application[:logger].debug("Auto-detected #{name}", path: path.to_s) }
|
|
55
|
+
end
|
|
56
|
+
rescue NotImplementedError => e
|
|
57
|
+
Application[:logger].error("Auto-detection failed and no configuration provided", error: e.message)
|
|
58
|
+
raise ConfigurationError, <<~MESSAGE
|
|
59
|
+
#{name} not configured and auto-detection is not supported for this platform.
|
|
60
|
+
Please configure it in #{Application[:runtime].factorix_config_path}:
|
|
61
|
+
|
|
62
|
+
Factorix::Application.configure do |config|
|
|
63
|
+
config.runtime.#{name} = "#{example_path}"
|
|
64
|
+
end
|
|
65
|
+
MESSAGE
|
|
66
|
+
end
|
|
67
|
+
end
|
|
68
|
+
end
|
|
69
|
+
end
|
|
@@ -0,0 +1,85 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Factorix
|
|
4
|
+
class Runtime
|
|
5
|
+
# Windows runtime environment
|
|
6
|
+
#
|
|
7
|
+
# This implementation uses Windows environment variables (APPDATA, LOCALAPPDATA)
|
|
8
|
+
# to locate Factorio directories. It assumes Factorio is installed via Steam.
|
|
9
|
+
# For other installation methods (GOG, itch.io, standalone), users should
|
|
10
|
+
# configure the installation path in the Factorix configuration file.
|
|
11
|
+
class Windows < Base
|
|
12
|
+
# Initialize Windows runtime environment
|
|
13
|
+
#
|
|
14
|
+
# @param path [WindowsPath] the path provider (for dependency injection)
|
|
15
|
+
def initialize(path: WindowsPath.new)
|
|
16
|
+
super()
|
|
17
|
+
@path = path
|
|
18
|
+
end
|
|
19
|
+
|
|
20
|
+
# Get the Factorio executable path
|
|
21
|
+
#
|
|
22
|
+
# Returns the default Steam installation path on Windows.
|
|
23
|
+
#
|
|
24
|
+
# @return [Pathname] the Factorio executable path
|
|
25
|
+
def executable_path = path.program_files_x86.join("Steam/steamapps/common/Factorio/bin/x64/factorio.exe")
|
|
26
|
+
|
|
27
|
+
# Get the Factorio user directory path
|
|
28
|
+
#
|
|
29
|
+
# @return [Pathname] the Factorio user directory
|
|
30
|
+
def user_dir = path.app_data.join("Factorio")
|
|
31
|
+
|
|
32
|
+
# Get the Factorio data directory path
|
|
33
|
+
#
|
|
34
|
+
# This directory contains the base game data and built-in expansion MODs.
|
|
35
|
+
#
|
|
36
|
+
# @return [Pathname] the Factorio data directory
|
|
37
|
+
def data_dir = path.program_files_x86.join("Steam/steamapps/common/Factorio/data")
|
|
38
|
+
|
|
39
|
+
private attr_reader :path
|
|
40
|
+
|
|
41
|
+
# Get the default cache home directory for Windows
|
|
42
|
+
#
|
|
43
|
+
# @return [Pathname] the default cache home directory
|
|
44
|
+
private def default_cache_home_dir = path.local_app_data
|
|
45
|
+
|
|
46
|
+
# Get the default config home directory for Windows
|
|
47
|
+
#
|
|
48
|
+
# @return [Pathname] the default config home directory
|
|
49
|
+
private def default_config_home_dir = path.app_data
|
|
50
|
+
|
|
51
|
+
# Get the default data home directory for Windows
|
|
52
|
+
#
|
|
53
|
+
# @return [Pathname] the default data home directory
|
|
54
|
+
private def default_data_home_dir = path.local_app_data
|
|
55
|
+
|
|
56
|
+
# Windows-specific path provider
|
|
57
|
+
class WindowsPath
|
|
58
|
+
# Get the Program Files (x86) directory path
|
|
59
|
+
#
|
|
60
|
+
# @return [Pathname] the Program Files (x86) directory
|
|
61
|
+
def program_files_x86 = Pathname(convert_separator(ENV.fetch("ProgramFiles(x86)")))
|
|
62
|
+
|
|
63
|
+
# Get the AppData directory path
|
|
64
|
+
#
|
|
65
|
+
# @return [Pathname] the AppData directory
|
|
66
|
+
def app_data = Pathname(convert_separator(ENV.fetch("APPDATA")))
|
|
67
|
+
|
|
68
|
+
# Get the Local AppData directory path
|
|
69
|
+
#
|
|
70
|
+
# @return [Pathname] the Local AppData directory
|
|
71
|
+
def local_app_data = Pathname(convert_separator(ENV.fetch("LOCALAPPDATA")))
|
|
72
|
+
|
|
73
|
+
# Convert Windows path separators to forward slashes for aesthetics and consistency
|
|
74
|
+
#
|
|
75
|
+
# While Ruby accepts both separators, normalizing to forward slashes prevents
|
|
76
|
+
# mixing backslashes and forward slashes when concatenating paths with Pathname#+,
|
|
77
|
+
# which improves readability.
|
|
78
|
+
#
|
|
79
|
+
# @param path_string [String] the path string with backslashes
|
|
80
|
+
# @return [String] the path string with forward slashes
|
|
81
|
+
private def convert_separator(path_string) = path_string.tr(File::ALT_SEPARATOR, File::SEPARATOR)
|
|
82
|
+
end
|
|
83
|
+
end
|
|
84
|
+
end
|
|
85
|
+
end
|