Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 5 additions & 4 deletions pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -51,10 +51,11 @@
</dependency>

<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<scope>provided</scope>
</dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<version>1.18.38</version>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>org.junit.jupiter</groupId>
<artifactId>junit-jupiter-api</artifactId>
Expand Down
47 changes: 47 additions & 0 deletions src/main/java/com/javarush/khmelov/cmd/Quest.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
package com.javarush.khmelov.cmd;

import com.javarush.khmelov.quest.Answer;
import com.javarush.khmelov.quest.Question;
import lombok.Getter;
import lombok.Setter;

import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.concurrent.atomic.AtomicInteger;

public class Quest {
static AtomicInteger atomicInteger = new AtomicInteger(0);
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Статическое поле 'atomicInteger' для генерации ID разделяется между всеми экземплярами класса в рамках JVM. Это может привести к непредсказуемому поведению в многопоточной среде сервлетов. Лучше перенести генерацию ID на уровень репозитория.

@Setter
@Getter
String win;
@Getter
int id = atomicInteger.getAndIncrement();
@Getter
String name;

public Quest(String name){
this.name = name;
}

Map<Integer, Question> questions = new HashMap<>();
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Поля 'questions' и 'loseMap' имеют доступ по умолчанию (package-private). Согласно принципу инкапсуляции, их следует сделать private.

Map<Integer, String> loseMap = new HashMap<>();
public Question getQuestionById(int i){
return questions.get(i);
}
public void crete(int i,String first, String second, String third, String lose){
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Опечатка в названии метода 'crete'. Следует исправить на 'create' для соблюдения чистоты кода и читаемости.

questions.put(i,
new Question(
i,
first,
List.of(new Answer(second,1),
new Answer(third,2))

));
loseMap.put(i,lose);
}

public String lose(int id){
return loseMap.get(id);
}
}
20 changes: 20 additions & 0 deletions src/main/java/com/javarush/khmelov/quest/Answer.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
package com.javarush.khmelov.quest;


public class Answer {
String text;
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Поля 'text' и 'id' не имеют модификаторов доступа. Их следует сделать private final.

int id;
public Answer(String text,int id) {
this.text = text;
this.id = id;
}

public String getText() {
return text;
}

public int getId() {
return id;
}
}

33 changes: 33 additions & 0 deletions src/main/java/com/javarush/khmelov/quest/Question.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
package com.javarush.khmelov.quest;


import java.util.List;

public class Question {
private final int id;
private final String text;
private final List<Answer> answers;

public Question(int id, String text, List<Answer> answers) {
this.id = id;
this.text = text;
this.answers = answers;
}


// Тоже добавь геттеры



public int getId() {
return id;
}

public String getText() {
return text;
}

public List<Answer> getAnswers() {
return answers;
}
}
34 changes: 34 additions & 0 deletions src/main/java/com/javarush/khmelov/repository/QuestRepository.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
package com.javarush.khmelov.repository;


import com.javarush.khmelov.cmd.Quest;

import java.util.*;
import java.util.concurrent.atomic.AtomicLong;

public class QuestRepository {



private final Map<Integer, Quest> questMap = new HashMap<>();
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Использование HashMap в многопоточной среде (сервлеты) небезопасно. Рекомендуется заменить на ConcurrentHashMap для предотвращения Race Condition.




public Quest getById(int id){
return questMap.get(id);
}

public void crate(Quest quest){
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Опечатка в названии метода 'crate'. Следует переименовать в 'create'.

questMap.put(quest.getId(), quest);
}

public Collection<Quest> getAll() {
return questMap.values();
}






}
19 changes: 19 additions & 0 deletions src/main/java/com/javarush/khmelov/something/AllQuests.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
package com.javarush.khmelov.something;

import jakarta.servlet.ServletException;
import jakarta.servlet.annotation.WebServlet;
import jakarta.servlet.http.HttpServlet;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;

