diff --git a/client/app.js b/client/app.js index 228c1c07..de7446eb 100644 --- a/client/app.js +++ b/client/app.js @@ -2,6 +2,7 @@ var express = require('express'); var path = require('path'); var cookieParser = require('cookie-parser'); var logger = require('morgan'); +var fs = require('fs'); var indexRouter = require('./routes/index'); var uikitRouter = require('./routes/uikit'); @@ -21,12 +22,14 @@ app.use('/uikit', uikitRouter); app.set('view engine', 'ejs'); +let rawdata = fs.readFileSync('../olympus.json'); +let config = JSON.parse(rawdata); +app.get('/config', (req, res) => res.send(config)); + module.exports = app; const DemoDataGenerator = require('./demo.js'); - var demoDataGenerator = new DemoDataGenerator(10); - app.get('/demo/units', (req, res) => demoDataGenerator.units(req, res)); app.get('/demo/logs', (req, res) => demoDataGenerator.logs(req, res)); app.get('/demo/bullseyes', (req, res) => demoDataGenerator.bullseyes(req, res)); diff --git a/client/package.json b/client/package.json index d4c79d6b..7f785313 100644 --- a/client/package.json +++ b/client/package.json @@ -2,7 +2,7 @@ "name": "DCSOlympus", "node-main": "./bin/www", "main": "http://localhost:3000", - "version": "0.1.0-alpha", + "version": "0.1.1-alpha", "private": true, "scripts": { "copy": "copy .\\node_modules\\leaflet\\dist\\leaflet.css .\\public\\stylesheets\\leaflet.css", diff --git a/client/src/index.ts b/client/src/index.ts index 2a8b52d5..14082f93 100644 --- a/client/src/index.ts +++ b/client/src/index.ts @@ -9,7 +9,7 @@ import { AIC } from "./aic/aic"; import { ATC } from "./atc/atc"; import { FeatureSwitches } from "./featureswitches"; import { LogPanel } from "./panels/logpanel"; -import { getAirbases, getBullseye as getBullseyes, getMission, getUnits, toggleDemoEnabled } from "./server/server"; +import { getAirbases, getBullseye as getBullseyes, getConfig, getMission, getUnits, setAddress, toggleDemoEnabled } from "./server/server"; import { UnitDataTable } from "./units/unitdatatable"; var map: Map; @@ -69,16 +69,33 @@ function setup() { /* Setup event handlers */ setupEvents(); - /* On the first connection, force request of full data */ - getAirbases((data: AirbasesData) => getMissionData()?.update(data)); - getBullseyes((data: BullseyesData) => getMissionData()?.update(data)); - getMission((data: any) => {getMissionData()?.update(data)}); - getUnits((data: UnitsData) => getUnitsManager()?.update(data), true /* Does a full refresh */); - - /* Start periodically requesting updates */ - startPeriodicUpdate(); + getConfig(readConfig) } +function readConfig(config: any) +{ + if (config && config["server"] != undefined && config["server"]["address"] != undefined && config["server"]["port"] != undefined) + { + const address = config["server"]["address"]; + const port = config["server"]["port"]; + if ((typeof address === 'string' || address instanceof String) && typeof port == 'number') + { + setAddress(address, port); + } + + /* On the first connection, force request of full data */ + getAirbases((data: AirbasesData) => getMissionData()?.update(data)); + getBullseyes((data: BullseyesData) => getMissionData()?.update(data)); + getMission((data: any) => {getMissionData()?.update(data)}); + getUnits((data: UnitsData) => getUnitsManager()?.update(data), true /* Does a full refresh */); + + /* Start periodically requesting updates */ + startPeriodicUpdate(); + } + else { + throw new Error('Could not read configuration file!'); + } +} function startPeriodicUpdate() { requestUpdate(); diff --git a/client/src/panels/unitcontrolpanel.ts b/client/src/panels/unitcontrolpanel.ts index 72993118..6ed8e807 100644 --- a/client/src/panels/unitcontrolpanel.ts +++ b/client/src/panels/unitcontrolpanel.ts @@ -87,7 +87,7 @@ export class UnitControlPanel extends Panel { } var button = document.createElement("button"); - var callsign = aircraftDatabase.getByName(unit.getBaseData().unitName)?.label || ""; + var callsign = unit.getBaseData().unitName || ""; button.innerText = unit.getBaseData().unitName; button.setAttribute("data-short-label", database?.getByName(unit.getBaseData().name)?.shortLabel || ""); diff --git a/client/src/server/server.ts b/client/src/server/server.ts index 07e1f826..822d5d9b 100644 --- a/client/src/server/server.ts +++ b/client/src/server/server.ts @@ -2,9 +2,8 @@ import * as L from 'leaflet' import { setConnected } from '..'; import { SpawnOptions } from '../controls/mapcontextmenu'; -/* Edit here to change server address */ -const REST_ADDRESS = "http://localhost:30000/olympus"; -const DEMO_ADDRESS = "http://localhost:3000/demo"; +var REST_ADDRESS = "http://localhost:30000/olympus"; +var DEMO_ADDRESS = window.location.href + "demo"; const UNITS_URI = "units"; const LOGS_URI = "logs"; const AIRBASES_URI = "airbases"; @@ -45,6 +44,25 @@ export function POST(request: object, callback: CallableFunction){ xhr.send(JSON.stringify(request)); } +export function getConfig(callback: CallableFunction) { + var xmlHttp = new XMLHttpRequest(); + xmlHttp.open("GET", window.location.href + "config", true); + xmlHttp.onload = function (e) { + var data = JSON.parse(xmlHttp.responseText); + callback(data); + lastUpdateTime = parseInt(data.time); + }; + xmlHttp.onerror = function () { + console.error("An error occurred during the XMLHttpRequest, could not retrieve configuration file"); + }; + xmlHttp.send(null); +} + +export function setAddress(address: string, port: number) { + REST_ADDRESS = `http://${address}:${port}/olympus` + console.log(`Setting REST address to ${REST_ADDRESS}`) +} + export function getAirbases(callback: CallableFunction) { GET(callback, AIRBASES_URI); } diff --git a/client/src/units/unit.ts b/client/src/units/unit.ts index 22982d70..616d098c 100644 --- a/client/src/units/unit.ts +++ b/client/src/units/unit.ts @@ -573,6 +573,15 @@ export class GroundUnit extends Unit { super(ID, data); } + getMarkerHTML() { + var role = groundUnitsDatabase.getByName(this.getBaseData().name)?.loadouts[0].roles[0]; + return `
+
+
+
${role?.substring(0, 1)?.toUpperCase() || ""}
+
` + } + getMarkerCategory() { // TODO this is very messy diff --git a/client/views/index.ejs b/client/views/index.ejs index 2a0a91ff..79e15902 100644 --- a/client/views/index.ejs +++ b/client/views/index.ejs @@ -10,13 +10,13 @@ - - + + diff --git a/client/views/navbar.ejs b/client/views/navbar.ejs index fd0960d1..9d5993c6 100644 --- a/client/views/navbar.ejs +++ b/client/views/navbar.ejs @@ -7,7 +7,7 @@

Olympus

-
v0.1.0
+
v0.1.1
Discord diff --git a/installer/olympus.iss b/installer/olympus.iss index 65ce43c8..ec4ca74c 100644 --- a/installer/olympus.iss +++ b/installer/olympus.iss @@ -18,6 +18,7 @@ Name: "desktopicon"; Description: "{cm:CreateDesktopIcon}"; GroupDescription: "{ ;Source: "..\scripts\OlympusExport.lua"; DestDir: "{app}\Scripts"; Flags: ignoreversion ;Source: "..\scripts\OlympusPatcher.exe"; DestDir: "{app}\Scripts"; Flags: ignoreversion Source: "..\scripts\OlympusHook.lua"; DestDir: "{app}\Scripts\Hooks"; Flags: ignoreversion +Source: "..\olympus.json"; DestDir: "{app}\Mods\Services\Olympus"; Flags: onlyifdoesntexist Source: "..\scripts\OlympusCommand.lua"; DestDir: "{app}\Mods\Services\Olympus\Scripts"; Flags: ignoreversion Source: "..\scripts\unitPayloads.lua"; DestDir: "{app}\Mods\Services\Olympus\Scripts"; Flags: ignoreversion ;Source: "..\scripts\OlympusMission.lua"; DestDir: "{app}\Mods\Services\Olympus\Scripts"; Flags: ignoreversion diff --git a/mod/entry.lua b/mod/entry.lua index 319e5f60..b2a04abe 100644 --- a/mod/entry.lua +++ b/mod/entry.lua @@ -15,7 +15,7 @@ declare_plugin(self_ID, shortName = "Olympus", fileMenuName = "Olympus", - version = "0.1.0-alpha", + version = "0.1.1-alpha", state = "installed", developerName= "DCS Refugees 767 squadron", info = _("DCS Olympus is a mod for DCS World. It allows users to spawn, control, task, group, and remove units from a DCS World server using a real-time map interface, similarly to Real Time Strategy games. The user interface also provides useful informations units, like loadouts, fuel, tasking, and so on. In the future, more features for DCS World GCI and JTAC will be available."), diff --git a/olympus.json b/olympus.json new file mode 100644 index 00000000..7b1b026b --- /dev/null +++ b/olympus.json @@ -0,0 +1,6 @@ +{ + "server": { + "address": "localhost", + "port": 30000 + } +} diff --git a/scripts/OlympusExport.lua b/scripts/OlympusExport.lua deleted file mode 100644 index fbc8f3f8..00000000 --- a/scripts/OlympusExport.lua +++ /dev/null @@ -1,40 +0,0 @@ -local version = 'v0.1.0-alpha' - -Olympus = {} -Olympus.OlympusDLL = nil -Olympus.cppRESTDLL = nil -Olympus.DLLsloaded = false -Olympus.OlympusModPath = os.getenv('DCSOLYMPUS_PATH')..'\\bin\\' - -log.write('Olympus.EXPORT.LUA', log.INFO, 'Executing OlympusExport.lua') - -function Olympus.loadDLLs() - -- Add the .dll paths - package.cpath = package.cpath..';'..Olympus.OlympusModPath..'?.dll;' - - local status - log.write('Olympus.HOOKS.LUA', log.INFO, 'Loading olympus.dll from ['..Olympus.OlympusModPath..']') - status, Olympus.OlympusDLL = pcall(require, 'olympus') - if status then - log.write('Olympus.HOOKS.LUA', log.INFO, 'olympus.dll loaded successfully') - return true - else - log.write('Olympus.HOOKS.LUA', log.ERROR, 'Error loading olympus.dll: '..Olympus.OlympusDLL) - return false - end -end - -do - if isOlympusModuleInitialized~=true then - local OlympusName = 'Olympus ' .. version .. ' C++ module'; - isOlympusModuleInitialized=true; - Olympus.DLLsloaded = Olympus.loadDLLs() - if Olympus.DLLsloaded then - log.write('Olympus.EXPORT.LUA', log.INFO, OlympusName..' successfully loaded.') - else - log.write('Olympus.EXPORT.LUA', log.ERROR, 'Failed to load '..OlympusName) - end - else - log.write('Olympus.EXPORT.LUA', log.INFO, 'olympus.dll already initialized') - end -end \ No newline at end of file diff --git a/scripts/OlympusMission.lua b/scripts/OlympusMission.lua deleted file mode 100644 index a0690e32..00000000 --- a/scripts/OlympusMission.lua +++ /dev/null @@ -1,135 +0,0 @@ -local version = 'v0.1.0-alpha' - -Olympus = {} -Olympus.groupIndex = 0 -Olympus.groupStep = 40 - -function Olympus.notify(message, displayFor) - trigger.action.outText(message, displayFor) -end - -function Olympus.setMissionData(arg, time) - local missionData = {} - - -- Bullseye data - local bullseyes = {} - for i = 0, 2 do - local bullseyeVec3 = coalition.getMainRefPoint(i) - local bullseyeLatitude, bullseyeLongitude, bullseyeAltitude = coord.LOtoLL(bullseyeVec3) - bullseyes[i] = {} - bullseyes[i]["latitude"] = bullseyeLatitude - bullseyes[i]["longitude"] = bullseyeLongitude - end - - -- Units tactical data - local unitsData = {} - - local startIndex = Olympus.groupIndex - local endIndex = startIndex + Olympus.groupStep - local index = 0 - for groupName, gp in pairs(mist.DBs.groupsByName) do - index = index + 1 - if index > startIndex then - if groupName ~= nil then - local group = Group.getByName(groupName) - if group ~= nil then - local controller = group:getController() - for index, unit in pairs(group:getUnits()) do - local table = {} - table["targets"] = {} - table["targets"]["visual"] = controller:getDetectedTargets(1) - table["targets"]["radar"] = controller:getDetectedTargets(4) - table["targets"]["rwr"] = controller:getDetectedTargets(16) - table["targets"]["other"] = controller:getDetectedTargets(2, 8, 32) - - table["hasTask"] = controller:hasTask() - - table["ammo"] = unit:getAmmo() - table["fuel"] = unit:getFuel() - table["life"] = unit:getLife() / unit:getLife0() - unitsData[unit:getObjectID()] = table - end - end - end - end - if index >= endIndex then - break - end - end - if index ~= endIndex then - Olympus.groupIndex = 0 - else - Olympus.groupIndex = endIndex - end - - -- Airbases data - local base = world.getAirbases() - local airbases = {} - for i = 1, #base do - local info = {} - local latitude, longitude, altitude = coord.LOtoLL(Airbase.getPoint(base[i])) - info["callsign"] = Airbase.getCallsign(base[i]) - local coalitionID = Airbase.getCoalition(base[i]) - if coalitionID == 0 then - info["coalition"] = "neutral" - elseif coalitionID == 1 then - info["coalition"] = "red" - else - info["coalition"] = "blue" - end - info["latitude"] = latitude - info["longitude"] = longitude - if Airbase.getUnit(base[i]) then - info["unitId"] = Airbase.getUnit(base[i]):getID() - end - airbases[i] = info - end - - local mission = {} - mission.theatre = env.mission.theatre - - -- Assemble missionData table - missionData["bullseyes"] = bullseyes - missionData["unitsData"] = unitsData - missionData["airbases"] = airbases - missionData["mission"] = mission - - local command = "Olympus.missionData = " .. Olympus.serializeTable(missionData) .. "\n" .. "Olympus.OlympusDLL.setMissionData()" - net.dostring_in("export", command) - return time + 1 -end - -function Olympus.serializeTable(val, name, skipnewlines, depth) - skipnewlines = skipnewlines or false - depth = depth or 0 - - local tmp = string.rep(" ", depth) - if name then - if type(name) == "number" then - tmp = tmp .. "[" .. name .. "]" .. " = " - else - tmp = tmp .. name .. " = " - end - end - - if type(val) == "table" then - tmp = tmp .. "{" .. (not skipnewlines and "\n" or "") - for k, v in pairs(val) do - tmp = tmp .. Olympus.serializeTable(v, k, skipnewlines, depth + 1) .. "," .. (not skipnewlines and "\n" or "") - end - tmp = tmp .. string.rep(" ", depth) .. "}" - elseif type(val) == "number" then - tmp = tmp .. tostring(val) - elseif type(val) == "string" then - tmp = tmp .. string.format("%q", val) - elseif type(val) == "boolean" then - tmp = tmp .. (val and "true" or "false") - else - tmp = tmp .. "\"[inserializeable datatype:" .. type(val) .. "]\"" - end - - return tmp -end - -timer.scheduleFunction(Olympus.setMissionData, {}, timer.getTime() + 1) -Olympus.notify("OlympusMission " .. version .. " script loaded correctly", 10) diff --git a/scripts/OlympusPatcher.exe b/scripts/OlympusPatcher.exe deleted file mode 100644 index 2c93d37e..00000000 Binary files a/scripts/OlympusPatcher.exe and /dev/null differ diff --git a/scripts/OlympusPatcher.py b/scripts/OlympusPatcher.py deleted file mode 100644 index 1fb13784..00000000 --- a/scripts/OlympusPatcher.py +++ /dev/null @@ -1,42 +0,0 @@ -import shutil -import sys - -START_STRING = "-- Olympus START\n" -END_STRING = "-- Olympus END\n" -EXPORT_STRING = "local Olympuslfs=require('lfs');dofile(Olympuslfs.writedir()..'Scripts/OlympusExport.lua')\n" - -def main(flag): - if flag == "-i": - try: - with open("Export.lua", "r") as f: - shutil.copyfile("Export.lua", "Export.lua.bak") - lines = f.readlines() - if START_STRING in lines: - return - except FileNotFoundError: - print('File does not exist') - - with open("Export.lua", "a") as f: - f.writelines(["\n", START_STRING, EXPORT_STRING, END_STRING, "\n"]) - elif flag == "-u": - try: - with open("Export.lua", "r") as f: - shutil.copyfile("Export.lua", "Export.lua.bak") - lines = f.readlines() - except FileNotFoundError: - print('File does not exist') - - with open("Export.lua", "w") as f: - block = False - for line in lines: - if line == START_STRING: - block = True - - if not block: - f.write(line) - - if line == END_STRING: - block = False - -if __name__ == "__main__": - main(sys.argv[1]) diff --git a/scripts/OlympusPatcher.spec b/scripts/OlympusPatcher.spec deleted file mode 100644 index 59ead9c4..00000000 --- a/scripts/OlympusPatcher.spec +++ /dev/null @@ -1,44 +0,0 @@ -# -*- mode: python ; coding: utf-8 -*- - - -block_cipher = None - - -a = Analysis( - ['OlympusPatcher.py'], - pathex=[], - binaries=[], - datas=[], - hiddenimports=[], - hookspath=[], - hooksconfig={}, - runtime_hooks=[], - excludes=[], - win_no_prefer_redirects=False, - win_private_assemblies=False, - cipher=block_cipher, - noarchive=False, -) -pyz = PYZ(a.pure, a.zipped_data, cipher=block_cipher) - -exe = EXE( - pyz, - a.scripts, - a.binaries, - a.zipfiles, - a.datas, - [], - name='OlympusPatcher', - debug=False, - bootloader_ignore_signals=False, - strip=False, - upx=True, - upx_exclude=[], - runtime_tmpdir=None, - console=True, - disable_windowed_traceback=False, - argv_emulation=False, - target_arch=None, - codesign_identity=None, - entitlements_file=None, -) diff --git a/src/core/src/server.cpp b/src/core/src/server.cpp index 3286cbaa..192a0440 100644 --- a/src/core/src/server.cpp +++ b/src/core/src/server.cpp @@ -179,7 +179,35 @@ void Server::handle_put(http_request request) void Server::task() { - http_listener listener(wstring(REST_ADDRESS) + L"/" + wstring(REST_URI)); + wstring address = wstring(REST_ADDRESS); + wstring modLocation; + char* buf = nullptr; + size_t sz = 0; + if (_dupenv_s(&buf, &sz, "DCSOLYMPUS_PATH") == 0 && buf != nullptr) + { + std::ifstream ifstream(string(buf) + "\\olympus.json"); + std::stringstream ss; + ss << ifstream.rdbuf(); + std::error_code errorCode; + json::value config = json::value::parse(to_wstring(ss.str()), errorCode); + if (config.is_object() && config.has_object_field(L"server") && + config[L"server"].has_string_field(L"address") && config[L"server"].has_number_field(L"port")) + { + address = L"http://" + config[L"server"][L"address"].as_string() + L":" + to_wstring(config[L"server"][L"port"].as_number().to_int32()); + log(L"Starting server on " + address); + } + else + { + log(L"Error reading configuration file. Starting server on " + address); + } + free(buf); + } + else + { + log(L"DCSOLYMPUS_PATH environment variable is missing, starting server on " + address); + } + + http_listener listener(address + L"/" + wstring(REST_URI)); std::function handle_options = std::bind(&Server::handle_options, this, std::placeholders::_1); std::function handle_get = std::bind(&Server::handle_get, this, std::placeholders::_1);