teeworlds_network/lib/teeworlds_server.rb

225 lines
5.8 KiB
Ruby
Raw Normal View History

2022-11-08 15:20:46 +00:00
# frozen_string_literal: true
require 'socket'
require_relative 'string'
require_relative 'array'
require_relative 'bytes'
require_relative 'network'
require_relative 'packet'
require_relative 'chunk'
require_relative 'net_base'
require_relative 'net_addr'
require_relative 'packer'
2022-11-09 15:46:04 +00:00
require_relative 'game_server'
require_relative 'message'
class Client
attr_accessor :id, :addr
def initialize(attr = {})
@id = attr[:id]
@addr = attr[:addr]
end
end
2022-11-08 15:20:46 +00:00
class TeeworldsServer
def initialize(options = {})
@verbose = options[:verbose] || false
@ip = '127.0.0.1'
@port = 8303
2022-11-09 15:46:04 +00:00
@game_server = GameServer.new(self)
@clients = {}
2022-11-08 15:20:46 +00:00
end
def run(ip, port)
@server_token = (1..4).to_a.map { |_| rand(0..255) }
2022-11-11 13:37:41 +00:00
@server_token = @server_token.map { |b| b.to_s(16).rjust(2, '0') }.join
2022-11-08 15:20:46 +00:00
puts "server token #{@server_token}"
2022-11-11 12:42:11 +00:00
@netbase = NetBase.new(verbose: @verbose)
2022-11-08 15:20:46 +00:00
NetChunk.reset
@ip = ip
@port = port
puts "listening on #{@ip}:#{@port} .."
@s = UDPSocket.new
@s.bind(@ip, @port)
@netbase.bind(@s)
loop do
tick
2022-11-09 12:55:09 +00:00
# TODO: proper tick speed sleep
sleep 0.001
2022-11-08 15:20:46 +00:00
end
end
2022-11-11 12:42:11 +00:00
def on_message(chunk, packet)
puts "got game chunk: #{chunk}"
case chunk.msg
when NETMSGTYPE_CL_STARTINFO then @game_server.on_startinfo(chunk, packet)
else
puts "Unsupported game msg: #{chunk.msg}"
exit(1)
end
2022-11-09 15:46:04 +00:00
end
def process_chunk(chunk, packet)
2022-11-09 15:46:04 +00:00
unless chunk.sys
2022-11-11 12:42:11 +00:00
on_message(chunk, packet)
2022-11-09 15:46:04 +00:00
return
end
puts "proccess chunk with msg: #{chunk.msg}"
case chunk.msg
when NETMSG_INFO
@game_server.on_info(chunk, packet)
2022-11-11 12:42:11 +00:00
when NETMSG_READY
@game_server.on_ready(chunk, packet)
when NETMSG_ENTERGAME
@game_server.on_enter_game(chunk, packet)
2022-11-09 15:46:04 +00:00
else
puts "Unsupported system msg: #{chunk.msg}"
exit(1)
end
end
def on_client_packet(packet)
chunks = BigChungusTheChunkGetter.get_chunks(packet.payload)
chunks.each do |chunk|
if chunk.flags_vital && !chunk.flags_resend
@netbase.ack = (@netbase.ack + 1) % NET_MAX_SEQUENCE
puts "got ack: #{@netbase.ack}" if @verbose
end
process_chunk(chunk, packet)
2022-11-09 15:46:04 +00:00
end
2022-11-08 15:20:46 +00:00
end
def on_ctrl_message(packet)
u = Unpacker.new(packet.payload)
msg = u.get_int
puts "got ctrl msg: #{msg}"
case msg
when NET_CTRLMSG_TOKEN then on_ctrl_token(packet)
when NET_CTRLMSG_CONNECT then on_ctrl_connect(packet)
when NET_CTRLMSG_KEEPALIVE then on_ctrl_keep_alive(packet)
when NET_CTRLMSG_CLOSE then on_ctrl_close(packet)
2022-11-08 15:20:46 +00:00
else
puts "Uknown control message #{msg}"
exit(1)
end
end
def send_ctrl_with_token(addr, token)
2022-11-11 13:37:41 +00:00
msg = [NET_CTRLMSG_TOKEN] + str_bytes(@server_token)
@netbase.peer_token = token
2022-11-08 15:20:46 +00:00
@netbase.send_packet(msg, 0, control: true, addr:)
2022-11-11 13:37:41 +00:00
# @netbase.peer_token = @server_token
2022-11-08 15:20:46 +00:00
end
def send_map(addr)
data = []
data += Packer.pack_str(@game_server.map.name)
data += Packer.pack_int(@game_server.map.crc)
data += Packer.pack_int(@game_server.map.size)
data += Packer.pack_int(8) # chunk num?
data += Packer.pack_int(MAP_CHUNK_SIZE)
data += @game_server.map.sha256_arr # poor mans pack_raw()
2022-11-11 12:42:11 +00:00
msg = NetChunk.create_vital_header({ vital: true }, data.size + 1) +
[pack_msg_id(NETMSG_MAP_CHANGE, system: true)] +
data
@netbase.send_packet(msg, 1, addr:)
end
2022-11-11 12:42:11 +00:00
def send_ready(addr)
msg = NetChunk.create_vital_header({ vital: true }, 1) +
[pack_msg_id(NETMSG_CON_READY, system: true)]
@netbase.send_packet(msg, 1, addr:)
end
def send_ready_to_enter(addr)
msg = NetChunk.create_vital_header({ vital: true }, 1) +
[pack_msg_id(NETMSGTYPE_SV_READYTOENTER, system: false)]
@netbase.send_packet(msg, 1, addr:)
end
def send_server_info(addr, server_info)
msg = NetChunk.create_vital_header({ vital: true }, 1 + server_info.size) +
[pack_msg_id(NETMSG_SERVERINFO, system: true)] +
server_info
@netbase.send_packet(msg, 1, addr:)
end
def send_game_info(addr, data)
msg = NetChunk.create_vital_header({ vital: true }, 1 + data.size) +
[pack_msg_id(NETMSGTYPE_SV_GAMEINFO, system: false)] +
data
@netbase.send_packet(msg, 1, addr:)
end
2022-11-08 15:20:46 +00:00
def on_ctrl_token(packet)
u = Unpacker.new(packet.payload[1..])
token = u.get_raw(4)
token = token.map { |b| b.to_s(16).rjust(2, '0') }.join
puts "got token #{token}"
2022-11-08 15:20:46 +00:00
send_ctrl_with_token(packet.addr, token)
end
def on_ctrl_keep_alive(packet)
puts "Got keep alive from #{packet.addr}" if @verbose
end
def on_ctrl_close(packet)
puts "Client closed the connection #{packet.addr}"
end
def on_ctrl_connect(packet)
puts 'got connection, sending accept'
id = get_next_client_id
if id == -1
puts 'server full drop packet. TODO: tell the client'
return
end
client = Client.new(id:, addr: packet.addr)
@clients[id] = client
2022-11-09 15:46:04 +00:00
@netbase.send_packet([NET_CTRLMSG_ACCEPT], 0, control: true, addr: packet.addr)
end
2022-11-08 15:20:46 +00:00
def on_packet(packet)
# process connless packets data
if packet.flags_control
on_ctrl_message(packet)
else # process non-connless packets
on_client_packet(packet)
end
end
def get_next_client_id
(0..MAX_CLIENTS).each do |i|
next if @clients[i]
return i
end
-1
end
2022-11-08 15:20:46 +00:00
def tick
begin
data, sender_inet_addr = @s.recvfrom_nonblock(1400)
2022-11-08 15:20:46 +00:00
rescue IO::EAGAINWaitReadable
data = nil
sender_inet_addr = nil
2022-11-08 15:20:46 +00:00
end
return unless data
packet = Packet.new(data, '<')
packet.addr.ip = sender_inet_addr[2] # or 3 idk bot 127.0.0.1 in my local test case
packet.addr.port = sender_inet_addr[1]
@clients.each do |id, client|
next unless packet.addr.eq(client.addr)
packet.client_id = id
end
2022-11-08 15:20:46 +00:00
puts packet.to_s if @verbose
on_packet(packet)
end
end