procon_bypass_man 0.1.16 → 0.1.19

Sign up to get free protection for your applications and to get access to all the features.
Files changed (59) hide show
  1. checksums.yaml +4 -4
  2. data/.circleci/config.yml +86 -11
  3. data/.github/workflows/release.yml +2 -2
  4. data/.rubocop.yml +2 -0
  5. data/CHANGELOG.md +19 -1
  6. data/Gemfile +1 -0
  7. data/Gemfile.lock +7 -3
  8. data/README.md +39 -15
  9. data/Steepfile +31 -17
  10. data/docs/getting_started.md +60 -0
  11. data/docs/setting/left-analogstick-cap.md +60 -0
  12. data/docs/setting/splatoon2_macro_sokuwari_bubble.md +52 -0
  13. data/docs/setup_raspi.md +2 -0
  14. data/docs/setup_raspi.mitamae.rb +7 -8
  15. data/docs/setup_raspi_by_mitamae.md +37 -1
  16. data/docs/upgrade_pbm.md +56 -0
  17. data/lib/ext/module.rb +16 -0
  18. data/lib/procon_bypass_man/buttons_setting_configuration/layer.rb +33 -7
  19. data/lib/procon_bypass_man/buttons_setting_configuration/loader.rb +4 -1
  20. data/lib/procon_bypass_man/buttons_setting_configuration/validator.rb +36 -0
  21. data/lib/procon_bypass_man/buttons_setting_configuration.rb +34 -21
  22. data/lib/procon_bypass_man/bypass/usb_hid_logger.rb +6 -4
  23. data/lib/procon_bypass_man/commands/bypass_command.rb +2 -2
  24. data/lib/procon_bypass_man/configuration.rb +5 -1
  25. data/lib/procon_bypass_man/domains/binary/base.rb +14 -0
  26. data/lib/procon_bypass_man/domains/binary/inbound_procon_binary.rb +0 -14
  27. data/lib/procon_bypass_man/domains/binary/processing_procon_binary.rb +2 -16
  28. data/lib/procon_bypass_man/plugin/splatoon2/macro/fast_return.rb +1 -1
  29. data/lib/procon_bypass_man/plugin/splatoon2/macro/jump_to_left_key.rb +1 -1
  30. data/lib/procon_bypass_man/plugin/splatoon2/macro/jump_to_right_key.rb +1 -1
  31. data/lib/procon_bypass_man/plugin/splatoon2/macro/jump_to_up_key.rb +1 -1
  32. data/lib/procon_bypass_man/plugin/splatoon2/macro/sokuwari_for_splash_bomb.rb +22 -0
  33. data/lib/procon_bypass_man/plugin/splatoon2/version.rb +1 -1
  34. data/lib/procon_bypass_man/plugins.rb +1 -0
  35. data/lib/procon_bypass_man/procon/button.rb +1 -1
  36. data/lib/procon_bypass_man/procon/button_collection.rb +8 -0
  37. data/lib/procon_bypass_man/procon/macro.rb +89 -0
  38. data/lib/procon_bypass_man/procon/macro_builder.rb +123 -0
  39. data/lib/procon_bypass_man/procon/macro_registry.rb +9 -27
  40. data/lib/procon_bypass_man/procon/mode_registry.rb +4 -4
  41. data/lib/procon_bypass_man/procon/press_button_aware.rb +6 -5
  42. data/lib/procon_bypass_man/procon/user_operation.rb +16 -2
  43. data/lib/procon_bypass_man/procon/value_objects/analog_stick.rb +1 -1
  44. data/lib/procon_bypass_man/procon.rb +6 -4
  45. data/lib/procon_bypass_man/remote_pbm_action/restore_pbm_setting.rb +3 -3
  46. data/lib/procon_bypass_man/support/compress_array.rb +5 -0
  47. data/lib/procon_bypass_man/support/http_client.rb +4 -0
  48. data/lib/procon_bypass_man/support/on_memory_cache.rb +3 -1
  49. data/lib/procon_bypass_man/support/server_pool.rb +4 -0
  50. data/lib/procon_bypass_man/support/yaml_writer.rb +9 -0
  51. data/lib/procon_bypass_man/version.rb +1 -1
  52. data/lib/procon_bypass_man/websocket/pbm_job_client.rb +13 -2
  53. data/lib/procon_bypass_man.rb +2 -0
  54. data/procon_bypass_man.gemspec +1 -1
  55. data/project_template/README.md +10 -5
  56. data/project_template/app.rb +3 -2
  57. data/sig/main.rbs +213 -42
  58. data/sig/on_memory_cache.rbs +16 -0
  59. metadata +15 -5
