mmi 0.2.1 → 0.2.3
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 +4 -4
- data/README.adoc +5 -9
- data/exe/mmi +1 -1
- data/lib/mmi/asset.rb +16 -0
- data/lib/mmi/assets_processor.rb +13 -44
- data/lib/mmi/constants.rb +14 -0
- data/lib/mmi/content_hash/base.rb +9 -0
- data/lib/mmi/content_hash/sha512.rb +25 -0
- data/lib/mmi/install_record.rb +72 -0
- data/lib/mmi/install_utils.rb +24 -0
- data/lib/mmi/interactive/assets.rb +71 -52
- data/lib/mmi/interactive/modloader.rb +16 -16
- data/lib/mmi/interactive/updater.rb +1 -1
- data/lib/mmi/mod_file_processor.rb +7 -59
- data/lib/mmi/modloader/fabric.rb +38 -47
- data/lib/mmi/modloader/none.rb +5 -1
- data/lib/mmi/modrinth_api.rb +2 -2
- data/lib/mmi/property_attributes.rb +192 -0
- data/lib/mmi/source/github.rb +13 -54
- data/lib/mmi/source/modrinth.rb +22 -53
- data/lib/mmi/source/url.rb +8 -37
- data/lib/mmi/version.rb +1 -1
- data/lib/mmi.rb +0 -11
- metadata +16 -81
- data/lib/mmi/cached_download.rb +0 -59
- data/lib/mmi/option_attributes.rb +0 -38
data/lib/mmi/modloader/fabric.rb
CHANGED
@@ -1,55 +1,40 @@
|
|
1
1
|
require 'fileutils'
|
2
2
|
require 'nokogiri'
|
3
|
+
require 'open-uri'
|
3
4
|
|
4
|
-
require 'mmi/
|
5
|
-
require 'mmi/
|
5
|
+
require 'mmi/install_utils'
|
6
|
+
require 'mmi/constants'
|
7
|
+
require 'mmi/content_hash/sha512'
|
8
|
+
require 'mmi/property_attributes'
|
6
9
|
|
7
10
|
module Mmi
|
8
11
|
module Modloader
|
9
12
|
class Fabric
|
10
|
-
|
13
|
+
prepend Mmi::PropertyAttributes
|
11
14
|
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
15
|
+
property :version
|
16
|
+
property :install_type, validate: :validate_install_type
|
17
|
+
property :minecraft_version
|
18
|
+
property :install_dir, default: Mmi::Constants.minecraft_dir
|
19
|
+
property :download_minecraft, default: false, validate: :validate_download_minecraft
|
17
20
|
|
18
|
-
def
|
19
|
-
|
20
|
-
|
21
|
-
|
21
|
+
def self.allowed_install_types
|
22
|
+
%w[
|
23
|
+
client
|
24
|
+
server
|
25
|
+
]
|
22
26
|
end
|
23
27
|
|
24
|
-
def
|
25
|
-
if
|
26
|
-
|
27
|
-
if allowed_install_types.include?(self.install_type)
|
28
|
-
if self.mcversion
|
29
|
-
if [true, false].include?(self.download_mc)
|
30
|
-
# Pass.
|
31
|
-
else
|
32
|
-
raise Mmi::InvalidAttributeError, %Q(Invalid "modloader.download_minecraft". Expecting true or false, got #{self.download_mc.inspect}.)
|
33
|
-
end
|
34
|
-
else
|
35
|
-
raise Mmi::MissingAttributeError, 'Missing "modloader.minecraft_version".'
|
36
|
-
end
|
37
|
-
else
|
38
|
-
raise Mmi::InvalidAttributeError, %Q(Invalid "modloader.install_type". Expecting "client" or "server", got #{self.install_type.inspect}.)
|
39
|
-
end
|
40
|
-
else
|
41
|
-
raise Mmi::MissingAttributeError, 'Missing "modloader.install_type".'
|
42
|
-
end
|
43
|
-
else
|
44
|
-
raise Mmi::MissingAttributeError, 'Missing "modloader.version".'
|
28
|
+
def self.validate_install_type(value, errors)
|
29
|
+
if !allowed_install_types.include?(value)
|
30
|
+
errors << %Q{modloader "install_type" must be one of #{allowed_install_types.map(&:inspect).join(', ')}}
|
45
31
|
end
|
46
32
|
end
|
47
33
|
|
48
|
-
def
|
49
|
-
|
50
|
-
|
51
|
-
|
52
|
-
]
|
34
|
+
def self.validate_download_minecraft(value, errors)
|
35
|
+
if ![true, false].include?(value)
|
36
|
+
errors << 'modloader "download_minecraft" must be true or false'
|
37
|
+
end
|
53
38
|
end
|
54
39
|
|
55
40
|
def base_uri
|
@@ -65,7 +50,7 @@ module Mmi
|
|
65
50
|
end
|
66
51
|
|
67
52
|
def metadata_path
|
68
|
-
File.join(Mmi.cache_dir, 'fabric_maven_metadata.xml')
|
53
|
+
File.join(Mmi::Constants.cache_dir, 'fabric_maven_metadata.xml')
|
69
54
|
end
|
70
55
|
|
71
56
|
def installer_uri
|
@@ -77,7 +62,7 @@ module Mmi
|
|
77
62
|
end
|
78
63
|
|
79
64
|
def installer_path
|
80
|
-
File.join(Mmi.cache_dir, "fabric-installer-#{self.version}.jar")
|
65
|
+
File.join(Mmi::Constants.cache_dir, "fabric-installer-#{self.version}.jar")
|
81
66
|
end
|
82
67
|
|
83
68
|
def absolute_install_dir
|
@@ -85,19 +70,25 @@ module Mmi
|
|
85
70
|
end
|
86
71
|
|
87
72
|
def download_installer
|
88
|
-
|
73
|
+
installer_hash = Mmi::ContentHash::Sha512.new(URI.parse(installer_sha512sum_uri).read)
|
89
74
|
|
90
|
-
|
91
|
-
Mmi
|
92
|
-
|
93
|
-
|
75
|
+
if !File.exist?(installer_path) || !installer_hash.match_file?(installer_path)
|
76
|
+
Mmi.info "Downloading fabric-installer version #{self.version.inspect}."
|
77
|
+
|
78
|
+
begin
|
79
|
+
Mmi::InstallUtils.download_to_file(installer_uri, installer_path, installer_hash)
|
80
|
+
rescue OpenURI::HTTPError => e
|
81
|
+
Mmi.fail! %Q{Error when requesting fabric installer. Maybe "modloader.version" == #{version.inspect} is invalid.\n#{e.inspect}}
|
82
|
+
end
|
83
|
+
else
|
84
|
+
Mmi.info "Using cached fabric-installer version #{self.version.inspect}."
|
94
85
|
end
|
95
86
|
end
|
96
87
|
|
97
88
|
def run_installer
|
98
89
|
FileUtils.mkdir_p(absolute_install_dir)
|
99
90
|
|
100
|
-
if system('java', '-jar', installer_path, self.install_type, '-dir', absolute_install_dir, '-noprofile', '-mcversion', self.
|
91
|
+
if system('java', '-jar', installer_path, self.install_type, '-dir', absolute_install_dir, '-noprofile', '-mcversion', self.minecraft_version, self.download_minecraft ? '-downloadMinecraft' : '')
|
101
92
|
# Pass.
|
102
93
|
else
|
103
94
|
Mmi.fail! 'Failed to install Fabric modloader.'
|
@@ -111,7 +102,7 @@ module Mmi
|
|
111
102
|
|
112
103
|
def available_versions
|
113
104
|
begin
|
114
|
-
Mmi::
|
105
|
+
Mmi::InstallUtils.download_to_file(metadata_uri, metadata_path, Mmi::ContentHash::Sha512.new(URI.parse(metadata_sha512sum_uri).read))
|
115
106
|
rescue OpenURI::HTTPError => e
|
116
107
|
Mmi.fail! "Error when requesting available fabric installer versions.\n#{e.inspect}"
|
117
108
|
end
|
data/lib/mmi/modloader/none.rb
CHANGED
data/lib/mmi/modrinth_api.rb
CHANGED
@@ -6,8 +6,8 @@ module Mmi
|
|
6
6
|
BASE_URL = URI('https://api.modrinth.com/v2/')
|
7
7
|
|
8
8
|
class << self
|
9
|
-
def project_versions(mod_slug)
|
10
|
-
JSON.parse((BASE_URL + "project/#{mod_slug}/version").open.read)
|
9
|
+
def project_versions(mod_slug, loader: nil, game_version: nil)
|
10
|
+
JSON.parse((BASE_URL + "project/#{mod_slug}/version?#{URI.encode_www_form(loaders: (%Q{["#{loader}"]} if loader), game_versions: (%Q{["#{game_version}"]} if game_version))}").open.read)
|
11
11
|
end
|
12
12
|
end
|
13
13
|
end
|
@@ -0,0 +1,192 @@
|
|
1
|
+
module Mmi
|
2
|
+
module PropertyAttributes
|
3
|
+
def initialize(hash)
|
4
|
+
@hash = hash
|
5
|
+
end
|
6
|
+
|
7
|
+
def to_h
|
8
|
+
@hash
|
9
|
+
end
|
10
|
+
|
11
|
+
def [](key)
|
12
|
+
@hash[key]
|
13
|
+
end
|
14
|
+
|
15
|
+
module ClassMethods
|
16
|
+
PropertyConfiguration = Struct.new(:key, :type, :required, :default, :requires, :conflicts, :validate)
|
17
|
+
|
18
|
+
def registered_properties
|
19
|
+
@registered_properties ||= {}
|
20
|
+
end
|
21
|
+
|
22
|
+
def property(method_name, key=method_name.to_s, type: nil, required: nil, default: nil, requires: [], conflicts: [], validate: nil)
|
23
|
+
raise(ArgumentError, '"method_name" must be a Symbol' ) if !method_name.is_a?(Symbol)
|
24
|
+
raise(ArgumentError, %Q{#{type.inspect} is not a valid "type"}) if !valid_atomic_type?(type) && !valid_hash_type?(type)
|
25
|
+
|
26
|
+
requires = [requires ] if !requires .is_a?(Array)
|
27
|
+
conflicts = [conflicts] if !conflicts.is_a?(Array)
|
28
|
+
|
29
|
+
raise(ArgumentError, '"required" is mutually exclusive with "requires" and "conflicts"') if required && (requires.any? || conflicts.any?)
|
30
|
+
raise(ArgumentError, '"requires" and "conflicts" must not share entries' ) if requires.intersect?(conflicts)
|
31
|
+
raise(ArgumentError, '"required" is mutually exclusive with "default"' ) if required && !default.nil?
|
32
|
+
|
33
|
+
required = requires.none? && conflicts.none? && default.nil? if required.nil?
|
34
|
+
|
35
|
+
raise(ArgumentError, '"validate" must be nil, a symbol or a Proc') if !validate.nil? && !validate.is_a?(Symbol) && !validate.is_a?(Proc)
|
36
|
+
|
37
|
+
registered_properties[method_name] = PropertyConfiguration.new(key: key, type: type, required: required, default: default, requires: requires, conflicts: conflicts, validate: validate)
|
38
|
+
|
39
|
+
define_method(method_name) do
|
40
|
+
parsed_property_store[method_name]
|
41
|
+
end
|
42
|
+
end
|
43
|
+
|
44
|
+
def parse(hash)
|
45
|
+
new(hash).parse!
|
46
|
+
end
|
47
|
+
|
48
|
+
def valid_atomic_type?(type)
|
49
|
+
type.nil? || type.respond_to?(:parse)
|
50
|
+
end
|
51
|
+
|
52
|
+
def valid_hash_type?(type_hash)
|
53
|
+
type_hash.is_a?(Hash) && type_hash.key?(:field) && type_hash.key?(:types) && type_hash.fetch(:types).is_a?(Hash) && type_hash.fetch(:types).all? do |_, type|
|
54
|
+
valid_atomic_type?(type)
|
55
|
+
end
|
56
|
+
end
|
57
|
+
end
|
58
|
+
|
59
|
+
def self.prepended(klass)
|
60
|
+
klass.extend(ClassMethods)
|
61
|
+
end
|
62
|
+
|
63
|
+
def parsed_property_store
|
64
|
+
@parsed_property_store ||= {}
|
65
|
+
end
|
66
|
+
|
67
|
+
class ValidationError < StandardError; end
|
68
|
+
|
69
|
+
def validate_constraints!
|
70
|
+
errors =
|
71
|
+
self.class.registered_properties.map do |method_name, configuration|
|
72
|
+
[
|
73
|
+
method_name,
|
74
|
+
[].tap do |property_errors|
|
75
|
+
if configuration.required && !@hash.key?(configuration.key)
|
76
|
+
property_errors << "missing field #{configuration.key}"
|
77
|
+
elsif !configuration.required && @hash.key?(configuration.key)
|
78
|
+
if (missing_requires = configuration.requires.reject{ |e| @hash.key?(e) }).any?
|
79
|
+
property_errors << "missing required field(s) #{missing_requires.map(&:inspect).join(', ')}"
|
80
|
+
end
|
81
|
+
|
82
|
+
if (present_conflicts = configuration.conflicts.select{ |e| @hash.key?(e) }).any?
|
83
|
+
property_errors << "conflicting with field(s) #{present_conflicts.map(&:inspect).join(', ')}"
|
84
|
+
end
|
85
|
+
end
|
86
|
+
|
87
|
+
if @hash.key?(configuration.key) && self.class.valid_hash_type?(configuration.type)
|
88
|
+
if !@hash.fetch(configuration.key).is_a?(Hash)
|
89
|
+
property_errors << "field #{configuration.key.inspect} must be a Hash"
|
90
|
+
elsif !@hash.fetch(configuration.key).key?(configuration.type[:field]) || !configuration.type[:types].keys.include?(@hash.fetch(configuration.key).fetch(configuration.type[:field]))
|
91
|
+
property_errors << "field #{configuration.key.inspect} must have key #{configuration.type[:field].inspect} with one of values #{configuration.type[:types].keys.map(&:inspect).join(', ')}"
|
92
|
+
end
|
93
|
+
end
|
94
|
+
|
95
|
+
if @hash.key?(configuration.key) && configuration.validate
|
96
|
+
deduced_proc =
|
97
|
+
if configuration.validate.is_a?(Symbol)
|
98
|
+
self.class.method(configuration.validate)
|
99
|
+
else
|
100
|
+
configuration.validate
|
101
|
+
end
|
102
|
+
|
103
|
+
deduced_proc.call(@hash[configuration.key], property_errors)
|
104
|
+
end
|
105
|
+
end,
|
106
|
+
]
|
107
|
+
end.select do |_, property_errors|
|
108
|
+
property_errors.any?
|
109
|
+
end.to_h
|
110
|
+
|
111
|
+
if errors.none?
|
112
|
+
true
|
113
|
+
else
|
114
|
+
raise(ValidationError, errors.map do |method_name, property_errors|
|
115
|
+
"#{method_name}: #{property_errors.join(', ')}"
|
116
|
+
end.join('; '))
|
117
|
+
end
|
118
|
+
end
|
119
|
+
|
120
|
+
def parse!
|
121
|
+
validate_constraints!
|
122
|
+
|
123
|
+
self.class.registered_properties.each do |method_name, configuration|
|
124
|
+
if !@hash.key?(configuration.key)
|
125
|
+
if configuration.required || !configuration.default.nil?
|
126
|
+
parsed_property_store[method_name] = configuration.default
|
127
|
+
else
|
128
|
+
# Do nothing.
|
129
|
+
end
|
130
|
+
elsif configuration.type.nil?
|
131
|
+
parsed_property_store[method_name] = self[configuration.key]
|
132
|
+
else
|
133
|
+
deduced_type =
|
134
|
+
if self.class.valid_hash_type?(configuration.type)
|
135
|
+
configuration.type[:types].fetch(self[configuration.key][configuration.type[:field]])
|
136
|
+
else
|
137
|
+
configuration.type
|
138
|
+
end
|
139
|
+
|
140
|
+
initializer_proc = proc do |item|
|
141
|
+
deduced_type.parse(item)
|
142
|
+
end
|
143
|
+
|
144
|
+
parsed_property_store[method_name] =
|
145
|
+
self[configuration.key].then do |value|
|
146
|
+
value.is_a?(Array) ? value.map(&initializer_proc) : initializer_proc[value]
|
147
|
+
end
|
148
|
+
end
|
149
|
+
end
|
150
|
+
|
151
|
+
self
|
152
|
+
end
|
153
|
+
|
154
|
+
def update_properties!(properties)
|
155
|
+
raise(ArgumentError, 'argument must be a Hash' ) if !properties.is_a?(Hash)
|
156
|
+
raise(ArgumentError, 'argument can only have keys that are defined properties') if (properties.keys - self.class.registered_properties.keys).any?
|
157
|
+
|
158
|
+
old_property_store = @parsed_property_store
|
159
|
+
@parsed_property_store = nil
|
160
|
+
|
161
|
+
old_properties = properties.map do |method_name, value|
|
162
|
+
key = self.class.registered_properties[method_name].key
|
163
|
+
|
164
|
+
old_value = @hash[key]
|
165
|
+
|
166
|
+
if !value.nil?
|
167
|
+
@hash[key] = value
|
168
|
+
elsif @hash.key?(key)
|
169
|
+
@hash.delete(key)
|
170
|
+
end
|
171
|
+
|
172
|
+
[method_name, old_value]
|
173
|
+
end
|
174
|
+
|
175
|
+
parse!
|
176
|
+
rescue ValidationError
|
177
|
+
@parsed_property_store = old_property_store
|
178
|
+
|
179
|
+
old_properties.each do |method_name, old_value|
|
180
|
+
key = self.class.registered_properties[method_name].key
|
181
|
+
|
182
|
+
if !old_value.nil?
|
183
|
+
@hash[key] = old_value
|
184
|
+
elsif @hash.key?(key)
|
185
|
+
@hash.delete(key)
|
186
|
+
end
|
187
|
+
end
|
188
|
+
|
189
|
+
raise
|
190
|
+
end
|
191
|
+
end
|
192
|
+
end
|
data/lib/mmi/source/github.rb
CHANGED
@@ -2,53 +2,23 @@ require 'fileutils'
|
|
2
2
|
require 'open-uri'
|
3
3
|
|
4
4
|
require 'mmi/github_api'
|
5
|
-
require 'mmi/
|
5
|
+
require 'mmi/property_attributes'
|
6
6
|
|
7
7
|
module Mmi
|
8
8
|
module Source
|
9
9
|
class Github
|
10
|
-
|
10
|
+
prepend Mmi::PropertyAttributes
|
11
11
|
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
12
|
+
property :owner
|
13
|
+
property :repo
|
14
|
+
property :install_dir
|
15
|
+
property :filename, required: false
|
16
16
|
|
17
|
-
|
18
|
-
|
19
|
-
|
17
|
+
property :asset_id, conflicts: %w[release file]
|
18
|
+
property :release, conflicts: 'asset_id', requires: 'file'
|
19
|
+
property :file, conflicts: 'asset_id', requires: 'release'
|
20
20
|
|
21
|
-
|
22
|
-
@options = options
|
23
|
-
|
24
|
-
parse!
|
25
|
-
end
|
26
|
-
|
27
|
-
def parse!
|
28
|
-
if self.owner
|
29
|
-
if self.repo
|
30
|
-
if self.install_dir
|
31
|
-
if self.asset_id
|
32
|
-
# Pass.
|
33
|
-
elsif self.release
|
34
|
-
if self.file
|
35
|
-
# Pass.
|
36
|
-
else
|
37
|
-
raise Mmi::MissingAttributeError, 'Missing "source.file" from asset because "source.asset_id" is not provided.'
|
38
|
-
end
|
39
|
-
else
|
40
|
-
raise Mmi::MissingAttributeError, 'Missing "source.release" from asset because "source.asset_id" is not provided.'
|
41
|
-
end
|
42
|
-
else
|
43
|
-
raise Mmi::MissingAttributeError, 'Missing "source.install_dir" from asset.'
|
44
|
-
end
|
45
|
-
else
|
46
|
-
raise Mmi::MissingAttributeError, 'Missing "source.repo" from asset.'
|
47
|
-
end
|
48
|
-
else
|
49
|
-
raise Mmi::MissingAttributeError, 'Missing "source.owner" from asset.'
|
50
|
-
end
|
51
|
-
end
|
21
|
+
# TODO: Ensure that either :asset_id or [:release, :file] is given.
|
52
22
|
|
53
23
|
def repository_url
|
54
24
|
"https://github.com/#{self.owner}/#{self.repo}"
|
@@ -66,21 +36,10 @@ module Mmi
|
|
66
36
|
end
|
67
37
|
end
|
68
38
|
|
69
|
-
def install(
|
70
|
-
|
71
|
-
filepath = File.join(install_dir, self.filename || (self.asset_id ? cached_asset_response.name : self.file))
|
72
|
-
|
73
|
-
Mmi.info "Downloading #{download_url.inspect} into #{filepath.inspect}."
|
39
|
+
def install(install_record)
|
40
|
+
filepath = File.join(install_dir, self.filename || (self.asset_id ? cached_asset_response.name : self.file))
|
74
41
|
|
75
|
-
|
76
|
-
|
77
|
-
begin
|
78
|
-
stream = URI.parse(download_url).open
|
79
|
-
|
80
|
-
IO.copy_stream(stream, filepath)
|
81
|
-
rescue OpenURI::HTTPError => e
|
82
|
-
Mmi.fail! "Error when requesting asset.\n#{e.inspect}"
|
83
|
-
end
|
42
|
+
install_record.add(download_url, filepath, content_hash: nil) # As of 2024-07-18, the GitHub API does not return any hash over the asset.
|
84
43
|
end
|
85
44
|
|
86
45
|
def display_name
|
data/lib/mmi/source/modrinth.rb
CHANGED
@@ -2,73 +2,42 @@ require 'fileutils'
|
|
2
2
|
require 'open-uri'
|
3
3
|
|
4
4
|
require 'mmi/modrinth_api'
|
5
|
-
require 'mmi/
|
5
|
+
require 'mmi/property_attributes'
|
6
|
+
require 'mmi/content_hash/sha512'
|
6
7
|
|
7
8
|
module Mmi
|
8
9
|
module Source
|
9
10
|
class Modrinth
|
10
|
-
|
11
|
+
prepend Mmi::PropertyAttributes
|
11
12
|
|
12
|
-
|
13
|
-
|
14
|
-
|
13
|
+
property :name
|
14
|
+
property :version
|
15
|
+
property :version_file
|
16
|
+
property :install_dir
|
17
|
+
property :filename, required: false
|
15
18
|
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
def initialize(options)
|
20
|
-
@options = options
|
21
|
-
|
22
|
-
parse!
|
23
|
-
end
|
24
|
-
|
25
|
-
def parse!
|
26
|
-
if self.name
|
27
|
-
if self.version
|
28
|
-
if self.version_file
|
29
|
-
if self.install_dir
|
30
|
-
# Pass.
|
31
|
-
else
|
32
|
-
raise Mmi::MissingAttributeError, 'Missing "source.install_dir" from asset.'
|
33
|
-
end
|
34
|
-
else
|
35
|
-
raise Mmi::MissingAttributeError, 'Missing "source.version_file" from asset.'
|
36
|
-
end
|
37
|
-
else
|
38
|
-
raise Mmi::MissingAttributeError, 'Missing "source.version" from asset.'
|
39
|
-
end
|
40
|
-
else
|
41
|
-
raise Mmi::MissingAttributeError, 'Missing "source.name" from asset.'
|
42
|
-
end
|
43
|
-
end
|
44
|
-
|
45
|
-
def cached_mod_versions
|
46
|
-
@cached_mod_versions ||= Mmi::ModrinthApi.project_versions(self.name)
|
19
|
+
def cached_mod_versions(loader: nil, game_version: nil)
|
20
|
+
(@cached_mod_versions ||= {})[{loader: loader, game_version: game_version}] ||= Mmi::ModrinthApi.project_versions(self.name, loader: loader, game_version: game_version)
|
47
21
|
end
|
48
22
|
|
49
|
-
def
|
23
|
+
def api_version_file
|
50
24
|
cached_mod_versions.select do |version|
|
51
25
|
version['name'] == self.version
|
52
|
-
end.
|
26
|
+
end.map do |version|
|
27
|
+
version['files']
|
28
|
+
end.flatten(1).select do |files|
|
53
29
|
files['filename'] == self.version_file
|
54
|
-
end.first
|
30
|
+
end.first
|
55
31
|
end
|
56
32
|
|
57
|
-
def
|
58
|
-
|
59
|
-
|
60
|
-
|
61
|
-
|
62
|
-
|
63
|
-
FileUtils.mkdir_p(install_dir)
|
33
|
+
def download_url
|
34
|
+
api_version_file['url'].gsub(/ /, '%20')
|
35
|
+
end
|
36
|
+
|
37
|
+
def install(install_record)
|
38
|
+
filepath = File.join(install_dir, self.filename || self.version_file)
|
64
39
|
|
65
|
-
|
66
|
-
stream = URI.parse(download_url).open
|
67
|
-
|
68
|
-
IO.copy_stream(stream, filepath)
|
69
|
-
rescue OpenURI::HTTPError => e
|
70
|
-
Mmi.fail! "Error when requesting asset.\n#{e.inspect}"
|
71
|
-
end
|
40
|
+
install_record.add(download_url, filepath, content_hash: Mmi::ContentHash::Sha512.new(api_version_file['hashes']['sha512']))
|
72
41
|
end
|
73
42
|
|
74
43
|
def display_name
|
data/lib/mmi/source/url.rb
CHANGED
@@ -1,54 +1,25 @@
|
|
1
1
|
require 'fileutils'
|
2
2
|
require 'open-uri'
|
3
3
|
|
4
|
-
require 'mmi/
|
4
|
+
require 'mmi/property_attributes'
|
5
5
|
|
6
6
|
module Mmi
|
7
7
|
module Source
|
8
8
|
class Url
|
9
|
-
|
9
|
+
prepend Mmi::PropertyAttributes
|
10
10
|
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
def initialize(options)
|
16
|
-
@options = options
|
17
|
-
|
18
|
-
parse!
|
19
|
-
end
|
20
|
-
|
21
|
-
def parse!
|
22
|
-
if self.url
|
23
|
-
if self.install_dir
|
24
|
-
# Pass.
|
25
|
-
else
|
26
|
-
raise Mmi::MissingAttributeError, 'Missing "source.install_dir" from asset.'
|
27
|
-
end
|
28
|
-
else
|
29
|
-
raise Mmi::MissingAttributeError, 'Missing "source.name" from asset.'
|
30
|
-
end
|
31
|
-
end
|
11
|
+
property :url
|
12
|
+
property :install_dir
|
13
|
+
property :filename, required: false
|
32
14
|
|
33
15
|
def download_uri
|
34
16
|
@download_uri ||= URI.parse(url)
|
35
17
|
end
|
36
18
|
|
37
|
-
def install(
|
38
|
-
|
39
|
-
filepath = File.join(install_dir, self.filename || File.basename(download_uri.path))
|
40
|
-
|
41
|
-
Mmi.info "Downloading #{url.inspect} into #{filepath.inspect}."
|
42
|
-
|
43
|
-
FileUtils.mkdir_p(install_dir)
|
19
|
+
def install(install_record)
|
20
|
+
filepath = File.join(install_dir, self.filename || File.basename(download_uri.path))
|
44
21
|
|
45
|
-
|
46
|
-
stream = download_uri.open
|
47
|
-
|
48
|
-
IO.copy_stream(stream, filepath)
|
49
|
-
rescue OpenURI::HTTPError => e
|
50
|
-
Mmi.fail! "Error when requesting asset.\n#{e.inspect}"
|
51
|
-
end
|
22
|
+
install_record.add(url, filepath, content_hash: nil)
|
52
23
|
end
|
53
24
|
|
54
25
|
def display_name
|
data/lib/mmi/version.rb
CHANGED
data/lib/mmi.rb
CHANGED
@@ -2,17 +2,6 @@ require 'mmi/version'
|
|
2
2
|
require 'mmi/mod_file_processor'
|
3
3
|
|
4
4
|
module Mmi
|
5
|
-
MMI_CACHE_DIR = File.join(Dir.home, '.cache', 'mmi')
|
6
|
-
MINECRAFT_DIR = File.join(Dir.home, '.minecraft')
|
7
|
-
|
8
|
-
def self.cache_dir
|
9
|
-
MMI_CACHE_DIR
|
10
|
-
end
|
11
|
-
|
12
|
-
def self.minecraft_dir
|
13
|
-
MINECRAFT_DIR
|
14
|
-
end
|
15
|
-
|
16
5
|
class ValidationError < StandardError; end
|
17
6
|
class MissingAttributeError < ValidationError; end
|
18
7
|
class InvalidAttributeError < ValidationError; end
|