· 8 years ago · Dec 03, 2017, 09:28 PM
1local userSearchRoot = createElement("userSearchRoot", "userSearchRoot")
2setElementData(userSearchRoot, "userList", {}, false)
3setElementData(userSearchRoot, "userSearch", {}, false)
4
5-- this event is triggered when a user search was successfully aborted
6addEvent("userSearchAborted", true)
7-- this event is triggered when the server sends a list of users to the client
8addEvent("userListReply", true)
9
10
11-- this function creates a new user search interface
12-- gridlistData is a table that contains information for the table in which the search results will be displayed
13-- gridlistData = { posX, posY, width, height }
14-- inputFieldData is a table that contains information for the input field in which the player can write the search pattern
15-- inputFieldData = { posX, posY, width, height, maxLength }
16-- submitButtonData is a table that contains information for the button which will submit the value of the input field to a callback function
17-- if no submitButtonData is given, no button will be created
18-- submitButtonData = { posX, posY, width, height, text, callBackFunction }
19function createUserSearch(gridlistData, inputFieldData, submitButtonData)
20
21
22 local occupiedIds, userSearchInterfaces = {}, getElementChildren(userSearchRoot)
23 for index, value in pairs(userSearchInterfaces) do
24 local occupiedId = getElementData(value, "id", false)
25 occupiedIds[occupiedId] = true
26 end
27 local count = 1
28 while true do
29 if occupiedIds[count] then
30 count = count + 1
31 else
32 break
33 end
34 end
35
36 local newSearchInterface = createElement("userSearchInterface", "userSearchInterface"..count)
37 setElementData(newSearchInterface, "id", count, false)
38
39 local gridList = guiCreateGridList(gridlistData.posX, gridlistData.posY + exports.FTAgui:getAbsoluteY(4), gridlistData.width, gridlistData.height - exports.FTAgui:getAbsoluteY(4), false)
40 guiSetProperty(gridList, "ZOrderChangeEnabled", "False")
41 guiGridListAddColumn(gridList, "Tag", 0.2)
42 guiGridListAddColumn(gridList, "Name", 0.6)
43 addEventHandler("onClientGUIClick", gridList, changeUsernameInputField)
44
45 local requestButton = exports.FTAgui:guiCreateButton(gridlistData.posX, gridlistData.posY + gridlistData.height - exports.FTAgui:getAbsoluteY(2.5), gridlistData.width, exports.FTAgui:getAbsoluteY(2.5), exports.FTAlanguage:translate("Request more"), false)
46 guiSetProperty(requestButton, "ZOrderChangeEnabled", "False")
47 addEventHandler("onClientGUIClick", requestButton, requestUserList)
48
49 local userlistLabel = guiCreateLabel(gridlistData.posX, gridlistData.posY, exports.FTAgui:getAbsoluteX(10), exports.FTAgui:getAbsoluteY(2.5), exports.FTAlanguage:translate("Userlist"), false)
50
51 local inputField = guiCreateEdit(inputFieldData.posX, inputFieldData.posY, inputFieldData.width, inputFieldData.height, "", false)
52 guiEditSetMaxLength(inputField, inputFieldData.maxLength)
53 addEventHandler("onClientGUIClick", inputField, deleteUserSearchGuiSourceText)
54
55 local submitButton
56 if submitButtonData then
57 submitButton = exports.FTAgui:guiCreateButton(submitButtonData.posX, submitButtonData.posY, submitButtonData.width, submitButtonData.height, submitButtonData.text, false)
58 guiSetEnabled(submitButton, false)
59 addEventHandler("onClientGUIClick", submitButton, submitButtonData.callBackFunction)
60 setElementData(newSearchInterface, "submitCallBackFunction", submitButtonData.callBackFunction, false)
61 end
62
63
64 local listedUsersCount = 0
65 --local userList = getElementData(userSearchRoot, "userList", false)
66 local userList = getElementsByType("player")
67 for index, value in pairs(userList) do
68
69 --if value ~= false then
70 local rowId = guiGridListAddRow(gridList)
71 local player_id = exports.FTaaccounts:getPlayerId(value)
72 if player_id then
73 local name = exports.FTAaccounts:getAccountName(player_id)
74 if name then
75 local _name = getPlayerName(value)
76 local tagValue = ""
77 guiGridListSetItemText(gridList, rowId, 1, tagValue, false, false)
78 guiGridListSetItemText(gridList, rowId, 2, name, false, false)
79 listedUsersCount = listedUsersCount + 1
80 end
81 end
82 --end
83 end
84 local rowId = guiGridListAddRow(gridList)
85 guiGridListSetItemText(gridList, rowId, 1, "", false, false)
86
87
88 local resultListLabel = guiCreateLabel(gridlistData.posX + exports.FTAgui:getAbsoluteX(10), gridlistData.posY, exports.FTAgui:getAbsoluteX(10), exports.FTAgui:getAbsoluteY(2.5), tostring(listedUsersCount).." of "..tostring(listedUsersCount), false)
89
90
91 local guiData = { gridList, requestButton, userlistLabel, inputField, submitButton, resultListLabel }
92 setElementData(newSearchInterface, "guiElements", guiData, false)
93
94
95 if getElementChildrenCount(userSearchRoot) == 0 then
96 addEventHandler("onClientRender", root, checkUserNameSearchInput)
97 end
98 setElementParent(newSearchInterface, userSearchRoot)
99
100 requestUserList()
101
102 return newSearchInterface
103end
104
105
106-- this function destroys the given user search interface
107function destroyUserSearch(userSearchElement)
108 if not isElement(userSearchElement) or getElementType(userSearchElement) ~= "userSearchInterface" then
109 return false
110 end
111
112 local guiData = getElementData(userSearchElement, "guiElements", false)
113
114 removeEventHandler("onClientGUIClick", guiData[1], changeUsernameInputField)
115
116 if isElement(guiData[2]) then
117 removeEventHandler("onClientGUIClick", guiData[2], requestUserList)
118 end
119
120 removeEventHandler("onClientGUIClick", guiData[4], deleteUserSearchGuiSourceText)
121 if isElement(guiData[5]) then
122 removeEventHandler("onClientGUIClick", guiData[5], getElementData(userSearchElement, "submitCallBackFunction", false))
123 end
124
125
126 for index, value in pairs(guiData) do
127 if isElement(value) then
128 destroyElement(value)
129 end
130 end
131
132 destroyElement(userSearchElement)
133
134 if getElementChildrenCount(userSearchRoot) == 0 then
135 removeEventHandler("onClientRender", root, checkUserNameSearchInput)
136 end
137end
138
139
140-- deletes the content of the input field in case that one of the search results is currently selected, when the input field gets clicked
141function deleteUserSearchGuiSourceText()
142 if not source then
143 return false
144 end
145 local searchInterfaces = getElementChildren(userSearchRoot)
146 for index, value in pairs(searchInterfaces) do
147 local guiData = getElementData(value, "guiElements", false)
148 if guiData[4] == source then
149 local row = guiGridListGetSelectedItem(guiData[1])
150 if row == -1 then
151 return true
152 else
153 return guiSetText(source, "")
154 end
155 break
156 end
157 end
158end
159
160-- this function changes the username input field content
161function changeUsernameInputField()
162 local searchInterfaces = getElementChildren(userSearchRoot)
163 for index, value in pairs(searchInterfaces) do
164 local guiData = getElementData(value, "guiElements", false)
165 if guiData[1] == source then
166 local row = guiGridListGetSelectedItem(source)
167 guiSetText(guiData[4], guiGridListGetItemText(source, row, 2))
168 break
169 end
170 end
171end
172
173-- this function requests the next unknown users from the server
174function requestUserList()
175 if not getElementData(userSearchRoot, "userListRequestPending", false) then
176 setElementData(userSearchRoot, "userListRequestPending", true, false)
177 for index, value in pairs(getElementChildren(userSearchRoot)) do
178 local guiData = getElementData(value, "guiElements", false)
179 if isElement(guiData[2]) then
180 guiSetText(guiData[2], "Requesting...")
181 guiSetEnabled(guiData[2], false)
182 end
183 end
184 else
185 return
186 end
187
188 local userList = getElementData(userSearchRoot, "userList", false)
189 local userSearch = getElementData(userSearchRoot, "userSearch", false)
190 local doAddEventHandler = true
191 for index, value in pairs(userSearch) do
192 if value.status == "requesting" or value.status == "aborting" then
193 doAddEventHandler = false
194 break
195 end
196 end
197
198 if doAddEventHandler then
199 addEventHandler("userListReply", root, onUserListReply)
200 end
201
202 local requestIds = {}
203 local count = 0
204 local idTest = 1
205 while count < 31 do
206 if userList[idTest] == nil then
207 requestIds[idTest] = idTest
208 count = count + 1
209 end
210 idTest = idTest + 1
211 end
212 triggerServerEvent("requestUserList", localPlayer, requestIds)
213end
214
215-- this function handles search requests in realtime
216function checkUserNameSearchInput()
217 if getElementChildrenCount(userSearchRoot) == 0 then
218 removeEventHandler("onClientRender", root, checkUserNameSearchInput)
219 return
220 end
221 local userSearch = getElementData(userSearchRoot, "userSearch", false)
222
223 for index, value in pairs(getElementChildren(userSearchRoot)) do
224 local guiData = getElementData(value, "guiElements", false)
225 local userName = guiGetText(guiData[4])
226 local sendNoRequest = false
227 local lastPattern = getElementData(value, "lastSearchPattern", false)
228 if lastPattern ~= userName then
229 setElementData(value, "lastSearchPattern", userName, false)
230 local userList = getElementData(userSearchRoot, "userList", false)
231 local knownUsersCount = 0
232 local listedUsersCount = 0
233 guiGridListClear(guiData[1])
234 if userName == "" then
235 for subIndex, subValue in pairs(userList) do
236 if subValue ~= false then
237 local row = guiGridListAddRow(guiData[1])
238 knownUsersCount = knownUsersCount + 1
239 local tagValue = ""
240 if subValue.tag ~= "" then
241 tagValue = "["..subValue.tag.."]"
242 end
243 guiGridListSetItemText(guiData[1], row, 1, tagValue, false, false)
244 guiGridListSetItemText(guiData[1], row, 2, subValue.name, false, false)
245 end
246 end
247
248 listedUsersCount = knownUsersCount
249 else
250
251 local pattern = string.upper(userName)
252
253 for subIndex, subValue in pairs(userList) do
254 if subValue ~= false then
255 knownUsersCount = knownUsersCount + 1
256 local patternHasTag = false
257 if string.sub(pattern, 1, 1) == "[" and string.find(pattern, "]", 1, true) then
258 patternHasTag = true
259 end
260 if listedUsersCount < 500 and (string.find(string.upper(subValue.name), pattern, 1, true) or (patternHasTag and subValue.tag ~= "" and string.find("["..string.upper(subValue.tag).."]"..string.upper(subValue.name), pattern, 1, true))) then
261 listedUsersCount = listedUsersCount + 1
262 local row = guiGridListAddRow(guiData[1])
263 local tagValue = ""
264 if subValue.tag ~= "" then
265 tagValue = "["..subValue.tag.."]"
266 end
267 guiGridListSetItemText(guiData[1], row, 1, tagValue, false, false)
268 guiGridListSetItemText(guiData[1], row, 2, subValue.name, false, false)
269 end
270 end
271 end
272 end
273
274 if isElement(guiData[2]) then
275 local row = guiGridListAddRow(guiData[1])
276 guiGridListSetItemText(guiData[1], row, 1, "", false, false)
277 end
278
279 if listedUsersCount == 500 then
280 sendNoRequest = true
281 end
282
283 guiSetText(guiData[6], tostring(listedUsersCount).." of "..tostring(knownUsersCount))
284
285 end
286
287 if userName == "" then
288 if isElement(guiData[5]) and guiGetEnabled(guiData[5]) then
289 guiSetEnabled(guiData[5], false)
290 end
291
292 local abort = {}
293 local abortRunning = false
294 for subIndex, subValue in pairs(userSearch) do
295 if subValue.status == "requesting" then
296 table.insert(abort, subIndex)
297 userSearch[subIndex].status = "aborting"
298 elseif subValue.status == "aborting" then
299 abortRunning = true
300 end
301 end
302 if #abort ~= 0 then
303 if not abortRunning then
304 addEventHandler("userSearchAborted", root, onUserSearchAborted)
305 end
306 triggerServerEvent("abortUserSearch", localPlayer, abort)
307 end
308
309 else
310
311 if isElement(guiData[5]) and not guiGetEnabled(guiData[5]) then
312 guiSetEnabled(guiData[5], true)
313 end
314
315 if not isElement(guiData[2]) or getElementData(value, "lastSearchPattern", false) ~= userName then
316 sendNoRequest = true
317 if getElementData(value, "lastSearchPattern", false) ~= userName then
318 setElementData(value, "sendRequestFrameCount", nil, false)
319 end
320 end
321 local doAddEventHandler = true
322
323
324 local abort = {}
325 local abortRunning = false
326 for subIndex, subValue in pairs(userSearch) do
327 if subValue.status == "requesting" then
328 if userName ~= subValue.pattern then
329 table.insert(abort, subIndex)
330 userSearch[subIndex].status = "aborting"
331 doAddEventHandler = false
332 else
333 sendNoRequest = true
334 end
335 elseif subValue.status == "aborting" then
336 abortRunning = true
337 doAddEventHandler = false
338 elseif (subValue.status == "finished" or subValue.status == "limit") and subValue.pattern == userName then
339 sendNoRequest = true
340 end
341 end
342 if #abort ~= 0 then
343 if not abortRunning then
344 addEventHandler("userSearchAborted", root, onUserSearchAborted)
345 end
346 triggerServerEvent("abortUserSearch", localPlayer, abort)
347 end
348
349 if not sendNoRequest then
350 local searchPattern = userName
351 if (not string.find(searchPattern, "[", 1, true) and not string.find(searchPattern, "]", 1, true)) or (searchPattern ~= "[]" and string.find(searchPattern, "[", 1, true) and string.find(searchPattern, "]", 1, true)) then
352
353 local upperSearchPattern = string.upper(searchPattern)
354 for subIndex, subValue in pairs(userSearch) do
355 if (subValue.status == "finished" or subValue.status == "requesting") and upperSearchPattern == subValue.pattern then
356 sendNoRequest = true
357 end
358 end
359
360 if not sendNoRequest then
361 local sendNextRequestFrameCounter = getElementData(value, "sendRequestFrameCount", false)
362 if not sendNextRequestFrameCounter or sendNextRequestFrameCounter < 50 then
363 if not sendNextRequestFrameCounter then
364 sendNextRequestFrameCounter = 0
365 end
366 setElementData(value, "sendRequestFrameCount", sendNextRequestFrameCounter + 1, false)
367 else
368 setElementData(value, "sendRequestFrameCount", nil, false)
369
370 local patternIndex
371 for subIndex, subValue in pairs(userSearch) do
372 if subValue.pattern == searchPattern then
373 userSearch[subIndex].status = "requesting"
374 patternIndex = subIndex
375 break
376 end
377 end
378
379 if not patternIndex then
380 table.insert(userSearch, { pattern = searchPattern, status = "requesting" })
381
382 for subIndex, subValue in pairs(userSearch) do
383 if subValue.pattern == searchPattern then
384 patternIndex = subIndex
385 break
386 end
387 end
388 end
389
390 if not getElementData(userSearchRoot, "userListRequestPending", false) and doAddEventHandler then
391 addEventHandler("userListReply", root, onUserListReply)
392 end
393
394 triggerServerEvent("startUserSearch", localPlayer, searchPattern, patternIndex)
395 end
396 end
397 end
398 end
399
400 end
401
402 end
403
404 setElementData(userSearchRoot, "userSearch", userSearch, false)
405end
406
407-- this function belongs to the userSearchAborted event
408function onUserSearchAborted(indexList)
409 local userSearch = getElementData(userSearchRoot, "userSearch", false)
410 for index, value in pairs(indexList) do
411 if userSearch[value] and userSearch[value].status == "aborting" then
412 userSearch[value].status = "aborted"
413 end
414 end
415 setElementData(userSearchRoot, "userSearch", userSearch, false)
416
417 local doRemoveEventHandler = true
418 for index, value in pairs(userSearch) do
419 if value.status == "aborting" then
420 doRemoveEventHandler = false
421 break
422 end
423 end
424 if doRemoveEventHandler then
425 removeEventHandler("userSearchAborted", root, onUserSearchAborted)
426 end
427
428 if not getElementData(userSearchRoot, "userListRequestPending", false) then
429 local doRemoveEventHandler = true
430 for index, value in pairs(userSearch) do
431 if value.status == "requesting" or value.status == "aborting" then
432 doRemoveEventHandler = false
433 end
434 end
435 if doRemoveEventHandler then
436 removeEventHandler("userListReply", root, onUserListReply)
437 end
438 end
439end
440
441-- this function is executed when the server sends a list of users to the client
442function onUserListReply(userList, moreUsersAvailable, indexOnEndOfSearch, searchLimitReached)
443 local userSearch = getElementData(userSearchRoot, "userSearch", false)
444 if type(indexOnEndOfSearch) == "number" then
445 if userSearch[indexOnEndOfSearch].status == "aborting" then
446 triggerServerEvent("userSearchAbortNotFailed", localPlayer, indexOnEndOfSearch)
447 end
448 if searchLimitReached then
449 userSearch[indexOnEndOfSearch].status = "limit"
450 else
451 userSearch[indexOnEndOfSearch].status = "finished"
452 end
453 setElementData(userSearchRoot, "userSearch", userSearch, false)
454 end
455
456
457 local deleteEventHandler = true
458 for index, value in pairs(userSearch) do
459 if value.status == "requesting" or value.status == "aborting" then
460 deleteEventHandler = false
461 break
462 end
463 end
464
465 if deleteEventHandler then
466 if getElementData(userSearchRoot, "userListRequestPending", false) then
467 setElementData(userSearchRoot, "userListRequestPending", nil, false)
468 end
469 removeEventHandler("userListReply", root, onUserListReply)
470 end
471
472
473 local userListCache = getElementData(userSearchRoot, "userList", false)
474 local knownUsersCount = 0
475 for index, value in pairs(userListCache) do
476 if value ~= false then
477 knownUsersCount = knownUsersCount + 1
478 end
479 end
480 local knownUsersCountCopy = knownUsersCount
481
482 for index, value in pairs(userList) do
483 if userListCache[index] == nil then
484 userListCache[index] = value
485 knownUsersCount = knownUsersCount + 1
486 else
487 userList[index] = false
488 end
489 end
490 setElementData(userSearchRoot, "userList", userListCache, false)
491
492 for index, value in pairs(getElementChildren(userSearchRoot)) do
493 local guiData = getElementData(value, "guiElements", false)
494 if isElement(guiData[2]) then
495 local rowId = guiGridListGetRowCount(guiData[1])
496 if rowId ~= 0 and guiGridListGetItemText(guiData[1], rowId - 1, 1) == "" then
497 guiGridListRemoveRow(guiData[1], rowId - 1)
498 end
499 end
500
501 local userName = guiGetText(guiData[4])
502 local listedUsersCount = guiGridListGetRowCount(guiData[1])
503
504 local upperUserName = string.upper(userName)
505
506 for subIndex, subValue in pairs(userList) do
507 if subValue ~= false then
508 if userName == "" or listedUsersCount <= 500 then
509 local patternHasTag = false
510 if string.sub(upperUserName, 1, 1) == "[" and string.find(upperUserName, "]", 1, true) then
511 patternHasTag = true
512 end
513 if userName == "" or string.find(string.upper(subValue.name), upperUserName, 1, true) or (patternHasTag and subValue.tag ~= "" and string.find("["..string.upper(subValue.tag).."]"..string.upper(subValue.name), upperUserName, 1, true)) then
514
515 local rowId = guiGridListAddRow(guiData[1])
516 local tagValue = ""
517 if subValue.tag ~= "" then
518 tagValue = "["..subValue.tag.."]"
519 end
520 guiGridListSetItemText(guiData[1], rowId, 1, tagValue, false, false)
521 guiGridListSetItemText(guiData[1], rowId, 2, subValue.name, false, false)
522
523 listedUsersCount = listedUsersCount + 1
524 end
525 elseif listedUsersCount == 500 then
526 local abortRunning = false
527 local abort = {}
528 for subSubIndex, subSubValue in pairs(userSearch) do
529 if subSubValue.pattern == userName then
530 if subSubValue.status == "requesting" then
531 subSubValue.status = "aborting"
532 table.insert(abort, subSubIndex)
533 end
534 end
535 if subSubValue.status == "aborting" then
536 abortRunning = true
537 end
538 end
539
540 setElementData(userSearchRoot, "userSearch", userSearch, false)
541
542 if #abort ~= 0 then
543 if not abortRunning then
544 addEventHandler("userSearchAborted", root, onUserSearchAborted)
545 end
546 triggerServerEvent("abortUserSearch", localPlayer, abort)
547 end
548 break
549 end
550 end
551 end
552
553 guiSetText(guiData[6], tostring(listedUsersCount).." of "..tostring(knownUsersCount))
554 if not moreUsersAvailable then
555 removeEventHandler("onClientGUIClick", guiData[2], requestUserList)
556 destroyElement(guiData[2])
557 else
558 local rowId = guiGridListAddRow(guiData[1])
559 guiGridListSetItemText(guiData[1], rowId, 1, "", false, false)
560 guiSetText(guiData[2], "Request more")
561 guiSetEnabled(guiData[2], true)
562 end
563
564
565
566 end
567
568
569 if knownUsersCountCopy == knownUsersCount and moreUsersAvailable and not indexOnEndOfSearch then
570 -- all the requested users are users that were already deleted. so request some more...
571 requestUserList()
572 end
573end
574
575-- this function removes the clan tag from a user listed in the search engine's user list and adjusts the gui if it's opened
576function adjustUserSearchGuiAfterClanLeave(playerId)
577 local userList = getElementData(userSearchRoot, "userList", false)
578 if userList[playerId] then
579 local clanTag = userList[playerId].tag
580 userList[playerId].tag = ""
581 setElementData(userSearchRoot, "userList", userList, false)
582
583 for index, value in pairs(getElementChildren(userSearchRoot)) do
584 local guiData = getElementData(value, "guiElements", false)
585 local searchPattern = string.upper(guiGetText(guiData[4]))
586
587 if searchPattern == "" or string.find(string.upper(userList[playerId].name), searchPattern, 1, true) then
588 -- search for the record. if it exists, edit it, else create a new one
589 local entryCount = guiGridListGetRowCount(guiData[1])
590 local recordRow
591 for count = 0, entryCount - 1 do
592 local cellString = guiGridListGetItemText(guiData[1], count, 2)
593 if cellString == userList[playerId].name then
594 recordRow = count
595 break
596 end
597 end
598
599 if recordRow then
600 guiGridListSetItemText(guiData[1], recordRow, 1, "", false, false)
601 else
602 if isElement(guiData[2]) and guiGridListGetItemText(guiData[1], entryCount - 1, 1) == "" then
603 guiGridListRemoveRow(guiData[1], entryCount - 1)
604 entryCount = entryCount - 1
605 end
606
607 if searchPattern == "" or entryCount < 500 then
608 local rowId = guiGridListAddRow(guiData[1])
609 guiGridListSetItemText(guiData[1], rowId, 1, "", false, false)
610 guiGridListSetItemText(guiData[1], rowId, 2, userList[playerId].name, false, false)
611 local knownUsersCount = 0
612 for subIndex, subValue in pairs(userList) do
613 knownUsersCount = knownUsersCount + 1
614 end
615 guiSetText(guiData[6], tostring(entryCount + 1).." of "..tostring(knownUsersCount))
616 end
617
618 if isElement(guiData[2]) then
619 local rowId = guiGridListAddRow(guiData[1])
620 guiGridListSetItemText(guiData[1], rowId, 1, "", false, false)
621 end
622
623 end
624 else
625 -- check if there's a record for the user, if so delete it
626 local entryCount = guiGridListGetRowCount(guiData[1])
627 for count = 0, entryCount - 1 do
628 local cellString = guiGridListGetItemText(guiData[1], count, 2)
629 if cellString == userList[playerId].name then
630
631 guiGridListRemoveRow(guiData[1], count)
632
633 local knownUsersCount = 0
634 for subIndex, subValue in pairs(userList) do
635 knownUsersCount = knownUsersCount + 1
636 end
637 guiSetText(guiData[6], tostring(entryCount - 1).." of "..tostring(knownUsersCount))
638 break
639 end
640 end
641 end
642 end
643 end
644end