Jump to content
3DXChat Community

AlexRyder

Members
  • Posts

    247
  • Joined

  • Last visited

Everything posted by AlexRyder

  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
  16. You can install/copy/move it wherever you want, it will work fine. You can even just extract the installation package without installing it, doesn't matter. Though user settings (resolution, chat window size and notifications, volume, etc.) are stored in the registry, so it you move the flash drive between computers those settings will be out of sync.
  17. Seriously guys, can't you do the random chatting somewhere else? My mailbox is already annoyingly full of the notifications spam.
  18. Well, I haven't checked it you can still get the old account ids from the messaging system (if yes, it will still be possible to collect character-account relations, just slower), and of course it will be very easy for Pandora to update its database with character ids right after the update goes live. The ignore system is bound to ids, so changing an id will remove a char from the ignored lists. Since there are no strict relations between characters and character slots, conceptually it makes no sense to carry on previous ignores to the newly created characters. Also, that would make things way more complicated compared to implementing a simple account-wide check on the server side.
  19. For those still wondering about the new character ids and stuff... The ids identify character records, not character slots. When a character is created it is assigned a consecutive unique id number which is used to identify it withing the game code. When you change the character name/sex/any other property, the id stays. However, when you delete a character, the whole record is removed, including the id, and when you create a new one in the same slot, the id will be new as well.
  20. From a quick peek at the code changes (haven't had time to dig into it throughly though), the character-related features now work based on a character id which is received from the server. Since we have no access to the server code (obviously) I can only assume that this id is bound to a specific character slot (may as well be the database character slot id) — in this case the ignore system should still work regardless of how many times a user changes their character name or gender. Not sure if deleting and re-creating a char would assign it a new slot id, but the system is probably in a wip state at the time anyway, right now it seems to be a mess of the old and new code.
  21. It's unlikely you'd gain anything in particular from changing to 64-bit client if you only have 4 Gb or RAM. Anyways, the point I was trying to make is that building both 32 an 64-bit clients is not something that requires much extra effort.
  22. Lol, dude, I'm the one who made that 64-bit patch XD And there is not that much outdated API used in the game's code that wouldn't be compatible with Unity 2018, as far as I remember. Probably also some shader code, can't check that with a compiled game.
  23. Uhm, what issues? People on old computers can still use the 32-bit version. And how is GPU even related to the OS bitness?
  24. You can easily compile to both 32 and 64 bit with Unity and release both versions. Also, it's probably time to update from the very outdated Unity 5.5 to the recent version
  25. Hey gizmo, I'm probably the one to care about the ignore system the least, but since everyone's so much into it why not do it this way? The ignore lists are stored and checked server-side only.The client can only request the server if a certain char is ignored or not (true/false).The server should check the ignore lists before sending messages and not send them to those who shouldn't receive them anyway.The server shouldn't also send ignored chars data to the clients who are ignoring them / are being ignored (i.e. room lists, etc.)This way the server can perform any kinds of checks without exposing the process to the clients, so account wide ignore system won't be causing any issues.
×
×
  • Create New...