Proper chat message support
This commit is contained in:
parent
316a24852e
commit
0f9c9a0804
20
README.md
20
README.md
|
@ -6,11 +6,21 @@ A teeworlds 0.7 client library written in ruby
|
|||
```ruby
|
||||
require_relative 'lib/teeworlds-client'
|
||||
|
||||
client = TwClient.new(verbose: false)
|
||||
client = TeeworldsClient.new(verbose: false)
|
||||
|
||||
# print all incoming chat messages
|
||||
# the variable `msg` holds an instance of the class `ChatMessage` which has the following fields
|
||||
#
|
||||
# msg.mode
|
||||
# msg.client_id
|
||||
# msg.target_id
|
||||
# msg.message
|
||||
# msg.author.id
|
||||
# msg.author.team
|
||||
# msg.author.name
|
||||
# msg.author.clan
|
||||
client.hook_chat do |msg|
|
||||
puts "chat: #{msg}"
|
||||
puts "[chat] #{msg}"
|
||||
end
|
||||
|
||||
# properly disconnect on ctrl+c
|
||||
|
@ -27,7 +37,7 @@ client.connect('localhost', 8303, detach: false)
|
|||
```ruby
|
||||
require_relative 'lib/teeworlds-client'
|
||||
|
||||
client = TwClient.new(verbose: true)
|
||||
client = TeeworldsClient.new(verbose: true)
|
||||
|
||||
# connect to localhost and block the current thread
|
||||
client.connect('localhost', 8303, detach: false)
|
||||
|
@ -38,7 +48,7 @@ client.connect('localhost', 8303, detach: false)
|
|||
```ruby
|
||||
require_relative 'lib/teeworlds-client'
|
||||
|
||||
client = TwClient.new(verbose: true)
|
||||
client = TeeworldsClient.new(verbose: true)
|
||||
|
||||
# connect to localhost and detach a background thread
|
||||
client.connect('localhost', 8303, detach: true)
|
||||
|
@ -55,7 +65,7 @@ end
|
|||
```ruby
|
||||
require_relative 'lib/teeworlds-client'
|
||||
|
||||
client = TwClient.new(verbose: true)
|
||||
client = TeeworldsClient.new(verbose: true)
|
||||
|
||||
# all keys are optional
|
||||
# if not provided they will fall back to the default value
|
||||
|
|
30
lib/chat_message.rb
Normal file
30
lib/chat_message.rb
Normal file
|
@ -0,0 +1,30 @@
|
|||
class ChatMesage
|
||||
attr_reader :mode, :client_id, :target_id, :message, :author
|
||||
|
||||
def initialize(data = {})
|
||||
# @mode
|
||||
# Type: Integer
|
||||
@mode = data[:mode]
|
||||
|
||||
# @client_id
|
||||
# Type: Integer
|
||||
@client_id = data[:client_id]
|
||||
|
||||
# @target_id
|
||||
# Type: Integer
|
||||
@target_id = data[:target_id]
|
||||
|
||||
# @message
|
||||
# Type: String
|
||||
@message = data[:message]
|
||||
|
||||
# @author
|
||||
# Type: Player see player.rb
|
||||
@author = data[:author]
|
||||
end
|
||||
|
||||
def to_s
|
||||
"#{@author.name}: #{@message}"
|
||||
end
|
||||
end
|
||||
|
54
lib/game_client.rb
Normal file
54
lib/game_client.rb
Normal file
|
@ -0,0 +1,54 @@
|
|||
require_relative 'player'
|
||||
require_relative 'packer'
|
||||
require_relative 'chat_message'
|
||||
|
||||
class GameClient
|
||||
attr_accessor :players
|
||||
|
||||
def initialize(client)
|
||||
@client = client
|
||||
@players = {}
|
||||
end
|
||||
|
||||
def on_player_join(chunk)
|
||||
# puts "Got playerinfo flags: #{chunk.flags}"
|
||||
u = Unpacker.new(chunk.data[1..])
|
||||
player = Player.new(
|
||||
id: u.get_int(),
|
||||
local: u.get_int(),
|
||||
team: u.get_int(),
|
||||
name: u.get_string(),
|
||||
clan: u.get_string(),
|
||||
country: u.get_int())
|
||||
# skinparts and the silent flag
|
||||
# are currently ignored
|
||||
|
||||
@players[player.id] = player
|
||||
puts "'#{player.name}' joined the game"
|
||||
end
|
||||
|
||||
def on_chat(chunk)
|
||||
# 06 01 00 40 41 00
|
||||
# msg mode cl_id trgt A nullbyte?
|
||||
# all -1
|
||||
# mode = chunk.data[1]
|
||||
# client_id = chunk.data[2]
|
||||
# target = chunk.data[3]
|
||||
# msg = chunk.data[4..]
|
||||
|
||||
u = Unpacker.new(chunk.data[1..])
|
||||
data = {
|
||||
mode: u.get_int(),
|
||||
client_id: u.get_int(),
|
||||
target_id: u.get_int(),
|
||||
message: u.get_string()
|
||||
}
|
||||
data[:author] = @players[data[:client_id]]
|
||||
msg = ChatMesage.new(data)
|
||||
|
||||
if @client.hooks[:chat]
|
||||
@client.hooks[:chat].call(msg)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
@ -69,6 +69,13 @@ end
|
|||
class Unpacker
|
||||
def initialize(data)
|
||||
@data = data
|
||||
if data.class == String
|
||||
@data = data.unpack("C*")
|
||||
elsif data.class == Array
|
||||
@data = data
|
||||
else
|
||||
raise "Error: Unpacker expects array of integers or byte string"
|
||||
end
|
||||
end
|
||||
|
||||
def get_string()
|
||||
|
@ -197,5 +204,5 @@ def todo_also_rspec_unpacker
|
|||
# p u.get_int()
|
||||
end
|
||||
|
||||
todo_also_rspec_unpacker
|
||||
# todo_also_rspec_unpacker
|
||||
|
||||
|
|
18
lib/player.rb
Normal file
18
lib/player.rb
Normal file
|
@ -0,0 +1,18 @@
|
|||
class Player
|
||||
attr_reader :id, :local, :team, :name, :clan
|
||||
attr_reader :country
|
||||
attr_reader :skin_parts, :skin_custom_colors, :skin_colors
|
||||
|
||||
def initialize(data = {})
|
||||
@id = data[:id] || -1
|
||||
@local = data[:local] || 0
|
||||
@team = data[:team] || 0
|
||||
@name = data[:name] || '(connecting..)'
|
||||
@clan = data[:clan] || ''
|
||||
@country = data[:country] || -1
|
||||
@skin_parts = data[:skin_parts] || Array.new(6, 'standard')
|
||||
@skin_custom_colors = data[:skin_custom_colors] || Array.new(6, 0)
|
||||
@skin_colors = data[:skin_colors] || Array.new(6, 0)
|
||||
end
|
||||
end
|
||||
|
|
@ -11,9 +11,11 @@ require_relative 'chunk'
|
|||
require_relative 'server_info'
|
||||
require_relative 'net_base'
|
||||
require_relative 'packer'
|
||||
require_relative 'player'
|
||||
require_relative 'game_client'
|
||||
|
||||
class TwClient
|
||||
attr_reader :state
|
||||
class TeeworldsClient
|
||||
attr_reader :state, :hooks
|
||||
|
||||
def initialize(options = {})
|
||||
@verbose = options[:verbose] || false
|
||||
|
@ -24,6 +26,7 @@ class TwClient
|
|||
@hooks = {}
|
||||
@thread_running = false
|
||||
@signal_disconnect = false
|
||||
@game_client = GameClient.new(self)
|
||||
@start_info = {
|
||||
name: "ruby gamer",
|
||||
clan: "",
|
||||
|
@ -64,6 +67,7 @@ class TwClient
|
|||
disconnect
|
||||
@signal_disconnect = false
|
||||
@ticks = 0
|
||||
@game_client = GameClient.new(self)
|
||||
# me trying to write cool code
|
||||
@client_token = (1..4).to_a.map { |_| rand(0..255) }
|
||||
@client_token = @client_token.map { |b| b.to_s(16) }.join('')
|
||||
|
@ -286,34 +290,16 @@ class TwClient
|
|||
end
|
||||
end
|
||||
|
||||
def on_player_join(chunk)
|
||||
puts "Got playerinfo flags: #{chunk.flags}"
|
||||
end
|
||||
|
||||
def on_emoticon(chunk)
|
||||
# puts "Got emoticon flags: #{chunk.flags}"
|
||||
end
|
||||
|
||||
def on_chat(chunk)
|
||||
# 06 01 00 40 41 00
|
||||
# msg mode cl_id trgt A nullbyte?
|
||||
# all -1
|
||||
mode = chunk.data[1]
|
||||
client_id = chunk.data[2]
|
||||
target = chunk.data[3]
|
||||
msg = chunk.data[4..]
|
||||
|
||||
if @hooks[:chat]
|
||||
@hooks[:chat].call(msg)
|
||||
end
|
||||
end
|
||||
|
||||
def on_message(chunk)
|
||||
case chunk.msg
|
||||
when NETMSGTYPE_SV_READYTOENTER then send_enter_game
|
||||
when NETMSGTYPE_SV_CLIENTINFO then on_player_join(chunk)
|
||||
when NETMSGTYPE_SV_CLIENTINFO then @game_client.on_player_join(chunk)
|
||||
when NETMSGTYPE_SV_EMOTICON then on_emoticon(chunk)
|
||||
when NETMSGTYPE_SV_CHAT then on_chat(chunk)
|
||||
when NETMSGTYPE_SV_CHAT then @game_client.on_chat(chunk)
|
||||
else
|
||||
if @verbose
|
||||
puts "todo non sys chunks. skipped msg: #{chunk.msg}"
|
||||
|
|
|
@ -25,13 +25,13 @@ end
|
|||
args[:ip] = args[:ip] || '127.0.0.1'
|
||||
args[:port] = args[:port] || 8303
|
||||
|
||||
client = TwClient.new(verbose: args[:verbose])
|
||||
client = TeeworldsClient.new(verbose: args[:verbose])
|
||||
|
||||
client.set_startinfo(
|
||||
name: "ruby gamer")
|
||||
|
||||
client.hook_chat do |msg|
|
||||
puts "chat: #{msg}"
|
||||
puts "[chat] #{msg}"
|
||||
end
|
||||
|
||||
Signal.trap('INT') do
|
||||
|
|
Loading…
Reference in a new issue