teeworlds_network/lib/game_client.rb

201 lines
4.8 KiB
Ruby
Raw Normal View History

2022-11-05 16:48:47 +00:00
# frozen_string_literal: true
require_relative 'models/player'
require_relative 'models/chat_message'
require_relative 'messages/input_timing'
2023-09-17 09:47:23 +00:00
require_relative 'messages/rcon_line'
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'
2022-11-04 15:26:24 +00:00
require_relative 'packer'
require_relative 'context'
2022-11-19 08:59:00 +00:00
require_relative 'snapshot/unpacker'
2022-11-04 15:26:24 +00:00
class GameClient
attr_accessor :players, :pred_game_tick, :ack_game_tick
2022-11-04 15:26:24 +00:00
def initialize(client)
@client = client
@players = {}
@ack_game_tick = -1
@pred_game_tick = 0
2022-11-04 15:26:24 +00:00
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.canceld?
end
context
end
2023-09-17 17:50:09 +00:00
def on_tick
2023-09-17 17:59:36 +00:00
call_hook(:tick, Context.new(nil))
2023-09-17 17:50:09 +00:00
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)
2022-11-26 10:25:23 +00:00
message = RconCmdAdd.new(chunk.data[1..])
context = Context.new(message)
call_hook(:rcon_cmd_add, context)
end
def on_rcon_cmd_rem(chunk)
2022-11-26 10:25:23 +00:00
message = RconCmdRem.new(chunk.data[1..])
context = Context.new(message)
call_hook(:rcon_cmd_rem, context)
end
def on_maplist_entry_add(chunk)
2022-11-26 10:25:23 +00:00
message = MaplistEntryAdd.new(chunk.data[1..])
context = Context.new(message)
call_hook(:maplist_entry_add, context)
end
def on_maplist_entry_rem(chunk)
2022-11-26 10:25:23 +00:00
message = MaplistEntryRem.new(chunk.data[1..])
context = Context.new(message)
call_hook(:maplist_entry_rem, context)
end
def on_client_info(chunk)
2022-11-04 15:26:24 +00:00
# puts "Got playerinfo flags: #{chunk.flags}"
u = Unpacker.new(chunk.data[1..])
player = Player.new(
2022-11-05 16:19:05 +00:00
id: u.get_int,
local: u.get_int,
team: u.get_int,
name: u.get_string,
clan: u.get_string,
country: u.get_int
)
2022-11-04 15:26:24 +00:00
# skinparts and the silent flag
# are currently ignored
context = Context.new(
nil,
2022-11-05 16:19:05 +00:00
player:,
chunk:
)
return if call_hook(:client_info, context).nil?
player = context.data[:player]
2022-11-19 12:19:22 +00:00
if player.local?
@client.local_client_id = player.id
puts "Our client id is #{@client.local_client_id}"
end
2022-11-04 15:26:24 +00:00
@players[player.id] = player
2022-11-05 10:59:36 +00:00
end
def on_input_timing(chunk)
2022-11-26 10:25:23 +00:00
message = InputTiming.new(chunk.data[1..])
context = Context.new(message, chunk:)
call_hook(:input_timing, context)
end
2022-11-05 10:59:36 +00:00
def on_client_drop(chunk)
2022-11-26 10:25:23 +00:00
message = SvClientDrop.new(chunk.data[1..])
2022-11-05 10:59:36 +00:00
context = Context.new(
nil,
2022-11-26 10:25:23 +00:00
player: @players[message.client_id],
2022-11-05 16:19:05 +00:00
chunk:,
2022-11-26 10:25:23 +00:00
client_id: message.client_id,
reason: message.reason,
silent: message.silent?
2022-11-05 10:59:36 +00:00
)
return if call_hook(:client_drop, context).nil?
2022-11-05 10:59:36 +00:00
@players.delete(context.data[:client_id])
2022-11-04 15:26:24 +00:00
end
2022-11-05 16:19:05 +00:00
def on_ready_to_enter(_chunk)
@client.send_enter_game
end
def on_connected
context = Context.new(nil)
return if call_hook(:connected, context).nil?
2022-11-13 09:37:46 +00:00
@client.send_msg_start_info
end
def on_disconnect(data)
context = Context.new(nil, reason: data)
return if call_hook(:disconnect, context).nil?
puts "got disconnect. reason='#{context.data[:reason]}'"
2022-11-12 15:47:12 +00:00
end
2022-11-06 17:26:14 +00:00
def on_rcon_line(chunk)
2023-09-17 09:47:23 +00:00
message = RconLine.new(chunk.data[1..])
context = Context.new(message)
2023-09-17 14:36:57 +00:00
return if call_hook(:rcon_line, context).nil?
puts "[rcon] #{context.message.command}"
2022-11-06 17:26:14 +00:00
end
def on_snapshot(chunk)
u = SnapshotUnpacker.new(@client)
2022-11-19 11:39:58 +00:00
snapshot = u.snap_single(chunk)
2022-11-19 12:19:22 +00:00
return if snapshot.nil?
2022-11-19 11:39:58 +00:00
context = Context.new(nil, chunk:)
return if call_hook(:snapshot, context, snapshot).nil?
# ack every snapshot no matter how broken
2022-11-19 11:39:58 +00:00
@ack_game_tick = snapshot.game_tick
return unless (@pred_game_tick - @ack_game_tick).abs > 10
@pred_game_tick = @ack_game_tick + 1
end
2022-11-05 16:19:05 +00:00
def on_emoticon(chunk); end
def on_map_change(chunk)
context = Context.new(nil, chunk:)
return if call_hook(:map_change, context).nil?
# ignore mapdownload at all times
# and claim to have the map
@client.send_msg_ready
end
2022-11-04 15:26:24 +00:00
def on_chat(chunk)
u = Unpacker.new(chunk.data[1..])
data = {
2022-11-05 16:19:05 +00:00
mode: u.get_int,
client_id: u.get_int,
target_id: u.get_int,
message: u.get_string
2022-11-04 15:26:24 +00:00
}
data[:author] = @players[data[:client_id]]
msg = ChatMesage.new(data)
context = Context.new(nil, chunk:)
call_hook(:chat, context, msg)
2022-11-04 15:26:24 +00:00
end
end