diff --git a/ASM/ArkServerManager_1.1.411.exe b/ASM/ArkServerManager_1.1.411.exe new file mode 100644 index 00000000..29077470 Binary files /dev/null and b/ASM/ArkServerManager_1.1.411.exe differ diff --git a/ASM/ArkServerManager_1.1.411.zip b/ASM/ArkServerManager_1.1.411.zip new file mode 100644 index 00000000..2efee118 Binary files /dev/null and b/ASM/ArkServerManager_1.1.411.zip differ diff --git a/ASM/ArkServerManager_1.1.412.exe b/ASM/ArkServerManager_1.1.412.exe new file mode 100644 index 00000000..3b854b72 Binary files /dev/null and b/ASM/ArkServerManager_1.1.412.exe differ diff --git a/ASM/ArkServerManager_1.1.412.zip b/ASM/ArkServerManager_1.1.412.zip new file mode 100644 index 00000000..350e1255 Binary files /dev/null and b/ASM/ArkServerManager_1.1.412.zip differ diff --git a/ASM/VersionFeed.xml b/ASM/VersionFeed.xml index 7a82ae25..36923baa 100644 --- a/ASM/VersionFeed.xml +++ b/ASM/VersionFeed.xml @@ -5,7 +5,84 @@ Ark Server Manager Version Feed This is the Ark Server Manager release version feed. - 2021-12-05T00:00:00Z + 2021-12-15T00:00:00Z + + + urn:uuid:18276A38-2C71-4BB8-9A83-96D5EBFE9C87 + 1.1.412 (1.1.412.4) + 1.1.412.4 + + 2021-12-15T00:00:00Z + +
+

+ BUGFIX +
+

    +
  • Fixed a bug with the Server Shutdown when the CheckForOnlinePlayers option not selected.
  • +
  • Fixed a bug when the backup path was a root directory that caused a 'Invalid URI: A Dos path must be rooted' crash.
  • +
  • Added additional validation when setting directories in the global setting, to ensure they are rooted correctly.
  • +
  • Fixed a bug when starting the server manager and it tries to download steamcmd, but fails as steamcmd is unavailable for download.
  • +
+ CHANGE +
+
    +
  • fr-FR Translation file updated.
  • +
  • zh-CN Translation file updated.
  • +
  • Added LostIsland to official mods in the SurvivalEvolved gamedata file.
  • +
  • Added coloring to the setting grids for the Lost Island DLC engrams, creatures and resources.
  • +
+

+
+
+ + bletch + bletch1971@hotmail.com + +
+ + + urn:uuid:DBAFCE91-2235-4B6C-AE9B-5E4EF9FEC8F5 + 1.1.411 (1.1.411.10) + 1.1.411.10 + + 2021-12-14T00:00:00Z + +
+

+ NEW +
+

    +
  • + A new Discord Bot has been added to the server manager.
    + This new discord bot will allow you to send Start, Stop, Shutdown, Restart, Backup and Update commands to the server manager from within your discord client.
    + To setup the new discord bot, open the global settings and scroll down to the Discord section.

    + + NOTE: This is long process to get the discord bot working, and I have created a forum post with detailed instructions how to do it. + +
  • +
  • Server Settings - Dino Section - Added MutagenLevelBoost and MutagenLevelBoostBred settings.
  • +
  • Added gamedata file for the Lost Island DLC - only contains the map name for the Map droplist - do not use until the DLC is officially released!
  • +
+ CHANGE +
+
    +
  • Global Settings - Added reset button to the Data Directory Location.
  • +
  • Server Monitor window now stores it's location.
  • +
  • Removed MOTD and Alternate Save Directory from the Administration section of the Sync.
  • +
  • fr-FR Translation file updated.
  • +
  • pt-BR Translation file updated.
  • +
  • ru-RU Translation file updated.
  • +
  • zh-CN Translation file updated.
  • +
+

+
+
+ + bletch + bletch1971@hotmail.com + +
urn:uuid:BE37B3FD-B498-4B1C-9D4D-754EA0F4A357 diff --git a/ASM/beta/VersionFeed.xml b/ASM/beta/VersionFeed.xml index b066b3e4..03eb394c 100644 --- a/ASM/beta/VersionFeed.xml +++ b/ASM/beta/VersionFeed.xml @@ -5,22 +5,22 @@ Ark Server Manager Version Feed This is the Ark Server Manager beta version feed. - 2021-12-12T00:00:00Z + 2021-12-16T00:00:00Z - urn:uuid:C02D44F9-E3AA-4CA0-BF2F-110F8C83CC21 - 1.1.411 (1.1.411.7) - 1.1.411.7 + urn:uuid:8EE5659C-18E6-47D3-941D-C32B129D2E06 + 1.1.413 (1.1.413.7) + 1.1.413.7 - 2021-12-12T00:00:00Z + 2021-12-17T00:00:00Z

NEW

    -
  • Added gamedata file for the Lost Island DLC - only contains the map name for the Map droplist - do not use until the DLC is officially released!
  • -
  • Added Lost Island to the language files for translation.
  • +
  • Main Window - Added Discord Bot Status and a button to Stop/Start the discord bot.
  • +
  • Global Settings - Discord Bot section - Added a checkbox to allow all bots.

@@ -32,42 +32,18 @@
- urn:uuid:F75C6AFF-2A27-49BE-917A-7EAAACCCBF17 - 1.1.411 (1.1.411.6) - 1.1.411.6 + urn:uuid:65A7E6B1-98D1-422D-B42F-B0EBB1D20E41 + 1.1.413 (1.1.413.6) + 1.1.413.6 - 2021-12-12T00:00:00Z + 2021-12-17T00:00:00Z

CHANGE

    -
  • fr-FR Translation file updated.
  • -
-

-
-
- - bletch - bletch1971@hotmail.com - -
- - - urn:uuid:0BCB8BBA-330F-4063-8B7E-5B144589999A - 1.1.411 (1.1.411.5) - 1.1.411.5 - - 2021-12-10T00:00:00Z - -
-

- CHANGE -
-

    -
  • Server Monitor window now stores it's location.
  • -
  • Reference library updates.
  • +
  • Added additional message logging when log level set to debug.
  • pt-BR Translation file updated.
  • ru-RU Translation file updated.
@@ -81,29 +57,69 @@ - urn:uuid:7697EBD7-DEF6-422B-B9A6-D4B77F31D572 - 1.1.411 (1.1.411.4) - 1.1.411.4 + urn:uuid:65A7E6B1-98D1-422D-B42F-B0EBB1D20E41 + 1.1.413 (1.1.413.5) + 1.1.413.5 - 2021-12-09T00:00:00Z + 2021-12-16T00:00:00Z + +
+

+ NEW +
+

    +
  • Global Settings - Discord Bot section - Added a log level droplist.
  • +
+

+
+
+ + bletch + bletch1971@hotmail.com + +
+ + + urn:uuid:98FFBFA1-4E99-4801-BF2B-CA68BE300C27 + 1.1.413 (1.1.413.4) + 1.1.413.4 + + 2021-12-16T00:00:00Z

BUGFIX

    -
  • Fixed a minor issue when clicking the data directory reset button - fringe case that I found during testing.
  • +
  • Fixed a bug that would prevent a bot on the whitelist processing the message.
+

+
+
+ + bletch + bletch1971@hotmail.com + +
+ + + urn:uuid:8958A494-DE13-4F6F-ACA2-10026D5FB8A9 + 1.1.413 (1.1.413.3) + 1.1.413.3 + + 2021-12-16T00:00:00Z + +
+

NEW

    -
  • Server Settings - Dino Section - Added MutagenLevelBoost and MutagenLevelBoostBred settings.
  • +
  • Server Settings - Discord Bot section - Added an alias that can be used with the discord command instead of the profile id.
CHANGE
    -
  • pt-BR Translation file updated.
  • -
  • ru-RU Translation file updated.
  • +
  • Removed the mandatory requirement to enter the '!' after the discord prefix. The '!' has been added to the existing prefix so no change to existing functionality, but you can now change it.
  • zh-CN Translation file updated.

@@ -116,73 +132,41 @@ - urn:uuid:8290F43D-98F4-4F4D-8DD8-0A3EF5A45656 - 1.1.411 (1.1.411.3) - 1.1.411.3 + urn:uuid:8958A494-DE13-4F6F-ACA2-10026D5FB8A9 + 1.1.413 (1.1.413.2) + 1.1.413.2 - 2021-12-08T00:00:00Z - -
-

- BUGFIX -
-

    -
  • Fixed language translation of Data Directory window.
  • -
-

-
-
- - bletch - bletch1971@hotmail.com - -
- - - urn:uuid:ADBF3FE0-017C-4E23-8FE8-EFE75E7C8BA1 - 1.1.411 (1.1.411.2) - 1.1.411.2 - - 2021-12-08T00:00:00Z - -
-

- CHANGE -
-

    -
  • Global Settings - Added reset button to the Data Directory Location.
  • -
  • pt-BR Translation file updated.
  • -
  • zh-CN Translation file updated.
  • -
-

-
-
- - bletch - bletch1971@hotmail.com - -
- - - urn:uuid:DBAFCE91-2235-4B6C-AE9B-5E4EF9FEC8F5 - 1.1.411 (1.1.411.1) - 1.1.411.1 - - 2021-12-06T00:00:00Z + 2021-12-16T00:00:00Z

NEW

    -
  • - A new Discord Bot has been added to the server manager.
    - This new discord bot will allow you to send Start, Stop, Shutdown, Restart, Backup and Update commands to the server manager from within your discord client.
    - To setup the new discord bot, open the global settings and scroll down to the Discord section.

    - - NOTE: This is long process to get the discord bot working, and I have created a forum post with detailed instructions how to do it. - -
  • +
  • Global Settings - Discord Bot section - Added a whitelist to allow bots to send commands to the server manager.
  • +
+

+
+
+ + bletch + bletch1971@hotmail.com + +
+ + + urn:uuid:3E33DCB2-ECFE-4489-B1A4-56F5D386F9DC + 1.1.413 (1.1.413.1) + 1.1.413.1 + + 2021-12-16T00:00:00Z + +
+

+ CHANGE +
+

    +
  • Made changes to the code to help improve performance.

diff --git a/ASM/beta/en-US.xaml b/ASM/beta/en-US.xaml index 12c4e7fe..4ad2bee2 100644 --- a/ASM/beta/en-US.xaml +++ b/ASM/beta/en-US.xaml @@ -83,12 +83,21 @@ All - + Normal Minimized Maximized + + Critical + Error + Warning + Info + Verbose + Debug + + Run as Administrator This application requires administration priviledges to access ALL functionality. Would you like to Run as Administrator? @@ -127,6 +136,7 @@ Server download complete Complete Cancelled + Failed Cancel @@ -621,8 +631,17 @@ The id of the discord server the bot will listen to. Prefix: The prefix that must be used when sending a command via discord. + Log Level: Get Token... Help... + Allow All Bots + If enabled, the server manager bot will respond to all other bots, otherwise they will be ignored unless they are in the whitelist. + Bot Whitelist + Bot ID + The id of the bot to whitelist. + Add Whitelist + Clear Whitelists + Delete Whitelist If enabled, the backup command can be sent from discord. If enabled, the restart command can be sent from discord. If enabled, the shutdown command can be sent from discord. @@ -773,12 +792,18 @@ Enable Enable the auto-update scheduled task Next Run Time: + Discord Bot: + Start + Start the discord bot + Stop + Stop the discord bot Unknown Disabled Queued Ready Running + Stopped Profile failed to load The profile at {0} failed to load. The error was: {1}\r\n{2} @@ -1207,6 +1232,8 @@ Discord Bot Details Channel Id: The id of the discord server channel this profile will listen to. + Alias: + A unique name to identify your server when using the discord commands, can be used instead of the profile id. Allow Backup If enabled, the profile will listen for backup commands from discord. Allow Restart @@ -5495,6 +5522,23 @@ Boss Inventory - Crystal Wyvern Queen (Gamma) Boss Inventory - Crystal Wyvern Queen (Beta) Boss Inventory - Crystal Wyvern Queen (Alpha) + + Boss Inventory - Dragon (Gamma) + Boss Inventory - Dragon (Beta) + Boss Inventory - Dragon (Alpha) + Boss Inventory - Manticore (Gamma) + Boss Inventory - Manticore (Beta) + Boss Inventory - Manticore (Alpha) + + Boss Inventory - Dragon (Easy) + Boss Inventory - Dragon (Medium) + Boss Inventory - Dragon (Hard) + Boss Inventory - Manticore (Easy) + Boss Inventory - Manticore (Medium) + Boss Inventory - Manticore (Hard) + Boss Inventory - Megapithecus (Easy) + Boss Inventory - Megapithecus (Medium) + Boss Inventory - Megapithecus (Hard) @@ -5580,8 +5624,9 @@ Another command '{0}' is currently running against profile '{1}'. Command '{0}' has been disabled for profile '{1}'. - The '{0}' command requires a profile id. + The '{0}' command requires a profile id or alias. Profile '{0}' was not found or is not associated with the channel. + Multiple profiles with '{0}' were found in the channel, command aborted. Profile '{0}' is in a state '{1}' that cannot run this command. Profile '{0}' is currently being updated. diff --git a/ASM/beta/latest.txt b/ASM/beta/latest.txt index b64b069b..359dfd67 100644 --- a/ASM/beta/latest.txt +++ b/ASM/beta/latest.txt @@ -1 +1 @@ -1.1.411.7 +1.1.413.7 diff --git a/ASM/beta/latest.zip b/ASM/beta/latest.zip index ef2570a1..b0a9e9b9 100644 Binary files a/ASM/beta/latest.zip and b/ASM/beta/latest.zip differ diff --git a/ASM/en-US.xaml b/ASM/en-US.xaml index 2384aae1..f6281726 100644 --- a/ASM/en-US.xaml +++ b/ASM/en-US.xaml @@ -24,6 +24,7 @@ Genesis: Part 1 Crystal Isles Genesis: Part 2 + Lost Island @@ -56,6 +57,7 @@ Genesis: Part 1 Crystal Isles Genesis: Part 2 + Lost Island PGM @@ -125,6 +127,7 @@ Server download complete Complete Cancelled + Failed Cancel @@ -610,7 +613,24 @@ This message will be displayed when the server shutdown has been cancelled. Show shutdown reason with ALL shutdown messages If enabled, the shutdown reason will be shown with all shutdown message; otherwise it will only be shown at the start of the server shutdown. - + + Enable Discord Bot + You will need to restart the server manager if you change any settings for the Discord Bot. + Token: + The token associated with the discord bot. + Server Id: + The id of the discord server the bot will listen to. + Prefix: + The prefix that must be used when sending a command via discord. + Get Token... + Help... + If enabled, the backup command can be sent from discord. + If enabled, the restart command can be sent from discord. + If enabled, the shutdown command can be sent from discord. + If enabled, the start command can be sent from discord. + If enabled, the stop command can be sent from discord. + If enabled, the update command can be sent from discord. + SMTP Email Settings Host: The name or IP address of the host used for SMTP transmissions. @@ -662,6 +682,8 @@ Changing the data directory will move any existing profiles to the new location, but it will not move any server installations. Do you still want to change this directory? Failed to change data directory There was an error changing the data directory: {0}\r\nPlease correct the error and try again, or contact technical support for assistance. + Confirm Data Directory Reset + Click 'Yes' to confirm you want to reset the location of the data directory. Once reset, the server manager will shutdown and you will need to restart. Select Backup Directory Select Cache Directory @@ -775,6 +797,9 @@ Reinstall SteamCMD Error An error occured while trying to reinstall SteamCMD. This has left SteamCmd in an unstable state, try reinstalling again or please report this.\r\nException: {0} + Discord Bot Running Commands + The discord bot has one or more running commands, do you want to continue shutting down the server manager? + Start Server Confirmation You are about to start the server, do you want to continue? Shutdown Server Confirmation @@ -1179,6 +1204,24 @@ If enabled, the server will be restarted even if shutdown for Auto-Restarts and Auto-Updates. + + Discord Bot Details + Channel Id: + The id of the discord server channel this profile will listen to. + Allow Backup + If enabled, the profile will listen for backup commands from discord. + Allow Restart + If enabled, the profile will listen for restart commands from discord. + Allow Shutdown + If enabled, the profile will listen for shutdown commands from discord. + Allow Start + If enabled, the profile will listen for start commands from discord. + Allow Stop + If enabled, the profile will listen for stop commands from discord. + Allow Update + If enabled, the profile will listen for update commands from discord. + + Rules Enable Hardcore Mode @@ -1598,14 +1641,18 @@ Per-Level Stat Multipliers (Tamed) - Add Per-Level Stat Multipliers (Tamed) - Affinity Per-Level Stat Multipliers (Wild) + Mutagen Level Boost (Wild) + Mutagen Level Boost (Bred) Dino Breeding Multipliers If enabled, allows scale factors to be applied to each base stat. If enabled, allows scale factors to be applied to each stat. If enabled, allows scale factors to be applied to the Taming Addition for each stat. If enabled, allows scale factors to be applied to the Taming Multiplier for each stat. If enabled, allows scale factors to be applied to the Tamed Stat Increase for each stat. - Reset all multipliers back to defaults. - Reset all multipliers back to defaults. + If enabled, allows scale factors to be applied to the Number of levels Mutagen adds to tames with wild ancestry. + If enabled, allows scale factors to be applied to the Number of levels Mutagen adds to tames with bred ancestry. + Reset all values back to defaults. + Reset all values back to defaults. Mating Interval: Specifies the multiplier for time between tamed dino mating. Lower values decrease the time between mating. @@ -5449,6 +5496,23 @@ Boss Inventory - Crystal Wyvern Queen (Gamma) Boss Inventory - Crystal Wyvern Queen (Beta) Boss Inventory - Crystal Wyvern Queen (Alpha) + + Boss Inventory - Dragon (Gamma) + Boss Inventory - Dragon (Beta) + Boss Inventory - Dragon (Alpha) + Boss Inventory - Manticore (Gamma) + Boss Inventory - Manticore (Beta) + Boss Inventory - Manticore (Alpha) + + Boss Inventory - Dragon (Easy) + Boss Inventory - Dragon (Medium) + Boss Inventory - Dragon (Hard) + Boss Inventory - Manticore (Easy) + Boss Inventory - Manticore (Medium) + Boss Inventory - Manticore (Hard) + Boss Inventory - Megapithecus (Easy) + Boss Inventory - Megapithecus (Medium) + Boss Inventory - Megapithecus (Hard) @@ -5523,4 +5587,32 @@ There was a problem while performing the server update. This may leave your server in a incomplete state.\r\n\r\nDo you want to continue with the server start, this could cause problems? + + Discord Bot Error + The discord bot requires a valid token so it can log into the discord server\r\nThis can be set in the global settings. + The discord bot prefix contains invalid characters. Only letters and numbers are allowed. + + Command '{0}' has not been enabled. + Unknown command '{0}'. + Another command is currently being processed. + Another command '{0}' is currently running against profile '{1}'. + Command '{0}' has been disabled for profile '{1}'. + + The '{0}' command requires a profile id. + Profile '{0}' was not found or is not associated with the channel. + Profile '{0}' is in a state '{1}' that cannot run this command. + Profile '{0}' is currently being updated. + + Call to server '{0}' failed. + A backup request for server '{0}' has been sent. + A restart request for server '{0}' has been sent. + A shutdown request for server '{0}' has been sent. + A start request for server '{0}' has been sent. + A stop request for server '{0}' has been sent. + An update request for server '{0}' has been sent. + + Count: + Map: + + \ No newline at end of file diff --git a/ASM/latest.exe b/ASM/latest.exe index 8964a320..3b854b72 100644 Binary files a/ASM/latest.exe and b/ASM/latest.exe differ diff --git a/ASM/latest.txt b/ASM/latest.txt index 73a41c03..a03f4f61 100644 --- a/ASM/latest.txt +++ b/ASM/latest.txt @@ -1 +1 @@ -1.1.410 +1.1.412 diff --git a/ASM/latest.zip b/ASM/latest.zip index eb8ad6cc..350e1255 100644 Binary files a/ASM/latest.zip and b/ASM/latest.zip differ diff --git a/CSM/ConanServerManager_1.1.56.exe b/CSM/ConanServerManager_1.1.56.exe new file mode 100644 index 00000000..54cb8dc3 Binary files /dev/null and b/CSM/ConanServerManager_1.1.56.exe differ diff --git a/CSM/ConanServerManager_1.1.56.zip b/CSM/ConanServerManager_1.1.56.zip new file mode 100644 index 00000000..e03e666f Binary files /dev/null and b/CSM/ConanServerManager_1.1.56.zip differ diff --git a/CSM/ConanServerManager_1.1.57.exe b/CSM/ConanServerManager_1.1.57.exe new file mode 100644 index 00000000..ff86db29 Binary files /dev/null and b/CSM/ConanServerManager_1.1.57.exe differ diff --git a/CSM/ConanServerManager_1.1.57.zip b/CSM/ConanServerManager_1.1.57.zip new file mode 100644 index 00000000..c039f9a4 Binary files /dev/null and b/CSM/ConanServerManager_1.1.57.zip differ diff --git a/CSM/VersionFeed.xml b/CSM/VersionFeed.xml index 6b861df7..9d75ce81 100644 --- a/CSM/VersionFeed.xml +++ b/CSM/VersionFeed.xml @@ -5,7 +5,69 @@ Conan Server Manager Version Feed This is the Conan Server Manager release version feed. - 2021-12-02T00:00:00Z + 2021-12-15T00:00:00Z + + + urn:uuid:6383E79A-C31F-462B-9730-B26B28DC5EFF + 1.1.57 (1.1.57.3) + 1.1.57.3 + + 2021-12-15T00:00:00Z + +
+

+ BUGFIX +
+

    +
  • Fixed a bug with the Server Shutdown when the CheckForOnlinePlayers option not selected.
  • +
  • Fixed a bug when the backup path was a root directory that caused a 'Invalid URI: A Dos path must be rooted' crash.
  • +
  • Added additional validation when setting directories in the global setting, to ensure they are rooted correctly.
  • +
  • Fixed a bug when starting the server manager and it tries to download steamcmd, but fails as steamcmd is unavailable for download.
  • +
+

+
+
+ + bletch + bletch1971@hotmail.com + +
+ + + urn:uuid:F8A08616-749B-48E7-87D5-D97E86AB3926 + 1.1.56 (1.1.56.6) + 1.1.56.6 + + 2021-12-12T00:00:00Z + +
+

+ NEW +
+

    +
  • + A new Discord Bot has been added to the server manager.
    + This new discord bot will allow you to send Start, Stop, Shutdown, Restart, Backup and Update commands to the server manager from within your discord client.
    + To setup the new discord bot, open the global settings and scroll down to the Discord section.

    + + NOTE: This is long process to get the discord bot working, and I have created a forum post with detailed instructions how to do it. + +
  • +
+ CHANGE +
+
    +
  • Global Settings - Added reset button to the Data Directory Location.
  • +
  • Server Monitor window now stores it's location.
  • +
+

+
+
+ + bletch + bletch1971@hotmail.com + +
urn:uuid:A3A790A9-D511-4CE8-AC15-A36B3DAB4385 diff --git a/CSM/beta/VersionFeed.xml b/CSM/beta/VersionFeed.xml index 9a499dcf..4fc8f6e2 100644 --- a/CSM/beta/VersionFeed.xml +++ b/CSM/beta/VersionFeed.xml @@ -5,144 +5,165 @@ Conan Server Manager Version Feed This is the Conan Server Manager beta version feed. - 2021-12-12T00:00:00Z + 2021-12-17T00:00:00Z - urn:uuid:4C15C7A9-317C-4A9B-B426-6E885B918D3B - 1.1.56 (1.1.56.6) - 1.1.56.6 + urn:uuid:9A427D82-9904-44F5-8C1E-7C943049869A + 1.1.58 (1.1.58.7) + 1.1.58.7 - 2021-12-12T00:00:00Z - -
-

- BUGFIX -
-

    -
  • Reference library updates.
  • -
-

-
-
- - bletch - bletch1971@hotmail.com - -
- - - urn:uuid:126E8E9F-7FF2-4239-88D6-5FFC89925463 - 1.1.56 (1.1.56.5) - 1.1.56.5 - - 2021-12-10T00:00:00Z - -
-

- BUGFIX -
-

    -
  • Server Monitor window now stores it's location.
  • -
  • Reference library updates.
  • -
