-
Notifications
You must be signed in to change notification settings - Fork 1
/
Copy pathserver.go
216 lines (191 loc) · 4.12 KB
/
server.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
package main
import (
"flag"
"fmt"
"log"
"math/rand"
"net"
"sync"
"time"
)
/*
map from token to the connection of the room
*/
var tokenToConn map[string]net.Conn = make(map[string]net.Conn)
var tokenGenMutex sync.Mutex
const (
connType = "tcp"
maxTimePerTurnSeconds = 60
)
var connPort string
func init() {
rand.Seed(time.Now().UnixNano())
cmd := flag.String("port", "12345", "port on which to run server")
flag.Parse()
connPort = *cmd
}
//generateToken generates a unique token to be used for a game room identifier
func generateToken(conn net.Conn) string {
tokenGenMutex.Lock()
var tok string
for {
token := ""
for i := 0; i < 5; i++ {
token += string(rune(rand.Intn(26) + 'a'))
}
if _, isDup := tokenToConn[token]; !isDup {
tok = token
break
}
}
tokenToConn[tok] = conn
tokenGenMutex.Unlock()
return tok
}
/*
connecion that are unused and must be closed
*/
var toClose chan net.Conn = make(chan net.Conn, 128)
func start() {
var quickOpponent net.Conn
//those who connect tot a room with a token
connectors := make(chan net.Conn, 128)
//those who created a room and wait for ssomeone to connect to them
waiters := make(chan net.Conn, 128)
//those who wait for a random player to connect to them
quick := make(chan net.Conn, 128)
// Start the server and listen for incoming connections.
listener, err := net.Listen("tcp", ":"+connPort)
if err != nil {
fmt.Println("Problem in creating tcp socket")
log.Fatal(err)
}
fmt.Println("listening on port " + connPort)
go func() {
for {
// Listen for an incoming connection.
conn, err := listener.Accept()
if err != nil {
fmt.Println("error on accepting connection")
continue
}
fmt.Println("Client " + conn.RemoteAddr().String() + " connected.")
var playerType string
fmt.Fscan(conn, &playerType)
if playerType == "connect" {
connectors <- conn
} else if playerType == "wait" {
waiters <- conn
} else if playerType == "quick" {
quick <- conn
}
}
}()
var mutex sync.Mutex
for {
select {
case conn := <-connectors:
go func() {
opponentToken := ""
_, err := fmt.Fscan(conn, &opponentToken)
if err != nil {
toClose <- conn
}
var connectTo net.Conn
ok := false
// check if conn is in map, synchronized so we dont get two player to connect to one
mutex.Lock()
if connectTo, ok = tokenToConn[opponentToken]; ok {
delete(tokenToConn, opponentToken)
}
mutex.Unlock()
if ok {
startGame(conn, connectTo)
} else {
fmt.Fprintf(conn, "wrong_token\n")
toClose <- conn
}
}()
case conn := <-waiters:
go func() {
token := generateToken(conn)
_, err := fmt.Fprintf(conn, "%s\n", token)
if err != nil {
toClose <- conn
}
}()
case conn := <-quick:
if quickOpponent == nil {
quickOpponent = conn
} else {
go startGame(conn, quickOpponent)
quickOpponent = nil
}
case conn := <-toClose:
go func() {
conn.Close()
fmt.Println("Client " + conn.RemoteAddr().String() + " disconnected.")
}()
}
}
}
func main() {
start()
}
//sendMsg - reads a string from the connection "from" and sent it to "to"
//if it takes more than 60 seconds return false. if the msg is "end" it means the game has end
//therefore return false
func sendMsg(from, to net.Conn) bool {
var msg string
c := make(chan bool)
go func() {
_, err := fmt.Fscan(from, &msg)
if err != nil {
c <- false
}
_, err = fmt.Fprintf(to, "%s\n", msg)
if err != nil {
c <- false
}
if msg == "end" {
c <- false
} else {
c <- true
}
}()
select {
case ok := <-c:
return ok
case <-time.After(maxTimePerTurnSeconds * time.Second):
return false
}
return false
}
func startGame(conn1, conn2 net.Conn) {
defer func() {
toClose <- conn1
toClose <- conn2
}()
_, err2 := fmt.Fprintf(conn2, "second\n")
if err2 != nil {
fmt.Println(conn1, "error")
return
}
_, err1 := fmt.Fprintf(conn1, "first\n")
if err1 != nil {
fmt.Println(conn2, "error")
return
}
go func() {
for {
if !sendMsg(conn1, conn2) {
return
}
}
}()
for {
if !sendMsg(conn2, conn1) {
return
}
}
}