Implement more wizard page, result page still wip

This commit is contained in:
Pax1601 2024-01-17 17:51:27 +01:00
parent 4a5c4ed7d7
commit 1568c65492
27 changed files with 502 additions and 334 deletions

View File

@ -1,142 +1,89 @@
<style>
#manager-connections .option {
background-color: var(--background);
border: 1px solid var(--offwhite);
width: 220px;
height: 60px;
color: var(--offwhite);
display: flex;
font-size: 18px;
font-weight: 600;
padding-left: 15px;
align-items: center;
border-radius: 5px;
cursor: pointer;
background-color: transparent;
color: var(--offwhite);
justify-content: center;
}
#manager-connections .option:hover {
color: var(--background);
background-color: var(--offwhite);
}
#manager-connections .buttons {
display: flex;
flex-direction: column;
row-gap: 15px;
}
#manager-connections .success,
#manager-connections .error {
#connections-page .success,
#connections-page .error {
position: absolute;
left: 420px;
left: 320px;
display: flex;
width: 150px;
column-gap: 8px;
}
#manager-connections .success {
#connections-page .success {
content: url("./icons/check-solid-green.svg");
height: 20px;
width: 20px;
}
#manager-connections .error img {
#connections-page .error img {
content: url("./icons/triangle-exclamation-solid.svg");
height: 20px;
width: 20px;
}
#manager-connections .error span {
#connections-page .error span {
font-weight: 600;
font-size: 12px;
color: var(--red);
height: fit-content;
}
</style>
<div id="manager-connections">
<div class="step-summary">
<div class="blue <%= singleInstance? 'hide': '' %>"><%= install? 'User path': 'Instance selection' %></div>
<div class="blue">Type of install</div>
<div class="white">Ports and address</div>
<div class="empty">Passwords</div>
<div class="empty"> <%= install? 'Install': 'Update' %></div>
<div id="connections-page">
<div class="instructions">
<div class="step">
Step 3 of 4
</div>
<div class="title">
Manually set Olympus port and address settings
</div>
<div class="note">
Please note: you may be required to allow these ports through your firewall and modem/router via port
forwarding. <br>
Otherwise, others may not be able to connect to Olympus.
</div>
</div>
<div class="content">
<% if (selectAutoOrManual) { %>
<div class="instructions">
<span>
Do you want to set port and address settings manually?
</span>
<span>
We can auto setup ports and addresses for you, or you can set the manually. <br>
If you don't have an understanding of how Olympus works, we recommend the auto option.
</span>
</div>
<div class="buttons">
<div class="option button auto">
Auto apply settings
<div class="wizard-inputs">
<div class="input-group client-port">
<span>Client port
<img src="./icons/circle-info-solid.svg"
title="This port is used to allow access to Olympus.
Be sure to allow this port through your firewall if you want people to connect remotely">
</span>
<div>
<input type="number" min="1024" max="65535" value="<%= activeInstance[" clientPort"] %>">
<img class="success hide">
<div class="error hide">
<img> <span>Port already in use</span>
</div>
<div class="option button manual">
Manually set options
</div>
</div>
<% } else { %>
<div class="instructions">
<span>
Enter the ports and address to use.
</span>
<span>
Select client and backend ports, making sure they are free to use with the provided check. <br>
Unless you want to support direct API calls to the backend, you can keep the address to localhost even for dedicated servers.
</span>
</div>
<div class="input-group client-port">
<span>Client port
<img src="./icons/circle-info-solid.svg" title="This port is used to allow access to Olympus. Be sure to allow this port through your firewall if you want people to connect remotely">
</span>
<div>
<input type="number" min="1024" max="65535" value="<%= activeInstance["clientPort"] %>">
<img class="success hide">
<div class="error hide">
<img> <span>Port already in use</span>
</div>
</div>
</div>
<div class="input-group backend-port">
<span>Backend port
<img src="./icons/circle-info-solid.svg" title="This port is used to allow access to Olympus. Be sure to allow this port through your firewall if you want people to connect remotely.">
</span>
<div>
<input type="number" min="1024" max="65535" value="<%= activeInstance["backendPort"] %>">
<img class="success hide">
<div class="error hide">
<img> <span>Port already in use</span>
</div>
</div>
</div>
<div class="input-group backend-address">
<span>Backend address
<img src="./icons/circle-info-solid.svg" title="This is the backend address Olympus will listen on. Unless you know what you are doing, leave it as localhost, even for dedicated server installations.">
</span>
<input type="text" value="<%= activeInstance["backendAddress"] %>">
</div>
<% } %>
<div class="buttons-footer">
<div class="button back">
Back
</div>
<div class="button next">
Next
</div>
</div>
<div class="button cancel">
<%= install? "Cancel installation": "Cancel editing" %>
<div class="input-group backend-port">
<span>Backend port
<img src="./icons/circle-info-solid.svg"
title="This port is used by Olympus to communicate with DCS.
You only need to allow it through your firewall if you enable direct API connection">
</span>
<div>
<input type="number" min="1024" max="65535" value="<%= activeInstance[" backendPort"] %>"
onchange="signal('onBackendPortChanged', this.value)">
<img class="success hide">
<div class="error hide">
<img> <span>Port already in use</span>
</div>
</div>
</div>
<div class="input-group backend-address">
<span onclick="signal('onEnableAPIClicked')">
<div class="checkbox"></div> Enable direct backend API connection
<img src="./icons/circle-info-solid.svg"
title="Allows services to connect to Olympus directly.
This is NOT NEEDED for normal Olympus operation, even for dedicated servers.
Leave it unchecked if in doubt.">
</span>
</div>
<div class="note warning">
Note: if you enable direct backend API connection, you will be required to run DCS as admin or run the netsh
command for others to connect. Leave unchecked if you don't know what this is. <br>See the Olympus
documentation for more details.
</div>
</div>
</div>

