diff --git a/src/main/java/com/alterdekim/game/component/OnlineStatus.java b/src/main/java/com/alterdekim/game/component/OnlineStatus.java new file mode 100644 index 0000000..1dbe865 --- /dev/null +++ b/src/main/java/com/alterdekim/game/component/OnlineStatus.java @@ -0,0 +1,20 @@ +package com.alterdekim.game.component; + +import com.alterdekim.game.service.UserServiceImpl; +import lombok.extern.slf4j.Slf4j; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.scheduling.annotation.Scheduled; +import org.springframework.stereotype.Component; + +@Component +@Slf4j +public class OnlineStatus { + + @Autowired + private UserServiceImpl userService; + + @Scheduled(fixedRate = 50000) + private void resetOnlineStatus() { + userService.setAllOffline(); + } +} diff --git a/src/main/java/com/alterdekim/game/controller/APIController.java b/src/main/java/com/alterdekim/game/controller/APIController.java new file mode 100644 index 0000000..0f93ccd --- /dev/null +++ b/src/main/java/com/alterdekim/game/controller/APIController.java @@ -0,0 +1,165 @@ +package com.alterdekim.game.controller; + + +import com.alterdekim.game.dto.*; +import com.alterdekim.game.entities.Chat; +import com.alterdekim.game.entities.User; +import com.alterdekim.game.service.*; +import lombok.extern.slf4j.Slf4j; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.http.ResponseEntity; +import org.springframework.security.core.Authentication; +import org.springframework.security.core.context.SecurityContextHolder; +import org.springframework.stereotype.Controller; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.PathVariable; +import org.springframework.web.bind.annotation.PostMapping; +import org.springframework.web.bind.annotation.RequestBody; +import org.springframework.web.context.request.async.DeferredResult; + +import java.io.UnsupportedEncodingException; +import java.net.URLDecoder; +import java.util.HashMap; +import java.util.List; +import java.util.concurrent.CompletableFuture; +import java.util.concurrent.TimeUnit; +import java.util.stream.Collectors; + +@Slf4j +@Controller +public class APIController { + + @Autowired + private RoomServiceImpl roomService; + + @Autowired + private RoomPlayerServiceImpl roomPlayerService; + + @Autowired + private ChatServiceImpl chatService; + + @Autowired + private BannerServiceImpl bannerService; + + @Autowired + private TextDataValServiceImpl textDataValService; + + @Autowired + private UserServiceImpl userService; + + @GetMapping("/api/v1/games/list") + public ResponseEntity> gamesList() { + List results = roomService.getAll() + .stream() + .map(room -> new RoomResult(room, roomPlayerService.findByRoomId(room.getId()))) + .collect(Collectors.toList()); + return ResponseEntity.ok(results); + } + + @GetMapping("/api/v1/chat/history/{count}/") + public ResponseEntity chatList(@PathVariable Integer count ) { + List results = chatService.getLastChats(count); + results = results.stream().map( c -> { + String message = c.getMessage(); + for( int i = 0; i < message.length(); i++ ) { + if( message.charAt(i) == '@' ) { + int u = message.substring(i).indexOf(' ') + i; + String username = message.substring(i+1, u); + User user = userService.findByUsername(username); + if( user != null ) { + Long uid = user.getId(); + message = message.substring(0, i) + "" + username + "" + message.substring(u + 1); + } else { + message = message.substring(0, i) + username + message.substring(u + 1); + } + i = 0; + } + } + c.setMessage(message); + return c; + }).collect(Collectors.toList()); + List users = results.stream() + .map(Chat::getUserId) + .distinct() + .map( l -> userService.findById(l) ) + .map( u -> new UserResult(u.getId(), u.getUsername()) ) + .collect(Collectors.toList()); + return ResponseEntity.ok(new ChatResult(results, users)); + } + + @GetMapping("/api/v1/market/banners/{lang}/") + public ResponseEntity> getBanners( @PathVariable String lang ) { + List banners = null; + if( lang.equals("ru") ) { + banners = bannerService.getAllActive() + .stream() + .map( b -> new Banner(b.getId(), + textDataValService.findById(b.getTitleId()).getTextRus(), + textDataValService.findById(b.getDescriptionId()).getTextRus(), + b.getGradientInfo(), + b.getImageUrl())) + .collect(Collectors.toList()); + } else { + banners = bannerService.getAllActive() + .stream() + .map( b -> new Banner(b.getId(), + textDataValService.findById(b.getTitleId()).getTextEng(), + textDataValService.findById(b.getDescriptionId()).getTextEng(), + b.getGradientInfo(), + b.getImageUrl())) + .collect(Collectors.toList()); + } + return ResponseEntity.ok(banners); + } + + @PostMapping("/api/v1/chat/send") + public ResponseEntity sendChat( @RequestBody String message ) { + try { + message = URLDecoder.decode(message, "UTF-8"); + message = message.substring(0, message.length() - 1); + Authentication auth = SecurityContextHolder.getContext().getAuthentication(); + Long userId = userService.findByUsername(((org.springframework.security.core.userdetails.User) auth.getPrincipal()).getUsername()).getId(); + chatService.sendChat(new Chat(userId, message, System.currentTimeMillis() / 1000L)); + } catch (UnsupportedEncodingException e) { + log.error(e.getMessage(), e); + } + return ResponseEntity.accepted().build(); + } + + @GetMapping("/api/v1/notify/get/{last_chat_id}/") + public ResponseEntity getNotify(@PathVariable Long last_chat_id ) { + + Authentication authentication = SecurityContextHolder.getContext().getAuthentication(); + Long userId = userService.findByUsername(((org.springframework.security.core.userdetails.User) authentication.getPrincipal()).getUsername()).getId(); + userService.updateOnline(userId); + + Integer onlineCount = userService.countByIsOnline(); + List results = chatService.getAfterLastChatId(last_chat_id); + List users = results.stream() + .map(Chat::getUserId) + .distinct() + .map( l -> userService.findById(l) ) + .map( u -> new UserResult(u.getId(), u.getUsername()) ) + .collect(Collectors.toList()); + results = results.stream().map( c -> { + String message = c.getMessage(); + for( int i = 0; i < message.length(); i++ ) { + if( message.charAt(i) == '@' ) { + int u = message.substring(i).indexOf(' ') + i; + String username = message.substring(i+1, u); + User user = userService.findByUsername(username); + if( user != null ) { + Long uid = user.getId(); + message = message.substring(0, i) + "" + username + "" + message.substring(u + 1); + } else { + message = message.substring(0, i) + username + message.substring(u + 1); + } + i = 0; + } + } + c.setMessage(message); + return c; + }).collect(Collectors.toList()); + return ResponseEntity.ok(new LongPollResult(onlineCount, results, users)); + } +} diff --git a/src/main/java/com/alterdekim/game/dto/Banner.java b/src/main/java/com/alterdekim/game/dto/Banner.java new file mode 100644 index 0000000..6c9f6e2 --- /dev/null +++ b/src/main/java/com/alterdekim/game/dto/Banner.java @@ -0,0 +1,24 @@ +package com.alterdekim.game.dto; + +import jakarta.persistence.Column; +import jakarta.persistence.GeneratedValue; +import jakarta.persistence.GenerationType; +import jakarta.persistence.Id; +import lombok.AllArgsConstructor; +import lombok.Getter; +import lombok.NoArgsConstructor; + +@AllArgsConstructor +@NoArgsConstructor +@Getter +public class Banner { + private Long id; + + private String title; + + private String description; + + private String gradientInfo; + + private String imageUrl; +} diff --git a/src/main/java/com/alterdekim/game/dto/ChatResult.java b/src/main/java/com/alterdekim/game/dto/ChatResult.java new file mode 100644 index 0000000..7ea5619 --- /dev/null +++ b/src/main/java/com/alterdekim/game/dto/ChatResult.java @@ -0,0 +1,16 @@ +package com.alterdekim.game.dto; + +import com.alterdekim.game.entities.Chat; +import lombok.AllArgsConstructor; +import lombok.Getter; +import lombok.NoArgsConstructor; + +import java.util.List; + +@AllArgsConstructor +@NoArgsConstructor +@Getter +public class ChatResult { + private List messages; + private List users; +} diff --git a/src/main/java/com/alterdekim/game/dto/LongPollResult.java b/src/main/java/com/alterdekim/game/dto/LongPollResult.java new file mode 100644 index 0000000..9c57156 --- /dev/null +++ b/src/main/java/com/alterdekim/game/dto/LongPollResult.java @@ -0,0 +1,17 @@ +package com.alterdekim.game.dto; + +import com.alterdekim.game.entities.Chat; +import lombok.AllArgsConstructor; +import lombok.Getter; +import lombok.NoArgsConstructor; + +import java.util.List; + +@AllArgsConstructor +@NoArgsConstructor +@Getter +public class LongPollResult { + private Integer onlineCount; + private List messages; + private List users; +} diff --git a/src/main/java/com/alterdekim/game/dto/RoomResult.java b/src/main/java/com/alterdekim/game/dto/RoomResult.java new file mode 100644 index 0000000..f6b9a1d --- /dev/null +++ b/src/main/java/com/alterdekim/game/dto/RoomResult.java @@ -0,0 +1,17 @@ +package com.alterdekim.game.dto; + +import com.alterdekim.game.entities.Room; +import com.alterdekim.game.entities.RoomPlayer; +import lombok.AllArgsConstructor; +import lombok.Getter; +import lombok.NoArgsConstructor; + +import java.util.List; + +@AllArgsConstructor +@NoArgsConstructor +@Getter +public class RoomResult { + private Room room; + private List players; +} diff --git a/src/main/java/com/alterdekim/game/dto/UserResult.java b/src/main/java/com/alterdekim/game/dto/UserResult.java new file mode 100644 index 0000000..e938b71 --- /dev/null +++ b/src/main/java/com/alterdekim/game/dto/UserResult.java @@ -0,0 +1,13 @@ +package com.alterdekim.game.dto; + +import lombok.AllArgsConstructor; +import lombok.Getter; +import lombok.NoArgsConstructor; + +@AllArgsConstructor +@NoArgsConstructor +@Getter +public class UserResult { + private Long id; + private String username; +} diff --git a/src/main/java/com/alterdekim/game/entities/Chat.java b/src/main/java/com/alterdekim/game/entities/Chat.java new file mode 100644 index 0000000..fff2b6d --- /dev/null +++ b/src/main/java/com/alterdekim/game/entities/Chat.java @@ -0,0 +1,37 @@ +package com.alterdekim.game.entities; + + +import jakarta.persistence.*; +import jakarta.validation.constraints.NotNull; +import lombok.AllArgsConstructor; +import lombok.Getter; +import lombok.NoArgsConstructor; +import lombok.Setter; + +@Getter +@Setter +@NoArgsConstructor +@AllArgsConstructor +@Entity +@Table(name = "chat_message") +public class Chat { + @Id + @GeneratedValue(strategy = GenerationType.IDENTITY) + private Long id; + + @Column(nullable = false) + private Long userId; + + @Column(length = 955) + @NotNull + private String message; + + @Column(nullable = false) + private Long createdAt; + + public Chat(Long userId, String message, Long createdAt) { + this.userId = userId; + this.message = message; + this.createdAt = createdAt; + } +} diff --git a/src/main/java/com/alterdekim/game/entities/FriendStatus.java b/src/main/java/com/alterdekim/game/entities/FriendStatus.java new file mode 100644 index 0000000..3bc5016 --- /dev/null +++ b/src/main/java/com/alterdekim/game/entities/FriendStatus.java @@ -0,0 +1,35 @@ +package com.alterdekim.game.entities; + + +import jakarta.persistence.*; +import lombok.AllArgsConstructor; +import lombok.Getter; +import lombok.NoArgsConstructor; +import lombok.Setter; + +@Getter +@Setter +@NoArgsConstructor +@AllArgsConstructor +@Entity +@Table(name = "friend_status") +public class FriendStatus { + @Id + @GeneratedValue(strategy = GenerationType.IDENTITY) + private Long id; + + @Column(nullable = false) + private Long firstUserId; + + @Column(nullable = false) + private Long secondUserId; + + @Column(nullable = false) + private Integer status; + + public FriendStatus(Long firstUserId, Long secondUserId, Integer status) { + this.firstUserId = firstUserId; + this.secondUserId = secondUserId; + this.status = status; + } +} diff --git a/src/main/java/com/alterdekim/game/entities/MarketBanner.java b/src/main/java/com/alterdekim/game/entities/MarketBanner.java new file mode 100644 index 0000000..5e67cf4 --- /dev/null +++ b/src/main/java/com/alterdekim/game/entities/MarketBanner.java @@ -0,0 +1,43 @@ +package com.alterdekim.game.entities; + +import jakarta.persistence.*; +import lombok.AllArgsConstructor; +import lombok.Getter; +import lombok.NoArgsConstructor; +import lombok.Setter; + +@Getter +@Setter +@NoArgsConstructor +@AllArgsConstructor +@Entity +@Table(name = "market_banner") +public class MarketBanner { + + @Id + @GeneratedValue(strategy = GenerationType.IDENTITY) + private Long id; + + @Column(nullable = false) + private Boolean isActive; + + @Column(nullable = false) + private Long titleId; + + @Column(nullable = false) + private Long descriptionId; + + @Column(nullable = false) + private String gradientInfo; + + @Column(nullable = false) + private String imageUrl; + + public MarketBanner(Boolean isActive, Long titleId, Long descriptionId, String gradientInfo, String imageUrl) { + this.isActive = isActive; + this.titleId = titleId; + this.descriptionId = descriptionId; + this.gradientInfo = gradientInfo; + this.imageUrl = imageUrl; + } +} diff --git a/src/main/java/com/alterdekim/game/entities/Mission.java b/src/main/java/com/alterdekim/game/entities/Mission.java new file mode 100644 index 0000000..d8f6094 --- /dev/null +++ b/src/main/java/com/alterdekim/game/entities/Mission.java @@ -0,0 +1,42 @@ +package com.alterdekim.game.entities; + +import jakarta.persistence.*; +import lombok.AllArgsConstructor; +import lombok.Getter; +import lombok.NoArgsConstructor; +import lombok.Setter; + +@Getter +@Setter +@NoArgsConstructor +@AllArgsConstructor +@Entity +@Table(name = "mission") +public class Mission { + @Id + @GeneratedValue(strategy = GenerationType.IDENTITY) + private Long id; + + @Column(nullable = false) + private Long nameId; + + @Column(nullable = false) + private Integer xp; + + @Column(nullable = false) + private Integer count; + + @Column(nullable = false) + private Long timestamp; + + @Column(nullable = false) + private Boolean isActive; + + public Mission(Long nameId, Integer xp, Integer count, Long timestamp, Boolean isActive) { + this.nameId = nameId; + this.xp = xp; + this.count = count; + this.timestamp = timestamp; + this.isActive = isActive; + } +} diff --git a/src/main/java/com/alterdekim/game/entities/MissionProgress.java b/src/main/java/com/alterdekim/game/entities/MissionProgress.java new file mode 100644 index 0000000..c01f770 --- /dev/null +++ b/src/main/java/com/alterdekim/game/entities/MissionProgress.java @@ -0,0 +1,30 @@ +package com.alterdekim.game.entities; + +import jakarta.persistence.*; +import lombok.AllArgsConstructor; +import lombok.Getter; +import lombok.NoArgsConstructor; +import lombok.Setter; + +@Getter +@Setter +@NoArgsConstructor +@AllArgsConstructor +@Entity +@Table(name = "mission_progress") +public class MissionProgress { + @Id + @GeneratedValue(strategy = GenerationType.IDENTITY) + private Long id; + + @Column(nullable = false) + private Long missionId; + + @Column(nullable = false) + private Long userId; + + public MissionProgress(Long missionId, Long userId) { + this.missionId = missionId; + this.userId = userId; + } +} \ No newline at end of file diff --git a/src/main/java/com/alterdekim/game/entities/Room.java b/src/main/java/com/alterdekim/game/entities/Room.java new file mode 100644 index 0000000..ed59e35 --- /dev/null +++ b/src/main/java/com/alterdekim/game/entities/Room.java @@ -0,0 +1,34 @@ +package com.alterdekim.game.entities; + +import jakarta.persistence.*; +import lombok.AllArgsConstructor; +import lombok.Getter; +import lombok.NoArgsConstructor; +import lombok.Setter; + +@Getter +@Setter +@NoArgsConstructor +@AllArgsConstructor +@Entity +@Table(name = "room") +public class Room { + @Id + @GeneratedValue(strategy = GenerationType.IDENTITY) + private Long id; + + @Column(nullable = false) + private Integer playerCount; + + @Column(nullable = false) + private Boolean isPrivate; + + @Column(nullable = false) + private String password; + + public Room(Integer playerCount, Boolean isPrivate, String password) { + this.playerCount = playerCount; + this.isPrivate = isPrivate; + this.password = password; + } +} diff --git a/src/main/java/com/alterdekim/game/entities/RoomPlayer.java b/src/main/java/com/alterdekim/game/entities/RoomPlayer.java new file mode 100644 index 0000000..705ce10 --- /dev/null +++ b/src/main/java/com/alterdekim/game/entities/RoomPlayer.java @@ -0,0 +1,30 @@ +package com.alterdekim.game.entities; + +import jakarta.persistence.*; +import lombok.AllArgsConstructor; +import lombok.Getter; +import lombok.NoArgsConstructor; +import lombok.Setter; + +@Getter +@Setter +@NoArgsConstructor +@AllArgsConstructor +@Entity +@Table(name = "room_player") +public class RoomPlayer { + @Id + @GeneratedValue(strategy = GenerationType.IDENTITY) + private Long id; + + @Column(nullable = false) + private Long roomId; + + @Column(nullable = false) + private Long userId; + + public RoomPlayer(Long roomId, Long userId) { + this.roomId = roomId; + this.userId = userId; + } +} diff --git a/src/main/java/com/alterdekim/game/entities/TextDataVal.java b/src/main/java/com/alterdekim/game/entities/TextDataVal.java new file mode 100644 index 0000000..320cf77 --- /dev/null +++ b/src/main/java/com/alterdekim/game/entities/TextDataVal.java @@ -0,0 +1,33 @@ +package com.alterdekim.game.entities; + +import jakarta.persistence.*; +import jakarta.validation.constraints.NotNull; +import lombok.AllArgsConstructor; +import lombok.Getter; +import lombok.NoArgsConstructor; +import lombok.Setter; + +@Getter +@Setter +@NoArgsConstructor +@AllArgsConstructor +@Entity +@Table(name = "textdata_val") +public class TextDataVal { + @Id + @GeneratedValue(strategy = GenerationType.IDENTITY) + private Long id; + + @Column(length = 65555) + @NotNull + private String textEng; + + @Column(length = 65555) + @NotNull + private String textRus; + + public TextDataVal(String textEng, String textRus) { + this.textEng = textEng; + this.textRus = textRus; + } +} diff --git a/src/main/java/com/alterdekim/game/entities/User.java b/src/main/java/com/alterdekim/game/entities/User.java index 9d7df9d..54aaed9 100644 --- a/src/main/java/com/alterdekim/game/entities/User.java +++ b/src/main/java/com/alterdekim/game/entities/User.java @@ -28,6 +28,9 @@ public class User { @Column(nullable=false) private String password; + @Column(columnDefinition = "boolean default false") + private Boolean isOnline; + @ManyToMany(fetch = FetchType.EAGER, cascade=CascadeType.ALL) @JoinTable( name="users_roles", diff --git a/src/main/java/com/alterdekim/game/repository/ChatRepository.java b/src/main/java/com/alterdekim/game/repository/ChatRepository.java new file mode 100644 index 0000000..50cea75 --- /dev/null +++ b/src/main/java/com/alterdekim/game/repository/ChatRepository.java @@ -0,0 +1,20 @@ +package com.alterdekim.game.repository; + +import com.alterdekim.game.entities.Chat; +import com.alterdekim.game.entities.Room; +import org.springframework.data.jpa.repository.JpaRepository; +import org.springframework.data.jpa.repository.Query; +import org.springframework.data.repository.query.Param; +import org.springframework.stereotype.Repository; + +import java.util.List; + +@Repository +public interface ChatRepository extends JpaRepository { + + @Query(value = "SELECT c FROM Chat c ORDER BY c.createdAt DESC LIMIT :count") + List getLastChats(@Param(value = "count") Integer count); + + @Query(value = "SELECT c FROM Chat c WHERE c.id > :lastChatId ORDER BY c.createdAt ASC") + List getAfterLastChatId(@Param(value = "lastChatId") Long lastChatId); +} diff --git a/src/main/java/com/alterdekim/game/repository/MarketBannerRepository.java b/src/main/java/com/alterdekim/game/repository/MarketBannerRepository.java new file mode 100644 index 0000000..73177c0 --- /dev/null +++ b/src/main/java/com/alterdekim/game/repository/MarketBannerRepository.java @@ -0,0 +1,14 @@ +package com.alterdekim.game.repository; + +import com.alterdekim.game.entities.Chat; +import com.alterdekim.game.entities.MarketBanner; +import org.springframework.data.jpa.repository.JpaRepository; +import org.springframework.data.jpa.repository.Query; + +import java.util.List; + +public interface MarketBannerRepository extends JpaRepository { + + @Query(value = "SELECT m FROM MarketBanner m WHERE m.isActive = true") + List getAllActive(); +} diff --git a/src/main/java/com/alterdekim/game/repository/RoomPlayerRepository.java b/src/main/java/com/alterdekim/game/repository/RoomPlayerRepository.java new file mode 100644 index 0000000..a34efe6 --- /dev/null +++ b/src/main/java/com/alterdekim/game/repository/RoomPlayerRepository.java @@ -0,0 +1,13 @@ +package com.alterdekim.game.repository; + +import com.alterdekim.game.entities.Room; +import com.alterdekim.game.entities.RoomPlayer; +import org.springframework.data.jpa.repository.JpaRepository; +import org.springframework.stereotype.Repository; + +import java.util.List; + +@Repository +public interface RoomPlayerRepository extends JpaRepository { + List findByRoomId(Long roomId); +} diff --git a/src/main/java/com/alterdekim/game/repository/RoomRepository.java b/src/main/java/com/alterdekim/game/repository/RoomRepository.java new file mode 100644 index 0000000..40a7a74 --- /dev/null +++ b/src/main/java/com/alterdekim/game/repository/RoomRepository.java @@ -0,0 +1,10 @@ +package com.alterdekim.game.repository; + +import com.alterdekim.game.entities.Room; +import com.alterdekim.game.entities.User; +import org.springframework.data.jpa.repository.JpaRepository; +import org.springframework.stereotype.Repository; + +@Repository +public interface RoomRepository extends JpaRepository { +} \ No newline at end of file diff --git a/src/main/java/com/alterdekim/game/repository/TextDataValRepository.java b/src/main/java/com/alterdekim/game/repository/TextDataValRepository.java new file mode 100644 index 0000000..2adac91 --- /dev/null +++ b/src/main/java/com/alterdekim/game/repository/TextDataValRepository.java @@ -0,0 +1,13 @@ +package com.alterdekim.game.repository; + +import com.alterdekim.game.entities.Room; +import com.alterdekim.game.entities.TextDataVal; +import org.springframework.data.jpa.repository.JpaRepository; +import org.springframework.stereotype.Repository; + +import java.util.Optional; + +@Repository +public interface TextDataValRepository extends JpaRepository { + Optional findById(Long id); +} diff --git a/src/main/java/com/alterdekim/game/repository/UserRepository.java b/src/main/java/com/alterdekim/game/repository/UserRepository.java index 78520e2..285f84d 100644 --- a/src/main/java/com/alterdekim/game/repository/UserRepository.java +++ b/src/main/java/com/alterdekim/game/repository/UserRepository.java @@ -2,9 +2,25 @@ package com.alterdekim.game.repository; import com.alterdekim.game.entities.User; import org.springframework.data.jpa.repository.JpaRepository; +import org.springframework.data.jpa.repository.Modifying; +import org.springframework.data.jpa.repository.Query; +import org.springframework.data.repository.query.Param; import org.springframework.stereotype.Repository; +import org.springframework.transaction.annotation.Transactional; @Repository -public interface UserRepository extends JpaRepository { +public interface UserRepository extends JpaRepository { User findByUsername(String username); + + @Transactional + @Modifying + @Query(value = "UPDATE User u SET u.isOnline = true WHERE u.id = :uuid") + void setOnline(@Param(value = "uuid") Long id); + + @Transactional + @Modifying + @Query(value = "UPDATE User u SET u.isOnline = false") + void setAllOffline(); + + Integer countByIsOnline(boolean isOnline); } diff --git a/src/main/java/com/alterdekim/game/security/SpringSecurity.java b/src/main/java/com/alterdekim/game/security/SpringSecurity.java index 7ff4c49..2ddae27 100644 --- a/src/main/java/com/alterdekim/game/security/SpringSecurity.java +++ b/src/main/java/com/alterdekim/game/security/SpringSecurity.java @@ -7,11 +7,13 @@ import org.springframework.context.annotation.Configuration; import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder; import org.springframework.security.config.annotation.web.builders.HttpSecurity; import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity; +import org.springframework.security.config.annotation.web.configuration.WebSecurityCustomizer; import org.springframework.security.core.userdetails.UserDetailsService; import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder; import org.springframework.security.crypto.password.PasswordEncoder; import org.springframework.security.web.SecurityFilterChain; import org.springframework.security.web.access.AccessDeniedHandler; +import org.springframework.security.web.firewall.StrictHttpFirewall; import org.springframework.security.web.savedrequest.HttpSessionRequestCache; import org.springframework.security.web.util.matcher.AntPathRequestMatcher; @@ -31,6 +33,8 @@ public class SpringSecurity { authorize .requestMatchers("/game").hasAnyAuthority("ROLE_ADMIN") .requestMatchers("/games").hasAnyAuthority("ROLE_ADMIN") + .requestMatchers("/profile/**").hasAnyAuthority("ROLE_ADMIN") + .requestMatchers("/api/**").hasAnyAuthority("ROLE_ADMIN") .requestMatchers("/static/**").permitAll() .requestMatchers("/access-denied").permitAll() .requestMatchers("/signup").permitAll() @@ -75,5 +79,12 @@ public class SpringSecurity { public static PasswordEncoder passwordEncoder(){ return new BCryptPasswordEncoder(); } + + @Bean + public WebSecurityCustomizer webSecurityCustomizer() { + StrictHttpFirewall firewall = new StrictHttpFirewall(); + firewall.setAllowSemicolon(true); + return (web) -> web.httpFirewall(firewall); + } } diff --git a/src/main/java/com/alterdekim/game/service/BannerService.java b/src/main/java/com/alterdekim/game/service/BannerService.java new file mode 100644 index 0000000..c35fcb4 --- /dev/null +++ b/src/main/java/com/alterdekim/game/service/BannerService.java @@ -0,0 +1,9 @@ +package com.alterdekim.game.service; + +import com.alterdekim.game.entities.MarketBanner; + +import java.util.List; + +public interface BannerService { + List getAllActive(); +} diff --git a/src/main/java/com/alterdekim/game/service/BannerServiceImpl.java b/src/main/java/com/alterdekim/game/service/BannerServiceImpl.java new file mode 100644 index 0000000..98b1bda --- /dev/null +++ b/src/main/java/com/alterdekim/game/service/BannerServiceImpl.java @@ -0,0 +1,22 @@ +package com.alterdekim.game.service; + +import com.alterdekim.game.entities.MarketBanner; +import com.alterdekim.game.repository.MarketBannerRepository; +import org.springframework.stereotype.Service; + +import java.util.List; + +@Service +public class BannerServiceImpl implements BannerService { + + private final MarketBannerRepository repository; + + public BannerServiceImpl(MarketBannerRepository repository) { + this.repository = repository; + } + + @Override + public List getAllActive() { + return repository.getAllActive(); + } +} diff --git a/src/main/java/com/alterdekim/game/service/ChatService.java b/src/main/java/com/alterdekim/game/service/ChatService.java new file mode 100644 index 0000000..1e1cd22 --- /dev/null +++ b/src/main/java/com/alterdekim/game/service/ChatService.java @@ -0,0 +1,13 @@ +package com.alterdekim.game.service; + +import com.alterdekim.game.entities.Chat; + +import java.util.List; + +public interface ChatService { + List getLastChats(Integer count); + + void sendChat(Chat chat); + + List getAfterLastChatId(Long lastChatId); +} diff --git a/src/main/java/com/alterdekim/game/service/ChatServiceImpl.java b/src/main/java/com/alterdekim/game/service/ChatServiceImpl.java new file mode 100644 index 0000000..664c8ca --- /dev/null +++ b/src/main/java/com/alterdekim/game/service/ChatServiceImpl.java @@ -0,0 +1,32 @@ +package com.alterdekim.game.service; + +import com.alterdekim.game.entities.Chat; +import com.alterdekim.game.repository.ChatRepository; +import org.springframework.stereotype.Service; + +import java.util.List; + +@Service +public class ChatServiceImpl implements ChatService { + + private final ChatRepository chatRepository; + + public ChatServiceImpl(ChatRepository chatRepository) { + this.chatRepository = chatRepository; + } + + @Override + public List getLastChats(Integer count) { + return chatRepository.getLastChats(count); + } + + @Override + public void sendChat(Chat chat) { + chatRepository.save(chat); + } + + @Override + public List getAfterLastChatId(Long lastChatId) { + return chatRepository.getAfterLastChatId(lastChatId); + } +} diff --git a/src/main/java/com/alterdekim/game/service/RoomPlayerService.java b/src/main/java/com/alterdekim/game/service/RoomPlayerService.java new file mode 100644 index 0000000..0753aed --- /dev/null +++ b/src/main/java/com/alterdekim/game/service/RoomPlayerService.java @@ -0,0 +1,12 @@ +package com.alterdekim.game.service; + +import com.alterdekim.game.entities.Room; +import com.alterdekim.game.entities.RoomPlayer; + +import java.util.List; + +public interface RoomPlayerService { + List getAll(); + + List findByRoomId(Long roomId); +} diff --git a/src/main/java/com/alterdekim/game/service/RoomPlayerServiceImpl.java b/src/main/java/com/alterdekim/game/service/RoomPlayerServiceImpl.java new file mode 100644 index 0000000..c75f660 --- /dev/null +++ b/src/main/java/com/alterdekim/game/service/RoomPlayerServiceImpl.java @@ -0,0 +1,27 @@ +package com.alterdekim.game.service; + +import com.alterdekim.game.entities.RoomPlayer; +import com.alterdekim.game.repository.RoomPlayerRepository; +import org.springframework.stereotype.Service; + +import java.util.List; + +@Service +public class RoomPlayerServiceImpl implements RoomPlayerService{ + + private final RoomPlayerRepository repository; + + public RoomPlayerServiceImpl(RoomPlayerRepository repository) { + this.repository = repository; + } + + @Override + public List getAll() { + return repository.findAll(); + } + + @Override + public List findByRoomId(Long roomId) { + return repository.findByRoomId(roomId); + } +} diff --git a/src/main/java/com/alterdekim/game/service/RoomService.java b/src/main/java/com/alterdekim/game/service/RoomService.java new file mode 100644 index 0000000..56685a4 --- /dev/null +++ b/src/main/java/com/alterdekim/game/service/RoomService.java @@ -0,0 +1,9 @@ +package com.alterdekim.game.service; + +import com.alterdekim.game.entities.Room; + +import java.util.List; + +public interface RoomService { + List getAll(); +} diff --git a/src/main/java/com/alterdekim/game/service/RoomServiceImpl.java b/src/main/java/com/alterdekim/game/service/RoomServiceImpl.java new file mode 100644 index 0000000..e0039f8 --- /dev/null +++ b/src/main/java/com/alterdekim/game/service/RoomServiceImpl.java @@ -0,0 +1,21 @@ +package com.alterdekim.game.service; + +import com.alterdekim.game.entities.Room; +import com.alterdekim.game.repository.RoomRepository; +import org.springframework.stereotype.Service; + +import java.util.List; + +@Service +public class RoomServiceImpl implements RoomService { + private final RoomRepository roomRepository; + + public RoomServiceImpl(RoomRepository roomRepository) { + this.roomRepository = roomRepository; + } + + @Override + public List getAll() { + return roomRepository.findAll(); + } +} diff --git a/src/main/java/com/alterdekim/game/service/TextDataValService.java b/src/main/java/com/alterdekim/game/service/TextDataValService.java new file mode 100644 index 0000000..04e218e --- /dev/null +++ b/src/main/java/com/alterdekim/game/service/TextDataValService.java @@ -0,0 +1,7 @@ +package com.alterdekim.game.service; + +import com.alterdekim.game.entities.TextDataVal; + +public interface TextDataValService { + TextDataVal findById(Long id); +} diff --git a/src/main/java/com/alterdekim/game/service/TextDataValServiceImpl.java b/src/main/java/com/alterdekim/game/service/TextDataValServiceImpl.java new file mode 100644 index 0000000..f50260d --- /dev/null +++ b/src/main/java/com/alterdekim/game/service/TextDataValServiceImpl.java @@ -0,0 +1,20 @@ +package com.alterdekim.game.service; + +import com.alterdekim.game.entities.TextDataVal; +import com.alterdekim.game.repository.TextDataValRepository; +import org.springframework.stereotype.Service; + +@Service +public class TextDataValServiceImpl implements TextDataValService { + + private final TextDataValRepository textDataValRepository; + + public TextDataValServiceImpl(TextDataValRepository textDataValRepository) { + this.textDataValRepository = textDataValRepository; + } + + @Override + public TextDataVal findById(Long id) { + return textDataValRepository.findById(id).orElse(null); + } +} diff --git a/src/main/java/com/alterdekim/game/service/UserService.java b/src/main/java/com/alterdekim/game/service/UserService.java index 559a7bf..8da2bdc 100644 --- a/src/main/java/com/alterdekim/game/service/UserService.java +++ b/src/main/java/com/alterdekim/game/service/UserService.java @@ -11,5 +11,7 @@ public interface UserService { User findByUsername(String usernane); List findAllUsers(); + + User findById(Long id); } diff --git a/src/main/java/com/alterdekim/game/service/UserServiceImpl.java b/src/main/java/com/alterdekim/game/service/UserServiceImpl.java index 98f2d71..7a6344c 100644 --- a/src/main/java/com/alterdekim/game/service/UserServiceImpl.java +++ b/src/main/java/com/alterdekim/game/service/UserServiceImpl.java @@ -53,6 +53,11 @@ public class UserServiceImpl implements UserService { .collect(Collectors.toList()); } + @Override + public User findById(Long id) { + return userRepository.findById(id).orElse(null); + } + private UserDTO convertEntityToDto(User user){ UserDTO userDto = new UserDTO(); userDto.setUsername(user.getUsername()); @@ -64,5 +69,17 @@ public class UserServiceImpl implements UserService { role.setName("ROLE_ADMIN"); return roleRepository.save(role); } + + public void updateOnline(Long userId) { + userRepository.setOnline(userId); + } + + public void setAllOffline() { + userRepository.setAllOffline(); + } + + public Integer countByIsOnline() { + return userRepository.countByIsOnline(true); + } } diff --git a/src/main/resources/static/css/style.css b/src/main/resources/static/css/style.css index 35ee32e..2eebaba 100644 --- a/src/main/resources/static/css/style.css +++ b/src/main/resources/static/css/style.css @@ -32,6 +32,14 @@ footer { background-color: #37bc9d; color: #fff; border-color: #37bc9d; + --bs-btn-active-bg: #37bc9d; +} + +.btn-primary:active { + background-color: #37bc9d; + color: #fff; + border-color: #37bc9d; + --bs-btn-active-bg: #37bc9d; } .btn-primary:hover { @@ -49,4 +57,10 @@ footer { align-items: center; justify-content: space-between; gap: 0.2em; +} + +.navbar-profile-img { + width: 2.3rem; + height: 2.3rem; + border-radius: 50%; } \ No newline at end of file diff --git a/src/main/resources/static/javascript/games.js b/src/main/resources/static/javascript/games.js new file mode 100644 index 0000000..16eec97 --- /dev/null +++ b/src/main/resources/static/javascript/games.js @@ -0,0 +1,142 @@ +$.fn.pressEnter = function(fn) { + + return this.each(function() { + $(this).bind('enterPress', fn); + $(this).keyup(function(e){ + if(e.keyCode == 13) + { + $(this).trigger("enterPress"); + } + }) + }); + }; + + var last_chat_id = 0; + +function findUser(users, id) { + for( let i = 0; i < users.length; i++ ) { + if( users[i].id == id ) { + return users[i]; + } + } +} + +function parseTime(unix_timestamp) { + var date = new Date(unix_timestamp * 1000); + var hours = date.getHours(); + var minutes = "0" + date.getMinutes(); + return hours + ':' + minutes.substr(-2); +} + +function replyButtonClicked(obj) { + let userid = $(obj).attr('data-userid'); + let username = $(obj).attr('data-username'); + $('#chat-message-input').val($('#chat-message-input').val() + " @" + username + ""); +} + +setInterval(function() { + $.ajax({ + url: "/api/v1/notify/get/" + last_chat_id + "/", + method: "GET" + }).done(function(data) { + console.log(data); + let onlineCount = data.onlineCount; + $(".chat-title").find("span").html(onlineCount + " online"); + let messages = data.messages; + let users = data.users; + if( messages.length > 0 ) { + last_chat_id = messages[0].id; + } + for( let i = 0; i < messages.length; i++ ) { + let obj = messages[i]; + let time = parseTime(obj.createdAt); + let username = findUser(users, obj.userId).username; + let userid = obj.userId; + let msgtext = obj.message; + let html = '
'+time+'
'+username+''+msgtext+'
'; + $(".chat-history").append(html); + } + $("#chat_list").find(".chat-history").css("display", ""); + $("#chat_list").find(".message-input").css("display", ""); + $("#chat_list").find(".block-content").css("display", "none"); + }); +}, 5000); + +$(document).ready(function() { + $("#players-count-range").on("input", function() { + $("label[for='players-count-range']").find("span").text($(this).val()); + }); + + $('#chat-message-input').pressEnter(function() { + let txt = $(this).val(); + $(this).val(""); + if( txt != "" ) { + $.ajax({ + url: "/api/v1/chat/send", + method: "POST", + data: txt + }); + } + }); + + $.ajax({ + url: "/api/v1/games/list", + method: "GET" + }).done(function(data) { + console.log(data); + }); + + // block-content + + $.ajax({ + url: "/api/v1/chat/history/100/", + method: "GET" + }).done(function(data) { + console.log(data); + let messages = data.messages; + let users = data.users; + if( messages.length > 0 ) { + last_chat_id = messages[0].id; + } + for( let i = messages.length-1; i >= 0; i-- ) { + let obj = messages[i]; + let time = parseTime(obj.createdAt); + let username = findUser(users, obj.userId).username; + let userid = obj.userId; + let msgtext = obj.message; + let html = '
'+time+'
'+username+''+msgtext+'
'; + $(".chat-history").append(html); + } + $("#chat_list").find(".chat-history").css("display", ""); + $("#chat_list").find(".message-input").css("display", ""); + $("#chat_list").find(".block-content").css("display", "none"); + }); + + $.ajax({ + url: "/api/v1/market/banners/en/", + method: "GET" + }).done(function(data) { + console.log(data); + for( var i = 0; i < data.length; i++ ) { + let obj = data[i]; + let tag = ''; + if( i != 0 ) { + let html = ''; + $("#market-carousel").find(".carousel-indicators").append(html); + } else { + tag = 'active'; + } + let _ihtml = ''; + $("#market-carousel").find(".carousel-inner").append(_ihtml); + } + $(".market").find(".spinner-grow").css("display", "none"); + $(".market").find("#market-carousel").css("display", ""); + }); + + /*$.ajax({ + url: "/api/v1/games/list", + method: "GET" + }).done(function(data) { + console.log(data); + });*/ +}); \ No newline at end of file diff --git a/src/main/resources/static/javascript/scale.js b/src/main/resources/static/javascript/scale.js new file mode 100644 index 0000000..562ea6e --- /dev/null +++ b/src/main/resources/static/javascript/scale.js @@ -0,0 +1,11 @@ +function resizeTable() { + var $window = $(window); + var height = window.innerHeight; + var theight = $('.game').height(); + var scale = height / theight; + $('.game').css('transform', 'scale(' + scale + ')'); +} +resizeTable(); +$(window).resize(function(evt) { + resizeTable(); +}); \ No newline at end of file diff --git a/src/main/resources/templates/fragments/navbar.html b/src/main/resources/templates/fragments/navbar.html index e38e37a..80421d5 100644 --- a/src/main/resources/templates/fragments/navbar.html +++ b/src/main/resources/templates/fragments/navbar.html @@ -2,7 +2,8 @@