modsvaskr 0.1.8 → 0.1.12
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/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
|