From 5b0ee02e11c60f78a2baa032d254baec8d3a2048 Mon Sep 17 00:00:00 2001 From: szporowolik Date: Wed, 23 Oct 2019 20:19:25 +0200 Subject: [PATCH] v0.9.0 & code cleanup in lua file --- 01_DCS/Hooks/Perun.lua | 693 +++++++++++++++++++++-------------------- 1 file changed, 359 insertions(+), 334 deletions(-) diff --git a/01_DCS/Hooks/Perun.lua b/01_DCS/Hooks/Perun.lua index 3be720b..a1ba6a9 100644 --- a/01_DCS/Hooks/Perun.lua +++ b/01_DCS/Hooks/Perun.lua @@ -7,20 +7,22 @@ package.cpath = package.cpath..";"..lfs.currentdir().."/LuaSocket/?.dll" -- ###################### SETTINGS - DO NOT MODIFY OUTSIDE THIS SECTION ############################# -Perun.RefreshStatus = 15 -- (int) base refresh rate in seconds to send status update (values lower than 60 may affect performance!) -Perun.RefreshMission = 60 -- (int) refresh rate in seconds to send mission information (values lower than 60 may affect performance!) -Perun.TCPTargetPort = 48622 -- (int) TCP port to send data to -Perun.TCPPerunHost = "localhost" -- (string) IP adress of the Perun instance or "localhost" -Perun.Instance = 2 -- (int) Id number of instance (if multiple DCS instances are to run at the same PC) -Perun.JsonStatusLocation = "Scripts\\Json\\" -- (string) folder relative do user's SaveGames DCS folder -> status file updated each RefreshMission -Perun.MOTD_L1 = "Witamy na serwerze Gildia.org !" -- (string) Message send to players connecting the server - Line 1 +Perun.RefreshStatus = 15 -- (int) [default: 60] Base refresh rate in seconds to send status update +Perun.RefreshMission = 60 -- (int) [default: 120] Refresh rate in seconds to send mission information +Perun.TCPTargetPort = 48622 -- (int) [default: 48621] TCP port to send data to +Perun.TCPPerunHost = "localhost" -- (string) [default: "localhost"] IP adress of the Perun instance or "localhost" +Perun.Instance = 2 -- (int) [default: 1] Id number of instance (if multiple DCS instances are to run at the same PC) +Perun.JsonStatusLocation = "Scripts\\Json\\" -- (string) [default: "Scripts\\Json\\"] Folder relative do user's SaveGames DCS folder -> status file updated each RefreshMission +Perun.MissionStartNoDeathWindow = 300 -- (int) [default: 300] Number of secounds after mission start when death of the pilot will not go to statistics, shall avoid death penalty during spawning DCS bugs +Perun.DebugMode = 2 -- (int) [0 (default),1,2] Value greater than 0 will display Perun information in DCS log file, values: 1 - minimal verbose, 2 - all log information will be logged +Perun.LogServerMessages = 1 -- (int) [0,1 (default)] Set to 1 if you want to log also the chat messages send by server +Perun.MOTD_L1 = "Witamy na serwerze Gildia.org !" -- (string) Message send to players connecting the server - Line 1 Perun.MOTD_L2 = "Wymagamy obecnosci DCS SRS oraz TeamSpeak - szczegoly na forum" -- (string) Message send to players connecting the server - Line 2 -- ###################### END OF SETTINGS - DO NOT MODIFY OUTSIDE THIS SECTION ###################### - -- Variable init -Perun.Version = "v0.8.3" +Perun.Version = "v0.9.0" Perun.StatusData = {} Perun.SlotsData = {} Perun.MissionData = {} @@ -28,108 +30,113 @@ Perun.ServerData = {} Perun.StatData = {} Perun.StatDataLastType = {} Perun.MissionHash="" -Perun.lastSentStatus =0 -Perun.lastSentMission =0 -Perun.lastSentKeepAlive =0 +Perun.lastSentStatus = 0 +Perun.lastSentMission = 0 +Perun.lastSentKeepAlive = 0 Perun.lastReconnect = 0 +Perun.SendRetries = 3 +Perun.RefreshKeepAlive = 3 Perun.JsonStatusLocation = lfs.writedir() .. Perun.JsonStatusLocation Perun.socket = require("socket") Perun.IsServer = true --DCS.isServer( ) -- TBD looks like DCS API error, always returning True -- ########### Helper function definitions ########### function stripChars(str) - -- remove accents characters from string + -- Hellper functions removes accents characters from string -- via https://stackoverflow.com/questions/50459102/replace-accented-characters-in-string-to-standard-with-lua - tableAccents = {} - tableAccents["à"] = "a" - tableAccents["á"] = "a" - tableAccents["â"] = "a" - tableAccents["ã"] = "a" - tableAccents["ä"] = "a" - tableAccents["ç"] = "c" - tableAccents["è"] = "e" - tableAccents["é"] = "e" - tableAccents["ê"] = "e" - tableAccents["ë"] = "e" - tableAccents["ì"] = "i" - tableAccents["í"] = "i" - tableAccents["î"] = "i" - tableAccents["ï"] = "i" - tableAccents["ñ"] = "n" - tableAccents["ò"] = "o" - tableAccents["ó"] = "o" - tableAccents["ô"] = "o" - tableAccents["õ"] = "o" - tableAccents["ö"] = "o" - tableAccents["ù"] = "u" - tableAccents["ú"] = "u" - tableAccents["û"] = "u" - tableAccents["ü"] = "u" - tableAccents["ý"] = "y" - tableAccents["ÿ"] = "y" - tableAccents["À"] = "A" - tableAccents["Á"] = "A" - tableAccents["Â"] = "A" - tableAccents["Ã"] = "A" - tableAccents["Ä"] = "A" - tableAccents["Ç"] = "C" - tableAccents["È"] = "E" - tableAccents["É"] = "E" - tableAccents["Ê"] = "E" - tableAccents["Ë"] = "E" - tableAccents["Ì"] = "I" - tableAccents["Í"] = "I" - tableAccents["Î"] = "I" - tableAccents["Ï"] = "I" - tableAccents["Ñ"] = "N" - tableAccents["Ò"] = "O" - tableAccents["Ó"] = "O" - tableAccents["Ô"] = "O" - tableAccents["Õ"] = "O" - tableAccents["Ö"] = "O" - tableAccents["Ù"] = "U" - tableAccents["Ú"] = "U" - tableAccents["Û"] = "U" - tableAccents["Ü"] = "U" - tableAccents["Ý"] = "Y" + local _tableAccents = {} + _tableAccents["à"] = "a" + _tableAccents["á"] = "a" + _tableAccents["â"] = "a" + _tableAccents["ã"] = "a" + _tableAccents["ä"] = "a" + _tableAccents["ç"] = "c" + _tableAccents["è"] = "e" + _tableAccents["é"] = "e" + _tableAccents["ê"] = "e" + _tableAccents["ë"] = "e" + _tableAccents["ì"] = "i" + _tableAccents["í"] = "i" + _tableAccents["î"] = "i" + _tableAccents["ï"] = "i" + _tableAccents["ñ"] = "n" + _tableAccents["ò"] = "o" + _tableAccents["ó"] = "o" + _tableAccents["ô"] = "o" + _tableAccents["õ"] = "o" + _tableAccents["ö"] = "o" + _tableAccents["ù"] = "u" + _tableAccents["ú"] = "u" + _tableAccents["û"] = "u" + _tableAccents["ü"] = "u" + _tableAccents["ý"] = "y" + _tableAccents["ÿ"] = "y" + _tableAccents["À"] = "A" + _tableAccents["Á"] = "A" + _tableAccents["Â"] = "A" + _tableAccents["Ã"] = "A" + _tableAccents["Ä"] = "A" + _tableAccents["Ç"] = "C" + _tableAccents["È"] = "E" + _tableAccents["É"] = "E" + _tableAccents["Ê"] = "E" + _tableAccents["Ë"] = "E" + _tableAccents["Ì"] = "I" + _tableAccents["Í"] = "I" + _tableAccents["Î"] = "I" + _tableAccents["Ï"] = "I" + _tableAccents["Ñ"] = "N" + _tableAccents["Ò"] = "O" + _tableAccents["Ó"] = "O" + _tableAccents["Ô"] = "O" + _tableAccents["Õ"] = "O" + _tableAccents["Ö"] = "O" + _tableAccents["Ù"] = "U" + _tableAccents["Ú"] = "U" + _tableAccents["Û"] = "U" + _tableAccents["Ü"] = "U" + _tableAccents["Ý"] = "Y" -- Polish accents - tableAccents["ę"] = "e" - tableAccents["Ę"] = "Ę" - tableAccents["ó"] = "o" - tableAccents["Ó"] = "O" - tableAccents["ą"] = "a" - tableAccents["Ą"] = "A" - tableAccents["ś"] = "s" - tableAccents["Ś"] = "S" - tableAccents["ć"] = "c" - tableAccents["Ć"] = "C" - tableAccents["ż"] = "z" - tableAccents["Ż"] = "Z" - tableAccents["ź"] = "z" - tableAccents["Ź"] = "Z" - tableAccents["ł"] = "l" - tableAccents["Ł"] = "L" + _tableAccents["ę"] = "e" + _tableAccents["Ę"] = "Ę" + _tableAccents["ó"] = "o" + _tableAccents["Ó"] = "O" + _tableAccents["ą"] = "a" + _tableAccents["Ą"] = "A" + _tableAccents["ś"] = "s" + _tableAccents["Ś"] = "S" + _tableAccents["ć"] = "c" + _tableAccents["Ć"] = "C" + _tableAccents["ż"] = "z" + _tableAccents["Ż"] = "Z" + _tableAccents["ź"] = "z" + _tableAccents["Ź"] = "Z" + _tableAccents["ł"] = "l" + _tableAccents["Ł"] = "L" - -- TBD additonal characters + -- TBD additonal characters for other languages - normalizedString = '' - - for strChar in string.gmatch(str, "([%z\1-\127\194-\244][\128-\191]*)") do - if tableAccents[strChar] ~= nil then - normalizedString = normalizedString..tableAccents[strChar] + -- Check string and replace special chars via replacement table + local _normalizedString = '' + for _strChar in string.gmatch(str, "([%z\1-\127\194-\244][\128-\191]*)") do + if _tableAccents[_strChar] ~= nil then + -- Replace char + _normalizedString = _normalizedString.._tableAccents[_strChar] else - normalizedString = normalizedString..strChar + -- No need to replace + _normalizedString = _normalizedString.._strChar end end - return normalizedString + return _normalizedString end Perun.GetCategory = function(id) - -- Returns object category basing on ID - -- Helper function via https://pastebin.com/GUAXrd2U TBD: rewrite + -- Helper function returns object category basing on I via https://pastebin.com/GUAXrd2U TBD: rewrite local _killed_target_category = DCS.getUnitTypeAttribute(id, "category") - if _killed_target_category == nil then + + -- Below, simple hack to get the propper category when DCS API is not returning correct value + if _killed_target_category == nil then local _killed_target_cat_check_ship = DCS.getUnitTypeAttribute(id, "DeckLevel") local _killed_target_cat_check_plane = DCS.getUnitTypeAttribute(id, "WingSpan") if _killed_target_cat_check_ship ~= nil and _killed_target_cat_check_plane == nil then @@ -145,86 +152,89 @@ end Perun.SideID2Name = function(id) -- Helper function returns side name per side (coalition) id - local sides = { + local _sides = { [0] = 'SPECTATOR', [1] = 'RED', [2] = 'BLUE', + [3] = 'NEUTRAL', -- TBD check once this is released in DCS } - return sides[id] -end - -Perun.UpdateStatusPart = function(part_id, data_package) - -- Helper for status update container - Perun.StatusData[part_id] = data_package + return _sides[id] end -- ########### Main code ########### -Perun.AddLog = function(text) +Perun.AddLog = function(text,LogLevel) -- Adds logs to DCS.log file - net.log("Perun : ".. text) + LogLevel = LogLevel or 1 + if Perun.DebugMode == LogLevel then + net.log("Perun : ".. text) + end end Perun.UpdateJsonStatus = function() -- Updates status json file - TempData={} - TempData["1"]=Perun.ServerData - TempData["2"]=Perun.StatusData - TempData["3"]=Perun.SlotsData - -- TempData["4"]=Perun.MissionData -- TBD: hangs for some large missions + local _TempData={} + _TempData["1"]=Perun.ServerData + _TempData["2"]=Perun.StatusData + _TempData["3"]=Perun.SlotsData + -- _TempData["4"]=Perun.MissionData -- TBD: hangs for some large missions - _temp=net.lua2json(TempData) - - perun_export = io.open(Perun.JsonStatusLocation .. "perun_status_data.json", "w") - perun_export:write(_temp .. "\n") - perun_export:close() - Perun.AddLog("Updated JSON") + -- Export data to JSON file + local _perun_export = io.open(Perun.JsonStatusLocation .. "perun_status_data.json", "w") + _perun_export:write(net.lua2json(_TempData) .. "\n") + _perun_export:close() + Perun.AddLog("Updated JSON",2) end Perun.ConnectToPerun = function () -- Connects to Perun server - Perun.AddLog("Connecting to TCP server") + Perun.AddLog("Connecting to TCP server",2) Perun.TCP = assert(Perun.socket.tcp()) Perun.TCP:settimeout(5000) - _, err = Perun.TCP:connect(Perun.TCPPerunHost, Perun.TCPTargetPort) - if err then - Perun.AddLog("ERROR - TCP connection error : " .. err) + _, _err = Perun.TCP:connect(Perun.TCPPerunHost, Perun.TCPTargetPort) + if _err then + -- Could not connect + Perun.AddLog("ERROR - TCP connection error : " .. _err) else - Perun.AddLog("Sucess - connected to TCP server") - -- Perun.TCP:setoption("keepalive") + -- Connected + Perun.AddLog("Sucess - connected to TCP server",2) Perun.lastReconnect = _now end end Perun.SendToPerun = function(data_id, data_package) -- Prepares and sends data package - -- Prepare data - TempData={} - TempData["type"]=data_id - TempData["payload"]=data_package - TempData["timestamp"]=os.date('%Y-%m-%d %H:%M:%S') - TempData["instance"]=Perun.Instance + local _TempData={} + _TempData["type"]=data_id + _TempData["payload"]=data_package + _TempData["timestamp"]=os.date('%Y-%m-%d %H:%M:%S') + _TempData["instance"]=Perun.Instance - temp=net.lua2json(TempData) - temp="" .. stripChars(temp) .. "" + -- Build TCP frame + local _TCPFrame="" .. stripChars(net.lua2json(_TempData)) .. "" -- TCP Part - sending - intStatus = nil - intTries =0 - err=nil - while intStatus == nil and intTries < 3 do - intStatus, err = Perun.TCP:send(temp) - if err then - Perun.AddLog("Packed not send : " .. data_id .. " , error: " .. err .. ", tries: " .. intTries) + local _intStatus = nil + local _intTries =0 + local _err=nil + + -- Try to send a few times (defind in settings section) + while _intStatus == nil and _intTries < Perun.SendRetries do + _intStatus, _err = Perun.TCP:send(_TCPFrame) + if _err then + -- Failure, packet was not send + Perun.AddLog("Packed not send : " .. data_id .. " , error: " .. _err .. ", tries: " .. _intTries,2) Perun.ConnectToPerun() else - Perun.AddLog("Packet send : " .. data_id .. " , tries:" .. intTries) + -- Succes, packet was send + Perun.AddLog("Packet send : " .. data_id .. " , tries:" .. _intTries,2) end - intTries=intTries+1 - err = nil + _intTries=_intTries+1 + _err = nil end - if err then + if _err then + -- Add information to log file Perun.AddLog("ERROR - packed dropped : " .. data_id) end end @@ -236,266 +246,266 @@ Perun.UpdateStatus = function() -- Update version data Perun.ServerData['v_dcs_hook']=Perun.Version - -- Update clients data data - _table=net.get_player_list() + -- Update clients data - count connected players + _playerlist=net.get_player_list() _count = 0 - for _ in pairs(_table) do _count = _count + 1 end + for _ in pairs(_playerlist) do _count = _count + 1 end Perun.ServerData['c_players']=_count -- Send + Perun.AddLog("Sending server data",2) Perun.SendToPerun(1,Perun.ServerData) -- Status data - update all subsections - -- 1 - Mission - temp={} - temp['name']=DCS.getMissionName() - temp['modeltime']=DCS.getModelTime() - temp['realtime']=DCS.getRealTime() - temp['pause']=DCS.getPause() - temp['multiplayer']=DCS.isMultiplayer() - temp['theatre'] = Perun.MissionData['mission']['theatre'] - temp['weather'] = Perun.MissionData['mission']['weather'] - Perun.UpdateStatusPart("mission",temp) + -- 1 - Mission data + local _TempData={} + _TempData['name']=DCS.getMissionName() + _TempData['modeltime']=DCS.getModelTime() + _TempData['realtime']=DCS.getRealTime() + _TempData['pause']=DCS.getPause() + _TempData['multiplayer']=DCS.isMultiplayer() + _TempData['theatre'] = Perun.MissionData['mission']['theatre'] + _TempData['weather'] = Perun.MissionData['mission']['weather'] + Perun.StatusData["mission"] = _TempData - -- 2 - Players - _temp = net.get_player_list() - _temp2={} - for k, i in ipairs(_temp) do - _temp2[k]=net.get_player_info(i) + -- 2 - Players data + _PlayersTable={} + for _k, _i in ipairs(_playerlist) do + _PlayersTable[_k]=net.get_player_info(_i) end - Perun.UpdateStatusPart("players",_temp2) + Perun.StatusData["players"] = _PlayersTable -- Send + Perun.AddLog("Sending status data",2) Perun.SendToPerun(2,Perun.StatusData) -- Update slots data Perun.SlotsData['coalitions']=DCS.getAvailableCoalitions() Perun.SlotsData['slots']={} - for j, i in pairs(Perun.SlotsData['coalitions']) do - Perun.SlotsData['slots'][j]=DCS.getAvailableSlots(j) + -- Build up slot table + for _j, _i in pairs(Perun.SlotsData['coalitions']) do + Perun.SlotsData['slots'][_j]=DCS.getAvailableSlots(_j) - for sj, si in pairs(Perun.SlotsData['slots'][j]) do - Perun.SlotsData['slots'][j][sj]['countryName']= nil - Perun.SlotsData['slots'][j][sj]['onboard_num']= nil - Perun.SlotsData['slots'][j][sj]['groupSize']= nil - Perun.SlotsData['slots'][j][sj]['groupName']= nil - Perun.SlotsData['slots'][j][sj]['callsign']= nil - Perun.SlotsData['slots'][j][sj]['task']= nil - Perun.SlotsData['slots'][j][sj]['airdromeId']= nil - Perun.SlotsData['slots'][j][sj]['helipadName']= nil + for _sj, _si in pairs(Perun.SlotsData['slots'][_j]) do + Perun.SlotsData['slots'][_j][_sj]['countryName']= nil + Perun.SlotsData['slots'][_j][_sj]['onboard_num']= nil + Perun.SlotsData['slots'][_j][_sj]['groupSize']= nil + Perun.SlotsData['slots'][_j][_sj]['groupName']= nil + Perun.SlotsData['slots'][_j][_sj]['callsign']= nil + Perun.SlotsData['slots'][_j][_sj]['task']= nil + Perun.SlotsData['slots'][_j][_sj]['airdromeId']= nil + Perun.SlotsData['slots'][_j][_sj]['helipadName']= nil end - end + -- Send + Perun.AddLog("Sending slots data",2) Perun.SendToPerun(3,Perun.SlotsData) end - Perun.UpdateMission = function() -- Main function for mission information updates + Perun.AddLog("Updating mission data",2) Perun.MissionData=DCS.getCurrentMission() -- Perun.SendToPerun(4,Perun.MissionData) -- TBD can cause data transmission troubles + Perun.AddLog("Mission data updated",2) end Perun.LogChat = function(playerID,msg,all) -- Logs chat messages + local _TempData={} + _TempData['player']= net.get_player_info(playerID, "name") + _TempData['msg']=stripChars(msg) + _TempData['all']=all + _TempData['ucid']=net.get_player_info(playerID, 'ucid') + _TempData['datetime']=os.date('%Y-%m-%d %H:%M:%S') + _TempData['missionhash']=Perun.MissionHash - data={} - data['player']= net.get_player_info(playerID, "name") - data['msg']=stripChars(msg) - data['all']=all - data['ucid']=net.get_player_info(playerID, 'ucid') - data['datetime']=os.date('%Y-%m-%d %H:%M:%S') - data['missionhash']=Perun.MissionHash - - Perun.SendToPerun(50,data) + Perun.AddLog("Sending chat message",2) + Perun.SendToPerun(50,_TempData) end Perun.LogEvent = function(log_type,log_content,log_arg_1,log_arg_2) -- Logs events messages + local _TempData={} + _TempData['log_type']= log_type + _TempData['log_arg_1']= log_arg_1 + _TempData['log_arg_2']= log_arg_2 + _TempData['log_content']=log_content + _TempData['log_datetime']=os.date('%Y-%m-%d %H:%M:%S') + _TempData['log_missionhash']=Perun.MissionHash - data={} - data['log_type']= log_type - data['log_arg_1']= log_arg_1 - data['log_arg_2']= log_arg_2 - data['log_content']=log_content - data['log_datetime']=os.date('%Y-%m-%d %H:%M:%S') - data['log_missionhash']=Perun.MissionHash - - Perun.SendToPerun(51,data) + Perun.AddLog("Sending event data, event: "..log_type .. ", arg1:" .. log_arg_1 .. ", arg2:" .. log_arg_2 .. ", content: " .. log_content,2) + Perun.SendToPerun(51,_TempData) end Perun.LogStatsCount = function(argPlayerID,argAction,argType) -- Creates or updates Perun statistics array - _ucid=net.get_player_info(argPlayerID, 'ucid')..argType + local _player_hash=net.get_player_info(argPlayerID, 'ucid')..argType - if Perun.StatData[_ucid] == nil then + if Perun.StatData[_player_hash] == nil then -- Create empty element - _temp={} - _temp['ps_type'] = argType - _temp['ps_pvp'] = 0 - _temp['ps_deaths'] = 0 - _temp['ps_ejections'] = 0 - _temp['ps_crashes'] = 0 - _temp['ps_teamkills'] = 0 - _temp['ps_kills_planes'] = 0 - _temp['ps_kills_helicopters'] = 0 - _temp['ps_kills_air_defense'] = 0 - _temp['ps_kills_armor'] = 0 - _temp['ps_kills_unarmed'] = 0 - _temp['ps_kills_infantry'] = 0 - _temp['ps_kills_ships'] = 0 - _temp['ps_kills_fortification'] = 0 - _temp['ps_kills_other'] = 0 - _temp['ps_airfield_takeoffs'] = 0 - _temp['ps_airfield_landings'] = 0 - _temp['ps_ship_takeoffs'] = 0 - _temp['ps_ship_landings'] = 0 - _temp['ps_farp_takeoffs'] = 0 - _temp['ps_farp_landings'] = 0 - _temp['ps_other_takeoffs'] = 0 - _temp['ps_other_landings'] = 0 + local _TempData={} + _TempData['ps_type'] = argType + _TempData['ps_pvp'] = 0 + _TempData['ps_deaths'] = 0 + _TempData['ps_ejections'] = 0 + _TempData['ps_crashes'] = 0 + _TempData['ps_teamkills'] = 0 + _TempData['ps_kills_planes'] = 0 + _TempData['ps_kills_helicopters'] = 0 + _TempData['ps_kills_air_defense'] = 0 + _TempData['ps_kills_armor'] = 0 + _TempData['ps_kills_unarmed'] = 0 + _TempData['ps_kills_infantry'] = 0 + _TempData['ps_kills_ships'] = 0 + _TempData['ps_kills_fortification'] = 0 + _TempData['ps_kills_other'] = 0 + _TempData['ps_airfield_takeoffs'] = 0 + _TempData['ps_airfield_landings'] = 0 + _TempData['ps_ship_takeoffs'] = 0 + _TempData['ps_ship_landings'] = 0 + _TempData['ps_farp_takeoffs'] = 0 + _TempData['ps_farp_landings'] = 0 + _TempData['ps_other_takeoffs'] = 0 + _TempData['ps_other_landings'] = 0 - Perun.StatData[_ucid]=_temp + Perun.StatData[_player_hash]=_TempData end if argType ~= nil then - Perun.StatData[_ucid]['ps_type']=argType; - Perun.StatDataLastType[net.get_player_info(argPlayerID, 'ucid')]=_ucid + Perun.StatData[_player_hash]['ps_type']=argType; + Perun.StatDataLastType[net.get_player_info(argPlayerID, 'ucid')]=_player_hash else -- Do nothing end if argAction == "eject" then - Perun.StatData[_ucid]['ps_ejections']=Perun.StatData[_ucid]['ps_ejections']+1 + Perun.StatData[_player_hash]['ps_ejections']=Perun.StatData[_player_hash]['ps_ejections']+1 elseif argAction == "pilot_death" then - if DCS.getModelTime() > 300 then - -- we do not track deaths during first 5 minutes due to spawning issues TBD - Perun.StatData[_ucid]['ps_deaths']=Perun.StatData[_ucid]['ps_deaths']+1 + if DCS.getModelTime() > Perun.MissionStartNoDeathWindow then + -- we do not track deaths during mission startup due to spawning issues + Perun.StatData[_player_hash]['ps_deaths']=Perun.StatData[_player_hash]['ps_deaths']+1 end elseif argAction == "friendly_fire" then - Perun.StatData[_ucid]['ps_teamkills']=Perun.StatData[_ucid]['ps_teamkills']+1 + Perun.StatData[_player_hash]['ps_teamkills']=Perun.StatData[_player_hash]['ps_teamkills']+1 elseif argAction == "crash" then - Perun.StatData[_ucid]['ps_crashes']=Perun.StatData[_ucid]['ps_crashes']+1 + Perun.StatData[_player_hash]['ps_crashes']=Perun.StatData[_player_hash]['ps_crashes']+1 elseif argAction == "landing_FARP" then - Perun.StatData[_ucid]['ps_farp_landings']=Perun.StatData[_ucid]['ps_farp_landings']+1 + Perun.StatData[_player_hash]['ps_farp_landings']=Perun.StatData[_player_hash]['ps_farp_landings']+1 elseif argAction == "landing_SHIP" then - Perun.StatData[_ucid]['ps_ship_landings']=Perun.StatData[_ucid]['ps_ship_landings']+1 + Perun.StatData[_player_hash]['ps_ship_landings']=Perun.StatData[_player_hash]['ps_ship_landings']+1 elseif argAction == "landing_AIRFIELD" then - Perun.StatData[_ucid]['ps_airfield_landings']=Perun.StatData[_ucid]['ps_airfield_landings']+1 + Perun.StatData[_player_hash]['ps_airfield_landings']=Perun.StatData[_player_hash]['ps_airfield_landings']+1 elseif argAction == "tookoff_FARP" then - Perun.StatData[_ucid]['ps_farp_takeoffs']=Perun.StatData[_ucid]['ps_farp_takeoffs']+1 + Perun.StatData[_player_hash]['ps_farp_takeoffs']=Perun.StatData[_player_hash]['ps_farp_takeoffs']+1 elseif argAction == "tookoff_SHIP" then - Perun.StatData[_ucid]['ps_ship_takeoffs']=Perun.StatData[_ucid]['ps_ship_takeoffs']+1 + Perun.StatData[_player_hash]['ps_ship_takeoffs']=Perun.StatData[_player_hash]['ps_ship_takeoffs']+1 elseif argAction == "tookoff_AIRFIELD" then - Perun.StatData[_ucid]['ps_airfield_takeoffs']=Perun.StatData[_ucid]['ps_airfield_takeoffs']+1 + Perun.StatData[_player_hash]['ps_airfield_takeoffs']=Perun.StatData[_player_hash]['ps_airfield_takeoffs']+1 elseif argAction == "kill_Planes" then - Perun.StatData[_ucid]['ps_kills_planes']=Perun.StatData[_ucid]['ps_kills_planes']+1 + Perun.StatData[_player_hash]['ps_kills_planes']=Perun.StatData[_player_hash]['ps_kills_planes']+1 elseif argAction == "kill_Helicopters" then - Perun.StatData[_ucid]['ps_kills_helicopters']=Perun.StatData[_ucid]['ps_kills_helicopters']+1 + Perun.StatData[_player_hash]['ps_kills_helicopters']=Perun.StatData[_player_hash]['ps_kills_helicopters']+1 elseif argAction == "kill_Ships" then - Perun.StatData[_ucid]['ps_kills_ships']=Perun.StatData[_ucid]['ps_kills_ships']+1 + Perun.StatData[_player_hash]['ps_kills_ships']=Perun.StatData[_player_hash]['ps_kills_ships']+1 elseif argAction == "kill_Air_Defence" then - Perun.StatData[_ucid]['ps_kills_air_defense']=Perun.StatData[_ucid]['ps_kills_air_defense']+1 + Perun.StatData[_player_hash]['ps_kills_air_defense']=Perun.StatData[_player_hash]['ps_kills_air_defense']+1 elseif argAction == "kill_Unarmed" then - Perun.StatData[_ucid]['ps_kills_unarmed']=Perun.StatData[_ucid]['ps_kills_unarmed']+1 + Perun.StatData[_player_hash]['ps_kills_unarmed']=Perun.StatData[_player_hash]['ps_kills_unarmed']+1 elseif argAction == "kill_Armor" then - Perun.StatData[_ucid]['ps_kills_armor']=Perun.StatData[_ucid]['ps_kills_armor']+1 + Perun.StatData[_player_hash]['ps_kills_armor']=Perun.StatData[_player_hash]['ps_kills_armor']+1 elseif argAction == "kill_Infantry" then - Perun.StatData[_ucid]['ps_kills_infantry']=Perun.StatData[_ucid]['ps_kills_infantry']+1 + Perun.StatData[_player_hash]['ps_kills_infantry']=Perun.StatData[_player_hash]['ps_kills_infantry']+1 elseif argAction == "kill_Fortification" then - Perun.StatData[_ucid]['ps_kills_fortification']=Perun.StatData[_ucid]['ps_kills_fortification']+1 + Perun.StatData[_player_hash]['ps_kills_fortification']=Perun.StatData[_player_hash]['ps_kills_fortification']+1 elseif argAction == "kill_Other" then - Perun.StatData[_ucid]['ps_kills_other']=Perun.StatData[_ucid]['ps_kills_other']+1 + Perun.StatData[_player_hash]['ps_kills_other']=Perun.StatData[_player_hash]['ps_kills_other']+1 elseif argAction == "kill_PvP" then - Perun.StatData[_ucid]['ps_pvp']=Perun.StatData[_ucid]['ps_pvp']+1 + Perun.StatData[_player_hash]['ps_pvp']=Perun.StatData[_player_hash]['ps_pvp']+1 elseif argAction == "landing_OTHER" then - Perun.StatData[_ucid]['ps_other_landings']=Perun.StatData[_ucid]['ps_other_landings']+1 + Perun.StatData[_player_hash]['ps_other_landings']=Perun.StatData[_player_hash]['ps_other_landings']+1 elseif argAction == "tookoff_OTHER" then - Perun.StatData[_ucid]['ps_other_takeoffs']=Perun.StatData[_ucid]['ps_other_takeoffs']+1 + Perun.StatData[_player_hash]['ps_other_takeoffs']=Perun.StatData[_player_hash]['ps_other_takeoffs']+1 end + Perun.AddLog("Stats data prepared",2) Perun.LogStats(argPlayerID); end Perun.LogStatsGet = function(playerID) - -- Gets Perun statistics array per player - - local next = next + -- Gets Perun statistics array per player TBD rewrite + local next = next -- Make next function local - this improves performance TBD + local _player_hash = nil + if next(Perun.StatDataLastType) == nil then - _ucid=net.get_player_info(playerID, 'ucid')..DCS.getUnitType(playerID) + -- Array is empty + _player_hash=net.get_player_info(playerID, 'ucid')..DCS.getUnitType(playerID) elseif Perun.StatDataLastType[net.get_player_info(playerID, 'ucid')]== nil then - _ucid=net.get_player_info(playerID, 'ucid')..DCS.getUnitType(playerID) + -- Last type entry is empty + _player_hash=net.get_player_info(playerID, 'ucid')..DCS.getUnitType(playerID) else - _ucid=Perun.StatDataLastType[net.get_player_info(playerID, 'ucid')] + -- Return last type entry + _player_hash=Perun.StatDataLastType[net.get_player_info(playerID, 'ucid')] end - local next = next + local next = next -- Make next function local - this improves performance TBD if next(Perun.StatData) == nil then - Perun.LogStatsCount(playerID,'init') - + -- Array is empty + Perun.LogStatsCount(playerID,'init') -- Init statistics end - if Perun.StatData[_ucid]== nil then - Perun.LogStatsCount(playerID,'init') + if Perun.StatData[_player_hash]== nil then + -- Stats for player are empty + Perun.LogStatsCount(playerID,'init') -- Init statistics end - return Perun.StatData[_ucid]; + + Perun.AddLog("Getting stats data",2) + return Perun.StatData[_player_hash]; end Perun.LogStats = function(playerID) -- Log player status - - -- TBD : DCS stats not working at the moment WIP - p_stats_dcs={} - p_stats_dcs['PS_CAR']=net.get_stat(playerID,2) - p_stats_dcs['PS_PLANE']=net.get_stat(playerID,3) - p_stats_dcs['PS_SHIP']=net.get_stat(playerID,4) - p_stats_dcs['PS_SCORE']=net.get_stat(playerID,5) - p_stats_dcs['PS_LAND']=net.get_stat(playerID,6) - p_stats_dcs['PS_PING']=net.get_stat(playerID,0) - p_stats_dcs['PS_CRASH']=net.get_stat(playerID,1) - p_stats_dcs['PS_EJECT']=net.get_stat(playerID,7) - - _temp=Perun.LogStatsGet(playerID) - data={} - data['stat_data_dcs']=p_stats_dcs - data['stat_data_perun']=_temp - data['stat_data_type']=_temp['ps_type']; - data['stat_ucid']=net.get_player_info(playerID, 'ucid') - data['stat_name']=net.get_player_info(playerID, 'name') - data['stat_datetime']=os.date('%Y-%m-%d %H:%M:%S') - data['stat_missionhash']=Perun.MissionHash + local _PlayerStatsTable=Perun.LogStatsGet(playerID) + local _TempData={} + _TempData['stat_data_perun']=_PlayerStatsTable + _TempData['stat_data_type']=_PlayerStatsTable['ps_type']; + _TempData['stat_ucid']=net.get_player_info(playerID, 'ucid') + _TempData['stat_name']=net.get_player_info(playerID, 'name') + _TempData['stat_datetime']=os.date('%Y-%m-%d %H:%M:%S') + _TempData['stat_missionhash']=Perun.MissionHash - Perun.SendToPerun(52,data) + Perun.AddLog("Sending stats data",2) + Perun.SendToPerun(52,_TempData) end Perun.LogLogin = function(playerID) -- Player logged in + local _TempData={} + _TempData['login_ucid']=net.get_player_info(playerID, 'ucid') + _TempData['login_ipaddr']=net.get_player_info(playerID, 'ipaddr') + _TempData['login_name']=net.get_player_info(playerID, 'name') + _TempData['login_datetime']=os.date('%Y-%m-%d %H:%M:%S') - data={} - data['login_ucid']=net.get_player_info(playerID, 'ucid') - data['login_ipaddr']=net.get_player_info(playerID, 'ipaddr') - data['login_name']=net.get_player_info(playerID, 'name') - data['login_datetime']=os.date('%Y-%m-%d %H:%M:%S') - - Perun.SendToPerun(53,data) + Perun.AddLog("Sending login event",2) + Perun.SendToPerun(53,_TempData) end Perun.CheckMulticrew = function (owner_playerID,owner_unittype) -- Check multicrew and return list of co-player - _coplayers = {} + local _coplayers = {} table.insert(_coplayers, owner_playerID) if owner_unittype == "F-14B" or owner_unittype == "Yak-52" or owner_unittype == "L-39C" or owner_unittype == "SA342M" or owner_unittype =="SA342Minigun" or owner_unittype == "SA342Mistral" or owner_unittype == "SA342L" then -- TBD add additional multicrew model types - - _owner_slot=net.get_player_info(owner_playerID, 'slot') - _owner_side=net.get_player_info(owner_playerID, 'side') + local _owner_slot=net.get_player_info(owner_playerID, 'slot') + local _owner_side=net.get_player_info(owner_playerID, 'side') -- Check if we are co-player _t_start, _t_end = string.find(_owner_slot, '_%d+') - _sub_slot = nil + local _master_slot = nil + local _sub_slot = nil if _t_start then -- This is co-player _master_slot = string.sub(_owner_slot, 0 , _t_start -1 ) @@ -507,7 +517,7 @@ Perun.CheckMulticrew = function (owner_playerID,owner_unittype) if _master_slot ~= "" then -- Search for all players to account for event - _all_players = net.get_player_list() + local _all_players = net.get_player_list() for PlayerIDIndex, playerID in ipairs(_all_players) do local _playerDetails = net.get_player_info( playerID ) @@ -520,6 +530,8 @@ Perun.CheckMulticrew = function (owner_playerID,owner_unittype) end end end + + Perun.AddLog("Multicrew check completed",2) return _coplayers end @@ -527,24 +539,31 @@ end --- ########### Event callbacks ########### Perun.onSimulationStart = function() + -- Simulation was started Perun.MissionHash=DCS.getMissionName( ).."@".. Perun.Instance .. "@"..os.date('%Y%m%d_%H%M%S'); Perun.LogEvent("SimStart","Mission " .. Perun.MissionHash .. " started",nil,nil); Perun.StatData = {} Perun.StatDataLastType = {} + -- TBD send this to Perun end Perun.onSimulationStop = function() + -- Simulation was stopped Perun.LogEvent("SimStop","Mission " .. Perun.MissionHash .. " finished",nil,nil); Perun.MissionHash=""; Perun.StatData = {} Perun.StatDataLastType = {} + -- TBD send this to Perun end Perun.onPlayerDisconnect = function(id, err_code) + -- Player disconnected - TBD DCS Bug, this is not triggered at this point of time Perun.LogEvent("disconnect", "Player " .. net.get_player_info(id, "name") .. " disconnected; " .. err_code,net.get_player_info(id, "name"),err_code); + -- TBD send this to Perun end Perun.onSimulationFrame = function() + -- Repeat for each simulator frame local _now = DCS.getRealTime() -- First run @@ -568,18 +587,22 @@ Perun.onSimulationFrame = function() end -- Send keepalive - if _now > Perun.lastSentKeepAlive + 3 then + if _now > Perun.lastSentKeepAlive + Perun.SendRetries then Perun.lastSentKeepAlive = _now Perun.SendToPerun(0,nil) end end Perun.onPlayerStart = function (id) + -- Player entered cocpit net.send_chat_to(Perun.MOTD_L1, id); net.send_chat_to(Perun.MOTD_L2, id); + + Perun.AddLog("Player entered the game",2) end Perun.onPlayerTrySendChat = function (playerID, msg, all) + -- Somebody tries to send chat message if msg~=Perun.MOTD_L1 and msg~=Perun.MOTD_L2 then Perun.LogChat(playerID,msg,all) end @@ -588,6 +611,8 @@ Perun.onPlayerTrySendChat = function (playerID, msg, all) end Perun.onGameEvent = function (eventName,arg1,arg2,arg3,arg4,arg5,arg6,arg7) + -- Game event has occured + Perun.AddLog("Event handler for "..eventName .. " started",2) if eventName == "friendly_fire" then --"friendly_fire", playerID, weaponName, victimPlayerID if arg2 == "" then @@ -602,17 +627,17 @@ Perun.onGameEvent = function (eventName,arg1,arg2,arg3,arg4,arg5,arg6,arg7) elseif eventName == "kill" then --"kill", killerPlayerID, killerUnitType, killerSide, victimPlayerID, victimUnitType, victimSide, weaponName - _tempX="" + local _temp_victim="" if net.get_player_info(arg4, "name") ~= nil then - _tempX = " player ".. net.get_player_info(arg4, "name") .." "; + _temp_victim = " player ".. net.get_player_info(arg4, "name") .." "; Perun.LogStats(arg4); else - _tempX = " AI "; + _temp_victim = " AI "; end if net.get_player_info(arg1, "name") ~= nil then - _temp2 = " player ".. net.get_player_info(arg1, "name") .." "; + _temp_killer = " player ".. net.get_player_info(arg1, "name") .." "; _temp_event_type="" if arg3 ~= arg6 then @@ -651,30 +676,32 @@ Perun.onGameEvent = function (eventName,arg1,arg2,arg3,arg4,arg5,arg6,arg7) end else - _temp2 = " AI "; + _temp_killer = " AI "; end if arg7 == "" then arg7 = "Cannon" end - Perun.LogEvent(eventName,Perun.SideID2Name(arg3) .. _temp2 .. " in " .. arg2 .. " killed " .. Perun.SideID2Name(arg6) .. _tempX .. " in " .. arg5 .. " using " .. arg7 .. " [".. Perun.GetCategory(arg5).."]",arg7,Perun.GetCategory(arg5)); + Perun.LogEvent(eventName,Perun.SideID2Name(arg3) .. _temp_killer .. " in " .. arg2 .. " killed " .. Perun.SideID2Name(arg6) .. _temp_victim .. " in " .. arg5 .. " using " .. arg7 .. " [".. Perun.GetCategory(arg5).."]",arg7,Perun.GetCategory(arg5)); + elseif eventName == "self_kill" then --"self_kill", playerID + Perun.LogStats(arg1); Perun.LogEvent(eventName,net.get_player_info(arg1, "name") .. " killed himself",nil,nil); - Perun.LogStats(arg1); elseif eventName == "change_slot" then --"change_slot", playerID, slotID, prevSide if DCS.getUnitType(arg2) ~= nil and DCS.getUnitType(arg2) ~= "" then - _temp = DCS.getUnitType(arg2); + _temp_slot = DCS.getUnitType(arg2); else - _temp = "SPECTATOR"; + _temp_slot = "SPECTATOR"; end - Perun.LogEvent(eventName,Perun.SideID2Name( net.get_player_info(arg1, "side")) .. " player " .. net.get_player_info(arg1, "name") .. " changed slot to " .. _temp,nil,nil); - Perun.LogStatsCount(arg1,"init",_temp) + Perun.LogStatsCount(arg1,"init",_temp_slot) + Perun.LogEvent(eventName,Perun.SideID2Name( net.get_player_info(arg1, "side")) .. " player " .. net.get_player_info(arg1, "name") .. " changed slot to " .. _temp_slot,nil,nil); + elseif eventName == "connect" then --"connect", playerID, name @@ -683,52 +710,50 @@ Perun.onGameEvent = function (eventName,arg1,arg2,arg3,arg4,arg5,arg6,arg7) elseif eventName == "disconnect" then --"disconnect", playerID, name, playerSide, reason_code + Perun.LogStats(arg1); Perun.LogEvent(eventName, Perun.SideID2Name(arg3) .. " player " ..net.get_player_info(arg1, "name") .. " disconnected",nil,nil); - Perun.LogStats(arg1); elseif eventName == "crash" then --"crash", playerID, unit_missionID - Perun.LogEvent(eventName, Perun.SideID2Name( net.get_player_info(arg1, "side")) .. " player " .. net.get_player_info(arg1, "name") .. " crashed in " .. DCS.getUnitType(arg2),nil,nil); - pilots_accounted = Perun.CheckMulticrew(arg1,DCS.getUnitType(arg2)) for _, pilotID in ipairs(pilots_accounted) do Perun.LogStatsCount(pilotID,"crash",DCS.getUnitType(arg2)); end + Perun.LogEvent(eventName, Perun.SideID2Name( net.get_player_info(arg1, "side")) .. " player " .. net.get_player_info(arg1, "name") .. " crashed in " .. DCS.getUnitType(arg2),nil,nil); + elseif eventName == "eject" then --"eject", playerID, unit_missionID - Perun.LogEvent(eventName, Perun.SideID2Name( net.get_player_info(arg1, "side")) .. " player " .. net.get_player_info(arg1, "name") .. " ejected " .. DCS.getUnitType(arg2),nil,nil); - pilots_accounted = Perun.CheckMulticrew(arg1,DCS.getUnitType(arg2)) for _, pilotID in ipairs(pilots_accounted) do Perun.LogStatsCount(pilotID,"eject",DCS.getUnitType(arg2)); end - + Perun.LogEvent(eventName, Perun.SideID2Name( net.get_player_info(arg1, "side")) .. " player " .. net.get_player_info(arg1, "name") .. " ejected " .. DCS.getUnitType(arg2),nil,nil); + elseif eventName == "takeoff" then --"takeoff", playerID, unit_missionID, airdromeName if arg3 ~= "" then - _temp = " from " .. arg3; + _temp_airfield = " from " .. arg3; else - _temp = ""; + _temp_airfield = ""; end - Perun.LogEvent(eventName, Perun.SideID2Name( net.get_player_info(arg1, "side")) .. " player " .. net.get_player_info(arg1, "name") .. " took off in ".. DCS.getUnitType(arg2) .. _temp,arg3,nil); - - _type = "" + _temp_type = "" if string.find(arg3, "FARP",1,true) then - _type="tookoff_FARP" - elseif string.find(arg3, "CVN-74 John C. Stennis",1,true) or string.find(arg3, "LHA-1 Tarawa",1,true) then - _type="tookoff_SHIP" + _temp_type="tookoff_FARP" + elseif string.find(arg3, "CVN-74 John C. Stennis",1,true) or string.find(arg3, "LHA-1 Tarawa",1,true) or string.find(arg3, "SHIP",1,true) then + _temp_type="tookoff_SHIP" elseif arg3 ~= "" then - _type="tookoff_AIRFIELD" + _temp_type="tookoff_AIRFIELD" else - _type="tookoff_OTHER" + _temp_type="tookoff_OTHER" end pilots_accounted = Perun.CheckMulticrew(arg1,DCS.getUnitType(arg2)) for _, pilotID in ipairs(pilots_accounted) do - Perun.LogStatsCount(pilotID,_type,DCS.getUnitType(arg2)) + Perun.LogStatsCount(pilotID,_temp_type,DCS.getUnitType(arg2)) end - + Perun.LogEvent(eventName, Perun.SideID2Name( net.get_player_info(arg1, "side")) .. " player " .. net.get_player_info(arg1, "name") .. " took off in ".. DCS.getUnitType(arg2) .. _temp_airfield,arg3,nil); + elseif eventName == "landing" then --"landing", playerID, unit_missionID, airdromeName if arg3 ~= "" then @@ -737,42 +762,42 @@ Perun.onGameEvent = function (eventName,arg1,arg2,arg3,arg4,arg5,arg6,arg7) _temp =""; end - Perun.LogEvent(eventName, Perun.SideID2Name( net.get_player_info(arg1, "side")) .. " player " .. net.get_player_info(arg1, "name") .. " landed in " .. DCS.getUnitType(arg2).. _temp,arg3,nil); - - _type = "" + _temp_type = "" if string.find(arg3, "FARP",1,true) then - _type = "landing_FARP" - elseif string.find(arg3, "CVN-74 John C. Stennis",1,true) or string.find(arg3, "LHA-1 Tarawa",1,true) then - _type = "landing_SHIP" + _temp_type = "landing_FARP" + elseif string.find(arg3, "CVN-74 John C. Stennis",1,true) or string.find(arg3, "LHA-1 Tarawa",1,true) or string.find(arg3, "SHIP",1,true) then + _temp_type = "landing_SHIP" elseif arg3 ~= "" then - _type = "landing_AIRFIELD" + _temp_type = "landing_AIRFIELD" else - _type = "landing_OTHER" + _temp_type = "landing_OTHER" end pilots_accounted = Perun.CheckMulticrew(arg1,DCS.getUnitType(arg2)) for _, pilotID in ipairs(pilots_accounted) do - Perun.LogStatsCount(pilotID,_type,DCS.getUnitType(arg2)) + Perun.LogStatsCount(pilotID,_temp_type,DCS.getUnitType(arg2)) end - + Perun.LogEvent(eventName, Perun.SideID2Name( net.get_player_info(arg1, "side")) .. " player " .. net.get_player_info(arg1, "name") .. " landed in " .. DCS.getUnitType(arg2).. _temp,arg3,nil); + elseif eventName == "pilot_death" then --"pilot_death", playerID, unit_missionID - Perun.LogEvent(eventName, Perun.SideID2Name( net.get_player_info(arg1, "side")) .. " player " .. net.get_player_info(arg1, "name") .. " in " .. DCS.getUnitType(arg2) .. " died",nil,nil); - pilots_accounted = Perun.CheckMulticrew(arg1,DCS.getUnitType(arg2)) for _, pilotID in ipairs(pilots_accounted) do Perun.LogStatsCount(pilotID,"pilot_death",DCS.getUnitType(arg2)) end - + Perun.LogEvent(eventName, Perun.SideID2Name( net.get_player_info(arg1, "side")) .. " player " .. net.get_player_info(arg1, "name") .. " in " .. DCS.getUnitType(arg2) .. " died",nil,nil); + else Perun.LogEvent(eventName,"Unknown event type",nil,nil); - end + end + Perun.AddLog("Event handler for "..eventName .. " finished",2) end -- ########### Finalize and set callbacks ########### if Perun.IsServer then - DCS.setUserCallbacks(Perun) - net.log("Perun by VladMordock was loaded: " .. Perun.Version ) - Perun.ConnectToPerun() + -- If this game instance is hosting multiplayer game, start Perun + DCS.setUserCallbacks(Perun) -- Set user callbacs, map DCS event handlers with functions defined above + net.log("Perun by VladMordock was loaded: " .. Perun.Version ) -- Display perun information in log + Perun.ConnectToPerun() -- Connect to Perun server end