Exemplo n.º 1
0
//Close closes the lobby, which has the following effects:
//
//  All unfilled substitutes for the lobby are "filled" (ie, their filled field is set to true)
//  The corresponding ServerRecord is deleted
//
//If rpc == true, the log listener in Pauling for the corresponding server is stopped, this is
//used when the lobby is closed manually by a player
func (lobby *Lobby) Close(doRPC, matchEnded bool) {
	var count int

	db.DB.Preload("ServerInfo").First(lobby, lobby.ID)
	db.DB.Model(&gameserver.ServerRecord{}).Where("host = ?", lobby.ServerInfo.Host).Count(&count)
	if count != 0 {
		gameserver.PutStoredServer(lobby.ServerInfo.Host)
	}

	lobby.SetState(Ended)
	db.DB.First(lobby).UpdateColumn("match_ended", matchEnded)
	//db.DB.Exec("DELETE FROM spectators_players_lobbies WHERE lobby_id = ?", lobby.ID)
	if doRPC {
		rpc.End(lobby.ID)
	}
	if matchEnded {
		lobby.UpdateStats()
	}
	if lobby.ServemeID != 0 {
		context := helpers.GetServemeContext(lobby.ServerInfo.Host)
		err := context.Delete(lobby.ServemeID, lobby.CreatedBySteamID)
		if err != nil {
			logrus.Error(err)
		}
		if matchEnded {
			time.AfterFunc(10*time.Second, func() {
				lobby.DownloadDemo(context)
			})
		}
	}

	privateRoom := fmt.Sprintf("%d_private", lobby.ID)
	broadcaster.SendMessageToRoom(privateRoom, "lobbyLeft", LobbyEvent{ID: lobby.ID})

	publicRoom := fmt.Sprintf("%d_public", lobby.ID)
	broadcaster.SendMessageToRoom(publicRoom, "lobbyClosed", DecorateLobbyClosed(lobby))

	db.DB.Model(&gameserver.ServerRecord{}).Where("id = ?", lobby.ServerInfoID).Delete(&gameserver.ServerRecord{})
	BroadcastSubList()
	BroadcastLobby(lobby)
	BroadcastLobbyList() // has to be done manually for now
	rpc.FumbleLobbyEnded(lobby.ID)
	lobby.deleteLock()
}
Exemplo n.º 2
0
//Delete removes the lobby object from the database.
//Closed lobbies aren't deleted, this function is used for
//lobbies where the game server had an error while being setup.
func (lobby *Lobby) Delete() {
	var count int

	db.DB.Model(&gameserver.ServerRecord{}).Where("host = ?", lobby.ServerInfo.Host).Count(&count)
	if count != 0 {
		gameserver.PutStoredServer(lobby.ServerInfo.Host)
	}

	if lobby.ServemeID != 0 {
		context := helpers.GetServemeContext(lobby.ServerInfo.Host)
		err := context.Delete(lobby.ServemeID, lobby.CreatedBySteamID)
		for err != nil {
			err = context.Delete(lobby.ServemeID, lobby.CreatedBySteamID)
		}
	}

	db.DB.Delete(lobby)
	db.DB.Delete(&lobby.ServerInfo)

	lobby.deleteLock()
}
Exemplo n.º 3
0
func (Lobby) LobbyCreate(so *wsevent.Client, args struct {
	Map         *string        `json:"map"`
	Type        *string        `json:"type" valid:"debug,6s,highlander,4v4,ultiduo,bball"`
	League      *string        `json:"league" valid:"ugc,etf2l,esea,asiafortress,ozfortress,bballtf"`
	ServerType  *string        `json:"serverType" valid:"server,storedServer,serveme"`
	Serveme     *servemeServer `json:"serveme" empty:"-"`
	Server      *string        `json:"server" empty:"-"`
	RconPwd     *string        `json:"rconpwd" empty:"-"`
	WhitelistID *string        `json:"whitelistID"`
	Mumble      *bool          `json:"mumbleRequired"`

	Password            *string `json:"password" empty:"-"`
	SteamGroupWhitelist *string `json:"steamGroupWhitelist" empty:"-"`
	// restrict lobby slots to twitch subs for a particular channel
	// not a pointer, since it is set to false when the argument json
	// string doesn't have the field
	TwitchWhitelistSubscribers bool `json:"twitchWhitelistSubs"`
	TwitchWhitelistFollowers   bool `json:"twitchWhitelistFollows"`
	RegionLock                 bool `json:"regionLock"`

	Requirements *struct {
		Classes map[string]Requirement `json:"classes,omitempty"`
		General Requirement            `json:"general,omitempty"`
	} `json:"requirements" empty:"-"`

	Discord *struct {
		RedChannel *string `json:"redChannel,omitempty"`
		BluChannel *string `json:"bluChannel,omitempty"`
	} `json:"discord" empty:"-"`
}) interface{} {
	p := chelpers.GetPlayer(so.Token)
	if banned, until := p.IsBannedWithTime(player.BanCreate); banned {
		ban, _ := p.GetActiveBan(player.BanCreate)
		return fmt.Errorf("You've been banned from creating lobbies till %s (%s)", until.Format(time.RFC822), ban.Reason)
	}

	if p.HasCreatedLobby() {
		if p.Role != helpers.RoleAdmin && p.Role != helpers.RoleMod {
			return errors.New("You have already created a lobby.")
		}
	}

	var steamGroup string
	var context *servemetf.Context
	var reservation servemetf.Reservation

	if *args.SteamGroupWhitelist != "" {
		if reSteamGroup.MatchString(*args.SteamGroupWhitelist) {
			steamGroup = reSteamGroup.FindStringSubmatch(*args.SteamGroupWhitelist)[1]
		} else {
			return errors.New("Invalid Steam group URL")
		}
	}

	if *args.ServerType == "serveme" {
		if args.Serveme == nil {
			return errors.New("No serveme info given.")
		}
		var err error
		var start, end time.Time

		if start, err = time.Parse(servemetf.TimeFormat, (*args.Serveme).StartsAt); err != nil {
			return err
		}
		if end, err = time.Parse(servemetf.TimeFormat, (*args.Serveme).EndsAt); err != nil {
			return err
		}

		randBytes := make([]byte, 6)
		rand.Read(randBytes)
		*args.RconPwd = base64.URLEncoding.EncodeToString(randBytes)

		reservation = servemetf.Reservation{
			StartsAt:    start.Format(servemetf.TimeFormat),
			EndsAt:      end.Format(servemetf.TimeFormat),
			ServerID:    (*args.Serveme).Server.ID,
			WhitelistID: 1,
			RCON:        *args.RconPwd,
			Password:    "******",
		}

		context = helpers.GetServemeContextIP(chelpers.GetIPAddr(so.Request))
		resp, err := context.Create(reservation, so.Token.Claims.(*chelpers.TF2StadiumClaims).SteamID)
		if err != nil || resp.Reservation.Errors != nil {
			if err != nil {
				logrus.Error(err)
			} else {
				logrus.Error(resp.Reservation.Errors)
			}

			return errors.New("Couldn't get serveme reservation")
		}

		*args.Server = resp.Reservation.Server.IPAndPort
		reservation = resp.Reservation
	} else if *args.ServerType == "storedServer" {
		if *args.Server == "" {
			return errors.New("No server ID given")
		}

		id, err := strconv.ParseUint(*args.Server, 10, 64)
		if err != nil {
			return err
		}
		server, err := gameserver.GetStoredServer(uint(id))
		if err != nil {
			return err
		}
		*args.Server = server.Address
		*args.RconPwd = server.RCONPassword
	} else { // *args.ServerType == "server"
		if args.RconPwd == nil || *args.RconPwd == "" {
			return errors.New("RCON Password cannot be empty")
		}
		if args.Server == nil || *args.Server == "" {
			return errors.New("Server Address cannot be empty")
		}
	}

	var count int

	lobbyType := playermap[*args.Type]
	db.DB.Model(&gameserver.ServerRecord{}).Where("host = ?", *args.Server).Count(&count)
	if count != 0 {
		return errors.New("A lobby is already using this server.")
	}

	randBytes := make([]byte, 6)
	rand.Read(randBytes)
	serverPwd := base64.URLEncoding.EncodeToString(randBytes)

	//TODO what if playermap[lobbytype] is nil?
	info := gameserver.ServerRecord{
		Host:           *args.Server,
		RconPassword:   *args.RconPwd,
		ServerPassword: serverPwd,
	}

	lob := lobby.NewLobby(*args.Map, lobbyType, *args.League, info, *args.WhitelistID, *args.Mumble, steamGroup)

	if args.TwitchWhitelistSubscribers || args.TwitchWhitelistFollowers {
		if p.TwitchName == "" {
			return errors.New("Please connect your twitch account first.")
		}

		lob.TwitchChannel = p.TwitchName
		if args.TwitchWhitelistFollowers {
			lob.TwitchRestriction = lobby.TwitchFollowers
		} else {
			lob.TwitchRestriction = lobby.TwitchSubscribers
		}
	}

	lob.Discord = args.Discord != nil
	if lob.Discord {
		if !reDiscordInvite.MatchString(*args.Discord.RedChannel) || !reDiscordInvite.MatchString(*args.Discord.BluChannel) {
			return errors.New("Invalid Discord invite URL")
		}

		lob.DiscordRedChannel = *args.Discord.RedChannel
		lob.DiscordBluChannel = *args.Discord.BluChannel
	}

	lob.RegionLock = args.RegionLock
	lob.CreatedBySteamID = p.SteamID
	lob.RegionCode, lob.RegionName = helpers.GetRegion(*args.Server)
	if (lob.RegionCode == "" || lob.RegionName == "") && config.Constants.GeoIP {
		if reservation.ID != 0 {
			err := context.Delete(reservation.ID, p.SteamID)
			for err != nil {
				err = context.Delete(reservation.ID, p.SteamID)
			}
		} else if *args.ServerType == "storedServer" {
			gameserver.PutStoredServer(*args.Server)
		}

		return errors.New("Couldn't find the region for this server.")
	}

	if lobby.MapRegionFormatExists(lob.MapName, lob.RegionCode, lob.Type) {
		if reservation.ID != 0 {
			err := context.Delete(reservation.ID, p.SteamID)
			for err != nil {
				err = context.Delete(reservation.ID, p.SteamID)
			}
		} else if *args.ServerType == "storedServer" {
			gameserver.PutStoredServer(*args.Server)
		}

		return errors.New("Your region already has a lobby with this map and format.")
	}

	if *args.ServerType == "serveme" {
		lob.ServemeID = reservation.ID
	}

	lob.Save()
	lob.CreateLock()

	if *args.ServerType == "serveme" {
		now := time.Now()

		for {
			status, err := context.Status(reservation.ID, p.SteamID)
			if err != nil {
				logrus.Error(err)
			}
			if status == "ready" {
				break
			}

			time.Sleep(10 * time.Second)
			if time.Since(now) >= 3*time.Minute {
				lob.Delete()
				return errors.New("Couldn't get Serveme reservation, try another server.")
			}
		}

		lob.ServemeCheck(context)
	}

	err := lob.SetupServer()
	if err != nil { //lobby setup failed, delete lobby and corresponding server record
		lob.Delete()
		return err
	}

	lob.SetState(lobby.Waiting)

	if args.Requirements != nil {
		for class, requirement := range (*args.Requirements).Classes {
			if requirement.Restricted.Blu {
				newRequirement("blu", class, requirement, lob)
			}
			if requirement.Restricted.Red {
				newRequirement("red", class, requirement, lob)
			}
		}
		if args.Requirements.General.Hours != 0 || args.Requirements.General.Lobbies != 0 {
			for i := 0; i < 2*format.NumberOfClassesMap[lob.Type]; i++ {
				req := &lobby.Requirement{
					LobbyID: lob.ID,
					Hours:   args.Requirements.General.Hours,
					Lobbies: args.Requirements.General.Lobbies,
					Slot:    i,
				}
				req.Save()
			}

		}
	}

	if *args.Password != "" {
		for i := 0; i < 2*format.NumberOfClassesMap[lob.Type]; i++ {
			req := &lobby.Requirement{
				LobbyID:  lob.ID,
				Slot:     i,
				Password: *args.Password,
			}
			req.Save()
		}
	}

	chat.NewBotMessage(fmt.Sprintf("Lobby created by %s", p.Alias()), int(lob.ID)).Send()

	lobby.BroadcastLobbyList()
	return newResponse(
		struct {
			ID uint `json:"id"`
		}{lob.ID})
}