Skip to content

Commit 051ee70

Browse files
authored
Added Web Socket Chat Bot
Web Socket Chat Bot
2 parents 9855e2e + 95f6c57 commit 051ee70

File tree

9 files changed

+1670
-25
lines changed

9 files changed

+1670
-25
lines changed

MinecraftClient/ChatBots/WebSocketBot.cs

Lines changed: 1341 additions & 0 deletions
Large diffs are not rendered by default.

MinecraftClient/Json.cs

Lines changed: 176 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -25,12 +25,24 @@ public static JSONData ParseJson(string json)
2525
/// </summary>
2626
public class JSONData
2727
{
28-
public enum DataType { Object, Array, String };
28+
public enum DataType
29+
{
30+
Object,
31+
Array,
32+
String
33+
};
34+
2935
private readonly DataType type;
30-
public DataType Type { get { return type; } }
36+
37+
public DataType Type
38+
{
39+
get { return type; }
40+
}
41+
3142
public Dictionary<string, JSONData> Properties;
3243
public List<JSONData> DataArray;
3344
public string StringValue;
45+
3446
public JSONData(DataType datatype)
3547
{
3648
type = datatype;
@@ -63,12 +75,21 @@ private static JSONData String2Data(string toparse, ref int cursorpos)
6375
if (toparse[cursorpos] == '"')
6476
{
6577
JSONData propertyname = String2Data(toparse, ref cursorpos);
66-
if (toparse[cursorpos] == ':') { cursorpos++; } else { /* parse error ? */ }
78+
if (toparse[cursorpos] == ':')
79+
{
80+
cursorpos++;
81+
}
82+
else
83+
{
84+
/* parse error ? */
85+
}
86+
6787
JSONData propertyData = String2Data(toparse, ref cursorpos);
6888
data.Properties[propertyname.StringValue] = propertyData;
6989
}
7090
else cursorpos++;
7191
}
92+
7293
cursorpos++;
7394
break;
7495

@@ -79,10 +100,15 @@ private static JSONData String2Data(string toparse, ref int cursorpos)
79100
SkipSpaces(toparse, ref cursorpos);
80101
while (toparse[cursorpos] != ']')
81102
{
82-
if (toparse[cursorpos] == ',') { cursorpos++; }
103+
if (toparse[cursorpos] == ',')
104+
{
105+
cursorpos++;
106+
}
107+
83108
JSONData arrayItem = String2Data(toparse, ref cursorpos);
84109
data.DataArray.Add(arrayItem);
85110
}
111+
86112
cursorpos++;
87113
break;
88114

@@ -103,9 +129,11 @@ private static JSONData String2Data(string toparse, ref int cursorpos)
103129
&& IsHex(toparse[cursorpos + 5]))
104130
{
105131
//"abc\u0123abc" => "0123" => 0123 => Unicode char n°0123 => Add char to string
106-
data.StringValue += char.ConvertFromUtf32(int.Parse(toparse.Substring(cursorpos + 2, 4),
132+
data.StringValue += char.ConvertFromUtf32(int.Parse(
133+
toparse.Substring(cursorpos + 2, 4),
107134
System.Globalization.NumberStyles.HexNumber));
108-
cursorpos += 6; continue;
135+
cursorpos += 6;
136+
continue;
109137
}
110138
else if (toparse[cursorpos + 1] == 'n')
111139
{
@@ -127,12 +155,20 @@ private static JSONData String2Data(string toparse, ref int cursorpos)
127155
}
128156
else cursorpos++; //Normal character escapement \"
129157
}
130-
catch (IndexOutOfRangeException) { cursorpos++; } // \u01<end of string>
131-
catch (ArgumentOutOfRangeException) { cursorpos++; } // Unicode index 0123 was invalid
158+
catch (IndexOutOfRangeException)
159+
{
160+
cursorpos++;
161+
} // \u01<end of string>
162+
catch (ArgumentOutOfRangeException)
163+
{
164+
cursorpos++;
165+
} // Unicode index 0123 was invalid
132166
}
167+
133168
data.StringValue += toparse[cursorpos];
134169
cursorpos++;
135170
}
171+
136172
cursorpos++;
137173
break;
138174

@@ -151,47 +187,93 @@ private static JSONData String2Data(string toparse, ref int cursorpos)
151187
case '-':
152188
data = new JSONData(JSONData.DataType.String);
153189
StringBuilder sb = new();
154-
while ((toparse[cursorpos] >= '0' && toparse[cursorpos] <= '9') || toparse[cursorpos] == '.' || toparse[cursorpos] == '-')
190+
while ((toparse[cursorpos] >= '0' && toparse[cursorpos] <= '9') || toparse[cursorpos] == '.' ||
191+
toparse[cursorpos] == '-')
155192
{
156193
sb.Append(toparse[cursorpos]);
157194
cursorpos++;
158195
}
196+
159197
data.StringValue = sb.ToString();
160198
break;
161199

