Class: Chingu::GameStates::NetworkServer
- Inherits:
-
NetworkState
- Object
- Chingu::GameState
- NetworkState
- Chingu::GameStates::NetworkServer
- Defined in:
- lib/chingu/game_states/network_server.rb
Overview
The following callbacks can be overwritten to add your game logic:
on_connect(socket) # when the TCP connection to the server is opened
on_disconnect(socket) # when server dies or disconnects you
on_data(socket, data) # when raw data arrives from server, if not overloaded this will unpack and call on_msg
on_msg(socket, msg) # an incoming msgs, could be a ruby hash or array or whatever datastructure you've chosen to send from server
on_start # called when socket is listening and ready
on_start_error(msg) # callback for any error during server setup process
Usage:
ServerState < Chingu::GameStates::NetworkServer
# incoming client/connection...
def on_connect(socket)
send_msg(:cmd => :ping, :timestamp => Gosu::milliseconds)
end
def on_msg(socket, msg)
if msg[:cmd] == :pong
latency = Gosu::milliseconds - msg[:timestamp]
puts "Server/Client roundtrip #{latency}ms"
end
end
end
push_game_state NetworkServer.new(:address => "127.0.0.1", :port => 7778).start
NetworkServer works mostly like NetworkClient with a few differences
- since a server handles many sockets (1 for each connected client) all callbacks first argument is 'socket'
- same with outgoing packets, #send_data and #send_msg, first argument is a socket.
A good idea is to have a socket-ivar in your Player-model and a Player.find_by_socket(socket)
Constant Summary
Constants inherited from NetworkState
Chingu::GameStates::NetworkState::DEFAULT_PORT, Chingu::GameStates::NetworkState::PACKET_HEADER_FORMAT, Chingu::GameStates::NetworkState::PACKET_HEADER_LENGTH
Instance Attribute Summary collapse
-
#max_connections ⇒ Object
readonly
Returns the value of attribute max_connections.
-
#socket ⇒ Object
readonly
Returns the value of attribute socket.
-
#sockets ⇒ Object
readonly
Returns the value of attribute sockets.
Attributes inherited from NetworkState
#address, #bytes_received, #bytes_sent, #packets_received, #packets_sent, #port
Attributes inherited from Chingu::GameState
#game_objects, #game_state_manager, #options, #previous_game_state
Attributes included from Helpers::InputDispatcher
Attributes included from Helpers::GameObject
Instance Method Summary collapse
-
#broadcast_msg(msg) ⇒ Object
Broadcast 'msg' to all connected clients.
-
#disconnect_client(socket) ⇒ Object
Shuts down all communication (closes socket) with a specific socket.
-
#flush ⇒ Object
Ensure that the buffer is cleared of data to write (call at the end of update or, at least after all sends).
- #handle_incoming_connections ⇒ Object
-
#handle_incoming_data(max_size = @max_read_per_update) ⇒ Object
Call this from your update() to read from socket.
-
#initialize(options = {}) ⇒ NetworkServer
constructor
A new instance of NetworkServer.
-
#on_connect(socket) ⇒ Object
on_connect will be called when client successfully makes a connection to server.
-
#on_data(socket, data) ⇒ Object
on_data(data) will be called from handle_incoming_data() by default.
-
#on_disconnect(socket) ⇒ Object
on_disconnect will be called when server disconnects client for whatever reason.
-
#on_msg(socket, packet) ⇒ Object
Handler when message packets are received.
-
#on_start ⇒ Object
Callback for when Socket listens correctly on given host/port.
-
#on_start_error(msg) ⇒ Object
Callback for when something goes wrong with startup (when making TCP socket listen to a port).
-
#send_data(socket, data) ⇒ Object
Send raw 'data' to the 'socket' Returns amount of data sent, including headers.
-
#send_msg(socket, msg) ⇒ Object
Send 'msg' to a specific client 'socket'.
-
#start(address = nil, port = nil) ⇒ Object
Start server.
-
#stop ⇒ Object
(also: #close)
Stops server.
-
#update ⇒ Object
Default network loop: 1) Save incoming connections with #handle_incoming_connections 2) read raw data from server with #handle_incoming_data 3) #handle_incoming_data call #on_data(data) 4) #on_data(data) will call #on_msgs(msg) 5) send all buffered broadcast data in one fell swoop.
Methods inherited from NetworkState
Methods inherited from Chingu::GameState
#button_down, #button_up, #close_game, #draw, #draw_trait, #filename, #setup, #setup_trait, #to_s, #to_sym, trait, #trait_options, traits, #update_trait
Methods included from Helpers::ClassInheritableAccessor
Methods included from Helpers::InputClient
#add_inputs, #holding?, #holding_all?, #holding_any?, #input, #input=, #on_input
Methods included from Helpers::InputDispatcher
#add_input_client, #dispatch_button_down, #dispatch_button_up, #dispatch_input_for, #remove_input_client
Methods included from Helpers::GameObject
#game_objects_of_class, #load_game_objects, #save_game_objects
Methods included from Helpers::GFX
#draw_arc, #draw_circle, #draw_rect, #fill, #fill_arc, #fill_circle, #fill_gradient, #fill_rect
Constructor Details
#initialize(options = {}) ⇒ NetworkServer
Returns a new instance of NetworkServer.
74 75 76 77 78 79 80 81 82 83 |
# File 'lib/chingu/game_states/network_server.rb', line 74 def initialize( = {}) super() @max_read_per_update = [:max_read_per_update] || 20000 @max_connections = [:max_connections] || 256 @socket = nil @sockets = [] @packet_buffers = Hash.new end |
Instance Attribute Details
#max_connections ⇒ Object (readonly)
Returns the value of attribute max_connections
72 73 74 |
# File 'lib/chingu/game_states/network_server.rb', line 72 def max_connections @max_connections end |
#socket ⇒ Object (readonly)
Returns the value of attribute socket
72 73 74 |
# File 'lib/chingu/game_states/network_server.rb', line 72 def socket @socket end |
#sockets ⇒ Object (readonly)
Returns the value of attribute sockets
72 73 74 |
# File 'lib/chingu/game_states/network_server.rb', line 72 def sockets @sockets end |
Instance Method Details
#broadcast_msg(msg) ⇒ Object
Broadcast 'msg' to all connected clients. Returns amount of data sent.
207 208 209 210 |
# File 'lib/chingu/game_states/network_server.rb', line 207 def broadcast_msg(msg) data = Marshal.dump(msg) @sockets.inject(0) {|tot, s| tot + send_data(s, data) } end |
#disconnect_client(socket) ⇒ Object
Shuts down all communication (closes socket) with a specific socket
236 237 238 239 240 241 242 243 |
# File 'lib/chingu/game_states/network_server.rb', line 236 def disconnect_client(socket) socket.close rescue Errno::ENOTCONN ensure @sockets.delete socket @packet_buffers.delete socket on_disconnect(socket) end |
#flush ⇒ Object
Ensure that the buffer is cleared of data to write (call at the end of update or, at least after all sends).
246 247 248 249 250 251 252 253 254 |
# File 'lib/chingu/game_states/network_server.rb', line 246 def flush @sockets.each do |socket| begin socket.flush rescue IOError disconnect_client(socket) end end end |
#handle_incoming_connections ⇒ Object
150 151 152 153 154 155 156 157 158 159 |
# File 'lib/chingu/game_states/network_server.rb', line 150 def handle_incoming_connections begin while @sockets.size < @max_connections and @socket and socket = @socket.accept_nonblock @sockets << socket @packet_buffers[socket] = PacketBuffer.new on_connect(socket) end rescue IO::WaitReadable, Errno::EAGAIN, Errno::EINTR end end |
#handle_incoming_data(max_size = @max_read_per_update) ⇒ Object
Call this from your update() to read from socket. handle_incoming_data will call on_data(raw_data) when stuff comes on on the socket.
165 166 167 168 169 170 171 172 173 174 175 176 |
# File 'lib/chingu/game_states/network_server.rb', line 165 def handle_incoming_data(max_size = @max_read_per_update) @sockets.each do |socket| if IO.select([socket], nil, nil, 0.0) begin packet, sender = socket.recvfrom(max_size) on_data(socket, packet) rescue Errno::ECONNABORTED, Errno::ECONNRESET, IOError disconnect_client(socket) end end end end |
#on_connect(socket) ⇒ Object
on_connect will be called when client successfully makes a connection to server
139 140 141 |
# File 'lib/chingu/game_states/network_server.rb', line 139 def on_connect(socket) puts "[Client Connected: #{socket}]" if @debug end |
#on_data(socket, data) ⇒ Object
on_data(data) will be called from handle_incoming_data() by default.
181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 |
# File 'lib/chingu/game_states/network_server.rb', line 181 def on_data(socket, data) buffer = @packet_buffers[socket] buffer.buffer_data data @bytes_received += data.length while packet = buffer.next_packet @packets_received += 1 begin on_msg(socket, Marshal.load(packet)) rescue TypeError disconnect_client(socket) break end end end |
#on_disconnect(socket) ⇒ Object
on_disconnect will be called when server disconnects client for whatever reason
146 147 148 |
# File 'lib/chingu/game_states/network_server.rb', line 146 def on_disconnect(socket) puts "[Client Disconnected: #{socket}]" if @debug end |
#on_msg(socket, packet) ⇒ Object
Handler when message packets are received. Should be overriden in your code.
200 201 202 |
# File 'lib/chingu/game_states/network_server.rb', line 200 def on_msg(socket, packet) # should be overridden. end |
#on_start ⇒ Object
Callback for when Socket listens correctly on given host/port
104 105 106 |
# File 'lib/chingu/game_states/network_server.rb', line 104 def on_start puts "* Server listening on #{address}:#{port}" if @debug end |
#on_start_error(msg) ⇒ Object
Callback for when something goes wrong with startup (when making TCP socket listen to a port)
111 112 113 114 115 116 |
# File 'lib/chingu/game_states/network_server.rb', line 111 def on_start_error(msg) if @debug puts "Can't start server on #{address}:#{port}:\n" puts msg end end |
#send_data(socket, data) ⇒ Object
Send raw 'data' to the 'socket' Returns amount of data sent, including headers.
222 223 224 225 226 227 228 229 230 231 |
# File 'lib/chingu/game_states/network_server.rb', line 222 def send_data(socket, data) length = socket.write([data.length].pack(PACKET_HEADER_FORMAT)) length += socket.write(data) @packets_sent += 1 @bytes_sent += length length rescue Errno::ECONNABORTED, Errno::ECONNRESET, Errno::EPIPE, Errno::ENOTCONN disconnect_client(socket) 0 end |
#send_msg(socket, msg) ⇒ Object
Send 'msg' to a specific client 'socket'. Returns amount of data sent.
215 216 217 |
# File 'lib/chingu/game_states/network_server.rb', line 215 def send_msg(socket, msg) send_data(socket, Marshal.dump(msg)) end |
#start(address = nil, port = nil) ⇒ Object
Start server
88 89 90 91 92 93 94 95 96 97 98 99 |
# File 'lib/chingu/game_states/network_server.rb', line 88 def start(address = nil, port = nil) @address = address if address @port = port if port begin @socket = TCPServer.new(@address, @port) on_start rescue on_start_error($!) end return self end |
#stop ⇒ Object Also known as: close
Stops server
259 260 261 262 263 264 265 266 267 268 |
# File 'lib/chingu/game_states/network_server.rb', line 259 def stop begin @socket.close if @socket and not @socket.closed? rescue Errno::ENOTCONN end @socket = nil @sockets.each {|socket| disconnect_client(socket) } @sockets = [] end |
#update ⇒ Object
Default network loop: 1) Save incoming connections with #handle_incoming_connections 2) read raw data from server with #handle_incoming_data 3) #handle_incoming_data call #on_data(data) 4) #on_data(data) will call #on_msgs(msg) 5) send all buffered broadcast data in one fell swoop
127 128 129 130 131 132 133 134 |
# File 'lib/chingu/game_states/network_server.rb', line 127 def update if @socket && !@socket.closed? handle_incoming_connections handle_incoming_data end super end |