class ServerCore
ServerCore: should only contain the networking and no gamelogic
Public Class Methods
new()
click to toggle source
# File lib/server/chichilku3_server.rb, line 18 def initialize # single dimension array holding player objects @players = [] # multi dimensional array # 0 - client network socket # 1 - player id @clients = [] @current_id = 0 @tick = 0 @last_alive_pck_by_client = Time.now @console = Console.new @cfg = ServerCfg.new(@console, "server.json") @gamelogic = GameLogic.new(@console) @global_pack = nil end
Public Instance Methods
add_player(name, version, client, ip)
click to toggle source
# File lib/server/chichilku3_server.rb, line 77 def add_player(name, version, client, ip) @current_id = get_free_id() return -1 if @current_id > MAX_CLIENTS || @current_id < 1 @console.log "Added player id='#{@current_id}' version='#{version}' ip='#{ip}'" @players << Player.new(@current_id, 0, nil, nil, name, version, ip) client[PLAYER_ID] = @current_id @current_id # implicit return end
client_by_playerid(player_id)
click to toggle source
# File lib/server/chichilku3_server.rb, line 237 def client_by_playerid(player_id) @clients.find do |client| client[PLAYER_ID] == player_id end end
client_tick(cli, dt)
click to toggle source
TODO: this func and it dependencies should be new file Handles each client
# File lib/server/chichilku3_server.rb, line 204 def client_tick(cli, dt) client_data = save_read(cli[NET_CLIENT], CLIENT_PACKAGE_LEN) if client_data == '' # diff = Time.now - @last_alive_pck_by_client # if (diff > MAX_TIMEOUT) # @console.log "sombody timed out" # end return end @console.dbg "tick recv: '#{client_data}'" @last_alive_pck_by_client = Time.now port, ip = Socket.unpack_sockaddr_in(cli[NET_CLIENT].getpeername) server_response = handle_client_data(cli, client_data, ip, dt) pck_type = server_response[0] if pck_type == SERVER_PCK_TYPE[:error] disconnect_client(cli, server_response) else net_write(server_response, cli[NET_CLIENT]) end end
command_package(data, client)
click to toggle source
# File lib/server/chichilku3_server.rb, line 129 def command_package(data, client) id = data[0..1].to_i(16) cmd = data[1..-1] @console.log "[chat] ID=#{id} command='#{cmd}'" msg = "server_recived_cmd: #{cmd}" msg = msg.ljust(SERVER_PACKAGE_LEN - 2, '0') msg = msg[0..SERVER_PACKAGE_LEN - CMD_LEN] if cmd == "test" # return "0l#{NET_ERR_DISCONNECT} SAMPLE MESSAGE " msg = "id=#{client[PLAYER_ID]}" end msg = msg.ljust(SERVER_PACKAGE_LEN - 2, ' ') msg = msg[0..SERVER_PACKAGE_LEN - 2] # protocol 4 (chat command) "4l#{msg}" end
create_name_package(data, client)
click to toggle source
# File lib/server/chichilku3_server.rb, line 49 def create_name_package(data, client) if !client.nil? && !data.nil? player = Player.get_player_by_id(@players, client[PLAYER_ID]) player.set_name(data) end # protocol 3 name prot # gamestate # | pck = "3l#{net_pack_int(@players.count)}g" @players.each do |p| pck += p.to_n_pck @console.dbg "pname='#{p.name}'" end pck.ljust(SERVER_PACKAGE_LEN, '0') end
delete_player(id)
click to toggle source
# File lib/server/chichilku3_server.rb, line 87 def delete_player(id) @players.delete(Player.get_player_by_id(@players, id)) end
disconnect_client(client, server_response = nil)
click to toggle source
# File lib/server/chichilku3_server.rb, line 226 def disconnect_client(client, server_response = nil) player_id = client[PLAYER_ID] @console.log "player id=#{player_id} left the game." if player_id != -1 @console.dbg "client disconnected.#{" (" + server_response + ")" unless server_response.nil?}" net_write(server_response, client[NET_CLIENT]) unless server_response.nil? client[NET_CLIENT].close delete_player(player_id) @clients.delete(client) @current_id -= 1 end
get_free_id()
click to toggle source
# File lib/server/chichilku3_server.rb, line 66 def get_free_id # TODO: do this smarter used_ids = @clients.map{ |c| c[1] } id = 0 while id < MAX_CLIENTS do id += 1 return id unless used_ids.include? id end -1 end
handle_client_data(client, data, ip, dt)
click to toggle source
# File lib/server/chichilku3_server.rb, line 172 def handle_client_data(client, data, ip, dt) response = handle_protocol(client, data[0].to_i, data[1], data[2..-1], ip, dt) # the response is a direct respond to an protocol # everything above this could override important responds # like id assignment # every think that is after this guad case just overrides update pcks return response unless response.nil? if (@tick % 100).zero? return create_name_package(nil, nil) end # some debug suff for class vars # if (@tick % 50).zero? # puts "" # @console.log "id=#{data[0].to_i} currentid=#{@current_id}" # end # if @global_pack.nil? # @global_pack = nil # @console.log "sending an global pck" # return "5l#{players_to_packet}" # end # if error occurs or something unexpected # just send regular update pck # protocol 1 (update) "1l#{players_to_packet}" # implicit return end
handle_protocol(client, protocol, p_status, data, ip, dt)
click to toggle source
# File lib/server/chichilku3_server.rb, line 146 def handle_protocol(client, protocol, p_status, data, ip, dt) @console.dbg "HANDLE PROTOCOL=#{protocol} status=#{p_status}" if protocol.zero? # error pck @console.log "Error pck=#{data}" elsif protocol == 1 # id pck return id_pck(data, client, ip) elsif protocol == 3 # initial request names return create_name_package(data, client) else # all other types require id id = data[0].to_i(16) if id != client[PLAYER_ID] @console.log("id=#{client[PLAYER_ID]} tried to spoof id=#{id} ip=#{ip}") disconnect_client(client, "0l#{NET_ERR_DISCONNECT}invalid player id ") return nil end if protocol == 2 # update pck return update_pck(data, dt) elsif protocol == 4 # command return command_package(data, client) else @console.log "ERROR unkown protocol=#{protocol} data=#{data}" end end end
id_pck(data, client, ip)
click to toggle source
# File lib/server/chichilku3_server.rb, line 108 def id_pck(data, client, ip) player_version = data[0..3] id = add_player("(connecting)", player_version, client, ip) if id == -1 @console.log "'#{name}' failed to connect (server full)" # protocol 0 (error) code=404 slot not found return "0l#{NET_ERR_FULL} " end if player_version.to_i < GAME_VERSION.to_i @console.log "IP='#{ip}' failed to connect (client too old '#{player_version}' < '#{GAME_VERSION}')" return "0l#{NET_ERR_CLIENT_OUTDATED}#{GAME_VERSION}".ljust(SERVER_PACKAGE_LEN, ' ') elsif player_version.to_i > GAME_VERSION.to_i @console.log "IP='#{ip}' failed to connect (client too new '#{player_version}' < '#{GAME_VERSION}')" return "0l#{NET_ERR_SERVER_OUTDATED}#{GAME_VERSION}".ljust(SERVER_PACKAGE_LEN, ' ') end @console.log "id='#{id}' ip='#{ip}' joined the game" @global_pack = "true" # protocol 2 (id) "2l#{net_pack_int(@players.count)}#{net_pack_int(MAX_CLIENTS)}#{id.to_s(16)}X#{GAME_VERSION}".ljust(SERVER_PACKAGE_LEN, '0') end
parse_client_version(data)
click to toggle source
# File lib/server/chichilku3_server.rb, line 34 def parse_client_version(data) return if data.nil? id = data[0].to_i(16) version = data[1..4] player = Player.get_player_by_id(@players, id) if player @console.log "name req id='#{id}' vrs='#{version}' name='#{player.name}'" player.set_version(version) else @console.log "error parsing version data=#{data}" end player end
players_to_packet()
click to toggle source
# File lib/server/chichilku3_server.rb, line 91 def players_to_packet # player count packet = net_pack_int(@players.empty? ? 0 : @players.count) packet += 'g' # gamestate @players.each do |player| packet += player.to_s end packet end
run()
click to toggle source
# File lib/server/chichilku3_server.rb, line 243 def run server = TCPServer.open(@cfg.data['port']) server.setsockopt(Socket::IPPROTO_TCP, Socket::TCP_NODELAY, 1) # nagle's algorithm Thread.new do accept(server) end loop do diff = 0 # TODO: unused lmao traced it through the half source t = Time.now if $next_tick > t sleep $next_tick - t else unless @tick.zero? @console.log "[WARNING] tick took #{t - $next_tick} too long" end end $next_tick = Time.now + MAX_TICK_SPEED @tick += 1 @players = @gamelogic.tick(@players, diff) # there is no gurantee the client will tick here # there might be 2 gamelogic ticks and posticks # before the server recieves client data # since it is a nonblocking read and server/client are not in perfect sync @clients.each do |client| begin client_tick(client, diff) rescue Errno::ECONNRESET, Errno::ENOTCONN, EOFError, IOError disconnect_client(client) end end @players = @gamelogic.posttick(@players, diff) end end
update_pck(data, dt)
click to toggle source
# File lib/server/chichilku3_server.rb, line 101 def update_pck(data, dt) id = data[0].to_i(16) @console.dbg "got player with id: #{id}" @players = @gamelogic.handle_client_requests(data[1..-1], id, @players, dt) nil # defaults to normal update pck end
Private Instance Methods
accept(server)
click to toggle source
# File lib/server/chichilku3_server.rb, line 279 def accept(server) Socket.accept_loop(server) do |client| @clients << [client, -1] @console.log "client connected. clients connected: #{@clients.count}" end end
net_write(data, cli)
click to toggle source
# File lib/server/chichilku3_server.rb, line 286 def net_write(data, cli) @console.dbg("sending: #{data}") cli.write(data) end