예제 #1
0
/*
 * 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
}
예제 #2
0
/*
 * 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)
	}
}
예제 #3
0
/*
 * handleOperate processes a Operate message and fires a PlayerOperate event
 */
func (g *Game) handleOperate(c *network.Conn, msg interface{}) error {
	operate := msg.(messages.Operate)
	log.WithField("msg", operate).Info("Operate message")

	g.eventManager.PostEvent(
		events.NewEvent(events.PlayerOperateId,
			events.PlayerOperate{
				Id:       c.GetUserData().(protocol.ClientData).Id,
				EntityId: operate.Id,
			}))
	return nil
}
예제 #4
0
/*
 * handleAttack processes a Attack message and fires a PlayerAttack event
 */
func (g *Game) handleAttack(c *network.Conn, msg interface{}) error {
	attack := msg.(messages.Attack)
	log.WithField("msg", attack).Info("Attack message")

	g.eventManager.PostEvent(
		events.NewEvent(events.PlayerAttackId,
			events.PlayerAttack{
				Id:       c.GetUserData().(protocol.ClientData).Id,
				EntityId: attack.Id,
			}))
	return nil
}
예제 #5
0
/*
 * handleRepair processes a Repair message and fires a PlayerRepair event
 */
func (g *Game) handleRepair(c *network.Conn, msg interface{}) error {
	repair := msg.(messages.Repair)
	log.WithField("msg", repair).Info("Repair message")

	g.eventManager.PostEvent(
		events.NewEvent(events.PlayerRepairId,
			events.PlayerRepair{
				Id:         c.GetUserData().(protocol.ClientData).Id,
				BuildingId: repair.Id,
			}))
	return nil
}
예제 #6
0
/*
 * handleBuild processes a Build message and fires a PlayerBuild event
 */
func (g *Game) handleBuild(c *network.Conn, msg interface{}) error {
	build := msg.(messages.Build)
	log.WithField("msg", build).Info("Build message")

	g.eventManager.PostEvent(
		events.NewEvent(events.PlayerBuildId,
			events.PlayerBuild{
				Id:   c.GetUserData().(protocol.ClientData).Id,
				Type: build.Type,
				Xpos: build.Xpos, Ypos: build.Ypos,
			}))
	return nil
}
예제 #7
0
/*
 * handleMove processes a Move message and fires a PlayerMove event
 */
func (g *Game) handleMove(c *network.Conn, msg interface{}) error {
	move := msg.(messages.Move)
	log.WithField("msg", move).Info("Move message")

	g.eventManager.PostEvent(
		events.NewEvent(
			events.PlayerMoveId,
			events.PlayerMove{
				Id:   (c.GetUserData().(protocol.ClientData)).Id,
				Xpos: move.Xpos, Ypos: move.Ypos,
			}))
	return nil
}
예제 #8
0
/*
 * Leave sends a LEAVE message to the client associated to given connection
 */
func (reg *ClientRegistry) Leave(reason string, c *network.Conn) {
	clientData := c.GetUserData().(ClientData)

	// send LEAVE to client
	leave := messages.New(messages.LeaveId, messages.Leave{
		Id:     uint32(clientData.Id),
		Reason: reason,
	})
	if err := c.AsyncSendPacket(leave, 5*time.Millisecond); err != nil {
		// either the client received the LEAVE or not, we will close the
		// connection afterwards, so there's nothing more to do in order to
		// gracefully handle this error
		log.WithError(err).WithField("clientID", clientData.Id).Error("LEAVE message couldn't be sent")
	} else {
		log.WithField("clientID", clientData.Id).Info("LEAVE message has been sent")
	}

	// TODO: Remove this: the client should remain alive even when the connection
	// is closed. Example: when the lobby will be implemented

	// closes the connection, registry cleanup will be performed in OnClose
	go func() {
		time.Sleep(100 * time.Millisecond)
		if !c.IsClosed() {
			c.Close()
		}
	}()
}
예제 #9
0
/*
 * 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
}
예제 #10
0
func (reg *ClientRegistry) Join(join messages.Join, c *network.Conn) bool {
	clientData := c.GetUserData().(ClientData)

	log.WithFields(log.Fields{"name": join.Name, "clientData": clientData}).Info("Received JOIN from client")

	// client already JOINED?
	if clientData.Joined {
		reg.Leave("Joined already received", c)
		return false
	}

	// name length condition
	if len(join.Name) < 3 {
		reg.Leave("Name is too short", c)
		return false
	}

	// name already taken?
	nameTaken := false
	playerNames := make(map[uint32]string)

	// compute the list of joined players, populating the STAY message and
	// checking if name is taken as well
	reg.ForEach(func(cd ClientData) bool {
		nameTaken = cd.Name == join.Name
		playerNames[cd.Id] = cd.Name
		// stop iteration if name is taken
		return !nameTaken
	})

	if nameTaken {
		reg.Leave("Name is already taken", c)
		return false
	}

	// create and send STAY to the new client
	stay := messages.Stay{Id: clientData.Id, Players: playerNames}
	err := c.AsyncSendPacket(messages.New(messages.StayId, stay), time.Second)
	if err != nil {
		// handle error in case we couldn't send the STAY message
		log.WithError(err).Error("Couldn't send STAY message to the new client")
		reg.Leave("Couldn't finish handshaking", c)
		return false
	}

	// fill a JOINED message
	joined := &messages.Joined{
		Id:   clientData.Id,
		Name: join.Name,
		Type: join.Type,
	}

	log.WithField("joined", joined).Info("Tell to the world this client has joined")
	reg.Broadcast(messages.New(messages.JoinedId, joined))

	// at this point we consider the client as accepted
	clientData.Joined = true
	clientData.Name = join.Name
	c.SetUserData(clientData)
	return true
}