Files
perun/01_DCS/Hooks/Perun.lua
Szymon Porwolik 8d1835f6f5 - MOTD will be send only to player who is joining
- after mission end mission hash will be set to ""
2019-03-10 00:48:32 +01:00

483 lines
16 KiB
Lua

-- Perun for DCS World https://github.com/szporwolik/perun -> DCS Hook component
-- Initial init
local Perun = {}
package.path = package.path..";"..lfs.currentdir().."/LuaSocket/?.lua"
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.UDPTargetPort = 48620 -- (int) UDP port to send data to
Perun.UDPSourcePort = 48621 -- (int) UDP port to send data from
Perun.Instance = 1 -- (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.MOTD_L2 = "Wymagamy obecnosci na 255Mhz (DCS SRS)" -- (string) Message send to players connecting the server - Line 2
-- ###################### END OF SETTINGS - DO NOT MODIFY OUTSIDE THIS SECTION ######################
-- Variable init
Perun.Version = "v0.5.0"
Perun.StatusData = {}
Perun.SlotsData = {}
Perun.MissionData = {}
Perun.ServerData = {}
Perun.MissionHash=""
Perun.lastSentStatus =0
Perun.lastSentMission =0
Perun.JsonStatusLocation = lfs.writedir() .. Perun.JsonStatusLocation
Perun.socket = require("socket")
Perun.IsServer = true --DCS.isServer( ) -- TBD looks like DCS API error, always returning True
Perun.UDPSourcePort = Perun.UDPSourcePort + Perun.Instance - 1
-- ########### Helper function definitions ###########
function stripChars(str)
-- remove accents characters from string
-- via https://stackoverflow.com/questions/50459102/replace-accented-characters-in-string-to-standard-with-lua TBD: rewrite
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"
-- TBD additonal characters
normalizedString = ''
for strChar in string.gmatch(str, "([%z\1-\127\194-\244][\128-\191]*)") do
if tableAccents[strChar] ~= nil then
normalizedString = normalizedString..tableAccents[strChar]
else
normalizedString = normalizedString..strChar
end
end
return normalizedString
end
Perun.GetCategory = function(id)
-- Returns object category basing on ID
-- Helper function via https://pastebin.com/GUAXrd2U TBD: rewrite
local _killed_target_category = DCS.getUnitTypeAttribute(id, "category")
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
_killed_target_category = "Ships"
elseif _killed_target_cat_check_ship == nil and _killed_target_cat_check_plane ~= nil then
_killed_target_category = "Planes"
else
_killed_target_category = "Helicopters"
end
end
return _killed_target_category
end
Perun.SideID2Name = function(id)
-- Helper function returns side name per side (coalition) id
local sides = {
[0] = 'SPECTATOR',
[1] = 'RED',
[2] = 'BLUE',
}
return sides[id]
end
-- ########### Main code ###########
Perun.AddLog = function(text)
-- Adds logs to DCS.log file
net.log("Perun : ".. text)
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
_temp=net.lua2json(TempData)
perun_export = io.open(Perun.JsonStatusLocation .. "perun_status_data.json", "w")
perun_export:write(_temp .. "\n")
perun_export:close()
end
Perun.Send = function(data_id, data_package)
-- Sends data package
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)
Perun.UDP:send(temp)
Perun.AddLog("Packet send")
end
Perun.UpdateStatusPart = function(part_id, data_package)
-- Helper for status update container
Perun.StatusData[part_id] = data_package
end
Perun.UpdateStatus = function()
-- Main function for status updates
-- Diagnostic data
-- Update version data
Perun.ServerData['v_dcs_hook']=Perun.Version
-- Update server data
_table=net.get_player_list()
_count = 0
for _ in pairs(_table) do _count = _count + 1 end
Perun.ServerData['c_players']=_count
-- Send
Perun.Send(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)
-- 2 - Players
_temp = net.get_player_list()
_temp2={}
for k, i in ipairs(_temp) do
_temp2[k]=net.get_player_info(i)
end
Perun.UpdateStatusPart("players",_temp2)
-- Send
Perun.Send(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)
end
-- Send
Perun.Send(3,Perun.SlotsData)
end
Perun.UpdateMission = function()
-- Main function for mission information updates
Perun.MissionData=DCS.getCurrentMission()
end
Perun.LogChat = function(playerID,msg,all)
-- Log chat messages
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.Send(50,data)
end
Perun.LogEvent = function(log_type,log_content)
-- Log chat messages
data={}
data['log_type']= log_type
data['log_content']=log_content
data['log_datetime']=os.date('%Y-%m-%d %H:%M:%S')
data['log_missionhash']=Perun.MissionHash
Perun.Send(51,data)
end
Perun.LogStats = function(playerID)
-- Log player status
-- TBD : not working at the moment WIP
p_stats={}
p_stats['PS_CAR']=net.get_stat(playerID,2)
p_stats['PS_PLANE']=net.get_stat(playerID,3)
p_stats['PS_SHIP']=net.get_stat(playerID,4)
p_stats['PS_SCORE']=net.get_stat(playerID,5)
p_stats['PS_LAND']=net.get_stat(playerID,6)
p_stats['PS_PING']=net.get_stat(playerID,0)
p_stats['PS_CRASH']=net.get_stat(playerID,1)
p_stats['PS_EJECT']=net.get_stat(playerID,7)
p_stats['debug']="ok"
data={}
data['stat_data']=p_stats
data['stat_ucid']=net.get_player_info(playerID, 'ucid')
data['stat_datetime']=os.date('%Y-%m-%d %H:%M:%S')
data['stat_missionhash']=Perun.MissionHash
Perun.Send(52,data)
end
Perun.LogLogin = function(playerID)
-- Player logged in
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.Send(53,data)
end
--- ########### Event callbacks ###########
Perun.onSimulationStart = function()
Perun.MissionHash=DCS.getMissionName( ).."@".. Perun.Instance .. "@"..os.date('%Y%m%d_%H%M%S');
Perun.LogEvent("SimStart","Mission " .. Perun.MissionHash .. " started");
end
Perun.onSimulationStop = function()
Perun.LogEvent("SimStop","Mission " .. Perun.MissionHash .. " finished");
Perun.MissionHash="";
end
Perun.onPlayerDisconnect = function(id, err_code)
Perun.LogEvent("disconnect", "Player " .. net.get_player_info(id, "name") .. " disconnected; " .. err_code);
end
Perun.onSimulationFrame = function()
local _now = DCS.getRealTime()
-- First run
if Perun.lastSentMission ==0 and Perun.lastSentStatus ==0 then
Perun.UpdateMission()
end
-- Send mission update and update JSON
if _now > Perun.lastSentMission + Perun.RefreshMission then
Perun.lastSentMission = _now
Perun.UpdateMission()
Perun.UpdateJsonStatus()
end
-- Send status update
if _now > Perun.lastSentStatus + Perun.RefreshStatus then
Perun.lastSentStatus = _now
Perun.UpdateStatus()
end
end
Perun.onPlayerStart = function (id)
net.send_chat_to(Perun.MOTD_L1, id);
net.send_chat_to(Perun.MOTD_L2, id);
end
Perun.onPlayerTrySendChat = function (playerID, msg, all)
if msg~=Perun.MOTD_L1 and msg~=Perun.MOTD_L2 then
Perun.LogChat(playerID,msg,all)
end
return msg
end
Perun.onGameEvent = function (eventName,arg1,arg2,arg3,arg4,arg5,arg6,arg7)
if eventName == "friendly_fire" then
--"friendly_fire", playerID, weaponName, victimPlayerID
if arg2 == nil then
arg2 = Gun
end
Perun.LogEvent(eventName,Perun.SideID2Name( net.get_player_info(arg1, "side")) .. " player " .. net.get_player_info(arg1, "name").." killed friendy " .. net.get_player_info(arg3, "name") .. " using " .. arg2);
Perun.LogStats(arg1);
elseif eventName == "mission_end" then
--"mission_end", winner, msg
Perun.LogEvent(eventName,"Mission finished, winner " .. arg1 .. " message: " .. arg2);
elseif eventName == "kill" then
--"kill", killerPlayerID, killerUnitType, killerSide, victimPlayerID, victimUnitType, victimSide, weaponName
if net.get_player_info(arg4, "name") ~= nil then
_temp = " player ".. net.get_player_info(arg4, "name") .." ";
Perun.LogStats(arg4);
else
_temp = " AI ";
end
if net.get_player_info(arg1, "name") ~= nil then
_temp2 = " player ".. net.get_player_info(arg1, "name") .." ";
Perun.LogStats(arg1);
else
_temp2 = " AI ";
end
if arg7 == nil then
arg7 = Gun
end
Perun.LogEvent(eventName,Perun.SideID2Name(arg3) .. _temp2 .. " in " .. arg2 .. " killed " .. Perun.SideID2Name(arg6) .. _temp .. " in " .. arg5 .. " using " .. arg7 .. " [".. Perun.GetCategory(arg5).."]");
elseif eventName == "self_kill" then
--"self_kill", playerID
Perun.LogEvent(eventName,net.get_player_info(arg1, "name") .. " killed himself");
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);
else
_temp = "SPECTATOR";
end
Perun.LogEvent(eventName,Perun.SideID2Name( net.get_player_info(arg1, "side")) .. " player " .. net.get_player_info(arg1, "name") .. " changed slot to " .. _temp);
Perun.LogStats(arg1);
elseif eventName == "connect" then
--"connect", playerID, name
Perun.LogLogin(arg1);
Perun.LogEvent(eventName,"Player "..net.get_player_info(arg1, "name") .. " connected");
elseif eventName == "disconnect" then
--"disconnect", playerID, name, playerSide, reason_code
Perun.LogEvent(eventName, Perun.SideID2Name(arg3) .. " player " ..net.get_player_info(arg1, "name") .. " disconnected");
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));
Perun.LogStats(arg1);
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));
Perun.LogStats(arg1);
elseif eventName == "takeoff" then
--"takeoff", playerID, unit_missionID, airdromeName
if arg3 then
_temp = " from " .. arg3;
else
_temp = "";
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);
Perun.LogStats(arg1);
elseif eventName == "landing" then
--"landing", playerID, unit_missionID, airdromeName
if arg3 then
_temp = " at " .. arg3;
else
_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);
Perun.LogStats(arg1);
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");
Perun.LogStats(arg1);
else
Perun.LogEvent(eventName,"Unknown event type");
end
end
-- ########### Finalize and set callbacks ###########
if Perun.IsServer then
Perun.UDP = assert(Perun.socket.udp())
Perun.UDP:settimeout(0)
Perun.UDP:setsockname("*", Perun.UDPSourcePort)
Perun.UDP:setpeername("127.0.0.1",Perun.UDPTargetPort)
DCS.setUserCallbacks(Perun)
net.log("Loaded - Perun - VladMordock - " .. Perun.Version )
end