Skip to content

Commit

Permalink
Merge pull request #38 from PaulKreft/multiplayer
Browse files Browse the repository at this point in the history
Multiplayer
  • Loading branch information
PaulKreft authored Feb 14, 2024
2 parents b28fc2a + e56d939 commit 76eb3b3
Show file tree
Hide file tree
Showing 17 changed files with 591 additions and 7 deletions.
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
package de.neuefische.paulkreft.backend.lobby.controller;

import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.node.ObjectNode;
import de.neuefische.paulkreft.backend.lobby.model.Lobby;
import de.neuefische.paulkreft.backend.lobby.service.LobbyService;
import de.neuefische.paulkreft.backend.users.models.Player;
import lombok.RequiredArgsConstructor;
import org.springframework.web.bind.annotation.*;

@RestController
@RequestMapping("/api/lobby")
@RequiredArgsConstructor
public class LobbyController {
private final LobbyService lobbyService;

@PostMapping
public Lobby createLobby(@RequestBody Lobby lobby) {
return lobbyService.createLobby(lobby);
}

@GetMapping("/{id}")
public Lobby getLobbyById(@PathVariable String id) {
return lobbyService.getLobbyById(id);
}

@PutMapping("/{id}/join")
public Lobby joinLobby(@PathVariable String id, @RequestBody Player player) {
return lobbyService.joinLobby(id, player);
}

@PutMapping
public Lobby updateLobby(@RequestBody Lobby lobby) {
return lobbyService.updateLobby(lobby);
}

@PutMapping("/{id}/leave")
public Lobby leaveLobby(@PathVariable String id, @RequestBody Player player) {
return lobbyService.leaveLobby(id, player);
}

@DeleteMapping("/{id}")
public Lobby deleteLobby(@PathVariable String id) {
return lobbyService.deleteLobby(id);
}

@PutMapping("/{id}/setWinner")
public Lobby setWinner(@PathVariable String id, @RequestBody ObjectNode payload) throws JsonProcessingException {
return lobbyService.setWinner(id, payload);
}

@PutMapping("/{id}/setLoser")
public Lobby setLoser(@PathVariable String id, @RequestBody Player player) {
return lobbyService.setLoser(id, player);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
package de.neuefische.paulkreft.backend.lobby.model;

import de.neuefische.paulkreft.backend.users.models.Player;
import lombok.With;
import org.springframework.data.annotation.Id;

import java.util.List;

@With
public record Lobby(
@Id
String id,
List<Player> players,
boolean isGameInProgress,
boolean isGameOver,
int difficulty,
Player winner,
List<Player> losers,
Integer timeToBeat
) {
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
package de.neuefische.paulkreft.backend.lobby.repository;

import de.neuefische.paulkreft.backend.lobby.model.Lobby;
import org.springframework.data.mongodb.repository.MongoRepository;
import org.springframework.stereotype.Repository;

@Repository
public interface LobbyRepo extends MongoRepository<Lobby, String> {
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,85 @@
package de.neuefische.paulkreft.backend.lobby.service;

import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.node.ObjectNode;
import de.neuefische.paulkreft.backend.lobby.model.Lobby;
import de.neuefische.paulkreft.backend.lobby.repository.LobbyRepo;
import de.neuefische.paulkreft.backend.users.models.Player;
import lombok.RequiredArgsConstructor;
import org.springframework.stereotype.Service;
import org.springframework.web.bind.annotation.RequestBody;

@Service
@RequiredArgsConstructor
public class LobbyService {
private final LobbyRepo lobbyRepo;


public Lobby createLobby(Lobby lobby) {
return lobbyRepo.save(lobby);
}

public Lobby getLobbyById(String id) {
return lobbyRepo.findById(id).orElseThrow(RuntimeException::new);
}

public Lobby joinLobby(String id, @RequestBody Player player) {
Lobby lobby = lobbyRepo.findById(id).orElseThrow(RuntimeException::new);

lobby.players().add(player);
return lobbyRepo.save(lobby);
}

public Lobby updateLobby(Lobby lobby) {
if (!lobbyRepo.existsById(lobby.id())) {
throw new RuntimeException();
}

return lobbyRepo.save(lobby);
}

public Lobby leaveLobby(String id, @RequestBody Player player) {
Lobby lobby = lobbyRepo.findById(id).orElseThrow(RuntimeException::new);

lobby.players().remove(player);
return lobbyRepo.save(lobby);
}

public Lobby deleteLobby(String id) {
Lobby lobby = lobbyRepo.findById(id).orElseThrow(RuntimeException::new);

lobbyRepo.deleteById(id);
return lobby;
}

public Lobby setWinner(String id, ObjectNode payload) throws JsonProcessingException {
Lobby lobby = lobbyRepo.findById(id).orElseThrow(RuntimeException::new);

Player player = new ObjectMapper().treeToValue(payload.get("player"), Player.class);
Integer time = payload.get("time").asInt();


Player winner = lobby.winner();

if (winner != null) {
if (lobby.timeToBeat() <= time) {
lobby.losers().add(player);
return lobbyRepo.save(lobby);
} else {
lobby.losers().add(lobby.winner());
return lobbyRepo.save(lobby.withWinner(player).withTimeToBeat(time));
}
}

return lobbyRepo.save(lobby.withWinner(player).withTimeToBeat(time));
}

public Lobby setLoser(String id, Player loser) {
Lobby lobby = lobbyRepo.findById(id).orElseThrow(RuntimeException::new);

lobby.losers().add(loser);
return lobbyRepo.save(lobby);
}

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
package de.neuefische.paulkreft.backend.users.models;

public record Player(
String id,
String name
) {
}
4 changes: 4 additions & 0 deletions frontend/src/App.tsx
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
import "./App.css";
import { Play } from "./components/Play.tsx";
import { LobbyEntrance } from "./components/LobbyEntrance.tsx";
import { MultiPlayerLobby } from "./components/MultiPlayerLobby.tsx";
import { Route, Routes, useNavigate } from "react-router-dom";
import Home from "./components/Home.tsx";
import Login from "./components/Login.tsx";
Expand Down Expand Up @@ -54,6 +56,8 @@ function App() {
<Routes>
<Route path="/" element={<Home />} />
<Route path="/play" element={<Play userId={user?.id} />} />
<Route path="/multiplayer" element={<LobbyEntrance user={user} />} />
<Route path="/multiplayer/lobby/:id" element={<MultiPlayerLobby user={user} />} />
<Route path="/profile" element={<Profile user={user} />} />
<Route path="/login" element={<Login />} />
<Route path="/login/email" element={<EmailLogin login={login} />} />
Expand Down
11 changes: 8 additions & 3 deletions frontend/src/components/Home.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -12,9 +12,14 @@ export default function Home() {
<span className="cursor-magic text-[#F8F991] hover:text-[#07066e]">x</span>
<span className="cursor-magic text-[#720026] hover:text-[#8dffd9]">!</span>
</h1>
<Link to="/play">
<button className="mt-32 rounded-3xl bg-black px-16 py-6 text-4xl text-white">Play!</button>
</Link>
<div className="flex gap-20">
<Link to="/play">
<button className="mt-32 rounded-3xl bg-black px-16 py-6 text-4xl text-white">Classic</button>
</Link>
<Link to="/multiplayer">
<button className="mt-32 rounded-3xl bg-black px-16 py-6 text-4xl text-white">Multiplayer</button>
</Link>
</div>
</div>
);
}
68 changes: 68 additions & 0 deletions frontend/src/components/LobbyEntrance.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,68 @@
import React, { useState } from "react";
import { useNavigate } from "react-router-dom";
import axios from "axios";
import { Lobby } from "../types/Lobby.ts";
import { Player } from "../types/Player.ts";
import { User } from "../types/User.ts";

type MultiPlayerProps = {
user: User;
};

export const LobbyEntrance: React.FC<MultiPlayerProps> = ({ user }) => {
const [lobbyId, setLobbyId] = useState<string>("");

const navigate = useNavigate();

if (!user) {
return <div>No user found</div>;
}

const currentPlayer: Player = { id: user.id, name: user.name };

const createLobby = (): void => {
const targetLobby = Math.ceil(Math.random() * 1000000).toString();

const lobby: Lobby = {
id: targetLobby,
players: [currentPlayer],
isGameInProgress: false,
isGameOver: false,
difficulty: 4,
losers: [],
};

axios.post("/api/lobby", lobby).then(() => navigate(`/multiplayer/lobby/${targetLobby}`));
};

const joinLobby = (): void => {
axios.put(`/api/lobby/${lobbyId}/join`, currentPlayer).then(() => navigate(`/multiplayer/lobby/${lobbyId}`));
};

return (
<div className="flex h-max flex-1 flex-col items-center px-5 pb-32 pt-20 xs:pb-20 sm:px-10">
<div className="flex h-80 w-96 flex-col items-center justify-evenly gap-5 rounded-2xl border-2 border-black p-5">
<button
className="h-max items-center rounded-lg border-2 border-black px-6 py-2 text-xl font-light hover:bg-black hover:text-white"
onClick={createLobby}
>
Create new lobby
</button>
<div className="flex justify-center gap-5">
<input
className="h-max w-2/5 rounded-lg border-2 border-black px-3 py-1 font-light"
type="text"
value={lobbyId}
onChange={(event) => setLobbyId(event.target.value)}
/>
<button
className="h-max items-center rounded-lg border-2 border-black px-3 py-1 font-light hover:bg-black hover:text-white"
onClick={joinLobby}
>
Join Lobby
</button>
</div>
</div>
</div>
);
};
Loading

0 comments on commit 76eb3b3

Please sign in to comment.