function tvRP.teleport(x,y,z)
tvRP.unjail() -- force unjail before a teleportation
SetEntityCoords(GetPlayerPed(-1), x+0.0001, y+0.0001, z+0.0001, 1,0,0,1)
vRPserver.updatePos({x,y,z})
end
-- return x,y,z
function tvRP.getPosition()
local x,y,z = table.unpack(GetEntityCoords(GetPlayerPed(-1),true))
return x,y,z
end
function tvRP.serStamina(value)
local x,y,z = table.unpack(GetEntityCoords(GetPlayerPed(-1),true))
return x,y,z
end
-- return false if in exterior, true if inside a building
function tvRP.isInside()
local x,y,z = tvRP.getPosition()
return not (GetInteriorAtCoords(x,y,z) == 0)
end
-- return vx,vy,vz
function tvRP.getSpeed()
local vx,vy,vz = table.unpack(GetEntityVelocity(GetPlayerPed(-1)))
return math.sqrt(vx*vx+vy*vy+vz*vz)
end
function tvRP.getCamDirection()
local heading = GetGameplayCamRelativeHeading()+GetEntityHeading(GetPlayerPed(-1))
local pitch = GetGameplayCamRelativePitch()
local x = -math.sin(heading*math.pi/180.0)
local y = math.cos(heading*math.pi/180.0)
local z = math.sin(pitch*math.pi/180.0)
-- normalize
local len = math.sqrt(x*x+y*y+z*z)
if len ~= 0 then
x = x/len
y = y/len
z = z/len
end
return x,y,z
end
function tvRP.addPlayer(player)
players[player] = true
end
function tvRP.removePlayer(player)
players[player] = nil
end
function tvRP.getNearestPlayers(radius)
local r = {}
local ped = GetPlayerPed(i)
local pid = PlayerId()
local px,py,pz = tvRP.getPosition()
for k,v in pairs(players) do
local player = GetPlayerFromServerId(k)
if v and player ~= pid and NetworkIsPlayerConnected(player) then
local oped = GetPlayerPed(player)
local x,y,z = table.unpack(GetEntityCoords(oped,true))
local distance = GetDistanceBetweenCoords(x,y,z,px,py,pz,true)
if distance <= radius then
r[GetPlayerServerId(player)] = distance
end
end
end
return r
end
function tvRP.getNearestPlayer(radius)
local p = nil
local players = tvRP.getNearestPlayers(radius)
local min = radius+10.0
for k,v in pairs(players) do
if v < min then
min = v
p = k
end
end
return p
end
function tvRP.notify(msg)
SetNotificationTextEntry("STRING")
AddTextComponentString(msg)
DrawNotification(true, false)
--exports.pNotify:SendNotification({text = msg, type = "info", timeout = 4000, layout = "centerRight", queue = "right"})
end
-- SCREEN
-- play a screen effect
-- name, see https://w...content-available-to-author-only...m.net/wiki/Screen_Effects
-- duration: in seconds, if -1, will play until stopScreenEffect is called
function tvRP.playScreenEffect(name, duration)
if duration < 0 then -- loop
StartScreenEffect(name, 0, true)
else
StartScreenEffect(name, 0, true)
Citizen.CreateThread(function() -- force stop the screen effect after duration+1 seconds
Citizen.Wait(math.floor((duration+1)*1000))
StopScreenEffect(name)
end)
end
end
-- stop a screen effect
-- name, see https://w...content-available-to-author-only...m.net/wiki/Screen_Effects
function tvRP.stopScreenEffect(name)
StopScreenEffect(name)
end
-- ANIM
-- animations dict and names: http://d...content-available-to-author-only...k.net/html/62951c37-a440-478c-b389-c471230ddfc5.htm
local anims = {}
local anim_ids = Tools.newIDGenerator()
-- play animation (new version)
-- upper: true, only upper body, false, full animation
-- seq: list of animations as {dict,anim_name,loops} (loops is the number of loops, default 1) or a task def (properties: task, play_exit)
-- looping: if true, will infinitely loop the first element of the sequence until stopAnim is called
function tvRP.playAnim(upper, seq, looping)
if seq.task ~= nil then -- is a task (cf https://g...content-available-to-author-only...b.com/ImagicTheCat/vRP/pull/118)
DeleteEntity(phoneProp)
tvRP.stopAnim(true)
local ped = GetPlayerPed(-1)
if seq.task == "PROP_HUMAN_SEAT_CHAIR_MP_PLAYER" then -- special case, sit in a chair
local x,y,z = tvRP.getPosition()
TaskStartScenarioAtPosition(ped, seq.task, x, y, z-1, GetEntityHeading(ped), 0, 0, false)
else
TaskStartScenarioInPlace(ped, seq.task, 0, not seq.play_exit)
end
else -- a regular animation sequence
tvRP.stopAnim(upper)
local flags = 0
if upper then flags = flags+48 end
if looping then flags = flags+1 end
Citizen.CreateThread(function()
-- prepare unique id to stop sequence when needed
local id = anim_ids:gen()
anims[id] = true
for k,v in pairs(seq) do
local dict = v[1]
local name = v[2]
local loops = v[3] or 1
for i=1,loops do
if anims[id] then -- check animation working
local first = (k == 1 and i == 1)
local last = (k == #seq and i == loops)
-- request anim dict
RequestAnimDict(dict)
local i = 0
while not HasAnimDictLoaded(dict) and i < 1000 do -- max time, 10 seconds
Citizen.Wait(10)
RequestAnimDict(dict)
i = i+1
end
-- play anim
if HasAnimDictLoaded(dict) and anims[id] then
local inspeed = 8.0001
local outspeed = -8.0001
if not first then inspeed = 2.0001 end
if not last then outspeed = 2.0001 end
TaskPlayAnim(GetPlayerPed(-1),dict,name,inspeed,outspeed,-1,flags,0,0,0,0)
end
Citizen.Wait(0)
while GetEntityAnimCurrentTime(GetPlayerPed(-1),dict,name) <= 0.95 and IsEntityPlayingAnim(GetPlayerPed(-1),dict,name,3) and anims[id] do
Citizen.Wait(0)
end
end
end
end
-- free id
anim_ids:free(id)
anims[id] = nil
end)
end
end
-- stop animation (new version)
-- upper: true, stop the upper animation, false, stop full animations
function tvRP.stopAnim(upper)
anims = {} -- stop all sequences
if upper then
ClearPedSecondaryTask(GetPlayerPed(-1))
else
ClearPedTasks(GetPlayerPed(-1))
end
end
-- RAGDOLL
local ragdoll = false
-- set player ragdoll flag (true or false)
function tvRP.setRagdoll(flag)
ragdoll = flag
end
-- ragdoll thread
Citizen.CreateThread(function()
while true do
Citizen.Wait(10)
if ragdoll then
SetPedToRagdoll(GetPlayerPed(-1), 1000, 1000, 0, 0, 0, 0)
end
end
end)
-- SOUND
-- some lists:
-- pastebin.com/A8Ny8AHZ
-- https://w...content-available-to-author-only...t.work/index.php?title=FrontEndSoundlist
-- play sound at a specific position
function tvRP.playSpatializedSound(dict,name,x,y,z,range)
PlaySoundFromCoord(-1,name,x+0.0001,y+0.0001,z+0.0001,dict,0,range+0.0001,0)
end
-- play sound
function tvRP.playSound(dict,name)
PlaySound(-1,name,dict,0,0,1)
end
function tvRP.setDrunk(player)
SetPedIsDrunk(player, true)
SetPedMovementClipset(player, "MOVE_M@DRUNK@SLIGHTLYDRUNK", true)
SetTimeout(60000, function()
ResetPedMovementClipset(player, 0)
SetPedIsDrunk(player, false)
end)
end
function tvRP.setDrugged(player)
DoScreenFadeOut(500)
Citizen.Wait(500)
--vRPclient.playScreenEffect("DrugsDrivingIn", 60)
SetTimecycleModifier("spectator5")
SetPedMotionBlur(player, true)
Citizen.Wait(500)
DoScreenFadeIn(1500)
SetTimeout(60000, function()
SetPedMotionBlur(player, false)
ClearTimecycleModifier()
end)
end
function tvRP.setSpeedboost(player)
SetRunSprintMultiplierForPlayer(player, 1.2)
SetSwimMultiplierForPlayer(player, 1.2)
SetTimeout(120000, function()
SetRunSprintMultiplierForPlayer(player, 1.0)
SetSwimMultiplierForPlayer(player, 1.0)
end)
end
function tvRP.setStaminaboost(player)
local staminaboost = true
while (staminaboost == true) do
RestorePlayerStamina(player, 1.0)
end
SetTimeout(120000, function()
staminaboost = false
end)
end
-- events
AddEventHandler("playerSpawned",function()
TriggerServerEvent("vRPcli:playerSpawned")
end)
AddEventHandler("onPlayerDied",function(player,reason)
TriggerServerEvent("vRPcli:playerDied")
end)
AddEventHandler("onPlayerKilled",function(player,killer,reason)
TriggerServerEvent("vRPcli:playerDied")
end)
function tvRP.useLockpick(player)
Citizen.CreateThread(function()
Citizen.Wait(1)
local plyCoords = GetEntityCoords(GetPlayerPed(-1), 0)
veh = GetClosestVehicle(plyCoords["x"], plyCoords["y"], plyCoords["z"], 5.001, 0, 70)
if veh then
TaskPlayAnim(GetPlayerPed(-1),"mini@repair","fixing_a_player", 8.0, 0.0, -1, 1, 0, 0, 0, 0)
StartVehicleAlarm(veh)
Citizen.Wait(20000)
SetVehicleDoorsLocked(veh, 1)
ClearPedTasksImmediately(GetPlayerPed(-1))
end
end)
end
local voice = "base"
local voicetitle = "обычная"
-- voice proximity computation
Citizen.CreateThread(function()
while true do
Citizen.Wait(1)
local proximity = 20.0
if IsControlJustPressed(1, 182) then
if voice == "base" then
voice = "short"
voicetitle = "ближняя"
proximity = 5.0
else
voice = "base"
voicetitle = "обычная"
proximity = 20.0
end
end
SetTextFont(0)
SetTextProportional(1)
SetTextScale(0.0, 0.30)
SetTextColour(255, 255, 255, 255)
SetTextDropshadow(0, 0, 0, 0, 255)
SetTextEdge(1, 0, 0, 0, 255)
SetTextDropShadow()
SetTextOutline()
SetTextEntry("STRING")
AddTextComponentString("Слышимость: "..voicetitle)
DrawText(0.15, 0.94)
if IsPedSittingInAnyVehicle(ped) then
local veh = GetVehiclePedIsIn(ped,false)
local hash = GetEntityModel(veh)
if IsThisModelACar(hash) or IsThisModelAHeli(hash) or IsThisModelAPlane(hash) then
if voice == "short" then
proximity = 5.0
else
proximity = 10.0
end
end
elseif tvRP.isInside() then
if voice == "short" then
proximity = 3.0
else
proximity = 7.0
end
end
NetworkSetTalkerProximity(proximity+0.0001)
end
end)
Citizen.CreateThread(function()
while true do
Citizen.Wait(1)
local ped = GetPlayerPed(-1)
local radius = 5
local x,y,z = table.unpack(GetEntityCoords(ped,true))
local veh = GetClosestVehicle(x+0.0001,y+0.0001,z+0.0001, radius+0.0001, 0, 8192+4096+4+2+1) -- boats, helicos
if not IsEntityAVehicle(veh) then veh = GetClosestVehicle(x+0.0001,y+0.0001,z+0.0001, radius+0.0001, 0, 4+2+1) end -- cars
if veh ~= false then
SetVehicleAutomaticallyAttaches(veh, 1, 0)
end
end
end)
CgpmdW5jdGlvbiB0dlJQLnRlbGVwb3J0KHgseSx6KQogIHR2UlAudW5qYWlsKCkgLS0gZm9yY2UgdW5qYWlsIGJlZm9yZSBhIHRlbGVwb3J0YXRpb24KICBTZXRFbnRpdHlDb29yZHMoR2V0UGxheWVyUGVkKC0xKSwgeCswLjAwMDEsIHkrMC4wMDAxLCB6KzAuMDAwMSwgMSwwLDAsMSkKICB2UlBzZXJ2ZXIudXBkYXRlUG9zKHt4LHksen0pCmVuZAoKLS0gcmV0dXJuIHgseSx6CmZ1bmN0aW9uIHR2UlAuZ2V0UG9zaXRpb24oKQogIGxvY2FsIHgseSx6ID0gdGFibGUudW5wYWNrKEdldEVudGl0eUNvb3JkcyhHZXRQbGF5ZXJQZWQoLTEpLHRydWUpKQogIHJldHVybiB4LHksegplbmQKCmZ1bmN0aW9uIHR2UlAuc2VyU3RhbWluYSh2YWx1ZSkKICBsb2NhbCB4LHkseiA9IHRhYmxlLnVucGFjayhHZXRFbnRpdHlDb29yZHMoR2V0UGxheWVyUGVkKC0xKSx0cnVlKSkKICByZXR1cm4geCx5LHoKZW5kCgoKLS0gcmV0dXJuIGZhbHNlIGlmIGluIGV4dGVyaW9yLCB0cnVlIGlmIGluc2lkZSBhIGJ1aWxkaW5nCmZ1bmN0aW9uIHR2UlAuaXNJbnNpZGUoKQogIGxvY2FsIHgseSx6ID0gdHZSUC5nZXRQb3NpdGlvbigpCiAgcmV0dXJuIG5vdCAoR2V0SW50ZXJpb3JBdENvb3Jkcyh4LHkseikgPT0gMCkKZW5kCgotLSByZXR1cm4gdngsdnksdnoKZnVuY3Rpb24gdHZSUC5nZXRTcGVlZCgpCiAgbG9jYWwgdngsdnksdnogPSB0YWJsZS51bnBhY2soR2V0RW50aXR5VmVsb2NpdHkoR2V0UGxheWVyUGVkKC0xKSkpCiAgcmV0dXJuIG1hdGguc3FydCh2eCp2eCt2eSp2eSt2eip2eikKZW5kCgpmdW5jdGlvbiB0dlJQLmdldENhbURpcmVjdGlvbigpCiAgbG9jYWwgaGVhZGluZyA9IEdldEdhbWVwbGF5Q2FtUmVsYXRpdmVIZWFkaW5nKCkrR2V0RW50aXR5SGVhZGluZyhHZXRQbGF5ZXJQZWQoLTEpKQogIGxvY2FsIHBpdGNoID0gR2V0R2FtZXBsYXlDYW1SZWxhdGl2ZVBpdGNoKCkKCiAgbG9jYWwgeCA9IC1tYXRoLnNpbihoZWFkaW5nKm1hdGgucGkvMTgwLjApCiAgbG9jYWwgeSA9IG1hdGguY29zKGhlYWRpbmcqbWF0aC5waS8xODAuMCkKICBsb2NhbCB6ID0gbWF0aC5zaW4ocGl0Y2gqbWF0aC5waS8xODAuMCkKCiAgLS0gbm9ybWFsaXplCiAgbG9jYWwgbGVuID0gbWF0aC5zcXJ0KHgqeCt5Knkreip6KQogIGlmIGxlbiB+PSAwIHRoZW4KICAgIHggPSB4L2xlbgogICAgeSA9IHkvbGVuCiAgICB6ID0gei9sZW4KICBlbmQKCiAgcmV0dXJuIHgseSx6CmVuZAoKZnVuY3Rpb24gdHZSUC5hZGRQbGF5ZXIocGxheWVyKQogIHBsYXllcnNbcGxheWVyXSA9IHRydWUKZW5kCgpmdW5jdGlvbiB0dlJQLnJlbW92ZVBsYXllcihwbGF5ZXIpCiAgcGxheWVyc1twbGF5ZXJdID0gbmlsCmVuZAoKZnVuY3Rpb24gdHZSUC5nZXROZWFyZXN0UGxheWVycyhyYWRpdXMpCiAgbG9jYWwgciA9IHt9CgogIGxvY2FsIHBlZCA9IEdldFBsYXllclBlZChpKQogIGxvY2FsIHBpZCA9IFBsYXllcklkKCkKICBsb2NhbCBweCxweSxweiA9IHR2UlAuZ2V0UG9zaXRpb24oKQoKICBmb3Igayx2IGluIHBhaXJzKHBsYXllcnMpIGRvCiAgICBsb2NhbCBwbGF5ZXIgPSBHZXRQbGF5ZXJGcm9tU2VydmVySWQoaykKCiAgICBpZiB2IGFuZCBwbGF5ZXIgfj0gcGlkIGFuZCBOZXR3b3JrSXNQbGF5ZXJDb25uZWN0ZWQocGxheWVyKSB0aGVuCiAgICAgIGxvY2FsIG9wZWQgPSBHZXRQbGF5ZXJQZWQocGxheWVyKQogICAgICBsb2NhbCB4LHkseiA9IHRhYmxlLnVucGFjayhHZXRFbnRpdHlDb29yZHMob3BlZCx0cnVlKSkKICAgICAgbG9jYWwgZGlzdGFuY2UgPSBHZXREaXN0YW5jZUJldHdlZW5Db29yZHMoeCx5LHoscHgscHkscHosdHJ1ZSkKICAgICAgaWYgZGlzdGFuY2UgPD0gcmFkaXVzIHRoZW4KICAgICAgICByW0dldFBsYXllclNlcnZlcklkKHBsYXllcildID0gZGlzdGFuY2UKICAgICAgZW5kCiAgICBlbmQKICBlbmQKCiAgcmV0dXJuIHIKZW5kCgpmdW5jdGlvbiB0dlJQLmdldE5lYXJlc3RQbGF5ZXIocmFkaXVzKQogIGxvY2FsIHAgPSBuaWwKCiAgbG9jYWwgcGxheWVycyA9IHR2UlAuZ2V0TmVhcmVzdFBsYXllcnMocmFkaXVzKQogIGxvY2FsIG1pbiA9IHJhZGl1cysxMC4wCiAgZm9yIGssdiBpbiBwYWlycyhwbGF5ZXJzKSBkbwogICAgaWYgdiA8IG1pbiB0aGVuCiAgICAgIG1pbiA9IHYKICAgICAgcCA9IGsKICAgIGVuZAogIGVuZAoKICByZXR1cm4gcAplbmQKCmZ1bmN0aW9uIHR2UlAubm90aWZ5KG1zZykKICBTZXROb3RpZmljYXRpb25UZXh0RW50cnkoIlNUUklORyIpCiAgQWRkVGV4dENvbXBvbmVudFN0cmluZyhtc2cpCiAgRHJhd05vdGlmaWNhdGlvbih0cnVlLCBmYWxzZSkKICAtLWV4cG9ydHMucE5vdGlmeTpTZW5kTm90aWZpY2F0aW9uKHt0ZXh0ID0gbXNnLCB0eXBlID0gImluZm8iLCB0aW1lb3V0ID0gNDAwMCwgbGF5b3V0ID0gImNlbnRlclJpZ2h0IiwgcXVldWUgPSAicmlnaHQifSkKZW5kCgotLSBTQ1JFRU4KCi0tIHBsYXkgYSBzY3JlZW4gZWZmZWN0Ci0tIG5hbWUsIHNlZSBodHRwczovL3cuLi5jb250ZW50LWF2YWlsYWJsZS10by1hdXRob3Itb25seS4uLm0ubmV0L3dpa2kvU2NyZWVuX0VmZmVjdHMKLS0gZHVyYXRpb246IGluIHNlY29uZHMsIGlmIC0xLCB3aWxsIHBsYXkgdW50aWwgc3RvcFNjcmVlbkVmZmVjdCBpcyBjYWxsZWQKZnVuY3Rpb24gdHZSUC5wbGF5U2NyZWVuRWZmZWN0KG5hbWUsIGR1cmF0aW9uKQogIGlmIGR1cmF0aW9uIDwgMCB0aGVuIC0tIGxvb3AKICAgIFN0YXJ0U2NyZWVuRWZmZWN0KG5hbWUsIDAsIHRydWUpCiAgZWxzZQogICAgU3RhcnRTY3JlZW5FZmZlY3QobmFtZSwgMCwgdHJ1ZSkKCiAgICBDaXRpemVuLkNyZWF0ZVRocmVhZChmdW5jdGlvbigpIC0tIGZvcmNlIHN0b3AgdGhlIHNjcmVlbiBlZmZlY3QgYWZ0ZXIgZHVyYXRpb24rMSBzZWNvbmRzCiAgICAgIENpdGl6ZW4uV2FpdChtYXRoLmZsb29yKChkdXJhdGlvbisxKSoxMDAwKSkKICAgICAgU3RvcFNjcmVlbkVmZmVjdChuYW1lKQogICAgZW5kKQogIGVuZAplbmQKCi0tIHN0b3AgYSBzY3JlZW4gZWZmZWN0Ci0tIG5hbWUsIHNlZSBodHRwczovL3cuLi5jb250ZW50LWF2YWlsYWJsZS10by1hdXRob3Itb25seS4uLm0ubmV0L3dpa2kvU2NyZWVuX0VmZmVjdHMKZnVuY3Rpb24gdHZSUC5zdG9wU2NyZWVuRWZmZWN0KG5hbWUpCiAgU3RvcFNjcmVlbkVmZmVjdChuYW1lKQplbmQKCi0tIEFOSU0KCi0tIGFuaW1hdGlvbnMgZGljdCBhbmQgbmFtZXM6IGh0dHA6Ly9kLi4uY29udGVudC1hdmFpbGFibGUtdG8tYXV0aG9yLW9ubHkuLi5rLm5ldC9odG1sLzYyOTUxYzM3LWE0NDAtNDc4Yy1iMzg5LWM0NzEyMzBkZGZjNS5odG0KCmxvY2FsIGFuaW1zID0ge30KbG9jYWwgYW5pbV9pZHMgPSBUb29scy5uZXdJREdlbmVyYXRvcigpCgotLSBwbGF5IGFuaW1hdGlvbiAobmV3IHZlcnNpb24pCi0tIHVwcGVyOiB0cnVlLCBvbmx5IHVwcGVyIGJvZHksIGZhbHNlLCBmdWxsIGFuaW1hdGlvbgotLSBzZXE6IGxpc3Qgb2YgYW5pbWF0aW9ucyBhcyB7ZGljdCxhbmltX25hbWUsbG9vcHN9IChsb29wcyBpcyB0aGUgbnVtYmVyIG9mIGxvb3BzLCBkZWZhdWx0IDEpIG9yIGEgdGFzayBkZWYgKHByb3BlcnRpZXM6IHRhc2ssIHBsYXlfZXhpdCkKLS0gbG9vcGluZzogaWYgdHJ1ZSwgd2lsbCBpbmZpbml0ZWx5IGxvb3AgdGhlIGZpcnN0IGVsZW1lbnQgb2YgdGhlIHNlcXVlbmNlIHVudGlsIHN0b3BBbmltIGlzIGNhbGxlZApmdW5jdGlvbiB0dlJQLnBsYXlBbmltKHVwcGVyLCBzZXEsIGxvb3BpbmcpCiAgaWYgc2VxLnRhc2sgfj0gbmlsIHRoZW4gLS0gaXMgYSB0YXNrIChjZiBodHRwczovL2cuLi5jb250ZW50LWF2YWlsYWJsZS10by1hdXRob3Itb25seS4uLmIuY29tL0ltYWdpY1RoZUNhdC92UlAvcHVsbC8xMTgpCiAgICBEZWxldGVFbnRpdHkocGhvbmVQcm9wKQoJdHZSUC5zdG9wQW5pbSh0cnVlKQoKICAgIGxvY2FsIHBlZCA9IEdldFBsYXllclBlZCgtMSkKICAgIGlmIHNlcS50YXNrID09ICJQUk9QX0hVTUFOX1NFQVRfQ0hBSVJfTVBfUExBWUVSIiB0aGVuIC0tIHNwZWNpYWwgY2FzZSwgc2l0IGluIGEgY2hhaXIKICAgICAgbG9jYWwgeCx5LHogPSB0dlJQLmdldFBvc2l0aW9uKCkKICAgICAgVGFza1N0YXJ0U2NlbmFyaW9BdFBvc2l0aW9uKHBlZCwgc2VxLnRhc2ssIHgsIHksIHotMSwgR2V0RW50aXR5SGVhZGluZyhwZWQpLCAwLCAwLCBmYWxzZSkKICAgIGVsc2UKICAgICAgVGFza1N0YXJ0U2NlbmFyaW9JblBsYWNlKHBlZCwgc2VxLnRhc2ssIDAsIG5vdCBzZXEucGxheV9leGl0KQogICAgZW5kCiAgZWxzZSAtLSBhIHJlZ3VsYXIgYW5pbWF0aW9uIHNlcXVlbmNlCiAgICB0dlJQLnN0b3BBbmltKHVwcGVyKQoKICAgIGxvY2FsIGZsYWdzID0gMAogICAgaWYgdXBwZXIgdGhlbiBmbGFncyA9IGZsYWdzKzQ4IGVuZAogICAgaWYgbG9vcGluZyB0aGVuIGZsYWdzID0gZmxhZ3MrMSBlbmQKCiAgICBDaXRpemVuLkNyZWF0ZVRocmVhZChmdW5jdGlvbigpCiAgICAgIC0tIHByZXBhcmUgdW5pcXVlIGlkIHRvIHN0b3Agc2VxdWVuY2Ugd2hlbiBuZWVkZWQKICAgICAgbG9jYWwgaWQgPSBhbmltX2lkczpnZW4oKQogICAgICBhbmltc1tpZF0gPSB0cnVlCgogICAgICBmb3Igayx2IGluIHBhaXJzKHNlcSkgZG8KICAgICAgICBsb2NhbCBkaWN0ID0gdlsxXQogICAgICAgIGxvY2FsIG5hbWUgPSB2WzJdCiAgICAgICAgbG9jYWwgbG9vcHMgPSB2WzNdIG9yIDEKCiAgICAgICAgZm9yIGk9MSxsb29wcyBkbwogICAgICAgICAgaWYgYW5pbXNbaWRdIHRoZW4gLS0gY2hlY2sgYW5pbWF0aW9uIHdvcmtpbmcKICAgICAgICAgICAgbG9jYWwgZmlyc3QgPSAoayA9PSAxIGFuZCBpID09IDEpCiAgICAgICAgICAgIGxvY2FsIGxhc3QgPSAoayA9PSAjc2VxIGFuZCBpID09IGxvb3BzKQoKICAgICAgICAgICAgLS0gcmVxdWVzdCBhbmltIGRpY3QKICAgICAgICAgICAgUmVxdWVzdEFuaW1EaWN0KGRpY3QpCiAgICAgICAgICAgIGxvY2FsIGkgPSAwCiAgICAgICAgICAgIHdoaWxlIG5vdCBIYXNBbmltRGljdExvYWRlZChkaWN0KSBhbmQgaSA8IDEwMDAgZG8gLS0gbWF4IHRpbWUsIDEwIHNlY29uZHMKICAgICAgICAgICAgICBDaXRpemVuLldhaXQoMTApCiAgICAgICAgICAgICAgUmVxdWVzdEFuaW1EaWN0KGRpY3QpCiAgICAgICAgICAgICAgaSA9IGkrMQogICAgICAgICAgICBlbmQKCiAgICAgICAgICAgIC0tIHBsYXkgYW5pbQogICAgICAgICAgICBpZiBIYXNBbmltRGljdExvYWRlZChkaWN0KSBhbmQgYW5pbXNbaWRdIHRoZW4KICAgICAgICAgICAgICBsb2NhbCBpbnNwZWVkID0gOC4wMDAxCiAgICAgICAgICAgICAgbG9jYWwgb3V0c3BlZWQgPSAtOC4wMDAxCiAgICAgICAgICAgICAgaWYgbm90IGZpcnN0IHRoZW4gaW5zcGVlZCA9IDIuMDAwMSBlbmQKICAgICAgICAgICAgICBpZiBub3QgbGFzdCB0aGVuIG91dHNwZWVkID0gMi4wMDAxIGVuZAoKICAgICAgICAgICAgICBUYXNrUGxheUFuaW0oR2V0UGxheWVyUGVkKC0xKSxkaWN0LG5hbWUsaW5zcGVlZCxvdXRzcGVlZCwtMSxmbGFncywwLDAsMCwwKQogICAgICAgICAgICBlbmQKCiAgICAgICAgICAgIENpdGl6ZW4uV2FpdCgwKQogICAgICAgICAgICB3aGlsZSBHZXRFbnRpdHlBbmltQ3VycmVudFRpbWUoR2V0UGxheWVyUGVkKC0xKSxkaWN0LG5hbWUpIDw9IDAuOTUgYW5kIElzRW50aXR5UGxheWluZ0FuaW0oR2V0UGxheWVyUGVkKC0xKSxkaWN0LG5hbWUsMykgYW5kIGFuaW1zW2lkXSBkbwogICAgICAgICAgICAgIENpdGl6ZW4uV2FpdCgwKQogICAgICAgICAgICBlbmQKICAgICAgICAgIGVuZAogICAgICAgIGVuZAogICAgICBlbmQKCiAgICAgIC0tIGZyZWUgaWQKICAgICAgYW5pbV9pZHM6ZnJlZShpZCkKICAgICAgYW5pbXNbaWRdID0gbmlsCiAgICBlbmQpCiAgZW5kCmVuZAoKLS0gc3RvcCBhbmltYXRpb24gKG5ldyB2ZXJzaW9uKQotLSB1cHBlcjogdHJ1ZSwgc3RvcCB0aGUgdXBwZXIgYW5pbWF0aW9uLCBmYWxzZSwgc3RvcCBmdWxsIGFuaW1hdGlvbnMKZnVuY3Rpb24gdHZSUC5zdG9wQW5pbSh1cHBlcikKICBhbmltcyA9IHt9IC0tIHN0b3AgYWxsIHNlcXVlbmNlcwogIGlmIHVwcGVyIHRoZW4KICAgIENsZWFyUGVkU2Vjb25kYXJ5VGFzayhHZXRQbGF5ZXJQZWQoLTEpKQogIGVsc2UKICAgIENsZWFyUGVkVGFza3MoR2V0UGxheWVyUGVkKC0xKSkKICBlbmQKZW5kCgotLSBSQUdET0xMCmxvY2FsIHJhZ2RvbGwgPSBmYWxzZQoKLS0gc2V0IHBsYXllciByYWdkb2xsIGZsYWcgKHRydWUgb3IgZmFsc2UpCmZ1bmN0aW9uIHR2UlAuc2V0UmFnZG9sbChmbGFnKQogIHJhZ2RvbGwgPSBmbGFnCmVuZAoKLS0gcmFnZG9sbCB0aHJlYWQKQ2l0aXplbi5DcmVhdGVUaHJlYWQoZnVuY3Rpb24oKQogIHdoaWxlIHRydWUgZG8KICAgIENpdGl6ZW4uV2FpdCgxMCkKICAgIGlmIHJhZ2RvbGwgdGhlbgogICAgICBTZXRQZWRUb1JhZ2RvbGwoR2V0UGxheWVyUGVkKC0xKSwgMTAwMCwgMTAwMCwgMCwgMCwgMCwgMCkKICAgIGVuZAogIGVuZAplbmQpCgotLSBTT1VORAotLSBzb21lIGxpc3RzOiAKLS0gcGFzdGViaW4uY29tL0E4Tnk4QUhaCi0tIGh0dHBzOi8vdy4uLmNvbnRlbnQtYXZhaWxhYmxlLXRvLWF1dGhvci1vbmx5Li4udC53b3JrL2luZGV4LnBocD90aXRsZT1Gcm9udEVuZFNvdW5kbGlzdAoKLS0gcGxheSBzb3VuZCBhdCBhIHNwZWNpZmljIHBvc2l0aW9uCmZ1bmN0aW9uIHR2UlAucGxheVNwYXRpYWxpemVkU291bmQoZGljdCxuYW1lLHgseSx6LHJhbmdlKQogIFBsYXlTb3VuZEZyb21Db29yZCgtMSxuYW1lLHgrMC4wMDAxLHkrMC4wMDAxLHorMC4wMDAxLGRpY3QsMCxyYW5nZSswLjAwMDEsMCkKZW5kCgotLSBwbGF5IHNvdW5kCmZ1bmN0aW9uIHR2UlAucGxheVNvdW5kKGRpY3QsbmFtZSkKICBQbGF5U291bmQoLTEsbmFtZSxkaWN0LDAsMCwxKQplbmQKCmZ1bmN0aW9uIHR2UlAuc2V0RHJ1bmsocGxheWVyKSAKICBTZXRQZWRJc0RydW5rKHBsYXllciwgdHJ1ZSkKICBTZXRQZWRNb3ZlbWVudENsaXBzZXQocGxheWVyLCAiTU9WRV9NQERSVU5LQFNMSUdIVExZRFJVTksiLCB0cnVlKQogIFNldFRpbWVvdXQoNjAwMDAsIGZ1bmN0aW9uKCkgCglSZXNldFBlZE1vdmVtZW50Q2xpcHNldChwbGF5ZXIsIDApCglTZXRQZWRJc0RydW5rKHBsYXllciwgZmFsc2UpCiAgZW5kKQplbmQKCmZ1bmN0aW9uIHR2UlAuc2V0RHJ1Z2dlZChwbGF5ZXIpIAogIERvU2NyZWVuRmFkZU91dCg1MDApCiAgQ2l0aXplbi5XYWl0KDUwMCkKICAtLXZSUGNsaWVudC5wbGF5U2NyZWVuRWZmZWN0KCJEcnVnc0RyaXZpbmdJbiIsIDYwKQogIFNldFRpbWVjeWNsZU1vZGlmaWVyKCJzcGVjdGF0b3I1IikKICBTZXRQZWRNb3Rpb25CbHVyKHBsYXllciwgdHJ1ZSkKICBDaXRpemVuLldhaXQoNTAwKQogIERvU2NyZWVuRmFkZUluKDE1MDApCiAgCiAgU2V0VGltZW91dCg2MDAwMCwgZnVuY3Rpb24oKSAKCVNldFBlZE1vdGlvbkJsdXIocGxheWVyLCBmYWxzZSkKICAgIENsZWFyVGltZWN5Y2xlTW9kaWZpZXIoKQogIGVuZCkKZW5kCgpmdW5jdGlvbiB0dlJQLnNldFNwZWVkYm9vc3QocGxheWVyKSAKICBTZXRSdW5TcHJpbnRNdWx0aXBsaWVyRm9yUGxheWVyKHBsYXllciwgMS4yKQogIFNldFN3aW1NdWx0aXBsaWVyRm9yUGxheWVyKHBsYXllciwgMS4yKQogIFNldFRpbWVvdXQoMTIwMDAwLCBmdW5jdGlvbigpIAogICAgU2V0UnVuU3ByaW50TXVsdGlwbGllckZvclBsYXllcihwbGF5ZXIsIDEuMCkKCVNldFN3aW1NdWx0aXBsaWVyRm9yUGxheWVyKHBsYXllciwgMS4wKQogIGVuZCkKZW5kCgpmdW5jdGlvbiB0dlJQLnNldFN0YW1pbmFib29zdChwbGF5ZXIpCiAgbG9jYWwgc3RhbWluYWJvb3N0ID0gdHJ1ZQogIHdoaWxlIChzdGFtaW5hYm9vc3QgPT0gdHJ1ZSkgZG8KICAgIFJlc3RvcmVQbGF5ZXJTdGFtaW5hKHBsYXllciwgMS4wKQogIGVuZAogCiAgU2V0VGltZW91dCgxMjAwMDAsIGZ1bmN0aW9uKCkgCiAgICBzdGFtaW5hYm9vc3QgPSBmYWxzZQogIGVuZCkKZW5kCgoKLS0gZXZlbnRzCgpBZGRFdmVudEhhbmRsZXIoInBsYXllclNwYXduZWQiLGZ1bmN0aW9uKCkKICBUcmlnZ2VyU2VydmVyRXZlbnQoInZSUGNsaTpwbGF5ZXJTcGF3bmVkIikgICAgCmVuZCkKCkFkZEV2ZW50SGFuZGxlcigib25QbGF5ZXJEaWVkIixmdW5jdGlvbihwbGF5ZXIscmVhc29uKQogIFRyaWdnZXJTZXJ2ZXJFdmVudCgidlJQY2xpOnBsYXllckRpZWQiKQplbmQpCgpBZGRFdmVudEhhbmRsZXIoIm9uUGxheWVyS2lsbGVkIixmdW5jdGlvbihwbGF5ZXIsa2lsbGVyLHJlYXNvbikKICBUcmlnZ2VyU2VydmVyRXZlbnQoInZSUGNsaTpwbGF5ZXJEaWVkIikKZW5kKQoKZnVuY3Rpb24gdHZSUC51c2VMb2NrcGljayhwbGF5ZXIpCiAgQ2l0aXplbi5DcmVhdGVUaHJlYWQoZnVuY3Rpb24oKQogICAgQ2l0aXplbi5XYWl0KDEpCiAgICBsb2NhbCBwbHlDb29yZHMgPSBHZXRFbnRpdHlDb29yZHMoR2V0UGxheWVyUGVkKC0xKSwgMCkKICAgIHZlaCA9IEdldENsb3Nlc3RWZWhpY2xlKHBseUNvb3Jkc1sieCJdLCBwbHlDb29yZHNbInkiXSwgcGx5Q29vcmRzWyJ6Il0sIDUuMDAxLCAwLCA3MCkKCWlmIHZlaCB0aGVuIAogICAgICBUYXNrUGxheUFuaW0oR2V0UGxheWVyUGVkKC0xKSwibWluaUByZXBhaXIiLCJmaXhpbmdfYV9wbGF5ZXIiLCA4LjAsIDAuMCwgLTEsIDEsIDAsIDAsIDAsIDApCQkKICAgICAgU3RhcnRWZWhpY2xlQWxhcm0odmVoKQogICAgICBDaXRpemVuLldhaXQoMjAwMDApCiAgICAgIFNldFZlaGljbGVEb29yc0xvY2tlZCh2ZWgsIDEpCiAgICAgIENsZWFyUGVkVGFza3NJbW1lZGlhdGVseShHZXRQbGF5ZXJQZWQoLTEpKQoJZW5kCiAgZW5kKQplbmQKCmxvY2FsIHZvaWNlID0gImJhc2UiCmxvY2FsIHZvaWNldGl0bGUgPSAi0L7QsdGL0YfQvdCw0Y8iCgotLSB2b2ljZSBwcm94aW1pdHkgY29tcHV0YXRpb24KQ2l0aXplbi5DcmVhdGVUaHJlYWQoZnVuY3Rpb24oKQogIHdoaWxlIHRydWUgZG8KICAgIENpdGl6ZW4uV2FpdCgxKQoJCglsb2NhbCBwcm94aW1pdHkgPSAyMC4wCgkKCWlmIElzQ29udHJvbEp1c3RQcmVzc2VkKDEsIDE4MikgdGhlbgogICAgICBpZiB2b2ljZSA9PSAiYmFzZSIgdGhlbgoJICAgIHZvaWNlID0gInNob3J0IgoJCXZvaWNldGl0bGUgPSAi0LHQu9C40LbQvdGP0Y8iCgkJcHJveGltaXR5ID0gNS4wCgkgIGVsc2UgCgkgICAgdm9pY2UgPSAiYmFzZSIKCQl2b2ljZXRpdGxlID0gItC+0LHRi9GH0L3QsNGPIgoJCXByb3hpbWl0eSA9IDIwLjAKCSAgZW5kCQoJZW5kCgkKICAgIFNldFRleHRGb250KDApCiAgICBTZXRUZXh0UHJvcG9ydGlvbmFsKDEpCiAgICBTZXRUZXh0U2NhbGUoMC4wLCAwLjMwKQogICAgU2V0VGV4dENvbG91cigyNTUsIDI1NSwgMjU1LCAyNTUpCiAgICBTZXRUZXh0RHJvcHNoYWRvdygwLCAwLCAwLCAwLCAyNTUpCiAgICBTZXRUZXh0RWRnZSgxLCAwLCAwLCAwLCAyNTUpCiAgICBTZXRUZXh0RHJvcFNoYWRvdygpCiAgICBTZXRUZXh0T3V0bGluZSgpCiAgICBTZXRUZXh0RW50cnkoIlNUUklORyIpCiAgICBBZGRUZXh0Q29tcG9uZW50U3RyaW5nKCLQodC70YvRiNC40LzQvtGB0YLRjDogIi4udm9pY2V0aXRsZSkKCURyYXdUZXh0KDAuMTUsIDAuOTQpCgkKCiAgICBpZiBJc1BlZFNpdHRpbmdJbkFueVZlaGljbGUocGVkKSB0aGVuCiAgICAgIGxvY2FsIHZlaCA9IEdldFZlaGljbGVQZWRJc0luKHBlZCxmYWxzZSkKICAgICAgbG9jYWwgaGFzaCA9IEdldEVudGl0eU1vZGVsKHZlaCkKCiAgICAgIGlmIElzVGhpc01vZGVsQUNhcihoYXNoKSBvciBJc1RoaXNNb2RlbEFIZWxpKGhhc2gpIG9yIElzVGhpc01vZGVsQVBsYW5lKGhhc2gpIHRoZW4KCSAgICBpZiB2b2ljZSA9PSAic2hvcnQiIHRoZW4KICAgICAgICAgIHByb3hpbWl0eSA9IDUuMAoJICAgIGVsc2UgCiAgICAgICAgICBwcm94aW1pdHkgPSAxMC4wCiAgICAgICAgZW5kCQogICAgICBlbmQKCSAgCiAgICBlbHNlaWYgdHZSUC5pc0luc2lkZSgpIHRoZW4KCSAgaWYgdm9pY2UgPT0gInNob3J0IiB0aGVuCiAgICAgICAgcHJveGltaXR5ID0gMy4wCgkgIGVsc2UJCiAgICAgICAgcHJveGltaXR5ID0gNy4wCiAgICAgIGVuZAkKICAgIGVuZAoKICAgIE5ldHdvcmtTZXRUYWxrZXJQcm94aW1pdHkocHJveGltaXR5KzAuMDAwMSkKICBlbmQKZW5kKQoKQ2l0aXplbi5DcmVhdGVUaHJlYWQoZnVuY3Rpb24oKQogIHdoaWxlIHRydWUgZG8KICAgIENpdGl6ZW4uV2FpdCgxKQogICAgbG9jYWwgcGVkID0gR2V0UGxheWVyUGVkKC0xKQkKICAgIGxvY2FsIHJhZGl1cyA9IDUKICAgIGxvY2FsIHgseSx6ID0gdGFibGUudW5wYWNrKEdldEVudGl0eUNvb3JkcyhwZWQsdHJ1ZSkpCgogICAgbG9jYWwgdmVoID0gR2V0Q2xvc2VzdFZlaGljbGUoeCswLjAwMDEseSswLjAwMDEseiswLjAwMDEsIHJhZGl1cyswLjAwMDEsIDAsIDgxOTIrNDA5Nis0KzIrMSkgIC0tIGJvYXRzLCBoZWxpY29zCiAgICBpZiBub3QgSXNFbnRpdHlBVmVoaWNsZSh2ZWgpIHRoZW4gdmVoID0gR2V0Q2xvc2VzdFZlaGljbGUoeCswLjAwMDEseSswLjAwMDEseiswLjAwMDEsIHJhZGl1cyswLjAwMDEsIDAsIDQrMisxKSBlbmQgLS0gY2FycwogICAgaWYgdmVoIH49IGZhbHNlIHRoZW4KICAgICAgU2V0VmVoaWNsZUF1dG9tYXRpY2FsbHlBdHRhY2hlcyh2ZWgsIDEsIDApCgllbmQKICBlbmQKZW5kKQo=