textflight-client 1.0.1

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,33 @@
1
+ require "textflight-client/version"
2
+ require "textflight-client/logging"
3
+ require "textflight-client/environment"
4
+ require "textflight-client/dot_dir"
5
+ require "textflight-client/string_utils"
6
+ require "textflight-client/command_parser"
7
+ require "textflight-client/response_parser"
8
+ require "textflight-client/models/model"
9
+ require "textflight-client/models/nav"
10
+ require "textflight-client/models/scan"
11
+ require "textflight-client/models/status_report"
12
+ require "textflight-client/models/status"
13
+ require "textflight-client/models/local"
14
+ require "textflight-client/tfprompt"
15
+
16
+ module TFClient
17
+
18
+ # Prints a deprecated message that includes the line number.
19
+ #
20
+ # @param [String] version Indicates when the feature was deprecated.
21
+ # @param [String] msg Deprecation message (possibly suggesting alternatives)
22
+ # @return [void]
23
+ def self.deprecated(version, msg)
24
+
25
+ stack = Kernel.caller(0, 6)[1..-1].join("\n")
26
+
27
+ msg = "deprecated '#{version}' - #{msg}\n#{stack}"
28
+
29
+ $stderr.puts "\033[34mWARN: #{msg}\033[0m"
30
+ $stderr.flush
31
+ end
32
+
33
+ end
@@ -0,0 +1,42 @@
1
+ module TFClient
2
+ class CommandParser
3
+
4
+ DIRECTION_MAP = {
5
+ n: "6",
6
+ ne: "7",
7
+ e: "4",
8
+ se: "2",
9
+ s: "1",
10
+ sw: "0",
11
+ w: "3",
12
+ nw: "5"
13
+ }
14
+
15
+ # 0: 315 degrees, X=-1, Y=-1 (northwest)
16
+ # 1: 0 degrees, X=0, Y=-1 (north)
17
+ # 2: 45 degrees, X=1, Y=-1 (northeast)
18
+ # 3: 270 degrees, X=-1, Y=0 (west)
19
+ # 4: 90 degrees, X=1, Y=0 (east)
20
+ # 5: 225 degrees, X=-1, Y=1 (southwest)
21
+ # 6: 180 degrees, X=0, Y=-1 (south)
22
+ # 7: 135 degrees, X=1, Y=-1 (southeast)
23
+
24
+ attr_reader :command
25
+ def initialize(command:)
26
+ @command = command
27
+ end
28
+
29
+ def parse
30
+ if @command == "quit" || @command == "exit"
31
+ return "exit"
32
+ end
33
+
34
+ direction = DIRECTION_MAP[@command.to_sym]
35
+ if direction
36
+ "jump #{direction}"
37
+ else
38
+ @command
39
+ end
40
+ end
41
+ end
42
+ end
@@ -0,0 +1,21 @@
1
+
2
+ module TFClient::DotDir
3
+
4
+ def self.directory
5
+ home = TFClient::Environment.user_home_directory
6
+ dir = File.join(home, ".textflight", "client")
7
+ if !File.exist?(dir)
8
+ FileUtils.mkdir_p(dir)
9
+ end
10
+ dir
11
+ end
12
+
13
+ def self.local_database_file(dev:)
14
+ if dev
15
+ File.expand_path(File.join(self.directory,"development.db"))
16
+ else
17
+ File.expand_path(File.join(self.directory,"production.db"))
18
+ end
19
+ end
20
+ end
21
+
@@ -0,0 +1,55 @@
1
+
2
+ module TFClient
3
+ class Environment
4
+
5
+ # Returns the user home directory
6
+ def self.user_home_directory
7
+ require 'etc'
8
+ # If uid is omitted, the value from Passwd[:uid] is returned instead.
9
+ #noinspection RubyArgCount
10
+ Etc.getpwuid.dir
11
+ end
12
+
13
+ # Returns true if Windows environment
14
+ def self.windows_env?
15
+ if @@windows_env.nil?
16
+ @@windows_env = Environment.host_os_is_win?
17
+ end
18
+
19
+ @@windows_env
20
+ end
21
+
22
+ # Returns true if debugging is enabled.
23
+ def self.debug?
24
+ ENV['DEBUG'] == '1'
25
+ end
26
+
27
+ private
28
+
29
+ # !@visibility private
30
+ def self.ci_var_defined?
31
+ value = ENV["CI"]
32
+ !!value && value != ''
33
+ end
34
+
35
+
36
+ # @visibility private
37
+ WIN_PATTERNS = [
38
+ /bccwin/i,
39
+ /cygwin/i,
40
+ /djgpp/i,
41
+ /mingw/i,
42
+ /mswin/i,
43
+ /wince/i,
44
+ ]
45
+
46
+ # @!visibility private
47
+ @@windows_env = nil
48
+
49
+ # @!visibility private
50
+ def self.host_os_is_win?
51
+ ruby_platform = RbConfig::CONFIG["host_os"]
52
+ !!WIN_PATTERNS.find { |r| ruby_platform =~ r }
53
+ end
54
+ end
55
+ end
@@ -0,0 +1,58 @@
1
+
2
+ module TFClient
3
+
4
+ # cyan
5
+ def self.log_unix_cmd(msg)
6
+ if ENV["DEBUG"] == "1"
7
+ puts Color.cyan("SHELL: #{msg}") if msg
8
+ end
9
+ end
10
+
11
+ # blue
12
+ def self.warn(msg)
13
+ puts Color.blue(" WARN: #{msg}") if msg
14
+ end
15
+
16
+ # magenta
17
+ def self.debug(msg)
18
+ if ENV["DEBUG"] == "1"
19
+ puts Color.magenta("DEBUG: #{msg}") if msg
20
+ end
21
+ end
22
+
23
+ # green
24
+ def self.info(msg)
25
+ puts Color.green(" INFO: #{msg}") if msg
26
+ end
27
+
28
+ # red
29
+ def self.error(msg)
30
+ puts Color.red("ERROR: #{msg}") if msg
31
+ end
32
+
33
+ module Color
34
+ def self.colorize(string, color)
35
+ "\033[#{color}m#{string}\033[0m"
36
+ end
37
+
38
+ def self.red(string)
39
+ colorize(string, 31)
40
+ end
41
+
42
+ def self.blue(string)
43
+ colorize(string, 34)
44
+ end
45
+
46
+ def self.magenta(string)
47
+ colorize(string, 35)
48
+ end
49
+
50
+ def self.cyan(string)
51
+ colorize(string, 36)
52
+ end
53
+
54
+ def self.green(string)
55
+ colorize(string, 32)
56
+ end
57
+ end
58
+ end
@@ -0,0 +1,69 @@
1
+
2
+ module TFClient
3
+ module Models
4
+ module Local
5
+ require "sequel"
6
+ end
7
+ end
8
+ end
9
+
10
+ module TFClient::Models::Local
11
+
12
+ require_relative "../../textflight-client/logging.rb"
13
+ require "json"
14
+
15
+ class Database
16
+
17
+ attr_reader :connection, :path
18
+
19
+ def initialize(path:)
20
+ @connection = Sequel.connect("sqlite://#{path}")
21
+
22
+ if !@connection.table_exists?(:systems)
23
+ @connection.create_table(:systems) do
24
+ primary_key :id
25
+ column :x, Integer
26
+ column :y, Integer
27
+ column :claimed_by, String
28
+ column :brightness, Integer
29
+ column :asteroid_ore, String
30
+ column :asteroid_density, Integer
31
+ column :links, String
32
+ column :planets, String
33
+ end
34
+ end
35
+ end
36
+
37
+ def system_for_id(id:)
38
+ table = @connection[:systems]
39
+ table.where(id: id)
40
+ end
41
+
42
+ def create_system(id:, nav:)
43
+ TFClient.info("creating a new system with id: #{id}")
44
+
45
+ links = nav.links.items.map do |link|
46
+ [link[:index], link[:direction], link[:drag], link[:faction]]
47
+ end
48
+
49
+ planets = nav.planets.items.map do |planet|
50
+ [planet[:index], planet[:type], planet[:name], planet[:faction]]
51
+ end
52
+
53
+ table = @connection[:systems]
54
+ table.insert(
55
+ {
56
+ id: id,
57
+ x: nav.coordinates.x,
58
+ y: nav.coordinates.y,
59
+ claimed_by: nav.claimed_by ? nav.claimed_by.faction : "",
60
+ brightness: nav.brightness.value,
61
+ asteroid_ore: nav.asteroids.ore,
62
+ asteroid_density: nav.asteroids.density,
63
+ links: JSON.generate(links),
64
+ planets: JSON.generate(planets)
65
+ }
66
+ )
67
+ end
68
+ end
69
+ end
@@ -0,0 +1,55 @@
1
+
2
+ module TFClient
3
+ module Models
4
+ require "cousin_roman"
5
+
6
+ TABLE_OPTIONS = { padding: [0,1,0,1], width: 80, resize: true }
7
+
8
+ class Response
9
+ attr_accessor :lines, :response
10
+
11
+ def initialize(lines:)
12
+ @lines = lines.dup
13
+ @response = []
14
+ end
15
+ end
16
+
17
+ class Model
18
+ attr_accessor :label, :translation, :values_hash
19
+
20
+ def initialize(line:)
21
+ tokens = ResponseParser.tokenize_line(line: line)
22
+ tokens_hash = ResponseParser.label_and_translation(tokens: tokens)
23
+ @label = tokens_hash[:label]
24
+ @translation = tokens_hash[:translation]
25
+ @values_hash = ResponseParser.hash_from_line_values(line: line)
26
+ end
27
+ end
28
+
29
+ class ModelWithItems < Model
30
+ require "tty-table"
31
+
32
+ attr_reader :items
33
+
34
+ def count
35
+ @items.count
36
+ end
37
+
38
+ def to_s
39
+ "#{@translation}: #{@items.map { |item| item[:string]}}"
40
+ end
41
+
42
+ def items_to_s
43
+ @items.map { |item| "\t#{item[:string]}" }
44
+ end
45
+
46
+ def response_str
47
+ "#{@translation}:\n#{items_to_s.join("\n")}"
48
+ end
49
+
50
+ def lines_offset
51
+ @items.length + 1
52
+ end
53
+ end
54
+ end
55
+ end
@@ -0,0 +1,293 @@
1
+
2
+ module TFClient
3
+ module Models
4
+ class Nav < Response
5
+
6
+ GAME_LINK_TO_COMPASS_MAP = {
7
+ "0" => "sw",
8
+ "1" => "s",
9
+ "2" => "se",
10
+ "3" => "w",
11
+ "4" => "e",
12
+ "5" => "nw",
13
+ "6" => "n",
14
+ "7" => "ne"
15
+ }
16
+
17
+ COMPASS_TO_GAME_LINK_MAP = {
18
+ "n" => 6,
19
+ "ne" => 7,
20
+ "e" => 4,
21
+ "se" => 2,
22
+ "s" => 1,
23
+ "sw" => 0,
24
+ "w" => 3,
25
+ "nw" => 5
26
+ }
27
+
28
+ COMPASS_ORDER = {
29
+ "n" => 0,
30
+ "ne" => 1,
31
+ "e" => 2,
32
+ "se" => 3,
33
+ "s" => 4,
34
+ "sw" => 5,
35
+ "w" => 6,
36
+ "nw" => 7
37
+ }
38
+
39
+ LINE_IDENTIFIER = [
40
+ "Coordinates",
41
+ "Claimed by",
42
+ "Brightness",
43
+ "Asteroids",
44
+ "Links",
45
+ "Planets",
46
+ "Structures"
47
+ ].freeze
48
+
49
+ attr_reader :coordinates, :claimed_by, :brightness, :asteroids
50
+ attr_reader :links, :planets, :structures
51
+
52
+ def initialize(lines:)
53
+ super(lines: lines)
54
+
55
+ LINE_IDENTIFIER.each_with_index do |label, label_index|
56
+ var_name = ResponseParser.snake_case_sym_from_string(string: label)
57
+ class_name = ResponseParser.camel_case_from_string(string: label)
58
+
59
+ clazz = ResponseParser.model_class_from_string(string: class_name)
60
+ if clazz.nil?
61
+ raise "could not find class name: #{class_name}"
62
+ end
63
+
64
+ line, _ = ResponseParser.line_and_index_for_beginning_with(lines: @lines,
65
+ string: label)
66
+
67
+ next if line.nil?
68
+
69
+ if label_index < 4
70
+ var = clazz.new(line: line)
71
+ else
72
+ var = clazz.new(lines: @lines)
73
+ end
74
+
75
+ instance_variable_set("@#{var_name}", var)
76
+ end
77
+ end
78
+
79
+ def response
80
+ puts @planets
81
+
82
+ puts @links
83
+ table = TTY::Table.new(rows: [[
84
+ "#{@brightness.to_s}",
85
+ "#{@asteroids.to_s}",
86
+ "#{@coordinates}"]
87
+ ])
88
+
89
+ puts table.render(:ascii, Models::TABLE_OPTIONS) do |renderer|
90
+ renderer.alignments= [:left, :center, :right]
91
+ end
92
+
93
+ puts @structures
94
+ end
95
+
96
+ def to_s
97
+ "#<Nav: #{@coordinates.to_s}>"
98
+ end
99
+ end
100
+
101
+ class Coordinates < Model
102
+ attr_reader :x, :y
103
+
104
+ def initialize(line:)
105
+ super(line: line)
106
+ @x = @values_hash[:x].to_i
107
+ @y = @values_hash[:y].to_i
108
+ end
109
+
110
+ def to_s
111
+ %Q[#{@translation}: (#{@x},#{@y})]
112
+ end
113
+ end
114
+
115
+ class ClaimedBy < Model
116
+ attr_reader :faction
117
+
118
+ def initialize(line:)
119
+ super(line: line)
120
+ @faction = @values_hash[:faction]
121
+ end
122
+
123
+ def to_s
124
+ %Q[#{@translation} '#{@faction}']
125
+ end
126
+ end
127
+
128
+ class Brightness < Model
129
+ attr_reader :value, :percent
130
+
131
+ def initialize(line:)
132
+ super(line: line)
133
+ @value = @values_hash[:brightness].to_i
134
+ @percent = ((@value/255.0) * 100).round
135
+ end
136
+
137
+ def to_s
138
+ %Q[#{@translation}: #{@value} => #{@percent}%]
139
+ end
140
+ end
141
+
142
+ class Asteroids < Model
143
+ attr_reader :ore, :density, :percent
144
+
145
+ def initialize(line:)
146
+ super(line: line)
147
+ @ore = @values_hash[:asteroid_type]
148
+ @density = @values_hash[:asteroid_density].to_i
149
+ @percent = ((@density/7.0) * 100).round
150
+ end
151
+
152
+ def to_s
153
+ %Q[#{@translation}: #{@ore} (#{@density}) => #{@percent}%]
154
+ end
155
+ end
156
+
157
+ class Links < ModelWithItems
158
+ def initialize(lines:)
159
+ line, index = ResponseParser.line_and_index_for_beginning_with(lines: lines,
160
+ string: "Links")
161
+ super(line: line)
162
+ items = ResponseParser.collect_list_items(lines: lines, start_index: index + 1)
163
+ @items = items.map do |item|
164
+ line = item.strip
165
+ hash = ResponseParser.hash_from_line_values(line: line)
166
+
167
+ index = hash[:index].to_i
168
+ faction = hash[:faction]
169
+ drag = hash[:link_drag].to_i
170
+
171
+ # direction is WIP
172
+ direction = Nav::GAME_LINK_TO_COMPASS_MAP[index.to_s]
173
+ {
174
+ index: index, drag: drag, direction: direction, faction: faction
175
+ }
176
+ end.sort do |a, b|
177
+ Nav::COMPASS_ORDER[a[:direction]] <=> Nav::COMPASS_ORDER[b[:direction]]
178
+ end
179
+ end
180
+
181
+ def to_s
182
+ table = TTY::Table.new(header: [
183
+ {value: "#{@translation}", alignment: :right},
184
+ {value: "drag", alignment: :center},
185
+ {value: "faction", alignment: :center},
186
+ {value: "original", alignment: :center},
187
+ {value: "direction", alignment: :center}
188
+ ])
189
+
190
+ @items.each do |item|
191
+ table << [
192
+ "#{item[:direction]}",
193
+ item[:drag],
194
+ item[:faction],
195
+ "[#{item[:index]}]",
196
+ "#{item[:direction]}"
197
+ ]
198
+ end
199
+
200
+ table.render(:ascii, Models::TABLE_OPTIONS) do |renderer|
201
+ renderer.alignments= [:right, :right, :center, :center, :center, :center]
202
+ end
203
+ end
204
+ end
205
+
206
+ class Planets < ModelWithItems
207
+
208
+ def initialize(lines:)
209
+ line, index = ResponseParser.line_and_index_for_beginning_with(lines: lines,
210
+ string: "Planets")
211
+ super(line: line)
212
+
213
+ items = ResponseParser.collect_list_items(lines: lines, start_index: index + 1)
214
+ @items = items.map do |item|
215
+ line = item.strip
216
+
217
+ hash = ResponseParser.hash_from_line_values(line: line)
218
+
219
+ index = hash[:index].to_i
220
+ type = hash[:planet_type]
221
+ name = hash[:name]
222
+ faction = hash[:faction]
223
+
224
+ { index: index, type: type, name: name, faction: faction }
225
+ end
226
+ end
227
+
228
+ def to_s
229
+ return "" if @items.empty?
230
+ table = TTY::Table.new(header: [
231
+ {value: "#{@translation}", alignment: :right},
232
+ {value: "type", alignment: :center},
233
+ {value: "name", alignment: :center},
234
+ {value: "faction", alignment: :center},
235
+ {value: "index", alignment: :center}
236
+ ])
237
+
238
+ @items.each do |item|
239
+ table << [
240
+ "[#{item[:index]}]",
241
+ item[:type],
242
+ item[:name],
243
+ item[:faction],
244
+ "[#{item[:index]}]"
245
+ ]
246
+ end
247
+
248
+ table.render(:ascii, Models::TABLE_OPTIONS) do |renderer|
249
+ renderer.alignments= [:right, :right, :center, :center, :center]
250
+ end
251
+ end
252
+ end
253
+
254
+ class Structures < ModelWithItems
255
+
256
+ def initialize(lines:)
257
+ line, index = ResponseParser.line_and_index_for_beginning_with(lines: lines,
258
+ string: "Structures")
259
+ super(line: line)
260
+
261
+ items = ResponseParser.collect_list_items(lines: lines, start_index: index + 1)
262
+ @items = items.map do |item|
263
+ line = item.strip
264
+
265
+ hash = ResponseParser.hash_from_line_values(line: line)
266
+
267
+ id = hash[:id].to_i
268
+ name = hash[:name]
269
+ # TODO: distinguish shipyards from bases
270
+ type = hash[:sclass] || "base"
271
+ { id: id, name: name, type: type }
272
+ end
273
+ end
274
+
275
+ def to_s
276
+ table = TTY::Table.new(header: [
277
+ {value: "#{@translation}", alignment: :right},
278
+ {value: "name", alignment: :center},
279
+ {value: "ship class", alignment: :center},
280
+ {value: "id", alignment: :center }
281
+ ])
282
+
283
+ @items.each do |item|
284
+ table << ["[#{item[:id]}]", item[:name], item[:type], "[#{item[:id]}]"]
285
+ end
286
+
287
+ table.render(:ascii, Models::TABLE_OPTIONS) do |renderer|
288
+ renderer.alignments= [:right, :right, :center, :center]
289
+ end
290
+ end
291
+ end
292
+ end
293
+ end