Start working on the server side
This commit is contained in:
parent
fff2e9efb0
commit
3a39d3f9f8
10
lib/net_addr.rb
Normal file
10
lib/net_addr.rb
Normal file
|
@ -0,0 +1,10 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
class NetAddr
|
||||
attr_accessor :ip, :port
|
||||
|
||||
def initialize(ip, port)
|
||||
@ip = ip
|
||||
@port = port
|
||||
end
|
||||
end
|
|
@ -6,14 +6,18 @@
|
|||
# Lowest network layer logic. Sends packets via udp.
|
||||
# Also adding the teeworlds protocol packet header.
|
||||
class NetBase
|
||||
attr_accessor :client_token, :server_token, :ack
|
||||
attr_accessor :peer_token, :ack
|
||||
|
||||
def initialize
|
||||
@ip = nil
|
||||
@port = nil
|
||||
@s = nil
|
||||
@ack = 0
|
||||
@server_token = [0xFF, 0xFF, 0xFF, 0xFF].map { |b| b.to_s(16) }.join
|
||||
@peer_token = [0xFF, 0xFF, 0xFF, 0xFF].map { |b| b.to_s(16) }.join
|
||||
end
|
||||
|
||||
def bind(socket)
|
||||
@s = socket
|
||||
end
|
||||
|
||||
def connect(socket, ip, port)
|
||||
|
@ -29,7 +33,7 @@ class NetBase
|
|||
# @param payload [Array] The Integer list representing the data after the header
|
||||
# @param num_chunks [Integer] Amount of NetChunks in the payload
|
||||
# @param flags [Hash] Packet header flags for more details check the class +PacketFlags+
|
||||
def send_packet(payload, num_chunks = 1, flags = {})
|
||||
def send_packet(payload, num_chunks = 1, opts = {})
|
||||
# unsigned char flags_ack; // 6bit flags, 2bit ack
|
||||
# unsigned char ack; // 8bit ack
|
||||
# unsigned char numchunks; // 8bit chunks
|
||||
|
@ -41,7 +45,7 @@ class NetBase
|
|||
# // TTTTTTTT
|
||||
# // TTTTTTTT
|
||||
# // TTTTTTTT
|
||||
flags_bits = PacketFlags.new(flags).bits
|
||||
flags_bits = PacketFlags.new(opts).bits
|
||||
# unused flags ack num chunks
|
||||
# ff ffff aa aaaa aaaa NNNN NNNN
|
||||
header_bits = "00#{flags_bits}#{@ack.to_s(2).rjust(10, '0')}#{num_chunks.to_s(2).rjust(8, '0')}"
|
||||
|
@ -50,10 +54,17 @@ class NetBase
|
|||
eight_bits.join.to_i(2)
|
||||
end
|
||||
|
||||
header += str_bytes(@server_token)
|
||||
header += str_bytes(@peer_token)
|
||||
data = (header + payload).pack('C*')
|
||||
@s.send(data, 0, @ip, @port)
|
||||
ip = @ip
|
||||
port = @port
|
||||
unless opts[:addr].nil?
|
||||
ip = opts[:addr].ip
|
||||
port = opts[:addr].port
|
||||
end
|
||||
puts "send to #{ip}:#{port}"
|
||||
@s.send(data, 0, ip, port)
|
||||
|
||||
puts Packet.new(data, '>').to_s if @verbose || flags[:test]
|
||||
puts Packet.new(data, '>').to_s if @verbose || opts[:test]
|
||||
end
|
||||
end
|
||||
|
|
|
@ -129,6 +129,11 @@ class Unpacker
|
|||
num = bits.join.to_i(2)
|
||||
sign == '1' ? -(num + 1) : num
|
||||
end
|
||||
|
||||
def get_raw(size = -1)
|
||||
# TODO: error if size exceeds @data.size
|
||||
@data.shift(size == -1 ? @data.size : size)
|
||||
end
|
||||
end
|
||||
|
||||
u = Unpacker.new(['01000000'.to_i(2)])
|
||||
|
|
|
@ -1,5 +1,7 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
require_relative 'net_addr'
|
||||
|
||||
require 'huffman_tw'
|
||||
|
||||
class PacketFlags
|
||||
|
@ -45,7 +47,7 @@ end
|
|||
|
||||
# Class holding the parsed packet data
|
||||
class Packet
|
||||
attr_reader :flags, :payload
|
||||
attr_reader :flags, :payload, :addr
|
||||
|
||||
def initialize(data, prefix = '')
|
||||
# @data and @payload
|
||||
|
@ -56,6 +58,7 @@ class Packet
|
|||
# use '>' and '<' for example to indicate
|
||||
# network direction (client/server)
|
||||
@prefix = prefix
|
||||
@addr = NetAddr.new(nil, nil)
|
||||
@huffman = Huffman.new
|
||||
@data = data
|
||||
flags_byte = @data[0].unpack('B*')
|
||||
|
|
95
lib/srv.rb
Executable file
95
lib/srv.rb
Executable file
|
@ -0,0 +1,95 @@
|
|||
#!/usr/bin/env ruby
|
||||
# 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'
|
||||
|
||||
class TeeworldsServer
|
||||
def initialize(options = {})
|
||||
@verbose = options[:verbose] || false
|
||||
@ip = '127.0.0.1'
|
||||
@port = 8303
|
||||
end
|
||||
|
||||
def run(ip, port)
|
||||
@server_token = (1..4).to_a.map { |_| rand(0..255) }
|
||||
@server_token = @server_token.map { |b| b.to_s(16) }.join
|
||||
puts "server token #{@server_token}"
|
||||
@netbase = NetBase.new
|
||||
NetChunk.reset
|
||||
@ip = ip
|
||||
@port = port
|
||||
puts "listening on #{@ip}:#{@port} .."
|
||||
@s = UDPSocket.new
|
||||
@s.bind(@ip, @port)
|
||||
@netbase.bind(@s)
|
||||
loop do
|
||||
tick
|
||||
end
|
||||
end
|
||||
|
||||
def on_client_packet(_packet)
|
||||
puts 'got client packet'
|
||||
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)
|
||||
else
|
||||
puts "Uknown control message #{msg}"
|
||||
exit(1)
|
||||
end
|
||||
end
|
||||
|
||||
def send_ctrl_with_token(addr, token)
|
||||
msg = [NET_CTRLMSG_TOKEN] + token
|
||||
@netbase.send_packet(msg, 0, control: true, addr:)
|
||||
end
|
||||
|
||||
def on_ctrl_token(packet)
|
||||
u = Unpacker.new(packet.payload[1..])
|
||||
token = u.get_raw(4)
|
||||
# puts "got token #{token.map { |b| b.to_s(16) }.join('')}"
|
||||
send_ctrl_with_token(packet.addr, token)
|
||||
end
|
||||
|
||||
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 tick
|
||||
begin
|
||||
data, client = @s.recvfrom_nonblock(1400)
|
||||
rescue IO::EAGAINWaitReadable
|
||||
data = nil
|
||||
client = nil
|
||||
end
|
||||
return unless data
|
||||
|
||||
packet = Packet.new(data, '<')
|
||||
packet.addr.ip = client[2] # or 3 idk bot 127.0.0.1 in my local test case
|
||||
packet.addr.port = client[1]
|
||||
puts packet.to_s if @verbose
|
||||
on_packet(packet)
|
||||
|
||||
# TODO: proper tick speed sleep
|
||||
sleep 0.001
|
||||
end
|
||||
end
|
|
@ -23,7 +23,6 @@ class TeeworldsClient
|
|||
@state = NET_CONNSTATE_OFFLINE
|
||||
@ip = 'localhost'
|
||||
@port = 8303
|
||||
@packet_flags = {}
|
||||
@hooks = {}
|
||||
@thread_running = false
|
||||
@signal_disconnect = false
|
||||
|
@ -108,7 +107,6 @@ class TeeworldsClient
|
|||
@client_token = @client_token.map { |b| b.to_s(16) }.join
|
||||
puts "client token #{@client_token}"
|
||||
@netbase = NetBase.new
|
||||
@netbase.client_token = @client_token
|
||||
NetChunk.reset
|
||||
@ip = ip
|
||||
@port = port
|
||||
|
@ -295,7 +293,7 @@ class TeeworldsClient
|
|||
|
||||
def on_msg_token(data)
|
||||
@token = bytes_to_str(data)
|
||||
@netbase.server_token = @token
|
||||
@netbase.peer_token = @token
|
||||
puts "Got token #{@token}"
|
||||
send_msg_connect
|
||||
end
|
||||
|
|
Loading…
Reference in a new issue