• We have updated our Community Code of Conduct. Please read through the new rules for the forum that are an integral part of Paradox Interactive’s User Agreement.

MarkDey

General
Moderator
30 Badges
Mar 18, 2002
1.845
1.121
  • BATTLETECH
  • Age of Wonders: Planetfall Sign Up
  • Crusader Kings III Referal
  • Stellaris
  • Crusader Kings II
  • Europa Universalis IV
Hello everybody! I have a Battletech code question and I'm hoping that a code expert could help. I'll also post this in the Battletech Discords that I know about.

I'd like to try and fix the multiplayer issue where game language matters between clients. I've been testing for a while and have posted my findings along the way in this bug report.

Basically, the game always has an out-of-sync error if one of the clients uses English and the other client uses something else. I strongly suspect that this has to do with the decimal separator localization, since Russian and German and French write one-half as 0,5 but English writes it as 0.5.

What I'd like to do is replace all '.' with ',' during the multiplayer stats comparison that runs during the game and I think I've located where to do that. It's in the below method.

Code:
// BattleTech.StatsCompare
// Token: 0x0600721D RID: 29213 RVA: 0x001DD448 File Offset: 0x001DB648
public override CompareResult Compare(BaseStatsCompare rightBase)
{
    StatsCompare statsCompare = rightBase as StatsCompare;
    if (statsCompare == null)
    {
        base.Error("Attempting to compare something else with StatsCompre");
        return CompareResult.COMPARISON_ERROR;
    }
    if (this.dataGUID != statsCompare.dataGUID)
    {
        string thing = string.Format("GUIDs don't match: {0}\n{1}", this.dataGUID, statsCompare.dataGUID);
        base.Error(thing);
        return CompareResult.COMPARISON_ERROR;
    }
    CompareResult compareResult = CompareResult.OK;
    if (string.Compare(this.dataHash, statsCompare.dataHash) == 0)       <---- LINE OF INTEREST
    {
        if (base.IsDebugEnabled)
        {
            base.Debug(string.Format("Data correct\nHashes {0} and {1}", this.dataHash, statsCompare.dataHash));
        }
        return compareResult;
    }
    compareResult |= CompareResult.DATA_MISMATCH;
    if (!base.HasData)
    {
        compareResult |= CompareResult.NEEDS_DATA;
    }
    if (base.IsDebugEnabled)
    {
        base.Debug(string.Format("Data doesn't match\nHashes {0} and {1}", this.dataHash, statsCompare.dataHash));
    }
    return compareResult;
}


I'd like to change this line
Code:
"if (string.Compare(this.dataHash, statsCompare.dataHash) == 0)"
to something like this
Code:
"if (string.Compare(ReplaceAllDotWithComma(this.dataHash), ReplaceAllDotWithComma(statsCompare.dataHash)) == 0)"

However, I'm not that familiar with the classes involved and so I'm not sure how easy that would be to do with a statsCompare.dataHash.

Is someone able to point me in the right direction?
 
I don't know what classes are involved here, but if the dataHash is indeed a Hash as the name implies you're out of luck. Hashing mangles your data with zero chance to recover the original data.
Think of this (very simplified) hashing: Instead of storing huge numbers with billions of digits you store the digit sum modulo 255 so your number fits into a single byte. Now you only compare these one byte numbers.
The game files including all mods would now be your huge number.
That's super useful to compare if two users use the same version of a game and the same mods because you only transfer a single byte instead of the several Gigabytes of game data. And while it's technically possible for two different versions to end up with the same hash - and indeed some would - but if you use a hash function a bit smarter then the simple digit sum and a few more bytes then one it's practically impossible to do so deliberately or by accident.

Alas, trying to replace the dots with commas has to be done before hashing and trying to do aftwerwards completely negates the point of using a hash in the first place.
 
Yeah, that looks right. KMission was able to provide some code that I used to test the hashes and they do indeed not match. The code is below and the details are in the attached RAR file.

Code:
[HarmonyPatch(typeof(StatsCompare), "Compare")]
    public static class StatsCompare_Compare
    {
        // Token: 0x06000002 RID: 2 RVA: 0x00002068 File Offset: 0x00000268
        public static void Prefix(StatsCompare __instance, BaseStatsCompare rightBase)
        {
            Traverse.Create(__instance).Method("Error", new object[]
            {
                string.Concat(new string[]
                {
                    "StatsCompare.Compare Prefix data hashes: '",
                    __instance.dataHash,
                    "' and '",
                    rightBase.dataHash,
                    "'"
                })
            }).GetValue();
        }
    }

So, it looks like the next step is to iterate through the StatsCompare variables of __instance and rightBase to see what data isn't being put into an InvariantCulture type and isolate where the problem is actually happening. The error files seem to indicate that it's an issue with Current Stability, Remaining Damage, and Previous Network Random Values.
 

Attachments

  • 20230313_19-47-19.rar
    2,9 MB · Views: 0