View File

@ -0,0 +1,25 @@
<style>
</style>
<div>
<div class="instructions">
<div class="step">
Step 2 of 4
</div>
<div class="title">
Do you want to set port and address settings?
</div>
<div class="description">
We can automatically set port and address settings for you, or you can set them manually. <br>
If you don't have a good understanding of how Olympus works, we recommend the <i>auto apply settings</i> option.
</div>
</div>
<div class="wizard-inputs">
<div class="button radio auto selected" onclick="signal('onConnectionsTypeClicked', 'auto')">
Auto apply settings
</div>
<div class="button radio manual" onclick="signal('onConnectionsTypeClicked', 'manual')">
Manually set
</div>
</div>
</div>

View File

@ -1,51 +1,37 @@
<style>
</style>
<div id="manager-passwords">
<div class="step-summary">
<div class="blue <%= singleInstance? 'hide': '' %>"><%= install? 'User path': 'Instance selection' %></div>
<div class="blue">Type of install</div>
<div class="blue">Ports and address</div>
<div class="white">Passwords</div>
<div class="empty"> <%= install? 'Install': 'Update' %></div>
</div>
<div class="content">
<div class="instructions">
<span>
Enter your passwords to access Olympus
</span>
<span>
When logging into Olympus, these passwords will let you access the different roles. Gamemaster is the default.
</span>
</div>
</style>
<div id="passwords-page">
<div class="instructions">
<div class="step">
Step 4 of 4
</div>
<div class="title">
Enter your passwords for Olympus
</div>
<div class="description">
When logging into Olympus, these passwords will let you access the different roles. <br>
Game Master is the default and is used as a global commander. The other two are used as a part of the RTS mode.
</div>
</div>
<div class="wizard-inputs">
<div class="input-group game-master">
<span>Game Master Password<img src="./icons/circle-info-solid.svg" title="This password is used to access Olympus as Game Master with full privileges.">
<span>Game Master Password<img src="./icons/circle-info-solid.svg"
title="This password is used to access Olympus as Game Master with full privileges.">
</span>
<input type="password" minlength="8">
</div>
<div class="input-group blue-commander">
<span>Blue Commander Password<img src="./icons/circle-info-solid.svg" title="This password is used to access Olympus as blue coalition Commander.">
<span>Blue Commander Password<img src="./icons/circle-info-solid.svg"
title="This password is used to access Olympus as blue coalition Commander.">
</span>
<input type="password" minlength="8">
</div>
<div class="input-group red-commander">
<span>Red Commander Password<img src="./icons/circle-info-solid.svg" title="This password is used to access Olympus as red coalition Commander.">
<span>Red Commander Password<img src="./icons/circle-info-solid.svg"
title="This password is used to access Olympus as red coalition Commander.">
</span>
<input type="password" minlength="8">
</div>
<div class="buttons-footer">
<div class="button back">
Back
</div>
<div class="button next">
Next
</div>
</div>
<% if (!singleInstance) { %>
<div class="button cancel">
<%= install? "Cancel installation": "Cancel editing" %>
</div>
<% } %>
</div>
</div>
</div>

View File