@@ -0,0 +1,89 @@
1
+ class ProconBypassMan::Procon::Macro
2
+ class NestedStep
3
+ def initialize(value)
4
+ @hash = value
5
+ unless @hash[:end_at]
6
+ @hash[:end_at] = (Time.now + @hash[:continue_for]).round(4)
7
+ end
8
+ end
9
+
10
+ def over_end_at?
11
+ (@hash[:end_at] < Time.now).tap do |result|
12
+ if result
13
+ ProconBypassMan.logger.debug { "[Macro] nested step is finished(#{@hash})" }
14
+ end
15
+ end
16
+ end
17
+
18
+ def next_step
19
+ incr_step_index!
20
+
21
+ debug_incr_called_count!
22
+ if step = current_step
23
+ return step
24
+ else
25
+ reset_step_index!
26
+ return current_step
27
+ end
28
+ end
29
+
30
+ private
31
+
32
+ def current_step
33
+ @hash[:steps][step_index]
34
+ end
35
+
36
+ def step_index
37
+ @hash[:step_index]
38
+ end
39
+
40
+ def incr_step_index!
41
+ if step_index
42
+ @hash[:step_index] += 1
43
+ else
44
+ @hash[:step_index] = 0
45
+ end
46
+ end
47
+
48
+ def reset_step_index!
49
+ @hash[:step_index] = 0
50
+ end
51
+
52
+ def debug_incr_called_count!
53
+ @hash[:debug_called_count] ||= 0
54
+ @hash[:debug_called_count] += 1
55
+ end
56
+ end
57
+
58
+ attr_accessor :name, :steps
59
+
60
+ def initialize(name: , steps: )
61
+ self.name = name
62
+ self.steps = steps
63
+ end
64
+
65
+ def next_step
66
+ step = steps.first
67
+ if step.is_a?(Symbol)
68
+ return steps.shift
69
+ end
70
+
71
+ if step.is_a?(Hash)
72
+ nested_step = NestedStep.new(step)
73
+ if nested_step.over_end_at?
74
+ steps.shift # NestedStepを破棄する
75
+ return next_step
76
+ else
77
+ return nested_step.next_step
78
+ end
79
+ end
80
+ end
81
+
82
+ def finished?
83
+ steps.empty?
84
+ end
85
+
86
+ def ongoing?
87
+ !finished?
88
+ end
89
+ end
@@ -0,0 +1,123 @@
1
+ class ProconBypassMan::Procon::MacroBuilder
2
+ class SubjectMerger
3
+ def self.merge(subjects)
4
+ if subjects.size == 1
5
+ return subjects.first.to_steps
6
+ end
7
+
8
+ base = subjects.first
9
+ remain = subjects[1..-1]
10
+ remain.map { |x| base.to_steps.zip(x.to_steps) }.first
11
+ end
12
+ end
13
+
14
+ class Subject
15
+ def initialize(value)
16
+ @button =
17
+ if match = value.match(/_(\w+)\z/)
18
+ match[1]
19
+ else
20
+ :unknown
21
+ end
22
+ @type =
23
+ if value.start_with?("toggle_")
24
+ :toggle
25
+ else
26
+ :pressing
27
+ end
28
+ end
29
+
30
+ def toggle?
31
+ @type == :toggle
32
+ end
33
+
34
+ def pressing?
35
+ not toggle?
36
+ end
37
+
38
+ def to_steps
39
+ case @type
40
+ when :toggle
41
+ [@button.to_sym, :none]
42
+ when :pressing
43
+ [@button.to_sym, @button.to_sym]
44
+ end
45
+ end
46
+ end
47
+
48
+ RESERVED_WORD_NONE = :none
49
+ RESERVED_WORDS = {
50
+ RESERVED_WORD_NONE => true,
51
+ }
52
+
53
+ def initialize(steps)
54
+ @steps = steps.map(&:to_s)
55
+ end
56
+
57
+ # @return [Arary<Symbol>]
58
+ def build
59
+ steps = @steps.map { |step|
60
+ if is_reserved?(step: step) || v1_format?(step: step)
61
+ step.to_sym
62
+ elsif value = build_if_v2_format?(step: step)
63
+ value
64
+ else
65
+ nil
66
+ end
67
+ }
68
+ steps.compact.flatten
69
+ end
70
+
71
+ private
72
+
73
+ def is_reserved?(step: )
74
+ RESERVED_WORDS[step.to_sym]
75
+ end
76
+
77
+ def v1_format?(step: )
78
+ if is_button(step)
79
+ step
80
+ end
81
+ end
82
+
83
+ def build_if_v2_format?(step: )
84
+ # 時間指定なし
85
+ if(match = step.match(%r!\Atoggle_(\w+)\z!)) && (button_candidate = match[1]) && is_button(button_candidate)
86
+ button = button_candidate
87
+ return [button.to_sym, :none]
88
+ end
89
+
90
+ # 時間指定あり
91
+ if %r!^(pressing_|toggle_)! =~ step && (subjects = step.scan(%r!pressing_[^_]+|toggle_[^_]+!)) && (match = step.match(%r!_for_([\d_]+)(sec)?\z!))
92
+ sec = match[1]
93
+ return [
94
+ { continue_for: to_num(sec),
95
+ steps: SubjectMerger.merge(subjects.map { |x| Subject.new(x) }),
96
+ }
97
+ ]
98
+ end
99
+
100
+ # no-op command
101
+ if(match = step.match(%r!wait_for_([\d_]+)(sec)?\z!))
102
+ sec = match[1]
103
+ return [
104
+ { continue_for: to_num(sec),
105
+ steps: [:none],
106
+ }
107
+ ]
108
+ end
109
+ end
110
+
111
+ # @return [Boolean]
112
+ def is_button(step)
113
+ !!ProconBypassMan::Procon::ButtonCollection::BUTTONS_MAP[step.to_sym]
114
+ end
115
+
116
+ def to_num(value)
117
+ if value.include?("_")
118
+ value.sub("_", ".").to_f
119
+ else
120
+ value.to_i
121
+ end
122
+ end
123
+ end
@@ -1,39 +1,21 @@
1
1
  class ProconBypassMan::Procon::MacroRegistry
