/
Game Commands.vb
474 lines (447 loc) · 25 KB
/
Game Commands.vb
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
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
Imports Tinker.Commands
Namespace WC3
Public NotInheritable Class GameCommands
Private Sub New()
End Sub
Private Shared Function Conv(command As ICommand(Of Game)) As ICommand(Of GameManager)
Contract.Requires(command IsNot Nothing)
Contract.Ensures(Contract.Result(Of ICommand(Of GameManager))() IsNot Nothing)
Return command.ProjectedFrom(Function(x As GameManager) x.Game)
End Function
Public Shared Function MakeBotAdminCommands() As CommandSet(Of GameManager)
Contract.Ensures(Contract.Result(Of CommandSet(Of GameManager))() IsNot Nothing)
Dim result = New CommandSet(Of GameManager)
result.IncludeCommand(New CommandBot())
Return result
End Function
Public Shared Function MakeGuestLobbyCommands() As CommandSet(Of GameManager)
Contract.Ensures(Contract.Result(Of CommandSet(Of GameManager))() IsNot Nothing)
Dim result = New CommandSet(Of GameManager)
result.IncludeCommand(Conv(New CommandPing))
Return result
End Function
Public Shared Function MakeGuestInGameCommands() As CommandSet(Of GameManager)
Contract.Ensures(Contract.Result(Of CommandSet(Of GameManager))() IsNot Nothing)
Dim result = New CommandSet(Of GameManager)
For Each command In From c In New ICommand(Of Game)() {
New CommandElevate,
New CommandPing,
New CommandVoteStart
} Select Conv(c)
Contract.Assume(command IsNot Nothing)
result.IncludeCommand(command)
Next command
Return result
End Function
Public Shared Function MakeHostLobbyCommands() As CommandSet(Of GameManager)
Contract.Ensures(Contract.Result(Of CommandSet(Of GameManager))() IsNot Nothing)
Dim result = New CommandSet(Of GameManager)
For Each command In From c In New ICommand(Of Game)() {
New CommandBoot,
New CommandCancel,
New CommandClose,
New CommandColor,
New CommandCPU,
New CommandGet,
New CommandHandicap,
New CommandLock,
New CommandOpen,
New CommandPing,
New CommandRace,
New CommandReserve,
New CommandSet,
New CommandSetTeam,
New CommandSetupTeams,
New CommandStart,
New CommandSwap,
New CommandUnlock
} Select Conv(c)
Contract.Assume(command IsNot Nothing)
result.IncludeCommand(command)
Next command
Return result
End Function
Public Shared Function MakeHostInGameCommands() As CommandSet(Of GameManager)
Contract.Ensures(Contract.Result(Of CommandSet(Of GameManager))() IsNot Nothing)
Dim result = New CommandSet(Of GameManager)
For Each command In From c In New ICommand(Of Game)() {
New CommandBoot,
New CommandDisconnect,
New CommandGet,
New CommandPing,
New CommandSet
} Select Conv(c)
Contract.Assume(command IsNot Nothing)
result.IncludeCommand(command)
Next command
Return result
End Function
Private NotInheritable Class CommandBoot
Inherits TemplatedCommand(Of Game)
Public Sub New()
MyBase.New(Name:="Boot",
template:="Name/Color -close",
Description:="Kicks a player from the game. Closes their slot if -close is specified.")
End Sub
<SuppressMessage("Microsoft.Contracts", "Ensures-40-81")>
Protected Overloads Overrides Async Function PerformInvoke(target As Game, user As BotUser, argument As CommandArgument) As Task(Of String)
Dim slotQuery = argument.RawValue(0)
Dim shouldClose = argument.HasOptionalSwitch("close")
Await target.QueueBoot(slotQuery, shouldClose)
Return "Booted"
End Function
End Class
Private NotInheritable Class CommandBot
Inherits BaseCommand(Of GameManager)
Public Sub New()
MyBase.New(Name:="Bot",
Format:="subcommand...",
Description:="Forwards commands to the bot.")
End Sub
<SuppressMessage("Microsoft.Contracts", "Ensures-40-81")>
Protected Overrides Async Function PerformInvoke(target As GameManager, user As BotUser, argument As String) As Task(Of String)
Dim botManagers = (Await target.Bot.Components.QueueGetAllComponents()).OfType(Of Bot.MainBotManager)()
Return Await botManagers.Single().InvokeCommand(user, argument)
End Function
End Class
Private NotInheritable Class CommandCancel
Inherits TemplatedCommand(Of Game)
Public Sub New()
MyBase.New(Name:="Cancel",
template:="",
Description:="Closes this game instance.")
End Sub
<SuppressMessage("Microsoft.Contracts", "Ensures-40-81")>
Protected Overloads Overrides Async Function PerformInvoke(target As Game, user As BotUser, argument As CommandArgument) As Task(Of String)
target.Dispose()
Await target.DisposalTask
Return "Cancelled"
End Function
End Class
Private NotInheritable Class CommandClose
Inherits TemplatedCommand(Of Game)
Public Sub New()
MyBase.New(Name:="Close",
template:="slot",
Description:="Closes a slot.")
End Sub
<SuppressMessage("Microsoft.Contracts", "Ensures-40-81")>
Protected Overloads Overrides Async Function PerformInvoke(target As Game, user As BotUser, argument As CommandArgument) As Task(Of String)
Await target.QueueCloseSlot(argument.RawValue(0))
Return "Closed"
End Function
End Class
Private NotInheritable Class CommandColor
Inherits TemplatedCommand(Of Game)
Public Sub New()
MyBase.New(Name:="Color",
template:="slot value",
Description:="Sets the color of a slot. Not allowed when the map uses Fixed Player Settings.")
End Sub
<SuppressMessage("Microsoft.Contracts", "Ensures-40-81")>
Protected Overloads Overrides Async Function PerformInvoke(target As Game, user As BotUser, argument As CommandArgument) As Task(Of String)
Dim argSlot = argument.RawValue(0)
Dim argColor = argument.RawValue(1)
Dim color = argColor.EnumTryParse(Of Protocol.PlayerColor)(ignoreCase:=True)
If Not color.HasValue Then Throw New InvalidOperationException("Unrecognized color: '{0}'.".Frmt(argColor))
Await target.QueueSetSlotColor(argSlot, color.Value)
Return "Set Color"
End Function
End Class
Private NotInheritable Class CommandCPU
Inherits TemplatedCommand(Of Game)
Public Sub New()
MyBase.New(Name:="CPU",
template:="slot ?difficulty",
Description:="Places a computer in a slot, unless it contains a player.")
End Sub
<SuppressMessage("Microsoft.Contracts", "Ensures-40-81")>
Protected Overloads Overrides Async Function PerformInvoke(target As Game, user As BotUser, argument As CommandArgument) As Task(Of String)
Dim argSlot = argument.RawValue(0)
Dim argDifficulty = If(argument.RawValueCount >= 2, argument.RawValue(1), WC3.Protocol.ComputerLevel.Normal.ToString)
Dim difficulty = argDifficulty.EnumTryParse(Of Protocol.ComputerLevel)(ignoreCase:=True)
If Not difficulty.HasValue Then Throw New InvalidOperationException("Unrecognized difficulty: '{0}'.".Frmt(argDifficulty))
Await target.QueueSetSlotCpu(argSlot, difficulty.Value)
Return "Set {0} to Computer ({1})".Frmt(argSlot, argDifficulty)
End Function
End Class
Private NotInheritable Class CommandDisconnect
Inherits TemplatedCommand(Of Game)
Public Sub New()
MyBase.New(Name:="Disconnect",
template:="",
Description:="Causes the bot to disconnect from the game. The game might continue if one of the players can host.")
End Sub
<SuppressMessage("Microsoft.Contracts", "Ensures-40-81")>
Protected Overloads Overrides Async Function PerformInvoke(target As Game, user As BotUser, argument As CommandArgument) As Task(Of String)
target.Dispose()
Await target.DisposalTask
Return "Disconnected"
End Function
End Class
Private NotInheritable Class CommandElevate
Inherits TemplatedCommand(Of Game)
Public Sub New()
MyBase.New(Name:="Elevate",
template:="password",
Description:="Gives access to admin or host commands.")
End Sub
<SuppressMessage("Microsoft.Contracts", "Ensures-40-81")>
Protected Overloads Overrides Async Function PerformInvoke(target As Game, user As BotUser, argument As CommandArgument) As Task(Of String)
If user Is Nothing Then Throw New InvalidOperationException("User not specified.")
Await target.QueueElevatePlayer(user.Name, argument.RawValue(0))
Return "Elevated"
End Function
End Class
Private NotInheritable Class CommandGet
Inherits TemplatedCommand(Of Game)
Public Sub New()
MyBase.New(Name:="Get",
template:="setting",
Description:="Returns the current value of a game setting {tickperiod, laglimit, gamerate}.")
End Sub
Protected Overloads Overrides Function PerformInvoke(target As Game, user As BotUser, argument As CommandArgument) As Task(Of String)
Contract.Assume(target IsNot Nothing)
Dim val As Task(Of Double)
Dim argSetting = argument.RawValue(0).ToInvariant
Select Case argSetting
Case "TickPeriod" : val = From e In target.Motor.QueueGetTickPeriod Select e.TotalMilliseconds
Case "LagLimit" : val = From e In target.Motor.QueueGetLagLimit Select e.TotalMilliseconds
Case "GameRate" : val = From e In target.Motor.QueueGetSpeedFactor
Case Else : Throw New ArgumentException("Unrecognized setting '{0}'.".Frmt(argSetting))
End Select
Return From v In val
Select "{0} = '{1}'".Frmt(argSetting, v)
End Function
End Class
Private NotInheritable Class CommandHandicap
Inherits TemplatedCommand(Of Game)
Public Sub New()
MyBase.New(Name:="Handicap",
template:="slot value",
Description:="Sets the handicap of a slot.")
End Sub
<SuppressMessage("Microsoft.Contracts", "Ensures-40-81")>
Protected Overloads Overrides Async Function PerformInvoke(target As Game, user As BotUser, argument As CommandArgument) As Task(Of String)
Dim argSlot = argument.RawValue(0)
Dim argHandicap = argument.RawValue(1)
Dim newHandicap As Byte
If Not Byte.TryParse(argHandicap, newHandicap) Then newHandicap = 0
Select Case newHandicap
Case 50, 60, 70, 80, 90, 100
Await target.QueueSetSlotHandicap(argSlot, newHandicap)
Return "Set Handicap to {0}".Frmt(newHandicap)
Case Else
Throw New InvalidOperationException("Invalid handicap: '{0}'.".Frmt(argHandicap))
End Select
End Function
End Class
Private NotInheritable Class CommandLock
Inherits TemplatedCommand(Of Game)
Public Sub New()
MyBase.New(Name:="Lock",
template:="?slot -full",
Description:="Prevents players from leaving a slot or from changing slot properties (if -full). Omit the slot argument to affect all slots.")
End Sub
<SuppressMessage("Microsoft.Contracts", "Ensures-40-81")>
Protected Overloads Overrides Async Function PerformInvoke(target As Game, user As BotUser, argument As CommandArgument) As Task(Of String)
Dim lockType = If(argument.HasOptionalSwitch("full"), WC3.Slot.LockState.Frozen, WC3.Slot.LockState.Sticky)
If argument.RawValueCount = 0 Then
Await target.QueueSetAllSlotsLocked(lockType)
Return "Locked slots"
Else
Await target.QueueSetSlotLocked(argument.RawValue(0), lockType)
Return "Locked slot"
End If
End Function
End Class
Private NotInheritable Class CommandOpen
Inherits TemplatedCommand(Of Game)
Public Sub New()
MyBase.New(Name:="Open",
template:="slot",
Description:="Opens a slot.")
End Sub
<SuppressMessage("Microsoft.Contracts", "Ensures-40-81")>
Protected Overloads Overrides Async Function PerformInvoke(target As Game, user As BotUser, argument As CommandArgument) As Task(Of String)
Await target.QueueOpenSlot(argument.RawValue(0))
Return "Opened"
End Function
End Class
Private NotInheritable Class CommandPing
Inherits TemplatedCommand(Of Game)
Public Sub New()
MyBase.New(Name:="Ping",
template:="",
Description:="Returns estimated network round trip times for each player.")
End Sub
Protected Overloads Overrides Function PerformInvoke(target As Game, user As BotUser, argument As CommandArgument) As Task(Of String)
Contract.Assume(target IsNot Nothing)
Return From players In target.QueueGetPlayers()
From latencies In (From player In players Select player.QueueGetLatencyDescription).Cache.AsAggregateTask
Select "Estimated RTT: {0}".Frmt((From pair In players.Zip(latencies)
Where Not pair.Item1.IsFake
Select "{0}={1}".Frmt(pair.Item1.Name, pair.Item2)
).StringJoin(" "))
End Function
End Class
Private NotInheritable Class CommandRace
Inherits TemplatedCommand(Of Game)
Public Sub New()
MyBase.New(Name:="Race",
template:="slot race",
Description:="Sets the race of a slot. Not allowed when the map uses Fixed Player Settings and the slot race is not Selectable.")
End Sub
<SuppressMessage("Microsoft.Contracts", "Ensures-40-81")>
Protected Overloads Overrides Async Function PerformInvoke(target As Game, user As BotUser, argument As CommandArgument) As Task(Of String)
Dim argSlot = argument.RawValue(0)
Dim argRace = argument.RawValue(1)
Dim race = argRace.EnumTryParse(Of Protocol.Races)(ignoreCase:=True)
If Not race.HasValue Then Throw New InvalidOperationException("Unrecognized race: '{0}'.".Frmt(argRace))
Await target.QueueSetSlotRace(argSlot, race.Value)
Return "Set Race"
End Function
End Class
Private NotInheritable Class CommandReserve
Inherits TemplatedCommand(Of Game)
Public Sub New()
MyBase.New(Name:="Reserve",
template:="name -slot=any",
Description:="Reserves a slot for a player.")
End Sub
<SuppressMessage("Microsoft.Contracts", "Ensures-40-81")>
Protected Overloads Overrides Async Function PerformInvoke(target As Game, user As BotUser, argument As CommandArgument) As Task(Of String)
Dim name = argument.RawValue(0)
Dim slotQueryString = argument.TryGetOptionalNamedValue("slot")
Dim slotQuery = If(slotQueryString Is Nothing, Nothing, New InvariantString?(slotQueryString))
Await target.QueueReserveSlot(name, slotQuery)
Return "Reserved {0} for {1}.".Frmt(If(slotQueryString, "slot"), name)
End Function
End Class
Private NotInheritable Class CommandSet
Inherits TemplatedCommand(Of Game)
Public Sub New()
MyBase.New(Name:="Set",
template:="setting value",
Description:="Sets the value of a game setting {tickperiod, laglimit, gamerate}.")
End Sub
Protected Overloads Overrides Function PerformInvoke(target As Game, user As BotUser, argument As CommandArgument) As Task(Of String)
Contract.Assume(target IsNot Nothing)
Dim val_us As UShort
Dim vald As Double
Dim isShort = UShort.TryParse(argument.RawValue(1), val_us)
Dim isDouble = Double.TryParse(argument.RawValue(1), vald)
Dim argSetting = argument.RawValue(0).ToInvariant
Select Case argSetting
Case "TickPeriod"
If Not isShort OrElse val_us < 1 OrElse val_us > 20000 Then Throw New ArgumentException("Invalid value")
Dim t = CInt(val_us).Milliseconds
target.Motor.QueueSetTickPeriod(t)
Case "LagLimit"
If Not isShort OrElse val_us < 1 OrElse val_us > 20000 Then Throw New ArgumentException("Invalid value")
Dim t = CInt(val_us).Milliseconds
target.Motor.QueueSetLagLimit(t)
Case "GameRate"
If Not isDouble OrElse vald < 0.01 OrElse vald > 10 Then Throw New ArgumentException("Invalid value")
Contract.Assume(vald > 0)
target.Motor.QueueSetSpeedFactor(vald)
Case Else
Throw New ArgumentException("Unrecognized setting '{0}'.".Frmt(argument.RawValue(0)))
End Select
Return "{0} set to {1}".Frmt(argument.RawValue(0), argument.RawValue(1)).AsTask
End Function
End Class
Private NotInheritable Class CommandSetTeam
Inherits TemplatedCommand(Of Game)
Public Sub New()
MyBase.New(Name:="SetTeam",
template:="slot team",
Description:="Sets a slot's team. Only works in melee games.")
End Sub
<SuppressMessage("Microsoft.Contracts", "Ensures-40-81")>
Protected Overloads Overrides Async Function PerformInvoke(target As Game, user As BotUser, argument As CommandArgument) As Task(Of String)
Dim argSlot = argument.RawValue(0)
Dim argTeam = argument.RawValue(1)
Dim team As Byte
If Not Byte.TryParse(argTeam, team) OrElse team < 1 OrElse team > 13 Then
Throw New ArgumentException("Invalid team: '{0}'.".Frmt(argTeam))
End If
team -= CByte(1)
Await target.QueueSetSlotTeam(argSlot, team)
Return "Set Team"
End Function
End Class
Private NotInheritable Class CommandSetupTeams
Inherits TemplatedCommand(Of Game)
Public Sub New()
MyBase.New(Name:="SetupTeams",
template:="teams",
Description:="Sets up the number of slots on each team (eg. 'SetupTeams 2v2' will leave two open slots on each team).")
End Sub
<SuppressMessage("Microsoft.Contracts", "Ensures-40-81")>
Protected Overloads Overrides Async Function PerformInvoke(target As Game, user As BotUser, argument As CommandArgument) As Task(Of String)
Await target.QueueTrySetTeamSizes(TeamVersusStringToTeamSizes(argument.RawValue(0)))
Return "Set Teams"
End Function
End Class
Private NotInheritable Class CommandStart
Inherits TemplatedCommand(Of Game)
Public Sub New()
MyBase.New(Name:="Start",
template:="",
Description:="Starts the launch countdown.")
End Sub
<SuppressMessage("Microsoft.Contracts", "Ensures-40-81")>
Protected Overloads Overrides Async Function PerformInvoke(target As Game, user As BotUser, argument As CommandArgument) As Task(Of String)
Await target.QueueStartCountdown()
Return "Started Countdown"
End Function
End Class
Private NotInheritable Class CommandSwap
Inherits TemplatedCommand(Of Game)
Public Sub New()
MyBase.New(Name:="Swap",
template:="slot1 slot2",
Description:="Swaps the contents of two slots.")
End Sub
<SuppressMessage("Microsoft.Contracts", "Ensures-40-81")>
Protected Overloads Overrides Async Function PerformInvoke(target As Game, user As BotUser, argument As CommandArgument) As Task(Of String)
Await target.QueueSwapSlotContents(argument.RawValue(0), argument.RawValue(1))
Return "Swapped Slots"
End Function
End Class
Private NotInheritable Class CommandUnlock
Inherits TemplatedCommand(Of Game)
Public Sub New()
MyBase.New(Name:="Unlock",
template:="?slot",
Description:="Allows players to move from a slot and change its properties. Omit the slot argument to affect all slots.")
End Sub
<SuppressMessage("Microsoft.Contracts", "Ensures-40-81")>
Protected Overloads Overrides Async Function PerformInvoke(target As Game, user As BotUser, argument As CommandArgument) As Task(Of String)
Dim lockType = WC3.Slot.LockState.Unlocked
If argument.RawValueCount = 0 Then
Await target.QueueSetAllSlotsLocked(lockType)
Return "Unlocked slots"
Else
Await target.QueueSetSlotLocked(argument.RawValue(0), lockType)
Return "Unlocked slot"
End If
End Function
End Class
Private NotInheritable Class CommandVoteStart
Inherits TemplatedCommand(Of Game)
Public Sub New()
MyBase.New(Name:="VoteStart",
template:="-cancel",
Description:="Places or cancels a vote to prematurely start an autostarted game. Requires at least 2 players and at least a 2/3 majority.")
End Sub
<SuppressMessage("Microsoft.Contracts", "Ensures-40-81")>
Protected Overloads Overrides Async Function PerformInvoke(target As Game, user As BotUser, argument As CommandArgument) As Task(Of String)
If user Is Nothing Then Throw New InvalidOperationException("User not specified.")
Await target.QueueSetPlayerVoteToStart(user.Name, wantsToStart:=Not argument.HasOptionalSwitch("cancel"))
Return "Voted to start"
End Function
End Class
End Class
End Namespace