@ -1,146 +1,128 @@
<style>
#manager-result .content {
width: 100% !important;
}
#manager-result img.success {
content: url("./icons/check-solid-green.svg");
height: 20px;
width: 20px;
#result-page {
display: flex;
flex-direction: column;
row-gap: 30px;
padding: 60px 120px;
}
#manager-result img.error {
content: url("./icons/triangle-exclamation-solid.svg");
height: 20px;
width: 20px;
#result-page .result-summary {
padding: 25px 15px;
display: flex;
flex-direction: column;
row-gap: 10px;
}
#manager-result img.wait {
content: url("./icons/spinner-solid.svg");
height: 20px;
width: 20px;
animation: rotate 2s linear infinite;
}
#manager-result .summary {
font-weight: 600;
font-size: 24px;
}
#manager-result .summary.success {
color: var(--green);
}
#manager-result .summary.error {
color: var(--red);
}
#manager-result .info {
font-weight: 600;
font-size: 14px;
color: var(--offwhite);
}
#manager-result .step {
#result-page .result-summary .title {
font-weight: bold;
font-size: 15px;
display: flex;
align-items: center;
}
#result-page .result-summary .title img {
margin-right: 10px;
}
#result-page .result-summary .description {
font-size: 13px;
font-weight: 600;
}
#result-page .result-summary.wait{
color: var(--offwhite);
column-gap: 10px;
border: 1px solid var(--offwhite);
}
#manager-result .result {
cursor: pointer;
background-color: var(--darkgray);
border-left: 5px solid var(--blue);
width: 100%;
height: 80px;
color: white;
#result-page .result-summary.success{
color: var(--background-color);
background-color: var(--green);
}
#result-page .result-summary.error{
color: var(--background-color);
background-color: var(--red);
}
#result-page .usage-instructions {
background-color: var(--background-usage);
border-radius: 10px;
display: flex;
font-size: 13px;
font-weight: 600;
padding-left: 15px;
flex-direction: row;
column-gap: 25px;
align-items: center;
border-radius: 5px;
width: 500px;
padding: 25px;
}
#manager-result .result>img {
margin-left: 5px;
margin-right: 20px;
}
#manager-result .result>div {
#result-page .usage-instructions>div {
color: var(--offwhite);
display: flex;
flex-direction: column;
row-gap: 5px;
}
#manager-result .result>div>span:nth-child(1) {
font-size: 15px;
font-weight: 600;
}
#manager-result .result>div>span:nth-child(2) {
display: flex;
column-gap: 10px;
justify-content: center;
justify-items: center;
align-items: start;
font-size: 13px;
font-weight: normal;
}
#result-page .usage-instructions>div>img {
height: 40px;
width: 40px;
}
#result-page .usage-instructions>img {
height: 30px;
width: 30px;
}
</style>
<div id="manager-result">
<div class="content">
<div class="step hook <%= !install? 'hide': '' %>">
Installing hook scripts<img class="wait"><img class="success hide"><img class="error hide">
</div>
<div class="step mod <%= !install? 'hide': '' %>">
Installing mod folder<img class="wait"><img class="success hide"><img class="error hide">
</div>
<div class="step json <%= !install? 'hide': '' %>">
Installing configuration file<img class="wait"><img class="success hide"><img class="error hide">
</div>
<div class="step config">
Applying configuration<img class="wait"><img class="success hide"><img class="error hide">
</div>
<div class="step shortcuts <%= !install? 'hide': '' %>">
Creating shortcuts<img class="wait"><img class="success hide"><img class="error hide">
</div>
<div class="summary success hide">
Olympus successfully installed in the following DCS instance
</div>
<div class="summary error hide">
An error has occurred while installing Olympus
</div>
<div class="info success hide">
You may now start DCS and use Olympus either with the shortcuts or the "View and manage instances" entry in the
main menu.
</div>
<div class="info error hide">
Please make sure DCS is not currently being executed. Check <%= logLocation %> for more info.
</div>
<div class="result">
<img class="wait"><img class="success hide"><img class="error hide">
<div id="result-page">
<div class="result-summary wait">
<div class="title"><img src="./icons/spinner-solid.svg">Please wait while Olympus is being added to <i><%= activeInstance["name"] %></i></div>
</div>
<div class="result-summary success">
<div class="title"><img src="./icons/check-solid-background.svg">Olympus successfully added to <i><%= activeInstance["name"] %></i></div>
<div class="description">See the <b>DCS Olympus Wiki</b> for more information on how to use Olympus and for troubleshooting issues. You may now close the installer.</div>
</div>
<div class="result-summary error">
<div class="title"><img src="./icons/triangle-exclamation-solid-background.svg">An error occurred while adding Olympus to <i><%= activeInstance["name"] %></i></div>
<div class="description">See the manager log located in TODO for more information.</div>
</div>
<div>
How to launch Olympus
</div>
<div>
To launch Olympus, there are shortcuts available in the DCS Olympus folder under Saved Games.
</div>
<div class="usage-instructions">
<div>
<img src="./icons/server-solid.svg">
<div>
<span>
<%= activeInstance.name %>
</span>
<span><img src="./icons/folder-open-solid.svg">
<%= activeInstance.folder %>
</span>
Launch the Olympus Server via the shortcut in DCS Olympus / Saved Games.
</div>
</div>
<div class="buttons-footer">
<div class="button back">
Back to main menu
<img src="./icons/arrow-right-solid.svg">
<div>
<img src="./icons/chrome.svg">
<div>
Visit http://localhost:3000 in a web browser (Google Chrome recommended).
</div>
</div>
<img src="./icons/arrow-right-solid.svg">
<div>
<img src="./icons/gamepad-solid.svg">
<div>
Launch DCS, load a mission and unpause the game. Enjoy!
</div>
</div>
</div>
<div>
Alternatively, you can run the Olympus Client instead to replace the first two steps above.
</div>
<div class="buttons-footer">
<div class="button return">
Return to main menu
</div>
<div class="button close">
Close manager
</div>
</div>
</div>