import java.io.IOException;

@WebServlet("/questRepository")
public class AllQuests extends HttpServlet {
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
req.setAttribute("questRepository", Create.questRepository);
req.getRequestDispatcher("/WEB-INF/allQuests.jsp").forward(req, resp);

}
}
34 changes: 34 additions & 0 deletions src/main/java/com/javarush/khmelov/something/Create.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
package com.javarush.khmelov.something;

import com.javarush.khmelov.cmd.Quest;
import com.javarush.khmelov.repository.QuestRepository;
import jakarta.servlet.ServletException;
import jakarta.servlet.annotation.WebServlet;
import jakarta.servlet.http.HttpServlet;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;

import java.io.IOException;

@WebServlet("/create")
public class Create extends HttpServlet {

public static QuestRepository questRepository = new QuestRepository();
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Публичное статическое поле 'questRepository' — это нарушение инкапсуляции и плохая практика управления зависимостями. Репозиторий должен внедряться через конструктор или Winter.find.

Quest quest;
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Поле 'quest' в сервлете приведет к состоянию гонки (Race Condition). Перенесите его в локальную область видимости метода doPost.

@Override
protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
quest = new Quest(NewQuest.name);

quest.setWin(req.getParameter("e"));
for (int i = 0; i < NewQuest.num; i++) {
String first = req.getParameter("a" + i);
String second = req.getParameter("b" + i);
String third = req.getParameter("c" + i);
String fourth = req.getParameter("d" + i);
quest.crete(i, first, second, third,fourth);
}
questRepository.crate(quest);
req.getRequestDispatcher("/WEB-INF/start-page.jsp").forward(req, resp);
}

}
30 changes: 30 additions & 0 deletions src/main/java/com/javarush/khmelov/something/NewQuest.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
package com.javarush.khmelov.something;

import jakarta.servlet.ServletException;
import jakarta.servlet.annotation.WebServlet;
import jakarta.servlet.http.HttpServlet;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;

import java.io.IOException;

@WebServlet("/quest")
public class NewQuest extends HttpServlet {
static int num;
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Объявление переменных 'num' и 'name' как static в сервлете — критическая ошибка. Сервлет создается в единственном экземпляре, и эти данные будут перезаписываться при одновременном обращении разных пользователей.

static String name;
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
req.getRequestDispatcher("/WEB-INF/quest.jsp").forward(req,resp);
}

@Override
protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
name = req.getParameter("name");
num =Integer.parseInt( req.getParameter("num"));
req.setAttribute("num",num);

req.getRequestDispatcher("/WEB-INF/create.jsp").forward(req,resp);


}
}
51 changes: 51 additions & 0 deletions src/main/java/com/javarush/khmelov/something/Play.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
package com.javarush.khmelov.something;

import jakarta.servlet.ServletException;
import jakarta.servlet.annotation.WebServlet;
import jakarta.servlet.http.HttpServlet;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;

import java.io.IOException;

@WebServlet("/play")
public class Play extends HttpServlet {
int qid;
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Поле 'qid' на уровне класса сервлета не является потокобезопасным. Значение, установленное одним пользователем, может быть прочитано другим. Данные состояния должны храниться в HttpSession.

@Override
protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
System.out.println("=== PLAY POST ===");
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Использование System.out.println для логирования недопустимо в продакшн-коде. Рекомендуется использовать логгер (например, SLF4J + Logback).

System.out.println("answerId = " + req.getParameter("answerId"));
System.out.println("questionId = " + req.getParameter("questionId"));
try{
qid = Integer.parseInt(req.getParameter("questId"));
}
catch (Exception e){
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Пустой блок catch (Exception e) 'проглатывает' ошибки. Это затрудняет отладку. Необходимо как минимум логировать исключение.


}

int ans = Integer.parseInt(req.getParameter("answerId"));
int que = Integer.parseInt(req.getParameter("questionId"));
if(ans %2 == 0){
req.setAttribute("lose", Create.questRepository.getById(qid).lose(que));
req.getRequestDispatcher("/WEB-INF/lose.jsp").forward(req, resp);
return;
}

if ( Create.questRepository.getById(qid).getQuestionById(que+1 ) == null){
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Сложная цепочка вызовов (Create.questRepository.getById...). Это нарушает закон Деметры. Следует извлечь объект Quest в локальную переменную.

req.setAttribute("win", Create.questRepository.getById(qid).getWin());
req.getRequestDispatcher("/WEB-INF/win.jsp").forward(req, resp);
return;
}
req.setAttribute("question", Create.questRepository.getById(qid).getQuestionById(que+1 ));

req.getRequestDispatcher("/WEB-INF/game.jsp").forward(req, resp);

}