2
- class Macro
3
- attr_accessor :name, :steps
4
-
5
- def initialize(name: , steps: )
6
- self.name = name
7
- self.steps = steps
8
- end
9
-
10
- def next_step
11
- steps.shift
12
- end
13
-
14
- def finished?
15
- steps.empty?
16
- end
17
-
18
- def ongoing?
19
- !finished?
20
- end
21
- end
22
-
23
2
  PRESETS = {
24
3
  null: [],
25
4
  }
26
5
 
27
- def self.install_plugin(klass)
28
- if plugins[klass.name]
29
- raise "すでに登録済みです"
6
+ def self.install_plugin(klass, steps: nil)
7
+ if plugins[klass.to_s.to_sym]
8
+ raise "#{klass} macro is already registered"
30
9
  end
31
- plugins[klass.name] = klass.steps
10
+
11
+ plugins[klass.to_s.to_sym] = ->{
12
+ ProconBypassMan::Procon::MacroBuilder.new(steps || klass.steps).build
13
+ }
32
14
  end
33
15
 
34
16
  def self.load(name)
35
- steps = PRESETS[name] || plugins[name] || raise("unknown macro")
36
- Macro.new(name: name, steps: steps.dup)
17
+ steps = PRESETS[name] || plugins[name].call || raise("unknown macro")
18
+ ProconBypassMan::Procon::Macro.new(name: name, steps: steps.dup)
37
19
  end
38
20
 
39
21
  def self.reset!
