modsvaskr 0.1.8 → 0.1.12
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/bin/modsvaskr +11 -11
- data/lib/modsvaskr/config.rb +78 -79
- data/lib/modsvaskr/encoding.rb +31 -0
- data/lib/modsvaskr/game.rb +208 -179
- data/lib/modsvaskr/games/skyrim_se.rb +106 -92
- data/lib/modsvaskr/in_game_tests_runner.rb +348 -348
- data/lib/modsvaskr/logger.rb +45 -44
- data/lib/modsvaskr/run_cmd.rb +23 -22
- data/lib/modsvaskr/tests_runner.rb +204 -205
- data/lib/modsvaskr/tests_suite.rb +70 -69
- data/lib/modsvaskr/tests_suites/exterior_cell.rb +120 -117
- data/lib/modsvaskr/tests_suites/interior_cell.rb +63 -63
- data/lib/modsvaskr/tests_suites/npc.rb +67 -39
- data/lib/modsvaskr/tests_suites/npc_head.rb +6 -10
- data/lib/modsvaskr/ui.rb +205 -205
- data/lib/modsvaskr/version.rb +5 -5
- data/lib/modsvaskr/xedit.rb +65 -52
- data/xedit_scripts/Modsvaskr_DumpInfo.pas +13 -0
- metadata +39 -10
@@ -1,69 +1,70 @@
|
|
1
|
-
require 'fileutils'
|
2
|
-
require 'json'
|
3
|
-
require 'modsvaskr/logger'
|
4
|
-
require 'modsvaskr/run_cmd'
|
5
|
-
|
6
|
-
module Modsvaskr
|
7
|
-
|
8
|
-
# Common functionality for any tests suite
|
9
|
-
class TestsSuite
|
10
|
-
|
11
|
-
include
|
12
|
-
|
13
|
-
|
14
|
-
#
|
15
|
-
#
|
16
|
-
#
|
17
|
-
# * *
|
18
|
-
|
19
|
-
|
20
|
-
@
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
#
|
25
|
-
#
|
26
|
-
#
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
|
32
|
-
#
|
33
|
-
#
|
34
|
-
#
|
35
|
-
#
|
36
|
-
|
37
|
-
|
38
|
-
|
39
|
-
|
40
|
-
|
41
|
-
|
42
|
-
|
43
|
-
|
44
|
-
|
45
|
-
|
46
|
-
|
47
|
-
|
48
|
-
|
49
|
-
File.
|
50
|
-
|
51
|
-
|
52
|
-
|
53
|
-
|
54
|
-
|
55
|
-
|
56
|
-
|
57
|
-
|
58
|
-
|
59
|
-
|
60
|
-
#
|
61
|
-
#
|
62
|
-
#
|
63
|
-
|
64
|
-
|
65
|
-
|
66
|
-
|
67
|
-
|
68
|
-
|
69
|
-
|
1
|
+
require 'fileutils'
|
2
|
+
require 'json'
|
3
|
+
require 'modsvaskr/logger'
|
4
|
+
require 'modsvaskr/run_cmd'
|
5
|
+
|
6
|
+
module Modsvaskr
|
7
|
+
|
8
|
+
# Common functionality for any tests suite
|
9
|
+
class TestsSuite
|
10
|
+
|
11
|
+
include RunCmd
|
12
|
+
include Logger
|
13
|
+
|
14
|
+
# Constructor
|
15
|
+
#
|
16
|
+
# Parameters::
|
17
|
+
# * *tests_suite* (Symbol): The tests suite name
|
18
|
+
# * *game* (Game): The game for which this test type is instantiated
|
19
|
+
def initialize(tests_suite, game)
|
20
|
+
@tests_suite = tests_suite
|
21
|
+
@game = game
|
22
|
+
end
|
23
|
+
|
24
|
+
# Get test statuses
|
25
|
+
#
|
26
|
+
# Result::
|
27
|
+
# * Array<[String, String]>: Ordered list of [test name, test status]
|
28
|
+
def statuses
|
29
|
+
File.exist?(json_statuses_file) ? JSON.parse(File.read(json_statuses_file)) : []
|
30
|
+
end
|
31
|
+
|
32
|
+
# Set test statuses.
|
33
|
+
# Add new ones and overwrites existing ones.
|
34
|
+
#
|
35
|
+
# Parameters::
|
36
|
+
# * *statuses* (Array<[String, String]>): Ordered list of [test name, test status]
|
37
|
+
def statuses=(statuses)
|
38
|
+
current_statuses = self.statuses
|
39
|
+
statuses.each do |(test_name, test_status)|
|
40
|
+
test_status_info = current_statuses.find { |(search_test_name, _search_test_status)| search_test_name == test_name }
|
41
|
+
if test_status_info.nil?
|
42
|
+
# New one. Add it to the end.
|
43
|
+
current_statuses << [test_name, test_status]
|
44
|
+
else
|
45
|
+
# Already existing. Just change its status.
|
46
|
+
test_status_info[1] = test_status
|
47
|
+
end
|
48
|
+
end
|
49
|
+
FileUtils.mkdir_p File.dirname(json_statuses_file)
|
50
|
+
File.write(json_statuses_file, JSON.pretty_generate(current_statuses))
|
51
|
+
end
|
52
|
+
|
53
|
+
# Remove all tests from this suite
|
54
|
+
def clear_tests
|
55
|
+
File.unlink(json_statuses_file) if File.exist?(json_statuses_file)
|
56
|
+
end
|
57
|
+
|
58
|
+
private
|
59
|
+
|
60
|
+
# Get the JSON statuses file name
|
61
|
+
#
|
62
|
+
# Result::
|
63
|
+
# * String: The JSON statuses file name
|
64
|
+
def json_statuses_file
|
65
|
+
"#{@game.path}/Data/Modsvaskr/Tests/Statuses_#{@tests_suite}.json"
|
66
|
+
end
|
67
|
+
|
68
|
+
end
|
69
|
+
|
70
|
+
end
|
@@ -1,117 +1,120 @@
|
|
1
|
-
require 'modsvaskr/in_game_tests_suite'
|
2
|
-
|
3
|
-
module Modsvaskr
|
4
|
-
|
5
|
-
module TestsSuites
|
6
|
-
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
#
|
13
|
-
#
|
14
|
-
#
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
#
|
21
|
-
#
|
22
|
-
#
|
23
|
-
#
|
24
|
-
|
25
|
-
|
26
|
-
#
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
|
32
|
-
|
33
|
-
|
34
|
-
|
35
|
-
|
36
|
-
|
37
|
-
|
38
|
-
|
39
|
-
exterior_cells[esp_name]
|
40
|
-
exterior_cells[esp_name][cell_name]
|
41
|
-
|
42
|
-
|
43
|
-
|
44
|
-
|
45
|
-
|
46
|
-
|
47
|
-
|
48
|
-
|
49
|
-
|
50
|
-
|
51
|
-
|
52
|
-
|
53
|
-
|
54
|
-
|
55
|
-
|
56
|
-
|
57
|
-
|
58
|
-
|
59
|
-
|
60
|
-
|
61
|
-
|
62
|
-
|
63
|
-
|
64
|
-
|
65
|
-
#
|
66
|
-
|
67
|
-
|
68
|
-
|
69
|
-
|
70
|
-
#
|
71
|
-
|
72
|
-
|
73
|
-
|
74
|
-
remaining_cells[cell_x]
|
75
|
-
|
76
|
-
|
77
|
-
|
78
|
-
|
79
|
-
|
80
|
-
#
|
81
|
-
#
|
82
|
-
|
83
|
-
|
84
|
-
|
85
|
-
|
86
|
-
|
87
|
-
|
88
|
-
|
89
|
-
|
90
|
-
|
91
|
-
|
92
|
-
|
93
|
-
|
94
|
-
|
95
|
-
|
96
|
-
|
97
|
-
|
98
|
-
|
99
|
-
|
100
|
-
|
101
|
-
|
102
|
-
|
103
|
-
|
104
|
-
|
105
|
-
|
106
|
-
|
107
|
-
|
108
|
-
|
109
|
-
|
110
|
-
|
111
|
-
|
112
|
-
|
113
|
-
|
114
|
-
|
115
|
-
|
116
|
-
|
117
|
-
|
1
|
+
require 'modsvaskr/in_game_tests_suite'
|
2
|
+
|
3
|
+
module Modsvaskr
|
4
|
+
|
5
|
+
module TestsSuites
|
6
|
+
|
7
|
+
# Test exterior cells by using cow and camera spanning
|
8
|
+
class ExteriorCell < TestsSuite
|
9
|
+
|
10
|
+
include InGameTestsSuite
|
11
|
+
|
12
|
+
# Return the in-game tests suite to which we forward the tests to be run
|
13
|
+
#
|
14
|
+
# Result::
|
15
|
+
# * Symbol: In-game tests suite
|
16
|
+
def in_game_tests_suite
|
17
|
+
:locations
|
18
|
+
end
|
19
|
+
|
20
|
+
# Discover the list of tests information that could be run.
|
21
|
+
# [API] - This method is mandatory
|
22
|
+
#
|
23
|
+
# Result::
|
24
|
+
# * Hash< String, Hash<Symbol,Object> >: Ordered hash of test information, per test name
|
25
|
+
def discover_tests
|
26
|
+
# List of exterior cells coordinates, per worldspace name, per plugin name
|
27
|
+
# Hash< String, Hash< String, Array<[Integer, Integer]> > >
|
28
|
+
exterior_cells = {}
|
29
|
+
@game.xedit.run_script('DumpInfo', only_once: true)
|
30
|
+
@game.xedit.parse_csv('Modsvaskr_ExportedDumpInfo') do |row|
|
31
|
+
esp_name, record_type = row[0..1]
|
32
|
+
if record_type.downcase == 'cell'
|
33
|
+
cell_type, cell_name, cell_x, cell_y = row[3..6]
|
34
|
+
if cell_type == 'cow'
|
35
|
+
if cell_x.nil?
|
36
|
+
log "!!! Invalid record: #{row}"
|
37
|
+
else
|
38
|
+
esp_name.downcase!
|
39
|
+
exterior_cells[esp_name] = {} unless exterior_cells.key?(esp_name)
|
40
|
+
exterior_cells[esp_name][cell_name] = [] unless exterior_cells[esp_name].key?(cell_name)
|
41
|
+
exterior_cells[esp_name][cell_name] << [Integer(cell_x), Integer(cell_y)]
|
42
|
+
end
|
43
|
+
end
|
44
|
+
end
|
45
|
+
end
|
46
|
+
# Test only exterior cells that have been changed by mods, and make sure we test the minimum, knowing that each cell loaded in game tests 5x5 cells around
|
47
|
+
vanilla_esps = @game.game_esps
|
48
|
+
vanilla_exterior_cells = vanilla_esps.inject({}) do |merged_worldspaces, esp_name|
|
49
|
+
merged_worldspaces.merge(exterior_cells[esp_name] || {}) do |_worldspace, ext_cells_1, ext_cells_2|
|
50
|
+
(ext_cells_1 + ext_cells_2).sort.uniq
|
51
|
+
end
|
52
|
+
end
|
53
|
+
changed_exterior_cells = {}
|
54
|
+
exterior_cells.each do |esp_name, esp_exterior_cells|
|
55
|
+
next if vanilla_esps.include?(esp_name)
|
56
|
+
|
57
|
+
esp_exterior_cells.each do |worldspace, worldspace_exterior_cells|
|
58
|
+
if vanilla_exterior_cells.key?(worldspace)
|
59
|
+
changed_exterior_cells[worldspace] = [] unless changed_exterior_cells.key?(worldspace)
|
60
|
+
changed_exterior_cells[worldspace].concat(vanilla_exterior_cells[worldspace] & worldspace_exterior_cells)
|
61
|
+
end
|
62
|
+
end
|
63
|
+
end
|
64
|
+
tests = {}
|
65
|
+
# Value taken from the ini file
|
66
|
+
# TODO: Read it from there (uiGrid)
|
67
|
+
loaded_grid = 5
|
68
|
+
delta_cells = loaded_grid / 2
|
69
|
+
changed_exterior_cells.each do |worldspace, worldspace_exterior_cells|
|
70
|
+
# Make sure we select the minimum cells
|
71
|
+
# Use a Hash of Hashes for the coordinates to speed-up their lookup.
|
72
|
+
remaining_cells = {}
|
73
|
+
worldspace_exterior_cells.each do |(cell_x, cell_y)|
|
74
|
+
remaining_cells[cell_x] = {} unless remaining_cells.key?(cell_x)
|
75
|
+
remaining_cells[cell_x][cell_y] = nil
|
76
|
+
end
|
77
|
+
until remaining_cells.empty?
|
78
|
+
cell_x, cell_ys = remaining_cells.first
|
79
|
+
cell_y, _nil = cell_ys.first
|
80
|
+
# We want to test cell_x, cell_y.
|
81
|
+
# Knowing that we can test it by loading any cell in the range ((cell_x - delta_cells..cell_x + delta_cells), (cell_y - delta_cells..cell_y + delta_cells)),
|
82
|
+
# check which cell would test the most wanted cells from our list
|
83
|
+
best_cell_x = nil
|
84
|
+
best_cell_y = nil
|
85
|
+
best_cell_score = nil
|
86
|
+
(cell_x - delta_cells..cell_x + delta_cells).each do |candidate_cell_x|
|
87
|
+
(cell_y - delta_cells..cell_y + delta_cells).each do |candidate_cell_y|
|
88
|
+
# Check the number of cells that would be tested if we were to test (candidate_cell_x, candidate_cell_y)
|
89
|
+
nbr_tested_cells = remaining_cells.
|
90
|
+
slice(*(candidate_cell_x - delta_cells..candidate_cell_x + delta_cells)).
|
91
|
+
inject(0) { |sum_cells, (_cur_cell_x, cur_cell_ys)| sum_cells + cur_cell_ys.slice(*(candidate_cell_y - delta_cells..candidate_cell_y + delta_cells)).size }
|
92
|
+
next unless best_cell_score.nil? || nbr_tested_cells > best_cell_score
|
93
|
+
|
94
|
+
best_cell_score = nbr_tested_cells
|
95
|
+
best_cell_x = candidate_cell_x
|
96
|
+
best_cell_y = candidate_cell_y
|
97
|
+
end
|
98
|
+
end
|
99
|
+
# Remove the tested cells from the remaining ones
|
100
|
+
(best_cell_x - delta_cells..best_cell_x + delta_cells).each do |cur_cell_x|
|
101
|
+
next unless remaining_cells.key?(cur_cell_x)
|
102
|
+
|
103
|
+
(best_cell_y - delta_cells..best_cell_y + delta_cells).each do |cur_cell_y|
|
104
|
+
remaining_cells[cur_cell_x].delete(cur_cell_y)
|
105
|
+
end
|
106
|
+
remaining_cells.delete(cur_cell_x) if remaining_cells[cur_cell_x].empty?
|
107
|
+
end
|
108
|
+
tests["#{worldspace}/#{best_cell_x}/#{best_cell_y}"] = {
|
109
|
+
name: "Load #{worldspace} cell #{best_cell_x}, #{best_cell_y}"
|
110
|
+
}
|
111
|
+
end
|
112
|
+
end
|
113
|
+
tests
|
114
|
+
end
|
115
|
+
|
116
|
+
end
|
117
|
+
|
118
|
+
end
|
119
|
+
|
120
|
+
end
|
@@ -1,63 +1,63 @@
|
|
1
|
-
require 'modsvaskr/in_game_tests_suite'
|
2
|
-
|
3
|
-
module Modsvaskr
|
4
|
-
|
5
|
-
module TestsSuites
|
6
|
-
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
#
|
13
|
-
#
|
14
|
-
#
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
#
|
21
|
-
#
|
22
|
-
#
|
23
|
-
#
|
24
|
-
|
25
|
-
|
26
|
-
#
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
|
32
|
-
|
33
|
-
|
34
|
-
|
35
|
-
|
36
|
-
interior_cells[esp_name]
|
37
|
-
|
38
|
-
|
39
|
-
|
40
|
-
|
41
|
-
|
42
|
-
|
43
|
-
|
44
|
-
|
45
|
-
|
46
|
-
|
47
|
-
|
48
|
-
|
49
|
-
|
50
|
-
|
51
|
-
|
52
|
-
|
53
|
-
|
54
|
-
|
55
|
-
|
56
|
-
|
57
|
-
end
|
58
|
-
|
59
|
-
end
|
60
|
-
|
61
|
-
end
|
62
|
-
|
63
|
-
end
|
1
|
+
require 'modsvaskr/in_game_tests_suite'
|
2
|
+
|
3
|
+
module Modsvaskr
|
4
|
+
|
5
|
+
module TestsSuites
|
6
|
+
|
7
|
+
# Test interior cells by using coc and camera spanning
|
8
|
+
class InteriorCell < TestsSuite
|
9
|
+
|
10
|
+
include InGameTestsSuite
|
11
|
+
|
12
|
+
# Return the in-game tests suite to which we forward the tests to be run
|
13
|
+
#
|
14
|
+
# Result::
|
15
|
+
# * Symbol: In-game tests suite
|
16
|
+
def in_game_tests_suite
|
17
|
+
:locations
|
18
|
+
end
|
19
|
+
|
20
|
+
# Discover the list of tests information that could be run.
|
21
|
+
# [API] - This method is mandatory
|
22
|
+
#
|
23
|
+
# Result::
|
24
|
+
# * Hash< String, Hash<Symbol,Object> >: Ordered hash of test information, per test name
|
25
|
+
def discover_tests
|
26
|
+
# List of interior cells, per plugin name
|
27
|
+
# Hash< String, Array<String> >
|
28
|
+
interior_cells = {}
|
29
|
+
@game.xedit.run_script('DumpInfo', only_once: true)
|
30
|
+
@game.xedit.parse_csv('Modsvaskr_ExportedDumpInfo') do |row|
|
31
|
+
esp_name, record_type = row[0..1]
|
32
|
+
if record_type.downcase == 'cell'
|
33
|
+
cell_type, cell_name = row[3..4]
|
34
|
+
if cell_type == 'coc'
|
35
|
+
esp_name.downcase!
|
36
|
+
interior_cells[esp_name] = [] unless interior_cells.key?(esp_name)
|
37
|
+
interior_cells[esp_name] << cell_name
|
38
|
+
end
|
39
|
+
end
|
40
|
+
end
|
41
|
+
# Test only interior cells that have been changed by mods
|
42
|
+
vanilla_esps = @game.game_esps
|
43
|
+
vanilla_interior_cells = vanilla_esps.map { |esp_name| interior_cells[esp_name] || [] }.flatten.sort.uniq
|
44
|
+
interior_cells.
|
45
|
+
map { |esp_name, esp_cells| vanilla_esps.include?(esp_name) ? [] : vanilla_interior_cells & esp_cells }.
|
46
|
+
flatten.
|
47
|
+
sort.
|
48
|
+
uniq.
|
49
|
+
map do |cell_name|
|
50
|
+
[
|
51
|
+
cell_name,
|
52
|
+
{
|
53
|
+
name: "Load cell #{cell_name}"
|
54
|
+
}
|
55
|
+
]
|
56
|
+
end.to_h
|
57
|
+
end
|
58
|
+
|
59
|
+
end
|
60
|
+
|
61
|
+
end
|
62
|
+
|
63
|
+
end
|