forked from AkadenTK/superwarp
-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathsendall.lua
More file actions
356 lines (315 loc) · 9.97 KB
/
sendall.lua
File metadata and controls
356 lines (315 loc) · 9.97 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
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
require('tables')
local texts = require('texts')
local screen_w = windower.get_windower_settings().ui_x_res
local screen_h = windower.get_windower_settings().ui_y_res
local status_box = texts.new({
pos = {x = math.floor(screen_w / 3), y = math.floor(screen_h / 1.75)},
text = {font = 'Consolas', size = 10,
stroke = {
alpha = 255,
red = 0,
green = 0,
blue = 0,
width = 1,
padding = 1
}
},
bg = {alpha = 190, red = 0, green = 75, blue = 95},
flags = {right = false, bottom = false},
})
status_box:hide()
-- =========================
-- Vars
-- =========================
local scans = {}
local scan_counter = 0
local last_known_count = 1
local max_timeout = 0.15
local quiet_time = 0.03
local pending_confirms = {}
local active_warp_id = nil
current_warp_id = nil
warp_sender = nil
local spinner_frames = {'|', '/', '-', '\\'}
local spinner_index = 1
-- =========================
-- Helper functionators
-- =========================
local function get_spinner()
local frame = spinner_frames[spinner_index]
spinner_index = (spinner_index % #spinner_frames) + 1
return frame
end
local function get_player_name()
local me = windower.ffxi.get_mob_by_target('me')
return me and me.name
end
local function make_id()
return tostring(os.clock()) .. '_' .. tostring(math.random(1000,9999))
end
local function exec(participant, msg)
windower.send_ipc_message('execute '..participant..' '..msg)
end
local function get_missing_players(entry)
local missing = {}
if entry then
for _, name in ipairs(entry.expected) do
if not entry.received[name] then
missing[#missing+1] = name
end
end
end
return missing
end
-- =========================
-- Scan System
-- =========================
local function start_scan()
scan_counter = scan_counter + 1
local id = scan_counter
local player = get_player_name()
if not player then return nil end
scans[id] = {
participants = {[player] = true}, -- dedup via keys
start_time = os.clock(),
}
windower.send_ipc_message('marco '..player..' '..id)
return id
end
function get_participants()
local id = start_scan()
if not id then return T{} end
if last_known_count >= 7 then quiet_time = 0.06 -- about 6 passes; Increase required time since polo for larger multibox systems.
elseif last_known_count <= 6 then quiet_time = 0.03 end --atleast 3 passes.
local start = os.clock()
local last_increase = os.clock()
local last_count = 0
while os.clock() - start < max_timeout do
local scan = scans[id]
if not scan then break end
-- count participants
local count = 0
for _ in pairs(scan.participants) do
count = count + 1
end
-- we detect the increase
if count > last_count then
last_count = count
last_increase = os.clock()
end
-- early return if no new participants for quiet_time
if os.clock() - last_increase >= quiet_time then
local result = T{}
for name,_ in pairs(scan.participants) do
result:append(name)
end
last_known_count = math.max(last_known_count, count)
scans[id] = nil
return result
end
coroutine.sleep(0.01)
end
-- If we experience an unlikely scenario where responses have came in with delay we build the list after max timeout here.
local scan = scans[id]
local result = T{}
if scan then
local count = 0
for name,_ in pairs(scan.participants) do
result:append(name)
count = count + 1
end
last_known_count = math.max(last_known_count, count)
scans[id] = nil
end
return result
end
function receive_send_all(msg)
print('receive_send_all not overridden! msg: '..msg)
end
-- =========================
-- Send All
-- =========================
function reset_all(delay, participants)
if participants == nil then
participants = get_participants()
end
local total_delay = 0
local me = get_player_name()
for _,c in ipairs(participants) do
if c == me then
reset:schedule(total_delay)
else
windower.send_ipc_message('reset '..c..' '..total_delay)
end
total_delay = total_delay + delay
end
end
function send_all(msg, delay, participants)
if participants == nil then
participants = get_participants()
end
local total_delay = 0
local me = get_player_name()
for _,c in ipairs(participants) do
if c == me then
receive_send_all:schedule(total_delay, msg)
else
exec:schedule(total_delay, c, msg)
end
total_delay = total_delay + delay
end
end
function send_all_with_confirm(msg, delay, participants, on_complete, readout)
if participants == nil then
participants = get_participants()
end
warp_sender = windower.ffxi.get_mob_by_target('me').name
local warp_id = make_id()
active_warp_id, current_warp_id = warp_id, warp_id
pending_confirms[warp_id] = {
expected = participants,
received = {},
callback = on_complete
}
local total_delay = 0
local me = get_player_name()
for _,c in ipairs(participants) do
if c == me then
coroutine.schedule(function()
receive_send_all(msg)
end, total_delay)
else
coroutine.schedule(function()
windower.send_ipc_message('execute '..warp_id..' '..c..' '..msg)
end, total_delay)
end
total_delay = total_delay + delay
end
-- start completion watcher
coroutine.schedule(function()
local timeoutinator = os.clock() + 16
local complete = true
while pending_confirms[warp_id] and active_warp_id == warp_id and os.clock() < timeoutinator do
local entry = pending_confirms[warp_id]
if readout then
local missing = entry and get_missing_players(entry)
if #missing > 0 then
status_box:text(
string.format(
' |superwarp| %s \n----------------\nWaiting on (%d/%d)\n %s ',
get_spinner(),
#missing,
#entry.expected,
table.concat(missing, '\n ')
)
)
status_box:show()
else
status_box:hide()
end
end
complete = true
for _,name in ipairs(entry.expected) do
if not entry.received[name] then
complete = false
break
end
end
if complete then
if entry.callback then
entry.callback(warp_id, entry)
end
status_box:hide()
pending_confirms[warp_id] = nil
active_warp_id = nil
break
end
coroutine.sleep(0.1)
end
if not complete then
local message = table.concat(get_missing_players(pending_confirms[warp_id]), ', ')
if #message < 1 then
log('Failed to warp.')
else
log('Failed to warp '..message)
end
end
status_box:hide()
pending_confirms = {}
active_warp_id = nil
end, 0)
end
function warp_listener(confirm, player)
if confirm and player == warp_sender and pending_confirms[current_warp_id] then
local warp_id = current_warp_id
local entry = pending_confirms[warp_id]
entry.received[player] = true
else
active_warp_id = make_id()
pending_confirms = {}
end
end
windower.register_event('ipc message', function(msg)
local args = msg:split(' ')
local cmd = args[1]
args:remove(1)
local player = get_player_name()
if cmd == 'marco' then
local sender = args[1]
local id = tonumber(args[2])
if id then
if player then
windower.send_ipc_message('polo '..player..' '..id)
else
coroutine.schedule(function()
local retry_player = get_player_name()
if retry_player then
windower.send_ipc_message('polo '..retry_player..' '..id)
end
end, 0.1)
end
end
elseif cmd == 'polo' then
local name = args[1]
local id = tonumber(args[2])
local scan = id and scans[id]
if scan and name then
scan.participants[name] = true -- dedup safe
end
elseif cmd == 'confirm' then
local warp_id = args[1]
local name = args[2]
local entry = pending_confirms[warp_id]
if entry and name then
entry.received[name] = true
end
elseif cmd == 'sync_limbus_chests' then
local chestdata = args[1]
if chestdata then
local success, chest_table = pcall(function()
return loadstring('return '..chestdata)()
end)
if success and type(chest_table) == 'table' then
sync_chest_data(chest_table)
end
end
elseif cmd == 'execute' then
local warp_id = args[1]
local target = args[2]
if player and target == player then
args:remove(1)
args:remove(1)
local msg = args:concat(' ')
warp_sender = nil
current_warp_id = warp_id
-- execute command
receive_send_all(msg)
end
elseif cmd == 'reset' then
local target = args[1]
local delayer = args[2]
if player and target == player then
reset:schedule(delayer)
end
end
end)