162200
//Boolean : true
163201
case 't':
164202
data = new JSONData(JSONData.DataType.String);
165203
cursorpos++;
166-
if (toparse[cursorpos] == 'r') { cursorpos++; }
167-
if (toparse[cursorpos] == 'u') { cursorpos++; }
168-
if (toparse[cursorpos] == 'e') { cursorpos++; data.StringValue = "true"; }
204+
if (toparse[cursorpos] == 'r')
205+
{
206+
cursorpos++;
207+
}
208+
209+
if (toparse[cursorpos] == 'u')
210+
{
211+
cursorpos++;
212+
}
213+
214+
if (toparse[cursorpos] == 'e')
215+
{
216+
cursorpos++;
217+
data.StringValue = "true";
218+
}
219+
169220
break;
170221

171222
//Boolean : false
172223
case 'f':
173224
data = new JSONData(JSONData.DataType.String);
174225
cursorpos++;
175-
if (toparse[cursorpos] == 'a') { cursorpos++; }
176-
if (toparse[cursorpos] == 'l') { cursorpos++; }
177-
if (toparse[cursorpos] == 's') { cursorpos++; }
178-
if (toparse[cursorpos] == 'e') { cursorpos++; data.StringValue = "false"; }
226+
if (toparse[cursorpos] == 'a')
227+
{
228+
cursorpos++;
229+
}
230+
231+
if (toparse[cursorpos] == 'l')
232+
{
233+
cursorpos++;
234+
}
235+
236+
if (toparse[cursorpos] == 's')
237+
{
238+
cursorpos++;
239+
}
240+
241+
if (toparse[cursorpos] == 'e')
242+
{
243+
cursorpos++;
244+
data.StringValue = "false";
245+
}
246+
179247
break;
180248

181249
//Null field
182250
case 'n':
183251
data = new JSONData(JSONData.DataType.String);
184252
cursorpos++;
185-
if (toparse[cursorpos] == 'u') { cursorpos++; }
186-
if (toparse[cursorpos] == 'l') { cursorpos++; }
187-
if (toparse[cursorpos] == 'l') { cursorpos++; data.StringValue = "null"; }
253+
if (toparse[cursorpos] == 'u')
254+
{
255+
cursorpos++;
256+
}
257+
258+
if (toparse[cursorpos] == 'l')
259+
{
260+
cursorpos++;
261+
}
262+
263+
if (toparse[cursorpos] == 'l')
264+
{
265+
cursorpos++;
266+
data.StringValue = "null";
267+
}
268+
188269
break;
189270

190271
//Unknown data
191272
default:
192273
cursorpos++;
193274
return String2Data(toparse, ref cursorpos);
194275
}
276+
195277
SkipSpaces(toparse, ref cursorpos);
196278
return data;
197279
}
@@ -206,7 +288,10 @@ private static JSONData String2Data(string toparse, ref int cursorpos)
206288
/// </summary>
207289
/// <param name="c">Char to test</param>
208290
/// <returns>True if hexadecimal</returns>
209-
private static bool IsHex(char c) { return ((c >= '0' && c <= '9') || (c >= 'A' && c <= 'F') || (c >= 'a' && c <= 'f')); }
291+
private static bool IsHex(char c)
292+
{
293+
return ((c >= '0' && c <= '9') || (c >= 'A' && c <= 'F') || (c >= 'a' && c <= 'f'));
294+
}
210295

