Make client callbacks arrays
Allows to setup multiple on_* hook blocks as a lib user closed #10
This commit is contained in:
parent
2bec19ff21
commit
fe3e581f86
|
@ -15,7 +15,7 @@ require_relative 'lib/teeworlds_client'
|
|||
|
||||
client = TeeworldsClient.new(verbose: false)
|
||||
|
||||
client.on_chat do |msg|
|
||||
client.on_chat do |_, msg|
|
||||
# note use `next` instead of `return` in the block
|
||||
next unless msg.message[0] == '!'
|
||||
|
||||
|
|
|
@ -28,7 +28,7 @@ args[:port] = args[:port] || 8303
|
|||
|
||||
client = TeeworldsClient.new(verbose: args[:verbose])
|
||||
|
||||
client.on_chat do |msg|
|
||||
client.on_chat do |_, msg|
|
||||
puts "[chat] #{msg}"
|
||||
end
|
||||
|
||||
|
|
|
@ -87,17 +87,19 @@ client.connect('localhost', 8303, detach: true)
|
|||
|
||||
### <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.
|
||||
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:**
|
||||
|
||||
```ruby
|
||||
client = TeeworldsClient.new
|
||||
|
||||
client.on_chat do |msg|
|
||||
client.on_chat do |context, msg|
|
||||
puts "[chat] #{msg}"
|
||||
end
|
||||
|
||||
|
|
|
@ -18,7 +18,7 @@ client = TeeworldsClient.new(verbose: false)
|
|||
# msg.author.team
|
||||
# msg.author.name
|
||||
# msg.author.clan
|
||||
client.on_chat do |msg|
|
||||
client.on_chat do |_, msg|
|
||||
puts "[chat] #{msg}"
|
||||
end
|
||||
|
||||
|
|
|
@ -11,7 +11,7 @@ require_relative '../lib/teeworlds_client'
|
|||
|
||||
client = TeeworldsClient.new(verbose: false)
|
||||
|
||||
client.on_chat do |msg|
|
||||
client.on_chat do |_, msg|
|
||||
next if msg.message[0] != '!'
|
||||
|
||||
case msg.message[1..]
|
||||
|
|
24
integration_test/client/multiple_blocks.rb
Executable file
24
integration_test/client/multiple_blocks.rb
Executable 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)
|
|
@ -78,9 +78,9 @@ then
|
|||
fi
|
||||
if [[ "$testname" =~ ^client/ ]]
|
||||
then
|
||||
ruby_logfile="$logdir/ruby_client.tx"
|
||||
ruby_logfile="$logdir/ruby_client.txt"
|
||||
else
|
||||
ruby_logfile="$logdir/ruby_server.tx"
|
||||
ruby_logfile="$logdir/ruby_server.txt"
|
||||
fi
|
||||
|
||||
function cleanup() {
|
||||
|
@ -160,6 +160,36 @@ then
|
|||
then
|
||||
fail "Error: server still running rcon shutdown failed"
|
||||
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
|
||||
echo "Error: unkown test '$testname'"
|
||||
exit 1
|
||||
|
|
29
lib/context.rb
Normal file
29
lib/context.rb
Normal 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
|
|
@ -3,34 +3,7 @@
|
|||
require_relative 'models/player'
|
||||
require_relative 'models/chat_message'
|
||||
require_relative 'packer'
|
||||
|
||||
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
|
||||
require_relative 'context'
|
||||
|
||||
class GameClient
|
||||
attr_accessor :players, :pred_game_tick, :ack_game_tick
|
||||
|
@ -42,6 +15,21 @@ class GameClient
|
|||
@pred_game_tick = 0
|
||||
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)
|
||||
# puts "Got playerinfo flags: #{chunk.flags}"
|
||||
u = Unpacker.new(chunk.data[1..])
|
||||
|
@ -61,11 +49,7 @@ class GameClient
|
|||
player:,
|
||||
chunk:
|
||||
)
|
||||
if @client.hooks[:client_info]
|
||||
@client.hooks[:client_info].call(context)
|
||||
context.verify
|
||||
return if context.cancled?
|
||||
end
|
||||
return if call_hook(:client_info, context).nil?
|
||||
|
||||
player = context.data[:player]
|
||||
@players[player.id] = player
|
||||
|
@ -78,18 +62,14 @@ class GameClient
|
|||
silent = u.get_int
|
||||
|
||||
context = Context.new(
|
||||
@cliemt,
|
||||
@client,
|
||||
player: @players[client_id],
|
||||
chunk:,
|
||||
client_id:,
|
||||
reason: reason == '' ? nil : reason,
|
||||
silent: silent != 0
|
||||
)
|
||||
if @client.hooks[:client_drop]
|
||||
@client.hooks[:client_drop].call(context)
|
||||
context.verify
|
||||
return if context.cancled?
|
||||
end
|
||||
return if call_hook(:client_drop, context).nil?
|
||||
|
||||
@players.delete(context.data[:client_id])
|
||||
end
|
||||
|
@ -100,16 +80,13 @@ class GameClient
|
|||
|
||||
def on_connected
|
||||
context = Context.new(@client)
|
||||
if @client.hooks[:connected]
|
||||
@client.hooks[:connected].call(context)
|
||||
context.verify
|
||||
return if context.cancled?
|
||||
end
|
||||
return if call_hook(:connected, context).nil?
|
||||
|
||||
@client.send_msg_start_info
|
||||
end
|
||||
|
||||
def on_disconnect
|
||||
@client.hooks[:disconnect]&.call
|
||||
call_hook(:disconnect, Context.new(@client))
|
||||
end
|
||||
|
||||
def on_rcon_line(chunk)
|
||||
|
@ -118,7 +95,7 @@ class GameClient
|
|||
@client,
|
||||
line: u.get_string
|
||||
)
|
||||
@client.hooks[:rcon_line]&.call(context)
|
||||
call_hook(:rcon_line, context)
|
||||
end
|
||||
|
||||
def on_snapshot(chunk)
|
||||
|
@ -162,11 +139,8 @@ class GameClient
|
|||
|
||||
def on_map_change(chunk)
|
||||
context = Context.new(@client, chunk:)
|
||||
if @client.hooks[:map_change]
|
||||
@client.hooks[:map_change].call(context)
|
||||
context.verify
|
||||
return if context.cancled?
|
||||
end
|
||||
return if call_hook(:map_change, context).nil?
|
||||
|
||||
# ignore mapdownload at all times
|
||||
# and claim to have the map
|
||||
@client.send_msg_ready
|
||||
|
@ -183,6 +157,7 @@ class GameClient
|
|||
data[:author] = @players[data[:client_id]]
|
||||
msg = ChatMesage.new(data)
|
||||
|
||||
@client.hooks[:chat]&.call(msg)
|
||||
context = Context.new(@client, chunk:)
|
||||
call_hook(:chat, context, msg)
|
||||
end
|
||||
end
|
||||
|
|
|
@ -23,7 +23,16 @@ class TeeworldsClient
|
|||
@state = NET_CONNSTATE_OFFLINE
|
||||
@ip = 'localhost'
|
||||
@port = 8303
|
||||
@hooks = {}
|
||||
@hooks = {
|
||||
chat: [],
|
||||
map_change: [],
|
||||
client_info: [],
|
||||
client_drop: [],
|
||||
connected: [],
|
||||
disconnect: [],
|
||||
rcon_line: [],
|
||||
snapshot: []
|
||||
}
|
||||
@thread_running = false
|
||||
@signal_disconnect = false
|
||||
@game_client = GameClient.new(self)
|
||||
|
@ -53,35 +62,35 @@ class TeeworldsClient
|
|||
end
|
||||
|
||||
def on_chat(&block)
|
||||
@hooks[:chat] = block
|
||||
@hooks[:chat].push(block)
|
||||
end
|
||||
|
||||
def on_map_change(&block)
|
||||
@hooks[:map_change] = block
|
||||
@hooks[:map_change].push(block)
|
||||
end
|
||||
|
||||
def on_client_info(&block)
|
||||
@hooks[:client_info] = block
|
||||
@hooks[:client_info].push(block)
|
||||
end
|
||||
|
||||
def on_client_drop(&block)
|
||||
@hooks[:client_drop] = block
|
||||
@hooks[:client_drop].push(block)
|
||||
end
|
||||
|
||||
def on_connected(&block)
|
||||
@hooks[:connected] = block
|
||||
@hooks[:connected].push(block)
|
||||
end
|
||||
|
||||
def on_disconnect(&block)
|
||||
@hooks[:disconnect] = block
|
||||
@hooks[:disconnect].push(block)
|
||||
end
|
||||
|
||||
def on_rcon_line(&block)
|
||||
@hooks[:rcon_line] = block
|
||||
@hooks[:rcon_line].push(block)
|
||||
end
|
||||
|
||||
def on_snapshot(&block)
|
||||
@hooks[:snapshot] = block
|
||||
@hooks[:snapshot].push(block)
|
||||
end
|
||||
|
||||
def send_chat(str)
|
||||
|
|
|
@ -144,9 +144,9 @@ function check_file() {
|
|||
{
|
||||
echo '# frozen_string_literal: true'
|
||||
echo ''
|
||||
echo "require_relative '../../$ruby_file'"
|
||||
echo "require_relative '../../${ruby_file::-3}'"
|
||||
echo "obj = $ruby_class.new"
|
||||
echo "obj.$hook { |_| _ }"
|
||||
echo "obj.$hook(&:verify)"
|
||||
} > "$tmpfile"
|
||||
if ! ruby "$tmpfile" &>/dev/null
|
||||
then
|
||||
|
|
Loading…
Reference in a new issue