Skip to content

Commit 46c75d2

Browse files
committed
add zlib compression
1 parent 611dc49 commit 46c75d2

9 files changed

+173
-6
lines changed

CMakeLists.txt

+5-1
Original file line numberDiff line numberDiff line change
@@ -133,7 +133,7 @@ if(AUTO_DOWNLOAD_LIBRARY)
133133
if(USE_ZLIB_NG)
134134
download_project(
135135
PROJ zlib-ng
136-
GIT_REPOSITORY https://github.com/jedisct1/libsodium.git
136+
GIT_REPOSITORY https://github.com/zlib-ng/zlib-ng.git
137137
GIT_TAG develop
138138
SOURCE_DIR ${PROJECT_SOURCE_DIR}/deps/zlib-ng
139139
UPDATE_DISCONNECTED 1
@@ -205,6 +205,10 @@ if(USE_LIBOPUS)
205205
add_subdirectory(deps/opus)
206206
endif()
207207

208+
if(USE_ZLIB_NG)
209+
add_subdirectory(deps/zlib-ng)
210+
endif()
211+
208212
# Get Version Info
209213
if(Git_FOUND)
210214
execute_process(

include/sleepy_discord/client.h

+22-1
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,7 @@
2727
#include "timer.h"
2828
#include "voice_connection.h"
2929
#include "asio_schedule.h"
30+
#include "compression.h"
3031

3132
namespace SleepyDiscord {
3233
#define TOKEN_SIZE 64
@@ -385,6 +386,22 @@ namespace SleepyDiscord {
385386
setIntents(intents);
386387
}
387388

389+
void useCompression(bool value) {
390+
#ifdef SLEEPY_DEFAULT_COMPRESSION
391+
compressionHandler = value ?
392+
std::unique_ptr<GenericCompression>(new DefaultCompression()) :
393+
nullptr;
394+
#else
395+
assert(value == false, "No default compress handler, use template function instead");
396+
#endif
397+
}
398+
399+
template <class Handler, class... Types>
400+
void useCompression(Types&&... arguments) {
401+
compressionHandler = std::unique_ptr<GenericCompression>(
402+
new Handler(std::forward<Types>(arguments)...));
403+
}
404+
388405
//time
389406
template <class Handler, class... Types>
390407
inline void setScheduleHandler(Types&&... arguments) {
@@ -570,6 +587,7 @@ namespace SleepyDiscord {
570587
/*do not use or overwrite the protected values below,
571588
unless you know what you are doing*/
572589
void processMessage(const std::string &message) override;
590+
void processMessage(const WebSocketMessage message) override;
573591
void processCloseCode(const int16_t code) override;
574592
void heartbeat();
575593
void sendHeartbeat();
@@ -660,14 +678,17 @@ namespace SleepyDiscord {
660678
//
661679
//voice
662680
//
663-
std::list<VoiceConnection> voiceConnections;
681+
std::forward_list<VoiceConnection> voiceConnections;
664682
std::forward_list<VoiceContext> voiceContexts;
665683
std::forward_list<VoiceContext*> waitingVoiceContexts;
666684
#ifdef SLEEPY_VOICE_ENABLED
667685
void connectToVoiceIfReady(VoiceContext& context);
668686
void removeVoiceConnectionAndContext(VoiceConnection& connection);
669687
#endif
670688

689+
//compression
690+
std::unique_ptr<GenericCompression> compressionHandler;
691+
671692
template<class Callback>
672693
void findServerInCache(Snowflake<Server>& serverID, Callback onSuccessCallback) {
673694
if (serverCache) {

include/sleepy_discord/compression.h

+7
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
#pragma once
2+
3+
#ifdef EXISTENT_ZLIB_NG
4+
#include "zlib-ng_compression.h"
5+
#else
6+
#include "generic_compression.h"
7+
#endif // EXISTENT_ZLIB_NG
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
#pragma once
2+
3+
namespace SleepyDiscord {
4+
class GenericCompression {
5+
public:
6+
virtual bool uncompress(const std::string& compressed, std::string& uncompressedOut) = 0;
7+
};
8+
}

include/sleepy_discord/message_receiver.h

+17
Original file line numberDiff line numberDiff line change
@@ -4,13 +4,30 @@
44
#include "timer.h"
55

66
namespace SleepyDiscord {
7+
struct WebSocketMessage {
8+
enum OPCode {
9+
continuation = 0x0,
10+
text = 0x1,
11+
binary = 0x2,
12+
close = 0x8,
13+
ping = 0x9,
14+
pong = 0xA,
15+
};
16+
using OPCodeType = OPCode;
17+
OPCodeType opCode = text;
18+
const std::string& payload;
19+
};
20+
721
class GenericMessageReceiver {
822
public:
923
virtual ~GenericMessageReceiver() = default;
1024
virtual void initialize() {} //called when ready to recevie messages
1125
virtual void handleFailToConnect() {} //called when connection has failed to start
1226
virtual void processMessage(const std::string & message) = 0; //called when recevicing a message
1327
virtual void processCloseCode(const int16_t /*code*/) {}
28+
virtual void processMessage(const WebSocketMessage message) {
29+
processMessage(message.payload);
30+
}
1431
WebsocketConnection connection; //maybe this should be set to protected?
1532
protected:
1633
int consecutiveReconnectsCount = 0;
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,74 @@
1+
#pragma once
2+
#include "generic_compression.h"
3+
#include "zlib-ng/zlib-ng.h"
4+
#include <array>
5+
#include <queue>
6+
7+
namespace SleepyDiscord {
8+
class ZLibCompression : public GenericCompression {
9+
public:
10+
bool uncompress(const std::string& compressed, std::string& uncompressedOut) override {
11+
auto stream = zng_stream{};
12+
memset(&stream, 0, sizeof(stream));
13+
int output = zng_inflateInit(&stream);
14+
if (output != Z_OK) {
15+
zng_inflateEnd(&stream);
16+
return false;
17+
}
18+
19+
stream.next_in = reinterpret_cast<const uint8_t*>(compressed.data());
20+
stream.avail_in = static_cast<uint32_t>(compressed.length());
21+
22+
constexpr size_t chunkSize = 16 * 1024;
23+
using Data = std::array<char, chunkSize>;
24+
using Buffer = std::pair<Data, std::size_t>;
25+
std::list<Buffer> outputQueue;
26+
27+
bool makeNewBuffer = true;
28+
std::size_t totalSize = 0;
29+
output = Z_BUF_ERROR;
30+
do {
31+
if (makeNewBuffer == true) {
32+
outputQueue.emplace_back(); //make a new output buffer
33+
makeNewBuffer = false;
34+
}
35+
Buffer& buffer = outputQueue.back();
36+
Data& data = buffer.first;
37+
std::size_t size = buffer.second;
38+
39+
stream.next_out = reinterpret_cast<uint8_t*>(&data[size]);
40+
stream.avail_out = static_cast<uint32_t>(data.max_size() - size);
41+
42+
output = zng_inflate(&stream, Z_SYNC_FLUSH);
43+
44+
auto oldSize = size;
45+
size = data.max_size() - stream.avail_out;
46+
buffer.second = size;
47+
auto deltaSize = size - oldSize;
48+
totalSize += deltaSize;
49+
50+
if (output == Z_STREAM_END) {
51+
output = zng_inflateEnd(&stream);
52+
break;
53+
} else if (deltaSize == 0) {
54+
makeNewBuffer = true;
55+
}
56+
} while (output == Z_OK || output == Z_BUF_ERROR);
57+
58+
uncompressedOut.clear();
59+
uncompressedOut.reserve(totalSize);
60+
for (; 0 < outputQueue.size(); outputQueue.pop_front()) {
61+
Buffer& buffer = outputQueue.front();
62+
Data& data = buffer.first;
63+
std::size_t size = buffer.second;
64+
65+
uncompressedOut.append(data.data(), size);
66+
}
67+
68+
return true;
69+
}
70+
};
71+
72+
using DefaultCompression = ZLibCompression;
73+
#define SLEEPY_DEFAULT_COMPRESSION ZLibCompression
74+
}

sleepy_discord/CMakeLists.txt

+9-1
Original file line numberDiff line numberDiff line change
@@ -116,7 +116,6 @@ if (NOT ONLY_SLEEPY_DISCORD)
116116
add_dependencies(sleepy-discord libsodium-make)
117117
ExternalProject_Get_property(libsodium-make BINARY_DIR)
118118
set(libsodium-make_BINARY_DIR ${BINARY_DIR})
119-
message(WARNING ${libsodium-make_BINARY_DIR})
120119
set(
121120
LIB_SODIUM_BUILDS
122121
${libsodium-make_BINARY_DIR}/src/libsodium/.libs/${CMAKE_STATIC_LIBRARY_PREFIX}sodium${CMAKE_STATIC_LIBRARY_SUFFIX}
@@ -132,6 +131,15 @@ if (NOT ONLY_SLEEPY_DISCORD)
132131
else()
133132
target_compile_definitions(sleepy-discord PUBLIC NONEXISTENT_SODIUM)
134133
endif()
134+
135+
if(USE_ZLIB_NG)
136+
get_target_property(ZLIB_BINARY_DIR zlib BINARY_DIR)
137+
target_include_directories(sleepy-discord PUBLIC ${zlib-ng_SOURCE_DIR}/../ ${ZLIB_BINARY_DIR})
138+
list(APPEND LIBRARIES_TO_LINK "zlib")
139+
target_compile_definitions(sleepy-discord PUBLIC EXISTENT_ZLIB_NG)
140+
else()
141+
target_compile_definitions(sleepy-discord PUBLIC NONEXISTENT_ZLIB_NG)
142+
endif()
135143
target_link_libraries(sleepy-discord PUBLIC ${LIBRARIES_TO_LINK})
136144
else()
137145
target_compile_definitions(sleepy-discord PUBLIC

sleepy_discord/client.cpp

+25-1
Original file line numberDiff line numberDiff line change
@@ -335,7 +335,9 @@ namespace SleepyDiscord {
335335
"\"$browser\":\"Sleepy_Discord\","
336336
"\"$device\":\"Sleepy_Discord\""
337337
"},"
338-
"\"compress\":false,";
338+
"\"compress\":";
339+
identity += compressionHandler ?
340+
"true" : "false"; identity += ",";
339341
if (shardCount != 0 && shardID <= shardCount) {
340342
identity +=
341343
"\"shard\":[";
@@ -659,6 +661,26 @@ namespace SleepyDiscord {
659661
}
660662
}
661663

664+
void BaseDiscordClient::processMessage(const WebSocketMessage message) {
665+
switch (message.opCode) {
666+
case WebSocketMessage::OPCode::binary: {
667+
if (!compressionHandler)
668+
break;
669+
std::shared_ptr<std::string> uncompressed = std::make_shared<std::string>();
670+
compressionHandler->uncompress(message.payload, *uncompressed);
671+
postTask(
672+
[this, uncompressed]() {
673+
processMessage(*uncompressed);
674+
}
675+
);
676+
break;
677+
}
678+
case WebSocketMessage::OPCode::text:
679+
processMessage(message.payload);
680+
break;
681+
}
682+
}
683+
662684
void BaseDiscordClient::processCloseCode(const int16_t code) {
663685
setError(code);
664686

@@ -714,6 +736,8 @@ namespace SleepyDiscord {
714736
sendHeartbeat();
715737
lastHeartbeat = currentTime;
716738

739+
if (heart.isValid())
740+
heart.stop();
717741
heart = schedule(&BaseDiscordClient::heartbeat, heartbeatInterval);
718742
}
719743

sleepy_discord/websocketpp_websocket.cpp

+6-2
Original file line numberDiff line numberDiff line change
@@ -158,8 +158,12 @@ namespace SleepyDiscord {
158158
websocketpp::connection_hdl hdl,
159159
websocketpp::config::asio_client::message_type::ptr msg,
160160
GenericMessageReceiver* messageProcessor) {
161-
postTask([=]() { messageProcessor->processMessage(msg->get_payload()); });
162-
//messageProcessor->processMessage(msg->get_payload());
161+
postTask([=]() {
162+
messageProcessor->processMessage(WebSocketMessage{
163+
static_cast<WebSocketMessage::OPCodeType>(msg->get_opcode()),
164+
msg->get_payload()
165+
});
166+
});
163167
}
164168

165169
//UDPClient WebsocketppDiscordClient::createUDPClient() {

0 commit comments

Comments
 (0)