local Keys = {
[ "ESC" ] = 322 , [ "F1" ] = 288 , [ "F2" ] = 289 , [ "F3" ] = 170 , [ "F5" ] = 166 , [ "F6" ] = 167 , [ "F7" ] = 168 , [ "F8" ] = 169 , [ "F9" ] = 56 , [ "F10" ] = 57 ,
[ "~" ] = 243 , [ "1" ] = 157 , [ "2" ] = 158 , [ "3" ] = 160 , [ "4" ] = 164 , [ "5" ] = 165 , [ "6" ] = 159 , [ "7" ] = 161 , [ "8" ] = 162 , [ "9" ] = 163 , [ "-" ] = 84 , [ "=" ] = 83 , [ "BACKSPACE" ] = 177 ,
[ "TAB" ] = 37 , [ "Q" ] = 44 , [ "W" ] = 32 , [ "E" ] = 38 , [ "R" ] = 45 , [ "T" ] = 245 , [ "Y" ] = 246 , [ "U" ] = 303 , [ "P" ] = 199 , [ "[" ] = 39 , [ "]" ] = 40 , [ "ENTER" ] = 18 ,
[ "CAPS" ] = 137 , [ "A" ] = 34 , [ "S" ] = 8 , [ "D" ] = 9 , [ "F" ] = 23 , [ "G" ] = 47 , [ "H" ] = 74 , [ "K" ] = 311 , [ "L" ] = 182 ,
[ "LEFTSHIFT" ] = 21 , [ "Z" ] = 20 , [ "X" ] = 73 , [ "C" ] = 26 , [ "V" ] = 0 , [ "B" ] = 29 , [ "N" ] = 249 , [ "M" ] = 244 , [ "," ] = 82 , [ "." ] = 81 ,
[ "LEFTCTRL" ] = 36 , [ "LEFTALT" ] = 19 , [ "SPACE" ] = 22 , [ "RIGHTCTRL" ] = 70 ,
[ "HOME" ] = 213 , [ "PAGEUP" ] = 10 , [ "PAGEDOWN" ] = 11 , [ "DELETE" ] = 178 ,
[ "LEFT" ] = 174 , [ "RIGHT" ] = 175 , [ "TOP" ] = 27 , [ "DOWN" ] = 173 ,
[ "NENTER" ] = 201 , [ "N4" ] = 108 , [ "N5" ] = 60 , [ "N6" ] = 107 , [ "N+" ] = 96 , [ "N-" ] = 97 , [ "N7" ] = 117 , [ "N8" ] = 61 , [ "N9" ] = 118
}
local PlayerData = { }
local GUI = { }
local HasAlreadyEnteredMarker = false
local LastZone = nil
local CurrentAction = nil
local CurrentActionMsg = ''
local CurrentActionData = { }
local OnJob = false
local CurrentCustomer = nil
local CurrentCustomerBlip = nil
local DestinationBlip = nil
local IsNearCustomer = false
local CustomerIsEnteringVehicle = false
local CustomerEnteredVehicle = false
local TargetCoords = nil
ESX = nil
GUI. Time = 0
Citizen. CreateThread( function ( )
while ESX == nil do
TriggerEvent( 'esx:getSharedObject' , function ( obj) ESX = obj end )
Citizen. Wait( 0 )
end
end )
function DrawSub( msg, time )
ClearPrints( )
SetTextEntry_2( "STRING" )
AddTextComponentString( msg)
DrawSubtitleTimed( time , 1 )
end
function ShowLoadingPromt( msg, time , type )
Citizen. CreateThread( function ( )
Citizen. Wait( 0 )
N_0xaba17d7ce615adbf( "STRING" )
AddTextComponentString( msg)
N_0xbd12f8228410d9b4( type )
Citizen. Wait( time )
N_0x10d373323e5b9c0d( )
end )
end
function GetRandomWalkingNPC( )
local search = { }
local peds = ESX. Game. GetPeds( )
for i= 1 , # peds, 1 do
if IsPedHuman( peds[ i] ) and IsPedWalking( peds[ i] ) and not IsPedAPlayer( peds[ i] ) then
table.insert ( search, peds[ i] )
end
end
if # search > 0 then
return search[ GetRandomIntInRange( 1 , # search) ]
end
print ( 'Using fallback code to find walking ped' )
for i= 1 , 250 , 1 do
local ped = GetRandomPedAtCoord( 0.0 , 0.0 , 0.0 , math . huge + 0.0 , math . huge + 0.0 , math . huge + 0.0 , 26 )
if DoesEntityExist( ped) and IsPedHuman( ped) and IsPedWalking( ped) and not IsPedAPlayer( ped) then
table.insert ( search, ped)
end
end
if # search > 0 then
return search[ GetRandomIntInRange( 1 , # search) ]
end
end
function ClearCurrentMission( )
if DoesBlipExist( CurrentCustomerBlip) then
RemoveBlip( CurrentCustomerBlip)
end
if DoesBlipExist( DestinationBlip) then
RemoveBlip( DestinationBlip)
end
CurrentCustomer = nil
CurrentCustomerBlip = nil
DestinationBlip = nil
IsNearCustomer = false
CustomerIsEnteringVehicle = false
CustomerEnteredVehicle = false
TargetCoords = nil
end
function StartTaxiJob( )
ShowLoadingPromt( _U( 'taking_service' ) .. 'Taxi/Uber' , 5000 , 3 )
ClearCurrentMission( )
OnJob = true
end
function StopTaxiJob( )
local playerPed = GetPlayerPed( - 1 )
if IsPedInAnyVehicle( playerPed, false ) and CurrentCustomer ~= nil then
local vehicle = GetVehiclePedIsIn( playerPed, false )
TaskLeaveVehicle( CurrentCustomer, vehicle, 0 )
if CustomerEnteredVehicle then
TaskGoStraightToCoord( CurrentCustomer, TargetCoords. x, TargetCoords. y, TargetCoords. z, 1.0 , - 1 , 0.0 , 0.0 )
end
end
ClearCurrentMission( )
OnJob = false
DrawSub( _U( 'mission_complete' ) , 5000 )
end
function OpenTaxiActionsMenu( )
local elements = {
{ label = _U( 'spawn_veh' ) , value = 'spawn_vehicle' } ,
{ label = _U( 'deposit_stock' ) , value = 'put_stock' } ,
{ label = _U( 'take_stock' ) , value = 'get_stock' }
}
if Config. EnablePlayerManagement and PlayerData. job ~= nil and PlayerData. job. grade_name == 'boss' then
table.insert ( elements, { label = _U( 'boss_actions' ) , value = 'boss_actions' } )
end
ESX. UI. Menu. CloseAll( )
ESX. UI. Menu. Open(
'default' , GetCurrentResourceName( ) , 'taxi_actions' ,
{
title = 'Taxi' ,
elements = elements
} ,
function ( data, menu)
if data. current. value == 'put_stock' then
OpenPutStocksMenu( )
end
if data. current. value == 'get_stock' then
OpenGetStocksMenu( )
end
if data. current. value == 'spawn_vehicle' then
if Config. EnableSocietyOwnedVehicles then
local elements = { }
ESX. TriggerServerCallback( 'esx_society:getVehiclesInGarage' , function ( vehicles)
for i= 1 , # vehicles, 1 do
table.insert ( elements, { label = GetDisplayNameFromVehicleModel( vehicles[ i] . model) .. ' [' .. vehicles[ i] . plate .. ']' , value = vehicles[ i] } )
end
ESX. UI. Menu. Open(
'default' , GetCurrentResourceName( ) , 'vehicle_spawner' ,
{
title = _U( 'spawn_veh' ) ,
align = 'top-left' ,
elements = elements,
} ,
function ( data, menu)
menu. close( )
local vehicleProps = data. current. value
ESX. Game. SpawnVehicle( vehicleProps. model, Config. Zones. VehicleSpawnPoint. Pos, 270.0 , function ( vehicle)
ESX. Game. SetVehicleProperties( vehicle, vehicleProps)
local playerPed = GetPlayerPed( - 1 )
TaskWarpPedIntoVehicle( playerPed, vehicle, - 1 )
end )
TriggerServerEvent( 'esx_society:removeVehicleFromGarage' , 'taxi' , vehicleProps)
end ,
function ( data, menu)
menu. close( )
end
)
end , 'taxi' )
else
menu. close( )
if Config. MaxInService == - 1 then
local playerPed = GetPlayerPed( - 1 )
local coords = Config. Zones. VehicleSpawnPoint. Pos
ESX. Game. SpawnVehicle( 'taxi' , coords, 225.0 , function ( vehicle)
TaskWarpPedIntoVehicle( playerPed, vehicle, - 1 )
end )
else
ESX. TriggerServerCallback( 'esx_service:enableService' , function ( canTakeService, maxInService, inServiceCount)
if canTakeService then
local playerPed = GetPlayerPed( - 1 )
local coords = Config. Zones. VehicleSpawnPoint. Pos
ESX. Game. SpawnVehicle( 'taxi' , coords, 225.0 , function ( vehicle)
TaskWarpPedIntoVehicle( playerPed, vehicle, - 1 )
end )
else
ESX. ShowNotification( _U( 'full_service' ) .. inServiceCount .. '/' .. maxInService)
end
end , 'taxi' )
end
end
end
if data. current. value == 'boss_actions' then
TriggerEvent( 'esx_society:openBossMenu' , 'taxi' , function ( data, menu)
menu. close( )
end )
end
end ,
function ( data, menu)
menu. close( )
CurrentAction = 'taxi_actions_menu'
CurrentActionMsg = _U( 'press_to_open' )
CurrentActionData = { }
end
)
end
function OpenMobileTaxiActionsMenu( )
ESX. UI. Menu. CloseAll( )
ESX. UI. Menu. Open(
'default' , GetCurrentResourceName( ) , 'mobile_taxi_actions' ,
{
title = 'Taxi' ,
elements = {
{ label = _U( 'billing' ) , value = 'billing' }
}
} ,
function ( data, menu)
if data. current. value == 'billing' then
ESX. UI. Menu. Open(
'dialog' , GetCurrentResourceName( ) , 'billing' ,
{
title = _U( 'invoice_amount' )
} ,
function ( data, menu)
local amount = tonumber ( data. value)
if amount == nil then
ESX. ShowNotification( _U( 'amount_invalid' ) )
else
menu. close( )
local closestPlayer, closestDistance = ESX. Game. GetClosestPlayer( )
if closestPlayer == - 1 or closestDistance > 3.0 then
ESX. ShowNotification( _U( 'no_players_near' ) )
else
TriggerServerEvent( 'esx_billing:sendBill' , GetPlayerServerId( closestPlayer) , 'society_taxi' , 'Taxi' , amount)
end
end
end ,
function ( data, menu)
menu. close( )
end
)
end
end ,
function ( data, menu)
menu. close( )
end
)
end
function OpenGetStocksMenu( )
ESX. TriggerServerCallback( 'esx_taxijob:getStockItems' , function ( items)
print ( json. encode( items) )
local elements = { }
for i= 1 , # items, 1 do
table.insert ( elements, { label = 'x' .. items[ i] . count .. ' ' .. items[ i] . label, value = items[ i] . name} )
end
ESX. UI. Menu. Open(
'default' , GetCurrentResourceName( ) , 'stocks_menu' ,
{
title = 'Taxi Stock' ,
elements = elements
} ,
function ( data, menu)
local itemName = data. current. value
ESX. UI. Menu. Open(
'dialog' , GetCurrentResourceName( ) , 'stocks_menu_get_item_count' ,
{
title = _U( 'quantity' )
} ,
function ( data2, menu2)
local count = tonumber ( data2. value)
if count == nil then
ESX. ShowNotification( _U( 'quantity_invalid' ) )
else
menu2. close( )
menu. close( )
OpenGetStocksMenu( )
TriggerServerEvent( 'esx_taxijob:getStockItem' , itemName, count)
end
end ,
function ( data2, menu2)
menu2. close( )
end
)
end ,
function ( data, menu)
menu. close( )
end
)
end )
end
function OpenPutStocksMenu( )
ESX. TriggerServerCallback( 'esx_taxijob:getPlayerInventory' , function ( inventory)
local elements = { }
for i= 1 , # inventory. items, 1 do
local item = inventory. items[ i]
if item. count > 0 then
table.insert ( elements, { label = item. label .. ' x' .. item. count, type = 'item_standard' , value = item. name} )
end
end
ESX. UI. Menu. Open(
'default' , GetCurrentResourceName( ) , 'stocks_menu' ,
{
title = _U( 'inventory' ) ,
elements = elements
} ,
function ( data, menu)
local itemName = data. current. value
ESX. UI. Menu. Open(
'dialog' , GetCurrentResourceName( ) , 'stocks_menu_put_item_count' ,
{
title = _U( 'quantity' )
} ,
function ( data2, menu2)
local count = tonumber ( data2. value)
if count == nil then
ESX. ShowNotification( _U( 'quantity_invalid' ) )
else
menu2. close( )
menu. close( )
OpenPutStocksMenu( )
TriggerServerEvent( 'esx_taxijob:putStockItems' , itemName, count)
end
end ,
function ( data2, menu2)
menu2. close( )
end
)
end ,
function ( data, menu)
menu. close( )
end
)
end )
end
RegisterNetEvent( 'esx:playerLoaded' )
AddEventHandler( 'esx:playerLoaded' , function ( xPlayer)
PlayerData = xPlayer
end )
RegisterNetEvent( 'esx:setJob' )
AddEventHandler( 'esx:setJob' , function ( job)
PlayerData. job = job
end )
AddEventHandler( 'esx_taxijob:hasEnteredMarker' , function ( zone)
if zone == 'TaxiActions' then
CurrentAction = 'taxi_actions_menu'
CurrentActionMsg = _U( 'press_to_open' )
CurrentActionData = { }
end
if zone == 'VehicleDeleter' then
local playerPed = GetPlayerPed( - 1 )
if IsPedInAnyVehicle( playerPed, false ) then
local vehicle = GetVehiclePedIsIn( playerPed, false )
CurrentAction = 'delete_vehicle'
CurrentActionMsg = _U( 'veh_stored' )
CurrentActionData = { vehicle = vehicle}
end
end
end )
AddEventHandler( 'esx_taxijob:hasExitedMarker' , function ( zone)
ESX. UI. Menu. CloseAll( )
CurrentAction = nil
end )
RegisterNetEvent( 'esx_phone:loaded' )
AddEventHandler( 'esx_phone:loaded' , function ( phoneNumber, contacts)
local specialContact = {
name = 'Taxi' ,
number = 'taxi' ,
base64Icon = 'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACAAAAAgCAYAAABzenr0AAAGGElEQVR4XsWWW2gd1xWGv7Vn5pyRj47ut8iOYlmyWxw1KSZN4riOW6eFuCYldaBtIL1Ag4NNmt5ICORCaNKXlF6oCy0hpSoJKW4bp7Sk6YNb01RuLq4d0pQ0kWQrshVJ1uX46HJ0zpy5rCKfQYgjCUs4kA+GtTd786+ftW8jqsqHibB6TLZn2zeq09ZTWAIWCxACoTI1E+6v+eSpXwHRqkVZPcmqlBzCApLQ8dk3IWVKMQlYcHG81OODNmD6D7d9VQrTSbwsH73lFKePtvOxXSfn48U+Xpb58fl5gPmgl6DiR19PZN4+G7iODY4liIAACqiCHyp+AFvb7ML3uot1QP5yDUim292RtIqfU6Lr8wFVDVV8AsPKRDAxzYkKm2kj5sSFuUT3+v2FXkDXakD6f+7c1NGS7Ml0Pkah6jq8mhvwUy7Cyijg5Aoks6/hTp+k7vRjDJ73dmw8WHxlJRM2y5Nsb3GPDuzsZURbGMsUmRkoUPByCMrKCG7SobJiO01X7OKq6utoe3XX34BaoLDaCljj3faTcu3j3z3T+iADwzNYEmKIWcGAIAtqqkKAxZa2Sja/tY+59/7y48aveQ8A4Woq4Fa3bj7Q1/EgwWRAZ52NMTYCWAZEwIhBUEQgUiVQ8IpKvqj4kVJCyGRCRrb+hvap+gPAo0DuUhWQfx2q29u+t/vPmarbCLwII7qQTEQRLbUtBJ2PAkZARBADqkLBV/I+BGrhpoSN577FWz3P3XbTvRMvAlpuwC4crv5jwtK9RAFSu46+G8cRwESxQ+K2gESAgCiIASHuA8YCBdSUohdCKGCF0H6iGc3MgrEphvKi+6Wp24HABioSjuxFARGobyJ5OMXEiGHW6iLR0EmifhPJDddj3CoqtuwEZSkCc73/RAvTeEOvU5w8gz/Zj2TfoLFFibZvQrI5EOFiPqgAZmzApTINKKgPiW20ffkXtPXfA9Ysmf5/kHn/T0z8e5rpCS5JVQNUN1ayfn2a+qvT2JWboOOXMPg0ms6C2IAAWTc2ACPeupdbm5yb8XNQczOM90DOB0uoa01Ttz5FZ6IL3Ctg9DUIg7Lto2DZ0HIDFEbAz4AaiBRyxZJe9U7kQg84KYbH/JeJESANXPXwXdWffvzu1p+x5VE4/ST4EyAOoEAI6WsAhdx/AYulhJDqAgRm/hPPEVAfnAboeAB6v88jTw/f98SzU8eAwbgC5IGRg3vsW3E7YewYzJwF4wAhikJURGqvBO8ouAFIxBI0gqgPEp9B86+ASSAIEEHhbEnX7eTgnrFbn3iW5+K82EAA+M2V+d2EeRj9K/izIBYgJZGwCO4Gzm/uRQOwDEsI41PSfPZ+xJsBKwFo6dOwpJvezMU84Md5sSmRCM51uacGbUKvHWEjAKIelXaGJqePyopjzFTdx6Ef/gDbjo3FKEoQKN+8/yEqRt8jf67IaNDBnF9FZFwERRGspMM20+XC64nym9AMhSE1G7fjbb0bCQsISi6vFCdPMPzuUwR9AcmOKQ7cew+WZcq3IGEYMZeb4p13sjjmU4TX7Cfdtp0oDAFBbZfk/37N0MALAKbcAKaY4yPeuwy3t2J8MAKDIxDVd1Lz8Ts599vb8Wameen532GspRWIQmXPHV8k0BquvPP3TOSgsRmiCFRAHWh9420Gi7nl34JaBen7O7UWRMD740AQ7yEf8nW78TIeN+7+PCIsOYaqMJHxqKtpJ++D+DA5ARsawEmASqzv1Cz7FjRpbt951tUAOcAHdNEUC7C5NAJo7Dws03CAFMxlkdSRZmCMxaq8ejKuVwSqIJfzA61LmyIgBoxZfgmYmQazKLGumHitRso0ZVkD0aE/FI7UrYv2WUYXjo0ihNhEatA1GBEUIxEWAcKCHhHCVMG8AETlda0ENn3hrm+/6Zh47RBCtXn+mZ/sAXzWjnPHV77zkiXBgl6gFkee+em1wBlgdnEF8sCF5moLI7KwlSIMwABwgbVT21htMNjleheAfPkShEBh/PzQccexdxBT9IPjQAYYZ+3o2OjQ8cQiPb+kVwBCliENXA3sAm6Zj3E/zaq4fD07HmwEmuKYXsUFcDl6Hz7/B1RGfEbPim/bAAAAAElFTkSuQmCC' ,
}
TriggerEvent( 'esx_phone:addSpecialContact' , specialContact. name, specialContact. number, specialContact. base64Icon)
end )
-- Create Blips
Citizen. CreateThread( function ( )
local blip = AddBlipForCoord( Config. Zones. TaxiActions. Pos. x, Config. Zones. TaxiActions. Pos. y, Config. Zones. TaxiActions. Pos. z)
SetBlipSprite ( blip, 198 )
SetBlipDisplay( blip, 4 )
SetBlipScale ( blip, 1.0 )
SetBlipColour ( blip, 5 )
SetBlipAsShortRange( blip, true )
BeginTextCommandSetBlipName( "STRING" )
AddTextComponentString( "Taxi" )
EndTextCommandSetBlipName( blip)
end )
-- Display markers
Citizen. CreateThread( function ( )
while true do
Wait( 0 )
if PlayerData. job ~= nil and PlayerData. job. name == 'taxi' then
local coords = GetEntityCoords( GetPlayerPed( - 1 ) )
for k, v in pairs ( Config. Zones) do
if ( v. Type ~= - 1 and GetDistanceBetweenCoords( coords, v. Pos. x, v. Pos. y, v. Pos. z, true ) < Config. DrawDistance) then
DrawMarker( v. Type, v. Pos. x, v. Pos. y, v. Pos. z, 0.0 , 0.0 , 0.0 , 0 , 0.0 , 0.0 , v. Size. x, v. Size. y, v. Size. z, v. Color. r, v. Color. g, v. Color. b, 100 , false , true , 2 , false , false , false , false )
end
end
end
end
end )
-- Enter / Exit marker events
Citizen. CreateThread( function ( )
while true do
Wait( 0 )
if PlayerData. job ~= nil and PlayerData. job. name == 'taxi' then
local coords = GetEntityCoords( GetPlayerPed( - 1 ) )
local isInMarker = false
local currentZone = nil
for k, v in pairs ( Config. Zones) do
if ( GetDistanceBetweenCoords( coords, v. Pos. x, v. Pos. y, v. Pos. z, true ) < v. Size. x) then
isInMarker = true
currentZone = k
end
end
if ( isInMarker and not HasAlreadyEnteredMarker) or ( isInMarker and LastZone ~= currentZone) then
HasAlreadyEnteredMarker = true
LastZone = currentZone
TriggerEvent( 'esx_taxijob:hasEnteredMarker' , currentZone)
end
if not isInMarker and HasAlreadyEnteredMarker then
HasAlreadyEnteredMarker = false
TriggerEvent( 'esx_taxijob:hasExitedMarker' , LastZone)
end
end
end
end )
-- Taxi Job
Citizen. CreateThread( function ( )
while true do
Citizen. Wait( 0 )
local playerPed = GetPlayerPed( - 1 )
if OnJob then
if CurrentCustomer == nil then
DrawSub( _U( 'drive_search_pass' ) , 5000 )
if IsPedInAnyVehicle( playerPed, false ) and GetEntitySpeed( playerPed) > 0 then
local waitUntil = GetGameTimer( ) + GetRandomIntInRange( 30000 , 45000 )
while OnJob and waitUntil > GetGameTimer( ) do
Citizen. Wait( 0 )
end
if OnJob and IsPedInAnyVehicle( playerPed, false ) and GetEntitySpeed( playerPed) > 0 then
CurrentCustomer = GetRandomWalkingNPC( )
if CurrentCustomer ~= nil then
CurrentCustomerBlip = AddBlipForEntity( CurrentCustomer)
SetBlipAsFriendly( CurrentCustomerBlip, 1 )
SetBlipColour( CurrentCustomerBlip, 2 )
SetBlipCategory( CurrentCustomerBlip, 3 )
SetBlipRoute( CurrentCustomerBlip, true )
SetEntityAsMissionEntity( CurrentCustomer, true , false )
ClearPedTasksImmediately( CurrentCustomer)
SetBlockingOfNonTemporaryEvents( CurrentCustomer, 1 )
local standTime = GetRandomIntInRange( 60000 , 180000 )
TaskStandStill( CurrentCustomer, standTime)
ESX. ShowNotification( _U( 'customer_found' ) )
end
end
end
else
if IsPedFatallyInjured( CurrentCustomer) then
ESX. ShowNotification( _U( 'client_unconcious' ) )
if DoesBlipExist( CurrentCustomerBlip) then
RemoveBlip( CurrentCustomerBlip)
end
if DoesBlipExist( DestinationBlip) then
RemoveBlip( DestinationBlip)
end
SetEntityAsMissionEntity( CurrentCustomer, false , true )
CurrentCustomer = nil
CurrentCustomerBlip = nil
DestinationBlip = nil
IsNearCustomer = false
CustomerIsEnteringVehicle = false
CustomerEnteredVehicle = false
TargetCoords = nil
end
if IsPedInAnyVehicle( playerPed, false ) then
local vehicle = GetVehiclePedIsIn( playerPed, false )
local playerCoords = GetEntityCoords( playerPed)
local customerCoords = GetEntityCoords( CurrentCustomer)
local customerDistance = GetDistanceBetweenCoords( playerCoords. x, playerCoords. y, playerCoords. z, customerCoords. x, customerCoords. y, customerCoords. z)
if IsPedSittingInVehicle( CurrentCustomer, vehicle) then
if CustomerEnteredVehicle then
local targetDistance = GetDistanceBetweenCoords( playerCoords. x, playerCoords. y, playerCoords. z, TargetCoords. x, TargetCoords. y, TargetCoords. z)
if targetDistance <= 10.0 then
TaskLeaveVehicle( CurrentCustomer, vehicle, 0 )
ESX. ShowNotification( _U( 'arrive_dest' ) )
TaskGoStraightToCoord( CurrentCustomer, TargetCoords. x, TargetCoords. y, TargetCoords. z, 1.0 , - 1 , 0.0 , 0.0 )
SetEntityAsMissionEntity( CurrentCustomer, false , true )
TriggerServerEvent( 'esx_taxijob:success' )
RemoveBlip( DestinationBlip)
local scope = function ( customer)
ESX. SetTimeout( 60000 , function ( )
DeletePed( customer)
end )
end
scope( CurrentCustomer)
CurrentCustomer = nil
CurrentCustomerBlip = nil
DestinationBlip = nil
IsNearCustomer = false
CustomerIsEnteringVehicle = false
CustomerEnteredVehicle = false
TargetCoords = nil
end
if TargetCoords ~= nil then
DrawMarker( 1 , TargetCoords. x, TargetCoords. y, TargetCoords. z - 1.0 , 0 , 0 , 0 , 0 , 0 , 0 , 4.0 , 4.0 , 2.0 , 178 , 236 , 93 , 155 , 0 , 0 , 2 , 0 , 0 , 0 , 0 )
end
else
RemoveBlip( CurrentCustomerBlip)
CurrentCustomerBlip = nil
TargetCoords = Config. JobLocations[ GetRandomIntInRange( 1 , # Config. JobLocations) ]
local street = table . pack( GetStreetNameAtCoord( TargetCoords. x, TargetCoords. y, TargetCoords. z) )
local msg = nil
if street[ 2 ] ~= 0 and street[ 2 ] ~= nil then
msg = string.format ( _U( 'take_me_to_near' , GetStreetNameFromHashKey( street[ 1 ] ) , GetStreetNameFromHashKey( street[ 2 ] ) ) )
else
msg = string.format ( _U( 'take_me_to' , GetStreetNameFromHashKey( street[ 1 ] ) ) )
end
ESX. ShowNotification( msg)
DestinationBlip = AddBlipForCoord( TargetCoords. x, TargetCoords. y, TargetCoords. z)
BeginTextCommandSetBlipName( "STRING" )
AddTextComponentString( "Destination" )
EndTextCommandSetBlipName( blip)
SetBlipRoute( DestinationBlip, true )
CustomerEnteredVehicle = true
end
else
DrawMarker( 1 , customerCoords. x, customerCoords. y, customerCoords. z - 1.0 , 0 , 0 , 0 , 0 , 0 , 0 , 4.0 , 4.0 , 2.0 , 178 , 236 , 93 , 155 , 0 , 0 , 2 , 0 , 0 , 0 , 0 )
if not CustomerEnteredVehicle then
if customerDistance <= 30.0 then
if not IsNearCustomer then
ESX. ShowNotification( _U( 'close_to_client' ) )
IsNearCustomer = true
end
end
if customerDistance <= 100.0 then
if not CustomerIsEnteringVehicle then
ClearPedTasksImmediately( CurrentCustomer)
local seat = 0
for i= 4 , 0 , 1 do
if IsVehicleSeatFree( vehicle, seat) then
seat = i
break
end
end
TaskEnterVehicle( CurrentCustomer, vehicle, - 1 , seat, 2.0 , 1 )
CustomerIsEnteringVehicle = true
end
end
end
end
else
DrawSub( _U( 'return_to_veh' ) , 5000 )
end
end
end
end
end )
-- Key Controls
Citizen. CreateThread( function ( )
while true do
Citizen. Wait( 0 )
if CurrentAction ~= nil then
SetTextComponentFormat( 'STRING' )
AddTextComponentString( CurrentActionMsg)
DisplayHelpTextFromStringLabel( 0 , 0 , 1 , - 1 )
if IsControlPressed( 0 , Keys[ 'E' ] ) and PlayerData. job ~= nil and PlayerData. job. name == 'taxi' and ( GetGameTimer( ) - GUI. Time) > 300 then
if CurrentAction == 'taxi_actions_menu' then
OpenTaxiActionsMenu( )
end
if CurrentAction == 'delete_vehicle' then
local playerPed = GetPlayerPed( - 1 )
if Config. EnableSocietyOwnedVehicles then
local vehicleProps = ESX. Game. GetVehicleProperties( CurrentActionData. vehicle)
TriggerServerEvent( 'esx_society:putVehicleInGarage' , 'taxi' , vehicleProps)
else
if GetEntityModel( CurrentActionData. vehicle) == GetHashKey( 'taxi' ) then
if Config. MaxInService ~= - 1 then
TriggerServerEvent( 'esx_service:disableService' , 'taxi' )
end
end
end
ESX. Game. DeleteVehicle( CurrentActionData. vehicle)
end
CurrentAction = nil
GUI. Time = GetGameTimer( )
end
end
if IsControlPressed( 0 , Keys[ 'F2' ] ) and Config. EnablePlayerManagement and PlayerData. job ~= nil and PlayerData. job. name == 'taxi' and ( GetGameTimer( ) - GUI. Time) > 150 then
OpenMobileTaxiActionsMenu( )
GUI. Time = GetGameTimer( )
end
if IsControlPressed( 0 , Keys[ 'DELETE' ] ) and ( GetGameTimer( ) - GUI. Time) > 150 then
if OnJob then
StopTaxiJob( )
else
if PlayerData. job ~= nil and PlayerData. job. name == 'taxi' then
local playerPed = GetPlayerPed( - 1 )
if IsPedInAnyVehicle( playerPed, false ) then
local vehicle = GetVehiclePedIsIn( playerPed, false )
if PlayerData. job. grade >= 3 then
StartTaxiJob( )
else
if GetEntityModel( vehicle) == GetHashKey( 'taxi' ) then
StartTaxiJob( )
else
ESX. ShowNotification( _U( 'must_in_taxi' ) )
end
end
else
if PlayerData. job. grade >= 3 then
ESX. ShowNotification( _U( 'must_in_vehicle' ) )
else
ESX. ShowNotification( _U( 'must_in_taxi' ) )
end
end
end
end
GUI. Time = GetGameTimer( )
end
end
end )
bG9jYWwgS2V5cyA9IHsKICBbIkVTQyJdID0gMzIyLCBbIkYxIl0gPSAyODgsIFsiRjIiXSA9IDI4OSwgWyJGMyJdID0gMTcwLCBbIkY1Il0gPSAxNjYsIFsiRjYiXSA9IDE2NywgWyJGNyJdID0gMTY4LCBbIkY4Il0gPSAxNjksIFsiRjkiXSA9IDU2LCBbIkYxMCJdID0gNTcsCiAgWyJ+Il0gPSAyNDMsIFsiMSJdID0gMTU3LCBbIjIiXSA9IDE1OCwgWyIzIl0gPSAxNjAsIFsiNCJdID0gMTY0LCBbIjUiXSA9IDE2NSwgWyI2Il0gPSAxNTksIFsiNyJdID0gMTYxLCBbIjgiXSA9IDE2MiwgWyI5Il0gPSAxNjMsIFsiLSJdID0gODQsIFsiPSJdID0gODMsIFsiQkFDS1NQQUNFIl0gPSAxNzcsCiAgWyJUQUIiXSA9IDM3LCBbIlEiXSA9IDQ0LCBbIlciXSA9IDMyLCBbIkUiXSA9IDM4LCBbIlIiXSA9IDQ1LCBbIlQiXSA9IDI0NSwgWyJZIl0gPSAyNDYsIFsiVSJdID0gMzAzLCBbIlAiXSA9IDE5OSwgWyJbIl0gPSAzOSwgWyJdIl0gPSA0MCwgWyJFTlRFUiJdID0gMTgsCiAgWyJDQVBTIl0gPSAxMzcsIFsiQSJdID0gMzQsIFsiUyJdID0gOCwgWyJEIl0gPSA5LCBbIkYiXSA9IDIzLCBbIkciXSA9IDQ3LCBbIkgiXSA9IDc0LCBbIksiXSA9IDMxMSwgWyJMIl0gPSAxODIsCiAgWyJMRUZUU0hJRlQiXSA9IDIxLCBbIloiXSA9IDIwLCBbIlgiXSA9IDczLCBbIkMiXSA9IDI2LCBbIlYiXSA9IDAsIFsiQiJdID0gMjksIFsiTiJdID0gMjQ5LCBbIk0iXSA9IDI0NCwgWyIsIl0gPSA4MiwgWyIuIl0gPSA4MSwKICBbIkxFRlRDVFJMIl0gPSAzNiwgWyJMRUZUQUxUIl0gPSAxOSwgWyJTUEFDRSJdID0gMjIsIFsiUklHSFRDVFJMIl0gPSA3MCwKICBbIkhPTUUiXSA9IDIxMywgWyJQQUdFVVAiXSA9IDEwLCBbIlBBR0VET1dOIl0gPSAxMSwgWyJERUxFVEUiXSA9IDE3OCwKICBbIkxFRlQiXSA9IDE3NCwgWyJSSUdIVCJdID0gMTc1LCBbIlRPUCJdID0gMjcsIFsiRE9XTiJdID0gMTczLAogIFsiTkVOVEVSIl0gPSAyMDEsIFsiTjQiXSA9IDEwOCwgWyJONSJdID0gNjAsIFsiTjYiXSA9IDEwNywgWyJOKyJdID0gOTYsIFsiTi0iXSA9IDk3LCBbIk43Il0gPSAxMTcsIFsiTjgiXSA9IDYxLCBbIk45Il0gPSAxMTgKfQoKbG9jYWwgUGxheWVyRGF0YSAgICAgICAgICAgICAgICA9IHt9CmxvY2FsIEdVSSAgICAgICAgICAgICAgICAgICAgICAgPSB7fQpsb2NhbCBIYXNBbHJlYWR5RW50ZXJlZE1hcmtlciAgID0gZmFsc2UKbG9jYWwgTGFzdFpvbmUgICAgICAgICAgICAgICAgICA9IG5pbApsb2NhbCBDdXJyZW50QWN0aW9uICAgICAgICAgICAgID0gbmlsCmxvY2FsIEN1cnJlbnRBY3Rpb25Nc2cgICAgICAgICAgPSAnJwpsb2NhbCBDdXJyZW50QWN0aW9uRGF0YSAgICAgICAgID0ge30KbG9jYWwgT25Kb2IgICAgICAgICAgICAgICAgICAgICA9IGZhbHNlCmxvY2FsIEN1cnJlbnRDdXN0b21lciAgICAgICAgICAgPSBuaWwKbG9jYWwgQ3VycmVudEN1c3RvbWVyQmxpcCAgICAgICA9IG5pbApsb2NhbCBEZXN0aW5hdGlvbkJsaXAgICAgICAgICAgID0gbmlsCmxvY2FsIElzTmVhckN1c3RvbWVyICAgICAgICAgICAgPSBmYWxzZQpsb2NhbCBDdXN0b21lcklzRW50ZXJpbmdWZWhpY2xlID0gZmFsc2UKbG9jYWwgQ3VzdG9tZXJFbnRlcmVkVmVoaWNsZSAgICA9IGZhbHNlCmxvY2FsIFRhcmdldENvb3JkcyAgICAgICAgICAgICAgPSBuaWwKCkVTWCAgICAgICAgICAgICAgICAgICAgICAgICAgICAgPSBuaWwKR1VJLlRpbWUgICAgICAgICAgICAgICAgICAgICAgICA9IDAKCkNpdGl6ZW4uQ3JlYXRlVGhyZWFkKGZ1bmN0aW9uKCkKICB3aGlsZSBFU1ggPT0gbmlsIGRvCiAgICBUcmlnZ2VyRXZlbnQoJ2VzeDpnZXRTaGFyZWRPYmplY3QnLCBmdW5jdGlvbihvYmopIEVTWCA9IG9iaiBlbmQpCiAgICBDaXRpemVuLldhaXQoMCkKICBlbmQKZW5kKQoKZnVuY3Rpb24gRHJhd1N1Yihtc2csIHRpbWUpCiAgQ2xlYXJQcmludHMoKQogIFNldFRleHRFbnRyeV8yKCJTVFJJTkciKQogIEFkZFRleHRDb21wb25lbnRTdHJpbmcobXNnKQogIERyYXdTdWJ0aXRsZVRpbWVkKHRpbWUsIDEpCmVuZAoKZnVuY3Rpb24gU2hvd0xvYWRpbmdQcm9tdChtc2csIHRpbWUsIHR5cGUpCiAgQ2l0aXplbi5DcmVhdGVUaHJlYWQoZnVuY3Rpb24oKQogICAgQ2l0aXplbi5XYWl0KDApCiAgICBOXzB4YWJhMTdkN2NlNjE1YWRiZigiU1RSSU5HIikKICAgIEFkZFRleHRDb21wb25lbnRTdHJpbmcobXNnKQogICAgTl8weGJkMTJmODIyODQxMGQ5YjQodHlwZSkKICAgIENpdGl6ZW4uV2FpdCh0aW1lKQogICAgTl8weDEwZDM3MzMyM2U1YjljMGQoKQogIGVuZCkKZW5kCgpmdW5jdGlvbiBHZXRSYW5kb21XYWxraW5nTlBDKCkKCiAgbG9jYWwgc2VhcmNoID0ge30KICBsb2NhbCBwZWRzICAgPSBFU1guR2FtZS5HZXRQZWRzKCkKCiAgZm9yIGk9MSwgI3BlZHMsIDEgZG8KICAgIGlmIElzUGVkSHVtYW4ocGVkc1tpXSkgYW5kIElzUGVkV2Fsa2luZyhwZWRzW2ldKSBhbmQgbm90IElzUGVkQVBsYXllcihwZWRzW2ldKSB0aGVuCiAgICAgIHRhYmxlLmluc2VydChzZWFyY2gsIHBlZHNbaV0pCiAgICBlbmQKICBlbmQKCiAgaWYgI3NlYXJjaCA+IDAgdGhlbgogICAgcmV0dXJuIHNlYXJjaFtHZXRSYW5kb21JbnRJblJhbmdlKDEsICNzZWFyY2gpXQogIGVuZAoKICBwcmludCgnVXNpbmcgZmFsbGJhY2sgY29kZSB0byBmaW5kIHdhbGtpbmcgcGVkJykKCiAgZm9yIGk9MSwgMjUwLCAxIGRvCgogICAgbG9jYWwgcGVkID0gR2V0UmFuZG9tUGVkQXRDb29yZCgwLjAsICAwLjAsICAwLjAsICBtYXRoLmh1Z2UgKyAwLjAsICBtYXRoLmh1Z2UgKyAwLjAsICBtYXRoLmh1Z2UgKyAwLjAsICAyNikKCiAgICBpZiBEb2VzRW50aXR5RXhpc3QocGVkKSBhbmQgSXNQZWRIdW1hbihwZWQpIGFuZCBJc1BlZFdhbGtpbmcocGVkKSBhbmQgbm90IElzUGVkQVBsYXllcihwZWQpIHRoZW4KICAgICAgdGFibGUuaW5zZXJ0KHNlYXJjaCwgcGVkKQogICAgZW5kCgogIGVuZAoKICBpZiAjc2VhcmNoID4gMCB0aGVuCiAgICByZXR1cm4gc2VhcmNoW0dldFJhbmRvbUludEluUmFuZ2UoMSwgI3NlYXJjaCldCiAgZW5kCgplbmQKCmZ1bmN0aW9uIENsZWFyQ3VycmVudE1pc3Npb24oKQoKICBpZiBEb2VzQmxpcEV4aXN0KEN1cnJlbnRDdXN0b21lckJsaXApIHRoZW4KICAgIFJlbW92ZUJsaXAoQ3VycmVudEN1c3RvbWVyQmxpcCkKICBlbmQKCiAgaWYgRG9lc0JsaXBFeGlzdChEZXN0aW5hdGlvbkJsaXApIHRoZW4KICAgIFJlbW92ZUJsaXAoRGVzdGluYXRpb25CbGlwKQogIGVuZAoKICBDdXJyZW50Q3VzdG9tZXIgICAgICAgICAgID0gbmlsCiAgQ3VycmVudEN1c3RvbWVyQmxpcCAgICAgICA9IG5pbAogIERlc3RpbmF0aW9uQmxpcCAgICAgICAgICAgPSBuaWwKICBJc05lYXJDdXN0b21lciAgICAgICAgICAgID0gZmFsc2UKICBDdXN0b21lcklzRW50ZXJpbmdWZWhpY2xlID0gZmFsc2UKICBDdXN0b21lckVudGVyZWRWZWhpY2xlICAgID0gZmFsc2UKICBUYXJnZXRDb29yZHMgICAgICAgICAgICAgID0gbmlsCgplbmQKCmZ1bmN0aW9uIFN0YXJ0VGF4aUpvYigpCgogIFNob3dMb2FkaW5nUHJvbXQoX1UoJ3Rha2luZ19zZXJ2aWNlJykgLi4gJ1RheGkvVWJlcicsIDUwMDAsIDMpCiAgQ2xlYXJDdXJyZW50TWlzc2lvbigpCgogIE9uSm9iID0gdHJ1ZQoKZW5kCgpmdW5jdGlvbiBTdG9wVGF4aUpvYigpCgogIGxvY2FsIHBsYXllclBlZCA9IEdldFBsYXllclBlZCgtMSkKCiAgaWYgSXNQZWRJbkFueVZlaGljbGUocGxheWVyUGVkLCBmYWxzZSkgYW5kIEN1cnJlbnRDdXN0b21lciB+PSBuaWwgdGhlbgogICAgbG9jYWwgdmVoaWNsZSA9IEdldFZlaGljbGVQZWRJc0luKHBsYXllclBlZCwgIGZhbHNlKQogICAgVGFza0xlYXZlVmVoaWNsZShDdXJyZW50Q3VzdG9tZXIsICB2ZWhpY2xlLCAgMCkKCiAgICBpZiBDdXN0b21lckVudGVyZWRWZWhpY2xlIHRoZW4KICAgICAgVGFza0dvU3RyYWlnaHRUb0Nvb3JkKEN1cnJlbnRDdXN0b21lciwgIFRhcmdldENvb3Jkcy54LCAgVGFyZ2V0Q29vcmRzLnksICBUYXJnZXRDb29yZHMueiwgIDEuMCwgIC0xLCAgMC4wLCAgMC4wKQogICAgZW5kCgogIGVuZAoKICBDbGVhckN1cnJlbnRNaXNzaW9uKCkKCiAgT25Kb2IgPSBmYWxzZQoKICBEcmF3U3ViKF9VKCdtaXNzaW9uX2NvbXBsZXRlJyksIDUwMDApCgplbmQKCmZ1bmN0aW9uIE9wZW5UYXhpQWN0aW9uc01lbnUoKQoKICBsb2NhbCBlbGVtZW50cyA9IHsKICAgIHtsYWJlbCA9IF9VKCdzcGF3bl92ZWgnKSwgdmFsdWUgPSAnc3Bhd25fdmVoaWNsZSd9LAogICAge2xhYmVsID0gX1UoJ2RlcG9zaXRfc3RvY2snKSwgdmFsdWUgPSAncHV0X3N0b2NrJ30sCiAgICB7bGFiZWwgPSBfVSgndGFrZV9zdG9jaycpLCB2YWx1ZSA9ICdnZXRfc3RvY2snfQogIH0KCiAgaWYgQ29uZmlnLkVuYWJsZVBsYXllck1hbmFnZW1lbnQgYW5kIFBsYXllckRhdGEuam9iIH49IG5pbCBhbmQgUGxheWVyRGF0YS5qb2IuZ3JhZGVfbmFtZSA9PSAnYm9zcycgdGhlbgogICAgdGFibGUuaW5zZXJ0KGVsZW1lbnRzLCB7bGFiZWwgPSBfVSgnYm9zc19hY3Rpb25zJyksIHZhbHVlID0gJ2Jvc3NfYWN0aW9ucyd9KQogIGVuZAoKICBFU1guVUkuTWVudS5DbG9zZUFsbCgpCgogIEVTWC5VSS5NZW51Lk9wZW4oCiAgICAnZGVmYXVsdCcsIEdldEN1cnJlbnRSZXNvdXJjZU5hbWUoKSwgJ3RheGlfYWN0aW9ucycsCiAgICB7CiAgICAgIHRpdGxlICAgID0gJ1RheGknLAogICAgICBlbGVtZW50cyA9IGVsZW1lbnRzCiAgICB9LAogICAgZnVuY3Rpb24oZGF0YSwgbWVudSkKCiAgICAgIGlmIGRhdGEuY3VycmVudC52YWx1ZSA9PSAncHV0X3N0b2NrJyB0aGVuCiAgICAgICAgT3BlblB1dFN0b2Nrc01lbnUoKQogICAgICBlbmQKCiAgICAgIGlmIGRhdGEuY3VycmVudC52YWx1ZSA9PSAnZ2V0X3N0b2NrJyB0aGVuCiAgICAgICAgT3BlbkdldFN0b2Nrc01lbnUoKQogICAgICBlbmQKCiAgICAgIGlmIGRhdGEuY3VycmVudC52YWx1ZSA9PSAnc3Bhd25fdmVoaWNsZScgdGhlbgoKICAgICAgICBpZiBDb25maWcuRW5hYmxlU29jaWV0eU93bmVkVmVoaWNsZXMgdGhlbgoKICAgICAgICAgIGxvY2FsIGVsZW1lbnRzID0ge30KCiAgICAgICAgICBFU1guVHJpZ2dlclNlcnZlckNhbGxiYWNrKCdlc3hfc29jaWV0eTpnZXRWZWhpY2xlc0luR2FyYWdlJywgZnVuY3Rpb24odmVoaWNsZXMpCgogICAgICAgICAgICBmb3IgaT0xLCAjdmVoaWNsZXMsIDEgZG8KICAgICAgICAgICAgICB0YWJsZS5pbnNlcnQoZWxlbWVudHMsIHtsYWJlbCA9IEdldERpc3BsYXlOYW1lRnJvbVZlaGljbGVNb2RlbCh2ZWhpY2xlc1tpXS5tb2RlbCkgLi4gJyBbJyAuLiB2ZWhpY2xlc1tpXS5wbGF0ZSAuLiAnXScsIHZhbHVlID0gdmVoaWNsZXNbaV19KQogICAgICAgICAgICBlbmQKCiAgICAgICAgICAgIEVTWC5VSS5NZW51Lk9wZW4oCiAgICAgICAgICAgICAgJ2RlZmF1bHQnLCBHZXRDdXJyZW50UmVzb3VyY2VOYW1lKCksICd2ZWhpY2xlX3NwYXduZXInLAogICAgICAgICAgICAgIHsKICAgICAgICAgICAgICAgIHRpdGxlICAgID0gX1UoJ3NwYXduX3ZlaCcpLAogICAgICAgICAgICAgICAgYWxpZ24gICAgPSAndG9wLWxlZnQnLAogICAgICAgICAgICAgICAgZWxlbWVudHMgPSBlbGVtZW50cywKICAgICAgICAgICAgICB9LAogICAgICAgICAgICAgIGZ1bmN0aW9uKGRhdGEsIG1lbnUpCgogICAgICAgICAgICAgICAgbWVudS5jbG9zZSgpCgogICAgICAgICAgICAgICAgbG9jYWwgdmVoaWNsZVByb3BzID0gZGF0YS5jdXJyZW50LnZhbHVlCgogICAgICAgICAgICAgICAgRVNYLkdhbWUuU3Bhd25WZWhpY2xlKHZlaGljbGVQcm9wcy5tb2RlbCwgQ29uZmlnLlpvbmVzLlZlaGljbGVTcGF3blBvaW50LlBvcywgMjcwLjAsIGZ1bmN0aW9uKHZlaGljbGUpCiAgICAgICAgICAgICAgICAgIEVTWC5HYW1lLlNldFZlaGljbGVQcm9wZXJ0aWVzKHZlaGljbGUsIHZlaGljbGVQcm9wcykKICAgICAgICAgICAgICAgICAgbG9jYWwgcGxheWVyUGVkID0gR2V0UGxheWVyUGVkKC0xKQogICAgICAgICAgICAgICAgICBUYXNrV2FycFBlZEludG9WZWhpY2xlKHBsYXllclBlZCwgIHZlaGljbGUsICAtMSkKICAgICAgICAgICAgICAgIGVuZCkKCiAgICAgICAgICAgICAgICBUcmlnZ2VyU2VydmVyRXZlbnQoJ2VzeF9zb2NpZXR5OnJlbW92ZVZlaGljbGVGcm9tR2FyYWdlJywgJ3RheGknLCB2ZWhpY2xlUHJvcHMpCgogICAgICAgICAgICAgIGVuZCwKICAgICAgICAgICAgICBmdW5jdGlvbihkYXRhLCBtZW51KQogICAgICAgICAgICAgICAgbWVudS5jbG9zZSgpCiAgICAgICAgICAgICAgZW5kCiAgICAgICAgICAgICkKCiAgICAgICAgICBlbmQsICd0YXhpJykKCiAgICAgICAgZWxzZQoKICAgICAgICAgIG1lbnUuY2xvc2UoKQoKICAgICAgICAgIGlmIENvbmZpZy5NYXhJblNlcnZpY2UgPT0gLTEgdGhlbgoKICAgICAgICAgICAgbG9jYWwgcGxheWVyUGVkID0gR2V0UGxheWVyUGVkKC0xKQogICAgICAgICAgICBsb2NhbCBjb29yZHMgICAgPSBDb25maWcuWm9uZXMuVmVoaWNsZVNwYXduUG9pbnQuUG9zCgogICAgICAgICAgICBFU1guR2FtZS5TcGF3blZlaGljbGUoJ3RheGknLCBjb29yZHMsIDIyNS4wLCBmdW5jdGlvbih2ZWhpY2xlKQogICAgICAgICAgICAgIFRhc2tXYXJwUGVkSW50b1ZlaGljbGUocGxheWVyUGVkLCAgdmVoaWNsZSwgLTEpCiAgICAgICAgICAgIGVuZCkKCiAgICAgICAgICBlbHNlCgogICAgICAgICAgICBFU1guVHJpZ2dlclNlcnZlckNhbGxiYWNrKCdlc3hfc2VydmljZTplbmFibGVTZXJ2aWNlJywgZnVuY3Rpb24oY2FuVGFrZVNlcnZpY2UsIG1heEluU2VydmljZSwgaW5TZXJ2aWNlQ291bnQpCgogICAgICAgICAgICAgIGlmIGNhblRha2VTZXJ2aWNlIHRoZW4KCiAgICAgICAgICAgICAgICBsb2NhbCBwbGF5ZXJQZWQgPSBHZXRQbGF5ZXJQZWQoLTEpCiAgICAgICAgICAgICAgICBsb2NhbCBjb29yZHMgICAgPSBDb25maWcuWm9uZXMuVmVoaWNsZVNwYXduUG9pbnQuUG9zCgogICAgICAgICAgICAgICAgRVNYLkdhbWUuU3Bhd25WZWhpY2xlKCd0YXhpJywgY29vcmRzLCAyMjUuMCwgZnVuY3Rpb24odmVoaWNsZSkKICAgICAgICAgICAgICAgICAgVGFza1dhcnBQZWRJbnRvVmVoaWNsZShwbGF5ZXJQZWQsICB2ZWhpY2xlLCAtMSkKICAgICAgICAgICAgICAgIGVuZCkKCiAgICAgICAgICAgICAgZWxzZQoKICAgICAgICAgICAgICAgIEVTWC5TaG93Tm90aWZpY2F0aW9uKF9VKCdmdWxsX3NlcnZpY2UnKSAuLiBpblNlcnZpY2VDb3VudCAuLiAnLycgLi4gbWF4SW5TZXJ2aWNlKQoKICAgICAgICAgICAgICBlbmQKCiAgICAgICAgICAgIGVuZCwgJ3RheGknKQoKICAgICAgICAgIGVuZAoKICAgICAgICBlbmQKCiAgICAgIGVuZAoKICAgICAgaWYgZGF0YS5jdXJyZW50LnZhbHVlID09ICdib3NzX2FjdGlvbnMnIHRoZW4KICAgICAgICBUcmlnZ2VyRXZlbnQoJ2VzeF9zb2NpZXR5Om9wZW5Cb3NzTWVudScsICd0YXhpJywgZnVuY3Rpb24oZGF0YSwgbWVudSkKICAgICAgICAgIG1lbnUuY2xvc2UoKQogICAgICAgIGVuZCkKICAgICAgZW5kCgogICAgZW5kLAogICAgZnVuY3Rpb24oZGF0YSwgbWVudSkKCiAgICAgIG1lbnUuY2xvc2UoKQoKICAgICAgQ3VycmVudEFjdGlvbiAgICAgPSAndGF4aV9hY3Rpb25zX21lbnUnCiAgICAgIEN1cnJlbnRBY3Rpb25Nc2cgID0gX1UoJ3ByZXNzX3RvX29wZW4nKQogICAgICBDdXJyZW50QWN0aW9uRGF0YSA9IHt9CgogICAgZW5kCiAgKQoKZW5kCgpmdW5jdGlvbiBPcGVuTW9iaWxlVGF4aUFjdGlvbnNNZW51KCkKCiAgRVNYLlVJLk1lbnUuQ2xvc2VBbGwoKQoKICBFU1guVUkuTWVudS5PcGVuKAogICAgJ2RlZmF1bHQnLCBHZXRDdXJyZW50UmVzb3VyY2VOYW1lKCksICdtb2JpbGVfdGF4aV9hY3Rpb25zJywKICAgIHsKICAgICAgdGl0bGUgICAgPSAnVGF4aScsCiAgICAgIGVsZW1lbnRzID0gewogICAgICAgIHtsYWJlbCA9IF9VKCdiaWxsaW5nJyksIHZhbHVlID0gJ2JpbGxpbmcnfQogICAgICB9CiAgICB9LAogICAgZnVuY3Rpb24oZGF0YSwgbWVudSkKCiAgICAgIGlmIGRhdGEuY3VycmVudC52YWx1ZSA9PSAnYmlsbGluZycgdGhlbgoKICAgICAgICBFU1guVUkuTWVudS5PcGVuKAogICAgICAgICAgJ2RpYWxvZycsIEdldEN1cnJlbnRSZXNvdXJjZU5hbWUoKSwgJ2JpbGxpbmcnLAogICAgICAgICAgewogICAgICAgICAgICB0aXRsZSA9IF9VKCdpbnZvaWNlX2Ftb3VudCcpCiAgICAgICAgICB9LAogICAgICAgICAgZnVuY3Rpb24oZGF0YSwgbWVudSkKCiAgICAgICAgICAgIGxvY2FsIGFtb3VudCA9IHRvbnVtYmVyKGRhdGEudmFsdWUpCgogICAgICAgICAgICBpZiBhbW91bnQgPT0gbmlsIHRoZW4KICAgICAgICAgICAgICBFU1guU2hvd05vdGlmaWNhdGlvbihfVSgnYW1vdW50X2ludmFsaWQnKSkKICAgICAgICAgICAgZWxzZQoKICAgICAgICAgICAgICBtZW51LmNsb3NlKCkKCiAgICAgICAgICAgICAgbG9jYWwgY2xvc2VzdFBsYXllciwgY2xvc2VzdERpc3RhbmNlID0gRVNYLkdhbWUuR2V0Q2xvc2VzdFBsYXllcigpCgogICAgICAgICAgICAgIGlmIGNsb3Nlc3RQbGF5ZXIgPT0gLTEgb3IgY2xvc2VzdERpc3RhbmNlID4gMy4wIHRoZW4KICAgICAgICAgICAgICAgIEVTWC5TaG93Tm90aWZpY2F0aW9uKF9VKCdub19wbGF5ZXJzX25lYXInKSkKICAgICAgICAgICAgICBlbHNlCiAgICAgICAgICAgICAgICBUcmlnZ2VyU2VydmVyRXZlbnQoJ2VzeF9iaWxsaW5nOnNlbmRCaWxsJywgR2V0UGxheWVyU2VydmVySWQoY2xvc2VzdFBsYXllciksICdzb2NpZXR5X3RheGknLCAnVGF4aScsIGFtb3VudCkKICAgICAgICAgICAgICBlbmQKCiAgICAgICAgICAgIGVuZAoKICAgICAgICAgIGVuZCwKICAgICAgICAgIGZ1bmN0aW9uKGRhdGEsIG1lbnUpCiAgICAgICAgICAgIG1lbnUuY2xvc2UoKQogICAgICAgICAgZW5kCiAgICAgICAgKQoKICAgICAgZW5kCgogICAgZW5kLAogICAgZnVuY3Rpb24oZGF0YSwgbWVudSkKICAgICAgbWVudS5jbG9zZSgpCiAgICBlbmQKICApCgplbmQKCmZ1bmN0aW9uIE9wZW5HZXRTdG9ja3NNZW51KCkKCiAgRVNYLlRyaWdnZXJTZXJ2ZXJDYWxsYmFjaygnZXN4X3RheGlqb2I6Z2V0U3RvY2tJdGVtcycsIGZ1bmN0aW9uKGl0ZW1zKQoKICAgIHByaW50KGpzb24uZW5jb2RlKGl0ZW1zKSkKCiAgICBsb2NhbCBlbGVtZW50cyA9IHt9CgogICAgZm9yIGk9MSwgI2l0ZW1zLCAxIGRvCiAgICAgIHRhYmxlLmluc2VydChlbGVtZW50cywge2xhYmVsID0gJ3gnIC4uIGl0ZW1zW2ldLmNvdW50IC4uICcgJyAuLiBpdGVtc1tpXS5sYWJlbCwgdmFsdWUgPSBpdGVtc1tpXS5uYW1lfSkKICAgIGVuZAoKICAgIEVTWC5VSS5NZW51Lk9wZW4oCiAgICAgICdkZWZhdWx0JywgR2V0Q3VycmVudFJlc291cmNlTmFtZSgpLCAnc3RvY2tzX21lbnUnLAogICAgICB7CiAgICAgICAgdGl0bGUgICAgPSAnVGF4aSBTdG9jaycsCiAgICAgICAgZWxlbWVudHMgPSBlbGVtZW50cwogICAgICB9LAogICAgICBmdW5jdGlvbihkYXRhLCBtZW51KQoKICAgICAgICBsb2NhbCBpdGVtTmFtZSA9IGRhdGEuY3VycmVudC52YWx1ZQoKICAgICAgICBFU1guVUkuTWVudS5PcGVuKAogICAgICAgICAgJ2RpYWxvZycsIEdldEN1cnJlbnRSZXNvdXJjZU5hbWUoKSwgJ3N0b2Nrc19tZW51X2dldF9pdGVtX2NvdW50JywKICAgICAgICAgIHsKICAgICAgICAgICAgdGl0bGUgPSBfVSgncXVhbnRpdHknKQogICAgICAgICAgfSwKICAgICAgICAgIGZ1bmN0aW9uKGRhdGEyLCBtZW51MikKCiAgICAgICAgICAgIGxvY2FsIGNvdW50ID0gdG9udW1iZXIoZGF0YTIudmFsdWUpCgogICAgICAgICAgICBpZiBjb3VudCA9PSBuaWwgdGhlbgogICAgICAgICAgICAgIEVTWC5TaG93Tm90aWZpY2F0aW9uKF9VKCdxdWFudGl0eV9pbnZhbGlkJykpCiAgICAgICAgICAgIGVsc2UKICAgICAgICAgICAgICBtZW51Mi5jbG9zZSgpCiAgICAgICAgICAgICAgbWVudS5jbG9zZSgpCiAgICAgICAgICAgICAgT3BlbkdldFN0b2Nrc01lbnUoKQoKICAgICAgICAgICAgICBUcmlnZ2VyU2VydmVyRXZlbnQoJ2VzeF90YXhpam9iOmdldFN0b2NrSXRlbScsIGl0ZW1OYW1lLCBjb3VudCkKICAgICAgICAgICAgZW5kCgogICAgICAgICAgZW5kLAogICAgICAgICAgZnVuY3Rpb24oZGF0YTIsIG1lbnUyKQogICAgICAgICAgICBtZW51Mi5jbG9zZSgpCiAgICAgICAgICBlbmQKICAgICAgICApCgogICAgICBlbmQsCiAgICAgIGZ1bmN0aW9uKGRhdGEsIG1lbnUpCiAgICAgICAgbWVudS5jbG9zZSgpCiAgICAgIGVuZAogICAgKQoKICBlbmQpCgplbmQKCmZ1bmN0aW9uIE9wZW5QdXRTdG9ja3NNZW51KCkKCiAgRVNYLlRyaWdnZXJTZXJ2ZXJDYWxsYmFjaygnZXN4X3RheGlqb2I6Z2V0UGxheWVySW52ZW50b3J5JywgZnVuY3Rpb24oaW52ZW50b3J5KQoKICAgIGxvY2FsIGVsZW1lbnRzID0ge30KCiAgICBmb3IgaT0xLCAjaW52ZW50b3J5Lml0ZW1zLCAxIGRvCgogICAgICBsb2NhbCBpdGVtID0gaW52ZW50b3J5Lml0ZW1zW2ldCgogICAgICBpZiBpdGVtLmNvdW50ID4gMCB0aGVuCiAgICAgICAgdGFibGUuaW5zZXJ0KGVsZW1lbnRzLCB7bGFiZWwgPSBpdGVtLmxhYmVsIC4uICcgeCcgLi4gaXRlbS5jb3VudCwgdHlwZSA9ICdpdGVtX3N0YW5kYXJkJywgdmFsdWUgPSBpdGVtLm5hbWV9KQogICAgICBlbmQKCiAgICBlbmQKCiAgICBFU1guVUkuTWVudS5PcGVuKAogICAgICAnZGVmYXVsdCcsIEdldEN1cnJlbnRSZXNvdXJjZU5hbWUoKSwgJ3N0b2Nrc19tZW51JywKICAgICAgewogICAgICAgIHRpdGxlICAgID0gX1UoJ2ludmVudG9yeScpLAogICAgICAgIGVsZW1lbnRzID0gZWxlbWVudHMKICAgICAgfSwKICAgICAgZnVuY3Rpb24oZGF0YSwgbWVudSkKCiAgICAgICAgbG9jYWwgaXRlbU5hbWUgPSBkYXRhLmN1cnJlbnQudmFsdWUKCiAgICAgICAgRVNYLlVJLk1lbnUuT3BlbigKICAgICAgICAgICdkaWFsb2cnLCBHZXRDdXJyZW50UmVzb3VyY2VOYW1lKCksICdzdG9ja3NfbWVudV9wdXRfaXRlbV9jb3VudCcsCiAgICAgICAgICB7CiAgICAgICAgICAgIHRpdGxlID0gX1UoJ3F1YW50aXR5JykKICAgICAgICAgIH0sCiAgICAgICAgICBmdW5jdGlvbihkYXRhMiwgbWVudTIpCgogICAgICAgICAgICBsb2NhbCBjb3VudCA9IHRvbnVtYmVyKGRhdGEyLnZhbHVlKQoKICAgICAgICAgICAgaWYgY291bnQgPT0gbmlsIHRoZW4KICAgICAgICAgICAgICBFU1guU2hvd05vdGlmaWNhdGlvbihfVSgncXVhbnRpdHlfaW52YWxpZCcpKQogICAgICAgICAgICBlbHNlCiAgICAgICAgICAgICAgbWVudTIuY2xvc2UoKQogICAgICAgICAgICAgIG1lbnUuY2xvc2UoKQogICAgICAgICAgICAgIE9wZW5QdXRTdG9ja3NNZW51KCkKCiAgICAgICAgICAgICAgVHJpZ2dlclNlcnZlckV2ZW50KCdlc3hfdGF4aWpvYjpwdXRTdG9ja0l0ZW1zJywgaXRlbU5hbWUsIGNvdW50KQogICAgICAgICAgICBlbmQKCiAgICAgICAgICBlbmQsCiAgICAgICAgICBmdW5jdGlvbihkYXRhMiwgbWVudTIpCiAgICAgICAgICAgIG1lbnUyLmNsb3NlKCkKICAgICAgICAgIGVuZAogICAgICAgICkKCiAgICAgIGVuZCwKICAgICAgZnVuY3Rpb24oZGF0YSwgbWVudSkKICAgICAgICBtZW51LmNsb3NlKCkKICAgICAgZW5kCiAgICApCgogIGVuZCkKCmVuZAoKClJlZ2lzdGVyTmV0RXZlbnQoJ2VzeDpwbGF5ZXJMb2FkZWQnKQpBZGRFdmVudEhhbmRsZXIoJ2VzeDpwbGF5ZXJMb2FkZWQnLCBmdW5jdGlvbih4UGxheWVyKQogIFBsYXllckRhdGEgPSB4UGxheWVyCmVuZCkKClJlZ2lzdGVyTmV0RXZlbnQoJ2VzeDpzZXRKb2InKQpBZGRFdmVudEhhbmRsZXIoJ2VzeDpzZXRKb2InLCBmdW5jdGlvbihqb2IpCiAgUGxheWVyRGF0YS5qb2IgPSBqb2IKZW5kKQoKQWRkRXZlbnRIYW5kbGVyKCdlc3hfdGF4aWpvYjpoYXNFbnRlcmVkTWFya2VyJywgZnVuY3Rpb24oem9uZSkKCiAgaWYgem9uZSA9PSAnVGF4aUFjdGlvbnMnIHRoZW4KICAgIEN1cnJlbnRBY3Rpb24gICAgID0gJ3RheGlfYWN0aW9uc19tZW51JwogICAgQ3VycmVudEFjdGlvbk1zZyAgPSBfVSgncHJlc3NfdG9fb3BlbicpCiAgICBDdXJyZW50QWN0aW9uRGF0YSA9IHt9CiAgZW5kCgogaWYgem9uZSA9PSAnVmVoaWNsZURlbGV0ZXInIHRoZW4KCiAgICBsb2NhbCBwbGF5ZXJQZWQgPSBHZXRQbGF5ZXJQZWQoLTEpCgogICAgaWYgSXNQZWRJbkFueVZlaGljbGUocGxheWVyUGVkLCAgZmFsc2UpIHRoZW4KCiAgICAgIGxvY2FsIHZlaGljbGUgPSBHZXRWZWhpY2xlUGVkSXNJbihwbGF5ZXJQZWQsICBmYWxzZSkKCiAgICAgIEN1cnJlbnRBY3Rpb24gICAgID0gJ2RlbGV0ZV92ZWhpY2xlJwogICAgICBDdXJyZW50QWN0aW9uTXNnICA9IF9VKCd2ZWhfc3RvcmVkJykKICAgICAgQ3VycmVudEFjdGlvbkRhdGEgPSB7dmVoaWNsZSA9IHZlaGljbGV9CiAgICBlbmQKICBlbmQKCmVuZCkKCkFkZEV2ZW50SGFuZGxlcignZXN4X3RheGlqb2I6aGFzRXhpdGVkTWFya2VyJywgZnVuY3Rpb24oem9uZSkKICBFU1guVUkuTWVudS5DbG9zZUFsbCgpCiAgQ3VycmVudEFjdGlvbiA9IG5pbAplbmQpCgpSZWdpc3Rlck5ldEV2ZW50KCdlc3hfcGhvbmU6bG9hZGVkJykKQWRkRXZlbnRIYW5kbGVyKCdlc3hfcGhvbmU6bG9hZGVkJywgZnVuY3Rpb24ocGhvbmVOdW1iZXIsIGNvbnRhY3RzKQoKICBsb2NhbCBzcGVjaWFsQ29udGFjdCA9IHsKICAgIG5hbWUgICAgICAgPSAnVGF4aScsCiAgICBudW1iZXIgICAgID0gJ3RheGknLAogICAgYmFzZTY0SWNvbiA9ICdkYXRhOmltYWdlL3BuZztiYXNlNjQsaVZCT1J3MEtHZ29BQUFBTlNVaEVVZ0FBQUNBQUFBQWdDQVlBQUFCemVucjBBQUFHR0VsRVFWUjRYc1dXVzJnZDF4V0d2N1ZuNXB5Umo0N3V0OGlPWWxteVd4dzFLU1pONHJpT1c2ZUZ1Q1lsZGFCdElMMUFnNE5ObXQ1SUNPUkNhTktYbEY2b0N5MGhwU29KS1c0YnA3U2s2WU5iMDFSdUxxNGQwcFEwa1dRcnNoVkoxdVg0NkhKMHpweTVyQ0tmUVlnakNVczRrQStHdFRkNzg2K2Z0VzhqcXNxSGliQjZUTFpuMnplcTA5WlRXQUlXQ3hBQ29USTFFKzZ2K2VTcFh3SFJxa1ZaUGNtcWxCekNBcExROGRrM0lXVktNUWxZY0hHODFPT0RObUQ2RDdkOVZRclRTYndzSDczbEZLZVB0dk94WFNmbjQ4VStYcGI1OGZsNWdQbWdsNkRpUjE5UFpONCtHN2lPRFk0bGlJQUFDcWlDSHlwK0FGdmI3TUwzdW90MVFQNXlEVWltMjkyUnRJcWZVNkxyOHdGVkRWVjhBc1BLUkRBeHpZa0ttMmtqNXNTRnVVVDMrdjJGWGtEWGFrRDZmKzdjMU5HUzdNbDBQa2FoNmpxOG1odndVeTdDeWlqZzVBb2tzNi9oVHArazd2UmpESjczZG13OFdIeGxKUk0yeTVOc2IzR1BEdXpzWlVSYkdNc1VtUmtvVVBCeUNNcktDRzdTb2JKaU8wMVg3T0txNnV0b2UzWFgzNEJhb0xEYUNsamozZmFUY3UzajN6M1QraUFEd3pOWUVtS0lXY0dBSUF0cXFrS0F4WmEyU2phL3RZKzU5Lzd5NDhhdmVROEE0V29xNEZhM2JqN1ExL0Vnd1dSQVo1Mk5NVFlDV0FaRXdJaEJVRVFnVWlWUThJcEt2cWo0a1ZKQ3lHUkNScmIraHZhcCtnUEFvMER1VWhXUWZ4MnEyOXUrdC92UG1hcmJDTHdJSTdxUVRFUVJMYlV0QkoyUEFrWkFSQkFEcWtMQlYvSStCR3JocG9TTjU3N0ZXejNQM1hiVHZSTXZBbHB1d0M0Y3J2NWp3dEs5UkFGU3U0NitHOGNSd0VTeFErSzJnRVNBZ0NpSUFTSHVBOFlDQmRTVW9oZENLR0NGMEg2aUdjM01nckVwaHZLaSs2V3AyNEhBQmlvU2p1eEZBUkdvYnlKNU9NWEVpR0hXNmlMUjBFbWlmaFBKRGRkajNDb3F0dXdFWlNrQ2M3My9SQXZUZUVPdlU1dzhnei9aajJUZm9MRkZpYlp2UXJJNUVPRmlQcWdBWm16QXBUSU5LS2dQaVcyMGZma1h0UFhmQTlZc21mNS9rSG4vVDB6OGU1cnBDUzVKVlFOVU4xYXlmbjJhK3F2VDJKV2JvT09YTVBnMG1zNkMySUFBV1RjMkFDUGV1cGRibTV5YjhYTlFjek9NOTBET0IwdW9hMDFUdHo1Rlo2SUwzQ3RnOURVSWc3THRvMkRaMEhJREZFYkF6NEFhaUJSeXhaSmU5VTdrUWc4NEtZYkgvSmVKRVNBTlhQWHdYZFdmZnZ6dTFwK3g1VkU0L1NUNEV5QU9vRUFJNldzQWhkeC9BWXVsaEpEcUFnUm0vaFBQRVZBZm5BYm9lQUI2djg4alR3L2Y5OFN6VThlQXdiZ0M1SUdSZzN2c1czRTdZZXdZekp3RjR3QWhpa0pVUkdxdkJPOG91QUZJeEJJMGdxZ1BFcDlCODYrQVNTQUlFRUhoYkVuWDdlVGduckZibjNpVzUrSzgyRUFBK00yVitkMkVlUmo5Sy9peklCWWdKWkd3Q080R3ptL3VSUU93REVzSTQxUFNmUForeEpzQkt3Rm82ZE93cEp2ZXpNVTg0TWQ1c1NtUkNNNTF1YWNHYlVLdkhXRWpBS0llbFhhR0pxZVB5b3BqekZUZHg2RWYvZ0Riam8zRktFb1FLTis4L3lFcVJ0OGpmNjdJYU5EQm5GOUZaRndFUlJHc3BNTTIwK1hDNjRueW05QU1oU0UxRzdmamJiMGJDUXNJU2k2dkZDZFBNUHp1VXdSOUFjbU9LUTdjZXcrV1pjcTNJR0VZTVplYjRwMTNzamptVTRUWDdDZmR0cDBvREFGQmJaZmsvMzdOME1BTEFLYmNBS2FZNHlQZXV3eTN0Mko4TUFLREl4RFZkMUx6OFRzNTk5dmI4V2FtZWVuNTMyR3NwUldJUW1YUEhWOGswQnF1dlBQM1RPU2dzUm1pQ0ZSQUhXaDk0MjBHaTdubDM0SmFCZW43TzdVV1JNRDc0MEFRN3lFZjhuVzc4VEllTis3K1BDSXNPWWFxTUpIeHFLdHBKKytEK0RBNUFSc2F3RW1BU3F6djFDejdGalJwYnQ5NTF0VUFPY0FIZE5FVUM3QzVOQUpvN0R3czAzQ0FGTXhsa2RTUlptQ014YXE4ZWpLdVZ3U3FJSmZ6QTYxTG15SWdCb3haZmdtWW1RYXpLTEd1bUhpdFJzbzBaVmtEMGFFL0ZJN1VyWXYyV1VZWGpvMGloTmhFYXRBMUdCRVVJeEVXQWNLQ0hoSENWTUc4QUVUbGRhMEVObjNocm0rLzZaaDQ3UkJDdFhuK21aL3NBWHpXam5QSFY3N3praVhCZ2w2Z0ZrZWUrZW0xd0JsZ2RuRUY4c0NGNW1vTEk3S3dsU0lNd0FCd2diVlQyMWh0TU5qbGVoZUFmUGtTaEVCaC9QelFjY2V4ZHhCVDlJUGpRQVlZWiszbzJPalE4Y1FpUGIra1Z3QkNsaUVOWEEzc0FtNlpqM0UvemFxNGZEMDdIbXdFbXVLWVhzVUZjRGw2SHo3L0IxUkdmRWJQaW0vYkFBQUFBRWxGVGtTdVFtQ0MnLAogIH0KCiAgVHJpZ2dlckV2ZW50KCdlc3hfcGhvbmU6YWRkU3BlY2lhbENvbnRhY3QnLCBzcGVjaWFsQ29udGFjdC5uYW1lLCBzcGVjaWFsQ29udGFjdC5udW1iZXIsIHNwZWNpYWxDb250YWN0LmJhc2U2NEljb24pCgplbmQpCgotLSBDcmVhdGUgQmxpcHMKQ2l0aXplbi5DcmVhdGVUaHJlYWQoZnVuY3Rpb24oKQoKICBsb2NhbCBibGlwID0gQWRkQmxpcEZvckNvb3JkKENvbmZpZy5ab25lcy5UYXhpQWN0aW9ucy5Qb3MueCwgQ29uZmlnLlpvbmVzLlRheGlBY3Rpb25zLlBvcy55LCBDb25maWcuWm9uZXMuVGF4aUFjdGlvbnMuUG9zLnopCgogIFNldEJsaXBTcHJpdGUgKGJsaXAsIDE5OCkKICBTZXRCbGlwRGlzcGxheShibGlwLCA0KQogIFNldEJsaXBTY2FsZSAgKGJsaXAsIDEuMCkKICBTZXRCbGlwQ29sb3VyIChibGlwLCA1KQogIFNldEJsaXBBc1Nob3J0UmFuZ2UoYmxpcCwgdHJ1ZSkKCiAgQmVnaW5UZXh0Q29tbWFuZFNldEJsaXBOYW1lKCJTVFJJTkciKQogIEFkZFRleHRDb21wb25lbnRTdHJpbmcoIlRheGkiKQogIEVuZFRleHRDb21tYW5kU2V0QmxpcE5hbWUoYmxpcCkKCmVuZCkKCi0tIERpc3BsYXkgbWFya2VycwpDaXRpemVuLkNyZWF0ZVRocmVhZChmdW5jdGlvbigpCiAgd2hpbGUgdHJ1ZSBkbwoKICAgIFdhaXQoMCkKCiAgICBpZiBQbGF5ZXJEYXRhLmpvYiB+PSBuaWwgYW5kIFBsYXllckRhdGEuam9iLm5hbWUgPT0gJ3RheGknIHRoZW4KCiAgICAgIGxvY2FsIGNvb3JkcyA9IEdldEVudGl0eUNvb3JkcyhHZXRQbGF5ZXJQZWQoLTEpKQoKICAgICAgZm9yIGssdiBpbiBwYWlycyhDb25maWcuWm9uZXMpIGRvCiAgICAgICAgaWYodi5UeXBlIH49IC0xIGFuZCBHZXREaXN0YW5jZUJldHdlZW5Db29yZHMoY29vcmRzLCB2LlBvcy54LCB2LlBvcy55LCB2LlBvcy56LCB0cnVlKSA8IENvbmZpZy5EcmF3RGlzdGFuY2UpIHRoZW4KICAgICAgICAgIERyYXdNYXJrZXIodi5UeXBlLCB2LlBvcy54LCB2LlBvcy55LCB2LlBvcy56LCAwLjAsIDAuMCwgMC4wLCAwLCAwLjAsIDAuMCwgdi5TaXplLngsIHYuU2l6ZS55LCB2LlNpemUueiwgdi5Db2xvci5yLCB2LkNvbG9yLmcsIHYuQ29sb3IuYiwgMTAwLCBmYWxzZSwgdHJ1ZSwgMiwgZmFsc2UsIGZhbHNlLCBmYWxzZSwgZmFsc2UpCiAgICAgICAgZW5kCiAgICAgIGVuZAoKICAgIGVuZAoKICBlbmQKZW5kKQoKLS0gRW50ZXIgLyBFeGl0IG1hcmtlciBldmVudHMKQ2l0aXplbi5DcmVhdGVUaHJlYWQoZnVuY3Rpb24oKQogIHdoaWxlIHRydWUgZG8KCiAgICBXYWl0KDApCgogICAgaWYgUGxheWVyRGF0YS5qb2Igfj0gbmlsIGFuZCBQbGF5ZXJEYXRhLmpvYi5uYW1lID09ICd0YXhpJyB0aGVuCgogICAgICBsb2NhbCBjb29yZHMgICAgICA9IEdldEVudGl0eUNvb3JkcyhHZXRQbGF5ZXJQZWQoLTEpKQogICAgICBsb2NhbCBpc0luTWFya2VyICA9IGZhbHNlCiAgICAgIGxvY2FsIGN1cnJlbnRab25lID0gbmlsCgogICAgICBmb3Igayx2IGluIHBhaXJzKENvbmZpZy5ab25lcykgZG8KICAgICAgICBpZihHZXREaXN0YW5jZUJldHdlZW5Db29yZHMoY29vcmRzLCB2LlBvcy54LCB2LlBvcy55LCB2LlBvcy56LCB0cnVlKSA8IHYuU2l6ZS54KSB0aGVuCiAgICAgICAgICBpc0luTWFya2VyICA9IHRydWUKICAgICAgICAgIGN1cnJlbnRab25lID0gawogICAgICAgIGVuZAogICAgICBlbmQKCiAgICAgIGlmIChpc0luTWFya2VyIGFuZCBub3QgSGFzQWxyZWFkeUVudGVyZWRNYXJrZXIpIG9yIChpc0luTWFya2VyIGFuZCBMYXN0Wm9uZSB+PSBjdXJyZW50Wm9uZSkgdGhlbgogICAgICAgIEhhc0FscmVhZHlFbnRlcmVkTWFya2VyID0gdHJ1ZQogICAgICAgIExhc3Rab25lICAgICAgICAgICAgICAgID0gY3VycmVudFpvbmUKICAgICAgICBUcmlnZ2VyRXZlbnQoJ2VzeF90YXhpam9iOmhhc0VudGVyZWRNYXJrZXInLCBjdXJyZW50Wm9uZSkKICAgICAgZW5kCgogICAgICBpZiBub3QgaXNJbk1hcmtlciBhbmQgSGFzQWxyZWFkeUVudGVyZWRNYXJrZXIgdGhlbgogICAgICAgIEhhc0FscmVhZHlFbnRlcmVkTWFya2VyID0gZmFsc2UKICAgICAgICBUcmlnZ2VyRXZlbnQoJ2VzeF90YXhpam9iOmhhc0V4aXRlZE1hcmtlcicsIExhc3Rab25lKQogICAgICBlbmQKCiAgICBlbmQKCiAgZW5kCmVuZCkKCi0tIFRheGkgSm9iCkNpdGl6ZW4uQ3JlYXRlVGhyZWFkKGZ1bmN0aW9uKCkKCiAgd2hpbGUgdHJ1ZSBkbwoKICAgIENpdGl6ZW4uV2FpdCgwKQoKICAgIGxvY2FsIHBsYXllclBlZCA9IEdldFBsYXllclBlZCgtMSkKCiAgICBpZiBPbkpvYiB0aGVuCgogICAgICBpZiBDdXJyZW50Q3VzdG9tZXIgPT0gbmlsIHRoZW4KCiAgICAgICAgRHJhd1N1YihfVSgnZHJpdmVfc2VhcmNoX3Bhc3MnKSwgNTAwMCkKCiAgICAgICAgaWYgSXNQZWRJbkFueVZlaGljbGUocGxheWVyUGVkLCAgZmFsc2UpIGFuZCBHZXRFbnRpdHlTcGVlZChwbGF5ZXJQZWQpID4gMCB0aGVuCgogICAgICAgICAgbG9jYWwgd2FpdFVudGlsID0gR2V0R2FtZVRpbWVyKCkgKyBHZXRSYW5kb21JbnRJblJhbmdlKDMwMDAwLCAgNDUwMDApCgogICAgICAgICAgd2hpbGUgT25Kb2IgYW5kIHdhaXRVbnRpbCA+IEdldEdhbWVUaW1lcigpIGRvCiAgICAgICAgICAgIENpdGl6ZW4uV2FpdCgwKQogICAgICAgICAgZW5kCgogICAgICAgICAgaWYgT25Kb2IgYW5kIElzUGVkSW5BbnlWZWhpY2xlKHBsYXllclBlZCwgIGZhbHNlKSBhbmQgR2V0RW50aXR5U3BlZWQocGxheWVyUGVkKSA+IDAgdGhlbgoKICAgICAgICAgICAgQ3VycmVudEN1c3RvbWVyID0gR2V0UmFuZG9tV2Fsa2luZ05QQygpCgogICAgICAgICAgICBpZiBDdXJyZW50Q3VzdG9tZXIgfj0gbmlsIHRoZW4KCiAgICAgICAgICAgICAgQ3VycmVudEN1c3RvbWVyQmxpcCA9IEFkZEJsaXBGb3JFbnRpdHkoQ3VycmVudEN1c3RvbWVyKQoKICAgICAgICAgICAgICBTZXRCbGlwQXNGcmllbmRseShDdXJyZW50Q3VzdG9tZXJCbGlwLCAxKQogICAgICAgICAgICAgIFNldEJsaXBDb2xvdXIoQ3VycmVudEN1c3RvbWVyQmxpcCwgMikKICAgICAgICAgICAgICBTZXRCbGlwQ2F0ZWdvcnkoQ3VycmVudEN1c3RvbWVyQmxpcCwgMykKICAgICAgICAgICAgICBTZXRCbGlwUm91dGUoQ3VycmVudEN1c3RvbWVyQmxpcCwgIHRydWUpCgogICAgICAgICAgICAgIFNldEVudGl0eUFzTWlzc2lvbkVudGl0eShDdXJyZW50Q3VzdG9tZXIsICB0cnVlLCBmYWxzZSkKICAgICAgICAgICAgICBDbGVhclBlZFRhc2tzSW1tZWRpYXRlbHkoQ3VycmVudEN1c3RvbWVyKQogICAgICAgICAgICAgIFNldEJsb2NraW5nT2ZOb25UZW1wb3JhcnlFdmVudHMoQ3VycmVudEN1c3RvbWVyLCAxKQoKICAgICAgICAgICAgICBsb2NhbCBzdGFuZFRpbWUgPSBHZXRSYW5kb21JbnRJblJhbmdlKDYwMDAwLCAgMTgwMDAwKQoKICAgICAgICAgICAgICBUYXNrU3RhbmRTdGlsbChDdXJyZW50Q3VzdG9tZXIsIHN0YW5kVGltZSkKCiAgICAgICAgICAgICAgRVNYLlNob3dOb3RpZmljYXRpb24oX1UoJ2N1c3RvbWVyX2ZvdW5kJykpCgogICAgICAgICAgICBlbmQKCiAgICAgICAgICBlbmQKCiAgICAgICAgZW5kCgogICAgICBlbHNlCgogICAgICAgIGlmIElzUGVkRmF0YWxseUluanVyZWQoQ3VycmVudEN1c3RvbWVyKSB0aGVuCgogICAgICAgICAgRVNYLlNob3dOb3RpZmljYXRpb24oX1UoJ2NsaWVudF91bmNvbmNpb3VzJykpCgogICAgICAgICAgaWYgRG9lc0JsaXBFeGlzdChDdXJyZW50Q3VzdG9tZXJCbGlwKSB0aGVuCiAgICAgICAgICAgIFJlbW92ZUJsaXAoQ3VycmVudEN1c3RvbWVyQmxpcCkKICAgICAgICAgIGVuZAoKICAgICAgICAgIGlmIERvZXNCbGlwRXhpc3QoRGVzdGluYXRpb25CbGlwKSB0aGVuCiAgICAgICAgICAgIFJlbW92ZUJsaXAoRGVzdGluYXRpb25CbGlwKQogICAgICAgICAgZW5kCgogICAgICAgICAgU2V0RW50aXR5QXNNaXNzaW9uRW50aXR5KEN1cnJlbnRDdXN0b21lciwgIGZhbHNlLCB0cnVlKQoKICAgICAgICAgIEN1cnJlbnRDdXN0b21lciAgICAgICAgICAgPSBuaWwKICAgICAgICAgIEN1cnJlbnRDdXN0b21lckJsaXAgICAgICAgPSBuaWwKICAgICAgICAgIERlc3RpbmF0aW9uQmxpcCAgICAgICAgICAgPSBuaWwKICAgICAgICAgIElzTmVhckN1c3RvbWVyICAgICAgICAgICAgPSBmYWxzZQogICAgICAgICAgQ3VzdG9tZXJJc0VudGVyaW5nVmVoaWNsZSA9IGZhbHNlCiAgICAgICAgICBDdXN0b21lckVudGVyZWRWZWhpY2xlICAgID0gZmFsc2UKICAgICAgICAgIFRhcmdldENvb3JkcyAgICAgICAgICAgICAgPSBuaWwKCiAgICAgICAgZW5kCgogICAgICAgIGlmIElzUGVkSW5BbnlWZWhpY2xlKHBsYXllclBlZCwgIGZhbHNlKSB0aGVuCgogICAgICAgICAgbG9jYWwgdmVoaWNsZSAgICAgICAgICA9IEdldFZlaGljbGVQZWRJc0luKHBsYXllclBlZCwgIGZhbHNlKQogICAgICAgICAgbG9jYWwgcGxheWVyQ29vcmRzICAgICA9IEdldEVudGl0eUNvb3JkcyhwbGF5ZXJQZWQpCiAgICAgICAgICBsb2NhbCBjdXN0b21lckNvb3JkcyAgID0gR2V0RW50aXR5Q29vcmRzKEN1cnJlbnRDdXN0b21lcikKICAgICAgICAgIGxvY2FsIGN1c3RvbWVyRGlzdGFuY2UgPSBHZXREaXN0YW5jZUJldHdlZW5Db29yZHMocGxheWVyQ29vcmRzLngsICBwbGF5ZXJDb29yZHMueSwgIHBsYXllckNvb3Jkcy56LCAgY3VzdG9tZXJDb29yZHMueCwgIGN1c3RvbWVyQ29vcmRzLnksICBjdXN0b21lckNvb3Jkcy56KQoKICAgICAgICAgIGlmIElzUGVkU2l0dGluZ0luVmVoaWNsZShDdXJyZW50Q3VzdG9tZXIsICB2ZWhpY2xlKSB0aGVuCgogICAgICAgICAgICBpZiBDdXN0b21lckVudGVyZWRWZWhpY2xlIHRoZW4KCiAgICAgICAgICAgICAgbG9jYWwgdGFyZ2V0RGlzdGFuY2UgPSBHZXREaXN0YW5jZUJldHdlZW5Db29yZHMocGxheWVyQ29vcmRzLngsICBwbGF5ZXJDb29yZHMueSwgIHBsYXllckNvb3Jkcy56LCAgVGFyZ2V0Q29vcmRzLngsICBUYXJnZXRDb29yZHMueSwgIFRhcmdldENvb3Jkcy56KQoKICAgICAgICAgICAgICBpZiB0YXJnZXREaXN0YW5jZSA8PSAxMC4wIHRoZW4KCiAgICAgICAgICAgICAgICBUYXNrTGVhdmVWZWhpY2xlKEN1cnJlbnRDdXN0b21lciwgIHZlaGljbGUsICAwKQoKICAgICAgICAgICAgICAgIEVTWC5TaG93Tm90aWZpY2F0aW9uKF9VKCdhcnJpdmVfZGVzdCcpKQoKICAgICAgICAgICAgICAgIFRhc2tHb1N0cmFpZ2h0VG9Db29yZChDdXJyZW50Q3VzdG9tZXIsICBUYXJnZXRDb29yZHMueCwgIFRhcmdldENvb3Jkcy55LCAgVGFyZ2V0Q29vcmRzLnosICAxLjAsICAtMSwgIDAuMCwgIDAuMCkKICAgICAgICAgICAgICAgIFNldEVudGl0eUFzTWlzc2lvbkVudGl0eShDdXJyZW50Q3VzdG9tZXIsICBmYWxzZSwgdHJ1ZSkKCiAgICAgICAgICAgICAgICBUcmlnZ2VyU2VydmVyRXZlbnQoJ2VzeF90YXhpam9iOnN1Y2Nlc3MnKQoKICAgICAgICAgICAgICAgIFJlbW92ZUJsaXAoRGVzdGluYXRpb25CbGlwKQoKICAgICAgICAgICAgICAgIGxvY2FsIHNjb3BlID0gZnVuY3Rpb24oY3VzdG9tZXIpCiAgICAgICAgICAgICAgICAgIEVTWC5TZXRUaW1lb3V0KDYwMDAwLCBmdW5jdGlvbigpCiAgICAgICAgICAgICAgICAgICAgRGVsZXRlUGVkKGN1c3RvbWVyKQogICAgICAgICAgICAgICAgICBlbmQpCiAgICAgICAgICAgICAgICBlbmQKCiAgICAgICAgICAgICAgICBzY29wZShDdXJyZW50Q3VzdG9tZXIpCgogICAgICAgICAgICAgICAgQ3VycmVudEN1c3RvbWVyICAgICAgICAgICA9IG5pbAogICAgICAgICAgICAgICAgQ3VycmVudEN1c3RvbWVyQmxpcCAgICAgICA9IG5pbAogICAgICAgICAgICAgICAgRGVzdGluYXRpb25CbGlwICAgICAgICAgICA9IG5pbAogICAgICAgICAgICAgICAgSXNOZWFyQ3VzdG9tZXIgICAgICAgICAgICA9IGZhbHNlCiAgICAgICAgICAgICAgICBDdXN0b21lcklzRW50ZXJpbmdWZWhpY2xlID0gZmFsc2UKICAgICAgICAgICAgICAgIEN1c3RvbWVyRW50ZXJlZFZlaGljbGUgICAgPSBmYWxzZQogICAgICAgICAgICAgICAgVGFyZ2V0Q29vcmRzICAgICAgICAgICAgICA9IG5pbAoKICAgICAgICAgICAgICBlbmQKCiAgICAgICAgICAgICAgaWYgVGFyZ2V0Q29vcmRzIH49IG5pbCB0aGVuCiAgICAgICAgICAgICAgICBEcmF3TWFya2VyKDEsIFRhcmdldENvb3Jkcy54LCBUYXJnZXRDb29yZHMueSwgVGFyZ2V0Q29vcmRzLnogLSAxLjAsIDAsIDAsIDAsIDAsIDAsIDAsIDQuMCwgNC4wLCAyLjAsIDE3OCwgMjM2LCA5MywgMTU1LCAwLCAwLCAyLCAwLCAwLCAwLCAwKQogICAgICAgICAgICAgIGVuZAoKICAgICAgICAgICAgZWxzZQoKICAgICAgICAgICAgICBSZW1vdmVCbGlwKEN1cnJlbnRDdXN0b21lckJsaXApCgogICAgICAgICAgICAgIEN1cnJlbnRDdXN0b21lckJsaXAgPSBuaWwKCiAgICAgICAgICAgICAgVGFyZ2V0Q29vcmRzID0gQ29uZmlnLkpvYkxvY2F0aW9uc1tHZXRSYW5kb21JbnRJblJhbmdlKDEsICAjQ29uZmlnLkpvYkxvY2F0aW9ucyldCgogICAgICAgICAgICAgIGxvY2FsIHN0cmVldCA9IHRhYmxlLnBhY2soR2V0U3RyZWV0TmFtZUF0Q29vcmQoVGFyZ2V0Q29vcmRzLngsIFRhcmdldENvb3Jkcy55LCBUYXJnZXRDb29yZHMueikpCiAgICAgICAgICAgICAgbG9jYWwgbXNnICAgID0gbmlsCgogICAgICAgICAgICAgIGlmIHN0cmVldFsyXSB+PSAwIGFuZCBzdHJlZXRbMl0gfj0gbmlsIHRoZW4KICAgICAgICAgICAgICAgIG1zZyA9IHN0cmluZy5mb3JtYXQoX1UoJ3Rha2VfbWVfdG9fbmVhcicsIEdldFN0cmVldE5hbWVGcm9tSGFzaEtleShzdHJlZXRbMV0pLEdldFN0cmVldE5hbWVGcm9tSGFzaEtleShzdHJlZXRbMl0pKSkKICAgICAgICAgICAgICBlbHNlCiAgICAgICAgICAgICAgICBtc2cgPSBzdHJpbmcuZm9ybWF0KF9VKCd0YWtlX21lX3RvJywgR2V0U3RyZWV0TmFtZUZyb21IYXNoS2V5KHN0cmVldFsxXSkpKQogICAgICAgICAgICAgIGVuZAoKICAgICAgICAgICAgICBFU1guU2hvd05vdGlmaWNhdGlvbihtc2cpCgogICAgICAgICAgICAgIERlc3RpbmF0aW9uQmxpcCA9IEFkZEJsaXBGb3JDb29yZChUYXJnZXRDb29yZHMueCwgVGFyZ2V0Q29vcmRzLnksIFRhcmdldENvb3Jkcy56KQoKICAgICAgICAgICAgICBCZWdpblRleHRDb21tYW5kU2V0QmxpcE5hbWUoIlNUUklORyIpCiAgICAgICAgICAgICAgQWRkVGV4dENvbXBvbmVudFN0cmluZygiRGVzdGluYXRpb24iKQogICAgICAgICAgICAgIEVuZFRleHRDb21tYW5kU2V0QmxpcE5hbWUoYmxpcCkKCiAgICAgICAgICAgICAgU2V0QmxpcFJvdXRlKERlc3RpbmF0aW9uQmxpcCwgIHRydWUpCgogICAgICAgICAgICAgIEN1c3RvbWVyRW50ZXJlZFZlaGljbGUgPSB0cnVlCgogICAgICAgICAgICBlbmQKCiAgICAgICAgICBlbHNlCgogICAgICAgICAgICBEcmF3TWFya2VyKDEsIGN1c3RvbWVyQ29vcmRzLngsIGN1c3RvbWVyQ29vcmRzLnksIGN1c3RvbWVyQ29vcmRzLnogLSAxLjAsIDAsIDAsIDAsIDAsIDAsIDAsIDQuMCwgNC4wLCAyLjAsIDE3OCwgMjM2LCA5MywgMTU1LCAwLCAwLCAyLCAwLCAwLCAwLCAwKQoKICAgICAgICAgICAgaWYgbm90IEN1c3RvbWVyRW50ZXJlZFZlaGljbGUgdGhlbgoKICAgICAgICAgICAgICBpZiBjdXN0b21lckRpc3RhbmNlIDw9IDMwLjAgdGhlbgoKICAgICAgICAgICAgICAgIGlmIG5vdCBJc05lYXJDdXN0b21lciB0aGVuCiAgICAgICAgICAgICAgICAgIEVTWC5TaG93Tm90aWZpY2F0aW9uKF9VKCdjbG9zZV90b19jbGllbnQnKSkKICAgICAgICAgICAgICAgICAgSXNOZWFyQ3VzdG9tZXIgPSB0cnVlCiAgICAgICAgICAgICAgICBlbmQKCiAgICAgICAgICAgICAgZW5kCgogICAgICAgICAgICAgIGlmIGN1c3RvbWVyRGlzdGFuY2UgPD0gMTAwLjAgdGhlbgoKICAgICAgICAgICAgICAgIGlmIG5vdCBDdXN0b21lcklzRW50ZXJpbmdWZWhpY2xlIHRoZW4KCiAgICAgICAgICAgICAgICAgIENsZWFyUGVkVGFza3NJbW1lZGlhdGVseShDdXJyZW50Q3VzdG9tZXIpCgogICAgICAgICAgICAgICAgICBsb2NhbCBzZWF0ID0gMAoKICAgICAgICAgICAgICAgICAgZm9yIGk9NCwgMCwgMSBkbwogICAgICAgICAgICAgICAgICAgIGlmIElzVmVoaWNsZVNlYXRGcmVlKHZlaGljbGUsICBzZWF0KSB0aGVuCiAgICAgICAgICAgICAgICAgICAgICBzZWF0ID0gaQogICAgICAgICAgICAgICAgICAgICAgYnJlYWsKICAgICAgICAgICAgICAgICAgICBlbmQKICAgICAgICAgICAgICAgICAgZW5kCgogICAgICAgICAgICAgICAgICBUYXNrRW50ZXJWZWhpY2xlKEN1cnJlbnRDdXN0b21lciwgIHZlaGljbGUsICAtMSwgIHNlYXQsICAyLjAsICAxKQoKICAgICAgICAgICAgICAgICAgQ3VzdG9tZXJJc0VudGVyaW5nVmVoaWNsZSA9IHRydWUKCiAgICAgICAgICAgICAgICBlbmQKCiAgICAgICAgICAgICAgZW5kCgogICAgICAgICAgICBlbmQKCiAgICAgICAgICBlbmQKCiAgICAgICAgZWxzZQoKICAgICAgICAgIERyYXdTdWIoX1UoJ3JldHVybl90b192ZWgnKSwgNTAwMCkKCiAgICAgICAgZW5kCgogICAgICBlbmQKCiAgICBlbmQKCiAgZW5kCmVuZCkKCi0tIEtleSBDb250cm9scwpDaXRpemVuLkNyZWF0ZVRocmVhZChmdW5jdGlvbigpCiAgd2hpbGUgdHJ1ZSBkbwoKICAgIENpdGl6ZW4uV2FpdCgwKQoKICAgIGlmIEN1cnJlbnRBY3Rpb24gfj0gbmlsIHRoZW4KCiAgICAgIFNldFRleHRDb21wb25lbnRGb3JtYXQoJ1NUUklORycpCiAgICAgIEFkZFRleHRDb21wb25lbnRTdHJpbmcoQ3VycmVudEFjdGlvbk1zZykKICAgICAgRGlzcGxheUhlbHBUZXh0RnJvbVN0cmluZ0xhYmVsKDAsIDAsIDEsIC0xKQoKICAgICAgaWYgSXNDb250cm9sUHJlc3NlZCgwLCAgS2V5c1snRSddKSBhbmQgUGxheWVyRGF0YS5qb2Igfj0gbmlsIGFuZCBQbGF5ZXJEYXRhLmpvYi5uYW1lID09ICd0YXhpJyBhbmQgKEdldEdhbWVUaW1lcigpIC0gR1VJLlRpbWUpID4gMzAwIHRoZW4KCiAgICAgICAgaWYgQ3VycmVudEFjdGlvbiA9PSAndGF4aV9hY3Rpb25zX21lbnUnIHRoZW4KICAgICAgICAgIE9wZW5UYXhpQWN0aW9uc01lbnUoKQogICAgICAgIGVuZAoKICAgICAgICBpZiBDdXJyZW50QWN0aW9uID09ICdkZWxldGVfdmVoaWNsZScgdGhlbgoKICAgICAgICAgIGxvY2FsIHBsYXllclBlZCA9IEdldFBsYXllclBlZCgtMSkKCiAgICAgICAgICBpZiBDb25maWcuRW5hYmxlU29jaWV0eU93bmVkVmVoaWNsZXMgdGhlbgogICAgICAgICAgICBsb2NhbCB2ZWhpY2xlUHJvcHMgPSBFU1guR2FtZS5HZXRWZWhpY2xlUHJvcGVydGllcyhDdXJyZW50QWN0aW9uRGF0YS52ZWhpY2xlKQogICAgICAgICAgICBUcmlnZ2VyU2VydmVyRXZlbnQoJ2VzeF9zb2NpZXR5OnB1dFZlaGljbGVJbkdhcmFnZScsICd0YXhpJywgdmVoaWNsZVByb3BzKQogICAgICAgICAgZWxzZQogICAgICAgICAgICBpZiBHZXRFbnRpdHlNb2RlbChDdXJyZW50QWN0aW9uRGF0YS52ZWhpY2xlKSA9PSBHZXRIYXNoS2V5KCd0YXhpJykgdGhlbgogICAgICAgICAgICAgIGlmIENvbmZpZy5NYXhJblNlcnZpY2Ugfj0gLTEgdGhlbgogICAgICAgICAgICAgICAgVHJpZ2dlclNlcnZlckV2ZW50KCdlc3hfc2VydmljZTpkaXNhYmxlU2VydmljZScsICd0YXhpJykKICAgICAgICAgICAgICBlbmQKICAgICAgICAgICAgZW5kCiAgICAgICAgICBlbmQKCiAgICAgICAgICBFU1guR2FtZS5EZWxldGVWZWhpY2xlKEN1cnJlbnRBY3Rpb25EYXRhLnZlaGljbGUpCgogICAgICAgIGVuZAoKICAgICAgICBDdXJyZW50QWN0aW9uID0gbmlsCiAgICAgICAgR1VJLlRpbWUgICAgICA9IEdldEdhbWVUaW1lcigpCgogICAgICBlbmQKCiAgICBlbmQKCiAgICBpZiBJc0NvbnRyb2xQcmVzc2VkKDAsICBLZXlzWydGMiddKSBhbmQgQ29uZmlnLkVuYWJsZVBsYXllck1hbmFnZW1lbnQgYW5kIFBsYXllckRhdGEuam9iIH49IG5pbCBhbmQgUGxheWVyRGF0YS5qb2IubmFtZSA9PSAndGF4aScgYW5kIChHZXRHYW1lVGltZXIoKSAtIEdVSS5UaW1lKSA+IDE1MCB0aGVuCiAgICAgIE9wZW5Nb2JpbGVUYXhpQWN0aW9uc01lbnUoKQogICAgICBHVUkuVGltZSA9IEdldEdhbWVUaW1lcigpCiAgICBlbmQKCiAgICBpZiBJc0NvbnRyb2xQcmVzc2VkKDAsICBLZXlzWydERUxFVEUnXSkgYW5kIChHZXRHYW1lVGltZXIoKSAtIEdVSS5UaW1lKSA+IDE1MCB0aGVuCgogICAgICBpZiBPbkpvYiB0aGVuCiAgICAgICAgU3RvcFRheGlKb2IoKQogICAgICBlbHNlCgogICAgICAgIGlmIFBsYXllckRhdGEuam9iIH49IG5pbCBhbmQgUGxheWVyRGF0YS5qb2IubmFtZSA9PSAndGF4aScgdGhlbgoKICAgICAgICAgIGxvY2FsIHBsYXllclBlZCA9IEdldFBsYXllclBlZCgtMSkKCiAgICAgICAgICBpZiBJc1BlZEluQW55VmVoaWNsZShwbGF5ZXJQZWQsICBmYWxzZSkgdGhlbgoKICAgICAgICAgICAgbG9jYWwgdmVoaWNsZSA9IEdldFZlaGljbGVQZWRJc0luKHBsYXllclBlZCwgIGZhbHNlKQoKICAgICAgICAgICAgaWYgUGxheWVyRGF0YS5qb2IuZ3JhZGUgPj0gMyB0aGVuCiAgICAgICAgICAgICAgU3RhcnRUYXhpSm9iKCkKICAgICAgICAgICAgZWxzZQogICAgICAgICAgICAgIGlmIEdldEVudGl0eU1vZGVsKHZlaGljbGUpID09IEdldEhhc2hLZXkoJ3RheGknKSB0aGVuCiAgICAgICAgICAgICAgICBTdGFydFRheGlKb2IoKQogICAgICAgICAgICAgIGVsc2UKICAgICAgICAgICAgICAgIEVTWC5TaG93Tm90aWZpY2F0aW9uKF9VKCdtdXN0X2luX3RheGknKSkKICAgICAgICAgICAgICBlbmQKICAgICAgICAgICAgZW5kCgogICAgICAgICAgZWxzZQoKICAgICAgICAgICAgaWYgUGxheWVyRGF0YS5qb2IuZ3JhZGUgPj0gMyB0aGVuCiAgICAgICAgICAgICAgRVNYLlNob3dOb3RpZmljYXRpb24oX1UoJ211c3RfaW5fdmVoaWNsZScpKQogICAgICAgICAgICBlbHNlCiAgICAgICAgICAgICAgRVNYLlNob3dOb3RpZmljYXRpb24oX1UoJ211c3RfaW5fdGF4aScpKQogICAgICAgICAgICBlbmQKCiAgICAgICAgICBlbmQKCiAgICAgICAgZW5kCgogICAgICBlbmQKCiAgICAgIEdVSS5UaW1lID0gR2V0R2FtZVRpbWVyKCkKCiAgICBlbmQKCiAgZW5kCmVuZCkK