Skip to content

Commit

Permalink
Backend: Updated Backend before merge
Browse files Browse the repository at this point in the history
  • Loading branch information
hanmpark committed Oct 14, 2024
1 parent 9b2a581 commit c3ef8f0
Show file tree
Hide file tree
Showing 3 changed files with 94 additions and 65 deletions.
150 changes: 89 additions & 61 deletions backend/api/consumers.py
Original file line number Diff line number Diff line change
Expand Up @@ -356,9 +356,7 @@ async def receive_json(self, content):
event_type = content.get('e')
data = content.get('d')


if event_type != 'HEARTBEAT':
# logger.info(f"[{self.__class__.__name__}] Received event: {event_type}")
pass

if event_type == 'HEARTBEAT':
Expand All @@ -379,7 +377,7 @@ async def heartbeat_check(self):
while True:
await asyncio.sleep(2) # Check every 2 seconds
current_time = time.time()
if current_time - self.last_heartbeat > 10000: # No heartbeat for 10 seconds
if current_time - self.last_heartbeat > 10: # No heartbeat for 10 seconds
logger.warning(f"[{self.__class__.__name__}] User {self.user.userID} missed heartbeat too many times, closing connection")
await self.close()
break
Expand Down Expand Up @@ -536,10 +534,13 @@ async def delayed_match_start(self, match):
await self.start_match(match)

async def match_ready(self, event):
await self.send_json({
"e": "MATCH_READY",
"d": event["match_state"]
})
try:
await self.send_json({
"e": "MATCH_READY",
"d": event["match_state"]
})
except Exception as _:
pass

async def handle_paddle_move(self, data):
direction = data.get('direction')
Expand All @@ -553,7 +554,6 @@ async def handle_paddle_move(self, data):
elif direction == 'down':
match_state[player_key]['paddle_y'] = max(73, match_state[player_key]['paddle_y'] - paddle_speed)

logger.debug(f"[MatchConsumer] Paddle moved: {player_key} - {direction}")
await self.send_match_update()

async def handle_player_quit(self):
Expand All @@ -564,17 +564,13 @@ async def handle_player_quit(self):
logger.info(f"[{self.__class__.__name__}] Player {self.user.userID} quit the match {self.match.matchID}")

if self.match.playerB is not None and self.match.playerA is not None:
await self.end_match(self.match.matchID, self.user.userID)
winner_id = self.match.playerA['id'] if self.match.playerB['id'] == self.user.userID else self.match.playerB['id']
await self.send_match_end(winner_id)

if self.match.playerB is None or self.match.playerA is None:
if self.match.finishedAt is None:
await self.delete_match(self.match.matchID)

await self.end_match(self.match.matchID, self.user.userID)
if self.match.matchID in self.active_matches:
del self.active_matches[self.match.matchID]


@database_sync_to_async
def delete_match(self, match_id):
try:
Expand Down Expand Up @@ -624,7 +620,6 @@ def find_or_create_match(self, match_type):
new_match = Match.objects.create(
matchID=generate_id("match"),
playerA={"id": self.user.userID, "platform": "web"},
# playerB={"id": "ai", "platform": "server"},
winnerID=None,
finishedAt=None,
scores={},
Expand All @@ -642,13 +637,11 @@ def get_opponent_info(self, match, user):
if match.playerB is None:
return None
opponent_id = match.playerB['id'] if match.playerA['id'] == user.userID else match.playerA['id']
if opponent_id == 'ai':
return {"userID": "ai", "username": "AI Opponent", "displayName": "AI", "avatarID": "ai_avatar"}
try:
opponent = User.objects.get(userID=opponent_id)
opponent_data = UserSerializer(opponent).data
safe_profile = get_safe_profile(opponent_data, me=False)
opponent_settings, created = UserSettings.objects.get_or_create(userID=opponent_id)
opponent_settings, _ = UserSettings.objects.get_or_create(userID=opponent_id)
opponent_settings_data = UserSettingsSerializer(opponent_settings).data
safe_profile['paddle_skin'] = opponent_settings_data['selectedPaddleSkin']
logger.info(f"[{self.__class__.__name__}] Opponent info retrieved for: {opponent_id}")
Expand All @@ -666,19 +659,24 @@ async def send_match_update(self):
"match_state": match_state
}
)
# logger.debug(f"[{self.__class__.__name__}] Match update sent for match: {self.match.matchID}")

async def match_update(self, event):
await self.send_json({
"e": "MATCH_UPDATE",
"d": event["match_state"]
})
try:
await self.send_json({
"e": "MATCH_UPDATE",
"d": event["match_state"]
})
except Exception as _:
pass