@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
req.setAttribute("question", Create.questRepository.getById(Integer.parseInt(req.getParameter("id"))).getQuestionById(0));
req.setAttribute("questId",Integer.parseInt(req.getParameter("id")) );
req.getRequestDispatcher("/WEB-INF/game.jsp").forward(req, resp);
}
}
21 changes: 21 additions & 0 deletions src/main/webapp/WEB-INF/allQuests.jsp
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
<%--
Created by IntelliJ IDEA.
User: ADMIN
Date: 2/9/2026
Time: 1:34 PM
To change this template use File | Settings | File Templates.
--%>
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<%@include file="head.jsp" %>
<html>
<body>
<c:forEach var="quest" items="${questRepository.all}">
<a href="play?id=${quest.id}">${quest.name}</a>
</c:forEach>

<a href="/">
<button>На главную</button>
</a>

</body>
</html>
41 changes: 41 additions & 0 deletions src/main/webapp/WEB-INF/create.jsp
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
<%--
Created by IntelliJ IDEA.
User: ADMIN
Date: 2/9/2026
Time: 9:47 AM
To change this template use File | Settings | File Templates.
--%>
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<%@include file="head.jsp"%>
<html>
<head>

</head>
<body>

<h1>Привет тут ты можешь написать свой квест</h1>
<h2>в каждом блоке тебе надо:</h2>
<h2>в первом столбце написать вопрос</h2>
<h2>во втором правильный ответ</h2>
<h2>в третьем неправильный ответ</h2>
<h2>в четвертом что будет за не правильный ответ</h2>
<h2>в последнюю если он победил </h2>
<form method="post" action="create">
<input type="hidden" name="answerId" value="${answer.id}">
<input type="hidden" name="questionId" value="${question.id}">

<c:forEach var="i" begin="0" end="${num-1}">
<div style="border:1px solid #ccc; padding:10px; margin-bottom:15px;">
<input type="text" name="a${i}">
<input type="text" name="b${i}">
<input type="text" name="c${i}">
<input type="text" name="d${i}">
</div>
</c:forEach>
<input type="text" name="e">
<button type="submit">OK</button>
</form>


</body>
</html>
29 changes: 29 additions & 0 deletions src/main/webapp/WEB-INF/game.jsp
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
<%@ page isELIgnored="false" contentType="text/html;charset=UTF-8" language="java" %>
<%@ taglib uri="http://java.sun.com/jsp/jstl/core" prefix="c" %>
<html>
<head>
<title>Space Quest</title>
</head>
<body>
<%-- Берем имя из сессии --%>

<h3>${question.text}</h3>


<%-- Если игра НЕ закончена, рисуем кнопки ответов --%>
<form method="post" action="play">
<input type="hidden" name="questId" value="${questId}">
<input type="hidden" name="questionId" value="${question.id}">

<c:forEach items="${question.answers}" var="answer">
<button type="submit" name="answerId" value="${answer.id}">
${answer.text}
</button>
</c:forEach>
</form>


<%-- Если это финал (победа или поражение) --%>

</body>
</html>
Loading