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) {