teeworlds_network/lib/game_server.rb

186 lines
5.3 KiB
Ruby
Raw Normal View History

2022-11-09 15:46:04 +00:00
# frozen_string_literal: true
2024-01-20 13:52:51 +00:00
require_relative 'context'
require_relative 'models/map'
2022-11-13 10:15:33 +00:00
require_relative 'models/chat_message'
require_relative 'messages/game_info'
require_relative 'messages/server_info'
require_relative 'messages/server_settings'
require_relative 'messages/start_info'
require_relative 'messages/cl_say'
require_relative 'messages/cl_emoticon'
2024-02-21 03:39:57 +00:00
require_relative 'messages/cl_info'
require_relative 'messages/cl_input'
2022-11-09 15:46:04 +00:00
class GameServer
attr_accessor :pred_game_tick, :ack_game_tick, :map
2022-11-09 15:46:04 +00:00
def initialize(server)
@server = server
@ack_game_tick = -1
@pred_game_tick = 0
@map = Map.new(
name: 'dm1',
crc: '98a0a4c50c', # decimal 64548818
size: 6793,
sha256: '491af17a510214506270904f147a4c30ae0a85b91bb854395bef8c397fc078c3'
)
2022-11-09 15:46:04 +00:00
end
2024-01-20 13:39:21 +00:00
##
# 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)
@server.hooks[hook_sym].each do |hook|
hook.call(context, optional)
context.verify
return nil if context.canceld?
end
context
end
def on_emoticon(chunk, packet)
2024-02-21 03:39:57 +00:00
msg = ClEmoticon.new(chunk.data[1..])
return if call_hook(:emote, Context.new(msg, chunk:, packet:)).nil?
@server.send_emoticon(packet.client.player.id, msg.emoticon)
2022-11-13 14:54:27 +00:00
end
def on_info(chunk, packet)
2024-02-21 03:39:57 +00:00
msg = ClInfo.new(chunk.data[1..])
2022-11-09 15:46:04 +00:00
2024-02-21 03:39:57 +00:00
return if call_hook(:info, Context.new(msg, chunk:, packet:)).nil?
2024-02-21 03:39:57 +00:00
# TODO: check version and password
puts "vers=#{msg.net_version} vers=#{msg.client_version} pass=#{msg.password}"
2022-11-12 10:18:12 +00:00
@server.send_map(packet.client)
2022-11-09 15:46:04 +00:00
end
2022-11-11 12:42:11 +00:00
def on_ready(_chunk, packet)
# vanilla server sends 3 chunks here usually
# - motd
# - server settings
# - ready
#
2024-02-21 03:39:57 +00:00
return if call_hook(:ready, Context.new(nil, chunk: nil, packet:)).nil?
2022-11-13 08:40:05 +00:00
@server.send_server_settings(packet.client, ServerSettings.new.to_a)
2022-11-12 10:18:12 +00:00
@server.send_ready(packet.client)
2022-11-11 12:42:11 +00:00
end
2022-11-13 09:37:46 +00:00
def on_start_info(chunk, packet)
2022-11-11 12:42:11 +00:00
# vanilla server sends 3 chunks here usually
# - vote clear options
# - tune params
# - ready to enter
#
# We only send ready to enter for now
2022-11-13 09:37:46 +00:00
info = StartInfo.new(chunk.data[1..])
2024-02-21 03:39:57 +00:00
return if call_hook(:start_info, Context.new(info, chunk: nil, packet:)).nil?
2022-11-13 09:37:46 +00:00
packet.client.player.set_start_info(info)
info_str = info.to_s
puts "got start info: #{info_str}" if @verbose
2022-11-12 10:18:12 +00:00
@server.send_ready_to_enter(packet.client)
2022-11-11 12:42:11 +00:00
end
2022-11-13 10:15:33 +00:00
def on_say(chunk, packet)
say = ClSay.new(chunk.data[1..])
author = packet.client.player
msg = ChatMesage.new(say.to_h.merge(client_id: author.id, author:))
2024-01-20 13:39:21 +00:00
context = Context.new(say, chunk:)
return if call_hook(:chat, context, msg).nil?
2024-01-20 14:21:59 +00:00
puts msg
2022-11-13 10:15:33 +00:00
end
2022-11-11 12:42:11 +00:00
def on_enter_game(_chunk, packet)
# vanilla server responds to enter game with two packets
# first:
# - server info
# second:
# - game info
# - client info
# - snap single
2024-02-21 03:39:57 +00:00
return if call_hook(:enter_game, Context.new(nil, chunk: nil, packet:)).nil?
2022-11-13 07:40:17 +00:00
packet.client.in_game = true
2022-11-12 10:18:12 +00:00
@server.send_server_info(packet.client, ServerInfo.new.to_a)
@server.send_game_info(packet.client, GameInfo.new.to_a)
puts "'#{packet.client.player.name}' joined the game"
2022-11-11 12:42:11 +00:00
end
2022-11-12 10:38:46 +00:00
2024-01-20 14:32:12 +00:00
def on_rcon_cmd(chunk, packet)
2022-11-12 14:46:38 +00:00
u = Unpacker.new(chunk.data[1..])
2024-01-20 14:21:59 +00:00
command = u.get_string
return if call_hook(:rcon_cmd, Context.new(nil, chunk:, packet:, command:)).nil?
return unless packet.client.authed?
puts "[server] ClientID=#{packet.client.player.id} rcon='#{command}'"
if command == 'shutdown'
2024-01-20 14:32:12 +00:00
@server.shutdown!('Server shutdown')
2024-01-20 14:21:59 +00:00
else
puts "[console] No such command: #{command}:"
end
end
def on_rcon_auth(chunk, packet)
u = Unpacker.new(chunk.data[1..])
password = u.get_string
return if call_hook(:rcon_auth, Context.new(nil, chunk:, packet:, password:)).nil?
# TODO: we accept any password lol
puts "[server] ClientID=#{packet.client.player.id} addr=#{packet.client.addr} authed (admin)"
packet.client.authed = true
2024-01-20 14:32:12 +00:00
@server.send_rcon_auth_on(packet.client)
2022-11-12 14:46:38 +00:00
end
2022-11-12 10:38:46 +00:00
def on_input(chunk, packet)
# vanilla server responds to input with 2 chunks
# - input_timing
# - snap (empty)
2024-02-21 03:39:57 +00:00
msg = ClInput.new(chunk.data[1..])
return if call_hook(:input, Context.new(msg, chunk:, packet:)).nil?
dir = msg.direction
2024-02-21 04:32:21 +00:00
puts "#{packet.client.player.id} tried to move #{dir}" unless dir.zero?
2022-11-12 10:38:46 +00:00
end
def on_client_drop(client, reason = nil)
reason = reason.nil? ? '' : " (#{reason})"
return if call_hook(:client_drop, Context.new(nil, client:, reason:)).nil?
2024-02-21 03:39:57 +00:00
puts "'#{client.player.name}' left the game#{reason}"
end
2024-01-20 14:21:59 +00:00
def on_shutdown
return if call_hook(:shutdown, Context.new(nil)).nil?
2024-01-20 14:32:12 +00:00
puts '[gameserver] disconnecting all clients ...'
2024-01-20 14:37:18 +00:00
@server.clients.each_value do |client|
2024-01-20 14:32:12 +00:00
@server.send_ctrl_close(client, @server.shutdown_reason)
end
2024-01-20 14:21:59 +00:00
puts '[gameserver] shutting down ...'
end
def on_tick
return if call_hook(:tick, Context.new(nil)).nil?
2024-02-21 03:39:57 +00:00
now = Time.now
timeout_ids = []
@server.clients.each do |id, client|
diff = now - client.last_recv_time
timeout_ids.push(id) if diff > 10
end
timeout_ids.each do |id|
@server.drop_client(@server.clients[id], 'Timeout')
end
end
2022-11-09 15:46:04 +00:00
end