@@ -23,14 +23,14 @@ class ProconBypassMan::Procon::ModeRegistry
23
23
  }
24
24
 
25
25
  def self.install_plugin(klass)
26
- if plugins[klass.name.to_sym]
27
- raise "すでに登録済みです"
26
+ if plugins[klass.to_s.to_sym]
27
+ raise "#{klass} mode is already registered"
28
28
  end
29
- plugins[klass.name.to_sym] = klass.binaries
29
+ plugins[klass.to_s.to_sym] = ->{ klass.binaries }
30
30
  end
31
31
 
32
32
  def self.load(name)
33
- b = PRESETS[name] || plugins[name] || raise("unknown mode")
33
+ b = PRESETS[name] || plugins[name]&.call || raise("#{name} is unknown mode")
34
34
  Mode.new(name: name, binaries: b.dup)
35
35
  end
36
36
 
@@ -1,14 +1,15 @@
1
1
  class ProconBypassMan::PressButtonAware
2
+ BIT_ON = '1'.freeze
3
+
2
4
  def initialize(binary)
3
5
  @binary = binary
4
6
  end
5
7
 
8
+ # @param [Symbol]
9
+ # @return [Boolean]
6
10
  def pressing_button?(button)
7
11
  button_obj = ProconBypassMan::Procon::Button.new(button)
8
- @binary[
9
- button_obj.byte_position
10
- ].unpack("H*").first.to_i(16).to_s(2).reverse[
11
- button_obj.bit_position
12
- ] == '1'
12
+ byte = @binary[button_obj.byte_position].unpack("C").first.to_s(2).reverse
13
+ byte[button_obj.bit_position] == BIT_ON
13
14
  end
14
15
  end
@@ -33,9 +33,23 @@ class ProconBypassMan::Procon::UserOperation
33
33
  binary.write_as_press_button(button)
34
34
  end
35
35
 
36
- # @param [Symbol] button
36
+ # @param [Symbol, Array<Symbol>] button
37
37
  def press_button_only(button)
38
- binary.write_as_press_button_only(button)
38
+ if button.is_a?(Array)
39
+ binary.set_no_action!
40
+ button.uniq.each do |b|
41
+ unless ProconBypassMan::Procon::MacroBuilder::RESERVED_WORD_NONE == b
42
+ binary.write_as_press_button(b)
43
+ end
44
+ end
45
+ return
46
+ end
47
+
48
+ if ProconBypassMan::Procon::MacroBuilder::RESERVED_WORD_NONE == button
49
+ binary.set_no_action!
50
+ else
51
+ binary.write_as_press_button_only(button)
52
+ end
39
53
  end
40
54
 
41
55
  # @return [void]
@@ -5,7 +5,7 @@ class ProconBypassMan::Procon::AnalogStick
5
5
  def initialize(binary: )
6
6
  @neutral_position = ProconBypassMan::ButtonsSettingConfiguration.instance.neutral_position
7
7
  bytes = binary[ProconBypassMan::Procon::ButtonCollection::LEFT_ANALOG_STICK.fetch(:byte_position)]
8
- byte6, byte7, byte8 = bytes.each_char.map { |x| x.unpack("H*").first.to_i(16).to_s(2).rjust(8, "0") }
8
+ byte6, byte7, byte8 = bytes.each_char.map { |x| x.unpack("C").first.to_s(2).rjust(8, "0") }
9
9
 
10
10
  self.bin_x = "#{byte7[4..7]}#{byte6}"
11
11
  self.bin_y = "#{byte8}#{byte7[0..3]}"
@@ -1,7 +1,9 @@
1
1
  class ProconBypassMan::Procon
2
2
  require "procon_bypass_man/procon/consts"
3
3
  require "procon_bypass_man/procon/mode_registry"
4
+ require "procon_bypass_man/procon/macro"
4
5
  require "procon_bypass_man/procon/macro_registry"
6
+ require "procon_bypass_man/procon/macro_builder"
5
7
  require "procon_bypass_man/procon/layer_changer"
6
8
  require "procon_bypass_man/procon/button_collection"
7
9
  require "procon_bypass_man/procon/user_operation"