211296
/// <summary>
212297
/// Advance the cursor to skip white spaces and line breaks
@@ -216,10 +301,77 @@ private static JSONData String2Data(string toparse, ref int cursorpos)
216301
private static void SkipSpaces(string toparse, ref int cursorpos)
217302
{
218303
while (cursorpos < toparse.Length
219-
&& (char.IsWhiteSpace(toparse[cursorpos])
220-
|| toparse[cursorpos] == '\r'
221-
|| toparse[cursorpos] == '\n'))
304+
&& (char.IsWhiteSpace(toparse[cursorpos])
305+
|| toparse[cursorpos] == '\r'
306+
|| toparse[cursorpos] == '\n'))
222307
cursorpos++;
223308
}
309+
310+
// Original: https://github.com/mono/mono/blob/master/mcs/class/System.Json/System.Json/JsonValue.cs
311+
private static bool NeedEscape(string src, int i)
312+
{
313+
var c = src[i];
314+
return c < 32 || c == '"' || c == '\\'
315+
// Broken lead surrogate
316+
|| (c is >= '\uD800' and <= '\uDBFF' &&
317+
(i == src.Length - 1 || src[i + 1] < '\uDC00' || src[i + 1] > '\uDFFF'))
318+
// Broken tail surrogate
319+
|| (c is >= '\uDC00' and <= '\uDFFF' &&
320+
(i == 0 || src[i - 1] < '\uD800' || src[i - 1] > '\uDBFF'))
321+
// To produce valid JavaScript
322+
|| c == '\u2028' || c == '\u2029'
323+
// Escape "</" for <script> tags
324+
|| (c == '/' && i > 0 && src[i - 1] == '<');
325+
}
326+
327+
public static string EscapeString(string src)
328+
{
329+
var sb = new StringBuilder();
330+
var start = 0;
331+
332+
for (var i = 0; i < src.Length; i++)
333+
{
334+
if (!NeedEscape(src, i)) continue;
335+
sb.Append(src, start, i - start);
336+
337+
switch (src[i])
338+
{
339+
case '\b':
340+
sb.Append("\\b");
341+
break;
342+
case '\f':
343+
sb.Append("\\f");
344+
break;
345+
case '\n':
346+
sb.Append("\\n");
347+
break;
348+
case '\r':
349+
sb.Append("\\r");
350+
break;
351+
case '\t':
352+
sb.Append("\\t");
353+
break;
354+
case '\"':
355+
sb.Append("\\\"");
356+
break;
357+
case '\\':
358+
sb.Append("\\\\");
359+
break;
360+
case '/':
361+
sb.Append("\\/");
362+
break;
363+
364+
default:
365+
sb.Append("\\u");
366+
sb.Append(((int)src[i]).ToString("x04"));
367+
break;
368+
}
369+
370+
start = i + 1;
371+
}
372+
373+
sb.Append(src, start, src.Length - start);
374+
return sb.ToString();
375+
}
224376
}
225-
}
377+
}

MinecraftClient/McClient.cs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -297,6 +297,7 @@ private void RegisterBots(bool reload = false)
297297
if (Config.ChatBot.ScriptScheduler.Enabled) { BotLoad(new ScriptScheduler()); }
298298
if (Config.ChatBot.TelegramBridge.Enabled) { BotLoad(new TelegramBridge()); }
299299
if (Config.ChatBot.ItemsCollector.Enabled) { BotLoad(new ItemsCollector()); }
300+
if (Config.ChatBot.WebSocketBot.Enabled) { BotLoad(new WebSocketBot()); }
300301
//Add your ChatBot here by uncommenting and adapting
301302
//BotLoad(new ChatBots.YourBot());
302303
}

MinecraftClient/Resources/ConfigComments/ConfigComments.Designer.cs

Lines changed: 30 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

MinecraftClient/Resources/ConfigComments/ConfigComments.resx

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -828,4 +828,19 @@ If the connection to the Minecraft game server is blocked by the firewall, set E
828828
<data name="ChatBot.ItemsCollector" xml:space="preserve">
829829
<value>A Chat Bot that collects items on the ground</value>
830830
</data>
831+
<data name="ChatBot.WebSocketBot" xml:space="preserve">
832+
<value>Remotely control the client using Web Sockets.\n# This is useful if you want to implement an application that can remotely and asynchronously execute procedures in MCC.\n# Example implementation written in JavaScript: https://github.com/milutinke/MCC.js.git\n# The protocol specification will be available in the documentation soon.</value>
833+
</data>
834+
<data name="ChatBot.WebSocketBot.Ip" xml:space="preserve">
835+
<value>The IP address that Websocket server will be bound to.</value>
836+
</data>
837+
<data name="ChatBot.WebSocketBot.Port" xml:space="preserve">
838+
<value>The Port that Websocket server will be bounded to.</value>
839+
</data>
840+
<data name="ChatBot.WebSocketBot.Password" xml:space="preserve">
841+
<value>A password that will be used to authenticate on thw Websocket server (It is recommended to change the default password and to set a strong one).</value>
842+
</data>
843+
<data name="ChatBot.WebSocketBot.DebugMode" xml:space="preserve">
844+
<value>This setting is for developers who are developing a library that uses this chat bot to remotely execute procedures/commands/functions.</value>
845+
</data>
831846
</root>

0 commit comments

Comments
 (0)