-

-
-
- - bletch - bletch1971@hotmail.com - -
- - - urn:uuid:BECA74AB-1107-40B0-932A-A851E978EABF - 1.1.56 (1.1.56.4) - 1.1.56.4 - - 2021-12-09T00:00:00Z - -
-

- BUGFIX -
-

    -
  • Fixed a minor issue when clicking the data directory reset button - fringe case that I found during testing.
  • -
-

-
-
- - bletch - bletch1971@hotmail.com - -
- - - urn:uuid:8C834060-F20F-4ACE-8F49-0B459531BA3C - 1.1.56 (1.1.56.3) - 1.1.56.3 - - 2021-12-08T00:00:00Z - -
-

- BUGFIX -
-

    -
  • Fixed language translation of Data Directory window.
  • -
-

-
-
- - bletch - bletch1971@hotmail.com - -
- - - urn:uuid:339F72FC-920A-4425-AF86-AF9B34611A21 - 1.1.56 (1.1.56.2) - 1.1.56.2 - - 2021-12-08T00:00:00Z - -
-

- CHANGE -
-

    -
  • Global Settings - Added reset button to the Data Directory Location.
  • -
-

-
-
- - bletch - bletch1971@hotmail.com - -
- - - urn:uuid:F8A08616-749B-48E7-87D5-D97E86AB3926 - 1.1.56 (1.1.56.1) - 1.1.56.1 - - 2021-12-06T00:00:00Z + 2021-12-17T00:00:00Z

NEW

    -
  • - A new Discord Bot has been added to the server manager.
    - This new discord bot will allow you to send Start, Stop, Shutdown, Restart, Backup and Update commands to the server manager from within your discord client.
    - To setup the new discord bot, open the global settings and scroll down to the Discord section.

    - - NOTE: This is long process to get the discord bot working, and I have created a forum post with detailed instructions how to do it. - -
  • +
  • Main Window - Added Discord Bot Status and a button to Stop/Start the discord bot.
  • +
  • Global Settings - Discord Bot section - Added a checkbox to allow all bots.
  • +
+

+
+
+ + bletch + bletch1971@hotmail.com + +
+ + + urn:uuid:A189668E-DA03-471A-9C5A-7FF2A7264F9C + 1.1.58 (1.1.58.6) + 1.1.58.6 + + 2021-12-17T00:00:00Z + +
+

+ CHANGE +
+

    +
  • Added additional message logging when log level set to debug.
  • +
+

+
+
+ + bletch + bletch1971@hotmail.com + +
+ + + urn:uuid:A189668E-DA03-471A-9C5A-7FF2A7264F9C + 1.1.58 (1.1.58.5) + 1.1.58.5 + + 2021-12-16T00:00:00Z + +
+

+ NEW +
+

    +
  • Global Settings - Discord Bot section - Added a log level droplist.
  • +
+

+
+
+ + bletch + bletch1971@hotmail.com + +
+ + + urn:uuid:F3C22842-A089-46F7-AB1A-5D3DED105412 + 1.1.58 (1.1.58.4) + 1.1.58.4 + + 2021-12-16T00:00:00Z + +
+

+ BUGFIX +
+

    +
  • Fixed a bug that would prevent a bot on the whitelist processing the message.
  • +
+

+
+
+ + bletch + bletch1971@hotmail.com + +
+ + + urn:uuid:F3C22842-A089-46F7-AB1A-5D3DED105412 + 1.1.58 (1.1.58.3) + 1.1.58.3 + + 2021-12-16T00:00:00Z + +
+

+ NEW +
+

    +
  • Server Settings - Discord Bot section - Added an alias that can be used with the discord command instead of the profile id.
  • +
+ CHANGE +
+
    +
  • Removed the mandatory requirement to enter the '!' after the discord prefix. The '!' has been added to the existing prefix so no change to existing functionality, but you can now change it.
  • +
+

+
+
+ + bletch + bletch1971@hotmail.com + +
+ + + urn:uuid:C566D9D2-3566-46DF-8AD4-39F5FC0FFEF2 + 1.1.58 (1.1.58.2) + 1.1.58.2 + + 2021-12-16T00:00:00Z + +
+

+ NEW +
+

    +
  • Global Settings - Discord Bot section - Added a whitelist to allow bots to send commands to the server manager.
  • +
+

+
+
+ + bletch + bletch1971@hotmail.com + +
+ + + urn:uuid:19B09A66-43F2-4D5F-AF33-5C77D7EA9A6B + 1.1.58 (1.1.58.1) + 1.1.58.1 + + 2021-12-16T00:00:00Z + +
+

+ CHANGE +
+

    +
  • Made changes to the code to help improve performance.

