Make client callbacks arrays

Allows to setup multiple on_* hook blocks as a lib user

closed #10
This commit is contained in:
ChillerDragon 2022-11-14 10:25:28 +01:00
parent 2bec19ff21
commit fe3e581f86
11 changed files with 141 additions and 72 deletions

View file

@ -15,7 +15,7 @@ require_relative 'lib/teeworlds_client'
client = TeeworldsClient.new(verbose: false) client = TeeworldsClient.new(verbose: false)
client.on_chat do |msg| client.on_chat do |_, msg|
# note use `next` instead of `return` in the block # note use `next` instead of `return` in the block
next unless msg.message[0] == '!' next unless msg.message[0] == '!'

View file

@ -28,7 +28,7 @@ args[:port] = args[:port] || 8303
client = TeeworldsClient.new(verbose: args[:verbose]) client = TeeworldsClient.new(verbose: args[:verbose])
client.on_chat do |msg| client.on_chat do |_, msg|
puts "[chat] #{msg}" puts "[chat] #{msg}"
end end

View file

@ -87,17 +87,19 @@ client.connect('localhost', 8303, detach: true)
### <a name="on_chat"></a> #on_chat(&block) ### <a name="on_chat"></a> #on_chat(&block)
**Parameter: block [Block |[ChatMessage](../classes/ChatMessage.md)|]** **Parameter: block [Block |[context](../classes/Context.md), [ChatMessage](../classes/ChatMessage.md)|]**
Takes a block that will be called when the client receives a chat message. Takes a block that will be called when the client receives a chat message.
The block takes one parameter of type [ChatMessage](../classes/ChatMessage.md). The block takes two parameters:
[context](../classes/Context.md) - pretty much useless as of right now
[ChatMessage](../classes/ChatMessage.md) - holds all the information of the chat message
**Example:** **Example:**
```ruby ```ruby
client = TeeworldsClient.new client = TeeworldsClient.new
client.on_chat do |msg| client.on_chat do |context, msg|
puts "[chat] #{msg}" puts "[chat] #{msg}"
end end

View file

@ -18,7 +18,7 @@ client = TeeworldsClient.new(verbose: false)
# msg.author.team # msg.author.team
# msg.author.name # msg.author.name
# msg.author.clan # msg.author.clan
client.on_chat do |msg| client.on_chat do |_, msg|
puts "[chat] #{msg}" puts "[chat] #{msg}"
end end

View file

@ -11,7 +11,7 @@ require_relative '../lib/teeworlds_client'
client = TeeworldsClient.new(verbose: false) client = TeeworldsClient.new(verbose: false)
client.on_chat do |msg| client.on_chat do |_, msg|
next if msg.message[0] != '!' next if msg.message[0] != '!'
case msg.message[1..] case msg.message[1..]

View file

@ -0,0 +1,24 @@
#!/usr/bin/env ruby
# frozen_string_literal: true
require_relative '../../lib/teeworlds_client'
client = TeeworldsClient.new
client.on_connected do |_|
puts 'block 1 rcon auth'
client.rcon_auth(password: 'rcon')
end
client.on_connected do |_|
puts 'block 2 rcon shutdown'
client.rcon('shutdown')
end
client.on_disconnect do
puts 'got disconnect'
exit 0
end
# connect and block main thread
client.connect('localhost', 8377, detach: false)

View file

