/* * OnClose gets called by the server at connection closing, once by * connection. This gives us the chance to unregister a new client and perform * client cleanup */ func (srv *Server) OnClose(c *network.Conn) { log.WithField("addr", c.GetRawConn().RemoteAddr()).Debug("Connection closed") // unregister the client before anything clientData := c.GetUserData().(ClientData) srv.clients.unregister(clientData.Id) if clientData.Joined { // client is still JOINED so that's a disconnection initiated externally // send a LEAVE to the rest of the world msg := messages.New(messages.LeaveId, messages.Leave{ Id: uint32(clientData.Id), Reason: "client disconnection", }) srv.Broadcast(msg) } else { // the client was not marked as JOINED, so nobody knows about him // and we have nothing more to do } if srv.playerLeftCb != nil { // raise 'player left' external callback srv.playerLeftCb(clientData.Id) } }
/* * register creates a new client, assigns it an id and returns it */ func (reg *ClientRegistry) register(client *network.Conn) uint32 { var clientId uint32 // protect: // - increment the next available id // - client map write reg.mutex.Lock() // we have a new client, assign him an id. clientId = reg.allocId() reg.clients[clientId] = client // record the client id inside the connection, this is needed for later // retriving the clientId when we just have a connection clientData := ClientData{ Id: clientId, Name: "", Joined: false, } client.SetUserData(clientData) // Note: for now we just stupidly increment the next available id. // We will have other problems to solve before this overflows... reg.mutex.Unlock() log.WithFields(log.Fields{ "client": clientData, "addr": client.GetRawConn().RemoteAddr(), }).Info("Accepted a new client") return clientId }
/* * OnIncomingMsg gets called by the server each time a message has been read * from the connection */ func (srv *Server) OnIncomingPacket(c *network.Conn, packet network.Packet) bool { clientData := c.GetUserData().(ClientData) raw := packet.(*messages.Message) log.WithFields( log.Fields{ "clientData": clientData, "addr": c.GetRawConn().RemoteAddr(), "type": raw.Type.String(), }).Debug("Incoming message") // decode the raw message msg := srv.factory.Decode(raw) // get handler handler, ok := srv.msgHandlers[raw.Type] if ok { if err := handler(c, msg); err != nil { log.WithError(err).Error("Error handling message") return false } } else { switch raw.Type { case messages.PingId: // immediately reply pong ping := msg.(messages.Ping) pong := messages.New(messages.PongId, messages.Pong{ Id: ping.Id, Tstamp: time.Now().UnixNano() / int64(time.Millisecond), }) if err := c.AsyncSendPacket(pong, time.Second); err != nil { log.WithError(err).Error("Error handling message") return false } case messages.JoinId: join := msg.(messages.Join) // JOIN is handled by the handshaker if srv.handshaker.Join(join, c) { // new client has been accepted if srv.playerJoinedCb != nil { // raise 'player joined' external callback srv.playerJoinedCb(clientData.Id, join.Type) } } } } return true }