Example #1
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})
}
Example #2
0
func (Lobby) LobbyJoin(so *wsevent.Client, args struct {
	Id       *uint   `json:"id"`
	Class    *string `json:"class"`
	Team     *string `json:"team" valid:"red,blu"`
	Password *string `json:"password" empty:"-"`
}) interface{} {

	p := chelpers.GetPlayer(so.Token)
	if banned, until := p.IsBannedWithTime(player.BanJoin); banned {
		ban, _ := p.GetActiveBan(player.BanJoin)
		return fmt.Errorf("You have been banned from joining lobbies till %s (%s)", until.Format(time.RFC822), ban.Reason)
	}

	//logrus.Debug("id %d class %s team %s", *args.Id, *args.Class, *args.Team)
	lob, tperr := lobby.GetLobbyByID(*args.Id)

	if tperr != nil {
		return tperr
	}

	if lob.Mumble {
		if banned, until := p.IsBannedWithTime(player.BanJoinMumble); banned {
			ban, _ := p.GetActiveBan(player.BanJoinMumble)
			return fmt.Errorf("You have been banned from joining Mumble lobbies till %s (%s)", until.Format(time.RFC822), ban.Reason)
		}
	}

	if lob.State == lobby.Ended {
		return errors.New("Cannot join a closed lobby.")

	}
	if lob.State == lobby.Initializing {
		return errors.New("Lobby is being setup right now.")
	}

	if lob.RegionLock {
		region, _ := helpers.GetRegion(chelpers.GetIPAddr(so.Request))
		if region != lob.RegionCode {
			return errors.New("This lobby is region locked.")
		}
	}

	//Check if player is in the same lobby
	var sameLobby bool
	if id, err := p.GetLobbyID(false); err == nil && id == *args.Id {
		sameLobby = true
	}

	slot, tperr := format.GetSlot(lob.Type, *args.Team, *args.Class)
	if tperr != nil {
		return tperr
	}

	if prevId, _ := p.GetLobbyID(false); prevId != 0 && !sameLobby {
		lob, _ := lobby.GetLobbyByID(prevId)
		hooks.AfterLobbyLeave(lob, p, false, false)
	}

	tperr = lob.AddPlayer(p, slot, *args.Password)

	if tperr != nil {
		return tperr
	}

	if !sameLobby {
		hooks.AfterLobbyJoin(so, lob, p)
	}

	playersCnt := lob.GetPlayerNumber()
	lastNotif, timerExists := lobbyJoinLastNotif[lob.ID]
	if playersCnt >= notifThreshold[lob.Type] && !lob.IsEnoughPlayers(playersCnt) && (!timerExists || time.Since(lastNotif).Minutes() > 5) {
		lob.DiscordNotif(fmt.Sprintf("Almost ready [%d/%d]", playersCnt, lob.RequiredPlayers()))
		lobbyJoinLastNotif[lob.ID] = time.Now()
	}

	//check if lobby isn't already in progress (which happens when the player is subbing)
	lob.Lock()
	if lob.IsEnoughPlayers(playersCnt) && lob.State != lobby.InProgress && lob.State != lobby.ReadyingUp {
		lob.State = lobby.ReadyingUp
		lob.ReadyUpTimestamp = time.Now().Unix() + 30
		lob.Save()

		helpers.GlobalWait.Add(1)
		time.AfterFunc(time.Second*30, func() {
			state := lob.CurrentState()
			//if all player's haven't readied up,
			//remove unreadied players and unready the
			//rest.
			//don't do this when:
			//  lobby.State == Waiting (someone already unreadied up, so all players have been unreadied)
			// lobby.State == InProgress (all players have readied up, so the lobby has started)
			// lobby.State == Ended (the lobby has been closed)
			if state != lobby.Waiting && state != lobby.InProgress && state != lobby.Ended {
				lob.SetState(lobby.Waiting)
				removeUnreadyPlayers(lob)
				lob.UnreadyAllPlayers()
				//get updated lobby object
				lob, _ = lobby.GetLobbyByID(lob.ID)
				lobby.BroadcastLobby(lob)
			}
			helpers.GlobalWait.Done()
		})

		room := fmt.Sprintf("%s_private",
			hooks.GetLobbyRoom(lob.ID))
		broadcaster.SendMessageToRoom(room, "lobbyReadyUp",
			struct {
				Timeout int `json:"timeout"`
			}{30})
		lobby.BroadcastLobbyList()
	}
	lob.Unlock()

	if lob.State == lobby.InProgress { //this happens when the player is a substitute
		db.DB.Preload("ServerInfo").First(lob, lob.ID)
		so.EmitJSON(helpers.NewRequest("lobbyStart", lobby.DecorateLobbyConnect(lob, p, slot)))
	}

	return emptySuccess
}