-
Notifications
You must be signed in to change notification settings - Fork 53
/
KMPCommon.cs
234 lines (215 loc) · 7.51 KB
/
KMPCommon.cs
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
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
using ICSharpCode.SharpZipLib.GZip;
using System;
using System.IO;
using System.Linq;
using System.Reflection;
using System.Text;
public class KMPCommon
{
public static String PROGRAM_VERSION
{
get
{
return Assembly.GetExecutingAssembly().GetName().Version.ToString();
}
}
public const Int32 FILE_FORMAT_VERSION = 10000;
public const Int32 NET_PROTOCOL_VERSION = 10016;
public const int MSG_HEADER_LENGTH = 8;
public const int MAX_MESSAGE_SIZE = 1024 * 1024; //Enough room for a max-size craft file
public const int MESSAGE_COMPRESSION_THRESHOLD = 4096;
public const int SPLIT_MESSAGE_SIZE = 8192; //Split big messages into smaller chunks, so high priority messages can get through faster.
public const int INTEROP_MSG_HEADER_LENGTH = 8;
public const int SERVER_SETTINGS_LENGTH = 23; //Length of fixed position information. Variable length settings start after this (including the header).
public const int MAX_CRAFT_FILE_BYTES = 1024 * 1024;
public const String SHARE_CRAFT_COMMAND = "!sharecraft";//"/" chat commands handled by client
public const String GET_CRAFT_COMMAND = "!getcraft"; //"!" chat commands handled by server
public const String RCON_COMMAND = "!rcon";
public const String ADMIN_MARKER = "@";
public static string filteredFileName(string filename)
{
const String illegal = "\\/:*?\"<>|";
StringBuilder sb = new StringBuilder();
foreach (char c in filename)
{
//Filter illegal characters out of the player name
if (!illegal.Contains(c))
sb.Append(c);
}
return sb.ToString();
}
public static byte[] intToBytes(Int32 val)
{
return BitConverter.GetBytes(val);
}
public static Int32 intFromBytes(byte[] bytes, int offset = 0)
{
return BitConverter.ToInt32(bytes, offset);
}
public enum CraftType
{
VAB,
SPH,
SUBASSEMBLY
}
public enum ClientMessageID
{
HANDSHAKE /*Username Length : Username : Version*/,
PRIMARY_PLUGIN_UPDATE /*data*/,
SECONDARY_PLUGIN_UPDATE /*data*/,
SCENARIO_UPDATE /*data*/,
TEXT_MESSAGE /*Message text*/,
SCREEN_WATCH_PLAYER /*Player name*/,
SCREENSHOT_SHARE /*Description Length : Description : data*/,
KEEPALIVE,
CONNECTION_END /*Message*/ ,
UDP_PROBE,
NULL,
SHARE_CRAFT_FILE /*Craft Type Byte : Craft name length : Craft Name : File bytes*/,
ACTIVITY_UPDATE_IN_GAME,
ACTIVITY_UPDATE_IN_FLIGHT,
PING,
WARPING,
SSYNC,
SPLIT_MESSAGE, /* Pre-emptive add for when message-queueing is implemented client side */
SYNC_TIME /*NTP style sync*/
}
public enum ServerMessageID
{
HANDSHAKE /*Protocol Version : Version String Length : Version String : ClientID : Mode*/,
HANDSHAKE_REFUSAL /*Refusal message*/,
SERVER_MESSAGE /*Message text*/,
TEXT_MESSAGE /*Message text*/,
MOTD_MESSAGE /*Message text*/,
PLUGIN_UPDATE /*data*/,
SCENARIO_UPDATE /*data*/,
SERVER_SETTINGS /*UpdateInterval (4) : Screenshot Interval (4) : Screenshot Height (4) : Bubble Size (8) : InactiveShips (1)*/,
SCREENSHOT_SHARE /*Description Length : Description : data*/,
KEEPALIVE,
CONNECTION_END /*Message*/,
UDP_ACKNOWLEDGE,
NULL,
CRAFT_FILE /*Craft Type Byte : Craft name length : Craft Name : File bytes*/,
PING_REPLY,
SYNC /*tick*/,
SYNC_COMPLETE,
SPLIT_MESSAGE, /* This allows higher priority messages more entry points into the send queue */
SYNC_TIME /*NTP style sync*/
}
public enum ClientInteropMessageID
{
NULL,
CLIENT_DATA /*Byte - Inactive Vessels Per Update : Screenshot Height : UpdateInterval : Player Name*/,
SCREENSHOT_RECEIVE /*Description Length : Description : data*/,
CHAT_RECEIVE /*Message*/,
PLUGIN_UPDATE /*data*/,
SCENARIO_UPDATE /*data*/
}
public enum PluginInteropMessageID
{
NULL,
PLUGIN_DATA /*Byte - In-Flight : Int32 - Current Game Title length : Current Game Title : Int32 - Screenshot watch player name length : Screenshot watch player name*/,
SCREENSHOT_SHARE /*Description Length : Description : data*/,
CHAT_SEND /*Message*/,
PRIMARY_PLUGIN_UPDATE /*data*/,
SECONDARY_PLUGIN_UPDATE /*data*/,
SCENARIO_UPDATE /*data*/,
WARPING /*data*/,
SSYNC /*data*/,
SYNC_TIME /*data*/
}
/* KMP message data format
* Uncompressed data: [bool-false : data]
* Compressed data: [bool-true : Int32-uncompressed length : compressed_data]
*/
public static byte[] Compress(byte[] data, bool forceUncompressed = false)
{
if (data == null) return null;
byte[] compressedData = null;
MemoryStream ms = null;
GZipOutputStream gzip = null;
try
{
ms = new MemoryStream();
if (data.Length < MESSAGE_COMPRESSION_THRESHOLD || forceUncompressed)
{
//Small message, don't compress
using (BinaryWriter writer = new BinaryWriter(ms))
{
writer.Write(false);
writer.Write(data, 0, data.Length);
compressedData = ms.ToArray();
ms.Close();
writer.Close();
}
}
else
{
//Compression enabled
Int32 size = data.Length;
using (BinaryWriter writer = new BinaryWriter(ms))
{
writer.Write(true);
writer.Write(size);
gzip = new GZipOutputStream(ms);
gzip.Write(data, 0, data.Length);
gzip.Close();
compressedData = ms.ToArray();
ms.Close();
writer.Close();
}
}
}
catch
{
return null;
}
finally
{
if (gzip != null) gzip.Dispose();
if (ms != null) ms.Dispose();
}
return compressedData;
}
public static byte[] Decompress(byte[] data)
{
if (data == null) return null;
byte[] decompressedData = null;
MemoryStream ms = null;
GZipInputStream gzip = null;
try
{
ms = new MemoryStream(data, false);
using (BinaryReader reader = new BinaryReader(ms))
{
bool compressedFlag = reader.ReadBoolean();
if (compressedFlag == false)
{
//Uncompressed
decompressedData = reader.ReadBytes(data.Length - 1);
}
else
{
//Decompress
Int32 size = reader.ReadInt32();
gzip = new GZipInputStream(ms);
decompressedData = new byte[size];
gzip.Read(decompressedData, 0, decompressedData.Length);
gzip.Close();
ms.Close();
}
reader.Close();
}
}
catch
{
return null;
}
finally
{
if (gzip != null) gzip.Dispose();
if (ms != null) ms.Dispose();
}
return decompressedData;
}
}