View File

@ -1,23 +1,25 @@
<style>
</style>
<div class="instructions">
<div class="step">
Step 1 of 4
<div>
<div class="instructions">
<div class="step">
Step 1 of 4
</div>
<div class="title">
Do you want to add Olympus for singleplayer or multiplayer?
</div>
<div class="description">
Select singleplayer if you only want to play locally on your own computer. <br>
Select multiplayer if you want Olympus to be useable over the internet from a different computer, or this instance is a dedicated server.
</div>
</div>
<div class="title">
Do you want to add Olympus for singleplayer or multiplayer?
</div>
<div class="description">
Select singleplayer if you only want to play locally on your own computer. <br>
Select multiplayer if you want Olympus to be useable over the internet from a different computer, or this instance is a dedicated server.
</div>
</div>
<div class="buttons">
<div class="button">
Singleplayer
</div>
<div class="button">
Multiplayer
<div class="wizard-inputs">
<div class="button radio singleplayer selected" onclick="signal('onInstallTypeClicked', 'singleplayer')">
Singleplayer
</div>
<div class="button radio multiplayer" onclick="signal('onInstallTypeClicked', 'multiplayer')">
Multiplayer
</div>
</div>
</div>

View File

@ -4,6 +4,7 @@
flex-direction: column;
row-gap: 20px;
width: 100%;
height: 100%;
justify-content: center;
align-items: center;
}

View File

