diff --git a/src/main/java/com/alterdekim/hearthhack/Application.java b/src/main/java/com/alterdekim/hearthhack/Application.java index c0a1fda..0f25d4f 100644 --- a/src/main/java/com/alterdekim/hearthhack/Application.java +++ b/src/main/java/com/alterdekim/hearthhack/Application.java @@ -3,10 +3,12 @@ package com.alterdekim.hearthhack; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; +import org.springframework.scheduling.annotation.EnableAsync; import org.springframework.scheduling.annotation.EnableScheduling; @SpringBootApplication @EnableScheduling +@EnableAsync public class Application { public static void main(String[] args) { SpringApplication.run(Application.class, args); diff --git a/src/main/java/com/alterdekim/hearthhack/component/GameConnection.java b/src/main/java/com/alterdekim/hearthhack/component/GameConnection.java new file mode 100644 index 0000000..8f61526 --- /dev/null +++ b/src/main/java/com/alterdekim/hearthhack/component/GameConnection.java @@ -0,0 +1,94 @@ +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.InputStream; +import java.io.OutputStream; +import java.net.Socket; + +@Slf4j +@RequiredArgsConstructor +public class GameConnection extends Thread { + private final Socket client; + private OutputStream outToClient; + + public void stopListeningAndDisconnect() { + log.warn("Tried to stopListening"); + } + + private void processPacket( PegasusPacket packet ) throws Exception { + switch (packet.getType()) { + case 168: + PegasusGame.GameSetup setup = PegasusGame.GameSetup.newBuilder() + .setBoard(0) + .setMaxFriendlyMinionsPerPlayer(7) + .setMaxSecretsPerPlayer(5) + .setKeepAliveFrequencySeconds(30) + .setDisconnectWhenStuckSeconds(500) + .build(); + PegasusPacket result = new PegasusPacket(16, 0, setup); + this.send(result); + break; + case 1: + // GetGameState + this.sendRaw(Util.hexStringToByteArray("0AA6022AA3020A2F0801120408351001120508C6011004120408311001120508CC011002120408141001120508CA0110011204080A1055127D0802120F08C786D1BAA58080800210E7F2AC251800226608031204083210021204083510031205088F031003120508B001100A12040807104B120408311001120508CA011002120508900210011204081D10041204081C100A1204081E10021204081F10021204081710011204081110011204081B104212040818100112710801120F08C786D1BAA58080800210B59DFA241800225A08021204083210011204083510021205088F031004120508B001100A12040807104B120508CA011002120508900210011204081D10041204081C100A1204081E10011204081F10011204083110011204081110011204081B10400A180A16082212001A04083510221A04083210021A04083110020A180A16082312001A04083510231A04083210021A04083110020A180A16082412001A04083510241A04083210021A04083110020A620A60082512074558315F3539331A04083510251A04083210021A0508870210031A04083110031A0508C10210011A0508B70110021A0508DA0110011A04082D10021A04082F10041A04083010041A0508CA0110041A0508CB0110021A0508C90110030A93010A9001082612074558315F3338331A04083510261A04083210021A0508870210011A04083110031A0508FB0110011A0508C20110001A0508C10210011A0508B70110031A0508BE0110001A0508CA0210001A0508CB0210001A04082D10101A04082F10031A04087210011A04083010031A0508C70110051A0508CA0110041A0508CB0110051A0508D90110001A0508C90110030A180A16082712001A04083510271A04083210021A04083110020A180A16082812001A04083510281A04083210021A04083110020A180A16082912001A04083510291A04083210021A04083110020A180A16082A12001A040835102A1A04083210021A04083110020A180A16082B12001A040835102B1A04083210021A04083110020A180A16082C12001A040835102C1A04083210021A04083110020A180A16082D12001A040835102D1A04083210021A04083110020A180A16082E12001A040835102E1A04083210021A04083110020A180A16082F12001A040835102F1A04083210021A04083110020A180A16083012001A04083510301A04083210021A04083110020A180A16083112001A04083510311A04083210021A04083110020A180A16083212001A04083510321A04083210021A04083110020A180A16083312001A04083510331A04083210021A04083110020A180A16083412001A04083510341A04083210021A04083110020A180A16083512001A04083510351A04083210021A04083110020A490A47083612084C4F454131365F391A04083510361A04083210021A0508870210021A04083110031A04083010001A0508CA0210001A0508CB0210001A0508CA0110051A0508B70110140A180A16083712001A04083510371A04083210021A04083110020A180A16083812001A04083510381A04083210021A04083110020A180A16083912001A04083510391A04083210021A04083110020A180A16083A12001A040835103A1A04083210021A04083110020A180A16083B12001A040835103B1A04083210021A04083110020A180A16083C12001A040835103C1A04083210021A04083110020A180A16083D12001A040835103D1A04083210021A04083110020A180A16083E12001A040835103E1A04083210021A04083110020A180A16083F12001A040835103F1A04083210021A04083110020A180A16080412001A04083510041A04083210011A04083110020A180A16080512001A04083510051A04083210011A04083110020A180A16080612001A04083510061A04083210011A04083110020A180A16080712001A04083510071A04083210011A04083110020A180A16080812001A04083510081A04083210011A04083110020A180A16080912001A04083510091A04083210011A04083110020A180A16080A12001A040835100A1A04083210011A04083110020A1F0A1D080B12001A040835100B1A04083210011A0508870210041A04083110030A180A16080C12001A040835100C1A04083210011A04083110020A180A16080D12001A040835100D1A04083210011A04083110020A180A16080E12001A040835100E1A04083210011A04083110020A180A16080F12001A040835100F1A04083210011A04083110020A180A16081012001A04083510101A04083210011A04083110020A1F0A1D081112001A04083510111A04083210011A0508870210031A04083110030A180A16081212001A04083510121A04083210011A04083110020A180A16081312001A04083510131A04083210011A04083110020A180A16081412001A04083510141A04083210011A04083110020A1F0A1D081512001A04083510151A04083210011A0508870210011A04083110030A180A16081612001A04083510161A04083210011A04083110020A180A16081712001A04083510171A04083210011A04083110020A180A16081812001A04083510181A04083210011A04083110020A180A16081912001A04083510191A04083210011A04083110020A1F0A1D081A12001A040835101A1A04083210011A0508870210021A04083110030A180A16081B12001A040835101B1A04083210011A04083110020A180A16081C12001A040835101C1A04083210011A04083110020A180A16081D12001A040835101D1A04083210011A04083110020A180A16081E12001A040835101E1A04083210011A04083110020A180A16081F12001A040835101F1A04083210011A04083110020A180A16082012001A04083510201A04083210011A04083110020A180A16082112001A04083510211A04083210011A04083110020A4B0A49084212094C4F454130345F30311A04083110011A04083510421A04083210021A0508B70110141A0608FC0210E77E1A0508CA0210001A0508CB0210001A04082D101E1A0508CA0110030A5D0A5B08431208435331685F3030311A0508B90210421A04083110011A04083510431A04083210021A04083110011A0508FB0110011A0508B70110021A04083010021A0508C70110061A0508CA01100A1A0508CB0110021A0508C90110030A460A440840120B54425F5350545F426F73731A04083110011A04083510401A04083210011A04082F10001A04082D101E1A0508CA0110031A0508B70110121A0708FC0210F1B4020A5D0A5B08411208435331685F3030311A0508B90210401A04083110011A04083510411A04083210011A04083110011A0508FB0110011A0508B70110021A04083010021A0508C70110061A0508CA01100A1A0508CB0110021A0508C90110030A1F0A1D084412001A04083510441A04083210011A0508870210051A0408311003")); + break; + case 115: + PegasusGame.Pong pong = PegasusGame.Pong.newBuilder().build(); + result = new PegasusPacket(116, 0, pong); + this.send(result); + break; + default: + log.info("PegasusPacket: type={}, context={}, size={}", packet.getType(), packet.getContext(), packet.getSize()); + break; + } + } + + public void send(PegasusPacket pp) throws Exception { + this.outToClient.write(pp.Encode()); + this.outToClient.flush(); + } + + public void sendRaw(byte[] data) throws Exception { + this.outToClient.write(data); + this.outToClient.flush(); + } + + @Override + public void run() { + try { + InputStream is = client.getInputStream(); + outToClient = client.getOutputStream(); + while (true) { + try { + int count; + byte[] buffer = new byte[4096]; // or 4096, or more + ByteArrayOutputStream baos = new ByteArrayOutputStream(); + while ((count = is.read(buffer)) > 0) { + baos.write(buffer, 0, count); + byte[] r = baos.toByteArray(); + PegasusPacket pp = new PegasusPacket(); + int offset = 0; + while( offset < r.length ) { + offset += pp.Decode(r, offset, r.length); + processPacket(pp); + } + baos.reset(); + } + baos.close(); + } catch (Exception e) { + log.error(e.getMessage()); + break; + } + } + } catch (Exception e) { + log.error(e.getMessage()); + } + } +} diff --git a/src/main/java/com/alterdekim/hearthhack/component/GameServer.java b/src/main/java/com/alterdekim/hearthhack/component/GameServer.java new file mode 100644 index 0000000..5cbbb5f --- /dev/null +++ b/src/main/java/com/alterdekim/hearthhack/component/GameServer.java @@ -0,0 +1,52 @@ +package com.alterdekim.hearthhack.component; + +import lombok.extern.slf4j.Slf4j; +import org.springframework.scheduling.annotation.Scheduled; +import org.springframework.stereotype.Component; + +import java.io.IOException; +import java.net.ServerSocket; +import java.net.Socket; +import java.util.LinkedList; +import java.util.List; + +@Slf4j +@Component +public class GameServer { + + private final int port = 3724; + + private ServerSocket serverSocket; + + private List<GameConnection> connections; + + @Scheduled(fixedDelay = 5000) + private void start() { + try { + this.connections = new LinkedList<>(); + this.serverSocket = new ServerSocket(this.port); + while(true) { + Socket client = this.serverSocket.accept(); + log.info("New WOW Connection Established From {}", client.getInetAddress().toString()); + GameConnection c = new GameConnection(client); + this.connections.add(c); + c.start(); + } + } catch (IOException e) { + log.error(e.getMessage()); + } + } + + public void stopListening() { + connections.forEach(GameConnection::stopListeningAndDisconnect); + try { + serverSocket.close(); + } catch (IOException e) { + log.error(e.getMessage()); + } + } + + public void removeConnection(TcpConnection c) { + connections.remove(c); + } +} diff --git a/src/main/java/com/alterdekim/hearthhack/component/TcpConnection.java b/src/main/java/com/alterdekim/hearthhack/component/TcpConnection.java index e0ecb2e..33f8531 100644 --- a/src/main/java/com/alterdekim/hearthhack/component/TcpConnection.java +++ b/src/main/java/com/alterdekim/hearthhack/component/TcpConnection.java @@ -4,6 +4,7 @@ import com.alterdekim.hearthhack.component.processor.*; import com.alterdekim.hearthhack.util.BattleNetPacket; import com.alterdekim.hearthhack.util.Util; import lombok.Getter; +import lombok.RequiredArgsConstructor; import lombok.extern.slf4j.Slf4j; import javax.net.ssl.SSLSocket; @@ -13,6 +14,7 @@ import java.io.OutputStream; import java.util.Map; @Slf4j +@RequiredArgsConstructor public class TcpConnection extends Thread { private final SSLSocket fromClient; @@ -24,18 +26,12 @@ public class TcpConnection extends Thread { @Getter private final Map<Integer, Processor> processors; - public TcpConnection(SSLSocket socket, Map<Integer, Processor> processors) { - this.fromClient = socket; - this.processors = processors; - this.start(); - } - public void stopListeningAndDisconnect() { log.warn("Tried to stopListening"); } public void send(BattleNetPacket bp) throws Exception { - log.info("TcpConnection.send: service={}, method={}, body={}", bp.getHeader().getServiceId(), bp.getHeader().getMethodId(), Util.bytesToHex(bp.getBody())); + //log.info("TcpConnection.send: service={}, method={}, body={}", bp.getHeader().getServiceId(), bp.getHeader().getMethodId(), Util.bytesToHex(bp.getBody())); this.outToClient.write(bp.Encode()); this.outToClient.flush(); } diff --git a/src/main/java/com/alterdekim/hearthhack/component/TcpServer.java b/src/main/java/com/alterdekim/hearthhack/component/TcpServer.java index 4255f82..5a74283 100644 --- a/src/main/java/com/alterdekim/hearthhack/component/TcpServer.java +++ b/src/main/java/com/alterdekim/hearthhack/component/TcpServer.java @@ -44,6 +44,7 @@ public class TcpServer extends ReflectionLoader<Processor> { SSLSocket s = (SSLSocket) serverSocket.accept(); TcpConnection c = new TcpConnection(s, this.getParsers()); connections.add(c); + c.start(); log.info("New Connection Established From {}", s.getInetAddress().toString()); } } catch (IOException e) { diff --git a/src/main/java/com/alterdekim/hearthhack/component/processor/ChannelSubProcessor.java b/src/main/java/com/alterdekim/hearthhack/component/processor/ChannelSubProcessor.java new file mode 100644 index 0000000..97d6ff2 --- /dev/null +++ b/src/main/java/com/alterdekim/hearthhack/component/processor/ChannelSubProcessor.java @@ -0,0 +1,30 @@ +package com.alterdekim.hearthhack.component.processor; + +import com.alterdekim.Protocol; +import com.alterdekim.hearthhack.component.TcpConnection; +import com.alterdekim.hearthhack.util.BattleNetPacket; +import lombok.extern.slf4j.Slf4j; + +@Slf4j +public class ChannelSubProcessor extends Processor { + + public ChannelSubProcessor() { + this.setProcessorId(5); + } + + @Override + public void process(BattleNetPacket packet, TcpConnection conn) throws Exception { + if( packet.getHeader().getMethodId() != 3 ) return; + Protocol.JoinChannelRequest request = Protocol.JoinChannelRequest.parseFrom(packet.getBody()); + Protocol.JoinChannelResponse response = Protocol.JoinChannelResponse.newBuilder() + .setObjectId(request.getObjectId()) + .build(); + Protocol.Header header = generateResponse(response.getSerializedSize(), packet.getHeader().getToken(), 0, (int) packet.getHeader().getObjectId()); + conn.send(new BattleNetPacket(header, response.toByteArray())); + } + + @Override + public String getExportName() { + return "bnet.protocol.channel.Channel"; + } +} diff --git a/src/main/java/com/alterdekim/hearthhack/component/processor/GameMasterProcessor.java b/src/main/java/com/alterdekim/hearthhack/component/processor/GameMasterProcessor.java new file mode 100644 index 0000000..53738a3 --- /dev/null +++ b/src/main/java/com/alterdekim/hearthhack/component/processor/GameMasterProcessor.java @@ -0,0 +1,193 @@ +package com.alterdekim.hearthhack.component.processor; + +import com.alterdekim.Protocol; +import com.alterdekim.hearthhack.component.TcpConnection; +import com.alterdekim.hearthhack.util.BattleNetPacket; +import com.alterdekim.hearthhack.util.GameType; +import com.alterdekim.hearthhack.util.Util; +import com.google.protobuf.ByteString; +import lombok.extern.slf4j.Slf4j; + +import java.nio.charset.StandardCharsets; + +import static com.alterdekim.hearthhack.util.GameUtilities.generateSmallNotification; + +@Slf4j +public class GameMasterProcessor extends Processor { + + public GameMasterProcessor() { + this.setProcessorId(8); + } + + private void processFindGame(BattleNetPacket packet, TcpConnection conn) throws Exception { + // 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) + .build(); + + Protocol.Header header = Processor.generateResponse(response.getSerializedSize(), packet.getHeader().getToken(), 0, 0); + conn.send(new BattleNetPacket(header, response.toByteArray())); + + + Protocol.GameFoundNotification notification = Protocol.GameFoundNotification.newBuilder() + .setRequestId(gameRequest.getRequestId()) + //.setGameHandle(Protocol.GameHandle.newBuilder().setFactoryId(gameRequest.getFactoryId())) + .addConnectInfo(Protocol.ConnectInfo.newBuilder() + .setHost("localhost") + .setPort(1119) + .setMemberId(Protocol.EntityId.newBuilder() + .setHigh(72057594037927936L) + .setLow(437154195L)) + ).build(); + + // method_id = 1 + // service = 3 + + header = Protocol.Header.newBuilder() + .setServiceId(3) + .setMethodId(1) + .setSize(notification.getSerializedSize()) + .setToken(conn.nextToken()) + .setObjectId(0) + .setStatus(0) + .build(); + + conn.send(new BattleNetPacket(header, notification.toByteArray())); + + + /* byte[] ip = "localhost".getBytes(StandardCharsets.US_ASCII); + log.info("ip: {} lenhex: {}, len: {}", Util.bytesToHex(ip), Util.intToHex(ip.length), ip.length); + byte[] bb = Util.hexStringToByteArray("0A120900000000000000021100000000000000001212094743545702000002116739AB04000000001A08475F524553554C54221E0A0F67616D655F726571756573745F6964120B48B6D2A3C7A4FCA2E1B901220B0A056572726F7212024800222E0A0B67616D655F68616E646C65121F3A1D09F64263F5D33456C7121209EEA60D5703000006118C270B000A62A3AB22B3010A0F636F6E6E656374696F6E5F696E666F129F013A9C010A12094743545702000002116739AB040000000012"+Util.intToHex(ip.length)+Util.bytesToHex(ip)+"188C1D2206456A634465492A130A0776657273696F6E12082A0665694B3271762A0D0A0467616D65120518AA80C4062A0C0A06706C61796572120218012A0B0A026964120518B2B0AA1C2A0F0A09726573756D61626C65120210012A1E0A12737065637461746F725F70617373776F726412082A066F44697049664A0A0895A56710E9E6ACB90552150A050D93710E1A120C0D6739AB0415474354571802"); + + header = Protocol.Header.newBuilder() + .setServiceId(4) + .setMethodId(1) + .setSize(bb.length) + .setToken(conn.nextToken()) + .setObjectId(0) + .setStatus(0) + .build(); + + conn.send(new BattleNetPacket(header, bb)); + + Protocol.Notification n = Protocol.Notification.parseFrom(bb); + Protocol.ConnectInfo ci = Protocol.ConnectInfo.parseFrom( + n.getAttributeList().stream().filter(p -> p.hasName() && p.getName().equals("connection_info")).findFirst().get().getValue().getMessageValue() + ); + log.info("GGGG: {}", ci);*/ + + Protocol.Notification n1 = Protocol.Notification.newBuilder() + .setType("G_RESULT") + .setSenderId(Protocol.EntityId.newBuilder() + .setLow(0) + .setHigh(144115188075855872L)) + .setTargetId(Protocol.EntityId.newBuilder() + .setLow(78330215L) + .setHigh(144115198130930503L)) + .addAttribute(Protocol.Attribute.newBuilder() + .setName("game_request_id") + .setValue( + Protocol.Variant.newBuilder() + .setUintValue(1338541484650062879L * 10L) + ) + ) + .addAttribute(Protocol.Attribute.newBuilder() + .setName("game_handle") + .setValue( + Protocol.Variant.newBuilder() + .setMessageValue( + Protocol.GameHandle.newBuilder() + .setFactoryId( (143637261465044426L * 100L) + 14L ) + .setGameId(Protocol.EntityId.newBuilder() + .setHigh(432345578572981998L) + .setLow((123678367967794400L * 100L) + 12L)) + .build() + .toByteString() + ) + ) + ) + .addAttribute(Protocol.Attribute.newBuilder() + .setName("connection_info") + .setValue( + Protocol.Variant.newBuilder() + .setMessageValue( + Protocol.ConnectInfo.newBuilder() + .setMemberId(Protocol.EntityId.newBuilder() + .setHigh(144115198130930503L) + .setLow(78330215L) + ) + .setHost("192.168.0.9") + .setPort(3724) + .setToken(ByteString.copyFromUtf8("EjcDeI")) + .addAttribute(Protocol.Attribute + .newBuilder() + .setName("version") + .setValue(Protocol.Variant.newBuilder().setStringValue("eiK2qv"))) + .addAttribute(Protocol.Attribute + .newBuilder() + .setName("game") + .setValue(Protocol.Variant.newBuilder().setIntValue(13697066L))) + .addAttribute(Protocol.Attribute + .newBuilder() + .setName("player") + .setValue(Protocol.Variant.newBuilder().setIntValue(1L))) + .addAttribute(Protocol.Attribute + .newBuilder() + .setName("id") + .setValue(Protocol.Variant.newBuilder().setIntValue(59414578L))) + .addAttribute(Protocol.Attribute + .newBuilder() + .setName("resumable") + .setValue(Protocol.Variant.newBuilder().setBoolValue(true))) + .addAttribute(Protocol.Attribute + .newBuilder() + .setName("spectator_password") + .setValue(Protocol.Variant.newBuilder().setStringValue("oDipIf"))) + .build().toByteString() + ) + ) + ) + .addAttribute(Protocol.Attribute.newBuilder() + .setName("error") + .setValue( + Protocol.Variant.newBuilder() + .setUintValue(0) + ) + ).build(); + + header = Protocol.Header.newBuilder() + .setServiceId(4) + .setMethodId(1) + .setSize(n1.getSerializedSize()) + .setToken(conn.nextToken()) + .setObjectId(0) + .setStatus(0) + .build(); + + conn.send(new BattleNetPacket(header, n1.toByteArray())); + } + + @Override + public void process(BattleNetPacket packet, TcpConnection conn) throws Exception { + switch(packet.getHeader().getMethodId()) { + case 3 -> processFindGame(packet, conn); + default -> log.info("Weird method id: {}", packet.getHeader().getMethodId()); + } + } + + @Override + public String getExportName() { + return "bnet.protocol.channel_invitation.ChannelInvitationService"; + } +} diff --git a/src/main/java/com/alterdekim/hearthhack/component/processor/GameUtilitiesProcessor.java b/src/main/java/com/alterdekim/hearthhack/component/processor/GameUtilitiesProcessor.java index 4f05b04..c05c630 100644 --- a/src/main/java/com/alterdekim/hearthhack/component/processor/GameUtilitiesProcessor.java +++ b/src/main/java/com/alterdekim/hearthhack/component/processor/GameUtilitiesProcessor.java @@ -90,7 +90,7 @@ public class GameUtilitiesProcessor extends Processor { ClientRequestBody p = parseClientRequest(cr); int type = p != null ? p.getType() : 0; - if( type != 314 && type != 255 ) { + if( type != 314 && type != 255 && type != 214 ) { Protocol.GenericResponse genericResponse = Protocol.GenericResponse .newBuilder() .setRequestId(type) diff --git a/src/main/java/com/alterdekim/hearthhack/component/processor/PresenceProcessor.java b/src/main/java/com/alterdekim/hearthhack/component/processor/PresenceProcessor.java index fcba44f..1823af7 100644 --- a/src/main/java/com/alterdekim/hearthhack/component/processor/PresenceProcessor.java +++ b/src/main/java/com/alterdekim/hearthhack/component/processor/PresenceProcessor.java @@ -43,8 +43,8 @@ public class PresenceProcessor extends Processor { .setStatus(0) .build(); // FIRST - /*Protocol.AddNotification addNotification = Protocol.AddNotification.parseFrom(Util.hexStringToByteArray("1A71AA066E0A120900000000000000011193710E1A0000000012190A170A0A08CE84011001180620001209189AB1D8B499BCCC02121A0A180A0A08CE8401100118012000120A2A086A6F686E20626F6E12210A1F0A0A08CE840110011804200012112A0F517569726B794F7263233239363838")); - System.out.println(addNotification);*/ + Protocol.AddNotification addNotification = Protocol.AddNotification.parseFrom(Util.hexStringToByteArray("1A71AA066E0A120900000000000000011193710E1A0000000012190A170A0A08CE84011001180620001209189AB1D8B499BCCC02121A0A180A0A08CE8401100118012000120A2A086A6F686E20626F6E12210A1F0A0A08CE840110011804200012112A0F517569726B794F7263233239363838")); + //log.info("AddNotification: {}", addNotification); //System.out.println(Util.bytesToHex(bb)); @@ -85,15 +85,17 @@ public class PresenceProcessor extends Processor { } break; case 3: + header = Processor.generateResponse(0, packet.getHeader().getToken(), 0, 0); + conn.send(new BattleNetPacket(header, new byte[0])); if( packet.getHeader().getToken() == 12 ) { /* Protocol.Header header1 = Processor.generateResponse(0, packet.getHeader().getToken(), 0, 0); conn.send(new BattleNetPacket(header1, new byte[0]));*/ conn.sendRaw(Util.hexStringToByteArray("000c08fe011800200028950130000a0c08f4c9ccf30d10c687bcb805120a0889ff5c1092e5a1b9051800220e0a0c0109080a04030507060211102a2e0a2c0d55450000157a72746d1a206ff4fdd5fa5f6d62a278a04403e075d69d734cd4880732dce8edc6a3f528089230a08a95f4cebdcc02422e0a2c0d55450000157a72746d1a20b4bd0f0096a7648de1d19042fb7a79b96c0df48eea3488f498b08ad3d38fb2cc")); } else if( packet.getHeader().getToken() == 13 ) { - Protocol.Header header1 = Processor.generateResponse(0, packet.getHeader().getToken(), 0, 0); - conn.send(new BattleNetPacket(header1, new byte[0])); + /* Protocol.Header header1 = Processor.generateResponse(0, packet.getHeader().getToken(), 0, 0); + conn.send(new BattleNetPacket(header1, new byte[0]));*/ - header1 = Protocol.Header.newBuilder() + Protocol.Header header1 = Protocol.Header.newBuilder() .setServiceId(5) .setMethodId(6) .setToken(conn.nextToken()) @@ -104,12 +106,12 @@ public class PresenceProcessor extends Processor { conn.send(new BattleNetPacket(header1, Util.hexStringToByteArray("1233AA06300A12094743545702000002116739AB0400000000121A0A180A0A08C786D1BA0510021813120A2A083833383836303830"))); } else if( packet.getHeader().getToken() == 18 ) { - Protocol.Header header1 = Processor.generateResponse(0, packet.getHeader().getToken(), 0, 0); - conn.send(new BattleNetPacket(header1, new byte[0])); + /* Protocol.Header header1 = Processor.generateResponse(0, packet.getHeader().getToken(), 0, 0); + conn.send(new BattleNetPacket(header1, new byte[0]));*/ byte[] b = Util.hexStringToByteArray("122BAA06280A12094743545702000002116739AB040000000012120A100A0A08C786D1BA051002180112021000"); - header1 = Protocol.Header.newBuilder() + Protocol.Header header1 = Protocol.Header.newBuilder() .setServiceId(5) .setMethodId(6) .setToken(conn.nextToken()) @@ -134,8 +136,8 @@ public class PresenceProcessor extends Processor { conn.send(new BattleNetPacket(header, b)); } else if( packet.getHeader().getToken() == 26 ) { - Protocol.Header header1 = Processor.generateResponse(0, packet.getHeader().getToken(), 0, 0); - conn.send(new BattleNetPacket(header1, new byte[0])); + /* Protocol.Header header1 = Processor.generateResponse(0, packet.getHeader().getToken(), 0, 0); + conn.send(new BattleNetPacket(header1, new byte[0]));*/ byte[] b = Util.hexStringToByteArray("1231AA062E0A12094743545702000002116739AB040000000012180A160A0A08C786D1BA051002181212083206190000190000"); @@ -153,7 +155,7 @@ public class PresenceProcessor extends Processor { byte[] b = Util.hexStringToByteArray("0D5545000015737368001A20BEC5292231D7686AF00CE64E0C58CC6E360EA950AAFFAC6A114F03A958E275F3"); Protocol.Header header1 = Processor.generateResponse(b.length, packet.getHeader().getToken(), 0, 0); - conn.send(new BattleNetPacket(header1, b)); + //conn.send(new BattleNetPacket(header1, b)); b = Util.hexStringToByteArray("1230AA062D0A12094743545702000002116739AB040000000012170A150A0A08C786D1BA0510021811120732050103000000"); @@ -170,12 +172,12 @@ public class PresenceProcessor extends Processor { byte[] b = Util.hexStringToByteArray("0D5545000015737368001A20BEC5292231D7686AF00CE64E0C58CC6E360EA950AAFFAC6A114F03A958E275F3"); Protocol.Header header1 = Processor.generateResponse(b.length, packet.getHeader().getToken(), 0, 0); - conn.send(new BattleNetPacket(header1, b)); + // conn.send(new BattleNetPacket(header1, b)); } else if( packet.getHeader().getToken() == 36 ) { byte[] b = Util.hexStringToByteArray("0A090A026964120318C6020A120A0570726F746F12093207080110C9011806"); Protocol.Header header1 = Processor.generateResponse(b.length, packet.getHeader().getToken(), 0, 0); - conn.send(new BattleNetPacket(header1, b)); + // conn.send(new BattleNetPacket(header1, b)); b = Util.hexStringToByteArray("0A120900000000000000021100000000000000001212094743545702000002116739AB04000000001A1C575443472E5574696C4E6F74696669636174696F6E4D65737361676522130A0C6D6573736167655F74797065120318E00122120A0C6D6573736167655F73697A65120218002A1209000000000000000111000000000000000032120900000000000000011193710E1A000000003A004A0A0889FF5C1092E5A1B90552150A050D93710E1A120C0D6739AB0415474354571802"); 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 new file mode 100644 index 0000000..e8dd3bb --- /dev/null +++ b/src/main/java/com/alterdekim/hearthhack/component/processor/client/request/GetDeck.java @@ -0,0 +1,96 @@ +package com.alterdekim.hearthhack.component.processor.client.request; + +import com.alterdekim.Protocol; +import com.alterdekim.hearthhack.component.TcpConnection; +import com.alterdekim.hearthhack.component.processor.Processor; +import com.alterdekim.hearthhack.util.BattleNetPacket; +import com.alterdekim.hearthhack.util.ClientRequestBody; +import com.alterdekim.hearthhack.util.Util; +import com.google.protobuf.ByteString; +import lombok.extern.slf4j.Slf4j; + +import java.util.ArrayList; +import java.util.List; + +import static com.alterdekim.hearthhack.util.GameUtilities.generateNotification; + +@Slf4j +public class GetDeck extends ClientRequestParser { + @Override + public void parse(BattleNetPacket packet, ClientRequestBody body, TcpConnection conn) throws Exception { + Protocol.GetDeckContents request = Protocol.GetDeckContents.parseFrom(body.getBody()); + + List<Protocol.DeckCardData> ddl = new ArrayList<>(); + for( int i = 0; i < 10; i++ ) { + ddl.add(Protocol.DeckCardData.newBuilder() + .setDef(Protocol.CardDef.newBuilder().setAsset(242)).setHandle(0).setQty(2).setPrev(0).build()); + } + for( int i = 0; i < 10; i++ ) { + ddl.add(Protocol.DeckCardData.newBuilder() + .setDef(Protocol.CardDef.newBuilder().setAsset(670)).setHandle(0).setQty(2).setPrev(0).build()); + } + for( int i = 0; i < 5; i++ ) { + ddl.add(Protocol.DeckCardData.newBuilder() + .setDef(Protocol.CardDef.newBuilder().setAsset(555)).setHandle(0).setQty(2).setPrev(0).build()); + } + for( int i = 0; i < 5; i++ ) { + ddl.add(Protocol.DeckCardData.newBuilder() + .setDef(Protocol.CardDef.newBuilder().setAsset(289)).setHandle(0).setQty(2).setPrev(0).build()); + } + Protocol.GetDeckContentsResponse.Builder deckContentsResponse = Protocol.GetDeckContentsResponse.newBuilder(); + + for( long id : request.getDeckIdList() ) { + log.info("DeckId: {}", id); + deckContentsResponse.addDecks( + Protocol.DeckContents.newBuilder() + .setSuccess(true) + .setDeckId(id) + .addAllCards(ddl) + ); + } + + // Protocol.GetDeckContentsResponse dd = deckContentsResponse.build(); + + // Protocol.Notification n = generateNotification(215, + // dd.toByteString(), + // dd.getSerializedSize()); + + /* Protocol.Header header = Protocol.Header.newBuilder() + .setServiceId(4) + .setMethodId(1) + .setToken(conn.nextToken()) + .setObjectId(0) + .setSize(n.getSerializedSize()) + .setStatus(0) + .build();*/ + + // conn.send(new BattleNetPacket(header, n.toByteArray())); + + + ///////////////////// + + byte[] bb = Util.hexStringToByteArray("08CFED81FB02120D0A05089E051000100018022800120D0A0508AB041000100018022800120C0A04084D1000100018022800120D0A0508FB041000100018022800120D0A0508A1021000100018022800120D0A0508BB021000100018022800120D0A05089C021000100018022800120D0A0508BF011000100018022800120D0A0508DA0A1000100018022800120D0A0508BF031000100018022800120D0A0508960D1000100018022800120D0A0508F90A1000100018022800120D0A0508D8011000100018022800120D0A0508D90A1000100018022800120D0A0508B40410001000180228001AE801080110CFED81FB021A0D0A05089E0510001000180228001A0D0A0508AB0410001000180228001A0C0A04084D10001000180228001A0D0A0508FB0410001000180228001A0D0A0508A10210001000180228001A0D0A0508BB0210001000180228001A0D0A05089C0210001000180228001A0D0A0508BF0110001000180228001A0D0A0508DA0A10001000180228001A0D0A0508BF0310001000180228001A0D0A0508960D10001000180228001A0D0A0508F90A10001000180228001A0D0A0508D80110001000180228001A0D0A0508D90A10001000180228001A0D0A0508B40410001000180228001AE801080110969787FB021A0D0A0508EB0710001000180228001A0D0A0508DE0410001000180228001A0D0A0508970810001000180228001A0C0A04084310001000180228001A0D0A0508970D10001000180228001A0D0A0508D30210001000180228001A0D0A0508880510001000180228001A0D0A05089D0310001000180228001A0D0A0508810210001000180228001A0D0A0508DA0A10001000180228001A0D0A0508F90A10001000180228001A0D0A0508A40210001000180228001A0D0A0508D80110001000180228001A0D0A0508ED0610001000180228001A0D0A0508D90A1000100018022800"); + + + Protocol.ClientResponse clResponse = Protocol.ClientResponse.newBuilder() + .addAttribute(Protocol.Attribute.newBuilder() + .setName("id") + .setValue(Protocol.Variant.newBuilder() + .setIntValue(215) + )) + .addAttribute(Protocol.Attribute.newBuilder() + .setName("proto") + .setValue(Protocol.Variant.newBuilder() + .setBlobValue(ByteString.copyFrom(bb)) + )) + .build(); + + Protocol.Header header = Processor.generateResponse(clResponse.getSerializedSize(), packet.getHeader().getToken(), 0, 0); + conn.send(new BattleNetPacket(header, clResponse.toByteArray())); + } + + @Override + public int getId() { + return 214; + } +} diff --git a/src/main/java/com/alterdekim/hearthhack/component/processor/client/request/generic/AvailableFeatures.java b/src/main/java/com/alterdekim/hearthhack/component/processor/client/request/generic/AvailableFeatures.java index 06bb74c..a60e395 100644 --- a/src/main/java/com/alterdekim/hearthhack/component/processor/client/request/generic/AvailableFeatures.java +++ b/src/main/java/com/alterdekim/hearthhack/component/processor/client/request/generic/AvailableFeatures.java @@ -3,17 +3,20 @@ package com.alterdekim.hearthhack.component.processor.client.request.generic; import com.alterdekim.Protocol; import com.alterdekim.hearthhack.component.TcpConnection; import com.alterdekim.hearthhack.util.BattleNetPacket; -import com.alterdekim.hearthhack.util.GetAccountInfoRequest; +import lombok.AllArgsConstructor; +import lombok.RequiredArgsConstructor; +import lombok.Setter; import lombok.extern.slf4j.Slf4j; import static com.alterdekim.hearthhack.util.GameUtilities.generateNotification; import static com.alterdekim.hearthhack.util.GetAccountInfoRequest.FEATURES; -import static com.alterdekim.hearthhack.util.Util.setInterval; @Slf4j public class AvailableFeatures extends GenericParser { - private void executeFeatures(TcpConnection conn) { + private Executor executor; + + private void executeFeatures(TcpConnection conn) throws Exception { Protocol.GuardianVars guardianVars = Protocol.GuardianVars.newBuilder() .setShowUserUi(1) .setClientOptionsUpdateIntervalSeconds(300) @@ -30,20 +33,38 @@ public class AvailableFeatures extends GenericParser { .setSize(n.getSerializedSize()) .build(); - try { - conn.send(new BattleNetPacket(header, n.toByteArray())); - } catch (Exception e) { - log.error(e.getMessage()); - } + conn.send(new BattleNetPacket(header, n.toByteArray())); } @Override public void parseGenericRequest(int token, TcpConnection conn) throws Exception { - setInterval(() -> executeFeatures(conn), 4000); + if( this.executor != null ) this.executor.setRunning(false); + this.executor = new Executor(conn); + this.executor.start(); } @Override public int getId() { return FEATURES.getValue(); } + + @RequiredArgsConstructor + private class Executor extends Thread { + + private final TcpConnection conn; + @Setter + private boolean isRunning = true; + + @Override + public void run() { + try { + while(isRunning) { + Thread.sleep(2500); + executeFeatures(conn); + } + } catch (Exception e) { + log.error(e.getMessage()); + } + } + } } diff --git a/src/main/java/com/alterdekim/hearthhack/component/processor/client/request/generic/Boosters.java b/src/main/java/com/alterdekim/hearthhack/component/processor/client/request/generic/Boosters.java index 77a292f..8d689bc 100644 --- a/src/main/java/com/alterdekim/hearthhack/component/processor/client/request/generic/Boosters.java +++ b/src/main/java/com/alterdekim/hearthhack/component/processor/client/request/generic/Boosters.java @@ -6,14 +6,16 @@ import com.alterdekim.hearthhack.util.BattleNetPacket; import com.alterdekim.hearthhack.util.GetAccountInfoRequest; import static com.alterdekim.hearthhack.util.GameUtilities.generateEmptyNotification; +import static com.alterdekim.hearthhack.util.GameUtilities.generateNotification; import static com.alterdekim.hearthhack.util.GetAccountInfoRequest.BOOSTERS; public class Boosters extends GenericParser { @Override public void parseGenericRequest(int token, TcpConnection conn) throws Exception { - //Protocol.BoosterList boosterList = Protocol.BoosterList.newBuilder().build(); + Protocol.BoosterList boosterList = Protocol.BoosterList.newBuilder() + .build(); - Protocol.Notification n = generateEmptyNotification(224); + Protocol.Notification n = generateNotification(224, boosterList.toByteString(), boosterList.getSerializedSize()); Protocol.Header header = Protocol.Header.newBuilder() .setServiceId(4) diff --git a/src/main/java/com/alterdekim/hearthhack/component/processor/client/request/generic/PlayQueue.java b/src/main/java/com/alterdekim/hearthhack/component/processor/client/request/generic/PlayQueue.java new file mode 100644 index 0000000..e24af6a --- /dev/null +++ b/src/main/java/com/alterdekim/hearthhack/component/processor/client/request/generic/PlayQueue.java @@ -0,0 +1,39 @@ +package com.alterdekim.hearthhack.component.processor.client.request.generic; + +import com.alterdekim.Protocol; +import com.alterdekim.hearthhack.component.TcpConnection; +import com.alterdekim.hearthhack.util.BattleNetPacket; +import com.alterdekim.hearthhack.util.GetAccountInfoRequest; +import lombok.extern.slf4j.Slf4j; + +import static com.alterdekim.hearthhack.util.GameUtilities.generateNotification; +import static com.alterdekim.hearthhack.util.GetAccountInfoRequest.PVP_QUEUE; + +@Slf4j +public class PlayQueue extends GenericParser { + @Override + public void parseGenericRequest(int token, TcpConnection conn) throws Exception { + Protocol.PlayQueue playQueue = Protocol.PlayQueue.newBuilder() + .setQueue(Protocol.PlayQueueInfo.newBuilder() + .setGameType(Protocol.BnetGameType.BGT_RANKED_STANDARD)) + .build(); + + Protocol.Notification n = generateNotification(286, playQueue.toByteString(), playQueue.getSerializedSize()); + + Protocol.Header header = Protocol.Header.newBuilder() + .setServiceId(4) + .setMethodId(1) + .setToken(conn.nextToken()) + .setObjectId(0) + .setSize(n.getSerializedSize()) + .setStatus(0) + .build(); + + conn.send(new BattleNetPacket(header, n.toByteArray())); + } + + @Override + public int getId() { + return PVP_QUEUE.getValue(); + } +} diff --git a/src/main/java/com/alterdekim/hearthhack/component/processor/client/request/generic/ProfileNotices.java b/src/main/java/com/alterdekim/hearthhack/component/processor/client/request/generic/ProfileNotices.java index 19b98c6..4d44564 100644 --- a/src/main/java/com/alterdekim/hearthhack/component/processor/client/request/generic/ProfileNotices.java +++ b/src/main/java/com/alterdekim/hearthhack/component/processor/client/request/generic/ProfileNotices.java @@ -4,15 +4,26 @@ import com.alterdekim.Protocol; import com.alterdekim.hearthhack.component.TcpConnection; import com.alterdekim.hearthhack.util.BattleNetPacket; import com.alterdekim.hearthhack.util.GetAccountInfoRequest; +import lombok.AllArgsConstructor; +import lombok.RequiredArgsConstructor; +import lombok.Setter; +import lombok.extern.slf4j.Slf4j; import static com.alterdekim.hearthhack.util.GameUtilities.generateEmptyNotification; +import static com.alterdekim.hearthhack.util.GameUtilities.generateNotification; import static com.alterdekim.hearthhack.util.GetAccountInfoRequest.NOTICES; + +@Slf4j public class ProfileNotices extends GenericParser { - @Override - public void parseGenericRequest(int token, TcpConnection conn) throws Exception { - // Protocol.ProfileNotices - Protocol.Notification n = generateEmptyNotification(212); + + private Executor executor; + + private void sendNotices(TcpConnection conn) throws Exception { + Protocol.ProfileNotices notices = Protocol.ProfileNotices.newBuilder() + .build(); + + Protocol.Notification n = generateNotification(212, notices.toByteString(), notices.getSerializedSize()); Protocol.Header header = Protocol.Header.newBuilder() .setServiceId(4) @@ -26,8 +37,36 @@ public class ProfileNotices extends GenericParser { conn.send(new BattleNetPacket(header, n.toByteArray())); } + @Override + public void parseGenericRequest(int token, TcpConnection conn) throws Exception { + if( this.executor != null ) this.executor.setRunning(false); + this.executor = new Executor(conn); + this.executor.start(); + } + @Override public int getId() { return NOTICES.getValue(); } + + + @RequiredArgsConstructor + private class Executor extends Thread { + + private final TcpConnection conn; + @Setter + private boolean isRunning = true; + + @Override + public void run() { + try { + while(isRunning) { + Thread.sleep(7500); + sendNotices(conn); + } + } catch (Exception e) { + log.error(e.getMessage()); + } + } + } } diff --git a/src/main/java/com/alterdekim/hearthhack/util/GameType.java b/src/main/java/com/alterdekim/hearthhack/util/GameType.java new file mode 100644 index 0000000..74fbf5d --- /dev/null +++ b/src/main/java/com/alterdekim/hearthhack/util/GameType.java @@ -0,0 +1,42 @@ +package com.alterdekim.hearthhack.util; + +public enum GameType { + // Token: 0x04000710 RID: 1808 + GT_UNKNOWN, + // Token: 0x04000711 RID: 1809 + GT_VS_AI, + // Token: 0x04000712 RID: 1810 + GT_VS_FRIEND, + // Token: 0x04000713 RID: 1811 + GT_TUTORIAL, // =4 + // Token: 0x04000714 RID: 1812 + GT_ARENA, + // Token: 0x04000715 RID: 1813 + GT_TEST, + // Token: 0x04000716 RID: 1814 + GT_RANKED, + // Token: 0x04000717 RID: 1815 + GT_CASUAL, + // Token: 0x04000719 RID: 1817 + GT_TB_1P_VS_AI, + // Token: 0x0400071A RID: 1818 + GT_TB_2P_COOP, + // Token: 0x0400071B RID: 1819 + GT_LAST; + + /* + // Token: 0x04000718 RID: 1816 + GT_TAVERNBRAWL, // = 16 + */ + + public int getValue() { + return ordinal() + 3; + } + + public static GameType parseFromInt(long val) { + for( GameType r : values() ) { + if( r.getValue() == val ) return r; + } + return GT_LAST; + } +} diff --git a/src/main/java/com/alterdekim/hearthhack/util/GameUtilities.java b/src/main/java/com/alterdekim/hearthhack/util/GameUtilities.java index 284b5ab..3f1f4ca 100644 --- a/src/main/java/com/alterdekim/hearthhack/util/GameUtilities.java +++ b/src/main/java/com/alterdekim/hearthhack/util/GameUtilities.java @@ -35,6 +35,16 @@ public class GameUtilities { .build(); } + public static Protocol.Notification.Builder generateSmallNotification() { + return Protocol.Notification.newBuilder() + .setSenderId(Protocol.EntityId.newBuilder() + .setHigh(144115188075855872L) + .setLow(0)) + .setTargetId(Protocol.EntityId.newBuilder() + .setHigh(144115198130930503L) + .setLow(78330215)); + } + public static Protocol.Notification generateEmptyNotification(int message_type) { return Protocol.Notification.newBuilder() .setSenderId(Protocol.EntityId.newBuilder() diff --git a/src/main/java/com/alterdekim/hearthhack/util/PegasusPacket.java b/src/main/java/com/alterdekim/hearthhack/util/PegasusPacket.java index cf68bb2..fa888f5 100644 --- a/src/main/java/com/alterdekim/hearthhack/util/PegasusPacket.java +++ b/src/main/java/com/alterdekim/hearthhack/util/PegasusPacket.java @@ -1,8 +1,12 @@ package com.alterdekim.hearthhack.util; import com.google.protobuf.GeneratedMessageV3; +import lombok.Getter; import lombok.NoArgsConstructor; +import java.nio.ByteBuffer; +import java.nio.ByteOrder; + @NoArgsConstructor public class PegasusPacket { @@ -10,12 +14,16 @@ public class PegasusPacket { private final int SIZE_BYTES = 4; + @Getter public int size; + @Getter public int type; + @Getter public int context; + @Getter public Object body; private boolean sizeRead; @@ -79,21 +87,28 @@ public class PegasusPacket { return num2; } - /* public byte[] Encode() { + public byte[] Encode() { if (this.body instanceof GeneratedMessageV3) { GeneratedMessageV3 protoBuf = (GeneratedMessageV3) this.body; - this.size = (int) protoBuf.GetSerializedSize(); + this.size = protoBuf.getSerializedSize(); byte[] array = new byte[this.size + 4 + 4]; - Array.Copy(BitConverter.GetBytes(this.Type), 0, array, 0, 4); - Array.Copy(BitConverter.GetBytes(this.Size), 0, array, 4, 4); - protoBuf.Serialize(new MemoryStream(array, 8, this.Size)); + System.arraycopy(getBytes(this.type), 0, array, 0, 4); + System.arraycopy(getBytes(this.size), 0, array, 4, 4); + System.arraycopy(protoBuf.toByteArray(), 0, array, 8, this.size); return array; } return null; - }*/ + } private int toInt32_2(byte[] bytes, int index) { int a = (int)((int)(0xff & bytes[index]) << 32 | (int)(0xff & bytes[index + 1]) << 40 | (int)(0xff & bytes[index + 2]) << 48 | (int)(0xff & bytes[index + 3]) << 56); return a; } + + private static byte[] getBytes(int value) { + return ByteBuffer.allocate(4) + .order(ByteOrder.nativeOrder()) + .putInt(value) + .array(); + } } diff --git a/src/main/java/com/alterdekim/hearthhack/util/Util.java b/src/main/java/com/alterdekim/hearthhack/util/Util.java index 9799b49..58d6f8d 100644 --- a/src/main/java/com/alterdekim/hearthhack/util/Util.java +++ b/src/main/java/com/alterdekim/hearthhack/util/Util.java @@ -137,17 +137,4 @@ public class Util { String s = Integer.toHexString(i).toUpperCase(); return s.length() % 2 == 0 ? s : "0" + s; } - - public static void setInterval(Runnable runnable, int interval) { - new Thread(() -> { - try { - while(true) { - Thread.sleep(interval); - runnable.run(); - } - } catch (Exception e){ - log.error(e.getMessage()); - } - }).start(); - } } diff --git a/src/main/proto/bnet/protocol/protocol.proto b/src/main/proto/bnet/protocol/protocol.proto index 9c83285..3f96391 100644 --- a/src/main/proto/bnet/protocol/protocol.proto +++ b/src/main/proto/bnet/protocol/protocol.proto @@ -3173,4 +3173,79 @@ message PurchaseError { required Error error = 1; optional string purchase_in_progress = 2; optional string error_code = 3; +} + +// ref: PegasusUtil.PlayQueue +message PlayQueue { + // ref: PegasusUtil.PlayQueue/PacketID + enum PacketID { + ID = 286; + } + + required PlayQueueInfo queue = 1; +} + +// ref: PegasusShared.PlayQueueInfo +message PlayQueueInfo { + required BnetGameType game_type = 1; +} + +// ref: PegasusShared.BnetGameType +enum BnetGameType { + BGT_UNKNOWN = 0; + BGT_FRIENDS = 1; + BGT_RANKED_STANDARD = 2; + BGT_ARENA = 3; + BGT_VS_AI = 4; + BGT_TUTORIAL = 5; + BGT_ASYNC = 6; + BGT_NEWBIE = 9; + BGT_CASUAL_STANDARD = 10; + BGT_TEST1 = 11; + BGT_TEST2 = 12; + BGT_TEST3 = 13; + BGT_TAVERNBRAWL_PVP = 16; + BGT_TAVERNBRAWL_1P_VERSUS_AI = 17; + BGT_TAVERNBRAWL_2P_COOP = 18; + BGT_RANKED_WILD = 30; + BGT_CASUAL_WILD = 31; + BGT_LAST = 32; +} + +// ref: PegasusUtil.GetDeckContents +message GetDeckContents { + // ref: PegasusUtil.GetDeckContents/PacketID + enum PacketID { + system = 0; + ID = 214; + } + + repeated int64 deck_id = 1; +} + +// ref: PegasusUtil.GetDeckContentsResponse +message GetDeckContentsResponse { + // ref: PegasusUtil.GetDeckContentsResponse/PacketID + enum PacketID { + ID = 215; + } + + optional int64 deprecated_deck_id = 1; + repeated DeckCardData deprecated_cards = 2; + repeated DeckContents decks = 3; +} + +// ref: PegasusShared.DeckCardData +message DeckCardData { + required CardDef def = 1; + required int32 handle = 2 [default = 0]; + optional int32 qty = 3; + required int32 prev = 5 [default = 0]; +} + +// ref: PegasusUtil.DeckContents +message DeckContents { + required bool success = 1; + required int64 deck_id = 2; + repeated DeckCardData cards = 3; } \ No newline at end of file diff --git a/src/main/proto/pegasus/pegasusgame/pegasusgame.proto b/src/main/proto/pegasus/pegasusgame/pegasusgame.proto new file mode 100644 index 0000000..d99333c --- /dev/null +++ b/src/main/proto/pegasus/pegasusgame/pegasusgame.proto @@ -0,0 +1,448 @@ +syntax = "proto2"; +package pegasus.pegasusgame; + +// Proto extractor compiled unit - https://github.com/HearthSim/proto-extractor + +import "pegasus/pegasusshared/pegasusshared.proto"; + +option java_package = "com.alterdekim"; +option java_outer_classname = "PegasusGame"; + +// ref: PegasusGame.AllOptions +message AllOptions { + // ref: PegasusGame.AllOptions/PacketID + enum PacketID { + ID = 14; + } + + required int32 id = 1; + repeated Option options = 2; +} + +// ref: PegasusGame.ChooseEntities +message ChooseEntities { + // ref: PegasusGame.ChooseEntities/PacketID + enum PacketID { + ID = 3; + } + + required int32 id = 1; + repeated int32 entities = 2 [packed=true]; +} + +// ref: PegasusGame.ChooseOption +message ChooseOption { + // ref: PegasusGame.ChooseOption/PacketID + enum PacketID { + ID = 2; + } + + required int32 id = 1; + required int32 index = 2; + required int32 target = 3; + optional int32 sub_option = 4; + optional int32 position = 5; +} + +// ref: PegasusGame.ClientHistoryMeta +message ClientHistoryMeta { + // ref: PegasusGame.ClientHistoryMeta/Type + enum Type { + INVALID = 0; + SHOW_BIG_CARD = 1; + EFFECT_TIMING_HINT = 2; + } + +} + +// ref: PegasusGame.Concede +message Concede { + // ref: PegasusGame.Concede/PacketID + enum PacketID { + ID = 11; + } + +} + +// ref: PegasusGame.DebugMessage +message DebugMessage { + // ref: PegasusGame.DebugMessage/PacketID + enum PacketID { + ID = 5; + } + + required string message = 1; +} + +// ref: PegasusGame.EntitiesChosen +message EntitiesChosen { + // ref: PegasusGame.EntitiesChosen/PacketID + enum PacketID { + ID = 13; + } + + required ChooseEntities choose_entities = 3; + required int32 player_id = 4; +} + +// ref: PegasusGame.Entity +message Entity { + required int32 id = 1; + repeated Tag tags = 2; +} + +// ref: PegasusGame.EntityChoices +message EntityChoices { + // ref: PegasusGame.EntityChoices/PacketID + enum PacketID { + ID = 17; + } + + required int32 id = 1; + required int32 choice_type = 2; + required int32 count_min = 4; + required int32 count_max = 5; + repeated int32 entities = 6 [packed=true]; + optional int32 source = 7; + required int32 player_id = 8; +} + +// ref: PegasusGame.GameCanceled +message GameCanceled { + // ref: PegasusGame.GameCanceled/PacketID + enum PacketID { + ID = 12; + } + + // ref: PegasusGame.GameCanceled/Reason + enum Reason { + OPPONENT_TIMEOUT = 1; + } + + required Reason reason = 1; +} + +// ref: PegasusGame.GameSetup +message GameSetup { + // ref: PegasusGame.GameSetup/PacketID + enum PacketID { + ID = 16; + } + + required int32 board = 1; + required int32 max_secrets_per_player = 2; + required int32 max_friendly_minions_per_player = 3; + optional int32 keep_alive_frequency_seconds = 4; + optional int32 disconnect_when_stuck_seconds = 5; +} + +// ref: PegasusGame.GetGameState +message GetGameState { + // ref: PegasusGame.GetGameState/PacketID + enum PacketID { + ID = 1; + } + +} + +// ref: PegasusGame.Handshake +message Handshake { + // ref: PegasusGame.Handshake/PacketID + enum PacketID { + ID = 168; + } + + required int32 game_handle = 1; + required string password = 2; + required int64 client_handle = 3; + optional int32 mission = 4; + required string version = 5; + required pegasus.pegasusshared.Platform platform = 7; +} + +// ref: PegasusGame.HistoryBlock +message HistoryBlock { + // ref: PegasusGame.HistoryBlock/Type + enum Type { + INVALID = 0; + ATTACK = 1; + JOUST = 2; + POWER = 3; + TRIGGER = 5; + DEATHS = 6; + PLAY = 7; + FATIGUE = 8; + RITUAL = 9; + } + +} + +// ref: PegasusGame.HistoryMeta +message HistoryMeta { + // ref: PegasusGame.HistoryMeta/Type + enum Type { + TARGET = 0; + DAMAGE = 1; + HEALING = 2; + JOUST = 3; + CLIENT_HISTORY = 4; + } + +} + +// ref: PegasusGame.InviteToSpectate +message InviteToSpectate { + // ref: PegasusGame.InviteToSpectate/PacketID + enum PacketID { + ID = 25; + } + + optional pegasus.pegasusshared.BnetId target_bnet_account_id = 1; + required pegasus.pegasusshared.BnetId target_game_account_id = 2; +} + +// ref: PegasusGame.MouseInfo +message MouseInfo { + required int32 arrow_origin = 1; + required int32 held_card = 2; + required int32 over_card = 3; + required int32 x = 4; + required int32 y = 5; +} + +// ref: PegasusGame.NAckOption +message NAckOption { + // ref: PegasusGame.NAckOption/PacketID + enum PacketID { + ID = 10; + } + + required int32 id = 1; +} + +// ref: PegasusGame.Option +message Option { + // ref: PegasusGame.Option/Type + enum Type { + PASS = 1; + END_TURN = 2; + POWER = 3; + } + + required Type type = 1; + optional SubOption main_option = 2; + repeated SubOption sub_options = 3; +} + +// ref: PegasusGame.Ping +message Ping { + // ref: PegasusGame.Ping/PacketID + enum PacketID { + ID = 115; + } + +} + +// ref: PegasusGame.Player +message Player { + required int32 id = 1; + required pegasus.pegasusshared.BnetId game_account_id = 2; + required int32 card_back = 3; + required Entity entity = 4; +} + +// ref: PegasusGame.Pong +message Pong { + // ref: PegasusGame.Pong/PacketID + enum PacketID { + ID = 116; + } + +} + +// ref: PegasusGame.PowerHistory +message PowerHistory { + // ref: PegasusGame.PowerHistory/PacketID + enum PacketID { + ID = 19; + } + + repeated PowerHistoryData list = 1; +} + +// ref: PegasusGame.PowerHistoryCreateGame +message PowerHistoryCreateGame { + required Entity game_entity = 1; + repeated Player players = 2; +} + +// ref: PegasusGame.PowerHistoryData +message PowerHistoryData { + optional PowerHistoryEntity full_entity = 1; + optional PowerHistoryEntity show_entity = 2; + optional PowerHistoryHide hide_entity = 3; + optional PowerHistoryTagChange tag_change = 4; + optional PowerHistoryCreateGame create_game = 5; + optional PowerHistoryStart power_start = 6; + optional PowerHistoryEnd power_end = 7; + optional PowerHistoryMetaData meta_data = 8; + optional PowerHistoryEntity change_entity = 9; +} + +// ref: PegasusGame.PowerHistoryEnd +message PowerHistoryEnd { +} + +// ref: PegasusGame.PowerHistoryEntity +message PowerHistoryEntity { + required int32 entity = 1; + required string name = 2; + repeated Tag tags = 3; +} + +// ref: PegasusGame.PowerHistoryHide +message PowerHistoryHide { + required int32 entity = 1; + required int32 zone = 2; +} + +// ref: PegasusGame.PowerHistoryMetaData +message PowerHistoryMetaData { + repeated int32 info = 2 [packed=true]; + optional HistoryMeta.Type type = 3 [default = TARGET]; + optional int32 data = 4; +} + +// ref: PegasusGame.PowerHistoryStart +message PowerHistoryStart { + required HistoryBlock.Type type = 1; + required int32 index = 2; + required int32 source = 3; + required int32 target = 4; + optional string effect_card_id = 5; +} + +// ref: PegasusGame.PowerHistoryTagChange +message PowerHistoryTagChange { + required int32 entity = 1; + required int32 tag = 2; + required int32 value = 3; +} + +// ref: PegasusGame.RemoveSpectators +message RemoveSpectators { + // ref: PegasusGame.RemoveSpectators/PacketID + enum PacketID { + ID = 26; + } + + repeated pegasus.pegasusshared.BnetId target_gameaccount_ids = 1; + optional bool kick_all_spectators = 2 [default = false]; + optional bool regenerate_spectator_password = 3 [default = false]; +} + +// ref: PegasusGame.ServerResult +message ServerResult { + // ref: PegasusGame.ServerResult/Code + enum Code { + RESULT_OK = 0; + RESULT_RETRY = 1; + RESULT_NOT_EXISTS = 2; + } + + // ref: PegasusGame.ServerResult/Constants + enum Constants { + DEFAULT_RETRY_SECONDS = 2; + } + + // ref: PegasusGame.ServerResult/PacketID + enum PacketID { + ID = 23; + } + + required int32 result_code = 1; + optional float retry_delay_seconds = 2; +} + +// ref: PegasusGame.SpectatorChange +message SpectatorChange { + required pegasus.pegasusshared.BnetId game_account_id = 1; + required bool is_removed = 2; +} + +// ref: PegasusGame.SpectatorHandshake +message SpectatorHandshake { + // ref: PegasusGame.SpectatorHandshake/PacketID + enum PacketID { + ID = 22; + } + + required int32 game_handle = 1; + required string password = 2; + required string version = 3; + required pegasus.pegasusshared.Platform platform = 4; + required pegasus.pegasusshared.BnetId game_account_id = 5; +} + +// ref: PegasusGame.SpectatorNotify +message SpectatorNotify { + // ref: PegasusGame.SpectatorNotify/PacketID + enum PacketID { + ID = 24; + } + + required int32 player_id = 1; + optional ChooseOption choose_option = 2; + repeated SpectatorChange spectator_change = 4; + optional string spectator_password_update = 5; + optional SpectatorRemoved spectator_removed = 6; +} + +// ref: PegasusGame.SpectatorRemoved +message SpectatorRemoved { + // ref: PegasusGame.SpectatorRemoved/SpectatorRemovedReason + enum SpectatorRemovedReason { + SPECTATOR_REMOVED_REASON_KICKED = 0; + SPECTATOR_REMOVED_REASON_GAMEOVER = 1; + } + + required int32 reason_code = 1; + optional pegasus.pegasusshared.BnetId removed_by = 2; +} + +// ref: PegasusGame.SubOption +message SubOption { + required int32 id = 1; + repeated int32 targets = 3 [packed=true]; +} + +// ref: PegasusGame.Tag +message Tag { + required int32 name = 1; + required int32 value = 2; +} + +// ref: PegasusGame.TurnTimer +message TurnTimer { + // ref: PegasusGame.TurnTimer/PacketID + enum PacketID { + ID = 9; + } + + required int32 seconds = 1; + required int32 turn = 2; + required bool show = 3; +} + +// ref: PegasusGame.UserUI +message UserUI { + // ref: PegasusGame.UserUI/PacketID + enum PacketID { + ID = 15; + } + + optional MouseInfo mouse_info = 1; + optional int32 emote = 2; + optional int32 player_id = 3; +} + diff --git a/src/main/proto/pegasus/pegasusshared/pegasusshared.proto b/src/main/proto/pegasus/pegasusshared/pegasusshared.proto new file mode 100644 index 0000000..b8e15f3 --- /dev/null +++ b/src/main/proto/pegasus/pegasusshared/pegasusshared.proto @@ -0,0 +1,662 @@ +syntax = "proto2"; +package pegasus.pegasusshared; + +// Proto extractor compiled unit - https://github.com/HearthSim/proto-extractor + +option java_package = "com.alterdekim"; +option java_outer_classname = "PegasusShared"; + +// ref: PegasusShared.AssetType +enum AssetType { + ASSET_TYPE_SCENARIO = 1; + ASSET_TYPE_SUBSET_CARD = 2; + ASSET_TYPE_DECK_RULESET = 3; +} + +// ref: PegasusShared.BattlePayProvider +enum BattlePayProvider { + BP_PROVIDER_BLIZZARD = 1; + BP_PROVIDER_APPLE = 2; + BP_PROVIDER_GOOGLE_PLAY = 3; + BP_PROVIDER_AMAZON = 4; +} + +// ref: PegasusShared.BnetGameType +enum BnetGameType { + BGT_UNKNOWN = 0; + BGT_FRIENDS = 1; + BGT_RANKED_STANDARD = 2; + BGT_ARENA = 3; + BGT_VS_AI = 4; + BGT_TUTORIAL = 5; + BGT_ASYNC = 6; + BGT_NEWBIE = 9; + BGT_CASUAL_STANDARD = 10; + BGT_TEST1 = 11; + BGT_TEST2 = 12; + BGT_TEST3 = 13; + BGT_TAVERNBRAWL_PVP = 16; + BGT_TAVERNBRAWL_1P_VERSUS_AI = 17; + BGT_TAVERNBRAWL_2P_COOP = 18; + BGT_RANKED_WILD = 30; + BGT_CASUAL_WILD = 31; + BGT_LAST = 32; +} + +// ref: PegasusShared.DatabaseAction +enum DatabaseAction { + DB_A_UNKNOWN = 0; + DB_A_GET_DECK = 1; + DB_A_CREATE_DECK = 2; + DB_A_RENAME_DECK = 3; + DB_A_DELETE_DECK = 4; + DB_A_SET_DECK = 5; + DB_A_OPEN_BOOSTER = 6; + DB_A_GAMES_INFO = 7; +} + +// ref: PegasusShared.DatabaseResult +enum DatabaseResult { + DB_E_SQL_EX = -1; + DB_E_UNKNOWN = 0; + DB_E_SUCCESS = 1; + DB_E_NOT_OWNED = 2; + DB_E_CONSTRAINT = 3; + DB_E_NOT_FOUND = 4; + DB_E_EXCEPTION = 9; + DB_E_BAD_PARAM = 11; +} + +// ref: PegasusShared.DeckSourceType +enum DeckSourceType { + DECK_SOURCE_TYPE_UNKNOWN = 0; + DECK_SOURCE_TYPE_NORMAL = 1; + DECK_SOURCE_TYPE_TEMPLATE = 2; + DECK_SOURCE_TYPE_BASIC_DECK = 3; +} + +// ref: PegasusShared.DeckType +enum DeckType { + NORMAL_DECK = 1; + AI_DECK = 2; + DRAFT_DECK = 4; + PRECON_DECK = 5; + TAVERN_BRAWL_DECK = 6; + HIDDEN_DECK = 1000; +} + +// ref: PegasusShared.ErrorCode +enum ErrorCode { + ERROR_OK = 0; + ERROR_HEARTHSTONE_BEGIN = 1000000; + ERROR_GLOBAL_INVALID_INPUT = 1000001; + ERROR_GLOBAL_NO_DATA = 1000002; + ERROR_GLOBAL_NOT_YET_IMPLEMENTED = 1000003; + ERROR_GLOBAL_DATA_MODIFIED = 1000004; + ERROR_SCENARIO_INCORRECT_NUM_PLAYERS = 1000500; + ERROR_SCENARIO_NO_DECK_SPECIFIED = 1000501; + ERROR_SCENARIO_MUST_BE_SERVER_ONLY = 1000502; + ERROR_TAVERN_BRAWL_SEASON_INCREMENTED = 1001000; + ERROR_TAVERN_BRAWL_NOT_ACTIVE = 1001001; + ERROR_DECK_RULESET_RULE_UNKNOWN_TYPE = 1002000; + ERROR_DECK_RULESET_RULE_DB_READ_ERROR = 1002001; + ERROR_DECK_RULESET_RULE_VIOLATION = 1002002; + ERROR_DECK_RULESET_DECK_CARD_ID_UNKNOWN = 1002003; + ERROR_DECK_RULESET_HERO_CARD_GUID_UNKNOWN = 1002004; + ERROR_DECK_RULESET_DECK_CARD_GUID_UNKNOWN = 1002005; + ERROR_DECK_VALIDATION_DB_WRITE_ERROR = 1002006; + ERROR_DECK_VALIDATION_WRONG_FORMAT = 1002007; +} + +// ref: PegasusShared.GameType +enum GameType { + GT_UNKNOWN = 0; + GT_VS_AI = 1; + GT_VS_FRIEND = 2; + GT_TUTORIAL = 4; + GT_ARENA = 5; + GT_TEST = 6; + GT_RANKED = 7; + GT_CASUAL = 8; + GT_TAVERNBRAWL = 16; + GT_TB_1P_VS_AI = 17; + GT_TB_2P_COOP = 18; + GT_LAST = 19; +} + +// ref: PegasusShared.RewardTrigger +enum RewardTrigger { + REWARD_TRIGGER_UNKNOWN = 0; + REWARD_TRIGGER_NONE = 1; + REWARD_TRIGGER_WIN_GAME = 2; + REWARD_TRIGGER_FINISH_GAME = 3; +} + +// ref: PegasusShared.RewardType +enum RewardType { + REWARD_UNKNOWN = 0; + REWARD_NONE = 1; + REWARD_ADVENTURE_PROGRESS = 2; + REWARD_ARCANE_DUST = 3; + REWARD_BASIC_CARD = 4; + REWARD_BOOSTER_PACKS = 5; + REWARD_CARD_BACK = 6; + REWARD_CARD_ID = 7; + REWARD_CARD_ID_2X = 8; + REWARD_CARD_SET = 9; + DEPRECATED_REWARD_CRAFTABLE_GOLDEN = 10; + REWARD_GOLD = 11; + REWARD_GOLD_HERO = 12; + REWARD_FORGE_TICKETS = 13; + REWARD_HERO = 14; + REWARD_EXTERNAL_GAME_MOUNT = 15; +} + +// ref: PegasusShared.RuleType +enum RuleType { + RULE_NONE = 0; + RULE_CHOOSE_HERO = 1; + RULE_CHOOSE_DECK = 2; +} + +// ref: PegasusShared.AccountLicenseInfo +message AccountLicenseInfo { + // ref: PegasusShared.AccountLicenseInfo/Flags + enum Flags { + OWNED = 1; + } + + required int64 license = 1; + required uint64 flags = 2; + required int64 cas_id = 3; +} + +// ref: PegasusShared.AdventureProgress +message AdventureProgress { + // ref: PegasusShared.AdventureProgress/Flags + enum Flags { + OWNED = 1; + DEFEAT_HEROIC_MISSION_1 = 2; + DEFEAT_HEROIC_MISSION_2 = 4; + DEFEAT_HEROIC_MISSION_3 = 8; + DEFEAT_HEROIC_MISSION_4 = 16; + DEFEAT_CLASS_CHALLENGE_MISSION_1 = 256; + DEFEAT_CLASS_CHALLENGE_MISSION_2 = 512; + } + + required int32 wing_id = 1; + required int32 progress = 2; + optional int32 ack = 3 [default = 0]; + required uint64 flags = 4; +} + +// ref: PegasusShared.AssetKey +message AssetKey { + required AssetType type = 1; + optional int32 asset_id = 2; +} + +// ref: PegasusShared.AssetRecordInfo +message AssetRecordInfo { + required AssetKey asset = 1; + required uint32 record_byte_size = 2; + required bytes record_hash = 3; +} + +// ref: PegasusShared.BnetId +message BnetId { + required uint64 hi = 1; + required uint64 lo = 2; +} + +// ref: PegasusShared.BoosterInfo +message BoosterInfo { + required int32 type = 2; + required int32 count = 3; +} + +// ref: PegasusShared.CachedCard +message CachedCard { + required int64 card_id = 1; + required int32 asset_card_id = 2; + required Date insert_date = 3; + required bool is_seen = 4; + required int32 premium = 5; +} + +// ref: PegasusShared.CachedCollection +message CachedCollection { + repeated CachedCard card_collection = 1; +} + +// ref: PegasusShared.CardDef +message CardDef { + required int32 asset = 1; + optional int32 premium = 2; +} + +// ref: PegasusShared.CardStack +message CardStack { + required CardDef card_def = 1; + required Date latest_insert_date = 2; + required int32 count = 3; + required int32 num_seen = 4; +} + +// ref: PegasusShared.DatabaseDeckCard +message DatabaseDeckCard { + required int32 asset_card_id = 1; + required int32 premium = 2; + required int32 quantity = 3; +} + +// ref: PegasusShared.DatabaseDeckContent +message DatabaseDeckContent { + repeated DatabaseDeckCard deck_cards = 1; +} + +// ref: PegasusShared.Date +message Date { + required int32 year = 1; + required int32 month = 2; + required int32 day = 3; + required int32 hours = 4; + required int32 min = 5; + required int32 sec = 6; +} + +// ref: PegasusShared.DeckCardData +message DeckCardData { + required CardDef def = 1; + required int32 handle = 2 [default = 0]; + optional int32 qty = 3; + required int32 prev = 5 [default = 0]; +} + +// ref: PegasusShared.DeckInfo +message DeckInfo { + // ref: PegasusShared.DeckInfo/ValidityFlags + enum ValidityFlags { + UNLOCKED_HERO_CLASS = 1; + OWNS_CARDS = 2; + HAS_30_CARDS = 4; + OBEYS_MAXES = 8; + CLASS_MATCHES = 16; + OWNS_CARD_BACK = 32; + OWNS_HERO = 64; + TAGGED_STANDARD = 128; + NEEDS_VALIDATION = 256; + NEEDS_NAME = 512; + } + + required int64 id = 1; + required string name = 2; + required int32 card_back = 3; + required int32 hero = 4; + required DeckType deck_type = 5; + required uint64 validity = 6; + required int32 hero_premium = 7; + required bool card_back_override = 8; + required bool hero_override = 9; + optional int64 last_modified = 10; + optional int32 season_id = 11; + optional int64 sort_order = 12; + optional int64 create_date = 13; + optional DeckSourceType source_type = 14 [default = DECK_SOURCE_TYPE_UNKNOWN]; +} + +// ref: PegasusShared.DeckRulesetDbRecord +message DeckRulesetDbRecord { + required int32 id = 1; + repeated DeckRulesetRuleDbRecord rules = 2; +} + +// ref: PegasusShared.DeckRulesetRuleDbRecord +message DeckRulesetRuleDbRecord { + required int32 id = 1; + required int32 deck_ruleset_id = 2; + optional int32 applies_to_subset_id = 3; + optional bool applies_to_is_not = 4; + required string rule_type = 5; + required bool rule_is_not = 6; + optional int32 min_value = 7; + optional int32 max_value = 8; + optional int32 tag = 9; + optional int32 tag_min_value = 10; + optional int32 tag_max_value = 11; + optional string string_value = 12; + repeated int32 target_subset_ids = 13; + repeated LocalizedString strings = 100; +} + +// ref: PegasusShared.DeckRulesetValidationResults +message DeckRulesetValidationResults { + required int32 deck_ruleset_id = 1 [default = 0]; + optional ErrorCode error_code = 2 [default = ERROR_OK]; + repeated DeckRulesetViolation violations = 3; +} + +// ref: PegasusShared.DeckRulesetViolation +message DeckRulesetViolation { + optional CardDef card = 1; + optional int32 count = 2; + required int32 deck_rule_id = 100; + optional string deck_rule_desc = 101; +} + +// ref: PegasusShared.FavoriteHero +message FavoriteHero { + required int32 class_id = 1; + required CardDef hero = 2; +} + +// ref: PegasusShared.GameSetupRule +message GameSetupRule { + required int32 id = 1; + required RuleType rule_type = 2; + required int64 data1 = 3; + required int64 data2 = 4; + required int64 data3 = 5; +} + +// ref: PegasusShared.LocalizedString +message LocalizedString { + required string key = 1; + optional string deprecated_value = 2; + optional int32 deprecated_locale = 3; + repeated LocalizedStringValue values = 4; +} + +// ref: PegasusShared.LocalizedStringValue +message LocalizedStringValue { + required int32 locale = 1; + required string value = 2; +} + +// ref: PegasusShared.Platform +message Platform { + required int32 os = 1; + required int32 screen = 2; + required string name = 3; + optional int32 store = 4; +} + +// ref: PegasusShared.PlayQueueInfo +message PlayQueueInfo { + required BnetGameType game_type = 1; +} + +// ref: PegasusShared.ProfileNoticeAccountLicense +message ProfileNoticeAccountLicense { + // ref: PegasusShared.ProfileNoticeAccountLicense/NoticeID + enum NoticeID { + ID = 16; + } + + required int64 license = 1; + required int64 cas_id = 2; +} + +// ref: PegasusShared.ProfileNoticeAdventureProgress +message ProfileNoticeAdventureProgress { + // ref: PegasusShared.ProfileNoticeAdventureProgress/NoticeID + enum NoticeID { + ID = 14; + } + + required int32 wing_id = 1; +} + +// ref: PegasusShared.ProfileNoticeBonusStars +message ProfileNoticeBonusStars { + // ref: PegasusShared.ProfileNoticeBonusStars/NoticeID + enum NoticeID { + ID = 12; + } + + required int32 star_level = 1; + required int32 stars = 2; +} + +// ref: PegasusShared.ProfileNoticeCardBack +message ProfileNoticeCardBack { + // ref: PegasusShared.ProfileNoticeCardBack/NoticeID + enum NoticeID { + ID = 11; + } + + required int32 card_back = 1; +} + +// ref: PegasusShared.ProfileNoticeDisconnectedGameResult +message ProfileNoticeDisconnectedGameResult { + // ref: PegasusShared.ProfileNoticeDisconnectedGameResult/GameResult + enum GameResult { + GR_UNKNOWN = 0; + GR_PLAYING = 1; + GR_WINNER = 2; + GR_TIE = 3; + } + + // ref: PegasusShared.ProfileNoticeDisconnectedGameResult/NoticeID + enum NoticeID { + ID = 4; + } + + // ref: PegasusShared.ProfileNoticeDisconnectedGameResult/PlayerResult + enum PlayerResult { + PR_UNKNOWN = 0; + PR_WON = 1; + PR_LOST = 2; + PR_DISCONNECTED = 3; + PR_QUIT = 4; + } + + optional GameType game_type = 8 [default = GT_UNKNOWN]; + optional int32 mission_id = 9; + optional GameResult game_result = 10 [default = GR_UNKNOWN]; + optional PlayerResult your_result = 11 [default = PR_UNKNOWN]; + optional PlayerResult opponent_result = 12 [default = PR_UNKNOWN]; +} + +// ref: PegasusShared.ProfileNoticeLevelUp +message ProfileNoticeLevelUp { + // ref: PegasusShared.ProfileNoticeLevelUp/NoticeID + enum NoticeID { + ID = 15; + } + + required int32 hero_class = 1; + required int32 new_level = 2; +} + +// ref: PegasusShared.ProfileNoticeMedal +message ProfileNoticeMedal { + // ref: PegasusShared.ProfileNoticeMedal/MedalType + enum MedalType { + UNKNOWN_MEDAL = 0; + STANDARD_MEDAL = 1; + WILD_MEDAL = 2; + } + + // ref: PegasusShared.ProfileNoticeMedal/NoticeID + enum NoticeID { + ID = 1; + } + + required int32 star_level = 1; + optional int32 legend_rank = 2; + optional int32 best_star_level = 3; + optional RewardChest chest = 4; + optional MedalType medal_type = 5 [default = UNKNOWN_MEDAL]; +} + +// ref: PegasusShared.ProfileNoticePreconDeck +message ProfileNoticePreconDeck { + // ref: PegasusShared.ProfileNoticePreconDeck/NoticeID + enum NoticeID { + ID = 5; + } + + required int64 deck = 1; + required int32 hero = 2; +} + +// ref: PegasusShared.ProfileNoticePurchase +message ProfileNoticePurchase { + // ref: PegasusShared.ProfileNoticePurchase/NoticeID + enum NoticeID { + ID = 10; + } + + required string product_id = 1; + optional int64 data = 2; + optional int32 currency = 3; +} + +// ref: PegasusShared.ProfileNoticeRewardBooster +message ProfileNoticeRewardBooster { + // ref: PegasusShared.ProfileNoticeRewardBooster/NoticeID + enum NoticeID { + ID = 2; + } + + required int32 booster_type = 1; + required int32 booster_count = 2; +} + +// ref: PegasusShared.ProfileNoticeRewardCard +message ProfileNoticeRewardCard { + // ref: PegasusShared.ProfileNoticeRewardCard/NoticeID + enum NoticeID { + ID = 3; + } + + required CardDef card = 1; + optional int32 quantity = 2; +} + +// ref: PegasusShared.ProfileNoticeRewardCard2x +message ProfileNoticeRewardCard2x { + // ref: PegasusShared.ProfileNoticeRewardCard2x/NoticeID + enum NoticeID { + ID = 13; + } + +} + +// ref: PegasusShared.ProfileNoticeRewardDust +message ProfileNoticeRewardDust { + // ref: PegasusShared.ProfileNoticeRewardDust/NoticeID + enum NoticeID { + ID = 6; + } + + required int32 amount = 1; +} + +// ref: PegasusShared.ProfileNoticeRewardForge +message ProfileNoticeRewardForge { + // ref: PegasusShared.ProfileNoticeRewardForge/NoticeID + enum NoticeID { + ID = 8; + } + + required int32 quantity = 1; +} + +// ref: PegasusShared.ProfileNoticeRewardGold +message ProfileNoticeRewardGold { + // ref: PegasusShared.ProfileNoticeRewardGold/NoticeID + enum NoticeID { + ID = 9; + } + + required int32 amount = 1; +} + +// ref: PegasusShared.ProfileNoticeRewardMount +message ProfileNoticeRewardMount { + // ref: PegasusShared.ProfileNoticeRewardMount/NoticeID + enum NoticeID { + ID = 7; + } + + required int32 mount_id = 1; +} + +// ref: PegasusShared.RewardBag +message RewardBag { + optional ProfileNoticeRewardBooster reward_booster = 1; + optional ProfileNoticeRewardCard reward_card = 2; + optional ProfileNoticeRewardDust reward_dust = 3; + optional ProfileNoticeRewardGold reward_gold = 4; + optional ProfileNoticeCardBack reward_card_back = 5; +} + +// ref: PegasusShared.RewardChest +message RewardChest { + optional RewardBag bag1 = 1; + optional RewardBag bag2 = 2; + optional RewardBag bag3 = 3; + optional RewardBag bag4 = 4; + optional RewardBag bag5 = 5; +} + +// ref: PegasusShared.ScenarioDbRecord +message ScenarioDbRecord { + required int32 id = 1; + optional string note_desc = 2; + required int32 num_players = 3; + required int64 player1_hero_card_id = 4; + required int64 player2_hero_card_id = 5; + required bool is_expert = 6; + required int32 adventure_id = 7; + optional int32 adventure_mode_id = 8; + required int32 wing_id = 9; + required int32 sort_order = 10; + optional int64 client_player2_hero_card_id = 11; + optional string tavern_brawl_texture = 12; + optional string tavern_brawl_texture_phone = 13; + optional Vector2 tavern_brawl_texture_phone_offset = 14; + optional bool is_coop = 15; + optional int32 deck_ruleset_id = 16; + repeated LocalizedString strings = 100; + repeated GameSetupRule rules = 101; +} + +// ref: PegasusShared.SubsetCardListDbRecord +message SubsetCardListDbRecord { + required int32 subset_id = 1; + repeated int32 card_ids = 2; +} + +// ref: PegasusShared.TavernBrawlPlayerRecord +message TavernBrawlPlayerRecord { + required int32 reward_progress = 1; + optional int32 games_played = 2; + required int32 games_won = 3; + optional int32 win_streak = 4; +} + +// ref: PegasusShared.TavernBrawlSpec +message TavernBrawlSpec { + optional uint64 end_seconds_from_now = 1; + required int32 scenario_id = 2; + required uint32 scenario_record_byte_size = 3; + required bytes scenario_record_hash = 4; + required RewardType reward_type = 5; + required int64 reward_data1 = 6; + required int64 reward_data2 = 7; + optional RewardTrigger reward_trigger = 8 [default = REWARD_TRIGGER_UNKNOWN]; + required int32 season_id = 11; + repeated AssetRecordInfo additional_assets = 100; + required TavernBrawlPlayerRecord my_record = 105; +} + +// ref: PegasusShared.Vector2 +message Vector2 { + required float x = 1 [default = 0]; + required float y = 2 [default = 0]; +} +