@@ -102,12 +104,12 @@ class ProconBypassMan::Procon
102
104
  user_operation.unpress_button(button)
103
105
  end
104
106
 
105
- current_layer.left_analog_stick_caps.each do |buttons, options|
106
- if buttons.nil? || user_operation.pressing_all_buttons?(buttons)
107
- options[:force_neutral]&.each do |force_neutral_button|
107
+ current_layer.left_analog_stick_caps.each do |config|
108
+ if config[:if_pressed].nil? || user_operation.pressing_all_buttons?(config[:if_pressed])
109
+ config[:force_neutral]&.each do |force_neutral_button|
108
110
  user_operation.unpress_button(force_neutral_button)
109
111
  end
110
- user_operation.apply_left_analog_stick_cap(cap: options[:cap])
112
+ user_operation.apply_left_analog_stick_cap(cap: config[:cap])
111
113
  end
112
114
  end
113
115
 
@@ -6,9 +6,9 @@ module ProconBypassMan
6
6
  require "pbmenv"
7
7
  ProconBypassMan.logger.info "execute RestorePbmSettingAction!"
8
8
  setting = args.dig("setting") or raise(ProconBypassMan::RemotePbmAction::NeedPbmVersionError, "settingが必要です, #{args.inspect}")
9
- File.write(
10
- ProconBypassMan::ButtonsSettingConfiguration.instance.setting_path,
11
- setting.to_yaml,
9
+ ProconBypassMan::YamlWriter.execute(
10
+ path: ProconBypassMan::ButtonsSettingConfiguration.instance.setting_path,
11
+ content: setting,
12
12
  )
13
13
  ProconBypassMan.hot_reload!
14
14
  end
@@ -54,3 +54,8 @@ module ProconBypassMan
54
54
  end
55
55
  end
56
56
  end
57
+
58
+
59
+ if $0 == __FILE__
60
+ ProconBypassMan::CompressArray.new([''])
61
+ end
@@ -100,3 +100,7 @@ module ProconBypassMan
100
100
  end
101
101
  end
102
102
  end
103
+
104
+ if $0 == __FILE__
105
+ ProconBypassMan::HttpClient.new(path: '/', server_pool: nil, retry_on_connection_error: false).get(response_body: '')
106
+ end
@@ -1,6 +1,8 @@
1
1
  class ProconBypassMan::OnMemoryCache
2
2
  class CacheValue
3
- attr_accessor :expired_at, :value
3
+ # @param [Time]
4
+ attr_accessor :expired_at
5
+ attr_accessor :value
4
6
 
5
7
  def initialize(expired_at: , value: )
6
8
  self.expired_at = expired_at
@@ -40,3 +40,7 @@ module ProconBypassMan
40
40
  end
41
41
  end
42
42
  end
43
+
44
+ if $0 == __FILE__
45
+ ProconBypassMan::ServerPool.new(servers: ['http://example.com'])
46
+ end
@@ -0,0 +1,9 @@
1
+ class ProconBypassMan::YamlWriter
2
+ # @return [void]
3
+ def self.write(path: , content: )
4
+ File.write(
5
+ path,
6
+ content.gsub("\r\n", "\n").to_yaml,
7
+ )
8
+ end
9
+ end
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module ProconBypassMan
4
- VERSION = "0.1.16"
4
+ VERSION = "0.1.19"
5
5
  end
@@ -32,7 +32,9 @@ module ProconBypassMan
32
32
  }
33
33
 
34
34
  client.received do |data|
35
- validate_and_run(data: data)
35
+ ProconBypassMan.logger.info(data)
36
+
37
+ dispatch(data: data, client: client)
36
38
  rescue => e
37
39
  ProconBypassMan::SendErrorCommand.execute(error: e)
38
40
  end
@@ -51,11 +53,20 @@ module ProconBypassMan
51
53
  end
52
54
  end
53
55
 
56
+ # @param [Hash] data
57
+ def self.dispatch(data: , client: )
58
+ pbm_job_hash = data.dig("message")
59
+ if pbm_job_hash['action'] == "ping"
60
+ client.perform('pong', { device_id: ProconBypassMan.device_id, message: 'hello from pbm' })
61
+ else
62
+ validate_and_run(data: data)
63
+ end
64
+ end
65
+
54
66
  # @raise [ProconBypassMan::RemotePbmActionObject::ValidationError]