async def match_begin(self, event):
await self.send_json({
"e": "MATCH_BEGIN",
"d": event["match_state"]
})
try:
await self.send_json({
"e": "MATCH_BEGIN",
"d": event["match_state"]
})
except Exception as _:
pass

@database_sync_to_async
def start_match(self, match):
Expand All @@ -700,32 +698,34 @@ async def run_match_loop(self, match_id):
TERRAIN_WIDTH = 1200
TERRAIN_HEIGHT = 750
PADDLE_WIDTH = 10
PADDLE_HEIGHT = 60
PADDLE_HEIGHT = 60 # Paddle height is actually 120 but we're using half of it
BALL_RADIUS = 25 / 2
BALL_SPEED = 0.6
BALL_MAX_SPEED = 17
MAX_SCORE = 10
REFRESH_RATE = 1 / 60

while match_id in self.active_matches:
match_state = self.active_matches[match_id]

# Update ball position
match_state['ball']['x'] += match_state['ball']['dx']
match_state['ball']['y'] += match_state['ball']['dy']
match_state['ball']['x'] += match_state['ball']['dx'] * BALL_SPEED
match_state['ball']['y'] += match_state['ball']['dy'] * BALL_SPEED

# Check for collisions with top and bottom walls
if match_state['ball']['y'] - BALL_RADIUS <= 0 or match_state['ball']['y'] + BALL_RADIUS >= TERRAIN_HEIGHT:
match_state['ball']['dy'] *= -1

# Check for collisions with paddles
if (match_state['ball']['x'] <= PADDLE_WIDTH and
match_state['ball']['y'] + BALL_RADIUS >= match_state['playerA']['paddle_y'] and
match_state['ball']['y'] <= match_state['playerA']['paddle_y'] + PADDLE_HEIGHT):
if (match_state['ball']['x'] - BALL_RADIUS <= PADDLE_WIDTH and # Left side of ball hits Player A's paddle
match_state['ball']['y'] - BALL_RADIUS <= match_state['playerA']['paddle_y'] + PADDLE_HEIGHT and # Ball's bottom is above paddle's bottom
match_state['ball']['y'] + BALL_RADIUS >= match_state['playerA']['paddle_y'] - PADDLE_HEIGHT): # Ball's top is below paddle's top
match_state['ball']['dx'] *= -1
if abs(match_state['ball']['dx']) < BALL_MAX_SPEED:
match_state['ball']['dx'] *= 1.1
if abs(match_state['ball']['dy']) < BALL_MAX_SPEED:
match_state['ball']['dy'] *= 1.1
match_state['ball']['x'] = PADDLE_WIDTH + BALL_RADIUS
await self.send_paddle_hit(match_state['playerA'], match_state['ball'])