@ -78,9 +78,9 @@ then
fi fi
if [[ "$testname" =~ ^client/ ]] if [[ "$testname" =~ ^client/ ]]
then then
ruby_logfile="$logdir/ruby_client.tx" ruby_logfile="$logdir/ruby_client.txt"
else else
ruby_logfile="$logdir/ruby_server.tx" ruby_logfile="$logdir/ruby_server.txt"
fi fi
function cleanup() { function cleanup() {
@ -160,6 +160,36 @@ then
then then
fail "Error: server still running rcon shutdown failed" fail "Error: server still running rcon shutdown failed"
fi fi
elif [ "$testname" == "client/multiple_blocks.rb" ]
then
sleep 1
if pgrep -f "$tw_srv_bin $srvcfg"
then
fail "Error: server still running rcon shutdown failed (2 blocks)"
fi
block1_ln="$(grep -n "block 1" "$ruby_logfile" | cut -d':' -f1)"
block2_ln="$(grep -n "block 2" "$ruby_logfile" | cut -d':' -f1)"
if [ "$block1_ln" == "" ]
then
fail "Error: 'block 1' not found in client log"
fi
if [ "$block2_ln" == "" ]
then
fail "Error: 'block 2' not found in client log"
fi
if [[ ! "$block1_ln" =~ ^[0-9]+$ ]]
then
fail "Error: failed to parse line number of 'block 1' got='$block1_ln'"
fi
if [[ ! "$block2_ln" =~ ^[0-9]+$ ]]
then
fail "Error: failed to parse line number of 'block 2' got='$block2_ln'"
fi
# ensure block call order matches definition order
if [ "$block1_ln" -gt "$block2_ln" ]
then
fail "Error: 'block 1' found after 'block 2' in client log"
fi
else else
echo "Error: unkown test '$testname'" echo "Error: unkown test '$testname'"
exit 1 exit 1

29
lib/context.rb Normal file
View file

@ -0,0 +1,29 @@
# frozen_string_literal: true
class Context
attr_reader :old_data, :client
attr_accessor :data
def initialize(client, keys = {})
@client = client
@cancle = false
@old_data = keys
@data = keys
end
def verify
@data.each do |key, _value|
next if @old_data.key? key
raise "Error: invalid data key '#{key}'\n valid keys: #{@old_data.keys}"
end
end
def cancled?
@cancle
end
def cancle
@cancle = true
end
end

View file

@ -3,34 +3,7 @@
require_relative 'models/player' require_relative 'models/player'
require_relative 'models/chat_message' require_relative 'models/chat_message'
require_relative 'packer' require_relative 'packer'
require_relative 'context'
class Context
attr_reader :old_data, :client
attr_accessor :data
def initialize(client, keys = {})
@client = client
@cancle = false
@old_data = keys
@data = keys
end
def verify
@data.each do |key, _value|
next if @old_data.key? key
raise "Error: invalid data key '#{key}'\n valid keys: #{@old_data.keys}"
end
end
def cancled?
@cancle
end
def cancle
@cancle = true
end
end
class GameClient class GameClient
attr_accessor :players, :pred_game_tick, :ack_game_tick attr_accessor :players, :pred_game_tick, :ack_game_tick
@ -42,6 +15,21 @@ class GameClient
@pred_game_tick = 0 @pred_game_tick = 0
end end
##
# call_hook
#
# @param: hook_sym [Symbol] name of the symbol to call
# @param: context [Context] context object to pass on data
# @param: optional [Any] optional 2nd parameter passed to the callback
def call_hook(hook_sym, context, optional = nil)
@client.hooks[hook_sym].each do |hook|
hook.call(context, optional)
context.verify
return nil if context.cancled?
end
context
end
def on_client_info(chunk) def on_client_info(chunk)
# puts "Got playerinfo flags: #{chunk.flags}" # puts "Got playerinfo flags: #{chunk.flags}"
u = Unpacker.new(chunk.data[1..]) u = Unpacker.new(chunk.data[1..])
@ -61,11 +49,7 @@ class GameClient
player:, player:,
chunk: chunk:
) )
if @client.hooks[:client_info] return if call_hook(:client_info, context).nil?
@client.hooks[:client_info].call(context)
context.verify
return if context.cancled?
end
player = context.data[:player] player = context.data[:player]
@players[player.id] = player @players[player.id] = player
@ -78,18 +62,14 @@ class GameClient
silent = u.get_int silent = u.get_int
context = Context.new( context = Context.new(
@cliemt, @client,
player: @players[client_id], player: @players[client_id],
chunk:, chunk:,
client_id:, client_id:,
reason: reason == '' ? nil : reason, reason: reason == '' ? nil : reason,
silent: silent != 0 silent: silent != 0
) )
if @client.hooks[:client_drop] return if call_hook(:client_drop, context).nil?
@client.hooks[:client_drop].call(context)
context.verify
return if context.cancled?
end
@players.delete(context.data[:client_id]) @players.delete(context.data[:client_id])
end end
@ -100,16 +80,13 @@ class GameClient
def on_connected def on_connected
context = Context.new(@client) context = Context.new(@client)
if @client.hooks[:connected] return if call_hook(:connected, context).nil?
@client.hooks[:connected].call(context)
context.verify
return if context.cancled?
end
@client.send_msg_start_info @client.send_msg_start_info
end end
def on_disconnect def on_disconnect
@client.hooks[:disconnect]&.call call_hook(:disconnect, Context.new(@client))
end end
def on_rcon_line(chunk) def on_rcon_line(chunk)
@ -118,7 +95,7 @@ class GameClient
@client, @client,
line: u.get_string line: u.get_string
) )
@client.hooks[:rcon_line]&.call(context) call_hook(:rcon_line, context)
end end
def on_snapshot(chunk) def on_snapshot(chunk)
@ -162,11 +139,8 @@ class GameClient
def on_map_change(chunk) def on_map_change(chunk)
context = Context.new(@client, chunk:) context = Context.new(@client, chunk:)
if @client.hooks[:map_change] return if call_hook(:map_change, context).nil?
@client.hooks[:map_change].call(context)
context.verify
return if context.cancled?
end
# ignore mapdownload at all times # ignore mapdownload at all times
# and claim to have the map # and claim to have the map
@client.send_msg_ready @client.send_msg_ready
@ -183,6 +157,7 @@ class GameClient
data[:author] = @players[data[:client_id]] data[:author] = @players[data[:client_id]]
msg = ChatMesage.new(data) msg = ChatMesage.new(data)
@client.hooks[:chat]&.call(msg) context = Context.new(@client, chunk:)
call_hook(:chat, context, msg)
end end
end end