55
67
  # @param [Hash] data
56
68
  # @return [Void]
57
69
  def self.validate_and_run(data: )
58
- ProconBypassMan.logger.debug { data }
59
70
  pbm_job_hash = data.dig("message")
60
71
  begin
61
72
  pbm_job_object = ProconBypassMan::RemotePbmActionObject.new(action: pbm_job_hash["action"],
@@ -7,11 +7,13 @@ require "securerandom"
7
7
  require 'em/pure_ruby'
8
8
  require "action_cable_client"
9
9
  require "ext/em_pure_ruby"
10
+ require "ext/module"
10
11
 
11
12
  require_relative "procon_bypass_man/version"
12
13
  require_relative "procon_bypass_man/remote_pbm_action"
13
14
  require_relative "procon_bypass_man/support/signal_handler"
14
15
  require_relative "procon_bypass_man/support/callbacks"
16
+ require_relative "procon_bypass_man/support/yaml_writer"
15
17
  require_relative "procon_bypass_man/support/safe_timeout"
16
18
  require_relative "procon_bypass_man/support/compress_array"
17
19
  require_relative "procon_bypass_man/support/uptime"
@@ -8,7 +8,7 @@ Gem::Specification.new do |spec|
8
8
  spec.authors = ["jiikko"]
9
9
  spec.email = ["n905i.1214@gmail.com"]
10
10
 
11
- spec.summary = "An extension for Nintendo Switch Pro Controller"
11
+ spec.summary = "A programmable converter for Nintendo Switch Pro Controller"
12
12
  spec.description = spec.summary
13
13
  spec.homepage = "https://github.com/splaplapla/procon_bypass_man"
14
14
  spec.license = "MIT"
@@ -1,11 +1,16 @@
1
1
  # Project Template
2
- これらは https://github.com/splaplapla/pbmenv がinstallするときに配備するファイルです
2
+ * これらは https://github.com/splaplapla/pbmenv がinstallするときに配備するファイルです
3
+ * pbm_webはオプショナルです
3
4
 
4
5
  ## systemdを使ってサービスに登録する方法
5
- * sudo ln -s /usr/share/pbm/current/systemd_units/pbm.service /etc/systemd/system/pbm.service
6
- * sudo ln -s /usr/share/pbm/current/systemd_units/pbm_web.service /etc/systemd/system/pbm_web.service
7
- * sudo systemctl enable pbm.service
8
- * sudo systemctl enable pbm_web.service
6
+ systemctl enableした後は、次回のOS起動時にserviceも自動起動します
7
+
8
+ * pbm
9
+ * sudo systemctl link /usr/share/pbm/current/systemd_units/pbm.service
10
+ * sudo systemctl enable pbm.service
11
+ * pbm_web
12
+ * sudo systemctl link /usr/share/pbm/current/systemd_units/pbm_web.service
13
+ * sudo systemctl enable pbm_web.service
9
14
 
10
15
  ## systemdを使ってサービスから解除する方法
11
16
  * sudo systemctl disable pbm.service
@@ -5,14 +5,15 @@ require 'bundler/inline'
5
5
  gemfile do
6
6
  source 'https://rubygems.org'
7
7
  git_source(:github) {|repo_name| "https://github.com/#{repo_name}" }
8
- gem 'procon_bypass_man', '0.1.16'
8
+ gem 'procon_bypass_man', '0.1.19'
9
9
  end
10
10
 
11
11
  ProconBypassMan.configure do |config|
12
12
  config.root = File.expand_path(__dir__)
13
13
  config.logger = Logger.new("#{ProconBypassMan.root}/app.log", 5, 1024 * 1024 * 10)
14
14
  config.logger.level = :debug
15
- # config.api_servers = ['https://...']
15
+ # webからProconBypassManを操作できるwebサービス
16
+ # config.api_servers = ['https://pbm-cloud.herokuapp.com']
16
17
  config.enable_critical_error_logging = true
17
18
  end
18
19