Jump to content
3DXChat Community

AlexRyder

Members
  • Posts

    247
  • Joined

  • Last visited

Profile Information

  • Gender
    Male

Recent Profile Visitors

1,329 profile views

AlexRyder's Achievements

Advanced Member

Advanced Member (3/3)

296

Reputation

  1. Aaand the 60-second check is of course client-sided. Which means β€” workaroundable. Only took about 10 min to find it in the binaries
  2. Just as an info note, it's still fairly simple to patch the compiled binaries to workaround certain security checks. For example, only took me a few minutes with IDA to find the player ignored check before processing chat messages: Can be easily patched as well. It only became impossible to add complicated stuff to the game, i.e. the good stuff. To make the game actually secure such checks should be performed server-side, not on the client.
  3. It could also have the HSV and alpha sliders though
  4. 3dx places clothes models on top of the full avi models and uses alpha masks to hide parts of the body covered with clothes so that it won't randomly peek through. Adding texture layers to the models (i.e. tattoos and stuff) is fairly easy but requires modifying shaders. Stockings are implemented this way. Placing textures on random places of the models would be more complicated, but doable. Making earrings, piercings, gloves, etc.is for the most part a matter of modeling and rigging.
  5. I think they have an automatic UnityScript-to-C# conversion tool, but I haven't checked that. But there can be other legacy content which can be harder to update, like shaders or animations.
  6. Rewriting scripts from UnityScript to C# is not that hard really, can probably be done in a couple of weeks or something if you have sources. Or can be done via .dll decompilation to C# and some manual fixing (mostly required for IEnumerators).
  7. 3dxChat is still using Unity 5.5 (pretty outdated version by now, from 2016), this blog post is about Unity 2017. And seriously, Windows XP in 2019?
  8. The simplest and most obvious one β€” just rename Patcher.exe to something else, the game won't be able to run it then.
  9. The check for gift id < 787348 seems to be rather unreliable. The ids may be mixed in the database, or new entries may be inserted in place of earlier removed ones, dunno, that depends on the server backend. Weirdly enough, the date check can also misfire for some reason, so I would say the most reliable way is to do a validity check against the 1252 codepage before trying to convert data.
  10. But... it's not really a fix, the data in the database still stays broken. You should at least run this fixer on the database (though it's probably the worst way to deal with this problem ever). Also, the code in 383 is missing the check for broken encoding for profiles (some people have managed to change their data between 380 and 382) and a few special cases handling, which will probably still crash the profiles script. Also, a lot of gifs are missing from people's profiles and the gift count received with the profile in most cases is now greater than the actual gift count. It seems like the gifts from deleted accounts have not been transferred from the old database, which is obviously an error.
  11. Regarding the Bob and Betty issue: you currently have their data switched in the database (i.e. Betty should have id 1 and Bob β€” id 2). Since they are reverse the client tries to apply female char data to a male and vice versa. Edit: Correction. Betty now seems to occupy ids 2 and 3, Bob β€” ids 1 and 4, where ids 1 and 2 have working char data and broken profiles, and ids 3 and 4 have broken char data but working profiles. It's a mess.
  12. A few corrections to the previous bugfix post. Apparently, the mess with the data in the database has gotten even more complicated since there are now old broken profiles with broken encodings, old broken profiles with normal encodings, and new profiles. Also, the gifts sent after the update has been rolled out don't need to be fixed. For the profiles: private void LoadProfile(string profileData) { profileData = profileData.Trim(); if (profileData.StartsWith("{", StringComparison.Ordinal)) { // New JSON profiles LoadJsonProfile(profileData); } else if (profileData.StartsWith("<profile>", StringComparison.Ordinal)) { // Old XML profiles LoadXmlProfile(profileData); } else { // Some very ancient string-concatenated profiles LoadLegacyProfile(profileData); } } // New JSON profiles private void LoadJsonProfile(string jsonData) { // Go on with deserialization here } // Old XML profiles private void LoadXmlProfile(string xmlData) { xmlData = FixBrokenUtf8Encoding(xmlData); // Since most of the xmls are now malformed, need to rebuild them manually StringBuilder sb = new StringBuilder(); sb.Append("<profile>"); AppendXmlTag(sb, xmlData, "age", "18", false, true); AppendXmlTag(sb, xmlData, "interest", "?", false, true); AppendXmlTag(sb, xmlData, "location", "3DXChat", false, true); AppendXmlTag(sb, xmlData, "about", "I love 3DXChat!", true, true); sb.Append("</profile>"); xmlData = sb.ToString(); // Remove illegal XML chars xmlData = SanitizeInvalidXmlCharacterReferences(xmlData); // Go on with deserialization here } // Sorta-fix for an UTF-8 --> Latin1 --> UTF-8 conversion private string FixBrokenUtf8Encoding(string text) { if (IsValidForEncoding(text, 1252)) { Encoding utf8 = Encoding.UTF8; Encoding latin1 = Encoding.GetEncoding(1252); byte[] bytes = Encoding.Convert(utf8, latin1, utf8.GetBytes(text)); return utf8.GetString(bytes); } return text; } // Sorta-check to figure out if we really need to fix the encoding, may misfire private bool IsValidForEncoding(string text, int codePage) { Encoding encoder = Encoding.GetEncoding(codePage, new EncoderExceptionFallback(), new DecoderExceptionFallback()); try { encoder.GetBytes(text); } catch (EncoderFallbackException) { return false; } return true; } private void AppendXmlTag(StringBuilder sb, string xmlData, string tag, string defaultValue, bool findMostInnerMatch, bool reparse) { string openTag = $"<{tag}>"; string closeTag = $"</{tag}>"; int startIndex; int endIndex; bool isMatch = false; if (findMostInnerMatch) { // Some profiles saved with client versions 380 and 381 are weirdly malformed and contain nested data, so, need to try and untangle that startIndex = xmlData.IndexOf(openTag, StringComparison.Ordinal) + openTag.Length; endIndex = startIndex >= openTag.Length && startIndex < xmlData.Length ? xmlData.LastIndexOf(closeTag, xmlData.Length - 1, xmlData.Length - startIndex, StringComparison.Ordinal) : -1; while (endIndex > -1) { isMatch = true; xmlData = xmlData.Substring(startIndex, endIndex - startIndex); startIndex = xmlData.IndexOf(openTag, StringComparison.Ordinal) + openTag.Length; endIndex = startIndex >= openTag.Length && startIndex < xmlData.Length ? xmlData.LastIndexOf(closeTag, xmlData.Length - 1, xmlData.Length - startIndex, StringComparison.Ordinal) : -1; } } else { startIndex = xmlData.IndexOf(openTag, StringComparison.Ordinal) + openTag.Length; endIndex = startIndex >= openTag.Length && startIndex < xmlData.Length ? xmlData.IndexOf(closeTag, startIndex, StringComparison.Ordinal) : -1; if (endIndex > -1) { isMatch = true; xmlData = xmlData.Substring(startIndex, endIndex - startIndex); } } if (isMatch) { if (reparse) { // Inner values can be a total mess now, so it's easier to just re-encode them xmlData = HttpUtility.HtmlEncode(HttpUtility.HtmlDecode(xmlData)); } sb.Append(openTag); sb.Append(xmlData); sb.Append(closeTag); } else { sb.Append(openTag); sb.Append(defaultValue); sb.Append(closeTag); } } private static Regex _xmlEncodedCharacterRegex = new Regex("(x?)([A-Fa-f0-9]+);"); private string SanitizeInvalidXmlCharacterReferences(string xmlData) { if (xmlData.IndexOf("", StringComparison.Ordinal) < 0) { return xmlData; } return _xmlEncodedCharacterRegex.Replace( xmlData, match => { string matchValue = match.Value; uint result; bool isParsed = matchValue[2] == 'x' ? uint.TryParse(matchValue.Substring(3, matchValue.Length - 4), NumberStyles.AllowHexSpecifier, NumberFormatInfo.InvariantInfo, out result) : uint.TryParse(matchValue.Substring(2, matchValue.Length - 3), NumberStyles.Integer, NumberFormatInfo.InvariantInfo, out result); return isParsed && !IsValidXmlChar((char)result) ? "οΏ½" : matchValue; }); } private bool IsValidXmlChar(char character) { return character == 0x9 || character == 0xa || character == 0xd || character >= 0x20 && character <= 0xd7ff || character >= 0xe000 && character <= 0xfffd || character >= 0x10000 && character <= 0x10ffff; } // Some very ancient string-concatenated profiles private void LoadLegacyProfile(string profileData) { string[] args = profileData.Split('|'); if (args.Length < 1) { return; } if (args.Length >= 5) { // Go on with deserialization here } } For the gifts: // The time the database was converted private static DateTime _patch380ReleaseDate = new DateTime(2018, 11, 15, 19, 0, 0, 0, DateTimeKind.Utc); private string FixBrokenGiftText(string giftText) { // Check if really need to fix the text if (giftText != null && timeStamp.ToUniversalTime() < _patch380ReleaseDate) { // Correct broken encoding Encoding utf8 = Encoding.UTF8; Encoding latin1 = Encoding.GetEncoding(1252); byte[] bytes = Encoding.Convert(utf8, latin1, utf8.GetBytes(giftText)); giftText = utf8.GetString(bytes); // Fix some string conversion leftovers giftText = giftText.Replace("\\'", "\'"); } return giftText; }
  13. Having looked at what's going on with the texts, it seems to me the most likely scenario of what went wrong was a double conversion of the database encoding: UTF-8 --> Latin1 --> UTF-8. Most like this conversion was not lossless, so it won't be possible to restore 100% of invalid characters by only working with corrupt data. Also, there seem to have been some string conversions which broke some of the XMLs and added some extra mess to the pile in general. I've managed to invent a pretty ugly "unmesser" for all that (at least, it works for all of the profiles and gift texts I have tried it on), but by no means it's something that should be used in the final production since it's not really solving anything, just tries to fight the consequences. For the profiles (both new JSON and old broken XML profiles will be loaded, except for a few broken chars): Code is removed by moderator. Please do not publish our code to the public.
  14. To everyone concerned about their profiles, friendlists, photos, etc.: all the data is still on the server, but there are obviously some character encoding issues, so when the game tries to display a profile with such illegal XML characters, the profile script crashes, which causes random profile bugs like part of the data disappearing or being incomplete. But by all means you SHOULD avoid editing anything in your profiles, because most likely it will erase or break your data on the server as well.
  15. That's probably intended at the moment, put the patcher fails because of a 403 access denied error when trying to download the patch binary from http://download.3dxchat.com/StandaloneWindows_379_380.m2hpatch
×
×
  • Create New...