296 lines
17 KiB
Python
296 lines
17 KiB
Python
from flask import Blueprint, render_template, request, redirect, url_for, jsonify, make_response, abort, flash
|
|
from app.util import auth, placeinfo
|
|
from app.models.asset import Asset
|
|
from app.models.placeservers import PlaceServer
|
|
from app.models.placeserver_players import PlaceServerPlayer
|
|
from app.models.gameservers import GameServer
|
|
from app.models.place import Place
|
|
from app.models.gamepass_link import GamepassLink
|
|
from app.models.userassets import UserAsset
|
|
from app.models.groups import Group, GroupRole, GroupRolePermission
|
|
from app.models.place_badge import PlaceBadge, UserBadge
|
|
from app.models.universe import Universe
|
|
from slugify import slugify
|
|
from app.pages.develop.develop import ShutdownServer
|
|
from app.enums.AssetType import AssetType
|
|
from app.models.user import User
|
|
from app.util.membership import GetUserMembership
|
|
from app.enums.MembershipType import MembershipType
|
|
from app.routes.rate import GetAssetLikesAndDislikes, GetUserVoteStatus, GetAssetVotePercentage, GetAssetFavoriteCount, GetUserFavoriteStatus
|
|
from app.routes.badgesapi import CalculateBadgeRarity, GetBadgeAwardedPastDay
|
|
from app.services import groups
|
|
from sqlalchemy import and_, func
|
|
from datetime import datetime, timedelta
|
|
import logging
|
|
|
|
GamePagesRoute = Blueprint("games", __name__, template_folder="pages")
|
|
|
|
def DoesUserownGamepass( gamepass_id, userid ):
|
|
GamepassObj : Asset = Asset.query.filter_by(id=gamepass_id, asset_type = AssetType.GamePass).first()
|
|
if GamepassObj is None:
|
|
return False
|
|
UserGamepass : UserAsset = UserAsset.query.filter_by(userid=userid, assetid=GamepassObj.id).first()
|
|
if UserGamepass is None:
|
|
return False
|
|
return True
|
|
def DoesUserOwnBadge( badge_id, userid ):
|
|
BadgeObj : PlaceBadge = PlaceBadge.query.filter_by(id=badge_id).first()
|
|
if BadgeObj is None:
|
|
return False
|
|
UserBadgeObj : UserBadge = UserBadge.query.filter_by(badge_id=badge_id, user_id=userid).first()
|
|
if UserBadgeObj is None:
|
|
return False
|
|
return True
|
|
|
|
def GetTotalBadgeAwardedCount( badge_id ) -> int:
|
|
return UserBadge.query.filter_by( badge_id = badge_id ).count()
|
|
|
|
@GamePagesRoute.route("/games/<int:placeid>/", methods=["GET"])
|
|
@GamePagesRoute.route("/games/<int:placeid>/<string:slug>", methods=["GET"])
|
|
@auth.authenticated_required
|
|
def game_page(placeid : int, slug=None):
|
|
AssetObject : Asset = Asset.query.filter_by(id=placeid).first()
|
|
if AssetObject is None:
|
|
return abort(404)
|
|
if AssetObject.asset_type != AssetType.Place:
|
|
# Let catalog handle the redirect
|
|
return redirect(f"/catalog/{str(placeid)}/")
|
|
SlugName = slugify(AssetObject.name, lowercase=False)
|
|
if SlugName is None or SlugName == "":
|
|
SlugName = "unnamed"
|
|
if slug is None:
|
|
return redirect(f"/games/{str(placeid)}/{SlugName}")
|
|
if slug != SlugName:
|
|
return redirect(f"/games/{str(placeid)}/{SlugName}")
|
|
if AssetObject.creator_type == 0:
|
|
CreatorObject : User = User.query.filter_by(id=AssetObject.creator_id).first()
|
|
else:
|
|
CreatorObject : Group = Group.query.filter_by(id=AssetObject.creator_id).first()
|
|
PlaceObj : Place = Place.query.filter_by(placeid=placeid).first()
|
|
if PlaceObj is None:
|
|
return abort(404)
|
|
UniverseObj : Universe = Universe.query.filter_by(id=PlaceObj.parent_universe_id).first()
|
|
ActualRootPlace : Asset = Asset.query.filter_by(id=UniverseObj.root_place_id).first()
|
|
|
|
try:
|
|
SplittedDescription = AssetObject.description.split("\n")
|
|
LikeCount, DislikeCount = GetAssetLikesAndDislikes(AssetObject.id)
|
|
AuthenticatedUser : User = auth.GetCurrentUser()
|
|
|
|
PlaceServerObjs : list[PlaceServer] = PlaceServer.query.filter_by(serverPlaceId=placeid).all()
|
|
ActiveServers = []
|
|
for PlaceServerObj in PlaceServerObjs:
|
|
PlayersInServer : list[PlaceServerPlayer] = PlaceServerPlayer.query.filter_by(serveruuid=PlaceServerObj.serveruuid).all()
|
|
GameServerObj : GameServer = GameServer.query.filter_by(serverId=PlaceServerObj.originServerId).first()
|
|
if GameServerObj is None:
|
|
continue
|
|
ActiveServers.append({
|
|
"id": PlaceServerObj.serveruuid,
|
|
"playercount": PlaceServerObj.playerCount,
|
|
"maxplayercount": PlaceServerObj.maxPlayerCount,
|
|
"players": PlayersInServer,
|
|
"host": GameServerObj.serverName,
|
|
"is_reserved_server": PlaceServerObj.reservedServerAccessCode is not None
|
|
})
|
|
|
|
Gamepasses : list[GamepassLink] = GamepassLink.query.filter_by(universe_id = PlaceObj.parent_universe_id).all()
|
|
UserDoesNotHaveBC : bool = UniverseObj.bc_required and GetUserMembership(AuthenticatedUser) == MembershipType.NonBuildersClub
|
|
CanShutdownServer = False if AuthenticatedUser.id != 1 else True
|
|
if AssetObject.creator_type == 0:
|
|
if AuthenticatedUser.id == AssetObject.creator_id:
|
|
CanShutdownServer = True
|
|
else:
|
|
GroupObj : Group = groups.GetGroupFromId( AssetObject.creator_id )
|
|
UserGroupRole : GroupRole = groups.GetUserRolesetInGroup( AuthenticatedUser, GroupObj )
|
|
if UserGroupRole is None:
|
|
CanShutdownServer = False
|
|
UserGroupRolePermission : GroupRolePermission = groups.GetRolesetPermission( UserGroupRole )
|
|
if UserGroupRolePermission.manage_group_games:
|
|
CanShutdownServer = True
|
|
|
|
GameBadges : list[PlaceBadge] = PlaceBadge.query.filter_by( universe_id = PlaceObj.parent_universe_id ).all()
|
|
|
|
return render_template("games/view.html", PlaceAssetObj=AssetObject,
|
|
CreatorObj=CreatorObject,
|
|
PlaceObj=PlaceObj,
|
|
SplittedDescription=SplittedDescription,
|
|
PlayerCount=placeinfo.GetPlayingCount(PlaceObj),
|
|
LikeCount=LikeCount,
|
|
DislikeCount=DislikeCount,
|
|
UserVoteStatus=GetUserVoteStatus(AssetObject.id, AuthenticatedUser.id),
|
|
UserFavoriteStatus=GetUserFavoriteStatus(AssetObject.id, AuthenticatedUser.id),
|
|
FavoriteCount=GetAssetFavoriteCount(AssetObject.id),
|
|
ActiveServers=ActiveServers,
|
|
ActiveServerCount=len(ActiveServers),
|
|
Gamepasses=Gamepasses,
|
|
DoesUserownGamepass=DoesUserownGamepass,
|
|
UserDoesNotHaveBC=UserDoesNotHaveBC,
|
|
IsTooYoung = (datetime.utcnow() - AuthenticatedUser.created).days < UniverseObj.minimum_account_age,
|
|
MinAccountAge = UniverseObj.minimum_account_age,
|
|
CanShutdownServer = CanShutdownServer,
|
|
GameBadges = GameBadges,
|
|
CalculateBadgeRarity = CalculateBadgeRarity,
|
|
DoesUserOwnBadge = DoesUserOwnBadge,
|
|
GetTotalBadgeAwardedCount = GetTotalBadgeAwardedCount,
|
|
GetBadgeAwardedPastDay = GetBadgeAwardedPastDay,
|
|
UniverseObj = UniverseObj,
|
|
UniverseRootPlace = ActualRootPlace
|
|
)
|
|
|
|
except Exception as e:
|
|
logging.error(f"Error during rendering game page [{placeid}]: {str(e)}")
|
|
return redirect(f"/games/{str(placeid)}/{SlugName}")
|
|
|
|
@GamePagesRoute.route("/games/<int:placeid>/shutdown-server/<jobid>", methods=["POST"])
|
|
@auth.authenticated_required
|
|
def game_shutdown_server(placeid : int, jobid : str):
|
|
AuthenticatedUser : User = auth.GetCurrentUser()
|
|
AssetObject : Asset = Asset.query.filter_by(id=placeid).first()
|
|
if AssetObject is None:
|
|
return abort(404)
|
|
if AssetObject.asset_type != AssetType.Place:
|
|
return abort(404)
|
|
TargetPlaceServer : PlaceServer = PlaceServer.query.filter_by(serveruuid=jobid).first()
|
|
if TargetPlaceServer is None:
|
|
return abort(404)
|
|
if (AssetObject.creator_id != AuthenticatedUser.id and AssetObject.creator_type != 1) and AuthenticatedUser.id != 1:
|
|
return abort(403)
|
|
if AssetObject.creator_type == 1:
|
|
GroupObj : Group = groups.GetGroupFromId( AssetObject.creator_id )
|
|
UserGroupRole : GroupRole = groups.GetUserRolesetInGroup( AuthenticatedUser, GroupObj )
|
|
if UserGroupRole is None:
|
|
return abort(403)
|
|
UserGroupRolePermission : GroupRolePermission = groups.GetRolesetPermission( UserGroupRole )
|
|
if not UserGroupRolePermission.manage_group_games:
|
|
return abort(403)
|
|
ShutdownServer(TargetPlaceServer.serveruuid)
|
|
flash("Requested server shutdown", "success")
|
|
return redirect(f"/games/{str(placeid)}/")
|
|
|
|
@GamePagesRoute.route("/games", methods=["GET"])
|
|
@auth.authenticated_required
|
|
def games_page():
|
|
TopPlacesInfo = []
|
|
PageNumber = max( request.args.get('page', default = 1, type = int ), 1 )
|
|
SearchQuery = request.args.get('q', default = None, type = str)
|
|
GameLookupResultsBackendResult = None
|
|
GameLookupResults = []
|
|
if SearchQuery:
|
|
SearchQuery = SearchQuery.strip().replace('%', '')
|
|
if len(SearchQuery) > 0 and len(SearchQuery) <= 32:
|
|
GameLookupResultsBackendResult = Universe.query.filter( and_( Universe.is_public == True, Universe.moderation_status == 0) ).outerjoin( Place, Place.parent_universe_id == Universe.id ).join( Asset, Asset.id == Place.placeid ).filter( Asset.name.ilike(f"%{SearchQuery}%") ).filter_by( moderation_status = 0 ).order_by( Universe.visit_count.desc() ).paginate(page=PageNumber, per_page=36, error_out=False)
|
|
else:
|
|
flash('Invalid search query.', 'danger')
|
|
|
|
if GameLookupResultsBackendResult:
|
|
for UniverseObj in GameLookupResultsBackendResult:
|
|
UniverseObj : Universe
|
|
AssetObj : Asset = Asset.query.filter_by(id=UniverseObj.root_place_id).first()
|
|
if AssetObj is None:
|
|
continue
|
|
|
|
PlaceObjData = {
|
|
"id": UniverseObj.root_place_id,
|
|
"name": AssetObj.name,
|
|
"playingcount": placeinfo.GetUniversePlayingCount(UniverseObj),
|
|
"slug": slugify(AssetObj.name, lowercase=False) if AssetObj.name is not None else "",
|
|
"likePercentage": GetAssetVotePercentage(AssetObj.id),
|
|
"placeyear": UniverseObj.place_year
|
|
}
|
|
GameLookupResults.append(PlaceObjData)
|
|
|
|
PopularGames = Universe.query.filter( and_( Universe.is_public == True, Universe.moderation_status == 0) ).outerjoin( Place, Place.parent_universe_id == Universe.id ).join( Asset, Asset.id == Place.placeid ).filter_by( moderation_status = 0 ).outerjoin( PlaceServer, PlaceServer.serverPlaceId == Place.placeid ).group_by( Universe.id )
|
|
PopularGames = PopularGames.order_by( func.coalesce( func.sum( PlaceServer.playerCount ), 0 ).desc() ).order_by( Universe.visit_count.desc() )
|
|
|
|
UniverseObjsList = PopularGames.limit(24).all()
|
|
for UniverseObj in UniverseObjsList:
|
|
UniverseObj : Universe
|
|
AssetObj : Asset = Asset.query.filter_by(id=UniverseObj.root_place_id).first()
|
|
if AssetObj is None:
|
|
continue
|
|
PlaceObjData = {
|
|
"id": UniverseObj.root_place_id,
|
|
"name": AssetObj.name,
|
|
"playingcount": placeinfo.GetUniversePlayingCount( UniverseObj ),
|
|
"slug": slugify(AssetObj.name, lowercase=False) if AssetObj.name is not None else "",
|
|
"likePercentage": GetAssetVotePercentage(UniverseObj.root_place_id),
|
|
"placeyear": UniverseObj.place_year
|
|
}
|
|
TopPlacesInfo.append(PlaceObjData)
|
|
|
|
RecentlyUpdatedUniverses : list[Universe] = Universe.query.filter( and_( Universe.is_public == True, Universe.moderation_status == 0) ).join( Asset, Asset.id == Universe.root_place_id ).filter_by( moderation_status = 0 ).order_by(Universe.updated_at.desc()).limit(24).all()
|
|
RecentlyUpdatedUniversesInfo = []
|
|
for UniverseObj in RecentlyUpdatedUniverses:
|
|
PlaceObj : Place = Place.query.filter_by(placeid=UniverseObj.root_place_id).first()
|
|
if PlaceObj is None:
|
|
continue
|
|
AssetObj : Asset = Asset.query.filter_by(id=PlaceObj.placeid).first()
|
|
if len(RecentlyUpdatedUniversesInfo) >= 24:
|
|
break
|
|
PlaceObjData = {
|
|
"id": AssetObj.id,
|
|
"name": AssetObj.name,
|
|
"playingcount": placeinfo.GetUniversePlayingCount( UniverseObj ),
|
|
"slug": slugify(AssetObj.name, lowercase=False) if AssetObj.name is not None else "",
|
|
"likePercentage": GetAssetVotePercentage(AssetObj.id),
|
|
"placeyear": UniverseObj.place_year
|
|
}
|
|
RecentlyUpdatedUniversesInfo.append(PlaceObjData)
|
|
|
|
FeaturedGames = Universe.query.filter( and_( Universe.is_public == True, Universe.moderation_status == 0, Universe.is_featured == True ) ).outerjoin( Place, Place.parent_universe_id == Universe.id ).join( Asset, Asset.id == Place.placeid ).filter_by( moderation_status = 0 ).outerjoin( PlaceServer, PlaceServer.serverPlaceId == Place.placeid ).group_by( Universe.id )
|
|
FeaturedGames = FeaturedGames.order_by( func.coalesce( func.sum( PlaceServer.playerCount ), 0 ).desc() ).order_by( Universe.visit_count.desc() )
|
|
FeaturedPlaceObjs = FeaturedGames.limit(24).all()
|
|
FeaturedPlacesInfo = []
|
|
for UniverseObj in FeaturedPlaceObjs:
|
|
UniverseObj : Universe
|
|
AssetObj : Asset = Asset.query.filter_by(id=UniverseObj.root_place_id).first()
|
|
if AssetObj is None:
|
|
continue
|
|
PlaceObjData = {
|
|
"id": UniverseObj.root_place_id,
|
|
"name": AssetObj.name,
|
|
"playingcount": placeinfo.GetUniversePlayingCount( UniverseObj ),
|
|
"slug": slugify(AssetObj.name, lowercase=False) if AssetObj.name is not None else "",
|
|
"likePercentage": GetAssetVotePercentage(AssetObj.id),
|
|
"placeyear": UniverseObj.place_year
|
|
}
|
|
FeaturedPlacesInfo.append(PlaceObjData)
|
|
|
|
return render_template("games/index.html", PopularPlaces=TopPlacesInfo, RecentlyUpdatedPlaces=RecentlyUpdatedUniversesInfo, FeaturedPlacesInfo=FeaturedPlacesInfo, GameLookupResults = GameLookupResults, PageNumber=PageNumber, SearchQuery = SearchQuery, GameLookupResultsBackendResult = GameLookupResultsBackendResult)
|
|
|
|
from sqlalchemy import func
|
|
@GamePagesRoute.route("/games/<genre>/view", methods=["GET"])
|
|
@auth.authenticated_required
|
|
def games_genre_page(genre : str):
|
|
pageNumber = max( request.args.get("page", default=1, type=int), 1 )
|
|
if genre == "popular":
|
|
PopularGames = Universe.query.filter( and_( Universe.is_public == True, Universe.moderation_status == 0) ).outerjoin( Place, Place.parent_universe_id == Universe.id ).join(Asset, Asset.id == Place.placeid).outerjoin(PlaceServer, PlaceServer.serverPlaceId == Place.placeid).group_by(Universe.id)
|
|
PopularGames = PopularGames.filter( Asset.moderation_status == 0).order_by(func.coalesce(func.sum(PlaceServer.playerCount), 0).desc()).order_by( Universe.visit_count.desc() )
|
|
PopularGames = PopularGames.paginate(page=pageNumber, per_page=30, error_out=False)
|
|
ViewerFacingGenre = "Popular Games"
|
|
pageResults = PopularGames
|
|
elif genre == "updated":
|
|
UpdatedGames = Universe.query.filter( and_( Universe.is_public == True, Universe.moderation_status == 0) ).join( Asset, Asset.id == Universe.root_place_id ).filter( Asset.moderation_status == 0 ).order_by(Universe.updated_at.desc())
|
|
UpdatedGames = UpdatedGames.paginate(page=pageNumber, per_page=30, error_out=False)
|
|
ViewerFacingGenre = "Recently Updated"
|
|
pageResults = UpdatedGames
|
|
elif genre == "featured":
|
|
FeaturedGames = Universe.query.filter( and_( Universe.is_public == True, Universe.moderation_status == 0, Universe.is_featured == True ) ).outerjoin( Place, Place.parent_universe_id == Universe.id ).join(Asset, Asset.id == Place.placeid).outerjoin(PlaceServer, PlaceServer.serverPlaceId == Place.placeid).group_by(Universe.id)
|
|
FeaturedGames = FeaturedGames.filter( Asset.moderation_status == 0).order_by(func.coalesce(func.sum(PlaceServer.playerCount), 0).desc()).order_by( Universe.visit_count.desc() )
|
|
FeaturedGames = FeaturedGames.paginate(page=pageNumber, per_page=30, error_out=False)
|
|
ViewerFacingGenre = "Featured Games"
|
|
pageResults = FeaturedGames
|
|
else:
|
|
return abort(404)
|
|
|
|
if pageResults is None:
|
|
return abort(404)
|
|
|
|
def getRootPlace( UniverseObj : Universe ) -> Place :
|
|
try:
|
|
return UniverseObj.root_place_obj
|
|
except:
|
|
UniverseObj.root_place_obj = Place.query.filter_by(placeid=UniverseObj.root_place_id).first()
|
|
return UniverseObj.root_place_obj
|
|
|
|
return render_template("games/genre.html", Games=pageResults, Genre=genre, getPlayerCount = placeinfo.GetUniversePlayingCount, getAssetVotePercentage = GetAssetVotePercentage, ViewerFacingGenre=ViewerFacingGenre, getRootPlace=getRootPlace) |