58555aec0d118fb26c221714c0e5f8b1bae3d88a
[nit.git] / contrib / tinks / src / server / server.nit
1 # This file is part of NIT ( http://www.nitlanguage.org ).
2 #
3 # Licensed under the Apache License, Version 2.0 (the "License");
4 # you may not use this file except in compliance with the License.
5 # You may obtain a copy of the License at
6 #
7 # http://www.apache.org/licenses/LICENSE-2.0
8 #
9 # Unless required by applicable law or agreed to in writing, software
10 # distributed under the License is distributed on an "AS IS" BASIS,
11 # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 # See the License for the specific language governing permissions and
13 # limitations under the License.
14
15 # Server to host multiplayer games
16 module server
17
18 import gamnit::network
19
20 import game
21 import common
22
23 redef class RemoteClient
24 # `Player` associated to this client
25 var player = new Player
26 end
27
28 redef class Server
29 # `UDPSocket` to which clients send discovery requests
30 var discovery_socket: UDPSocket do
31 var s = new UDPSocket
32 s.blocking = false
33 s.bind(null, discovery_port)
34 return s
35 end
36
37 # The current game
38 var game = new TGame is lazy, writable
39
40 # Is this a dedicated server
41 var dedicated = false
42
43 # Create and run a new `Game`
44 fun run_dedicated
45 do
46 dedicated = true
47
48 # Setup game
49 print "Server: Setup game"
50 game
51
52 # Play
53 print "Server: Starting play"
54 loop do_turn
55 end
56
57 # Run the server logic over a single turn
58 fun do_turn: TTurn
59 do
60 var game = game
61
62 # Do game logic
63 var turn = game.do_turn
64
65 # Respond to discovery requests
66 loop
67 var ptr = new Ref[nullable SocketAddress](null)
68 var read = discovery_socket.recv_from(1024, ptr)
69
70 # No sender means there is no request (an error would also do it)
71 var sender = ptr.item
72 if sender == null then break
73
74 var words = read.split(" ")
75 if words.length != 2 or words[0] != "Server?" or words[1] != handshake_app_name then
76 print "Server Warning: Rejected discovery request '{read}'"
77 continue
78 end
79
80 discovery_socket.send_to(sender.address, sender.port,
81 "Server! {handshake_app_name} {self.port}")
82 end
83
84 # Setup clients
85 var new_clients = accept_clients
86 for client in new_clients do
87 # Register player and spawn first tank
88 game.players.add client.player
89 turn.spawn_tank client.player
90
91 client.writer.serialize game
92 client.writer.serialize client.player
93 client.socket.flush
94
95 clients.add client
96 end
97
98 if dedicated and clients.is_empty then
99 # No clients, sleep for a while
100 nanosleep(0, 10000000)
101 return turn
102 end
103
104 # Update clients
105 broadcast turn
106
107 # Get orders from players
108 var clients_to_remove = new Array[RemoteClient]
109 for client in clients do
110 if not client.socket.poll_in then continue
111
112 var orders = client.reader.deserialize
113 var errors = client.reader.errors
114 if errors.not_empty then
115 print_error "Comm Error: (Dropping client) {errors.join(", ")}"
116 clients_to_remove.add client
117 else if not orders isa Array[TOrder] then
118 if orders == null then
119 print_error "Comm Error: (Dropping client) Unexpected null"
120 else print_error "Comm Error: (Dropping client) Unexpected {orders.class_name}"
121 # TODO remove code duplication when we have ? or an equivalent
122
123 clients_to_remove.add client
124 else
125 client.player.orders.add_all orders
126 end
127 end
128
129 for client in clients_to_remove do clients.remove client
130
131 return turn
132 end
133 end