diff --git a/LEGAL b/LEGAL index b0059956..ff20b280 100644 --- a/LEGAL +++ b/LEGAL @@ -2,7 +2,8 @@ DCS Olympus A real-time AI unit control mod for DCS World -Copyright (C) 2023 Veltro & Gang +Copyright (C) 2023 Veltro & Gang (the "DCS Olympus Team" or the +"Rightsholders") DCS Olympus (the "MATERIAL" or "Software") is provided completely free to users subject to the it under both the terms of version 3 of the GNU @@ -665,4 +666,13 @@ you, the licensee, and the authors and/or copyright holders of the Software with respect to the subject matter to which it pertains. It supersedes all prior agreements and understandings (if applicable), oral or written, with respect to such matters. - + +3. Unilateral Modification + + The parties agree that the DCS Olympus Team shall have the right to +unilaterally modify these terms (i.e. the agreement between you and the +DCS Olympus Team for the use of the Software), and that parties shall +be bound by such terms as modified from time to time. The DCS Olympus Team +shall not have an obligation to inform you of such modification, save that +such changes will be published on the DCS Olympus Github Repository located at +https://github.com/Pax1601/DCSOlympus. diff --git a/README.md b/README.md index 83953e29..eafa8299 100644 --- a/README.md +++ b/README.md @@ -1,4 +1,4 @@ -# Important note: DCS Olympus is in alpha state. No official release has been produced yet. The first public version is planned for Q4 2023. +# Important note: DCS Olympus is in alpha state. No official release has been produced yet. The first public version is planned for mid december 2023. # DCS Olympus *A real-time web interface to spawn and control units in DCS World* @@ -6,32 +6,19 @@ ![alt text](https://github.com/Pax1601/DCSOlympus/blob/main/client/sample.png?raw=true) ### What is this? -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. +DCS: Olympus is a free and open-source mod for DCS that enables dynamic real-time control through a map interface. The user is able to spawn units/groups, deploy a variety of effects such as smoke, flares, or explosions, and waypoints/tasks can be given to AI units in real-time in a way similar to a classic RTS game. -### Features and how to use it -- Spawn air and ground units, with preset loadouts - - Double click on the map to spawn a blue and red units, both in the air and in the ground, with preset loadouts for air-to-air or air-to-ground tasks; -- Control units - - Select one ore more units to move them around. Hold down ctrl and click to create a route for the unit to follow; -- Attack other units - - After selecting one ore more units, double click on another unit and select "Attack" to attack it, depending on the available weapons. +Additionally Olympus is able to run several effects and unit behaviours beyond the core DCS offerings. This includes such things as napalm and white phosphosous explosions, or setting up AA units to fire at players and miss, and more. + +It even includes Red and Blue modes which limit your view and powers to just seeing what your coalition sees, with a spawning budget you could play against your friends even with no-one in the game piloting, or have a Red commander working against a squadron of blue pilots, and/or a blue commander working with them. + +Even better it requires no client mods be installed if used on a server + +The full feature list is simply too long to enumerate in a short summary but needless to say Olympus offers up a lot of unique gameplay that has previously not existed, and enhances many other elements of DCS in exciting ways ### Installing DCS Olympus A prebuilt installer will soon be released and available here -### Building DCS Olympus -DCS Olympus is comprised of two modules: - -A "core" c++ .dll module, which is run by DCS and exposes all the necessary data, and provides endpoints for commands from a REST server. A Visual Studio 2017/2019/2022 solution is provided, and requires no additional configuration. The core dll solution has two dependencies, both can be installed using vcpkg (https://vcpkg.io/en/getting-started.html): -- cpprestsdk: `vcpkg install cpprestsdk:x64-windows` -- geographiclib: `vcpkg install geographiclib:x64-windows` - - -A "client" node.js typescript web app, which can be hosted on the server using express.js. A Visual Studio Code configuration is provided for debugging. The client requires node.js to be installed for building (https://nodejs.org/en/). After installing node.js, move in the client folder and run the following commands: -- `npm install` -- `npm -g install` - - After installing all the necessary dependencies you can start a development server executing the *client/debug.bat* batch file, and visiting http:\\localhost:3000 with any modern browser (tested with updated Chrome, Firefox and Edge). However, it is highly suggested to simply run the `Launch Chrome against localhost` debug configuration in Visual Studio Code. diff --git a/client/public/stylesheets/other/toolbar.css b/client/public/stylesheets/other/toolbar.css index c90df5f8..4754fa83 100644 --- a/client/public/stylesheets/other/toolbar.css +++ b/client/public/stylesheets/other/toolbar.css @@ -14,6 +14,10 @@ width: fit-content; } +#app-icon>.ol-select-value { + box-shadow: none; +} + #toolbar-summary { background-image: url("/images/icon-round.png"); background-position: 20px 22px; @@ -29,22 +33,65 @@ white-space: nowrap; } -#toolbar-container>*:nth-child(2)>svg { - display: none; - width: 0px; - height: 0px; +.ol-panel-tab { + align-items: center; + display:flex; + flex-direction: row; + margin-right:6px; +} + +.ol-panel-tab svg { + height:24; + width:24px; +} + +.ol-panel-tab svg * { + fill:white; +} + +.ol-panel-tab span { + font-size:13px; + font-weight:400; + padding:0 6px; +} + +#view-label { + margin-left: 5px; +} + +#view-label svg { + height: 20px; + width: 20px; } #toolbar-container>*:nth-child(3)>svg { display: none; } +#unit-visibility-control > div:nth-child(3), +#coalition-visibility-control { + border-left: 2px solid white; + padding-left: 12px; +} + @media (max-width: 1145px) { #toolbar-container { flex-direction: column; align-items: start; } + #toolbar-container .ol-panel .ol-panel-tab { + margin-right:0; + } + + #toolbar-container .ol-panel:hover .ol-panel-tab { + display:none; + } + + #toolbar-container .ol-panel-tab span { + display:none; + } + #toolbar-container>*:nth-child(1):not(:hover) { width: fit-content; height: fit-content; @@ -62,10 +109,10 @@ } #toolbar-container>*:not(:first-child):not(:hover)>svg { - display: block; - width: 24px; - height: 24px; + display: block; filter: invert(); + height: 24px; + width: 24px; } #toolbar-container>*:not(:first-child):not(:hover)>*:not(:first-child) { diff --git a/client/public/stylesheets/style/style.css b/client/public/stylesheets/style/style.css index 505e662a..f8643943 100644 --- a/client/public/stylesheets/style/style.css +++ b/client/public/stylesheets/style/style.css @@ -72,6 +72,18 @@ form { padding: 0; } +button svg.fill-coalition * { + fill: var(--primary-neutral) !important; +} + +button svg.fill-coalition[data-coalition="blue"] * { + fill: var(--primary-blue) !important; +} + +button svg.fill-coalition[data-coalition="red"] * { + fill: var(--primary-red) !important; +} + .pill { background-color: var(--background-steel); border-radius: 999px; @@ -384,7 +396,7 @@ button.ol-button-warning>svg:first-child { } nav.ol-panel { - column-gap: 20px; + column-gap: 10px; display: flex; flex-direction: row; height: 58px; @@ -396,8 +408,7 @@ nav.ol-panel> :last-child { .ol-panel .ol-group { align-items: center; - border-radius: var(--border-radius-sm); - column-gap: 10px; + column-gap: 12px; display: flex; flex-direction: row; flex-wrap: nowrap; @@ -665,13 +676,13 @@ nav.ol-panel> :last-child { } .ol-navbar-buttons-group button.off svg * { - fill: white !important; - stroke: white !important; + fill: white; + stroke: white; } .ol-navbar-buttons-group button svg * { - fill: var(--background-steel) !important; - stroke: var(--background-steel) !important; + fill: var(--background-steel); + stroke: var(--background-steel); } .ol-navbar-buttons-group .protectable button:first-of-type { @@ -1604,6 +1615,24 @@ input[type=number]::-webkit-outer-spin-button { fill: lightgray; } +#map-visibility-options .ol-select-options .ol-checkbox { + font-size:13px; + font-weight:400; + padding:6px 15px; +} + +#map-visibility-options .ol-select-options .ol-checkbox:first-of-type { + padding-top:12px; +} + +#map-visibility-options .ol-select-options .ol-checkbox:last-of-type { + padding-bottom:12px; +} + +#map-visibility-options .ol-select-options .ol-checkbox label:hover span { + text-decoration: underline; +} + .ol-log-entry:first-of-type { border-top: 1px solid #FFFFFF44; } @@ -1699,4 +1728,5 @@ input[type=number]::-webkit-outer-spin-button { 50% { opacity: 0; } -} \ No newline at end of file +} + diff --git a/client/public/themes/olympus/images/buttons/visibility/circle-dot.svg b/client/public/themes/olympus/images/buttons/visibility/circle-dot.svg new file mode 100644 index 00000000..f98ede3a --- /dev/null +++ b/client/public/themes/olympus/images/buttons/visibility/circle-dot.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/client/public/themes/olympus/images/buttons/visibility/flag.svg b/client/public/themes/olympus/images/buttons/visibility/flag.svg new file mode 100644 index 00000000..a1efc147 --- /dev/null +++ b/client/public/themes/olympus/images/buttons/visibility/flag.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/client/public/themes/olympus/images/buttons/visibility/shield.svg b/client/public/themes/olympus/images/buttons/visibility/shield.svg new file mode 100644 index 00000000..d8ff87ec --- /dev/null +++ b/client/public/themes/olympus/images/buttons/visibility/shield.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/client/public/themes/olympus/images/icons/eye-solid.svg b/client/public/themes/olympus/images/icons/eye-solid.svg index 3b5d2412..63bc71ba 100644 --- a/client/public/themes/olympus/images/icons/eye-solid.svg +++ b/client/public/themes/olympus/images/icons/eye-solid.svg @@ -1 +1 @@ - \ No newline at end of file + \ No newline at end of file diff --git a/client/sample.png b/client/sample.png index 5e941cd6..4534d728 100644 Binary files a/client/sample.png and b/client/sample.png differ diff --git a/client/src/contextmenus/coalitionareacontextmenu.ts b/client/src/contextmenus/coalitionareacontextmenu.ts index ad6ab040..ca993f6e 100644 --- a/client/src/contextmenus/coalitionareacontextmenu.ts +++ b/client/src/contextmenus/coalitionareacontextmenu.ts @@ -27,8 +27,8 @@ export class CoalitionAreaContextMenu extends ContextMenu { super(ID); /* Create the coalition switch */ - this.#coalitionSwitch = new Switch("coalition-area-switch", (value: boolean) => this.#onSwitchClick(value)); - this.#coalitionSwitch.setValue(false); + this.#coalitionSwitch = new Switch("coalition-area-switch", (value: boolean) => this.#onSwitchClick(value), true); + this.#coalitionSwitch.setValue(true); /* Create the controls of the IADS creation submenu */ this.#iadsTypesDropdown = new Dropdown("iads-units-type-options", () => { }); @@ -146,11 +146,11 @@ export class CoalitionAreaContextMenu extends ContextMenu { /** Callback event called when the coalition switch is clicked to change the coalition of the CoalitionArea * - * @param value Switch position (false: blue, true: red) + * @param value Switch position (false: red, true: blue) */ #onSwitchClick(value: boolean) { if (getApp().getMissionManager().getCommandModeOptions().commandMode == GAME_MASTER) { - this.getCoalitionArea()?.setCoalition(value ? "red" : "blue"); + this.getCoalitionArea()?.setCoalition(value ? "blue" : "red"); this.getContainer()?.querySelectorAll('[data-coalition]').forEach((element: any) => { element.setAttribute("data-coalition", this.getCoalitionArea()?.getCoalition()) }); diff --git a/client/views/contextmenus/coalitionarea.ejs b/client/views/contextmenus/coalitionarea.ejs index 7341fa99..85ad2873 100644 --- a/client/views/contextmenus/coalitionarea.ejs +++ b/client/views/contextmenus/coalitionarea.ejs @@ -1,7 +1,7 @@
-
+
-
Options
+
Options
@@ -46,25 +46,28 @@
\ No newline at end of file diff --git a/scripts/OlympusCommand.lua b/scripts/OlympusCommand.lua index 3f186e90..2037c261 100644 --- a/scripts/OlympusCommand.lua +++ b/scripts/OlympusCommand.lua @@ -1345,3 +1345,11 @@ Olympus.initializeUnits() Olympus.notify("OlympusCommand script " .. version .. " loaded successfully", 2, true) +-- Load the current instance folder +local lfs = require('lfs') + +Olympus.instancePath = lfs.writedir().."Mods\\Services\\Olympus" + +Olympus.notify("Starting DCS Olympus backend session in "..Olympus.instancePath, 2) +Olympus.OlympusDLL.setInstancePath() + diff --git a/src/core/include/unit.h b/src/core/include/unit.h index d60f6c36..73d8ca0b 100644 --- a/src/core/include/unit.h +++ b/src/core/include/unit.h @@ -148,7 +148,7 @@ public: virtual DataTypes::Radio getRadio() { return radio; } virtual DataTypes::GeneralSettings getGeneralSettings() { return generalSettings; } virtual vector getAmmo() { return ammo; } - virtual vector getTargets() { return contacts; } + virtual vector getContacts() { return contacts; } virtual list getActivePath() { return activePath; } virtual bool getIsLeader() { return isLeader; } virtual unsigned char getOperateAs() { return operateAs; } diff --git a/src/core/src/aircraft.cpp b/src/core/src/aircraft.cpp index be3021f9..eb716c01 100644 --- a/src/core/src/aircraft.cpp +++ b/src/core/src/aircraft.cpp @@ -12,24 +12,18 @@ using namespace GeographicLib; extern Scheduler* scheduler; extern UnitsManager* unitsManager; json::value Aircraft::database = json::value(); +extern string instancePath; void Aircraft::loadDatabase(string path) { - char* buf = nullptr; - size_t sz = 0; - if (_dupenv_s(&buf, &sz, "DCSOLYMPUS_PATH") == 0 && buf != nullptr) - { - std::ifstream ifstream(string(buf) + path); - std::stringstream ss; - ss << ifstream.rdbuf(); - std::error_code errorCode; - database = json::value::parse(ss.str(), errorCode); - if (database.is_object()) - log("Aircrafts database loaded correctly"); - else - log("Error reading Aircrafts database file"); - - free(buf); - } + std::ifstream ifstream(instancePath + path); + std::stringstream ss; + ss << ifstream.rdbuf(); + std::error_code errorCode; + database = json::value::parse(ss.str(), errorCode); + if (database.is_object()) + log("Aircrafts database loaded correctly from " + instancePath + path); + else + log("Error reading Aircrafts database file"); } /* Aircraft */ diff --git a/src/core/src/core.cpp b/src/core/src/core.cpp index 35822d46..d59432ce 100644 --- a/src/core/src/core.cpp +++ b/src/core/src/core.cpp @@ -25,6 +25,7 @@ json::value missionData = json::value::object(); mutex mutexLock; string sessionHash; +string instancePath; bool initialized = false; @@ -69,6 +70,20 @@ extern "C" DllExport int coreInit(lua_State* L) return(0); } +extern "C" DllExport int coreInstancePath(lua_State * L) +{ + /* Lock for thread safety */ + lock_guard guard(mutexLock); + + lua_getglobal(L, "Olympus"); + lua_getfield(L, -1, "instancePath"); + instancePath = lua_tostring(L, -1); + + log("Setting instance path to " + instancePath); + + return(0); +} + extern "C" DllExport int coreFrame(lua_State* L) { if (!initialized) diff --git a/src/core/src/groundunit.cpp b/src/core/src/groundunit.cpp index 56ba31d9..b9ab0183 100644 --- a/src/core/src/groundunit.cpp +++ b/src/core/src/groundunit.cpp @@ -12,27 +12,21 @@ using namespace GeographicLib; extern Scheduler* scheduler; extern UnitsManager* unitsManager; json::value GroundUnit::database = json::value(); +extern string instancePath; #define RANDOM_ZERO_TO_ONE (double)(rand()) / (double)(RAND_MAX) #define RANDOM_MINUS_ONE_TO_ONE (((double)(rand()) / (double)(RAND_MAX) - 0.5) * 2) void GroundUnit::loadDatabase(string path) { - char* buf = nullptr; - size_t sz = 0; - if (_dupenv_s(&buf, &sz, "DCSOLYMPUS_PATH") == 0 && buf != nullptr) - { - std::ifstream ifstream(string(buf) + path); - std::stringstream ss; - ss << ifstream.rdbuf(); - std::error_code errorCode; - database = json::value::parse(ss.str(), errorCode); - if (database.is_object()) - log("Ground Units database loaded correctly"); - else - log("Error reading Ground Units database file"); - - free(buf); - } + std::ifstream ifstream(instancePath + path); + std::stringstream ss; + ss << ifstream.rdbuf(); + std::error_code errorCode; + database = json::value::parse(ss.str(), errorCode); + if (database.is_object()) + log("GroundUnits database loaded correctly from " + instancePath + path); + else + log("Error reading GroundUnits database file"); } /* Ground unit */ @@ -68,6 +62,10 @@ void GroundUnit::setState(unsigned char newState) case State::REACH_DESTINATION: { break; } + case State::ATTACK: { + setTargetID(NULL); + break; + } case State::FIRE_AT_AREA: { setTargetPosition(Coords(NULL)); break; @@ -102,6 +100,13 @@ void GroundUnit::setState(unsigned char newState) resetActiveDestination(); break; } + case State::ATTACK: { + setEnableTaskCheckFailed(true); + clearActivePath(); + resetActiveDestination(); + resetTask(); + break; + } case State::FIRE_AT_AREA: { setEnableTaskCheckFailed(true); clearActivePath(); @@ -174,6 +179,27 @@ void GroundUnit::AIloop() } break; } + case State::ATTACK: { + Unit* target = unitsManager->getUnit(getTargetID()); + if (target != nullptr) { + setTask("Attacking " + target->getUnitName()); + + if (!getHasTask()) { + /* Send the command */ + std::ostringstream taskSS; + taskSS.precision(10); + taskSS << "{id = 'AttackUnit', unitID = " << target->getID() << " }"; + Command* command = dynamic_cast(new SetTask(groupName, taskSS.str(), [this]() { this->setHasTaskAssigned(true); })); + scheduler->appendCommand(command); + setHasTask(true); + } + } + else { + setState(State::IDLE); + } + + break; + } case State::FIRE_AT_AREA: { setTask("Firing at area"); diff --git a/src/core/src/helicopter.cpp b/src/core/src/helicopter.cpp index c452f118..f12a5d3e 100644 --- a/src/core/src/helicopter.cpp +++ b/src/core/src/helicopter.cpp @@ -12,24 +12,18 @@ using namespace GeographicLib; extern Scheduler* scheduler; extern UnitsManager* unitsManager; json::value Helicopter::database = json::value(); +extern string instancePath; void Helicopter::loadDatabase(string path) { - char* buf = nullptr; - size_t sz = 0; - if (_dupenv_s(&buf, &sz, "DCSOLYMPUS_PATH") == 0 && buf != nullptr) - { - std::ifstream ifstream(string(buf) + path); - std::stringstream ss; - ss << ifstream.rdbuf(); - std::error_code errorCode; - database = json::value::parse(ss.str(), errorCode); - if (database.is_object()) - log("Helicopters database loaded correctly"); - else - log("Error reading Helicopters database file"); - - free(buf); - } + std::ifstream ifstream(instancePath + path); + std::stringstream ss; + ss << ifstream.rdbuf(); + std::error_code errorCode; + database = json::value::parse(ss.str(), errorCode); + if (database.is_object()) + log("Helicopters database loaded correctly from " + instancePath + path); + else + log("Error reading Helicopters database file"); } /* Helicopter */ diff --git a/src/core/src/navyunit.cpp b/src/core/src/navyunit.cpp index b965d423..317178e1 100644 --- a/src/core/src/navyunit.cpp +++ b/src/core/src/navyunit.cpp @@ -12,24 +12,18 @@ using namespace GeographicLib; extern Scheduler* scheduler; extern UnitsManager* unitsManager; json::value NavyUnit::database = json::value(); +extern string instancePath; void NavyUnit::loadDatabase(string path) { - char* buf = nullptr; - size_t sz = 0; - if (_dupenv_s(&buf, &sz, "DCSOLYMPUS_PATH") == 0 && buf != nullptr) - { - std::ifstream ifstream(string(buf) + path); - std::stringstream ss; - ss << ifstream.rdbuf(); - std::error_code errorCode; - database = json::value::parse(ss.str(), errorCode); - if (database.is_object()) - log("Navy Units database loaded correctly"); - else - log("Error reading Navy Units database file"); - - free(buf); - } + std::ifstream ifstream(instancePath + path); + std::stringstream ss; + ss << ifstream.rdbuf(); + std::error_code errorCode; + database = json::value::parse(ss.str(), errorCode); + if (database.is_object()) + log("NavyUnits database loaded correctly from " + instancePath + path); + else + log("Error reading NavyUnits database file"); } /* Navy Unit */ @@ -65,6 +59,10 @@ void NavyUnit::setState(unsigned char newState) case State::REACH_DESTINATION: { break; } + case State::ATTACK: { + setTargetID(NULL); + break; + } case State::FIRE_AT_AREA: { setTargetPosition(Coords(NULL)); break; @@ -91,6 +89,13 @@ void NavyUnit::setState(unsigned char newState) resetActiveDestination(); break; } + case State::ATTACK: { + setEnableTaskCheckFailed(true); + clearActivePath(); + resetActiveDestination(); + resetTask(); + break; + } case State::FIRE_AT_AREA: { setEnableTaskCheckFailed(true); clearActivePath(); @@ -148,6 +153,25 @@ void NavyUnit::AIloop() } break; } + case State::ATTACK: { + Unit* target = unitsManager->getUnit(getTargetID()); + if (target != nullptr) { + setTask("Attacking " + target->getUnitName()); + + if (!getHasTask()) { + /* Send the command */ + std::ostringstream taskSS; + taskSS.precision(10); + taskSS << "{id = 'AttackUnit', unitID = " << target->getID() << " }"; + Command* command = dynamic_cast(new SetTask(groupName, taskSS.str(), [this]() { this->setHasTaskAssigned(true); })); + scheduler->appendCommand(command); + setHasTask(true); + } + } + else { + setState(State::IDLE); + } + } case State::FIRE_AT_AREA: { setTask("Firing at area"); diff --git a/src/core/src/server.cpp b/src/core/src/server.cpp index e3be2f2f..b810db13 100644 --- a/src/core/src/server.cpp +++ b/src/core/src/server.cpp @@ -19,6 +19,7 @@ extern Scheduler* scheduler; extern json::value missionData; extern mutex mutexLock; extern string sessionHash; +extern string instancePath; void handle_eptr(std::exception_ptr eptr) { @@ -286,39 +287,32 @@ string Server::extractPassword(http_request& request) { void Server::task() { string address = REST_ADDRESS; - string 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_PATH); - std::stringstream ss; - ss << ifstream.rdbuf(); - std::error_code errorCode; - json::value config = json::value::parse(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 = "http://" + to_string(config[L"server"][L"address"]) + ":" + to_string(config[L"server"][L"port"].as_number().to_int32()); - log("Starting server on " + address); - } - else - log("Error reading configuration file. Starting server on " + address); + string jsonLocation = instancePath + OLYMPUS_JSON_PATH; - if (config.is_object() && config.has_object_field(L"authentication")) - { - if (config[L"authentication"].has_string_field(L"gameMasterPassword")) gameMasterPassword = to_string(config[L"authentication"][L"gameMasterPassword"]); - if (config[L"authentication"].has_string_field(L"blueCommanderPassword")) blueCommanderPassword = to_string(config[L"authentication"][L"blueCommanderPassword"]); - if (config[L"authentication"].has_string_field(L"redCommanderPassword")) redCommanderPassword = to_string(config[L"authentication"][L"redCommanderPassword"]); - } - else - log("Error reading configuration file. No password set."); - free(buf); + log("Reading configuration from " + jsonLocation); + + std::ifstream ifstream(jsonLocation); + std::stringstream ss; + ss << ifstream.rdbuf(); + std::error_code errorCode; + json::value config = json::value::parse(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 = "http://" + to_string(config[L"server"][L"address"]) + ":" + to_string(config[L"server"][L"port"].as_number().to_int32()); + log("Starting server on " + address); } else + log("Error reading configuration file. Starting server on " + address); + + if (config.is_object() && config.has_object_field(L"authentication")) { - log("DCSOLYMPUS_PATH environment variable is missing, starting server on " + address); + if (config[L"authentication"].has_string_field(L"gameMasterPassword")) gameMasterPassword = to_string(config[L"authentication"][L"gameMasterPassword"]); + if (config[L"authentication"].has_string_field(L"blueCommanderPassword")) blueCommanderPassword = to_string(config[L"authentication"][L"blueCommanderPassword"]); + if (config[L"authentication"].has_string_field(L"redCommanderPassword")) redCommanderPassword = to_string(config[L"authentication"][L"redCommanderPassword"]); } + else + log("Error reading configuration file. No password set."); http_listener listener(to_wstring(address + "/" + REST_URI)); diff --git a/src/olympus/src/olympus.cpp b/src/olympus/src/olympus.cpp index 601a3ebb..2417b054 100644 --- a/src/olympus/src/olympus.cpp +++ b/src/olympus/src/olympus.cpp @@ -11,12 +11,14 @@ typedef int(__stdcall* f_coreFrame)(lua_State* L); typedef int(__stdcall* f_coreUnitsData)(lua_State* L); typedef int(__stdcall* f_coreWeaponsData)(lua_State* L); typedef int(__stdcall* f_coreMissionData)(lua_State* L); +typedef int(__stdcall* f_coreInstancePath)(lua_State* L); f_coreInit coreInit = nullptr; f_coreDeinit coreDeinit = nullptr; f_coreFrame coreFrame = nullptr; f_coreUnitsData coreUnitsData = nullptr; f_coreWeaponsData coreWeaponsData = nullptr; f_coreMissionData coreMissionData = nullptr; +f_coreInstancePath coreInstancePath = nullptr; static int onSimulationStart(lua_State* L) { @@ -90,6 +92,13 @@ static int onSimulationStart(lua_State* L) goto error; } + coreInstancePath = (f_coreInstancePath)GetProcAddress(hGetProcIDDLL, "coreInstancePath"); + if (!coreInstancePath) + { + LogError(L, "Error getting coreInstancePath ProcAddress from DLL"); + goto error; + } + coreInit(L); LogInfo(L, "Module loaded and started successfully."); @@ -137,6 +146,7 @@ static int onSimulationStop(lua_State* L) coreUnitsData = nullptr; coreWeaponsData = nullptr; coreMissionData = nullptr; + coreInstancePath = nullptr; } hGetProcIDDLL = NULL; @@ -175,6 +185,15 @@ static int setMissionData(lua_State* L) return 0; } +static int setInstancePath(lua_State* L) +{ + if (coreInstancePath) + { + coreInstancePath(L); + } + return 0; +} + static const luaL_Reg Map[] = { {"onSimulationStart", onSimulationStart}, {"onSimulationFrame", onSimulationFrame}, @@ -182,6 +201,7 @@ static const luaL_Reg Map[] = { {"setUnitsData", setUnitsData }, {"setWeaponsData", setWeaponsData }, {"setMissionData", setMissionData }, + {"setInstancePath", setInstancePath }, {NULL, NULL} };