diff --git a/docs/v0.0.1/classes/Context.md b/docs/v0.0.1/classes/Context.md index a09b86f..5da680f 100644 --- a/docs/v0.0.1/classes/Context.md +++ b/docs/v0.0.1/classes/Context.md @@ -5,15 +5,15 @@ When you hook into methods using a ``on_*`` method you can access its context. This gives you the ability to read and modify the data before the default behavior processes it. Or skip the default behavior and implement your own logic. -### #cancle +### #cancel -Call the ``cancle()`` on the context object to not run any default code for that event. +Call the ``cancel()`` on the context object to not run any default code for that event. ```ruby client.on_map_change do |context| # do nothing when a map change packet comes in # skips the send ready packet code - context.cancle + context.cancel end ``` @@ -23,12 +23,12 @@ Access the network client to send packets. **Example:** -Reimplement your on on_connected logic and cancle the default one +Reimplement your on on_connected logic and cancel the default one ```ruby client.on_connected do |ctx| ctx.client.send_msg_start_info - ctx.cancle + ctx.cancel end ``` diff --git a/docs/v0.0.1/classes/TeeworldsClient.md b/docs/v0.0.1/classes/TeeworldsClient.md index 3477550..d3c407f 100644 --- a/docs/v0.0.1/classes/TeeworldsClient.md +++ b/docs/v0.0.1/classes/TeeworldsClient.md @@ -1,5 +1,107 @@ # TeeworldsClient +### #on_maplist_entry_rem(&block) + +**Parameter: block [Block |[context](../classes/Context.md)|]** + +TODO: generated documentation + +**Example:** +```ruby +client = TeeworldsClient.new + +client.on_maplist_entry_rem do |context| + # TODO: generated documentation +end + +client.connect('localhost', 8303, detach: true) +``` + +### #on_maplist_entry_add(&block) + +**Parameter: block [Block |[context](../classes/Context.md)|]** + +TODO: generated documentation + +**Example:** +```ruby +client = TeeworldsClient.new + +client.on_maplist_entry_add do |context| + # TODO: generated documentation +end + +client.connect('localhost', 8303, detach: true) +``` + +### #on_rcon_cmd_rem(&block) + +**Parameter: block [Block |[context](../classes/Context.md)|]** + +TODO: generated documentation + +**Example:** +```ruby +client = TeeworldsClient.new + +client.on_rcon_cmd_rem do |context| + # TODO: generated documentation +end + +client.connect('localhost', 8303, detach: true) +``` + +### #on_rcon_cmd_add(&block) + +**Parameter: block [Block |[context](../classes/Context.md)|]** + +TODO: generated documentation + +**Example:** +```ruby +client = TeeworldsClient.new + +client.on_rcon_cmd_add do |context| + # TODO: generated documentation +end + +client.connect('localhost', 8303, detach: true) +``` + +### #on_auth_off(&block) + +**Parameter: block [Block |[context](../classes/Context.md)|]** + +TODO: generated documentation + +**Example:** +```ruby +client = TeeworldsClient.new + +client.on_auth_off do |context| + # TODO: generated documentation +end + +client.connect('localhost', 8303, detach: true) +``` + +### #on_auth_on(&block) + +**Parameter: block [Block |[context](../classes/Context.md)|]** + +TODO: generated documentation + +**Example:** +```ruby +client = TeeworldsClient.new + +client.on_auth_on do |context| + # TODO: generated documentation +end + +client.connect('localhost', 8303, detach: true) +``` + ### #on_input_timing(&block) **Parameter: block [Block |[context](../classes/Context.md)|]** @@ -138,7 +240,7 @@ client.on_map_change do |context| # skip default behavior # in this case do not send the ready packet - context.cancle + context.cancel end client.connect('localhost', 8303, detach: true) diff --git a/lib/chunk.rb b/lib/chunk.rb index 49565c1..643c430 100644 --- a/lib/chunk.rb +++ b/lib/chunk.rb @@ -5,7 +5,7 @@ require_relative 'network' require_relative 'bytes' class NetChunk - attr_reader :next, :data, :msg, :sys, :flags, :header_raw + attr_reader :next, :data, :msg, :sys, :flags, :header_raw, :full_raw @@sent_vital_chunks = 0 @@ -27,6 +27,7 @@ class NetChunk @sys = @msg & 1 == 1 @msg >>= 1 @next = data[chunk_end..] if data.size > chunk_end + @full_raw = data[..chunk_end] end def self.reset diff --git a/lib/context.rb b/lib/context.rb index 3289cf5..5640cbe 100644 --- a/lib/context.rb +++ b/lib/context.rb @@ -6,7 +6,7 @@ class Context def initialize(todo_rename_this, keys = {}) @todo_rename_this = todo_rename_this # the obj holding the parsed chunk - @cancle = false + @cancel = false @old_data = keys @data = keys end @@ -19,11 +19,11 @@ class Context end end - def cancled? - @cancle + def canceld? + @cancel end - def cancle - @cancle = true + def cancel + @cancel = true end end diff --git a/lib/game_client.rb b/lib/game_client.rb index ba4b44f..12faa03 100644 --- a/lib/game_client.rb +++ b/lib/game_client.rb @@ -4,6 +4,10 @@ require_relative 'models/player' require_relative 'models/chat_message' require_relative 'messages/input_timing' require_relative 'messages/sv_client_drop' +require_relative 'messages/rcon_cmd_add' +require_relative 'messages/rcon_cmd_rem' +require_relative 'messages/maplist_entry_add' +require_relative 'messages/maplist_entry_rem' require_relative 'packer' require_relative 'context' @@ -27,11 +31,57 @@ class GameClient @client.hooks[hook_sym].each do |hook| hook.call(context, optional) context.verify - return nil if context.cancled? + return nil if context.canceld? end context end + def on_auth_on + return if call_hook(:auth_on, Context.new(nil)).nil? + + @client.rcon_authed = true + puts 'rcon logged in' + end + + def on_auth_off + return if call_hook(:auth_off, Context.new(nil)).nil? + + @client.rcon_authed = false + puts 'rcon logged out' + end + + def on_rcon_cmd_add(chunk) + todo_rename_this = RconCmdAdd.new(chunk.data[1..]) + context = Context.new(todo_rename_this) + return if call_hook(:rcon_cmd_add, context).nil? + + p context.todo_rename_this + end + + def on_rcon_cmd_rem(chunk) + todo_rename_this = RconCmdRem.new(chunk.data[1..]) + context = Context.new(todo_rename_this) + return if call_hook(:rcon_cmd_rem, context).nil? + + p context.todo_rename_this + end + + def on_maplist_entry_add(chunk) + todo_rename_this = MaplistEntryAdd.new(chunk.data[1..]) + context = Context.new(todo_rename_this) + return if call_hook(:maplist_entry_add, context).nil? + + p context.todo_rename_this + end + + def on_maplist_entry_rem(chunk) + todo_rename_this = MaplistEntryRem.new(chunk.data[1..]) + context = Context.new(todo_rename_this) + return if call_hook(:maplist_entry_rem, context).nil? + + p context.todo_rename_this + end + def on_client_info(chunk) # puts "Got playerinfo flags: #{chunk.flags}" u = Unpacker.new(chunk.data[1..]) diff --git a/lib/messages/maplist_entry_add.rb b/lib/messages/maplist_entry_add.rb new file mode 100644 index 0000000..83c207a --- /dev/null +++ b/lib/messages/maplist_entry_add.rb @@ -0,0 +1,44 @@ +# frozen_string_literal: true + +require_relative '../packer' + +## +# MaplistEntryAdd +# +# Server -> Client +class MaplistEntryAdd + attr_accessor :name + + def initialize(hash_or_raw) + if hash_or_raw.instance_of?(Hash) + init_hash(hash_or_raw) + else + init_raw(hash_or_raw) + end + end + + def init_raw(data) + u = Unpacker.new(data) + @name = u.get_string(SANITIZE_CC) + end + + def init_hash(attr) + @name = attr[:name] || 'TODO: fill default' + end + + def to_h + { + name: @name + } + end + + # basically to_network + # int array the Server sends to the Client + def to_a + Packer.pack_str(@name) + end + + def to_s + to_h + end +end diff --git a/lib/messages/maplist_entry_rem.rb b/lib/messages/maplist_entry_rem.rb new file mode 100644 index 0000000..bad9e3d --- /dev/null +++ b/lib/messages/maplist_entry_rem.rb @@ -0,0 +1,44 @@ +# frozen_string_literal: true + +require_relative '../packer' + +## +# MaplistEntryRem +# +# Server -> Client +class MaplistEntryRem + attr_accessor :name + + def initialize(hash_or_raw) + if hash_or_raw.instance_of?(Hash) + init_hash(hash_or_raw) + else + init_raw(hash_or_raw) + end + end + + def init_raw(data) + u = Unpacker.new(data) + @name = u.get_string(SANITIZE_CC) + end + + def init_hash(attr) + @name = attr[:name] || 'TODO: fill default' + end + + def to_h + { + name: @name + } + end + + # basically to_network + # int array the Server sends to the Client + def to_a + Packer.pack_str(@name) + end + + def to_s + to_h + end +end diff --git a/lib/messages/rcon_cmd_add.rb b/lib/messages/rcon_cmd_add.rb new file mode 100644 index 0000000..3605b20 --- /dev/null +++ b/lib/messages/rcon_cmd_add.rb @@ -0,0 +1,52 @@ +# frozen_string_literal: true + +require_relative '../packer' + +## +# RconCmdAdd +# +# Server -> Client +class RconCmdAdd + attr_accessor :name, :help, :params + + def initialize(hash_or_raw) + if hash_or_raw.instance_of?(Hash) + init_hash(hash_or_raw) + else + init_raw(hash_or_raw) + end + end + + def init_raw(data) + u = Unpacker.new(data) + @name = u.get_string(SANITIZE_CC) + @help = u.get_string(SANITIZE_CC) + @params = u.get_string(SANITIZE_CC) + end + + def init_hash(attr) + @name = attr[:name] || '' + @help = attr[:help] || '' + @params = attr[:params] || '' + end + + def to_h + { + name: @name, + help: @help, + params: @params + } + end + + # basically to_network + # int array the Server sends to the Client + def to_a + Packer.pack_str(@name) + + Packer.pack_str(@help) + + Packer.pack_str(@params) + end + + def to_s + to_h + end +end diff --git a/lib/messages/rcon_cmd_rem.rb b/lib/messages/rcon_cmd_rem.rb new file mode 100644 index 0000000..212a10b --- /dev/null +++ b/lib/messages/rcon_cmd_rem.rb @@ -0,0 +1,44 @@ +# frozen_string_literal: true + +require_relative '../packer' + +## +# RconCmdRem +# +# Server -> Client +class RconCmdRem + attr_accessor :name + + def initialize(hash_or_raw) + if hash_or_raw.instance_of?(Hash) + init_hash(hash_or_raw) + else + init_raw(hash_or_raw) + end + end + + def init_raw(data) + u = Unpacker.new(data) + @name = u.get_string(SANITIZE_CC) + end + + def init_hash(attr) + @name = attr[:name] || '' + end + + def to_h + { + name: @name + } + end + + # basically to_network + # int array the Server sends to the Client + def to_a + Packer.pack_str(@name) + end + + def to_s + to_h + end +end diff --git a/lib/network.rb b/lib/network.rb index 273d34c..4563c58 100644 --- a/lib/network.rb +++ b/lib/network.rb @@ -22,6 +22,9 @@ NETMSG_RCON_LINE = 13 # line that should be printed to the remote console NETMSG_RCON_CMD_ADD = 14 NETMSG_RCON_CMD_REM = 15 +NETMSG_MAPLIST_ENTRY_ADD = 29 # TODO: 0.8: move up +NETMSG_MAPLIST_ENTRY_REM = 30 + # sent by client NETMSG_READY = 18 NETMSG_ENTERGAME = 19 diff --git a/lib/packer.rb b/lib/packer.rb index fbe5149..62d48fd 100644 --- a/lib/packer.rb +++ b/lib/packer.rb @@ -103,7 +103,6 @@ class Unpacker return nil if @data.nil? str = '' - p @data @data.each_with_index do |byte, index| if byte.zero? @data = index == @data.length - 1 ? nil : @data[(index + 1)..] diff --git a/lib/teeworlds_client.rb b/lib/teeworlds_client.rb index aadeb6b..639e736 100644 --- a/lib/teeworlds_client.rb +++ b/lib/teeworlds_client.rb @@ -16,6 +16,7 @@ require_relative 'game_client' class TeeworldsClient attr_reader :state, :hooks, :game_client + attr_accessor :rcon_authed def initialize(options = {}) @verbose = options[:verbose] || false @@ -31,7 +32,13 @@ class TeeworldsClient disconnect: [], rcon_line: [], snapshot: [], - input_timing: [] + input_timing: [], + auth_on: [], + auth_off: [], + rcon_cmd_add: [], + rcon_cmd_rem: [], + maplist_entry_add: [], + maplist_entry_rem: [] } @thread_running = false @signal_disconnect = false @@ -59,6 +66,35 @@ class TeeworldsClient color_feet: 0, color_eyes: 0 } + @rcon_authed = false + end + + def rcon_authed? + @rcon_authed + end + + def on_auth_on(&block) + @hooks[:auth_on].push(block) + end + + def on_auth_off(&block) + @hooks[:auth_off].push(block) + end + + def on_rcon_cmd_add(&block) + @hooks[:rcon_cmd_add].push(block) + end + + def on_rcon_cmd_rem(&block) + @hooks[:rcon_cmd_rem].push(block) + end + + def on_maplist_entry_add(&block) + @hooks[:maplist_entry_add].push(block) + end + + def on_maplist_entry_rem(&block) + @hooks[:maplist_entry_rem].push(block) end def on_chat(&block) @@ -353,7 +389,7 @@ class TeeworldsClient on_message(chunk) return end - puts "proccess chunk with msg: #{chunk.msg}" + puts "proccess chunk with msg: #{chunk.msg}" if @verbose case chunk.msg when NETMSG_MAP_CHANGE @game_client.on_map_change(chunk) @@ -369,8 +405,21 @@ class TeeworldsClient @game_client.on_snapshot(chunk) when NETMSG_INPUTTIMING @game_client.on_input_timing(chunk) + when NETMSG_RCON_AUTH_ON + @game_client.on_auth_on + when NETMSG_RCON_AUTH_OFF + @game_client.on_auth_off + when NETMSG_RCON_CMD_ADD + @game_client.on_rcon_cmd_add(chunk) + when NETMSG_RCON_CMD_REM + @game_client.on_rcon_cmd_rem(chunk) + when NETMSG_MAPLIST_ENTRY_ADD + @game_client.on_maplist_entry_add(chunk) + when NETMSG_MAPLIST_ENTRY_REM + @game_client.on_maplist_entry_rem(chunk) else puts "Unsupported system msg: #{chunk.msg}" + p str_hex(chunk.full_raw) exit(1) end end