在.NET 4.5中,此密码可完美地在32位和64位体系结构上运行。将项目切换到.NET 4.6会完全破坏此密码(在64位系统中),而在32位系统中,有一个针对此问题的奇数补丁。

在我的方法“DecodeSkill”中,SkillLevel是唯一在.NET 4.6上中断的部分。
此处使用的变量是从网络流中读取并进行编码的。

DecodeSkill(始终返回SkillLevel的正确解码值)

    private void DecodeSkill()
    {
        SkillId = (ushort) (ExchangeShortBits((SkillId ^ ObjectId ^ 0x915d), 13) + 0x14be);
        SkillLevel = ((ushort) ((byte)SkillLevel ^ 0x21));
        TargetObjectId = (ExchangeLongBits(TargetObjectId, 13) ^ ObjectId ^ 0x5f2d2463) + 0x8b90b51a;
        PositionX = (ushort) (ExchangeShortBits((PositionX ^ ObjectId ^ 0x2ed6), 15) + 0xdd12);
        PositionY = (ushort) (ExchangeShortBits((PositionY ^ ObjectId ^ 0xb99b), 11) + 0x76de);
    }

ExchangeShortBits
    private static uint ExchangeShortBits(uint data, int bits)
    {
        data &= 0xffff;
        return (data >> bits | data << (16 - bits)) & 65535;
    }

DecodeSkill(已针对.NET 4.6 32位补丁,请注意“var patch = SkillLevel”)
    private void DecodeSkill()
    {
        SkillId = (ushort) (ExchangeShortBits((SkillId ^ ObjectId ^ 0x915d), 13) + 0x14be);
        var patch = SkillLevel = ((ushort) ((byte)SkillLevel ^ 0x21));
        TargetObjectId = (ExchangeLongBits(TargetObjectId, 13) ^ ObjectId ^ 0x5f2d2463) + 0x8b90b51a;
        PositionX = (ushort) (ExchangeShortBits((PositionX ^ ObjectId ^ 0x2ed6), 15) + 0xdd12);
        PositionY = (ushort) (ExchangeShortBits((PositionY ^ ObjectId ^ 0xb99b), 11) + 0x76de);
    }

仅将变量分配为SkillLevel(仅限32位)将导致SkillLevel始终为正确值。删除此修补程序,并且该值始终不正确。在64位中,即使使用补丁程序,也总是不正确的。

我试过在解码方法上使用MethodImplOptions.NoOptimization和MethodImplOptions.NoInlining,以为这会有所作为。

有什么想法会导致这种情况吗?

编辑:
我被要求举一个输入,输出好和输出差的例子。
这是从实际使用情况中得出的,值是从客户端发送的,并由服务器使用.NET 4.6上的“修补程序”正确解码。

输入:
ObjectId = 1000001

TargetObjectId = 2778236265
PositionX = 32409
PositionY = 16267
SkillId = 28399
SkillLevel = 8481

良好的输出
TargetObjectId = 0
PositionX = 302
PositionY = 278
SkillId = 1115
SkillLevel = 0

输出不良
TargetObjectId = 0
PositionX = 302
PositionY = 278
SkillId = 1115
SkillLevel = 34545

编辑#2:

我应该包括这部分,这绝对是其中的重要部分。

EncodeSkill(时间戳是Environment.TickCount)
     private void EncodeSkill()
    {
        SkillId = (ushort) (ExchangeShortBits(ObjectId - 0x14be, 3) ^ ObjectId ^ 0x915d);
        SkillLevel = (ushort) ((SkillLevel + 0x100*(Timestamp%0x100)) ^ 0x3721);
        Arg1 = MathUtils.BitFold32(SkillId, SkillLevel);
        TargetObjectId = ExchangeLongBits(((TargetObjectId - 0x8b90b51a) ^ ObjectId ^ 0x5f2d2463u), 19);
        PositionX = (ushort) (ExchangeShortBits((uint) PositionX - 0xdd12, 1) ^ ObjectId ^ 0x2ed6);
        PositionY = (ushort) (ExchangeShortBits((uint) PositionY - 0x76de, 5) ^ ObjectId ^ 0xb99b);
    }

BitFold32
    public static int BitFold32(int lower16, int higher16)
    {
        return (lower16) | (higher16 << 16);
    }

ExchangeLongBits
    private static uint ExchangeLongBits(uint data, int bits)
    {
        return data >> bits | data << (32 - bits);
    }

最佳答案

这是我想出的与我的实际情况类似的代码:

using System;
using System.Diagnostics;

class Program
{
    static void Main(string[] args)
    {
        var dc = new Decoder();
        dc.DecodeSkill();
        Debug.Assert(dc.TargetObjectId == 0m && dc.PositionX == 302 && dc.PositionY == 278 && dc.SkillId == 1115 && dc.SkillLevel == 0);
    }
}

class Decoder
{
    public uint ObjectId = 1000001;
    public uint TargetObjectId = 2778236265;
    public ushort PositionX = 32409;
    public ushort PositionY = 16267;
    public ushort SkillId = 28399;
    public ushort SkillLevel = 8481;

    public void DecodeSkill()
    {
        SkillId = (ushort)(ExchangeShortBits((SkillId ^ ObjectId ^ 0x915d), 13) + 0x14be);
        SkillLevel = ((ushort)((byte)(SkillLevel) ^ 0x21));
        TargetObjectId = (ExchangeLongBits(TargetObjectId, 13) ^ ObjectId ^ 0x5f2d2463) + 0x8b90b51a;
        PositionX = (ushort)(ExchangeShortBits((PositionX ^ ObjectId ^ 0x2ed6), 15) + 0xdd12);
        PositionY = (ushort)(ExchangeShortBits((PositionY ^ ObjectId ^ 0xb99b), 11) + 0x76de);
    }

    private static uint ExchangeShortBits(uint data, int bits)
    {
        data &= 0xffff;
        return (data >> bits | data << (16 - bits)) & 65535;
    }

    public static int BitFold32(int lower16, int higher16)
    {
        return (lower16) | (higher16 << 16);
    }

    private static uint ExchangeLongBits(uint data, int bits)
    {
        return data >> bits | data << (32 - bits);
    }
}

您正在将8481与33异或。就是8448,这是我在计算机上看到的。假设SkillLevelushort,我想发生的是,您期望对byte的转换会截断SkillLevel,以便剩下的仅是最后8位,但这不会发生,因此当您将其转换为ushort高阶位仍然存在。

如果要可靠地截断小数点后8位后的所有数字,则需要像这样对它进行位掩码:
SkillLevel = ((ushort) ((SkillLevel & 255) ^ 0x21));

编辑:

我怀疑这与运营商的数字促销有关。 ^运算符应用于byteushortint时,会将两个操作数都升级为int,因为存在从第一个操作数到int的两种可能类型的隐式转换。似乎正在发生的事情是跳过了从ushortbyte显式转换,该转换会导致截断,因此被跳过了。现在您只有两个int,对它们进行XOR运算后将其截断为ushort,保留它们的高阶位。

10-06 00:57