Skip to content

Commit

Permalink
Kicking players
Browse files Browse the repository at this point in the history
  • Loading branch information
hopperelec committed Mar 26, 2024
1 parent b8e9967 commit 8aefd08
Show file tree
Hide file tree
Showing 22 changed files with 245 additions and 54 deletions.
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
-- AlterTable
ALTER TABLE `Player` ADD COLUMN `kicked` BOOLEAN NOT NULL DEFAULT false;
1 change: 1 addition & 0 deletions prisma/schema.prisma
Original file line number Diff line number Diff line change
Expand Up @@ -125,6 +125,7 @@ model Player {
currMove Room? @relation("roomQueue", fields: [currMoveId], references: [id])
currMoveId Int? @db.UnsignedInt
points Int @default(0) @db.UnsignedInt
kicked Boolean @default(false)
@@unique([userId, gameId])
}
Expand Down
40 changes: 40 additions & 0 deletions src/lib/Kickable.svelte
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
<script lang="ts">
export let userId: number;
async function kickPlayer(userId: number) {
const res = await fetch("../kick/" + userId + "/", { method: "POST" });
if (!res.ok) alert((await res.json()).message);
}
</script>

<button
on:click={async () => await kickPlayer(userId)}
tabindex="0"
type="button"
>
<slot/>
</button>

<style>
button {
border: 0;
padding: 0;
background: none;
position: relative;
font: inherit;
&:hover {
cursor: pointer;
/* Strikethrough, including icons */
&::after {
content: "";
position: absolute;
width: calc(100% + 10px);
top: 50%;
left: -5px;
border-top: 2px solid black;
}
}
}
</style>
24 changes: 18 additions & 6 deletions src/lib/Leaderboard.svelte
Original file line number Diff line number Diff line change
@@ -1,16 +1,23 @@
<script lang="ts">
export let orderedPlayers: {
name: string;
points: number;
}[];
import type { LeaderboardPlayer } from "$lib/types";
import Kickable from "$lib/Kickable.svelte";
export let orderedPlayers: LeaderboardPlayer[];
</script>

