Exemple #1
0
// Negotiate a new player client login. This function runs in a new goroutine
// for each player connection and therefore should not attempt to alter the
// game structure without enqueue().
func (game *Game) login(conn net.Conn) {
	// TODO: This function should access game in a thread-safe way for reading
	var err, clientErr os.Error

	defer func() {
		if err != nil {
			log.Print(err.String())
			if clientErr == nil {
				clientErr = os.NewError("Server error.")
			}
			proto.WriteDisconnect(conn, clientErr.String())
			conn.Close()
		}
	}()

	var username string
	if username, err = proto.ServerReadHandshake(conn); err != nil {
		clientErr = os.NewError("Handshake error.")
		return
	}

	if !validPlayerUsername.MatchString(username) {
		err = os.NewError("Bad username")
		clientErr = err
		return
	}

	log.Print("Client ", conn.RemoteAddr(), " connected as ", username)

	if game.UnderMaintenanceMsg != "" {
		err = fmt.Errorf("Server under maintenance, kicking player: %q", username)
		clientErr = os.NewError(game.UnderMaintenanceMsg)
		return
	}

	// Load player permissions.
	permissions := gamerules.Permissions.UserPermissions(username)
	if !permissions.Has("login") {
		err = fmt.Errorf("Player %q does not have login permission", username)
		clientErr = os.NewError("You do not have access to this server.")
		return
	}

	if err = proto.ServerWriteHandshake(conn, game.serverId); err != nil {
		clientErr = os.NewError("Handshake error.")
		return
	}

	if game.serverId != "-" {
		var authenticated bool
		authserver := &server_auth.ServerAuth{"http://www.minecraft.net/game/checkserver.jsp"}
		authenticated, err = authserver.Authenticate(game.serverId, username)
		if !authenticated || err != nil {
			var reason string
			if err != nil {
				reason = "Authentication check failed: " + err.String()
			} else {
				reason = "Failed authentication"
			}
			err = fmt.Errorf("Client %v: %s", conn.RemoteAddr(), reason)
			clientErr = os.NewError(reason)
			return
		}
		log.Print("Client ", conn.RemoteAddr(), " passed minecraft.net authentication")
	}

	if _, err = proto.ServerReadLogin(conn); err != nil {
		clientErr = os.NewError("Login error.")
		return
	}

	entityId := game.entityManager.NewEntity()

	var playerData nbt.ITag
	if playerData, err = game.worldStore.PlayerData(username); err != nil {
		clientErr = os.NewError("Error reading user data. Please contact the server administrator.")
		return
	}

	player := player.NewPlayer(entityId, game.shardManager, conn, username, game.worldStore.SpawnPosition, game.playerDisconnect, game)
	if playerData != nil {
		if err = player.ReadNbt(playerData); err != nil {
			// Don't let the player log in, as they will only have default inventory
			// etc., which could lose items from them. Better for an administrator to
			// sort this out.
			err = fmt.Errorf("Error parsing player data for %q: %v", username, err)
			clientErr = os.NewError("Error reading user data. Please contact the server administrator.")
			return
		}
	}

	game.playerConnect <- player
	player.Start()
}
func (l *pktHandler) handleLogin(pktHandshake *proto.PacketHandshake) (err, clientErr error) {
	pversion := int(pktHandshake.ProtocolVersion)
	if l.gameInfo.protocolVersion > pversion {
		err = loginErrVersionHigh
		clientErr = err
	}
	if l.gameInfo.protocolVersion < pversion {
		err = loginErrVersionLow
		clientErr = err
	}

	username := pktHandshake.Username
	if !validPlayerUsername.MatchString(username) {
		err = clientErrUsername
		clientErr = err
		return
	}

	log.Print("Client ", l.conn.RemoteAddr(), " connected as ", username)

	//@TODO Allow admins to connect.
	if l.gameInfo.maintenanceMsg != "" {
		err = loginErrorMaintenance
		clientErr = errors.New(l.gameInfo.maintenanceMsg)
		return
	}

	// Load player permissions.
	permissions := gamerules.Permissions.UserPermissions(username)
	if !permissions.Has("login") {
		err = fmt.Errorf("Player %q does not have login permission", username)
		clientErr = clientErrLoginDenied
		return
	}

	sessionId := fmt.Sprintf("%016x", rand.Int63())
	log.Printf("Player %q has sessionId %s", username, sessionId)

	// @ISSUE #4
	// authenticated, err := l.gameInfo.authserver.Authenticate(sessionId, username)
	// if !authenticated || err != nil {
	//     var reason string
	//     if err != nil {
	//         reason = "Authentication check failed: " + err.Error()
	//     } else {
	//         reason = "Failed authentication"
	//     }
	//     err = fmt.Errorf("Client %v: %s", l.conn.RemoteAddr(), reason)
	//     clientErr = clientErrAuthFailed
	//     return
	// }
	// log.Print("Client ", l.conn.RemoteAddr(), " passed minecraft.net authentication")

	entityId := l.gameInfo.entityManager.NewEntity()

	var playerData nbt.Compound
	if playerData, err = l.gameInfo.game.worldStore.PlayerData(username); err != nil {
		clientErr = clientErrUserData
		return
	}

	player := player.NewPlayer(entityId, l.gameInfo.shardManager, l.conn, username, l.gameInfo.worldStore.SpawnPosition, l.gameInfo.game.playerDisconnect, l.gameInfo.game)
	if playerData != nil {
		if err = player.UnmarshalNbt(playerData); err != nil {
			// Don't let the player log in, as they will only have default inventory
			// etc., which could lose items from them. Better for an administrator to
			// sort this out.
			err = fmt.Errorf("Error parsing player data for %q: %v", username, err)
			clientErr = clientErrUserData
			return
		}
	}

	l.gameInfo.game.playerConnect <- player
	player.Run()

	return
}
func (l *pktHandler) handleLogin(conn net.Conn) (err, clientErr error) {
	if !validPlayerUsername.MatchString(l.username) {
		err = clientErrUsername
		clientErr = err
		return
	}

	log.Print("Client ", conn.RemoteAddr(), " connected as ", l.username)

	// TODO Allow admins to connect.
	if l.gameInfo.maintenanceMsg != "" {
		err = loginErrorMaintenance
		clientErr = errors.New(l.gameInfo.maintenanceMsg)
		return
	}

	// Load player permissions.
	permissions := gamerules.Permissions.UserPermissions(l.username)
	if !permissions.Has("login") {
		err = fmt.Errorf("Player %q does not have login permission", l.username)
		clientErr = clientErrLoginDenied
		return
	}

	if err = proto.ServerWriteHandshake(conn, l.gameInfo.serverId); err != nil {
		clientErr = clientErrHandshake
		return
	}

	if l.gameInfo.serverId != "-" {
		var authenticated bool
		authenticated, err = l.gameInfo.authserver.Authenticate(l.gameInfo.serverId, l.username)
		if !authenticated || err != nil {
			var reason string
			if err != nil {
				reason = "Authentication check failed: " + err.Error()
			} else {
				reason = "Failed authentication"
			}
			err = fmt.Errorf("Client %v: %s", conn.RemoteAddr(), reason)
			clientErr = clientErrAuthFailed
			return
		}
		log.Print("Client ", conn.RemoteAddr(), " passed minecraft.net authentication")
	}

	err = proto.ServerReadPacketExpect(conn, l, []byte{
		proto.PacketIdLogin,
	})
	if err != nil {
		clientErr = clientErrLoginGeneral
		return
	}

	entityId := l.gameInfo.entityManager.NewEntity()

	var playerData *nbt.Compound
	if playerData, err = l.gameInfo.game.worldStore.PlayerData(l.username); err != nil {
		clientErr = clientErrUserData
		return
	}

	player := player.NewPlayer(entityId, l.gameInfo.shardManager, conn, l.username, l.gameInfo.worldStore.SpawnPosition, l.gameInfo.game.playerDisconnect, l.gameInfo.game)
	if playerData != nil {
		if err = player.UnmarshalNbt(playerData); err != nil {
			// Don't let the player log in, as they will only have default inventory
			// etc., which could lose items from them. Better for an administrator to
			// sort this out.
			err = fmt.Errorf("Error parsing player data for %q: %v", l.username, err)
			clientErr = clientErrUserData
			return
		}
	}

	l.gameInfo.game.playerConnect <- player
	player.Run()

	return
}