elif (match_state['ball']['x'] + BALL_RADIUS >= TERRAIN_WIDTH - PADDLE_WIDTH and # Right side of ball hits Player B's paddle
Expand All @@ -736,6 +736,7 @@ async def run_match_loop(self, match_id):
match_state['ball']['dx'] *= 1.1
if abs(match_state['ball']['dy']) < BALL_MAX_SPEED:
match_state['ball']['dy'] *= 1.1
match_state['ball']['x'] = TERRAIN_WIDTH - PADDLE_WIDTH - BALL_RADIUS
await self.send_paddle_hit(match_state['playerB'], match_state['ball'])

# Check for scoring
Expand All @@ -756,7 +757,7 @@ async def run_match_loop(self, match_id):
break

await self.send_match_update()
await asyncio.sleep(1 / 60) # 120 FPS
await asyncio.sleep(REFRESH_RATE)

async def send_paddle_hit(self, player, ball):
await self.channel_layer.group_send(
Expand All @@ -769,11 +770,14 @@ async def send_paddle_hit(self, player, ball):
)

async def paddle_hit(self, event):
await self.send_json({
"e": "PADDLE_HIT",
"d": {"player": event["player"], "ball": event["ball"]}
})
logger.info(f"[{self.__class__.__name__}] Paddle hit event sent for player: {event['player']['id']}")
try:
await self.send_json({
"e": "PADDLE_HIT",
"d": {"player": event["player"], "ball": event["ball"]}
})
logger.info(f"[{self.__class__.__name__}] Paddle hit event sent for player: {event['player']['id']}")
except Exception as _:
pass

async def send_ball_scored(self, player):
await self.channel_layer.group_send(
Expand All @@ -785,11 +789,14 @@ async def send_ball_scored(self, player):
)

async def ball_scored(self, event):
await self.send_json({
"e": "BALL_SCORED",
"d": {"player": event["player"]}
})
logger.info(f"[{self.__class__.__name__}] Ball scored event processed for player: {event['player']['id']}")
try:
await self.send_json({
"e": "BALL_SCORED",
"d": {"player": event["player"]}
})
logger.info(f"[{self.__class__.__name__}] Ball scored event processed for player: {event['player']['id']}")
except Exception as _:
pass

def reset_ball(self, match_state):
if self.match.flags & (1 << 1):
Expand All @@ -809,6 +816,11 @@ async def end_match(self, match_id, winner_id):
self.match.finishedAt = timezone.now()
self.match.scores = self.active_matches[match_id]['scores']
await self.save_match()

if self.match.matchID in self.active_matches:
logger.info(f"[{self.__class__.__name__}] Deleting match state for match: {self.match.matchID}")
del self.active_matches[self.match.matchID]

logger.info(f"[{self.__class__.__name__}] Match {match_id} ended. Winner: {winner_id}")
return {
"type": "match.ended",
Expand All @@ -820,24 +832,40 @@ async def save_match(self):
await loop.run_in_executor(None, self.match.save)

async def send_match_end(self, winner_id):
end_match_data = await self.end_match(self.match.matchID, winner_id)
await self.channel_layer.group_send(
f"match_{self.match.matchID}",
end_match_data
)
logger.info(f"[{self.__class__.__name__}] Match end event sent for match: {self.match.matchID}")
try:
end_match_data = await self.end_match(self.match.matchID, winner_id)
await self.channel_layer.group_send(
f"match_{self.match.matchID}",
end_match_data
)
logger.info(f"[{self.__class__.__name__}] Match end event sent for match: {self.match.matchID}")
except Exception as _:
pass

async def match_ended(self, event):
await self.send_json({
"e": "MATCH_END",
"d": {"won": event["winner"] == self.user.userID}
})
logger.info(f"[{self.__class__.__name__}] Match end event processed for user: {self.user.userID}")
await self.close()
try:
await self.send_json({
"e": "MATCH_END",
"d": {"won": event["winner"] == self.user.userID}
})
logger.info(f"[{self.__class__.__name__}] Match end event processed for user: {self.user.userID}")
await self.close()
except Exception as _:
pass

async def player_join(self, event):
await self.send_json({
"e": "PLAYER_JOIN",
"d": event["player"]
})
logger.info(f"[MatchConsumer] Player join event sent for match: {self.match.matchID}")
player_data = event["player"]

if player_data['userID'] == self.user.userID:
return

self.match.playerB = {"id": player_data['userID'], "platform": ("web" if player_data['userID'] != 'user_ai' else "server")}

try:
await self.send_json({
"e": "PLAYER_JOIN",
"d": player_data
})
logger.info(f"[{self.__class__.__name__}] Player join event sent for match: {self.match.matchID}")
except Exception as _:
pass
6 changes: 3 additions & 3 deletions backend/api/views/leaderboards.py
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ def get(self, request, *args, **kwargs):
return Response({"error": "No stats requested"}, status=400)

response = requests.get('https://statcruncher:9000/leaderboard/daily' + '?stats=' + requested_stats, verify=False)
return Response(response.json(), status=response.status_code)
return Response(response.json() if response else {}, status=response.status_code)

class Weekly(APIView):
def get(self, request, *args, **kwargs):
Expand All @@ -26,7 +26,7 @@ def get(self, request, *args, **kwargs):
return Response({"error": "No stats requested"}, status=400)

response = requests.get('https://statcruncher:9000/leaderboard/weekly' + '?stats=' + requested_stats, verify=False)
return Response(response.json(), status=response.status_code)
return Response(response.json() if response else {}, status=response.status_code)

class Lifetime(APIView):
def get(self, request, *args, **kwargs):
Expand All @@ -36,4 +36,4 @@ def get(self, request, *args, **kwargs):
return Response({"error": "No stats requested"}, status=400)

response = requests.get('https://statcruncher:9000/leaderboard/lifetime' + '?stats=' + requested_stats, verify=False)
return Response(response.json(), status=response.status_code)
return Response(response.json() if response else {}, status=response.status_code)
3 changes: 2 additions & 1 deletion backend/api/views/users.py
Original file line number Diff line number Diff line change
Expand Up @@ -353,7 +353,8 @@ def delete(self, request, relationshipID, *args, **kwargs):
)

if relationship.status == 0:
self.notify_chat_websocket(relationship, status="rejected")
if me.userID != relationship.userA:
self.notify_chat_websocket(relationship, status="rejected")

relationship.delete()

Expand Down

0 comments on commit c3ef8f0

Please sign in to comment.