-
Notifications
You must be signed in to change notification settings - Fork 4
Expand file tree
/
Copy pathSID_ENTERCHAT.cs
More file actions
154 lines (127 loc) · 7.14 KB
/
SID_ENTERCHAT.cs
File metadata and controls
154 lines (127 loc) · 7.14 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
using Atlasd.Battlenet.Exceptions;
using Atlasd.Daemon;
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Text;
namespace Atlasd.Battlenet.Protocols.Game.Messages
{
class SID_ENTERCHAT : Message
{
public SID_ENTERCHAT()
{
Id = (byte)MessageIds.SID_ENTERCHAT;
Buffer = new byte[0];
}
public SID_ENTERCHAT(byte[] buffer)
{
Id = (byte)MessageIds.SID_ENTERCHAT;
Buffer = buffer;
}
public override bool Invoke(MessageContext context)
{
if (context == null || context.Client == null || !context.Client.Connected) return false;
var gameState = context.Client.GameState;
switch (context.Direction)
{
case MessageDirection.ClientToServer:
{
Logging.WriteLine(Logging.LogLevel.Debug, Logging.LogType.Client_Game, context.Client.RemoteEndPoint, $"[{Common.DirectionToString(context.Direction)}] {MessageName(Id)} ({4 + Buffer.Length} bytes)");
if (Buffer.Length < 2)
throw new GameProtocolViolationException(context.Client, $"{MessageName(Id)} buffer must be at least 2 bytes");
if (gameState.ActiveAccount == null || string.IsNullOrEmpty(gameState.OnlineName))
throw new GameProtocolViolationException(context.Client, $"{MessageName(Id)} received before logon");
/**
* (STRING) Username
* (STRING) Statstring
*/
using var m = new MemoryStream(Buffer);
using var r = new BinaryReader(m);
var username = r.ReadByteString(); // Defunct
var statstring = r.ReadByteString();
var productId = (UInt32)gameState.Product;
// Statstring has a maximum length of 128 bytes
if (statstring.Length > 128)
throw new GameProtocolViolationException(context.Client, $"Client sent invalid statstring size in {MessageName(Id)}");
return new SID_ENTERCHAT().Invoke(new MessageContext(context.Client, MessageDirection.ServerToClient,
new Dictionary<string, dynamic>(){{ "username", username }, { "statstring", statstring }})
);
}
case MessageDirection.ServerToClient:
{
var uniqueName = gameState.OnlineName;
var statstring = context.Arguments.ContainsKey("statstring") ? (byte[])context.Arguments["statstring"] : gameState.Statstring;
var accountName = gameState.Username;
if (Product.IsDiabloII(gameState.Product))
{
statstring = Product.ToByteArray(gameState.Product);
}
// Do not use client-provided statstring if config.battlenet.emulation.statstring_updates is not enabled for this product.
// Blizzard servers allowed statstring updates for Diablo, Diablo II (changing characters), Warcraft III (changing icons), and Shareware variants.
if (!GameState.CanStatstringUpdate(gameState.Product) || statstring.Length == 0) statstring = gameState.GenerateStatstring();
/**
* (STRING) Unique name
* (STRING) Statstring
* (STRING) Account name
*/
Buffer = new byte[3 + Encoding.UTF8.GetByteCount(uniqueName) + statstring.Length + Encoding.UTF8.GetByteCount(accountName)];
using var m = new MemoryStream(Buffer);
using var w = new BinaryWriter(m);
w.Write((string)uniqueName);
w.WriteByteString(statstring);
w.Write((string)accountName);
lock (gameState)
{
var newFlags = (Account.Flags)gameState.ActiveAccount.Get(Account.FlagsKey);
if (!gameState.UDPSupported && (Product.IsUDPSupported(gameState.Product) || Product.IsChat(gameState.Product))) newFlags |= Account.Flags.NoUDP;
var newPing = gameState.Ping;
if (Product.IsChat(gameState.Product)) newPing = 0;
if (gameState.GameAd != null)
{
var gameAd = gameState.GameAd;
if (gameAd.RemoveClient(gameState)) gameState.GameAd = null;
if (gameAd.Clients.Count == 0) lock (Battlenet.Common.ActiveGameAdsLock) Battlenet.Common.ActiveGameAds.Remove(gameAd);
}
if (gameState.ActiveChannel == null)
{
Logging.WriteLine(Logging.LogLevel.Info, Logging.LogType.Client_Game, context.Client.RemoteEndPoint, $"Entering chat as [{uniqueName}]");
gameState.ChannelFlags = newFlags;
gameState.Ping = newPing;
gameState.Statstring = statstring;
}
else
{
Logging.WriteLine(Logging.LogLevel.Info, Logging.LogType.Client_Game, context.Client.RemoteEndPoint, $"Re-entering chat as [{uniqueName}]");
gameState.ActiveChannel.UpdateUser(gameState, newFlags, newPing, statstring);
}
}
Logging.WriteLine(Logging.LogLevel.Debug, Logging.LogType.Client_Game, context.Client.RemoteEndPoint, $"[{Common.DirectionToString(context.Direction)}] {MessageName(Id)} ({4 + Buffer.Length} bytes)");
context.Client.Send(ToByteArray(context.Client.ProtocolType));
return true;
}
}
return false;
}
public new byte[] ToByteArray(ProtocolType protocolType)
{
if (protocolType.IsChat())
{
using var _m = new MemoryStream(Buffer);
using var r = new BinaryReader(_m);
var username = r.ReadByteString();
var statstring = r.ReadByteString();
using var m = new MemoryStream(0xFFFF);
using var w = new System.IO.BinaryWriter(m);
w.Write(Encoding.UTF8.GetBytes($"{2000 + Id} NAME "));
w.Write(username);
w.Write(Encoding.UTF8.GetBytes(Battlenet.Common.NewLine));
return m.GetBuffer()[0..(int)w.BaseStream.Length];
}
else
{
return base.ToByteArray(protocolType);
}
}
}
}