From 5c8778ca9659e0744f2eacda6fdc55ce4bca3837 Mon Sep 17 00:00:00 2001 From: alterdekim Date: Sat, 8 Jun 2024 01:14:06 +0300 Subject: [PATCH] ladder start x2. --- pom.xml | 18 +++++ .../alterdekim/hearthhack/Application.java | 2 + .../hearthhack/component/GameConnection.java | 58 ++++++++++++--- .../hearthhack/component/GamePool.java | 71 +++++++++++++++++++ .../hearthhack/component/GameRoom.java | 19 +++++ .../hearthhack/component/GameServer.java | 2 +- .../processor/GameMasterProcessor.java | 2 - .../processor/client/request/GetDeck.java | 1 - .../alterdekim/hearthhack/dto/UserDTO.java | 26 +++++++ .../alterdekim/hearthhack/entity/Role.java | 27 +++++++ .../alterdekim/hearthhack/entity/Room.java | 23 ++++++ .../hearthhack/entity/RoomPlayer.java | 31 ++++++++ .../alterdekim/hearthhack/entity/User.java | 40 +++++++++++ .../hearthhack/repository/RoleRepository.java | 13 ++++ .../repository/RoomPlayerRepository.java | 32 +++++++++ .../hearthhack/repository/RoomRepository.java | 20 ++++++ .../hearthhack/repository/UserRepository.java | 10 +++ .../hearthhack/security/SpringSecurity.java | 60 ++++++++++++++++ .../hearthhack/service/RoomPlayerService.java | 40 +++++++++++ .../hearthhack/service/RoomService.java | 35 +++++++++ .../hearthhack/service/UserService.java | 67 +++++++++++++++++ .../com/alterdekim/hearthhack/util/Hash.java | 35 +++++++++ .../hearthhack/util/PegasusPacket.java | 19 +++-- 23 files changed, 633 insertions(+), 18 deletions(-) create mode 100644 src/main/java/com/alterdekim/hearthhack/component/GamePool.java create mode 100644 src/main/java/com/alterdekim/hearthhack/component/GameRoom.java create mode 100644 src/main/java/com/alterdekim/hearthhack/dto/UserDTO.java create mode 100644 src/main/java/com/alterdekim/hearthhack/entity/Role.java create mode 100644 src/main/java/com/alterdekim/hearthhack/entity/Room.java create mode 100644 src/main/java/com/alterdekim/hearthhack/entity/RoomPlayer.java create mode 100644 src/main/java/com/alterdekim/hearthhack/entity/User.java create mode 100644 src/main/java/com/alterdekim/hearthhack/repository/RoleRepository.java create mode 100644 src/main/java/com/alterdekim/hearthhack/repository/RoomPlayerRepository.java create mode 100644 src/main/java/com/alterdekim/hearthhack/repository/RoomRepository.java create mode 100644 src/main/java/com/alterdekim/hearthhack/repository/UserRepository.java create mode 100644 src/main/java/com/alterdekim/hearthhack/security/SpringSecurity.java create mode 100644 src/main/java/com/alterdekim/hearthhack/service/RoomPlayerService.java create mode 100644 src/main/java/com/alterdekim/hearthhack/service/RoomService.java create mode 100644 src/main/java/com/alterdekim/hearthhack/service/UserService.java create mode 100644 src/main/java/com/alterdekim/hearthhack/util/Hash.java diff --git a/pom.xml b/pom.xml index 89ee7d8..3f6d824 100644 --- a/pom.xml +++ b/pom.xml @@ -72,6 +72,24 @@ reflections 0.10.2 + + org.springframework.boot + spring-boot-starter-security + + + org.thymeleaf.extras + thymeleaf-extras-springsecurity6 + + 3.1.1.RELEASE + + + org.springframework.boot + spring-boot-starter-thymeleaf + + + org.springframework.boot + spring-boot-starter-web + diff --git a/src/main/java/com/alterdekim/hearthhack/Application.java b/src/main/java/com/alterdekim/hearthhack/Application.java index 0f25d4f..e66cf75 100644 --- a/src/main/java/com/alterdekim/hearthhack/Application.java +++ b/src/main/java/com/alterdekim/hearthhack/Application.java @@ -3,8 +3,10 @@ package com.alterdekim.hearthhack; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; +import org.springframework.context.annotation.Bean; import org.springframework.scheduling.annotation.EnableAsync; import org.springframework.scheduling.annotation.EnableScheduling; +import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor; @SpringBootApplication @EnableScheduling diff --git a/src/main/java/com/alterdekim/hearthhack/component/GameConnection.java b/src/main/java/com/alterdekim/hearthhack/component/GameConnection.java index 8f61526..21455bd 100644 --- a/src/main/java/com/alterdekim/hearthhack/component/GameConnection.java +++ b/src/main/java/com/alterdekim/hearthhack/component/GameConnection.java @@ -1,13 +1,13 @@ package com.alterdekim.hearthhack.component; import com.alterdekim.PegasusGame; -import com.alterdekim.hearthhack.util.BattleNetPacket; import com.alterdekim.hearthhack.util.PegasusPacket; import com.alterdekim.hearthhack.util.Util; import lombok.RequiredArgsConstructor; import lombok.extern.slf4j.Slf4j; import java.io.ByteArrayOutputStream; +import java.io.IOException; import java.io.InputStream; import java.io.OutputStream; import java.net.Socket; @@ -18,15 +18,30 @@ public class GameConnection extends Thread { private final Socket client; private OutputStream outToClient; + private Long id; + + private boolean isLoggedIn = false; + public void stopListeningAndDisconnect() { - log.warn("Tried to stopListening"); + try { + this.client.close(); + this.interrupt(); + } catch (IOException e) { + log.error(e.getMessage()); + } } private void processPacket( PegasusPacket packet ) throws Exception { + log.info("PegasusPacket: type={}, context={}, size={}", packet.getType(), packet.getContext(), packet.getSize()); + if( !isLoggedIn && packet.getType() != 168 ) stopListeningAndDisconnect(); switch (packet.getType()) { case 168: + log.info("Handshake: {}", PegasusGame.Handshake.parseFrom((byte[]) packet.getBody())); + // game_handle: gameId + // client_handle: userId + // password: roomPassword PegasusGame.GameSetup setup = PegasusGame.GameSetup.newBuilder() - .setBoard(0) + .setBoard(2) .setMaxFriendlyMinionsPerPlayer(7) .setMaxSecretsPerPlayer(5) .setKeepAliveFrequencySeconds(30) @@ -34,22 +49,49 @@ public class GameConnection extends Thread { .build(); PegasusPacket result = new PegasusPacket(16, 0, setup); this.send(result); + this.isLoggedIn = true; break; case 1: // GetGameState - this.sendRaw(Util.hexStringToByteArray("0AA6022AA3020A2F0801120408351001120508C6011004120408311001120508CC011002120408141001120508CA0110011204080A1055127D0802120F08C786D1BAA58080800210E7F2AC251800226608031204083210021204083510031205088F031003120508B001100A12040807104B120408311001120508CA011002120508900210011204081D10041204081C100A1204081E10021204081F10021204081710011204081110011204081B104212040818100112710801120F08C786D1BAA58080800210B59DFA241800225A08021204083210011204083510021205088F031004120508B001100A12040807104B120508CA011002120508900210011204081D10041204081C100A1204081E10011204081F10011204083110011204081110011204081B10400A180A16082212001A04083510221A04083210021A04083110020A180A16082312001A04083510231A04083210021A04083110020A180A16082412001A04083510241A04083210021A04083110020A620A60082512074558315F3539331A04083510251A04083210021A0508870210031A04083110031A0508C10210011A0508B70110021A0508DA0110011A04082D10021A04082F10041A04083010041A0508CA0110041A0508CB0110021A0508C90110030A93010A9001082612074558315F3338331A04083510261A04083210021A0508870210011A04083110031A0508FB0110011A0508C20110001A0508C10210011A0508B70110031A0508BE0110001A0508CA0210001A0508CB0210001A04082D10101A04082F10031A04087210011A04083010031A0508C70110051A0508CA0110041A0508CB0110051A0508D90110001A0508C90110030A180A16082712001A04083510271A04083210021A04083110020A180A16082812001A04083510281A04083210021A04083110020A180A16082912001A04083510291A04083210021A04083110020A180A16082A12001A040835102A1A04083210021A04083110020A180A16082B12001A040835102B1A04083210021A04083110020A180A16082C12001A040835102C1A04083210021A04083110020A180A16082D12001A040835102D1A04083210021A04083110020A180A16082E12001A040835102E1A04083210021A04083110020A180A16082F12001A040835102F1A04083210021A04083110020A180A16083012001A04083510301A04083210021A04083110020A180A16083112001A04083510311A04083210021A04083110020A180A16083212001A04083510321A04083210021A04083110020A180A16083312001A04083510331A04083210021A04083110020A180A16083412001A04083510341A04083210021A04083110020A180A16083512001A04083510351A04083210021A04083110020A490A47083612084C4F454131365F391A04083510361A04083210021A0508870210021A04083110031A04083010001A0508CA0210001A0508CB0210001A0508CA0110051A0508B70110140A180A16083712001A04083510371A04083210021A04083110020A180A16083812001A04083510381A04083210021A04083110020A180A16083912001A04083510391A04083210021A04083110020A180A16083A12001A040835103A1A04083210021A04083110020A180A16083B12001A040835103B1A04083210021A04083110020A180A16083C12001A040835103C1A04083210021A04083110020A180A16083D12001A040835103D1A04083210021A04083110020A180A16083E12001A040835103E1A04083210021A04083110020A180A16083F12001A040835103F1A04083210021A04083110020A180A16080412001A04083510041A04083210011A04083110020A180A16080512001A04083510051A04083210011A04083110020A180A16080612001A04083510061A04083210011A04083110020A180A16080712001A04083510071A04083210011A04083110020A180A16080812001A04083510081A04083210011A04083110020A180A16080912001A04083510091A04083210011A04083110020A180A16080A12001A040835100A1A04083210011A04083110020A1F0A1D080B12001A040835100B1A04083210011A0508870210041A04083110030A180A16080C12001A040835100C1A04083210011A04083110020A180A16080D12001A040835100D1A04083210011A04083110020A180A16080E12001A040835100E1A04083210011A04083110020A180A16080F12001A040835100F1A04083210011A04083110020A180A16081012001A04083510101A04083210011A04083110020A1F0A1D081112001A04083510111A04083210011A0508870210031A04083110030A180A16081212001A04083510121A04083210011A04083110020A180A16081312001A04083510131A04083210011A04083110020A180A16081412001A04083510141A04083210011A04083110020A1F0A1D081512001A04083510151A04083210011A0508870210011A04083110030A180A16081612001A04083510161A04083210011A04083110020A180A16081712001A04083510171A04083210011A04083110020A180A16081812001A04083510181A04083210011A04083110020A180A16081912001A04083510191A04083210011A04083110020A1F0A1D081A12001A040835101A1A04083210011A0508870210021A04083110030A180A16081B12001A040835101B1A04083210011A04083110020A180A16081C12001A040835101C1A04083210011A04083110020A180A16081D12001A040835101D1A04083210011A04083110020A180A16081E12001A040835101E1A04083210011A04083110020A180A16081F12001A040835101F1A04083210011A04083110020A180A16082012001A04083510201A04083210011A04083110020A180A16082112001A04083510211A04083210011A04083110020A4B0A49084212094C4F454130345F30311A04083110011A04083510421A04083210021A0508B70110141A0608FC0210E77E1A0508CA0210001A0508CB0210001A04082D101E1A0508CA0110030A5D0A5B08431208435331685F3030311A0508B90210421A04083110011A04083510431A04083210021A04083110011A0508FB0110011A0508B70110021A04083010021A0508C70110061A0508CA01100A1A0508CB0110021A0508C90110030A460A440840120B54425F5350545F426F73731A04083110011A04083510401A04083210011A04082F10001A04082D101E1A0508CA0110031A0508B70110121A0708FC0210F1B4020A5D0A5B08411208435331685F3030311A0508B90210401A04083110011A04083510411A04083210011A04083110011A0508FB0110011A0508B70110021A04083010021A0508C70110061A0508CA01100A1A0508CB0110021A0508C90110030A1F0A1D084412001A04083510441A04083210011A0508870210051A0408311003")); + PegasusGame.GetGameState state = PegasusGame.GetGameState.parseFrom((byte[]) packet.getBody()); + log.info("GetGameState: {}", state); + this.send(new PegasusPacket( 19, 0, Util.hexStringToByteArray("0AA6022AA3020A2F0801120408351001120508C6011004120408311001120508CC011002120408141001120508CA0110011204080A1055127D0802120F08C786D1BAA58080800210E7F2AC251800226608031204083210021204083510031205088F031003120508B001100A12040807104B120408311001120508CA011002120508900210011204081D10041204081C100A1204081E10021204081F10021204081710011204081110011204081B104212040818100112710801120F08C786D1BAA58080800210B59DFA241800225A08021204083210011204083510021205088F031004120508B001100A12040807104B120508CA011002120508900210011204081D10041204081C100A1204081E10011204081F10011204083110011204081110011204081B10400A180A16082212001A04083510221A04083210021A04083110020A180A16082312001A04083510231A04083210021A04083110020A180A16082412001A04083510241A04083210021A04083110020A620A60082512074558315F3539331A04083510251A04083210021A0508870210031A04083110031A0508C10210011A0508B70110021A0508DA0110011A04082D10021A04082F10041A04083010041A0508CA0110041A0508CB0110021A0508C90110030A93010A9001082612074558315F3338331A04083510261A04083210021A0508870210011A04083110031A0508FB0110011A0508C20110001A0508C10210011A0508B70110031A0508BE0110001A0508CA0210001A0508CB0210001A04082D10101A04082F10031A04087210011A04083010031A0508C70110051A0508CA0110041A0508CB0110051A0508D90110001A0508C90110030A180A16082712001A04083510271A04083210021A04083110020A180A16082812001A04083510281A04083210021A04083110020A180A16082912001A04083510291A04083210021A04083110020A180A16082A12001A040835102A1A04083210021A04083110020A180A16082B12001A040835102B1A04083210021A04083110020A180A16082C12001A040835102C1A04083210021A04083110020A180A16082D12001A040835102D1A04083210021A04083110020A180A16082E12001A040835102E1A04083210021A04083110020A180A16082F12001A040835102F1A04083210021A04083110020A180A16083012001A04083510301A04083210021A04083110020A180A16083112001A04083510311A04083210021A04083110020A180A16083212001A04083510321A04083210021A04083110020A180A16083312001A04083510331A04083210021A04083110020A180A16083412001A04083510341A04083210021A04083110020A180A16083512001A04083510351A04083210021A04083110020A490A47083612084C4F454131365F391A04083510361A04083210021A0508870210021A04083110031A04083010001A0508CA0210001A0508CB0210001A0508CA0110051A0508B70110140A180A16083712001A04083510371A04083210021A04083110020A180A16083812001A04083510381A04083210021A04083110020A180A16083912001A04083510391A04083210021A04083110020A180A16083A12001A040835103A1A04083210021A04083110020A180A16083B12001A040835103B1A04083210021A04083110020A180A16083C12001A040835103C1A04083210021A04083110020A180A16083D12001A040835103D1A04083210021A04083110020A180A16083E12001A040835103E1A04083210021A04083110020A180A16083F12001A040835103F1A04083210021A04083110020A180A16080412001A04083510041A04083210011A04083110020A180A16080512001A04083510051A04083210011A04083110020A180A16080612001A04083510061A04083210011A04083110020A180A16080712001A04083510071A04083210011A04083110020A180A16080812001A04083510081A04083210011A04083110020A180A16080912001A04083510091A04083210011A04083110020A180A16080A12001A040835100A1A04083210011A04083110020A1F0A1D080B12001A040835100B1A04083210011A0508870210041A04083110030A180A16080C12001A040835100C1A04083210011A04083110020A180A16080D12001A040835100D1A04083210011A04083110020A180A16080E12001A040835100E1A04083210011A04083110020A180A16080F12001A040835100F1A04083210011A04083110020A180A16081012001A04083510101A04083210011A04083110020A1F0A1D081112001A04083510111A04083210011A0508870210031A04083110030A180A16081212001A04083510121A04083210011A04083110020A180A16081312001A04083510131A04083210011A04083110020A180A16081412001A04083510141A04083210011A04083110020A1F0A1D081512001A04083510151A04083210011A0508870210011A04083110030A180A16081612001A04083510161A04083210011A04083110020A180A16081712001A04083510171A04083210011A04083110020A180A16081812001A04083510181A04083210011A04083110020A180A16081912001A04083510191A04083210011A04083110020A1F0A1D081A12001A040835101A1A04083210011A0508870210021A04083110030A180A16081B12001A040835101B1A04083210011A04083110020A180A16081C12001A040835101C1A04083210011A04083110020A180A16081D12001A040835101D1A04083210011A04083110020A180A16081E12001A040835101E1A04083210011A04083110020A180A16081F12001A040835101F1A04083210011A04083110020A180A16082012001A04083510201A04083210011A04083110020A180A16082112001A04083510211A04083210011A04083110020A4B0A49084212094C4F454130345F30311A04083110011A04083510421A04083210021A0508B70110141A0608FC0210E77E1A0508CA0210001A0508CB0210001A04082D101E1A0508CA0110030A5D0A5B08431208435331685F3030311A0508B90210421A04083110011A04083510431A04083210021A04083110011A0508FB0110011A0508B70110021A04083010021A0508C70110061A0508CA01100A1A0508CB0110021A0508C90110030A460A440840120B54425F5350545F426F73731A04083110011A04083510401A04083210011A04082F10001A04082D101E1A0508CA0110031A0508B70110121A0708FC0210F1B4020A5D0A5B08411208435331685F3030311A0508B90210401A04083110011A04083510411A04083210011A04083110011A0508FB0110011A0508B70110021A04083010021A0508C70110061A0508CA01100A1A0508CB0110021A0508C90110030A1F0A1D084412001A04083510441A04083210011A0508870210051A0408311003"))); + + this.send(new PegasusPacket( 19, 0, Util.hexStringToByteArray("0A0822060801100A18550A092207080110CA0118010A0822060801101418010A092207080110CC0118020A0822060801103118010A092207080110C60118040A0822060801103518010A0822060801101318040A0822060802101B18400A0822060802101118010A0822060802103118010A0822060802101F18010A0822060802101E18010A0822060802101C180A0A0822060802101D18040A092207080210900218010A092207080210CA0118020A08220608021007184B0A092207080210B001180A0A0922070802108F0318040A0822060802103518020A0822060802103218010A092207080210B10218010A0822060803101818010A0822060803101B18420A0822060803101118010A0822060803101718010A0822060803101F18020A0822060803101E18020A0822060803101C180A0A0822060803101D18040A092207080310900218010A092207080310CA0118020A0822060803103118010A08220608031007184B0A092207080310B001180A0A0922070803108F0318030A0822060803103518030A0822060803103218020A133211080510FFFFFFFFFFFFFFFFFF0118012000"))); + + this.send(new PegasusPacket( 17, 0, Util.hexStringToByteArray("0801100120002803320336262538014002"))); + + this.send(new PegasusPacket( 17, 0, Util.hexStringToByteArray("08011001200028053205441A15110B38014001"))); + this.send(new PegasusPacket( 19, 0, Util.hexStringToByteArray("0A092207080310B1021801"))); + + this.send(new PegasusPacket( 15, 0, Util.hexStringToByteArray("0A0A08001000181A2000280010001801"))); + this.send(new PegasusPacket( 15, 0, Util.hexStringToByteArray("0A0A0800100018152000280010001801"))); + this.send(new PegasusPacket( 15, 0, Util.hexStringToByteArray("0A0A08001000181A2000280010001801"))); + this.send(new PegasusPacket( 15, 0, Util.hexStringToByteArray("0A0A0800100018112000280010001801"))); + this.send(new PegasusPacket( 15, 0, Util.hexStringToByteArray("0A0A08001000180B2000280010001801"))); + this.send(new PegasusPacket( 15, 0, Util.hexStringToByteArray("0A0A0800100018112000280010001801"))); + this.send(new PegasusPacket( 15, 0, Util.hexStringToByteArray("0A0A0800100018002000280010001801"))); + this.send(new PegasusPacket( 19, 0, Util.hexStringToByteArray("0A092207080310B10218020A0A320808051006180320000A092207080310B10218030A023A000A0A320808051007180320000A092207080310B10218040A023A00"))); + this.send(new PegasusPacket( 19, 0, Util.hexStringToByteArray("0A092207080210B10218020A0A320808051006180220000A092207080210B10218030A023A000A0A320808051007180220000A092207080210B10218040A092207080110C60118060A023A00"))); + this.send(new PegasusPacket( 19, 0, Util.hexStringToByteArray("0A0822060801101318060A0A320808051001180320000A0822060803101A18010A0922070803108F0318000A092207080110C60118110A023A000A0822060801101318110A0A320808051008180320000A092207080110C60118090A023A000A0822060801101318090A0A320808051000180320000A441242083C12074756475F3031301A0508C10210011A0508B701100D1A0508CA0210001A0508CB0210001A04083010001A0508C70110061A0508CA0110051A0508CB0110010A082206083C103118030A092207083C10870218040A0922070803108F0318010A092207080110C601180A0A023A000A08220608011013180A0A0A320808051002180320000A092207080110C601180C0A023A00"))); + this.send(new PegasusPacket( 14, 0, Util.hexStringToByteArray("080112020802120A0803120608431A024042120608031202083C12060803120208361206080312020826"))); + this.send(new PegasusPacket( 15, 0, Util.hexStringToByteArray("0A0A0800100018402000280010001801"))); + this.send(new PegasusPacket( 15, 0, Util.hexStringToByteArray("0A0A0800100018002000280010001801"))); + this.send(new PegasusPacket( 19, 0, Util.hexStringToByteArray("0A0A320808071000182620000A0822060826102B18010A092207082510870218020A092207083610870218010A092207083C10870218030A133211080310FFFFFFFFFFFFFFFFFF01180020000A023A000A3E0A3C084512094353325F31303365321A04083510451A04083210021A0508CB0210001A0508CA0210001A0508C701100A1A0508CA0110061A0508B70110020A0822060845102818260A092207084510870218000A0822060845103118060A0822060826103118010A092207082610870218010A0822060826102B18010A023A00"))); + this.send(new PegasusPacket( 14, 0, Util.hexStringToByteArray("080112020802120B0803120708431A03404226120608031202083C1206080312020836"))); break; case 115: - PegasusGame.Pong pong = PegasusGame.Pong.newBuilder().build(); - result = new PegasusPacket(116, 0, pong); - this.send(result); + pong(); break; default: - log.info("PegasusPacket: type={}, context={}, size={}", packet.getType(), packet.getContext(), packet.getSize()); break; } } + private void pong() throws Exception { + this.send(new PegasusPacket(116, 0, PegasusGame.Pong.newBuilder().build())); + } + public void send(PegasusPacket pp) throws Exception { this.outToClient.write(pp.Encode()); this.outToClient.flush(); diff --git a/src/main/java/com/alterdekim/hearthhack/component/GamePool.java b/src/main/java/com/alterdekim/hearthhack/component/GamePool.java new file mode 100644 index 0000000..2ae4d6e --- /dev/null +++ b/src/main/java/com/alterdekim/hearthhack/component/GamePool.java @@ -0,0 +1,71 @@ +package com.alterdekim.hearthhack.component; + +import com.alterdekim.hearthhack.entity.RoomPlayer; +import com.alterdekim.hearthhack.entity.User; +import com.alterdekim.hearthhack.service.RoomPlayerService; +import com.alterdekim.hearthhack.service.RoomService; +import com.alterdekim.hearthhack.service.UserService; +import com.alterdekim.hearthhack.util.Hash; +import lombok.extern.slf4j.Slf4j; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.scheduling.annotation.Scheduled; +import org.springframework.stereotype.Component; + +import java.io.IOException; +import java.security.NoSuchAlgorithmException; +import java.util.List; +import java.util.Optional; +import java.util.concurrent.ConcurrentHashMap; + +@Slf4j +@Component +public class GamePool { + + @Autowired + private UserService userService; + + @Autowired + private RoomService roomService; + + @Autowired + private RoomPlayerService roomPlayerService; + + private ConcurrentHashMap games = new ConcurrentHashMap<>(); + + private static final int PLAYERS_COUNT = 2; + + @Scheduled(fixedRate = 1000) + private void refreshRooms() { + roomService.getAll() + .forEach(r -> { + if( roomPlayerService.findByRoomId(r.getId()).size() != PLAYERS_COUNT ) return; + List players = roomPlayerService.findByRoomId(r.getId()); + roomPlayerService.removeByRoomId(r.getId()); + roomService.removeRoom(r.getId()); + games.put(r.getId(), new GameRoom(players, userService)); + }); + } + + public Boolean containsPlayer(Long userId) { + return games.keySet() + .stream() + .anyMatch(k -> games.get(k) + .getPlayers() + .stream() + .anyMatch(p -> p.getId().longValue() == userId.longValue() + ) + ); + } + + public Optional getGameIdByPlayerId(Long userId) { + return games.keySet() + .stream() + .filter(k -> games.get(k) + .getPlayers() + .stream() + .anyMatch(p -> p.getId().longValue() == userId.longValue() + ) + ) + .findFirst(); + } +} diff --git a/src/main/java/com/alterdekim/hearthhack/component/GameRoom.java b/src/main/java/com/alterdekim/hearthhack/component/GameRoom.java new file mode 100644 index 0000000..9f65c53 --- /dev/null +++ b/src/main/java/com/alterdekim/hearthhack/component/GameRoom.java @@ -0,0 +1,19 @@ +package com.alterdekim.hearthhack.component; + +import com.alterdekim.hearthhack.entity.RoomPlayer; +import com.alterdekim.hearthhack.entity.User; +import com.alterdekim.hearthhack.service.UserService; +import lombok.AllArgsConstructor; +import lombok.Getter; +import lombok.extern.slf4j.Slf4j; + +import java.util.List; + +@Slf4j +@AllArgsConstructor +public class GameRoom { + @Getter + private List players; + + private UserService userService; +} diff --git a/src/main/java/com/alterdekim/hearthhack/component/GameServer.java b/src/main/java/com/alterdekim/hearthhack/component/GameServer.java index 5cbbb5f..f3e46f1 100644 --- a/src/main/java/com/alterdekim/hearthhack/component/GameServer.java +++ b/src/main/java/com/alterdekim/hearthhack/component/GameServer.java @@ -46,7 +46,7 @@ public class GameServer { } } - public void removeConnection(TcpConnection c) { + public void removeConnection(GameConnection c) { connections.remove(c); } } diff --git a/src/main/java/com/alterdekim/hearthhack/component/processor/GameMasterProcessor.java b/src/main/java/com/alterdekim/hearthhack/component/processor/GameMasterProcessor.java index 53738a3..6e039ad 100644 --- a/src/main/java/com/alterdekim/hearthhack/component/processor/GameMasterProcessor.java +++ b/src/main/java/com/alterdekim/hearthhack/component/processor/GameMasterProcessor.java @@ -23,14 +23,12 @@ public class GameMasterProcessor extends Processor { // FindGameRequest FindGameResponse game_master // GameType Protocol.FindGameRequest gameRequest = Protocol.FindGameRequest.parseFrom(packet.getBody()); - log.info("FindGameRequest: {}", gameRequest); Protocol.Attribute a = gameRequest.getProperties().getCreationAttributesList().stream() .filter(p -> p.hasName() && p.getName().equals("type")) .findFirst() .get(); GameType gameType = GameType.parseFromInt((int) a.getValue().getIntValue()); - log.info("YUUUP {}", gameType); if( gameType != GameType.GT_CASUAL ) return; Protocol.FindGameResponse response = Protocol.FindGameResponse.newBuilder() .setQueued(true) diff --git a/src/main/java/com/alterdekim/hearthhack/component/processor/client/request/GetDeck.java b/src/main/java/com/alterdekim/hearthhack/component/processor/client/request/GetDeck.java index e8dd3bb..8087dcc 100644 --- a/src/main/java/com/alterdekim/hearthhack/component/processor/client/request/GetDeck.java +++ b/src/main/java/com/alterdekim/hearthhack/component/processor/client/request/GetDeck.java @@ -40,7 +40,6 @@ public class GetDeck extends ClientRequestParser { Protocol.GetDeckContentsResponse.Builder deckContentsResponse = Protocol.GetDeckContentsResponse.newBuilder(); for( long id : request.getDeckIdList() ) { - log.info("DeckId: {}", id); deckContentsResponse.addDecks( Protocol.DeckContents.newBuilder() .setSuccess(true) diff --git a/src/main/java/com/alterdekim/hearthhack/dto/UserDTO.java b/src/main/java/com/alterdekim/hearthhack/dto/UserDTO.java new file mode 100644 index 0000000..9fe08ca --- /dev/null +++ b/src/main/java/com/alterdekim/hearthhack/dto/UserDTO.java @@ -0,0 +1,26 @@ +package com.alterdekim.hearthhack.dto; + +import jakarta.validation.constraints.NotEmpty; +import lombok.AllArgsConstructor; +import lombok.Getter; +import lombok.NoArgsConstructor; +import lombok.Setter; + +@Getter +@Setter +@NoArgsConstructor +@AllArgsConstructor +public class UserDTO { + private Long id; + @NotEmpty(message = "Username should not be empty") + private String username; + @NotEmpty(message = "Password should not be empty") + private String password; + @NotEmpty(message = "Invite code should not be empty") + private String invite_code; + private Boolean terms_and_conditions_accept; + + private String lang; +} + + diff --git a/src/main/java/com/alterdekim/hearthhack/entity/Role.java b/src/main/java/com/alterdekim/hearthhack/entity/Role.java new file mode 100644 index 0000000..0d03090 --- /dev/null +++ b/src/main/java/com/alterdekim/hearthhack/entity/Role.java @@ -0,0 +1,27 @@ +package com.alterdekim.hearthhack.entity; + +import jakarta.persistence.*; +import lombok.AllArgsConstructor; +import lombok.Getter; +import lombok.NoArgsConstructor; +import lombok.Setter; + +import java.util.List; + +@Setter +@Getter +@NoArgsConstructor +@AllArgsConstructor +@Entity +@Table(name="roles") +public class Role { + @Id + @GeneratedValue(strategy = GenerationType.IDENTITY) + private Long id; + + @Column(nullable=false, unique=true) + private String name; + + @ManyToMany(mappedBy="roles") + private List users; +} \ No newline at end of file diff --git a/src/main/java/com/alterdekim/hearthhack/entity/Room.java b/src/main/java/com/alterdekim/hearthhack/entity/Room.java new file mode 100644 index 0000000..767e7f4 --- /dev/null +++ b/src/main/java/com/alterdekim/hearthhack/entity/Room.java @@ -0,0 +1,23 @@ +package com.alterdekim.hearthhack.entity; + +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 String roomPassword; +} + diff --git a/src/main/java/com/alterdekim/hearthhack/entity/RoomPlayer.java b/src/main/java/com/alterdekim/hearthhack/entity/RoomPlayer.java new file mode 100644 index 0000000..c8beb59 --- /dev/null +++ b/src/main/java/com/alterdekim/hearthhack/entity/RoomPlayer.java @@ -0,0 +1,31 @@ +package com.alterdekim.hearthhack.entity; + +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/hearthhack/entity/User.java b/src/main/java/com/alterdekim/hearthhack/entity/User.java new file mode 100644 index 0000000..f9087a5 --- /dev/null +++ b/src/main/java/com/alterdekim/hearthhack/entity/User.java @@ -0,0 +1,40 @@ +package com.alterdekim.hearthhack.entity; + +import jakarta.persistence.*; +import lombok.AllArgsConstructor; +import lombok.Getter; +import lombok.NoArgsConstructor; +import lombok.Setter; + +import java.util.ArrayList; +import java.util.List; + +@Getter +@Setter +@NoArgsConstructor +@AllArgsConstructor +@Entity +@Table(name="users") +public class User { + + @Id + @GeneratedValue(strategy = GenerationType.IDENTITY) + private Long id; + + @Column(nullable=false, unique=true) + private String username; + + @Column(nullable=false) + private String password; + + @Column(nullable = false) + private String displayName; + + @ManyToMany(fetch = FetchType.EAGER, cascade=CascadeType.ALL) + @JoinTable( + name="users_roles", + joinColumns={@JoinColumn(name="USER_ID", referencedColumnName="ID")}, + inverseJoinColumns={@JoinColumn(name="ROLE_ID", referencedColumnName="ID")}) + private List roles = new ArrayList<>(); + +} diff --git a/src/main/java/com/alterdekim/hearthhack/repository/RoleRepository.java b/src/main/java/com/alterdekim/hearthhack/repository/RoleRepository.java new file mode 100644 index 0000000..8e0a9b2 --- /dev/null +++ b/src/main/java/com/alterdekim/hearthhack/repository/RoleRepository.java @@ -0,0 +1,13 @@ +package com.alterdekim.hearthhack.repository; + +import com.alterdekim.hearthhack.entity.Role; +import org.springframework.data.jpa.repository.JpaRepository; + +import java.util.List; + +public interface RoleRepository extends JpaRepository { + Role findByName(String name); + + List findAll(); +} + diff --git a/src/main/java/com/alterdekim/hearthhack/repository/RoomPlayerRepository.java b/src/main/java/com/alterdekim/hearthhack/repository/RoomPlayerRepository.java new file mode 100644 index 0000000..1a21f7d --- /dev/null +++ b/src/main/java/com/alterdekim/hearthhack/repository/RoomPlayerRepository.java @@ -0,0 +1,32 @@ +package com.alterdekim.hearthhack.repository; + +import com.alterdekim.hearthhack.entity.RoomPlayer; +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; + +import java.util.List; + +@Repository +public interface RoomPlayerRepository extends JpaRepository { + + @Query(value = "SELECT new RoomPlayer(r.id, r.roomId, r.userId) FROM RoomPlayer r WHERE r.roomId = :roomId ORDER BY r.userId ASC") + List findByRoomId(@Param("roomId") Long roomId); + + @Transactional + @Modifying + @Query(value = "DELETE FROM RoomPlayer r WHERE r.userId = :userId") + void deleteAllByUserId(@Param("userId") Long userId); + + @Transactional + @Modifying + @Query(value = "DELETE FROM RoomPlayer r WHERE r.roomId = :roomId") + void removeByRoomId(@Param("roomId") Long roomId); + + @Query(value = "SELECT r.roomId FROM RoomPlayer r WHERE r.userId = :userId ORDER BY r.roomId ASC LIMIT 1") + Long hasUserId(@Param("userId") Long userId); +} + diff --git a/src/main/java/com/alterdekim/hearthhack/repository/RoomRepository.java b/src/main/java/com/alterdekim/hearthhack/repository/RoomRepository.java new file mode 100644 index 0000000..95424ef --- /dev/null +++ b/src/main/java/com/alterdekim/hearthhack/repository/RoomRepository.java @@ -0,0 +1,20 @@ +package com.alterdekim.hearthhack.repository; + +import com.alterdekim.hearthhack.entity.Room; +import org.springframework.data.jpa.repository.JpaRepository; +import org.springframework.data.jpa.repository.Modifying; +import org.springframework.data.jpa.repository.Query; +import org.springframework.stereotype.Repository; +import org.springframework.transaction.annotation.Propagation; +import org.springframework.transaction.annotation.Transactional; + +import java.util.List; + +@Repository +public interface RoomRepository extends JpaRepository { + + @Transactional(propagation = Propagation.REQUIRES_NEW) + @Modifying + @Query(value = "DELETE FROM room WHERE room.id NOT IN (SELECT t.room_id FROM (SELECT COUNT(id) as UID, room_id FROM room_player GROUP BY room_id) t)", nativeQuery = true) + void clearEmptyRooms(); +} diff --git a/src/main/java/com/alterdekim/hearthhack/repository/UserRepository.java b/src/main/java/com/alterdekim/hearthhack/repository/UserRepository.java new file mode 100644 index 0000000..33b4b3a --- /dev/null +++ b/src/main/java/com/alterdekim/hearthhack/repository/UserRepository.java @@ -0,0 +1,10 @@ +package com.alterdekim.hearthhack.repository; + +import com.alterdekim.hearthhack.entity.User; +import org.springframework.data.jpa.repository.JpaRepository; +import org.springframework.stereotype.Repository; + +@Repository +public interface UserRepository extends JpaRepository { + User findByUsername(String username); +} \ No newline at end of file diff --git a/src/main/java/com/alterdekim/hearthhack/security/SpringSecurity.java b/src/main/java/com/alterdekim/hearthhack/security/SpringSecurity.java new file mode 100644 index 0000000..1462108 --- /dev/null +++ b/src/main/java/com/alterdekim/hearthhack/security/SpringSecurity.java @@ -0,0 +1,60 @@ +package com.alterdekim.hearthhack.security; + +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.context.annotation.Bean; +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.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.savedrequest.HttpSessionRequestCache; +import org.springframework.security.web.util.matcher.AntPathRequestMatcher; + +@Configuration +@EnableWebSecurity +public class SpringSecurity { + @Autowired + private UserDetailsService userDetailsService; + + @Bean + public SecurityFilterChain filterChain(HttpSecurity http) throws Exception { + HttpSessionRequestCache requestCache = new HttpSessionRequestCache(); + requestCache.setMatchingRequestParameterName(null); + http.csrf().disable() + .authorizeHttpRequests((authorize) -> + authorize + .requestMatchers("/profile/**").hasAnyAuthority("ROLE_USER") + .requestMatchers("/").permitAll() + ).formLogin( + form -> form + .loginPage("/login") + .loginProcessingUrl("/login") + .failureForwardUrl("/") + .defaultSuccessUrl("/games") + .permitAll() + ) + .logout( + logout -> logout + .logoutUrl("/logout") + .logoutRequestMatcher(new AntPathRequestMatcher("/logout")) + .permitAll() + ) + .requestCache((cache) -> cache + .requestCache(requestCache) + ); + return http.build(); + } + + @Autowired + public void configureGlobal(AuthenticationManagerBuilder auth) throws Exception { + auth.userDetailsService(userDetailsService).passwordEncoder(passwordEncoder()); + } + + @Bean + public static PasswordEncoder passwordEncoder(){ + return new BCryptPasswordEncoder(); + } +} diff --git a/src/main/java/com/alterdekim/hearthhack/service/RoomPlayerService.java b/src/main/java/com/alterdekim/hearthhack/service/RoomPlayerService.java new file mode 100644 index 0000000..bfc7e05 --- /dev/null +++ b/src/main/java/com/alterdekim/hearthhack/service/RoomPlayerService.java @@ -0,0 +1,40 @@ +package com.alterdekim.hearthhack.service; + +import com.alterdekim.hearthhack.entity.RoomPlayer; +import com.alterdekim.hearthhack.repository.RoomPlayerRepository; +import lombok.RequiredArgsConstructor; +import org.springframework.stereotype.Service; + +import java.util.List; + +@Service +@RequiredArgsConstructor +public class RoomPlayerService { + + private final RoomPlayerRepository repository; + + public List getAll() { + return repository.findAll(); + } + + public List findByRoomId(Long roomId) { + return repository.findByRoomId(roomId); + } + + public void joinRoom(Long id, Long userId) { + repository.save(new RoomPlayer(id, userId)); + } + + public void leaveByUserId(Long userId) { + repository.deleteAllByUserId(userId); + } + + public Long hasUserId(Long userId) { + return repository.hasUserId(userId); + } + + public void removeByRoomId(Long roomId) { + repository.removeByRoomId(roomId); + } +} + diff --git a/src/main/java/com/alterdekim/hearthhack/service/RoomService.java b/src/main/java/com/alterdekim/hearthhack/service/RoomService.java new file mode 100644 index 0000000..a30301f --- /dev/null +++ b/src/main/java/com/alterdekim/hearthhack/service/RoomService.java @@ -0,0 +1,35 @@ +package com.alterdekim.hearthhack.service; + +import com.alterdekim.hearthhack.entity.Room; +import com.alterdekim.hearthhack.repository.RoomRepository; +import lombok.RequiredArgsConstructor; +import org.springframework.stereotype.Service; + +import java.util.List; +import java.util.Optional; + +@RequiredArgsConstructor +@Service +public class RoomService { + private final RoomRepository roomRepository; + + public List getAll() { + return roomRepository.findAll(); + } + + public Optional findById(Long id) { + return roomRepository.findById(id); + } + + public Long createRoom(Room room) { + return roomRepository.save(room).getId(); + } + + public void clearEmptyRooms() { + roomRepository.clearEmptyRooms(); + } + + public void removeRoom(Long roomId) { + roomRepository.deleteById(roomId); + } +} diff --git a/src/main/java/com/alterdekim/hearthhack/service/UserService.java b/src/main/java/com/alterdekim/hearthhack/service/UserService.java new file mode 100644 index 0000000..d62b5d2 --- /dev/null +++ b/src/main/java/com/alterdekim/hearthhack/service/UserService.java @@ -0,0 +1,67 @@ +package com.alterdekim.hearthhack.service; + +import com.alterdekim.hearthhack.dto.UserDTO; +import com.alterdekim.hearthhack.entity.Role; +import com.alterdekim.hearthhack.entity.User; +import com.alterdekim.hearthhack.repository.RoleRepository; +import com.alterdekim.hearthhack.repository.UserRepository; +import lombok.RequiredArgsConstructor; +import org.springframework.security.crypto.password.PasswordEncoder; +import org.springframework.stereotype.Service; + +import java.util.Collections; +import java.util.List; +import java.util.stream.Collectors; + +@RequiredArgsConstructor +@Service +public class UserService { + + private final UserRepository userRepository; + private final RoleRepository roleRepository; + private final PasswordEncoder passwordEncoder; + + + public void saveUser(UserDTO userDto) { + User user = new User(); + user.setUsername(userDto.getUsername()); + user.setDisplayName(userDto.getUsername()); + user.setPassword(passwordEncoder.encode(userDto.getPassword())); + Role role = roleRepository.findByName("ROLE_USER"); + if(role == null){ + role = checkRoleExist(); + } + user.setRoles(Collections.singletonList(role)); + userRepository.save(user); + } + + + public User findByUsername(String username) { + return userRepository.findByUsername(username); + } + + + public List findAllUsers() { + List users = userRepository.findAll(); + return users.stream().map(this::convertEntityToDto) + .collect(Collectors.toList()); + } + + + public User findById(Long id) { + return userRepository.findById(id).orElse(null); + } + + private UserDTO convertEntityToDto(User user){ + UserDTO userDto = new UserDTO(); + userDto.setUsername(user.getUsername()); + return userDto; + } + + private Role checkRoleExist() { + Role role = new Role(); + role.setName("ROLE_USER"); + return roleRepository.save(role); + } +} + diff --git a/src/main/java/com/alterdekim/hearthhack/util/Hash.java b/src/main/java/com/alterdekim/hearthhack/util/Hash.java new file mode 100644 index 0000000..a2c3aaf --- /dev/null +++ b/src/main/java/com/alterdekim/hearthhack/util/Hash.java @@ -0,0 +1,35 @@ +package com.alterdekim.hearthhack.util; + +import java.security.MessageDigest; +import java.security.NoSuchAlgorithmException; +import java.util.Random; +import java.util.stream.Collectors; +import java.util.stream.IntStream; + +public class Hash { + public static String sha256( byte[] b ) throws NoSuchAlgorithmException { + MessageDigest digest = MessageDigest.getInstance("SHA-256"); + return bytesToHex(digest.digest(b)); + } + + private static String bytesToHex(byte[] hash) { + StringBuilder hexString = new StringBuilder(2 * hash.length); + for (byte b : hash) { + String hex = Integer.toHexString(0xff & b); + if (hex.length() == 1) { + hexString.append('0'); + } + hexString.append(hex); + } + return hexString.toString(); + } + + public static String rnd() { + return IntStream.generate(() -> new Random().nextInt(0, 62)) + .limit(32) + .boxed() + .map(i -> "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ1234567890".charAt(i)+"") + .collect(Collectors.joining()); + } +} + diff --git a/src/main/java/com/alterdekim/hearthhack/util/PegasusPacket.java b/src/main/java/com/alterdekim/hearthhack/util/PegasusPacket.java index fa888f5..74befa5 100644 --- a/src/main/java/com/alterdekim/hearthhack/util/PegasusPacket.java +++ b/src/main/java/com/alterdekim/hearthhack/util/PegasusPacket.java @@ -37,6 +37,13 @@ public class PegasusPacket { this.body = body; } + public PegasusPacket(int type, int context, byte[] body) { + this.type = type; + this.context = context; + this.size = body.length; + this.body = body; + } + public PegasusPacket(int type, int context, int size, Object body) { this.type = type; this.context = context; @@ -44,11 +51,6 @@ public class PegasusPacket { this.body = body; } - - public Object GetBody() { - return this.body; - } - public boolean IsLoaded() { return this.body != null; } @@ -97,7 +99,12 @@ public class PegasusPacket { System.arraycopy(protoBuf.toByteArray(), 0, array, 8, this.size); return array; } - return null; + this.size = ((byte[]) this.body).length; + byte[] array = new byte[this.size + 4 + 4]; + System.arraycopy(getBytes(this.type), 0, array, 0, 4); + System.arraycopy(getBytes(this.size), 0, array, 4, 4); + System.arraycopy(this.body, 0, array, 8, this.size); + return array; } private int toInt32_2(byte[] bytes, int index) {