mirror of
https://github.com/DaKerboul/perun.git
synced 2026-08-09 19:55:40 +02:00
481 lines
14 KiB
Lua
481 lines
14 KiB
Lua
-- 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
|
|
|
|
|