gamnit :: Server :: defaultinit
# Game server controller
class Server
# Port for the `listening_socket`
var port: Int
# All connected `RemoteClient`
var clients = new Array[RemoteClient]
# TCP socket accepting new connections
#
# Opened on the first call to `accept_clients`.
var listening_socket: TCPServer is lazy do
var socket = new TCPServer(port)
socket.listen 8
socket.blocking = false
return socket
end
# Accept currently waiting clients and return them as an array
#
# If `add_to_clients`, the default, the new clients are added to `clients`.
# Otherwise, the return value of `accept_clients` may be added to `clients`
# explicitly by the caller after an extra verification or sorting.
fun accept_clients(add_to_clients: nullable Bool): Array[RemoteClient]
do
add_to_clients = add_to_clients or else true
assert not listening_socket.closed
var new_clients = new Array[RemoteClient]
loop
var client_socket = listening_socket.accept
if client_socket == null then break
var rc = new RemoteClient(client_socket)
var handshake_success = rc.handshake
if handshake_success then
new_clients.add rc
print "Server: Client at {client_socket.address} passed the handshake"
else
print_error "Server Error: Client at {client_socket.address} failed the handshake"
client_socket.close
end
end
if add_to_clients then clients.add_all new_clients
return new_clients
end
# Broadcast a `message` to all `clients`, then flush the connection
#
# The client `except` is skipped and will not receive the `message`.
fun broadcast(message: Serializable, except: nullable RemoteClient)
do
for client in clients do if client != except then
client.writer.serialize(message)
client.socket.flush
end
end
# Respond to pending discovery requests by sending the TCP listening address and port
#
# Returns the number of valid requests received.
#
# The response messages includes the TCP listening address and port
# for remote clients to connect with TCP using `connect`.
# These connections are accepted by the server with `accept_clients`.
fun answer_discovery_requests: Int
do
var count = 0
loop
var ptr = new Ref[nullable SocketAddress](null)
var read = discovery_socket.recv_from(1024, ptr)
# No sender means there is no discovery request
var sender = ptr.item
if sender == null then break
var words = read.split(" ")
if words.length != 2 or words[0] != discovery_request_message or words[1] != handshake_app_name then
print "Server Warning: Rejected discovery request '{read}' from {sender.address}:{sender.port}"
continue
end
var msg = "{discovery_response_message} {handshake_app_name} {self.port}"
discovery_socket.send_to(sender.address, sender.port, msg)
count += 1
end
return count
end
# UDP socket responding to discovery requests
#
# Usually opened on the first call to `answer_discovery_request`.
var discovery_socket: UDPSocket is lazy do
var s = new UDPSocket
s.blocking = false
s.bind(null, discovery_port)
return s
end
end
lib/gamnit/network/server.nit:50,1--152,3