@ -13,11 +13,20 @@
}
.instructions {
display: flex;
flex-direction: column;
row-gap: 15px;
color: var(--offwhite);
}
.instructions .step {
font-size: 14px;
color: var(--lightgray);
}
.instructions .description {
font-size: 14px;
color: var(--lightgray);
}
.instructions .title {
@ -25,7 +34,7 @@
font-weight: bold;
}
.content {
.content > div {
height: 100%;
width: 100%;
display: flex;
@ -34,6 +43,32 @@
align-items: start;
justify-content: center;
}
.wizard-inputs {
display: flex;
flex-direction: column;
row-gap: 10px;
}
.wizard-page .button.radio {
width: 300px;
}
.note {
width: 100%;
background-color: var(--background-note);
color: var(--offwhite);
border-left: 5px solid var(--offwhite);
font-size: 14px;
padding: 15px;
font-weight: 600;
}
.warning {
background-color: var(--background-warning);
border-left: 5px solid var(--orange);
}
</style>
<div class="wizard-page">
<div class="cancel" style="font-size: 14px; font-weight: 600; color: var(--offwhite); display: flex; align-items: center; column-gap: 10px;">
@ -43,10 +78,10 @@
</div>
<div class="buttons-footer">
<div class="button back" style="color: var(--offwhite); background-color: var(--background); border: 1px solid var(--offwhite);">
<div class="button back" style="color: var(--offwhite); background-color: var(--background); border: 1px solid var(--offwhite);" onclick="signal('onBackClicked')">
Back
</div>
<div class="button next" style="color: var(--background); background-color: var(--offwhite);">
<div class="button next" style="color: var(--background); background-color: var(--offwhite);" onclick="signal('onNextClicked')">
Next
</div>
</div>

View File

@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" height="16" width="14" viewBox="0 0 448 512"><!--!Font Awesome Free 6.5.1 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license/free Copyright 2024 Fonticons, Inc.--><path fill="#F2F2F2" d="M438.6 278.6c12.5-12.5 12.5-32.8 0-45.3l-160-160c-12.5-12.5-32.8-12.5-45.3 0s-12.5 32.8 0 45.3L338.8 224 32 224c-17.7 0-32 14.3-32 32s14.3 32 32 32l306.7 0L233.4 393.4c-12.5 12.5-12.5 32.8 0 45.3s32.8 12.5 45.3 0l160-160z"/></svg>

After

Width:  |  Height:  |  Size: 490 B

View File

@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" height="16" width="14" viewBox="0 0 448 512"><!--!Font Awesome Free 6.5.1 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license/free Copyright 2023 Fonticons, Inc.--><path fill="#181e25" d="M438.6 105.4c12.5 12.5 12.5 32.8 0 45.3l-256 256c-12.5 12.5-32.8 12.5-45.3 0l-128-128c-12.5-12.5-12.5-32.8 0-45.3s32.8-12.5 45.3 0L160 338.7 393.4 105.4c12.5-12.5 32.8-12.5 45.3 0z"/></svg>

After

Width:  |  Height:  |  Size: 449 B

1
manager/icons/chrome.svg Normal file
View File

@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" height="16" width="16" viewBox="0 0 512 512"><!--!Font Awesome Free 6.5.1 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license/free Copyright 2024 Fonticons, Inc.--><path fill="#F2F2F2" d="M0 256C0 209.4 12.5 165.6 34.3 127.1L144.1 318.3C166 357.5 207.9 384 256 384C270.3 384 283.1 381.7 296.8 377.4L220.5 509.6C95.9 492.3 0 385.3 0 256zM365.1 321.6C377.4 302.4 384 279.1 384 256C384 217.8 367.2 183.5 340.7 160H493.4C505.4 189.6 512 222.1 512 256C512 397.4 397.4 511.1 256 512L365.1 321.6zM477.8 128H256C193.1 128 142.3 172.1 130.5 230.7L54.2 98.5C101 38.5 174 0 256 0C350.8 0 433.5 51.5 477.8 128V128zM168 256C168 207.4 207.4 168 256 168C304.6 168 344 207.4 344 256C344 304.6 304.6 344 256 344C207.4 344 168 304.6 168 256z"/></svg>

After

Width:  |  Height:  |  Size: 804 B

View File

@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" height="16" width="20" viewBox="0 0 640 512"><!--!Font Awesome Free 6.5.1 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license/free Copyright 2024 Fonticons, Inc.--><path fill="#F2F2F2" d="M192 64C86 64 0 150 0 256S86 448 192 448H448c106 0 192-86 192-192s-86-192-192-192H192zM496 168a40 40 0 1 1 0 80 40 40 0 1 1 0-80zM392 304a40 40 0 1 1 80 0 40 40 0 1 1 -80 0zM168 200c0-13.3 10.7-24 24-24s24 10.7 24 24v32h32c13.3 0 24 10.7 24 24s-10.7 24-24 24H216v32c0 13.3-10.7 24-24 24s-24-10.7-24-24V280H136c-13.3 0-24-10.7-24-24s10.7-24 24-24h32V200z"/></svg>

After

Width:  |  Height:  |  Size: 622 B

View File

@ -1 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" height="16" width="16" viewBox="0 0 512 512"><!--!Font Awesome Free 6.5.1 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license/free Copyright 2023 Fonticons, Inc.--><path opacity="1" fill="#F2F2F2" d="M64 32C28.7 32 0 60.7 0 96v64c0 35.3 28.7 64 64 64H448c35.3 0 64-28.7 64-64V96c0-35.3-28.7-64-64-64H64zm280 72a24 24 0 1 1 0 48 24 24 0 1 1 0-48zm48 24a24 24 0 1 1 48 0 24 24 0 1 1 -48 0zM64 288c-35.3 0-64 28.7-64 64v64c0 35.3 28.7 64 64 64H448c35.3 0 64-28.7 64-64V352c0-35.3-28.7-64-64-64H64zm280 72a24 24 0 1 1 0 48 24 24 0 1 1 0-48zm56 24a24 24 0 1 1 48 0 24 24 0 1 1 -48 0z"/></svg>
<svg xmlns="http://www.w3.org/2000/svg" height="16" width="16" viewBox="0 0 512 512"><!--!Font Awesome Free 6.5.1 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license/free Copyright 2024 Fonticons, Inc.--><path fill="#F2F2F2" d="M64 32C28.7 32 0 60.7 0 96v64c0 35.3 28.7 64 64 64H448c35.3 0 64-28.7 64-64V96c0-35.3-28.7-64-64-64H64zm280 72a24 24 0 1 1 0 48 24 24 0 1 1 0-48zm48 24a24 24 0 1 1 48 0 24 24 0 1 1 -48 0zM64 288c-35.3 0-64 28.7-64 64v64c0 35.3 28.7 64 64 64H448c35.3 0 64-28.7 64-64V352c0-35.3-28.7-64-64-64H64zm280 72a24 24 0 1 1 0 48 24 24 0 1 1 0-48zm56 24a24 24 0 1 1 48 0 24 24 0 1 1 -48 0z"/></svg>

Before

Width:  |  Height:  |  Size: 659 B

After

Width:  |  Height:  |  Size: 647 B

View File

@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" height="16" width="16" viewBox="0 0 512 512"><!--!Font Awesome Free 6.5.1 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license/free Copyright 2023 Fonticons, Inc.--><path opacity="1" fill="#181e25" d="M256 32c14.2 0 27.3 7.5 34.5 19.8l216 368c7.3 12.4 7.3 27.7 .2 40.1S486.3 480 472 480H40c-14.3 0-27.6-7.7-34.7-20.1s-7-27.8 .2-40.1l216-368C228.7 39.5 241.8 32 256 32zm0 128c-13.3 0-24 10.7-24 24V296c0 13.3 10.7 24 24 24s24-10.7 24-24V184c0-13.3-10.7-24-24-24zm32 224a32 32 0 1 0 -64 0 32 32 0 1 0 64 0z"/></svg>

After

Width:  |  Height:  |  Size: 584 B

View File

@ -8,7 +8,7 @@ const { checkPort, fetchWithTimeout } = require('./net')
const dircompare = require('dir-compare');
const { spawn } = require('child_process');
const find = require('find-process');
const { uninstallInstance } = require('./filesystem')
const { uninstallInstance, installHooks, installMod, installJSON, applyConfiguration, installShortCuts } = require('./filesystem')
const { showErrorPopup, showConfirmPopup } = require('./popup')
const { logger } = require("./filesystem")
@ -79,6 +79,8 @@ class DCSInstance {
missionTime = "";
load = 0;
fps = 0;
installationType = 'singleplayer';
connectionsType = 'auto';
constructor(folder) {
this.folder = folder;
@ -146,7 +148,7 @@ class DCSInstance {
*/
async setBackendPort(newPort) {
if (await this.checkBackendPort(newPort)) {
logger.log(`Instance ${this.folder} client port set to ${newPort}`)
logger.log(`Instance ${this.folder} backend port set to ${newPort}`)
this.backendPort = newPort;
return true;
}
@ -236,6 +238,7 @@ class DCSInstance {
} else {
logger.log(`Port ${port} currently in use`);
}
logger.log(`Port ${port} is free`);
res(portFree);
})
})
@ -340,6 +343,48 @@ class DCSInstance {
})
}
/* Install this instance */
install() {
installHooks(getManager().getActiveInstance().folder).then(
() => {
},
(err) => {
return Promise.reject(err);
}
).then(() => installMod(getManager().getActiveInstance().folder, getManager().getActiveInstance().name)).then(
() => {
},
(err) => {
return Promise.reject(err);
}
).then(() => installJSON(getManager().getActiveInstance().folder)).then(
() => {
},
(err) => {
return Promise.reject(err);
}
).then(() => applyConfiguration(getManager().getActiveInstance().folder, getManager().getActiveInstance())).then(
() => {
},
(err) => {
return Promise.reject(err);
}
).then(() => installShortCuts(getManager().getActiveInstance().folder, getManager().getActiveInstance().name)).then(
() => {
},
(err) => {
return Promise.reject(err);
}
).then(
() => {
//getManager().resultPage.getElement()
},
() => {
//getManager().resultPage.getElement()
}
);
}
/* Uninstall this instance */
uninstall() {
showConfirmPopup("<div style='font-size: 18px; max-width: 100%'> Are you sure you want to remove Olympus? </div> If you click Accept, the Olympus mod will be removed from your DCS installation.", () =>
@ -353,7 +398,8 @@ class DCSInstance {
location.reload();
});
}
));
)
);
}
}