<h3>Leaderboard</h3>
<ol>
{#each orderedPlayers as player}
<li>
<span>{player.name}</span> -
<span>{player.points} point{player.points === 1 ? "" : "s"}</span>
{#if player.kickable}
<Kickable userId={player.id}>
<span>{player.name}</span> -
<span>{player.points} point{player.points === 1 ? "" : "s"}</span>
</Kickable>
{:else}
<span>{player.name}</span> -
<span>{player.points} point{player.points === 1 ? "" : "s"}</span>
{/if}
</li>
{/each}
</ol>
Expand All @@ -25,5 +32,10 @@
ol {
overflow-y: auto;
margin-left: 10px; /* Counter li padding */
}
li {
margin-right: 10px; /* Prevent overflow for Kickable hover */
}
</style>
1 change: 0 additions & 1 deletion src/lib/button.css
Original file line number Diff line number Diff line change
Expand Up @@ -14,5 +14,4 @@
flex-flow: wrap;
justify-content: center;
text-align: center;

}
4 changes: 4 additions & 0 deletions src/lib/server/get-player-id.ts
Original file line number Diff line number Diff line change
Expand Up @@ -57,9 +57,12 @@ export default async function getPlayerId(
select: {
id: true,
game: { select: { state: true } },
kicked: true,
},
});
if (player) {
if (player.kicked) error(403, "You've been kicked from this game!");
/* eslint-disable no-fallthrough */
switch (player.game.state) {
case "LOBBY":
redirect(303, "/game/" + gameId + "/lobby/");
Expand All @@ -68,6 +71,7 @@ export default async function getPlayerId(
case "ENDED":
redirect(301, "/game/" + gameId + "/end/");
}
/* eslint-enable no-fallthrough */
}
return await addPlayer(gameId, user);
}
6 changes: 6 additions & 0 deletions src/lib/types.d.ts
Original file line number Diff line number Diff line change
@@ -1 +1,7 @@
export type LocalDoor = { svgRef1: number; svgRef2: number };
export type LeaderboardPlayer = {
id: number;
name: string;
points: number;
kickable?: boolean;
};
3 changes: 3 additions & 0 deletions src/routes/game/[gameId=id]/+layout.server.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
export const load = async ({ locals }) => {
return { userId: locals.user.id };
};
16 changes: 14 additions & 2 deletions src/routes/game/[gameId=id]/+layout.svelte
Original file line number Diff line number Diff line change
Expand Up @@ -3,11 +3,23 @@
import { page } from "$app/stores";
import { goto } from "$app/navigation";
export let data;
const announcement = getChannel(
"game:" + $page.params.gameId + ":announcements",
);
$: if ($announcement?.name == "end") {
goto("/game/" + $page.params.gameId + "/end/");
$: if ($announcement) {
/* eslint-disable no-fallthrough */
switch ($announcement.name) {
case "end":
goto("/game/" + $page.params.gameId + "/end/");
case "kick":
if ($announcement.data.userId == data.userId) {
alert("You've been kicked from this game!");
goto("/");
}
}
/* eslint-enable no-fallthrough */
}
</script>

Expand Down
1 change: 1 addition & 0 deletions src/routes/game/[gameId=id]/+page.server.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ export const load = async ({ params, locals }) => {
select: {
map: { select: { id: true, imgURL: true } },
players: {
where: { kicked: false },
select: {
user: { select: { id: true, picture: true } },
currRoom: { select: { svgRef: true } },
Expand Down
13 changes: 13 additions & 0 deletions src/routes/game/[gameId=id]/+page.svelte
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@
}
movePlayer($positionsMessage.data.userId, svgRef);
}
const playerMessage = getChannel(
"player:" + $page.params.gameId + ":" + data.userId,
);
Expand All @@ -40,6 +41,18 @@
data.currPoints = $playerMessage.data.points;
}
const announcement = getChannel(
"game:" + $page.params.gameId + ":announcements",
);
$: if ($announcement?.name == "kick") {
const userId = $announcement.data.userId;
delete data.players[userId];
if (map) {
const icon = map.getElmWhere("user", userId) as SVGImageElement;
if (icon) map.removeIcon(icon);
}
}
function movePlayer(userId: number, svgRef: number) {
data.players[userId].currSvgRef = svgRef;
// A position update could occur before the map has finished loading.
Expand Down
1 change: 1 addition & 0 deletions src/routes/game/[gameId=id]/answer/+page.server.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ export const load = async ({ params, locals, url }) => {
where: {
userId_gameId: { userId: locals.user.id, gameId: +params.gameId },
game: { state: "ONGOING" },
kicked: false,
},
select: {
id: true,
Expand Down
5 changes: 4 additions & 1 deletion src/routes/game/[gameId=id]/answer/+server.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,10 @@ import { moveRoom } from "$lib/server/ably-server";
export const POST = async ({ request, params, locals }) => {
const gameId = +params.gameId;
const player = await prisma.player.findUnique({
where: { userId_gameId: { userId: locals.user.id, gameId } },
where: {
userId_gameId: { userId: locals.user.id, gameId },
kicked: false,
},
select: {
id: true,
currQuestion: { select: { answerRegex: true } },
Expand Down
4 changes: 1 addition & 3 deletions src/routes/game/[gameId=id]/end/+page.server.ts
Original file line number Diff line number Diff line change
Expand Up @@ -16,9 +16,7 @@ async function endGame(gameId: number) {
export const load = async ({ params, locals }) => {
const gameId = +params.gameId;
const player = await prisma.player.findUnique({
where: {
userId_gameId: { userId: locals.user.id, gameId },
},
where: { userId_gameId: { userId: locals.user.id, gameId } },
select: {
isHost: true,
points: true,
Expand Down
38 changes: 38 additions & 0 deletions src/routes/game/[gameId=id]/kick/[userId=id]/+server.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
import prisma from "$lib/server/prisma";
import { error } from "@sveltejs/kit";
import ablyServer from "$lib/server/ably-server";

export const POST = async ({ locals, params }) => {
const requestedUserId = +params.userId;
if (requestedUserId == locals.user.id) error(403, "You can't kick yourself!");

const gameId = +params.gameId;
const requestingPlayer = await prisma.player.findUnique({
where: {
userId_gameId: { userId: locals.user.id, gameId },
isHost: true,
},
select: { game: { select: { state: true } } },
});
if (!requestingPlayer) error(403, "You are not a host of this game!");
if (requestingPlayer.game.state == "ENDED")
error(403, "You can't kick a player after the game has ended!");

const requestedPlayer = await prisma.player.findUnique({
where: { userId_gameId: { userId: requestedUserId, gameId } },
select: { isHost: true, kicked: true },
});
if (!requestedPlayer) error(403, "This user isn't in your game!");
if (requestedPlayer.isHost) error(403, "You can't kick a host!");
if (requestedPlayer.kicked) error(403, "This player is already kicked");

await prisma.player.update({
where: { userId_gameId: { userId: requestedUserId, gameId } },
data: { kicked: true },
select: { id: true },
});
ablyServer.channels
.get("game:" + gameId + ":announcements")
.publish("kick", { userId: requestedUserId });
return new Response();
};
8 changes: 6 additions & 2 deletions src/routes/game/[gameId=id]/leaderboard/+page.server.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,13 +8,14 @@ export const load = async ({ params, locals }) => {
where: { id: playerId },
select: {
isHost: true,
user: { select: { id: true } },
game: {
select: {
players: {
where: { kicked: false },
select: {
user: { select: { id: true, name: true } },
points: true,
isHost: true,
},
},
},
Expand All @@ -27,11 +28,14 @@ export const load = async ({ params, locals }) => {
"An unexpected error occurred while trying to retrieve your player data",
);
return {
userId: ret.user.id,
userId: locals.user.id,
isHost: ret.isHost,
players: ret.game.players.map((player) => {
return {
points: player.points,
kickable: ret.isHost
? !player.isHost && player.user.id != locals.user.id
: undefined,
...player.user,
};
}),
Expand Down
20 changes: 16 additions & 4 deletions src/routes/game/[gameId=id]/leaderboard/+page.svelte
Original file line number Diff line number Diff line change
Expand Up @@ -4,18 +4,19 @@
import { page } from "$app/stores";
import "$lib/button.css";
import getDisplayName from "$lib/get-display-name";
import type { LeaderboardPlayer } from "$lib/types";
export let data;
const players = data.players.reduce(
let players = data.players.reduce(
(acc, player) => {
acc[player.id] = {
...player,
name: getDisplayName(player),
points: player.points,
};
}
return acc;
},
{} as { [key: number]: { name: string; points: number } },
{} as { [key: number]: LeaderboardPlayer },
);
$: orderedPlayers = Object.values(players).sort(
(a, b) => b.points - a.points,
Expand All @@ -29,11 +30,22 @@
break;
case "create":
players[$pointsMessage.data.userId] = {
id: $pointsMessage.data.userId,
name: getDisplayName($pointsMessage.data),
points: 0,
kickable: data.isHost,
};
}
}
const announcement = getChannel(
"game:" + $page.params.gameId + ":announcements",
);
$: if ($announcement?.name == "kick") {
const userId = $announcement.data.userId;
delete players[userId];
players = players; // trigger reactivity
}
</script>

{#if data.isHost}<a id="end" class="button" href="../end">End game</a>{/if}
Expand Down
25 changes: 16 additions & 9 deletions src/routes/game/[gameId=id]/lobby/+page.server.ts
Original file line number Diff line number Diff line change
Expand Up @@ -16,13 +16,18 @@ export const load = async ({ params, locals }) => {
players: {
select: {
isHost: true,
kicked: true,
user: { select: { id: true, name: true, picture: true } },
},
},
},
});
if (!game) error(403, "You do not have access to this game's lobby!");
if (!game.players.some((player) => player.user.id == locals.user.id)) {

const self = game.players.filter(
(player) => player.user.id == locals.user.id,
)[0];
if (!self) {
const spawnpoint = await chooseSpawnpoint(game.mapId);
await prisma.player.create({
data: {
Expand All @@ -36,16 +41,18 @@ export const load = async ({ params, locals }) => {
name: locals.user.name,
picture: locals.user.picture,
};
game.players.push({ isHost: false, user });
game.players.push({ isHost: false, kicked: false, user });
ablyServer.channels.get("game:" + gameId + ":lobby").publish("join", user);
}
} else if (self.kicked) error(403, "You've been kicked from this game!");
return {
userId: locals.user.id,
players: game.players.map((player) => {
return {
isHost: player.isHost,
...player.user,
};
}),
players: game.players
.filter((player) => !player.kicked)
.map((player) => {
return {
isHost: player.isHost,
...player.user,
};
}),
};
};
Loading

0 comments on commit 8aefd08

Please sign in to comment.