Skip to content

Commit 232630b

Browse files
committed
What the FUCK-
1 parent 376c356 commit 232630b

File tree

7 files changed

+883
-8
lines changed

7 files changed

+883
-8
lines changed

Project.xml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -162,6 +162,7 @@
162162
<haxelib name="actuate" />
163163
<haxelib name="tentools" />
164164
<haxelib name="systools" />
165+
<haxelib name="deflatex" />
165166
<ndll name="systools" haxelib="systools" />
166167

167168
<!-- Disable Discord IO Thread -->

setup/windows.bat

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@ haxelib install hxdiscord_rpc 1.3.0
1515
haxelib install hxvlc 2.0.1 --skip-dependencies
1616
haxelib install helder.set 0.3.1
1717
haxelib install yaml 2.0.1
18-
haxelib install hxWebSockets 1.4.0
18+
haxelib git hxWebSockets https://github.com/ianharrigan/hxWebSockets
1919
haxelib install haxe-concurrent 5.1.3
2020
haxelib install actuate 1.9.0
2121
haxelib install flixel-ui 2.6.1
@@ -54,7 +54,7 @@ if /i "%ensure_versions%"=="y" (
5454
haxelib set hxvlc 2.0.1
5555
haxelib set helder.set 0.3.1
5656
haxelib set yaml 2.0.1
57-
haxelib set hxWebSockets 1.4.0
57+
haxelib set hxWebSockets git
5858
haxelib set haxe-concurrent 5.1.3
5959
haxelib set actuate 1.9.0
6060
haxelib set flixel-ui 2.6.1

source/archipelago/Client.hx

Lines changed: 196 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,10 @@ import helder.Set;
1111
import hx.ws.Types.MessageType;
1212
import hx.ws.WebSocket;
1313
import haxe.Json as TJson;
14+
import haxe.zip.Uncompress;
15+
import haxe.io.Bytes;
16+
import deflatex.*;
17+
import yutautil.PermessageDeflate;
1418
import backend.ClientPrefs;
1519
#if sys
1620
import sys.FileSystem;
@@ -1317,7 +1321,7 @@ class Client {
13171321
if (_sendQueue.length > 0) {
13181322
_sendLock.execute(() -> {
13191323
trace('Sending ${_sendQueue.length} queued packet(s)');
1320-
_ws.send(haxe.io.Bytes.ofString(TJson.stringify(_sendQueue)));
1324+
_ws.send((TJson.stringify(_sendQueue)));
13211325
_sendQueue = [];
13221326
});
13231327
}
@@ -1692,21 +1696,202 @@ class Client {
16921696
case StrMessage(content):
16931697
_recvLock.execute(() -> {
16941698
try {
1695-
var newPackets:Array<IncomingPacket> = TJson.parse(content);
1696-
// trace(newPackets);
1699+
// Comprehensive debugging of the received data
1700+
trace("=== RAW MESSAGE ANALYSIS ===");
1701+
trace("Content length: " + content.length);
1702+
// trace("First 100 chars: " + content.substr(0, 100));
1703+
1704+
// // Show character codes for first 20 characters
1705+
// var charCodes = [];
1706+
// for (i in 0...Std.int(Math.min(20, content.length))) {
1707+
// charCodes.push(content.charCodeAt(i));
1708+
// }
1709+
// trace("First 20 char codes: " + charCodes.join(", "));
1710+
1711+
// // Convert to bytes and show hex
1712+
// var contentBytes = haxe.io.Bytes.ofString(content, haxe.io.Encoding.RawNative);
1713+
// var hexBytes = [];
1714+
// for (i in 0...Std.int(Math.min(20, contentBytes.length))) {
1715+
// hexBytes.push(StringTools.hex(contentBytes.get(i), 2));
1716+
// }
1717+
// trace("First 20 bytes (hex): " + hexBytes.join(" "));
1718+
1719+
// Check if it's already valid JSON
1720+
var trimmed = StringTools.trim(content);
1721+
if (trimmed.startsWith("[") || trimmed.startsWith("{")) {
1722+
trace("Data appears to be uncompressed JSON");
1723+
var newPackets:Array<IncomingPacket> = TJson.parse(content);
1724+
for (newPacket in newPackets)
1725+
_recvQueue.push(newPacket);
1726+
trace('=== END OF RAW MESSAGE ANALYSIS ===');
1727+
return;
1728+
}
1729+
1730+
// Check for common compression signatures
1731+
var first2Bytes = contentBytes.length >= 2 ? (contentBytes.get(0) << 8) | contentBytes.get(1) : 0;
1732+
trace("First 2 bytes as uint16: 0x" + StringTools.hex(first2Bytes, 4));
1733+
1734+
// Check for various compression formats
1735+
if (contentBytes.length >= 2) {
1736+
var b1 = contentBytes.get(0);
1737+
var b2 = contentBytes.get(1);
1738+
1739+
if (b1 == 0x78 && (b2 == 0x01 || b2 == 0x5E || b2 == 0x9C || b2 == 0xDA)) {
1740+
trace("Detected zlib header");
1741+
} else if (b1 == 0x1F && b2 == 0x8B) {
1742+
trace("Detected gzip header");
1743+
} else if (b1 == 0x08 || b1 == 0x01) {
1744+
trace("Possible deflate block header");
1745+
} else {
1746+
trace("Unknown compression format or uncompressed data");
1747+
}
1748+
}
1749+
1750+
// Try multiple decompression approaches
1751+
var approaches = [
1752+
// Approach 1: Try as-is if it looks like JSON
1753+
function():String {
1754+
trace("Approach 1: Direct parsing");
1755+
if (content.indexOf('"') >= 0 || content.indexOf('[') >= 0 || content.indexOf('{') >= 0) {
1756+
return content;
1757+
}
1758+
return null;
1759+
},
1760+
1761+
// Approach 2: Our custom permessage-deflate
1762+
function():String {
1763+
trace("Approach 2: PermessageDeflate");
1764+
return PermessageDeflate.smartDecompress(content);
1765+
},
1766+
1767+
// Approach 3: Raw deflate with Haxe's implementation
1768+
function():String {
1769+
trace("Approach 3: Raw deflate");
1770+
try {
1771+
var input = new haxe.io.StringInput(content);
1772+
var inflater = new haxe.zip.InflateImpl(input, false, false);
1773+
var output = new haxe.io.BytesOutput();
1774+
@:privateAccess
1775+
inflater.output = output.getBytes();
1776+
var output = haxe.zip.InflateImpl.run(input);
1777+
return output.toString();
1778+
} catch (e:Dynamic) {
1779+
trace("Raw deflate failed: " + e);
1780+
return null;
1781+
}
1782+
},
1783+
1784+
// Approach 4: Try base64 decode first
1785+
function():String {
1786+
trace("Approach 4: Base64 decode");
1787+
try {
1788+
var decoded = haxe.crypto.Base64.decode(content);
1789+
return decoded.toString();
1790+
} catch (e:Dynamic) {
1791+
trace("Base64 decode failed: " + e);
1792+
return null;
1793+
}
1794+
},
1795+
1796+
// Approach 5: Try URL decode
1797+
function():String {
1798+
trace("Approach 5: URL decode");
1799+
try {
1800+
var decoded = StringTools.urlDecode(content);
1801+
if (decoded != content) {
1802+
return decoded;
1803+
}
1804+
return null;
1805+
} catch (e:Dynamic) {
1806+
trace("URL decode failed: " + e);
1807+
return null;
1808+
}
1809+
},
1810+
1811+
// Approach 6: Try treating each character as a byte
1812+
function():String {
1813+
trace("Approach 6: Character-to-byte conversion");
1814+
try {
1815+
var bytes = haxe.io.Bytes.alloc(content.length);
1816+
for (i in 0...content.length) {
1817+
bytes.set(i, content.charCodeAt(i) & 0xFF);
1818+
}
1819+
return bytes.toString();
1820+
} catch (e:Dynamic) {
1821+
trace("Char-to-byte failed: " + e);
1822+
return null;
1823+
}
1824+
}
1825+
];
1826+
1827+
var decompressedContent:String = null;
1828+
for (i in 0...approaches.length) {
1829+
try {
1830+
var result = approaches[i]();
1831+
if (result != null && result != content) {
1832+
trace("Approach " + (i + 1) + " produced different result (length: " + result.length + ")");
1833+
trace("Result preview: " + result.substr(0, 100));
1834+
1835+
// Check if result looks like valid JSON
1836+
var trimmedResult = StringTools.trim(result);
1837+
if (trimmedResult.startsWith("[") || trimmedResult.startsWith("{")) {
1838+
trace("Approach " + (i + 1) + " produced valid JSON!");
1839+
decompressedContent = result;
1840+
break;
1841+
}
1842+
} else if (result != null) {
1843+
trace("Approach " + (i + 1) + " returned same content");
1844+
} else {
1845+
trace("Approach " + (i + 1) + " returned null");
1846+
}
1847+
} catch (e:Dynamic) {
1848+
trace("Approach " + (i + 1) + " failed: " + e);
1849+
}
1850+
}
1851+
1852+
// If nothing worked, use original content
1853+
if (decompressedContent == null) {
1854+
trace("All decompression approaches failed, using original content");
1855+
decompressedContent = content;
1856+
}
1857+
1858+
trace("Final content preview: " + decompressedContent.substr(0, 100));
1859+
trace("=== END ANALYSIS ===");
1860+
1861+
var newPackets:Array<IncomingPacket> = TJson.parse(decompressedContent);
16971862
for (newPacket in newPackets)
16981863
_recvQueue.push(newPacket);
16991864
} catch (e) {
17001865
trace("EXCEPTION onmessage: " + e);
17011866
_hOnThrow("onmessage", e);
1867+
trace("Content: " + content);
17021868
}
17031869
});
17041870

17051871
case BytesMessage(bytes):
17061872
_recvLock.execute(() -> {
17071873
try {
1708-
var content = bytes.readAllAvailableBytes().toString();
1709-
var newPackets:Array<IncomingPacket> = TJson.parse(content);
1874+
var rawBytes = bytes.readAllAvailableBytes();
1875+
var decompressedContent:String;
1876+
1877+
try {
1878+
// Try to decompress the bytes using our custom permessage-deflate handler
1879+
var uncompressed = PermessageDeflate.decompress(rawBytes);
1880+
if (uncompressed != null) {
1881+
decompressedContent = uncompressed.toString();
1882+
trace("Successfully decompressed bytes message using PermessageDeflate (length: " + rawBytes.length + " -> " + decompressedContent.length + ")");
1883+
} else {
1884+
// If our handler fails, treat as plain text
1885+
decompressedContent = rawBytes.toString();
1886+
trace("PermessageDeflate returned null, using raw bytes as string");
1887+
}
1888+
} catch (e:Dynamic) {
1889+
// If all decompression fails, treat as plain text
1890+
trace("All decompression failed for bytes message: " + e);
1891+
decompressedContent = rawBytes.toString();
1892+
}
1893+
1894+
var newPackets:Array<IncomingPacket> = TJson.parse(decompressedContent);
17101895
// trace(newPackets);
17111896
for (newPacket in newPackets)
17121897
_recvQueue.push(newPacket);
@@ -1751,12 +1936,17 @@ class Client {
17511936
_socketReconnectInterval = 15;
17521937
connectAttempts++;
17531938

1754-
_ws = new WebSocket(uri);
1939+
_ws = new WebSocket(uri, false, new OneOrMany<String>('permessage-deflate'));
17551940
_ws.onopen = onopen;
17561941
_ws.onclose = onclose;
17571942
_ws.onmessage = onmessage;
17581943
_ws.onerror = onerror;
1944+
if (ClientPrefs.data.apCompressed)
1945+
_ws.additionalHeaders.set('Sec-WebSocket-Extensions', 'permessage-deflate');
1946+
_ws.open();
1947+
trace("Using headers: " + _ws.additionalHeaders);
17591948
} catch (e:Exception) {
1949+
trace("Error Details: " + new DetailedException(e));
17601950
trace("Error connecting to AP socket", e);
17611951
_hOnThrow("connect_socket", e);
17621952
if (e.message == "ssl network error" && uri.startsWith("wss:")) {

source/backend/ClientPrefs.hx

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -86,6 +86,7 @@ import states.TitleState;
8686
public var checkForUpdates:Bool = true;
8787
public var comboStacking:Bool = true;
8888
public var loadingState:String = 'Song Only';
89+
public var apCompressed:Bool = false;
8990
public var gameplaySettings:Map<String, Dynamic> = [
9091
'scrollspeed' => 1.0,
9192
'scrolltype' => 'multiplicative',

source/options/MixtapeSettingsSubState.hx

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -516,6 +516,11 @@ class MixtapeSettingsSubState extends BaseOptionsMenu
516516
]);
517517
addOption(option);
518518

519+
var option:Option = new Option('AP Server Compression',
520+
'Tell the Engine to ask for compressed data. (WIP)',
521+
'apCompressed', BOOL);
522+
addOption(option);
523+
519524
if (Sys.args().indexOf('-livereload') != -1)
520525
{
521526

0 commit comments

Comments
 (0)