diff --git a/lib/game_server.rb b/lib/game_server.rb index 55f0bee..ba8d824 100644 --- a/lib/game_server.rb +++ b/lib/game_server.rb @@ -88,7 +88,7 @@ class GameServer context = Context.new(say, chunk:) return if call_hook(:chat, context, msg).nil? - puts msg.to_s + puts msg end def on_enter_game(_chunk, packet) @@ -108,8 +108,26 @@ class GameServer def on_rcon_cmd(chunk, _packet) u = Unpacker.new(chunk.data[1..]) - cmd = u.get_string - puts "got rcon_cmd=#{cmd}" + 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' + @server.shutdown! + 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 end def on_input(chunk, packet) @@ -126,6 +144,12 @@ class GameServer puts "'#{client.player.name}' left the game#{reason}" end + def on_shutdown + return if call_hook(:shutdown, Context.new(nil)).nil? + + puts '[gameserver] shutting down ...' + end + def on_tick now = Time.now timeout_ids = [] diff --git a/lib/teeworlds_server.rb b/lib/teeworlds_server.rb index 049f80e..d2c1122 100644 --- a/lib/teeworlds_server.rb +++ b/lib/teeworlds_server.rb @@ -16,6 +16,7 @@ require_relative 'models/token' class Client attr_accessor :id, :addr, :vital_sent, :last_recv_time, :token, :player, :in_game + attr_accessor :authed attr_reader :ack def initialize(attr = {}) @@ -33,10 +34,15 @@ class Client clan: '', country: -1 ) + @authed = false @token = attr[:token] SecurityToken.validate(@token) end + def authed? + @authed + end + def in_game? @in_game end @@ -70,17 +76,39 @@ class TeeworldsServer @current_game_tick = 0 @last_snap_time = Time.now @hooks = { - chat: [] + chat: [], + rcon_auth: [], + rcon_cmd: [], + shutdown: [] } @thread_running = false + @is_shutting_down = false + end + + def shutdown! + @is_shutting_down = true end def on_chat(&block) @hooks[:chat].push(block) end + def on_rcon_auth(&block) + @hooks[:rcon_auth].push(block) + end + + def on_rcon_cmd(&block) + @hooks[:rcon_cmd].push(block) + end + + def on_shutdown(&block) + @hooks[:shutdown].push(block) + end + def main_loop loop do + break if @is_shutting_down + tick # TODO: proper tick speed sleep # replace by blocking network read @@ -123,6 +151,8 @@ class TeeworldsServer else main_loop end + + @game_server.on_shutdown end def on_message(chunk, packet) @@ -154,6 +184,8 @@ class TeeworldsServer @game_server.on_input(chunk, packet) when NETMSG_RCON_CMD @game_server.on_rcon_cmd(chunk, packet) + when NETMSG_RCON_AUTH + @game_server.on_rcon_auth(chunk, packet) else puts "Unsupported system msg: #{chunk.msg}" exit(1)