-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathtargeted_input.txt
520 lines (391 loc) · 15.3 KB
/
targeted_input.txt
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
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
--@name Targeted Input
--@author legokidlogan
--@shared
--@include lkl/fancy_input.txt
targetedInput = targetedInput or {}
if targetedInput._alreadyLoaded then return end
targetedInput._alreadyLoaded = true
require( "lkl/fancy_input.txt" )
--[[
- A library which builds off of fancy_input and networks button presses from client to server.
- You must specify which players and buttons to target, for the sake of network efficiency.
- This does NOT network back to other clients, only to the server.
- See fancy_input for relevant hooks and utility functions.
- Example setup:
--@shared
--@include lkl/targeted_input.txt
require( "lkl/targeted_input.txt" )
if SERVER then
local buttonFuncs = {
[KEY.F] = function( ply, key, state )
local stateStr = state and "pressed" or "released"
print( "F was " .. stateStr .. " by " .. ply:getName() )
end,
[KEY.G] = function( ply, key, state )
local stateStr = state and "pressed" or "released"
print( "G was " .. stateStr .. " by " .. ply:getName() )
end,
}
targetedInput.setTargets( owner() ) -- Target the chip owner
targetedInput.setTargetButtons( table.getKeys( buttonFuncs ) ) -- Target only the desired buttons, reducing network traffic
local function keyClk( ply, key, state )
( buttonFuncs[key] or function() end )( ply, key, state )
end
hook.add( "LKL_FancyInput_InputClk", "MYPROJECT_KeyClk", keyClk )
else
-- Client listener
hook.add( "LKL_FancyInput_InputClk", "MYPROJECT_KeyClk", function( _, key, state )
local stateStr = state and "pressed" or "released"
print( "You " .. stateStr .. " button " .. key )
end )
end
--]]
local targets = {}
local targetButtons = {}
local targetAllButtons = false
local curtime = timer.curtime
if SERVER then
local tableIsEmpty = table.isEmpty
local tableRemove = table.remove
local timeoutDelay = 4 -- How long to wait before considering a message timed out and needs to be re-sent. Typically only happens during chip startup.
local incompleteResponses = {}
local getHumans
local awaitResponseRetry
local awaitResponse
--[[
- Sets the players to get input updates from.
- Cannot target bots.
plys:
- { plyOne, plyTwo, plyThree, ... } -> Sets these players as the new targets to get button presses from.
- Either numerically indexed, or a lookup table (i.e. { [1] = plyOne, [2] = plyTwo, ... } or { [plyOne] = tue, [plyTwo] = true, ... })
- {} / false -> Do not target any players.
- ply -> Target one specific player. Same as { ply }
- nil -> Target ALL players.
--]]
function targetedInput.setTargets( plys )
if plys == nil then
plys = getHumans()
end
if not plys then
plys = {}
end
local plysType = type( plys )
if plysType == "Player" and isValid( plys ) then
plys = {
plys,
}
elseif plysType ~= "table" then
return -- Invalid input (was a number, string, invalid ent, etc)
end
local oldTargets = targets
targets = {}
plys = getHumans( plys )
if tableIsEmpty( plys ) then
for _, ply in ipairs( find.allPlayers() ) do
fancyInput.setButtonStates( ply, {} )
end
awaitResponse( "SetTargets" )
net.start( "LKL_TargetedInput_SetTargets" )
net.writeTable( targets )
net.send()
return
end
local plyCount = #plys
if plyCount == 0 then
-- This better be a lookup table!
for ply in pairs( plys ) do -- deep-clone the table to ensure ply validity and unlink memory addresses
if isValid( ply ) and ply:isPlayer() then
targets[ply] = true
end
end
else
for i = 1, plyCount do
local ply = plys[i]
if isValid( ply ) and ply:isPlayer() then
targets[ply] = true
end
end
end
-- Discard button states of now-removed player targets
for ply in pairs( oldTargets ) do
if not targets[ply] then
fancyInput.setButtonStates( ply, {} )
end
end
awaitResponse( "SetTargets" )
net.start( "LKL_TargetedInput_SetTargets" )
net.writeTable( targets )
net.send()
end
--[[
- Adds to the current target players, instead of replacing them.
- Useful for a chip with multiple depencies that each use TargetedInput.
- Cannot target bots.
plys:
- { plyOne, plyTwo, plyThree, ... } -> Adds these players to the target list.
- Either numerically indexed, or a lookup table (i.e. { [1] = plyOne, [2] = plyTwo, ... } or { [plyOne] = tue, [plyTwo] = true, ... })
- {} / false -> Do not add any players.
- ply -> Add one specific player. Same as { ply }
- nil -> Add ALL players.
--]]
function targetedInput.addTargets( plys )
if plys == nil then
targetedInput.setTargets( nil )
return
end
if not plys then return end
if type( plys ) ~= "table" then
plys = {
plys,
}
elseif tableIsEmpty( plys ) then return end
local combinedTargets = {}
for ply in pairs( targets ) do
combinedTargets[ply] = true
end
for _, ply in pairs( plys ) do
combinedTargets[ply] = true
end
targetedInput.setTargets( table.getKeys( combinedTargets ) )
end
--[[
- Sets the buttons to get input updates for.
- Valid button values are from the KEY/MOUSE enums
buttons:
- { btnOne, btnTwo, btnThree, ... } -> Sets these buttons as the new target inputs.
- This can only be a numerical table, not a lookup, since buttons are themselves numbers and #tbl will get confused
- {} / false -> Do not target any buttons.
- btn -> Target one specific button value. Same as { btn }
- nil -> Target ALL possible button inputs.
--]]
function targetedInput.setTargetButtons( buttons )
if buttons == nil then
targetAllButtons = true
targetButtons = {}
awaitResponse( "SetTargetButtons" )
net.start( "LKL_TargetedInput_SetTargetButtons" )
net.writeBool( targetAllButtons )
net.writeTable( targetButtons )
net.send()
return
end
if not buttons then
buttons = {}
end
if type( buttons ) ~= "table" then
buttons = {
buttons,
}
elseif tableIsEmpty( buttons ) then
targetAllButtons = false
targetButtons = {}
for ply in ipairs( find.allPlayers() ) do
fancyInput.setButtonStates( ply, {} )
end
awaitResponse( "SetTargetButtons" )
net.start( "LKL_TargetedInput_SetTargetButtons" )
net.writeBool( targetAllButtons )
net.writeTable( targetButtons )
net.send()
return
end
for i = #buttons, 1, -1 do
local button = buttons[i]
if type( button ) ~= "number" then
tableRemove( buttons, i )
end
end
local plys = find.allPlayers()
-- Remove buttons states for now-removed button targets
for _, ply in ipairs( plys ) do
local states = fancyInput.getButtonStates( ply )
for button in pairs( targetButtons ) do
if not buttons[button] then
states[button] = nil
end
end
fancyInput.setButtonStates( ply, states )
end
targetAllButtons = false
targetButtons = {}
for _, button in pairs( buttons ) do
targetButtons[button] = true
end
awaitResponse( "SetTargetButtons" )
net.start( "LKL_TargetedInput_SetTargetButtons" )
net.writeBool( targetAllButtons )
net.writeTable( targetButtons )
net.send()
end
--[[
- Adds to the current target buttons, instead of replacing them.
- Useful for a chip with multiple depencies that each use TargetedInput.
- Valid button values are from the KEY/MOUSE enums.
buttons:
- { btnOne, btnTwo, btnThree, ... } -> Adds these buttons to the target inputs.
- This can only be a numerical table, not a lookup, since buttons are themselves numbers and #tbl will get confused
- {} / false -> Do not add any buttons.
- btn -> Add one specific button value. Same as { btn }
- nil -> Add ALL possible button inputs.
--]]
function targetedInput.addTargetButtons( buttons )
if targetAllButtons then return end
if buttons == nil then
targetedInput.setTargetButtons( nil )
return
end
if not buttons then return end
if type( buttons ) ~= "table" then
buttons = {
buttons,
}
elseif tableIsEmpty( buttons ) then return end
local combinedButtons = {}
for button in pairs( targetButtons ) do
combinedButtons[button] = true
end
for _, button in pairs( buttons ) do
combinedButtons[button] = true
end
targetedInput.setTargetButtons( table.getKeys( combinedButtons ) )
end
----- PRIVATE FUNCTIONS -----
getHumans = function( plys )
plys = plys or find.allPlayers()
for i, ply in ipairs( plys ) do
if not isValid( ply ) or ply:isBot() then
tableRemove( plys, i )
end
end
return plys
end
awaitResponseRetry = function( responseType, ply )
if not responseType then return end
if not isValid( ply ) then return end
if responseType == "SetTargets" then
net.start( "LKL_TargetedInput_SetTargets" )
net.writeTable( targets )
net.send( ply )
elseif responseType == "SetTargetButtons" then
net.start( "LKL_TargetedInput_SetTargetButtons" )
net.writeBool( targetAllButtons )
net.writeTable( targetButtons )
net.send( ply )
end
end
awaitResponse = function( responseType, waitFor )
if not responseType then return end
waitFor = waitFor or timeoutDelay
local plys = getHumans()
local plyLookup = {}
for _, ply in ipairs( plys ) do
plyLookup[ply] = true
end
incompleteResponses[responseType] = plyLookup
timer.create( "LKL_TargetedInput_AwaitResponse_" .. responseType, waitFor, 1, function()
local nonResponders = incompleteResponses[responseType] or {}
for ply in pairs( nonResponders ) do
awaitResponseRetry( responseType, ply )
end
incompleteResponses[responseType] = nil
end )
end
----- SETUP -----
net.receive( "LKL_TargetedInput_InputClkFromNet", function( _, ply )
if not targets[ply] then return end
local key = net.readUInt( 10 )
if not targetAllButtons and not targetButtons[key] then return end
local state = net.readBool()
local time = net.readFloat()
fancyInput.runInput( ply, key, state, time )
end )
net.receive( "LKL_TargetedInput_RefreshMyButtonStates", function( _, ply )
local states = {}
for _ = 1, net.readUInt( 10 ) do
states[net.readUInt( 10 )] = true
end
fancyInput.setButtonStates( ply, states )
end )
net.receive( "LKL_TargetedInput_Acknowledge", function( _, ply )
local responseType = net.readString()
local nonResponders = incompleteResponses[responseType]
if not nonResponders then return end
nonResponders[ply] = nil
end )
--[[
timer.simple( 0.5, function() -- Net isn't reliable on init, so we'll send a delayed update on startup for just in case
net.start( "LKL_TargetedInput_SetTargets" )
net.writeTable( targets )
net.send()
net.start( "LKL_TargetedInput_SetTargetButtons" )
net.writeBool( targetAllButtons )
net.writeTable( targetButtons )
net.send()
end )
--]]
hook.add( "ClientInitialized", "LKL_TargetedInput_ClientInit", function( ply )
timer.simple( 1, function()
if not isValid( ply ) then return end
net.start( "LKL_TargetedInput_SetTargets" )
net.writeTable( targets )
net.send( ply )
net.start( "LKL_TargetedInput_SetTargetButtons" )
net.writeBool( targetAllButtons )
net.writeTable( targetButtons )
net.send( ply )
end )
end )
else
local iAmATarget = false
local function refreshMyButtonStates()
if not iAmATarget then return end
local pressedButtons = fancyInput.getPressedButtons( ply )
net.start( "LKL_TargetedInput_RefreshMyButtonStates" )
net.writeUInt( #pressedButtons, 10 )
for _, key in ipairs( pressedButtons ) do
net.writeUInt( key, 10 )
end
net.send()
end
local function setTargets( newTargets )
targets = newTargets
iAmATarget = targets[player()]
net.start( "LKL_TargetedInput_Acknowledge" )
net.writeString( "SetTargets" )
net.send()
refreshMyButtonStates()
end
local function setTargetButtons( newTargetButtons, targetAll )
targetAllButtons = targetAll
targetButtons = newTargetButtons
net.start( "LKL_TargetedInput_Acknowledge" )
net.writeString( "SetTargetButtons" )
net.send()
refreshMyButtonStates()
end
local function keyClk( key, state )
if not iAmATarget then return end
if not targetAllButtons and not targetButtons[key] then return end
net.start( "LKL_TargetedInput_InputClkFromNet" )
net.writeUInt( key, 10 )
net.writeBool( state )
net.writeFloat( curtime() )
net.send()
end
hook.add( "inputPressed", "LKL_TargetedInput_InputPressed", function( key )
if not isFirstTimePredicted() then return end
keyClk( key, true )
end )
hook.add( "inputReleased", "LKL_TargetedInput_InputReleased", function( key )
if not isFirstTimePredicted() then return end
keyClk( key, false )
end )
net.receive( "LKL_TargetedInput_SetTargets", function()
setTargets( net.readTable() )
end )
net.receive( "LKL_TargetedInput_SetTargetButtons", function()
local targetAll = net.readBool()
local newTargetButtons = net.readTable()
setTargetButtons( newTargetButtons, targetAll )
end )
end