View file

@ -23,7 +23,16 @@ class TeeworldsClient
@state = NET_CONNSTATE_OFFLINE @state = NET_CONNSTATE_OFFLINE
@ip = 'localhost' @ip = 'localhost'
@port = 8303 @port = 8303
@hooks = {} @hooks = {
chat: [],
map_change: [],
client_info: [],
client_drop: [],
connected: [],
disconnect: [],
rcon_line: [],
snapshot: []
}
@thread_running = false @thread_running = false
@signal_disconnect = false @signal_disconnect = false
@game_client = GameClient.new(self) @game_client = GameClient.new(self)
@ -53,35 +62,35 @@ class TeeworldsClient
end end
def on_chat(&block) def on_chat(&block)
@hooks[:chat] = block @hooks[:chat].push(block)
end end
def on_map_change(&block) def on_map_change(&block)
@hooks[:map_change] = block @hooks[:map_change].push(block)
end end
def on_client_info(&block) def on_client_info(&block)
@hooks[:client_info] = block @hooks[:client_info].push(block)
end end
def on_client_drop(&block) def on_client_drop(&block)
@hooks[:client_drop] = block @hooks[:client_drop].push(block)
end end
def on_connected(&block) def on_connected(&block)
@hooks[:connected] = block @hooks[:connected].push(block)
end end
def on_disconnect(&block) def on_disconnect(&block)
@hooks[:disconnect] = block @hooks[:disconnect].push(block)
end end
def on_rcon_line(&block) def on_rcon_line(&block)
@hooks[:rcon_line] = block @hooks[:rcon_line].push(block)
end end
def on_snapshot(&block) def on_snapshot(&block)
@hooks[:snapshot] = block @hooks[:snapshot].push(block)
end end
def send_chat(str) def send_chat(str)

View file

@ -144,9 +144,9 @@ function check_file() {
{ {
echo '# frozen_string_literal: true' echo '# frozen_string_literal: true'
echo '' echo ''
echo "require_relative '../../$ruby_file'" echo "require_relative '../../${ruby_file::-3}'"
echo "obj = $ruby_class.new" echo "obj = $ruby_class.new"
echo "obj.$hook { |_| _ }" echo "obj.$hook(&:verify)"
} > "$tmpfile" } > "$tmpfile"
if ! ruby "$tmpfile" &>/dev/null if ! ruby "$tmpfile" &>/dev/null
then then