ladder start.

This commit is contained in:
Michael Wain 2024-06-07 03:07:38 +03:00
parent 0dc0f0b9ce
commit 42fe4ef3ac
21 changed files with 1861 additions and 55 deletions

View File

@ -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);

File diff suppressed because one or more lines are too long

View File

@ -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);
}
}

View File

@ -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();
}

View File

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

View File

@ -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";
}
}

View File

@ -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";
}
}

View File

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

View File

@ -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");

View File

@ -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;
}
}

View File

@ -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());
}
}
}
}

View File

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

View File

@ -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();
}
}

View File

@ -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());
}
}
}
}

View File

@ -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;
}
}

View File

@ -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()

View File

@ -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();
}
}

View File

@ -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();
}
}

View File

@ -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;
}

View File

@ -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;
}

View File

@ -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];
}