diff --git a/CSM/beta/en-US.xaml b/CSM/beta/en-US.xaml index 9a6c123b..db8c265c 100644 --- a/CSM/beta/en-US.xaml +++ b/CSM/beta/en-US.xaml @@ -57,12 +57,21 @@ All - + Normal Minimized Maximized + + Critical + Error + Warning + Info + Verbose + Debug + + x % @@ -120,6 +129,7 @@ Server download complete Complete Cancelled + Failed Cancel @@ -605,12 +615,18 @@ Enable Enable the auto-update scheduled task Next Run Time: + Discord Bot: + Start + Start the discord bot + Stop + Stop the discord bot Unknown Disabled Queued Ready Running + Stopped Profile failed to load The profile at {0} failed to load. The error was: {1}\r\n{2} @@ -779,8 +795,17 @@ The id of the discord server the bot will listen to. Prefix: The prefix that must be used when sending a command via discord. + Log Level: Get Token... Help... + Allow All Bots + If enabled, the server manager bot will respond to all other bots, otherwise they will be ignored unless they are in the whitelist. + Bot Whitelist + Bot ID + The id of the bot to whitelist. + Add Whitelist + Clear Whitelists + Delete Whitelist If enabled, the backup command can be sent from discord. If enabled, the restart command can be sent from discord. If enabled, the shutdown command can be sent from discord. @@ -1147,6 +1172,8 @@ Discord Bot Details Channel Id: The id of the discord server channel this profile will listen to. + Alias: + A unique name to identify your server when using the discord commands, can be used instead of the profile id. Allow Backup If enabled, the profile will listen for backup commands from discord. Allow Restart @@ -1244,8 +1271,9 @@ Another command '{0}' is currently running against profile '{1}'. Command '{0}' has been disabled for profile '{1}'. - The '{0}' command requires a profile id. + The '{0}' command requires a profile id or alias. Profile '{0}' was not found or is not associated with the channel. + Multiple profiles with '{0}' were found in the channel, command aborted. Profile '{0}' is in a state '{1}' that cannot run this command. Profile '{0}' is currently being updated. diff --git a/CSM/beta/latest.txt b/CSM/beta/latest.txt index 20d52c3e..96ec7d2c 100644 --- a/CSM/beta/latest.txt +++ b/CSM/beta/latest.txt @@ -1 +1 @@ -1.1.56.6 +1.1.58.7 diff --git a/CSM/beta/latest.zip b/CSM/beta/latest.zip index aac86862..e0b87bc2 100644 Binary files a/CSM/beta/latest.zip and b/CSM/beta/latest.zip differ diff --git a/CSM/en-US.xaml b/CSM/en-US.xaml index e61a9e28..50e599fb 100644 --- a/CSM/en-US.xaml +++ b/CSM/en-US.xaml @@ -120,6 +120,7 @@ Server download complete Complete Cancelled + Failed Cancel @@ -628,6 +629,9 @@ Reinstall SteamCMD Error An error occured while trying to reinstall SteamCMD. This has left SteamCmd in an unstable state, try reinstalling again or please report this.\r\nException: {0} + Discord Bot Running Commands + The discord bot has one or more running commands, do you want to continue shutting down the server manager? + Start Server Confirmation You are about to start the server, do you want to continue? Shutdown Server Confirmation @@ -768,6 +772,23 @@ Show shutdown reason with ALL shutdown messages If enabled, the shutdown reason will be shown with all shutdown message; otherwise it will only be shown at the start of the server shutdown. + Enable Discord Bot + You will need to restart the server manager if you change any settings for the Discord Bot. + Token: + The token associated with the discord bot. + Server Id: + The id of the discord server the bot will listen to. + Prefix: + The prefix that must be used when sending a command via discord. + Get Token... + Help... + If enabled, the backup command can be sent from discord. + If enabled, the restart command can be sent from discord. + If enabled, the shutdown command can be sent from discord. + If enabled, the start command can be sent from discord. + If enabled, the stop command can be sent from discord. + If enabled, the update command can be sent from discord. + SMTP Email Settings Host: The name or IP address of the host used for SMTP transmissions. @@ -815,6 +836,8 @@ Changing the data directory will move any existing profiles to the new location, but it will not move any server installations. Do you still want to change this directory? Failed to change data directory There was an error changing the data directory: {0}\r\nPlease correct the error and try again, or contact technical support for assistance. + Confirm Data Directory Reset + Click 'Yes' to confirm you want to reset the location of the data directory. Once reset, the server manager will shutdown and you will need to restart. Select Backup Directory Select Cache Directory @@ -1121,6 +1144,24 @@ If enabled, the server will be restarted even if shutdown for Auto-Restarts and Auto-Updates. + + Discord Bot Details + Channel Id: + The id of the discord server channel this profile will listen to. + Allow Backup + If enabled, the profile will listen for backup commands from discord. + Allow Restart + If enabled, the profile will listen for restart commands from discord. + Allow Shutdown + If enabled, the profile will listen for shutdown commands from discord. + Allow Start + If enabled, the profile will listen for start commands from discord. + Allow Stop + If enabled, the profile will listen for stop commands from discord. + Allow Update + If enabled, the profile will listen for update commands from discord. + + Server File Details NOTE: Any changes will require a server restart to take effect. @@ -1193,4 +1234,32 @@ There was a problem while performing the server update. This may leave your server in a incomplete state.\r\n\r\nDo you want to continue with the server start, this could cause problems? + + Discord Bot Error + The discord bot requires a valid token so it can log into the discord server\r\nThis can be set in the global settings. + The discord bot prefix contains invalid characters. Only letters and numbers are allowed. + + Command '{0}' has not been enabled. + Unknown command '{0}'. + Another command is currently being processed. + Another command '{0}' is currently running against profile '{1}'. + Command '{0}' has been disabled for profile '{1}'. + + The '{0}' command requires a profile id. + Profile '{0}' was not found or is not associated with the channel. + Profile '{0}' is in a state '{1}' that cannot run this command. + Profile '{0}' is currently being updated. + + Call to server '{0}' failed. + A backup request for server '{0}' has been sent. + A restart request for server '{0}' has been sent. + A shutdown request for server '{0}' has been sent. + A start request for server '{0}' has been sent. + A stop request for server '{0}' has been sent. + An update request for server '{0}' has been sent. + + Count: + Map: + + \ No newline at end of file diff --git a/CSM/latest.exe b/CSM/latest.exe index 33267533..ff86db29 100644 Binary files a/CSM/latest.exe and b/CSM/latest.exe differ diff --git a/CSM/latest.txt b/CSM/latest.txt index 5ebc7199..5df20b4e 100644 --- a/CSM/latest.txt +++ b/CSM/latest.txt @@ -1 +1 @@ -1.1.55 +1.1.57 diff --git a/CSM/latest.zip b/CSM/latest.zip index cc4e3efc..c039f9a4 100644 Binary files a/CSM/latest.zip and b/CSM/latest.zip differ diff --git a/GameData/Template/Template.gamedata b/GameData/Template/Template.gamedata index f7c58ff7..47765c96 100644 --- a/GameData/Template/Template.gamedata +++ b/GameData/Template/Template.gamedata @@ -2,7 +2,7 @@ "Application": "ark", "Version": "1.0.0", "Created": "2020-03-04T00:00:00.0000000Z", - "Mod": "ArkPrime|TheCenter|PrimitivePlus|ScorchedEarth|Ragnarok|Aberration|Extinction|Genesis|CrystalIsles|Genesis2|LostIsland|", + "Mod": "ArkPrime|TheCenter|PrimitivePlus|ScorchedEarth|Ragnarok|Aberration|Extinction|Genesis|Valguero|CrystalIsles|Genesis2|LostIsland|", "Creatures": [ { "NameTag": "Achatina", diff --git a/Plugins/Discord/ServerManager.Plugin.Discord_1.0.19.zip b/Plugins/Discord/ServerManager.Plugin.Discord_1.0.19.zip new file mode 100644 index 00000000..f8f09d75 Binary files /dev/null and b/Plugins/Discord/ServerManager.Plugin.Discord_1.0.19.zip differ diff --git a/Plugins/Discord/VersionFeed.xml b/Plugins/Discord/VersionFeed.xml index 981fd7c5..52a870db 100644 --- a/Plugins/Discord/VersionFeed.xml +++ b/Plugins/Discord/VersionFeed.xml @@ -5,7 +5,39 @@ Discord Plugin Version Feed This is the Discord Plugin release version feed. - 2021-12-03T00:00:00Z + 2021-12-12T00:00:00Z + + + urn:uuid:A9EC2F32-E026-485F-BD7B-C657DBA95B54 + 1.0.19 (1.0.19.5) + 1.0.19.5 + + 2021-12-12T00:00:00Z + +
+

+ BUGFIX +
+

    +
  • Fixed alert test message to support globalization.
  • +
+ CHANGE +
+
    +
  • Changed the plugin initialization so it throws an exception if unable to load config file, rather than creating a new config file.
  • +
  • de-DE Translation file updated.
  • +
  • fr-FR Translation file added.
  • +
  • pt-BR Translation file updated.
  • +
  • ru-RU Translation file added.
  • +
+

+
+
+ + bletch + bletch1971@hotmail.com + +
urn:uuid:C01A4AA0-D839-4732-9814-301DAC6EB094 diff --git a/Plugins/Discord/beta/VersionFeed.xml b/Plugins/Discord/beta/VersionFeed.xml index 3c7cbb83..48b3f9c8 100644 --- a/Plugins/Discord/beta/VersionFeed.xml +++ b/Plugins/Discord/beta/VersionFeed.xml @@ -7,6 +7,52 @@ 2021-12-12T00:00:00Z + + urn:uuid:A0D7BFD2-F2F0-481A-A22D-3C193BFB4C99 + 1.0.19 (1.0.19.5) + 1.0.19.5 + + 2021-12-12T00:00:00Z + +
+

+ CHANGE +
+

    +
  • ru-RU Translation file updated.
  • +
+

+
+
+ + bletch + bletch1971@hotmail.com + +
+ + + urn:uuid:38A61493-CA4E-479E-A3FD-266565E83D90 + 1.0.19 (1.0.19.4) + 1.0.19.4 + + 2021-12-12T00:00:00Z + +
+

+ CHANGE +
+

    +
  • ru-RU Translation file added.
  • +
+

+
+
+ + bletch + bletch1971@hotmail.com + +
+ urn:uuid:DDB25735-1D20-4580-B5FE-1AD4BD107376 1.0.19 (1.0.19.3) diff --git a/Plugins/Discord/beta/latest.txt b/Plugins/Discord/beta/latest.txt index 4821a307..a9fd2b1c 100644 --- a/Plugins/Discord/beta/latest.txt +++ b/Plugins/Discord/beta/latest.txt @@ -1 +1 @@ -1.0.19.3 +1.0.19.5 diff --git a/Plugins/Discord/beta/latest.zip b/Plugins/Discord/beta/latest.zip index 4838a11d..0c59dc88 100644 Binary files a/Plugins/Discord/beta/latest.zip and b/Plugins/Discord/beta/latest.zip differ diff --git a/Plugins/Discord/en-US.xaml b/Plugins/Discord/en-US.xaml index 82a5f111..0ff41e2c 100644 --- a/Plugins/Discord/en-US.xaml +++ b/Plugins/Discord/en-US.xaml @@ -118,6 +118,8 @@ Test Action Error An error occurred while trying to test the config profile. The profile is not enabled and cannot be tested. + + Test '{0}' alert type message for profile name '{1}' diff --git a/Plugins/Discord/latest.txt b/Plugins/Discord/latest.txt index 54473825..ecba93a4 100644 --- a/Plugins/Discord/latest.txt +++ b/Plugins/Discord/latest.txt @@ -1 +1 @@ -1.0.18 +1.0.19 diff --git a/Plugins/Discord/latest.zip b/Plugins/Discord/latest.zip index c5f1d7a8..f8f09d75 100644 Binary files a/Plugins/Discord/latest.zip and b/Plugins/Discord/latest.zip differ diff --git a/src/ARKServerManager/ARKServerManager.csproj b/src/ARKServerManager/ARKServerManager.csproj index b7242f38..380bd3cd 100644 --- a/src/ARKServerManager/ARKServerManager.csproj +++ b/src/ARKServerManager/ARKServerManager.csproj @@ -145,7 +145,6 @@ - diff --git a/src/ARKServerManager/App.config b/src/ARKServerManager/App.config index febac3a6..f74ceca5 100644 --- a/src/ARKServerManager/App.config +++ b/src/ARKServerManager/App.config @@ -353,6 +353,9 @@ https://arkservermanager.freeforums.net/thread/8764/get-own-discord-bot + + 10 + @@ -814,7 +817,7 @@ False - asm + asm! @@ -849,6 +852,20 @@ 50 + + + + + + + False + + + Info + + + False + diff --git a/src/ARKServerManager/App.xaml.cs b/src/ARKServerManager/App.xaml.cs index 2e79faa1..aded5630 100644 --- a/src/ARKServerManager/App.xaml.cs +++ b/src/ARKServerManager/App.xaml.cs @@ -11,6 +11,7 @@ using ServerManagerTool.Plugin.Common; using ServerManagerTool.Utils; using ServerManagerTool.Windows; using System; +using System.Collections.Generic; using System.ComponentModel; using System.Diagnostics; using System.IO; @@ -38,7 +39,7 @@ namespace ServerManagerTool public event PropertyChangedEventHandler PropertyChanged; - private CancellationTokenSource _tokenSource; + private CancellationTokenSource _tokenSourceDiscordBot; private GlobalizedApplication _globalizer; private bool _applicationStarted; private string _args; @@ -49,7 +50,45 @@ namespace ServerManagerTool public App() { if (string.IsNullOrWhiteSpace(Config.Default.ASMUniqueKey)) + { Config.Default.ASMUniqueKey = Guid.NewGuid().ToString(); + } + + if (!string.IsNullOrWhiteSpace(Config.Default.DataDir)) + { + var root = Path.GetPathRoot(Config.Default.DataDir); + if (!root.EndsWith("\\")) + { + Config.Default.DataDir = Config.Default.DataDir.Replace(root, root + "\\"); + } + } + + if (!string.IsNullOrWhiteSpace(Config.Default.ConfigDirectory)) + { + var root = Path.GetPathRoot(Config.Default.ConfigDirectory); + if (!root.EndsWith("\\")) + { + Config.Default.ConfigDirectory = Config.Default.ConfigDirectory.Replace(root, root + "\\"); + } + } + + if (!string.IsNullOrWhiteSpace(Config.Default.BackupPath)) + { + var root = Path.GetPathRoot(Config.Default.BackupPath); + if (!root.EndsWith("\\")) + { + Config.Default.BackupPath = Config.Default.BackupPath.Replace(root, root + "\\"); + } + } + + if (!string.IsNullOrWhiteSpace(Config.Default.AutoUpdate_CacheDir)) + { + var root = Path.GetPathRoot(Config.Default.AutoUpdate_CacheDir); + if (!root.EndsWith("\\")) + { + Config.Default.AutoUpdate_CacheDir = Config.Default.AutoUpdate_CacheDir.Replace(root, root + "\\"); + } + } App.Instance = this; ApplicationStarted = false; @@ -112,6 +151,18 @@ namespace ServerManagerTool } } + public bool DiscordBotStarted + { + get + { + return _tokenSourceDiscordBot != null; + } + set + { + OnPropertyChanged(); + } + } + public string Title { get @@ -246,6 +297,14 @@ namespace ServerManagerTool CommonConfig.Default.Save(); } } + if (!Config.Default.DiscordBotPrefixFixed) + { + if (!Config.Default.DiscordBotPrefix.EndsWith("!")) + Config.Default.DiscordBotPrefix += "!"; + Config.Default.DiscordBotPrefixFixed = true; + Config.Default.Save(); + Config.Default.Reload(); + } Config.Default.SteamCmdRedirectOutput = false; } @@ -451,21 +510,7 @@ namespace ServerManagerTool if (Config.Default.DiscordBotEnabled) { - _tokenSource = new CancellationTokenSource(); - - Task discordTask = Task.Run(async () => - { - await ServerManagerBotFactory.GetServerManagerBot()?.StartAsync(Config.Default.DiscordBotToken, Config.Default.DiscordBotPrefix, Config.Default.DataDir, DiscordBotHelper.HandleDiscordCommand, DiscordBotHelper.HandleTranslation, _tokenSource.Token); - }, _tokenSource.Token) - .ContinueWith(t => { - var message = t.Exception.InnerException is null ? t.Exception.Message : t.Exception.InnerException.Message; - if (message.StartsWith("#")) - { - message = _globalizer.GetResourceString(message.Substring(1)) ?? message.Substring(1); - } - - MessageBox.Show(message, _globalizer.GetResourceString("DiscordBot_ErrorTitle"), MessageBoxButton.OK, MessageBoxImage.Error); - }, TaskContinuationOptions.OnlyOnFaulted); + StartDiscordBot(); } } @@ -507,11 +552,7 @@ namespace ServerManagerTool private void ShutDownApplication() { - if (!(_tokenSource is null)) - { - _tokenSource.Cancel(); - _tokenSource.Dispose(); - } + StopDiscordBot(); if (ApplicationStarted) { @@ -544,13 +585,68 @@ namespace ServerManagerTool var installFolder = Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location); var backupFolder = includeBackup - ? IOUtils.NormalizePath(string.IsNullOrWhiteSpace(Config.Default.BackupPath) + ? string.IsNullOrWhiteSpace(Config.Default.BackupPath) ? Path.Combine(Config.Default.DataDir, Config.Default.BackupDir) - : Path.Combine(Config.Default.BackupPath)) + : Path.Combine(Config.Default.BackupPath) : null; SettingsUtils.BackupUserConfigSettings(Config.Default, "userconfig.json", installFolder, backupFolder); SettingsUtils.BackupUserConfigSettings(CommonConfig.Default, "commonconfig.json", installFolder, backupFolder); } + + public void StartDiscordBot() + { + if (_tokenSourceDiscordBot != null) + { + return; + } + + _tokenSourceDiscordBot = new CancellationTokenSource(); + DiscordBotStarted = true; + + Task discordTask = Task.Run(async () => + { + var discordWhiteList = new List(); + if (Config.Default.DiscordBotWhitelist != null) + { + discordWhiteList.AddRange(Config.Default.DiscordBotWhitelist.Cast()); + } + + await ServerManagerBotFactory.GetServerManagerBot()?.StartAsync(Config.Default.DiscordBotLogLevel, Config.Default.DiscordBotToken, Config.Default.DiscordBotPrefix, Config.Default.DataDir, Config.Default.DiscordBotAllowAllBots, discordWhiteList, DiscordBotHelper.HandleDiscordCommand, DiscordBotHelper.HandleTranslation, _tokenSourceDiscordBot.Token); + + if (_tokenSourceDiscordBot != null) + { + // cleanup the token + _tokenSourceDiscordBot.Dispose(); + _tokenSourceDiscordBot = null; + } + DiscordBotStarted = false; + }, _tokenSourceDiscordBot.Token) + .ContinueWith(t => { + var message = t.Exception.InnerException is null ? t.Exception.Message : t.Exception.InnerException.Message; + if (message.StartsWith("#")) + { + message = _globalizer.GetResourceString(message.Substring(1)) ?? message.Substring(1); + } + + MessageBox.Show(message, _globalizer.GetResourceString("DiscordBot_ErrorTitle"), MessageBoxButton.OK, MessageBoxImage.Error); + + if (_tokenSourceDiscordBot != null) + { + // cleanup the token + _tokenSourceDiscordBot.Dispose(); + _tokenSourceDiscordBot = null; + } + DiscordBotStarted = false; + }, TaskContinuationOptions.OnlyOnFaulted); + } + + public void StopDiscordBot() + { + if (!(_tokenSourceDiscordBot is null)) + { + _tokenSourceDiscordBot.Cancel(); + } + } } } diff --git a/src/ARKServerManager/Config.Designer.cs b/src/ARKServerManager/Config.Designer.cs index 5f9d64bb..ae20bf76 100644 --- a/src/ARKServerManager/Config.Designer.cs +++ b/src/ARKServerManager/Config.Designer.cs @@ -2827,7 +2827,7 @@ namespace ServerManagerTool { [global::System.Configuration.UserScopedSettingAttribute()] [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] - [global::System.Configuration.DefaultSettingValueAttribute("asm")] + [global::System.Configuration.DefaultSettingValueAttribute("asm!")] public string DiscordBotPrefix { get { return ((string)(this["DiscordBotPrefix"])); @@ -2986,5 +2986,63 @@ namespace ServerManagerTool { this["ServerMonitorWindow_Top"] = value; } } + + [global::System.Configuration.UserScopedSettingAttribute()] + [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] + [global::System.Configuration.DefaultSettingValueAttribute("\r\n")] + public global::System.Collections.Specialized.StringCollection DiscordBotWhitelist { + get { + return ((global::System.Collections.Specialized.StringCollection)(this["DiscordBotWhitelist"])); + } + set { + this["DiscordBotWhitelist"] = value; + } + } + + [global::System.Configuration.UserScopedSettingAttribute()] + [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] + [global::System.Configuration.DefaultSettingValueAttribute("False")] + public bool DiscordBotPrefixFixed { + get { + return ((bool)(this["DiscordBotPrefixFixed"])); + } + set { + this["DiscordBotPrefixFixed"] = value; + } + } + + [global::System.Configuration.UserScopedSettingAttribute()] + [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] + [global::System.Configuration.DefaultSettingValueAttribute("Info")] + public global::ServerManagerTool.DiscordBot.Enums.LogLevel DiscordBotLogLevel { + get { + return ((global::ServerManagerTool.DiscordBot.Enums.LogLevel)(this["DiscordBotLogLevel"])); + } + set { + this["DiscordBotLogLevel"] = value; + } + } + + [global::System.Configuration.ApplicationScopedSettingAttribute()] + [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] + [global::System.Configuration.DefaultSettingValueAttribute("10")] + public int DiscordBotStatusCheckTime { + get { + return ((int)(this["DiscordBotStatusCheckTime"])); + } + } + + [global::System.Configuration.UserScopedSettingAttribute()] + [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] + [global::System.Configuration.DefaultSettingValueAttribute("False")] + public bool DiscordBotAllowAllBots { + get { + return ((bool)(this["DiscordBotAllowAllBots"])); + } + set { + this["DiscordBotAllowAllBots"] = value; + } + } } } diff --git a/src/ARKServerManager/Config.settings b/src/ARKServerManager/Config.settings index c11d5ae4..6f6869f6 100644 --- a/src/ARKServerManager/Config.settings +++ b/src/ARKServerManager/Config.settings @@ -783,7 +783,7 @@ False - asm + asm! @@ -824,5 +824,21 @@ 50 + + <?xml version="1.0" encoding="utf-16"?> +<ArrayOfString xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" /> + + + False + + + Info + + + 10 + + + False + \ No newline at end of file diff --git a/src/ARKServerManager/Enums/ServerProcessType.cs b/src/ARKServerManager/Enums/ServerProcessType.cs index 7298dfd9..cced8642 100644 --- a/src/ARKServerManager/Enums/ServerProcessType.cs +++ b/src/ARKServerManager/Enums/ServerProcessType.cs @@ -8,8 +8,9 @@ AutoShutdown1, AutoShutdown2, Backup, - Shutdown, Restart, + Shutdown, + Stop, Update, } } diff --git a/src/ARKServerManager/GameData/SurvivalEvolved.gamedata b/src/ARKServerManager/GameData/SurvivalEvolved.gamedata index 55d5a720..3c7e8e42 100644 --- a/src/ARKServerManager/GameData/SurvivalEvolved.gamedata +++ b/src/ARKServerManager/GameData/SurvivalEvolved.gamedata @@ -13406,6 +13406,10 @@ { "ModId": "CrystalIsles", "ModName": "Crystal Isles" + }, + { + "ModId": "LostIsland", + "ModName": "Lost Island" } ] } \ No newline at end of file diff --git a/src/ARKServerManager/Globalization/en-US/en-US.xaml b/src/ARKServerManager/Globalization/en-US/en-US.xaml index c59fab28..d905aa52 100644 --- a/src/ARKServerManager/Globalization/en-US/en-US.xaml +++ b/src/ARKServerManager/Globalization/en-US/en-US.xaml @@ -83,12 +83,21 @@ All - + Normal Minimized Maximized + + Critical + Error + Warning + Info + Verbose + Debug + + Run as Administrator This application requires administration priviledges to access ALL functionality. Would you like to Run as Administrator? @@ -127,6 +136,7 @@ Server download complete Complete Cancelled + Failed Cancel @@ -621,8 +631,17 @@ The id of the discord server the bot will listen to. Prefix: The prefix that must be used when sending a command via discord. + Log Level: Get Token... Help... + Allow All Bots + If enabled, the server manager bot will respond to all other bots, otherwise they will be ignored unless they are in the whitelist. + Bot Whitelist + Bot ID + The id of the bot to whitelist. + Add Whitelist + Clear Whitelists + Delete Whitelist If enabled, the backup command can be sent from discord. If enabled, the restart command can be sent from discord. If enabled, the shutdown command can be sent from discord. @@ -773,12 +792,18 @@ Enable Enable the auto-update scheduled task Next Run Time: + Discord Bot: + Start + Start the discord bot + Stop + Stop the discord bot Unknown Disabled Queued Ready Running + Stopped Profile failed to load The profile at {0} failed to load. The error was: {1}\r\n{2} @@ -1207,6 +1232,8 @@ Discord Bot Details Channel Id: The id of the discord server channel this profile will listen to. + Alias: + A unique name to identify your server when using the discord commands, can be used instead of the profile id. Allow Backup If enabled, the profile will listen for backup commands from discord. Allow Restart @@ -5495,6 +5522,23 @@ Boss Inventory - Crystal Wyvern Queen (Gamma) Boss Inventory - Crystal Wyvern Queen (Beta) Boss Inventory - Crystal Wyvern Queen (Alpha) + + Boss Inventory - Dragon (Gamma) + Boss Inventory - Dragon (Beta) + Boss Inventory - Dragon (Alpha) + Boss Inventory - Manticore (Gamma) + Boss Inventory - Manticore (Beta) + Boss Inventory - Manticore (Alpha) + + Boss Inventory - Dragon (Easy) + Boss Inventory - Dragon (Medium) + Boss Inventory - Dragon (Hard) + Boss Inventory - Manticore (Easy) + Boss Inventory - Manticore (Medium) + Boss Inventory - Manticore (Hard) + Boss Inventory - Megapithecus (Easy) + Boss Inventory - Megapithecus (Medium) + Boss Inventory - Megapithecus (Hard) @@ -5580,8 +5624,9 @@ Another command '{0}' is currently running against profile '{1}'. Command '{0}' has been disabled for profile '{1}'. - The '{0}' command requires a profile id. + The '{0}' command requires a profile id or alias. Profile '{0}' was not found or is not associated with the channel. + Multiple profiles with '{0}' were found in the channel, command aborted. Profile '{0}' is in a state '{1}' that cannot run this command. Profile '{0}' is currently being updated. diff --git a/src/ARKServerManager/Globalization/fr-FR/fr-FR.xaml b/src/ARKServerManager/Globalization/fr-FR/fr-FR.xaml index 89e0c5d0..35f838cb 100644 --- a/src/ARKServerManager/Globalization/fr-FR/fr-FR.xaml +++ b/src/ARKServerManager/Globalization/fr-FR/fr-FR.xaml @@ -24,6 +24,7 @@ Genesis Crystal Isles Genesis: Part 2 + Lost Island @@ -44,8 +45,8 @@ - All - Unknown + Tout + Inconnu ARK Prime The Center Primitive Plus @@ -56,6 +57,7 @@ Genesis Crystal Isles Genesis: Part 2 + Lost Island PGM @@ -610,6 +612,23 @@ Ce message sera affiché lorsque la fermeture du serveur a été annulée. Afficher le motif d'arrêt avec TOUS les messages d'arrêt Si elle est activée, la raison de l'arrêt s'affichera avec tous les messages d'arrêt; sinon, il ne sera affiché qu'au début de l'arrêt du serveur. + + Activer le Bot Discord + Vous devrez redémarrer le gestionnaire de serveur si vous modifiez les paramètres du Bot Discord.. + Token: + Le token associé au bot discord. + ID Serveur: + L'ID du serveur que le Bot discord écoutera. + Prefixe: + Le préfixe qui doit être utilisé lors de l'envoi d'une commande via discord. + Faire desToken... + Help... + Si activé, la commande de sauvegarde peut être envoyée du Discord. + Si activé, la commande de redémarrage peut être envoyée du discord. + Si activé, la commande d'arrêt peut être envoyée du discord. + Si activé, la commande de démarrage peut être envoyée du discord. + Si activé, la commande stop peut être envoyée du discord. + Si activé, la commande de mise à jour peut être envoyée du discord. Paramètres de messagerie SMTP Host: @@ -662,6 +681,8 @@ La modification du répertoire de données déplacera tous les profils existants vers le nouvel emplacement, mais ne déplacera aucune installation de serveur. Voulez-vous toujours changer ce répertoire? Échec de la modification du répertoire de données Une erreur s'est produite lors de la modification du répertoire de données: {0}\r\nVeuillez corriger l'erreur et réessayer, ou contactez le support technique pour obtenir de l'aide. + Confirmer la réinitialisation du répertoire de données + Cliquez sur "Oui" pour confirmer que vous souhaitez réinitialiser l'emplacement du répertoire de données. Une fois réinitialisé, le gestionnaire de serveur va arrêter et vous devrez redémarrer. Sélectionnez le répertoire de sauvegarde Sélectionnez le répertoire du cache @@ -775,6 +796,9 @@ Erreur lors de la réinstallation de SteamCMD Une erreur est survenue lors de la réinstallation de SteamCMD. Cela a laissé SteamCmd dans un état instable, essayez de le réinstaller à nouveau ou signalez-le.\r\nException: {0} + Discord Bot Exécute des commandes + Le Bot discord à une ou plusieurs commandes en cours d'exécution, voulez-vous continuer à arrêter le gestionnaire de serveur? + Confirmer le démarage du serveur Vous êtes sur le point de démarrer le serveur, voulez-vous continuer? Confirmer l'arrêt du serveur @@ -1178,6 +1202,24 @@ Redémarrer le serveur en cas d'arrêt Si cette option est activée, le serveur sera redémarré même s'il est arrêté pour les redémarrages automatiques et les mises à jour automatiques. + + + Détail du bot Discord + Id DU canal: + L'ID du canal de Discord Server que ce profil écoutera. + Autoriser la sauvegarde + Si activé, le profil écoutera les commandes de sauvegarde de discord. + Autoriser le redémarrage + Si activé, le profil écoutera les commandes de redémarrage de discord. + Autoriser l'arrêt + Si activé, le profil écoutera les commandes d'arrêt de discord. + Autoriser le démarrage + Si activé, le profil écoutera les commandes de démarrage de discord. + Autoriser l'arrêt + Si activé, le profil écoutera les commandes d'arrêt de discord. + Autoriser la mise à jour + Si activé, le profil écoutera les commandes de mise à jour de discord. + Règles @@ -1598,12 +1640,16 @@ Multiplicateurs de statistiques par niveau (apprivoisés) - Ajouter Multiplicateurs de statistiques par niveau (apprivoisés)- Affinité Multiplicateurs de statistiques par niveau (sauvage) + Boost de niveau mutagène (sauvage) + Boost de niveau mutagène (élevée) Multiplicateurs d'élevage de dinosaures Si activé, permet d'appliquer des facteurs d'échelle à chaque statistique de base. Si activé, permet d'appliquer des facteurs d'échelle à chaque statistique. Si activé, permet d'appliquer des facteurs d'échelle à l'ajout d'apprivoisement pour chaque statistique. Si activé, permet d'appliquer des facteurs d'échelle au multiplicateur d'apprivoisement pour chaque statistique. Si activé, permet d'appliquer des facteurs d'échelle à l'augmentation de la statistique apprivoisée pour chaque statistique. + Si activé, permet aux facteurs d'échelle d'être appliqués au nombre de niveaux mutagen ajoute aux Tames avec une ascendance sauvage. + Si activé, permet aux facteurs d'échelle d'être appliqués au nombre de niveaux mutagen ajoute aux Tames avec ascendance élevée. Réinitialiser tous les multiplicateurs aux valeurs par défaut. Réinitialiser tous les multiplicateurs aux valeurs par défaut. @@ -5449,6 +5495,23 @@ Boss Inventory - Crystal Wyvern Queen (Gamma) Boss Inventory - Crystal Wyvern Queen (Beta) Boss Inventory - Crystal Wyvern Queen (Alpha) + + Boss Inventory - Dragon (Gamma) + Boss Inventory - Dragon (Beta) + Boss Inventory - Dragon (Alpha) + Boss Inventory - Manticore (Gamma) + Boss Inventory - Manticore (Beta) + Boss Inventory - Manticore (Alpha) + + Boss Inventory - Dragon (Easy) + Boss Inventory - Dragon (Medium) + Boss Inventory - Dragon (Hard) + Boss Inventory - Manticore (Easy) + Boss Inventory - Manticore (Medium) + Boss Inventory - Manticore (Hard) + Boss Inventory - Megapithecus (Easy) + Boss Inventory - Megapithecus (Medium) + Boss Inventory - Megapithecus (Hard) @@ -5523,4 +5586,32 @@ Un problème est survenu lors de la mise à jour du serveur. Cela peut laisser votre serveur dans un état incomplet.\r\n\r\n Souhaitez-vous continuer le démarrage du serveur, cela pourrait provoquer des problèmes? + + Erreur Bot Discord Bot + Le bot de la discussion nécessite un Token valide afin qu'il puisse se connecter au serveur de discord\r\nthis peut être défini dans les paramètres globaux.. + Le préfixe du Bot discord contient des caractères non valides. Seules les lettres et les chiffres sont autorisés. + + Commande '{0}' n'a pas été activée. + Commande inconnue '{0}'. + Une autre commande est en cours de traitement. + Une autre commande '{0}' fonctionne actuellement contre le profil'{1}'. + Commande '{0}' a été désactivé pour le profil '{1}'. + + The '{0}' commande nécessite un ID du profil. + Profile '{0}' n'a pas été trouvé ou n'est pas associé au canal. + Profile '{0}' est dans un état '{1}' qui ne peut pas exécuter cette commande. + Profile '{0}' est actuellement mis à jour. + + Appeler au serveur '{0}' a échoué. + Une demande de sauvegarde pour serveur '{0}' a été envoyée. + Une demande de redémarrage de serveur '{0}' a été envoyée. + Une demande d'arrêt pour serveur '{0}' a été envoyée. + Une demande de démarrage pour le serveur '{0}' a été envoyée. + Une demande d'arrêt de serveur '{0}' a été envoyée. + Une demande de mise à jour du serveur '{0}' a été envoyée. + + Dénombrer: + Map: + + \ No newline at end of file diff --git a/src/ARKServerManager/Globalization/pt-BR/pt-BR.xaml b/src/ARKServerManager/Globalization/pt-BR/pt-BR.xaml index beb43096..e7e3acdb 100644 --- a/src/ARKServerManager/Globalization/pt-BR/pt-BR.xaml +++ b/src/ARKServerManager/Globalization/pt-BR/pt-BR.xaml @@ -24,6 +24,7 @@ Genesis: Part 1 Crystal Isles Genesis: Part 2 + Lost Island @@ -56,6 +57,7 @@ Genesis: Part 1 Crystal Isles Genesis: Part 2 + Lost Island PGM @@ -81,10 +83,19 @@ Todos - + Normal Minimizado Maximizado + + + + Crítico + Erro + Aviso + Informações + Verboso + Debug @@ -125,6 +136,7 @@ Download do servidor concluído Completo Cancelado + Falhou Cancela @@ -619,8 +631,15 @@ O id do servidor discord que o bot usará. Prefix: O prefixo que deve ser usado ao enviar um comando via discord. + Nível de registro: Obter Token... Ajuda... + Bot Whitelist + Bot ID + O id do bot a ser colocado na whitelist. + Add Whitelist + Limpar Whitelists + Deletar Whitelist Se ativado, o comando de backup pode ser enviado do discord. Se ativado, o comando de reiniciar pode ser enviado do discord. Se habilitado, o comando de desligar pode ser enviado do discord. @@ -1205,6 +1224,8 @@ Detalhes do Discord Bot Id do canal: O id do canal do discord que este perfil irá usar. + Nome exclusivo: + Um nome exclusivo para identificar seu servidor ao usar os comandos discord, pode ser usado em vez do ID do perfil. Permitir backup Se ativado, o perfil usará os comandos de backup pelo discord. Permitir Reinício @@ -5504,6 +5525,10 @@ Boss Inventory - Crystal Wyvern Queen (Beta) Boss Inventory - Crystal Wyvern Queen (Alpha) + Boss Inventory - Dinopithecus (Gamma) + Boss Inventory - Dinopithecus (Beta) + Boss Inventory - Dinopithecus (Alpha) + Boss Inventory - Dragon (Gamma) Boss Inventory - Dragon (Beta) Boss Inventory - Dragon (Alpha) @@ -5597,6 +5622,7 @@ O comando '{0}' requer um id de perfil. O perfil '{0}' não foi encontrado ou não está associado ao canal. + Vários perfis com '{0}' foram encontrados no canal, comando abortado. O perfil '{0}' está em um estado '{1}' que não pode executar este comando. O perfil '{0}' está sendo atualizado. diff --git a/src/ARKServerManager/Globalization/ru-RU/ru-RU.xaml b/src/ARKServerManager/Globalization/ru-RU/ru-RU.xaml index 63d3f224..c4328ce3 100644 --- a/src/ARKServerManager/Globalization/ru-RU/ru-RU.xaml +++ b/src/ARKServerManager/Globalization/ru-RU/ru-RU.xaml @@ -9,7 +9,7 @@ Последнее обновление: - 09.12.2021 от Varlonec, Эдван + 16.12.2021 от Varlonec, Эдван Ошибка @@ -24,6 +24,7 @@ Genesis: Part 1 Crystal Isles Genesis: Part 2 + Lost Island @@ -56,6 +57,7 @@ Genesis: Part 1 Crystal Isles Genesis: Part 2 + Lost Island PGM @@ -87,6 +89,15 @@ По максимуму + + Критично + Ошибка + Внимание + Информация + Многословный + Отладка + + Запустить в режиме Администратора. Это приложение требует Административных привилегий для доступа ко всем функциям. Вы хотите запустить как из режима Администратора? @@ -125,6 +136,7 @@ Загрузка сервера завершена Завершено Отменено + Ошибка Отмена @@ -619,8 +631,15 @@ Идентификатор сервера Discord, который бот будет слушать. Префикс: Префикс, который необходимо использовать при отправке команды через Discord. + Лог уровня: Получить токен ... Помощь... + Белый лист бота + Бот ID + ID бота для внесения в белый список. + Добавить в белый лист + Очистить белый лист + Удалить белый лист Если включено, команду резервного копирования можно отправить из Discord. Если этот параметр включен, команда перезапуска может быть отправлена из Discord. Если включено, команду выключения можно отправить из Discord. @@ -1205,6 +1224,8 @@ Детали бота Discord Канал Id: ID канала сервера Discord, который будет слушать этот профиль. + Псевдоним: + Вместо идентификатора профиля можно использовать уникальное имя для идентификации вашего сервера при использовании команд discord. Разрешить Бекап Если этот параметр включен, профиль будет прослушивать команды Бекапа от Discord. Разрешить Перезагрузку @@ -1255,7 +1276,7 @@ Увеличение множителя позволяет размещать структуры дальше от края платформы. Макс количество ворот на седлах-платформах Ограничивает количество ворот, которые можно разместить на седлах-платформах. Этот вариант не имеет обратной силы, поэтому применение этого изменения потребует некоторой ручной административной работы, чтобы очистить все существующие платформы, превышающие лимит. - + Включить перераспределение сложности Если включено, это позволит изменить максимальный уровень существ в мире. Максимальный уровень динозавров: @@ -1264,7 +1285,7 @@ Определяет максимальные уровни существ в мире. Чем выше значение, тем выше максимальный уровень существа. Destroy Tames Over Level: Tames that exceed this level will be deleted on server start. To disable deletion set to 0. - + Вкл загрузку из данных кластера Если включено, это позволит загружать выживших/предметов/динозавров на сервер. Откл загрузку выживших на сервер @@ -5678,6 +5699,7 @@ Для команды '{0}' требуется идентификатор профиля. Профиль '{0}' не найден или не связан с каналом. + В канале было обнаружено несколько профилей с "{0}", команда прервана. Профиль '{0}' находится в состоянии '{1}', и эта команда не может быть запущена. Профиль '{0}' в настоящее время обновляется. diff --git a/src/ARKServerManager/Globalization/zh-CN/zh-CN.xaml b/src/ARKServerManager/Globalization/zh-CN/zh-CN.xaml index 8a40b837..db610027 100644 --- a/src/ARKServerManager/Globalization/zh-CN/zh-CN.xaml +++ b/src/ARKServerManager/Globalization/zh-CN/zh-CN.xaml @@ -9,7 +9,7 @@ 翻译: - 2021.12.09(龍柒) + 2021.12.13(龍柒) 错误 @@ -24,6 +24,7 @@ 创世纪 1(Genesis: Part 1) 创世纪 2(Genesis: Part 2) 水晶岛(Crystal Isles) + 迷失岛(Lost Island) @@ -57,6 +58,7 @@ 创世纪 2(Genesis: Part 2) 水晶岛(Crystal Isles) 程序生成地图(PGM) + 迷失岛(Lost Island) 瓦尔盖罗(Valguero) @@ -103,8 +105,8 @@ 选择一个数据目录 确认位置 方舟服务器管理工具将存储配置文件和steamcmd以下目录:\r\n\r\n配置文件: {0}\r\nSteamCMD: {1}\r\n\r\n这样可以吗? - 数据目录选择错误 - 您选择的目录在服务器管理器安装文件夹中。 不建议这样做,请选择另一个文件夹。 + 数据目录选择错误 + 您选择的目录在服务器管理器安装文件夹中。 不建议这样做,请选择另一个文件夹。 保存配置文件失败 保存配置文件失败 {0}. {1}\n{2} @@ -383,9 +385,9 @@ 该文件无效。 它可能包含错误或格式不正确。 选择游戏数据文件 - 游戏数据 + gamedata 游戏数据文件 - *.游戏数据 + *.gamedata @@ -3002,6 +3004,22 @@ 罗克韦尔节点 BOSS(困难) + + 阿马加龙 + 中华大眼翼龙 + 恐狒 + + + + BOSS-恐狒 + BOSS-恐狒(简单) + BOSS-恐狒(中等) + BOSS-恐狒(困难) + 狒狒BOSS爪牙[阿马加龙] + 狒狒BOSS爪牙 + 狒狒BOSS爪牙[中华大眼翼龙] + + @@ -3768,6 +3786,10 @@ 脊颌翼龙的鞍 + + 阿马加龙鞍 + + @@ -5236,6 +5258,16 @@ 植物R-5 种子(优质生鱼肉) + + 阿马加龙鞍 + 恐狒皮肤 + 阿马加龙尖刺 + 恐狒BOSS战利品(迷失岛) + 恐狒BOSS战利品(简单)(迷失岛) + 恐狒BOSS战利品(中等)(迷失岛) + 恐狒BOSS战利品(困难)(迷失岛) + + 恐龙洗点卡 泰克一键解锁 @@ -5757,9 +5789,9 @@ 极品飞艇(简单){任务} 极品飞艇(中等){任务} 极品飞艇(困难){任务} - 资源包1级 - 资源包2级 - 资源包3级 + 一等战利品箱 + 二等战利品箱 + 三等战利品箱 空投_白色(创世2) 空投_绿色(创世2) @@ -5814,6 +5846,7 @@ Boss 清单 - (水晶岛)水晶女王 (简单) Boss 清单 - (水晶岛)水晶女王 (中等) Boss 清单 - (水晶岛)水晶女王 (困难) + 水晶飞龙 Boss 清单 - 喷火龙 (简单) Boss 清单 - 喷火龙 (中等) diff --git a/src/ARKServerManager/Lib/GameData.cs b/src/ARKServerManager/Lib/GameData.cs index 2f0117fe..a904a434 100644 --- a/src/ARKServerManager/Lib/GameData.cs +++ b/src/ARKServerManager/Lib/GameData.cs @@ -52,7 +52,7 @@ namespace ServerManagerTool.Lib items = gameData.Items.ConvertAll(item => new PrimalItem { ClassName = item.ClassName, Mod = item.Mod, KnownItem = true, Category = item.Category }).ToArray(); // resources - resourceMultipliers = gameData.Items.Where(item => item.IsHarvestable).ToList().ConvertAll(item => new ResourceClassMultiplier { ClassName = item.ClassName, Mod = item.Mod, KnownResource = true }).ToArray(); + resourceMultipliers = gameData.Items.Where(item => item.IsHarvestable).Select(item => new ResourceClassMultiplier { ClassName = item.ClassName, Mod = item.Mod, KnownResource = true }).ToArray(); // map spawners gameData.MapSpawners.AddRange(userGameData.MapSpawners); @@ -77,9 +77,9 @@ namespace ServerManagerTool.Lib if (gameData.GameMaps.Count > 0) { var maps1 = gameMaps.ToList(); - maps1.AddRange(gameData.GameMaps.Where(item => !item.IsSotF).ToList().ConvertAll(item => new ComboBoxItem { ValueMember = item.ClassName, DisplayMember = item.Description })); + maps1.AddRange(gameData.GameMaps.Where(item => !item.IsSotF).Select(item => new ComboBoxItem { ValueMember = item.ClassName, DisplayMember = item.Description })); var maps2 = gameMapsSotF.ToList(); - maps2.AddRange(gameData.GameMaps.Where(item => item.IsSotF).ToList().ConvertAll(item => new ComboBoxItem { ValueMember = item.ClassName, DisplayMember = item.Description })); + maps2.AddRange(gameData.GameMaps.Where(item => item.IsSotF).Select(item => new ComboBoxItem { ValueMember = item.ClassName, DisplayMember = item.Description })); gameMaps = maps1.ToArray(); gameMapsSotF = maps2.ToArray(); @@ -91,9 +91,9 @@ namespace ServerManagerTool.Lib if (gameData.TotalConversions.Count > 0) { var mods1 = totalConversions.ToList(); - mods1.AddRange(gameData.TotalConversions.Where(item => !item.IsSotF).ToList().ConvertAll(item => new ComboBoxItem { ValueMember = item.ClassName, DisplayMember = item.Description })); + mods1.AddRange(gameData.TotalConversions.Where(item => !item.IsSotF).Select(item => new ComboBoxItem { ValueMember = item.ClassName, DisplayMember = item.Description })); var mods2 = totalConversionsSotF.ToList(); - mods2.AddRange(gameData.TotalConversions.Where(item => item.IsSotF).ToList().ConvertAll(item => new ComboBoxItem { ValueMember = item.ClassName, DisplayMember = item.Description })); + mods2.AddRange(gameData.TotalConversions.Where(item => item.IsSotF).Select(item => new ComboBoxItem { ValueMember = item.ClassName, DisplayMember = item.Description })); totalConversions = mods1.ToArray(); totalConversionsSotF = mods2.ToArray(); @@ -130,9 +130,9 @@ namespace ServerManagerTool.Lib if (gameData.Branches.Count > 0) { var branches1 = branches.ToList(); - branches1.AddRange(gameData.Branches.Where(item => !item.IsSotF).ToList().ConvertAll(item => new ComboBoxItem { ValueMember = item.BranchName, DisplayMember = item.Description })); + branches1.AddRange(gameData.Branches.Where(item => !item.IsSotF).Select(item => new ComboBoxItem { ValueMember = item.BranchName, DisplayMember = item.Description })); var branches2 = branchesSotF.ToList(); - branches2.AddRange(gameData.Branches.Where(item => item.IsSotF).ToList().ConvertAll(item => new ComboBoxItem { ValueMember = item.BranchName, DisplayMember = item.Description })); + branches2.AddRange(gameData.Branches.Where(item => item.IsSotF).Select(item => new ComboBoxItem { ValueMember = item.BranchName, DisplayMember = item.Description })); branches = branches1.ToArray(); branchesSotF = branches2.ToArray(); @@ -144,9 +144,9 @@ namespace ServerManagerTool.Lib if (gameData.Events.Count > 0) { var events1 = events.ToList(); - events1.AddRange(gameData.Events.Where(item => !item.IsSotF).ToList().ConvertAll(item => new ComboBoxItem { ValueMember = item.EventName, DisplayMember = item.Description })); + events1.AddRange(gameData.Events.Where(item => !item.IsSotF).Select(item => new ComboBoxItem { ValueMember = item.EventName, DisplayMember = item.Description })); var events2 = eventsSotF.ToList(); - events2.AddRange(gameData.Events.Where(item => item.IsSotF).ToList().ConvertAll(item => new ComboBoxItem { ValueMember = item.EventName, DisplayMember = item.Description })); + events2.AddRange(gameData.Events.Where(item => item.IsSotF).Select(item => new ComboBoxItem { ValueMember = item.EventName, DisplayMember = item.Description })); events = events1.ToArray(); eventsSotF = events2.ToArray(); @@ -157,7 +157,7 @@ namespace ServerManagerTool.Lib if (gameData.OfficialMods.Count > 0) { - ModUtils.AddOfficialMods(gameData.OfficialMods.Where(m => !string.IsNullOrWhiteSpace(m.ModId)).Select(m => m.ModId)); + ModUtils.AddOfficialMods(gameData.OfficialMods.Where(m => !string.IsNullOrWhiteSpace(m.ModId)).Select(m => m.ModId).ToList()); } } diff --git a/src/ARKServerManager/Lib/Model/EngramAutoUnlock.cs b/src/ARKServerManager/Lib/Model/EngramAutoUnlock.cs index 92c836c2..588ef774 100644 --- a/src/ARKServerManager/Lib/Model/EngramAutoUnlock.cs +++ b/src/ARKServerManager/Lib/Model/EngramAutoUnlock.cs @@ -17,15 +17,13 @@ namespace ServerManagerTool.Lib public override void FromIniValues(IEnumerable iniValues) { - var items = iniValues?.Select(AggregateIniValue.FromINIValue).ToArray(); + var items = iniValues?.Select(AggregateIniValue.FromINIValue); Clear(); - var itemsToAdd = items.Where(i => !this.Any(e => e.IsEquivalent(i))).ToArray(); - AddRange(itemsToAdd); + AddRange(items.Where(i => !this.Any(e => e.IsEquivalent(i)))); - var itemsToUpdate = items.Where(i => this.Any(e => e.IsEquivalent(i))).ToArray(); - foreach (var item in itemsToUpdate) + foreach (var item in items.Where(i => this.Any(e => e.IsEquivalent(i)))) { var e = this.FirstOrDefault(r => r.IsEquivalent(item)); e.LevelToAutoUnlock = item.LevelToAutoUnlock; diff --git a/src/ARKServerManager/Lib/Model/EngramEntry.cs b/src/ARKServerManager/Lib/Model/EngramEntry.cs index f446055c..611a52f3 100644 --- a/src/ARKServerManager/Lib/Model/EngramEntry.cs +++ b/src/ARKServerManager/Lib/Model/EngramEntry.cs @@ -18,15 +18,13 @@ namespace ServerManagerTool.Lib public override void FromIniValues(IEnumerable iniValues) { - var items = iniValues?.Select(AggregateIniValue.FromINIValue).ToArray(); + var items = iniValues?.Select(AggregateIniValue.FromINIValue); Clear(); - var itemsToAdd = items.Where(i => !this.Any(e => e.IsEquivalent(i))).ToArray(); - AddRange(itemsToAdd); + AddRange(items.Where(i => !this.Any(e => e.IsEquivalent(i)))); - var itemsToUpdate = items.Where(i => this.Any(e => e.IsEquivalent(i))).ToArray(); - foreach (var item in itemsToUpdate) + foreach (var item in items.Where(i => this.Any(e => e.IsEquivalent(i)))) { var e = this.FirstOrDefault(r => r.IsEquivalent(item)); e.EngramLevelRequirement = item.EngramLevelRequirement; diff --git a/src/ARKServerManager/Lib/Model/Level.cs b/src/ARKServerManager/Lib/Model/Level.cs index 4c243b1d..40959641 100644 --- a/src/ARKServerManager/Lib/Model/Level.cs +++ b/src/ARKServerManager/Lib/Model/Level.cs @@ -61,7 +61,7 @@ namespace ServerManagerTool.Lib int index = 0; int xpTotal = 0; int engramTotal = 0; - foreach (var existingLevel in this.OrderBy(l => l.XPRequired).ToArray()) + foreach (var existingLevel in this.OrderBy(l => l.XPRequired)) { xpTotal += existingLevel.XPRequired; engramTotal += existingLevel.EngramPoints; diff --git a/src/ARKServerManager/Lib/Model/NPCSpawner.cs b/src/ARKServerManager/Lib/Model/NPCSpawner.cs index 61922efc..5784f182 100644 --- a/src/ARKServerManager/Lib/Model/NPCSpawner.cs +++ b/src/ARKServerManager/Lib/Model/NPCSpawner.cs @@ -6,7 +6,6 @@ using ServerManagerTool.Enums; using ServerManagerTool.Interface; using System; using System.Collections.Generic; -using System.ComponentModel; using System.Linq; using System.Runtime.Serialization; using System.Text; diff --git a/src/ARKServerManager/Lib/Model/PreventTransferOverride.cs b/src/ARKServerManager/Lib/Model/PreventTransferOverride.cs index 29600480..ef151823 100644 --- a/src/ARKServerManager/Lib/Model/PreventTransferOverride.cs +++ b/src/ARKServerManager/Lib/Model/PreventTransferOverride.cs @@ -14,11 +14,9 @@ namespace ServerManagerTool.Lib { } - public string[] RenderToView() + public IEnumerable RenderToView() { - List errors = new List(); - - return errors.ToArray(); + return new List(); } public void RenderToModel() diff --git a/src/ARKServerManager/Lib/Model/ResourceClassMultiplier.cs b/src/ARKServerManager/Lib/Model/ResourceClassMultiplier.cs index b01adbde..5183d1e1 100644 --- a/src/ARKServerManager/Lib/Model/ResourceClassMultiplier.cs +++ b/src/ARKServerManager/Lib/Model/ResourceClassMultiplier.cs @@ -1,5 +1,4 @@ using ServerManagerTool.Common.Model; -using ServerManagerTool.Enums; using System; using System.Collections.Generic; using System.Linq; @@ -18,22 +17,20 @@ namespace ServerManagerTool.Lib public override void FromIniValues(IEnumerable iniValues) { - var items = iniValues?.Select(AggregateIniValue.FromINIValue).ToArray(); + var items = iniValues?.Select(AggregateIniValue.FromINIValue); Clear(); if (this._resetFunc != null) this.AddRange(this._resetFunc()); - var itemsToAdd = items.Where(i => !this.Any(r => r.IsEquivalent(i))).ToArray(); - AddRange(itemsToAdd); + AddRange(items.Where(i => !this.Any(r => r.IsEquivalent(i)))); - var itemsToUpdate = items.Where(i => this.Any(r => r.IsEquivalent(i))).ToArray(); - foreach (var item in itemsToUpdate) + foreach (var item in items.Where(i => this.Any(r => r.IsEquivalent(i)))) { this.FirstOrDefault(r => r.IsEquivalent(item)).Multiplier = item.Multiplier; } - IsEnabled = (items.Length > 0); + IsEnabled = (Count > 0); Sort(AggregateIniValue.SortKeySelector); } diff --git a/src/ARKServerManager/Lib/Model/StackSizeOverride.cs b/src/ARKServerManager/Lib/Model/StackSizeOverride.cs index ea6fe876..f86af661 100644 --- a/src/ARKServerManager/Lib/Model/StackSizeOverride.cs +++ b/src/ARKServerManager/Lib/Model/StackSizeOverride.cs @@ -14,7 +14,7 @@ namespace ServerManagerTool.Lib { } - public string[] RenderToView() + public IEnumerable RenderToView() { List errors = new List(); @@ -27,7 +27,7 @@ namespace ServerManagerTool.Lib } } - return errors.ToArray(); + return errors; } public void RenderToModel() diff --git a/src/ARKServerManager/Lib/Model/SupplyCrateOverride.cs b/src/ARKServerManager/Lib/Model/SupplyCrateOverride.cs index 16abf720..82104834 100644 --- a/src/ARKServerManager/Lib/Model/SupplyCrateOverride.cs +++ b/src/ARKServerManager/Lib/Model/SupplyCrateOverride.cs @@ -17,7 +17,7 @@ namespace ServerManagerTool.Lib { } - public string[] RenderToView() + public IEnumerable RenderToView() { List errors = new List(); @@ -48,7 +48,7 @@ namespace ServerManagerTool.Lib Update(); - return errors.ToArray(); + return errors; } public void RenderToModel() diff --git a/src/ARKServerManager/Lib/ServerApp.cs b/src/ARKServerManager/Lib/ServerApp.cs index 0f433ad8..a2715ef4 100644 --- a/src/ARKServerManager/Lib/ServerApp.cs +++ b/src/ARKServerManager/Lib/ServerApp.cs @@ -422,6 +422,16 @@ namespace ServerManagerTool.Lib LogProfileMessage("Starting shutdown timer..."); var minutesLeft = ShutdownInterval; + if (ServerProcess == ServerProcessType.Stop) + { + LogProfileMessage($"Server shutdown type is {ServerProcess}, shutdown timer cancelled."); + minutesLeft = 0; + } + else if (!CheckForOnlinePlayers) + { + LogProfileMessage("CheckForOnlinePlayers disabled, shutdown timer will not perform online player check."); + } + while (minutesLeft > 0) { if (cancellationToken.IsCancellationRequested) @@ -442,8 +452,8 @@ namespace ServerManagerTool.Lib { try { - var playerInfo = gameServer?.GetPlayers()?.Where(p => !string.IsNullOrWhiteSpace(p.Name?.Trim())).ToList(); - var playerCount = playerInfo?.Count ?? -1; + var playerInfo = gameServer?.GetPlayers()?.Where(p => !string.IsNullOrWhiteSpace(p.Name?.Trim())); + var playerCount = playerInfo?.Count() ?? -1; // check if anyone is logged into the server if (playerCount <= 0) @@ -462,7 +472,6 @@ namespace ServerManagerTool.Lib else { Debug.WriteLine($"CheckForOnlinePlayers disabled, shutdown timer cancelled."); - break; } var message = string.Empty; @@ -1866,7 +1875,7 @@ namespace ServerManagerTool.Lib comment.AppendLine($"PGM Server: {_profile.PGM_Enabled}"); comment.AppendLine($"Process: {ServerProcess}"); - ZipUtils.ZipFiles(backupFile, files.ToArray(), comment.ToString(), false); + ZipUtils.ZipFiles(backupFile, files, comment.ToString(), false); LogProfileMessage($"Backup file created - {backupFile}"); } @@ -2016,7 +2025,7 @@ namespace ServerManagerTool.Lib comment.AppendLine($"PGM Server: {_profile.PGM_Enabled}"); comment.AppendLine($"Process: {ServerProcess}"); - ZipUtils.ZipFiles(backupFile, files.ToArray(), comment.ToString(), false); + ZipUtils.ZipFiles(backupFile, files, comment.ToString(), false); LogProfileMessage($"Backed up world files - {saveFolder}"); LogProfileMessage($"Backup file created - {backupFile}"); @@ -2989,7 +2998,7 @@ namespace ServerManagerTool.Lib if (ExitCode == EXITCODE_NORMALEXIT) { // get the profile associated with the branch - var profiles = _profiles.Keys.Where(p => p.EnableAutoUpdate && p.BranchName.Equals(branch.BranchName, StringComparison.OrdinalIgnoreCase)).ToArray(); + var profiles = _profiles.Keys.Where(p => p.EnableAutoUpdate && p.BranchName.Equals(branch.BranchName, StringComparison.OrdinalIgnoreCase)); var profileExitCodes = new ConcurrentDictionary(); if (Config.Default.AutoUpdate_ParallelUpdate) @@ -3240,7 +3249,7 @@ namespace ServerManagerTool.Lib if (exitCode == EXITCODE_NORMALEXIT) { - var branches = _profiles.Keys.Where(p => p.EnableAutoUpdate).Select(p => BranchSnapshot.Create(p)).Distinct(new BranchSnapshotComparer()).ToArray(); + var branches = _profiles.Keys.Where(p => p.EnableAutoUpdate).Select(p => BranchSnapshot.Create(p)).Distinct(new BranchSnapshotComparer()); var exitCodes = new ConcurrentDictionary(); // update the server cache for each branch diff --git a/src/ARKServerManager/Lib/ServerPlayers.cs b/src/ARKServerManager/Lib/ServerPlayers.cs index bddaf25e..306c936e 100644 --- a/src/ARKServerManager/Lib/ServerPlayers.cs +++ b/src/ARKServerManager/Lib/ServerPlayers.cs @@ -223,7 +223,7 @@ namespace ServerManagerTool.Lib token.ThrowIfCancellationRequested(); // remove any players that do not have a player file. - var droppedPlayers = _players.Values.Where(p => dataContainer.Players.FirstOrDefault(pd => pd.PlayerId.Equals(p.PlayerId, StringComparison.OrdinalIgnoreCase)) == null).ToArray(); + var droppedPlayers = _players.Values.Where(p => dataContainer.Players.FirstOrDefault(pd => pd.PlayerId.Equals(p.PlayerId, StringComparison.OrdinalIgnoreCase)) == null); foreach (var droppedPlayer in droppedPlayers) { _players.TryRemove(droppedPlayer.PlayerId, out PlayerInfo player); diff --git a/src/ARKServerManager/Lib/ServerProfile.cs b/src/ARKServerManager/Lib/ServerProfile.cs index d8dbc0c3..09dc1038 100644 --- a/src/ARKServerManager/Lib/ServerProfile.cs +++ b/src/ARKServerManager/Lib/ServerProfile.cs @@ -968,6 +968,14 @@ namespace ServerManagerTool.Lib set { SetValue(DiscordChannelIdProperty, value); } } + public static readonly DependencyProperty DiscordAliasProperty = DependencyProperty.Register(nameof(DiscordAlias), typeof(string), typeof(ServerProfile), new PropertyMetadata(String.Empty)); + [DataMember] + public string DiscordAlias + { + get { return (string)GetValue(DiscordAliasProperty); } + set { SetValue(DiscordAliasProperty, value); } + } + public static readonly DependencyProperty AllowDiscordBackupProperty = DependencyProperty.Register(nameof(AllowDiscordBackup), typeof(bool), typeof(ServerProfile), new PropertyMetadata(true)); [DataMember] public bool AllowDiscordBackup @@ -3473,9 +3481,7 @@ namespace ServerManagerTool.Lib { InstallDirectory = folder; - LoadServerFileAdministrators(); - LoadServerFileExclusive(); - LoadServerFileWhitelisted(); + LoadServerFiles(true, true, true); SetupServerFilesWatcher(); } @@ -3561,9 +3567,7 @@ namespace ServerManagerTool.Lib settings.MutagenLevelBoostBred.Reset(); settings.PerLevelStatsMultiplier_Player.Reset(); settings.PlayerBaseStatMultipliers.Reset(); - settings.LoadServerFileAdministrators(); - settings.LoadServerFileExclusive(); - settings.LoadServerFileWhitelisted(); + settings.LoadServerFiles(true, true, true); settings.SetupServerFilesWatcher(); return settings; } @@ -3597,7 +3601,7 @@ namespace ServerManagerTool.Lib } } - private static Enum[] GetExclusions() + private static IEnumerable GetExclusions() { var exclusions = new List(); @@ -3641,7 +3645,7 @@ namespace ServerManagerTool.Lib exclusions.Add(ServerProfileCategory.SOTF); } - return exclusions.ToArray(); + return exclusions; } private LevelList GetLevelList(LevelProgression levelProgression) @@ -4026,7 +4030,7 @@ namespace ServerManagerTool.Lib return profile; } - public static ServerProfile LoadFromINIFiles(string file, ServerProfile profile, Enum[] exclusions = null) + public static ServerProfile LoadFromINIFiles(string file, ServerProfile profile, IEnumerable exclusions = null) { if (string.IsNullOrWhiteSpace(file) || !File.Exists(file)) return null; @@ -4140,9 +4144,7 @@ namespace ServerManagerTool.Lib if (Config.Default.SectionPreventTransferOverridesEnabled) profile.PreventTransferForClassNames.RenderToView(); - profile.LoadServerFileAdministrators(); - profile.LoadServerFileExclusive(); - profile.LoadServerFileWhitelisted(); + profile.LoadServerFiles(true, true, true); profile.SetupServerFilesWatcher(); profile._lastSaveLocation = file; @@ -4400,7 +4402,7 @@ namespace ServerManagerTool.Lib SaveINIFile(configDir); } - public void SaveINIFile(string profileIniDir, Enum[] exclusions = null) + public void SaveINIFile(string profileIniDir, IEnumerable exclusions = null) { if (exclusions == null) exclusions = GetExclusions(); @@ -4424,7 +4426,7 @@ namespace ServerManagerTool.Lib filteredValues.AddRange(this.PlayerLevels.ToINIValuesForEngramPoints()); } - iniFile.WriteSection(IniFiles.Game, IniSections.Game_ShooterGameMode, filteredValues.ToArray()); + iniFile.WriteSection(IniFiles.Game, IniSections.Game_ShooterGameMode, filteredValues); } public bool UpdateDirectoryPermissions() @@ -4984,7 +4986,7 @@ namespace ServerManagerTool.Lib var csvMapper = new CsvDinoLevelMapping(); var csvParser = new CsvParser(csvParserOptions, csvMapper); - var result = csvParser.ReadFromFile(fileName, Encoding.ASCII).ToList(); + var result = csvParser.ReadFromFile(fileName, Encoding.ASCII); if (result.Any(r => !r.IsValid)) { var error = result.First(r => r.Error != null); @@ -5011,7 +5013,7 @@ namespace ServerManagerTool.Lib var csvMapper = new CsvPlayerLevelMapping(); var csvParser = new CsvParser(csvParserOptions, csvMapper); - var result = csvParser.ReadFromFile(fileName, Encoding.ASCII).ToList(); + var result = csvParser.ReadFromFile(fileName, Encoding.ASCII); if (result.Any(r => !r.IsValid)) { var error = result.First(r => r.Error != null); @@ -5762,9 +5764,9 @@ namespace ServerManagerTool.Lib this.SetValue(AutoSavePeriodMinutesProperty, sourceProfile.AutoSavePeriodMinutes); - this.SetValue(MOTDProperty, sourceProfile.MOTD); - this.SetValue(MOTDDurationProperty, sourceProfile.MOTDDuration); - this.SetNullableValue(MOTDIntervalProperty, sourceProfile.MOTDInterval); + //this.SetValue(MOTDProperty, sourceProfile.MOTD); + //this.SetValue(MOTDDurationProperty, sourceProfile.MOTDDuration); + //this.SetNullableValue(MOTDIntervalProperty, sourceProfile.MOTDInterval); this.SetValue(EnableExtinctionEventProperty, sourceProfile.EnableExtinctionEvent); this.SetValue(ExtinctionEventTimeIntervalProperty, sourceProfile.ExtinctionEventTimeInterval); @@ -5802,7 +5804,7 @@ namespace ServerManagerTool.Lib this.SetValue(CrossplayProperty, sourceProfile.Crossplay); this.SetValue(EpicOnlyProperty, sourceProfile.EpicOnly); this.SetValue(EnablePublicIPForEpicProperty, sourceProfile.EnablePublicIPForEpic); - this.SetValue(AltSaveDirectoryNameProperty, sourceProfile.AltSaveDirectoryName); + //this.SetValue(AltSaveDirectoryNameProperty, sourceProfile.AltSaveDirectoryName); this.SetValue(CrossArkClusterIdProperty, sourceProfile.CrossArkClusterId); this.SetValue(ClusterDirOverrideProperty, sourceProfile.ClusterDirOverride); @@ -5866,7 +5868,7 @@ namespace ServerManagerTool.Lib this.CustomEngineSettings.Clear(); foreach (var section in sourceProfile.CustomEngineSettings) { - this.CustomEngineSettings.Add(section.SectionName, section.ToIniValues().ToArray()); + this.CustomEngineSettings.Add(section.SectionName, section.ToIniValues()); } } @@ -5875,7 +5877,7 @@ namespace ServerManagerTool.Lib this.CustomGameSettings.Clear(); foreach (var section in sourceProfile.CustomGameSettings) { - this.CustomGameSettings.Add(section.SectionName, section.ToIniValues().ToArray()); + this.CustomGameSettings.Add(section.SectionName, section.ToIniValues()); } } @@ -5884,7 +5886,7 @@ namespace ServerManagerTool.Lib this.CustomGameUserSettings.Clear(); foreach (var section in sourceProfile.CustomGameUserSettings) { - this.CustomGameUserSettings.Add(section.SectionName, section.ToIniValues().ToArray()); + this.CustomGameUserSettings.Add(section.SectionName, section.ToIniValues()); } } @@ -6364,18 +6366,24 @@ namespace ServerManagerTool.Lib #region Server Files private void ServerFilesWatcher_Changed(object sender, FileSystemEventArgs e) { + var adminFile = false; + var exclusiveFile = false; + var whitelistFile = false; + if (e.Name.Equals(Config.Default.ArkAdminFile, StringComparison.OrdinalIgnoreCase)) { - TaskUtils.RunOnUIThreadAsync(() => LoadServerFileAdministrators()).DoNotWait(); + adminFile = true; } - else if (e.Name.Equals(Config.Default.ArkExclusiveFile, StringComparison.OrdinalIgnoreCase)) + if (e.Name.Equals(Config.Default.ArkExclusiveFile, StringComparison.OrdinalIgnoreCase)) { - TaskUtils.RunOnUIThreadAsync(() => LoadServerFileExclusive()).DoNotWait(); + exclusiveFile = true; } - else if (e.Name.Equals(Config.Default.ArkWhitelistFile, StringComparison.OrdinalIgnoreCase)) + if (e.Name.Equals(Config.Default.ArkWhitelistFile, StringComparison.OrdinalIgnoreCase)) { - TaskUtils.RunOnUIThreadAsync(() => LoadServerFileWhitelisted()).DoNotWait(); + whitelistFile = true; } + + TaskUtils.RunOnUIThreadAsync(() => LoadServerFiles(adminFile, exclusiveFile, whitelistFile)).DoNotWait(); } private void ServerFilesWatcher_Error(object sender, ErrorEventArgs e) @@ -6447,74 +6455,76 @@ namespace ServerManagerTool.Lib _serverFilesWatcherSaved.EnableRaisingEvents = true; } - public void LoadServerFileAdministrators() + public void LoadServerFiles(bool adminFile, bool exclusiveFile, bool whitelistFile) { try { - var list = this.ServerFilesAdmins ?? new PlayerUserList(); + var list1 = this.ServerFilesAdmins ?? new PlayerUserList(); + var list2 = this.ServerFilesExclusive ?? new PlayerUserList(); + var list3 = this.ServerFilesWhitelisted ?? new PlayerUserList(); - var file = Path.Combine(InstallDirectory, Config.Default.SavedRelativePath, Config.Default.ArkAdminFile); - if (File.Exists(file)) + var allSteamIds = new List(); + string[] adminSteamIds = null; + string[] exclusiveSteamIds = null; + string[] whitelistSteamIds = null; + + if (adminFile) { - var steamIds = File.ReadAllLines(file); - var steamUsers = SteamUtils.GetSteamUserDetails(steamIds.ToList()); - - list = PlayerUserList.GetList(steamUsers, steamIds); + var file = Path.Combine(InstallDirectory, Config.Default.SavedRelativePath, Config.Default.ArkAdminFile); + if (File.Exists(file)) + { + adminSteamIds = File.ReadAllLines(file); + allSteamIds.AddRange(adminSteamIds); + } } - this.ServerFilesAdmins = list; + if (exclusiveFile) + { + var file = Path.Combine(InstallDirectory, Config.Default.ServerBinaryRelativePath, Config.Default.ArkExclusiveFile); + if (File.Exists(file)) + { + exclusiveSteamIds = File.ReadAllLines(file); + allSteamIds.AddRange(exclusiveSteamIds); + } + } + + if (whitelistFile) + { + var file = Path.Combine(InstallDirectory, Config.Default.ServerBinaryRelativePath, Config.Default.ArkWhitelistFile); + if (File.Exists(file)) + { + whitelistSteamIds = File.ReadAllLines(file); + allSteamIds.AddRange(whitelistSteamIds); + } + } + + // remove all duplicates + allSteamIds = allSteamIds.Distinct().ToList(); + + // fetch the details of all steam users in the list + var steamUsers = SteamUtils.GetSteamUserDetails(allSteamIds); + + if (adminFile && adminSteamIds != null) + { + list1 = PlayerUserList.GetList(steamUsers, adminSteamIds); + } + + if (exclusiveFile && exclusiveSteamIds != null) + { + list2 = PlayerUserList.GetList(steamUsers, exclusiveSteamIds); + } + + if (whitelistFile && whitelistSteamIds != null) + { + list3 = PlayerUserList.GetList(steamUsers, whitelistSteamIds); + } + + this.ServerFilesAdmins = list1; + this.ServerFilesExclusive = list2; + this.ServerFilesWhitelisted = list3; } catch (Exception ex) { - this.ServerFilesAdmins = new PlayerUserList(); - MessageBox.Show(ex.Message, _globalizer.GetResourceString("ServerSettings_ServerFilesLoadErrorTitle"), MessageBoxButton.OK, MessageBoxImage.Error); - } - } - - public void LoadServerFileExclusive() - { - try - { - var list = this.ServerFilesExclusive ?? new PlayerUserList(); - - var file = Path.Combine(InstallDirectory, Config.Default.ServerBinaryRelativePath, Config.Default.ArkExclusiveFile); - if (File.Exists(file)) - { - var steamIds = File.ReadAllLines(file); - var steamUsers = SteamUtils.GetSteamUserDetails(steamIds.ToList()); - - list = PlayerUserList.GetList(steamUsers, steamIds); - } - - this.ServerFilesExclusive = list; - } - catch (Exception ex) - { - this.ServerFilesExclusive = new PlayerUserList(); - MessageBox.Show(ex.Message, _globalizer.GetResourceString("ServerSettings_ServerFilesLoadErrorTitle"), MessageBoxButton.OK, MessageBoxImage.Error); - } - } - - public void LoadServerFileWhitelisted() - { - try - { - var list = this.ServerFilesWhitelisted ?? new PlayerUserList(); - - var file = Path.Combine(InstallDirectory, Config.Default.ServerBinaryRelativePath, Config.Default.ArkWhitelistFile); - if (File.Exists(file)) - { - var steamIds = File.ReadAllLines(file); - var steamUsers = SteamUtils.GetSteamUserDetails(steamIds.ToList()); - - list = PlayerUserList.GetList(steamUsers, steamIds); - } - - this.ServerFilesWhitelisted = list; - } - catch (Exception ex) - { - this.ServerFilesWhitelisted = new PlayerUserList(); MessageBox.Show(ex.Message, _globalizer.GetResourceString("ServerSettings_ServerFilesLoadErrorTitle"), MessageBoxButton.OK, MessageBoxImage.Error); } } @@ -6528,7 +6538,7 @@ namespace ServerManagerTool.Lib Directory.CreateDirectory(folder); var file = Path.Combine(folder, Config.Default.ArkAdminFile); - File.WriteAllLines(file, this.ServerFilesAdmins.ToArray()); + File.WriteAllLines(file, this.ServerFilesAdmins.ToEnumerable()); } catch (Exception ex) { @@ -6545,7 +6555,7 @@ namespace ServerManagerTool.Lib Directory.CreateDirectory(folder); var file = Path.Combine(folder, Config.Default.ArkExclusiveFile); - File.WriteAllLines(file, this.ServerFilesExclusive.ToArray()); + File.WriteAllLines(file, this.ServerFilesExclusive.ToEnumerable()); } catch (Exception ex) { @@ -6562,7 +6572,7 @@ namespace ServerManagerTool.Lib Directory.CreateDirectory(folder); var file = Path.Combine(folder, Config.Default.ArkWhitelistFile); - File.WriteAllLines(file, this.ServerFilesWhitelisted.ToArray()); + File.WriteAllLines(file, this.ServerFilesWhitelisted.ToEnumerable()); } catch (Exception ex) { diff --git a/src/ARKServerManager/Lib/ServerProfileSnapshot.cs b/src/ARKServerManager/Lib/ServerProfileSnapshot.cs index 0d2238ec..2a02fbe2 100644 --- a/src/ARKServerManager/Lib/ServerProfileSnapshot.cs +++ b/src/ARKServerManager/Lib/ServerProfileSnapshot.cs @@ -29,7 +29,7 @@ namespace ServerManagerTool.Lib public string ServerMap; public string ServerMapModId; public string TotalConversionModId; - public List ServerModIds; + public IEnumerable ServerModIds; public string MOTD; public int MotDDuration; public bool MOTDIntervalEnabled; diff --git a/src/ARKServerManager/Lib/ServerRCON.cs b/src/ARKServerManager/Lib/ServerRCON.cs index e64b4bd3..5a86d427 100644 --- a/src/ARKServerManager/Lib/ServerRCON.cs +++ b/src/ARKServerManager/Lib/ServerRCON.cs @@ -1,5 +1,6 @@ using ArkData; using NLog; +using ServerManagerTool.Common.Extensions; using ServerManagerTool.Common.Interfaces; using ServerManagerTool.Common.Lib; using ServerManagerTool.Common.Model; @@ -325,8 +326,8 @@ namespace ServerManagerTool.Lib else if (command.command.Equals(RCON_COMMAND_GETCHAT, StringComparison.OrdinalIgnoreCase)) { // TODO: Extract the player name from the chat - var lines = command.lines.Where(l => !String.IsNullOrEmpty(l) && l != NoResponseOutput).ToArray(); - if (lines.Length == 0 && command.suppressCommand) + var lines = command.lines.Where(l => !string.IsNullOrEmpty(l) && l != NoResponseOutput); + if (lines.IsEmpty() && command.suppressCommand) { command.suppressOutput = true; } @@ -398,7 +399,7 @@ namespace ServerManagerTool.Lib } } - var droppedPlayers = this.players.Values.Where(p => onlinePlayers.FirstOrDefault(np => np.PlayerId.Equals(p.PlayerId, StringComparison.OrdinalIgnoreCase)) == null).ToArray(); + var droppedPlayers = this.players.Values.Where(p => onlinePlayers.FirstOrDefault(np => np.PlayerId.Equals(p.PlayerId, StringComparison.OrdinalIgnoreCase)) == null); foreach (var droppedPlayer in droppedPlayers) { if (droppedPlayer.IsOnline) @@ -604,7 +605,7 @@ namespace ServerManagerTool.Lib token.ThrowIfCancellationRequested(); // remove any players that do not have a player file. - var droppedPlayers = this.players.Values.Where(p => dataContainer.Players.FirstOrDefault(pd => pd.PlayerId.Equals(p.PlayerId, StringComparison.OrdinalIgnoreCase)) == null).ToArray(); + var droppedPlayers = this.players.Values.Where(p => dataContainer.Players.FirstOrDefault(pd => pd.PlayerId.Equals(p.PlayerId, StringComparison.OrdinalIgnoreCase)) == null); foreach (var droppedPlayer in droppedPlayers) { players.TryRemove(droppedPlayer.PlayerId, out PlayerInfo player); diff --git a/src/ARKServerManager/Lib/ViewModel/DinoSettingsList.cs b/src/ARKServerManager/Lib/ViewModel/DinoSettingsList.cs index c132fb8e..fc8ce102 100644 --- a/src/ARKServerManager/Lib/ViewModel/DinoSettingsList.cs +++ b/src/ARKServerManager/Lib/ViewModel/DinoSettingsList.cs @@ -87,20 +87,24 @@ namespace ServerManagerTool.Lib.ViewModel { Reset(); - foreach(var entry in this.DinoSpawnWeightMultipliers) + foreach(var entry in this.DinoSpawnWeightMultipliers.Where(e => !string.IsNullOrWhiteSpace(e.DinoNameTag))) { - if (string.IsNullOrWhiteSpace(entry.DinoNameTag)) - continue; - - var dinoSettings = this.Where(vi => vi.NameTag == entry.DinoNameTag).ToArray(); - if (dinoSettings == null || dinoSettings.Length == 0) + if (this.Any(d => d.NameTag == entry.DinoNameTag)) { - this.Add(CreateDinoSetting(entry.DinoNameTag, entry.Mod, entry.KnownDino, true, false)); + foreach (var dinoSetting in this.Where(d => d.NameTag == entry.DinoNameTag)) + { + dinoSetting.SpawnWeightMultiplier = entry.SpawnWeightMultiplier; + dinoSetting.OverrideSpawnLimitPercentage = entry.OverrideSpawnLimitPercentage; + dinoSetting.SpawnLimitPercentage = entry.SpawnLimitPercentage; + + dinoSetting.OriginalSpawnWeightMultiplier = entry.SpawnWeightMultiplier; + dinoSetting.OriginalOverrideSpawnLimitPercentage = entry.OverrideSpawnLimitPercentage; + dinoSetting.OriginalSpawnLimitPercentage = entry.SpawnLimitPercentage; + } } - - dinoSettings = this.Where(vi => vi.NameTag == entry.DinoNameTag).ToArray(); - foreach (var dinoSetting in dinoSettings) + else { + var dinoSetting = CreateDinoSetting(entry.DinoNameTag, entry.Mod, entry.KnownDino, true, false); dinoSetting.SpawnWeightMultiplier = entry.SpawnWeightMultiplier; dinoSetting.OverrideSpawnLimitPercentage = entry.OverrideSpawnLimitPercentage; dinoSetting.SpawnLimitPercentage = entry.SpawnLimitPercentage; @@ -108,115 +112,118 @@ namespace ServerManagerTool.Lib.ViewModel dinoSetting.OriginalSpawnWeightMultiplier = entry.SpawnWeightMultiplier; dinoSetting.OriginalOverrideSpawnLimitPercentage = entry.OverrideSpawnLimitPercentage; dinoSetting.OriginalSpawnLimitPercentage = entry.SpawnLimitPercentage; + + this.Add(dinoSetting); } } - foreach(var entry in this.PreventDinoTameClassNames) + foreach(var entry in this.PreventDinoTameClassNames.Where(e => !string.IsNullOrWhiteSpace(e))) { - if (string.IsNullOrWhiteSpace(entry)) - continue; - - var dinoSettings = this.Where(vi => vi.ClassName == entry).ToArray(); - if (dinoSettings == null || dinoSettings.Length == 0) + if (this.Any(d => d.ClassName == entry)) { - this.Add(CreateDinoSetting(entry, GameData.MOD_UNKNOWN, false, false, true)); + foreach (var dinoSetting in this.Where(d => d.ClassName == entry && !d.CanTame)) + { + dinoSetting.CanTame = false; + } } - - dinoSettings = this.Where(vi => vi.ClassName == entry).ToArray(); - foreach (var dinoSetting in dinoSettings) + else { + var dinoSetting = CreateDinoSetting(entry, GameData.MOD_UNKNOWN, false, false, true); dinoSetting.CanTame = false; + + this.Add(dinoSetting); } } - foreach(var entry in this.NpcReplacements) + foreach(var entry in this.NpcReplacements.Where(e => !string.IsNullOrWhiteSpace(e.FromClassName))) { - if (string.IsNullOrWhiteSpace(entry.FromClassName)) - continue; - - var dinoSettings = this.Where(vi => vi.ClassName == entry.FromClassName).ToArray(); - if (dinoSettings == null || dinoSettings.Length == 0) + if (this.Any(d => d.ClassName == entry.FromClassName)) { - this.Add(CreateDinoSetting(entry.FromClassName, GameData.MOD_UNKNOWN, false, false, true)); + foreach (var dinoSetting in this.Where(d => d.ClassName == entry.FromClassName)) + { + dinoSetting.CanSpawn = !string.IsNullOrWhiteSpace(entry.ToClassName); + dinoSetting.ReplacementClass = dinoSetting.CanSpawn ? entry.ToClassName : dinoSetting.ClassName; + } } - - dinoSettings = this.Where(vi => vi.ClassName == entry.FromClassName).ToArray(); - foreach (var dinoSetting in dinoSettings) + else { + var dinoSetting = CreateDinoSetting(entry.FromClassName, GameData.MOD_UNKNOWN, false, false, true); dinoSetting.CanSpawn = !string.IsNullOrWhiteSpace(entry.ToClassName); dinoSetting.ReplacementClass = dinoSetting.CanSpawn ? entry.ToClassName : dinoSetting.ClassName; + + this.Add(dinoSetting); } } - foreach (var entry in this.TamedDinoClassDamageMultipliers) + foreach (var entry in this.TamedDinoClassDamageMultipliers.Where(e => !string.IsNullOrWhiteSpace(e.ClassName))) { - if (string.IsNullOrWhiteSpace(entry.ClassName)) - continue; - - var dinoSettings = this.Where(vi => vi.ClassName == entry.ClassName).ToArray(); - if (dinoSettings == null || dinoSettings.Length == 0) + if (this.Any(d => d.ClassName == entry.ClassName)) { - this.Add(CreateDinoSetting(entry.ClassName, GameData.MOD_UNKNOWN, false, false, true)); + foreach (var dinoSetting in this.Where(d => d.ClassName == entry.ClassName && d.TamedDamageMultiplier != entry.Multiplier)) + { + dinoSetting.TamedDamageMultiplier = entry.Multiplier; + } } - - dinoSettings = this.Where(vi => vi.ClassName == entry.ClassName).ToArray(); - foreach (var dinoSetting in dinoSettings) + else { + var dinoSetting = CreateDinoSetting(entry.ClassName, GameData.MOD_UNKNOWN, false, false, true); dinoSetting.TamedDamageMultiplier = entry.Multiplier; + + this.Add(dinoSetting); } } - foreach(var entry in this.TamedDinoClassResistanceMultipliers) + foreach(var entry in this.TamedDinoClassResistanceMultipliers.Where(e => !string.IsNullOrWhiteSpace(e.ClassName))) { - if (string.IsNullOrWhiteSpace(entry.ClassName)) - continue; - - var dinoSettings = this.Where(vi => vi.ClassName == entry.ClassName).ToArray(); - if (dinoSettings == null || dinoSettings.Length == 0) + if (this.Any(d => d.ClassName == entry.ClassName)) { - this.Add(CreateDinoSetting(entry.ClassName, GameData.MOD_UNKNOWN, false, false, true)); + foreach (var dinoSetting in this.Where(d => d.ClassName == entry.ClassName && d.TamedResistanceMultiplier != entry.Multiplier)) + { + dinoSetting.TamedResistanceMultiplier = entry.Multiplier; + } } - - dinoSettings = this.Where(vi => vi.ClassName == entry.ClassName).ToArray(); - foreach (var dinoSetting in dinoSettings) + else { + var dinoSetting = CreateDinoSetting(entry.ClassName, GameData.MOD_UNKNOWN, false, false, true); dinoSetting.TamedResistanceMultiplier = entry.Multiplier; + + this.Add(dinoSetting); } } - foreach (var entry in this.DinoClassDamageMultipliers) + foreach (var entry in this.DinoClassDamageMultipliers.Where(e => !string.IsNullOrWhiteSpace(e.ClassName))) { - if (string.IsNullOrWhiteSpace(entry.ClassName)) - continue; - - var dinoSettings = this.Where(vi => vi.ClassName == entry.ClassName).ToArray(); - if (dinoSettings == null || dinoSettings.Length == 0) + if (this.Any(d => d.ClassName == entry.ClassName)) { - this.Add(CreateDinoSetting(entry.ClassName, GameData.MOD_UNKNOWN, false, false, true)); + foreach (var dinoSetting in this.Where(d => d.ClassName == entry.ClassName && d.WildDamageMultiplier != entry.Multiplier)) + { + dinoSetting.WildDamageMultiplier = entry.Multiplier; + } } - - dinoSettings = this.Where(vi => vi.ClassName == entry.ClassName).ToArray(); - foreach (var dinoSetting in dinoSettings) + else { + var dinoSetting = CreateDinoSetting(entry.ClassName, GameData.MOD_UNKNOWN, false, false, true); dinoSetting.WildDamageMultiplier = entry.Multiplier; + + this.Add(dinoSetting); } } - foreach (var entry in this.DinoClassResistanceMultipliers) + foreach (var entry in this.DinoClassResistanceMultipliers.Where(e => !string.IsNullOrWhiteSpace(e.ClassName))) { - if (string.IsNullOrWhiteSpace(entry.ClassName)) - continue; - - var dinoSettings = this.Where(vi => vi.ClassName == entry.ClassName).ToArray(); - if (dinoSettings == null || dinoSettings.Length == 0) + if (this.Any(d => d.ClassName == entry.ClassName)) { - this.Add(CreateDinoSetting(entry.ClassName, GameData.MOD_UNKNOWN, false, false, true)); + foreach (var dinoSetting in this.Where(d => d.ClassName == entry.ClassName && d.WildResistanceMultiplier != entry.Multiplier)) + { + dinoSetting.WildResistanceMultiplier = entry.Multiplier; + } } - - dinoSettings = this.Where(vi => vi.ClassName == entry.ClassName).ToArray(); - foreach (var dinoSetting in dinoSettings) + else { + var dinoSetting = CreateDinoSetting(entry.ClassName, GameData.MOD_UNKNOWN, false, false, true); dinoSetting.WildResistanceMultiplier = entry.Multiplier; + + this.Add(dinoSetting); } } @@ -244,22 +251,9 @@ namespace ServerManagerTool.Lib.ViewModel !entry.SpawnLimitPercentage.Equals(DinoSpawn.DEFAULT_SPAWN_LIMIT_PERCENTAGE) || !entry.SpawnWeightMultiplier.Equals(DinoSpawn.DEFAULT_SPAWN_WEIGHT_MULTIPLIER)) { - - var dinoSpawns = this.DinoSpawnWeightMultipliers.Where(d => d.DinoNameTag.Equals(entry.NameTag, StringComparison.OrdinalIgnoreCase)).ToArray(); - if (dinoSpawns == null || dinoSpawns.Length == 0) + if (this.DinoSpawnWeightMultipliers.Any(d => d.DinoNameTag.Equals(entry.NameTag, StringComparison.OrdinalIgnoreCase))) { - this.DinoSpawnWeightMultipliers.Add(new DinoSpawn() - { - ClassName = entry.ClassName, - DinoNameTag = entry.NameTag, - OverrideSpawnLimitPercentage = entry.OverrideSpawnLimitPercentage, - SpawnLimitPercentage = entry.SpawnLimitPercentage, - SpawnWeightMultiplier = entry.SpawnWeightMultiplier - }); - } - else - { - foreach (var dinoSpawn in dinoSpawns) + foreach (var dinoSpawn in this.DinoSpawnWeightMultipliers.Where(d => d.DinoNameTag.Equals(entry.NameTag, StringComparison.OrdinalIgnoreCase))) { if (entry.SpawnWeightMultiplier != entry.OriginalSpawnWeightMultiplier || entry.OverrideSpawnLimitPercentage != entry.OriginalOverrideSpawnLimitPercentage || @@ -271,6 +265,17 @@ namespace ServerManagerTool.Lib.ViewModel } } } + else + { + this.DinoSpawnWeightMultipliers.Add(new DinoSpawn() + { + ClassName = entry.ClassName, + DinoNameTag = entry.NameTag, + OverrideSpawnLimitPercentage = entry.OverrideSpawnLimitPercentage, + SpawnLimitPercentage = entry.SpawnLimitPercentage, + SpawnWeightMultiplier = entry.SpawnWeightMultiplier + }); + } } } @@ -287,20 +292,28 @@ namespace ServerManagerTool.Lib.ViewModel { // check if the value has changed. if (!entry.TamedDamageMultiplier.Equals(ClassMultiplier.DEFAULT_MULTIPLIER)) + { this.TamedDinoClassDamageMultipliers.Add(new ClassMultiplier() { ClassName = entry.ClassName, Multiplier = entry.TamedDamageMultiplier }); + } // check if the value has changed. if (!entry.TamedResistanceMultiplier.Equals(ClassMultiplier.DEFAULT_MULTIPLIER)) + { this.TamedDinoClassResistanceMultipliers.Add(new ClassMultiplier() { ClassName = entry.ClassName, Multiplier = entry.TamedResistanceMultiplier }); + } } // check if the value has changed. if (!entry.WildDamageMultiplier.Equals(ClassMultiplier.DEFAULT_MULTIPLIER)) + { this.DinoClassDamageMultipliers.Add(new ClassMultiplier() { ClassName = entry.ClassName, Multiplier = entry.WildDamageMultiplier }); + } // check if the value has changed. if (!entry.WildResistanceMultiplier.Equals(ClassMultiplier.DEFAULT_MULTIPLIER)) + { this.DinoClassResistanceMultipliers.Add(new ClassMultiplier() { ClassName = entry.ClassName, Multiplier = entry.WildResistanceMultiplier }); + } } } } diff --git a/src/ARKServerManager/Lib/ViewModel/EngramSettings.cs b/src/ARKServerManager/Lib/ViewModel/EngramSettings.cs index df4620f4..56eca10d 100644 --- a/src/ARKServerManager/Lib/ViewModel/EngramSettings.cs +++ b/src/ARKServerManager/Lib/ViewModel/EngramSettings.cs @@ -80,14 +80,13 @@ namespace ServerManagerTool.Lib.ViewModel if (string.IsNullOrWhiteSpace(entry.EngramClassName)) continue; - var engramSettings = this.Where(vi => vi.EngramClassName == entry.EngramClassName).ToArray(); - if (engramSettings == null || engramSettings.Length == 0) + if (!this.Any(vi => vi.EngramClassName == entry.EngramClassName)) { var engram = GameData.GetEngramForClass(entry.EngramClassName); this.Add(CreateEngramSetting(entry.EngramClassName, engram?.Mod ?? GameData.MOD_UNKNOWN, engram?.KnownEngram ?? false, engram?.IsTekgram ?? false)); } - engramSettings = this.Where(vi => vi.EngramClassName == entry.EngramClassName).ToArray(); + var engramSettings = this.Where(vi => vi.EngramClassName == entry.EngramClassName); foreach (var engramSetting in engramSettings) { engramSetting.EngramLevelRequirement = entry.EngramLevelRequirement; @@ -117,14 +116,13 @@ namespace ServerManagerTool.Lib.ViewModel if (string.IsNullOrWhiteSpace(entry.EngramClassName)) continue; - var engramSettings = this.Where(vi => vi.EngramClassName == entry.EngramClassName).ToArray(); - if (engramSettings == null || engramSettings.Length == 0) + if (!this.Any(vi => vi.EngramClassName == entry.EngramClassName)) { var engram = GameData.GetEngramForClass(entry.EngramClassName); this.Add(CreateEngramSetting(entry.EngramClassName, engram?.Mod ?? GameData.MOD_UNKNOWN, engram?.KnownEngram ?? false, engram?.IsTekgram ?? false)); } - engramSettings = this.Where(vi => vi.EngramClassName == entry.EngramClassName).ToArray(); + var engramSettings = this.Where(vi => vi.EngramClassName == entry.EngramClassName); foreach (var engramSetting in engramSettings) { engramSetting.EngramAutoUnlock = true; diff --git a/src/ARKServerManager/UserControls/GlobalSettingsControl.xaml b/src/ARKServerManager/UserControls/GlobalSettingsControl.xaml index 6919def4..b82088fc 100644 --- a/src/ARKServerManager/UserControls/GlobalSettingsControl.xaml +++ b/src/ARKServerManager/UserControls/GlobalSettingsControl.xaml @@ -60,22 +60,22 @@
diff --git a/src/Plugin.Discord/VersionFeedBeta.xml b/src/Plugin.Discord/VersionFeedBeta.xml index 731453ee..0ffd8b43 100644 --- a/src/Plugin.Discord/VersionFeedBeta.xml +++ b/src/Plugin.Discord/VersionFeedBeta.xml @@ -7,6 +7,52 @@ 2021-12-12T00:00:00Z + + urn:uuid:A0D7BFD2-F2F0-481A-A22D-3C193BFB4C99 + 1.0.19 (1.0.19.5) + 1.0.19.5 + + 2021-12-12T00:00:00Z + +
+

+ CHANGE +
+

    +
  • ru-RU Translation file updated.
  • +
+

+
+
+ + bletch + bletch1971@hotmail.com + +
+ + + urn:uuid:38A61493-CA4E-479E-A3FD-266565E83D90 + 1.0.19 (1.0.19.4) + 1.0.19.4 + + 2021-12-12T00:00:00Z + +
+

+ CHANGE +
+

    +
  • ru-RU Translation file added.
  • +
+

+
+
+ + bletch + bletch1971@hotmail.com + +
+ urn:uuid:DDB25735-1D20-4580-B5FE-1AD4BD107376 1.0.19 (1.0.19.3) diff --git a/src/Plugin.Discord/app.config b/src/Plugin.Discord/app.config index a2c1109e..76682998 100644 --- a/src/Plugin.Discord/app.config +++ b/src/Plugin.Discord/app.config @@ -10,9 +10,6 @@ http://servermanager.azurewebsites.net/api/plugin/call/{0}/{1}/ - - http://whatismyip.akamai.com/ - 12 @@ -49,6 +46,12 @@ https://raw.githubusercontent.com/Bletch1971/ServerManagers/master/Plugins/Discord/beta/VersionFeed.xml + + https://api.ipify.org + + + http://whatismyip.akamai.com/ + diff --git a/src/QueryMaster/RconSource.cs b/src/QueryMaster/RconSource.cs index 8980a872..bcfb974f 100644 --- a/src/QueryMaster/RconSource.cs +++ b/src/QueryMaster/RconSource.cs @@ -50,14 +50,7 @@ namespace QueryMaster //consecutive rcon command replies start with an empty packet if (BitConverter.ToInt32(recvData[i], 4) == (int)PacketId.Empty) continue; - //if (recvData[i].Length - BitConverter.ToInt32(recvData[i], 0) == 4) - //{ str.Append(RconUtil.ProcessPacket(recvData[i]).Body); - //} - //else - //{ - // str.Append(RconUtil.ProcessPacket(recvData[i]).Body + Util.BytesToString(recvData[++i].Take(recvData[i].Length - 2).ToArray())); - //} } } catch (Exception e) diff --git a/src/QueryMaster/RconUtil.cs b/src/QueryMaster/RconUtil.cs index 133e9596..a8d6c09b 100644 --- a/src/QueryMaster/RconUtil.cs +++ b/src/QueryMaster/RconUtil.cs @@ -1,7 +1,5 @@ using System; using System.Collections.Generic; -using System.Linq; -using System.Text; namespace QueryMaster { diff --git a/src/ServerManager.Common/CommonConfig.Designer.cs b/src/ServerManager.Common/CommonConfig.Designer.cs index 4e793e96..590d4991 100644 --- a/src/ServerManager.Common/CommonConfig.Designer.cs +++ b/src/ServerManager.Common/CommonConfig.Designer.cs @@ -12,7 +12,7 @@ namespace ServerManagerTool.Common { [global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()] - [global::System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.VisualStudio.Editors.SettingsDesigner.SettingsSingleFileGenerator", "15.7.0.0")] + [global::System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.VisualStudio.Editors.SettingsDesigner.SettingsSingleFileGenerator", "17.0.3.0")] public sealed partial class CommonConfig : global::System.Configuration.ApplicationSettingsBase { private static CommonConfig defaultInstance = ((CommonConfig)(global::System.Configuration.ApplicationSettingsBase.Synchronized(new CommonConfig()))); @@ -23,15 +23,6 @@ namespace ServerManagerTool.Common { } } - [global::System.Configuration.ApplicationScopedSettingAttribute()] - [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] - [global::System.Configuration.DefaultSettingValueAttribute("http://whatismyip.akamai.com/")] - public string PublicIPCheckUrl { - get { - return ((string)(this["PublicIPCheckUrl"])); - } - } - [global::System.Configuration.ApplicationScopedSettingAttribute()] [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] [global::System.Configuration.DefaultSettingValueAttribute("SteamCMD")] @@ -160,5 +151,23 @@ namespace ServerManagerTool.Common { this["SteamCmdRemoveQuit"] = value; } } + + [global::System.Configuration.ApplicationScopedSettingAttribute()] + [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] + [global::System.Configuration.DefaultSettingValueAttribute("https://api.ipify.org")] + public string PublicIPCheckUrl1 { + get { + return ((string)(this["PublicIPCheckUrl1"])); + } + } + + [global::System.Configuration.ApplicationScopedSettingAttribute()] + [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] + [global::System.Configuration.DefaultSettingValueAttribute("http://whatismyip.akamai.com/")] + public string PublicIPCheckUrl2 { + get { + return ((string)(this["PublicIPCheckUrl2"])); + } + } } } diff --git a/src/ServerManager.Common/CommonConfig.settings b/src/ServerManager.Common/CommonConfig.settings index 74eaade6..290ed90c 100644 --- a/src/ServerManager.Common/CommonConfig.settings +++ b/src/ServerManager.Common/CommonConfig.settings @@ -2,9 +2,6 @@ - - http://whatismyip.akamai.com/ - SteamCMD @@ -44,5 +41,11 @@ False + + https://api.ipify.org + + + http://whatismyip.akamai.com/ + \ No newline at end of file diff --git a/src/ServerManager.Common/Extensions/IEnumerableExtensions.cs b/src/ServerManager.Common/Extensions/IEnumerableExtensions.cs new file mode 100644 index 00000000..1c7814cd --- /dev/null +++ b/src/ServerManager.Common/Extensions/IEnumerableExtensions.cs @@ -0,0 +1,47 @@ +using System; +using System.Collections.Generic; + +namespace ServerManagerTool.Common.Extensions +{ + public static class IEnumerableExtensions + { + public static bool IsEmpty(this IEnumerable source) + { + if (source == null) + { + throw new ArgumentNullException(nameof(source)); + } + + using (IEnumerator enumerator = source.GetEnumerator()) + { + if (enumerator.MoveNext()) + { + return false; + } + } + + return true; + } + + public static bool HasOne(this IEnumerable source) + { + if (source == null) + { + throw new ArgumentNullException(nameof(source)); + } + + var count = 0; + + using (IEnumerator enumerator = source.GetEnumerator()) + { + while (enumerator.MoveNext()) + { + if (++count > 1) + return false; + } + } + + return count == 1; + } + } +} diff --git a/src/ServerManager.Common/Interfaces/IIniSectionCollection.cs b/src/ServerManager.Common/Interfaces/IIniSectionCollection.cs index 332d72de..7ae69f60 100644 --- a/src/ServerManager.Common/Interfaces/IIniSectionCollection.cs +++ b/src/ServerManager.Common/Interfaces/IIniSectionCollection.cs @@ -1,10 +1,12 @@ -namespace ServerManagerTool.Common.Interfaces +using System.Collections.Generic; + +namespace ServerManagerTool.Common.Interfaces { public interface IIniSectionCollection { IIniValuesCollection[] Sections { get; } - void Add(string sectionName, string[] values); + void Add(string sectionName, IEnumerable values); void Update(); } diff --git a/src/ServerManager.Common/Lib/SteamCmdUpdater.cs b/src/ServerManager.Common/Lib/SteamCmdUpdater.cs index 47bd926c..4a8936aa 100644 --- a/src/ServerManager.Common/Lib/SteamCmdUpdater.cs +++ b/src/ServerManager.Common/Lib/SteamCmdUpdater.cs @@ -23,15 +23,10 @@ namespace ServerManagerTool.Common.Lib this.StatusKey = statusKey; this.CompletionPercent = completionPercent; this.Cancelled = false; + this.Failure = null; this.FailureText = null; } - public Update SetFailed(string failureText) - { - this.FailureText = failureText; - return this; - } - public static Update AsCompleted(string statusKey) { return new Update { StatusKey = statusKey, CompletionPercent = 100, Cancelled = false }; @@ -42,9 +37,17 @@ namespace ServerManagerTool.Common.Lib return new Update { StatusKey = statusKey, CompletionPercent = 100, Cancelled = true }; } + public Update SetFailed(Exception ex) + { + this.Failure = ex; + this.FailureText = ex.Message; + return this; + } + public string StatusKey; public float CompletionPercent; public bool Cancelled; + public Exception Failure; public string FailureText; } @@ -56,10 +59,11 @@ namespace ServerManagerTool.Common.Lib RunningSteamCmd, InstallSteamCmdComplete, Complete, - Cancelled + Cancelled, + Failed, } - Dictionary statuses = new Dictionary() + readonly Dictionary statuses = new Dictionary() { { Status.CleaningSteamCmd, new Update("AutoUpdater_Status_CleaningSteamCmd", 0) }, { Status.DownloadingSteamCmd, new Update("AutoUpdater_Status_DownloadingSteamCmd", 10) }, @@ -67,7 +71,8 @@ namespace ServerManagerTool.Common.Lib { Status.RunningSteamCmd, new Update("AutoUpdater_Status_RunningSteamCmd", 50) }, { Status.InstallSteamCmdComplete, new Update("AutoUpdater_Status_InstallSteamCmdComplete", 80) }, { Status.Complete, Update.AsCompleted("AutoUpdater_Status_Complete") }, - { Status.Cancelled, Update.AsCancelled("AutoUpdater_Status_Cancelled") } + { Status.Cancelled, Update.AsCancelled("AutoUpdater_Status_Cancelled") }, + { Status.Failed, Update.AsCancelled("AutoUpdater_Status_Failed") }, }; public static string GetSteamCmdFile(string dataPath) => IOUtils.NormalizePath(Path.Combine(dataPath, CommonConfig.Default.SteamCmdRelativePath, CommonConfig.Default.SteamCmdExeFile)); @@ -99,7 +104,7 @@ namespace ServerManagerTool.Common.Lib } catch (Exception ex) { - reporter?.Report(statuses[Status.Complete].SetFailed(ex.ToString())); + reporter?.Report(statuses[Status.Failed].SetFailed(ex)); } } @@ -189,7 +194,7 @@ namespace ServerManagerTool.Common.Lib } catch(Exception ex) { - reporter?.Report(statuses[Status.Complete].SetFailed(ex.ToString())); + reporter?.Report(statuses[Status.Failed].SetFailed(ex)); } } } diff --git a/src/ServerManager.Common/Lib/UserScopedSettingContractResolver.cs b/src/ServerManager.Common/Lib/UserScopedSettingContractResolver.cs index 94855d0f..4ed0a4ea 100644 --- a/src/ServerManager.Common/Lib/UserScopedSettingContractResolver.cs +++ b/src/ServerManager.Common/Lib/UserScopedSettingContractResolver.cs @@ -13,7 +13,7 @@ namespace ServerManagerTool.Common.Lib { JsonProperty property = base.CreateProperty(member, memberSerialization); - var customAttributes = member.CustomAttributes?.ToArray() ?? new CustomAttributeData[0]; + var customAttributes = member.CustomAttributes ?? new CustomAttributeData[0]; if (customAttributes.Any(a => a.AttributeType == typeof(System.Configuration.UserScopedSettingAttribute))) { property.ShouldSerialize = instance => { return property.PropertyType.IsValueType || property.PropertyType == typeof(string); }; diff --git a/src/ServerManager.Common/Model/AggregateIniValue.cs b/src/ServerManager.Common/Model/AggregateIniValue.cs index 291a7faa..7edb9020 100644 --- a/src/ServerManager.Common/Model/AggregateIniValue.cs +++ b/src/ServerManager.Common/Model/AggregateIniValue.cs @@ -171,16 +171,15 @@ namespace ServerManagerTool.Common.Model kvPropertyValue = kvPropertyValue.Substring(0, kvPropertyValue.Length - 1); } - var collection = property.GetValue(this) as IIniValuesCollection; - if (collection != null) + if (property.GetValue(this) is IIniValuesCollection collection) { - var values = SplitCollectionValues(kvPropertyValue, DELIMITER); - values = values.Where(v => !string.IsNullOrWhiteSpace(v)).ToArray(); + var values = SplitCollectionValues(kvPropertyValue, DELIMITER) + .Where(v => !string.IsNullOrWhiteSpace(v)); if (attr?.ListValueWithinBrackets ?? false) { - values = values.Select(v => v.Substring(1)).ToArray(); - values = values.Select(v => v.Substring(0, v.Length - 1)).ToArray(); + values = values.Select(v => v.Substring(1)); + values = values.Select(v => v.Substring(0, v.Length - 1)); } collection.FromIniValues(values); } @@ -267,7 +266,7 @@ namespace ServerManagerTool.Common.Model return result.ToString(); } - protected string[] SplitCollectionValues(string valueString, char delimiter) + protected IEnumerable SplitCollectionValues(string valueString, char delimiter) { if (string.IsNullOrWhiteSpace(valueString)) return new string[0]; @@ -307,7 +306,7 @@ namespace ServerManagerTool.Common.Model result.Add(tempString.Substring(startIndex)); - return result.ToArray(); + return result; } public void Update(AggregateIniValue other) diff --git a/src/ServerManager.Common/Model/AggregateIniValueList.cs b/src/ServerManager.Common/Model/AggregateIniValueList.cs index b62fb4af..87630037 100644 --- a/src/ServerManager.Common/Model/AggregateIniValueList.cs +++ b/src/ServerManager.Common/Model/AggregateIniValueList.cs @@ -59,17 +59,16 @@ namespace ServerManagerTool.Common.Model public virtual void FromIniValues(IEnumerable iniValues) { - var items = iniValues?.Select(AggregateIniValue.FromINIValue).ToArray(); + var items = iniValues?.Select(AggregateIniValue.FromINIValue); Clear(); AddRange(items); - IsEnabled = (Count != 0); + IsEnabled = (Count > 0); // Add any default values which were missing if (_resetFunc != null) { - var defaultItemsToAdd = _resetFunc().Where(r => !this.Any(v => v.IsEquivalent(r))).ToArray(); - AddRange(defaultItemsToAdd); + AddRange(_resetFunc().Where(r => !this.Any(v => v.IsEquivalent(r)))); } Sort(AggregateIniValue.SortKeySelector); diff --git a/src/ServerManager.Common/Model/CustomSectionList.cs b/src/ServerManager.Common/Model/CustomSectionList.cs index dc46c5b4..fc395447 100644 --- a/src/ServerManager.Common/Model/CustomSectionList.cs +++ b/src/ServerManager.Common/Model/CustomSectionList.cs @@ -1,5 +1,6 @@ using ServerManagerTool.Common.Interfaces; using System; +using System.Collections.Generic; using System.Linq; namespace ServerManagerTool.Common.Model @@ -14,12 +15,12 @@ namespace ServerManagerTool.Common.Model } } - public void Add(string sectionName, string[] values) + public void Add(string sectionName, IEnumerable values) { Add(sectionName, values, true); } - public void Add(string sectionName, string[] values, bool clearExisting) + public void Add(string sectionName, IEnumerable values, bool clearExisting) { var section = this.Items.FirstOrDefault(s => s.SectionName.Equals(sectionName, StringComparison.OrdinalIgnoreCase) && !s.IsDeleted); if (section == null) diff --git a/src/ServerManager.Common/Model/DiscordBotWhitelist.cs b/src/ServerManager.Common/Model/DiscordBotWhitelist.cs new file mode 100644 index 00000000..ea456fd9 --- /dev/null +++ b/src/ServerManager.Common/Model/DiscordBotWhitelist.cs @@ -0,0 +1,15 @@ +using System.Windows; + +namespace ServerManagerTool.Common.Model +{ + public class DiscordBotWhitelist : DependencyObject + { + public static readonly DependencyProperty BotIdProperty = DependencyProperty.Register(nameof(BotId), typeof(string), typeof(DiscordBotWhitelist), new PropertyMetadata("")); + + public string BotId + { + get { return (string)GetValue(BotIdProperty); } + set { SetValue(BotIdProperty, value); } + } + } +} diff --git a/src/ServerManager.Common/Model/IniValueList.cs b/src/ServerManager.Common/Model/IniValueList.cs index 22febae9..71540c3c 100644 --- a/src/ServerManager.Common/Model/IniValueList.cs +++ b/src/ServerManager.Common/Model/IniValueList.cs @@ -98,8 +98,7 @@ namespace ServerManagerTool.Common.Model // Add any default values which were missing if (this.ResetFunc != null) { - var defaultItemsToAdd = this.ResetFunc().Where(r => !this.Any(v => this.EquivalencyFunc(v, r))).ToArray(); - this.AddRange(defaultItemsToAdd); + this.AddRange(this.ResetFunc().Where(r => !this.Any(v => this.EquivalencyFunc(v, r)))); this.Sort(this.SortKeySelectorFunc); } } diff --git a/src/ServerManager.Common/Model/PlayerUserList.cs b/src/ServerManager.Common/Model/PlayerUserList.cs index 44e6a076..fadb48b6 100644 --- a/src/ServerManager.Common/Model/PlayerUserList.cs +++ b/src/ServerManager.Common/Model/PlayerUserList.cs @@ -1,4 +1,5 @@ -using System.Collections.ObjectModel; +using System.Collections.Generic; +using System.Collections.ObjectModel; using System.Linq; using System.Runtime.Serialization; @@ -22,7 +23,7 @@ namespace ServerManagerTool.Common.Model } } - public static PlayerUserList GetList(SteamUserDetailResponse response, string[] ids) + public static PlayerUserList GetList(SteamUserDetailResponse response, IEnumerable ids) { var result = new PlayerUserList(); if (ids != null) @@ -68,16 +69,15 @@ namespace ServerManagerTool.Common.Model public void Remove(string steamId) { - var items = this.Where(i => i.PlayerId.Equals(steamId, System.StringComparison.OrdinalIgnoreCase)).ToArray(); - foreach (var item in items) + foreach (var item in this.Where(i => i.PlayerId.Equals(steamId, System.StringComparison.OrdinalIgnoreCase))) { this.Remove(item); } } - public string[] ToArray() + public IEnumerable ToEnumerable() { - return this.Select(i => i.PlayerId).ToArray(); + return this.Select(i => i.PlayerId); } public string ToDelimitedString(string delimiter) diff --git a/src/ServerManager.Common/Model/SteamCmdManifestDetailsResult.cs b/src/ServerManager.Common/Model/SteamCmdManifestDetailsResult.cs index f6549537..bcc02796 100644 --- a/src/ServerManager.Common/Model/SteamCmdManifestDetailsResult.cs +++ b/src/ServerManager.Common/Model/SteamCmdManifestDetailsResult.cs @@ -18,7 +18,7 @@ namespace ServerManagerTool.Common.Model var tableValue = value as VdfTable; if (tableValue != null && tableValue.Count > 0) { - var betaKeyItems = tableValue.Where(v => v.Name.Equals("betakey", StringComparison.OrdinalIgnoreCase)).ToArray(); + var betaKeyItems = tableValue.Where(v => v.Name.Equals("betakey", StringComparison.OrdinalIgnoreCase)); foreach (var item in betaKeyItems) { tableValue.Remove(item); diff --git a/src/ServerManager.Common/Serialization/BaseSystemIniFile.cs b/src/ServerManager.Common/Serialization/BaseSystemIniFile.cs index 7956d173..b4d89f88 100644 --- a/src/ServerManager.Common/Serialization/BaseSystemIniFile.cs +++ b/src/ServerManager.Common/Serialization/BaseSystemIniFile.cs @@ -32,29 +32,38 @@ namespace ServerManagerTool.Common.Serialization get; } - public void Deserialize(object obj, Enum[] exclusions) + public void Deserialize(object obj, IEnumerable exclusions) { var iniFiles = new Dictionary(); - var fields = obj.GetType().GetProperties().Where(f => f.IsDefined(typeof(BaseIniFileEntryAttribute), false)); + var fields = obj.GetType() + .GetProperties() + .Where(f => f.IsDefined(typeof(BaseIniFileEntryAttribute), false)); if (exclusions == null) + { exclusions = new Enum[0]; + } foreach (var field in fields) { - var attributes = field.GetCustomAttributes(typeof(BaseIniFileEntryAttribute), false); - foreach (var attr in attributes.OfType()) + var attributes = field + .GetCustomAttributes(typeof(BaseIniFileEntryAttribute), false) + .OfType() + .Where(a => !exclusions.Contains(a.Category)); + + foreach (var attr in attributes) { if (exclusions.Contains(attr.Category)) + { continue; + } try { if (attr.IsCustom) { // this code is to handle custom sections - var collection = field.GetValue(obj) as IIniSectionCollection; - if (collection != null) + if (field.GetValue(obj) is IIniSectionCollection collection) { ReadFile(iniFiles, attr.File); @@ -77,61 +86,76 @@ namespace ServerManagerTool.Common.Serialization } else { - var iniValue = ReadValue(iniFiles, attr.File, attr.Section, keyName); - var fieldType = field.PropertyType; - var collection = field.GetValue(obj) as IIniValuesCollection; - - if (collection != null) + if (field.GetValue(obj) is IIniValuesCollection collection) { var section = ReadSection(iniFiles, attr.File, attr.Section); - var filteredSection = collection.IsArray - ? section.Where(s => s.StartsWith(collection.IniCollectionKey + "[")) + var filteredSection = collection.IsArray + ? section.Where(s => s.StartsWith(collection.IniCollectionKey + "[")) : section.Where(s => s.StartsWith(collection.IniCollectionKey + "=")); collection.FromIniValues(filteredSection); } - else if (fieldType == typeof(string)) - { - var stringValue = iniValue; - if (attr.QuotedString == QuotedStringType.True) - { - // remove the leading and trailing quotes, if any - if (stringValue.StartsWith("\"")) - stringValue = stringValue.Substring(1); - if (stringValue.EndsWith("\"")) - stringValue = stringValue.Substring(0, stringValue.Length - 1); - } - else if (attr.QuotedString == QuotedStringType.Remove) - { - // remove the leading and trailing quotes, if any - if (stringValue.StartsWith("\"")) - stringValue = stringValue.Substring(1); - if (stringValue.EndsWith("\"")) - stringValue = stringValue.Substring(0, stringValue.Length - 1); - } - if (attr.Multiline) - { - stringValue = stringValue.Replace(attr.MultilineSeparator, Environment.NewLine); - } - field.SetValue(obj, stringValue); - } else { - if (string.IsNullOrWhiteSpace(iniValue)) + var iniValue = ReadValue(iniFiles, attr.File, attr.Section, keyName); + + var fieldType = field.PropertyType; + if (fieldType == typeof(string)) { - // Skip non-string values which are not found - continue; + var stringValue = iniValue; + if (attr.QuotedString == QuotedStringType.True) + { + // remove the leading and trailing quotes, if any + if (stringValue.StartsWith("\"")) + { + stringValue = stringValue.Substring(1); + } + + if (stringValue.EndsWith("\"")) + { + stringValue = stringValue.Substring(0, stringValue.Length - 1); + } + } + else if (attr.QuotedString == QuotedStringType.Remove) + { + // remove the leading and trailing quotes, if any + if (stringValue.StartsWith("\"")) + { + stringValue = stringValue.Substring(1); + } + + if (stringValue.EndsWith("\"")) + { + stringValue = stringValue.Substring(0, stringValue.Length - 1); + } + } + if (attr.Multiline) + { + stringValue = stringValue.Replace(attr.MultilineSeparator, Environment.NewLine); + } + field.SetValue(obj, stringValue); + } + else + { + if (string.IsNullOrWhiteSpace(iniValue)) + { + // Skip non-string values which are not found + continue; + } + + // Update the ConditionedOn flag, if this field has one. + if (!string.IsNullOrWhiteSpace(attr.ConditionedOn)) + { + var conditionField = obj.GetType().GetProperty(attr.ConditionedOn); + conditionField.SetValue(obj, true); + } + + var valueSet = StringUtils.SetPropertyValue(iniValue, obj, field, attr); + if (!valueSet) + { + throw new ArgumentException($"Unexpected field type {fieldType} for INI key {keyName} in section {attr.Section}."); + } } - // Update the ConditionedOn flag, if this field has one. - if (!string.IsNullOrWhiteSpace(attr.ConditionedOn)) - { - var conditionField = obj.GetType().GetProperty(attr.ConditionedOn); - conditionField.SetValue(obj, true); - } - - var valueSet = StringUtils.SetPropertyValue(iniValue, obj, field, attr); - if (!valueSet) - throw new ArgumentException($"Unexpected field type {fieldType} for INI key {keyName} in section {attr.Section}."); } } } @@ -144,29 +168,33 @@ namespace ServerManagerTool.Common.Serialization } } - public void Serialize(object obj, Enum[] exclusions) + public void Serialize(object obj, IEnumerable exclusions) { var iniFiles = new Dictionary(); - var fields = obj.GetType().GetProperties().Where(f => f.IsDefined(typeof(BaseIniFileEntryAttribute), false)); + var fields = obj.GetType() + .GetProperties() + .Where(f => f.IsDefined(typeof(BaseIniFileEntryAttribute), false)); if (exclusions == null) + { exclusions = new Enum[0]; + } foreach (var field in fields) { - var attributes = field.GetCustomAttributes(typeof(BaseIniFileEntryAttribute), false).OfType(); + var attributes = field + .GetCustomAttributes(typeof(BaseIniFileEntryAttribute), false) + .OfType() + .Where(a => !exclusions.Contains(a.Category)); + foreach (var attr in attributes) { - if (exclusions.Contains(attr.Category)) - continue; - try { if (attr.IsCustom) { // this code is to handle custom sections - var collection = field.GetValue(obj) as IIniSectionCollection; - if (collection != null) + if (field.GetValue(obj) is IIniSectionCollection collection) { collection.Update(); @@ -177,7 +205,7 @@ namespace ServerManagerTool.Common.Serialization if (section.IsEnabled) { - WriteSection(iniFiles, attr.File, section.IniCollectionKey, section.ToIniValues().ToArray()); + WriteSection(iniFiles, attr.File, section.IniCollectionKey, section.ToIniValues()); } } } @@ -200,8 +228,7 @@ namespace ServerManagerTool.Common.Serialization { var section = ReadSection(iniFiles, attr.File, attr.Section); var filteredSection = section - .Where(s => !s.StartsWith(collection.IniCollectionKey + (collection.IsArray ? "[" : "="))) - .ToArray(); + .Where(s => !s.StartsWith(collection.IniCollectionKey + (collection.IsArray ? "[" : "="))); WriteSection(iniFiles, attr.File, attr.Section, filteredSection); } @@ -286,7 +313,7 @@ namespace ServerManagerTool.Common.Serialization result = result.Concat(collection.ToIniValues()); } - WriteSection(iniFiles, attr.File, attr.Section, result?.ToArray()); + WriteSection(iniFiles, attr.File, attr.Section, result); } } else @@ -353,74 +380,94 @@ namespace ServerManagerTool.Common.Serialization SaveFiles(iniFiles); } - public string[] ReadSection(Enum iniFile, Enum section) + public IEnumerable ReadSection(Enum iniFile, Enum section) { return ReadSection(iniFile, SectionNames[section]); } - public string[] ReadSection(Enum iniFile, string sectionName) + public IEnumerable ReadSection(Enum iniFile, string sectionName) { var file = Path.Combine(this.BasePath, FileNames[iniFile]); return IniFileUtils.ReadSection(file, sectionName); } - public void WriteSection(Enum iniFile, Enum section, string[] values) + public void WriteSection(Enum iniFile, Enum section, IEnumerable values) { WriteSection(iniFile, SectionNames[section], values); } - public void WriteSection(Enum iniFile, string sectionName, string[] values) + public void WriteSection(Enum iniFile, string sectionName, IEnumerable values) { var file = Path.Combine(this.BasePath, FileNames[iniFile]); - var result = IniFileUtils.WriteSection(file, sectionName, values); + IniFileUtils.WriteSection(file, sectionName, values); } - private string[] ReadCustomSectionNames(Dictionary iniFiles, Enum iniFile) + private IEnumerable ReadCustomSectionNames(Dictionary iniFiles, Enum iniFile) { - ReadFile(iniFiles, iniFile); - if (!iniFiles.ContainsKey(FileNames[iniFile])) - return new string[0]; + { + ReadFile(iniFiles, iniFile); - return iniFiles[FileNames[iniFile]].Sections.Select(s => s.SectionName).Where(s => !SectionNames.ContainsValue(s)).ToArray(); + if (!iniFiles.ContainsKey(FileNames[iniFile])) + { + return new string[0]; + } + } + + return iniFiles[FileNames[iniFile]].Sections.Select(s => s.SectionName).Where(s => !SectionNames.ContainsValue(s)); } - private string[] ReadSection(Dictionary iniFiles, Enum iniFile, Enum section) + private IEnumerable ReadSection(Dictionary iniFiles, Enum iniFile, Enum section) { return ReadSection(iniFiles, iniFile, SectionNames[section]); } - private string[] ReadSection(Dictionary iniFiles, Enum iniFile, string sectionName) + private IEnumerable ReadSection(Dictionary iniFiles, Enum iniFile, string sectionName) { - ReadFile(iniFiles, iniFile); - if (!iniFiles.ContainsKey(FileNames[iniFile])) - return new string[0]; + { + ReadFile(iniFiles, iniFile); - return iniFiles[FileNames[iniFile]].GetSection(sectionName)?.KeysToStringArray() ?? new string[0]; + if (!iniFiles.ContainsKey(FileNames[iniFile])) + { + return new string[0]; + } + } + + return iniFiles[FileNames[iniFile]].GetSection(sectionName)?.KeysToStringEnumerable() ?? new string[0]; } private string ReadValue(Dictionary iniFiles, Enum iniFile, Enum section, string keyName) { - ReadFile(iniFiles, iniFile); - if (!iniFiles.ContainsKey(FileNames[iniFile])) - return string.Empty; + { + ReadFile(iniFiles, iniFile); + + if (!iniFiles.ContainsKey(FileNames[iniFile])) + { + return string.Empty; + } + } return iniFiles[FileNames[iniFile]].GetKey(SectionNames[section], keyName)?.KeyValue ?? string.Empty; } - private void WriteSection(Dictionary iniFiles, Enum iniFile, Enum section, string[] values) + private void WriteSection(Dictionary iniFiles, Enum iniFile, Enum section, IEnumerable values) { WriteSection(iniFiles, iniFile, SectionNames[section], values); } - private void WriteSection(Dictionary iniFiles, Enum iniFile, string sectionName, string[] values) + private void WriteSection(Dictionary iniFiles, Enum iniFile, string sectionName, IEnumerable values) { - ReadFile(iniFiles, iniFile); - if (!iniFiles.ContainsKey(FileNames[iniFile])) - return; + { + ReadFile(iniFiles, iniFile); + + if (!iniFiles.ContainsKey(FileNames[iniFile])) + { + return; + } + } iniFiles[FileNames[iniFile]].WriteSection(sectionName, values); } @@ -432,10 +479,15 @@ namespace ServerManagerTool.Common.Serialization private void WriteValue(Dictionary iniFiles, Enum iniFile, string sectionName, string keyName, string keyValue) { - ReadFile(iniFiles, iniFile); - if (!iniFiles.ContainsKey(FileNames[iniFile])) - return; + { + ReadFile(iniFiles, iniFile); + + if (!iniFiles.ContainsKey(FileNames[iniFile])) + { + return; + } + } iniFiles[FileNames[iniFile]].WriteKey(sectionName, keyName, keyValue); } @@ -454,7 +506,7 @@ namespace ServerManagerTool.Common.Serialization foreach (var iniFile in iniFiles) { var file = Path.Combine(this.BasePath, iniFile.Key); - var result = IniFileUtils.SaveToFile(file, iniFile.Value); + IniFileUtils.SaveToFile(file, iniFile.Value); } } } diff --git a/src/ServerManager.Common/Serialization/IniFile.cs b/src/ServerManager.Common/Serialization/IniFile.cs index 8d7e856d..15c6fdd1 100644 --- a/src/ServerManager.Common/Serialization/IniFile.cs +++ b/src/ServerManager.Common/Serialization/IniFile.cs @@ -68,7 +68,7 @@ namespace ServerManagerTool.Common.Serialization Sections.Remove(section); } - public bool WriteSection(string sectionName, string[] keysValuePairs) + public bool WriteSection(string sectionName, IEnumerable keysValuePairs) { if (sectionName == null) return false; @@ -186,7 +186,7 @@ namespace ServerManagerTool.Common.Serialization { result.AppendLine($"[{section.SectionName}]"); - foreach (var keyString in section.KeysToStringArray()) + foreach (var keyString in section.KeysToStringEnumerable()) { result.AppendLine(keyString); } diff --git a/src/ServerManager.Common/Serialization/IniSection.cs b/src/ServerManager.Common/Serialization/IniSection.cs index 72c360a2..8a848b01 100644 --- a/src/ServerManager.Common/Serialization/IniSection.cs +++ b/src/ServerManager.Common/Serialization/IniSection.cs @@ -41,9 +41,9 @@ namespace ServerManagerTool.Common.Serialization return Keys?.FirstOrDefault(s => s.KeyName.Equals(keyName, StringComparison.OrdinalIgnoreCase)); } - public string[] KeysToStringArray() + public IEnumerable KeysToStringEnumerable() { - return Keys.Select(k => k.ToString()).ToArray(); + return Keys.Select(k => k.ToString()); } public void RemoveKey(string keyName) diff --git a/src/ServerManager.Common/ServerManager.Common.csproj b/src/ServerManager.Common/ServerManager.Common.csproj index b76aa89e..ad48231c 100644 --- a/src/ServerManager.Common/ServerManager.Common.csproj +++ b/src/ServerManager.Common/ServerManager.Common.csproj @@ -43,4 +43,17 @@ + + + True + True + CommonConfig.settings + + + + + PublicSettingsSingleFileGenerator + CommonConfig.Designer.cs + + \ No newline at end of file diff --git a/src/ServerManager.Common/Utils/IniFileUtils.cs b/src/ServerManager.Common/Utils/IniFileUtils.cs index ebfacfcd..1cbc29ab 100644 --- a/src/ServerManager.Common/Utils/IniFileUtils.cs +++ b/src/ServerManager.Common/Utils/IniFileUtils.cs @@ -1,5 +1,6 @@ using ServerManagerTool.Common.Serialization; using System; +using System.Collections.Generic; using System.IO; using System.Text.RegularExpressions; @@ -13,13 +14,13 @@ namespace ServerManagerTool.Common.Utils /// The name of the initialization file. /// The name of the section in the initialization file. /// A string array containing the key name and value pairs associated with the named section. - public static string[] ReadSection(string file, string sectionName) + public static IEnumerable ReadSection(string file, string sectionName) { if (sectionName == null) return new string[0]; var iniFile = ReadFromFile(file); - return iniFile?.GetSection(sectionName)?.KeysToStringArray() ?? new string[0]; + return iniFile?.GetSection(sectionName)?.KeysToStringEnumerable() ?? new string[0]; } /// @@ -46,7 +47,7 @@ namespace ServerManagerTool.Common.Utils /// The name of the section in which data is written. /// An array of key names and associated values that are to be written to the named section. /// True if the function succeeds; otherwise False. - public static bool WriteSection(string file, string sectionName, string[] keysValuePairs) + public static bool WriteSection(string file, string sectionName, IEnumerable keysValuePairs) { if (sectionName == null) return false; @@ -85,10 +86,14 @@ namespace ServerManagerTool.Common.Utils public static IniFile ReadFromFile(string file) { if (string.IsNullOrWhiteSpace(file)) + { return null; + } if (!File.Exists(file)) + { return new IniFile(); + } var iniFile = new IniFile(); @@ -96,18 +101,24 @@ namespace ServerManagerTool.Common.Utils { while (!reader.EndOfStream) { - var line = reader.ReadLine(); + var line = reader.ReadLine().Trim(); if (string.IsNullOrWhiteSpace(line) || line.StartsWith(";") || line.StartsWith("#")) + { continue; + } - var sectionName = Regex.Match(line, @"(?<=^\[).*(?=\]$)").Value.Trim(); + var sectionName = string.Empty; + if (line.StartsWith("[") && line.EndsWith("]")) + { + sectionName = Regex.Match(line, @"(?<=^\[).*(?=\]$)").Value.Trim(); + } var section = iniFile.AddSection(sectionName); - if (section != null) - continue; - - iniFile.AddKey(line); + if (section is null) + { + iniFile.AddKey(line); + } } reader.Close(); @@ -155,7 +166,7 @@ namespace ServerManagerTool.Common.Utils { writer.WriteLine($"[{section.SectionName}]"); - foreach (var keyString in section.KeysToStringArray()) + foreach (var keyString in section.KeysToStringEnumerable()) { writer.WriteLine(keyString); } diff --git a/src/ServerManager.Common/Utils/NetworkUtils.cs b/src/ServerManager.Common/Utils/NetworkUtils.cs index 390a9329..a0e255bb 100644 --- a/src/ServerManager.Common/Utils/NetworkUtils.cs +++ b/src/ServerManager.Common/Utils/NetworkUtils.cs @@ -81,8 +81,14 @@ namespace ServerManagerTool.Common.Utils { try { - var publicIP = webClient.DownloadString(CommonConfig.Default.PublicIPCheckUrl); - if (IPAddress.TryParse(publicIP, out IPAddress address)) + var publicIP = webClient.DownloadString(CommonConfig.Default.PublicIPCheckUrl1); + if (IPAddress.TryParse(publicIP, out IPAddress address1)) + { + return publicIP; + } + + publicIP = webClient.DownloadString(CommonConfig.Default.PublicIPCheckUrl2); + if (IPAddress.TryParse(publicIP, out IPAddress address2)) { return publicIP; } @@ -102,8 +108,14 @@ namespace ServerManagerTool.Common.Utils { try { - var publicIP = await webClient.DownloadStringTaskAsync(CommonConfig.Default.PublicIPCheckUrl); - if (IPAddress.TryParse(publicIP, out IPAddress address)) + var publicIP = await webClient.DownloadStringTaskAsync(CommonConfig.Default.PublicIPCheckUrl1); + if (IPAddress.TryParse(publicIP, out IPAddress address1)) + { + return publicIP; + } + + publicIP = await webClient.DownloadStringTaskAsync(CommonConfig.Default.PublicIPCheckUrl2); + if (IPAddress.TryParse(publicIP, out IPAddress address2)) { return publicIP; } diff --git a/src/ServerManager.Common/Utils/ProcessUtils.cs b/src/ServerManager.Common/Utils/ProcessUtils.cs index 42009d28..4e5fbad9 100644 --- a/src/ServerManager.Common/Utils/ProcessUtils.cs +++ b/src/ServerManager.Common/Utils/ProcessUtils.cs @@ -285,7 +285,7 @@ namespace ServerManagerTool.Common.Utils } } - public static BigInteger[] GetProcessorAffinityList() + public static IEnumerable GetProcessorAffinityList() { var processorCount = ProcessorCount; var results = new List(processorCount + 1); @@ -295,7 +295,7 @@ namespace ServerManagerTool.Common.Utils { results.Add((BigInteger)Math.Pow(2, index)); } - return results.ToArray(); + return results; } public static string[] GetProcessPriorityList() diff --git a/src/ServerManager.Common/Utils/SettingsUtils.cs b/src/ServerManager.Common/Utils/SettingsUtils.cs index e5f4f63d..6e727f9f 100644 --- a/src/ServerManager.Common/Utils/SettingsUtils.cs +++ b/src/ServerManager.Common/Utils/SettingsUtils.cs @@ -49,7 +49,7 @@ namespace ServerManagerTool.Common.Utils try { - var filesToDelete = new DirectoryInfo(backupPath).GetFiles($"{settingsFileName}_*{settingsFileExt}").Where(f => f.LastWriteTimeUtc.AddDays(7) < DateTime.UtcNow).ToArray(); + var filesToDelete = new DirectoryInfo(backupPath).GetFiles($"{settingsFileName}_*{settingsFileExt}").Where(f => f.LastWriteTimeUtc.AddDays(7) < DateTime.UtcNow); foreach (var fileToDelete in filesToDelete) { try diff --git a/src/ServerManager.Common/Utils/TaskSchedulerUtils.cs b/src/ServerManager.Common/Utils/TaskSchedulerUtils.cs index 5b961c1d..9114452c 100644 --- a/src/ServerManager.Common/Utils/TaskSchedulerUtils.cs +++ b/src/ServerManager.Common/Utils/TaskSchedulerUtils.cs @@ -1,5 +1,6 @@ using Microsoft.Win32.TaskScheduler; using NLog; +using ServerManagerTool.Common.Extensions; using System; using System.Diagnostics; using System.Linq; @@ -202,7 +203,7 @@ namespace ServerManagerTool.Common.Utils // Add/Edit the trigger that will fire every x minutes var triggers = taskDefinition.Triggers.OfType(); - if (triggers.Count() == 0) + if (triggers.IsEmpty()) { var trigger = new TimeTrigger { @@ -445,7 +446,7 @@ namespace ServerManagerTool.Common.Utils if (onBoot) { var triggers = taskDefinition.Triggers.OfType(); - if (triggers.Count() == 0) + if (triggers.IsEmpty()) { var trigger = new BootTrigger { @@ -465,7 +466,7 @@ namespace ServerManagerTool.Common.Utils else { var triggers = taskDefinition.Triggers.OfType(); - if (triggers.Count() == 0) + if (triggers.IsEmpty()) { var trigger = new LogonTrigger { @@ -572,7 +573,7 @@ namespace ServerManagerTool.Common.Utils // Add/Edit the trigger that will fire every x minutes var triggers = taskDefinition.Triggers.OfType(); - if (triggers.Count() == 0) + if (triggers.IsEmpty()) { var trigger = new TimeTrigger { diff --git a/src/ServerManager.Common/Utils/ZipUtils.cs b/src/ServerManager.Common/Utils/ZipUtils.cs index f246bae6..df1acf3b 100644 --- a/src/ServerManager.Common/Utils/ZipUtils.cs +++ b/src/ServerManager.Common/Utils/ZipUtils.cs @@ -1,5 +1,7 @@ using Ionic.Zip; +using ServerManagerTool.Common.Extensions; using System; +using System.Collections.Generic; using System.IO; using System.Linq; @@ -68,11 +70,12 @@ namespace ServerManagerTool.Common.Utils } } - public static void UpdateFiles(string zipFile, string[] filesToZip, string comment = "", bool preserveDirHierarchy = true, string directoryPathInArchive = "") + public static void UpdateFiles(string zipFile, IEnumerable filesToZip, string comment = "", bool preserveDirHierarchy = true, string directoryPathInArchive = "") { if (string.IsNullOrWhiteSpace(zipFile)) throw new ArgumentNullException(nameof(zipFile)); - if (filesToZip == null || filesToZip.Length == 0) + + if (filesToZip is null || filesToZip.IsEmpty()) return; if (!File.Exists(zipFile)) @@ -160,11 +163,12 @@ namespace ServerManagerTool.Common.Utils } } - public static void ZipFiles(string zipFile, string[] filesToZip, string comment = "", bool preserveDirHierarchy = true, string directoryPathInArchive = "") + public static void ZipFiles(string zipFile, IEnumerable filesToZip, string comment = "", bool preserveDirHierarchy = true, string directoryPathInArchive = "") { if (string.IsNullOrWhiteSpace(zipFile)) throw new ArgumentNullException(nameof(zipFile)); - if (filesToZip == null || filesToZip.Length == 0) + + if (filesToZip is null || filesToZip.IsEmpty()) throw new ArgumentNullException(nameof(filesToZip)); using (var zip = new ZipFile(zipFile)) diff --git a/src/ServerManager.Common/app.config b/src/ServerManager.Common/app.config index 9f599428..2160090b 100644 --- a/src/ServerManager.Common/app.config +++ b/src/ServerManager.Common/app.config @@ -10,9 +10,6 @@ - - http://whatismyip.akamai.com/ - SteamCMD @@ -29,7 +26,7 @@ +login anonymous +quit - + https://steamcommunity.com/dev/apikey @@ -40,15 +37,21 @@ Steam + + https://api.ipify.org + + + http://whatismyip.akamai.com/ + - + - + True diff --git a/src/ServerManager.Discord/Delegates/HandleCommandDelegate.cs b/src/ServerManager.Discord/Delegates/HandleCommandDelegate.cs index e4301ce4..ef626b53 100644 --- a/src/ServerManager.Discord/Delegates/HandleCommandDelegate.cs +++ b/src/ServerManager.Discord/Delegates/HandleCommandDelegate.cs @@ -4,5 +4,5 @@ using System.Threading; namespace ServerManagerTool.DiscordBot.Delegates { - public delegate IList HandleCommandDelegate(CommandType commandType, string serverId, string channelId, string profileId, CancellationToken token); + public delegate IList HandleCommandDelegate(CommandType commandType, string serverId, string channelId, string profileIdOrAlias, CancellationToken token); } diff --git a/src/ServerManager.Discord/DiscordBot.cs b/src/ServerManager.Discord/DiscordBot.cs deleted file mode 100644 index 63cc1c7a..00000000 --- a/src/ServerManager.Discord/DiscordBot.cs +++ /dev/null @@ -1,9 +0,0 @@ -using ServerManagerTool.DiscordBot.Delegates; - -namespace ServerManagerTool.DiscordBot -{ - public static class DiscordBot - { - public const string PREFIX_DELIMITER = "!"; - } -} diff --git a/src/ServerManager.Discord/Enums/BotStatus.cs b/src/ServerManager.Discord/Enums/BotStatus.cs new file mode 100644 index 00000000..3ae2f730 --- /dev/null +++ b/src/ServerManager.Discord/Enums/BotStatus.cs @@ -0,0 +1,10 @@ +namespace ServerManagerTool.DiscordBot.Enums +{ + public enum BotState + { + Unknown, + Disabled, + Stopped, + Running, + } +} diff --git a/src/ServerManager.Discord/Enums/LogLevel.cs b/src/ServerManager.Discord/Enums/LogLevel.cs new file mode 100644 index 00000000..8c18aeae --- /dev/null +++ b/src/ServerManager.Discord/Enums/LogLevel.cs @@ -0,0 +1,25 @@ +using Discord; +using System; + +namespace ServerManagerTool.DiscordBot.Enums +{ + public enum LogLevel + { + Critical = 0, + Error = 1, + Warning = 2, + Info = 3, + Verbose = 4, + Debug = 5 + } + + public class LogLevelHelper + { + public static LogSeverity GetLogSeverity(LogLevel logLevel) + { + if (Enum.TryParse(logLevel.ToString(), out LogSeverity logSeverity)) + return logSeverity; + return LogSeverity.Info; + } + } +} diff --git a/src/ServerManager.Discord/Interfaces/IServerManagerBot.cs b/src/ServerManager.Discord/Interfaces/IServerManagerBot.cs index f44c1878..a4ef8f1e 100644 --- a/src/ServerManager.Discord/Interfaces/IServerManagerBot.cs +++ b/src/ServerManager.Discord/Interfaces/IServerManagerBot.cs @@ -1,4 +1,6 @@ using ServerManagerTool.DiscordBot.Delegates; +using ServerManagerTool.DiscordBot.Enums; +using System.Collections.Generic; using System.Threading; using System.Threading.Tasks; @@ -8,6 +10,6 @@ namespace ServerManagerTool.DiscordBot.Interfaces { CancellationToken Token { get; } - Task StartAsync(string discordToken, string commandPrefix, string dataDirectory, HandleCommandDelegate handleCommandCallback, HandleTranslationDelegate handleTranslationCallback, CancellationToken token); + Task StartAsync(LogLevel logLevel, string discordToken, string commandPrefix, string dataDirectory, bool allowAllBots, IEnumerable botWhitelist, HandleCommandDelegate handleCommandCallback, HandleTranslationDelegate handleTranslationCallback, CancellationToken token); } } diff --git a/src/ServerManager.Discord/Models/DiscordBotConfig.cs b/src/ServerManager.Discord/Models/DiscordBotConfig.cs new file mode 100644 index 00000000..c6a0ca59 --- /dev/null +++ b/src/ServerManager.Discord/Models/DiscordBotConfig.cs @@ -0,0 +1,11 @@ +using System.Collections.Generic; + +namespace ServerManagerTool.DiscordBot.Models +{ + public class DiscordBotConfig + { + public bool AllowAllBots { get; set; } = false; + + public List DiscordBotWhitelists { get; set; } = new List(); + } +} diff --git a/src/ServerManager.Discord/Models/DiscordBotWhitelist.cs b/src/ServerManager.Discord/Models/DiscordBotWhitelist.cs new file mode 100644 index 00000000..4fb932b6 --- /dev/null +++ b/src/ServerManager.Discord/Models/DiscordBotWhitelist.cs @@ -0,0 +1,7 @@ +namespace ServerManagerTool.DiscordBot.Models +{ + public class DiscordBotWhitelist + { + public string BotId { get; set; } = string.Empty; + } +} diff --git a/src/ServerManager.Discord/Modules/ServerCommandModule.cs b/src/ServerManager.Discord/Modules/ServerCommandModule.cs index ea993166..b2bd5461 100644 --- a/src/ServerManager.Discord/Modules/ServerCommandModule.cs +++ b/src/ServerManager.Discord/Modules/ServerCommandModule.cs @@ -29,16 +29,16 @@ namespace ServerManagerTool.DiscordBot.Modules [Command("backup", RunMode = RunMode.Async)] [Summary("Backup the server")] - [Remarks("backup profileId")] + [Remarks("backup profileId|alias")] [RequireBotPermission(ChannelPermission.ViewChannel | ChannelPermission.SendMessages)] - public async Task BackupServerAsync(string profileId) + public async Task BackupServerAsync(string profileIdOrAlias) { try { var serverId = Context?.Guild?.Id.ToString() ?? string.Empty; var channelId = Context?.Channel?.Id.ToString() ?? string.Empty; - var response = _handleCommandCallback?.Invoke(CommandType.Backup, serverId, channelId, profileId, _serverManagerBot.Token); + var response = _handleCommandCallback?.Invoke(CommandType.Backup, serverId, channelId, profileIdOrAlias, _serverManagerBot.Token); if (response is null) { await ReplyAsync("No servers associated with this channel."); @@ -60,16 +60,16 @@ namespace ServerManagerTool.DiscordBot.Modules [Command("restart", RunMode = RunMode.Async)] [Summary("Restart the server")] - [Remarks("restart profileId")] + [Remarks("restart profileId|alias")] [RequireBotPermission(ChannelPermission.ViewChannel | ChannelPermission.SendMessages)] - public async Task RestartServerAsync(string profileId) + public async Task RestartServerAsync(string profileIdOrAlias) { try { var serverId = Context?.Guild?.Id.ToString() ?? string.Empty; var channelId = Context?.Channel?.Id.ToString() ?? string.Empty; - var response = _handleCommandCallback?.Invoke(CommandType.Restart, serverId, channelId, profileId, _serverManagerBot.Token); + var response = _handleCommandCallback?.Invoke(CommandType.Restart, serverId, channelId, profileIdOrAlias, _serverManagerBot.Token); if (response is null) { await ReplyAsync("No servers associated with this channel."); @@ -91,16 +91,16 @@ namespace ServerManagerTool.DiscordBot.Modules [Command("shutdown", RunMode = RunMode.Async)] [Summary("Shuts down the server properly")] - [Remarks("shutdown profileId")] + [Remarks("shutdown profileId|alias")] [RequireBotPermission(ChannelPermission.ViewChannel | ChannelPermission.SendMessages)] - public async Task ShutdownServerAsync(string profileId) + public async Task ShutdownServerAsync(string profileIdOrAlias) { try { var serverId = Context?.Guild?.Id.ToString() ?? string.Empty; var channelId = Context?.Channel?.Id.ToString() ?? string.Empty; - var response = _handleCommandCallback?.Invoke(CommandType.Shutdown, serverId, channelId, profileId, _serverManagerBot.Token); + var response = _handleCommandCallback?.Invoke(CommandType.Shutdown, serverId, channelId, profileIdOrAlias, _serverManagerBot.Token); if (response is null) { await ReplyAsync("No servers associated with this channel."); @@ -122,16 +122,16 @@ namespace ServerManagerTool.DiscordBot.Modules [Command("start", RunMode = RunMode.Async)] [Summary("Starts the server")] - [Remarks("start profileId")] + [Remarks("start profileId|alias")] [RequireBotPermission(ChannelPermission.ViewChannel | ChannelPermission.SendMessages)] - public async Task StartServerAsync(string profileId) + public async Task StartServerAsync(string profileIdOrAlias) { try { var serverId = Context?.Guild?.Id.ToString() ?? string.Empty; var channelId = Context?.Channel?.Id.ToString() ?? string.Empty; - var response = _handleCommandCallback?.Invoke(CommandType.Start, serverId, channelId, profileId, _serverManagerBot.Token); + var response = _handleCommandCallback?.Invoke(CommandType.Start, serverId, channelId, profileIdOrAlias, _serverManagerBot.Token); if (response is null) { await ReplyAsync("No servers associated with this channel."); @@ -153,16 +153,16 @@ namespace ServerManagerTool.DiscordBot.Modules [Command("stop", RunMode = RunMode.Async)] [Summary("Forcibly stops the server")] - [Remarks("stop profileId")] + [Remarks("stop profileId|alias")] [RequireBotPermission(ChannelPermission.ViewChannel | ChannelPermission.SendMessages)] - public async Task StopServerAsync(string profileId) + public async Task StopServerAsync(string profileIdOrAlias) { try { var serverId = Context?.Guild?.Id.ToString() ?? string.Empty; var channelId = Context?.Channel?.Id.ToString() ?? string.Empty; - var response = _handleCommandCallback?.Invoke(CommandType.Stop, serverId, channelId, profileId, _serverManagerBot.Token); + var response = _handleCommandCallback?.Invoke(CommandType.Stop, serverId, channelId, profileIdOrAlias, _serverManagerBot.Token); if (response is null) { await ReplyAsync("No servers associated with this channel."); @@ -184,16 +184,16 @@ namespace ServerManagerTool.DiscordBot.Modules [Command("update", RunMode = RunMode.Async)] [Summary("Updates the server")] - [Remarks("update profileId")] + [Remarks("update profileId|alias")] [RequireBotPermission(ChannelPermission.ViewChannel | ChannelPermission.SendMessages)] - public async Task UpdateServerAsync(string profileId) + public async Task UpdateServerAsync(string profileIdOrAlias) { try { var serverId = Context?.Guild?.Id.ToString() ?? string.Empty; var channelId = Context?.Channel?.Id.ToString() ?? string.Empty; - var response = _handleCommandCallback?.Invoke(CommandType.Update, serverId, channelId, profileId, _serverManagerBot.Token); + var response = _handleCommandCallback?.Invoke(CommandType.Update, serverId, channelId, profileIdOrAlias, _serverManagerBot.Token); if (response is null) { await ReplyAsync("No servers associated with this channel."); diff --git a/src/ServerManager.Discord/Modules/ServerQueryModule.cs b/src/ServerManager.Discord/Modules/ServerQueryModule.cs index a793c3b7..407486c3 100644 --- a/src/ServerManager.Discord/Modules/ServerQueryModule.cs +++ b/src/ServerManager.Discord/Modules/ServerQueryModule.cs @@ -38,16 +38,16 @@ namespace ServerManagerTool.DiscordBot.Modules [Command("info", RunMode = RunMode.Async)] [Summary("Poll server for information")] - [Remarks("info profileId")] + [Remarks("info profileId|alias")] [RequireBotPermission(ChannelPermission.ViewChannel | ChannelPermission.SendMessages)] - public async Task ServerInfoAsync(string profileId) + public async Task ServerInfoAsync(string profileIdOrAlias) { try { var serverId = Context?.Guild?.Id.ToString() ?? string.Empty; var channelId = Context?.Channel?.Id.ToString() ?? string.Empty; - var response = _handleCommandCallback?.Invoke(CommandType.Info, serverId, channelId, profileId, _serverManagerBot.Token); + var response = _handleCommandCallback?.Invoke(CommandType.Info, serverId, channelId, profileIdOrAlias, _serverManagerBot.Token); if (response is null) { await ReplyAsync("No servers associated with this channel."); @@ -109,16 +109,16 @@ namespace ServerManagerTool.DiscordBot.Modules [Command("status", RunMode = RunMode.Async)] [Summary("Poll server for status")] - [Remarks("status profileId")] + [Remarks("status profileId|alias")] [RequireBotPermission(ChannelPermission.ViewChannel | ChannelPermission.SendMessages)] - public async Task ServerStatusAsync(string profileId) + public async Task ServerStatusAsync(string profileIdOrAlias) { try { var serverId = Context?.Guild?.Id.ToString() ?? string.Empty; var channelId = Context?.Channel?.Id.ToString() ?? string.Empty; - var response = _handleCommandCallback?.Invoke(CommandType.Status, serverId, channelId, profileId, _serverManagerBot.Token); + var response = _handleCommandCallback?.Invoke(CommandType.Status, serverId, channelId, profileIdOrAlias, _serverManagerBot.Token); if (response is null) { await ReplyAsync("No servers associated with this channel."); diff --git a/src/ServerManager.Discord/ServerManagerBot.cs b/src/ServerManager.Discord/ServerManagerBot.cs index 6c7f0d63..41684c39 100644 --- a/src/ServerManager.Discord/ServerManagerBot.cs +++ b/src/ServerManager.Discord/ServerManagerBot.cs @@ -1,12 +1,13 @@ -using Discord; -using Discord.Addons.Interactive; +using Discord.Addons.Interactive; using Discord.Commands; using Discord.Net.Providers.WS4Net; using Discord.WebSocket; using Microsoft.Extensions.Configuration; using Microsoft.Extensions.DependencyInjection; using ServerManagerTool.DiscordBot.Delegates; +using ServerManagerTool.DiscordBot.Enums; using ServerManagerTool.DiscordBot.Interfaces; +using ServerManagerTool.DiscordBot.Models; using ServerManagerTool.DiscordBot.Services; using System; using System.Collections.Generic; @@ -27,35 +28,26 @@ namespace ServerManagerTool.DiscordBot public CancellationToken Token { get; private set; } public bool Started { get; private set; } - public async Task StartAsync(string discordToken, string commandPrefix, string dataDirectory, HandleCommandDelegate handleCommandCallback, HandleTranslationDelegate handleTranslationCallback, CancellationToken token) + public async Task StartAsync(LogLevel logLevel, string discordToken, string commandPrefix, string dataDirectory, bool allowAllBots, IEnumerable botWhitelist, HandleCommandDelegate handleCommandCallback, HandleTranslationDelegate handleTranslationCallback, CancellationToken token) { - if (Started) - { - return; - } - Started = true; - if (string.IsNullOrWhiteSpace(commandPrefix) || string.IsNullOrWhiteSpace(discordToken) || handleTranslationCallback is null || handleCommandCallback is null) { return; } + if (Started) + { + return; + } + + Started = true; Token = token; - if (commandPrefix.Any(c => !char.IsLetterOrDigit(c))) - { - throw new Exception("#DiscordBot_InvalidPrefixError"); - } - - if (!commandPrefix.EndsWith(DiscordBot.PREFIX_DELIMITER)) - { - commandPrefix += DiscordBot.PREFIX_DELIMITER; - } - var settings = new Dictionary { { "DiscordSettings:Token", discordToken }, { "DiscordSettings:Prefix", commandPrefix }, + { "DiscordSettings:LogLevel", logLevel.ToString() }, { "ServerManager:DataDirectory", dataDirectory }, }; @@ -66,12 +58,7 @@ namespace ServerManagerTool.DiscordBot var socketConfig = new DiscordSocketConfig { -#if DEBUG - LogLevel = LogSeverity.Verbose, -#else - LogLevel = LogSeverity.Info, -#endif - // Tell Discord.Net to cache 1000 messages per channel + LogLevel = LogLevelHelper.GetLogSeverity(logLevel), MessageCacheSize = 1000, }; if (Environment.OSVersion.Version < new Version(6, 2)) @@ -84,11 +71,14 @@ namespace ServerManagerTool.DiscordBot { // Force all commands to run async DefaultRunMode = RunMode.Async, -#if DEBUG - LogLevel = LogSeverity.Verbose, -#else - LogLevel = LogSeverity.Info, -#endif + LogLevel = LogLevelHelper.GetLogSeverity(logLevel), + CaseSensitiveCommands = false, + }; + + var discordBotConfig = new DiscordBotConfig + { + AllowAllBots = allowAllBots, + DiscordBotWhitelists = new List ( botWhitelist.Select(i => new DiscordBotWhitelist { BotId = i }) ), }; // Build the service provider @@ -105,6 +95,7 @@ namespace ServerManagerTool.DiscordBot .AddSingleton() .AddSingleton() .AddSingleton(config) + .AddSingleton(discordBotConfig) .AddSingleton(handleCommandCallback) .AddSingleton(handleTranslationCallback) .AddSingleton(this); @@ -132,6 +123,7 @@ namespace ServerManagerTool.DiscordBot } await provider?.GetRequiredService().StopAsync(); + Started = false; } } } diff --git a/src/ServerManager.Discord/Services/CommandHandlerService.cs b/src/ServerManager.Discord/Services/CommandHandlerService.cs index dcb2f917..70fc6474 100644 --- a/src/ServerManager.Discord/Services/CommandHandlerService.cs +++ b/src/ServerManager.Discord/Services/CommandHandlerService.cs @@ -1,7 +1,11 @@ -using Discord.Commands; +using Discord; +using Discord.Commands; using Discord.WebSocket; using Microsoft.Extensions.Configuration; +using ServerManagerTool.DiscordBot.Enums; +using ServerManagerTool.DiscordBot.Models; using System; +using System.Linq; using System.Threading.Tasks; namespace ServerManagerTool.DiscordBot.Services @@ -10,47 +14,71 @@ namespace ServerManagerTool.DiscordBot.Services { private readonly DiscordSocketClient _discord; private readonly CommandService _commands; + private readonly LoggingService _logger; private readonly IConfigurationRoot _config; private readonly IServiceProvider _provider; + private readonly DiscordBotConfig _botConfig; - public CommandHandlerService(DiscordSocketClient discord, CommandService commands, IConfigurationRoot config, IServiceProvider provider) + public CommandHandlerService(DiscordSocketClient discord, CommandService commands, LoggingService logger, IConfigurationRoot config, IServiceProvider provider, DiscordBotConfig botConfig) { _discord = discord; _commands = commands; + _logger = logger; _config = config; _provider = provider; - + _botConfig = botConfig ?? new DiscordBotConfig(); _discord.MessageReceived += OnMessageReceivedAsync; } private async Task OnMessageReceivedAsync(SocketMessage s) { - // Ensure the message is from a user/bot - var msg = s as SocketUserMessage; - if (msg is null) + if (LogLevel.Debug.ToString().Equals(_config["DiscordSettings:LogLevel"])) + await _logger?.OnLogAsync(new LogMessage(LogSeverity.Debug, MessageSource.System.ToString(), $"Intercepted the following message from {s.Author.Username} ({s.Author.Id}) - {s.Content}")); + + // Ensure the message is a valid user socket message + if (!(s is SocketUserMessage msg)) + return; + + // Ignore self + if (msg.Author.Id == _discord.CurrentUser.Id) { + if (LogLevel.Debug.ToString().Equals(_config["DiscordSettings:LogLevel"])) + await _logger?.OnLogAsync(new LogMessage(LogSeverity.Debug, MessageSource.System.ToString(), $"Message has come from this bot, message will be ignored.")); + return; } - // Ignore self when checking commands - if (msg.Author == _discord.CurrentUser) - { - return; - } - - //Tell bot to ignore itself. + // check if the author is a bot if (msg.Author.IsBot) - { - return; - } + if (_botConfig.AllowAllBots) + { + if (LogLevel.Debug.ToString().Equals(_config["DiscordSettings:LogLevel"])) + await _logger?.OnLogAsync(new LogMessage(LogSeverity.Debug, MessageSource.System.ToString(), $"Message has come from another bot, allow all bots enabled.")); + } + else + { + if (LogLevel.Debug.ToString().Equals(_config["DiscordSettings:LogLevel"])) + await _logger?.OnLogAsync(new LogMessage(LogSeverity.Debug, MessageSource.System.ToString(), $"Message has come from another bot, checking if bot is in the whitelist.")); - // Create the command context - var context = new SocketCommandContext(_discord, msg); + if (!_botConfig.DiscordBotWhitelists.Any(b => b.BotId.Equals(msg.Author.Id.ToString()))) + { + if (LogLevel.Debug.ToString().Equals(_config["DiscordSettings:LogLevel"])) + await _logger?.OnLogAsync(new LogMessage(LogSeverity.Debug, MessageSource.System.ToString(), $"Message has come from another bot, bot is not in the whitelist, message will be ignored.")); + + return; + } + } // Check if the message has a valid command prefix var argPos = 0; - if (msg.HasStringPrefix(_config["DiscordSettings:Prefix"], ref argPos) || msg.HasMentionPrefix(_discord.CurrentUser, ref argPos)) + if (msg.HasStringPrefix(_config["DiscordSettings:Prefix"], ref argPos, StringComparison.OrdinalIgnoreCase) || msg.HasMentionPrefix(_discord.CurrentUser, ref argPos)) { + if (LogLevel.Debug.ToString().Equals(_config["DiscordSettings:LogLevel"])) + await _logger?.OnLogAsync(new LogMessage(LogSeverity.Debug, MessageSource.System.ToString(), $"Message prefix matched, message will be processed.")); + + // Create the command context + var context = new SocketCommandContext(_discord, msg); + // Execute the command var result = await _commands.ExecuteAsync(context, argPos, _provider); diff --git a/src/ServerManager.Discord/Services/LoggingService.cs b/src/ServerManager.Discord/Services/LoggingService.cs index 03b0d34e..83a27406 100644 --- a/src/ServerManager.Discord/Services/LoggingService.cs +++ b/src/ServerManager.Discord/Services/LoggingService.cs @@ -31,7 +31,7 @@ namespace ServerManagerTool.DiscordBot.Services _commands.Log += OnLogAsync; } - private async Task OnLogAsync(LogMessage message) + internal async Task OnLogAsync(LogMessage message) { // Create the log directory if it doesn't exist if (!Directory.Exists(LogDirectory)) diff --git a/src/ServerManager.Updater/Extensions/IEnumerableExtensions.cs b/src/ServerManager.Updater/Extensions/IEnumerableExtensions.cs new file mode 100644 index 00000000..50db86f1 --- /dev/null +++ b/src/ServerManager.Updater/Extensions/IEnumerableExtensions.cs @@ -0,0 +1,47 @@ +using System; +using System.Collections.Generic; + +namespace ServerManagerTool.Updater +{ + public static class IEnumerableExtensions + { + public static bool IsEmpty(this IEnumerable source) + { + if (source == null) + { + throw new ArgumentNullException(nameof(source)); + } + + using (IEnumerator enumerator = source.GetEnumerator()) + { + if (enumerator.MoveNext()) + { + return false; + } + } + + return true; + } + + public static bool HasOne(this IEnumerable source) + { + if (source == null) + { + throw new ArgumentNullException(nameof(source)); + } + + var count = 0; + + using (IEnumerator enumerator = source.GetEnumerator()) + { + while (enumerator.MoveNext()) + { + if (++count > 1) + return false; + } + } + + return count == 1; + } + } +} diff --git a/src/ServerManager.Updater/Program.cs b/src/ServerManager.Updater/Program.cs index ec4ea0c9..a7c9238c 100644 --- a/src/ServerManager.Updater/Program.cs +++ b/src/ServerManager.Updater/Program.cs @@ -277,7 +277,7 @@ namespace ServerManagerTool.Updater var processes = ProcessUtils.GetProcesses(process.ProcessName, executablePath); // check if there is more than one instance of the application running - if (processes.Length != 1) + if (!processes.HasOne()) throw new Exception("The application to be updated has more than one instance running."); // get the command line of the process diff --git a/src/ServerManager.Updater/Utils/ProcessUtils.cs b/src/ServerManager.Updater/Utils/ProcessUtils.cs index ccf7248f..c60d54c5 100644 --- a/src/ServerManager.Updater/Utils/ProcessUtils.cs +++ b/src/ServerManager.Updater/Utils/ProcessUtils.cs @@ -1,4 +1,5 @@ using System; +using System.Collections.Generic; using System.ComponentModel; using System.Diagnostics; using System.IO; @@ -83,7 +84,7 @@ namespace ServerManagerTool.Updater return Process.GetProcessById(processId); } - public static Process[] GetProcesses(string processName, string executablePath) + public static IEnumerable GetProcesses(string processName, string executablePath) { var runningProcesses = Process.GetProcessesByName(processName).ToList(); @@ -95,7 +96,7 @@ namespace ServerManagerTool.Updater runningProcesses.RemoveAt(i); } - return runningProcesses.ToArray(); + return runningProcesses; } public static bool IsAlreadyRunning()