View File

@ -15,37 +15,40 @@ class Manager {
configLoaded: false
};
activePage = null;
welcomePage = null;
folderPage = null;
typePage = null;
connectionsTypePage = null;
connectionsPage = null;
passwordsPage = null;
resultPage = null;
instancesPage = null;
constructor() {
console.log("constructor")
document.addEventListener("signal", (ev) => {
const callback = ev.detail.callback;
const params = ev.detail.params;
const params = JSON.stringify(ev.detail.params);
try {
eval(`this.${callback}(${params})`)
} catch (e) {
console.error(e);
}
});
}
}
async start() {
/* Check if the options file exists */
if (fs.existsSync("options.json")) {
/* Load the options from the json file */
try {
this.options = {...this.options, ...JSON.parse(fs.readFileSync("options.json"))};
this.options = { ...this.options, ...JSON.parse(fs.readFileSync("options.json")) };
this.options.configLoaded = true;
} catch (e) {
logger.error(`An error occurred while reading the options.json file: ${e}`);
}
}
}
if (!this.options.configLoaded) {
/* Hide the loading page */
@ -82,9 +85,9 @@ class Manager {
return instance.installed && instance.error;
})).then(
() => { location.reload() },
(err) => {
(err) => {
logger.error(err);
showErrorPopup(`An error occurred while trying to fix your installations. Please reinstall Olympus manually. <br><br> You can find more info in ${path.join(__dirname, "..", "manager.log")}`);
showErrorPopup(`An error occurred while trying to fix your installations. Please reinstall Olympus manually. <br><br> You can find more info in ${path.join(__dirname, "..", "manager.log")}`);
}
)
})
@ -101,13 +104,14 @@ class Manager {
/* Create all the HTML pages */
this.menuPage = new ManagerPage(this, "./ejs/menu.ejs");
this.folderPage = new WizardPage(this, "./ejs/installation.ejs");
this.typePage = new WizardPage(this, "./ejs/type.ejs");
//this.connectionsPage = new ConnectionsPage(this);
//this.passwordsPage = new PasswordsPage(this);
//this.resultPage = new ResultPage(this);
this.folderPage = new WizardPage(this, "./ejs/installation.ejs");
this.typePage = new WizardPage(this, "./ejs/type.ejs");
this.connectionsTypePage = new WizardPage(this, "./ejs/connectionsType.ejs");
this.connectionsPage = new WizardPage(this, "./ejs/connections.ejs");
this.passwordsPage = new WizardPage(this, "./ejs/passwords.ejs");
this.resultPage = new ManagerPage(this, "./ejs/result.ejs");
//this.instancesPage = new InstancesPage(this);
if (this.options.mode === "basic") {
/* In basic mode no dashboard is shown */
this.menuPage.show();
@ -124,7 +128,7 @@ class Manager {
createOptionsFile(mode) {
try {
fs.writeFileSync("options.json", JSON.stringify({mode: mode}));
fs.writeFileSync("options.json", JSON.stringify({ mode: mode }));
location.reload();
} catch (e) {
showErrorPopup(`A critical error occurred, check ${this.options.logLocation} for more info.`)
@ -155,26 +159,26 @@ class Manager {
/* When the install button is clicked go the installation page */
onInstallClicked() {
this.options.install = true;
if (this.options.singleInstance) {
this.options.activeInstance = this.options.instances[0];
/* Show the type selection page */
if (!this.options.activeInstance.installed) {
this.menuPage.hide();
this.typePage.show(this.menuPage);
this.typePage.show();
} else {
showConfirmPopup("<div style='font-size: 18px; max-width: 100%'> Olympus is already installed in this instance! </div> If you click Accept, it will be installed again and all changes, e.g. custom databases or mods support, will be lost. Are you sure you want to continue?",
() => {
this.menuPage.hide();
this.typePage.show(this.menuPage);
this.typePage.show();
}
)
}
} else {
/* Show the folder selection page */
this.menuPage.hide();
this.folderPage.show(this.menuPage);
this.folderPage.show();
}
}
@ -182,16 +186,115 @@ class Manager {
onEditClicked() {
this.hide();
this.options.install = false;
if (this.options.singleInstance) {
this.options.activeInstance = this.options.instances[0];
this.typePage.show(this);
this.typePage.show();
} else {
this.folderPage.show(this);
this.folderPage.show();
}
}
/* When the installation type is selected */
onInstallTypeClicked(type) {
this.typePage.getElement().querySelector(`.singleplayer`).classList.toggle("selected", type === 'singleplayer');
this.typePage.getElement().querySelector(`.multiplayer`).classList.toggle("selected", type === 'multiplayer');
if (this.options.activeInstance)
this.options.activeInstance.installationType = type;
else
showErrorPopup("A critical error has occurred. Please restart the Manager.")
}
/* When the connections type is selected */
onConnectionsTypeClicked(type) {
this.connectionsTypePage.getElement().querySelector(`.auto`).classList.toggle("selected", type === 'auto');
this.connectionsTypePage.getElement().querySelector(`.manual`).classList.toggle("selected", type === 'manual');
if (this.options.activeInstance)
this.options.activeInstance.connectionsType = type;
else
showErrorPopup("A critical error has occurred. Please restart the Manager.")
}
/* When the back button of a wizard page is clicked */
onBackClicked() {
this.activePage.hide();
this.activePage.previousPage.show();
}
/* When the next button of a wizard page is clicked */
onNextClicked() {
this.activePage.hide();
/* Choose which page to show depending on the active page */
if (this.activePage == this.typePage) {
this.connectionsTypePage.show();
} else if (this.activePage == this.connectionsTypePage) {
if (this.options.activeInstance) {
if (this.options.activeInstance.connectionsType === 'auto') {
this.passwordsPage.show();
}
else {
this.connectionsPage.show();
this.setPort('client', this.options.activeInstance.clientPort);
this.setPort('backend', this.options.activeInstance.backendPort);
this.connectionsPage.getElement().querySelector(".backend-address .checkbox").classList.toggle("checked", this.options.activeInstance.backendAddress === '*')
}
} else {
showErrorPopup("A critical error has occurred. Please restart the Manager.")
}
} else if (this.activePage == this.connectionsPage) {
this.passwordsPage.show();
} else if (this.activePage == this.passwordsPage) {
if (this.options.activeInstance) {
this.options.activeInstance.install();
this.resultPage.show();
} else {
showErrorPopup("A critical error has occurred. Please restart the Manager.")
}
}
}
/* When the client port input value is changed */
onClientPortChanged(value) {
this.setPort('client', Number(value));
}
/* When the backend port input value is changed */
onBackendPortChanged(value) {
this.setPort('backend', Number(value));
}
/* When the "Enable API connection" checkbox is clicked */
onEnableAPIClicked() {
if (this.options.activeInstance) {
if (this.options.activeInstance.backendAddress === 'localhost') {
this.options.activeInstance.backendAddress = '*';
} else {
this.options.activeInstance.backendAddress = 'localhost';
}
this.connectionsPage.getElement().querySelector(".backend-address .checkbox").classList.toggle("checked", this.options.activeInstance.backendAddress === '*')
} else {
showErrorPopup("A critical error has occurred. Please restart the Manager.")
}
}
/* Set the selected port to the dcs instance */
async setPort(port, value) {
var success;
if (port === 'client')
success = await this.options.activeInstance.setClientPort(value);
else
success = await this.options.activeInstance.setBackendPort(value);
var successEls = this.connectionsPage.getElement().querySelector(`.${port}-port`).querySelectorAll(".success");
for (let i = 0; i < successEls.length; i++) {
successEls[i].classList.toggle("hide", !success);
}
var errorEls = this.connectionsPage.getElement().querySelector(`.${port}-port`).querySelectorAll(".error");
for (let i = 0; i < errorEls.length; i++) {
errorEls[i].classList.toggle("hide", success);
}
}
}
module.exports = Manager;

View File

@ -1,12 +1,12 @@
var manager = null;
/* TODO: find a better solution without using the window object to persist the manager singleton */
function getManager() {
if (manager) {
return manager;
if (window.manager) {
return window.manager;
} else {
const Manager = require("./manager");
manager = new Manager();
return manager;
window.manager = new Manager();
return window.manager;
}
}

View File

@ -31,8 +31,8 @@ class ManagerPage {
this.element.classList.remove("hide");
if (previousPage !== undefined)
this.previousPage = previousPage;
this.previousPage = this.manager.activePage;
this.manager.activePage = this;
}
hide() {

View File

@ -10,8 +10,8 @@ process.env['PATH'] = process.env['PATH'] + "%WINDIR%\\System32;"
function createWindow() {
const window = new electronBrowserWindow({
width: 1500,
height: 850,
width: 1200,
height: 750,
frame: false,
resizable: true,
maximizable: true,

View File

@ -1,9 +1,8 @@
echo D|xcopy /Y /S /E .\icons ..\package\manager\icons
echo D|xcopy /Y /S /E .\ejs ..\package\manager\ejs
echo D|xcopy /Y /S /E .\javascripts ..\package\manager\javascripts
echo D|xcopy /Y /S /E .\stylesheets ..\package\manager\stylesheets
echo F|xcopy /Y /I .\*.* ..\package\manager
xcopy /Y /S /E .\icons ..\package\manager\icons
xcopy /Y /S /E .\ejs ..\package\manager\ejs
xcopy /Y /S /E .\javascripts ..\package\manager\javascripts
xcopy /Y /S /E .\stylesheets ..\package\manager\stylesheets
xcopy /Y /I .\*.* ..\package\manager
cd ..
call node .\scripts\node\set_version_text.js

View File

@ -3,14 +3,18 @@
--background-dark: #13181f;
--background-light: #202831;
--background-disabled: #212A34;
--background-note: #2C3540;
--background-warning: #3D3322;
--background-usage: #28313A;
--offwhite: #F2F2F2;
--offwhite-transparent: #F2F2F255;
--blue: #247be2;
--red: #FF5858;
--green: #8bff63;
--green: #8BFF63;
--lightgray: #cfd9e8;
--gray: #989898;
--darkgray: #3d4651;
--orange: #FF7B42;
}
* {
@ -36,7 +40,7 @@ body {
display: block;
-webkit-user-select: none;
-webkit-app-region: drag;
height: 20px;
height: 30px;
width: 100%;
display: flex;
justify-content: end;
@ -197,6 +201,24 @@ body {
column-gap: 10px;
}
.button.radio {
border: 1px solid var(--offwhite);
color: var(--offwhite);
}
.button.radio::before {
content: "";
display: block;
width: 10px;
height: 10px;
border: 1px solid var(--offwhite);
border-radius: 999px;
}
.button.radio.selected::before {
background-color: var(--offwhite);
}
.close-popup {
color: var(--offwhite);
background-color: var(--blue);
@ -214,7 +236,7 @@ input {
font-size: 13px;
padding: 3px 10px;
border-radius: 5px;
text-align: center;
text-align: left;
width: 300px;
}
@ -288,7 +310,7 @@ input {
display: flex;
flex-direction: column;
row-gap: 5px;
align-items: center;
align-items: start;
position: relative;
width: 500px;
}
@ -317,10 +339,6 @@ input {
flex-wrap: wrap;
}
.instructions {
margin-bottom: 10px;
}
.divider {
border-top: 0px solid transparent !important;
border-bottom: 1px solid var(--offwhite) !important;
@ -392,3 +410,22 @@ input {
border-top-left-radius: 0px;
border-top-right-radius: 0px;
}
.checkbox {
position: relative;
height: 15px;
width: 15px;
border: 1px solid var(--offwhite);
border-radius: 2px;
}
.checkbox.checked::after {
display: block;
position: absolute;
content: "";
height: 3px;
width: 8px;
transform: translate(1px, -1px) rotate(-45deg);
border-left: 2px solid var(--offwhite);
border-bottom: 2px solid var(--offwhite);
}