From b87276cfe374c291c776313af2c8f8bb36519300 Mon Sep 17 00:00:00 2001 From: ags Date: Thu, 25 Nov 2021 01:31:55 +0000 Subject: [PATCH] lighter perun event listener --- .../Mods/services/Perun/lua/perun_config.lua | 30 +- 01_DCS/Scripts/Hooks/Perun-hook.lua | 79 ++- 01_DCS/Scripts/Hooks/Perun_light_hook.lua | 480 ++++++++++++++++++ 03_Perun_Lua_Wrapper/lua-5.1.5/src/luaconf.h | 2 +- 03_Perun_Lua_Wrapper/perun/CmakeLists.txt | 2 +- .../src/{Connecton.cpp => Connection.cpp} | 9 +- 03_Perun_Lua_Wrapper/perun/src/Connection.h | 6 +- 03_Perun_Lua_Wrapper/perun/src/library.cpp | 50 +- 04_MySQL/m1081_perun.sql | 28 +- 9 files changed, 615 insertions(+), 71 deletions(-) create mode 100644 01_DCS/Scripts/Hooks/Perun_light_hook.lua rename 03_Perun_Lua_Wrapper/perun/src/{Connecton.cpp => Connection.cpp} (94%) diff --git a/01_DCS/Mods/services/Perun/lua/perun_config.lua b/01_DCS/Mods/services/Perun/lua/perun_config.lua index af632f1..57139a4 100644 --- a/01_DCS/Mods/services/Perun/lua/perun_config.lua +++ b/01_DCS/Mods/services/Perun/lua/perun_config.lua @@ -1,26 +1,28 @@ -- Perun for DCS World https://github.com/szporwolik/perun -> DCS Hook config component -local PerunConfig = {} +local config = {} -- ###################### SETTINGS - DO NOT MODIFY OUTSIDE THIS SECTION ############################# -- Connection -PerunConfig.TCPPerunHost = "localhost" -- (string) [default: "localhost"] IP adress of the Perun instance or "localhost" -PerunConfig.TCPTargetPort = 48621 -- (int) [default: 48621] TCP port to send data to -PerunConfig.Instance = 1 -- (int) [default: 1] Id number of instance (if multiple DCS instances are to run at the same PC) -PerunConfig.RefreshStatus = 60 -- (int) [default: 60] Base refresh rate in seconds to send status update - --- Mission Configuration -PerunConfig.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 +config.host = "localhost" -- (string) [default: "localhost"] IP adress of the Perun instance or "localhost" +config.port = 48621 -- (int) [default: 48621] TCP port to send data to +config.Instance = 1 -- (int) [default: 1] Id number of instance (if multiple DCS instances run on the same machine) +config.keepAliveSeconds = 5 -- +config.refreshSlotsSeconds = 300 -- delay between slot updates are sent +config.refreshMissionSeconds = 600 -- delay between mission update is sent +config.bigUpdatesSeconds = 100 -- big updates (mission, unit IDs, slots) have to wait between being sent -- Localisation -PerunConfig.MOTD_L1 = "[Perun] Welcome to our server !" -- (string) Message send to players connecting the server - Line 1 -PerunConfig.MOTD_L2 = "[Perun] Stats and event data integrated with Perun for DCS World" -- (string) Message send to players connecting the server - Line 2 -PerunConfig.ConnectionError_L1 = "[Perun] ERROR: Connection broken - contact server admin!" -- (string) Information to send to players when Perun connection is broken +-- Server MOTD, sent to ALL players when entering cockpit +config.MOTD_line1 = "[Perun] Welcome to our server!"; +config.MOTD_line2 = "[Perun] Stats and event data integrated with Perun for DCS World"; +-- (string) Information to send to players when Perun connection is broken +config.ConnectionError = "[Perun] ERROR: Connection broken - contact server admin!" -- Misc -PerunConfig.BroadcastPerunErrors = 1 -- (int) [0 (default),1] Value greater than 0 will broadcast chat message about missing connection to Perun +config.BroadcastPerunErrors = 1 -- (int) [0 (default),1] Value greater than 0 will broadcast chat message about missing connection to Perun -- Debug -PerunConfig.DebugMode = 1 -- (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 +config.DebugMode = 1 -- (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 -- ###################### END OF SETTINGS - DO NOT MODIFY OUTSIDE THIS SECTION ###################### -return PerunConfig +return config diff --git a/01_DCS/Scripts/Hooks/Perun-hook.lua b/01_DCS/Scripts/Hooks/Perun-hook.lua index d3fd71a..4c73aba 100644 --- a/01_DCS/Scripts/Hooks/Perun-hook.lua +++ b/01_DCS/Scripts/Hooks/Perun-hook.lua @@ -13,17 +13,18 @@ package.cpath = package.cpath..';'.. lfs.writedir()..'/Mods/services/Perun/bin/' Perun.DLL = require('perun') -- Load config file -local PerunConfig = require "perun_config" -Perun.RefreshStatus = PerunConfig.RefreshStatus -Perun.TCPTargetPort = PerunConfig.TCPTargetPort -Perun.TCPPerunHost = PerunConfig.TCPPerunHost -Perun.Instance = PerunConfig.Instance -Perun.MissionStartNoDeathWindow = PerunConfig.MissionStartNoDeathWindow -Perun.DebugMode = PerunConfig.DebugMode -Perun.MOTD_L1 = PerunConfig.MOTD_L1 -Perun.MOTD_L2 = PerunConfig.MOTD_L2 -Perun.ConnectionError = PerunConfig.ConnectionError_L1 -Perun.BroadcastPerunErrors = PerunConfig.BroadcastPerunErrors +local config = require "perun_config" + +Perun.RefreshStatus = config.RefreshStatus +Perun.TCPTargetPort = config.port +Perun.TCPPerunHost = config.host +Perun.Instance = config.Instance +Perun.MissionStartNoDeathWindow = config.MissionStartNoDeathWindow +Perun.DebugMode = config.DebugMode +Perun.MOTD_L1 = config.MOTD_L1 +Perun.MOTD_L2 = config.MOTD_L2 +Perun.ConnectionError = config.ConnectionError_L1 +Perun.BroadcastPerunErrors = config.BroadcastPerunErrors -- Variable init Perun.Version = "v0.12.1" @@ -47,6 +48,23 @@ Perun.lastFrameTime = 0; Perun.ReconnectTimeout = 30; Perun.RefreshKeepAlive = 3 +Perun.State = {} +Perun.State.connected = false; + +-- to avoid declaring every time a function is run +Perun.Globals = {} +Perun.Globals.Sides = { + [0] = 'SPECTATOR', + [1] = 'RED', + [2] = 'BLUE', + [3] = 'NEUTRAL', -- TBD check once this is released in DCS +} + +Perun.Mission = {} +Perun.Mission.Coalitions = {} + + + -- ################################ Helper function definitions ################################ Perun.GetCategory = function(id) -- Helper function returns object category basing on https://pastebin.com/GUAXrd2U @@ -75,14 +93,8 @@ end Perun.SideID2Name = function(id) -- Helper function returns side name per side (coalition) id - local _sides = { - [0] = 'SPECTATOR', - [1] = 'RED', - [2] = 'BLUE', - [3] = 'NEUTRAL', -- TBD check once this is released in DCS - } if id > 0 and id <= 3 then - return _sides[id] + return Perun.Globals.Sides[id] else return "?" end @@ -91,11 +103,11 @@ end Perun.AddLog = function(text,LogLevel) -- Adds logs to DCS.log file if Perun.DebugMode >= LogLevel then - net.log("[Perun] ".. text) + net.log("[Perun] ", text) end end -Perun.GenerateMissionHash = function() +Perun.generate_mission_hash = function() -- Generates unique simulation mission hash return DCS.getMissionName( ).."@".. Perun.Instance .. "@" .. Perun.Version .. "@".. os.date('%Y%m%d_%H%M%S') end @@ -239,6 +251,7 @@ Perun.ConnectToPerun = function () Perun.DLL.tcpConnect(Perun.TCPPerunHost, Perun.TCPTargetPort) Perun.AddLog(string.format("Connecting to TCP server %s:%i", Perun.TCPPerunHost, Perun.TCPTargetPort), 2) + Perun.State.connected = true; end Perun.SendToPerun = function(data_id, data_package) @@ -253,7 +266,7 @@ Perun.SendToPerun = function(data_id, data_package) _payload = net.lua2json(data_package); end - local _TempData={"","{\"dcs_current_frame_delay\":",((DCS.getRealTime() - Perun.lastFrameStart) * 1000000),",\"type\":",data_id,",\"dcs_frame_time\":",(Perun.lastFrameTime * 1000000),",\"instance\":",(Perun.Instance),",\"timestamp\":\"",(os.date('%Y-%m-%d %H:%M:%S')),"\",\"payload\":",(_payload),"}"} + local _TempData={"","{\"dcs_current_frame_delay\":",((DCS.getRealTime() - Perun.lastFrameStart) * 1000),",\"type\":",data_id,",\"dcs_frame_time\":",(Perun.lastFrameTime * 1000),",\"instance\":",(Perun.Instance),",\"timestamp\":\"",(os.date('%Y-%m-%d %H:%M:%S')),"\",\"payload\":",(_payload),"}"} local _tcpMessage= table.concat( _TempData ) -- TCP Part - sending @@ -620,7 +633,7 @@ end Perun.onSimulationStart = function() -- Simulation was started - Perun.MissionHash=Perun.GenerateMissionHash() + Perun.MissionHash=Perun.generate_mission_hash() Perun.LogEvent("SimStart","Mission " .. Perun.MissionHash .. " started",nil,nil); Perun.StatData = {} Perun.StatDataLastType = {} @@ -634,7 +647,7 @@ Perun.onSimulationStop = function() -- Simulation was stopped Perun.LogEvent("SimStop","Mission " .. Perun.MissionHash .. " finished",nil,nil); Perun.LogAllStats() - Perun.MissionHash=Perun.GenerateMissionHash(); + Perun.MissionHash=Perun.generate_mission_hash(); Perun.StatData = {} Perun.MissionData = {} Perun.StatDataLastType = {} @@ -716,6 +729,24 @@ end Perun.onGameEvent = function (eventName,arg1,arg2,arg3,arg4,arg5,arg6,arg7) -- Game event has occured local _now = DCS.getRealTime() + + local _payload = net.lua2json({eventName, arg1, arg2, arg3, arg4, arg5, arg6, arg7}) + Perun.DLL.tcpSend("" .. _payload .. "") + + for i, value in pairs({ "red", "blue" }) do + -- table { type, unit_missionID } + local slots = DCS.getAvailableSlots(value) + for slot in slots do + local idx = slot[1]; + Perun.Mission.Coalitions[value][idx] = {} + Perun.Mission.Coalitions[value][idx]["type"] = slot[2]; + Perun.DLL.tcpSend("" .. net.lua2json(DCS.getUnitType(idx)) .. "") + end + end + + local _payload = net.lua2json(Perun.Mission.Coalitions) + Perun.DLL.tcpSend(" SLOTS " .. _payload .. "") + Perun.AddLog("Event handler for ".. eventName .. " started",2) if eventName == "friendly_fire" then @@ -873,7 +904,7 @@ end if DCS.isServer() then -- If this game instance is hosting multiplayer game, start Perun Perun.DLL.StartOfApp() -- Start the main Perun dll - Perun.MissionHash=Perun.GenerateMissionHash() -- Generate initial missionhash + Perun.MissionHash=Perun.generate_mission_hash() -- Generate initial missionhash DCS.setUserCallbacks(Perun) -- Set user callbacs, map DCS event handlers with functions defined above Perun.AddLog("Loaded - Perun for DCS World - version: " .. Perun.Version,0) -- Display perun information in log Perun.ConnectToPerun() -- Connect to Perun server diff --git a/01_DCS/Scripts/Hooks/Perun_light_hook.lua b/01_DCS/Scripts/Hooks/Perun_light_hook.lua new file mode 100644 index 0000000..0dc9e3c --- /dev/null +++ b/01_DCS/Scripts/Hooks/Perun_light_hook.lua @@ -0,0 +1,480 @@ +-- Perun for DCS World https://github.com/szporwolik/perun -> DCS Hook component +net.log("[Perun] Loading Perun") + +-- Initial init +local Perun = {} + +-- Load Luas +package.path = package.path..";"..lfs.currentdir().."/LuaSocket/?.lua"..";"..lfs.writedir() .. "/Mods/services/Perun/lua/?.lua" +package.cpath = package.cpath..";"..lfs.currentdir().."/LuaSocket/?.dll" + +-- Load Dlls +package.cpath = package.cpath..';'.. lfs.writedir()..'/Mods/services/Perun/bin/' ..'?.dll;' +net.log("[Perun]", "Loading DLL") +Perun.DLL = require('perun') +net.log("[Perun]", "Loaded DLL, starting") +Perun.DLL.StartOfApp(); +net.log("[Perun]", "DLL started") + +-- Load config file +net.log("[Perun]", "Loading config") +local config = require "perun_config" +net.log("[Perun]", "Config loaded") + +Perun.host = config.host +Perun.port = config.port +Perun.Instance = config.Instance +Perun.DebugMode = config.DebugMode + +Perun.MOTD_line1 = config.MOTD_line1 +Perun.MOTD_line2 = config.MOTD_line2 +Perun.ConnectionError = config.ConnectionError +Perun.BroadcastPerunErrors = config.BroadcastPerunErrors + +Perun.keepAliveSeconds = config.keepAliveSeconds +Perun.bigUpdatesSeconds = config.bigUpdatesSeconds +Perun.refreshSlotsSeconds = config.refreshSlotsSeconds +Perun.refreshMissionSeconds = config.refreshMissionSeconds + +Perun.Version = "v0.12.1" + +Perun.MissionHash="" + +Perun.lastSentStatus = 0 +Perun.lastSentMission = 0 +Perun.lastSentKeepAlive = 0 +Perun.lastConnectionError = 0 +Perun.lastFrameStart = 0; +Perun.lastTimer = 0 +Perun.lastFrameTime = 0; + +Perun.ReconnectTimeout = 30; +Perun.RefreshKeepAlive = 5 + +Perun.State = {} +Perun.State.connected = false; +Perun.State.slotsInitialised = false; + +-- to avoid declaring every time a function is run +Perun.Globals = {} +Perun.Globals.Sides = { + [0] = 'SPECTATOR', + [1] = 'RED', + [2] = 'BLUE', + [3] = 'NEUTRAL', -- TBD check once this is released in DCS +} + +Perun.Mission = {} +Perun.Mission.Slots = {} +Perun.Mission.Slots["blue"] = {} +Perun.Mission.Slots["red"] = {} +Perun.Mission.Players = {} +Perun.Mission.Units = {} + +Perun.HandlerArgCounts = {} + +Perun.HandlerArgCounts["self_kill"] = 1 +Perun.HandlerArgCounts["crash"] = 2 +Perun.HandlerArgCounts["eject"] = 2 +Perun.HandlerArgCounts["connect"] = 2 +Perun.HandlerArgCounts["mission_end"] = 2 +Perun.HandlerArgCounts["pilot_death"] = 2 +Perun.HandlerArgCounts["takeoff"] = 3 +Perun.HandlerArgCounts["landing"] = 3 +Perun.HandlerArgCounts["change_slot"] = 3 +Perun.HandlerArgCounts["friendly_fire"] = 3 +Perun.HandlerArgCounts["disconnect"] = 4 +Perun.HandlerArgCounts["kill"] = 7 + +net.log("[Perun]", "Global variables initialised") + +Perun.Stats = {} +Perun.Stats.LastSentTCP = 0 +Perun.Stats.LastSentSlots = 0 +Perun.Stats.LastBigUpdate = 0 +Perun.Stats.LastSentMission = 0 +Perun.Stats.PreviousFrameStart = 0 +Perun.Stats.LastConnectionError = 0 + +Perun.func = {}; +Perun.func.ensureSlots = function() + if(not Perun.State.slotsInitialised) then + net.log("Perun", "initialising slots") + + local blue = DCS.getAvailableSlots("blue"); + for k, unit in pairs(blue) do + Perun.Mission.Slots["blue"][unit["unitId"]] = unit + end + + local red = DCS.getAvailableSlots("red"); + for k, unit in pairs(red) do + Perun.Mission.Slots["red"][unit["unitId"]] = unit + end + + Perun.State.slotsInitialised = true; + end +end + +Perun.func.ensureUnitId = function(unitId) + if(not Perun.Mission.Units[unitId]) then + local unitData = DCS.getUnitType(unitId) + Perun.Mission.Units[unitId] = unitData + local payload = {} + payload["unit"] = unitData + + Perun.func.sendData(net.lua2json(unitData)) + end +end + +Perun.func.sendData = function(payload) + + local now = DCS.getRealTime() + + local connected, reconnected = Perun.DLL.tcpSend(payload) + net.log("[Perun]", connected, reconnected) + if(connected < 1) and (now > Perun.Stats.LastConnectionError + Perun.ReconnectTimeout) then + Perun.AddLog("ERROR - TCP connection is not available",0) + + if Perun.BroadcastPerunErrors > 0 then + local _all_players = net.get_player_list() + for i, playerId in ipairs(_all_players) do + net.send_chat_to(Perun.ConnectionError , playerId) + end + end + + Perun.Stats.LastConnectionError = now + else + Perun.Stats.LastSentTCP = now + + if(reconnected > 0) then + net.log("Perun", "TCP reconnected") + Perun.Stats.LastBigUpdate = 0 + Perun.Stats.LastSentSlots = 0 + Perun.Stats.LastSentMission = 0 + end + end +end + +Perun.Handlers = {} +Perun.Handlers["event"] = function(eventName) + local data = {}; + data["event"] = eventName; + data["hash"] = Perun.MissionHash + data['datetime']=os.date('%Y-%m-%d %H:%M:%S') + + return data; +end + +Perun.Handlers["friendly_fire"] = function(playerID, weaponName, victimPlayerID) + local data = Perun.Handlers["event"]("friendly_fire") + + data["killer"] = playerID; + data["victim"] = victimPlayerID; + data["weapon"] = weaponName; + + return data; +end + +Perun.Handlers["mission_end"] = function(winner, msg) + local data = Perun.Handlers["event"]("mission_end") + + data["winner"] = winner; + data["message"] = msg; + + return data; +end + +Perun.Handlers["kill"] = function(killerPlayerID, killerUnitType, killerSide, victimPlayerID, victimUnitType, victimSide, weaponName) + local data = Perun.Handlers["event"]("kill") + + data["killer"] = {killerPlayerID, killerUnitType, killerSide} + data["victim"] = {victimPlayerID, victimUnitType, victimSide} + data["weapon"] = weaponName; + + return data; +end + +Perun.Handlers["self_kill"] = function(playerID) + local data = Perun.Handlers["event"]("self_kill") + data["who"] = playerID; + + return data; +end + +Perun.Handlers["change_slot"] = function(playerID, slotID, prevSide) + local details = net.get_player_info(playerID, "side") + Perun.Mission.Players[playerID]["side"] = details + + local data = Perun.Handlers["event"]("change_slot") + data["previousSlot"] = slotID + data["previousSide"] = prevSide + + return data; +end + +Perun.Handlers["connect"] = function(playerID, name) + net.log("Perun", "Getting player details") + local details = net.get_player_info(playerID) + Perun.Mission.Players[playerID] = details + + net.log("Perun", "Preparing event") + local data = Perun.Handlers["event"]("connect") + data["who"] = playerID + data["details"] = details; + + return data; +end + +Perun.Handlers["disconnect"] = function(playerID, name, playerSide, reason_code) + local data = Perun.Handlers["event"]("disconnect") + data["who"] = playerID; + data["details"] = {name, playerSide, reason_code} + + Perun.Mission.Players[playerID] = nil + + return data; +end +--"crash", playerID, unit_missionID +Perun.Handlers["crash"] = function(playerID, unit_missionID) + Perun.func.ensureUnitId(unit_missionID) + + local data = Perun.Handlers["event"]("crash") + data["who"] = playerID; + data["unitId"] = unit_missionID; + + return data; +end +--"eject", playerID, unit_missionID +Perun.Handlers["eject"] = function(playerID, unit_missionID) + Perun.func.ensureUnitId(unit_missionID) + + local data = Perun.Handlers["event"]("eject") + data["who"] = playerID; + data["unitId"] = unit_missionID; + + return data; +end +--"takeoff", playerID, unit_missionID, airdromeName +Perun.Handlers["takeoff"] = function(playerID, unit_missionID, airdromeName) + Perun.func.ensureUnitId(unit_missionID) + + local data = Perun.Handlers["event"]("takeoff") + data["who"] = playerID; + data["unitId"] = unit_missionID; + data["from"] = airdromeName; + + return data; +end +--"landing", playerID, unit_missionID, airdromeName +Perun.Handlers["landing"] = function(playerID, unit_missionID, airdromeName) + Perun.func.ensureUnitId(unit_missionID) + + local data = Perun.Handlers["event"]("landing") + data["who"] = playerID; + data["unitId"] = unit_missionID; + data["at"] = airdromeName; + + return data; +end +--"pilot_death", playerID, unit_missionID +Perun.Handlers["pilot_death"] = function(playerID, unit_missionID) + Perun.func.ensureUnitId(unit_missionID) + + local data = Perun.Handlers["event"]("pilot_death") + data["who"] = playerID; + data["unitId"] = unit_missionID; + + return data; +end + +net.log("[Perun] Handlers defined") + +--- ################################ Helper function definitions ################################ + +Perun.AddLog = function(text, LogLevel) + -- Adds logs to DCS.log file + if Perun.DebugMode >= LogLevel then + net.log("[Perun]", text) + end +end + +Perun.generate_mission_hash = function() + -- Generates unique simulation mission hash + return DCS.getMissionName( ).."@".. Perun.Instance .. "@" .. Perun.Version .. "@".. os.date('%Y%m%d_%H%M%S'); +end + +--- ################################ TCP Connection ################################ + +Perun.ConnectToPerun = function () + + Perun.AddLog(string.format("Connecting to TCP server %s:%i", Perun.host, Perun.port), 0); + + local status = Perun.DLL.tcpConnect(Perun.host, Perun.port); + + if(status == 0) then + Perun.AddLog(string.format("Connection failed: %s:%i", Perun.host, Perun.port), 0); + else + Perun.State.connected = true; + end +end + +--- ################################ Log functions ################################ + +Perun.LogChat = function(playerID, msg, all) + + local data = Perun.Handlers["event"]("chat"); + + data['player']= Perun.Mission.Players[playerID] + data['msg']=msg + data['messageToAll']=all + data['missionhash']=Perun.MissionHash + + Perun.func.sendData(net.lua2json(data)); +end + +Perun.LogEvent = function(log_type, log_content, log_arg_1, log_arg_2) + -- Logs events messages + local data = Perun.Handlers["event"]("customLog"); + 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_missionhash']=Perun.MissionHash + + Perun.AddLog("Sending event data, event: " .. log_type .. ", arg1:" .. log_arg_1 .. ", arg2:" .. log_arg_2 .. ", content: " .. log_content,1) + + Perun.func.sendData(net.lua2json(data)); +end + +--- ################################ Event callbacks ################################ + +Perun.onSimulationStart = function() + -- Simulation was started + Perun.MissionHash=Perun.generate_mission_hash() + Perun.LogEvent("SimStart","Mission " .. Perun.MissionHash .. " started",nil,nil); + Perun.lastSentMission = 0 -- reset so mission information will be send +end + +Perun.onSimulationStop = function() + -- Simulation was stopped + Perun.LogEvent("SimStop","Mission " .. Perun.MissionHash .. " finished",nil,nil); + Perun.MissionHash=Perun.generate_mission_hash(); + + Perun.Mission = {} + Perun.Mission.Slots = {} + Perun.Mission.Slots["blue"] = {} + Perun.Mission.Slots["red"] = {} + Perun.Mission.Players = {} + Perun.Mission.Units = {} +end + +Perun.onPlayerDisconnect = function(id, err_code) + -- Player disconnected + Perun.LogEvent("disconnect", "Player " .. id .. " disconnected.",nil,nil); + + return +end + +Perun.onPlayerStop = function (id) + -- Player left the simulation (happens right before a disconnect, if player exited by desire) + + + Perun.LogEvent("quit", "Player " .. id .. " quit the server.",nil,nil); + return +end + +Perun.onSimulationFrame = function() + + if(not Perun.State.slotsInitialised) then + local foo = Perun.func.ensureSlots; + + local status, error = pcall(foo); + if(status) then + Perun.AddLog( "slot information loaded") + else + Perun.AddLog( "error getting slot information", error) + end + end + + local now = DCS.getRealTime() + --net.log("Perun", "big updates check, now", now, "last sent slots", Perun.Stats.LastSentSlots, "last sent mission", Perun.Stats.LastSentMission) + + if(now - Perun.Stats.LastBigUpdate > Perun.bigUpdatesSeconds) then + if(now > Perun.Stats.LastSentSlots + Perun.refreshSlotsSeconds) then + local payload = Perun.Handlers["event"]("slots") + payload["slots"] = Perun.Mission.Slots; + + local json = net.lua2json(payload) + --net.log("Perun, slots", json) + Perun.func.sendData(json) + Perun.Stats.LastSentSlots = now + Perun.Stats.LastBigUpdate = now + elseif (now > Perun.Stats.LastSentMission + Perun.refreshMissionSeconds) then + local payload = Perun.Handlers["event"]("mission") + payload["mission"] = DCS.getCurrentMission()["mission"] + + local json = net.lua2json(payload); + --net.log("Perun, mission", json) + Perun.func.sendData(json) + Perun.Stats.LastSentMission = now + Perun.Stats.LastBigUpdate = now + end + end + + if(now > Perun.LastSentTCP + Perun.RefreshKeepAlive) then + Perun.func.sendData(" ") + end + + Perun.Stats.PreviousFrameStart = DCS.getRealTime() +end + +Perun.onPlayerStart = function (playerId) + -- Player entered cocpit + + net.send_chat_to(Perun.MOTD_line1, playerId); + net.send_chat_to(Perun.MOTD_line2, playerId); + +end + +Perun.onPlayerTrySendChat = function (playerID, msg, all) + -- Somebody tries to send chat message + if (msg == Perun.ConnectionError or msg == Perun.MOTD_line1 or msg == Perun.MOTD_line2) then + return msg; + end + + Perun.LogChat(playerID,msg,all) + return msg; +end + +Perun.onGameEvent = function (eventName,arg1,arg2,arg3,arg4,arg5,arg6,arg7) + local argCount = Perun.HandlerArgCounts[eventName] + local handler = Perun.Handlers[eventName] + + local payload = {} + if(argCount == 1) then + payload = handler(arg1) + elseif argCount == 2 then + payload = handler(arg1, arg2) + elseif argCount == 3 then + payload = handler(arg1, arg2, arg3) + elseif argCount == 4 then + payload = handler(arg1, arg2, arg3, arg4) + else + payload = handler(arg1, arg2, arg3, arg4, arg5, arg6, arg7) + end + + local json = net.lua2json(payload) + Perun.func.sendData(json) +end + +-- ########### Finalize and set callbacks ########### +if DCS.isServer() then + -- If this game instance is hosting multiplayer game, start Perun + Perun.DLL.StartOfApp(); -- Perun dll StartOfApp callback + Perun.MissionHash=Perun.generate_mission_hash(); -- Generate initial missionhash + DCS.setUserCallbacks(Perun); -- Set user callbacs, map DCS event handlers with functions defined above + Perun.ConnectToPerun(); -- Connect to Perun server + Perun.AddLog("Loaded - Perun for DCS World - version: " .. Perun.Version,0) -- Display perun information in log +end + + diff --git a/03_Perun_Lua_Wrapper/lua-5.1.5/src/luaconf.h b/03_Perun_Lua_Wrapper/lua-5.1.5/src/luaconf.h index e2cb261..01e65bf 100644 --- a/03_Perun_Lua_Wrapper/lua-5.1.5/src/luaconf.h +++ b/03_Perun_Lua_Wrapper/lua-5.1.5/src/luaconf.h @@ -91,7 +91,7 @@ ".\\?.lua;" LUA_LDIR"?.lua;" LUA_LDIR"?\\init.lua;" \ LUA_CDIR"?.lua;" LUA_CDIR"?\\init.lua" #define LUA_CPATH_DEFAULT \ - ".\\?.dll;" LUA_CDIR"?.dll;" LUA_CDIR"loadall.dll" + ".\\?.bin;" LUA_CDIR"?.bin;" LUA_CDIR"loadall.bin" #else #define LUA_ROOT "/usr/local/" diff --git a/03_Perun_Lua_Wrapper/perun/CmakeLists.txt b/03_Perun_Lua_Wrapper/perun/CmakeLists.txt index bfc31d2..5264b6f 100644 --- a/03_Perun_Lua_Wrapper/perun/CmakeLists.txt +++ b/03_Perun_Lua_Wrapper/perun/CmakeLists.txt @@ -7,7 +7,7 @@ set (PERUN_DLL_SOURCES "src/library.h" "src/library.cpp" "src/Connection.h" - "src/Connecton.cpp" + "src/Connection.cpp" ) include (GenerateExportHeader) diff --git a/03_Perun_Lua_Wrapper/perun/src/Connecton.cpp b/03_Perun_Lua_Wrapper/perun/src/Connection.cpp similarity index 94% rename from 03_Perun_Lua_Wrapper/perun/src/Connecton.cpp rename to 03_Perun_Lua_Wrapper/perun/src/Connection.cpp index 85c52e5..9faf2d5 100644 --- a/03_Perun_Lua_Wrapper/perun/src/Connecton.cpp +++ b/03_Perun_Lua_Wrapper/perun/src/Connection.cpp @@ -1,6 +1,6 @@ #include "Connection.h" -SocketWrapper::SocketWrapper() { +SocketWrapper::SocketWrapper(std::string name): outputFile(name, std::ofstream::app) { tcpSocket = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP); } @@ -44,14 +44,15 @@ void SocketWrapper::createConnection(std::string* host, const int* port) { // TCP sending loop bool nothingToSend = false; while (true) { - // Endless loop (will run after main dll thread is active) if (connectionState == CONNECTED && mutexLock.try_lock()) { if (sendQueue.empty()) { nothingToSend = true; } else { // Payload in queue auto payload = sendQueue.front(); - int length = payload->length(); + + outputFile.write(payload->c_str(), payload->length()); + outputFile.flush(); int bytesSent = send(tcpSocket, payload->c_str(), payload->length(), 0); if (bytesSent == payload->length()) { @@ -62,7 +63,7 @@ void SocketWrapper::createConnection(std::string* host, const int* port) { // Remaining paylad if (bytesSent > 0) { // Send remaining bytes - auto shortened = payload->substr(bytesSent, length - bytesSent); + auto shortened = payload->substr(bytesSent, payload->length() - bytesSent); sendQueue.pop_front(); sendQueue.push_front(&shortened); delete payload; diff --git a/03_Perun_Lua_Wrapper/perun/src/Connection.h b/03_Perun_Lua_Wrapper/perun/src/Connection.h index cd2480c..8e7ad32 100644 --- a/03_Perun_Lua_Wrapper/perun/src/Connection.h +++ b/03_Perun_Lua_Wrapper/perun/src/Connection.h @@ -17,7 +17,7 @@ enum enumConnectionState { class SocketWrapper { public: - SocketWrapper(); + SocketWrapper(std::string name); ~SocketWrapper(); void disconnect(); @@ -28,6 +28,9 @@ public: int getFlagConnected(); private: +// SocketWrapper(const SocketWrapper&); +// SocketWrapper& operator=(const SocketWrapper&); + SOCKET tcpSocket; std::string* tcpHost; int tcpPort; @@ -39,6 +42,7 @@ private: std::deque sendQueue; std::mutex mutexLock; + std::ofstream outputFile; void reconnect(); void tcpConnect(); diff --git a/03_Perun_Lua_Wrapper/perun/src/library.cpp b/03_Perun_Lua_Wrapper/perun/src/library.cpp index d3eba43..c6c5e2b 100644 --- a/03_Perun_Lua_Wrapper/perun/src/library.cpp +++ b/03_Perun_Lua_Wrapper/perun/src/library.cpp @@ -1,42 +1,68 @@ #include "library.h" -static SocketWrapper tcpConnection; +static SocketWrapper * tcpConnection = nullptr; + +/* this method exists for comments */ +static int valuesToReturn(int input) { + return input; +} static int appStartHook(lua_State* luaState) { // Starting the app - prepare + if(tcpConnection == nullptr) { + auto nowMillis = std::chrono::time_point_cast(std::chrono::system_clock::now()); + auto millis = nowMillis.time_since_epoch().count(); + + char buffer[50]; + sprintf_s(buffer, "c:/temp/perun.%llu.log", millis); + + tcpConnection = new SocketWrapper(std::string(buffer)); + } lua_pushinteger(luaState, 1); // First return value: confirmation that app was started - return (1); // Set return to number of arguments returned + return valuesToReturn(1); } static int appEndHook(lua_State* luaState) { // Closing the app - clean up - tcpConnection.disconnect(); + if(tcpConnection != nullptr) { + tcpConnection->disconnect(); + } - return (0); // No return values + return valuesToReturn(0); } static int tcpConnect(lua_State* luaState) { // Connect to the TCP socket - auto *host = new std::string(lua_tolstring(luaState, 1, 0)); - const int *port = new int(lua_tointeger(luaState, 2)); - tcpConnection.createConnection(host, port); + if(tcpConnection != nullptr) { + auto *host = new std::string(lua_tolstring(luaState, 1, 0)); + const int *port = new int(lua_tointeger(luaState, 2)); - return (0); // No return values + tcpConnection->createConnection(host, port); + } + + return valuesToReturn(0); } static int tcpSend(lua_State* luaState) { // Send frame over TCP socket - tcpConnection.enqueueForSending(new std::string(lua_tolstring(luaState, 1, 0))); + if(tcpConnection != nullptr) { + tcpConnection->enqueueForSending(new std::string(lua_tolstring(luaState, 1, 0))); - lua_pushinteger(luaState, tcpConnection.getFlagConnected()); // First return value: information if there is TCP connection - lua_pushinteger(luaState, tcpConnection.getAndResetReconnected()); // Secound return value: information if there was recent reconnection to TCP server + lua_pushinteger(luaState, + tcpConnection->getFlagConnected()); // First return value: information if there is TCP connection + lua_pushinteger(luaState, + tcpConnection->getAndResetReconnected()); // Second return value: information if there was recent reconnection to TCP server + } else { + lua_pushinteger(luaState, 0); + lua_pushinteger(luaState, 0); + } - return (2); // Set return to number of arguments returned + return valuesToReturn(2); } extern "C" int __declspec(dllexport) luaopen_perun(lua_State * L) { diff --git a/04_MySQL/m1081_perun.sql b/04_MySQL/m1081_perun.sql index d67ef90..cdc9d74 100644 --- a/04_MySQL/m1081_perun.sql +++ b/04_MySQL/m1081_perun.sql @@ -14,7 +14,7 @@ CREATE TABLE IF NOT EXISTS `pe_Config` ( `pe_Config_id` int(11) NOT NULL, `pe_Config_payload` varchar(255) DEFAULT NULL, PRIMARY KEY (`pe_Config_id`) -) ENGINE=InnoDB DEFAULT CHARSET=utf8; +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4; INSERT INTO `pe_Config` (`pe_Config_id`, `pe_Config_payload`) VALUES (1, 'v0.12.1'); @@ -28,7 +28,7 @@ CREATE TABLE IF NOT EXISTS `pe_DataMissionHashes` ( PRIMARY KEY (`pe_DataMissionHashes_id`), UNIQUE KEY `UNIQUE_hash` (`pe_DataMissionHashes_hash`), KEY `pe_DataMissionHashes_instance` (`pe_DataMissionHashes_instance`) -) ENGINE=InnoDB DEFAULT CHARSET=utf8; +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4; DROP TABLE IF EXISTS `pe_DataPlayers`; CREATE TABLE IF NOT EXISTS `pe_DataPlayers` ( @@ -39,7 +39,7 @@ CREATE TABLE IF NOT EXISTS `pe_DataPlayers` ( `pe_DataPlayers_updated` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP, PRIMARY KEY (`pe_DataPlayers_id`), UNIQUE KEY `UNIQUE_UCID` (`pe_DataPlayers_ucid`) -) ENGINE=InnoDB DEFAULT CHARSET=utf8; +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4; DROP TABLE IF EXISTS `pe_DataRaw`; CREATE TABLE IF NOT EXISTS `pe_DataRaw` ( @@ -50,7 +50,7 @@ CREATE TABLE IF NOT EXISTS `pe_DataRaw` ( PRIMARY KEY (`pe_dataraw_type`,`pe_dataraw_instance`), KEY `pe_dataraw_type_pe_dataraw_instance` (`pe_dataraw_type`,`pe_dataraw_instance`), KEY `pe_dataraw_updated` (`pe_dataraw_updated`) -) ENGINE=InnoDB DEFAULT CHARSET=utf8; +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4; DROP TABLE IF EXISTS `pe_DataTypes`; CREATE TABLE IF NOT EXISTS `pe_DataTypes` ( @@ -59,14 +59,14 @@ CREATE TABLE IF NOT EXISTS `pe_DataTypes` ( `pe_DataTypes_update` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP, PRIMARY KEY (`pe_DataTypes_id`), UNIQUE KEY `UNIQUE_TYPE_NAME` (`pe_DataTypes_name`) -) ENGINE=InnoDB DEFAULT CHARSET=utf8; +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4; DROP TABLE IF EXISTS `pe_LogChat`; CREATE TABLE IF NOT EXISTS `pe_LogChat` ( - `pe_LogChat_id` bigint(20) NOT NULL AUTO_INCREMENT, + `pe_LogChat_id` bigint NOT NULL AUTO_INCREMENT, `pe_LogChat_datetime` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP, - `pe_LogChat_missionhash_id` bigint(20) DEFAULT NULL, - `pe_LogChat_playerid` varchar(100) NOT NULL, + `pe_LogChat_missionhash_id` bigint DEFAULT NULL, + `pe_LogChat_playerid` bigint NOT NULL, `pe_LogChat_msg` text NOT NULL, `pe_LogChat_all` varchar(10) NOT NULL, PRIMARY KEY (`pe_LogChat_id`), @@ -74,7 +74,7 @@ CREATE TABLE IF NOT EXISTS `pe_LogChat` ( KEY `pe_LogChat_playerid` (`pe_LogChat_playerid`), KEY `pe_LogChat_datetime` (`pe_LogChat_datetime`), KEY `pe_LogChat_all` (`pe_LogChat_all`) -) ENGINE=InnoDB DEFAULT CHARSET=utf8; +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4; DROP TABLE IF EXISTS `pe_LogEvent`; CREATE TABLE IF NOT EXISTS `pe_LogEvent` ( @@ -90,7 +90,7 @@ CREATE TABLE IF NOT EXISTS `pe_LogEvent` ( KEY `pe_LogEvent_missionhash_id` (`pe_LogEvent_missionhash_id`), KEY `pe_LogEvent_datetime` (`pe_LogEvent_datetime`), KEY `pe_LogEvent_type_2` (`pe_LogEvent_type`) -) ENGINE=InnoDB DEFAULT CHARSET=utf8; +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4; DROP TABLE IF EXISTS `pe_LogLogins`; CREATE TABLE IF NOT EXISTS `pe_LogLogins` ( @@ -104,7 +104,7 @@ CREATE TABLE IF NOT EXISTS `pe_LogLogins` ( KEY `pe_LogLogins_playerid` (`pe_LogLogins_playerid`), KEY `pe_LogLogins_datetime` (`pe_LogLogins_datetime`), KEY `pe_LogLogins_instance` (`pe_LogLogins_instance`) -) ENGINE=InnoDB DEFAULT CHARSET=utf8; +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4; DROP TABLE IF EXISTS `pe_LogStats`; CREATE TABLE IF NOT EXISTS `pe_LogStats` ( @@ -149,7 +149,7 @@ CREATE TABLE IF NOT EXISTS `pe_LogStats` ( KEY `pe_LogStats_masterslot` (`pe_LogStats_masterslot`), KEY `pe_LogStats_mstatus` (`pe_LogStats_mstatus`), KEY `pe_LogStats_seat` (`pe_LogStats_seat`) -) ENGINE=InnoDB DEFAULT CHARSET=utf8; +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4; DROP TRIGGER IF EXISTS `pe_LogStats_UPDATE`; DELIMITER $$ CREATE TRIGGER `pe_LogStats_UPDATE` BEFORE UPDATE ON `pe_LogStats` FOR EACH ROW BEGIN @@ -182,7 +182,7 @@ CREATE TABLE IF NOT EXISTS `pe_OnlinePlayers` ( `pe_OnlinePlayers_slot` varchar(255) DEFAULT NULL, `pe_OnlinePlayers_ucid` varchar(255) DEFAULT NULL, `pe_OnlinePlayers_updated` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP -) ENGINE=InnoDB DEFAULT CHARSET=utf8; +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4; DROP TABLE IF EXISTS `pe_OnlineStatus`; CREATE TABLE IF NOT EXISTS `pe_OnlineStatus` ( @@ -197,7 +197,7 @@ CREATE TABLE IF NOT EXISTS `pe_OnlineStatus` ( `pe_OnlineStatus_perunversion_winapp` varchar(255) DEFAULT NULL, `pe_OnlineStatus_perunversion_dcshook` varchar(255) DEFAULT NULL, `pe_OnlineStatus_updated` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP -) ENGINE=InnoDB DEFAULT CHARSET=utf8; +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4; ALTER TABLE `pe_DataMissionHashes` ADD FULLTEXT KEY `pe_DataMissionHashes_hash` (`pe_DataMissionHashes_hash`);